diff --git a/DEPS b/DEPS
index 83bb2f79..353b879 100644
--- a/DEPS
+++ b/DEPS
@@ -294,11 +294,11 @@
   # 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': '3e2c3b98e53dd8a76e3196c383d6692c9a661a73',
+  'skia_revision': '43c262f54f69ea547666f3ee8740170f79299784',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': '7340824e150b71e6946823947d31d2299cd9c3e1',
+  'v8_revision': '79505e36825784d7bd501ea02062a0e817b04c6c',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
@@ -310,7 +310,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': 'bd10345940d1d20e744f4816b646b532ae7b51a7',
+  'pdfium_revision': 'd91ef173878574803000e455d882092cdd4f7104',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling BoringSSL
   # and whatever else without interference from each other.
@@ -365,7 +365,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': 'e8f62ffda835deb1aa2230d018ee388365342226',
+  'catapult_revision': '880567709d857be348bd9d88d3a1de43abc16ed3',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -453,7 +453,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'libcxxabi_revision':    '3007992fc7706df4e0b17b63eaf601c421e163ae',
+  'libcxxabi_revision':    'b954e3e65634a9e2f7b595598a30c455f5f2eb26',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -859,7 +859,7 @@
       'packages': [
         {
           'package': 'chromium/rts/model/linux-amd64',
-          'version': 'c3aMpnAo1ue6RABQnk2axNqx6H43jCYufXngHyx5bCUC',
+          'version': 'v26lblo8DLzgn9p0Ew4JSx7FV7oPo9ngXPmFr8CE9cQC',
         },
       ],
       'dep_type': 'cipd',
@@ -870,7 +870,7 @@
       'packages': [
         {
           'package': 'chromium/rts/model/mac-amd64',
-          'version': 'C4igURhSjy8bfAI0tq6BmtRGzeVOAIWCdm3rgPvN-w8C',
+          'version': 'IuzFXXBtgnmd-AAR6zw5DTZRzH6BOaROPGURtjSi6xUC',
         },
       ],
       'dep_type': 'cipd',
@@ -1153,7 +1153,7 @@
   # Tools used when building Chrome for Chrome OS. This affects both the Simple
   # Chrome workflow, as well as the chromeos-chrome ebuild.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '338f8b7d8a223b8d8e1a984f3f0f00e02cf1067c',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'fd92310398dc2d3d97087d863e95afff553dada9',
       'condition': 'checkout_chromeos',
   },
 
@@ -1190,7 +1190,7 @@
     Var('chromium_git') + '/chromium/dom-distiller/dist.git' + '@' + '199de96b345ada7c6e7e6ba3d2fa7a6911b8767d',
 
   'src/third_party/eigen3/src':
-    Var('chromium_git') + '/external/gitlab.com/libeigen/eigen.git' + '@' + 'b02c384ef4e8eba7b8bdef16f9dc6f8f4d6a6b2b',
+    Var('chromium_git') + '/external/gitlab.com/libeigen/eigen.git' + '@' + '0e187141679fdb91da33249d18cb79a011c0e2ea',
 
   'src/third_party/emoji-metadata/src': {
     'url': Var('chromium_git') + '/external/github.com/googlefonts/emoji-metadata' + '@' + '8de89a7a36cd024dcd30ac9f67f3f02c37a7c8fb',
@@ -1564,7 +1564,7 @@
     Var('chromium_git') + '/external/github.com/cisco/openh264' + '@' + 'fac04ceb3e966f613ed17e98178e9d690280bba6',
 
   'src/third_party/openscreen/src':
-    Var('chromium_git') + '/openscreen' + '@' + 'ea3b9a20ac6e5adb4d9ff0a666759ce0195bc77d',
+    Var('chromium_git') + '/openscreen' + '@' + 'cd61d5d54ff63d98f5f0db4faf16cda863714e59',
 
   'src/third_party/openxr/src': {
     'url': Var('chromium_git') + '/external/github.com/KhronosGroup/OpenXR-SDK' + '@' + 'bf21ccb1007bb531b45d9978919a56ea5059c245',
@@ -1668,7 +1668,7 @@
     Var('chromium_git') + '/external/github.com/google/snappy.git' + '@' + '65dc7b383985eb4f63cd3e752136db8d9b4be8c0',
 
   'src/third_party/sqlite/src':
-    Var('chromium_git') + '/chromium/deps/sqlite.git' + '@' + '0fe9a75bf2b7e41e5f1bc9fe99d4712f49a442af',
+    Var('chromium_git') + '/chromium/deps/sqlite.git' + '@' + '98bd61ec66ad759f14743f001fb0a55e82417983',
 
   'src/third_party/sqlite4java': {
       'packages': [
@@ -1699,7 +1699,7 @@
     Var('chromium_git') + '/external/github.com/GoogleChromeLabs/text-fragments-polyfill.git' + '@' + 'c036420683f672d685e27415de0a5f5e85bdc23f',
 
   'src/third_party/tflite/src':
-    Var('chromium_git') + '/external/github.com/tensorflow/tensorflow.git' + '@' + '77b5b086f95d3d20b90704ead451923f9dad0014',
+    Var('chromium_git') + '/external/github.com/tensorflow/tensorflow.git' + '@' + 'ca77d58bb625ee21bc33660c074c624659b8a3c2',
 
   'src/third_party/turbine': {
       'packages': [
@@ -1712,7 +1712,7 @@
       'dep_type': 'cipd',
   },
 
-  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@07a6dec37162e62dc905236610256620c643853d',
+  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@3cc80b7988b64231617ce87582c4d3d01e61503a',
 
   'src/third_party/vulkan_memory_allocator':
     Var('chromium_git') + '/external/github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git' + '@' + 'ebe84bec02c041d28f902da0214bf442743fc907',
@@ -1748,10 +1748,10 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '44e4c8770158c505b03ee7feafa4859d083b0912',
 
   'src/third_party/webgpu-cts/src':
-    Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + 'a41bc407a83a6851e4de0cd04b2ac550bbae10fb',
+    Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '9d30433b74d4b77d5431427f0dc8301150c2ced7',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '70d6f2ed63a20299bf71cab90232a88cc9923bdc',
+    Var('webrtc_git') + '/src.git' + '@' + '98c78cdd20215e5fb9b429e4a136f477c7a4b445',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1824,7 +1824,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@d7b362b78b293f8868e14e0721ea681b4ec07c58',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@10e53579b11c64ddfc57dc7c22d98bb6f0b96027',
     'condition': 'checkout_src_internal',
   },
 
@@ -1854,7 +1854,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/help_app/app',
-        'version': 'mewhVDMYH321aHifZWWNvZHdI4UpOHyn6nRFCugBA7AC',
+        'version': 'vXBXDJDqL26tLZcVAaMPVzgmj4SEZtXfWtvZ5fmCk6gC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -1865,7 +1865,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/media_app/app',
-        'version': '6NMpSYQ8ySQzb56w_X8amC3FHJrjbGFy8UNLddI-Y-gC',
+        'version': '0KIc7P9AUQu8VWy9sgXWGQZ9z4sBC2eLEq7LZVFNUM8C',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -1876,7 +1876,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/projector_app/app',
-        'version': 'fWslfNfWDX-DAdVmMw2JMlKVHFzEdhkYyPlXA8sETIUC',
+        'version': 'mkMCmZJQy8YIAl8XyoqwVY68-MXU3wE2nk31_1nDy-0C',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
diff --git a/android_webview/js_sandbox/java/src/org/chromium/android_webview/js_sandbox/client/JsIsolate.java b/android_webview/js_sandbox/java/src/org/chromium/android_webview/js_sandbox/client/JsIsolate.java
index 367a2a0..59a15ffc 100644
--- a/android_webview/js_sandbox/java/src/org/chromium/android_webview/js_sandbox/client/JsIsolate.java
+++ b/android_webview/js_sandbox/java/src/org/chromium/android_webview/js_sandbox/client/JsIsolate.java
@@ -30,7 +30,21 @@
 
 import javax.annotation.concurrent.GuardedBy;
 
-/** Provides a sandboxed execution Isolate. This class should be accessed in a single-thread. */
+/**
+ * Environment within a {@link JsSandbox} where Javascript is executed.
+ *
+ * A single {@link JsSandbox} process can contain any number of {@link JsIsolate} instances where JS
+ * can be evaluated independently and in parallel.
+ * <p>
+ * Each isolate has its own state and JS global object,
+ * and cannot interact with any other isolate through JS APIs. There is only a <em>moderate</em>
+ * security boundary between isolates in a single {@link JsSandbox}. If the code in one {@link
+ * JsIsolate} is able to compromise the security of the JS engine then it may be able to observe or
+ * manipulate other isolates, since they run in the same process. For strong isolation multiple
+ * {@link JsSandbox} processes should be used, but it is not supported at the moment.
+ * <p>
+ * Each isolate object must only be used from one thread.
+ */
 public class JsIsolate implements AutoCloseable {
     private static final String TAG = "JsIsolate";
     private final Object mSetLock = new Object();
@@ -82,8 +96,36 @@
     }
 
     /**
-     * Evaluates the Javascript code and returns a ListenableFuture for the result of execution.
-     * TODO(crbug.com/1297672): Add timeouts for requests.
+     * Evaluates the given JavaScript code and returns the result.
+     *
+     * There are 3 possible behaviours based on the output of the expression:
+     * <ul>
+     *   <li><strong>If the JS expression returns a JS String</strong>, then the Java Future
+     * resolves to Java String.</li>
+     *   <li><strong>If the JS expression returns a JS Promise</strong>,
+     * and if {@link JsSandbox#isFeatureSupported(String)} for
+     * {@link JsSandbox#PROMISE_RETURN} returns {@code true}, Java Future resolves to Java String
+     *       once the promise resolves. If it returns {@code false}, then the Future resolves to
+     *       empty string.</li>
+     *   <li><strong>If the JS expression returns another data type</strong>, then Java Future
+     * resolves to empty Java String.</li>
+     * </ul>
+     * The environment uses a single JS global object for all the calls to {@link
+     * #evaluateJavascriptAsync(String)} and {@link #provideNamedData(String, byte[])} methods.
+     * These calls are queued up and are run one at a time in sequence, using the single JS
+     * environment for the isolate. The global variables set by one evaluation will be visible for
+     * later evaluations. This is similar to adding multiple {@code <script>} tags in HTML. The
+     * behaviour is also similar to
+     * {@link android.webkit.WebView#evaluateJavascript(String, android.webkit.ValueCallback)}.
+     * <p>
+     * Size of the expression to be evaluated and the result are both limited by the binder
+     * transaction limit. Refer {@link android.os.TransactionTooLargeException} for more details.
+     *
+     * @param code JavaScript code that will be evaluated, it should return a JavaScript String or a
+     *         Promise of a String in case {@link JsSandbox#PROMISE_RETURN} is supported
+     *
+     * @return Future that evaluates to the result String of the evaluation or exceptions({@link
+     *         IsolateTerminatedException}, {@link SandboxDeadException}) if there is an error
      */
     @NonNull
     public ListenableFuture<String> evaluateJavascriptAsync(@NonNull String code) {
@@ -117,6 +159,17 @@
         });
     }
 
+    /**
+     * Closes the {@link JsIsolate} object and renders it unusable.
+     *
+     * Once closed, no more method calls should be made. Pending evaluations will resolve with
+     * {@link IsolateTerminatedException} immediately.
+     * <p>
+     * If {@link JsSandbox#isFeatureSupported(String)} is {@code true} for {@link
+     * JsSandbox#ISOLATE_TERMINATION}, then any pending evaluation is immediately terminated and
+     * memory is freed. If it is {@code false}, the isolate will not get cleaned up until the
+     * pending evaluations have run to completion and will consume resources until then.
+     */
     @Override
     public void close() {
         if (mJsIsolateStub == null) {
@@ -133,6 +186,52 @@
         mGuard.close();
     }
 
+    /**
+     * Provides a byte array for consumption from the JavaScript environment.
+     *
+     * This method provides an efficient way to pass in data from Java into the JavaScript
+     * environment which can be referred to from JavaScript. This is more efficient than including
+     * data in the JS expression, and allows large data to be sent.
+     * <p>
+     * This data can be consumed in the JS environment using {@code
+     * android.consumeNamedDataAsArrayBuffer(String)} by referring to the data with the name that
+     * was used when calling this method. This is a one-time transfer and the calls should be
+     * paired.
+     * <p>
+     * A single name can only be used once in a particular {@link JsIsolate}.
+     * Clients can generate unique names for each call if they
+     * need to use this method multiple times. The same name should be included into the JS code.
+     * <p>
+     * This API can be used to pass a WASM module into the JS
+     * environment for compilation if {@link JsSandbox#isFeatureSupported(String)} returns {@code
+     * true} for {@link JsSandbox#WASM_COMPILATION}.
+     * <br>
+     * In Java,
+     * <pre>
+     *     jsIsolate.provideNamedData("id-1", byteArray);
+     * </pre>
+     * In JS,
+     * <pre>
+     *     android.consumeNamedDataAsArrayBuffer("id-1").then((value) => {
+     *       return WebAssembly.compile(value).then((module) => {
+     *          ...
+     *       });
+     *     });
+     * </pre>
+     * <p>
+     * The environment uses a single JS global object for all the calls to {@link
+     * #evaluateJavascriptAsync(String)} and {@link #provideNamedData(String, byte[])} methods.
+     * <p>
+     * This method should only be called if
+     * {@link JsSandbox#isFeatureSupported(String)}
+     * returns true for {@link JsSandbox#PROVIDE_CONSUME_ARRAY_BUFFER}.
+     *
+     * @param name Identifier for the data that is passed, the same identifier should be used in the
+     *         JavaScript environment to refer to the data
+     * @param inputBytes Bytes to be passed into the JavaScript environment
+     *
+     * @return {@code true} on success, {@code false} otherwise
+     */
     @RequiresFeature(name = JsSandbox.PROVIDE_CONSUME_ARRAY_BUFFER,
             enforcement =
                     "org.chromium.android_webview.js_sandbox.client.JsSandbox#isFeatureSupported")
diff --git a/android_webview/js_sandbox/java/src/org/chromium/android_webview/js_sandbox/client/JsSandbox.java b/android_webview/js_sandbox/java/src/org/chromium/android_webview/js_sandbox/client/JsSandbox.java
index e749651..855ee85 100644
--- a/android_webview/js_sandbox/java/src/org/chromium/android_webview/js_sandbox/client/JsSandbox.java
+++ b/android_webview/js_sandbox/java/src/org/chromium/android_webview/js_sandbox/client/JsSandbox.java
@@ -36,7 +36,26 @@
 
 import javax.annotation.concurrent.GuardedBy;
 
-/** Sandbox that can execute JS in a safe environment. This class is thread safe. */
+/**
+ * Sandbox that provides APIs for JavaScript evaluation in a restricted environment.
+ *
+ * JsSandbox represents a connection to an isolated process. The isolated process will be exclusive
+ * to the calling app (i.e. it doesn't share anything with, and can't be compromised by another
+ * app's isolated process).
+ * <p>
+ * Code that is run in a sandbox does not have any access to data
+ * belonging to the original app unless explicitly passed into it by using the methods of this
+ * class. This provides a security boundary between the calling app and the Javascript execution
+ * environment.
+ * <p>
+ * The calling app can only have only one isolated process at a time, so only one
+ * instance of this object can exist at a time.
+ * <p>
+ * It's safe to share a single {@link JsSandbox}
+ * object with multiple threads and use it from multiple threads at once.
+ * For example, {@link JsSandbox} can be stored at a global location and multiple threads can create
+ * their own {@link JsIsolate} objects from it but the {@link JsIsolate} object cannot be shared.
+ */
 public class JsSandbox implements AutoCloseable {
     // TODO(crbug.com/1297672): Add capability to this class to support spawning
     // different processes as needed. This might require that we have a static
@@ -74,34 +93,43 @@
 
     /**
      * Feature for {@link #isFeatureSupported(String)}.
-     * <p>This features provides additional behaviour to {@link JsIsolate#close()}. When this
+     *
+     * When this
      * feature is present, {@link JsIsolate#close()} will terminate the currently running JS
-     * evaluation and close the isolate. If it is absent, {@link JsIsolate#close()} will close the
-     * isolate after all pending evaluations are run.
+     * evaluation and close the isolate. If it is absent, {@link JsIsolate#close()} cannot terminate
+     * any running or queued evaluations in the background,
+     * so the isolate will continue to consume resources until they complete.
+     * <p>
+     * Irrespective of this feature, calling {@link JsSandbox#close()} will terminate all
+     * {@link JsIsolate} objects (and the isolated process) immediately and all pending
+     * {@link JsIsolate#evaluateJavascriptAsync(String)} futures will resolve with {@link
+     * IsolateTerminatedException}.
      */
     public static final String ISOLATE_TERMINATION = "ISOLATE_TERMINATION";
 
     /**
      * Feature for {@link #isFeatureSupported(String)}.
-     * <p>This features provides additional behaviour to {@link
-     * JsIsolate#evaluateJavascriptAsync(String)} ()}. When this feature is present, we support the
-     * JS to return promises, and {@link JsIsolate#evaluateJavascriptAsync(String)} waits for the
-     * promise to resolve and returns the resolved value of the promise.
+     *
+     * When this feature is present, JS expressions may return promises. The Future returned by
+     * {@link JsIsolate#evaluateJavascriptAsync(String)} will resolve to the promise's result,
+     * once the promise resolves.
      */
     public static final String PROMISE_RETURN = "PROMISE_RETURN";
 
     /**
      * Feature for {@link #isFeatureSupported(String)}.
-     * This feature covers {@link JsIsolate#provideNamedData(String, byte[])}
-     * <p>This also covers the JS API android.consumeNamedDataAsArrayBuffer(string).
+     * When this feature is present, {@link JsIsolate#provideNamedData(String, byte[])} can be used.
+     * <p>
+     * This also covers the JS API android.consumeNamedDataAsArrayBuffer(string).
      */
     public static final String PROVIDE_CONSUME_ARRAY_BUFFER = "PROVIDE_CONSUME_ARRAY_BUFFER";
 
     /**
      * Feature for {@link #isFeatureSupported(String)}.
-     * <p>This features provides additional behaviour to {@link
-     * JsIsolate#evaluateJavascriptAsync(String)} ()}. When this feature is present, we support
-     * using the JS API WebAssembly.compile(ArrayBuffer).
+     *
+     * This features provides additional behaviour to {@link
+     * JsIsolate#evaluateJavascriptAsync(String)} ()}. When this feature is present, the JS API
+     * WebAssembly.compile(ArrayBuffer) can be used.
      */
     public static final String WASM_COMPILATION = "WASM_COMPILATION";
 
@@ -159,12 +187,17 @@
     }
 
     /**
-     * Use this method to create new instances that are connected to the service. We only support
-     * creation of a single connected instance, we would need to add restrictions to enforce this.
+     * Asynchronously create and connect to the sandbox process.
+     *
+     * Only one sandbox process can exist at a time. Attempting to create a new instance before
+     * the previous instance has been closed will fail with an {@link IllegalStateException}.
      *
      * @param context When the context is destroyed, the connection will be closed. Use an
      *         application
      *     context if the connection is expected to outlive a single activity/service.
+     *
+     * @return Future that evaluates to a connected {@link JsSandbox} instance or an exception if
+     *     binding to service fails.
      */
     @NonNull
     public static ListenableFuture<JsSandbox> newConnectedInstanceAsync(@NonNull Context context) {
@@ -226,7 +259,9 @@
         // This should be at the end of the constructor.
     }
 
-    /** Creates an execution isolate within which JS can be executed multiple times. */
+    /**
+     * Creates and returns an {@link JsIsolate} within which JS can be executed.
+     */
     @NonNull
     public JsIsolate createIsolate() {
         synchronized (mLock) {
@@ -268,7 +303,18 @@
     }
 
     /**
-     * Feature detection interface.
+     * Checks whether a given feature is supported by the JS Sandbox implementation.
+     *
+     * The sandbox implementation is provided by the version of WebView installed on the device.
+     * The app must use this method to check which library features are supported by the device's
+     * implementation before using them.
+     * <p>
+     * A feature check should be made prior to depending
+     * on features enlisted in {@link JsSandboxFeature}.
+     *
+     * @param feature feature to be checked
+     *
+     * @return {@code true} if supported, {@code false} otherwise
      */
     public boolean isFeatureSupported(@NonNull @JsSandboxFeature String feature) {
         synchronized (mLock) {
@@ -291,6 +337,16 @@
         }
     }
 
+    /**
+     * Closes the {@link JsSandbox} object and renders it unusable.
+     *
+     * The client is expected to call this method explicitly to terminate the isolated process.
+     * <p>
+     * Once closed, no more {@link JsSandbox} and {@link JsIsolate} method calls can be made.
+     * Closing will terminate the isolated process immediately. All pending evaluations are
+     * immediately terminated. Once closed, the client may call {@link
+     * JsSandbox#newConnectedInstanceAsync(Context)} to create another {@link JsSandbox}.
+     */
     @Override
     public void close() {
         doClose(new IsolateTerminatedException());
diff --git a/android_webview/junit/src/org/chromium/android_webview/robolectric/AwWebContentsMetricsRecorderTest.java b/android_webview/junit/src/org/chromium/android_webview/robolectric/AwWebContentsMetricsRecorderTest.java
index 6e08cc7d..f7cd428f 100644
--- a/android_webview/junit/src/org/chromium/android_webview/robolectric/AwWebContentsMetricsRecorderTest.java
+++ b/android_webview/junit/src/org/chromium/android_webview/robolectric/AwWebContentsMetricsRecorderTest.java
@@ -23,7 +23,7 @@
 import org.chromium.android_webview.AwWebContentsMetricsRecorder;
 import org.chromium.android_webview.DarkModeHelper;
 import org.chromium.base.metrics.RecordHistogram;
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.Feature;
 
@@ -34,11 +34,11 @@
  * cases.
  */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(manifest = Config.NONE, shadows = {ShadowRecordHistogram.class})
+@Config(manifest = Config.NONE)
 public class AwWebContentsMetricsRecorderTest {
     @Before
     public void setUp() {
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
     }
 
     @Test
diff --git a/ash/accelerators/accelerator_controller_impl.cc b/ash/accelerators/accelerator_controller_impl.cc
index cfb6cfd..c8b7901 100644
--- a/ash/accelerators/accelerator_controller_impl.cc
+++ b/ash/accelerators/accelerator_controller_impl.cc
@@ -46,6 +46,7 @@
 #include "ash/public/cpp/accelerators.h"
 #include "ash/public/cpp/assistant/controller/assistant_ui_controller.h"
 #include "ash/public/cpp/new_window_delegate.h"
+#include "ash/public/cpp/projector/projector_controller.h"
 #include "ash/public/cpp/system/toast_data.h"
 #include "ash/root_window_controller.h"
 #include "ash/rotator/window_rotation.h"
@@ -72,6 +73,7 @@
 #include "ash/wm/desks/desks_controller.h"
 #include "ash/wm/desks/desks_histogram_enums.h"
 #include "ash/wm/desks/desks_util.h"
+#include "ash/wm/float/float_controller.h"
 #include "ash/wm/mru_window_tracker.h"
 #include "ash/wm/overview/overview_controller.h"
 #include "ash/wm/overview/overview_session.h"
@@ -97,7 +99,6 @@
 #include "chromeos/ui/base/display_util.h"
 #include "chromeos/ui/wm/desks/chromeos_desks_histogram_enums.h"
 #include "chromeos/ui/wm/features.h"
-#include "chromeos/ui/wm/window_util.h"
 #include "components/user_manager/user_type.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/aura/client/aura_constants.h"
@@ -826,7 +827,7 @@
   DCHECK(window);
   // TODO(sammiequon|shidi): Add some UI like a bounce if a window cannot be
   // floated.
-  chromeos::ToggleFloating(window);
+  Shell::Get()->float_controller()->ToggleFloat(window);
   base::RecordAction(UserMetricsAction("Accel_Toggle_Floating"));
 }
 
@@ -1467,6 +1468,21 @@
   controller->touch_hud_debug()->ChangeToNextMode();
 }
 
+bool CanHandleToggleProjectorMarker() {
+  auto* projector_controller = ProjectorController::Get();
+  if (projector_controller) {
+    return projector_controller->GetAnnotatorAvailability();
+  }
+  return false;
+}
+
+void HandleToggleProjectorMarker() {
+  auto* projector_controller = ProjectorController::Get();
+  if (projector_controller) {
+    projector_controller->ToggleAnnotationTray();
+  }
+}
+
 }  // namespace
 
 constexpr const char* AcceleratorControllerImpl::kVolumeButtonRegion;
@@ -1927,6 +1943,8 @@
     case TAKE_SCREENSHOT:
     case TAKE_WINDOW_SCREENSHOT:
       return CanHandleScreenshot(action);
+    case TOGGLE_PROJECTOR_MARKER:
+      return CanHandleToggleProjectorMarker();
     case TOGGLE_RESIZE_LOCK_MENU:
       return CanHandleToggleResizeLockMenu();
     case TOGGLE_FLOATING:
@@ -2313,6 +2331,9 @@
     case TOGGLE_IME_MENU_BUBBLE:
       HandleToggleImeMenuBubble();
       break;
+    case TOGGLE_PROJECTOR_MARKER:
+      HandleToggleProjectorMarker();
+      break;
     case SHOW_SHORTCUT_VIEWER:
       HandleShowKeyboardShortcutViewer();
       break;
diff --git a/ash/ambient/backdrop/ambient_backend_controller_impl.cc b/ash/ambient/backdrop/ambient_backend_controller_impl.cc
index afa9d74a..2a179aa 100644
--- a/ash/ambient/backdrop/ambient_backend_controller_impl.cc
+++ b/ash/ambient/backdrop/ambient_backend_controller_impl.cc
@@ -407,8 +407,8 @@
           auto json_handler =
               [](FetchWeatherCallback callback,
                  data_decoder::DataDecoder::ValueOrError result) {
-                if (result.value) {
-                  std::move(callback).Run(ToWeatherInfo(result.value.value()));
+                if (result.has_value()) {
+                  std::move(callback).Run(ToWeatherInfo(*result));
                 } else {
                   DVLOG(1) << "Failed to parse weather json.";
                   std::move(callback).Run(absl::nullopt);
diff --git a/ash/app_list/views/app_list_bubble_apps_page_unittest.cc b/ash/app_list/views/app_list_bubble_apps_page_unittest.cc
index 9ff92c6..3be46d057 100644
--- a/ash/app_list/views/app_list_bubble_apps_page_unittest.cc
+++ b/ash/app_list/views/app_list_bubble_apps_page_unittest.cc
@@ -50,16 +50,20 @@
   }
 
   void OnReorderAnimationDone(base::OnceClosure closure,
+                              bool expect_abort,
                               bool aborted,
                               AppListGridAnimationStatus status) {
-    EXPECT_FALSE(aborted);
+    EXPECT_EQ(aborted, expect_abort);
     EXPECT_EQ(AppListGridAnimationStatus::kReorderFadeIn, status);
     std::move(closure).Run();
   }
 
   // Sorts app list with the specified order. If `wait` is true, wait for the
-  // reorder animation to complete.
-  void SortAppList(const absl::optional<AppListSortOrder>& order, bool wait) {
+  // reorder animation to complete. The animation is expected to be aborted if
+  // `expect_abort` is set to true.
+  void SortAppList(const absl::optional<AppListSortOrder>& order,
+                   bool wait,
+                   bool expect_abort = false) {
     AppListController::Get()->UpdateAppListWithNewTemporarySortOrder(
         order,
         /*animate=*/true, /*update_position_closure=*/base::DoNothing());
@@ -73,7 +77,7 @@
         ->scrollable_apps_grid_view()
         ->AddReorderCallbackForTest(base::BindRepeating(
             &AppListBubbleAppsPageTest::OnReorderAnimationDone,
-            base::Unretained(this), run_loop.QuitClosure()));
+            base::Unretained(this), run_loop.QuitClosure(), expect_abort));
     run_loop.Run();
   }
 
@@ -640,5 +644,24 @@
   EXPECT_FALSE(toast_container->IsToastVisible());
 }
 
+// Verifies that sorting the app list with no app is properly handled.
+TEST_F(AppListBubbleAppsPageTest, SortingAppListWithNoApp) {
+  ui::ScopedAnimationDurationScaleMode scope_duration(
+      ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
+
+  auto* helper = GetAppListTestHelper();
+  helper->ShowAppList();
+
+  // Sort app list that contains no app. The animation should be aborted in this
+  // case.
+  SortAppList(AppListSortOrder::kNameAlphabetical, /*wait=*/true,
+              /*expect_abort=*/true);
+
+  // Make sure the toast container shows up even if the animation is aborted.
+  auto* toast_container =
+      helper->GetBubbleAppsPage()->toast_container_for_test();
+  EXPECT_TRUE(toast_container->IsToastVisible());
+}
+
 }  // namespace
 }  // namespace ash
diff --git a/ash/app_list/views/apps_grid_view.cc b/ash/app_list/views/apps_grid_view.cc
index 717c7557..14aa6df 100644
--- a/ash/app_list/views/apps_grid_view.cc
+++ b/ash/app_list/views/apps_grid_view.cc
@@ -2111,8 +2111,22 @@
   const absl::optional<VisibleItemIndexRange> range =
       GetVisibleItemIndexRange();
 
-  // TODO(https://crbug.com/1289411): handle the case that `range` is null.
-  DCHECK(range);
+  views::AnimationBuilder animation_builder;
+
+  // No items to be sorted are visible - return an empty animation builder that
+  // ends immediately.
+  if (!range) {
+    animation_builder
+        .OnEnded(base::BindOnce(&AppsGridView::OnFadeInAnimationEnded,
+                                weak_factory_.GetWeakPtr(), done_callback,
+                                /*abort=*/true))
+        .OnAborted(base::BindOnce(&AppsGridView::OnFadeInAnimationEnded,
+                                  weak_factory_.GetWeakPtr(), done_callback,
+                                  /*abort=*/true))
+        .Once()
+        .SetDuration(base::TimeDelta());
+    return animation_builder;
+  }
 
   // Only show the visible items during animation to reduce the cost of painting
   // that is triggered by view bounds changes due to reorder.
@@ -2121,7 +2135,6 @@
     view_model_.view_at(visible_view_index)->SetVisible(true);
   }
 
-  views::AnimationBuilder animation_builder;
   grid_animation_abort_handle_ = animation_builder.GetAbortHandle();
   animation_builder
       .OnEnded(base::BindOnce(&AppsGridView::OnFadeInAnimationEnded,
diff --git a/ash/frame/caption_buttons/frame_caption_button_container_view_unittest.cc b/ash/frame/caption_buttons/frame_caption_button_container_view_unittest.cc
index 8936dc4..4012fbd 100644
--- a/ash/frame/caption_buttons/frame_caption_button_container_view_unittest.cc
+++ b/ash/frame/caption_buttons/frame_caption_button_container_view_unittest.cc
@@ -13,6 +13,7 @@
 #include "base/test/bind.h"
 #include "base/test/scoped_feature_list.h"
 #include "chromeos/ui/base/window_properties.h"
+#include "chromeos/ui/base/window_state_type.h"
 #include "chromeos/ui/vector_icons/vector_icons.h"
 #include "chromeos/ui/wm/features.h"
 #include "ui/events/test/event_generator.h"
@@ -377,19 +378,19 @@
   widget->GetContentsView()->AddChildView(&container);
   container.Layout();
   FrameCaptionButtonContainerView::TestApi testApi(&container);
-  FloatController* controller = Shell::Get()->float_controller();
   ClickFloatButton(&testApi);
   auto* window_state = WindowState::Get(widget->GetNativeWindow());
   // Check if window is floated.
   auto* window = widget->GetNativeWindow();
   EXPECT_TRUE(window_state->IsFloated());
-  EXPECT_TRUE(window->GetProperty(chromeos::kWindowToggleFloatKey));
-  EXPECT_TRUE(controller->IsFloated(window));
+  EXPECT_EQ(window->GetProperty(chromeos::kWindowStateTypeKey),
+            chromeos::WindowStateType::kFloated);
   ClickFloatButton(&testApi);
   // Check if window is unfloated.
   EXPECT_TRUE(window_state->IsNormalStateType());
-  EXPECT_FALSE(controller->IsFloated(window));
-  EXPECT_FALSE(window->GetProperty(chromeos::kWindowToggleFloatKey));
+  EXPECT_FALSE(window_state->IsFloated());
+  EXPECT_EQ(window->GetProperty(chromeos::kWindowStateTypeKey),
+            chromeos::WindowStateType::kNormal);
 }
 
 }  // namespace ash
diff --git a/ash/login/ui/scrollable_users_list_view.cc b/ash/login/ui/scrollable_users_list_view.cc
index ebb3418..cd71a3f 100644
--- a/ash/login/ui/scrollable_users_list_view.cc
+++ b/ash/login/ui/scrollable_users_list_view.cc
@@ -297,9 +297,10 @@
       SkScalar bottom_gradient_start = 1.f - top_gradient_end;
       SkScalar color_positions[4] = {0.f, top_gradient_end,
                                      bottom_gradient_start, 1.f};
-      SkColor colors[4] = {gradient_params_.color_from,
-                           gradient_params_.color_to, gradient_params_.color_to,
-                           gradient_params_.color_from};
+      SkColor4f colors[4] = {SkColor4f::FromColor(gradient_params_.color_from),
+                             SkColor4f::FromColor(gradient_params_.color_to),
+                             SkColor4f::FromColor(gradient_params_.color_to),
+                             SkColor4f::FromColor(gradient_params_.color_from)};
 
       flags.setShader(cc::PaintShader::MakeLinearGradient(
           in_view_coordinates, colors, color_positions, 4, SkTileMode::kClamp));
diff --git a/ash/multi_device_setup/multi_device_notification_presenter.cc b/ash/multi_device_setup/multi_device_notification_presenter.cc
index 1458de86..288a32f 100644
--- a/ash/multi_device_setup/multi_device_notification_presenter.cc
+++ b/ash/multi_device_setup/multi_device_notification_presenter.cc
@@ -30,6 +30,8 @@
 
 namespace {
 
+bool g_disable_notifications_for_test_ = false;
+
 const char kNotifierMultiDevice[] = "ash.multi_device_setup";
 
 }  // namespace
@@ -152,6 +154,13 @@
                                 NotificationType::kWifiSyncAnnouncement);
 }
 
+// static
+std::unique_ptr<base::AutoReset<bool>>
+MultiDeviceNotificationPresenter::DisableNotificationsForTesting() {
+  return std::make_unique<base::AutoReset<bool>>(
+      &g_disable_notifications_for_test_, true);
+}
+
 void MultiDeviceNotificationPresenter::RemoveMultiDeviceSetupNotification() {
   notification_status_ = Status::kNoNotificationVisible;
   message_center_->RemoveNotification(kSetupNotificationId,
@@ -298,6 +307,9 @@
     const std::u16string& title,
     const std::u16string& message,
     message_center::RichNotificationData optional_fields) {
+  if (g_disable_notifications_for_test_)
+    return;
+
   std::unique_ptr<message_center::Notification> notification =
       CreateSystemNotification(
           message_center::NotificationType::NOTIFICATION_TYPE_SIMPLE, id, title,
diff --git a/ash/multi_device_setup/multi_device_notification_presenter.h b/ash/multi_device_setup/multi_device_notification_presenter.h
index c48ba7c..08934fc4 100644
--- a/ash/multi_device_setup/multi_device_notification_presenter.h
+++ b/ash/multi_device_setup/multi_device_notification_presenter.h
@@ -11,6 +11,7 @@
 #include "ash/ash_export.h"
 #include "ash/public/cpp/session/session_observer.h"
 #include "ash/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h"
+#include "base/auto_reset.h"
 #include "base/callback.h"
 #include "base/memory/weak_ptr.h"
 #include "mojo/public/cpp/bindings/receiver.h"
@@ -54,6 +55,10 @@
 
   ~MultiDeviceNotificationPresenter() override;
 
+  // Disables notifications for tests.
+  static std::unique_ptr<base::AutoReset<bool>>
+  DisableNotificationsForTesting();
+
   // Removes the notification created by NotifyPotentialHostExists() or does
   // nothing if that notification is not currently displayed.
   void RemoveMultiDeviceSetupNotification();
diff --git a/ash/projector/projector_annotation_tray.h b/ash/projector/projector_annotation_tray.h
index 1465cdc..bd0c284 100644
--- a/ash/projector/projector_annotation_tray.h
+++ b/ash/projector/projector_annotation_tray.h
@@ -52,9 +52,9 @@
 
   void HideAnnotationTray();
   void SetTrayEnabled(bool enabled);
+  void ToggleAnnotator();
 
  private:
-  void ToggleAnnotator();
   void EnableAnnotatorWithPenColor();
   // Deactivates any annotation tool that is currently enabled and updates the
   // UI.
diff --git a/ash/projector/projector_controller_impl.cc b/ash/projector/projector_controller_impl.cc
index d2702049..79a5cad6 100644
--- a/ash/projector/projector_controller_impl.cc
+++ b/ash/projector/projector_controller_impl.cc
@@ -293,6 +293,14 @@
   ui_controller_->OnCanvasInitialized(success);
 }
 
+bool ProjectorControllerImpl::GetAnnotatorAvailability() {
+  return ui_controller_->GetAnnotatorAvailability();
+}
+
+void ProjectorControllerImpl::ToggleAnnotationTray() {
+  return ui_controller_->ToggleAnnotationTray();
+}
+
 void ProjectorControllerImpl::OnRecordingStarted(aura::Window* current_root,
                                                  bool is_in_projector_mode) {
   if (!is_in_projector_mode) {
diff --git a/ash/projector/projector_controller_impl.h b/ash/projector/projector_controller_impl.h
index 00865d7..31b2d48 100644
--- a/ash/projector/projector_controller_impl.h
+++ b/ash/projector/projector_controller_impl.h
@@ -78,6 +78,8 @@
   void OnUndoRedoAvailabilityChanged(bool undo_available,
                                      bool redo_available) override;
   void OnCanvasInitialized(bool success) override;
+  bool GetAnnotatorAvailability() override;
+  void ToggleAnnotationTray() override;
 
   // Create the screencast container directory. If there is an error, the
   // callback will be triggered with an empty FilePath.
diff --git a/ash/projector/projector_ui_controller.cc b/ash/projector/projector_ui_controller.cc
index 7f5ae71..40ae96a 100644
--- a/ash/projector/projector_ui_controller.cc
+++ b/ash/projector/projector_ui_controller.cc
@@ -76,7 +76,7 @@
     projector_annotation_tray->SetVisiblePreferred(visible);
 }
 
-void ToggleAnnotator() {
+void ToggleAnnotatorCanvas() {
   auto* capture_mode_controller = CaptureModeController::Get();
   // TODO(b/200292852): This check should not be necessary, but because
   // several Projector unit tests that rely on mocking and don't test the real
@@ -168,7 +168,7 @@
 
 void ProjectorUiController::EnableAnnotatorTool() {
   if (!annotator_enabled_) {
-    ToggleAnnotator();
+    ToggleAnnotatorCanvas();
     annotator_enabled_ = !annotator_enabled_;
     RecordToolbarMetrics(ProjectorToolbar::kMarkerTool);
   }
@@ -181,7 +181,7 @@
 
 void ProjectorUiController::ResetTools() {
   if (annotator_enabled_) {
-    ToggleAnnotator();
+    ToggleAnnotatorCanvas();
     annotator_enabled_ = false;
     ash::ProjectorAnnotatorController::Get()->Clear();
   }
@@ -192,6 +192,20 @@
   UpdateTrayEnabledState();
 }
 
+bool ProjectorUiController::GetAnnotatorAvailability() {
+  if (!canvas_initialized_state_) {
+    return false;
+  }
+  return *canvas_initialized_state_;
+}
+
+void ProjectorUiController::ToggleAnnotationTray() {
+  if (auto* projector_annotation_tray =
+          GetProjectorAnnotationTrayForRoot(current_root_)) {
+    projector_annotation_tray->ToggleAnnotator();
+  }
+}
+
 void ProjectorUiController::OnRecordedWindowChangingRoot(
     aura::Window* new_root) {
   DCHECK_NE(new_root, current_root_);
@@ -199,7 +213,7 @@
   SetProjectorAnnotationTrayVisibility(current_root_, /*visible=*/false);
   SetProjectorAnnotationTrayVisibility(new_root, /*visible=*/true);
   current_root_ = new_root;
-  if (canvas_initialized_state_)
+  if (GetAnnotatorAvailability())
     UpdateTrayEnabledState();
 }
 
@@ -221,7 +235,7 @@
 void ProjectorUiController::UpdateTrayEnabledState() {
   if (auto* projector_annotation_tray =
           GetProjectorAnnotationTrayForRoot(current_root_)) {
-    projector_annotation_tray->SetTrayEnabled(*canvas_initialized_state_);
+    projector_annotation_tray->SetTrayEnabled(GetAnnotatorAvailability());
   }
 }
 }  // namespace ash
diff --git a/ash/projector/projector_ui_controller.h b/ash/projector/projector_ui_controller.h
index cdcb3bd7..a0e8fdc 100644
--- a/ash/projector/projector_ui_controller.h
+++ b/ash/projector/projector_ui_controller.h
@@ -49,6 +49,10 @@
   void ResetTools();
   // Invoked when the canvas has either succeeded or failed to initialize.
   void OnCanvasInitialized(bool success);
+  // Returns if the annotation canvas has been initialized.
+  bool GetAnnotatorAvailability();
+  // Toggles the UI of the annotation tray and the marker's enabled state.
+  void ToggleAnnotationTray();
 
   void OnRecordedWindowChangingRoot(aura::Window* new_root);
 
diff --git a/ash/projector/projector_ui_controller_unittest.cc b/ash/projector/projector_ui_controller_unittest.cc
index ca27f55..39f68dda 100644
--- a/ash/projector/projector_ui_controller_unittest.cc
+++ b/ash/projector/projector_ui_controller_unittest.cc
@@ -162,6 +162,18 @@
                                       /*count=*/1);
 }
 
+TEST_F(ProjectorUiControllerTest, ToggleMarkerWithKeyboardShortcut) {
+  controller_->ShowAnnotationTray(Shell::GetPrimaryRootWindow());
+  controller_->OnCanvasInitialized(true);
+
+  auto* event_generator = GetEventGenerator();
+  event_generator->PressAndReleaseKey(ui::VKEY_OEM_3, ui::EF_COMMAND_DOWN);
+  EXPECT_TRUE(controller_->is_annotator_enabled());
+
+  event_generator->PressAndReleaseKey(ui::VKEY_OEM_3, ui::EF_COMMAND_DOWN);
+  EXPECT_FALSE(controller_->is_annotator_enabled());
+}
+
 TEST_F(ProjectorUiControllerTest, SetAnnotatorTool) {
   auto* projector_annotation_tray = Shell::GetPrimaryRootWindowController()
                                         ->GetStatusAreaWidget()
diff --git a/ash/public/cpp/accelerators.cc b/ash/public/cpp/accelerators.cc
index 5f099df7a..73453860 100644
--- a/ash/public/cpp/accelerators.cc
+++ b/ash/public/cpp/accelerators.cc
@@ -233,6 +233,9 @@
     // ARC-specific shortcut.
     {true, ui::VKEY_C, ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN,
      TOGGLE_RESIZE_LOCK_MENU},
+
+    // Projector shortcuts.
+    {true, ui::VKEY_OEM_3, ui::EF_COMMAND_DOWN, TOGGLE_PROJECTOR_MARKER},
 };
 
 const size_t kAcceleratorDataLength = std::size(kAcceleratorData);
diff --git a/ash/public/cpp/accelerators.h b/ash/public/cpp/accelerators.h
index 892d4e84..e8eb1e8 100644
--- a/ash/public/cpp/accelerators.h
+++ b/ash/public/cpp/accelerators.h
@@ -127,6 +127,7 @@
   TOGGLE_MESSAGE_CENTER_BUBBLE,
   TOGGLE_MIRROR_MODE,
   TOGGLE_OVERVIEW,
+  TOGGLE_PROJECTOR_MARKER,
   TOGGLE_RESIZE_LOCK_MENU,
   TOGGLE_SPOKEN_FEEDBACK,
   TOGGLE_SYSTEM_TRAY_BUBBLE,
diff --git a/ash/public/cpp/projector/projector_controller.h b/ash/public/cpp/projector/projector_controller.h
index 6024bdb8..d0d164a 100644
--- a/ash/public/cpp/projector/projector_controller.h
+++ b/ash/public/cpp/projector/projector_controller.h
@@ -89,6 +89,12 @@
                                              bool redo_available) = 0;
   // Called when the ink canvas has either succeeded or failed in initializing.
   virtual void OnCanvasInitialized(bool success) = 0;
+
+  // Returns if the annotatotion canvas is available.
+  virtual bool GetAnnotatorAvailability() = 0;
+
+  // Toggles the Projector annotation tray UI and marker enabled state.
+  virtual void ToggleAnnotationTray() = 0;
 };
 
 }  // namespace ash
diff --git a/ash/public/cpp/test/mock_projector_controller.h b/ash/public/cpp/test/mock_projector_controller.h
index 266576b5..0439f6b 100644
--- a/ash/public/cpp/test/mock_projector_controller.h
+++ b/ash/public/cpp/test/mock_projector_controller.h
@@ -33,6 +33,8 @@
   MOCK_METHOD2(OnUndoRedoAvailabilityChanged,
                void(bool undo_available, bool redo_available));
   MOCK_METHOD1(OnCanvasInitialized, void(bool success));
+  MOCK_METHOD0(GetAnnotatorAvailability, bool());
+  MOCK_METHOD0(ToggleAnnotationTray, void());
 };
 
 }  // namespace ash
diff --git a/ash/quick_pair/common/fast_pair/fast_pair_metrics.cc b/ash/quick_pair/common/fast_pair/fast_pair_metrics.cc
index 871308ba9..662849f 100644
--- a/ash/quick_pair/common/fast_pair/fast_pair_metrics.cc
+++ b/ash/quick_pair/common/fast_pair/fast_pair_metrics.cc
@@ -185,6 +185,19 @@
     "Bluetooth.ChromeOS.FastPair.ConfirmPasskey.Latency";
 const char kFastPairRetryCount[] =
     "Bluetooth.ChromeOS.FastPair.PairRetry.Count";
+const char kSavedDeviceRemoveResult[] =
+    "Bluetooth.ChromeOS.FastPair.SavedDevices.Remove.Result";
+const char kSavedDeviceUpdateOptInStatusInitialResult[] =
+    "Bluetooth.ChromeOS.FastPair.SavedDevices.UpdateOptInStatus.Result."
+    "InitialPairingProtocol";
+const char kSavedDeviceUpdateOptInStatusRetroactiveResult[] =
+    "Bluetooth.ChromeOS.FastPair.SavedDevices.UpdateOptInStatus.Result."
+    "RetroactivePairingProtocol";
+const char kSavedDeviceUpdateOptInStatusSubsequentResult[] =
+    "Bluetooth.ChromeOS.FastPair.SavedDevices.UpdateOptInStatus.Result."
+    "SubsequentPairingProtocol";
+const char kSavedDeviceGetDevicesResult[] =
+    "Bluetooth.ChromeOS.FastPair.SavedDevices.GetSavedDevices.Result";
 
 }  // namespace
 
@@ -538,5 +551,31 @@
                                 /*exclusive_max=*/10);
 }
 
+void RecordSavedDevicesRemoveResult(bool success) {
+  base::UmaHistogramBoolean(kSavedDeviceRemoveResult, success);
+}
+
+void RecordSavedDevicesUpdatedOptInStatusResult(const Device& device,
+                                                bool success) {
+  switch (device.protocol) {
+    case Protocol::kFastPairInitial:
+      base::UmaHistogramBoolean(kSavedDeviceUpdateOptInStatusInitialResult,
+                                success);
+      break;
+    case Protocol::kFastPairRetroactive:
+      base::UmaHistogramBoolean(kSavedDeviceUpdateOptInStatusRetroactiveResult,
+                                success);
+      break;
+    case Protocol::kFastPairSubsequent:
+      base::UmaHistogramBoolean(kSavedDeviceUpdateOptInStatusSubsequentResult,
+                                success);
+      break;
+  }
+}
+
+void RecordGetSavedDevicesResult(bool success) {
+  base::UmaHistogramBoolean(kSavedDeviceGetDevicesResult, success);
+}
+
 }  // namespace quick_pair
 }  // namespace ash
diff --git a/ash/quick_pair/common/fast_pair/fast_pair_metrics.h b/ash/quick_pair/common/fast_pair/fast_pair_metrics.h
index 1cb037f9..e3c6eda 100644
--- a/ash/quick_pair/common/fast_pair/fast_pair_metrics.h
+++ b/ash/quick_pair/common/fast_pair/fast_pair_metrics.h
@@ -260,6 +260,16 @@
 COMPONENT_EXPORT(QUICK_PAIR_COMMON)
 void RecordPairFailureRetry(int num_retries);
 
+COMPONENT_EXPORT(QUICK_PAIR_COMMON)
+void RecordSavedDevicesRemoveResult(bool success);
+
+COMPONENT_EXPORT(QUICK_PAIR_COMMON)
+void RecordSavedDevicesUpdatedOptInStatusResult(const Device& device,
+                                                bool success);
+
+COMPONENT_EXPORT(QUICK_PAIR_COMMON)
+void RecordGetSavedDevicesResult(bool success);
+
 }  // namespace quick_pair
 }  // namespace ash
 
diff --git a/ash/quick_pair/pairing/fast_pair/fast_pair_pairer_impl.cc b/ash/quick_pair/pairing/fast_pair/fast_pair_pairer_impl.cc
index aa04a47f..a5e28b6 100644
--- a/ash/quick_pair/pairing/fast_pair/fast_pair_pairer_impl.cc
+++ b/ash/quick_pair/pairing/fast_pair/fast_pair_pairer_impl.cc
@@ -488,6 +488,9 @@
 }
 
 void FastPairPairerImpl::OnUpdateOptInStatus(bool success) {
+  RecordSavedDevicesUpdatedOptInStatusResult(/*device=*/*device_,
+                                             /*success=*/success);
+
   if (!success) {
     QP_LOG(WARNING) << __func__ << ": failure";
     return;
diff --git a/ash/quick_pair/pairing/fast_pair/fast_pair_pairer_impl_unittest.cc b/ash/quick_pair/pairing/fast_pair/fast_pair_pairer_impl_unittest.cc
index 4ecddc50..1669d9f 100644
--- a/ash/quick_pair/pairing/fast_pair/fast_pair_pairer_impl_unittest.cc
+++ b/ash/quick_pair/pairing/fast_pair/fast_pair_pairer_impl_unittest.cc
@@ -89,6 +89,15 @@
     "Bluetooth.ChromeOS.FastPair.RequestPasskey.Latency";
 const char kConfirmPasskeyConfirmTime[] =
     "Bluetooth.ChromeOS.FastPair.ConfirmPasskey.Latency";
+const char kSavedDeviceUpdateOptInStatusInitialResult[] =
+    "Bluetooth.ChromeOS.FastPair.SavedDevices.UpdateOptInStatus.Result."
+    "InitialPairingProtocol";
+const char kSavedDeviceUpdateOptInStatusRetroactiveResult[] =
+    "Bluetooth.ChromeOS.FastPair.SavedDevices.UpdateOptInStatus.Result."
+    "RetroactivePairingProtocol";
+const char kSavedDeviceUpdateOptInStatusSubsequentResult[] =
+    "Bluetooth.ChromeOS.FastPair.SavedDevices.UpdateOptInStatus.Result."
+    "SubsequentPairingProtocol";
 
 class FakeBluetoothAdapter
     : public testing::NiceMock<device::MockBluetoothAdapter> {
@@ -1991,6 +2000,12 @@
   // Start opted out.
   fast_pair_repository_.SetOptInStatus(
       nearby::fastpair::OptInStatus::STATUS_OPTED_OUT);
+  histogram_tester().ExpectBucketCount(
+      kSavedDeviceUpdateOptInStatusInitialResult,
+      /*success=*/true, 0);
+  histogram_tester().ExpectBucketCount(
+      kSavedDeviceUpdateOptInStatusInitialResult,
+      /*success=*/false, 0);
   base::RunLoop().RunUntilIdle();
 
   // Pair the device via Initial Pairing protocol.
@@ -2017,6 +2032,12 @@
   // Expect that the user is now opted in.
   EXPECT_EQ(nearby::fastpair::OptInStatus::STATUS_OPTED_IN,
             fast_pair_repository_.GetOptInStatus());
+  histogram_tester().ExpectBucketCount(
+      kSavedDeviceUpdateOptInStatusInitialResult,
+      /*success=*/true, 1);
+  histogram_tester().ExpectBucketCount(
+      kSavedDeviceUpdateOptInStatusInitialResult,
+      /*success=*/false, 0);
 }
 
 TEST_F(FastPairPairerImplTest, UpdateOptInStatus_RetroactivePairing) {
@@ -2025,6 +2046,12 @@
   // Start opted out
   fast_pair_repository_.SetOptInStatus(
       nearby::fastpair::OptInStatus::STATUS_OPTED_OUT);
+  histogram_tester().ExpectBucketCount(
+      kSavedDeviceUpdateOptInStatusRetroactiveResult,
+      /*success=*/true, 0);
+  histogram_tester().ExpectBucketCount(
+      kSavedDeviceUpdateOptInStatusRetroactiveResult,
+      /*success=*/false, 0);
 
   base::test::ScopedFeatureList feature_list;
   feature_list.InitWithFeatures(
@@ -2046,6 +2073,12 @@
   // Expect that the user is now opted in
   EXPECT_EQ(nearby::fastpair::OptInStatus::STATUS_OPTED_IN,
             fast_pair_repository_.GetOptInStatus());
+  histogram_tester().ExpectBucketCount(
+      kSavedDeviceUpdateOptInStatusRetroactiveResult,
+      /*success=*/true, 1);
+  histogram_tester().ExpectBucketCount(
+      kSavedDeviceUpdateOptInStatusRetroactiveResult,
+      /*success=*/false, 0);
 }
 
 TEST_F(FastPairPairerImplTest, UpdateOptInStatus_SubsequentPairing) {
@@ -2058,6 +2091,12 @@
   // Start opted out
   fast_pair_repository_.SetOptInStatus(
       nearby::fastpair::OptInStatus::STATUS_OPTED_OUT);
+  histogram_tester().ExpectBucketCount(
+      kSavedDeviceUpdateOptInStatusSubsequentResult,
+      /*success=*/true, 0);
+  histogram_tester().ExpectBucketCount(
+      kSavedDeviceUpdateOptInStatusSubsequentResult,
+      /*success=*/false, 0);
 
   // Subsequent pair
   SuccessfulDataEncryptorSetUp(/*fast_pair_v1=*/false,
@@ -2079,6 +2118,12 @@
   // Expect that the user is opted in now
   EXPECT_EQ(nearby::fastpair::OptInStatus::STATUS_OPTED_IN,
             fast_pair_repository_.GetOptInStatus());
+  histogram_tester().ExpectBucketCount(
+      kSavedDeviceUpdateOptInStatusSubsequentResult,
+      /*success=*/true, 1);
+  histogram_tester().ExpectBucketCount(
+      kSavedDeviceUpdateOptInStatusSubsequentResult,
+      /*success=*/false, 0);
 }
 
 }  // namespace quick_pair
diff --git a/ash/quick_pair/repository/fast_pair_repository_impl.cc b/ash/quick_pair/repository/fast_pair_repository_impl.cc
index 1c02745..c235788a 100644
--- a/ash/quick_pair/repository/fast_pair_repository_impl.cc
+++ b/ash/quick_pair/repository/fast_pair_repository_impl.cc
@@ -316,6 +316,9 @@
     GetSavedDevicesCallback callback,
     absl::optional<nearby::fastpair::UserReadDevicesResponse> user_devices) {
   QP_LOG(INFO) << __func__;
+
+  RecordGetSavedDevicesResult(/*success=*/user_devices.has_value());
+
   if (!user_devices) {
     QP_LOG(WARNING)
         << __func__
diff --git a/ash/quick_pair/repository/fast_pair_repository_impl_unittest.cc b/ash/quick_pair/repository/fast_pair_repository_impl_unittest.cc
index 01e547d7..a967767c7 100644
--- a/ash/quick_pair/repository/fast_pair_repository_impl_unittest.cc
+++ b/ash/quick_pair/repository/fast_pair_repository_impl_unittest.cc
@@ -24,6 +24,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/test/gmock_callback_support.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/mock_callback.h"
 #include "components/prefs/testing_pref_service.h"
 #include "device/bluetooth/bluetooth_adapter_factory.h"
@@ -52,6 +53,9 @@
 const std::vector<uint8_t> kFilterBytes1{0x0A, 0x42, 0x88, 0x10};
 const uint8_t salt = 0xC7;
 
+const char kSavedDeviceGetDevicesResultMetricName[] =
+    "Bluetooth.ChromeOS.FastPair.SavedDevices.GetSavedDevices.Result";
+
 }  // namespace
 
 namespace ash {
@@ -166,10 +170,13 @@
     devices_ = devices;
   }
 
+  base::HistogramTester& histogram_tester() { return histogram_tester_; }
+
  protected:
   std::unique_ptr<FastPairRepositoryImpl> fast_pair_repository_;
   nearby::fastpair::OptInStatus status_;
   std::vector<nearby::fastpair::FastPairDevice> devices_;
+  base::HistogramTester histogram_tester_;
   scoped_refptr<testing::NiceMock<device::MockBluetoothAdapter>> adapter_;
   testing::NiceMock<device::MockBluetoothDevice> ble_bluetooth_device_;
   testing::NiceMock<device::MockBluetoothDevice> classic_bluetooth_device_;
@@ -489,6 +496,10 @@
 }
 
 TEST_F(FastPairRepositoryImplTest, GetSavedDevices_OptedIn) {
+  histogram_tester().ExpectBucketCount(kSavedDeviceGetDevicesResultMetricName,
+                                       /*success=*/true, 0);
+  histogram_tester().ExpectBucketCount(kSavedDeviceGetDevicesResultMetricName,
+                                       /*success=*/false, 0);
   fast_pair_repository_->UpdateOptInStatus(
       nearby::fastpair::OptInStatus::STATUS_OPTED_IN, base::DoNothing());
   base::RunLoop().RunUntilIdle();
@@ -511,9 +522,17 @@
 
   EXPECT_EQ(nearby::fastpair::OptInStatus::STATUS_OPTED_IN, status_);
   EXPECT_EQ(1u, devices_.size());
+  histogram_tester().ExpectBucketCount(kSavedDeviceGetDevicesResultMetricName,
+                                       /*success=*/true, 1);
+  histogram_tester().ExpectBucketCount(kSavedDeviceGetDevicesResultMetricName,
+                                       /*success=*/false, 0);
 }
 
 TEST_F(FastPairRepositoryImplTest, GetSavedDevices_OptedOut) {
+  histogram_tester().ExpectBucketCount(kSavedDeviceGetDevicesResultMetricName,
+                                       /*success=*/true, 0);
+  histogram_tester().ExpectBucketCount(kSavedDeviceGetDevicesResultMetricName,
+                                       /*success=*/false, 0);
   fast_pair_repository_->UpdateOptInStatus(
       nearby::fastpair::OptInStatus::STATUS_OPTED_OUT, base::DoNothing());
   base::RunLoop().RunUntilIdle();
@@ -524,9 +543,17 @@
 
   EXPECT_EQ(nearby::fastpair::OptInStatus::STATUS_OPTED_OUT, status_);
   EXPECT_EQ(0u, devices_.size());
+  histogram_tester().ExpectBucketCount(kSavedDeviceGetDevicesResultMetricName,
+                                       /*success=*/true, 1);
+  histogram_tester().ExpectBucketCount(kSavedDeviceGetDevicesResultMetricName,
+                                       /*success=*/false, 0);
 }
 
 TEST_F(FastPairRepositoryImplTest, GetSavedDevices_OptStatusUnknown) {
+  histogram_tester().ExpectBucketCount(kSavedDeviceGetDevicesResultMetricName,
+                                       /*success=*/true, 0);
+  histogram_tester().ExpectBucketCount(kSavedDeviceGetDevicesResultMetricName,
+                                       /*success=*/false, 0);
   fast_pair_repository_->UpdateOptInStatus(
       nearby::fastpair::OptInStatus::STATUS_UNKNOWN, base::DoNothing());
   base::RunLoop().RunUntilIdle();
@@ -537,9 +564,17 @@
 
   EXPECT_EQ(nearby::fastpair::OptInStatus::STATUS_UNKNOWN, status_);
   EXPECT_EQ(0u, devices_.size());
+  histogram_tester().ExpectBucketCount(kSavedDeviceGetDevicesResultMetricName,
+                                       /*success=*/true, 1);
+  histogram_tester().ExpectBucketCount(kSavedDeviceGetDevicesResultMetricName,
+                                       /*success=*/false, 0);
 }
 
 TEST_F(FastPairRepositoryImplTest, GetSavedDevices_MissingResponse) {
+  histogram_tester().ExpectBucketCount(kSavedDeviceGetDevicesResultMetricName,
+                                       /*success=*/true, 0);
+  histogram_tester().ExpectBucketCount(kSavedDeviceGetDevicesResultMetricName,
+                                       /*success=*/false, 0);
   footprints_fetcher_->SetGetUserDevicesResponse(absl::nullopt);
   fast_pair_repository_->GetSavedDevices(
       base::BindOnce(&FastPairRepositoryImplTest::GetSavedDevicesCallback,
@@ -548,6 +583,10 @@
 
   EXPECT_EQ(nearby::fastpair::OptInStatus::STATUS_UNKNOWN, status_);
   EXPECT_EQ(0u, devices_.size());
+  histogram_tester().ExpectBucketCount(kSavedDeviceGetDevicesResultMetricName,
+                                       /*success=*/true, 0);
+  histogram_tester().ExpectBucketCount(kSavedDeviceGetDevicesResultMetricName,
+                                       /*success=*/false, 1);
 }
 
 TEST_F(FastPairRepositoryImplTest, IsAccountKeyPairedLocally) {
diff --git a/ash/style/ash_color_id.h b/ash/style/ash_color_id.h
index 0a536de..ae51e55 100644
--- a/ash/style/ash_color_id.h
+++ b/ash/style/ash_color_id.h
@@ -18,7 +18,14 @@
   E_CPONLY(kColorAshShieldAndBase80) \
   E_CPONLY(kColorAshShieldAndBase90) \
   E_CPONLY(kColorAshShieldAndBase95) \
-  E_CPONLY(kColorAshShieldAndBaseOpaque)
+  E_CPONLY(kColorAshShieldAndBaseOpaque) \
+  E_CPONLY(kColorAshHairlineBorderColor) \
+  E_CPONLY(kColorAshControlBackgroundColorActive) \
+  E_CPONLY(kColorAshControlBackgroundColorAlert) \
+  E_CPONLY(kColorAshControlBackgroundColorInactive) \
+  E_CPONLY(kColorAshControlBackgroundColorWarning) \
+  E_CPONLY(kColorAshControlBackgroundColorPositive) \
+  E_CPONLY(kColorAshFocusAuraColor)
 
 #include "ui/color/color_id_macros.inc"
 
diff --git a/ash/style/ash_color_mixer.cc b/ash/style/ash_color_mixer.cc
index cad5613..f5d299c0 100644
--- a/ash/style/ash_color_mixer.cc
+++ b/ash/style/ash_color_mixer.cc
@@ -20,6 +20,13 @@
 
 namespace {
 
+constexpr int kAlpha20 = SK_AlphaOPAQUE * 0.2f;
+constexpr int kAlpha40 = SK_AlphaOPAQUE * 0.4f;
+constexpr int kAlpha60 = SK_AlphaOPAQUE * 0.6f;
+constexpr int kAlpha80 = SK_AlphaOPAQUE * 0.8f;
+constexpr int kAlpha90 = SK_AlphaOPAQUE * 0.9f;
+constexpr int kAlpha95 = SK_AlphaOPAQUE * 0.95f;
+
 void AddShieldAndBaseColors(ui::ColorMixer& mixer,
                             const ui::ColorProviderManager::Key& key) {
   const bool use_dark_color =
@@ -32,22 +39,67 @@
   // the value of `use_color` here.
   const SkColor background_color =
       key.user_color.value_or(default_background_color);
-  mixer[kColorAshShieldAndBase20] = {
-      SkColorSetA(background_color, SK_AlphaOPAQUE * 0.2f)};
-  mixer[kColorAshShieldAndBase40] = {
-      SkColorSetA(background_color, SK_AlphaOPAQUE * 0.4f)};
-  mixer[kColorAshShieldAndBase60] = {
-      SkColorSetA(background_color, SK_AlphaOPAQUE * 0.6f)};
-  mixer[kColorAshShieldAndBase80] = {
-      SkColorSetA(background_color, SK_AlphaOPAQUE * 0.8f)};
-  mixer[kColorAshShieldAndBase90] = {
-      SkColorSetA(background_color, SK_AlphaOPAQUE * 0.9f)};
-  mixer[kColorAshShieldAndBase95] = {
-      SkColorSetA(background_color, SK_AlphaOPAQUE * 0.95f)};
+  mixer[kColorAshShieldAndBase20] = {SkColorSetA(background_color, kAlpha20)};
+  mixer[kColorAshShieldAndBase40] = {SkColorSetA(background_color, kAlpha40)};
+  mixer[kColorAshShieldAndBase60] = {SkColorSetA(background_color, kAlpha60)};
+  mixer[kColorAshShieldAndBase80] = {SkColorSetA(background_color, kAlpha80)};
+  mixer[kColorAshShieldAndBase90] = {SkColorSetA(background_color, kAlpha90)};
+  mixer[kColorAshShieldAndBase95] = {SkColorSetA(background_color, kAlpha95)};
   mixer[kColorAshShieldAndBaseOpaque] = {
       SkColorSetA(background_color, SK_AlphaOPAQUE)};
 }
 
+// Mappings of Controls Colors for Material 2.
+void AddControlsColors(ui::ColorMixer& mixer,
+                       const ui::ColorProviderManager::Key& key) {
+  const bool use_dark_color =
+      key.color_mode == ui::ColorProviderManager::ColorMode::kDark;
+
+  // ControlsLayer colors
+  mixer[kColorAshHairlineBorderColor] =
+      use_dark_color ? ui::ColorTransform(SkColorSetA(SK_ColorWHITE, 0x24))
+                     : ui::ColorTransform(SkColorSetA(SK_ColorBLACK, 0x24));
+  mixer[kColorAshControlBackgroundColorActive] =
+      use_dark_color ? ui::ColorTransform(gfx::kGoogleBlue300)
+                     : ui::ColorTransform(gfx::kGoogleBlue600);
+  mixer[kColorAshControlBackgroundColorInactive] =
+      use_dark_color ? ui::ColorTransform(SkColorSetA(SK_ColorWHITE, 0x1A))
+                     : ui::ColorTransform(SkColorSetA(SK_ColorBLACK, 0x0D));
+  mixer[kColorAshControlBackgroundColorAlert] =
+      use_dark_color ? ui::ColorTransform(gfx::kGoogleRed300)
+                     : ui::ColorTransform(gfx::kGoogleRed600);
+  mixer[kColorAshControlBackgroundColorWarning] =
+      use_dark_color ? ui::ColorTransform(gfx::kGoogleYellow300)
+                     : ui::ColorTransform(gfx::kGoogleYellow600);
+  mixer[kColorAshControlBackgroundColorPositive] =
+      use_dark_color ? ui::ColorTransform(gfx::kGoogleGreen300)
+                     : ui::ColorTransform(gfx::kGoogleGreen600);
+  mixer[kColorAshFocusAuraColor] =
+      use_dark_color
+          ? ui::ColorTransform(SkColorSetA(gfx::kGoogleBlue300, 0x3D))
+          : ui::ColorTransform(SkColorSetA(gfx::kGoogleBlue600, 0x3D));
+  mixer[ui::kColorAshFocusRing] = use_dark_color
+                                      ? ui::ColorTransform(gfx::kGoogleBlue300)
+                                      : ui::ColorTransform(gfx::kGoogleBlue600);
+
+  mixer[ui::kColorAshSystemUIBorderColor1] =
+      use_dark_color ? ui::ColorTransform(kColorAshShieldAndBase80)
+                     : ui::ColorTransform(SkColorSetA(SK_ColorBLACK, 0x0F));
+  mixer[ui::kColorAshSystemUIBorderColor2] =
+      use_dark_color ? ui::ColorTransform(kColorAshShieldAndBase60)
+                     : ui::ColorTransform(SkColorSetA(SK_ColorBLACK, 0x0F));
+  mixer[ui::kColorAshSystemUIBorderColor3] = {SkColorSetA(SK_ColorBLACK, 0x0F)};
+
+  mixer[ui::kColorAshSystemUIHighlightColor1] =
+      use_dark_color ? ui::ColorTransform(SkColorSetA(SK_ColorWHITE, 0x14))
+                     : ui::ColorTransform(SkColorSetA(SK_ColorWHITE, 0x4C));
+  mixer[ui::kColorAshSystemUIHighlightColor2] =
+      use_dark_color ? ui::ColorTransform(SkColorSetA(SK_ColorWHITE, 0x0F))
+                     : ui::ColorTransform(SkColorSetA(SK_ColorWHITE, 0x33));
+  mixer[ui::kColorAshSystemUIHighlightColor3] = {
+      ui::kColorAshSystemUIHighlightColor1};
+}
+
 }  // namespace
 
 void AddCrosStylesColorMixer(ui::ColorProvider* provider,
@@ -67,6 +119,7 @@
   ui::ColorMixer& mixer = provider->AddMixer();
 
   AddShieldAndBaseColors(mixer, key);
+  AddControlsColors(mixer, key);
 
   mixer[ui::kColorAshActionLabelFocusRingEdit] = {gfx::kGoogleBlue300};
   mixer[ui::kColorAshActionLabelFocusRingError] = {gfx::kGoogleRed300};
@@ -79,8 +132,6 @@
   mixer[ui::kColorAshAppListSeparator] =
       ui::SetAlpha(gfx::kGoogleGrey900, 0x24);
   mixer[ui::kColorAshArcInputMenuSeparator] = {SK_ColorGRAY};
-  mixer[ui::kColorAshFocusRing] = {ash_color_provider->GetControlsLayerColor(
-      ash::AshColorProvider::ControlsLayerType::kFocusRingColor)};
   mixer[ui::kColorAshEditFinishFocusRing] = {gfx::kGoogleBlue300};
   mixer[ui::kColorAshIconInOobe] = {kIconColorInOobe};
 
@@ -91,39 +142,16 @@
 
   mixer[ui::kColorAshOnboardingFocusRing] = {gfx::kGoogleBlue300};
 
-  mixer[ui::kColorAshSystemUIBorderColor1] = {
-      ash_color_provider->GetControlsLayerColor(
-          ash::AshColorProvider::ControlsLayerType::kBorderColor1)};
-  mixer[ui::kColorAshSystemUIBorderColor2] = {
-      ash_color_provider->GetControlsLayerColor(
-          ash::AshColorProvider::ControlsLayerType::kBorderColor2)};
-  mixer[ui::kColorAshSystemUIBorderColor3] = {
-      ash_color_provider->GetControlsLayerColor(
-          ash::AshColorProvider::ControlsLayerType::kBorderColor3)};
-  mixer[ui::kColorAshSystemUIHighlightColor1] = {
-      ash_color_provider->GetControlsLayerColor(
-          ash::AshColorProvider::ControlsLayerType::kHighlightColor1)};
-  mixer[ui::kColorAshSystemUIHighlightColor2] = {
-      ash_color_provider->GetControlsLayerColor(
-          ash::AshColorProvider::ControlsLayerType::kHighlightColor2)};
-  mixer[ui::kColorAshSystemUIHighlightColor3] = {
-      ash_color_provider->GetControlsLayerColor(
-          ash::AshColorProvider::ControlsLayerType::kHighlightColor3)};
-
   if (!features::IsDarkLightModeEnabled()) {
     ash::ScopedLightModeAsDefault scoped_light_mode_as_default;
     mixer[ui::kColorAshSystemUILightBorderColor1] = {
-        ash_color_provider->GetControlsLayerColor(
-            ash::AshColorProvider::ControlsLayerType::kBorderColor1)};
+        ui::kColorAshSystemUIBorderColor1};
     mixer[ui::kColorAshSystemUILightBorderColor2] = {
-        ash_color_provider->GetControlsLayerColor(
-            ash::AshColorProvider::ControlsLayerType::kBorderColor1)};
+        ui::kColorAshSystemUIBorderColor2};
     mixer[ui::kColorAshSystemUILightHighlightColor1] = {
-        ash_color_provider->GetControlsLayerColor(
-            ash::AshColorProvider::ControlsLayerType::kHighlightColor1)};
+        ui::kColorAshSystemUIHighlightColor1};
     mixer[ui::kColorAshSystemUILightHighlightColor2] = {
-        ash_color_provider->GetControlsLayerColor(
-            ash::AshColorProvider::ControlsLayerType::kHighlightColor2)};
+        ui::kColorAshSystemUIHighlightColor2};
     return;
   }
 
diff --git a/ash/style/ash_color_mixer.h b/ash/style/ash_color_mixer.h
index 6566acec..efc3591 100644
--- a/ash/style/ash_color_mixer.h
+++ b/ash/style/ash_color_mixer.h
@@ -9,7 +9,7 @@
 
 namespace ui {
 class ColorProvider;
-}
+}  // namespace ui
 
 namespace ash {
 
diff --git a/ash/style/ash_color_provider.cc b/ash/style/ash_color_provider.cc
index 6d1c6a2..ebe668faf 100644
--- a/ash/style/ash_color_provider.cc
+++ b/ash/style/ash_color_provider.cc
@@ -9,6 +9,7 @@
 #include "ash/constants/ash_constants.h"
 #include "ash/constants/ash_features.h"
 #include "ash/shell.h"
+#include "ash/style/ash_color_id.h"
 #include "ash/style/dark_light_mode_controller_impl.h"
 #include "ash/wallpaper/wallpaper_controller_impl.h"
 #include "base/bind.h"
@@ -17,6 +18,8 @@
 #include "base/feature_list.h"
 #include "base/strings/string_number_conversions.h"
 #include "ui/chromeos/styles/cros_styles.h"
+#include "ui/color/color_id.h"
+#include "ui/color/color_provider_manager.h"
 #include "ui/gfx/color_analysis.h"
 #include "ui/gfx/color_utils.h"
 
@@ -133,7 +136,8 @@
 }
 
 SkColor AshColorProvider::GetControlsLayerColor(ControlsLayerType type) const {
-  return GetControlsLayerColorImpl(type, IsDarkModeEnabled());
+  // TODO(skau): Delete this function
+  return GetControlsLayerColorImpl(type);
 }
 
 SkColor AshColorProvider::GetContentLayerColor(ContentLayerType type) const {
@@ -222,43 +226,41 @@
       kAlphas[static_cast<int>(type)]);
 }
 
-SkColor AshColorProvider::GetControlsLayerColorImpl(ControlsLayerType type,
-                                                    bool use_dark_color) const {
+SkColor AshColorProvider::GetControlsLayerColorImpl(
+    ControlsLayerType type) const {
+  // TODO(crbug.com/1292244): Delete this function after all callers migrate.
+  auto* color_provider = GetColorProvider();
+  DCHECK(color_provider);
+
   switch (type) {
     case ControlsLayerType::kHairlineBorderColor:
-      return use_dark_color ? SkColorSetA(SK_ColorWHITE, 0x24)
-                            : SkColorSetA(SK_ColorBLACK, 0x24);
+      return color_provider->GetColor(kColorAshHairlineBorderColor);
     case ControlsLayerType::kControlBackgroundColorActive:
-      return use_dark_color ? gfx::kGoogleBlue300 : gfx::kGoogleBlue600;
+      return color_provider->GetColor(kColorAshControlBackgroundColorActive);
     case ControlsLayerType::kControlBackgroundColorInactive:
-      return use_dark_color ? SkColorSetA(SK_ColorWHITE, 0x1A)
-                            : SkColorSetA(SK_ColorBLACK, 0x0D);
+      return color_provider->GetColor(kColorAshControlBackgroundColorInactive);
     case ControlsLayerType::kControlBackgroundColorAlert:
-      return use_dark_color ? gfx::kGoogleRed300 : gfx::kGoogleRed600;
+      return color_provider->GetColor(kColorAshControlBackgroundColorAlert);
     case ControlsLayerType::kControlBackgroundColorWarning:
-      return use_dark_color ? gfx::kGoogleYellow300 : gfx::kGoogleYellow600;
+      return color_provider->GetColor(kColorAshControlBackgroundColorWarning);
     case ControlsLayerType::kControlBackgroundColorPositive:
-      return use_dark_color ? gfx::kGoogleGreen300 : gfx::kGoogleGreen600;
+      return color_provider->GetColor(kColorAshControlBackgroundColorPositive);
     case ControlsLayerType::kFocusAuraColor:
-      return use_dark_color ? SkColorSetA(gfx::kGoogleBlue300, 0x3D)
-                            : SkColorSetA(gfx::kGoogleBlue600, 0x3D);
+      return color_provider->GetColor(kColorAshFocusAuraColor);
     case ControlsLayerType::kFocusRingColor:
-      return use_dark_color ? gfx::kGoogleBlue300 : gfx::kGoogleBlue600;
+      return color_provider->GetColor(ui::kColorAshFocusRing);
     case ControlsLayerType::kHighlightColor1:
-    case ControlsLayerType::kHighlightColor3:
-      return use_dark_color ? SkColorSetA(SK_ColorWHITE, 0x14)
-                            : SkColorSetA(SK_ColorWHITE, 0x4C);
-    case ControlsLayerType::kBorderColor1:
-      return use_dark_color ? GetBaseLayerColor(BaseLayerType::kTransparent80)
-                            : SkColorSetA(SK_ColorBLACK, 0x0F);
+      return color_provider->GetColor(ui::kColorAshSystemUIHighlightColor1);
     case ControlsLayerType::kHighlightColor2:
-      return use_dark_color ? SkColorSetA(SK_ColorWHITE, 0x0F)
-                            : SkColorSetA(SK_ColorWHITE, 0x33);
+      return color_provider->GetColor(ui::kColorAshSystemUIHighlightColor2);
+    case ControlsLayerType::kHighlightColor3:
+      return color_provider->GetColor(ui::kColorAshSystemUIHighlightColor3);
+    case ControlsLayerType::kBorderColor1:
+      return color_provider->GetColor(ui::kColorAshSystemUIBorderColor1);
     case ControlsLayerType::kBorderColor2:
-      return use_dark_color ? GetBaseLayerColor(BaseLayerType::kTransparent60)
-                            : SkColorSetA(SK_ColorBLACK, 0x0F);
+      return color_provider->GetColor(ui::kColorAshSystemUIBorderColor2);
     case ControlsLayerType::kBorderColor3:
-      return SkColorSetA(SK_ColorBLACK, 0x0F);
+      return color_provider->GetColor(ui::kColorAshSystemUIBorderColor3);
   }
 }
 
@@ -360,4 +362,10 @@
       muted_color);
 }
 
+ui::ColorProvider* AshColorProvider::GetColorProvider() const {
+  auto* native_theme = ui::NativeTheme::GetInstanceForNativeUi();
+  return ui::ColorProviderManager::Get().GetColorProviderFor(
+      native_theme->GetColorProviderKey(nullptr));
+}
+
 }  // namespace ash
diff --git a/ash/style/ash_color_provider.h b/ash/style/ash_color_provider.h
index 8253095..c193ccb 100644
--- a/ash/style/ash_color_provider.h
+++ b/ash/style/ash_color_provider.h
@@ -11,6 +11,10 @@
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/gfx/color_palette.h"
 
+namespace ui {
+class ColorProvider;
+}
+
 namespace ash {
 
 // TODO(minch): AshColorProvider is not needed to be a class now.
@@ -77,8 +81,7 @@
   SkColor GetBaseLayerColorImpl(BaseLayerType type, bool inverted) const;
   // Gets the color of |type| of the corresponding layer. Returns the color on
   // dark mode if |use_dark_color| is true.
-  SkColor GetControlsLayerColorImpl(ControlsLayerType type,
-                                    bool use_dark_color) const;
+  SkColor GetControlsLayerColorImpl(ControlsLayerType type) const;
   SkColor GetContentLayerColorImpl(ContentLayerType type,
                                    bool use_dark_color) const;
 
@@ -94,6 +97,10 @@
   // dark mode if |use_dark_color| is true.
   SkColor GetBackgroundThemedColorImpl(SkColor default_color,
                                        bool use_dark_color) const;
+
+  // Returns a ColorProvider for the current NativeTheme which will correctly
+  // reflect the current ColorMode.
+  ui::ColorProvider* GetColorProvider() const;
 };
 
 }  // namespace ash
diff --git a/ash/system/keyboard_brightness/keyboard_backlight_color_controller.cc b/ash/system/keyboard_brightness/keyboard_backlight_color_controller.cc
index 4d7ce35..1f0ae0af 100644
--- a/ash/system/keyboard_brightness/keyboard_backlight_color_controller.cc
+++ b/ash/system/keyboard_brightness/keyboard_backlight_color_controller.cc
@@ -33,6 +33,14 @@
       account_id);
 }
 
+// Determines whether to use the |kDefaultColor| instead of |color|.
+bool ShouldUseDefaultColor(SkColor color) {
+  color_utils::HSL hsl;
+  color_utils::SkColorToHSL(color, &hsl);
+  // Determines if the color is nearly black or white.
+  return hsl.l >= 0.9 || hsl.l <= 0.08;
+}
+
 }  // namespace
 
 KeyboardBacklightColorController::KeyboardBacklightColorController()
@@ -100,11 +108,14 @@
       base::UmaHistogramBoolean(
           "Ash.Personalization.KeyboardBacklight.WallpaperColor.Valid",
           valid_color);
-      // Default to |kDefaultColor| if |color| is invalid.
-      if (!valid_color)
+      // Default to |kDefaultColor| if |color| is invalid or
+      // |ShouldUseDefaultColor| is true.
+      if (!valid_color || ShouldUseDefaultColor(color)) {
         color = kDefaultColor;
+      }
       rgb_keyboard_manager->SetStaticBackgroundColor(
           SkColorGetR(color), SkColorGetG(color), SkColorGetB(color));
+      displayed_color_for_testing_ = color;
       break;
     }
     case personalization_app::mojom::BacklightColor::kWhite:
@@ -117,6 +128,7 @@
       SkColor color = ConvertBacklightColorToSkColor(backlight_color);
       rgb_keyboard_manager->SetStaticBackgroundColor(
           SkColorGetR(color), SkColorGetG(color), SkColorGetB(color));
+      displayed_color_for_testing_ = color;
       break;
     }
     case personalization_app::mojom::BacklightColor::kRainbow:
diff --git a/ash/system/keyboard_brightness/keyboard_backlight_color_controller.h b/ash/system/keyboard_brightness/keyboard_backlight_color_controller.h
index 277131727..3e42b16 100644
--- a/ash/system/keyboard_brightness/keyboard_backlight_color_controller.h
+++ b/ash/system/keyboard_brightness/keyboard_backlight_color_controller.h
@@ -11,6 +11,7 @@
 #include "ash/public/cpp/wallpaper/wallpaper_controller_observer.h"
 #include "ash/webui/personalization_app/mojom/personalization_app.mojom-shared.h"
 #include "base/scoped_observation.h"
+#include "third_party/skia/include/core/SkColor.h"
 
 class PrefRegistrySimple;
 
@@ -55,6 +56,8 @@
   };
 
  private:
+  friend class KeyboardBacklightColorControllerTest;
+
   // Displays the |backlight_color| on the keyboard.
   void DisplayBacklightColor(
       personalization_app::mojom::BacklightColor backlight_color);
@@ -64,6 +67,8 @@
       personalization_app::mojom::BacklightColor backlight_color,
       const AccountId& account_id);
 
+  SkColor displayed_color_for_testing_ = SK_ColorTRANSPARENT;
+
   ScopedSessionObserver scoped_session_observer_{this};
 
   base::ScopedObservation<WallpaperController, WallpaperControllerObserver>
diff --git a/ash/system/keyboard_brightness/keyboard_backlight_color_controller_unittest.cc b/ash/system/keyboard_brightness/keyboard_backlight_color_controller_unittest.cc
index 770276a..2015d7cf9 100644
--- a/ash/system/keyboard_brightness/keyboard_backlight_color_controller_unittest.cc
+++ b/ash/system/keyboard_brightness/keyboard_backlight_color_controller_unittest.cc
@@ -4,13 +4,14 @@
 
 #include "ash/system/keyboard_brightness/keyboard_backlight_color_controller.h"
 #include "ash/constants/ash_features.h"
-#include "ash/constants/ash_pref_names.h"
-#include "ash/session/session_controller_impl.h"
+#include "ash/rgb_keyboard/rgb_keyboard_util.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
+#include "ash/wallpaper/wallpaper_controller_impl.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkColor.h"
 
 namespace ash {
 
@@ -20,6 +21,44 @@
 
 constexpr char kUser2[] = "user2@test.com";
 const AccountId account_id_2 = AccountId::FromUserEmailGaiaId(kUser2, kUser2);
+
+// Creates an image of size |size|.
+gfx::ImageSkia CreateImage(int width, int height, SkColor color) {
+  SkBitmap bitmap;
+  bitmap.allocN32Pixels(width, height);
+  bitmap.eraseColor(color);
+  gfx::ImageSkia image = gfx::ImageSkia::CreateFrom1xBitmap(bitmap);
+  return image;
+}
+
+class TestWallpaperObserver : public ash::WallpaperControllerObserver {
+ public:
+  TestWallpaperObserver() {
+    wallpaper_controller_observation_.Observe(WallpaperControllerImpl::Get());
+  }
+
+  TestWallpaperObserver(const TestWallpaperObserver&) = delete;
+  TestWallpaperObserver& operator=(const TestWallpaperObserver&) = delete;
+
+  ~TestWallpaperObserver() override = default;
+
+  // ash::WallpaperControllerObserver:
+  void OnWallpaperColorsChanged() override {
+    DCHECK(ui_run_loop_);
+    ui_run_loop_->QuitWhenIdle();
+  }
+
+  // Wait until the wallpaper update is completed.
+  void WaitForWallpaperColorsChanged() {
+    ui_run_loop_ = std::make_unique<base::RunLoop>();
+    ui_run_loop_->Run();
+  }
+
+ private:
+  std::unique_ptr<base::RunLoop> ui_run_loop_;
+  base::ScopedObservation<WallpaperController, WallpaperControllerObserver>
+      wallpaper_controller_observation_{this};
+};
 }  // namespace
 
 class KeyboardBacklightColorControllerTest : public AshTestBase {
@@ -41,6 +80,7 @@
     AshTestBase::SetUp();
 
     controller_ = Shell::Get()->keyboard_backlight_color_controller();
+    wallpaper_controller_ = Shell::Get()->wallpaper_controller();
   }
 
  protected:
@@ -48,7 +88,12 @@
     return histogram_tester_;
   }
 
+  SkColor displayed_color() const {
+    return controller_->displayed_color_for_testing_;
+  }
+
   KeyboardBacklightColorController* controller_ = nullptr;
+  WallpaperControllerImpl* wallpaper_controller_ = nullptr;
 
  private:
   base::test::ScopedFeatureList scoped_feature_list_;
@@ -89,4 +134,18 @@
             controller_->GetBacklightColor(account_id_1));
 }
 
+TEST_F(KeyboardBacklightColorControllerTest,
+       DisplaysDefaultColorForNearlyBlackColor) {
+  TestWallpaperObserver observer;
+  SimulateUserLogin(account_id_1);
+  gfx::ImageSkia one_shot_wallpaper =
+      CreateImage(640, 480, SkColorSetRGB(/*r=*/0, /*g=*/0, /*b=*/10));
+  wallpaper_controller_->ShowOneShotWallpaper(one_shot_wallpaper);
+  observer.WaitForWallpaperColorsChanged();
+
+  histogram_tester().ExpectBucketCount(
+      "Ash.Personalization.KeyboardBacklight.WallpaperColor.Valid", true, 1);
+  EXPECT_EQ(kDefaultColor, displayed_color());
+}
+
 }  // namespace ash
diff --git a/ash/system/network/network_detailed_network_view_impl.cc b/ash/system/network/network_detailed_network_view_impl.cc
index d7c1e2c..3a0f214 100644
--- a/ash/system/network/network_detailed_network_view_impl.cc
+++ b/ash/system/network/network_detailed_network_view_impl.cc
@@ -9,6 +9,7 @@
 #include "ash/system/network/network_list_mobile_header_view_impl.h"
 #include "ash/system/network/network_list_network_item_view.h"
 #include "ash/system/network/network_list_wifi_header_view_impl.h"
+#include "ash/system/network/network_utils.h"
 #include "ash/system/tray/detailed_view_delegate.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
 
@@ -22,6 +23,7 @@
                           NetworkDetailedView::ListType::LIST_TYPE_NETWORK),
       NetworkDetailedNetworkView(delegate) {
   DCHECK(ash::features::IsQuickSettingsNetworkRevampEnabled());
+  RecordDetailedViewSection(DetailedViewSection::kDetailedSection);
 }
 
 NetworkDetailedNetworkViewImpl::~NetworkDetailedNetworkViewImpl() = default;
diff --git a/ash/system/network/network_detailed_network_view_unittest.cc b/ash/system/network/network_detailed_network_view_unittest.cc
index 4364f67..b461653 100644
--- a/ash/system/network/network_detailed_network_view_unittest.cc
+++ b/ash/system/network/network_detailed_network_view_unittest.cc
@@ -12,8 +12,10 @@
 #include "ash/system/network/network_list_mobile_header_view.h"
 #include "ash/system/network/network_list_network_item_view.h"
 #include "ash/system/network/network_list_wifi_header_view.h"
+#include "ash/system/network/network_utils.h"
 #include "ash/system/tray/detailed_view_delegate.h"
 #include "ash/test/ash_test_base.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "chromeos/services/network_config/public/cpp/cros_network_config_test_helper.h"
 #include "chromeos/services/network_config/public/mojom/cros_network_config.mojom.h"
@@ -110,6 +112,10 @@
     // Wait for network state and device change events to be handled.
     base::RunLoop().RunUntilIdle();
 
+    histogram_tester_.ExpectBucketCount(
+        "ChromeOS.SystemTray.Network.SectionShown",
+        DetailedViewSection::kDetailedSection, 0);
+
     detailed_view_delegate_ =
         std::make_unique<DetailedViewDelegate>(/*tray_controller=*/nullptr);
 
@@ -119,6 +125,10 @@
                 detailed_view_delegate_.get(),
                 &fake_network_detailed_network_delagte_);
 
+    histogram_tester_.ExpectBucketCount(
+        "ChromeOS.SystemTray.Network.SectionShown",
+        DetailedViewSection::kDetailedSection, 1);
+
     widget_ = CreateFramelessTestWidget();
     widget_->SetFullscreen(true);
     network_detailed_network_view_ =
@@ -174,6 +184,7 @@
   FakeNetworkDetailedNetworkViewDelegate fake_network_detailed_network_delagte_;
   std::unique_ptr<DetailedViewDelegate> detailed_view_delegate_;
   NetworkDetailedNetworkViewImpl* network_detailed_network_view_;
+  base::HistogramTester histogram_tester_;
 };
 
 TEST_F(NetworkDetailedNetworkViewTest, ViewsAreCreated) {
diff --git a/ash/system/network/network_list_view_controller_impl.cc b/ash/system/network/network_list_view_controller_impl.cc
index c87ed6ce..21dd0d0 100644
--- a/ash/system/network/network_list_view_controller_impl.cc
+++ b/ash/system/network/network_list_view_controller_impl.cc
@@ -16,6 +16,7 @@
 #include "ash/system/network/network_list_mobile_header_view.h"
 #include "ash/system/network/network_list_network_header_view.h"
 #include "ash/system/network/network_list_network_item_view.h"
+#include "ash/system/network/network_utils.h"
 #include "ash/system/network/tray_network_state_model.h"
 #include "ash/system/tray/tray_info_label.h"
 #include "ash/system/tray/tri_view.h"
@@ -202,6 +203,7 @@
     }
 
     if (!mobile_header_view_) {
+      RecordDetailedViewSection(DetailedViewSection::kMobileSection);
       mobile_header_view_ =
           network_detailed_network_view()->AddMobileSectionHeader();
       mobile_header_view_->SetID(static_cast<int>(
@@ -387,6 +389,7 @@
 
 void NetworkListViewControllerImpl::UpdateWifiSection() {
   if (!wifi_header_view_) {
+    RecordDetailedViewSection(DetailedViewSection::kWifiSection);
     wifi_header_view_ = network_detailed_network_view()->AddWifiSectionHeader();
     wifi_header_view_->SetID(static_cast<int>(
         NetworkListViewControllerViewChildId::kWifiSectionHeader));
@@ -556,6 +559,10 @@
   NetworkIdToViewMap id_to_view_map;
   NetworkListNetworkItemView* network_view = nullptr;
 
+  // This value is used to determine whether at least one network of |type| type
+  // already existed prior to this method.
+  bool has_reordered_a_network = false;
+
   for (const auto& network : networks) {
     if (!NetworkTypeMatchesType(network->type, type))
       continue;
@@ -566,6 +573,7 @@
     if (it == previous_views->end()) {
       network_view = network_detailed_network_view()->AddNetworkListItem();
     } else {
+      has_reordered_a_network = true;
       network_view = it->second;
       previous_views->erase(it);
     }
@@ -575,6 +583,14 @@
     network_detailed_network_view()->network_list()->ReorderChildView(
         network_view, index);
 
+    // Only emit ethernet metric each time we show Ethernet section
+    // for the first time. We use |has_reordered_a_network| to determine
+    // if Ethernet networks already exist in network detailed list.
+    if (NetworkTypeMatchesType(network->type, NetworkType::kEthernet) &&
+        !has_reordered_a_network) {
+      RecordDetailedViewSection(DetailedViewSection::kEthernetSection);
+    }
+
     // Increment |index| since this position was taken by |network_view|.
     index++;
   }
diff --git a/ash/system/network/network_list_view_controller_unittest.cc b/ash/system/network/network_list_view_controller_unittest.cc
index ede5ff2d..1c7a013 100644
--- a/ash/system/network/network_list_view_controller_unittest.cc
+++ b/ash/system/network/network_list_view_controller_unittest.cc
@@ -15,6 +15,7 @@
 #include "ash/system/network/fake_network_detailed_network_view.h"
 #include "ash/system/network/fake_network_list_mobile_header_view.h"
 #include "ash/system/network/fake_network_list_wifi_header_view.h"
+#include "ash/system/network/network_utils.h"
 #include "ash/system/network/tray_network_state_model.h"
 #include "ash/system/tray/tray_info_label.h"
 #include "ash/system/tray/tri_view.h"
@@ -22,6 +23,7 @@
 #include "ash/test/ash_test_helper.h"
 #include "base/run_loop.h"
 #include "base/test/bind.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "chromeos/ash/components/network/mock_managed_network_configuration_handler.h"
 #include "chromeos/ash/components/network/network_state.h"
@@ -544,6 +546,8 @@
  protected:
   const std::vector<NetworkStatePropertiesPtr> empty_list_;
 
+  base::HistogramTester histogram_tester;
+
  private:
   template <class T>
   T FindViewById(
@@ -576,10 +580,14 @@
 TEST_F(NetworkListViewControllerTest, MobileDataSectionIsShown) {
   EXPECT_EQ(nullptr, GetMobileSubHeader());
   EXPECT_EQ(nullptr, GetMobileSeparator());
+  histogram_tester.ExpectBucketCount("ChromeOS.SystemTray.Network.SectionShown",
+                                     DetailedViewSection::kMobileSection, 0);
 
   AddEuicc();
   SetupCellular();
   EXPECT_NE(nullptr, GetMobileSubHeader());
+  histogram_tester.ExpectBucketCount("ChromeOS.SystemTray.Network.SectionShown",
+                                     DetailedViewSection::kMobileSection, 1);
 
   // Mobile separator is still null because mobile data is at index 0.
   EXPECT_EQ(nullptr, GetMobileSeparator());
@@ -588,36 +596,50 @@
   // tether device.
   network_state_helper()->ClearDevices();
   EXPECT_EQ(nullptr, GetMobileSubHeader());
+  histogram_tester.ExpectBucketCount("ChromeOS.SystemTray.Network.SectionShown",
+                                     DetailedViewSection::kMobileSection, 1);
 
   // Add tether networks
   AddTetherNetworkState();
   EXPECT_NE(nullptr, GetMobileSubHeader());
+  histogram_tester.ExpectBucketCount("ChromeOS.SystemTray.Network.SectionShown",
+                                     DetailedViewSection::kMobileSection, 2);
 
   // Tether device is prohibited.
   network_state_handler()->SetTetherTechnologyState(
       chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_PROHIBITED);
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(nullptr, GetMobileSubHeader());
+  histogram_tester.ExpectBucketCount("ChromeOS.SystemTray.Network.SectionShown",
+                                     DetailedViewSection::kMobileSection, 2);
 
   // Tether device is uninitialized but is primary user.
   network_state_handler()->SetTetherTechnologyState(
       chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_UNINITIALIZED);
   base::RunLoop().RunUntilIdle();
   EXPECT_NE(nullptr, GetMobileSubHeader());
+  histogram_tester.ExpectBucketCount("ChromeOS.SystemTray.Network.SectionShown",
+                                     DetailedViewSection::kMobileSection, 3);
 
   // Simulate login as secondary user.
   LoginAsSecondaryUser();
   UpdateNetworkList(empty_list_);
   EXPECT_EQ(nullptr, GetMobileSubHeader());
+  histogram_tester.ExpectBucketCount("ChromeOS.SystemTray.Network.SectionShown",
+                                     DetailedViewSection::kMobileSection, 3);
 
   // Add tether networks
   AddTetherNetworkState();
   EXPECT_NE(nullptr, GetMobileSubHeader());
+  histogram_tester.ExpectBucketCount("ChromeOS.SystemTray.Network.SectionShown",
+                                     DetailedViewSection::kMobileSection, 4);
 }
 
 TEST_F(NetworkListViewControllerTest, WifiSectionHeader) {
   EXPECT_EQ(nullptr, GetWifiSubHeader());
   EXPECT_EQ(nullptr, GetWifiSeparator());
+  histogram_tester.ExpectBucketCount("ChromeOS.SystemTray.Network.SectionShown",
+                                     DetailedViewSection::kWifiSection, 0);
 
   // Add an enabled wifi device.
   AddWifiDevice();
@@ -628,6 +650,8 @@
   EXPECT_TRUE(GetWifiSubHeader()->is_toggle_enabled());
   EXPECT_TRUE(GetWifiSubHeader()->is_toggle_on());
   EXPECT_TRUE(GetWifiSubHeader()->is_join_wifi_enabled());
+  histogram_tester.ExpectBucketCount("ChromeOS.SystemTray.Network.SectionShown",
+                                     DetailedViewSection::kWifiSection, 1);
 
   // Disable wifi device.
   network_state_handler()->SetTechnologyEnabled(
@@ -639,6 +663,8 @@
   EXPECT_TRUE(GetWifiSubHeader()->is_toggle_enabled());
   EXPECT_FALSE(GetWifiSubHeader()->is_toggle_on());
   EXPECT_FALSE(GetWifiSubHeader()->is_join_wifi_enabled());
+  histogram_tester.ExpectBucketCount("ChromeOS.SystemTray.Network.SectionShown",
+                                     DetailedViewSection::kWifiSection, 1);
 }
 
 TEST_F(NetworkListViewControllerTest, MobileSectionHeaderAddEsimButtonStates) {
@@ -750,6 +776,8 @@
 
 TEST_F(NetworkListViewControllerTest, HasCorrectEthernetNetworkList) {
   std::vector<NetworkStatePropertiesPtr> networks;
+  histogram_tester.ExpectBucketCount("ChromeOS.SystemTray.Network.SectionShown",
+                                     DetailedViewSection::kEthernetSection, 0);
 
   NetworkStatePropertiesPtr ethernet_network =
       CreateStandaloneNetworkProperties(kEthernet, NetworkType::kEthernet,
@@ -757,6 +785,9 @@
   networks.push_back(std::move(ethernet_network));
   UpdateNetworkList(networks);
 
+  histogram_tester.ExpectBucketCount("ChromeOS.SystemTray.Network.SectionShown",
+                                     DetailedViewSection::kEthernetSection, 1);
+
   CheckNetworkListOrdering(/*ethernet_network_count=*/1,
                            /*mobile_network_count=*/-1,
                            /*wifi_network_count=*/0);
@@ -775,15 +806,28 @@
                            /*mobile_network_count=*/1,
                            /*wifi_network_count=*/0);
 
+  // Metrics is recorded here because when AddEuicc() and SetupCellular() are
+  // called, model()->cros_network_config()->GetNetworkStateList returns an
+  // empty list of networks, this resets the present network list map.
+  // The next call to UpdateNetworkList(networks), the views are re-added and
+  // a metric is recorded.
+  histogram_tester.ExpectBucketCount("ChromeOS.SystemTray.Network.SectionShown",
+                                     DetailedViewSection::kEthernetSection, 2);
+
   // Mobile list item will be at index 3 after ethernet, separator and header.
   CheckNetworkListItem(NetworkType::kCellular, /*index=*/3u,
                        /*guid=*/kCellularName);
-
   ethernet_network = CreateStandaloneNetworkProperties(
       kEthernet2, NetworkType::kEthernet, ConnectionStateType::kNotConnected);
   networks.push_back(std::move(ethernet_network));
   UpdateNetworkList(networks);
 
+  // Metrics is only recorded the first time ethernet section is shown. Here a
+  // new ethernet network was added but the section was already being shown, so
+  // no new metric would be recorded.
+  histogram_tester.ExpectBucketCount("ChromeOS.SystemTray.Network.SectionShown",
+                                     DetailedViewSection::kEthernetSection, 2);
+
   CheckNetworkListOrdering(/*ethernet_network_count=*/2,
                            /*mobile_network_count=*/1,
                            /*wifi_network_count=*/0);
diff --git a/ash/system/network/network_utils.cc b/ash/system/network/network_utils.cc
index 3fc8e13..ec33448 100644
--- a/ash/system/network/network_utils.cc
+++ b/ash/system/network/network_utils.cc
@@ -13,4 +13,9 @@
                                 action);
 }
 
+void RecordDetailedViewSection(DetailedViewSection section) {
+  base::UmaHistogramEnumeration("ChromeOS.SystemTray.Network.SectionShown",
+                                section);
+}
+
 }  // namespace ash
\ No newline at end of file
diff --git a/ash/system/network/network_utils.h b/ash/system/network/network_utils.h
index b7cf2a99..6f94cc7 100644
--- a/ash/system/network/network_utils.h
+++ b/ash/system/network/network_utils.h
@@ -19,8 +19,21 @@
   kMaxValue = kOpenSimUnlockDialog
 };
 
+// This enum is tied directly to a UMA enum |DetailedViewSection| defined in
+// //tools/metrics/histograms/enums.xml, and should always reflect it (do not
+// change one without changing the other).
+enum class DetailedViewSection {
+  kWifiSection = 0,
+  kMobileSection = 1,
+  kEthernetSection = 2,
+  kDetailedSection = 3,
+  kMaxValue = kDetailedSection
+};
+
 ASH_EXPORT void RecordNetworkRowClickedAction(NetworkRowClickedAction action);
 
+ASH_EXPORT void RecordDetailedViewSection(DetailedViewSection section);
+
 }  // namespace ash
 
 #endif  // ASH_SYSTEM_NETWORK_NETWORK_UTILS_H_
\ No newline at end of file
diff --git a/ash/touch/touch_hud_renderer.cc b/ash/touch/touch_hud_renderer.cc
index 3184028..500f325a 100644
--- a/ash/touch/touch_hud_renderer.cc
+++ b/ash/touch/touch_hud_renderer.cc
@@ -24,8 +24,8 @@
 namespace ash {
 
 constexpr int kPointRadius = 20;
-constexpr SkColor kProjectionFillColor = SkColorSetRGB(0xF5, 0xF5, 0xDC);
-constexpr SkColor kProjectionStrokeColor = SK_ColorGRAY;
+constexpr SkColor4f kProjectionFillColor{0.96f, 0.96f, 0.86f, 1.0f};
+constexpr SkColor4f kProjectionStrokeColor = SkColors::kGray;
 constexpr int kProjectionAlpha = 0xB0;
 constexpr base::TimeDelta kFadeoutDuration = base::Milliseconds(250);
 constexpr int kFadeoutFrameRate = 60;
@@ -78,8 +78,8 @@
     cc::PaintFlags fill_flags;
     fill_flags.setAlpha(alpha);
 
-    constexpr SkColor gradient_colors[2] = {kProjectionFillColor,
-                                            kProjectionStrokeColor};
+    constexpr SkColor4f gradient_colors[2] = {kProjectionFillColor,
+                                              kProjectionStrokeColor};
     constexpr SkScalar gradient_pos[2] = {SkFloatToScalar(0.9f),
                                           SkFloatToScalar(1.0f)};
     constexpr gfx::Point center(kPointRadius + 1, kPointRadius + 1);
diff --git a/ash/wallpaper/wallpaper_controller_impl.h b/ash/wallpaper/wallpaper_controller_impl.h
index e7afde22..d3c7f6e 100644
--- a/ash/wallpaper/wallpaper_controller_impl.h
+++ b/ash/wallpaper/wallpaper_controller_impl.h
@@ -380,6 +380,7 @@
                            WallpaperMovementDuringUnlock);
   friend class WallpaperControllerTest;
   friend class WallpaperControllerTestApi;
+  friend class KeyboardBacklightColorControllerTest;
 
   enum WallpaperMode { WALLPAPER_NONE, WALLPAPER_IMAGE };
 
diff --git a/ash/webui/diagnostics_ui/backend/system_routine_controller.cc b/ash/webui/diagnostics_ui/backend/system_routine_controller.cc
index 9eb767c..3112e47 100644
--- a/ash/webui/diagnostics_ui/backend/system_routine_controller.cc
+++ b/ash/webui/diagnostics_ui/backend/system_routine_controller.cc
@@ -582,17 +582,15 @@
 void SystemRoutineController::OnPowerRoutineJsonParsed(
     mojom::RoutineType routine_type,
     data_decoder::DataDecoder::ValueOrError result) {
-  if (!result.value) {
+  if (!result.has_value()) {
     OnPowerRoutineResult(routine_type,
                          mojom::StandardRoutineResult::kExecutionError,
                          /*percent_change=*/0, /*seconds_elapsed=*/0);
-    DVLOG(2) << "JSON parsing failed: " << *result.error;
+    DVLOG(2) << "JSON parsing failed: " << result.error();
     return;
   }
 
-  const base::Value& parsed_json = *result.value;
-
-  if (parsed_json.type() != base::Value::Type::DICTIONARY) {
+  if (!result->is_dict()) {
     OnPowerRoutineResult(routine_type,
                          mojom::StandardRoutineResult::kExecutionError,
                          /*percent_change=*/0, /*seconds_elapsed=*/0);
@@ -600,8 +598,9 @@
     return;
   }
 
-  const base::Value* result_details_dict =
-      parsed_json.FindDictKey(kResultDetailsKey);
+  const base::Value::Dict& parsed_json = result->GetDict();
+  const base::Value::Dict* result_details_dict =
+      parsed_json.FindDict(kResultDetailsKey);
   if (!result_details_dict) {
     OnPowerRoutineResult(routine_type,
                          mojom::StandardRoutineResult::kExecutionError,
@@ -612,8 +611,8 @@
 
   absl::optional<double> charge_percent_opt =
       routine_type == mojom::RoutineType::kBatteryCharge
-          ? result_details_dict->FindDoubleKey(kChargePercentKey)
-          : result_details_dict->FindDoubleKey(kDischargePercentKey);
+          ? result_details_dict->FindDouble(kChargePercentKey)
+          : result_details_dict->FindDouble(kDischargePercentKey);
   if (!charge_percent_opt.has_value()) {
     OnPowerRoutineResult(routine_type,
                          mojom::StandardRoutineResult::kExecutionError,
diff --git a/ash/webui/multidevice_debug/url_constants.cc b/ash/webui/multidevice_debug/url_constants.cc
index 168ef0714..3b91d850 100644
--- a/ash/webui/multidevice_debug/url_constants.cc
+++ b/ash/webui/multidevice_debug/url_constants.cc
@@ -10,6 +10,7 @@
 
 const char kChromeUIProximityAuthHost[] = "proximity-auth";
 const char kChromeUIProximityAuthURL[] = "chrome://proximity-auth/";
+const char kOsUIProximityAuthURL[] = "os://proximity-auth";
 
 }  // namespace multidevice
 
diff --git a/ash/webui/multidevice_debug/url_constants.h b/ash/webui/multidevice_debug/url_constants.h
index ccf60aa..2d96978d 100644
--- a/ash/webui/multidevice_debug/url_constants.h
+++ b/ash/webui/multidevice_debug/url_constants.h
@@ -11,6 +11,7 @@
 
 extern const char kChromeUIProximityAuthHost[];
 extern const char kChromeUIProximityAuthURL[];
+extern const char kOsUIProximityAuthURL[];
 
 }  // namespace multidevice
 
diff --git a/ash/webui/os_feedback_ui/backend/help_content_provider.cc b/ash/webui/os_feedback_ui/backend/help_content_provider.cc
index 7fd6e39..7b912ca 100644
--- a/ash/webui/os_feedback_ui/backend/help_content_provider.cc
+++ b/ash/webui/os_feedback_ui/backend/help_content_provider.cc
@@ -279,12 +279,12 @@
     data_decoder::DataDecoder::ValueOrError result) {
   SearchResponsePtr response = SearchResponse::New();
 
-  if (result.value) {
-    PopulateSearchResponse(*result.value, response);
+  if (result.has_value()) {
+    PopulateSearchResponse(*result, response);
   } else {
     LOG(ERROR)
         << "HelpContentProvider data decoder failed to parse json. Error: "
-        << *result.error;
+        << result.error();
   }
 
   std::move(callback).Run(std::move(response));
diff --git a/ash/webui/os_feedback_ui/resources/file_attachment.html b/ash/webui/os_feedback_ui/resources/file_attachment.html
index bd97292..483a52a 100644
--- a/ash/webui/os_feedback_ui/resources/file_attachment.html
+++ b/ash/webui/os_feedback_ui/resources/file_attachment.html
@@ -5,6 +5,8 @@
   }
 
   :host {
+    --iron-icon-height: 20px;
+    --iron-icon-width: 20px;
     align-items: center;
     display: flex;
     flex-direction: column;
@@ -46,11 +48,26 @@
   #selectedFileImage:hover {
     opacity: 0.7;
   }
+
+  #addFileLabel {
+    background: none;
+    border: none;
+    color: var(--google-blue-600);
+    font-size: 13px;
+    font-weight: var(--feedback-medium-font-weight);
+    line-height: 20px;
+    padding: 0;
+  }
+
+  #addFileIcon {
+    margin-inline-end: 10px;
+    margin-inline-start: 12px;
+  }
 </style>
 <div id="addFileContainer" hidden="[[hasSelectedAFile_]]">
   <button id="addFileLabel" class="file-input"
       on-click="handleOpenFileInputClick_">
-    <iron-icon icon="attachment:add-file"></iron-icon>
+    <iron-icon id="addFileIcon" icon="attachment:add-file"></iron-icon>
     [[i18n('addFileLabel')]]
   </button>
 </div>
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 b191426..fad506d 100644
--- a/ash/webui/os_feedback_ui/resources/share_data_page.html
+++ b/ash/webui/os_feedback_ui/resources/share_data_page.html
@@ -49,8 +49,9 @@
   }
 
   #screenshotImage {
+    border-radius: 0 4px 4px 0;
     display: block;
-    height: auto;
+    height: 48px;
     transition: all 250ms ease;
     width: 68px;
   }
@@ -112,6 +113,30 @@
     color: var(--cros-text-color-primary);
     line-height: 20px;
   }
+
+  #screenshotCheckbox {
+    margin-inline-end: 10px;
+    margin-inline-start: 12px;
+  }
+
+  #screenshotCheckLabel {
+    color: var(--cros-text-color-primary);
+    font-weight: 400;
+    line-height: 20px;
+  }
+
+  #imageButton {
+    background: none;
+    border: none;
+    height: 48px;
+    padding: 0;
+    width: 68px;
+  }
+
+  cr-checkbox {
+    --cr-checkbox-label-padding-start: 0;
+    --cr-checkbox-size: 20px;
+  }
 </style>
 <div id="container">
   <div id="header">
@@ -124,9 +149,9 @@
       <div id="attachFilesContainer">
         <!-- Attach a screenshot -->
         <div id="screenshotContainer" class="card-frame">
-          <input id="screenshotCheckbox" type="checkbox"
-              aria-labelledby="screenshotCheckLabel"
+          <cr-checkbox id="screenshotCheckbox" aria-labelledby="screenshotCheckLabel"
               disabled="[[!hasScreenshot_(screenshotUrl)]]">
+          </cr-checkbox>
           <label id="screenshotCheckLabel">[[i18n('attachScreenshotLabel')]]</label>
           <button id="imageButton" on-click="handleScreenshotClick_">
             <img id="screenshotImage" aria-label="$i18n{screenshotA11y}"
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 d342f05..100e778c9 100644
--- a/ash/webui/os_feedback_ui/resources/share_data_page.js
+++ b/ash/webui/os_feedback_ui/resources/share_data_page.js
@@ -6,6 +6,7 @@
 import './file_attachment.js';
 import 'chrome://resources/cr_elements/cr_button/cr_button.m.js';
 import 'chrome://resources/cr_elements/cr_dialog/cr_dialog.m.js';
+import 'chrome://resources/cr_elements/cr_checkbox/cr_checkbox.m.js';
 import 'chrome://resources/polymer/v3_0/paper-tooltip/paper-tooltip.js';
 
 import {I18nBehavior, I18nBehaviorInterface} from 'chrome://resources/js/i18n_behavior.m.js';
@@ -136,7 +137,7 @@
     this.dispatchEvent(new CustomEvent('go-back-click', {
       composed: true,
       bubbles: true,
-      detail: {currentState: FeedbackFlowState.SHARE_DATA}
+      detail: {currentState: FeedbackFlowState.SHARE_DATA},
     }));
   }
 
@@ -153,7 +154,7 @@
       this.dispatchEvent(new CustomEvent('continue-click', {
         composed: true,
         bubbles: true,
-        detail: {currentState: FeedbackFlowState.SHARE_DATA, report: report}
+        detail: {currentState: FeedbackFlowState.SHARE_DATA, report: report},
       }));
     });
   }
@@ -199,7 +200,7 @@
 
     if (this.getElement_('#pageUrlCheckbox').checked) {
       report.feedbackContext.pageUrl = {
-        url: this.getElement_('#pageUrlText').textContent.trim()
+        url: this.getElement_('#pageUrlText').textContent.trim(),
       };
     }
 
diff --git a/ash/webui/personalization_app/mojom/personalization_app.mojom b/ash/webui/personalization_app/mojom/personalization_app.mojom
index fe3719d..2740b31 100644
--- a/ash/webui/personalization_app/mojom/personalization_app.mojom
+++ b/ash/webui/personalization_app/mojom/personalization_app.mojom
@@ -549,12 +549,12 @@
 
 // Hex values of the colors displayed in the rgb keyboard section.
 const uint32 kWhiteColor = 0xFFFFFF;
-const uint32 kRedColor = 0xF28B82;
-const uint32 kYellowColor = 0xFDD663;
-const uint32 kGreenColor = 0x81C995;
-const uint32 kBlueColor = 0x78D9EC;
-const uint32 kIndigoColor = 0x8AB4F8;
-const uint32 kPurpleColor = 0xC58AF9;
+const uint32 kRedColor = 0xEE675C;
+const uint32 kYellowColor = 0xFBBC04;
+const uint32 kGreenColor = 0x34A853;
+const uint32 kBlueColor = 0x00A3C2;
+const uint32 kIndigoColor = 0x4285F4;
+const uint32 kPurpleColor = 0xB76DF8;
 
 // Receives information whenever there are keyboard backlight related changes
 // such as backlight colors.
diff --git a/ash/webui/personalization_app/personalization_app_ui.cc b/ash/webui/personalization_app/personalization_app_ui.cc
index 043c34c..691b59f 100644
--- a/ash/webui/personalization_app/personalization_app_ui.cc
+++ b/ash/webui/personalization_app/personalization_app_ui.cc
@@ -53,7 +53,7 @@
                           IDR_WEBUI_JS_TEST_LOADER_UTIL_JS);
 
 #if !DCHECK_IS_ON()
-  // Add a default path to avoid crash when not debugging.
+  // Add a default path to avoid crashes when not debugging.
   source->SetDefaultResource(IDR_ASH_PERSONALIZATION_APP_TRUSTED_INDEX_HTML);
 #endif  // !DCHECK_IS_ON()
 }
diff --git a/ash/webui/personalization_app/resources/trusted/keyboard_backlight/keyboard_backlight_element.ts b/ash/webui/personalization_app/resources/trusted/keyboard_backlight/keyboard_backlight_element.ts
index 6d4d696..689a1a6 100644
--- a/ash/webui/personalization_app/resources/trusted/keyboard_backlight/keyboard_backlight_element.ts
+++ b/ash/webui/personalization_app/resources/trusted/keyboard_backlight/keyboard_backlight_element.ts
@@ -47,6 +47,12 @@
   return (r * 299 + g * 587 + b * 114) / 1000;
 }
 
+/** Returns the RGB hex in #FFFFFF format. */
+function convertToRgbHexStr(hexVal: number): string {
+  const PADDING_LENGTH = 6;
+  return `#${hexVal.toString(16).padStart(PADDING_LENGTH, '0')}`;
+}
+
 interface ColorInfo {
   hexVal: string;
   enumVal: BacklightColor;
@@ -131,31 +137,31 @@
   private computePresetColors_(): Record<string, ColorInfo> {
     return {
       'whiteColor': {
-        hexVal: `#${WHITE_COLOR.toString(16)}`,
+        hexVal: convertToRgbHexStr(WHITE_COLOR),
         enumVal: BacklightColor.kWhite,
       },
       'redColor': {
-        hexVal: `#${RED_COLOR.toString(16)}`,
+        hexVal: convertToRgbHexStr(RED_COLOR),
         enumVal: BacklightColor.kRed,
       },
       'yellowColor': {
-        hexVal: `#${YELLOW_COLOR.toString(16)}`,
+        hexVal: convertToRgbHexStr(YELLOW_COLOR),
         enumVal: BacklightColor.kYellow,
       },
       'greenColor': {
-        hexVal: `#${GREEN_COLOR.toString(16)}`,
+        hexVal: convertToRgbHexStr(GREEN_COLOR),
         enumVal: BacklightColor.kGreen,
       },
       'blueColor': {
-        hexVal: `#${BLUE_COLOR.toString(16)}`,
+        hexVal: convertToRgbHexStr(BLUE_COLOR),
         enumVal: BacklightColor.kBlue,
       },
       'indigoColor': {
-        hexVal: `#${INDIGO_COLOR.toString(16)}`,
+        hexVal: convertToRgbHexStr(INDIGO_COLOR),
         enumVal: BacklightColor.kIndigo,
       },
       'purpleColor': {
-        hexVal: `#${PURPLE_COLOR.toString(16)}`,
+        hexVal: convertToRgbHexStr(PURPLE_COLOR),
         enumVal: BacklightColor.kPurple,
       },
     };
@@ -247,34 +253,29 @@
 
   private getColorInnerContainerStyle_(
       colorId: string, colors: Record<string, ColorInfo>) {
+    const outlineStyle = `outline: 2px solid var(--cros-separator-color);
+                  outline-offset: -2px;`;
     switch (colorId) {
       case this.rainbowColorId_:
-        const hexColors =
-            Object.values(colors).map(color => color.hexVal).slice(1);
-        return `background-image: linear-gradient(${hexColors})`;
-      case 'whiteColor':
-        // Add the border for the white background.
-        return `background-color: ${
-            colors[colorId]
-                .hexVal}; outline: 1px solid var(--cros-separator-color);
-                outline-offset: -1px;`;
+        return `background-image: linear-gradient(${colors['redColor'].hexVal},
+            ${colors['yellowColor'].hexVal},
+            ${colors['greenColor'].hexVal},
+            ${colors['indigoColor'].hexVal});
+            ${outlineStyle}`;
       default:
-        return `background-color: ${colors[colorId].hexVal}`;
+        return `background-color: ${colors[colorId].hexVal};
+                                  ${outlineStyle};`;
     }
   }
 
   private getWallpaperColorInnerContainerStyle_(wallpaperColor: SkColor):
       string {
-    // Show the default style when wallpaper color is loading or invalid.
-    if (!wallpaperColor || (wallpaperColor.value & 0xFFFFFF) === 0xFFFFFF) {
-      return `background-color: #FFFFFF;
-          outline: 1px solid var(--cros-separator-color);
-          outline-offset: -1px;`;
-    }
-    // Strip the alpha value and convert to hex string.
-    const hexStr =
-        (wallpaperColor.value & 0xFFFFFF).toString(16).padStart(6, '0');
-    return `background-color: #${hexStr};`;
+    const hexStr = !wallpaperColor ?
+        '#FFFFFF' :
+        convertToRgbHexStr(wallpaperColor.value & 0xFFFFFF);
+    return `background-color: ${hexStr};
+            outline: 2px solid var(--cros-separator-color);
+            outline-offset: -2px;`;
   }
 
   private getWallpaperIconColorClass_(wallpaperColor: SkColor): string {
diff --git a/ash/wm/float/float_controller.cc b/ash/wm/float/float_controller.cc
index 85fd638..8f92aa1 100644
--- a/ash/wm/float/float_controller.cc
+++ b/ash/wm/float/float_controller.cc
@@ -12,6 +12,7 @@
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "ash/wm/tablet_mode/tablet_mode_window_state.h"
 #include "ash/wm/window_state.h"
+#include "ash/wm/wm_event.h"
 #include "ash/wm/work_area_insets.h"
 #include "base/check_op.h"
 #include "chromeos/ui/base/display_util.h"
@@ -175,11 +176,6 @@
   return true;
 }
 
-bool FloatController::IsFloated(const aura::Window* window) const {
-  DCHECK(window);
-  return float_window_ == window;
-}
-
 void FloatController::MaybeTuckFloatedWindow() {
   if (scoped_window_tucker_)
     return;
@@ -278,13 +274,17 @@
     MaybeUpdateWindowUIAndBoundsForTablet(float_window_);
 }
 
+void FloatController::ToggleFloat(aura::Window* window) {
+  WindowState* window_state = WindowState::Get(window);
+  const WMEvent toggle_event(window_state->IsFloated() ? WM_EVENT_RESTORE
+                                                       : WM_EVENT_FLOAT);
+  window_state->OnWMEvent(&toggle_event);
+}
+
 void FloatController::Float(aura::Window* window) {
   if (window == float_window_)
     return;
 
-  // TODO(shidi): temporary remove the DCHECK, will implement proper trigger
-  // on crbug/1339095.
-
   // Only one floating window is allowed, reset previously floated window.
   ResetFloatedWindow();
   DCHECK(!float_window_);
@@ -316,9 +316,11 @@
 }
 
 void FloatController::ResetFloatedWindow() {
-  // TODO(shidi): Remove `kWindowToggleFloatKey` and implement event trigger.
-  if (float_window_)
-    float_window_->SetProperty(chromeos::kWindowToggleFloatKey, false);
+  if (float_window_) {
+    DCHECK(WindowState::Get(float_window_)->IsFloated());
+    ToggleFloat(float_window_);
+    float_window_ = nullptr;
+  }
 }
 
 void FloatController::MaybeUpdateWindowUIAndBoundsForTablet(
diff --git a/ash/wm/float/float_controller.h b/ash/wm/float/float_controller.h
index 3e18339c..836f6838 100644
--- a/ash/wm/float/float_controller.h
+++ b/ash/wm/float/float_controller.h
@@ -9,6 +9,7 @@
 #include "ash/public/cpp/tablet_mode_observer.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "base/scoped_observation.h"
+#include "chromeos/ui/frame/multitask_menu/float_controller_base.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_observer.h"
 #include "ui/display/display_observer.h"
@@ -21,7 +22,8 @@
 // |float_container|.
 class ASH_EXPORT FloatController : public aura::WindowObserver,
                                    public TabletModeObserver,
-                                   public display::DisplayObserver {
+                                   public display::DisplayObserver,
+                                   public chromeos::FloatControllerBase {
  public:
   // The possible corners that a floated window can be placed in tablet mode.
   // The default is `kBottomRight` and this is changed by dragging the window.
@@ -52,9 +54,6 @@
   // floated.
   gfx::Rect GetPreferredFloatWindowTabletBounds(aura::Window* window);
 
-  // Return true if `window` is floated, otherwise false.
-  bool IsFloated(const aura::Window* window) const;
-
   // Tucks or untucks `float_window_`. Does nothing if the window is already
   // tucked or untucked.
   void MaybeTuckFloatedWindow();
@@ -83,6 +82,9 @@
   void OnDisplayMetricsChanged(const display::Display& display,
                                uint32_t metrics) override;
 
+  // chromeos::FloatControllerBase:
+  void ToggleFloat(aura::Window* window) override;
+
  private:
   class ScopedWindowTucker;
   friend class DefaultState;
diff --git a/ash/wm/float/float_controller_unittest.cc b/ash/wm/float/float_controller_unittest.cc
index b265c4ea..853d205 100644
--- a/ash/wm/float/float_controller_unittest.cc
+++ b/ash/wm/float/float_controller_unittest.cc
@@ -64,27 +64,26 @@
 TEST_F(WindowFloatTest, WindowFloatingSwitch) {
   std::unique_ptr<aura::Window> window_1(CreateTestWindow());
   std::unique_ptr<aura::Window> window_2(CreateTestWindow());
-  FloatController* controller = Shell::Get()->float_controller();
 
   // Activate 'window_1' and perform floating.
   wm::ActivateWindow(window_1.get());
   PressAndReleaseKey(ui::VKEY_F, ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN);
-  EXPECT_TRUE(controller->IsFloated(window_1.get()));
+  EXPECT_TRUE(WindowState::Get(window_1.get())->IsFloated());
 
   // Activate 'window_2' and perform floating.
   wm::ActivateWindow(window_2.get());
   PressAndReleaseKey(ui::VKEY_F, ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN);
-  EXPECT_TRUE(controller->IsFloated(window_2.get()));
+  EXPECT_TRUE(WindowState::Get(window_2.get())->IsFloated());
 
   // Only one floated window is allowed so when a different window is floated,
   // the previously floated window will be unfloated.
-  EXPECT_FALSE(controller->IsFloated(window_1.get()));
+  EXPECT_FALSE(WindowState::Get(window_1.get())->IsFloated());
 
   // When try to float the already floated 'window_2', it will unfloat this
   // window.
   wm::ActivateWindow(window_2.get());
   PressAndReleaseKey(ui::VKEY_F, ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN);
-  EXPECT_FALSE(controller->IsFloated(window_2.get()));
+  EXPECT_FALSE(WindowState::Get(window_2.get())->IsFloated());
 }
 
 // Tests that a floated window animates to and from overview.
@@ -128,17 +127,15 @@
 
   Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
 
-  FloatController* controller = Shell::Get()->float_controller();
+  PressAndReleaseKey(ui::VKEY_F, ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN);
+  ASSERT_TRUE(WindowState::Get(window.get())->IsFloated());
 
   PressAndReleaseKey(ui::VKEY_F, ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN);
-  ASSERT_TRUE(controller->IsFloated(window.get()));
-
-  PressAndReleaseKey(ui::VKEY_F, ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN);
-  ASSERT_FALSE(controller->IsFloated(window.get()));
+  ASSERT_FALSE(WindowState::Get(window.get())->IsFloated());
 
   window_delegate.set_minimum_size(gfx::Size(600, 600));
   PressAndReleaseKey(ui::VKEY_F, ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN);
-  EXPECT_FALSE(controller->IsFloated(window.get()));
+  EXPECT_FALSE(WindowState::Get(window.get())->IsFloated());
 }
 
 // Tests that a window that cannot be floated in tablet mode unfloats after
@@ -152,12 +149,11 @@
   window_delegate.set_minimum_size(gfx::Size(600, 600));
   wm::ActivateWindow(window.get());
 
-  FloatController* controller = Shell::Get()->float_controller();
   PressAndReleaseKey(ui::VKEY_F, ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN);
-  ASSERT_TRUE(controller->IsFloated(window.get()));
+  ASSERT_TRUE(WindowState::Get(window.get())->IsFloated());
 
   Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
-  EXPECT_FALSE(controller->IsFloated(window.get()));
+  EXPECT_FALSE(WindowState::Get(window.get())->IsFloated());
 }
 
 // Tests that a floated window unfloats if a display change makes it no longer a
@@ -173,14 +169,13 @@
 
   // Enter tablet mode and float `window`.
   Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
-  FloatController* controller = Shell::Get()->float_controller();
   PressAndReleaseKey(ui::VKEY_F, ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN);
-  ASSERT_TRUE(controller->IsFloated(window.get()));
+  ASSERT_TRUE(WindowState::Get(window.get())->IsFloated());
 
   // If the display width is 700, the minimum width exceeds half the display
   // width.
   UpdateDisplay("700x600");
-  EXPECT_FALSE(controller->IsFloated(window.get()));
+  EXPECT_FALSE(WindowState::Get(window.get())->IsFloated());
 }
 
 // Tests that windows floated in tablet mode have immersive mode disabled,
@@ -329,7 +324,7 @@
   // Tests that after we exit tablet mode, the window is untucked and fully
   // visible, but is still floated.
   Shell::Get()->tablet_mode_controller()->SetEnabledForTest(false);
-  ASSERT_TRUE(Shell::Get()->float_controller()->IsFloated(window.get()));
+  // TODO(crbug.com/1339489): Temporary remove float state check until fixed.
   EXPECT_TRUE(screen_util::GetDisplayBoundsInParent(window.get())
                   .Contains(window->bounds()));
 }
diff --git a/ash/wm/float/tablet_mode_float_window_resizer.cc b/ash/wm/float/tablet_mode_float_window_resizer.cc
index 0414143..1cff32e 100644
--- a/ash/wm/float/tablet_mode_float_window_resizer.cc
+++ b/ash/wm/float/tablet_mode_float_window_resizer.cc
@@ -36,7 +36,7 @@
 void TabletModeFloatWindowResizer::CompleteDrag() {
   aura::Window* float_window = GetTarget();
   auto* float_controller = Shell::Get()->float_controller();
-  DCHECK(float_controller->IsFloated(float_window));
+  DCHECK(WindowState::Get(float_window)->IsFloated());
   float_controller->OnDragCompleted(last_location_in_parent_);
 }
 
diff --git a/ash/wm/window_state.cc b/ash/wm/window_state.cc
index 00a1839..83ad229 100644
--- a/ash/wm/window_state.cc
+++ b/ash/wm/window_state.cc
@@ -1146,21 +1146,6 @@
     }
     return;
   }
-  // `kWindowToggleFloatKey` is only used to toggle float event, not an
-  // indicator of window's float state. this is created to allow access from
-  // both chromeos/ash and avoid recursive call to `kWindowStateTypeKey`.
-  // TODO(shidi): Create API to allow outside access and remove this property.
-  if (key == chromeos::kWindowToggleFloatKey) {
-    DCHECK(chromeos::wm::features::IsFloatWindowEnabled());
-    if (IsFloated()) {
-      // If window is already floated, unfloat and restore.
-      Restore();
-    } else {
-      WMEvent event(WM_EVENT_FLOAT);
-      OnWMEvent(&event);
-      return;
-    }
-  }
   if (key == chromeos::kWindowStateTypeKey) {
     if (!ignore_property_change_) {
       // This change came from somewhere else. Revert it.
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 69ece5b..e3adc4f 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -508,6 +508,7 @@
     "native_library.cc",
     "native_library.h",
     "no_destructor.h",
+    "notreached.cc",
     "notreached.h",
     "observer_list.h",
     "observer_list_internal.cc",
@@ -4402,7 +4403,6 @@
     sources = [
       "//third_party/robolectric/custom_asynctask/java/src/org/chromium/base/task/test/ShadowAsyncTask.java",
       "//third_party/robolectric/custom_asynctask/java/src/org/chromium/base/task/test/ShadowAsyncTaskBridge.java",
-      "android/junit/src/org/chromium/base/metrics/test/ShadowRecordHistogram.java",
       "android/junit/src/org/chromium/base/metrics/test/ShadowRecordUserAction.java",
       "test/android/junit/src/org/chromium/base/task/test/BackgroundShadowAsyncTask.java",
       "test/android/junit/src/org/chromium/base/task/test/CustomShadowAsyncTask.java",
@@ -4451,7 +4451,6 @@
       "android/junit/src/org/chromium/base/library_loader/LinkerTest.java",
       "android/junit/src/org/chromium/base/memory/MemoryPressureMonitorTest.java",
       "android/junit/src/org/chromium/base/metrics/CachingUmaRecorderTest.java",
-      "android/junit/src/org/chromium/base/metrics/test/ShadowRecordHistogramTest.java",
       "android/junit/src/org/chromium/base/metrics/test/ShadowRecordUserActionTest.java",
       "android/junit/src/org/chromium/base/process_launcher/ChildConnectionAllocatorTest.java",
       "android/junit/src/org/chromium/base/process_launcher/ChildProcessConnectionTest.java",
diff --git a/base/allocator/partition_allocator/partition_alloc_unittest.cc b/base/allocator/partition_allocator/partition_alloc_unittest.cc
index d7998fe8..239583d 100644
--- a/base/allocator/partition_allocator/partition_alloc_unittest.cc
+++ b/base/allocator/partition_allocator/partition_alloc_unittest.cc
@@ -81,7 +81,7 @@
   //
   // Set to 4GiB, since we have 2GiB Android devices where tests flakily fail
   // (e.g. Nexus 5X, crbug.com/1191195).
-  return base::SysInfo::AmountOfPhysicalMemory() >= 4000LL * 1024 * 1024;
+  return base::SysInfo::AmountOfPhysicalMemory() >= 4000ULL * 1024 * 1024;
 }
 
 bool SetAddressSpaceLimit() {
diff --git a/base/android/java/src/org/chromium/base/metrics/RecordHistogram.java b/base/android/java/src/org/chromium/base/metrics/RecordHistogram.java
index 7f0351f6..4c34c7f 100644
--- a/base/android/java/src/org/chromium/base/metrics/RecordHistogram.java
+++ b/base/android/java/src/org/chromium/base/metrics/RecordHistogram.java
@@ -13,8 +13,7 @@
 import org.chromium.base.annotations.NativeMethods;
 
 /**
- * Java API for recording UMA histograms. Note that when updating this file, please also update
- * {@link ShadowRecordHistogram} so that it correctly shadows all the methods.
+ * Java API for recording UMA histograms.
  * */
 @JNINamespace("base::android")
 @MainDex
@@ -262,25 +261,31 @@
 
     /**
      * Returns the number of samples recorded in the given bucket of the given histogram.
-     * Does not reset between batched tests. Use HistogramTestRule instead.
+     *
+     * WARNING:
+     * Does not reset between batched tests. Use
+     * {@link org.chromium.base.test.metrics.HistogramTestRule} instead. Or use
+     * {@link org.chromium.base.test.util.MetricsUtils.HistogramDelta} to account for cases where
+     * the initial histogram value is not 0 at the start of the testing logic.
      *
      * @param name name of the histogram to look up
      * @param sample the bucket containing this sample value will be looked up
      */
     @VisibleForTesting
-    @Deprecated
     public static int getHistogramValueCountForTesting(String name, int sample) {
         return UmaRecorderHolder.get().getHistogramValueCountForTesting(name, sample);
     }
 
     /**
      * Returns the number of samples recorded for the given histogram.
-     * Does not reset between batched tests. Use HistogramTestRule instead.
+     *
+     * WARNING:
+     * Does not reset between batched tests. Use
+     * {@link org.chromium.base.test.metrics.HistogramTestRule} instead.
      *
      * @param name name of the histogram to look up
      */
     @VisibleForTesting
-    @Deprecated
     public static int getHistogramTotalCountForTesting(String name) {
         return UmaRecorderHolder.get().getHistogramTotalCountForTesting(name);
     }
diff --git a/base/android/junit/PRESUBMIT.py b/base/android/junit/PRESUBMIT.py
deleted file mode 100644
index 04ffb9af..0000000
--- a/base/android/junit/PRESUBMIT.py
+++ /dev/null
@@ -1,30 +0,0 @@
-# Copyright 2020 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-USE_PYTHON3 = True
-
-
-def CheckChangeOnUpload(input_api, output_api):
-    results = []
-
-    # Dictionary of base/android files with corresponding Robolectric shadows.
-    # If new functions are added to the original file, it is very likely that
-    # function with the same signature should be added to the shadow.
-    impl_to_shadow_paths = {
-        'base/android/java/src/org/'
-        'chromium/base/metrics/RecordHistogram.java':
-            'base/android/junit/src/org/'
-            'chromium/base/metrics/test/ShadowRecordHistogram.java'
-    }
-
-    for impl_path, shadow_path in impl_to_shadow_paths.items():
-        if impl_path in input_api.change.LocalPaths():
-            if shadow_path not in input_api.change.LocalPaths():
-                results.append(
-                    output_api.PresubmitPromptWarning(
-                        'You modified the runtime class:\n  {}\n'
-                        'without changing the corresponding shadow test class'
-                        ':\n  {}\n').format(impl_path, shadow_path))
-
-    return results
diff --git a/base/android/junit/src/org/chromium/base/library_loader/LinkerTest.java b/base/android/junit/src/org/chromium/base/library_loader/LinkerTest.java
index 358b67aa..9535e6ec 100644
--- a/base/android/junit/src/org/chromium/base/library_loader/LinkerTest.java
+++ b/base/android/junit/src/org/chromium/base/library_loader/LinkerTest.java
@@ -24,14 +24,14 @@
 
 import org.chromium.base.library_loader.Linker.PreferAddress;
 import org.chromium.base.metrics.RecordHistogram;
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 
 /**
  *  Tests for {@link Linker}.
  */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(manifest = Config.NONE, shadows = {ShadowRecordHistogram.class})
+@Config(manifest = Config.NONE)
 @SuppressWarnings("GuardedBy") // doNothing().when(...).methodLocked() cannot resolve |mLock|.
 public class LinkerTest {
     @Mock
@@ -45,7 +45,7 @@
 
     @Before
     public void setUp() {
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
         Linker.setNativesForTesting(mNativeMock);
         ModernLinker.setModernLinkerNativesForTesting(mModernLinkerNativeMock);
     }
@@ -282,7 +282,7 @@
         // Verify.
         Assert.assertNotNull(linker.mWebviewReservationSearchResult);
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramTotalCountForTesting(
+                RecordHistogram.getHistogramTotalCountForTesting(
                         "ChromiumAndroidLinker.TimeToFindWebViewReservation.Found.Zygote"));
     }
 
@@ -303,7 +303,7 @@
         // Verify.
         Assert.assertNotNull(linker.mWebviewReservationSearchResult);
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramTotalCountForTesting(
+                RecordHistogram.getHistogramTotalCountForTesting(
                         "ChromiumAndroidLinker.TimeToFindWebViewReservation.NotFound.Child"));
     }
 }
diff --git a/base/android/junit/src/org/chromium/base/metrics/test/ShadowRecordHistogram.java b/base/android/junit/src/org/chromium/base/metrics/test/ShadowRecordHistogram.java
deleted file mode 100644
index 11e7f57..0000000
--- a/base/android/junit/src/org/chromium/base/metrics/test/ShadowRecordHistogram.java
+++ /dev/null
@@ -1,175 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.base.metrics.test;
-
-import android.util.Pair;
-
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
-import org.robolectric.annotation.Resetter;
-
-import org.chromium.base.metrics.RecordHistogram;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Implementation of RecordHistogram which does not rely on native and still enables testing of
- * histogram counts.
- * ShadowRecordHistogram.reset() should be called in the @Before method of test cases using this
- * class.
- */
-@Implements(RecordHistogram.class)
-public class ShadowRecordHistogram {
-    private static final Map<Pair<String, Integer>, Integer> sSamples = new HashMap<>();
-    private static final Map<String, Integer> sTotals = new HashMap<>();
-
-    @Resetter
-    public static void reset() {
-        sSamples.clear();
-        sTotals.clear();
-    }
-
-    @Implementation
-    protected static void recordBooleanHistogram(String name, boolean sample) {
-        Pair<String, Integer> key = Pair.create(name, sample ? 1 : 0);
-        recordSample(key);
-    }
-
-    @Implementation
-    protected static void recordEnumeratedHistogram(String name, int sample, int boundary) {
-        assert sample < boundary : "Sample " + sample + " is not within boundary " + boundary + "!";
-        recordSample(Pair.create(name, sample));
-    }
-
-    @Implementation
-    protected static void recordExactLinearHistogram(String name, int sample, int max) {
-        Pair<String, Integer> key = Pair.create(name, sample);
-        recordSample(key);
-    }
-
-    @Implementation
-    protected static void recordCount1MHistogram(String name, int sample) {
-        Pair<String, Integer> key = Pair.create(name, sample);
-        recordSample(key);
-    }
-
-    @Implementation
-    protected static void recordCount100Histogram(String name, int sample) {
-        Pair<String, Integer> key = Pair.create(name, sample);
-        recordSample(key);
-    }
-
-    @Implementation
-    protected static void recordCount1000Histogram(String name, int sample) {
-        Pair<String, Integer> key = Pair.create(name, sample);
-        recordSample(key);
-    }
-
-    @Implementation
-    protected static void recordCount100000Histogram(String name, int sample) {
-        Pair<String, Integer> key = Pair.create(name, sample);
-        recordSample(key);
-    }
-
-    @Implementation
-    protected static void recordCustomCountHistogram(
-            String name, int sample, int min, int max, int numBuckets) {
-        Pair<String, Integer> key = Pair.create(name, sample);
-        recordSample(key);
-    }
-
-    @Implementation
-    protected static void recordLinearCountHistogram(
-            String name, int sample, int min, int max, int numBuckets) {
-        Pair<String, Integer> key = Pair.create(name, sample);
-        recordSample(key);
-    }
-
-    @Implementation
-    protected static void recordPercentageHistogram(String name, int sample) {
-        Pair<String, Integer> key = Pair.create(name, sample);
-        recordSample(key);
-    }
-
-    @Implementation
-    protected static void recordSparseHistogram(String name, int sample) {
-        Pair<String, Integer> key = Pair.create(name, sample);
-        recordSample(key);
-    }
-
-    @Implementation
-    protected static void recordTimesHistogram(String name, long durationMs) {
-        Pair<String, Integer> key = Pair.create(name, (int) durationMs);
-        recordSample(key);
-    }
-
-    @Implementation
-    protected static void recordMediumTimesHistogram(String name, long durationMs) {
-        Pair<String, Integer> key = Pair.create(name, (int) durationMs);
-        recordSample(key);
-    }
-
-    @Implementation
-    protected static void recordLongTimesHistogram(String name, long durationMs) {
-        Pair<String, Integer> key = Pair.create(name, (int) durationMs);
-        recordSample(key);
-    }
-
-    @Implementation
-    protected static void recordLongTimesHistogram100(String name, long durationMs) {
-        Pair<String, Integer> key = Pair.create(name, (int) durationMs);
-        recordSample(key);
-    }
-
-    @Implementation
-    protected static void recordCustomTimesHistogram(
-            String name, long durationMs, long min, long max, int numBuckets) {
-        Pair<String, Integer> key = Pair.create(name, (int) durationMs);
-        recordSample(key);
-    }
-
-    @Implementation
-    protected static void recordMemoryKBHistogram(String name, int sizeInKB) {
-        Pair<String, Integer> key = Pair.create(name, sizeInKB);
-        recordSample(key);
-    }
-
-    /**
-     * Returns #getHistogramValueCountForTesting value in junit tests.
-     * While {@link RecordHistogram#getHistogramValueCountForTesting} is deprecated, junit tests
-     * should use this function to get the test counts without mocking JNI.
-     */
-    @Implementation
-    public static int getHistogramValueCountForTesting(String name, int sample) {
-        Integer i = sSamples.get(Pair.create(name, sample));
-        return (i != null) ? i : 0;
-    }
-
-    /**
-     * Returns #getHistogramTotalCountForTesting value in junit tests.
-     * While {@link RecordHistogram#getHistogramTotalCountForTesting} is deprecated, junit tests
-     * should use this function to get the correct tests counts without mocking JNI.
-     */
-    @Implementation
-    public static int getHistogramTotalCountForTesting(String name) {
-        Integer i = sTotals.get(name);
-        return (i != null) ? i : 0;
-    }
-
-    private static void recordSample(Pair<String, Integer> key) {
-        Integer bucketValue = sSamples.get(key);
-        if (bucketValue == null) {
-            bucketValue = 0;
-        }
-        sSamples.put(key, bucketValue + 1);
-
-        Integer totalCount = sTotals.get(key.first);
-        if (totalCount == null) {
-            totalCount = 0;
-        }
-        sTotals.put(key.first, totalCount + 1);
-    }
-}
diff --git a/base/android/junit/src/org/chromium/base/metrics/test/ShadowRecordHistogramTest.java b/base/android/junit/src/org/chromium/base/metrics/test/ShadowRecordHistogramTest.java
deleted file mode 100644
index a15fabb..0000000
--- a/base/android/junit/src/org/chromium/base/metrics/test/ShadowRecordHistogramTest.java
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.base.metrics.test;
-
-import static org.hamcrest.Matchers.equalTo;
-import static org.hamcrest.Matchers.greaterThan;
-import static org.junit.Assert.assertThat;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.base.metrics.RecordHistogram;
-import org.chromium.base.test.BaseRobolectricTestRunner;
-
-/** Unit tests for {@link ShadowRecordHistogram}. */
-@RunWith(BaseRobolectricTestRunner.class)
-@Config(shadows = {ShadowRecordHistogram.class})
-public final class ShadowRecordHistogramTest {
-    private static final String HISTOGRAM = "HISTOGRAM";
-    private static final boolean UNUSED = true;
-
-    @Test
-    public void testRecordAndGetForTesting() {
-        // Add some
-        RecordHistogram.recordBooleanHistogram(HISTOGRAM, UNUSED);
-        assertThat(RecordHistogram.getHistogramTotalCountForTesting(HISTOGRAM), greaterThan(0));
-    }
-
-    @Test
-    public void testReset() {
-        // Add some
-        RecordHistogram.recordBooleanHistogram(HISTOGRAM, UNUSED);
-        assertThat(RecordHistogram.getHistogramTotalCountForTesting(HISTOGRAM), greaterThan(0));
-        // Remove state
-        ShadowRecordHistogram.reset();
-        // Are the deleted ones gone?
-        assertThat(RecordHistogram.getHistogramTotalCountForTesting(HISTOGRAM), equalTo(0));
-
-        RecordHistogram.recordBooleanHistogram(HISTOGRAM, UNUSED);
-        assertThat(RecordHistogram.getHistogramTotalCountForTesting(HISTOGRAM), equalTo(1));
-    }
-}
diff --git a/base/check.cc b/base/check.cc
index fb19c25..f340ff6 100644
--- a/base/check.cc
+++ b/base/check.cc
@@ -30,9 +30,7 @@
 
 namespace {
 
-// DCHECK_IS_CONFIGURABLE and ENABLE_LOG_ERROR_NOT_REACHED are both interested
-// in non-FATAL DCHECK()/NOTREACHED() reports.
-#if defined(DCHECK_IS_CONFIGURABLE) || BUILDFLAG(ENABLE_LOG_ERROR_NOT_REACHED)
+#if defined(DCHECK_IS_CONFIGURABLE)
 void DCheckDumpOnceWithoutCrashing(LogMessage* log_message) {
   // Best-effort gate to prevent multiple DCHECKs from being dumped. This will
   // race if multiple threads DCHECK at the same time, but we'll eventually stop
@@ -58,21 +56,6 @@
   }
 }
 
-class NotReachedLogMessage : public LogMessage {
- public:
-  using LogMessage::LogMessage;
-  ~NotReachedLogMessage() override {
-    if (severity() != logging::LOGGING_FATAL)
-      DCheckDumpOnceWithoutCrashing(this);
-  }
-};
-#else
-using NotReachedLogMessage = LogMessage;
-#endif  // defined(DCHECK_IS_CONFIGURABLE) ||
-        // BUILDFLAG(ENABLE_LOG_ERROR_NOT_REACHED)
-
-#if defined(DCHECK_IS_CONFIGURABLE)
-
 class DCheckLogMessage : public LogMessage {
  public:
   using LogMessage::LogMessage;
@@ -103,11 +86,11 @@
 #endif  // BUILDFLAG(IS_WIN)
 #else
 static_assert(logging::LOGGING_DCHECK == logging::LOGGING_FATAL);
-using DCheckLogMessage = LogMessage;
+typedef LogMessage DCheckLogMessage;
 #if BUILDFLAG(IS_WIN)
-using DCheckWin32ErrorLogMessage = Win32ErrorLogMessage;
+typedef Win32ErrorLogMessage DCheckWin32ErrorLogMessage;
 #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
-using DCheckErrnoLogMessage = ErrnoLogMessage;
+typedef ErrnoLogMessage DCheckErrnoLogMessage;
 #endif  // BUILDFLAG(IS_WIN)
 #endif  // defined(DCHECK_IS_CONFIGURABLE)
 
@@ -191,17 +174,6 @@
   return CheckError(log_message);
 }
 
-CheckError CheckError::NotReached(const char* file, int line) {
-  // Outside DCHECK builds NOTREACHED() should not be FATAL. For now.
-  const LogSeverity severity = DCHECK_IS_ON() ? LOGGING_DCHECK : LOGGING_ERROR;
-  auto* const log_message = new NotReachedLogMessage(file, line, severity);
-
-  // TODO(pbos): Consider a better message for NotReached(), this is here to
-  // match existing behavior + test expectations.
-  log_message->stream() << "Check failed: false. ";
-  return CheckError(log_message);
-}
-
 std::ostream& CheckError::stream() {
   return log_message_->stream();
 }
diff --git a/base/check.h b/base/check.h
index b2db7d2f..7561cd3 100644
--- a/base/check.h
+++ b/base/check.h
@@ -82,8 +82,6 @@
                                    int line,
                                    const char* function);
 
-  static CheckError NotReached(const char* file, int line);
-
   // Stream for adding optional details to the error message.
   std::ostream& stream();
 
diff --git a/base/notreached.cc b/base/notreached.cc
new file mode 100644
index 0000000..734da14
--- /dev/null
+++ b/base/notreached.cc
@@ -0,0 +1,23 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/notreached.h"
+#include "base/base_export.h"
+
+// This is a widely included header and its size has significant impact on
+// build time. Try not to raise this limit unless absolutely necessary. See
+// https://chromium.googlesource.com/chromium/src/+/HEAD/docs/wmax_tokens.md
+#ifndef NACL_TC_REV
+#pragma clang max_tokens_here 17000
+#endif
+
+#include "base/logging.h"
+
+namespace logging {
+
+BASE_EXPORT void LogErrorNotReached(const char* file, int line) {
+  LogMessage(file, line, LOG_ERROR).stream() << "NOTREACHED() hit.";
+}
+
+}  // namespace logging
diff --git a/base/notreached.h b/base/notreached.h
index 044e7067..293451a2 100644
--- a/base/notreached.h
+++ b/base/notreached.h
@@ -12,14 +12,14 @@
 
 namespace logging {
 
-// Under these conditions NOTREACHED() will effectively either log or DCHECK.
-#if BUILDFLAG(ENABLE_LOG_ERROR_NOT_REACHED) || DCHECK_IS_ON()
-#define NOTREACHED() \
-  LAZY_CHECK_STREAM( \
-      ::logging::CheckError::NotReached(__FILE__, __LINE__).stream(), true)
+#if BUILDFLAG(ENABLE_LOG_ERROR_NOT_REACHED)
+void BASE_EXPORT LogErrorNotReached(const char* file, int line);
+#define NOTREACHED()                                       \
+  true ? ::logging::LogErrorNotReached(__FILE__, __LINE__) \
+       : EAT_CHECK_STREAM_PARAMS()
 #else
-#define NOTREACHED() EAT_CHECK_STREAM_PARAMS()
-#endif  // BUILDFLAG(ENABLE_LOG_ERROR_NOT_REACHED) || DCHECK_IS_ON()
+#define NOTREACHED() DCHECK(false)
+#endif
 
 // The NOTIMPLEMENTED() macro annotates codepaths which have not been
 // implemented yet. If output spam is a serious concern,
diff --git a/base/process/process_metrics_unittest.cc b/base/process/process_metrics_unittest.cc
index 8ab3e9b1..4a589f1 100644
--- a/base/process/process_metrics_unittest.cc
+++ b/base/process/process_metrics_unittest.cc
@@ -207,11 +207,11 @@
   EXPECT_EQ(meminfo.shmem, 140204);
   EXPECT_EQ(meminfo.slab, 54212);
 #endif
-  EXPECT_EQ(355725,
+  EXPECT_EQ(355725u,
             base::SysInfo::AmountOfAvailablePhysicalMemory(meminfo) / 1024);
   // Simulate as if there is no MemAvailable.
   meminfo.available = 0;
-  EXPECT_EQ(374448,
+  EXPECT_EQ(374448u,
             base::SysInfo::AmountOfAvailablePhysicalMemory(meminfo) / 1024);
   meminfo = {};
   EXPECT_TRUE(ParseProcMeminfo(valid_input2, &meminfo));
@@ -223,7 +223,7 @@
   EXPECT_EQ(meminfo.swap_total, 524280);
   EXPECT_EQ(meminfo.swap_free, 524200);
   EXPECT_EQ(meminfo.dirty, 4);
-  EXPECT_EQ(69936,
+  EXPECT_EQ(69936u,
             base::SysInfo::AmountOfAvailablePhysicalMemory(meminfo) / 1024);
 }
 
diff --git a/base/profiler/stack_copier_signal.cc b/base/profiler/stack_copier_signal.cc
index 1cf08fc..bc2ecb0 100644
--- a/base/profiler/stack_copier_signal.cc
+++ b/base/profiler/stack_copier_signal.cc
@@ -37,7 +37,7 @@
     // futex() can wake up spuriously if this memory address was previously used
     // for a pthread mutex. So, also check the condition.
     while (true) {
-      int res =
+      long res =
           syscall(SYS_futex, futex_int_ptr(), FUTEX_WAIT | FUTEX_PRIVATE_FLAG,
                   0, nullptr, nullptr, 0);
       if (futex_.load(std::memory_order_acquire) != 0)
diff --git a/base/sync_socket_posix.cc b/base/sync_socket_posix.cc
index 92d47fc..1b75de7 100644
--- a/base/sync_socket_posix.cc
+++ b/base/sync_socket_posix.cc
@@ -23,6 +23,7 @@
 #include "base/check_op.h"
 #include "base/containers/span.h"
 #include "base/files/file_util.h"
+#include "base/numerics/safe_conversions.h"
 #include "base/threading/scoped_blocking_call.h"
 #include "build/build_config.h"
 
@@ -174,8 +175,7 @@
     // If there is an error in ioctl, signal that the channel would block.
     return 0;
   }
-  DCHECK_GE(number_chars, 0);
-  return number_chars;
+  return checked_cast<size_t>(number_chars);
 }
 
 bool SyncSocket::IsValid() const {
diff --git a/base/system/sys_info.cc b/base/system/sys_info.cc
index a4c99975..7b068fb 100644
--- a/base/system/sys_info.cc
+++ b/base/system/sys_info.cc
@@ -23,21 +23,21 @@
 namespace {
 #if BUILDFLAG(IS_IOS)
 // For M99, 45% of devices have 2GB of RAM, and 55% have more.
-constexpr int64_t kLowMemoryDeviceThresholdMB = 1024;
+constexpr uint64_t kLowMemoryDeviceThresholdMB = 1024;
 #else
 // Updated Desktop default threshold to match the Android 2021 definition.
-constexpr int64_t kLowMemoryDeviceThresholdMB = 2048;
+constexpr uint64_t kLowMemoryDeviceThresholdMB = 2048;
 #endif
 }  // namespace
 
 // static
-int64_t SysInfo::AmountOfPhysicalMemory() {
+uint64_t SysInfo::AmountOfPhysicalMemory() {
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
           switches::kEnableLowEndDeviceMode)) {
     // Keep using 512MB as the simulated RAM amount for when users or tests have
     // manually enabled low-end device mode. Note this value is different from
     // the threshold used for low end devices.
-    constexpr int64_t kSimulatedMemoryForEnableLowEndDeviceMode =
+    constexpr uint64_t kSimulatedMemoryForEnableLowEndDeviceMode =
         512 * 1024 * 1024;
     return std::min(kSimulatedMemoryForEnableLowEndDeviceMode,
                     AmountOfPhysicalMemoryImpl());
@@ -47,14 +47,14 @@
 }
 
 // static
-int64_t SysInfo::AmountOfAvailablePhysicalMemory() {
+uint64_t SysInfo::AmountOfAvailablePhysicalMemory() {
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
           switches::kEnableLowEndDeviceMode)) {
     // Estimate the available memory by subtracting our memory used estimate
     // from the fake |kLowMemoryDeviceThresholdMB| limit.
-    int64_t memory_used =
+    uint64_t memory_used =
         AmountOfPhysicalMemoryImpl() - AmountOfAvailablePhysicalMemoryImpl();
-    int64_t memory_limit = kLowMemoryDeviceThresholdMB * 1024 * 1024;
+    uint64_t memory_limit = kLowMemoryDeviceThresholdMB * 1024 * 1024;
     // std::min ensures no underflow, as |memory_used| can be > |memory_limit|.
     return memory_limit - std::min(memory_used, memory_limit);
   }
@@ -82,7 +82,8 @@
     return false;
 
   int ram_size_mb = SysInfo::AmountOfPhysicalMemoryMB();
-  return (ram_size_mb > 0 && ram_size_mb <= kLowMemoryDeviceThresholdMB);
+  return ram_size_mb > 0 &&
+         static_cast<uint64_t>(ram_size_mb) <= kLowMemoryDeviceThresholdMB;
 }
 
 // static
diff --git a/base/system/sys_info.h b/base/system/sys_info.h
index 1cd1231..963eb48 100644
--- a/base/system/sys_info.h
+++ b/base/system/sys_info.h
@@ -34,19 +34,19 @@
   // Return the number of bytes of physical memory on the current machine.
   // If low-end device mode is manually enabled via command line flag, this
   // will return the lesser of the actual physical memory, or 512MB.
-  static int64_t AmountOfPhysicalMemory();
+  static uint64_t AmountOfPhysicalMemory();
 
   // Return the number of bytes of current available physical memory on the
   // machine.
   // (The amount of memory that can be allocated without any significant
   // impact on the system. It can lead to freeing inactive file-backed
   // and/or speculative file-backed memory).
-  static int64_t AmountOfAvailablePhysicalMemory();
+  static uint64_t AmountOfAvailablePhysicalMemory();
 
   // Return the number of bytes of virtual memory of this process. A return
   // value of zero means that there is no limit on the available virtual
   // memory.
-  static int64_t AmountOfVirtualMemory();
+  static uint64_t AmountOfVirtualMemory();
 
   // Return the number of megabytes of physical memory on the current machine.
   static int AmountOfPhysicalMemoryMB() {
@@ -215,14 +215,14 @@
   FRIEND_TEST_ALL_PREFIXES(SysInfoTest, AmountOfAvailablePhysicalMemory);
   FRIEND_TEST_ALL_PREFIXES(debug::SystemMetricsTest, ParseMeminfo);
 
-  static int64_t AmountOfPhysicalMemoryImpl();
-  static int64_t AmountOfAvailablePhysicalMemoryImpl();
+  static uint64_t AmountOfPhysicalMemoryImpl();
+  static uint64_t AmountOfAvailablePhysicalMemoryImpl();
   static bool IsLowEndDeviceImpl();
   static HardwareInfo GetHardwareInfoSync();
 
 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) || \
     BUILDFLAG(IS_AIX)
-  static int64_t AmountOfAvailablePhysicalMemory(
+  static uint64_t AmountOfAvailablePhysicalMemory(
       const SystemMemoryInfoKB& meminfo);
 #endif
 };
diff --git a/base/system/sys_info_fuchsia.cc b/base/system/sys_info_fuchsia.cc
index 25396a8..c555946 100644
--- a/base/system/sys_info_fuchsia.cc
+++ b/base/system/sys_info_fuchsia.cc
@@ -91,12 +91,12 @@
 }  // namespace
 
 // static
-int64_t SysInfo::AmountOfPhysicalMemoryImpl() {
+uint64_t SysInfo::AmountOfPhysicalMemoryImpl() {
   return zx_system_get_physmem();
 }
 
 // static
-int64_t SysInfo::AmountOfAvailablePhysicalMemoryImpl() {
+uint64_t SysInfo::AmountOfAvailablePhysicalMemoryImpl() {
   // TODO(https://crbug.com/986608): Implement this.
   NOTIMPLEMENTED_LOG_ONCE();
   return 0;
@@ -108,7 +108,7 @@
 }
 
 // static
-int64_t SysInfo::AmountOfVirtualMemory() {
+uint64_t SysInfo::AmountOfVirtualMemory() {
   return 0;
 }
 
diff --git a/base/system/sys_info_ios.mm b/base/system/sys_info_ios.mm
index e278a18..2b523e5 100644
--- a/base/system/sys_info_ios.mm
+++ b/base/system/sys_info_ios.mm
@@ -14,6 +14,7 @@
 #include "base/check_op.h"
 #include "base/mac/scoped_mach_port.h"
 #include "base/notreached.h"
+#include "base/numerics/safe_conversions.h"
 #include "base/process/process_metrics.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
@@ -115,7 +116,7 @@
 }
 
 // static
-int64_t SysInfo::AmountOfPhysicalMemoryImpl() {
+uint64_t SysInfo::AmountOfPhysicalMemoryImpl() {
   struct host_basic_info hostinfo;
   mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT;
   base::mac::ScopedMachSendRight host(mach_host_self());
@@ -126,17 +127,17 @@
     return 0;
   }
   DCHECK_EQ(HOST_BASIC_INFO_COUNT, count);
-  return static_cast<int64_t>(hostinfo.max_mem);
+  return hostinfo.max_mem;
 }
 
 // static
-int64_t SysInfo::AmountOfAvailablePhysicalMemoryImpl() {
+uint64_t SysInfo::AmountOfAvailablePhysicalMemoryImpl() {
   SystemMemoryInfoKB info;
   if (!GetSystemMemoryInfo(&info))
     return 0;
   // We should add inactive file-backed memory also but there is no such
   // information from iOS unfortunately.
-  return static_cast<int64_t>(info.free + info.speculative) * 1024;
+  return checked_cast<uint64_t>(info.free + info.speculative) * 1024;
 }
 
 // static
diff --git a/base/system/sys_info_linux.cc b/base/system/sys_info_linux.cc
index 881db6f5..1b30427 100644
--- a/base/system/sys_info_linux.cc
+++ b/base/system/sys_info_linux.cc
@@ -24,22 +24,20 @@
 
 namespace {
 
-int64_t AmountOfMemory(int pages_name) {
+uint64_t AmountOfMemory(int pages_name) {
   long pages = sysconf(pages_name);
   long page_size = sysconf(_SC_PAGESIZE);
-  if (pages == -1 || page_size == -1) {
-    NOTREACHED();
+  if (pages < 0 || page_size < 0)
     return 0;
-  }
-  return static_cast<int64_t>(pages) * page_size;
+  return static_cast<uint64_t>(pages) * static_cast<uint64_t>(page_size);
 }
 
-int64_t AmountOfPhysicalMemory() {
+uint64_t AmountOfPhysicalMemory() {
   return AmountOfMemory(_SC_PHYS_PAGES);
 }
 
 base::LazyInstance<
-    base::internal::LazySysInfoValue<int64_t, AmountOfPhysicalMemory>>::Leaky
+    base::internal::LazySysInfoValue<uint64_t, AmountOfPhysicalMemory>>::Leaky
     g_lazy_physical_memory = LAZY_INSTANCE_INITIALIZER;
 
 }  // namespace
@@ -47,12 +45,12 @@
 namespace base {
 
 // static
-int64_t SysInfo::AmountOfPhysicalMemoryImpl() {
+uint64_t SysInfo::AmountOfPhysicalMemoryImpl() {
   return g_lazy_physical_memory.Get().value();
 }
 
 // static
-int64_t SysInfo::AmountOfAvailablePhysicalMemoryImpl() {
+uint64_t SysInfo::AmountOfAvailablePhysicalMemoryImpl() {
   SystemMemoryInfoKB info;
   if (!GetSystemMemoryInfo(&info))
     return 0;
@@ -60,16 +58,16 @@
 }
 
 // static
-int64_t SysInfo::AmountOfAvailablePhysicalMemory(
+uint64_t SysInfo::AmountOfAvailablePhysicalMemory(
     const SystemMemoryInfoKB& info) {
   // See details here:
   // https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=34e431b0ae398fc54ea69ff85ec700722c9da773
   // The fallback logic (when there is no MemAvailable) would be more precise
   // if we had info about zones watermarks (/proc/zoneinfo).
-  int64_t res_kb = info.available != 0
-                       ? info.available - info.active_file
-                       : info.free + info.reclaimable + info.inactive_file;
-  return res_kb * 1024;
+  int res_kb = info.available != 0
+                   ? info.available - info.active_file
+                   : info.free + info.reclaimable + info.inactive_file;
+  return checked_cast<uint64_t>(res_kb) * 1024;
 }
 
 // static
diff --git a/base/system/sys_info_mac.mm b/base/system/sys_info_mac.mm
index 16f1b9d..a709895 100644
--- a/base/system/sys_info_mac.mm
+++ b/base/system/sys_info_mac.mm
@@ -16,6 +16,7 @@
 #include "base/mac/mac_util.h"
 #include "base/mac/scoped_mach_port.h"
 #include "base/notreached.h"
+#include "base/numerics/safe_conversions.h"
 #include "base/process/process_metrics.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
@@ -74,7 +75,7 @@
 }
 
 // static
-int64_t SysInfo::AmountOfPhysicalMemoryImpl() {
+uint64_t SysInfo::AmountOfPhysicalMemoryImpl() {
   struct host_basic_info hostinfo;
   mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT;
   base::mac::ScopedMachSendRight host(mach_host_self());
@@ -85,17 +86,17 @@
     return 0;
   }
   DCHECK_EQ(HOST_BASIC_INFO_COUNT, count);
-  return static_cast<int64_t>(hostinfo.max_mem);
+  return hostinfo.max_mem;
 }
 
 // static
-int64_t SysInfo::AmountOfAvailablePhysicalMemoryImpl() {
+uint64_t SysInfo::AmountOfAvailablePhysicalMemoryImpl() {
   SystemMemoryInfoKB info;
   if (!GetSystemMemoryInfo(&info))
     return 0;
   // We should add inactive file-backed memory also but there is no such
   // information from Mac OS unfortunately.
-  return static_cast<int64_t>(info.free + info.speculative) * 1024;
+  return checked_cast<uint64_t>(info.free + info.speculative) * 1024;
 }
 
 // static
diff --git a/base/system/sys_info_openbsd.cc b/base/system/sys_info_openbsd.cc
index 8849960..a32f06ee 100644
--- a/base/system/sys_info_openbsd.cc
+++ b/base/system/sys_info_openbsd.cc
@@ -14,14 +14,12 @@
 
 namespace {
 
-int64_t AmountOfMemory(int pages_name) {
+uint64_t AmountOfMemory(int pages_name) {
   long pages = sysconf(pages_name);
   long page_size = sysconf(_SC_PAGESIZE);
-  if (pages == -1 || page_size == -1) {
-    NOTREACHED();
+  if (pages < 0 || page_size < 0)
     return 0;
-  }
-  return static_cast<int64_t>(pages) * page_size;
+  return static_cast<uint64_t>(pages) * static_cast<uint64_t>(page_size);
 }
 
 }  // namespace
@@ -41,12 +39,12 @@
 }
 
 // static
-int64_t SysInfo::AmountOfPhysicalMemoryImpl() {
+uint64_t SysInfo::AmountOfPhysicalMemoryImpl() {
   return AmountOfMemory(_SC_PHYS_PAGES);
 }
 
 // static
-int64_t SysInfo::AmountOfAvailablePhysicalMemoryImpl() {
+uint64_t SysInfo::AmountOfAvailablePhysicalMemoryImpl() {
   // We should add inactive file-backed memory also but there is no such
   // information from OpenBSD unfortunately.
   return AmountOfMemory(_SC_AVPHYS_PAGES);
diff --git a/base/system/sys_info_posix.cc b/base/system/sys_info_posix.cc
index 7495c4c5..7ae2d021 100644
--- a/base/system/sys_info_posix.cc
+++ b/base/system/sys_info_posix.cc
@@ -17,6 +17,7 @@
 #include "base/files/file_util.h"
 #include "base/lazy_instance.h"
 #include "base/notreached.h"
+#include "base/numerics/safe_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/system/sys_info_internal.h"
 #include "base/threading/scoped_blocking_call.h"
@@ -78,7 +79,7 @@
     Leaky g_lazy_number_of_processors = LAZY_INSTANCE_INITIALIZER;
 #endif  // !BUILDFLAG(IS_OPENBSD)
 
-int64_t AmountOfVirtualMemory() {
+uint64_t AmountOfVirtualMemory() {
   struct rlimit limit;
   int result = getrlimit(RLIMIT_DATA, &limit);
   if (result != 0) {
@@ -89,7 +90,7 @@
 }
 
 base::LazyInstance<
-    base::internal::LazySysInfoValue<int64_t, AmountOfVirtualMemory>>::Leaky
+    base::internal::LazySysInfoValue<uint64_t, AmountOfVirtualMemory>>::Leaky
     g_lazy_virtual_memory = LAZY_INSTANCE_INITIALIZER;
 
 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
@@ -127,13 +128,14 @@
     *available_bytes =
         zero_size_means_unlimited
             ? std::numeric_limits<int64_t>::max()
-            : static_cast<int64_t>(stats.f_bavail) * stats.f_frsize;
+            : base::checked_cast<int64_t>(stats.f_bavail * stats.f_frsize);
   }
 
   if (total_bytes) {
-    *total_bytes = zero_size_means_unlimited
-                       ? std::numeric_limits<int64_t>::max()
-                       : static_cast<int64_t>(stats.f_blocks) * stats.f_frsize;
+    *total_bytes =
+        zero_size_means_unlimited
+            ? std::numeric_limits<int64_t>::max()
+            : base::checked_cast<int64_t>(stats.f_blocks * stats.f_frsize);
   }
   return true;
 }
@@ -149,7 +151,7 @@
 #endif  // !BUILDFLAG(IS_OPENBSD)
 
 // static
-int64_t SysInfo::AmountOfVirtualMemory() {
+uint64_t SysInfo::AmountOfVirtualMemory() {
   return g_lazy_virtual_memory.Get().value();
 }
 
@@ -245,7 +247,7 @@
 
 // static
 size_t SysInfo::VMAllocationGranularity() {
-  return getpagesize();
+  return checked_cast<size_t>(getpagesize());
 }
 
 }  // namespace base
diff --git a/base/system/sys_info_unittest.cc b/base/system/sys_info_unittest.cc
index 226ef8c2..eb12226 100644
--- a/base/system/sys_info_unittest.cc
+++ b/base/system/sys_info_unittest.cc
@@ -10,6 +10,7 @@
 #include "base/bind.h"
 #include "base/environment.h"
 #include "base/files/file_util.h"
+#include "base/numerics/safe_conversions.h"
 #include "base/process/process_metrics.h"
 #include "base/run_loop.h"
 #include "base/strings/pattern.h"
@@ -57,10 +58,10 @@
 
 TEST_F(SysInfoTest, AmountOfMem) {
   // We aren't actually testing that it's correct, just that it's sane.
-  EXPECT_GT(SysInfo::AmountOfPhysicalMemory(), 0);
+  EXPECT_GT(SysInfo::AmountOfPhysicalMemory(), 0u);
   EXPECT_GT(SysInfo::AmountOfPhysicalMemoryMB(), 0);
   // The maxmimal amount of virtual memory can be zero which means unlimited.
-  EXPECT_GE(SysInfo::AmountOfVirtualMemory(), 0);
+  EXPECT_GE(SysInfo::AmountOfVirtualMemory(), 0u);
 }
 
 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
@@ -78,27 +79,27 @@
   if (info.available != 0) {
     // If there is MemAvailable from kernel.
     EXPECT_LT(info.available, info.total);
-    const int64_t amount = SysInfo::AmountOfAvailablePhysicalMemory(info);
+    const uint64_t amount = SysInfo::AmountOfAvailablePhysicalMemory(info);
     // We aren't actually testing that it's correct, just that it's sane.
     // Available memory is |free - reserved + reclaimable (inactive, non-free)|.
     // On some android platforms, reserved is a substantial portion.
     const int available =
 #if BUILDFLAG(IS_ANDROID)
-        info.free - kReservedPhysicalMemory;
+        std::max(info.free - kReservedPhysicalMemory, 0);
 #else
         info.free;
 #endif  // BUILDFLAG(IS_ANDROID)
-    EXPECT_GT(amount, static_cast<int64_t>(available) * 1024);
-    EXPECT_LT(amount / 1024, info.available);
+    EXPECT_GT(amount, checked_cast<uint64_t>(available) * 1024);
+    EXPECT_LT(amount / 1024, checked_cast<uint64_t>(info.available));
     // Simulate as if there is no MemAvailable.
     info.available = 0;
   }
 
   // There is no MemAvailable. Check the fallback logic.
-  const int64_t amount = SysInfo::AmountOfAvailablePhysicalMemory(info);
+  const uint64_t amount = SysInfo::AmountOfAvailablePhysicalMemory(info);
   // We aren't actually testing that it's correct, just that it's sane.
-  EXPECT_GT(amount, static_cast<int64_t>(info.free) * 1024);
-  EXPECT_LT(amount / 1024, info.total);
+  EXPECT_GT(amount, checked_cast<uint64_t>(info.free) * 1024);
+  EXPECT_LT(amount / 1024, checked_cast<uint64_t>(info.total));
 }
 #endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) ||
         // BUILDFLAG(IS_ANDROID)
diff --git a/base/system/sys_info_win.cc b/base/system/sys_info_win.cc
index dd0cc51..c881ca5 100644
--- a/base/system/sys_info_win.cc
+++ b/base/system/sys_info_win.cc
@@ -13,6 +13,7 @@
 #include "base/check.h"
 #include "base/files/file_path.h"
 #include "base/notreached.h"
+#include "base/numerics/safe_conversions.h"
 #include "base/process/process_metrics.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
@@ -24,7 +25,7 @@
 
 namespace {
 
-int64_t AmountOfMemory(DWORDLONG MEMORYSTATUSEX::*memory_field) {
+uint64_t AmountOfMemory(DWORDLONG MEMORYSTATUSEX::*memory_field) {
   MEMORYSTATUSEX memory_info;
   memory_info.dwLength = sizeof(memory_info);
   if (!GlobalMemoryStatusEx(&memory_info)) {
@@ -32,8 +33,7 @@
     return 0;
   }
 
-  int64_t rv = static_cast<int64_t>(memory_info.*memory_field);
-  return rv < 0 ? std::numeric_limits<int64_t>::max() : rv;
+  return memory_info.*memory_field;
 }
 
 bool GetDiskSpaceInfo(const base::FilePath& path,
@@ -68,20 +68,20 @@
 }
 
 // static
-int64_t SysInfo::AmountOfPhysicalMemoryImpl() {
+uint64_t SysInfo::AmountOfPhysicalMemoryImpl() {
   return AmountOfMemory(&MEMORYSTATUSEX::ullTotalPhys);
 }
 
 // static
-int64_t SysInfo::AmountOfAvailablePhysicalMemoryImpl() {
+uint64_t SysInfo::AmountOfAvailablePhysicalMemoryImpl() {
   SystemMemoryInfoKB info;
   if (!GetSystemMemoryInfo(&info))
     return 0;
-  return static_cast<int64_t>(info.avail_phys) * 1024;
+  return checked_cast<uint64_t>(info.avail_phys) * 1024;
 }
 
 // static
-int64_t SysInfo::AmountOfVirtualMemory() {
+uint64_t SysInfo::AmountOfVirtualMemory() {
   return AmountOfMemory(&MEMORYSTATUSEX::ullTotalVirtual);
 }
 
diff --git a/base/third_party/nspr/prtime.cc b/base/third_party/nspr/prtime.cc
index 88fe1eba5..5d36b5b 100644
--- a/base/third_party/nspr/prtime.cc
+++ b/base/third_party/nspr/prtime.cc
@@ -1165,9 +1165,9 @@
 #endif
                   if (secs != (time_t) -1)
                     {
-                      *result_imploded = (PRInt64)secs * PR_USEC_PER_SEC;
-                      *result_imploded += result->tm_usec;
-                      return PR_SUCCESS;
+                    *result_imploded = secs * (PRTime)PR_USEC_PER_SEC;
+                    *result_imploded += result->tm_usec;
+                    return PR_SUCCESS;
                     }
                 }
 
diff --git a/base/threading/hang_watcher.cc b/base/threading/hang_watcher.cc
index 9ee92e0..8693636 100644
--- a/base/threading/hang_watcher.cc
+++ b/base/threading/hang_watcher.cc
@@ -740,7 +740,7 @@
       // Emit trace events for monitored threads.
       if (ThreadTypeLoggingLevelGreaterOrEqual(watch_state.get()->thread_type(),
                                                LoggingLevel::kUmaOnly)) {
-        const uint64_t thread_id = watch_state.get()->GetThreadID();
+        const PlatformThreadId thread_id = watch_state.get()->GetThreadID();
         const auto track = perfetto::Track::FromPointer(
             this, perfetto::ThreadTrack::ForThread(thread_id));
         TRACE_EVENT_BEGIN("base", "HangWatcher::ThreadHung", track, deadline);
@@ -758,9 +758,8 @@
       // the next capture then they'll already be marked and will be included
       // in the capture at that time.
       if (thread_marked && all_threads_marked) {
-        hung_watch_state_copies_.push_back(WatchStateCopy{
-            deadline,
-            static_cast<PlatformThreadId>(watch_state.get()->GetThreadID())});
+        hung_watch_state_copies_.push_back(
+            WatchStateCopy{deadline, watch_state.get()->GetThreadID()});
       } else {
         all_threads_marked = false;
       }
@@ -1277,7 +1276,7 @@
   return hang_watch_state.get();
 }
 
-uint64_t HangWatchState::GetThreadID() const {
+PlatformThreadId HangWatchState::GetThreadID() const {
   return thread_id_;
 }
 
diff --git a/base/threading/hang_watcher.h b/base/threading/hang_watcher.h
index a2a557c4..4f3bf2b24 100644
--- a/base/threading/hang_watcher.h
+++ b/base/threading/hang_watcher.h
@@ -625,7 +625,7 @@
   WatchHangsInScope* GetCurrentWatchHangsInScope();
 #endif
 
-  uint64_t GetThreadID() const;
+  PlatformThreadId GetThreadID() const;
 
   // Retrieve the current hang watch deadline directly. For testing only.
   HangWatchDeadline* GetHangWatchDeadlineForTesting();
@@ -652,9 +652,8 @@
   HangWatchDeadline deadline_;
 
   // A unique ID of the thread under watch. Used for logging in crash reports
-  // only. Unsigned type is used as it provides a correct behavior for all
-  // platforms for positive thread ids. Any valid thread id should be positive.
-  uint64_t thread_id_;
+  // only.
+  PlatformThreadId thread_id_;
 
   // Number of active HangWatchScopeEnables on this thread.
   int nesting_level_ = 0;
diff --git a/base/threading/platform_thread_linux.cc b/base/threading/platform_thread_linux.cc
index d7e2df4..2868427 100644
--- a/base/threading/platform_thread_linux.cc
+++ b/base/threading/platform_thread_linux.cc
@@ -164,8 +164,10 @@
                      const FilePath& cgroup_directory) {
   FilePath tasks_filepath = cgroup_directory.Append(FILE_PATH_LITERAL("tasks"));
   std::string tid = NumberToString(thread_id);
-  int bytes_written = WriteFile(tasks_filepath, tid.c_str(), tid.size());
-  if (bytes_written != static_cast<int>(tid.size())) {
+  // TODO(crbug.com/1333521): Remove cast.
+  const int size = static_cast<int>(tid.size());
+  int bytes_written = WriteFile(tasks_filepath, tid.data(), size);
+  if (bytes_written != size) {
     DVLOG(1) << "Failed to add " << tid << " to " << tasks_filepath.value();
   }
 }
@@ -416,7 +418,7 @@
 #endif
 
   const int nice_setting = internal::ThreadTypeToNiceValue(thread_type);
-  if (setpriority(PRIO_PROCESS, thread_id, nice_setting)) {
+  if (setpriority(PRIO_PROCESS, static_cast<id_t>(thread_id), nice_setting)) {
     DVPLOG(1) << "Failed to set nice value of thread (" << thread_id << ") to "
               << nice_setting;
   }
diff --git a/base/threading/platform_thread_posix.cc b/base/threading/platform_thread_posix.cc
index a39ed33..714232b0 100644
--- a/base/threading/platform_thread_posix.cc
+++ b/base/threading/platform_thread_posix.cc
@@ -220,7 +220,7 @@
       (g_is_main_thread &&
        !g_main_thread_tid_cache_valid.load(std::memory_order_relaxed))) {
     // Update the cached tid.
-    g_thread_id = syscall(__NR_gettid);
+    g_thread_id = static_cast<pid_t>(syscall(__NR_gettid));
     // If this is the main thread, we can mark the tid_cache as valid.
     // Otherwise, stop the current thread from always entering this slow path.
     if (g_thread_id == getpid()) {
diff --git a/base/time/time_exploded_posix.cc b/base/time/time_exploded_posix.cc
index 45327a8..a2562a9 100644
--- a/base/time/time_exploded_posix.cc
+++ b/base/time/time_exploded_posix.cc
@@ -158,7 +158,7 @@
   exploded->hour = timestruct.tm_hour;
   exploded->minute = timestruct.tm_min;
   exploded->second = timestruct.tm_sec;
-  exploded->millisecond = millisecond;
+  exploded->millisecond = static_cast<int>(millisecond);
 }
 
 // static
diff --git a/base/trace_event/malloc_dump_provider.cc b/base/trace_event/malloc_dump_provider.cc
index 7c30c56..69251d6 100644
--- a/base/trace_event/malloc_dump_provider.cc
+++ b/base/trace_event/malloc_dump_provider.cc
@@ -16,6 +16,7 @@
 #include "base/format_macros.h"
 #include "base/memory/nonscannable_memory.h"
 #include "base/metrics/histogram_functions.h"
+#include "base/numerics/safe_conversions.h"
 #include "base/strings/stringprintf.h"
 #include "base/trace_event/process_memory_dump.h"
 #include "base/trace_event/traced_value.h"
@@ -184,7 +185,7 @@
 
 #if (BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && BUILDFLAG(IS_ANDROID)) || \
     (!BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && !BUILDFLAG(IS_WIN) &&    \
-     !BUILDFLAG(IS_APPLE) && !BUILDFLAG(IS_WIN) && !BUILDFLAG(IS_FUCHSIA))
+     !BUILDFLAG(IS_APPLE) && !BUILDFLAG(IS_FUCHSIA))
 void ReportMallinfoStats(ProcessMemoryDump* pmd,
                          size_t* total_virtual_size,
                          size_t* resident_size,
@@ -203,17 +204,19 @@
   // In case of Android's jemalloc |arena| is 0 and the outer pages size is
   // reported by |hblkhd|. In case of dlmalloc the total is given by
   // |arena| + |hblkhd|. For more details see link: http://goo.gl/fMR8lF.
-  *total_virtual_size += info.arena + info.hblkhd;
-  *resident_size += info.uordblks;
+  *total_virtual_size += checked_cast<size_t>(info.arena + info.hblkhd);
+  size_t total_allocated_size = checked_cast<size_t>(info.uordblks);
+  *resident_size += total_allocated_size;
 
   // Total allocated space is given by |uordblks|.
-  *allocated_objects_size += info.uordblks;
+  *allocated_objects_size += total_allocated_size;
 
   if (pmd) {
     MemoryAllocatorDump* sys_alloc_dump =
         pmd->CreateAllocatorDump("malloc/sys_malloc");
     sys_alloc_dump->AddScalar(MemoryAllocatorDump::kNameSize,
-                              MemoryAllocatorDump::kUnitsBytes, info.uordblks);
+                              MemoryAllocatorDump::kUnitsBytes,
+                              total_allocated_size);
   }
 }
 #endif
diff --git a/base/trace_event/trace_log.cc b/base/trace_event/trace_log.cc
index 6e97ff1..aa721f6 100644
--- a/base/trace_event/trace_log.cc
+++ b/base/trace_event/trace_log.cc
@@ -2209,7 +2209,7 @@
   // See http://isthe.com/chongo/tech/comp/fnv/ for algorithm details.
   const uint64_t kOffsetBasis = 14695981039346656037ull;
   const uint64_t kFnvPrime = 1099511628211ull;
-  const uint64_t pid = process_id_;
+  const uint64_t pid = static_cast<uint64_t>(process_id_);
   process_id_hash_ = (kOffsetBasis ^ pid) * kFnvPrime;
 }
 
diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn
index 43cc496..b975153 100644
--- a/build/config/compiler/BUILD.gn
+++ b/build/config/compiler/BUILD.gn
@@ -426,13 +426,14 @@
   if (is_apple) {
     # On Intel, clang emits both Apple's "compact unwind" information and
     # DWARF eh_frame unwind information by default, for compatibility reasons.
-    # This flag tells it to emit eh_frame informantion only for functions
+    # This flag limits emission of eh_frame information to functions
     # whose unwind information can't be expressed in the compact unwind format
     # (which in practice means almost everything gets only compact unwind
     # entries). This reduces object file size a bit and makes linking a bit
     # faster.
     # On arm64, this is already the default behavior.
     if (current_cpu == "x64") {
+      asmflags += [ "-femit-dwarf-unwind=no-compact-unwind" ]
       cflags += [ "-femit-dwarf-unwind=no-compact-unwind" ]
     }
   }
diff --git a/build/fuchsia/test/log_manager.py b/build/fuchsia/test/log_manager.py
index 80ef56a..347aef6b 100755
--- a/build/fuchsia/test/log_manager.py
+++ b/build/fuchsia/test/log_manager.py
@@ -14,7 +14,8 @@
 from typing import Iterable, Optional, TextIO
 
 from common import read_package_paths, register_common_args, \
-                   register_device_args, run_continuous_ffx_command
+                   register_device_args, run_continuous_ffx_command, \
+                   run_ffx_command
 from ffx_integration import ScopedFfxConfig
 
 
@@ -36,6 +37,8 @@
     def __enter__(self) -> None:
         if self._scoped_ffx_log:
             self._scoped_ffx_log.__enter__()
+            run_ffx_command(('daemon', 'stop'))
+
         return self
 
     def is_logging_enabled(self) -> bool:
@@ -71,6 +74,7 @@
         self.stop()
         if self._scoped_ffx_log:
             self._scoped_ffx_log.__exit__(exc_type, exc_value, traceback)
+            run_ffx_command(('daemon', 'stop'))
 
 
 def start_system_log(log_manager: LogManager,
diff --git a/build/linux/unbundle/absl_algorithm.gn b/build/linux/unbundle/absl_algorithm.gn
new file mode 100644
index 0000000..cc41c68
--- /dev/null
+++ b/build/linux/unbundle/absl_algorithm.gn
@@ -0,0 +1,22 @@
+import("//build/config/linux/pkg_config.gni")
+import("//build/shim_headers.gni")
+
+pkg_config("system_absl_algorithm_container") {
+  packages = [ "absl_algorithm_container" ]
+}
+
+shim_headers("container_shim") {
+  root_path = "."
+  prefix = "absl/algorithm/"
+  headers = [ "container.h" ]
+}
+
+source_set("container") {
+  deps = [ ":container_shim" ]
+  public_configs = [ ":system_absl_algorithm_container" ]
+}
+
+source_set("algorithm_test") {
+}
+source_set("container_test") {
+}
diff --git a/build/linux/unbundle/absl_base.gn b/build/linux/unbundle/absl_base.gn
new file mode 100644
index 0000000..dc54ef2
--- /dev/null
+++ b/build/linux/unbundle/absl_base.gn
@@ -0,0 +1,64 @@
+import("//build/config/linux/pkg_config.gni")
+import("//build/shim_headers.gni")
+
+pkg_config("system_absl_base") {
+  packages = [ "absl_base" ]
+}
+
+pkg_config("system_absl_config") {
+  packages = [ "absl_config" ]
+}
+
+pkg_config("system_absl_core_headers") {
+  packages = [ "absl_core_headers" ]
+}
+
+shim_headers("base_shim") {
+  root_path = "."
+  prefix = "absl/base/"
+  headers = [
+    "call_once.h",
+    "casts.h",
+  ]
+}
+
+source_set("base") {
+  deps = [ ":base_shim" ]
+  public_configs = [ ":system_absl_base" ]
+}
+
+shim_headers("config_shim") {
+  root_path = "."
+  prefix = "absl/base/"
+  headers = [
+    "config.h",
+    "options.h",
+    "policy_checks.h",
+  ]
+}
+
+source_set("config") {
+  deps = [ ":config_shim" ]
+  public_configs = [ ":system_absl_config" ]
+}
+
+shim_headers("core_headers_shim") {
+  root_path = "."
+  prefix = "absl/base/"
+  headers = [
+    "attributes.h",
+    "const_init.h",
+    "macros.h",
+    "optimization.h",
+    "port.h",
+    "thread_annotations.h",
+  ]
+}
+
+source_set("core_headers") {
+  deps = [ ":core_headers_shim" ]
+  public_configs = [ ":system_absl_core_headers" ]
+}
+
+source_set("config_test") {
+}
diff --git a/build/linux/unbundle/absl_cleanup.gn b/build/linux/unbundle/absl_cleanup.gn
new file mode 100644
index 0000000..61455edf3
--- /dev/null
+++ b/build/linux/unbundle/absl_cleanup.gn
@@ -0,0 +1,20 @@
+import("//build/config/linux/pkg_config.gni")
+import("//build/shim_headers.gni")
+
+pkg_config("system_absl_cleanup") {
+  packages = [ "absl_cleanup" ]
+}
+
+shim_headers("cleanup_shim") {
+  root_path = "."
+  prefix = "absl/cleanup/"
+  headers = [ "cleanup.h" ]
+}
+
+source_set("cleanup") {
+  deps = [ ":cleanup_shim" ]
+  public_configs = [ ":system_absl_cleanup" ]
+}
+
+source_set("cleanup_test") {
+}
diff --git a/build/linux/unbundle/absl_container.gn b/build/linux/unbundle/absl_container.gn
new file mode 100644
index 0000000..7bf3482
--- /dev/null
+++ b/build/linux/unbundle/absl_container.gn
@@ -0,0 +1,117 @@
+import("//build/config/linux/pkg_config.gni")
+import("//build/shim_headers.gni")
+
+pkg_config("system_absl_btree") {
+  packages = [ "absl_btree" ]
+}
+
+pkg_config("system_absl_fixed_array") {
+  packages = [ "absl_fixed_array" ]
+}
+
+pkg_config("system_absl_flat_hash_map") {
+  packages = [ "absl_flat_hash_map" ]
+}
+
+pkg_config("system_absl_flat_hash_set") {
+  packages = [ "absl_flat_hash_set" ]
+}
+
+pkg_config("system_absl_inlined_vector") {
+  packages = [ "absl_inlined_vector" ]
+}
+
+pkg_config("system_absl_node_hash_map") {
+  packages = [ "absl_node_hash_map" ]
+}
+
+pkg_config("system_absl_node_hash_set") {
+  packages = [ "absl_node_hash_set" ]
+}
+
+shim_headers("btree_shim") {
+  root_path = "."
+  prefix = "absl/container/"
+  headers = [
+    "btree_map.h",
+    "btree_set.h",
+  ]
+}
+
+source_set("btree") {
+  deps = [ ":btree_shim" ]
+  public_configs = [ ":system_absl_btree" ]
+}
+
+shim_headers("fixed_array_shim") {
+  root_path = "."
+  prefix = "absl/container/"
+  headers = [ "fixed_array.h" ]
+}
+
+source_set("fixed_array") {
+  deps = [ ":fixed_array_shim" ]
+  public_configs = [ ":system_absl_fixed_array" ]
+}
+
+shim_headers("flat_hash_map_shim") {
+  root_path = "."
+  prefix = "absl/container/"
+  headers = [ "flat_hash_map.h" ]
+}
+
+source_set("flat_hash_map") {
+  deps = [ ":flat_hash_map_shim" ]
+  public_configs = [ ":system_absl_flat_hash_map" ]
+}
+
+shim_headers("flat_hash_set_shim") {
+  root_path = "."
+  prefix = "absl/container/"
+  headers = [ "flat_hash_set.h" ]
+}
+
+source_set("flat_hash_set") {
+  deps = [ ":flat_hash_set_shim" ]
+  public_configs = [ ":system_absl_flat_hash_set" ]
+}
+
+shim_headers("inlined_vector_shim") {
+  root_path = "."
+  prefix = "absl/container/"
+  headers = [ "inlined_vector.h" ]
+}
+
+source_set("inlined_vector") {
+  deps = [ ":inlined_vector_shim" ]
+  public_configs = [ ":system_absl_inlined_vector" ]
+}
+
+shim_headers("node_hash_map_shim") {
+  root_path = "."
+  prefix = "absl/container/"
+  headers = [ "node_hash_map.h" ]
+}
+
+source_set("node_hash_map") {
+  deps = [ ":node_hash_map_shim" ]
+  public_configs = [ ":system_absl_node_hash_map" ]
+}
+
+shim_headers("node_hash_set_shim") {
+  root_path = "."
+  prefix = "absl/container/"
+  headers = [ "node_hash_set.h" ]
+}
+
+source_set("node_hash_set") {
+  deps = [ ":node_hash_set_shim" ]
+  public_configs = [ ":system_absl_node_hash_set" ]
+}
+
+source_set("inlined_vector_test") {
+}
+source_set("node_slot_policy_test") {
+}
+source_set("sample_element_size_test") {
+}
diff --git a/build/linux/unbundle/absl_debugging.gn b/build/linux/unbundle/absl_debugging.gn
new file mode 100644
index 0000000..2c38e43
--- /dev/null
+++ b/build/linux/unbundle/absl_debugging.gn
@@ -0,0 +1,47 @@
+import("//build/config/linux/pkg_config.gni")
+import("//build/shim_headers.gni")
+
+pkg_config("system_absl_failure_signal_handler") {
+  packages = [ "absl_failure_signal_handler" ]
+}
+
+pkg_config("system_absl_stacktrace") {
+  packages = [ "absl_stacktrace" ]
+}
+
+pkg_config("system_absl_symbolize") {
+  packages = [ "absl_symbolize" ]
+}
+
+shim_headers("failure_signal_handler_shim") {
+  root_path = "."
+  prefix = "absl/debugging/"
+  headers = [ "failure_signal_handler.h" ]
+}
+
+source_set("failure_signal_handler") {
+  deps = [ ":failure_signal_handler_shim" ]
+  public_configs = [ ":system_absl_failure_signal_handler" ]
+}
+
+shim_headers("stacktrace_shim") {
+  root_path = "."
+  prefix = "absl/debugging/"
+  headers = [ "stacktrace.h" ]
+}
+
+source_set("stacktrace") {
+  deps = [ ":stacktrace_shim" ]
+  public_configs = [ ":system_absl_stacktrace" ]
+}
+
+shim_headers("symbolize_shim") {
+  root_path = "."
+  prefix = "absl/debugging/"
+  headers = [ "symbolize.h" ]
+}
+
+source_set("symbolize") {
+  deps = [ ":symbolize_shim" ]
+  public_configs = [ ":system_absl_symbolize" ]
+}
diff --git a/build/linux/unbundle/absl_flags.gn b/build/linux/unbundle/absl_flags.gn
new file mode 100644
index 0000000..e420603f
--- /dev/null
+++ b/build/linux/unbundle/absl_flags.gn
@@ -0,0 +1,50 @@
+import("//build/config/linux/pkg_config.gni")
+import("//build/shim_headers.gni")
+
+pkg_config("system_absl_flags") {
+  packages = [ "absl_flags" ]
+}
+
+pkg_config("system_absl_flags_parse") {
+  packages = [ "absl_flags_parse" ]
+}
+
+pkg_config("system_absl_flags_usage") {
+  packages = [ "absl_flags_usage" ]
+}
+
+shim_headers("flag_shim") {
+  root_path = "."
+  prefix = "absl/flags/"
+  headers = [
+    "declare.h",
+    "flag.h",
+  ]
+}
+
+source_set("flag") {
+  deps = [ ":flag_shim" ]
+  public_configs = [ ":system_absl_flags" ]
+}
+
+shim_headers("parse_shim") {
+  root_path = "."
+  prefix = "absl/flags/"
+  headers = [ "parse.h" ]
+}
+
+source_set("parse") {
+  deps = [ ":parse_shim" ]
+  public_configs = [ ":system_absl_flags_parse" ]
+}
+
+shim_headers("usage_shim") {
+  root_path = "."
+  prefix = "absl/flags/"
+  headers = [ "usage.h" ]
+}
+
+source_set("usage") {
+  deps = [ ":usage_shim" ]
+  public_configs = [ ":system_absl_flags_usage" ]
+}
diff --git a/build/linux/unbundle/absl_functional.gn b/build/linux/unbundle/absl_functional.gn
new file mode 100644
index 0000000..2a417d5
--- /dev/null
+++ b/build/linux/unbundle/absl_functional.gn
@@ -0,0 +1,17 @@
+import("//build/config/linux/pkg_config.gni")
+import("//build/shim_headers.gni")
+
+pkg_config("system_absl_bind_front") {
+  packages = [ "absl_bind_front" ]
+}
+
+shim_headers("bind_front_shim") {
+  root_path = "."
+  prefix = "absl/functional/"
+  headers = [ "bind_front.h" ]
+}
+
+source_set("bind_front") {
+  deps = [ ":bind_front_shim" ]
+  public_configs = [ ":system_absl_bind_front" ]
+}
diff --git a/build/linux/unbundle/absl_hash.gn b/build/linux/unbundle/absl_hash.gn
new file mode 100644
index 0000000..cb07851
--- /dev/null
+++ b/build/linux/unbundle/absl_hash.gn
@@ -0,0 +1,22 @@
+import("//build/config/linux/pkg_config.gni")
+import("//build/shim_headers.gni")
+
+pkg_config("system_absl_hash") {
+  packages = [ "absl_hash" ]
+}
+
+shim_headers("hash_shim") {
+  root_path = "."
+  prefix = "absl/hash/"
+  headers = [ "hash.h" ]
+}
+
+source_set("hash") {
+  deps = [ ":hash_shim" ]
+  public_configs = [ ":system_absl_hash" ]
+}
+
+source_set("hash_test") {
+}
+source_set("low_level_hash_test") {
+}
diff --git a/build/linux/unbundle/absl_memory.gn b/build/linux/unbundle/absl_memory.gn
new file mode 100644
index 0000000..5d6abe8
--- /dev/null
+++ b/build/linux/unbundle/absl_memory.gn
@@ -0,0 +1,20 @@
+import("//build/config/linux/pkg_config.gni")
+import("//build/shim_headers.gni")
+
+pkg_config("system_absl_memory") {
+  packages = [ "absl_memory" ]
+}
+
+shim_headers("memory_shim") {
+  root_path = "."
+  prefix = "absl/memory/"
+  headers = [ "memory.h" ]
+}
+
+source_set("memory") {
+  deps = [ ":memory_shim" ]
+  public_configs = [ ":system_absl_memory" ]
+}
+
+source_set("memory_test") {
+}
diff --git a/build/linux/unbundle/absl_meta.gn b/build/linux/unbundle/absl_meta.gn
new file mode 100644
index 0000000..7f79a06
--- /dev/null
+++ b/build/linux/unbundle/absl_meta.gn
@@ -0,0 +1,20 @@
+import("//build/config/linux/pkg_config.gni")
+import("//build/shim_headers.gni")
+
+pkg_config("system_absl_type_traits") {
+  packages = [ "absl_type_traits" ]
+}
+
+shim_headers("type_traits_shim") {
+  root_path = "."
+  prefix = "absl/meta/"
+  headers = [ "type_traits.h" ]
+}
+
+source_set("type_traits") {
+  deps = [ ":type_traits_shim" ]
+  public_configs = [ ":system_absl_type_traits" ]
+}
+
+source_set("type_traits_test") {
+}
diff --git a/build/linux/unbundle/absl_numeric.gn b/build/linux/unbundle/absl_numeric.gn
new file mode 100644
index 0000000..c3688f9
--- /dev/null
+++ b/build/linux/unbundle/absl_numeric.gn
@@ -0,0 +1,32 @@
+import("//build/config/linux/pkg_config.gni")
+import("//build/shim_headers.gni")
+
+pkg_config("system_absl_bits") {
+  packages = [ "absl_bits" ]
+}
+
+pkg_config("system_absl_int128") {
+  packages = [ "absl_int128" ]
+}
+
+shim_headers("bits_shim") {
+  root_path = "."
+  prefix = "absl/numeric/"
+  headers = [ "bits.h" ]
+}
+
+source_set("bits") {
+  deps = [ ":bits_shim" ]
+  public_configs = [ ":system_absl_bits" ]
+}
+
+shim_headers("int128_shim") {
+  root_path = "."
+  prefix = "absl/numeric/"
+  headers = [ "int128.h" ]
+}
+
+source_set("int128") {
+  deps = [ ":int128_shim" ]
+  public_configs = [ ":system_absl_int128" ]
+}
diff --git a/build/linux/unbundle/absl_random.gn b/build/linux/unbundle/absl_random.gn
new file mode 100644
index 0000000..e52c9fc
--- /dev/null
+++ b/build/linux/unbundle/absl_random.gn
@@ -0,0 +1,17 @@
+import("//build/config/linux/pkg_config.gni")
+import("//build/shim_headers.gni")
+
+pkg_config("system_absl_random_random") {
+  packages = [ "absl_random_random" ]
+}
+
+shim_headers("random_shim") {
+  root_path = "."
+  prefix = "absl/random/"
+  headers = [ "random.h" ]
+}
+
+source_set("random") {
+  deps = [ ":random_shim" ]
+  public_configs = [ ":system_absl_random_random" ]
+}
diff --git a/build/linux/unbundle/absl_status.gn b/build/linux/unbundle/absl_status.gn
new file mode 100644
index 0000000..b7f40b0b
--- /dev/null
+++ b/build/linux/unbundle/absl_status.gn
@@ -0,0 +1,38 @@
+import("//build/config/linux/pkg_config.gni")
+import("//build/shim_headers.gni")
+
+pkg_config("system_absl_status") {
+  packages = [ "absl_status" ]
+}
+
+pkg_config("system_absl_statusor") {
+  packages = [ "absl_statusor" ]
+}
+
+shim_headers("status_shim") {
+  root_path = "."
+  prefix = "absl/status/"
+  headers = [
+    "status.h",
+    "status_payload_printer.h",
+  ]
+}
+
+source_set("status") {
+  deps = [ ":status_shim" ]
+  public_configs = [ ":system_absl_status" ]
+}
+
+shim_headers("statusor_shim") {
+  root_path = "."
+  prefix = "absl/status/"
+  headers = [ "statusor.h" ]
+}
+
+source_set("statusor") {
+  deps = [ ":statusor_shim" ]
+  public_configs = [ ":system_absl_statusor" ]
+}
+
+source_set("statusor_test") {
+}
diff --git a/build/linux/unbundle/absl_strings.gn b/build/linux/unbundle/absl_strings.gn
new file mode 100644
index 0000000..fd6341c4
--- /dev/null
+++ b/build/linux/unbundle/absl_strings.gn
@@ -0,0 +1,91 @@
+import("//build/config/linux/pkg_config.gni")
+import("//build/shim_headers.gni")
+
+pkg_config("system_absl_cord") {
+  packages = [ "absl_cord" ]
+}
+
+pkg_config("system_absl_strings") {
+  packages = [ "absl_strings" ]
+}
+
+pkg_config("system_absl_str_format") {
+  packages = [ "absl_str_format" ]
+}
+
+shim_headers("cord_shim") {
+  root_path = "."
+  prefix = "absl/strings/"
+  headers = [ "cord.h" ]
+}
+
+source_set("cord") {
+  deps = [ ":cord_shim" ]
+  public_configs = [ ":system_absl_cord" ]
+}
+
+shim_headers("strings_shim") {
+  root_path = "."
+  prefix = "absl/strings/"
+  headers = [
+    "ascii.h",
+    "charconv.h",
+    "escaping.h",
+    "match.h",
+    "numbers.h",
+    "str_cat.h",
+    "str_join.h",
+    "str_replace.h",
+    "str_split.h",
+    "string_view.h",
+    "strip.h",
+    "substitute.h",
+  ]
+}
+
+source_set("strings") {
+  deps = [ ":strings_shim" ]
+  public_configs = [ ":system_absl_strings" ]
+}
+
+shim_headers("str_format_shim") {
+  root_path = "."
+  prefix = "absl/strings/"
+  headers = [ "str_format.h" ]
+}
+
+source_set("str_format") {
+  deps = [ ":str_format_shim" ]
+  public_configs = [ ":system_absl_str_format" ]
+}
+
+source_set("ascii_test") {
+}
+source_set("cord_data_edge_test") {
+}
+source_set("cord_rep_btree_navigator_test") {
+}
+source_set("cord_rep_btree_reader_test") {
+}
+source_set("cord_rep_btree_test") {
+}
+source_set("cord_rep_crc_test") {
+}
+source_set("cordz_functions_test") {
+}
+source_set("cordz_info_statistics_test") {
+}
+source_set("cordz_info_test") {
+}
+source_set("cordz_test") {
+}
+source_set("cordz_update_scope_test") {
+}
+source_set("cordz_update_tracker_test") {
+}
+source_set("match_test") {
+}
+source_set("str_replace_test") {
+}
+source_set("string_view_test") {
+}
diff --git a/build/linux/unbundle/absl_synchronization.gn b/build/linux/unbundle/absl_synchronization.gn
new file mode 100644
index 0000000..60bcf942
--- /dev/null
+++ b/build/linux/unbundle/absl_synchronization.gn
@@ -0,0 +1,22 @@
+import("//build/config/linux/pkg_config.gni")
+import("//build/shim_headers.gni")
+
+pkg_config("system_absl_synchronization") {
+  packages = [ "absl_synchronization" ]
+}
+
+shim_headers("synchronization_shim") {
+  root_path = "."
+  prefix = "absl/synchronization/"
+  headers = [
+    "barrier.h",
+    "blocking_counter.h",
+    "mutex.h",
+    "notification.h",
+  ]
+}
+
+source_set("synchronization") {
+  deps = [ ":synchronization_shim" ]
+  public_configs = [ ":system_absl_synchronization" ]
+}
diff --git a/build/linux/unbundle/absl_time.gn b/build/linux/unbundle/absl_time.gn
new file mode 100644
index 0000000..df5cd20
--- /dev/null
+++ b/build/linux/unbundle/absl_time.gn
@@ -0,0 +1,21 @@
+import("//build/config/linux/pkg_config.gni")
+import("//build/shim_headers.gni")
+
+pkg_config("system_absl_time") {
+  packages = [ "absl_time" ]
+}
+
+shim_headers("time_shim") {
+  root_path = "."
+  prefix = "absl/time/"
+  headers = [
+    "civil_time.h",
+    "clock.h",
+    "time.h",
+  ]
+}
+
+source_set("time") {
+  deps = [ ":time_shim" ]
+  public_configs = [ ":system_absl_time" ]
+}
diff --git a/build/linux/unbundle/absl_types.gn b/build/linux/unbundle/absl_types.gn
new file mode 100644
index 0000000..4bb77f1
--- /dev/null
+++ b/build/linux/unbundle/absl_types.gn
@@ -0,0 +1,97 @@
+import("//build/config/linux/pkg_config.gni")
+import("//build/shim_headers.gni")
+
+pkg_config("system_absl_any") {
+  packages = [ "absl_any" ]
+}
+
+pkg_config("system_absl_bad_any_cast") {
+  packages = [ "absl_bad_any_cast" ]
+}
+
+pkg_config("system_absl_bad_optional_access") {
+  packages = [ "absl_bad_optional_access" ]
+}
+
+pkg_config("system_absl_optional") {
+  packages = [ "absl_optional" ]
+}
+
+pkg_config("system_absl_span") {
+  packages = [ "absl_span" ]
+}
+
+pkg_config("system_absl_variant") {
+  packages = [ "absl_variant" ]
+}
+
+shim_headers("any_shim") {
+  root_path = "."
+  prefix = "absl/types/"
+  headers = [ "any.h" ]
+}
+
+source_set("any") {
+  deps = [ ":any_shim" ]
+  public_configs = [ ":system_absl_any" ]
+}
+
+shim_headers("bad_any_cast_shim") {
+  root_path = "."
+  prefix = "absl/types/"
+  headers = [ "bad_any_cast.h" ]
+}
+
+source_set("bad_any_cast") {
+  deps = [ ":bad_any_cast_shim" ]
+  public_configs = [ ":system_absl_bad_any_cast" ]
+}
+
+shim_headers("bad_optional_access_shim") {
+  root_path = "."
+  prefix = "absl/types/"
+  headers = [ "bad_optional_access.h" ]
+}
+
+source_set("bad_optional_access") {
+  deps = [ ":bad_optional_access_shim" ]
+  public_configs = [ ":system_absl_bad_optional_access" ]
+}
+
+shim_headers("optional_shim") {
+  root_path = "."
+  prefix = "absl/types/"
+  headers = [ "optional.h" ]
+}
+
+source_set("optional") {
+  deps = [ ":optional_shim" ]
+  public_configs = [ ":system_absl_optional" ]
+}
+
+shim_headers("span_shim") {
+  root_path = "."
+  prefix = "absl/types/"
+  headers = [ "span.h" ]
+}
+
+source_set("span") {
+  deps = [ ":span_shim" ]
+  public_configs = [ ":system_absl_span" ]
+}
+
+shim_headers("variant_shim") {
+  root_path = "."
+  prefix = "absl/types/"
+  headers = [ "variant.h" ]
+}
+
+source_set("variant") {
+  deps = [ ":variant_shim" ]
+  public_configs = [ ":system_absl_variant" ]
+}
+
+source_set("optional_test") {
+}
+source_set("variant_test") {
+}
diff --git a/build/linux/unbundle/brotli.gn b/build/linux/unbundle/brotli.gn
new file mode 100644
index 0000000..09f55d1a
--- /dev/null
+++ b/build/linux/unbundle/brotli.gn
@@ -0,0 +1,35 @@
+import("//build/config/linux/pkg_config.gni")
+import("//build/shim_headers.gni")
+
+pkg_config("system_brotli_dec") {
+  packages = [ "libbrotlidec" ]
+}
+
+pkg_config("system_brotli_enc") {
+  packages = [ "libbrotlienc" ]
+}
+
+shim_headers("brotli_shim") {
+  root_path = "include"
+  headers = [
+    "brotli/decode.h",
+    "brotli/encode.h",
+    "brotli/port.h",
+    "brotli/types.h",
+  ]
+}
+
+source_set("dec") {
+  deps = [ ":brotli_shim" ]
+  public_configs = [ ":system_brotli_dec" ]
+}
+
+source_set("enc") {
+  deps = [ ":brotli_shim" ]
+  public_configs = [ ":system_brotli_enc" ]
+}
+
+copy("brotli") {
+  sources = [ "/usr/bin/brotli" ]
+  outputs = [ "$root_out_dir/brotli" ]
+}
diff --git a/build/linux/unbundle/crc32c.gn b/build/linux/unbundle/crc32c.gn
new file mode 100644
index 0000000..23f2292
--- /dev/null
+++ b/build/linux/unbundle/crc32c.gn
@@ -0,0 +1,11 @@
+import("//build/shim_headers.gni")
+
+shim_headers("crc32c_shim") {
+  root_path = "src/include"
+  headers = [ "crc32c/crc32c.h" ]
+}
+
+source_set("crc32c") {
+  deps = [ ":crc32c_shim" ]
+  libs = [ "crc32c" ]
+}
diff --git a/build/linux/unbundle/dav1d.gn b/build/linux/unbundle/dav1d.gn
new file mode 100644
index 0000000..3d65158
--- /dev/null
+++ b/build/linux/unbundle/dav1d.gn
@@ -0,0 +1,23 @@
+import("//build/config/linux/pkg_config.gni")
+import("//build/shim_headers.gni")
+
+pkg_config("system_dav1d") {
+  packages = [ "dav1d" ]
+}
+
+shim_headers("dav1d_shim") {
+  root_path = "libdav1d/include"
+  headers = [
+    "dav1d/common.h",
+    "dav1d/data.h",
+    "dav1d/dav1d.h",
+    "dav1d/headers.h",
+    "dav1d/picture.h",
+    "dav1d/version.h",
+  ]
+}
+
+source_set("dav1d") {
+  deps = [ ":dav1d_shim" ]
+  public_configs = [ ":system_dav1d" ]
+}
diff --git a/build/linux/unbundle/double-conversion.gn b/build/linux/unbundle/double-conversion.gn
new file mode 100644
index 0000000..8f970c5
--- /dev/null
+++ b/build/linux/unbundle/double-conversion.gn
@@ -0,0 +1,23 @@
+import("//build/shim_headers.gni")
+
+shim_headers("double_conversion_shim") {
+  root_path = "."
+  headers = [
+    "double-conversion/bignum.h",
+    "double-conversion/cached-powers.h",
+    "double-conversion/diy-fp.h",
+    "double-conversion/double-conversion.h",
+    "double-conversion/double-to-string.h",
+    "double-conversion/fast-dtoa.h",
+    "double-conversion/fixed-dtoa.h",
+    "double-conversion/ieee.h",
+    "double-conversion/string-to-double.h",
+    "double-conversion/strtod.h",
+    "double-conversion/utils.h",
+  ]
+}
+
+source_set("double_conversion") {
+  deps = [ ":double_conversion_shim" ]
+  libs = [ "double-conversion" ]
+}
diff --git a/build/linux/unbundle/jsoncpp.gn b/build/linux/unbundle/jsoncpp.gn
new file mode 100644
index 0000000..544f9d13
--- /dev/null
+++ b/build/linux/unbundle/jsoncpp.gn
@@ -0,0 +1,27 @@
+import("//build/config/linux/pkg_config.gni")
+import("//build/shim_headers.gni")
+
+pkg_config("jsoncpp_config") {
+  packages = [ "jsoncpp" ]
+}
+
+shim_headers("jsoncpp_shim") {
+  root_path = "source/include"
+  headers = [
+    "json/allocator.h",
+    "json/assertions.h",
+    "json/config.h",
+    "json/forwards.h",
+    "json/json.h",
+    "json/json_features.h",
+    "json/reader.h",
+    "json/value.h",
+    "json/version.h",
+    "json/writer.h",
+  ]
+}
+
+source_set("jsoncpp") {
+  deps = [ ":jsoncpp_shim" ]
+  public_configs = [ ":jsoncpp_config" ]
+}
diff --git a/build/linux/unbundle/libXNVCtrl.gn b/build/linux/unbundle/libXNVCtrl.gn
new file mode 100644
index 0000000..0e1265b8
--- /dev/null
+++ b/build/linux/unbundle/libXNVCtrl.gn
@@ -0,0 +1,19 @@
+import("//build/shim_headers.gni")
+
+shim_headers("libXNVCtrl_shim") {
+  root_path = "../../../../../third_party/libXNVCtrl"
+  prefix = "NVCtrl/"
+  headers = [
+    "NVCtrl.h",
+    "NVCtrlLib.h",
+    "nv_control.h",
+  ]
+}
+
+source_set("libXNVCtrl") {
+  deps = [ ":libXNVCtrl_shim" ]
+  libs = [
+    "XNVCtrl",
+    "xcb",
+  ]
+}
diff --git a/build/linux/unbundle/libaom.gn b/build/linux/unbundle/libaom.gn
new file mode 100644
index 0000000..dab8dfab
--- /dev/null
+++ b/build/linux/unbundle/libaom.gn
@@ -0,0 +1,34 @@
+import("//build/buildflag_header.gni")
+import("//build/config/linux/pkg_config.gni")
+import("//build/shim_headers.gni")
+import("//third_party/libaom/options.gni")
+
+buildflag_header("libaom_buildflags") {
+  header = "libaom_buildflags.h"
+  flags = [ "ENABLE_LIBAOM=$enable_libaom" ]
+}
+
+pkg_config("system_aom") {
+  packages = [ "aom" ]
+}
+
+shim_headers("aom_shim") {
+  root_path = "source/libaom"
+  headers = [
+    "aom/aom.h",
+    "aom/aom_codec.h",
+    "aom/aom_decoder.h",
+    "aom/aom_encoder.h",
+    "aom/aom_external_partition.h",
+    "aom/aom_frame_buffer.h",
+    "aom/aom_image.h",
+    "aom/aom_integer.h",
+    "aom/aomcx.h",
+    "aom/aomdx.h",
+  ]
+}
+
+source_set("libaom") {
+  deps = [ ":aom_shim" ]
+  public_configs = [ ":system_aom" ]
+}
diff --git a/build/linux/unbundle/libavif.gn b/build/linux/unbundle/libavif.gn
new file mode 100644
index 0000000..c79f95b
--- /dev/null
+++ b/build/linux/unbundle/libavif.gn
@@ -0,0 +1,16 @@
+import("//build/config/linux/pkg_config.gni")
+import("//build/shim_headers.gni")
+
+pkg_config("system_libavif") {
+  packages = [ "libavif" ]
+}
+
+shim_headers("avif_shim") {
+  root_path = "src/include"
+  headers = [ "avif/avif.h" ]
+}
+
+source_set("libavif") {
+  deps = [ ":avif_shim" ]
+  public_configs = [ ":system_libavif" ]
+}
diff --git a/build/linux/unbundle/libjxl.gn b/build/linux/unbundle/libjxl.gn
new file mode 100644
index 0000000..227f209
--- /dev/null
+++ b/build/linux/unbundle/libjxl.gn
@@ -0,0 +1,34 @@
+import("//build/config/linux/pkg_config.gni")
+import("//build/shim_headers.gni")
+
+pkg_config("system_libjxl") {
+  packages = [ "libjxl" ]
+}
+
+shim_headers("jxl_shim") {
+  root_path = "src/lib/include"
+  headers = [
+    "jxl/butteraugli.h",
+    "jxl/butteraugli_cxx.h",
+    "jxl/codestream_header.h",
+    "jxl/color_encoding.h",
+    "jxl/decode.h",
+    "jxl/decode_cxx.h",
+    "jxl/encode.h",
+    "jxl/encode_cxx.h",
+    "jxl/jxl_export.h",
+    "jxl/jxl_threads_export.h",
+    "jxl/memory_manager.h",
+    "jxl/parallel_runner.h",
+    "jxl/resizable_parallel_runner.h",
+    "jxl/resizable_parallel_runner_cxx.h",
+    "jxl/thread_parallel_runner.h",
+    "jxl/thread_parallel_runner_cxx.h",
+    "jxl/types.h",
+  ]
+}
+
+source_set("libjxl") {
+  deps = [ ":jxl_shim" ]
+  public_configs = [ ":system_libjxl" ]
+}
diff --git a/build/linux/unbundle/libyuv.gn b/build/linux/unbundle/libyuv.gn
new file mode 100644
index 0000000..a3363e45e
--- /dev/null
+++ b/build/linux/unbundle/libyuv.gn
@@ -0,0 +1,37 @@
+import("//build/config/linux/pkg_config.gni")
+import("//build/shim_headers.gni")
+
+pkg_config("system_yuv") {
+  packages = [ "libyuv" ]
+}
+
+shim_headers("libyuv_shim") {
+  root_path = "include"
+  headers = [
+    "libyuv.h",
+    "libyuv/basic_types.h",
+    "libyuv/compare.h",
+    "libyuv/convert.h",
+    "libyuv/convert_argb.h",
+    "libyuv/convert_from.h",
+    "libyuv/convert_from_argb.h",
+    "libyuv/cpu_id.h",
+    "libyuv/mjpeg_decoder.h",
+    "libyuv/planar_functions.h",
+    "libyuv/rotate.h",
+    "libyuv/rotate_argb.h",
+    "libyuv/rotate_row.h",
+    "libyuv/row.h",
+    "libyuv/scale.h",
+    "libyuv/scale_argb.h",
+    "libyuv/scale_row.h",
+    "libyuv/scale_uv.h",
+    "libyuv/version.h",
+    "libyuv/video_common.h",
+  ]
+}
+
+source_set("libyuv") {
+  deps = [ ":libyuv_shim" ]
+  public_configs = [ ":system_yuv" ]
+}
diff --git a/build/linux/unbundle/replace_gn_files.py b/build/linux/unbundle/replace_gn_files.py
index bc7e92a..49f1e3f 100755
--- a/build/linux/unbundle/replace_gn_files.py
+++ b/build/linux/unbundle/replace_gn_files.py
@@ -15,24 +15,62 @@
 
 
 REPLACEMENTS = {
+  # Use system libabsl_2xxx. These 17 shims MUST be used together.
+  'absl_algorithm': 'third_party/abseil-cpp/absl/algorithm/BUILD.gn',
+  'absl_base': 'third_party/abseil-cpp/absl/base/BUILD.gn',
+  'absl_cleanup': 'third_party/abseil-cpp/absl/cleanup/BUILD.gn',
+  'absl_container': 'third_party/abseil-cpp/absl/container/BUILD.gn',
+  'absl_debugging': 'third_party/abseil-cpp/absl/debugging/BUILD.gn',
+  'absl_flags': 'third_party/abseil-cpp/absl/flags/BUILD.gn',
+  'absl_functional': 'third_party/abseil-cpp/absl/functional/BUILD.gn',
+  'absl_hash': 'third_party/abseil-cpp/absl/hash/BUILD.gn',
+  'absl_memory': 'third_party/abseil-cpp/absl/memory/BUILD.gn',
+  'absl_meta': 'third_party/abseil-cpp/absl/meta/BUILD.gn',
+  'absl_numeric': 'third_party/abseil-cpp/absl/numeric/BUILD.gn',
+  'absl_random': 'third_party/abseil-cpp/absl/random/BUILD.gn',
+  'absl_status': 'third_party/abseil-cpp/absl/status/BUILD.gn',
+  'absl_strings': 'third_party/abseil-cpp/absl/strings/BUILD.gn',
+  'absl_synchronization': 'third_party/abseil-cpp/absl/synchronization/BUILD.gn',
+  'absl_time': 'third_party/abseil-cpp/absl/time/BUILD.gn',
+  'absl_types': 'third_party/abseil-cpp/absl/types/BUILD.gn',
+  #
+  'brotli': 'third_party/brotli/BUILD.gn',
+  'crc32c': 'third_party/crc32c/BUILD.gn',
+  'dav1d': 'third_party/dav1d/BUILD.gn',
+  'double-conversion': 'base/third_party/double_conversion/BUILD.gn',
   'ffmpeg': 'third_party/ffmpeg/BUILD.gn',
   'flac': 'third_party/flac/BUILD.gn',
   'fontconfig': 'third_party/fontconfig/BUILD.gn',
   'freetype': 'build/config/freetype/freetype.gni',
   'harfbuzz-ng': 'third_party/harfbuzz-ng/harfbuzz.gni',
   'icu': 'third_party/icu/BUILD.gn',
+  'jsoncpp' : 'third_party/jsoncpp/BUILD.gn',
+  'libaom' : 'third_party/libaom/BUILD.gn',
+  'libavif' : 'third_party/libavif/BUILD.gn',
   'libdrm': 'third_party/libdrm/BUILD.gn',
   'libevent': 'third_party/libevent/BUILD.gn',
   'libjpeg': 'third_party/libjpeg.gni',
+  'libjxl' : 'third_party/libjxl/BUILD.gn',
   'libpng': 'third_party/libpng/BUILD.gn',
   'libvpx': 'third_party/libvpx/BUILD.gn',
   'libwebp': 'third_party/libwebp/BUILD.gn',
   'libxml': 'third_party/libxml/BUILD.gn',
+  'libXNVCtrl' : 'third_party/angle/src/third_party/libXNVCtrl/BUILD.gn',
   'libxslt': 'third_party/libxslt/BUILD.gn',
+  'libyuv' : 'third_party/libyuv/BUILD.gn',
   'openh264': 'third_party/openh264/BUILD.gn',
   'opus': 'third_party/opus/BUILD.gn',
   're2': 'third_party/re2/BUILD.gn',
   'snappy': 'third_party/snappy/BUILD.gn',
+  # Use system libSPIRV-Tools in Swiftshader. These two shims MUST be used together.
+  'swiftshader-SPIRV-Headers' : 'third_party/swiftshader/third_party/SPIRV-Headers/BUILD.gn',
+  'swiftshader-SPIRV-Tools' : 'third_party/swiftshader/third_party/SPIRV-Tools/BUILD.gn',
+  # Use system libSPIRV-Tools inside ANGLE. These two shims MUST be used together
+  # and can only be used if WebGPU is not compiled (use_dawn=false)
+  'vulkan-SPIRV-Headers' : 'third_party/vulkan-deps/spirv-headers/src/BUILD.gn',
+  'vulkan-SPIRV-Tools' : 'third_party/vulkan-deps/spirv-tools/src/BUILD.gn',
+  #
+  'woff2': 'third_party/woff2/BUILD.gn',
   'zlib': 'third_party/zlib/BUILD.gn',
 }
 
diff --git a/build/linux/unbundle/swiftshader-SPIRV-Headers.gn b/build/linux/unbundle/swiftshader-SPIRV-Headers.gn
new file mode 100644
index 0000000..24f79de
--- /dev/null
+++ b/build/linux/unbundle/swiftshader-SPIRV-Headers.gn
@@ -0,0 +1,17 @@
+import("//build/shim_headers.gni")
+
+shim_headers("SPIRV-Headers_shim") {
+  root_path = "../../../../third_party/SPIRV-Headers/include"
+  headers = [
+    "spirv/unified1/GLSL.std.450.h",
+    "spirv/unified1/NonSemanticClspvReflection.h",
+    "spirv/unified1/NonSemanticDebugPrintf.h",
+    "spirv/unified1/OpenCL.std.h",
+    "spirv/unified1/spirv.h",
+    "spirv/unified1/spirv.hpp",
+  ]
+}
+
+source_set("spv_headers") {
+  deps = [ ":SPIRV-Headers_shim" ]
+}
diff --git a/build/linux/unbundle/swiftshader-SPIRV-Tools.gn b/build/linux/unbundle/swiftshader-SPIRV-Tools.gn
new file mode 100644
index 0000000..eb9d9224
--- /dev/null
+++ b/build/linux/unbundle/swiftshader-SPIRV-Tools.gn
@@ -0,0 +1,32 @@
+import("//build/config/linux/pkg_config.gni")
+import("//build/shim_headers.gni")
+
+pkg_config("system_SPIRV-Tools") {
+  packages = [ "SPIRV-Tools" ]
+}
+
+shim_headers("SPIRV-Tools_shim") {
+  root_path = "../../../../third_party/SPIRV-Tools/include"
+  headers = [
+    "spirv-tools/instrument.hpp",
+    "spirv-tools/libspirv.h",
+    "spirv-tools/libspirv.hpp",
+    "spirv-tools/linker.hpp",
+    "spirv-tools/optimizer.hpp",
+  ]
+}
+
+source_set("spvtools_headers") {
+  deps = [ ":SPIRV-Tools_shim" ]
+  public_configs = [ ":system_SPIRV-Tools" ]
+}
+
+source_set("spvtools_opt") {
+  deps = [ ":SPIRV-Tools_shim" ]
+  public_configs = [ ":system_SPIRV-Tools" ]
+}
+
+source_set("spvtools_val") {
+  deps = [ ":SPIRV-Tools_shim" ]
+  public_configs = [ ":system_SPIRV-Tools" ]
+}
diff --git a/build/linux/unbundle/vulkan-SPIRV-Headers.gn b/build/linux/unbundle/vulkan-SPIRV-Headers.gn
new file mode 100644
index 0000000..eb2495c
--- /dev/null
+++ b/build/linux/unbundle/vulkan-SPIRV-Headers.gn
@@ -0,0 +1,19 @@
+# This shim can only be used if you build Chromium without DAWN
+
+import("//build/shim_headers.gni")
+
+shim_headers("vulkan-SPIRV-Headers_shim") {
+  root_path = "include"
+  headers = [
+    "spirv/unified1/GLSL.std.450.h",
+    "spirv/unified1/NonSemanticClspvReflection.h",
+    "spirv/unified1/NonSemanticDebugPrintf.h",
+    "spirv/unified1/OpenCL.std.h",
+    "spirv/unified1/spirv.h",
+    "spirv/unified1/spirv.hpp",
+  ]
+}
+
+source_set("spv_headers") {
+  deps = [ ":vulkan-SPIRV-Headers_shim" ]
+}
diff --git a/build/linux/unbundle/vulkan-SPIRV-Tools.gn b/build/linux/unbundle/vulkan-SPIRV-Tools.gn
new file mode 100644
index 0000000..a65c64c
--- /dev/null
+++ b/build/linux/unbundle/vulkan-SPIRV-Tools.gn
@@ -0,0 +1,69 @@
+# This shim can only be used if you build Chromium without DAWN
+
+import("//build/config/linux/pkg_config.gni")
+import("//build/shim_headers.gni")
+
+pkg_config("spvtools_internal_config") {
+  packages = [ "SPIRV-Tools" ]
+}
+
+shim_headers("vulkan-SPIRV-Tools_shim") {
+  root_path = "include"
+  headers = [
+    "spirv-tools/instrument.hpp",
+    "spirv-tools/libspirv.h",
+    "spirv-tools/libspirv.hpp",
+    "spirv-tools/linker.hpp",
+    "spirv-tools/optimizer.hpp",
+  ]
+}
+
+source_set("SPIRV-Tools") {
+  deps = [ ":vulkan-SPIRV-Tools_shim" ]
+  public_configs = [ ":spvtools_internal_config" ]
+}
+
+source_set("spvtools") {
+  deps = [ ":vulkan-SPIRV-Tools_shim" ]
+  public_configs = [ ":spvtools_internal_config" ]
+}
+
+source_set("spvtools_core_enums_unified1") {
+  deps = [ ":vulkan-SPIRV-Tools_shim" ]
+  public_configs = [ ":spvtools_internal_config" ]
+}
+
+source_set("spvtools_core_tables_unified1") {
+  deps = [ ":vulkan-SPIRV-Tools_shim" ]
+  public_configs = [ ":spvtools_internal_config" ]
+}
+
+source_set("spvtools_headers") {
+  deps = [ ":vulkan-SPIRV-Tools_shim" ]
+  public_configs = [ ":spvtools_internal_config" ]
+}
+
+source_set("spvtools_language_header_cldebuginfo100") {
+  deps = [ ":vulkan-SPIRV-Tools_shim" ]
+  public_configs = [ ":spvtools_internal_config" ]
+}
+
+source_set("spvtools_language_header_debuginfo") {
+  deps = [ ":vulkan-SPIRV-Tools_shim" ]
+  public_configs = [ ":spvtools_internal_config" ]
+}
+
+source_set("spvtools_language_header_vkdebuginfo100") {
+  deps = [ ":vulkan-SPIRV-Tools_shim" ]
+  public_configs = [ ":spvtools_internal_config" ]
+}
+
+source_set("spvtools_opt") {
+  deps = [ ":vulkan-SPIRV-Tools_shim" ]
+  public_configs = [ ":spvtools_internal_config" ]
+}
+
+source_set("spvtools_val") {
+  deps = [ ":vulkan-SPIRV-Tools_shim" ]
+  public_configs = [ ":spvtools_internal_config" ]
+}
diff --git a/build/linux/unbundle/woff2.gn b/build/linux/unbundle/woff2.gn
new file mode 100644
index 0000000..e7bae10
--- /dev/null
+++ b/build/linux/unbundle/woff2.gn
@@ -0,0 +1,20 @@
+import("//build/config/linux/pkg_config.gni")
+import("//build/shim_headers.gni")
+
+pkg_config("system_woff2") {
+  packages = [ "libwoff2dec" ]
+}
+
+shim_headers("woff2_shim") {
+  root_path = "include"
+  headers = [
+    "woff2/decode.h",
+    "woff2/encode.h",
+    "woff2/output.h",
+  ]
+}
+
+source_set("woff2_dec") {
+  deps = [ ":woff2_shim" ]
+  public_configs = [ ":system_woff2" ]
+}
diff --git a/build/nocompile.gni b/build/nocompile.gni
index db937ed..a7d3ba0 100644
--- a/build/nocompile.gni
+++ b/build/nocompile.gni
@@ -127,6 +127,13 @@
           rebase_path(sysroot, root_build_dir),
         ]
       }
+
+      if (llvm_force_head_revision && !is_nacl) {
+        args += [
+          # TODO(crbug.com/1343975) Evaluate and possibly enable.
+          "-Wno-deprecated-builtins",
+        ]
+      }
     }
 
     test(target_name) {
diff --git a/cc/layers/heads_up_display_layer_impl.cc b/cc/layers/heads_up_display_layer_impl.cc
index d564fb0..9a9fada3 100644
--- a/cc/layers/heads_up_display_layer_impl.cc
+++ b/cc/layers/heads_up_display_layer_impl.cc
@@ -858,8 +858,11 @@
                   memory_entry_.total_budget_in_bytes) *
                  180;
 
-  SkColor colors[] = {SK_ColorRED, SK_ColorGREEN, SK_ColorGREEN,
-                      SkColorSetARGB(255, 255, 140, 0), SK_ColorRED};
+  SkColor4f colors[] = {SkColors::kRed,
+                        SkColors::kGreen,
+                        SkColors::kGreen,
+                        {1.0f, 0.55f, 0.0f, 1.0f},
+                        SkColors::kRed};
   const SkScalar pos[] = {SkFloatToScalar(0.2f), SkFloatToScalar(0.4f),
                           SkFloatToScalar(0.6f), SkFloatToScalar(0.8f),
                           SkFloatToScalar(1.0f)};
diff --git a/cc/paint/paint_op_buffer_unittest.cc b/cc/paint/paint_op_buffer_unittest.cc
index 82ec8b2..0b80286 100644
--- a/cc/paint/paint_op_buffer_unittest.cc
+++ b/cc/paint/paint_op_buffer_unittest.cc
@@ -74,7 +74,7 @@
     shader->start_radius_ = 13.4f;
     shader->tx_ = SkTileMode::kRepeat;
     shader->ty_ = SkTileMode::kMirror;
-    shader->fallback_color_ = SkColorSetARGB(254, 252, 250, 248);
+    shader->fallback_color_ = {0.99f, 0.98f, 0.97f, 0.99f};
     shader->scaling_behavior_ = PaintShader::ScalingBehavior::kRasterAtScale;
     if (use_matrix) {
       shader->local_matrix_.emplace(SkMatrix::I());
@@ -88,8 +88,9 @@
     shader->start_degrees_ = 123;
     shader->end_degrees_ = 456;
     // TODO(vmpstr): Add PaintImage/PaintRecord.
-    shader->colors_ = {SkColorSetARGB(1, 2, 3, 4), SkColorSetARGB(5, 6, 7, 8),
-                       SkColorSetARGB(9, 0, 1, 2)};
+    shader->colors_ = {{0.1f, 0.2f, 0.3f, 0.4f},
+                       {0.05f, 0.15f, 0.25f, 0.35f},
+                       {0.0f, 0.5f, 0.9f, 0.1f}};
     shader->positions_ = {0.f, 0.4f, 1.f};
   }
 };
@@ -1127,7 +1128,7 @@
       flags.setStrokeJoin(PaintFlags::kBevel_Join);
       flags.setStyle(PaintFlags::kStroke_Style);
       flags.setFilterQuality(PaintFlags::FilterQuality::kMedium);
-      flags.setShader(PaintShader::MakeColor(SkColorSetARGB(1, 2, 3, 4)));
+      flags.setShader(PaintShader::MakeColor({0.1f, 0.2f, 0.3f, 0.4f}));
       return flags;
     }(),
     [] {
@@ -1159,7 +1160,8 @@
       looper_builder.addLayer(layer_info);
       flags.setLooper(looper_builder.detach());
 
-      sk_sp<PaintShader> shader = PaintShader::MakeColor(SK_ColorTRANSPARENT);
+      sk_sp<PaintShader> shader =
+          PaintShader::MakeColor(SkColors::kTransparent);
       PaintOpSerializationTestUtils::FillArbitraryShaderValues(shader.get(),
                                                                true);
       flags.setShader(std::move(shader));
@@ -1168,13 +1170,14 @@
     }(),
     [] {
       PaintFlags flags;
-      flags.setShader(PaintShader::MakeColor(SkColorSetARGB(12, 34, 56, 78)));
+      flags.setShader(PaintShader::MakeColor({0.1f, 0.2f, 0.3f, 0.4f}));
 
       return flags;
     }(),
     [] {
       PaintFlags flags;
-      sk_sp<PaintShader> shader = PaintShader::MakeColor(SK_ColorTRANSPARENT);
+      sk_sp<PaintShader> shader =
+          PaintShader::MakeColor(SkColors::kTransparent);
       PaintOpSerializationTestUtils::FillArbitraryShaderValues(shader.get(),
                                                                false);
       flags.setShader(std::move(shader));
@@ -1184,9 +1187,9 @@
     [] {
       PaintFlags flags;
       SkPoint points[2] = {SkPoint::Make(1, 2), SkPoint::Make(3, 4)};
-      SkColor colors[3] = {SkColorSetARGB(1, 2, 3, 4),
-                           SkColorSetARGB(4, 3, 2, 1),
-                           SkColorSetARGB(0, 10, 20, 30)};
+      SkColor4f colors[3] = {{0.1f, 0.2f, 0.3f, 0.4f},
+                             {0.4f, 0.3f, 0.2f, 0.1f},
+                             {0.2f, 0.4f, 0.6f, 0.0f}};
       SkScalar positions[3] = {0.f, 0.3f, 1.f};
       flags.setShader(PaintShader::MakeLinearGradient(points, colors, positions,
                                                       3, SkTileMode::kMirror));
@@ -1195,9 +1198,9 @@
     }(),
     [] {
       PaintFlags flags;
-      SkColor colors[3] = {SkColorSetARGB(1, 2, 3, 4),
-                           SkColorSetARGB(4, 3, 2, 1),
-                           SkColorSetARGB(0, 10, 20, 30)};
+      SkColor4f colors[3] = {{0.1f, 0.2f, 0.3f, 0.4f},
+                             {0.4f, 0.3f, 0.2f, 0.1f},
+                             {0.2f, 0.4f, 0.6f, 0.0f}};
       flags.setShader(PaintShader::MakeSweepGradient(
           0.2f, -0.8f, colors, nullptr, 3, SkTileMode::kMirror, 10, 20));
       return flags;
diff --git a/cc/paint/paint_op_perftest.cc b/cc/paint/paint_op_perftest.cc
index 043d8180..c3bba0b 100644
--- a/cc/paint/paint_op_perftest.cc
+++ b/cc/paint/paint_op_perftest.cc
@@ -137,7 +137,7 @@
   looper_builder.addLayer(layer_info);
   flags.setLooper(looper_builder.detach());
 
-  sk_sp<PaintShader> shader = PaintShader::MakeColor(SK_ColorTRANSPARENT);
+  sk_sp<PaintShader> shader = PaintShader::MakeColor(SkColors::kTransparent);
   flags.setShader(std::move(shader));
 
   SkPath path;
diff --git a/cc/paint/paint_op_reader.cc b/cc/paint/paint_op_reader.cc
index 88ad5a8..6f9ad5cd 100644
--- a/cc/paint/paint_op_reader.cc
+++ b/cc/paint/paint_op_reader.cc
@@ -566,7 +566,8 @@
                    kInsufficientRemainingBytes_Read_PaintShader_ColorSize);
     return;
   }
-  size_t colors_bytes = colors_size * sizeof(SkColor);
+  size_t colors_bytes =
+      colors_size * (colors_size > 0 ? sizeof(ref.colors_[0]) : 0u);
   if (colors_bytes > remaining_bytes_) {
     SetInvalid(DeserializationError::
                    kInsufficientRemainingBytes_Read_PaintShader_ColorBytes);
diff --git a/cc/paint/paint_op_writer.cc b/cc/paint/paint_op_writer.cc
index 648095a..497cdee 100644
--- a/cc/paint/paint_op_writer.cc
+++ b/cc/paint/paint_op_writer.cc
@@ -544,7 +544,9 @@
   }
 
   WriteSize(shader->colors_.size());
-  WriteData(shader->colors_.size() * sizeof(SkColor), shader->colors_.data());
+  WriteData(shader->colors_.size() *
+                (shader->colors_.size() > 0 ? sizeof(shader->colors_[0]) : 0u),
+            shader->colors_.data());
 
   WriteSize(shader->positions_.size());
   WriteData(shader->positions_.size() * sizeof(SkScalar),
diff --git a/cc/paint/paint_shader.cc b/cc/paint/paint_shader.cc
index 02031a9..dc267315 100644
--- a/cc/paint/paint_shader.cc
+++ b/cc/paint/paint_shader.cc
@@ -65,7 +65,7 @@
   return shader;
 }
 
-sk_sp<PaintShader> PaintShader::MakeColor(SkColor color) {
+sk_sp<PaintShader> PaintShader::MakeColor(SkColor4f color) {
   sk_sp<PaintShader> shader(new PaintShader(Type::kColor));
 
   // Just one color. Store it in the fallback color. Easy.
@@ -76,13 +76,13 @@
 }
 
 sk_sp<PaintShader> PaintShader::MakeLinearGradient(const SkPoint points[],
-                                                   const SkColor colors[],
+                                                   const SkColor4f colors[],
                                                    const SkScalar pos[],
                                                    int count,
                                                    SkTileMode mode,
                                                    uint32_t flags,
                                                    const SkMatrix* local_matrix,
-                                                   SkColor fallback_color) {
+                                                   SkColor4f fallback_color) {
   sk_sp<PaintShader> shader(new PaintShader(Type::kLinearGradient));
 
   // There are always two points, the start and the end.
@@ -98,13 +98,13 @@
 
 sk_sp<PaintShader> PaintShader::MakeRadialGradient(const SkPoint& center,
                                                    SkScalar radius,
-                                                   const SkColor colors[],
+                                                   const SkColor4f colors[],
                                                    const SkScalar pos[],
                                                    int count,
                                                    SkTileMode mode,
                                                    uint32_t flags,
                                                    const SkMatrix* local_matrix,
-                                                   SkColor fallback_color) {
+                                                   SkColor4f fallback_color) {
   sk_sp<PaintShader> shader(new PaintShader(Type::kRadialGradient));
 
   shader->center_ = center;
@@ -122,13 +122,13 @@
     SkScalar start_radius,
     const SkPoint& end,
     SkScalar end_radius,
-    const SkColor colors[],
+    const SkColor4f colors[],
     const SkScalar pos[],
     int count,
     SkTileMode mode,
     uint32_t flags,
     const SkMatrix* local_matrix,
-    SkColor fallback_color) {
+    SkColor4f fallback_color) {
   sk_sp<PaintShader> shader(new PaintShader(Type::kTwoPointConicalGradient));
 
   shader->start_point_ = start;
@@ -145,7 +145,7 @@
 
 sk_sp<PaintShader> PaintShader::MakeSweepGradient(SkScalar cx,
                                                   SkScalar cy,
-                                                  const SkColor colors[],
+                                                  const SkColor4f colors[],
                                                   const SkScalar pos[],
                                                   int color_count,
                                                   SkTileMode mode,
@@ -153,7 +153,7 @@
                                                   SkScalar end_degrees,
                                                   uint32_t flags,
                                                   const SkMatrix* local_matrix,
-                                                  SkColor fallback_color) {
+                                                  SkColor4f fallback_color) {
   sk_sp<PaintShader> shader(new PaintShader(Type::kSweepGradient));
 
   shader->center_ = SkPoint::Make(cx, cy);
@@ -223,7 +223,7 @@
          sizeof(shader->id_) +
          PaintOpWriter::GetRecordSize(shader->record_.get()) +
          sizeof(shader->colors_.size()) +
-         shader->colors_.size() * sizeof(SkColor) +
+         shader->colors_.size() * sizeof(SkColor4f) +
          sizeof(shader->positions_.size()) +
          shader->positions_.size() * sizeof(SkScalar);
 }
@@ -386,6 +386,13 @@
   SkSamplingOptions sampling(
       PaintFlags::FilterQualityToSkSamplingOptions(quality));
 
+  // TODO(crbug/1308932): Remove this helper vector colors and make all
+  // SkColor4f.
+  std::vector<SkColor> colors;
+  colors.reserve(colors.size());
+  for (auto& c : colors_)
+    colors.push_back(c.toSkColor());
+
   switch (shader_type_) {
     case Type::kEmpty:
       return SkShaders::Empty();
@@ -394,29 +401,37 @@
       break;
     case Type::kLinearGradient: {
       SkPoint points[2] = {start_point_, end_point_};
+      // TODO(crbug/1308932): Remove this helper vector colors and make all
+      // SkColor4f.
       return SkGradientShader::MakeLinear(
-          points, colors_.data(),
+          points, colors.data(),
           positions_.empty() ? nullptr : positions_.data(),
-          static_cast<int>(colors_.size()), tx_, flags_,
+          static_cast<int>(colors.size()), tx_, flags_,
           base::OptionalOrNullptr(local_matrix_));
     }
     case Type::kRadialGradient:
+      // TODO(crbug/1308932): Remove this helper vector colors and make all
+      // SkColor4f.
       return SkGradientShader::MakeRadial(
-          center_, start_radius_, colors_.data(),
+          center_, start_radius_, colors.data(),
           positions_.empty() ? nullptr : positions_.data(),
-          static_cast<int>(colors_.size()), tx_, flags_,
+          static_cast<int>(colors.size()), tx_, flags_,
           base::OptionalOrNullptr(local_matrix_));
     case Type::kTwoPointConicalGradient:
+      // TODO(crbug/1308932): Remove this helper vector colors and make all
+      // SkColor4f.
       return SkGradientShader::MakeTwoPointConical(
-          start_point_, start_radius_, end_point_, end_radius_, colors_.data(),
+          start_point_, start_radius_, end_point_, end_radius_, colors.data(),
           positions_.empty() ? nullptr : positions_.data(),
-          static_cast<int>(colors_.size()), tx_, flags_,
+          static_cast<int>(colors.size()), tx_, flags_,
           base::OptionalOrNullptr(local_matrix_));
     case Type::kSweepGradient:
+      // TODO(crbug/1308932): Remove this helper vector colors and make all
+      // SkColor4f.
       return SkGradientShader::MakeSweep(
-          center_.x(), center_.y(), colors_.data(),
+          center_.x(), center_.y(), colors.data(),
           positions_.empty() ? nullptr : positions_.data(),
-          static_cast<int>(colors_.size()), tx_, start_degrees_, end_degrees_,
+          static_cast<int>(colors.size()), tx_, start_degrees_, end_degrees_,
           flags_, base::OptionalOrNullptr(local_matrix_));
     case Type::kImage:
       if (sk_cached_image_) {
@@ -453,7 +468,7 @@
 
   // If we didn't create a shader for whatever reason, create a fallback
   // color one.
-  return SkShaders::Color(fallback_color_);
+  return SkShaders::Color(fallback_color_.toSkColor());
 }
 
 void PaintShader::ResolveSkObjects(const gfx::SizeF* raster_scale,
@@ -476,7 +491,7 @@
   }
 }
 
-void PaintShader::SetColorsAndPositions(const SkColor* colors,
+void PaintShader::SetColorsAndPositions(const SkColor4f* colors,
                                         const SkScalar* positions,
                                         int count) {
 #if DCHECK_IS_ON()
@@ -498,7 +513,8 @@
   ty_ = ty;
 }
 
-void PaintShader::SetFlagsAndFallback(uint32_t flags, SkColor fallback_color) {
+void PaintShader::SetFlagsAndFallback(uint32_t flags,
+                                      SkColor4f fallback_color) {
   flags_ = flags;
   fallback_color_ = fallback_color;
 }
@@ -516,7 +532,7 @@
       if (tx_ == SkTileMode::kDecal)
         return false;
       for (const auto& c : colors_)
-        if (SkColorGetA(c) != 0xFF)
+        if (!c.isOpaque())
           return false;
       return true;
     case Type::kTwoPointConicalGradient:
@@ -538,7 +554,7 @@
       NOTREACHED();
       break;
   }
-  return SkColorGetA(fallback_color_) == 0xFF;
+  return fallback_color_.isOpaque();
 }
 
 bool PaintShader::IsValid() const {
diff --git a/cc/paint/paint_shader.h b/cc/paint/paint_shader.h
index 582536b..b531743 100644
--- a/cc/paint/paint_shader.h
+++ b/cc/paint/paint_shader.h
@@ -53,47 +53,47 @@
 
   static sk_sp<PaintShader> MakeEmpty();
 
-  static sk_sp<PaintShader> MakeColor(SkColor color);
+  static sk_sp<PaintShader> MakeColor(SkColor4f color);
 
   // TODO(crbug.com/1155544) SkMatrix is deprecated in favor of SkM44.
   static sk_sp<PaintShader> MakeLinearGradient(
       const SkPoint* points,
-      const SkColor* colors,
+      const SkColor4f colors[],
       const SkScalar* pos,
       int count,
       SkTileMode mode,
       uint32_t flags = 0,
       const SkMatrix* local_matrix = nullptr,
-      SkColor fallback_color = SK_ColorTRANSPARENT);
+      SkColor4f fallback_color = SkColors::kTransparent);
 
   static sk_sp<PaintShader> MakeRadialGradient(
       const SkPoint& center,
       SkScalar radius,
-      const SkColor colors[],
+      const SkColor4f colors[],
       const SkScalar pos[],
       int color_count,
       SkTileMode mode,
       uint32_t flags = 0,
       const SkMatrix* local_matrix = nullptr,
-      SkColor fallback_color = SK_ColorTRANSPARENT);
+      SkColor4f fallback_color = SkColors::kTransparent);
 
   static sk_sp<PaintShader> MakeTwoPointConicalGradient(
       const SkPoint& start,
       SkScalar start_radius,
       const SkPoint& end,
       SkScalar end_radius,
-      const SkColor colors[],
+      const SkColor4f colors[],
       const SkScalar pos[],
       int color_count,
       SkTileMode mode,
       uint32_t flags = 0,
       const SkMatrix* local_matrix = nullptr,
-      SkColor fallback_color = SK_ColorTRANSPARENT);
+      SkColor4f fallback_color = SkColors::kTransparent);
 
   static sk_sp<PaintShader> MakeSweepGradient(
       SkScalar cx,
       SkScalar cy,
-      const SkColor colors[],
+      const SkColor4f colors[],
       const SkScalar pos[],
       int color_count,
       SkTileMode mode,
@@ -101,7 +101,7 @@
       SkScalar end_degrees,
       uint32_t flags = 0,
       const SkMatrix* local_matrix = nullptr,
-      SkColor fallback_color = SK_ColorTRANSPARENT);
+      SkColor4f fallback_color = SkColors::kTransparent);
 
   // |tile_rect| is not null only if the |image| is paint worklet backed.
   static sk_sp<PaintShader> MakeImage(const PaintImage& image,
@@ -226,11 +226,11 @@
   sk_sp<PaintShader> CreatePaintWorkletRecord(
       ImageProvider* image_provider) const;
 
-  void SetColorsAndPositions(const SkColor* colors,
+  void SetColorsAndPositions(const SkColor4f* colors,
                              const SkScalar* positions,
                              int count);
   void SetMatrixAndTiling(const SkMatrix* matrix, SkTileMode tx, SkTileMode ty);
-  void SetFlagsAndFallback(uint32_t flags, SkColor fallback_color);
+  void SetFlagsAndFallback(uint32_t flags, SkColor4f fallback_color);
 
   Type shader_type_ = Type::kShaderCount;
 
@@ -239,7 +239,7 @@
   SkScalar start_radius_ = 0;
   SkTileMode tx_ = SkTileMode::kClamp;
   SkTileMode ty_ = SkTileMode::kClamp;
-  SkColor fallback_color_ = SK_ColorTRANSPARENT;
+  SkColor4f fallback_color_ = SkColors::kTransparent;
   ScalingBehavior scaling_behavior_ = ScalingBehavior::kRasterAtScale;
 
   absl::optional<SkMatrix> local_matrix_;
@@ -260,7 +260,7 @@
   // will be rasterized.
   absl::optional<gfx::SizeF> tile_scale_;
 
-  std::vector<SkColor> colors_;
+  std::vector<SkColor4f> colors_;
   std::vector<SkScalar> positions_;
 
   // Cached intermediates, for Paint objects that may not be thread-safe
diff --git a/cc/test/paint_op_helper.h b/cc/test/paint_op_helper.h
index a417cf59..9f2fa3a 100644
--- a/cc/test/paint_op_helper.h
+++ b/cc/test/paint_op_helper.h
@@ -530,7 +530,8 @@
     str << ", start_radius=" << shader->start_radius_;
     str << ", tx=" << static_cast<unsigned>(shader->tx_);
     str << ", ty=" << static_cast<unsigned>(shader->ty_);
-    str << ", fallback_color=" << shader->fallback_color_;
+    str << ", fallback_color="
+        << PaintOpHelper::SkiaTypeToString(shader->fallback_color_);
     str << ", scaling_behavior=" << EnumToString(shader->scaling_behavior_);
     if (shader->local_matrix_.has_value()) {
       str << ", local_matrix=" << SkiaTypeToString(*shader->local_matrix_);
@@ -556,9 +557,10 @@
       str << "(nil)";
     }
     if (shader->colors_.size() > 0) {
-      str << ", colors=[" << shader->colors_[0];
+      str << ", colors=["
+          << PaintOpHelper::SkiaTypeToString(shader->colors_[0]);
       for (size_t i = 1; i < shader->colors_.size(); ++i) {
-        str << ", " << shader->colors_[i];
+        str << ", " << PaintOpHelper::SkiaTypeToString(shader->colors_[i]);
       }
       str << "]";
     } else {
diff --git a/chrome/android/chrome_test_java_sources.gni b/chrome/android/chrome_test_java_sources.gni
index 68050f89..48a49e9 100644
--- a/chrome/android/chrome_test_java_sources.gni
+++ b/chrome/android/chrome_test_java_sources.gni
@@ -335,8 +335,6 @@
   "javatests/src/org/chromium/chrome/browser/payments/PaymentManifestDownloaderTest.java",
   "javatests/src/org/chromium/chrome/browser/payments/PaymentManifestParserTest.java",
   "javatests/src/org/chromium/chrome/browser/payments/PaymentManifestVerifierTest.java",
-  "javatests/src/org/chromium/chrome/browser/payments/PaymentRequestBillingAddressTest.java",
-  "javatests/src/org/chromium/chrome/browser/payments/PaymentRequestBillingAddressWithoutPhoneTest.java",
   "javatests/src/org/chromium/chrome/browser/payments/PaymentRequestBlobUrlTest.java",
   "javatests/src/org/chromium/chrome/browser/payments/PaymentRequestCanMakePaymentMetricsTest.java",
   "javatests/src/org/chromium/chrome/browser/payments/PaymentRequestCanMakePaymentQueryNoCardTest.java",
@@ -378,7 +376,6 @@
   "javatests/src/org/chromium/chrome/browser/payments/PaymentRequestPaymentAppsSortingTest.java",
   "javatests/src/org/chromium/chrome/browser/payments/PaymentRequestPhoneAndFreeShippingTest.java",
   "javatests/src/org/chromium/chrome/browser/payments/PaymentRequestPhoneTest.java",
-  "javatests/src/org/chromium/chrome/browser/payments/PaymentRequestRemoveBillingAddressTest.java",
   "javatests/src/org/chromium/chrome/browser/payments/PaymentRequestRetryTest.java",
   "javatests/src/org/chromium/chrome/browser/payments/PaymentRequestServiceWorkerPaymentAppTest.java",
   "javatests/src/org/chromium/chrome/browser/payments/PaymentRequestShippingAddressAndOptionTest.java",
diff --git a/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingControllerTest.java b/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingControllerTest.java
index 61845887..1298399 100644
--- a/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingControllerTest.java
+++ b/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingControllerTest.java
@@ -55,7 +55,7 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.UserDataHost;
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.supplier.ObservableSupplierImpl;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.browser.ActivityTabProvider;
@@ -101,7 +101,7 @@
  * Controller tests for the root controller for interactions with the manual filling UI.
  */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(manifest = Config.NONE, shadows = {ShadowRecordHistogram.class})
+@Config(manifest = Config.NONE)
 @EnableFeatures({ChromeFeatureList.AUTOFILL_KEYBOARD_ACCESSORY,
         ChromeFeatureList.AUTOFILL_MANUAL_FALLBACK_ANDROID})
 public class ManualFillingControllerTest {
@@ -295,7 +295,7 @@
 
     @Before
     public void setUp() {
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
         MockitoAnnotations.initMocks(this);
         when(mMockWindow.getActivity()).thenReturn(new WeakReference<>(mMockActivity));
         when(mMockSoftKeyboardDelegate.calculateSoftKeyboardHeight(any())).thenReturn(0);
diff --git a/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/bar_component/KeyboardAccessoryControllerTest.java b/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/bar_component/KeyboardAccessoryControllerTest.java
index 89e1f630..5af6f86 100644
--- a/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/bar_component/KeyboardAccessoryControllerTest.java
+++ b/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/bar_component/KeyboardAccessoryControllerTest.java
@@ -39,7 +39,7 @@
 import org.chromium.base.FeatureList;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.metrics.RecordHistogramJni;
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.task.test.CustomShadowAsyncTask;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.JniMocker;
@@ -69,8 +69,7 @@
  * Controller tests for the keyboard accessory component.
  */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(manifest = Config.NONE,
-        shadows = {CustomShadowAsyncTask.class, ShadowRecordHistogram.class})
+@Config(manifest = Config.NONE, shadows = {CustomShadowAsyncTask.class})
 public class KeyboardAccessoryControllerTest {
     @Rule
     public JniMocker mocker = new JniMocker();
@@ -101,7 +100,7 @@
 
     @Before
     public void setUp() {
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
         MockitoAnnotations.initMocks(this);
         setAutofillFeature(false);
         mocker.mock(RecordHistogramJni.TEST_HOOKS, mMockRecordHistogram);
diff --git a/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/sheet_component/AccessorySheetControllerTest.java b/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/sheet_component/AccessorySheetControllerTest.java
index a7d5efe..8e6319b3 100644
--- a/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/sheet_component/AccessorySheetControllerTest.java
+++ b/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/sheet_component/AccessorySheetControllerTest.java
@@ -32,7 +32,7 @@
 
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.metrics.RecordHistogramJni;
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.task.test.CustomShadowAsyncTask;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.JniMocker;
@@ -49,8 +49,7 @@
  * Controller tests for the keyboard accessory bottom sheet component.
  */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(manifest = Config.NONE,
-        shadows = {CustomShadowAsyncTask.class, ShadowRecordHistogram.class})
+@Config(manifest = Config.NONE, shadows = {CustomShadowAsyncTask.class})
 public class AccessorySheetControllerTest {
     @Rule
     public JniMocker mocker = new JniMocker();
@@ -76,7 +75,7 @@
 
     @Before
     public void setUp() {
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
         MockitoAnnotations.initMocks(this);
         mocker.mock(RecordHistogramJni.TEST_HOOKS, mMockRecordHistogramNatives);
         when(mMockView.getLayoutParams()).thenReturn(new ViewGroup.LayoutParams(0, 0));
diff --git a/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/AddressAccessorySheetControllerTest.java b/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/AddressAccessorySheetControllerTest.java
index f034b3cd..5bc99ba 100644
--- a/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/AddressAccessorySheetControllerTest.java
+++ b/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/AddressAccessorySheetControllerTest.java
@@ -36,7 +36,7 @@
 
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.metrics.RecordHistogramJni;
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.task.test.CustomShadowAsyncTask;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.JniMocker;
@@ -54,8 +54,7 @@
  * Controller tests for the address accessory sheet.
  */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(manifest = Config.NONE,
-        shadows = {CustomShadowAsyncTask.class, ShadowRecordHistogram.class})
+@Config(manifest = Config.NONE, shadows = {CustomShadowAsyncTask.class})
 @Features.EnableFeatures({ChromeFeatureList.AUTOFILL_KEYBOARD_ACCESSORY})
 public class AddressAccessorySheetControllerTest {
     @Rule
@@ -75,7 +74,7 @@
 
     @Before
     public void setUp() {
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
         MockitoAnnotations.initMocks(this);
         mocker.mock(RecordHistogramJni.TEST_HOOKS, mMockRecordHistogramNatives);
         AccessorySheetTabCoordinator.IconProvider.setIconForTesting(mock(Drawable.class));
diff --git a/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/CreditCardAccessorySheetControllerTest.java b/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/CreditCardAccessorySheetControllerTest.java
index e460de8..51816cf 100644
--- a/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/CreditCardAccessorySheetControllerTest.java
+++ b/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/CreditCardAccessorySheetControllerTest.java
@@ -37,7 +37,7 @@
 
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.metrics.RecordHistogramJni;
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.task.test.CustomShadowAsyncTask;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.JniMocker;
@@ -56,8 +56,7 @@
  * Controller tests for the credit card accessory sheet.
  */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(manifest = Config.NONE,
-        shadows = {CustomShadowAsyncTask.class, ShadowRecordHistogram.class})
+@Config(manifest = Config.NONE, shadows = {CustomShadowAsyncTask.class})
 @Features.EnableFeatures(ChromeFeatureList.AUTOFILL_KEYBOARD_ACCESSORY)
 public class CreditCardAccessorySheetControllerTest {
     @Rule
@@ -76,7 +75,7 @@
 
     @Before
     public void setUp() {
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
         MockitoAnnotations.initMocks(this);
         mocker.mock(RecordHistogramJni.TEST_HOOKS, mMockRecordHistogramNatives);
         AccessorySheetTabCoordinator.IconProvider.setIconForTesting(mock(Drawable.class));
diff --git a/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessorySheetControllerTest.java b/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessorySheetControllerTest.java
index 9c13a0e..0422ca1 100644
--- a/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessorySheetControllerTest.java
+++ b/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessorySheetControllerTest.java
@@ -43,7 +43,7 @@
 
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.metrics.RecordHistogramJni;
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.task.test.CustomShadowAsyncTask;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.JniMocker;
@@ -68,8 +68,7 @@
  * Controller tests for the password accessory sheet.
  */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(manifest = Config.NONE,
-        shadows = {CustomShadowAsyncTask.class, ShadowRecordHistogram.class})
+@Config(manifest = Config.NONE, shadows = {CustomShadowAsyncTask.class})
 @Features.EnableFeatures(ChromeFeatureList.AUTOFILL_KEYBOARD_ACCESSORY)
 public class PasswordAccessorySheetControllerTest {
     @Rule
@@ -88,7 +87,7 @@
 
     @Before
     public void setUp() {
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
         MockitoAnnotations.initMocks(this);
         mocker.mock(RecordHistogramJni.TEST_HOOKS, mMockRecordHistogramNatives);
         AccessorySheetTabCoordinator.IconProvider.setIconForTesting(mock(Drawable.class));
diff --git a/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/tab_layout_component/KeyboardAccessoryTabLayoutControllerTest.java b/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/tab_layout_component/KeyboardAccessoryTabLayoutControllerTest.java
index 23a5c78..bd0a41c1 100644
--- a/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/tab_layout_component/KeyboardAccessoryTabLayoutControllerTest.java
+++ b/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/tab_layout_component/KeyboardAccessoryTabLayoutControllerTest.java
@@ -26,7 +26,7 @@
 import org.mockito.MockitoAnnotations;
 import org.robolectric.annotation.Config;
 
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.task.test.CustomShadowAsyncTask;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
@@ -41,8 +41,7 @@
  * Controller tests for the keyboard accessory tab layout component.
  */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(manifest = Config.NONE,
-        shadows = {CustomShadowAsyncTask.class, ShadowRecordHistogram.class})
+@Config(manifest = Config.NONE, shadows = {CustomShadowAsyncTask.class})
 @Features.EnableFeatures(ChromeFeatureList.AUTOFILL_KEYBOARD_ACCESSORY)
 public class KeyboardAccessoryTabLayoutControllerTest {
     @Rule
@@ -66,7 +65,7 @@
 
     @Before
     public void setUp() {
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
         MockitoAnnotations.initMocks(this);
 
         mCoordinator = new KeyboardAccessoryTabLayoutCoordinator();
diff --git a/chrome/android/features/start_surface/javatests/src/org/chromium/chrome/features/start_surface/TabSwitcherAndStartSurfaceLayoutTest.java b/chrome/android/features/start_surface/javatests/src/org/chromium/chrome/features/start_surface/TabSwitcherAndStartSurfaceLayoutTest.java
index eeecd12..22c6b9ac 100644
--- a/chrome/android/features/start_surface/javatests/src/org/chromium/chrome/features/start_surface/TabSwitcherAndStartSurfaceLayoutTest.java
+++ b/chrome/android/features/start_surface/javatests/src/org/chromium/chrome/features/start_surface/TabSwitcherAndStartSurfaceLayoutTest.java
@@ -45,6 +45,7 @@
 
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
 import android.os.Build;
 import android.os.Build.VERSION_CODES;
 import android.support.test.InstrumentationRegistry;
@@ -67,6 +68,7 @@
 import org.hamcrest.Matchers;
 import org.junit.After;
 import org.junit.Before;
+import org.junit.BeforeClass;
 import org.junit.ClassRule;
 import org.junit.Rule;
 import org.junit.Test;
@@ -75,6 +77,7 @@
 import org.chromium.base.Callback;
 import org.chromium.base.GarbageCollectionTestUtils;
 import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.base.test.metrics.HistogramTestRule;
 import org.chromium.base.test.util.ApplicationTestUtils;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.Criteria;
@@ -123,6 +126,7 @@
 import org.chromium.chrome.test.util.browser.Features.EnableFeatures;
 import org.chromium.components.embedder_support.util.UrlUtilities;
 import org.chromium.content_public.browser.LoadUrlParams;
+import org.chromium.content_public.browser.test.NativeLibraryTestUtils;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.content_public.browser.test.util.WebContentsUtils;
 import org.chromium.net.test.EmbeddedTestServer;
@@ -172,6 +176,9 @@
                     .setBugComponent(ChromeRenderTestRule.Component.UI_BROWSER_MOBILE_START)
                     .build();
 
+    @Rule
+    public HistogramTestRule mHistogramTester = new HistogramTestRule();
+
     @SuppressWarnings("FieldCanBeLocal")
     private EmbeddedTestServer mTestServer;
     private TabSwitcherAndStartSurfaceLayout mTabSwitcherAndStartSurfaceLayout;
@@ -182,6 +189,12 @@
             (bitmap) -> mAllBitmaps.add(new WeakReference<>(bitmap));
     private TabSwitcher.TabListDelegate mTabListDelegate;
 
+    @BeforeClass
+    public static void beforeClass() {
+        // Only needs to be loaded once and needs to be loaded before HistogramTestRule.
+        NativeLibraryTestUtils.loadNativeLibraryNoBrowserProcess();
+    }
+
     @Before
     public void setUp() {
         AccessibilityChecks.enable();
@@ -1143,159 +1156,90 @@
     @MediumTest
     @EnableFeatures({ChromeFeatureList.TAB_TO_GTS_ANIMATION + "<Study"})
     @CommandLineFlags.Add({BASE_PARAMS})
-    @DisabledTest(message = "https://crbug.com/1138739")
-    public void testThumbnailFetchingResult_defaultAspectRatio() throws Exception {
-        prepareTabs(2, 0, mUrl);
-        int oldJpegCount = RecordHistogram.getHistogramValueCountForTesting(
-                TabContentManager.UMA_THUMBNAIL_FETCHING_RESULT,
-                TabContentManager.ThumbnailFetchingResult.GOT_JPEG);
-        int oldEtc1Count = RecordHistogram.getHistogramValueCountForTesting(
-                TabContentManager.UMA_THUMBNAIL_FETCHING_RESULT,
-                TabContentManager.ThumbnailFetchingResult.GOT_ETC1);
-        int oldNothingCount = RecordHistogram.getHistogramValueCountForTesting(
-                TabContentManager.UMA_THUMBNAIL_FETCHING_RESULT,
-                TabContentManager.ThumbnailFetchingResult.GOT_NOTHING);
-        int oldDifferentAspectRatioJpegCount = RecordHistogram.getHistogramValueCountForTesting(
-                TabContentManager.UMA_THUMBNAIL_FETCHING_RESULT,
-                TabContentManager.ThumbnailFetchingResult.GOT_DIFFERENT_ASPECT_RATIO_JPEG);
+    public void testThumbnailFetchingResult_liveLayer() throws Exception {
+        prepareTabs(1, 0, "about:blank");
+        enterTabSwitcher(mActivityTestRule.getActivity());
+        // There might be an additional one from capturing thumbnail for the live layer.
+        CriteriaHelper.pollUiThread(
+                () -> Criteria.checkThat(mAllBitmaps.size(), Matchers.greaterThanOrEqualTo(1)));
+
+        assertEquals(0,
+                mHistogramTester.getHistogramValueCount(
+                        TabContentManager.UMA_THUMBNAIL_FETCHING_RESULT,
+                        TabContentManager.ThumbnailFetchingResult.GOT_JPEG));
+        assertEquals(0,
+                mHistogramTester.getHistogramValueCount(
+                        TabContentManager.UMA_THUMBNAIL_FETCHING_RESULT,
+                        TabContentManager.ThumbnailFetchingResult.GOT_ETC1));
+        assertEquals(0,
+                mHistogramTester.getHistogramValueCount(
+                        TabContentManager.UMA_THUMBNAIL_FETCHING_RESULT,
+                        TabContentManager.ThumbnailFetchingResult.GOT_DIFFERENT_ASPECT_RATIO_JPEG));
+        assertEquals(1,
+                mHistogramTester.getHistogramValueCount(
+                        TabContentManager.UMA_THUMBNAIL_FETCHING_RESULT,
+                        TabContentManager.ThumbnailFetchingResult.GOT_NOTHING));
+    }
+
+    @Test
+    @MediumTest
+    @EnableFeatures({ChromeFeatureList.TAB_TO_GTS_ANIMATION + "<Study"})
+    @CommandLineFlags.Add({BASE_PARAMS})
+    public void testThumbnailFetchingResult_jpeg() throws Exception {
+        prepareTabs(1, 0, "about:blank");
+        simulateJpegHasCachedWithDefaultAspectRatio();
 
         enterTabSwitcher(mActivityTestRule.getActivity());
-        int currentJpegCount = RecordHistogram.getHistogramValueCountForTesting(
-                TabContentManager.UMA_THUMBNAIL_FETCHING_RESULT,
-                TabContentManager.ThumbnailFetchingResult.GOT_JPEG);
-        int currentEtc1Count = RecordHistogram.getHistogramValueCountForTesting(
-                TabContentManager.UMA_THUMBNAIL_FETCHING_RESULT,
-                TabContentManager.ThumbnailFetchingResult.GOT_ETC1);
-        int currentNothingCount = RecordHistogram.getHistogramValueCountForTesting(
-                TabContentManager.UMA_THUMBNAIL_FETCHING_RESULT,
-                TabContentManager.ThumbnailFetchingResult.GOT_NOTHING);
-        int currentDifferentAspectRatioJpegCount = RecordHistogram.getHistogramValueCountForTesting(
-                TabContentManager.UMA_THUMBNAIL_FETCHING_RESULT,
-                TabContentManager.ThumbnailFetchingResult.GOT_DIFFERENT_ASPECT_RATIO_JPEG);
+        // There might be an additional one from capturing thumbnail for the live layer.
+        CriteriaHelper.pollUiThread(
+                () -> Criteria.checkThat(mAllBitmaps.size(), Matchers.greaterThanOrEqualTo(1)));
 
-        assertEquals(1, currentJpegCount - oldJpegCount);
-        assertEquals(0, currentEtc1Count - oldEtc1Count);
-        assertEquals(0, currentDifferentAspectRatioJpegCount - oldDifferentAspectRatioJpegCount);
-        // Get thumbnail from a live layer.
-        assertEquals(1, currentNothingCount - oldNothingCount);
-
-        oldJpegCount = currentJpegCount;
-        oldEtc1Count = currentEtc1Count;
-        oldNothingCount = currentNothingCount;
-        oldDifferentAspectRatioJpegCount = currentDifferentAspectRatioJpegCount;
-
-        TabModel currentTabModel =
-                mActivityTestRule.getActivity().getTabModelSelector().getCurrentModel();
-        for (int i = 0; i < currentTabModel.getCount(); i++) {
-            TabUiTestHelper.checkThumbnailsExist(currentTabModel.getTabAt(i));
-        }
-
-        TabUiTestHelper.finishActivity(mActivityTestRule.getActivity());
-        mActivityTestRule.startMainActivityFromLauncher();
-
-        enterTabSwitcher(mActivityTestRule.getActivity());
-        assertEquals(2,
-                RecordHistogram.getHistogramValueCountForTesting(
+        assertEquals(1,
+                mHistogramTester.getHistogramValueCount(
                         TabContentManager.UMA_THUMBNAIL_FETCHING_RESULT,
-                        TabContentManager.ThumbnailFetchingResult.GOT_JPEG)
-                        - oldJpegCount);
+                        TabContentManager.ThumbnailFetchingResult.GOT_JPEG));
         assertEquals(0,
-                RecordHistogram.getHistogramValueCountForTesting(
+                mHistogramTester.getHistogramValueCount(
                         TabContentManager.UMA_THUMBNAIL_FETCHING_RESULT,
-                        TabContentManager.ThumbnailFetchingResult.GOT_ETC1)
-                        - oldEtc1Count);
+                        TabContentManager.ThumbnailFetchingResult.GOT_ETC1));
         assertEquals(0,
-                RecordHistogram.getHistogramValueCountForTesting(
+                mHistogramTester.getHistogramValueCount(
                         TabContentManager.UMA_THUMBNAIL_FETCHING_RESULT,
-                        TabContentManager.ThumbnailFetchingResult.GOT_NOTHING)
-                        - oldNothingCount);
+                        TabContentManager.ThumbnailFetchingResult.GOT_DIFFERENT_ASPECT_RATIO_JPEG));
         assertEquals(0,
-                RecordHistogram.getHistogramValueCountForTesting(
+                mHistogramTester.getHistogramValueCount(
                         TabContentManager.UMA_THUMBNAIL_FETCHING_RESULT,
-                        TabContentManager.ThumbnailFetchingResult.GOT_DIFFERENT_ASPECT_RATIO_JPEG)
-                        - oldDifferentAspectRatioJpegCount);
+                        TabContentManager.ThumbnailFetchingResult.GOT_NOTHING));
     }
 
     @Test
     @MediumTest
     @EnableFeatures({ChromeFeatureList.TAB_TO_GTS_ANIMATION + "<Study"})
     @CommandLineFlags.Add({BASE_PARAMS + "/thumbnail_aspect_ratio/2.0/allow_to_refetch/true"})
-    @DisabledTest(message = "http://crbug/1119527 - Flaky on bots.")
+    @DisabledTest(message = "crbug.com/1315676#c20")
     public void testThumbnailFetchingResult_changingAspectRatio() throws Exception {
-        prepareTabs(2, 0, mUrl);
-        int oldJpegCount = RecordHistogram.getHistogramValueCountForTesting(
-                TabContentManager.UMA_THUMBNAIL_FETCHING_RESULT,
-                TabContentManager.ThumbnailFetchingResult.GOT_JPEG);
-        int oldEtc1Count = RecordHistogram.getHistogramValueCountForTesting(
-                TabContentManager.UMA_THUMBNAIL_FETCHING_RESULT,
-                TabContentManager.ThumbnailFetchingResult.GOT_ETC1);
-        int oldNothingCount = RecordHistogram.getHistogramValueCountForTesting(
-                TabContentManager.UMA_THUMBNAIL_FETCHING_RESULT,
-                TabContentManager.ThumbnailFetchingResult.GOT_NOTHING);
-        int oldDifferentAspectRatioJpegCount = RecordHistogram.getHistogramValueCountForTesting(
-                TabContentManager.UMA_THUMBNAIL_FETCHING_RESULT,
-                TabContentManager.ThumbnailFetchingResult.GOT_DIFFERENT_ASPECT_RATIO_JPEG);
-
+        prepareTabs(1, 0, "about:blank");
+        // Simulate Jpeg has cached with default aspect ratio.
+        simulateJpegHasCachedWithDefaultAspectRatio();
         enterTabSwitcher(mActivityTestRule.getActivity());
-        int currentJpegCount = RecordHistogram.getHistogramValueCountForTesting(
-                TabContentManager.UMA_THUMBNAIL_FETCHING_RESULT,
-                TabContentManager.ThumbnailFetchingResult.GOT_JPEG);
-        int currentEtc1Count = RecordHistogram.getHistogramValueCountForTesting(
-                TabContentManager.UMA_THUMBNAIL_FETCHING_RESULT,
-                TabContentManager.ThumbnailFetchingResult.GOT_ETC1);
-        int currentNothingCount = RecordHistogram.getHistogramValueCountForTesting(
-                TabContentManager.UMA_THUMBNAIL_FETCHING_RESULT,
-                TabContentManager.ThumbnailFetchingResult.GOT_NOTHING);
-        int currentDifferentAspectRatioJpegCount = RecordHistogram.getHistogramValueCountForTesting(
-                TabContentManager.UMA_THUMBNAIL_FETCHING_RESULT,
-                TabContentManager.ThumbnailFetchingResult.GOT_DIFFERENT_ASPECT_RATIO_JPEG);
+        // There might be an additional one from capturing thumbnail for the live layer.
+        CriteriaHelper.pollUiThread(
+                () -> Criteria.checkThat(mAllBitmaps.size(), Matchers.greaterThanOrEqualTo(1)));
 
-        assertEquals(1, currentJpegCount - oldJpegCount);
-        assertEquals(0, currentEtc1Count - oldEtc1Count);
-        assertEquals(0, currentDifferentAspectRatioJpegCount - oldDifferentAspectRatioJpegCount);
-        // Get thumbnail from a live layer.
-        assertEquals(1, currentNothingCount - oldNothingCount);
-
-        oldJpegCount = currentJpegCount;
-        oldEtc1Count = currentEtc1Count;
-        oldNothingCount = currentNothingCount;
-        oldDifferentAspectRatioJpegCount = currentDifferentAspectRatioJpegCount;
-
-        onView(tabSwitcherViewMatcher())
-                .check(ThumbnailAspectRatioAssertion.havingAspectRatio(2.0));
-
-        TabModel currentTabModel =
-                mActivityTestRule.getActivity().getTabModelSelector().getCurrentModel();
-        for (int i = 0; i < currentTabModel.getCount(); i++) {
-            TabUiTestHelper.checkThumbnailsExist(currentTabModel.getTabAt(i));
-        }
-        leaveGTSAndVerifyThumbnailsAreReleased();
-
-        simulateAspectRatioChangedToPoint75();
-        verifyAllThumbnailHasAspectRatio(0.75);
-
-        enterTabSwitcher(mActivityTestRule.getActivity());
         assertEquals(0,
-                RecordHistogram.getHistogramValueCountForTesting(
+                mHistogramTester.getHistogramValueCount(
                         TabContentManager.UMA_THUMBNAIL_FETCHING_RESULT,
-                        TabContentManager.ThumbnailFetchingResult.GOT_JPEG)
-                        - oldJpegCount);
-        assertEquals(2,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        TabContentManager.UMA_THUMBNAIL_FETCHING_RESULT,
-                        TabContentManager.ThumbnailFetchingResult.GOT_DIFFERENT_ASPECT_RATIO_JPEG)
-                        - oldDifferentAspectRatioJpegCount);
+                        TabContentManager.ThumbnailFetchingResult.GOT_JPEG));
         assertEquals(0,
-                RecordHistogram.getHistogramValueCountForTesting(
+                mHistogramTester.getHistogramValueCount(
                         TabContentManager.UMA_THUMBNAIL_FETCHING_RESULT,
-                        TabContentManager.ThumbnailFetchingResult.GOT_ETC1)
-                        - oldEtc1Count);
-        assertEquals(0,
-                RecordHistogram.getHistogramValueCountForTesting(
+                        TabContentManager.ThumbnailFetchingResult.GOT_ETC1));
+        assertEquals(1,
+                mHistogramTester.getHistogramValueCount(
                         TabContentManager.UMA_THUMBNAIL_FETCHING_RESULT,
-                        TabContentManager.ThumbnailFetchingResult.GOT_NOTHING)
-                        - oldNothingCount);
-        onView(tabSwitcherViewMatcher())
+                        TabContentManager.ThumbnailFetchingResult.GOT_DIFFERENT_ASPECT_RATIO_JPEG));
+
+        onViewWaiting(tabSwitcherViewMatcher())
                 .check(ThumbnailAspectRatioAssertion.havingAspectRatio(2.0));
     }
 
@@ -1971,6 +1915,7 @@
     private void assertThumbnailsAreReleased() {
         // Could not directly assert canAllBeGarbageCollected() because objects can be in Cleaner.
         CriteriaHelper.pollUiThread(() -> canAllBeGarbageCollected(mAllBitmaps));
+        mAllBitmaps.clear();
     }
 
     private boolean canAllBeGarbageCollected(List<WeakReference<Bitmap>> bitmaps) {
@@ -1982,6 +1927,18 @@
         return true;
     }
 
+    private void simulateJpegHasCachedWithDefaultAspectRatio() throws IOException {
+        TabModel currentModel = mActivityTestRule.getActivity().getCurrentTabModel();
+        int jpegWidth = 125;
+        int jpegHeight = (int) (jpegWidth * 1.0
+                / TabUtils.getTabThumbnailAspectRatio(mActivityTestRule.getActivity()));
+        for (int i = 0; i < currentModel.getCount(); i++) {
+            Tab tab = currentModel.getTabAt(i);
+            Bitmap bitmap = Bitmap.createBitmap(jpegWidth, jpegHeight, Config.ARGB_8888);
+            encodeJpeg(tab, bitmap);
+        }
+    }
+
     private void simulateAspectRatioChangedToPoint75() throws IOException {
         TabModel currentModel = mActivityTestRule.getActivity().getCurrentTabModel();
         for (int i = 0; i < currentModel.getCount(); i++) {
diff --git a/chrome/android/features/start_surface/junit/src/org/chromium/chrome/features/start_surface/StartSurfaceCoordinatorUnitTest.java b/chrome/android/features/start_surface/junit/src/org/chromium/chrome/features/start_surface/StartSurfaceCoordinatorUnitTest.java
index 1ae13a6..07a6c5a 100644
--- a/chrome/android/features/start_surface/junit/src/org/chromium/chrome/features/start_surface/StartSurfaceCoordinatorUnitTest.java
+++ b/chrome/android/features/start_surface/junit/src/org/chromium/chrome/features/start_surface/StartSurfaceCoordinatorUnitTest.java
@@ -20,7 +20,8 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.TimeUtils;
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.browser.ChromeInactivityTracker;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
@@ -32,7 +33,7 @@
 
 /** Tests for {@link StartSurfaceCoordinator}. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(manifest = Config.NONE, shadows = {ShadowRecordHistogram.class})
+@Config(manifest = Config.NONE)
 @Features.EnableFeatures(ChromeFeatureList.START_SURFACE_ANDROID)
 @Features.DisableFeatures({ChromeFeatureList.WEB_FEED, ChromeFeatureList.FEED_INTERACTIVE_REFRESH,
         ChromeFeatureList.SHOPPING_LIST})
@@ -50,7 +51,7 @@
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
         mCoordinator = mTestRule.getCoordinator();
     }
 
@@ -131,7 +132,7 @@
         Assert.assertFalse(manager.readBoolean(ChromePreferenceKeys.START_SHOW_ON_STARTUP, false));
         Assert.assertEquals(1, manager.readInt(ChromePreferenceKeys.TAP_MV_TILES_COUNT, 0));
         Assert.assertEquals(0,
-                ShadowRecordHistogram.getHistogramTotalCountForTesting(
+                RecordHistogram.getHistogramTotalCountForTesting(
                         "Startup.Android.ShowChromeStartSegmentationResultComparison"));
 
         // Verifies if the next decision time past and the clicks of MV tiles is higher than the
@@ -145,7 +146,7 @@
                 manager, StartSurfaceConfiguration.NUM_DAYS_KEEP_SHOW_START_AT_STARTUP.getValue());
         Assert.assertEquals(0, manager.readInt(ChromePreferenceKeys.TAP_MV_TILES_COUNT, 0));
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Startup.Android.ShowChromeStartSegmentationResultComparison",
                         ReturnToChromeUtil.ShowChromeStartSegmentationResultComparison
                                 .SEGMENTATION_DISABLED_LOGIC_ENABLED));
@@ -163,7 +164,7 @@
         verifyNextDecisionTimeStampInDays(
                 manager, StartSurfaceConfiguration.NUM_DAYS_USER_CLICK_BELOW_THRESHOLD.getValue());
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Startup.Android.ShowChromeStartSegmentationResultComparison",
                         ReturnToChromeUtil.ShowChromeStartSegmentationResultComparison
                                 .SEGMENTATION_ENABLED_LOGIC_DISABLED));
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinder.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinder.java
index a1502bd..6b231c9 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinder.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinder.java
@@ -112,7 +112,6 @@
         } else if (TabProperties.IS_SELECTED == propertyKey) {
             updateColor(view, model.get(TabProperties.IS_INCOGNITO),
                     model.get(TabProperties.IS_SELECTED));
-            updateThumbnail(view, model);
             updateFavicon(view, model);
             if (TabUiFeatureUtilities.ENABLE_SEARCH_CHIP.getValue()) {
                 ChipView pageInfoButton = (ChipView) view.fastFindViewById(R.id.page_info_button);
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java
index 76ff8c6..5955f92 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java
@@ -529,6 +529,8 @@
 
     private View.AccessibilityDelegate mAccessibilityDelegate;
 
+    private int mLastSelectedTabListModelIndex = TabList.INVALID_TAB_INDEX;
+
     /**
      * Interface for implementing a {@link Runnable} that takes a tabId for a generic action.
      */
@@ -585,14 +587,24 @@
             public void didSelectTab(Tab tab, int type, int lastId) {
                 mNextTabId = Tab.INVALID_TAB_ID;
                 if (tab.getId() == lastId) return;
+
                 int oldIndex = mModel.indexFromId(lastId);
+                mLastSelectedTabListModelIndex = oldIndex;
                 if (oldIndex != TabModel.INVALID_TAB_INDEX) {
                     mModel.get(oldIndex).model.set(TabProperties.IS_SELECTED, false);
+                    if (mActionsOnAllRelatedTabs && mThumbnailProvider != null && mVisible) {
+                        mModel.get(oldIndex).model.set(TabProperties.THUMBNAIL_FETCHER,
+                                new ThumbnailFetcher(mThumbnailProvider, lastId, true, false));
+                    }
                 }
+
                 int newIndex = mModel.indexFromId(tab.getId());
                 if (newIndex == TabModel.INVALID_TAB_INDEX) return;
-
                 mModel.get(newIndex).model.set(TabProperties.IS_SELECTED, true);
+                if (mThumbnailProvider != null && mVisible) {
+                    mModel.get(newIndex).model.set(TabProperties.THUMBNAIL_FETCHER,
+                            new ThumbnailFetcher(mThumbnailProvider, tab.getId(), true, false));
+                }
             }
 
             @Override
@@ -1066,7 +1078,8 @@
     }
 
     private int getIndexOfTab(Tab tab, boolean onlyShowRelatedTabs) {
-        int index;
+        int index = TabList.INVALID_TAB_INDEX;
+        if (tab == null) return index;
         if (onlyShowRelatedTabs) {
             if (mModel.size() == 0) return TabList.INVALID_TAB_INDEX;
             List<Tab> related = getRelatedTabsForId(mModel.get(0).model.get(TabProperties.TAB_ID));
@@ -1178,6 +1191,8 @@
             return true;
         }
         mModel.set(new ArrayList<>());
+        mLastSelectedTabListModelIndex = TabList.INVALID_TAB_INDEX;
+
         if (tabsList == null) {
             return true;
         }
@@ -1271,12 +1286,15 @@
 
         updateFaviconForTab(pseudoTab, null);
         boolean forceUpdate = isSelected && !quickMode;
+        boolean forceUpdateLastSelected =
+                mActionsOnAllRelatedTabs && index == mLastSelectedTabListModelIndex && !quickMode;
+
         if (mThumbnailProvider != null && mVisible
                 && (mModel.get(index).model.get(TabProperties.THUMBNAIL_FETCHER) == null
-                        || forceUpdate || isUpdatingId)) {
-            ThumbnailFetcher callback =
-                    new ThumbnailFetcher(mThumbnailProvider, pseudoTab.getId(), forceUpdate,
-                            forceUpdate && !TabUiFeatureUtilities.isTabToGtsAnimationEnabled());
+                        || forceUpdate || isUpdatingId || forceUpdateLastSelected)) {
+            ThumbnailFetcher callback = new ThumbnailFetcher(mThumbnailProvider, pseudoTab.getId(),
+                    forceUpdate || forceUpdateLastSelected,
+                    forceUpdate && !TabUiFeatureUtilities.isTabToGtsAnimationEnabled());
             mModel.get(index).model.set(TabProperties.THUMBNAIL_FETCHER, callback);
         }
     }
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/ConditionalTabStripUtilsUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/ConditionalTabStripUtilsUnitTest.java
index b5be6c8d..e683156 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/ConditionalTabStripUtilsUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/ConditionalTabStripUtilsUnitTest.java
@@ -21,7 +21,6 @@
 import org.chromium.base.ContextUtils;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.metrics.RecordHistogramJni;
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.JniMocker;
 import org.chromium.chrome.browser.tasks.ConditionalTabStripUtils;
@@ -33,7 +32,7 @@
  * Tests for {@link org.chromium.chrome.browser.tasks.ConditionalTabStripUtils}.
  */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(manifest = Config.NONE, shadows = {ShadowRecordHistogram.class})
+@Config(manifest = Config.NONE)
 public class ConditionalTabStripUtilsUnitTest {
     @Rule
     public TestRule mProcessor = new Features.JUnitProcessor();
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java
index 810431e..9faf173 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java
@@ -91,7 +91,7 @@
 import org.chromium.base.ContextUtils;
 import org.chromium.base.FeatureList;
 import org.chromium.base.metrics.RecordHistogram;
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.JniMocker;
 import org.chromium.build.BuildConfig;
@@ -135,6 +135,7 @@
 import org.chromium.chrome.browser.tasks.tab_management.TabListCoordinator.TabListMode;
 import org.chromium.chrome.browser.tasks.tab_management.TabListFaviconProvider.TabFavicon;
 import org.chromium.chrome.browser.tasks.tab_management.TabListMediator.ShoppingPersistedTabDataFetcher;
+import org.chromium.chrome.browser.tasks.tab_management.TabListMediator.ThumbnailFetcher;
 import org.chromium.chrome.browser.tasks.tab_management.TabProperties.UiType;
 import org.chromium.chrome.features.start_surface.StartSurfaceConfiguration;
 import org.chromium.chrome.tab_ui.R;
@@ -174,7 +175,7 @@
 @SuppressWarnings(
         {"ArraysAsListWithZeroOrOneArgument", "ResultOfMethodCallIgnored", "ConstantConditions"})
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(manifest = Config.NONE, shadows = {ShadowRecordHistogram.class},
+@Config(manifest = Config.NONE,
         instrumentedPackages =
                 {
                         "androidx.recyclerview.widget.RecyclerView" // required to mock final
@@ -368,6 +369,11 @@
 
         doReturn(mTabModelFilterProvider).when(mTabModelSelector).getTabModelFilterProvider();
         doReturn(mTabModelFilter).when(mTabModelFilterProvider).getCurrentTabModelFilter();
+        doReturn(2).when(mTabModelFilter).getCount();
+        doReturn(mTab1).when(mTabModelFilter).getTabAt(POSITION1);
+        doReturn(mTab2).when(mTabModelFilter).getTabAt(POSITION2);
+        doReturn(tabs1).when(mTabModelFilter).getRelatedTabList(TAB1_ID);
+        doReturn(tabs2).when(mTabModelFilter).getRelatedTabList(TAB2_ID);
         doReturn(mTab1).when(mTabModelSelector).getCurrentTab();
         doReturn(TAB1_ID).when(mTabModelSelector).getCurrentTabId();
         doNothing()
@@ -907,12 +913,43 @@
     public void tabSelection() {
         initAndAssertAllProperties();
 
+        ThumbnailFetcher tab1Fetcher = mModel.get(0).model.get(TabProperties.THUMBNAIL_FETCHER);
+        ThumbnailFetcher tab2Fetcher = mModel.get(1).model.get(TabProperties.THUMBNAIL_FETCHER);
+
         mTabModelObserverCaptor.getValue().didSelectTab(
                 mTab2, TabLaunchType.FROM_CHROME_UI, TAB1_ID);
 
         assertThat(mModel.size(), equalTo(2));
         assertThat(mModel.get(0).model.get(TabProperties.IS_SELECTED), equalTo(false));
+        assertThat(mModel.get(0).model.get(TabProperties.THUMBNAIL_FETCHER), equalTo(tab1Fetcher));
         assertThat(mModel.get(1).model.get(TabProperties.IS_SELECTED), equalTo(true));
+        assertNotEquals(tab2Fetcher, mModel.get(1).model.get(TabProperties.THUMBNAIL_FETCHER));
+    }
+
+    @Test
+    public void tabSelection_updatePreviousSelectedTabThumbnailFetcher() {
+        mMediator = new TabListMediator(mActivity, mModel, TabListMode.GRID, mTabModelSelector,
+                mTabContentManager::getTabThumbnailWithCallback, mTitleProvider,
+                mTabListFaviconProvider, true, null, mGridCardOnClickListenerProvider, null, null,
+                getClass().getSimpleName(), UiType.CLOSABLE);
+        mMediator.initWithNative(mProfile);
+        // mTabModelObserverCaptor captures on every initWithNative calls. There is one
+        // initWithNative call in the setup already.
+        verify(mTabModelFilterProvider, times(2))
+                .addTabModelFilterObserver(mTabModelObserverCaptor.capture());
+        initAndAssertAllProperties();
+
+        ThumbnailFetcher tab1Fetcher = mModel.get(0).model.get(TabProperties.THUMBNAIL_FETCHER);
+        ThumbnailFetcher tab2Fetcher = mModel.get(1).model.get(TabProperties.THUMBNAIL_FETCHER);
+
+        mTabModelObserverCaptor.getValue().didSelectTab(
+                mTab2, TabLaunchType.FROM_CHROME_UI, TAB1_ID);
+
+        assertThat(mModel.size(), equalTo(2));
+        assertThat(mModel.get(0).model.get(TabProperties.IS_SELECTED), equalTo(false));
+        assertNotEquals(tab1Fetcher, mModel.get(0).model.get(TabProperties.THUMBNAIL_FETCHER));
+        assertThat(mModel.get(1).model.get(TabProperties.IS_SELECTED), equalTo(true));
+        assertNotEquals(tab2Fetcher, mModel.get(1).model.get(TabProperties.THUMBNAIL_FETCHER));
     }
 
     @Test
@@ -3081,7 +3118,7 @@
 
     @Test
     public void testRecordPriceAnnotationsEnabledMetrics() {
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
         setPriceTrackingEnabledForTesting(true);
         PriceTrackingFeatures.setIsSignedInAndSyncEnabledForTesting(true);
         mMediator.setActionOnAllRelatedTabsForTesting(true);
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediatorUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediatorUnitTest.java
index 3e205ec6..d64fb3be 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediatorUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediatorUnitTest.java
@@ -42,7 +42,8 @@
 import org.robolectric.annotation.LooperMode;
 
 import org.chromium.base.UserDataHost;
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.supplier.ObservableSupplierImpl;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.browser.browser_controls.BrowserControlsStateProvider;
@@ -79,7 +80,7 @@
  */
 @SuppressWarnings({"ResultOfMethodCallIgnored", "ArraysAsListWithZeroOrOneArgument", "unchecked"})
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(manifest = Config.NONE, shadows = {ShadowRecordHistogram.class})
+@Config(manifest = Config.NONE)
 @LooperMode(LooperMode.Mode.LEGACY)
 @DisableFeatures(
         {ChromeFeatureList.TAB_SWITCHER_ON_RETURN, ChromeFeatureList.START_SURFACE_ANDROID})
@@ -157,7 +158,7 @@
 
     @Before
     public void setUp() {
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
 
         MockitoAnnotations.initMocks(this);
 
@@ -262,10 +263,10 @@
         assertThat(mModel.get(TabListContainerProperties.IS_VISIBLE), equalTo(true));
         assertThat(mMediator.overviewVisible(), equalTo(true));
 
-        assertThat(ShadowRecordHistogram.getHistogramValueCountForTesting(
+        assertThat(RecordHistogram.getHistogramValueCountForTesting(
                            TabSwitcherMediator.TAB_COUNT_HISTOGRAM, 3),
                 equalTo(1));
-        assertThat(ShadowRecordHistogram.getHistogramValueCountForTesting(
+        assertThat(RecordHistogram.getHistogramValueCountForTesting(
                            TabSwitcherMediator.TAB_ENTRIES_HISTOGRAM, 3),
                 equalTo(1));
     }
@@ -291,10 +292,10 @@
         assertThat(mModel.get(TabListContainerProperties.IS_VISIBLE), equalTo(true));
         assertThat(mMediator.overviewVisible(), equalTo(true));
 
-        assertThat(ShadowRecordHistogram.getHistogramValueCountForTesting(
+        assertThat(RecordHistogram.getHistogramValueCountForTesting(
                            TabSwitcherMediator.TAB_COUNT_HISTOGRAM, 3),
                 equalTo(1));
-        assertThat(ShadowRecordHistogram.getHistogramValueCountForTesting(
+        assertThat(RecordHistogram.getHistogramValueCountForTesting(
                            TabSwitcherMediator.TAB_ENTRIES_HISTOGRAM, 2),
                 equalTo(1));
     }
diff --git a/chrome/android/java/res/menu/history_manager_menu.xml b/chrome/android/java/res/menu/history_manager_menu.xml
index 6d8cdb3..d566968d 100644
--- a/chrome/android/java/res/menu/history_manager_menu.xml
+++ b/chrome/android/java/res/menu/history_manager_menu.xml
@@ -28,6 +28,11 @@
             app:showAsAction="ifRoom"
             app:iconTint="@color/default_icon_color_secondary_tint_list" />
      </group>
+        <item
+            android:id="@+id/optout_menu_id"
+            android:title="@string/history_clusters_disable_menu_item_label"
+            android:visible="false"
+            app:showAsAction="never" />
      <group
          android:id="@+id/selection_mode_menu_group"
          android:visible="false" >
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
index a8c1599..9563b7a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -2428,7 +2428,9 @@
                 getCurrentTabModel().closeTab(tabToClose, nextTab, false, true, false);
 
                 // If there is no next tab to open, enter overview mode.
-                if (nextTab == null) showOverview(StartSurfaceState.SHOWING_START);
+                if (nextTab == null && !isActivityFinishingOrDestroyed()) {
+                    showOverview(StartSurfaceState.SHOWING_START);
+                }
             }, CLOSE_TAB_ON_MINIMIZE_DELAY_MS);
         }
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/content/TabContentManager.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/content/TabContentManager.java
index 01e64ba0..4caf3de 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/content/TabContentManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/content/TabContentManager.java
@@ -491,6 +491,7 @@
                 }
                 if (jpeg != null) {
                     if (ALLOW_TO_REFETCH_TAB_THUMBNAIL_VARIATION.getValue()) {
+                        // TODO(crbug.com/1344354): compare the height instead of pixel tolerance.
                         double jpegAspectRatio = jpeg.getHeight() == 0
                                 ? 0
                                 : 1.0 * jpeg.getWidth() / jpeg.getHeight();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsConnection.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsConnection.java
index 11a61b5..62990d3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsConnection.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsConnection.java
@@ -165,9 +165,6 @@
     private static final String ON_RESIZED_CALLBACK = "onResized";
     private static final String ON_RESIZED_SIZE_EXTRA = "size";
 
-    private static final String ON_VERTICAL_SCROLL_EVENT_CALLBACK = "onVerticalScrollEvent";
-    private static final String ON_VERTICAL_SCROLL_EVENT_IS_DIRECTION_UP_EXTRA = "isDirectionUp";
-
     @IntDef({ParallelRequestStatus.NO_REQUEST, ParallelRequestStatus.SUCCESS,
             ParallelRequestStatus.FAILURE_NOT_INITIALIZED,
             ParallelRequestStatus.FAILURE_NOT_AUTHORIZED, ParallelRequestStatus.FAILURE_INVALID_URL,
@@ -1143,22 +1140,6 @@
     }
 
     /**
-     * Notifies the application of a vertical scroll event, i.e. when a scroll started or changed
-     * direction.
-     *
-     * @param session The Binder object identifying the session.
-     * @param isDirectionUp Whether the scroll direction is up.
-     */
-    public void notifyVerticalScrollEvent(CustomTabsSessionToken session, boolean isDirectionUp) {
-        Bundle args = new Bundle();
-        args.putBoolean(ON_VERTICAL_SCROLL_EVENT_IS_DIRECTION_UP_EXTRA, isDirectionUp);
-
-        if (safeExtraCallback(session, ON_VERTICAL_SCROLL_EVENT_CALLBACK, args)) {
-            logCallback("extraCallback(" + ON_VERTICAL_SCROLL_EVENT_CALLBACK + ")", args);
-        }
-    }
-
-    /**
      * Notifies the application of a navigation event.
      *
      * Delivers the {@link CustomTabsCallback#onNavigationEvent} callback to the application.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabController.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabController.java
index f2fbc84..5887ede 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabController.java
@@ -40,7 +40,6 @@
 import org.chromium.chrome.browser.customtabs.PageLoadMetricsObserver;
 import org.chromium.chrome.browser.customtabs.ReparentingTaskProvider;
 import org.chromium.chrome.browser.dependency_injection.ActivityScope;
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.lifecycle.InflationObserver;
 import org.chromium.chrome.browser.profiles.Profile;
@@ -58,8 +57,6 @@
 import org.chromium.chrome.browser.tabmodel.TabModelSelectorBase;
 import org.chromium.chrome.browser.tabmodel.TabReparentingParams;
 import org.chromium.chrome.browser.translate.TranslateBridge;
-import org.chromium.content_public.browser.GestureListenerManager;
-import org.chromium.content_public.browser.GestureStateListener;
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.ui.base.ActivityWindowAndroid;
 
@@ -114,9 +111,6 @@
     private final CustomTabsSessionToken mSession;
     private final Intent mIntent;
 
-    private GestureStateListener mGestureStateListener;
-    private ScrollState mScrollState;
-
     @Inject
     public CustomTabActivityTabController(AppCompatActivity activity,
             Lazy<CustomTabDelegateFactory> customTabDelegateFactory,
@@ -417,24 +411,6 @@
                 public void onContentChanged(Tab tab) {
                     if (tab.getWebContents() != null) {
                         mConnection.setClientDataHeaderForNewTab(mSession, tab.getWebContents());
-
-                        if (ChromeFeatureList.isEnabled(
-                                    ChromeFeatureList.CCT_REAL_TIME_ENGAGEMENT_SIGNALS)) {
-                            maybeStartSendingRealTimeEngagementSignals(tab);
-                        }
-                    }
-                }
-
-                @Override
-                public void webContentsWillSwap(Tab tab) {
-                    if (ChromeFeatureList.isEnabled(
-                                ChromeFeatureList.CCT_REAL_TIME_ENGAGEMENT_SIGNALS)) {
-                        if (tab.getWebContents() != null) {
-                            if (mGestureStateListener != null) {
-                                GestureListenerManager.fromWebContents(tab.getWebContents())
-                                        .removeListener(mGestureStateListener);
-                            }
-                        }
                     }
                 }
             };
@@ -488,63 +464,4 @@
 
         tab.addObserver(mediaObserver);
     }
-
-    /**
-     * Create |mScrollState| and |mGestureStateListener| and start sending real-time engagement
-     * signals through {@link androidx.browser.customtabs.CustomTabsCallback}.
-     */
-    private void maybeStartSendingRealTimeEngagementSignals(Tab tab) {
-        assert tab.getWebContents() != null;
-
-        if (mScrollState == null) mScrollState = new ScrollState();
-        if (mGestureStateListener == null) {
-            mGestureStateListener = new GestureStateListener() {
-                @Override
-                public void onScrollStarted(
-                        int scrollOffsetY, int scrollExtentY, boolean isDirectionUp) {
-                    mScrollState.onScrollStarted(isDirectionUp);
-                }
-
-                @Override
-                public void onVerticalScrollDirectionChanged(
-                        boolean directionUp, float currentScrollRatio) {
-                    mScrollState.onScrollDirectionChanged(directionUp);
-                }
-
-                @Override
-                public void onScrollEnded(int scrollOffsetY, int scrollExtentY) {
-                    mScrollState.onScrollEnded();
-                }
-            };
-        }
-
-        GestureListenerManager gestureListenerManager =
-                GestureListenerManager.fromWebContents(tab.getWebContents());
-        if (!gestureListenerManager.hasListener(mGestureStateListener)) {
-            gestureListenerManager.addListener(mGestureStateListener);
-        }
-    }
-
-    private class ScrollState {
-        boolean mIsScrollActive;
-        boolean mIsDirectionUp;
-
-        void onScrollStarted(boolean isDirectionUp) {
-            assert !mIsScrollActive;
-            mIsScrollActive = true;
-            mIsDirectionUp = isDirectionUp;
-            mConnection.notifyVerticalScrollEvent(mSession, mIsDirectionUp);
-        }
-
-        void onScrollDirectionChanged(boolean isDirectionUp) {
-            if (mIsScrollActive && isDirectionUp != mIsDirectionUp) {
-                mIsDirectionUp = isDirectionUp;
-                mConnection.notifyVerticalScrollEvent(mSession, mIsDirectionUp);
-            }
-        }
-
-        void onScrollEnded() {
-            mIsScrollActive = false;
-        }
-    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryAdapter.java
index 5f2d1f80..933cb506 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryAdapter.java
@@ -18,6 +18,7 @@
 import androidx.recyclerview.widget.RecyclerView.ViewHolder;
 
 import org.chromium.base.Function;
+import org.chromium.base.supplier.ObservableSupplier;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.history.HistoryProvider.BrowsingHistoryObserver;
 import org.chromium.chrome.browser.ui.favicon.FaviconHelper.DefaultFaviconHelper;
@@ -66,17 +67,19 @@
     private String mHostName;
 
     private boolean mDisableScrollToLoadForTest;
-    private boolean mShowHistoryToggle;
+    private ObservableSupplier<Boolean> mShowHistoryToggleSupplier;
     private Function<ViewGroup, ViewGroup> mToggleViewFactory;
 
     public HistoryAdapter(HistoryContentManager manager, HistoryProvider provider,
-            boolean showHistoryToggle, Function<ViewGroup, ViewGroup> toggleViewFactory) {
+            ObservableSupplier<Boolean> showHistoryToggleSupplier,
+            Function<ViewGroup, ViewGroup> toggleViewFactory) {
         mToggleViewFactory = toggleViewFactory;
         setHasStableIds(true);
         mHistoryProvider = provider;
         mHistoryProvider.setObserver(this);
         mManager = manager;
-        mShowHistoryToggle = showHistoryToggle;
+        mShowHistoryToggleSupplier = showHistoryToggleSupplier;
+        mShowHistoryToggleSupplier.addObserver((unused) -> setHeaders());
         mFaviconHelper = new DefaultFaviconHelper();
         mItemViews = new ArrayList<>();
     }
@@ -375,7 +378,9 @@
         ArrayList<HeaderItem> args = new ArrayList<>();
         if (mPrivacyDisclaimersVisible) args.add(mPrivacyDisclaimerHeaderItem);
         if (mClearBrowsingDataButtonVisible) args.add(mClearBrowsingDataButtonHeaderItem);
-        if (mShowHistoryToggle && mHistoryToggleHeaderItem != null) {
+        boolean showHistoryToggle =
+                mShowHistoryToggleSupplier.get() != null && mShowHistoryToggleSupplier.get();
+        if (showHistoryToggle && mHistoryToggleHeaderItem != null) {
             args.add(mHistoryToggleHeaderItem);
         }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryContentManager.java b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryContentManager.java
index 2f9269de..5019a88 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryContentManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryContentManager.java
@@ -26,6 +26,7 @@
 import org.chromium.base.ContextUtils;
 import org.chromium.base.Function;
 import org.chromium.base.IntentUtils;
+import org.chromium.base.supplier.ObservableSupplier;
 import org.chromium.base.supplier.Supplier;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ActivityUtils;
@@ -125,6 +126,8 @@
      *         unselectable items.
      * @param tabSupplier Supplies the current tab, null if the history UI will be shown in a
      *                    separate activity.
+     * @param showHistoryToggleSupplier A supplier that tells us if and when we should show the
+     *         toggle that swaps between the Journeys and regular history UIs.
      * @param toggleViewFactory Function that provides a toggle view container for the given parent
      *         ViewGroup. This toggle is used to switch between the Journeys UI and the regular
      *         history UI and is thus controlled by our parent component.
@@ -134,7 +137,8 @@
             boolean isSeparateActivity, boolean isIncognito, boolean shouldShowPrivacyDisclaimers,
             boolean shouldShowClearDataIfAvailable, @Nullable String hostName,
             @Nullable SelectionDelegate<HistoryItem> selectionDelegate,
-            @Nullable Supplier<Tab> tabSupplier, boolean showHistoryToggle,
+            @Nullable Supplier<Tab> tabSupplier,
+            ObservableSupplier<Boolean> showHistoryToggleSupplier,
             Function<ViewGroup, ViewGroup> toggleViewFactory, HistoryProvider historyProvider) {
         mActivity = activity;
         mObserver = observer;
@@ -170,8 +174,8 @@
         // explicitly redirects to use regular profile for Incognito case.
         Profile profile = Profile.getLastUsedRegularProfile();
         mHistoryAdapter = new HistoryAdapter(this,
-                sProviderForTests != null ? sProviderForTests : historyProvider, showHistoryToggle,
-                toggleViewFactory);
+                sProviderForTests != null ? sProviderForTests : historyProvider,
+                showHistoryToggleSupplier, toggleViewFactory);
 
         // Create a recycler view.
         mRecyclerView =
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManager.java b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManager.java
index e06d2ff..457e436 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManager.java
@@ -55,8 +55,10 @@
 import org.chromium.components.browser_ui.widget.selectable_list.SelectableListToolbar.SearchDelegate;
 import org.chromium.components.browser_ui.widget.selectable_list.SelectionDelegate;
 import org.chromium.components.browser_ui.widget.selectable_list.SelectionDelegate.SelectionObserver;
+import org.chromium.components.prefs.PrefService;
 import org.chromium.components.profile_metrics.BrowserProfileType;
 import org.chromium.components.search_engines.TemplateUrl;
+import org.chromium.components.user_prefs.UserPrefs;
 import org.chromium.ui.base.Clipboard;
 import org.chromium.url.GURL;
 
@@ -69,6 +71,7 @@
                                        SearchDelegate, SnackbarController,
                                        HistoryContentManager.Observer {
     private static final String METRICS_PREFIX = "Android.HistoryPage.";
+    static final String HISTORY_CLUSTERS_VISIBLE_PREF = "history_clusters.visible";
 
     // Keep consistent with the UMA constants on the WebUI history page (history/constants.js).
     private static final int UMA_MAX_BUCKET_VALUE = 1000;
@@ -86,6 +89,8 @@
     private final boolean mIsIncognito;
     private final boolean mIsSeparateActivity;
     private final HistoryProvider mHistoryProvider;
+    private final ObservableSupplierImpl<Boolean> mShowHistoryClustersToggleSupplier =
+            new ObservableSupplierImpl<>();
     private ViewGroup mRootView;
     private ViewGroup mContentView;
     private SelectableListLayout<HistoryItem> mSelectableListLayout;
@@ -99,6 +104,7 @@
             new ObservableSupplierImpl<>();
     private final ObservableSupplierImpl<Boolean> mShouldShowClearBrowsingDataSupplier =
             new ObservableSupplierImpl<>();
+    private final PrefService mPrefService;
 
     private boolean mIsSearching;
 
@@ -126,6 +132,7 @@
         mSnackbarManager = snackbarManager;
         mIsIncognito = isIncognito;
         mHistoryProvider = historyProvider;
+        mPrefService = UserPrefs.get(Profile.getLastUsedRegularProfile());
 
         recordUserAction("Show");
         // If incognito placeholder is shown, we don't need to create History UI elements.
@@ -228,6 +235,11 @@
                 public String getSearchEmptyString() {
                     return HistoryManager.this.getSearchEmptyString();
                 }
+
+                @Override
+                public void onOptOut() {
+                    onHistoryClustersOptOutChanged(false);
+                }
             };
 
             mHistoryClustersCoordinator =
@@ -247,7 +259,8 @@
                 ChromePreferenceKeys.HISTORY_SHOW_HISTORY_INFO, true);
         mContentManager = new HistoryContentManager(mActivity, this, isSeparateActivity,
                 isIncognito, shouldShowInfoHeader, /* shouldShowClearData */ true,
-                /* hostName */ null, mSelectionDelegate, tabSupplier, historyClustersEnabled,
+                /* hostName */ null, mSelectionDelegate, tabSupplier,
+                mShowHistoryClustersToggleSupplier,
                 (vg) -> buildToggleView(vg, HISTORY_TAB_INDEX), historyProvider);
         mSelectableListLayout.initializeRecyclerView(
                 mContentManager.getAdapter(), mContentManager.getRecyclerView());
@@ -263,6 +276,18 @@
         mToolbar.initializeSearchView(this, R.string.history_manager_search, R.id.search_menu_id);
         mToolbar.setInfoMenuItem(R.id.info_menu_id);
         mToolbar.updateInfoMenuItem(shouldShowInfoButton(), shouldShowInfoHeaderIfAvailable());
+        if (historyClustersEnabled) {
+            boolean historyClustersVisible = mPrefService.getBoolean(HISTORY_CLUSTERS_VISIBLE_PREF);
+            mShowHistoryClustersToggleSupplier.set(historyClustersVisible);
+            mToolbar.getMenu()
+                    .findItem(R.id.optout_menu_id)
+                    .setVisible(true)
+                    .setTitle(historyClustersVisible
+                                    ? R.string.history_clusters_disable_menu_item_label
+                                    : R.string.history_clusters_enable_menu_item_label);
+        } else {
+            mToolbar.getMenu().removeItem(R.id.optout_menu_id);
+        }
 
         // 4. Width constrain the SelectableListLayout.
         mSelectableListLayout.configureWideDisplayStyle();
@@ -285,6 +310,24 @@
         mRootView.addView(mContentView);
     }
 
+    private void onHistoryClustersOptOutChanged(boolean isVisible) {
+        mPrefService.setBoolean(HISTORY_CLUSTERS_VISIBLE_PREF, isVisible);
+        if (isVisible) {
+            mToolbar.getMenu()
+                    .findItem(R.id.optout_menu_id)
+                    .setTitle(R.string.history_clusters_disable_menu_item_label);
+            mShowHistoryClustersToggleSupplier.set(true);
+        } else {
+            mToolbar.getMenu()
+                    .findItem(R.id.optout_menu_id)
+                    .setTitle(R.string.history_clusters_enable_menu_item_label);
+            if (mContentView == mHistoryClustersCoordinator.getActivityContentView()) {
+                swapContentView();
+            }
+            mShowHistoryClustersToggleSupplier.set(false);
+        }
+    }
+
     private ViewGroup buildToggleView(ViewGroup parent, int selectedIndex) {
         ViewGroup viewGroup = (ViewGroup) LayoutInflater.from(mActivity).inflate(
                 R.layout.history_toggle, parent, false);
@@ -379,6 +422,9 @@
             return true;
         } else if (item.getItemId() == R.id.info_menu_id) {
             toggleInfoHeaderVisibility();
+        } else if (item.getItemId() == R.id.optout_menu_id) {
+            onHistoryClustersOptOutChanged(!mPrefService.getBoolean(HISTORY_CLUSTERS_VISIBLE_PREF));
+            return true;
         }
         return false;
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/page_info/PageInfoHistoryController.java b/chrome/android/java/src/org/chromium/chrome/browser/page_info/PageInfoHistoryController.java
index 027ad77..e35617d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/page_info/PageInfoHistoryController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/page_info/PageInfoHistoryController.java
@@ -11,6 +11,7 @@
 
 import androidx.annotation.VisibleForTesting;
 
+import org.chromium.base.supplier.ObservableSupplierImpl;
 import org.chromium.base.supplier.Supplier;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.history.BrowsingHistoryBridge;
@@ -83,7 +84,7 @@
                 /* isSeparateActivity */ false,
                 /* isIncognito */ false, /* shouldShowPrivacyDisclaimers */ true,
                 /* shouldShowClearData */ false, mHost,
-                /* selectionDelegate */ null, mTabSupplier, false,
+                /* selectionDelegate */ null, mTabSupplier, new ObservableSupplierImpl<>(),
                 vg -> null, new BrowsingHistoryBridge(Profile.getLastUsedRegularProfile()));
         mContentManager.startLoadingItems();
         return mContentManager.getRecyclerView();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateDataFetcher.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateDataFetcher.java
index 2992fa7..fbfcce3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateDataFetcher.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateDataFetcher.java
@@ -73,8 +73,8 @@
         mObserver = observer;
 
         mTab.addObserver(this);
-        mNativePointer = WebApkUpdateDataFetcherJni.get().initialize(
-                WebApkUpdateDataFetcher.this, mOldInfo.scopeUrl(), mOldInfo.manifestUrl());
+        mNativePointer = WebApkUpdateDataFetcherJni.get().initialize(WebApkUpdateDataFetcher.this,
+                mOldInfo.scopeUrl(), mOldInfo.manifestUrl(), mOldInfo.manifestId());
         WebApkUpdateDataFetcherJni.get().start(
                 mNativePointer, WebApkUpdateDataFetcher.this, mTab.getWebContents());
         return true;
@@ -114,14 +114,14 @@
      */
     @CalledByNative
     protected void onDataAvailable(String manifestStartUrl, String scopeUrl, String name,
-            String shortName, String primaryIconUrl, String primaryIconMurmur2Hash,
-            Bitmap primaryIconBitmap, boolean isPrimaryIconMaskable, String splashIconUrl,
-            String splashIconMurmur2Hash, Bitmap splashIconBitmap, boolean isSplashIconMaskable,
-            String[] iconUrls, @DisplayMode.EnumType int displayMode, int orientation,
-            long themeColor, long backgroundColor, String shareAction, String shareParamsTitle,
-            String shareParamsText, boolean isShareMethodPost, boolean isShareEncTypeMultipart,
-            String[] shareParamsFileNames, String[][] shareParamsAccepts, String[][] shortcuts,
-            byte[][] shortcutIconData) {
+            String shortName, String manifestId, String primaryIconUrl,
+            String primaryIconMurmur2Hash, Bitmap primaryIconBitmap, boolean isPrimaryIconMaskable,
+            String splashIconUrl, String splashIconMurmur2Hash, Bitmap splashIconBitmap,
+            boolean isSplashIconMaskable, String[] iconUrls, @DisplayMode.EnumType int displayMode,
+            int orientation, long themeColor, long backgroundColor, String shareAction,
+            String shareParamsTitle, String shareParamsText, boolean isShareMethodPost,
+            boolean isShareEncTypeMultipart, String[] shareParamsFileNames,
+            String[][] shareParamsAccepts, String[][] shortcuts, byte[][] shortcutIconData) {
         Context appContext = ContextUtils.getApplicationContext();
 
         HashMap<String, String> iconUrlToMurmur2HashMap = new HashMap<String, String>();
@@ -160,7 +160,7 @@
                         backgroundColor, defaultBackgroundColor, isPrimaryIconMaskable,
                         isSplashIconMaskable, mOldInfo.webApkPackageName(),
                         mOldInfo.shellApkVersion(), mOldInfo.manifestUrl(), manifestStartUrl,
-                        null /* manifestId */, null /*appKey*/, WebApkDistributor.BROWSER,
+                        manifestId, mOldInfo.appKey(), WebApkDistributor.BROWSER,
                         iconUrlToMurmur2HashMap, shareTarget, mOldInfo.shouldForceNavigation(),
                         mOldInfo.isSplashProvidedByWebApk(), null, shortcutItems,
                         mOldInfo.webApkVersionCode());
@@ -169,7 +169,8 @@
 
     @NativeMethods
     interface Natives {
-        long initialize(WebApkUpdateDataFetcher caller, String scope, String webManifestUrl);
+        long initialize(WebApkUpdateDataFetcher caller, String scope, String webManifestUrl,
+                String webManifestId);
         void replaceWebContents(long nativeWebApkUpdateDataFetcher, WebApkUpdateDataFetcher caller,
                 WebContents webContents);
         void destroy(long nativeWebApkUpdateDataFetcher, WebApkUpdateDataFetcher caller);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateManager.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateManager.java
index 6c0351e4..f8ec3598 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateManager.java
@@ -652,24 +652,24 @@
 
         WebApkUpdateManagerJni.get().storeWebApkUpdateRequestToFile(updateRequestPath,
                 info.manifestStartUrl(), info.scopeUrl(), info.name(), info.shortName(),
-                primaryIconUrl, primaryIconData, info.isIconAdaptive(), splashIconUrl,
-                splashIconData, info.isSplashIconMaskable(), iconUrls, iconHashes,
-                info.displayMode(), info.orientation(), info.toolbarColor(), info.backgroundColor(),
-                shareTargetAction, shareTargetParamTitle, shareTargetParamText,
-                shareTargetIsMethodPost, shareTargetIsEncTypeMultipart, shareTargetParamFileNames,
-                shareTargetParamAccepts, shortcuts, shortcutIconData, info.manifestUrl(),
-                info.webApkPackageName(), versionCode, isManifestStale,
+                info.manifestId(), info.appKey(), primaryIconUrl, primaryIconData,
+                info.isIconAdaptive(), splashIconUrl, splashIconData, info.isSplashIconMaskable(),
+                iconUrls, iconHashes, info.displayMode(), info.orientation(), info.toolbarColor(),
+                info.backgroundColor(), shareTargetAction, shareTargetParamTitle,
+                shareTargetParamText, shareTargetIsMethodPost, shareTargetIsEncTypeMultipart,
+                shareTargetParamFileNames, shareTargetParamAccepts, shortcuts, shortcutIconData,
+                info.manifestUrl(), info.webApkPackageName(), versionCode, isManifestStale,
                 isAppIdentityUpdateSupported, updateReasonsArray, callback);
     }
 
     @NativeMethods
     interface Natives {
         public void storeWebApkUpdateRequestToFile(String updateRequestPath, String startUrl,
-                String scope, String name, String shortName, String primaryIconUrl,
-                String primaryIconData, boolean isPrimaryIconMaskable, String splashIconUrl,
-                String splashIconData, boolean isSplashIconMaskable, String[] iconUrls,
-                String[] iconHashes, @DisplayMode.EnumType int displayMode, int orientation,
-                long themeColor, long backgroundColor, String shareTargetAction,
+                String scope, String name, String shortName, String manifestId, String appKey,
+                String primaryIconUrl, String primaryIconData, boolean isPrimaryIconMaskable,
+                String splashIconUrl, String splashIconData, boolean isSplashIconMaskable,
+                String[] iconUrls, String[] iconHashes, @DisplayMode.EnumType int displayMode,
+                int orientation, long themeColor, long backgroundColor, String shareTargetAction,
                 String shareTargetParamTitle, String shareTargetParamText,
                 boolean shareTargetParamIsMethodPost, boolean shareTargetParamIsEncTypeMultipart,
                 String[] shareTargetParamFileNames, Object[] shareTargetParamAccepts,
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/TabsOpenedFromExternalAppTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/TabsOpenedFromExternalAppTest.java
index bd5a573..dd80dd80 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/TabsOpenedFromExternalAppTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/TabsOpenedFromExternalAppTest.java
@@ -27,7 +27,6 @@
 import org.chromium.base.test.util.CriteriaHelper;
 import org.chromium.base.test.util.CriteriaNotSatisfiedException;
 import org.chromium.base.test.util.Feature;
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.init.AsyncInitializationActivity;
 import org.chromium.chrome.browser.tab.Tab;
@@ -35,7 +34,6 @@
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
 import org.chromium.chrome.test.util.ChromeTabUtils;
-import org.chromium.chrome.test.util.browser.Features.DisableFeatures;
 import org.chromium.content_public.browser.test.util.DOMUtils;
 import org.chromium.content_public.browser.test.util.JavaScriptUtils;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
@@ -50,7 +48,6 @@
  */
 @RunWith(ChromeJUnit4ClassRunner.class)
 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
-@DisableFeatures(ChromeFeatureList.GRID_TAB_SWITCHER_FOR_TABLETS)
 public class TabsOpenedFromExternalAppTest {
     @Rule
     public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestBillingAddressTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestBillingAddressTest.java
deleted file mode 100644
index 80211641..0000000
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestBillingAddressTest.java
+++ /dev/null
@@ -1,491 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.payments;
-
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.endsWith;
-
-import static org.chromium.chrome.browser.payments.PaymentRequestTestRule.DECEMBER;
-import static org.chromium.chrome.browser.payments.PaymentRequestTestRule.FIRST_BILLING_ADDRESS;
-import static org.chromium.chrome.browser.payments.PaymentRequestTestRule.NEXT_YEAR;
-
-import androidx.test.filters.MediumTest;
-
-import org.junit.Assert;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import org.chromium.base.test.util.CommandLineFlags;
-import org.chromium.base.test.util.Feature;
-import org.chromium.base.test.util.FlakyTest;
-import org.chromium.chrome.R;
-import org.chromium.chrome.browser.autofill.AutofillTestHelper;
-import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile;
-import org.chromium.chrome.browser.autofill.PersonalDataManager.CreditCard;
-import org.chromium.chrome.browser.flags.ChromeSwitches;
-import org.chromium.chrome.browser.payments.PaymentRequestTestRule.MainActivityStartCallback;
-import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
-
-import java.util.concurrent.TimeoutException;
-
-/**
- * A payment integration test for biling addresses.
- */
-@RunWith(ChromeJUnit4ClassRunner.class)
-@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
-public class PaymentRequestBillingAddressTest implements MainActivityStartCallback {
-    @Rule
-    public PaymentRequestTestRule mPaymentRequestTestRule =
-            new PaymentRequestTestRule("payment_request_free_shipping_test.html", this);
-
-    /**
-     * The index at which the option to add a billing address is located in the billing address
-     * selection dropdown.
-     */
-    private static final int ADD_BILLING_ADDRESS = 8;
-
-    /** The index of the billing address dropdown in the card editor. */
-    private static final int BILLING_ADDRESS_DROPDOWN_INDEX = 2;
-
-    @Override
-    public void onMainActivityStarted() throws TimeoutException {
-        AutofillTestHelper helper = new AutofillTestHelper();
-        String profile1 = helper.setProfile(new AutofillProfile("", "https://example.com", true,
-                "" /* honorific prefix */, "Jon Doe", "Google", "340 Main St", "CA", "Los Angeles",
-                "", "90291", "", "US", "650-253-0000", "jon.doe@gmail.com", "en-US"));
-        helper.setCreditCard(new CreditCard("", "https://example.com", true, true, "Jon Doe",
-                "4111111111111111", "1111", "12", "2050", "amex", R.drawable.amex_card, profile1,
-                "" /* serverId */));
-        String profile2 = helper.setProfile(new AutofillProfile("", "https://example.com", true,
-                "" /* honorific prefix */, "Rob Doe", "Google", "340 Main St", "CA", "Los Angeles",
-                "", "90291", "", "US", "650-253-0000", "jon.doe@gmail.com", "en-US"));
-        String profile3 = helper.setProfile(new AutofillProfile("", "https://example.com", true,
-                "" /* honorific prefix */, "Tom Doe", "Google", "340 Main St", "CA", "Los Angeles",
-                "", "90291", "", "US", "650-253-0000", "jon.doe@gmail.com", "en-US"));
-
-        // Incomplete profile (invalid address).
-        String profile4 = helper.setProfile(new AutofillProfile("", "https://example.com", true,
-                "" /* honorific prefix */, "Bart Doe", "Google", "340 Main St", "CA", "", "",
-                "90291", "", "US", "650-253-0000", "jon.doe@gmail.com", "en-US"));
-
-        // Incomplete profile (missing phone number)
-        String profile5 = helper.setProfile(new AutofillProfile("", "https://example.com", true,
-                "" /* honorific prefix */, "Lisa Doe", "Google", "340 Main St", "CA", "Los Angeles",
-                "", "90291", "", "US", "", "jon.doe@gmail.com", "en-US"));
-
-        // Incomplete profile (missing recipient name).
-        String profile6 = helper.setProfile(new AutofillProfile("", "https://example.com", true,
-                "" /* honorific prefix */, "", "Google", "340 Main St", "CA", "Los Angeles", "",
-                "90291", "", "US", "650-253-0000", "jon.doe@gmail.com", "en-US"));
-
-        // Incomplete profile (need more information).
-        String profile7 = helper.setProfile(
-                new AutofillProfile("", "https://example.com", true, "" /* honorific prefix */, "",
-                        "Google", "340 Main St", "CA", "", "", "90291", "", "US", "", "", "en-US"));
-
-        // Profile with empty street address (should not be presented to user).
-        String profile8 = helper.setProfile(
-                new AutofillProfile("", "https://example.com", true, "" /* honorific prefix */,
-                        "Jerry Doe", "Google", "" /* streetAddress */, "CA", "Los Angeles", "",
-                        "90291", "", "US", "650-253-0000", "jerry.doe@gmail.com", "en-US"));
-
-        // This card has no billing address selected.
-        helper.setCreditCard(new CreditCard("", "https://example.com", true, true, "Jane Doe",
-                "4242424242424242", "1111", "12", "2050", "amex", R.drawable.amex_card, profile6,
-                "" /* serverId */));
-
-        // Assign use stats so that incomplete profiles have the highest frecency, profile2 has the
-        // highest frecency and profile3 has the lowest among the complete profiles, and profile8
-        // has the highest frecency and profile4 has the lowest among the incomplete profiles.
-        helper.setProfileUseStatsForTesting(profile1, 5, 5);
-        helper.setProfileUseStatsForTesting(profile2, 10, 10);
-        helper.setProfileUseStatsForTesting(profile3, 1, 1);
-        helper.setProfileUseStatsForTesting(profile4, 15, 15);
-        helper.setProfileUseStatsForTesting(profile5, 30, 30);
-        helper.setProfileUseStatsForTesting(profile6, 25, 25);
-        helper.setProfileUseStatsForTesting(profile7, 20, 20);
-        helper.setProfileUseStatsForTesting(profile8, 40, 40);
-    }
-
-    /** Verifies the format of the billing address suggestions when adding a new credit card. */
-    @Test
-    @MediumTest
-    @FlakyTest(message = "crbug.com/1182234")
-    @Feature({"Payments"})
-    public void testNewCardBillingAddressFormat() throws TimeoutException {
-        mPaymentRequestTestRule.triggerUIAndWait(mPaymentRequestTestRule.getReadyToPay());
-        mPaymentRequestTestRule.clickInPaymentMethodAndWait(
-                R.id.payments_section, mPaymentRequestTestRule.getReadyForInput());
-        mPaymentRequestTestRule.clickInPaymentMethodAndWait(
-                R.id.payments_add_option_button, mPaymentRequestTestRule.getReadyToEdit());
-        mPaymentRequestTestRule.setTextInCardEditorAndWait(
-                new String[] {"5454-5454-5454-5454", "Bob"},
-                mPaymentRequestTestRule.getEditorTextUpdate());
-        mPaymentRequestTestRule.setSpinnerSelectionsInCardEditorAndWait(
-                new int[] {DECEMBER, NEXT_YEAR, FIRST_BILLING_ADDRESS},
-                mPaymentRequestTestRule.getBillingAddressChangeProcessed());
-        // The billing address suggestions should include only the name, address, city, state and
-        // zip code of the profile.
-        Assert.assertEquals(mPaymentRequestTestRule.getSpinnerSelectionTextInCardEditor(
-                                    BILLING_ADDRESS_DROPDOWN_INDEX),
-                "Rob Doe, 340 Main St, Los Angeles, CA 90291");
-    }
-
-    /**
-     * Verifies that the correct number of billing address suggestions are shown when adding a new
-     * credit card.
-     */
-    @Test
-    @MediumTest
-    @FlakyTest(message = "crbug.com/1182234")
-    @Feature({"Payments"})
-    public void testNumberOfBillingAddressSuggestions() throws TimeoutException {
-        mPaymentRequestTestRule.triggerUIAndWait(mPaymentRequestTestRule.getReadyToPay());
-        mPaymentRequestTestRule.clickInPaymentMethodAndWait(
-                R.id.payments_section, mPaymentRequestTestRule.getReadyForInput());
-        mPaymentRequestTestRule.clickInPaymentMethodAndWait(
-                R.id.payments_add_option_button, mPaymentRequestTestRule.getReadyToEdit());
-
-        // There should only be 9 suggestions, the 7 saved addresses, the select hint and the
-        // option to add a new address.
-        Assert.assertEquals(9,
-                mPaymentRequestTestRule.getSpinnerItemCountInCardEditor(
-                        BILLING_ADDRESS_DROPDOWN_INDEX));
-    }
-
-    /**
-     * Verifies that the correct number of billing address suggestions are shown when adding a new
-     * credit card, even after cancelling out of adding a new billing address.
-     */
-    @Test
-    @MediumTest
-    @FlakyTest(message = "crbug.com/1182234")
-    @Feature({"Payments"})
-    public void testNumberOfBillingAddressSuggestions_AfterCancellingNewBillingAddress()
-            throws TimeoutException {
-        // Add a payment method and add a new billing address.
-        mPaymentRequestTestRule.triggerUIAndWait(mPaymentRequestTestRule.getReadyToPay());
-        mPaymentRequestTestRule.clickInPaymentMethodAndWait(
-                R.id.payments_section, mPaymentRequestTestRule.getReadyForInput());
-        mPaymentRequestTestRule.clickInPaymentMethodAndWait(
-                R.id.payments_add_option_button, mPaymentRequestTestRule.getReadyToEdit());
-
-        // Select the "+ ADD ADDRESS" option for the billing address.
-        mPaymentRequestTestRule.setSpinnerSelectionsInCardEditorAndWait(
-                new int[] {DECEMBER, NEXT_YEAR, ADD_BILLING_ADDRESS},
-                mPaymentRequestTestRule.getReadyToEdit());
-
-        // Cancel the creation of a new billing address.
-        mPaymentRequestTestRule.clickInEditorAndWait(
-                R.id.payments_edit_cancel_button, mPaymentRequestTestRule.getReadyToEdit());
-        // There should still only be 9 suggestions, the 7 saved addresses, the select hint and
-        // the option to add a new address.
-        Assert.assertEquals(9,
-                mPaymentRequestTestRule.getSpinnerItemCountInCardEditor(
-                        BILLING_ADDRESS_DROPDOWN_INDEX));
-    }
-
-    /**
-     * Tests that for a card that already has a billing address, adding a new one and cancelling
-     * maintains the previous selection. */
-    @Test
-    @MediumTest
-    @FlakyTest(message = "crbug.com/1182234")
-    @Feature({"Payments"})
-    public void testAddBillingAddressOnCardAndCancel_MaintainsPreviousSelection()
-            throws TimeoutException {
-        mPaymentRequestTestRule.triggerUIAndWait(mPaymentRequestTestRule.getReadyToPay());
-        // Edit the only card.
-        mPaymentRequestTestRule.clickInPaymentMethodAndWait(
-                R.id.payments_section, mPaymentRequestTestRule.getReadyForInput());
-        mPaymentRequestTestRule.clickInPaymentMethodAndWait(
-                R.id.payments_open_editor_pencil_button, mPaymentRequestTestRule.getReadyToEdit());
-
-        // Jon Doe is selected as the billing address.
-        Assert.assertEquals(mPaymentRequestTestRule.getSpinnerSelectionTextInCardEditor(
-                                    BILLING_ADDRESS_DROPDOWN_INDEX),
-                "Jon Doe, 340 Main St, Los Angeles, CA 90291");
-
-        // Select the "+ ADD ADDRESS" option for the billing address.
-        mPaymentRequestTestRule.setSpinnerSelectionsInCardEditorAndWait(
-                new int[] {DECEMBER, NEXT_YEAR, ADD_BILLING_ADDRESS},
-                mPaymentRequestTestRule.getReadyToEdit());
-
-        // Cancel the creation of a new billing address.
-        mPaymentRequestTestRule.clickInEditorAndWait(
-                R.id.payments_edit_cancel_button, mPaymentRequestTestRule.getReadyToEdit());
-
-        // Jon Doe is STILL selected as the billing address.
-        Assert.assertEquals(mPaymentRequestTestRule.getSpinnerSelectionTextInCardEditor(
-                                    BILLING_ADDRESS_DROPDOWN_INDEX),
-                "Jon Doe, 340 Main St, Los Angeles, CA 90291");
-    }
-
-    /**
-     * Tests that adding a billing address for a card that has none, and cancelling then returns
-     * to the proper selection (Select...).
-     */
-    @Test
-    @MediumTest
-    @FlakyTest(message = "crbug.com/1182234")
-    @Feature({"Payments"})
-    public void testAddBillingAddressOnCardWithNoBillingAndCancel_MaintainsPreviousSelection()
-            throws TimeoutException {
-        mPaymentRequestTestRule.triggerUIAndWait(mPaymentRequestTestRule.getReadyToPay());
-        // Edit the second card.
-        mPaymentRequestTestRule.clickInPaymentMethodAndWait(
-                R.id.payments_section, mPaymentRequestTestRule.getReadyForInput());
-        mPaymentRequestTestRule.clickOnPaymentMethodSuggestionOptionAndWait(
-                1, mPaymentRequestTestRule.getReadyToEdit());
-
-        // Now in Card Editor to add a billing address. "Select" is selected in the dropdown.
-        Assert.assertEquals(mPaymentRequestTestRule.getSpinnerSelectionTextInCardEditor(
-                                    BILLING_ADDRESS_DROPDOWN_INDEX),
-                "Select");
-
-        // Select the "+ ADD ADDRESS" option for the billing address.
-        mPaymentRequestTestRule.setSpinnerSelectionsInCardEditorAndWait(
-                new int[] {DECEMBER, NEXT_YEAR, ADD_BILLING_ADDRESS},
-                mPaymentRequestTestRule.getReadyToEdit());
-
-        // Cancel the creation of a new billing address.
-        mPaymentRequestTestRule.clickInEditorAndWait(
-                R.id.payments_edit_cancel_button, mPaymentRequestTestRule.getReadyToEdit());
-
-        // "Select" is STILL selected as the billing address.
-        Assert.assertEquals(mPaymentRequestTestRule.getSpinnerSelectionTextInCardEditor(
-                                    BILLING_ADDRESS_DROPDOWN_INDEX),
-                "Select");
-    }
-
-    /**
-     * Verifies that the billing address suggestions are ordered by frecency.
-     */
-    @Test
-    @MediumTest
-    @FlakyTest(message = "crbug.com/1182234")
-    @Feature({"Payments"})
-    public void testBillingAddressSortedByFrecency() throws TimeoutException {
-        // Add a payment method.
-        mPaymentRequestTestRule.triggerUIAndWait(mPaymentRequestTestRule.getReadyToPay());
-        mPaymentRequestTestRule.clickInPaymentMethodAndWait(
-                R.id.payments_section, mPaymentRequestTestRule.getReadyForInput());
-        mPaymentRequestTestRule.clickInPaymentMethodAndWait(
-                R.id.payments_add_option_button, mPaymentRequestTestRule.getReadyToEdit());
-
-        // There should be 9 suggestions, the 7 saved addresses, the select hint and the option to
-        // add a new address.
-        Assert.assertEquals(9,
-                mPaymentRequestTestRule.getSpinnerItemCountInCardEditor(
-                        BILLING_ADDRESS_DROPDOWN_INDEX));
-
-        // The billing address suggestions should be ordered by frecency.
-        Assert.assertEquals(mPaymentRequestTestRule.getSpinnerTextAtPositionInCardEditor(
-                                    BILLING_ADDRESS_DROPDOWN_INDEX, 0),
-                "Select");
-        Assert.assertEquals(mPaymentRequestTestRule.getSpinnerTextAtPositionInCardEditor(
-                                    BILLING_ADDRESS_DROPDOWN_INDEX, 1),
-                "Rob Doe, 340 Main St, Los Angeles, CA 90291");
-        Assert.assertEquals(mPaymentRequestTestRule.getSpinnerTextAtPositionInCardEditor(
-                                    BILLING_ADDRESS_DROPDOWN_INDEX, 2),
-                "Jon Doe, 340 Main St, Los Angeles, CA 90291");
-        Assert.assertEquals(mPaymentRequestTestRule.getSpinnerTextAtPositionInCardEditor(
-                                    BILLING_ADDRESS_DROPDOWN_INDEX, 3),
-                "Tom Doe, 340 Main St, Los Angeles, CA 90291");
-        Assert.assertEquals(mPaymentRequestTestRule.getSpinnerTextAtPositionInCardEditor(
-                                    BILLING_ADDRESS_DROPDOWN_INDEX, 8),
-                "Add address");
-    }
-
-    /**
-     * Verifies that the billing address suggestions are ordered by frecency, except for a newly
-     * created address which should be suggested first.
-     */
-    @Test
-    @MediumTest
-    @FlakyTest(message = "crbug.com/1182234")
-    @Feature({"Payments"})
-    public void testBillingAddressSortedByFrecency_AddNewAddress() throws TimeoutException {
-        // Add a payment method.
-        mPaymentRequestTestRule.triggerUIAndWait(mPaymentRequestTestRule.getReadyToPay());
-        mPaymentRequestTestRule.clickInPaymentMethodAndWait(
-                R.id.payments_section, mPaymentRequestTestRule.getReadyForInput());
-        mPaymentRequestTestRule.clickInPaymentMethodAndWait(
-                R.id.payments_add_option_button, mPaymentRequestTestRule.getReadyToEdit());
-
-        // Add a new billing address.
-        mPaymentRequestTestRule.setSpinnerSelectionsInCardEditorAndWait(
-                new int[] {DECEMBER, NEXT_YEAR, ADD_BILLING_ADDRESS},
-                mPaymentRequestTestRule.getReadyToEdit());
-        mPaymentRequestTestRule.setTextInEditorAndWait(
-                new String[] {"Seb Doe", "Google", "340 Main St", "Los Angeles", "CA", "90291",
-                        "650-253-0000"},
-                mPaymentRequestTestRule.getEditorTextUpdate());
-        mPaymentRequestTestRule.clickInEditorAndWait(
-                R.id.editor_dialog_done_button, mPaymentRequestTestRule.getReadyToEdit());
-
-        // There should be 10 suggestions, the 7 initial addresses, the newly added address, the
-        // select hint and the option to add a new address.
-        Assert.assertEquals(10,
-                mPaymentRequestTestRule.getSpinnerItemCountInCardEditor(
-                        BILLING_ADDRESS_DROPDOWN_INDEX));
-
-        Assert.assertEquals(mPaymentRequestTestRule.getSpinnerTextAtPositionInCardEditor(
-                                    BILLING_ADDRESS_DROPDOWN_INDEX, 0),
-                "Select");
-        // The fist address suggestion should be the newly added address.
-        Assert.assertEquals(mPaymentRequestTestRule.getSpinnerTextAtPositionInCardEditor(
-                                    BILLING_ADDRESS_DROPDOWN_INDEX, 1),
-                "Seb Doe, 340 Main St, Los Angeles, CA 90291");
-
-        // The rest of the billing address suggestions should be ordered by frecency.
-        Assert.assertEquals(mPaymentRequestTestRule.getSpinnerTextAtPositionInCardEditor(
-                                    BILLING_ADDRESS_DROPDOWN_INDEX, 2),
-                "Rob Doe, 340 Main St, Los Angeles, CA 90291");
-        Assert.assertEquals(mPaymentRequestTestRule.getSpinnerTextAtPositionInCardEditor(
-                                    BILLING_ADDRESS_DROPDOWN_INDEX, 3),
-                "Jon Doe, 340 Main St, Los Angeles, CA 90291");
-        Assert.assertEquals(mPaymentRequestTestRule.getSpinnerTextAtPositionInCardEditor(
-                                    BILLING_ADDRESS_DROPDOWN_INDEX, 4),
-                "Tom Doe, 340 Main St, Los Angeles, CA 90291");
-        Assert.assertEquals(mPaymentRequestTestRule.getSpinnerTextAtPositionInCardEditor(
-                                    BILLING_ADDRESS_DROPDOWN_INDEX, 9),
-                "Add address");
-    }
-
-    /**
-     * Verifies that a newly created shipping address is offered as the first billing address
-     * suggestion.
-     */
-    @Test
-    @MediumTest
-    @FlakyTest(message = "crbug.com/1182234")
-    @Feature({"Payments"})
-    public void testNewShippingAddressSuggestedFirst() throws TimeoutException {
-        mPaymentRequestTestRule.triggerUIAndWait(mPaymentRequestTestRule.getReadyToPay());
-
-        // Add a shipping address.
-        mPaymentRequestTestRule.clickInShippingAddressAndWait(
-                R.id.payments_section, mPaymentRequestTestRule.getReadyForInput());
-        mPaymentRequestTestRule.clickInShippingAddressAndWait(
-                R.id.payments_add_option_button, mPaymentRequestTestRule.getReadyToEdit());
-        mPaymentRequestTestRule.setTextInEditorAndWait(
-                new String[] {"Seb Doe", "Google", "340 Main St", "Los Angeles", "CA", "90291",
-                        "650-253-0000"},
-                mPaymentRequestTestRule.getEditorTextUpdate());
-        mPaymentRequestTestRule.clickInEditorAndWait(
-                R.id.editor_dialog_done_button, mPaymentRequestTestRule.getReadyToPay());
-
-        // Navigate to the card editor UI.
-        mPaymentRequestTestRule.clickInPaymentMethodAndWait(
-                R.id.payments_section, mPaymentRequestTestRule.getReadyForInput());
-        mPaymentRequestTestRule.clickInPaymentMethodAndWait(
-                R.id.payments_add_option_button, mPaymentRequestTestRule.getReadyToEdit());
-
-        // There should be 10 suggestions, the 7 initial addresses, the newly added address, the
-        // select hint and the option to add a new address.
-        Assert.assertEquals(10,
-                mPaymentRequestTestRule.getSpinnerItemCountInCardEditor(
-                        BILLING_ADDRESS_DROPDOWN_INDEX));
-
-        // The new address must be put at the top of the dropdown list, right after the
-        // select hint.
-        Assert.assertEquals(mPaymentRequestTestRule.getSpinnerTextAtPositionInCardEditor(
-                                    BILLING_ADDRESS_DROPDOWN_INDEX, FIRST_BILLING_ADDRESS),
-                "Seb Doe, 340 Main St, Los Angeles, CA 90291");
-    }
-
-    @Test
-    @MediumTest
-    @FlakyTest(message = "crbug.com/1182234")
-    @Feature({"Payments"})
-    public void testSelectIncompleteBillingAddress_EditComplete() throws TimeoutException {
-        mPaymentRequestTestRule.triggerUIAndWait(mPaymentRequestTestRule.getReadyToPay());
-        // Edit the second card.
-        mPaymentRequestTestRule.clickInPaymentMethodAndWait(
-                R.id.payments_section, mPaymentRequestTestRule.getReadyForInput());
-        mPaymentRequestTestRule.clickOnPaymentMethodSuggestionOptionAndWait(
-                1, mPaymentRequestTestRule.getReadyToEdit());
-
-        // Now "Select" is selected in the dropdown.
-        Assert.assertEquals(mPaymentRequestTestRule.getSpinnerSelectionTextInCardEditor(
-                                    BILLING_ADDRESS_DROPDOWN_INDEX),
-                "Select");
-
-        // The incomplete addresses in the dropdown contain edit required messages.
-        assertThat(mPaymentRequestTestRule.getSpinnerTextAtPositionInCardEditor(
-                           BILLING_ADDRESS_DROPDOWN_INDEX, 5),
-                endsWith("Name required"));
-        assertThat(mPaymentRequestTestRule.getSpinnerTextAtPositionInCardEditor(
-                           BILLING_ADDRESS_DROPDOWN_INDEX, 6),
-                endsWith("More information required"));
-        assertThat(mPaymentRequestTestRule.getSpinnerTextAtPositionInCardEditor(
-                           BILLING_ADDRESS_DROPDOWN_INDEX, 7),
-                endsWith("Enter a valid address"));
-
-        // Selects the fourth billing address (the 5th option on the dropdown list) that misses
-        // recipient name brings up the address editor.
-        mPaymentRequestTestRule.setSpinnerSelectionsInCardEditorAndWait(
-                new int[] {DECEMBER, NEXT_YEAR, 5}, mPaymentRequestTestRule.getReadyToEdit());
-        mPaymentRequestTestRule.setTextInEditorAndWait(
-                new String[] {"Lisa Doh", "Google", "340 Main St", "Los Angeles", "CA", "90291",
-                        "650-253-0000"},
-                mPaymentRequestTestRule.getEditorTextUpdate());
-        mPaymentRequestTestRule.clickInEditorAndWait(
-                R.id.editor_dialog_done_button, mPaymentRequestTestRule.getReadyToEdit());
-
-        // The newly completed address must be selected and put at the top of the dropdown list,
-        // right after the select hint.
-        Assert.assertEquals(mPaymentRequestTestRule.getSpinnerSelectionTextInCardEditor(
-                                    BILLING_ADDRESS_DROPDOWN_INDEX),
-                "Lisa Doh, 340 Main St, Los Angeles, CA 90291");
-
-        Assert.assertEquals(mPaymentRequestTestRule.getSpinnerTextAtPositionInCardEditor(
-                                    BILLING_ADDRESS_DROPDOWN_INDEX, FIRST_BILLING_ADDRESS),
-                "Lisa Doh, 340 Main St, Los Angeles, CA 90291");
-    }
-
-    @Test
-    @MediumTest
-    @FlakyTest(message = "crbug.com/1182234")
-    @Feature({"Payments"})
-    public void testSelectIncompleteBillingAddress_EditCancel() throws TimeoutException {
-        mPaymentRequestTestRule.triggerUIAndWait(mPaymentRequestTestRule.getReadyToPay());
-        // Edit the only complete card.
-        mPaymentRequestTestRule.clickInPaymentMethodAndWait(
-                R.id.payments_section, mPaymentRequestTestRule.getReadyForInput());
-        mPaymentRequestTestRule.clickInPaymentMethodAndWait(
-                R.id.payments_open_editor_pencil_button, mPaymentRequestTestRule.getReadyToEdit());
-
-        // Jon Doe is selected as the billing address.
-        Assert.assertEquals(mPaymentRequestTestRule.getSpinnerSelectionTextInCardEditor(
-                                    BILLING_ADDRESS_DROPDOWN_INDEX),
-                "Jon Doe, 340 Main St, Los Angeles, CA 90291");
-
-        // The incomplete addresses in the dropdown contain edit required messages.
-        assertThat(mPaymentRequestTestRule.getSpinnerTextAtPositionInCardEditor(
-                           BILLING_ADDRESS_DROPDOWN_INDEX, 5),
-                endsWith("Name required"));
-        assertThat(mPaymentRequestTestRule.getSpinnerTextAtPositionInCardEditor(
-                           BILLING_ADDRESS_DROPDOWN_INDEX, 6),
-                endsWith("More information required"));
-        assertThat(mPaymentRequestTestRule.getSpinnerTextAtPositionInCardEditor(
-                           BILLING_ADDRESS_DROPDOWN_INDEX, 7),
-                endsWith("Enter a valid address"));
-
-        // Selects the forth billing address (the 5th option on the dropdown list) that misses
-        // recipient name brings up the address editor.
-        mPaymentRequestTestRule.setSpinnerSelectionsInCardEditorAndWait(
-                new int[] {DECEMBER, NEXT_YEAR, 5}, mPaymentRequestTestRule.getReadyToEdit());
-        mPaymentRequestTestRule.clickInEditorAndWait(
-                R.id.payments_edit_cancel_button, mPaymentRequestTestRule.getReadyToEdit());
-
-        // The previous selected address should be selected after canceling out from edit.
-        Assert.assertEquals(mPaymentRequestTestRule.getSpinnerSelectionTextInCardEditor(
-                                    BILLING_ADDRESS_DROPDOWN_INDEX),
-                "Jon Doe, 340 Main St, Los Angeles, CA 90291");
-    }
-}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestBillingAddressWithoutPhoneTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestBillingAddressWithoutPhoneTest.java
deleted file mode 100644
index 214b8ffa..0000000
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestBillingAddressWithoutPhoneTest.java
+++ /dev/null
@@ -1,159 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.payments;
-
-import static org.chromium.chrome.browser.payments.PaymentRequestTestRule.DECEMBER;
-import static org.chromium.chrome.browser.payments.PaymentRequestTestRule.FIRST_BILLING_ADDRESS;
-import static org.chromium.chrome.browser.payments.PaymentRequestTestRule.NEXT_YEAR;
-
-import androidx.test.filters.MediumTest;
-
-import org.junit.Assert;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import org.chromium.base.test.util.CommandLineFlags;
-import org.chromium.base.test.util.Feature;
-import org.chromium.base.test.util.FlakyTest;
-import org.chromium.chrome.R;
-import org.chromium.chrome.browser.autofill.AutofillTestHelper;
-import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile;
-import org.chromium.chrome.browser.autofill.PersonalDataManager.CreditCard;
-import org.chromium.chrome.browser.flags.ChromeSwitches;
-import org.chromium.chrome.browser.payments.PaymentRequestTestRule.MainActivityStartCallback;
-import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
-import org.chromium.ui.modaldialog.ModalDialogProperties;
-
-import java.util.concurrent.TimeoutException;
-
-/**
- * A payment integration test for biling addresses without a phone.
- */
-@RunWith(ChromeJUnit4ClassRunner.class)
-@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
-public class PaymentRequestBillingAddressWithoutPhoneTest implements MainActivityStartCallback {
-    @Rule
-    public PaymentRequestTestRule mPaymentRequestTestRule =
-            new PaymentRequestTestRule("payment_request_free_shipping_test.html", this);
-
-    /*
-     * The index at which the option to add a billing address is located in the billing address
-     * selection dropdown.
-     */
-    private static final int ADD_BILLING_ADDRESS = 3;
-
-    /** The index of the billing address dropdown in the card editor. */
-    private static final int BILLING_ADDRESS_DROPDOWN_INDEX = 2;
-
-    @Override
-    public void onMainActivityStarted() throws TimeoutException {
-        AutofillTestHelper helper = new AutofillTestHelper();
-        String address_without_phone =
-                helper.setProfile(new AutofillProfile("", "https://example.com", true,
-                        "" /* honorific prefix */, "Jon NoPhone", "Google", "340 Main St", "CA",
-                        "Los Angeles", "", "90291", "", "US", "", "jon.doe@gmail.com", "en-US"));
-        helper.setCreditCard(new CreditCard("", "https://example.com", true, true, "Jon Doe",
-                "4111111111111111", "1111", "12", "2050", "amex", R.drawable.amex_card,
-                address_without_phone, "" /* serverId */));
-        String address_with_phone = helper.setProfile(
-                new AutofillProfile("", "https://example.com", true, "" /* honorific prefix */,
-                        "Rob Phone", "Google", "340 Main St", "CA", "Los Angeles", "", "90291", "",
-                        "US", "310-310-6000", "jon.doe@gmail.com", "en-US"));
-
-        // Assign use stats so that the address without a phone number has a higher frecency score.
-        helper.setProfileUseStatsForTesting(address_without_phone, 10, 10);
-        helper.setProfileUseStatsForTesting(address_with_phone, 5, 5);
-    }
-
-    @Test
-    @MediumTest
-    @FlakyTest(message = "crbug.com/1182234")
-    @Feature({"Payments"})
-    public void testCanPayWithBillingNoPhone() throws TimeoutException {
-        mPaymentRequestTestRule.triggerUIAndWait(mPaymentRequestTestRule.getReadyToPay());
-        mPaymentRequestTestRule.clickAndWait(
-                R.id.button_primary, mPaymentRequestTestRule.getReadyForUnmaskInput());
-        mPaymentRequestTestRule.setTextInCardUnmaskDialogAndWait(
-                R.id.card_unmask_input, "123", mPaymentRequestTestRule.getReadyToUnmask());
-        mPaymentRequestTestRule.clickCardUnmaskButtonAndWait(
-                ModalDialogProperties.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
-        mPaymentRequestTestRule.expectResultContains(new String[] {"Jon NoPhone"});
-    }
-
-    @Test
-    @MediumTest
-    @FlakyTest(message = "crbug.com/1182234")
-    @Feature({"Payments"})
-    public void testCanSelectBillingAddressWithoutPhone() throws TimeoutException {
-        mPaymentRequestTestRule.triggerUIAndWait(mPaymentRequestTestRule.getReadyToPay());
-
-        // Go edit the credit card.
-        mPaymentRequestTestRule.clickInPaymentMethodAndWait(
-                R.id.payments_section, mPaymentRequestTestRule.getReadyForInput());
-        mPaymentRequestTestRule.clickOnPaymentMethodSuggestionEditIconAndWait(
-                0, mPaymentRequestTestRule.getReadyToEdit());
-
-        // Make sure that the currently selected address is valid and can be selected (does not
-        // include error messages).
-        Assert.assertTrue(
-                mPaymentRequestTestRule
-                        .getSpinnerSelectionTextInCardEditor(BILLING_ADDRESS_DROPDOWN_INDEX)
-                        .equals("Jon NoPhone, 340 Main St, Los Angeles, CA 90291"));
-
-        // Even though the current billing address is valid, the one with a phone number should be
-        // suggested first (right after the select hint) if the user wants to change it.
-        Assert.assertTrue(mPaymentRequestTestRule
-                                  .getSpinnerTextAtPositionInCardEditor(
-                                          BILLING_ADDRESS_DROPDOWN_INDEX, FIRST_BILLING_ADDRESS)
-                                  .equals("Rob Phone, 340 Main St, Los Angeles, CA 90291"));
-    }
-
-    @Test
-    @MediumTest
-    @FlakyTest(message = "crbug.com/1182234")
-    @Feature({"Payments"})
-    public void testCantSelectShippingAddressWithoutPhone() throws TimeoutException {
-        mPaymentRequestTestRule.triggerUIAndWait(mPaymentRequestTestRule.getReadyToPay());
-        mPaymentRequestTestRule.clickInShippingAddressAndWait(
-                R.id.payments_section, mPaymentRequestTestRule.getReadyForInput());
-
-        // The first suggestion should be the address with a phone.
-        Assert.assertTrue(
-                mPaymentRequestTestRule.getShippingAddressSuggestionLabel(0).contains("Rob Phone"));
-        Assert.assertFalse(mPaymentRequestTestRule.getShippingAddressSuggestionLabel(0).endsWith(
-                "Phone number required"));
-
-        // The address without a phone should be suggested after with a message indicating that the
-        // phone number is required.
-        Assert.assertTrue(mPaymentRequestTestRule.getShippingAddressSuggestionLabel(1).contains(
-                "Jon NoPhone"));
-        Assert.assertTrue(mPaymentRequestTestRule.getShippingAddressSuggestionLabel(1).endsWith(
-                "Phone number required"));
-    }
-
-    @Test
-    @MediumTest
-    @FlakyTest(message = "crbug.com/1182234")
-    @Feature({"Payments"})
-    public void testCantAddNewBillingAddressWithoutPhone() throws TimeoutException {
-        mPaymentRequestTestRule.triggerUIAndWait(mPaymentRequestTestRule.getReadyToPay());
-        mPaymentRequestTestRule.clickInPaymentMethodAndWait(
-                R.id.payments_section, mPaymentRequestTestRule.getReadyForInput());
-        mPaymentRequestTestRule.clickInPaymentMethodAndWait(
-                R.id.payments_add_option_button, mPaymentRequestTestRule.getReadyToEdit());
-
-        // Add a new billing address without a phone.
-        mPaymentRequestTestRule.setSpinnerSelectionsInCardEditorAndWait(
-                new int[] {DECEMBER, NEXT_YEAR, ADD_BILLING_ADDRESS},
-                mPaymentRequestTestRule.getReadyToEdit());
-        mPaymentRequestTestRule.setTextInEditorAndWait(
-                new String[] {"Seb Doe", "Google", "340 Main St", "Los Angeles", "CA", "90291", ""},
-                mPaymentRequestTestRule.getEditorTextUpdate());
-        // Trying to add the address without a phone number should fail.
-        mPaymentRequestTestRule.clickInEditorAndWait(
-                R.id.editor_dialog_done_button, mPaymentRequestTestRule.getEditorValidationError());
-    }
-}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestRemoveBillingAddressTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestRemoveBillingAddressTest.java
deleted file mode 100644
index 40344dad..0000000
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestRemoveBillingAddressTest.java
+++ /dev/null
@@ -1,102 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.payments;
-
-import static org.chromium.chrome.browser.payments.PaymentRequestTestRule.DECEMBER;
-import static org.chromium.chrome.browser.payments.PaymentRequestTestRule.FIRST_BILLING_ADDRESS;
-import static org.chromium.chrome.browser.payments.PaymentRequestTestRule.NEXT_YEAR;
-
-import androidx.test.filters.MediumTest;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import org.chromium.base.test.util.CommandLineFlags;
-import org.chromium.base.test.util.Feature;
-import org.chromium.base.test.util.FlakyTest;
-import org.chromium.chrome.R;
-import org.chromium.chrome.browser.autofill.AutofillTestHelper;
-import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile;
-import org.chromium.chrome.browser.autofill.PersonalDataManager.CreditCard;
-import org.chromium.chrome.browser.flags.ChromeSwitches;
-import org.chromium.chrome.browser.payments.PaymentRequestTestRule.MainActivityStartCallback;
-import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
-import org.chromium.ui.modaldialog.ModalDialogProperties;
-
-import java.util.concurrent.TimeoutException;
-
-/**
- * A payment integration test for removing a billing address that is associated with a credit card.
- */
-@RunWith(ChromeJUnit4ClassRunner.class)
-@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
-public class PaymentRequestRemoveBillingAddressTest implements MainActivityStartCallback {
-    @Rule
-    public PaymentRequestTestRule mPaymentRequestTestRule =
-            new PaymentRequestTestRule("payment_request_no_shipping_test.html", this);
-
-    @Override
-    public void onMainActivityStarted() throws TimeoutException {
-        AutofillTestHelper helper = new AutofillTestHelper();
-        helper.setProfile(
-                new AutofillProfile("", "https://example.com", true, "" /* honorific prefix */,
-                        "Jane Smith", "Google", "1600 Amphitheatre Pkwy", "CA", "Mountain View", "",
-                        "94043", "", "US", "515-543-5555", "jane.smith@google.com", "en-US"));
-        String billingAddressId = helper.setProfile(
-                new AutofillProfile("", "https://example.com", true, "" /* honorific prefix */,
-                        "Jon Doe", "Google", "340 Main St", "CA", "Los Angeles", "", "90291", "",
-                        "US", "515-543-5555", "jon.doe@google.com", "en-US"));
-        helper.setCreditCard(new CreditCard("", "https://example.com", true, true, "Alice",
-                "4111111111111111", "1111", "1", "2050", "visa", R.drawable.visa_card,
-                billingAddressId, "" /* serverId */));
-        helper.deleteProfile(billingAddressId);
-    }
-
-    /**
-     * The billing address for the credit card has been removed. Using this card should bring up an
-     * editor that requires selecting a new billing address.
-     */
-    @Test
-    @MediumTest
-    @FlakyTest(message = "crbug.com/1182234")
-    @Feature({"Payments"})
-    public void testPayWithCard() throws TimeoutException {
-        mPaymentRequestTestRule.triggerUIAndWait(mPaymentRequestTestRule.getReadyForInput());
-
-        // Expand the payment section.
-        mPaymentRequestTestRule.clickInPaymentMethodAndWait(
-                R.id.payments_section, mPaymentRequestTestRule.getReadyForInput());
-
-        // Selecting the credit card should bring up the editor.
-        mPaymentRequestTestRule.clickInPaymentMethodAndWait(
-                R.id.payments_first_radio_button, mPaymentRequestTestRule.getReadyToEdit());
-
-        // Tapping "save" in the editor should trigger a validation error.
-        mPaymentRequestTestRule.clickInCardEditorAndWait(
-                R.id.editor_dialog_done_button, mPaymentRequestTestRule.getEditorValidationError());
-
-        // Fix the validation error by selecting a billing address.
-        mPaymentRequestTestRule.setSpinnerSelectionsInCardEditorAndWait(
-                new int[] {DECEMBER, NEXT_YEAR, FIRST_BILLING_ADDRESS},
-                mPaymentRequestTestRule.getBillingAddressChangeProcessed());
-
-        // Tapping "save" in the editor now should close the editor dialog and enable the "pay"
-        // button.
-        mPaymentRequestTestRule.clickInCardEditorAndWait(
-                R.id.editor_dialog_done_button, mPaymentRequestTestRule.getReadyToPay());
-
-        // Pay with this card.
-        mPaymentRequestTestRule.clickAndWait(
-                R.id.button_primary, mPaymentRequestTestRule.getReadyForUnmaskInput());
-        mPaymentRequestTestRule.setTextInCardUnmaskDialogAndWait(
-                R.id.card_unmask_input, "123", mPaymentRequestTestRule.getReadyToUnmask());
-        mPaymentRequestTestRule.clickCardUnmaskButtonAndWait(
-                ModalDialogProperties.ButtonType.POSITIVE, mPaymentRequestTestRule.getDismissed());
-        mPaymentRequestTestRule.expectResultContains(new String[] {"4111111111111111", "Alice",
-                "12", "123", "Jane Smith", "Google", "1600 Amphitheatre Pkwy", "CA",
-                "Mountain View", "94043", "US", "+15155435555", "en-US"});
-    }
-}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/share/ShareButtonControllerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/share/ShareButtonControllerTest.java
index f47b5c76..c66a4adc 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/share/ShareButtonControllerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/share/ShareButtonControllerTest.java
@@ -119,7 +119,7 @@
             String shareString =
                     mActivityTestRule.getActivity().getResources().getString(R.string.share);
 
-            assertTrue(shareString.equals(experimentalButton.getContentDescription()));
+            assertEquals(shareString, experimentalButton.getContentDescription());
         }
     }
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkUpdateDataFetcherTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkUpdateDataFetcherTest.java
index 1f127924..a4e13d9 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkUpdateDataFetcherTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkUpdateDataFetcherTest.java
@@ -13,6 +13,7 @@
 import org.junit.runner.RunWith;
 
 import org.chromium.base.task.PostTask;
+import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.CallbackHelper;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.Feature;
@@ -23,16 +24,17 @@
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
+import org.chromium.chrome.test.util.browser.Features.EnableFeatures;
 import org.chromium.chrome.test.util.browser.webapps.WebApkIntentDataProviderBuilder;
 import org.chromium.chrome.test.util.browser.webapps.WebappTestPage;
 import org.chromium.components.webapps.WebappsIconUtils;
 import org.chromium.content_public.browser.UiThreadTaskTraits;
 import org.chromium.net.test.EmbeddedTestServerRule;
-
 /**
  * Tests the WebApkUpdateDataFetcher.
  */
 @RunWith(ChromeJUnit4ClassRunner.class)
+@Batch(Batch.PER_CLASS)
 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
 public class WebApkUpdateDataFetcherTest {
     @Rule
@@ -41,6 +43,9 @@
     @Rule
     public EmbeddedTestServerRule mTestServerRule = new EmbeddedTestServerRule();
 
+    private static final String WEBAPK_START_URL =
+            "/chrome/test/data/banners/manifest_test_page.html";
+
     private static final String WEB_MANIFEST_URL1 = "/chrome/test/data/banners/manifest.json";
     // Name for Web Manifest at {@link WEB_MANIFEST_URL1}.
     private static final String WEB_MANIFEST_NAME1 = "Manifest test app";
@@ -50,6 +55,11 @@
     // Name for Web Manifest at {@link WEB_MANIFEST_URL2}.
     private static final String WEB_MANIFEST_NAME2 = "Manifest";
 
+    private static final String WEB_MANIFEST_URL3 =
+            "/chrome/test/data/banners/manifest_with_id.json";
+    // Name for Web Manifest at {@link WEB_MANIFEST_URL3}.
+    private static final String WEB_MANIFEST_NAME3 = "Manifest test app with id specified";
+
     // Web Manifest with Murmur2 icon hash with value > {@link Long#MAX_VALUE}
     private static final String WEB_MANIFEST_WITH_LONG_ICON_MURMUR2_HASH =
             "/chrome/test/data/banners/manifest_long_icon_murmur2_hash.json";
@@ -71,6 +81,7 @@
             extends CallbackHelper implements WebApkUpdateDataFetcher.Observer {
         private boolean mWebApkCompatible;
         private String mName;
+        private String mManifestId;
         private String mPrimaryIconMurmur2Hash;
         private boolean mIsPrimaryIconMaskable;
 
@@ -82,6 +93,7 @@
 
             WebappExtras fetchedWebappExtras = fetchedInfo.getWebappExtras();
             mName = fetchedWebappExtras.name;
+            mManifestId = fetchedInfo.getWebApkExtras().manifestId;
             mPrimaryIconMurmur2Hash =
                     fetchedInfo.getWebApkExtras().iconUrlToMurmur2HashMap.get(primaryIconUrl);
             mIsPrimaryIconMaskable = fetchedWebappExtras.isIconAdaptive;
@@ -96,6 +108,10 @@
             return mName;
         }
 
+        public String manifestId() {
+            return mManifestId;
+        }
+
         public String primaryIconMurmur2Hash() {
             return mPrimaryIconMurmur2Hash;
         }
@@ -111,17 +127,21 @@
         mTab = mActivityTestRule.getActivity().getActivityTab();
     }
 
+    private WebApkIntentDataProviderBuilder getTestIntentDataProviderBuilder(
+            final String manifestUrl) {
+        WebApkIntentDataProviderBuilder builder = new WebApkIntentDataProviderBuilder(
+                "random.package", mTestServerRule.getServer().getURL(WEBAPK_START_URL));
+        builder.setScope(mTestServerRule.getServer().getURL(WEB_MANIFEST_SCOPE));
+        builder.setManifestUrl(mTestServerRule.getServer().getURL(manifestUrl));
+        return builder;
+    }
+
     /** Creates and starts a WebApkUpdateDataFetcher. */
-    private void startWebApkUpdateDataFetcher(final String scopeUrl,
-            final String manifestUrl, final WebApkUpdateDataFetcher.Observer observer) {
+    private void startWebApkUpdateDataFetcher(final WebApkIntentDataProviderBuilder builder,
+            final WebApkUpdateDataFetcher.Observer observer) {
         final WebApkUpdateDataFetcher fetcher = new WebApkUpdateDataFetcher();
-        PostTask.runOrPostTask(UiThreadTaskTraits.DEFAULT, () -> {
-            WebApkIntentDataProviderBuilder oldIntentDataProviderBuilder =
-                    new WebApkIntentDataProviderBuilder("random.package", "" /* url */);
-            oldIntentDataProviderBuilder.setScope(scopeUrl);
-            oldIntentDataProviderBuilder.setManifestUrl(manifestUrl);
-            fetcher.start(mTab, WebappInfo.create(oldIntentDataProviderBuilder.build()), observer);
-        });
+        PostTask.runOrPostTask(UiThreadTaskTraits.DEFAULT,
+                () -> { fetcher.start(mTab, WebappInfo.create(builder.build()), observer); });
     }
 
     /**
@@ -135,8 +155,7 @@
                 mTestServerRule.getServer(), mTab, WEB_MANIFEST_URL1);
 
         CallbackWaiter waiter = new CallbackWaiter();
-        startWebApkUpdateDataFetcher(mTestServerRule.getServer().getURL(WEB_MANIFEST_SCOPE),
-                mTestServerRule.getServer().getURL(WEB_MANIFEST_URL1), waiter);
+        startWebApkUpdateDataFetcher(getTestIntentDataProviderBuilder(WEB_MANIFEST_URL1), waiter);
         waiter.waitForCallback(0);
 
         Assert.assertTrue(waiter.isWebApkCompatible());
@@ -157,8 +176,8 @@
                 mTestServerRule.getServer(), mTab, WEB_MANIFEST_URL_MASKABLE);
 
         CallbackWaiter waiter = new CallbackWaiter();
-        startWebApkUpdateDataFetcher(mTestServerRule.getServer().getURL(WEB_MANIFEST_SCOPE),
-                mTestServerRule.getServer().getURL(WEB_MANIFEST_URL_MASKABLE), waiter);
+        startWebApkUpdateDataFetcher(
+                getTestIntentDataProviderBuilder(WEB_MANIFEST_URL_MASKABLE), waiter);
         waiter.waitForCallback(0);
 
         Assert.assertEquals(
@@ -178,8 +197,7 @@
                 mTestServerRule.getServer(), mTab, WEB_MANIFEST_URL1);
 
         CallbackWaiter waiter = new CallbackWaiter();
-        startWebApkUpdateDataFetcher(mTestServerRule.getServer().getURL(WEB_MANIFEST_SCOPE),
-                mTestServerRule.getServer().getURL(WEB_MANIFEST_URL2), waiter);
+        startWebApkUpdateDataFetcher(getTestIntentDataProviderBuilder(WEB_MANIFEST_URL2), waiter);
 
         WebappTestPage.navigateToServiceWorkerPageWithManifest(
                 mTestServerRule.getServer(), mTab, WEB_MANIFEST_URL2);
@@ -200,11 +218,80 @@
                 mTestServerRule.getServer(), mTab, WEB_MANIFEST_WITH_LONG_ICON_MURMUR2_HASH);
 
         CallbackWaiter waiter = new CallbackWaiter();
-        startWebApkUpdateDataFetcher(mTestServerRule.getServer().getURL(WEB_MANIFEST_SCOPE),
-                mTestServerRule.getServer().getURL(WEB_MANIFEST_WITH_LONG_ICON_MURMUR2_HASH),
-                waiter);
+        startWebApkUpdateDataFetcher(
+                getTestIntentDataProviderBuilder(WEB_MANIFEST_WITH_LONG_ICON_MURMUR2_HASH), waiter);
         waiter.waitForCallback(0);
 
         Assert.assertEquals(LONG_ICON_MURMUR2_HASH, waiter.primaryIconMurmur2Hash());
     }
+
+    /**
+     * Test starting WebApkUpdateDataFetcher on page which uses a different manifest URL but same
+     * manifest ID than the ManifestUpgradeDetectorFetcher is looking for.
+     */
+    @Test
+    @MediumTest
+    @Feature({"Webapps"})
+    @EnableFeatures({"WebApkUniqueId"})
+    public void testLaunchWithDifferentManifestUrlSameId() throws Exception {
+        WebappTestPage.navigateToServiceWorkerPageWithManifest(
+                mTestServerRule.getServer(), mTab, WEB_MANIFEST_URL1);
+
+        CallbackWaiter waiter = new CallbackWaiter();
+        startWebApkUpdateDataFetcher(getTestIntentDataProviderBuilder(WEB_MANIFEST_URL2), waiter);
+
+        waiter.waitForFirst();
+        Assert.assertTrue(waiter.isWebApkCompatible());
+        Assert.assertEquals(WEB_MANIFEST_NAME1, waiter.name());
+        Assert.assertNotNull(waiter.manifestId());
+    }
+
+    /**
+     * Test starting WebApkUpdateDataFetcher on page with different manifest ID. Check that the
+     * callback is only called once the user navigates to a page which uses the desired manifest ID.
+     */
+    @Test
+    @MediumTest
+    @Feature({"Webapps"})
+    @EnableFeatures({"WebApkUniqueId"})
+    public void testLaunchWithDifferentManifestId() throws Exception {
+        WebappTestPage.navigateToServiceWorkerPageWithManifest(
+                mTestServerRule.getServer(), mTab, WEB_MANIFEST_URL3);
+
+        CallbackWaiter waiter = new CallbackWaiter();
+        startWebApkUpdateDataFetcher(getTestIntentDataProviderBuilder(WEB_MANIFEST_URL1), waiter);
+
+        WebappTestPage.navigateToServiceWorkerPageWithManifest(
+                mTestServerRule.getServer(), mTab, WEB_MANIFEST_URL2);
+        waiter.waitForFirst();
+        Assert.assertTrue(waiter.isWebApkCompatible());
+        Assert.assertEquals(WEB_MANIFEST_NAME2, waiter.name());
+        Assert.assertNotNull(waiter.manifestId());
+    }
+
+    /**
+     * Test starting WebApkUpdateDataFetcher when current WebAPK has no ID specified.
+     */
+    @Test
+    @MediumTest
+    @Feature({"Webapps"})
+    @EnableFeatures({"WebApkUniqueId"})
+    public void testLaunchWithEmptyOldManifestId() throws Exception {
+        WebappTestPage.navigateToServiceWorkerPageWithManifest(
+                mTestServerRule.getServer(), mTab, WEB_MANIFEST_URL3);
+
+        CallbackWaiter waiter = new CallbackWaiter();
+
+        WebApkIntentDataProviderBuilder oldIntentDataProviderBuilder =
+                getTestIntentDataProviderBuilder(WEB_MANIFEST_URL1);
+        // Set manifestId to empty string.
+        oldIntentDataProviderBuilder.setWebApkManifestId("");
+
+        startWebApkUpdateDataFetcher(oldIntentDataProviderBuilder, waiter);
+
+        waiter.waitForFirst();
+        Assert.assertTrue(waiter.isWebApkCompatible());
+        Assert.assertEquals(WEB_MANIFEST_NAME3, waiter.name());
+        Assert.assertNotNull(waiter.manifestId());
+    }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerTest.java
index 3d3cfa9..b0fb675 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerTest.java
@@ -203,6 +203,8 @@
         public String scope;
         public String name;
         public String shortName;
+        public String manifestId;
+        public String appKey;
         public Map<String, String> iconUrlToMurmur2HashMap;
         public @DisplayMode.EnumType int displayMode;
         public int orientation;
@@ -217,6 +219,8 @@
         creationData.manifestUrl = mTestServer.getURL(WEBAPK_MANIFEST_URL);
         creationData.startUrl = mTestServer.getURL(WEBAPK_START_URL);
         creationData.scope = mTestServer.getURL(WEBAPK_SCOPE_URL);
+        creationData.manifestId = mTestServer.getURL(WEBAPK_START_URL);
+        creationData.appKey = mTestServer.getURL(WEBAPK_MANIFEST_URL);
         creationData.name = WEBAPK_NAME;
         creationData.shortName = WEBAPK_SHORT_NAME;
 
@@ -263,7 +267,7 @@
                             creationData.themeColor, creationData.backgroundColor, 0,
                             creationData.isPrimaryIconMaskable, false /* isSplashIconMaskable */,
                             "", 1000 /* shellApkVersion */, creationData.manifestUrl,
-                            creationData.startUrl, null /* manifestId */, null /*appKey*/,
+                            creationData.startUrl, creationData.manifestId, creationData.appKey,
                             WebApkDistributor.BROWSER, creationData.iconUrlToMurmur2HashMap, null,
                             false /* forceNavigation */, false /* isSplashProvidedByWebApk */,
                             null /* shareData */, creationData.shortcuts,
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/UndoRefocusHelperTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/UndoRefocusHelperTest.java
index d00bd15b..95d737c 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/UndoRefocusHelperTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/UndoRefocusHelperTest.java
@@ -21,7 +21,6 @@
 import org.chromium.base.Callback;
 import org.chromium.base.metrics.UmaRecorder;
 import org.chromium.base.metrics.UmaRecorderHolder;
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
 import org.chromium.base.supplier.ObservableSupplier;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.Feature;
@@ -43,7 +42,7 @@
  */
 @RunWith(BaseRobolectricTestRunner.class)
 @Features.EnableFeatures(ChromeFeatureList.TAB_STRIP_IMPROVEMENTS)
-@Config(manifest = Config.NONE, shadows = {ShadowRecordHistogram.class})
+@Config(manifest = Config.NONE)
 public class UndoRefocusHelperTest {
     @Mock
     TabModelSelector mTabModelSelector;
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/app/metrics/LaunchCauseMetricsTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/app/metrics/LaunchCauseMetricsTest.java
index 5324e8e..1b7a6f82 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/app/metrics/LaunchCauseMetricsTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/app/metrics/LaunchCauseMetricsTest.java
@@ -16,18 +16,16 @@
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 import org.mockito.quality.Strictness;
-import org.robolectric.annotation.Config;
 
 import org.chromium.base.ActivityState;
 import org.chromium.base.ApplicationStatus;
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 
 /**
  * Tests basic functionality of LaunchCauseMetrics.
  */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(shadows = {ShadowRecordHistogram.class})
 public final class LaunchCauseMetricsTest {
     @Mock
     private Activity mActivity;
@@ -46,7 +44,7 @@
     }
 
     private static int histogramCountForValue(int value) {
-        return ShadowRecordHistogram.getHistogramValueCountForTesting(
+        return RecordHistogram.getHistogramValueCountForTesting(
                 LaunchCauseMetrics.LAUNCH_CAUSE_HISTOGRAM, value);
     }
 
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/app/metrics/TabbedActivityLaunchCauseMetricsUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/app/metrics/TabbedActivityLaunchCauseMetricsUnitTest.java
index f6fffd4..3fedecab 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/app/metrics/TabbedActivityLaunchCauseMetricsUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/app/metrics/TabbedActivityLaunchCauseMetricsUnitTest.java
@@ -21,13 +21,12 @@
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 import org.mockito.quality.Strictness;
-import org.robolectric.annotation.Config;
 
 import org.chromium.base.ActivityState;
 import org.chromium.base.ApplicationStatus;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.IntentUtils;
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.browser.ChromeTabbedActivity;
 import org.chromium.chrome.browser.IntentHandler;
@@ -38,7 +37,6 @@
  * Unit tests for TabbedActivityLaunchCauseMetrics.
  */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(shadows = {ShadowRecordHistogram.class})
 public final class TabbedActivityLaunchCauseMetricsUnitTest {
     @Mock
     private Activity mActivity;
@@ -57,7 +55,7 @@
     }
 
     private static int histogramCountForValue(int value) {
-        return ShadowRecordHistogram.getHistogramValueCountForTesting(
+        return RecordHistogram.getHistogramValueCountForTesting(
                 LaunchCauseMetrics.LAUNCH_CAUSE_HISTOGRAM, value);
     }
 
@@ -99,13 +97,13 @@
                 histogramCountForValue(LaunchCauseMetrics.LaunchCause.OPEN_IN_BROWSER_FROM_MENU));
 
         // Ensures other metrics still aren't recorded when Chrome has already recorded a launch.
-        int total = ShadowRecordHistogram.getHistogramTotalCountForTesting(
+        int total = RecordHistogram.getHistogramTotalCountForTesting(
                 LaunchCauseMetrics.LAUNCH_CAUSE_HISTOGRAM);
         intent.putExtra(IntentHandler.EXTRA_FROM_OPEN_IN_BROWSER, false);
         metrics.onReceivedIntent();
         metrics.recordLaunchCause();
         Assert.assertEquals(total,
-                ShadowRecordHistogram.getHistogramTotalCountForTesting(
+                RecordHistogram.getHistogramTotalCountForTesting(
                         LaunchCauseMetrics.LAUNCH_CAUSE_HISTOGRAM));
     }
 
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/app/video_tutorials/NewTabPageVideoIPHManagerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/app/video_tutorials/NewTabPageVideoIPHManagerTest.java
index 09c87bd..357e0f4 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/app/video_tutorials/NewTabPageVideoIPHManagerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/app/video_tutorials/NewTabPageVideoIPHManagerTest.java
@@ -21,7 +21,7 @@
 
 import org.chromium.base.Callback;
 import org.chromium.base.FeatureList;
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
@@ -101,7 +101,7 @@
 
     @Before
     public void setUp() {
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
         MockitoAnnotations.initMocks(this);
         Mockito.when(mViewStub.getContext()).thenReturn(mContext);
 
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/ReadingListSectionHeaderTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/ReadingListSectionHeaderTest.java
index 45ec1f10..d3732895 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/ReadingListSectionHeaderTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/ReadingListSectionHeaderTest.java
@@ -17,7 +17,8 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.ContextUtils;
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.bookmarks.BookmarkBridge.BookmarkItem;
@@ -30,14 +31,14 @@
 
 /** Unit tests for {@link ReadingListSectionHeader}. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(manifest = Config.NONE, shadows = {ShadowRecordHistogram.class})
+@Config(manifest = Config.NONE)
 public class ReadingListSectionHeaderTest {
     private static final int NEWER_CREATION_TIMESTAMP = 2;
     private static final int OLDER_CREATION_TIMESTAMP = 1;
 
     @Before
     public void setup() {
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
     }
 
     private BookmarkListEntry createReadingListEntry(long id, boolean read, int dateAdded) {
@@ -88,13 +89,13 @@
         assertEquals(
                 "Expected a different item", 2, listItems.get(4).getBookmarkItem().getId().getId());
         assertEquals("Incorrect histogram value for unread items", 1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Bookmarks.ReadingList.NumberOfUnreadItems", 1));
         assertEquals("Incorrect histogram value for read items", 1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Bookmarks.ReadingList.NumberOfReadItems", 2));
         assertEquals("Incorrect histogram value for read list items", 1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Bookmarks.ReadingList.NumberOfItems", 3));
     }
 
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/TabUsageTrackerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/TabUsageTrackerTest.java
index c0843b1..020324a2 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/TabUsageTrackerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/TabUsageTrackerTest.java
@@ -16,7 +16,8 @@
 import org.mockito.MockitoAnnotations;
 import org.robolectric.annotation.Config;
 
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.browser.TabUsageTracker;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
@@ -36,7 +37,7 @@
  * percentage of tabs used.
  */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(manifest = Config.NONE, shadows = {ShadowRecordHistogram.class})
+@Config(manifest = Config.NONE)
 public class TabUsageTrackerTest {
     @Mock
     TabModelSelector mTabModelSelector;
@@ -68,7 +69,7 @@
 
     @After
     public void tearDown() {
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
     }
 
     @Test
@@ -90,10 +91,9 @@
 
         // Assert
         Assert.assertEquals(
-                1, ShadowRecordHistogram.getHistogramValueCountForTesting(NUMBER_OF_TABS_USED, 1));
-        Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
-                        PERCENTAGE_OF_TABS_USED, 50));
+                1, RecordHistogram.getHistogramValueCountForTesting(NUMBER_OF_TABS_USED, 1));
+        Assert.assertEquals(
+                1, RecordHistogram.getHistogramValueCountForTesting(PERCENTAGE_OF_TABS_USED, 50));
     }
 
     @Test
@@ -117,10 +117,9 @@
 
         // Assert that number of tabs used is 2 and percentage is 2/6 * 100 = 33
         Assert.assertEquals(
-                1, ShadowRecordHistogram.getHistogramValueCountForTesting(NUMBER_OF_TABS_USED, 2));
-        Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
-                        PERCENTAGE_OF_TABS_USED, 33));
+                1, RecordHistogram.getHistogramValueCountForTesting(NUMBER_OF_TABS_USED, 2));
+        Assert.assertEquals(
+                1, RecordHistogram.getHistogramValueCountForTesting(PERCENTAGE_OF_TABS_USED, 33));
     }
 
     @Test
@@ -129,9 +128,9 @@
         mTabUsageTracker.onStopWithNative();
 
         Assert.assertEquals(
-                0, ShadowRecordHistogram.getHistogramTotalCountForTesting(NUMBER_OF_TABS_USED));
+                0, RecordHistogram.getHistogramTotalCountForTesting(NUMBER_OF_TABS_USED));
         Assert.assertEquals(
-                0, ShadowRecordHistogram.getHistogramTotalCountForTesting(PERCENTAGE_OF_TABS_USED));
+                0, RecordHistogram.getHistogramTotalCountForTesting(PERCENTAGE_OF_TABS_USED));
     }
 
     @Test
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/ClientManagerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/ClientManagerTest.java
index f5779e1..7434d31 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/ClientManagerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/ClientManagerTest.java
@@ -33,7 +33,7 @@
 
 import org.chromium.base.IntentUtils;
 import org.chromium.base.metrics.RecordHistogram;
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.browser.browserservices.verification.OriginVerifier;
 import org.chromium.chrome.browser.browserservices.verification.OriginVerifierFactoryImpl;
@@ -43,9 +43,7 @@
 
 /** Tests for ClientManager. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(manifest = Config.NONE,
-        shadows = {ShadowRecordHistogram.class, ShadowUrlUtilities.class,
-                ShadowPackageManager.class})
+@Config(manifest = Config.NONE, shadows = {ShadowUrlUtilities.class, ShadowPackageManager.class})
 public class ClientManagerTest {
     private static final String URL = "https://www.android.com";
     private static final String PACKAGE_NAME = "org.chromium.chrome";
@@ -72,7 +70,7 @@
                 new ClientManager(new OriginVerifierFactoryImpl(), mInstalledAppProviderWrapper);
 
         OriginVerifier.clearCachedVerificationsForTesting();
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
 
         ShadowUrlUtilities.setTestImpl(new ShadowUrlUtilities.TestImpl() {
             @Override
@@ -347,7 +345,7 @@
 
         // Low and High confidence, same call.
         RequestThrottler.purgeAllEntriesForTesting();
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
         Assert.assertTrue(
                 mClientManager.updateStatsAndReturnWhetherAllowed(mSession, mUid, URL, true));
         mClientManager.registerLaunch(mSession, URL);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabActivityLifecycleUmaTrackerUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabActivityLifecycleUmaTrackerUnitTest.java
index 771c5157..4582a2b6 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabActivityLifecycleUmaTrackerUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabActivityLifecycleUmaTrackerUnitTest.java
@@ -21,7 +21,8 @@
 import org.robolectric.annotation.Config;
 import org.robolectric.shadows.ShadowSystemClock;
 
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.browser.IntentHandler;
 import org.chromium.chrome.browser.customtabs.CustomTabActivityLifecycleUmaTracker.ClientIdentifierType;
@@ -34,7 +35,7 @@
  * Unit test for {@link CustomTabActivityLifecycleUmaTracker}.
  */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(shadows = {ShadowRecordHistogram.class, ShadowSystemClock.class})
+@Config(shadows = {ShadowSystemClock.class})
 public class CustomTabActivityLifecycleUmaTrackerUnitTest {
     private static final String PACKAGE_A = "com.example.test.package";
     private static final String PACKAGE_B = "org.test.mypackage";
@@ -63,7 +64,7 @@
         mPref.removeKey(ChromePreferenceKeys.CUSTOM_TABS_LAST_TASK_ID);
         mPref.removeKey(ChromePreferenceKeys.CUSTOM_TABS_LAST_URL);
 
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
         ShadowSystemClock.reset();
     }
 
@@ -181,7 +182,7 @@
             String histogram = prefix + suffix;
             Assert.assertEquals("<" + histogram + "> record is different.",
                     expectedSuffix.equals(suffix) ? 1 : 0,
-                    ShadowRecordHistogram.getHistogramTotalCountForTesting(histogram));
+                    RecordHistogram.getHistogramTotalCountForTesting(histogram));
         }
     }
 
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabControllerUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabControllerUnitTest.java
index 1526fb7..c5f15e6 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabControllerUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabControllerUnitTest.java
@@ -13,38 +13,29 @@
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.content.Intent;
 import android.os.Bundle;
 
-import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.Tab;
-import org.chromium.chrome.browser.tab.TabObserver;
 import org.chromium.chrome.test.util.browser.Features;
-import org.chromium.chrome.test.util.browser.Features.DisableFeatures;
 import org.chromium.components.embedder_support.util.ShadowUrlUtilities;
-import org.chromium.content.browser.GestureListenerManagerImpl;
-import org.chromium.content_public.browser.GestureStateListener;
 import org.chromium.content_public.browser.WebContents;
 
 /**
@@ -52,7 +43,6 @@
  */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE, shadows = {ShadowUrlUtilities.class})
-@DisableFeatures({ChromeFeatureList.CCT_REAL_TIME_ENGAGEMENT_SIGNALS})
 public class CustomTabActivityTabControllerUnitTest {
     @Rule
     public final CustomTabActivityContentTestEnvironment env =
@@ -65,20 +55,12 @@
 
     @Mock
     private Profile mProfile;
-    @Mock
-    private GestureListenerManagerImpl mGestureListenerManagerImpl;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         Profile.setLastUsedProfileForTesting(mProfile);
         mTabController = env.createTabController();
-        GestureListenerManagerImpl.setInstanceForTesting(mGestureListenerManagerImpl);
-    }
-
-    @After
-    public void tearDown() {
-        GestureListenerManagerImpl.setInstanceForTesting(null);
     }
 
     @Test
@@ -215,85 +197,4 @@
         Tab tab = env.prepareTab();
         assertTrue(tab.isIncognito());
     }
-
-    @Test
-    public void doesNotAddListenersForSignalsIfFeatureIsDisabled() {
-        env.reachNativeInit(mTabController);
-
-        verify(mGestureListenerManagerImpl, never()).addListener(any(GestureStateListener.class));
-    }
-
-    @Test
-    @Features.EnableFeatures({ChromeFeatureList.CCT_REAL_TIME_ENGAGEMENT_SIGNALS})
-    public void addsListenersForSignalsIfFeatureIsEnabled() {
-        env.reachNativeInit(mTabController);
-
-        verify(mGestureListenerManagerImpl).addListener(any(GestureStateListener.class));
-    }
-
-    @Test
-    @Features.EnableFeatures({ChromeFeatureList.CCT_REAL_TIME_ENGAGEMENT_SIGNALS})
-    public void removesGestureStateListenerWhenWebContentsWillSwap() {
-        env.reachNativeInit(mTabController);
-
-        ArgumentCaptor<GestureStateListener> gestureStateListenerArgumentCaptor =
-                ArgumentCaptor.forClass(GestureStateListener.class);
-        verify(mGestureListenerManagerImpl)
-                .addListener(gestureStateListenerArgumentCaptor.capture());
-
-        ArgumentCaptor<TabObserver> tabObserverArgumentCaptor =
-                ArgumentCaptor.forClass(TabObserver.class);
-        verify(env.tabProvider.getTab(), atLeastOnce())
-                .addObserver(tabObserverArgumentCaptor.capture());
-
-        for (TabObserver observer : tabObserverArgumentCaptor.getAllValues()) {
-            observer.webContentsWillSwap(env.tabProvider.getTab());
-        }
-        verify(mGestureListenerManagerImpl)
-                .removeListener(gestureStateListenerArgumentCaptor.getValue());
-    }
-
-    @Test
-    @Features.EnableFeatures({ChromeFeatureList.CCT_REAL_TIME_ENGAGEMENT_SIGNALS})
-    public void sendsSignalsForScrollStartThenEnd() {
-        env.reachNativeInit(mTabController);
-
-        ArgumentCaptor<GestureStateListener> gestureStateListenerArgumentCaptor =
-                ArgumentCaptor.forClass(GestureStateListener.class);
-        verify(mGestureListenerManagerImpl)
-                .addListener(gestureStateListenerArgumentCaptor.capture());
-
-        // Start scrolling down.
-        gestureStateListenerArgumentCaptor.getValue().onScrollStarted(0, 100, false);
-        verify(env.connection).notifyVerticalScrollEvent(eq(env.session), eq(false));
-        // End scrolling at 50%.
-        gestureStateListenerArgumentCaptor.getValue().onScrollEnded(50, 100);
-        // We shouldn't make any more calls.
-        verify(env.connection, times(1)).notifyVerticalScrollEvent(eq(env.session), anyBoolean());
-    }
-
-    @Test
-    @Features.EnableFeatures({ChromeFeatureList.CCT_REAL_TIME_ENGAGEMENT_SIGNALS})
-    public void sendsSignalsForScrollStartDirectionChangeThenEnd() {
-        env.reachNativeInit(mTabController);
-
-        ArgumentCaptor<GestureStateListener> gestureStateListenerArgumentCaptor =
-                ArgumentCaptor.forClass(GestureStateListener.class);
-        verify(mGestureListenerManagerImpl)
-                .addListener(gestureStateListenerArgumentCaptor.capture());
-
-        // Start by scrolling down.
-        gestureStateListenerArgumentCaptor.getValue().onScrollStarted(0, 100, false);
-        verify(env.connection).notifyVerticalScrollEvent(eq(env.session), eq(false));
-        // Change direction to up at 10%.
-        gestureStateListenerArgumentCaptor.getValue().onVerticalScrollDirectionChanged(true, .1f);
-        verify(env.connection).notifyVerticalScrollEvent(eq(env.session), eq(true));
-        // Change direction to down at 5%.
-        gestureStateListenerArgumentCaptor.getValue().onVerticalScrollDirectionChanged(false, .05f);
-        verify(env.connection, times(2)).notifyVerticalScrollEvent(eq(env.session), eq(false));
-        // End scrolling at 50%.
-        gestureStateListenerArgumentCaptor.getValue().onScrollEnded(50, 100);
-        // We shouldn't make any more calls.
-        verify(env.connection, times(3)).notifyVerticalScrollEvent(eq(env.session), anyBoolean());
-    }
 }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityUrlLoadingTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityUrlLoadingTest.java
index 44cebe8..3744f9a 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityUrlLoadingTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityUrlLoadingTest.java
@@ -34,13 +34,11 @@
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.JniMocker;
 import org.chromium.chrome.browser.customtabs.CustomTabIntentDataProvider;
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.password_manager.PasswordChangeSuccessTrackerBridge;
 import org.chromium.chrome.browser.password_manager.PasswordChangeSuccessTrackerBridgeJni;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.test.util.browser.Features;
-import org.chromium.chrome.test.util.browser.Features.DisableFeatures;
 import org.chromium.components.embedder_support.util.UrlUtilities;
 import org.chromium.components.embedder_support.util.UrlUtilitiesJni;
 import org.chromium.content_public.browser.LoadUrlParams;
@@ -51,7 +49,6 @@
  */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
-@DisableFeatures({ChromeFeatureList.CCT_REAL_TIME_ENGAGEMENT_SIGNALS})
 public class CustomTabActivityUrlLoadingTest {
     public static final String PASSWORD_CHANGE_USERNAME = "Peter";
 
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/DEPS b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/DEPS
deleted file mode 100644
index ba9f81a..0000000
--- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/DEPS
+++ /dev/null
@@ -1,3 +0,0 @@
-include_rules = [
-    "+content/public/android/java/src/org/chromium/content/browser/GestureListenerManagerImpl.java",
-]
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/explore_sites/ExploreSitesBackgroundTaskUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/explore_sites/ExploreSitesBackgroundTaskUnitTest.java
index 3659b1c..2dcbed53 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/explore_sites/ExploreSitesBackgroundTaskUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/explore_sites/ExploreSitesBackgroundTaskUnitTest.java
@@ -32,7 +32,7 @@
 import org.robolectric.shadows.multidex.ShadowMultiDex;
 
 import org.chromium.base.Callback;
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.browser.device.DeviceConditions;
 import org.chromium.chrome.browser.device.ShadowDeviceConditions;
@@ -54,7 +54,7 @@
 /** Unit tests for {@link ExploreSitesBackgroundTask}. */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE,
-        shadows = {ShadowMultiDex.class, ShadowDeviceConditions.class, ShadowRecordHistogram.class,
+        shadows = {ShadowMultiDex.class, ShadowDeviceConditions.class,
                 ExploreSitesBackgroundTaskUnitTest.ShadowExploreSitesBridge.class})
 public class ExploreSitesBackgroundTaskUnitTest {
     /** Implementation of ExploreSitesBridge which does not rely on native. */
@@ -140,7 +140,7 @@
 
     @Before
     public void setUp() {
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
         MockitoAnnotations.initMocks(this);
         doNothing()
                 .when(mChromeBrowserInitializer)
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/feedback/ChromeFeedbackCollectorUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/feedback/ChromeFeedbackCollectorUnitTest.java
index 8970308..8d52da3 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/feedback/ChromeFeedbackCollectorUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/feedback/ChromeFeedbackCollectorUnitTest.java
@@ -41,7 +41,8 @@
 
 import org.chromium.base.Callback;
 import org.chromium.base.CollectionUtil;
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.Feature;
 import org.chromium.chrome.browser.profiles.Profile;
@@ -59,7 +60,7 @@
  * Test for {@link ChromeFeedbackCollector}.
  */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(manifest = Config.NONE, shadows = {ShadowRecordHistogram.class})
+@Config(manifest = Config.NONE)
 @LooperMode(LooperMode.Mode.LEGACY)
 public class ChromeFeedbackCollectorUnitTest {
     @Rule
@@ -274,7 +275,7 @@
 
     @Before
     public void setUp() {
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
         when(mAccountInfo.getEmail()).thenReturn(ACCOUNT_IN_USE);
         IdentityServicesProvider.setInstanceForTests(mock(IdentityServicesProvider.class));
         when(IdentityServicesProvider.get().getIdentityManager(any()))
@@ -302,7 +303,7 @@
         verify(callback, times(1)).onResult(any());
 
         assertEquals(1,
-                ShadowRecordHistogram.getHistogramTotalCountForTesting(
+                RecordHistogram.getHistogramTotalCountForTesting(
                         "Feedback.Duration.FetchSystemInformation"));
     }
 
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/firstrun/ChildAccountStatusSupplierTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/firstrun/ChildAccountStatusSupplierTest.java
index 97324c6..1cc2ff8 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/firstrun/ChildAccountStatusSupplierTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/firstrun/ChildAccountStatusSupplierTest.java
@@ -27,7 +27,8 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.Callback;
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.test.util.browser.signin.AccountManagerTestRule;
 import org.chromium.components.signin.test.util.FakeAccountManagerFacade;
@@ -36,7 +37,7 @@
  * Tests for {@link ChildAccountStatusSupplier}.
  */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(manifest = Config.NONE, shadows = {ShadowRecordHistogram.class})
+@Config(manifest = Config.NONE)
 public class ChildAccountStatusSupplierTest {
     private static final String ADULT_ACCOUNT_EMAIL = "adult.account@gmail.com";
     private static final String CHILD_ACCOUNT_EMAIL =
@@ -57,7 +58,7 @@
 
     @After
     public void tearDown() {
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
     }
 
     @Test
@@ -70,7 +71,7 @@
         // list of accounts from AccountManagerFacade.
         assertNull(supplier.get());
         assertEquals(0,
-                ShadowRecordHistogram.getHistogramTotalCountForTesting(
+                RecordHistogram.getHistogramTotalCountForTesting(
                         "MobileFre.ChildAccountStatusDuration"));
 
         mAccountManagerFacade.unblockGetAccounts();
@@ -78,7 +79,7 @@
 
         assertFalse(supplier.get());
         assertEquals(1,
-                ShadowRecordHistogram.getHistogramTotalCountForTesting(
+                RecordHistogram.getHistogramTotalCountForTesting(
                         "MobileFre.ChildAccountStatusDuration"));
     }
 
@@ -92,7 +93,7 @@
 
         assertTrue(supplier.get());
         assertEquals(1,
-                ShadowRecordHistogram.getHistogramTotalCountForTesting(
+                RecordHistogram.getHistogramTotalCountForTesting(
                         "MobileFre.ChildAccountStatusDuration"));
     }
 
@@ -106,7 +107,7 @@
 
         assertFalse(supplier.get());
         assertEquals(1,
-                ShadowRecordHistogram.getHistogramTotalCountForTesting(
+                RecordHistogram.getHistogramTotalCountForTesting(
                         "MobileFre.ChildAccountStatusDuration"));
     }
 
@@ -121,7 +122,7 @@
 
         assertTrue(supplier.get());
         assertEquals(1,
-                ShadowRecordHistogram.getHistogramTotalCountForTesting(
+                RecordHistogram.getHistogramTotalCountForTesting(
                         "MobileFre.ChildAccountStatusDuration"));
     }
 
@@ -144,7 +145,7 @@
         // No app restrictions should mean that the child account status is false.
         assertFalse(supplier.get());
         assertEquals(1,
-                ShadowRecordHistogram.getHistogramTotalCountForTesting(
+                RecordHistogram.getHistogramTotalCountForTesting(
                         "MobileFre.ChildAccountStatusDuration"));
     }
 
@@ -172,7 +173,7 @@
 
         assertTrue(supplier.get());
         assertEquals(1,
-                ShadowRecordHistogram.getHistogramTotalCountForTesting(
+                RecordHistogram.getHistogramTotalCountForTesting(
                         "MobileFre.ChildAccountStatusDuration"));
     }
 }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/firstrun/FirstRunAppRestrictionInfoTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/firstrun/FirstRunAppRestrictionInfoTest.java
index f95e88c..1d8d549 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/firstrun/FirstRunAppRestrictionInfoTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/firstrun/FirstRunAppRestrictionInfoTest.java
@@ -26,7 +26,8 @@
 
 import org.chromium.base.CommandLine;
 import org.chromium.base.ContextUtils;
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.task.TaskTraits;
 import org.chromium.base.task.test.ShadowPostTask;
 import org.chromium.base.test.BaseRobolectricTestRunner;
@@ -42,8 +43,7 @@
  * Unit test for {@link FirstRunAppRestrictionInfo}.
  */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(manifest = Config.NONE,
-        shadows = {ShadowRecordHistogram.class, ShadowPostTask.class, ShadowUserManager.class})
+@Config(manifest = Config.NONE, shadows = {ShadowPostTask.class, ShadowUserManager.class})
 @LooperMode(LooperMode.Mode.LEGACY)
 public class FirstRunAppRestrictionInfoTest {
     private static final List<String> HISTOGRAM_NAMES =
@@ -61,7 +61,7 @@
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
         ShadowPostTask.setTestImpl(new ShadowPostTask.TestImpl() {
             @Override
             public void postDelayedTask(TaskTraits taskTraits, Runnable task, long delay) {
@@ -88,7 +88,7 @@
     private void verifyHistograms(int expectedCallCount) {
         for (String name : HISTOGRAM_NAMES) {
             Assert.assertEquals("Histogram record count doesn't match.", expectedCallCount,
-                    ShadowRecordHistogram.getHistogramTotalCountForTesting(name));
+                    RecordHistogram.getHistogramTotalCountForTesting(name));
         }
     }
 
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/firstrun/SkipTosDialogPolicyListenerUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/firstrun/SkipTosDialogPolicyListenerUnitTest.java
index b5f7180a..c37894c 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/firstrun/SkipTosDialogPolicyListenerUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/firstrun/SkipTosDialogPolicyListenerUnitTest.java
@@ -29,7 +29,7 @@
 
 import org.chromium.base.Callback;
 import org.chromium.base.metrics.RecordHistogram;
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.supplier.OneshotSupplier;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.CallbackHelper;
@@ -45,9 +45,8 @@
  */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE,
-        shadows = {SkipTosDialogPolicyListenerUnitTest.ShadowFirstRunUtils.class,
-                ShadowRecordHistogram.class})
-//TODO(crbug.com/1210371): Rewrite using paused loop. See crbug for details.
+        shadows = {SkipTosDialogPolicyListenerUnitTest.ShadowFirstRunUtils.class})
+// TODO(crbug.com/1210371): Rewrite using paused loop. See crbug for details.
 @LooperMode(LooperMode.Mode.LEGACY)
 public class SkipTosDialogPolicyListenerUnitTest {
     private static final String HIST_IS_DEVICE_OWNED_DETECTED =
@@ -103,7 +102,7 @@
 
     @Before
     public void setUp() {
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
         Mockito.doAnswer(invocation -> {
                    mEnterpriseInfoCallback = invocation.getArgument(0);
                    return null;
@@ -130,7 +129,7 @@
 
     @After
     public void tearDown() {
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
     }
 
     @Test
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/fonts/FontPreloaderUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/fonts/FontPreloaderUnitTest.java
index 1fcad93..5445ca27 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/fonts/FontPreloaderUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/fonts/FontPreloaderUnitTest.java
@@ -30,7 +30,8 @@
 import org.robolectric.annotation.Resetter;
 import org.robolectric.shadows.ShadowSystemClock;
 
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.browser.fonts.FontPreloaderUnitTest.ShadowResourcesCompat;
 
@@ -41,9 +42,7 @@
  * Unit tests for {@link FontPreloader}.
  */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(manifest = Config.NONE,
-        shadows = {ShadowSystemClock.class, ShadowResourcesCompat.class,
-                ShadowRecordHistogram.class})
+@Config(manifest = Config.NONE, shadows = {ShadowSystemClock.class, ShadowResourcesCompat.class})
 @LooperMode(Mode.PAUSED)
 public class FontPreloaderUnitTest {
     private static final Integer[] FONTS = {org.chromium.chrome.R.font.chrome_google_sans,
@@ -92,7 +91,7 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         SystemClock.setCurrentTimeMillis(INITIAL_TIME);
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
         ShadowResourcesCompat.reset();
         when(mContext.getApplicationContext()).thenReturn(mContext);
         mFontPreloader = new FontPreloader(FONTS);
@@ -299,7 +298,7 @@
      */
     private void assertHistogramRecorded(String histogram, int expectedValue) {
         assertEquals(histogram + " isn't recorded correctly.", 1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(histogram, expectedValue));
+                RecordHistogram.getHistogramValueCountForTesting(histogram, expectedValue));
     }
 
     /**
@@ -307,6 +306,6 @@
      */
     private void assertHistogramNotRecorded(String histogram) {
         assertEquals(histogram + " shouldn't be recorded.", 0,
-                ShadowRecordHistogram.getHistogramTotalCountForTesting(histogram));
+                RecordHistogram.getHistogramTotalCountForTesting(histogram));
     }
 }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/history/HistoryAdapterAccessibilityTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/history/HistoryAdapterAccessibilityTest.java
index fa1599d..c48d9091 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/history/HistoryAdapterAccessibilityTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/history/HistoryAdapterAccessibilityTest.java
@@ -15,6 +15,7 @@
 import org.mockito.MockitoAnnotations;
 import org.robolectric.annotation.Config;
 
+import org.chromium.base.supplier.ObservableSupplierImpl;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.components.browser_ui.widget.MoreProgressButton;
 import org.chromium.components.browser_ui.widget.MoreProgressButton.State;
@@ -45,7 +46,8 @@
         mHistoryProvider = new StubbedHistoryProvider();
         mHistoryProvider.setPaging(PAGING);
 
-        mAdapter = new HistoryAdapter(mContentManager, mHistoryProvider, false, (vg) -> null);
+        mAdapter = new HistoryAdapter(
+                mContentManager, mHistoryProvider, new ObservableSupplierImpl<>(), (vg) -> null);
         mAdapter.generateHeaderItemsForTest();
         mAdapter.generateFooterItemsForTest(mMockButton);
         mAdapter.setScrollToLoadDisabledForTest(true);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/history/HistoryAdapterTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/history/HistoryAdapterTest.java
index 0da3a43..3c6628b 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/history/HistoryAdapterTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/history/HistoryAdapterTest.java
@@ -15,6 +15,7 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.ContextUtils;
+import org.chromium.base.supplier.ObservableSupplierImpl;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.R;
 import org.chromium.components.browser_ui.widget.MoreProgressButton;
@@ -40,7 +41,8 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mHistoryProvider = new StubbedHistoryProvider();
-        mAdapter = new HistoryAdapter(mContentManager, mHistoryProvider, false, (vg) -> null);
+        mAdapter = new HistoryAdapter(
+                mContentManager, mHistoryProvider, new ObservableSupplierImpl<>(), (vg) -> null);
         mAdapter.generateHeaderItemsForTest();
         mAdapter.generateFooterItemsForTest(mMockButton);
     }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/history/HistoryUITest.java b/chrome/android/junit/src/org/chromium/chrome/browser/history/HistoryUITest.java
index 671984a..9529334d 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/history/HistoryUITest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/history/HistoryUITest.java
@@ -155,6 +155,7 @@
         Profile.setLastUsedProfileForTesting(mProfile);
         doReturn(mPrefService).when(mUserPrefsJni).get(mProfile);
         doReturn(true).when(mPrefService).getBoolean(Pref.ALLOW_DELETING_BROWSER_HISTORY);
+        doReturn(true).when(mPrefService).getBoolean(HistoryManager.HISTORY_CLUSTERS_VISIBLE_PREF);
         IdentityServicesProvider.setInstanceForTests(mIdentityService);
         doReturn(mSigninManager).when(mIdentityService).getSigninManager(mProfile);
         mJniMocker.mock(PrefChangeRegistrarJni.TEST_HOOKS, mPrefChangeRegistrarJni);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/indicator/OfflineIndicatorMetricsDelegateUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/indicator/OfflineIndicatorMetricsDelegateUnitTest.java
index b9dcc75..5d3006e 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/indicator/OfflineIndicatorMetricsDelegateUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/indicator/OfflineIndicatorMetricsDelegateUnitTest.java
@@ -13,14 +13,15 @@
 import org.junit.runner.RunWith;
 import org.robolectric.annotation.Config;
 
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 
 /**
  * Unit tests for {@link OfflineIndicatorMetricsDelegate}.
  */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(manifest = Config.NONE, shadows = {ShadowRecordHistogram.class})
+@Config(manifest = Config.NONE)
 public final class OfflineIndicatorMetricsDelegateUnitTest {
     /**
      * Fake of OfflineIndicatorMetricsDelegate.Clock used to test metrics that rely on the wall
@@ -56,7 +57,7 @@
         mFakeClock = new FakeClock();
         OfflineIndicatorMetricsDelegate.setClockForTesting(mFakeClock);
 
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
 
         resetMetricsDelegate(/*isOffline=*/false, /*isForeground=*/true);
     }
@@ -94,11 +95,11 @@
                 0);
 
         assertEquals(0,
-                ShadowRecordHistogram.getHistogramTotalCountForTesting(
+                RecordHistogram.getHistogramTotalCountForTesting(
                         OfflineIndicatorMetricsDelegate
                                 .OFFLINE_INDICATOR_SHOWN_DURATION_V2_IN_BACKGROUND));
         assertEquals(0,
-                ShadowRecordHistogram.getHistogramTotalCountForTesting(
+                RecordHistogram.getHistogramTotalCountForTesting(
                         OfflineIndicatorMetricsDelegate
                                 .OFFLINE_INDICATOR_SHOWN_DURATION_V2_UNTIL_FIRST_TIME_BACKGROUNDED));
     }
@@ -146,7 +147,7 @@
                 1);
 
         assertEquals(0,
-                ShadowRecordHistogram.getHistogramTotalCountForTesting(
+                RecordHistogram.getHistogramTotalCountForTesting(
                         OfflineIndicatorMetricsDelegate
                                 .OFFLINE_INDICATOR_SHOWN_DURATION_V2_IN_FOREGROUND_WITHOUT_BEING_BACKGROUNDED));
     }
@@ -200,7 +201,7 @@
                 numStateChanges);
 
         assertEquals(0,
-                ShadowRecordHistogram.getHistogramTotalCountForTesting(
+                RecordHistogram.getHistogramTotalCountForTesting(
                         OfflineIndicatorMetricsDelegate
                                 .OFFLINE_INDICATOR_SHOWN_DURATION_V2_IN_FOREGROUND_WITHOUT_BEING_BACKGROUNDED));
     }
@@ -257,7 +258,7 @@
                 1);
 
         assertEquals(0,
-                ShadowRecordHistogram.getHistogramTotalCountForTesting(
+                RecordHistogram.getHistogramTotalCountForTesting(
                         OfflineIndicatorMetricsDelegate
                                 .OFFLINE_INDICATOR_SHOWN_DURATION_V2_IN_FOREGROUND_WITHOUT_BEING_BACKGROUNDED));
     }
@@ -306,7 +307,7 @@
                 1);
 
         assertEquals(0,
-                ShadowRecordHistogram.getHistogramTotalCountForTesting(
+                RecordHistogram.getHistogramTotalCountForTesting(
                         OfflineIndicatorMetricsDelegate
                                 .OFFLINE_INDICATOR_SHOWN_DURATION_V2_IN_FOREGROUND_WITHOUT_BEING_BACKGROUNDED));
     }
@@ -347,12 +348,12 @@
                 1);
 
         assertEquals(0,
-                ShadowRecordHistogram.getHistogramTotalCountForTesting(
+                RecordHistogram.getHistogramTotalCountForTesting(
                         OfflineIndicatorMetricsDelegate
                                 .OFFLINE_INDICATOR_SHOWN_DURATION_V2_IN_FOREGROUND_WITHOUT_BEING_BACKGROUNDED));
 
         // After checking the histograms, clear them, so our next check only looks at new data.
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
 
         // Simulate Chrome being killed, then restarted. After restarting, check that we are not be
         // tracking a shown duration.
@@ -384,11 +385,11 @@
                 0);
 
         assertEquals(0,
-                ShadowRecordHistogram.getHistogramTotalCountForTesting(
+                RecordHistogram.getHistogramTotalCountForTesting(
                         OfflineIndicatorMetricsDelegate
                                 .OFFLINE_INDICATOR_SHOWN_DURATION_V2_IN_BACKGROUND));
         assertEquals(0,
-                ShadowRecordHistogram.getHistogramTotalCountForTesting(
+                RecordHistogram.getHistogramTotalCountForTesting(
                         OfflineIndicatorMetricsDelegate
                                 .OFFLINE_INDICATOR_SHOWN_DURATION_V2_UNTIL_FIRST_TIME_BACKGROUNDED));
     }
@@ -412,9 +413,8 @@
      * @param expectedSample The expected value recorded to the histogram.
      */
     private void checkUniqueSample(String histogramName, int expectedSample) {
-        assertEquals(1, ShadowRecordHistogram.getHistogramTotalCountForTesting(histogramName));
-        assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
-                        histogramName, expectedSample));
+        assertEquals(1, RecordHistogram.getHistogramTotalCountForTesting(histogramName));
+        assertEquals(
+                1, RecordHistogram.getHistogramValueCountForTesting(histogramName, expectedSample));
     }
 }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/omaha/metrics/HistogramUtilsTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/omaha/metrics/HistogramUtilsTest.java
index 04fd8bc..036b1d9 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/omaha/metrics/HistogramUtilsTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/omaha/metrics/HistogramUtilsTest.java
@@ -10,7 +10,7 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.metrics.RecordHistogram;
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.browser.omaha.metrics.UpdateProtos.Tracking;
 import org.chromium.chrome.browser.omaha.metrics.UpdateProtos.Tracking.Source;
@@ -19,7 +19,7 @@
 
 /** Tests for HistogramUtils. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(manifest = Config.NONE, shadows = {ShadowRecordHistogram.class})
+@Config(manifest = Config.NONE)
 public class HistogramUtilsTest {
     /** Tests {@link HistogramUtils#recordStartedUpdateHistogram(boolean)} */
     @Test
@@ -28,13 +28,13 @@
         Assert.assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         "GoogleUpdate.StartingUpdateState", 0));
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
 
         HistogramUtils.recordStartedUpdateHistogram(true);
         Assert.assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         "GoogleUpdate.StartingUpdateState", 1));
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
     }
 
     /** Tests {@link HistogramUtils#recordResultHistogram(int, Tracking, boolean)}. */
@@ -107,6 +107,6 @@
         Assert.assertEquals(1, RecordHistogram.getHistogramValueCountForTesting(base, value));
         Assert.assertEquals(
                 1, RecordHistogram.getHistogramValueCountForTesting(base + "." + suffix, value));
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
     }
 }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/omaha/metrics/UpdateSuccessMetricsTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/omaha/metrics/UpdateSuccessMetricsTest.java
index aef4a39..9871c91f 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/omaha/metrics/UpdateSuccessMetricsTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/omaha/metrics/UpdateSuccessMetricsTest.java
@@ -31,7 +31,7 @@
 
 import org.chromium.base.Promise;
 import org.chromium.base.metrics.RecordHistogram;
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.browser.omaha.metrics.UpdateProtos.Tracking;
 import org.chromium.chrome.browser.omaha.metrics.UpdateProtos.Tracking.Source;
@@ -41,7 +41,7 @@
 
 /** Tests the API surface of UpdateSuccessMetrics. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(manifest = Config.NONE, shadows = {ShadowRecordHistogram.class})
+@Config(manifest = Config.NONE)
 public class UpdateSuccessMetricsTest {
     private static final int FAILED = 0;
     private static final int SUCCESS = 1;
@@ -68,7 +68,7 @@
 
     @After
     public void tearDown() {
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
     }
 
     /** Tests that StartTracking properly persists the right tracking information. */
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/reengagement/ReengagementNotificationControllerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/reengagement/ReengagementNotificationControllerTest.java
index 5db94fd..8144d3e0 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/reengagement/ReengagementNotificationControllerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/reengagement/ReengagementNotificationControllerTest.java
@@ -34,7 +34,8 @@
 
 import org.chromium.base.Callback;
 import org.chromium.base.FeatureList;
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.DefaultBrowserInfo2;
@@ -47,7 +48,7 @@
 
 /** Unit tests for {@link ReengagementNotificationController}. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(shadows = {ShadowNotificationManager.class, ShadowRecordHistogram.class})
+@Config(shadows = {ShadowNotificationManager.class})
 @LooperMode(LooperMode.Mode.LEGACY)
 public class ReengagementNotificationControllerTest {
     @Rule
@@ -81,12 +82,12 @@
         mContext = ApplicationProvider.getApplicationContext();
         mShadowNotificationManager = Shadows.shadowOf(
                 (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE));
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
     }
 
     @After
     public void tearDown() {
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
     }
 
     @Test
@@ -208,7 +209,7 @@
 
     private void testFeatureShowed(String feature) {
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Mobile.SystemNotification.Shown", getNotificationType(feature)));
 
         verify(mTracker, times(1)).shouldTriggerHelpUI(feature);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/search_engines/SearchEngineChoiceMetricsTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/search_engines/SearchEngineChoiceMetricsTest.java
index 71ecfad..de2b638 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/search_engines/SearchEngineChoiceMetricsTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/search_engines/SearchEngineChoiceMetricsTest.java
@@ -18,7 +18,8 @@
 import org.mockito.MockitoAnnotations;
 import org.robolectric.annotation.Config;
 
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.components.search_engines.TemplateUrl;
 import org.chromium.components.search_engines.TemplateUrlService;
@@ -27,7 +28,7 @@
  * Unit tests for {@link SearchEngineChoiceMetrics}.
  */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(manifest = Config.NONE, shadows = {ShadowRecordHistogram.class})
+@Config(manifest = Config.NONE)
 public final class SearchEngineChoiceMetricsTest {
     private static final String TEST_INITIAL_ENGINE = "google.com";
     private static final String TEST_ALTERNATIVE_ENGINE = "duckduckgo.com";
@@ -45,7 +46,7 @@
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
 
         // Sets up appropriate responses from Template URL service.
         TemplateUrlServiceFactory.setInstanceForTesting(mTemplateUrlService);
@@ -70,7 +71,7 @@
                 .getDefaultSearchEngineTemplateUrl();
         SearchEngineChoiceMetrics.recordSearchEngineTypeBeforeChoice();
         assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.SearchEngineChoice.SearchEngineBeforeChoicePrompt",
                         SearchEngineType.SEARCH_ENGINE_GOOGLE));
 
@@ -79,7 +80,7 @@
                 .getDefaultSearchEngineTemplateUrl();
         SearchEngineChoiceMetrics.recordSearchEngineTypeBeforeChoice();
         assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.SearchEngineChoice.SearchEngineBeforeChoicePrompt",
                         SearchEngineType.SEARCH_ENGINE_DUCKDUCKGO));
     }
@@ -98,7 +99,7 @@
 
         SearchEngineChoiceMetrics.recordSearchEngineTypeAfterChoice();
         assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         HISTOGRAM_AFTER_CHOICE, SearchEngineType.SEARCH_ENGINE_DUCKDUCKGO));
     }
 
@@ -113,10 +114,10 @@
 
         SearchEngineChoiceMetrics.recordSearchEngineTypeAfterChoice();
         assertEquals(0,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         HISTOGRAM_AFTER_CHOICE, SearchEngineType.SEARCH_ENGINE_GOOGLE));
         assertEquals(0,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.SearchEngineChoice.EventsV2",
                         SearchEngineChoiceMetrics.Events.SEARCH_ENGINE_CHANGED));
     }
@@ -163,7 +164,7 @@
         SearchEngineChoiceMetrics.recordEventV2(
                 SearchEngineChoiceMetrics.EventsV2.CHOICE_REQUEST_VALID);
         assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.SearchEngineChoice.EventsV2",
                         SearchEngineChoiceMetrics.EventsV2.CHOICE_REQUEST_VALID));
     }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/search_engines/SearchEngineChoiceNotificationTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/search_engines/SearchEngineChoiceNotificationTest.java
index 241ed3a..fe800132 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/search_engines/SearchEngineChoiceNotificationTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/search_engines/SearchEngineChoiceNotificationTest.java
@@ -32,7 +32,8 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.ContextUtils;
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
 import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
@@ -49,7 +50,7 @@
  * Unit tests for {@link SearchEngineChoiceNotification}.
  */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(manifest = Config.NONE, shadows = {ShadowRecordHistogram.class})
+@Config(manifest = Config.NONE)
 @Features.EnableFeatures({})
 public final class SearchEngineChoiceNotificationTest {
     private static final String TEST_INITIAL_ENGINE = "google.com";
@@ -78,7 +79,7 @@
         MockitoAnnotations.initMocks(this);
         ContextUtils.initApplicationContextForTests(mContext);
 
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
 
         // Sets up appropriate responses from Template URL service.
         TemplateUrlServiceFactory.setInstanceForTesting(mTemplateUrlService);
@@ -124,7 +125,7 @@
                 prefs.contains(ChromePreferenceKeys.SEARCH_ENGINE_CHOICE_PRESENTED_VERSION));
 
         assertEquals(0,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.SearchEngineChoice.Events",
                         SearchEngineChoiceMetrics.Events.SNACKBAR_SHOWN));
     }
@@ -144,7 +145,7 @@
                 prefs.contains(ChromePreferenceKeys.SEARCH_ENGINE_CHOICE_PRESENTED_VERSION));
 
         assertEquals(0,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.SearchEngineChoice.Events",
                         SearchEngineChoiceMetrics.Events.SNACKBAR_SHOWN));
     }
@@ -160,7 +161,7 @@
         verify(mSnackbarManager, times(1)).showSnackbar(any(Snackbar.class));
 
         assertEquals("We are expecting exactly one snackbar shown event.", 1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.SearchEngineChoice.Events",
                         SearchEngineChoiceMetrics.Events.SNACKBAR_SHOWN));
 
@@ -194,7 +195,7 @@
         // No increase in execution counter means it was not called again.
         verify(mSnackbarManager, times(1)).showSnackbar(any(Snackbar.class));
         assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.SearchEngineChoice.Events",
                         SearchEngineChoiceMetrics.Events.SNACKBAR_SHOWN));
     }
@@ -212,7 +213,7 @@
 
         mSnackbarArgument.getValue().getController().onAction(null);
         assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.SearchEngineChoice.Events",
                         SearchEngineChoiceMetrics.Events.PROMPT_FOLLOWED));
         verify(mContext, times(1)).startActivity(any(Intent.class), isNull());
@@ -237,11 +238,11 @@
                         ChromePreferenceKeys.SEARCH_ENGINE_CHOICE_DEFAULT_TYPE_BEFORE));
 
         assertEquals(0,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.SearchEngineChoice.Events",
                         SearchEngineChoiceMetrics.Events.SEARCH_ENGINE_CHANGED));
         assertEquals(0,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.SearchEngineChoice.ChosenSearchEngine",
                         SearchEngineType.SEARCH_ENGINE_DUCKDUCKGO));
     }
@@ -270,11 +271,11 @@
                 mContext, mSnackbarManager, mSettingsLauncher);
 
         assertEquals(0,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.SearchEngineChoice.Events",
                         SearchEngineChoiceMetrics.Events.SEARCH_ENGINE_CHANGED));
         assertEquals(0,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.SearchEngineChoice.ChosenSearchEngine",
                         SearchEngineType.SEARCH_ENGINE_DUCKDUCKGO));
     }
@@ -296,11 +297,11 @@
                 mContext, mSnackbarManager, mSettingsLauncher);
 
         assertEquals("Event is recorded when search engine was changed.", 1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.SearchEngineChoice.Events",
                         SearchEngineChoiceMetrics.Events.SEARCH_ENGINE_CHANGED));
         assertEquals("Newly chosen search engine type should be recoreded.", 1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.SearchEngineChoice.ChosenSearchEngine",
                         SearchEngineType.SEARCH_ENGINE_DUCKDUCKGO));
 
@@ -313,12 +314,12 @@
                 mContext, mSnackbarManager, mSettingsLauncher);
 
         assertEquals("Event should only be recorded once, therefore count should be still 1.", 1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.SearchEngineChoice.Events",
                         SearchEngineChoiceMetrics.Events.SEARCH_ENGINE_CHANGED));
         assertEquals("New Search Engine shoudl only be reported once, therefore count should be 1",
                 1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.SearchEngineChoice.ChosenSearchEngine",
                         SearchEngineType.SEARCH_ENGINE_DUCKDUCKGO));
     }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/suggestions/tile/TileRendererTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/suggestions/tile/TileRendererTest.java
index 5d08e190..135bd93 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/suggestions/tile/TileRendererTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/suggestions/tile/TileRendererTest.java
@@ -33,7 +33,6 @@
 import org.robolectric.shadows.ShadowDrawable;
 
 import org.chromium.base.library_loader.LibraryLoader;
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
 import org.chromium.base.task.TaskTraits;
 import org.chromium.base.task.test.ShadowPostTask;
 import org.chromium.chrome.R;
@@ -55,7 +54,7 @@
 
 /** A simple test for {@link TileRenderer} using real {@link android.view.View} objects. */
 @RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE, shadows = {ShadowRecordHistogram.class, ShadowPostTask.class})
+@Config(manifest = Config.NONE, shadows = {ShadowPostTask.class})
 public class TileRendererTest {
     /**
      * Backend that substitutes normal PostTask operations. Allow us to coordinate task execution
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/survey/ChromeSurveyControllerFlowTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/survey/ChromeSurveyControllerFlowTest.java
index b04b8d9..66f5deb 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/survey/ChromeSurveyControllerFlowTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/survey/ChromeSurveyControllerFlowTest.java
@@ -41,7 +41,8 @@
 
 import org.chromium.base.CommandLine;
 import org.chromium.base.ContextUtils;
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.task.test.BackgroundShadowAsyncTask;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.CallbackHelper;
@@ -83,7 +84,7 @@
 // clang-format off
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE,
-        shadows = { BackgroundShadowAsyncTask.class, ShadowRecordHistogram.class,
+        shadows = { BackgroundShadowAsyncTask.class,
             ChromeSurveyControllerFlowTest.ShadowChromeFeatureList.class,
             ChromeSurveyControllerFlowTest.ShadowSurveyInfoBar.class,
             ChromeSurveyControllerFlowTest.ShadowInfoBarContainer.class
@@ -216,7 +217,7 @@
         ShadowChromeFeatureList.sParamValues.clear();
         ShadowChromeFeatureList.sEnableSurvey = false;
         ShadowChromeFeatureList.sEnableMessages = false;
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
 
         CommandLine.getInstance().removeSwitch(ChromeSurveyController.COMMAND_LINE_PARAM_NAME);
     }
@@ -794,7 +795,7 @@
     }
 
     private void assertInfoBarClosingStateRecorded(@InfoBarClosingState int state) {
-        int count = ShadowRecordHistogram.getHistogramValueCountForTesting(
+        int count = RecordHistogram.getHistogramValueCountForTesting(
                 "Android.Survey.InfoBarClosingState", state);
         Assert.assertEquals(
                 String.format("InfoBarClosingState for state <%d> is not recorded.", state), 1,
@@ -830,7 +831,7 @@
                                             + "with sample <%d> is not recorded.",
                                     sample),
                 1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.Survey.DownloadAttemptsBeforeAccepted", sample));
     }
 
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/survey/ChromeSurveyControllerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/survey/ChromeSurveyControllerTest.java
index c0b4ae90..c14095c4 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/survey/ChromeSurveyControllerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/survey/ChromeSurveyControllerTest.java
@@ -26,7 +26,7 @@
 import org.mockito.junit.MockitoRule;
 import org.robolectric.annotation.Config;
 
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.browser.firstrun.FirstRunStatus;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
@@ -44,7 +44,7 @@
  * Unit tests for ChromeSurveyController.java.
  */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(manifest = Config.NONE, shadows = ShadowRecordHistogram.class)
+@Config(manifest = Config.NONE)
 public class ChromeSurveyControllerTest {
     private static final String TEST_SURVEY_TRIGGER_ID = "foobar";
 
@@ -269,7 +269,7 @@
     }
 
     private void verifyFilteringResultRecorded(@FilteringResult int reason, int expectedCount) {
-        int count = ShadowRecordHistogram.getHistogramValueCountForTesting(
+        int count = RecordHistogram.getHistogramValueCountForTesting(
                 "Android.Survey.SurveyFilteringResults", reason);
         Assert.assertEquals(String.format("FilteringResult for type <%s> does not match.", reason),
                 expectedCount, count);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/tasks/JourneyManagerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/tasks/JourneyManagerTest.java
index b28b0754..7c2be09 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/tasks/JourneyManagerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/tasks/JourneyManagerTest.java
@@ -25,7 +25,8 @@
 import org.robolectric.shadows.ShadowLooper;
 
 import org.chromium.base.ContextUtils;
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.task.test.BackgroundShadowAsyncTask;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.browser.layouts.LayoutStateProvider;
@@ -48,8 +49,7 @@
 
 /** Unit tests for JourneyManager. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(manifest = Config.NONE,
-        shadows = {ShadowRecordHistogram.class, BackgroundShadowAsyncTask.class})
+@Config(manifest = Config.NONE, shadows = {BackgroundShadowAsyncTask.class})
 @LooperMode(LooperMode.Mode.LEGACY)
 public final class JourneyManagerTest {
     private static final int LAST_ENGAGEMENT_ELAPSED_MS = 5000;
@@ -93,7 +93,7 @@
 
     @Before
     public void setUp() {
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
         Robolectric.getBackgroundThreadScheduler().reset();
 
         MockitoAnnotations.initMocks(this);
@@ -130,7 +130,7 @@
         mTabModelSelectorTabObserver.onShown(mTab, TabSelectionType.FROM_USER);
 
         assertEquals(0,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         JourneyManager.TAB_REVISIT_METRIC, LAST_ENGAGEMENT_ELAPSED_S));
     }
 
@@ -148,7 +148,7 @@
         mTabModelSelectorTabObserver.didFirstVisuallyNonEmptyPaint(mTab);
 
         assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         JourneyManager.TAB_REVISIT_METRIC, LAST_ENGAGEMENT_ELAPSED_S));
     }
 
@@ -171,7 +171,7 @@
 
         // Should still only record once.
         assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         JourneyManager.TAB_REVISIT_METRIC, LAST_ENGAGEMENT_ELAPSED_S));
     }
 
@@ -189,7 +189,7 @@
         mTabModelSelectorTabObserver.onShown(mTab, TabSelectionType.FROM_USER);
 
         assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         JourneyManager.TAB_REVISIT_METRIC, LAST_ENGAGEMENT_ELAPSED_S));
     }
 
@@ -221,7 +221,7 @@
         mTabModelSelectorTabObserver.onShown(mTab, TabSelectionType.FROM_EXIT);
 
         assertEquals(0,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         JourneyManager.TAB_REVISIT_METRIC, LAST_ENGAGEMENT_ELAPSED_S));
     }
 
@@ -260,7 +260,7 @@
         ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
 
         assertEquals(0,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         JourneyManager.TAB_REVISIT_METRIC, LAST_ENGAGEMENT_ELAPSED_S));
     }
 
@@ -272,7 +272,7 @@
         mTabModelSelectorTabObserver.onClosingStateChanged(mTab, true);
 
         assertEquals(0,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         JourneyManager.TAB_CLOSE_METRIC, LAST_ENGAGEMENT_ELAPSED_S));
     }
 
@@ -290,7 +290,7 @@
         mTabModelSelectorTabObserver.onClosingStateChanged(mTab, true);
 
         assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         JourneyManager.TAB_CLOSE_METRIC, LAST_ENGAGEMENT_ELAPSED_S));
 
         assertTrue(mSharedPreferences.contains(String.valueOf(mTab.getId())));
@@ -310,7 +310,7 @@
         mTabModelSelectorTabObserver.onClosingStateChanged(mTab, false);
 
         assertEquals(0,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         JourneyManager.TAB_CLOSE_METRIC, LAST_ENGAGEMENT_ELAPSED_S));
     }
 
@@ -328,7 +328,7 @@
         mTabModelSelectorTabObserver.onClosingStateChanged(mTab, true);
 
         assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         JourneyManager.TAB_CLOSE_METRIC, LAST_ENGAGEMENT_ELAPSED_S));
 
         mTabModelSelectorTabModelObserver.tabClosureCommitted(mTab);
@@ -358,7 +358,7 @@
         mTabModelSelectorTabObserver.onLoadUrl(mTab, params, 0);
 
         assertEquals(0,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         JourneyManager.TAB_CLOBBER_METRIC, LAST_ENGAGEMENT_ELAPSED_S));
     }
 
@@ -384,7 +384,7 @@
         mTabModelSelectorTabObserver.onLoadUrl(mTab, params, 0);
 
         assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         JourneyManager.TAB_CLOBBER_METRIC, LAST_ENGAGEMENT_ELAPSED_S));
     }
 
@@ -410,7 +410,7 @@
         mTabModelSelectorTabObserver.onLoadUrl(mTab, params, 0);
 
         assertEquals(0,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         JourneyManager.TAB_CLOBBER_METRIC, LAST_ENGAGEMENT_ELAPSED_S));
     }
 
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/ui/AppLaunchDrawBlockerUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/ui/AppLaunchDrawBlockerUnitTest.java
index 7caa5bd..19e808f3 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/ui/AppLaunchDrawBlockerUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/ui/AppLaunchDrawBlockerUnitTest.java
@@ -44,7 +44,8 @@
 import org.robolectric.annotation.LooperMode.Mode;
 import org.robolectric.shadows.ShadowSystemClock;
 
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.supplier.Supplier;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.CommandLineFlags;
@@ -68,7 +69,7 @@
 
 /** Unit tests for AppLaunchDrawBlocker behavior. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(manifest = Config.NONE, shadows = {ShadowRecordHistogram.class, ShadowSystemClock.class})
+@Config(manifest = Config.NONE, shadows = {ShadowSystemClock.class})
 @LooperMode(Mode.PAUSED)
 public class AppLaunchDrawBlockerUnitTest {
     @Rule
@@ -135,7 +136,7 @@
                 mShouldShowTabSwitcherOnStartSupplier, mIsInstantStartEnabledSupplier,
                 mIncognitoRestoreAppLaunchDrawBlockerFactoryMock);
         validateConstructorAndCaptureObservers();
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
         SystemClock.setCurrentTimeMillis(INITIAL_TIME);
     }
 
@@ -316,7 +317,7 @@
         assertAccuracyHistogram(true, true);
         final String histogram = APP_LAUNCH_BLOCK_OVERVIEW_PAGE_DRAW_DURATION_UMA;
         assertEquals(histogram + " isn't recorded correctly.", 1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(histogram, 10));
+                RecordHistogram.getHistogramValueCountForTesting(histogram, 10));
     }
 
     @Test
@@ -464,7 +465,7 @@
                                 : BlockDrawForInitialTabAccuracy.CORRECTLY_DID_NOT_BLOCK;
         }
         assertEquals(histogram + " isn't recorded correctly.", 1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(histogram, enumEntry));
+                RecordHistogram.getHistogramValueCountForTesting(histogram, enumEntry));
     }
 
     /**
@@ -476,10 +477,10 @@
         final String histogram = APP_LAUNCH_BLOCK_INITIAL_TAB_DRAW_DURATION_UMA;
         if (shouldBeBlocked) {
             assertEquals(histogram + " isn't recorded correctly.", 1,
-                    ShadowRecordHistogram.getHistogramValueCountForTesting(histogram, duration));
+                    RecordHistogram.getHistogramValueCountForTesting(histogram, duration));
         } else {
             assertEquals(histogram + " shouldn't be recorded since the view isn't blocked.", 0,
-                    ShadowRecordHistogram.getHistogramTotalCountForTesting(histogram));
+                    RecordHistogram.getHistogramTotalCountForTesting(histogram));
         }
     }
 }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerUnitTest.java
index 3d34d5f3..7b074e7 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerUnitTest.java
@@ -36,7 +36,8 @@
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.base.Callback;
 import org.chromium.base.PathUtils;
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.task.PostTask;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.CallbackHelper;
@@ -79,7 +80,7 @@
  * Unit tests for WebApkUpdateManager.
  */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(manifest = Config.NONE, shadows = {ShadowRecordHistogram.class, ShadowUrlUtilities.class})
+@Config(manifest = Config.NONE, shadows = {ShadowUrlUtilities.class})
 @LooperMode(LooperMode.Mode.LEGACY)
 public class WebApkUpdateManagerUnitTest {
     @Rule
@@ -100,6 +101,7 @@
     private static final String SCOPE_URL = "/";
     private static final String NAME = "Long Name";
     private static final String SHORT_NAME = "Short Name";
+    private static final String MANIFEST_ID = "manifestId";
     private static final String PRIMARY_ICON_URL = "/icon.png";
     private static final String PRIMARY_ICON_MURMUR2_HASH = "3";
     private static final @DisplayMode.EnumType int DISPLAY_MODE = DisplayMode.UNDEFINED;
@@ -150,11 +152,11 @@
 
         @Override
         public void storeWebApkUpdateRequestToFile(String updateRequestPath, String startUrl,
-                String scope, String name, String shortName, String primaryIconUrl,
-                String primaryIconData, boolean isPrimaryIconMaskable, String splashIconUrl,
-                String splashIconData, boolean isSplashIconMaskable, String[] iconUrls,
-                String[] iconHashes, @DisplayMode.EnumType int displayMode, int orientation,
-                long themeColor, long backgroundColor, String shareTargetAction,
+                String scope, String name, String shortName, String manifestId, String appKey,
+                String primaryIconUrl, String primaryIconData, boolean isPrimaryIconMaskable,
+                String splashIconUrl, String splashIconData, boolean isSplashIconMaskable,
+                String[] iconUrls, String[] iconHashes, @DisplayMode.EnumType int displayMode,
+                int orientation, long themeColor, long backgroundColor, String shareTargetAction,
                 String shareTargetParamTitle, String shareTargetParamText,
                 boolean shareTargetParamIsMethodPost, boolean shareTargetParamIsEncTypeMultipart,
                 String[] shareTargetParamFileNames, Object[] shareTargetParamAccepts,
@@ -179,6 +181,7 @@
         private Callback<Boolean> mStoreUpdateRequestCallback;
         private TestWebApkUpdateDataFetcher mFetcher;
         private String mUpdateName;
+        private String mAppKey;
         private boolean mDestroyedFetcher;
 
         /**
@@ -231,6 +234,14 @@
             return mUpdateName;
         }
 
+        /**
+         * Returns the "app_key" from the requested update. Null if an update has not been
+         * requested.
+         */
+        public String requestedAppKey() {
+            return mAppKey;
+        }
+
         public boolean destroyedFetcher() {
             return mDestroyedFetcher;
         }
@@ -277,6 +288,7 @@
                 Callback<Boolean> callback) {
             mStoreUpdateRequestCallback = callback;
             mUpdateName = info.name();
+            mAppKey = info.appKey();
             writeRandomTextToFile(updateRequestPath);
         }
 
@@ -292,6 +304,8 @@
         public String scopeUrl;
         public String name;
         public String shortName;
+        public String id;
+        public String appKey;
         public Map<String, String> iconUrlToMurmur2HashMap;
         public String primaryIconUrl;
         public Bitmap primaryIcon;
@@ -378,6 +392,8 @@
         metaData.putInt(WebApkMetaDataKeys.DEFAULT_BACKGROUND_COLOR_ID,
                 FakeDefaultBackgroundColorResource.ID);
         metaData.putString(WebApkMetaDataKeys.WEB_MANIFEST_URL, WEB_MANIFEST_URL);
+        metaData.putString(WebApkMetaDataKeys.WEB_MANIFEST_ID, manifestData.id);
+        metaData.putString(WebApkMetaDataKeys.APP_KEY, manifestData.appKey);
 
         String iconUrlsAndIconMurmur2Hashes = "";
         for (Map.Entry<String, String> mapEntry : manifestData.iconUrlToMurmur2HashMap.entrySet()) {
@@ -433,6 +449,8 @@
         manifestData.scopeUrl = SCOPE_URL;
         manifestData.name = NAME;
         manifestData.shortName = SHORT_NAME;
+        manifestData.id = MANIFEST_ID;
+        manifestData.appKey = MANIFEST_ID;
 
         manifestData.iconUrlToMurmur2HashMap = new HashMap<>();
         manifestData.iconUrlToMurmur2HashMap.put(PRIMARY_ICON_URL, PRIMARY_ICON_MURMUR2_HASH);
@@ -478,7 +496,7 @@
                 manifestData.themeColor, manifestData.backgroundColor,
                 manifestData.defaultBackgroundColor, false /* isPrimaryIconMaskable */,
                 false /* isSplashIconMaskable*/, kPackageName, -1, WEB_MANIFEST_URL,
-                manifestData.startUrl, null /* manifestId*/, null /* appId*/,
+                manifestData.startUrl, manifestData.id, manifestData.appKey,
                 WebApkDistributor.BROWSER, manifestData.iconUrlToMurmur2HashMap, shareTarget,
                 false /* forceNavigation */, false /* isSplashProvidedByWebApk */,
                 null /* shareData */, manifestData.shortcuts /* shortcutItems */,
@@ -600,7 +618,7 @@
 
     @Before
     public void setUp() {
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
 
         PathUtils.setPrivateDataDirectorySuffix("chrome");
         PostTask.setPrenativeThreadPoolExecutorForTesting(new RoboExecutorService());
@@ -795,6 +813,7 @@
         ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
         assertTrue(updateManager.updateRequested());
         assertEquals(NAME, updateManager.requestedUpdateName());
+        assertEquals(MANIFEST_ID, updateManager.requestedAppKey());
 
         // Check that the {@link WebApkUpdateDataFetcher} has been destroyed. This prevents
         // {@link #onGotManifestData()} from getting called.
@@ -840,6 +859,7 @@
         onGotManifestData(updateManager, defaultManifestData());
         assertTrue(updateManager.updateRequested());
         assertEquals(NAME, updateManager.requestedUpdateName());
+        assertEquals(MANIFEST_ID, updateManager.requestedAppKey());
 
         assertTrue(updateManager.destroyedFetcher());
     }
@@ -1422,7 +1442,7 @@
 
     private void verifyHistograms(String name, int expectedCallCount) {
         assertEquals("Histogram record count doesn't match.", expectedCallCount,
-                ShadowRecordHistogram.getHistogramTotalCountForTesting(name));
+                RecordHistogram.getHistogramTotalCountForTesting(name));
     }
 
     @Test
@@ -1556,4 +1576,55 @@
         updateManager.onGotManifestData(/* fetchedIntentDataProvider= */ null,
                 /* primaryIconUrl= */ null, /* splashIconUrl= */ null);
     }
+
+    @Test
+    public void testManifestIdChangeShouldNotUpdate() {
+        ManifestData androidData = defaultManifestData();
+        ManifestData fetchedData = defaultManifestData();
+        fetchedData.id = MANIFEST_ID + "1";
+        assertFalse(checkUpdateNeededForFetchedManifest(androidData, fetchedData));
+    }
+
+    @Test
+    public void testAppKeyNotChangeWhenUpdate() {
+        ManifestData androidData = defaultManifestData();
+        androidData.appKey = WEB_MANIFEST_URL;
+        registerWebApk(WEBAPK_PACKAGE_NAME, androidData, REQUEST_UPDATE_FOR_SHELL_APK_VERSION);
+        mClockRule.advance(WebappDataStorage.UPDATE_INTERVAL);
+
+        TestWebApkUpdateManager updateManager = new TestWebApkUpdateManager();
+        updateIfNeeded(WEBAPK_PACKAGE_NAME, updateManager);
+        assertTrue(updateManager.updateCheckStarted());
+
+        ManifestData fetchedData = defaultManifestData();
+        fetchedData.appKey = "another id";
+        // Set a different backgroundColor to trigger an update.
+        fetchedData.backgroundColor = DIFFERENT_BACKGROUND_COLOR;
+        onGotManifestData(updateManager, fetchedData);
+
+        assertTrue(updateManager.updateRequested());
+        assertEquals(WEB_MANIFEST_URL, updateManager.requestedAppKey());
+    }
+
+    /**
+     * Tests that WebAPK updates keeps the default appKey when no value specified from the WebAPK's
+     * Android Manifest <meta-data>.
+     */
+    @Test
+    public void testEmptyManifestAppKeyHasDefault() {
+        ManifestData androidData = defaultManifestData();
+        androidData.id = null;
+        androidData.appKey = null;
+
+        registerWebApk(WEBAPK_PACKAGE_NAME, androidData, REQUEST_UPDATE_FOR_SHELL_APK_VERSION);
+        mClockRule.advance(WebappDataStorage.UPDATE_INTERVAL);
+
+        TestWebApkUpdateManager updateManager = new TestWebApkUpdateManager();
+        updateIfNeeded(WEBAPK_PACKAGE_NAME, updateManager);
+        assertTrue(updateManager.updateCheckStarted());
+
+        onGotDifferentData(updateManager);
+        assertTrue(updateManager.updateRequested());
+        assertEquals(WEB_MANIFEST_URL, updateManager.requestedAppKey());
+    }
 }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebappDirectoryManagerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebappDirectoryManagerTest.java
index 6065c4c..19cf1e6 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebappDirectoryManagerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebappDirectoryManagerTest.java
@@ -22,7 +22,6 @@
 import org.chromium.base.PathUtils;
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.metrics.RecordHistogram;
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
 import org.chromium.base.task.test.CustomShadowAsyncTask;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.Feature;
@@ -35,8 +34,7 @@
  * Tests that directories for WebappActivities are managed correctly.
  */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(manifest = Config.NONE,
-        shadows = {CustomShadowAsyncTask.class, ShadowRecordHistogram.class})
+@Config(manifest = Config.NONE, shadows = {CustomShadowAsyncTask.class})
 @LooperMode(LooperMode.Mode.LEGACY)
 public class WebappDirectoryManagerTest {
     @Rule
diff --git a/chrome/android/webapk/shell_apk/manifest/bound_manifest_config.json b/chrome/android/webapk/shell_apk/manifest/bound_manifest_config.json
index f291ccd..c8309ef3 100644
--- a/chrome/android/webapk/shell_apk/manifest/bound_manifest_config.json
+++ b/chrome/android/webapk/shell_apk/manifest/bound_manifest_config.json
@@ -15,8 +15,8 @@
   "background_color_xml": "#202020",
   "icon_urls_and_icon_murmur2_hashes": "https://doom-fire.com/icons/fire-new-192.png 0 https://doom-fire.com/icons/fire-maskable-512.png 0",
   "web_manifest_url": "https://doom-fire.com/manifest.json",
-  "web_manifest_id": "https://doom-fire.com/",
-  "app_key": "https://doom-fire.com/",
+  "web_manifest_id": "https://doom-fire.com/?utm_source=homescreen",
+  "app_key": "https://doom-fire.com/manifest.json",
   "distributor": "browser",
   "version_code": "1",
   "version_name": "1.0",
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index e99255c7..1c3e4a5 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -3689,6 +3689,8 @@
       "enterprise/connectors/analysis/content_analysis_downloads_delegate.h",
       "enterprise/connectors/analysis/files_request_handler.cc",
       "enterprise/connectors/analysis/files_request_handler.h",
+      "enterprise/connectors/analysis/local_binary_upload_service.cc",
+      "enterprise/connectors/analysis/local_binary_upload_service.h",
       "enterprise/connectors/analysis/page_print_analysis_request.cc",
       "enterprise/connectors/analysis/page_print_analysis_request.h",
       "enterprise/connectors/analysis/request_handler_base.cc",
@@ -4455,6 +4457,7 @@
       "//components/web_modal",
       "//courgette:courgette_lib",
       "//services/device/public/cpp/hid",
+      "//third_party/content_analysis_sdk:liblcasdk",
       "//third_party/sqlite",
       "//third_party/zxcvbn-cpp",
       "//ui/webui/resources/cr_components/app_management:mojo_bindings",
@@ -6563,7 +6566,10 @@
       "//ui/wm",
     ]
     if (is_linux) {
-      deps += [ "//ui/views/linux_ui:linux_ui_factory" ]
+      deps += [
+        "//ui/linux:linux_ui",
+        "//ui/linux:linux_ui_factory",
+      ]
     }
   }
 
@@ -7629,13 +7635,6 @@
     ]
   }
 
-  if (chrome_root_store_supported) {
-    sources += [
-      "feedback/system_logs/log_sources/chrome_root_store_log_source.cc",
-      "feedback/system_logs/log_sources/chrome_root_store_log_source.h",
-    ]
-  }
-
   if (use_udev) {
     deps += [ "//device/udev_linux" ]
   }
diff --git a/chrome/browser/DEPS b/chrome/browser/DEPS
index 5f660333..09979c8 100644
--- a/chrome/browser/DEPS
+++ b/chrome/browser/DEPS
@@ -446,6 +446,7 @@
   "+services/viz/privileged",
   "+skia/ext",
   "+third_party/boringssl/src/include",
+  "+third_party/content_analysis_sdk",
   "+third_party/crashpad",
   "+third_party/cros_system_api",
   "+third_party/metrics_proto",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 44082e8d..d0855d8 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -5517,10 +5517,6 @@
      flag_descriptions::kNtpModulesRedesignedLayoutDescription, kOsDesktop,
      FEATURE_VALUE_TYPE(ntp_features::kNtpModulesRedesignedLayout)},
 
-    {"ntp-modules", flag_descriptions::kNtpModulesName,
-     flag_descriptions::kNtpModulesDescription, kOsDesktop,
-     FEATURE_VALUE_TYPE(ntp_features::kModules)},
-
     {"ntp-photos-module", flag_descriptions::kNtpPhotosModuleName,
      flag_descriptions::kNtpPhotosModuleDescription, kOsDesktop,
      FEATURE_WITH_PARAMS_VALUE_TYPE(ntp_features::kNtpPhotosModule,
@@ -6341,6 +6337,16 @@
      FEATURE_VALUE_TYPE(
          remoting::features::kEnableFrameSinkDesktopCapturerInCrd)},
 
+    {"enable-get-display-media-set", flag_descriptions::kGetDisplayMediaSetName,
+     flag_descriptions::kGetDisplayMediaSetDescription, kOsCrOS,
+     FEATURE_VALUE_TYPE(features::kGetDisplayMediaSet)},
+
+    {"enable-get-display-media-set-auto-select-all-screens",
+     flag_descriptions::kGetDisplayMediaSetAutoSelectAllScreensName,
+     flag_descriptions::kGetDisplayMediaSetAutoSelectAllScreensDescription,
+     kOsCrOS,
+     FEATURE_VALUE_TYPE(features::kGetDisplayMediaSetAutoSelectAllScreens)},
+
     {"multi-monitors-in-crd", flag_descriptions::kMultiMonitorsInCrdName,
      flag_descriptions::kMultiMonitorsInCrdDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(remoting::features::kEnableMultiMonitorsInCrd)},
@@ -8700,6 +8706,13 @@
      FEATURE_VALUE_TYPE(app_list_features::kLauncherPlayStoreSearch)},
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
+#if BUILDFLAG(IS_ANDROID)
+    {"safe-mode-for-cached-flags",
+     flag_descriptions::kSafeModeForCachedFlagsName,
+     flag_descriptions::kSafeModeForCachedFlagsDescription, kOsAndroid,
+     FEATURE_VALUE_TYPE(chrome::android::kSafeModeForCachedFlags)},
+#endif  // BUILDFLAG(IS_ANDROID)
+
 #if !BUILDFLAG(IS_ANDROID)
     {media_router::switches::kAccessCodeCastDeviceDurationSwitch,
      flag_descriptions::kAccessCodeCastDeviceDurationName,
@@ -8721,12 +8734,6 @@
          kAutofillSaveCardUiExperimentOptions,
          "AutofillSaveCardUiExperiment")},
 
-#if BUILDFLAG(IS_MAC)
-    {"force-60hz", flag_descriptions::kForce60HzName,
-     flag_descriptions::kForce60HzDescription, kOsMac,
-     FEATURE_VALUE_TYPE(display::features::kForce60Hz)},
-#endif  // BUILDFLAG(IS_MAC)
-
 #if BUILDFLAG(IS_ANDROID)
     {"network-service-in-process",
      flag_descriptions::kNetworkServiceInProcessName,
diff --git a/chrome/browser/android/browserservices/intents/java/src/org/chromium/chrome/browser/browserservices/intents/MergedWebappInfo.java b/chrome/browser/android/browserservices/intents/java/src/org/chromium/chrome/browser/browserservices/intents/MergedWebappInfo.java
index 97116e8e2..35bf7f0 100644
--- a/chrome/browser/android/browserservices/intents/java/src/org/chromium/chrome/browser/browserservices/intents/MergedWebappInfo.java
+++ b/chrome/browser/android/browserservices/intents/java/src/org/chromium/chrome/browser/browserservices/intents/MergedWebappInfo.java
@@ -113,4 +113,9 @@
         }
         return super.iconUrlToMurmur2HashMap();
     }
+
+    @Override
+    public String appKey() {
+        return mOldWebappInfo.appKey();
+    }
 }
diff --git a/chrome/browser/android/examples/custom_tabs_client/src/java/org/chromium/customtabsclient/MainActivity.java b/chrome/browser/android/examples/custom_tabs_client/src/java/org/chromium/customtabsclient/MainActivity.java
index ceb64af..3238393 100644
--- a/chrome/browser/android/examples/custom_tabs_client/src/java/org/chromium/customtabsclient/MainActivity.java
+++ b/chrome/browser/android/examples/custom_tabs_client/src/java/org/chromium/customtabsclient/MainActivity.java
@@ -19,6 +19,7 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.text.TextUtils;
+import android.util.Log;
 import android.util.Pair;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -33,8 +34,6 @@
 import android.widget.Spinner;
 import android.widget.TextView;
 
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 import androidx.appcompat.app.AppCompatActivity;
 import androidx.browser.customtabs.CustomTabsCallback;
 import androidx.browser.customtabs.CustomTabsClient;
@@ -44,7 +43,6 @@
 
 import com.google.android.material.button.MaterialButtonToggleGroup;
 
-import org.chromium.base.Log;
 import org.chromium.customtabsclient.shared.CustomTabsHelper;
 import org.chromium.customtabsclient.shared.ServiceConnection;
 import org.chromium.customtabsclient.shared.ServiceConnectionCallback;
@@ -57,7 +55,7 @@
  */
 public class MainActivity
         extends AppCompatActivity implements OnClickListener, ServiceConnectionCallback {
-    private static final String TAG = "CustomTabsClientEx";
+    private static final String TAG = "CustomTabsClientExample";
     private static final String TOOLBAR_COLOR = "#ef6c00";
 
     private EditText mEditText;
@@ -108,15 +106,6 @@
         public void onNavigationEvent(int navigationEvent, Bundle extras) {
             Log.w(TAG, "onNavigationEvent: Code = " + navigationEvent);
         }
-
-        @Override
-        public void extraCallback(@NonNull String callbackName, @Nullable Bundle args) {
-            if (callbackName.equals("onVerticalScrollEvent") && args != null) {
-                Log.w(TAG,
-                        "onVerticalScrollEvent: isDirectionUp = "
-                                + args.getBoolean("isDirectionUp"));
-            }
-        }
     }
 
     @Override
diff --git a/chrome/browser/android/explore_sites/ntp_json_fetcher.cc b/chrome/browser/android/explore_sites/ntp_json_fetcher.cc
index c04e897..46bb434 100644
--- a/chrome/browser/android/explore_sites/ntp_json_fetcher.cc
+++ b/chrome/browser/android/explore_sites/ntp_json_fetcher.cc
@@ -100,17 +100,17 @@
 
 void NTPJsonFetcher::OnJsonParse(
     data_decoder::DataDecoder::ValueOrError result) {
-  if (!result.value) {
-    OnJsonParseError(*result.error);
+  if (!result.has_value()) {
+    OnJsonParseError(result.error());
     return;
   }
 
-  if (!result.value->is_dict()) {
+  if (!result->is_dict()) {
     OnJsonParseError("Parsed JSON is not a dictionary.");
     return;
   }
 
-  std::move(callback_).Run(NTPCatalog::create(*result.value));
+  std::move(callback_).Run(NTPCatalog::create(*result));
 }
 
 void NTPJsonFetcher::OnJsonParseError(const std::string& error) {
diff --git a/chrome/browser/android/webapk/webapk_installer.cc b/chrome/browser/android/webapk/webapk_installer.cc
index b8f2273..974bd93 100644
--- a/chrome/browser/android/webapk/webapk_installer.cc
+++ b/chrome/browser/android/webapk/webapk_installer.cc
@@ -220,6 +220,7 @@
 void WebApkInstaller::StoreUpdateRequestToFile(
     const base::FilePath& update_request_path,
     const webapps::ShortcutInfo& shortcut_info,
+    const std::string& app_key,
     const std::string& primary_icon_data,
     bool is_primary_icon_maskable,
     const std::string& splash_icon_data,
@@ -233,12 +234,12 @@
     base::OnceCallback<void(bool)> callback) {
   base::PostTaskAndReplyWithResult(
       GetBackgroundTaskRunner().get(), FROM_HERE,
-      base::BindOnce(&webapps::StoreUpdateRequestToFileInBackground,
-                     update_request_path, shortcut_info, primary_icon_data,
-                     is_primary_icon_maskable, splash_icon_data, package_name,
-                     version, std::move(icon_url_to_murmur2_hash),
-                     is_manifest_stale, is_app_identity_update_supported,
-                     std::move(update_reasons)),
+      base::BindOnce(
+          &webapps::StoreUpdateRequestToFileInBackground, update_request_path,
+          shortcut_info, app_key, primary_icon_data, is_primary_icon_maskable,
+          splash_icon_data, package_name, version,
+          std::move(icon_url_to_murmur2_hash), is_manifest_stale,
+          is_app_identity_update_supported, std::move(update_reasons)),
       std::move(callback));
 }
 
diff --git a/chrome/browser/android/webapk/webapk_installer.h b/chrome/browser/android/webapk/webapk_installer.h
index 419ab5d..dbc2f4d9 100644
--- a/chrome/browser/android/webapk/webapk_installer.h
+++ b/chrome/browser/android/webapk/webapk_installer.h
@@ -150,6 +150,7 @@
   static void StoreUpdateRequestToFile(
       const base::FilePath& update_request_path,
       const webapps::ShortcutInfo& shortcut_info,
+      const std::string& app_key,
       const std::string& primary_icon_data,
       bool is_primary_icon_maskable,
       const std::string& splash_icon_data,
diff --git a/chrome/browser/android/webapk/webapk_installer_unittest.cc b/chrome/browser/android/webapk/webapk_installer_unittest.cc
index 2de4267..c5d901c 100644
--- a/chrome/browser/android/webapk/webapk_installer_unittest.cc
+++ b/chrome/browser/android/webapk/webapk_installer_unittest.cc
@@ -194,8 +194,8 @@
     base::RunLoop run_loop;
     quit_closure_ = run_loop.QuitClosure();
     WebApkInstaller::StoreUpdateRequestToFile(
-        update_request_path, webapps::ShortcutInfo((GURL())), "", false, "", "",
-        "", std::map<std::string, webapps::WebApkIconHasher::Icon>(), false,
+        update_request_path, webapps::ShortcutInfo((GURL())), "", "", false, "",
+        "", "", std::map<std::string, webapps::WebApkIconHasher::Icon>(), false,
         false, {webapps::WebApkUpdateReason::PRIMARY_ICON_HASH_DIFFERS},
         base::BindOnce(&UpdateRequestStorer::OnComplete,
                        base::Unretained(this)));
diff --git a/chrome/browser/android/webapk/webapk_update_data_fetcher.cc b/chrome/browser/android/webapk/webapk_update_data_fetcher.cc
index 1e7dc71..dae2401 100644
--- a/chrome/browser/android/webapk/webapk_update_data_fetcher.cc
+++ b/chrome/browser/android/webapk/webapk_update_data_fetcher.cc
@@ -13,6 +13,7 @@
 #include "base/android/jni_array.h"
 #include "base/android/jni_string.h"
 #include "base/bind.h"
+#include "base/feature_list.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/android/chrome_jni_headers/WebApkUpdateDataFetcher_jni.h"
 #include "chrome/browser/profiles/profile.h"
@@ -50,22 +51,31 @@
     JNIEnv* env,
     const JavaParamRef<jobject>& obj,
     const JavaParamRef<jstring>& java_scope_url,
-    const JavaParamRef<jstring>& java_web_manifest_url) {
+    const JavaParamRef<jstring>& java_web_manifest_url,
+    const JavaParamRef<jstring>& java_web_manifest_id) {
   GURL scope(base::android::ConvertJavaStringToUTF8(env, java_scope_url));
   GURL web_manifest_url(
       base::android::ConvertJavaStringToUTF8(env, java_web_manifest_url));
-  WebApkUpdateDataFetcher* fetcher =
-      new WebApkUpdateDataFetcher(env, obj, scope, web_manifest_url);
+  std::string web_manifest_id;
+  if (!java_web_manifest_id.is_null()) {
+    web_manifest_id =
+        base::android::ConvertJavaStringToUTF8(env, java_web_manifest_id);
+  }
+  WebApkUpdateDataFetcher* fetcher = new WebApkUpdateDataFetcher(
+      env, obj, scope, web_manifest_url, web_manifest_id);
   return reinterpret_cast<intptr_t>(fetcher);
 }
 
-WebApkUpdateDataFetcher::WebApkUpdateDataFetcher(JNIEnv* env,
-                                                 jobject obj,
-                                                 const GURL& scope,
-                                                 const GURL& web_manifest_url)
+WebApkUpdateDataFetcher::WebApkUpdateDataFetcher(
+    JNIEnv* env,
+    jobject obj,
+    const GURL& scope,
+    const GURL& web_manifest_url,
+    const std::string& web_manifest_id)
     : content::WebContentsObserver(nullptr),
       scope_(scope),
       web_manifest_url_(web_manifest_url),
+      web_manifest_id_(web_manifest_id),
       info_(GURL()),
       is_primary_icon_maskable_(false),
       is_splash_icon_maskable_(false) {
@@ -146,12 +156,25 @@
   // web developers to change the Web Manifest location. When it does
   // change, we will treat the new Web Manifest as the one of another WebAPK.
   if (!data.NoBlockingErrors() || blink::IsEmptyManifest(data.manifest) ||
-      web_manifest_url_ != data.manifest_url ||
       !webapps::WebappsUtils::AreWebManifestUrlsWebApkCompatible(
           data.manifest)) {
     return;
   }
 
+  if (!base::FeatureList::IsEnabled(webapps::features::kWebApkUniqueId) &&
+      web_manifest_url_ != data.manifest_url) {
+    return;
+  }
+
+  // If the fetched manifest id is different from the current one, we also
+  // continue observing as the id is the identity for the application. We
+  // will treat the manifest with different id as the one of another WebAPK.
+  if (base::FeatureList::IsEnabled(webapps::features::kWebApkUniqueId) &&
+      !web_manifest_id_.empty() &&
+      web_manifest_id_ != webapps::ShortcutInfo::GetManifestId(data.manifest)) {
+    return;
+  }
+
   info_.UpdateFromManifest(data.manifest);
   info_.manifest_url = data.manifest_url;
   info_.best_primary_icon_url = data.primary_icon_url;
@@ -202,6 +225,8 @@
       base::android::ConvertUTF16ToJavaString(env, info_.name);
   ScopedJavaLocalRef<jstring> java_short_name =
       base::android::ConvertUTF16ToJavaString(env, info_.short_name);
+  ScopedJavaLocalRef<jstring> java_manifest_id =
+      base::android::ConvertUTF8ToJavaString(env, info_.manifest_id);
   ScopedJavaLocalRef<jstring> java_primary_icon_url =
       base::android::ConvertUTF8ToJavaString(
           env, info_.best_primary_icon_url.spec());
@@ -290,8 +315,8 @@
 
   Java_WebApkUpdateDataFetcher_onDataAvailable(
       env, java_ref_, java_url, java_scope, java_name, java_short_name,
-      java_primary_icon_url, java_primary_icon_murmur2_hash, java_primary_icon,
-      java_is_primary_icon_maskable, java_splash_icon_url,
+      java_manifest_id, java_primary_icon_url, java_primary_icon_murmur2_hash,
+      java_primary_icon, java_is_primary_icon_maskable, java_splash_icon_url,
       java_splash_icon_murmur2_hash, java_splash_icon,
       java_is_splash_icon_maskable, java_icon_urls,
       static_cast<int>(info_.display), static_cast<int>(info_.orientation),
diff --git a/chrome/browser/android/webapk/webapk_update_data_fetcher.h b/chrome/browser/android/webapk/webapk_update_data_fetcher.h
index 068541cee9..3c4b623 100644
--- a/chrome/browser/android/webapk/webapk_update_data_fetcher.h
+++ b/chrome/browser/android/webapk/webapk_update_data_fetcher.h
@@ -32,7 +32,8 @@
   WebApkUpdateDataFetcher(JNIEnv* env,
                           jobject obj,
                           const GURL& scope,
-                          const GURL& web_manifest_url);
+                          const GURL& web_manifest_url,
+                          const std::string& web_manifest_id);
 
   WebApkUpdateDataFetcher(const WebApkUpdateDataFetcher&) = delete;
   WebApkUpdateDataFetcher& operator=(const WebApkUpdateDataFetcher&) = delete;
@@ -82,6 +83,9 @@
   // The WebAPK's Web Manifest URL that the detector is looking for.
   const GURL web_manifest_url_;
 
+  // The WebAPK's Web Manifest ID that the detector is looking for.
+  const std::string web_manifest_id_;
+
   // The URL for which the installable data is being fetched / was last fetched.
   GURL last_fetched_url_;
 
diff --git a/chrome/browser/android/webapk/webapk_update_manager.cc b/chrome/browser/android/webapk/webapk_update_manager.cc
index 3169dd0..5225d2bf 100644
--- a/chrome/browser/android/webapk/webapk_update_manager.cc
+++ b/chrome/browser/android/webapk/webapk_update_manager.cc
@@ -64,6 +64,8 @@
     const JavaParamRef<jstring>& java_scope,
     const JavaParamRef<jstring>& java_name,
     const JavaParamRef<jstring>& java_short_name,
+    const JavaParamRef<jstring>& java_manifest_id,
+    const JavaParamRef<jstring>& java_app_key,
     const JavaParamRef<jstring>& java_primary_icon_url,
     const JavaParamRef<jstring>& java_primary_icon_data,
     jboolean java_is_primary_icon_maskable,
@@ -114,6 +116,8 @@
       GURL(ConvertJavaStringToUTF8(env, java_splash_icon_url));
   info.is_splash_image_maskable = java_is_splash_icon_maskable;
   info.manifest_url = GURL(ConvertJavaStringToUTF8(env, java_web_manifest_url));
+  info.manifest_id = ConvertJavaStringToUTF8(env, java_manifest_id);
+  std::string app_key = ConvertJavaStringToUTF8(env, java_app_key);
 
   GURL share_target_action =
       GURL(ConvertJavaStringToUTF8(env, java_share_target_action));
@@ -217,7 +221,7 @@
         static_cast<webapps::WebApkUpdateReason>(update_reason));
 
   WebApkInstaller::StoreUpdateRequestToFile(
-      base::FilePath(update_request_path), info, primary_icon_data,
+      base::FilePath(update_request_path), info, app_key, primary_icon_data,
       java_is_primary_icon_maskable, splash_icon_data, webapk_package,
       base::NumberToString(java_webapk_version),
       std::move(icon_url_to_murmur2_hash), java_is_manifest_stale,
diff --git a/chrome/browser/ash/accessibility/select_to_speak_browsertest.cc b/chrome/browser/ash/accessibility/select_to_speak_browsertest.cc
index 73bfa4a..7ae402a 100644
--- a/chrome/browser/ash/accessibility/select_to_speak_browsertest.cc
+++ b/chrome/browser/ash/accessibility/select_to_speak_browsertest.cc
@@ -641,16 +641,10 @@
   sm_.Replay();
 }
 
-// Flaky on ChromeOS MSAN bots: https://crbug.com/1227368
-#if defined(MEMORY_SANITIZER)
-#define MAYBE_SelectToSpeakDoesNotDismissTrayBubble \
-  DISABLED_SelectToSpeakDoesNotDismissTrayBubble
-#else
-#define MAYBE_SelectToSpeakDoesNotDismissTrayBubble \
-  SelectToSpeakDoesNotDismissTrayBubble
-#endif
+// TODO(crbug.com/1227368): Flaky on ChromeOS MSAN bots.
+// TODO(crbug.com/1344562): Flaky on other CrOS bots too.
 IN_PROC_BROWSER_TEST_F(SelectToSpeakTest,
-                       MAYBE_SelectToSpeakDoesNotDismissTrayBubble) {
+                       DISABLED_SelectToSpeakDoesNotDismissTrayBubble) {
   // Open tray bubble menu.
   tray_test_api_->ShowBubble();
 
diff --git a/chrome/browser/ash/arc/policy/arc_policy_bridge.cc b/chrome/browser/ash/arc/policy/arc_policy_bridge.cc
index 350dc4e..9032aaf 100644
--- a/chrome/browser/ash/arc/policy/arc_policy_bridge.cc
+++ b/chrome/browser/ash/arc/policy/arc_policy_bridge.cc
@@ -703,7 +703,7 @@
 void ArcPolicyBridge::OnReportComplianceParse(
     base::OnceCallback<void(const std::string&)> callback,
     data_decoder::DataDecoder::ValueOrError result) {
-  if (!result.value) {
+  if (!result.has_value()) {
     // TODO(poromov@): Report to histogram.
     DLOG(ERROR) << "Can't parse policy compliance report";
     std::move(callback).Run(kPolicyCompliantJson);
@@ -716,10 +716,10 @@
       prefs::kArcPolicyComplianceReported, true);
 
   const base::DictionaryValue* dict = nullptr;
-  if (result.value->GetAsDictionary(&dict)) {
+  if (result->GetAsDictionary(&dict)) {
     UpdateComplianceReportMetrics(dict);
     for (Observer& observer : observers_) {
-      observer.OnComplianceReportReceived(&result.value.value());
+      observer.OnComplianceReportReceived(&*result);
     }
   }
 }
diff --git a/chrome/browser/ash/borealis/borealis_features.cc b/chrome/browser/ash/borealis/borealis_features.cc
index bef4048a..3b2a62e 100644
--- a/chrome/browser/ash/borealis/borealis_features.cc
+++ b/chrome/browser/ash/borealis/borealis_features.cc
@@ -35,7 +35,7 @@
 
 namespace {
 
-constexpr int64_t kGibi = 1024 * 1024 * 1024;
+constexpr uint64_t kGibi = 1024ull * 1024 * 1024;
 
 // Used to make it difficult to tell what someone's token is based on their
 // prefs.
diff --git a/chrome/browser/ash/borealis/borealis_features_unittest.cc b/chrome/browser/ash/borealis/borealis_features_unittest.cc
index 28b2c75..98c05e76 100644
--- a/chrome/browser/ash/borealis/borealis_features_unittest.cc
+++ b/chrome/browser/ash/borealis/borealis_features_unittest.cc
@@ -135,11 +135,11 @@
   EXPECT_TRUE(checker.CpuRegexMatches("cp"));
   EXPECT_TRUE(checker.CpuRegexMatches("^[a-z]*$"));
 
-  EXPECT_TRUE(checker.HasMemory(-1));
+  EXPECT_TRUE(checker.HasMemory(0));
   EXPECT_TRUE(checker.HasMemory(41));
   EXPECT_TRUE(checker.HasMemory(42));
   EXPECT_FALSE(checker.HasMemory(43));
-  EXPECT_FALSE(checker.HasMemory(std::numeric_limits<int64_t>::max()));
+  EXPECT_FALSE(checker.HasMemory(std::numeric_limits<uint64_t>::max()));
 }
 
 TEST(BorealisFeaturesUtilTest, DataCanBeBuilt) {
@@ -160,7 +160,7 @@
         // Faking CPU and RAM are not supported, so just assert they have some
         // trivial values.
         EXPECT_NE(data.cpu, "");
-        EXPECT_GT(data.memory, 0);
+        EXPECT_GT(data.memory, 0u);
         loop.Quit();
       }));
   loop.Run();
diff --git a/chrome/browser/ash/borealis/borealis_features_util.cc b/chrome/browser/ash/borealis/borealis_features_util.cc
index 6ae51eec..2e9b9cc9 100644
--- a/chrome/browser/ash/borealis/borealis_features_util.cc
+++ b/chrome/browser/ash/borealis/borealis_features_util.cc
@@ -91,7 +91,7 @@
                                  std::string board,
                                  std::string model,
                                  std::string cpu,
-                                 int64_t memory)
+                                 uint64_t memory)
     : token_hash(std::move(token_hash)),
       board(std::move(board)),
       model(std::move(model)),
@@ -176,7 +176,7 @@
   return RE2::PartialMatch(token_hardware_.cpu, cpu_regex);
 }
 
-bool TokenHardwareChecker::HasMemory(int64_t mem_bytes) const {
+bool TokenHardwareChecker::HasMemory(uint64_t mem_bytes) const {
   return token_hardware_.memory >= mem_bytes;
 }
 
diff --git a/chrome/browser/ash/borealis/borealis_features_util.h b/chrome/browser/ash/borealis/borealis_features_util.h
index 1bd73fb..d0311b8f 100644
--- a/chrome/browser/ash/borealis/borealis_features_util.h
+++ b/chrome/browser/ash/borealis/borealis_features_util.h
@@ -23,7 +23,7 @@
          std::string board,
          std::string model,
          std::string cpu,
-         int64_t memory);
+         uint64_t memory);
     Data(const Data& other);
     ~Data();
 
@@ -31,7 +31,7 @@
     std::string board;
     std::string model;
     std::string cpu;
-    int64_t memory;
+    uint64_t memory;
   };
 
   // A hashing function used for creating tokens.
@@ -53,7 +53,7 @@
   bool IsModel(const std::string& model) const;
   bool ModelIn(base::flat_set<std::string> models) const;
   bool CpuRegexMatches(const std::string& cpu_regex) const;
-  bool HasMemory(int64_t mem_bytes) const;
+  bool HasMemory(uint64_t mem_bytes) const;
 
  private:
   const Data token_hardware_;
diff --git a/chrome/browser/ash/enhanced_network_tts/enhanced_network_tts_impl.cc b/chrome/browser/ash/enhanced_network_tts/enhanced_network_tts_impl.cc
index 36031ec8..4aad69d 100644
--- a/chrome/browser/ash/enhanced_network_tts/enhanced_network_tts_impl.cc
+++ b/chrome/browser/ash/enhanced_network_tts/enhanced_network_tts_impl.cc
@@ -196,16 +196,18 @@
     const bool is_last_request,
     data_decoder::DataDecoder::ValueOrError result) {
   // Extract results for the request.
-  if (result.value && result.value->is_list()) {
-    SendResponse(UnpackJsonResponse(result.value->GetList(), start_index,
-                                    is_last_request));
+  if (result.has_value() && result->is_list()) {
+    SendResponse(
+        UnpackJsonResponse(result->GetList(), start_index, is_last_request));
     // Only start the next request after finishing the current one. This method
     // will also reset the internal state if there is no more request.
     ProcessNextServerRequest();
   } else {
     ResetAndSendErrorResponse(mojom::TtsRequestError::kReceivedUnexpectedData);
     DVLOG(1) << "Parsing server response JSON failed with error: "
-             << result.error.value_or("No reason reported.");
+             << (!result.has_value() || result.error().empty()
+                     ? "No reason reported."
+                     : result.error());
   }
 }
 
diff --git a/chrome/browser/ash/login/screens/recommend_apps/recommend_apps_fetcher_impl.cc b/chrome/browser/ash/login/screens/recommend_apps/recommend_apps_fetcher_impl.cc
index e16884b..b70b344 100644
--- a/chrome/browser/ash/login/screens/recommend_apps/recommend_apps_fetcher_impl.cc
+++ b/chrome/browser/ash/login/screens/recommend_apps/recommend_apps_fetcher_impl.cc
@@ -629,21 +629,21 @@
 void RecommendAppsFetcherImpl::OnJsonParsed(
     data_decoder::DataDecoder::ValueOrError result) {
   if (base::FeatureList::IsEnabled(features::kAppDiscoveryForOobe)) {
-    if (!result.value) {
+    if (!result.has_value()) {
       delegate_->OnParseResponseError();
       return;
     }
-    delegate_->OnLoadSuccess(std::move(*result.value));
+    delegate_->OnLoadSuccess(std::move(*result));
     return;
   }
 
-  if (!result.value || (!result.value->is_list() && !result.value->is_dict())) {
+  if (!result.has_value() || (!result->is_list() && !result->is_dict())) {
     RecordUmaResponseParseResult(
         RECOMMEND_APPS_RESPONSE_PARSE_RESULT_INVALID_JSON);
     delegate_->OnParseResponseError();
     return;
   }
-  absl::optional<base::Value> output = ParseResponse(*result.value);
+  absl::optional<base::Value> output = ParseResponse(*result);
   if (!output.has_value()) {
     RecordUmaResponseAppCount(0);
     delegate_->OnParseResponseError();
diff --git a/chrome/browser/ash/policy/status_collector/device_status_collector.h b/chrome/browser/ash/policy/status_collector/device_status_collector.h
index fcadffd..02487e3 100644
--- a/chrome/browser/ash/policy/status_collector/device_status_collector.h
+++ b/chrome/browser/ash/policy/status_collector/device_status_collector.h
@@ -373,7 +373,7 @@
   struct MemoryUsage {
     // Amount of free RAM (measures raw memory used by processes, not internal
     // memory waiting to be reclaimed by GC).
-    int64_t bytes_of_ram_free;
+    uint64_t bytes_of_ram_free;
 
     // Sampling timestamp.
     base::Time timestamp;
diff --git a/chrome/browser/ash/policy/status_collector/legacy_device_status_collector.h b/chrome/browser/ash/policy/status_collector/legacy_device_status_collector.h
index 47e09b8..d5d21d1 100644
--- a/chrome/browser/ash/policy/status_collector/legacy_device_status_collector.h
+++ b/chrome/browser/ash/policy/status_collector/legacy_device_status_collector.h
@@ -342,7 +342,7 @@
 
     // Amount of free RAM (measures raw memory used by processes, not internal
     // memory waiting to be reclaimed by GC).
-    int64_t bytes_of_ram_free;
+    uint64_t bytes_of_ram_free;
 
     // Sampling timestamp.
     base::Time timestamp;
diff --git a/chrome/browser/ash/power/ml/smart_dim/download_worker.cc b/chrome/browser/ash/power/ml/smart_dim/download_worker.cc
index 0c8a2b76..fb3ae85e 100644
--- a/chrome/browser/ash/power/ml/smart_dim/download_worker.cc
+++ b/chrome/browser/ash/power/ml/smart_dim/download_worker.cc
@@ -88,8 +88,8 @@
     const std::string& model_flatbuffer,
     const data_decoder::DataDecoder::ValueOrError result) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  if (!result.value || !result.value->is_dict() ||
-      !ParseMetaInfoFromJsonObject(result.value.value(), &metrics_model_name_,
+  if (!result.has_value() || !result->is_dict() ||
+      !ParseMetaInfoFromJsonObject(*result, &metrics_model_name_,
                                    &dim_threshold_, &expected_feature_size_,
                                    &inputs_, &outputs_)) {
     LogLoadComponentEvent(LoadComponentEvent::kLoadMetadataError);
diff --git a/chrome/browser/ash/system_extensions/system_extensions_sandboxed_unpacker.cc b/chrome/browser/ash/system_extensions/system_extensions_sandboxed_unpacker.cc
index 42b6cf4..c2a19e1 100644
--- a/chrome/browser/ash/system_extensions/system_extensions_sandboxed_unpacker.cc
+++ b/chrome/browser/ash/system_extensions/system_extensions_sandboxed_unpacker.cc
@@ -89,13 +89,13 @@
 void SystemExtensionsSandboxedUnpacker::OnSystemExtensionManifestParsed(
     GetSystemExtensionFromCallback callback,
     data_decoder::DataDecoder::ValueOrError value_or_error) {
-  if (value_or_error.error.has_value()) {
+  if (!value_or_error.has_value()) {
     std::move(callback).Run(
         SystemExtensionsInstallStatus::kFailedJsonErrorParsingManifest);
     return;
   }
 
-  base::Value& parsed_manifest = value_or_error.value.value();
+  base::Value& parsed_manifest = *value_or_error;
 
   SystemExtension system_extension;
 
diff --git a/chrome/browser/ash/wallpaper_handlers/wallpaper_handlers.cc b/chrome/browser/ash/wallpaper_handlers/wallpaper_handlers.cc
index 9b74bc60..a04c5e2 100644
--- a/chrome/browser/ash/wallpaper_handlers/wallpaper_handlers.cc
+++ b/chrome/browser/ash/wallpaper_handlers/wallpaper_handlers.cc
@@ -690,14 +690,16 @@
       *response_body,
       base::BindOnce(
           [](const GURL& service_url,
-             data_decoder::DataDecoder::ValueOrError result) {
-            if (result.error.has_value()) {
+             data_decoder::DataDecoder::ValueOrError result)
+              -> absl::optional<base::Value> {
+            if (!result.has_value()) {
               LOG(ERROR) << "Failed to parse JSON response from Google Photos "
                             "API request to "
                          << service_url.spec()
-                         << ". Error message: " << result.error.value();
+                         << ". Error message: " << result.error();
+              return absl::nullopt;
             }
-            return std::move(result.value);
+            return std::move(*result);
           },
           service_url)
           .Then(base::BindOnce(&GooglePhotosFetcher::OnResponseReady,
diff --git a/chrome/browser/autofill/autofill_context_menu_manager_unittest.cc b/chrome/browser/autofill/autofill_context_menu_manager_unittest.cc
index d50edb1..5090b1d 100644
--- a/chrome/browser/autofill/autofill_context_menu_manager_unittest.cc
+++ b/chrome/browser/autofill/autofill_context_menu_manager_unittest.cc
@@ -122,10 +122,10 @@
 
   // Check for submenu with address details.
   auto* address_details_submenu = address_menu_model->GetSubmenuModelAt(0);
-  ASSERT_EQ(address_details_submenu->GetItemCount(), 6);
+  ASSERT_EQ(address_details_submenu->GetItemCount(), 8);
   static constexpr std::array expected_address_values = {
-      u"666 Erebus St.\nApt 8", u"Elysium",          u"91111", u"",
-      u"16502111111",           u"johndoe@hades.com"};
+      u"John H. Doe", u"", u"666 Erebus St.\nApt 8", u"Elysium",
+      u"91111",       u"", u"16502111111",           u"johndoe@hades.com"};
   for (size_t i = 0; i < expected_address_values.size(); i++) {
     ASSERT_EQ(address_details_submenu->GetLabelAt(i),
               expected_address_values[i]);
diff --git a/chrome/browser/browser_switcher/ieem_sitelist_parser.cc b/chrome/browser/browser_switcher/ieem_sitelist_parser.cc
index 53de1fe3..f1dd31e 100644
--- a/chrome/browser/browser_switcher/ieem_sitelist_parser.cc
+++ b/chrome/browser/browser_switcher/ieem_sitelist_parser.cc
@@ -189,21 +189,20 @@
 void RawXmlParsed(ParsingMode parsing_mode,
                   base::OnceCallback<void(ParsedXml)> callback,
                   data_decoder::DataDecoder::ValueOrError xml) {
-  if (!xml.value) {
+  if (!xml.has_value()) {
     // Copies the string, but it should only be around 20 characters.
-    std::move(callback).Run(ParsedXml({}, {}, *xml.error));
+    std::move(callback).Run(ParsedXml({}, {}, xml.error()));
     return;
   }
   DCHECK(data_decoder::IsXmlElementOfType(
-      *xml.value, data_decoder::mojom::XmlParser::kElementType));
+      *xml, data_decoder::mojom::XmlParser::kElementType));
   ParsedXml result;
-  if (data_decoder::IsXmlElementNamed(*xml.value, kSchema1RulesElement)) {
+  if (data_decoder::IsXmlElementNamed(*xml, kSchema1RulesElement)) {
     // Enterprise Mode schema v.1 has <rules> element at its top level.
-    ParseIeFileVersionOne(*xml.value, parsing_mode, &result);
-  } else if (data_decoder::IsXmlElementNamed(*xml.value,
-                                             kSchema2SiteListElement)) {
+    ParseIeFileVersionOne(*xml, parsing_mode, &result);
+  } else if (data_decoder::IsXmlElementNamed(*xml, kSchema2SiteListElement)) {
     // Enterprise Mode schema v.2 has <site-list> element at its top level.
-    ParseIeFileVersionTwo(*xml.value, parsing_mode, &result);
+    ParseIeFileVersionTwo(*xml, parsing_mode, &result);
   } else {
     result.error = kInvalidRootElement;
   }
diff --git a/chrome/browser/cart/cart_service_browsertest.cc b/chrome/browser/cart/cart_service_browsertest.cc
index 8c9df5da..ce3ab11 100644
--- a/chrome/browser/cart/cart_service_browsertest.cc
+++ b/chrome/browser/cart/cart_service_browsertest.cc
@@ -60,7 +60,7 @@
  public:
   void SetUpInProcessBrowserTestFixture() override {
     scoped_feature_list_.InitWithFeatures(
-        {ntp_features::kModules, ntp_features::kNtpChromeCartModule,
+        {ntp_features::kNtpChromeCartModule,
          optimization_guide::features::kOptimizationHints},
         {});
   }
diff --git a/chrome/browser/commerce/merchant_viewer/android/javatests/src/org/chromium/chrome/browser/merchant_viewer/MerchantTrustMetricsTest.java b/chrome/browser/commerce/merchant_viewer/android/javatests/src/org/chromium/chrome/browser/merchant_viewer/MerchantTrustMetricsTest.java
index cb6f294af..d4820a5 100644
--- a/chrome/browser/commerce/merchant_viewer/android/javatests/src/org/chromium/chrome/browser/merchant_viewer/MerchantTrustMetricsTest.java
+++ b/chrome/browser/commerce/merchant_viewer/android/javatests/src/org/chromium/chrome/browser/merchant_viewer/MerchantTrustMetricsTest.java
@@ -15,7 +15,7 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.metrics.RecordHistogram;
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.test.util.browser.Features;
 
@@ -23,7 +23,7 @@
  * Tests for {@link MerchantTrustMessageContext}.
  */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(manifest = Config.NONE, shadows = {ShadowRecordHistogram.class})
+@Config(manifest = Config.NONE)
 public class MerchantTrustMetricsTest {
     @Rule
     public TestRule mProcessor = new Features.JUnitProcessor();
@@ -32,7 +32,7 @@
 
     @Before
     public void setUp() {
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
         mMetrics = new MerchantTrustMetrics();
     }
 
diff --git a/chrome/browser/commerce/subscriptions/test/android/java/src/org/chromium/chrome/browser/subscriptions/CommerceSubscriptionsServiceUnitTest.java b/chrome/browser/commerce/subscriptions/test/android/java/src/org/chromium/chrome/browser/subscriptions/CommerceSubscriptionsServiceUnitTest.java
index 0fd2eb6..c9b3ee1 100644
--- a/chrome/browser/commerce/subscriptions/test/android/java/src/org/chromium/chrome/browser/subscriptions/CommerceSubscriptionsServiceUnitTest.java
+++ b/chrome/browser/commerce/subscriptions/test/android/java/src/org/chromium/chrome/browser/subscriptions/CommerceSubscriptionsServiceUnitTest.java
@@ -33,7 +33,7 @@
 import org.chromium.base.Callback;
 import org.chromium.base.FeatureList;
 import org.chromium.base.metrics.RecordHistogram;
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.JniMocker;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
@@ -67,7 +67,7 @@
  * Unit tests for {@link CommerceSubscriptionsService}.
  */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(manifest = Config.NONE, shadows = {ShadowRecordHistogram.class})
+@Config(manifest = Config.NONE)
 public class CommerceSubscriptionsServiceUnitTest {
     @Rule
     public TestRule mProcessor = new Features.JUnitProcessor();
@@ -111,7 +111,7 @@
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
 
         doNothing().when(mActivityLifecycleDispatcher).register(any());
         doNothing().when(mSubscriptionsManager).getSubscriptions(anyString(), anyBoolean(), any());
diff --git a/chrome/browser/commerce/subscriptions/test/android/java/src/org/chromium/chrome/browser/subscriptions/ImplicitPriceDropSubscriptionsManagerUnitTest.java b/chrome/browser/commerce/subscriptions/test/android/java/src/org/chromium/chrome/browser/subscriptions/ImplicitPriceDropSubscriptionsManagerUnitTest.java
index 8a0499a3..5770a861 100644
--- a/chrome/browser/commerce/subscriptions/test/android/java/src/org/chromium/chrome/browser/subscriptions/ImplicitPriceDropSubscriptionsManagerUnitTest.java
+++ b/chrome/browser/commerce/subscriptions/test/android/java/src/org/chromium/chrome/browser/subscriptions/ImplicitPriceDropSubscriptionsManagerUnitTest.java
@@ -28,7 +28,7 @@
 import org.chromium.base.Callback;
 import org.chromium.base.UserDataHost;
 import org.chromium.base.metrics.RecordHistogram;
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.browser.subscriptions.CommerceSubscription.CommerceSubscriptionType;
 import org.chromium.chrome.browser.subscriptions.CommerceSubscription.SubscriptionManagementType;
@@ -50,7 +50,7 @@
  * Tests for {@link ImplicitPriceDropSubscriptionsManager}.
  */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(manifest = Config.NONE, shadows = {ShadowRecordHistogram.class})
+@Config(manifest = Config.NONE)
 @SuppressWarnings("DoNotMock") // Mocks GURL
 public class ImplicitPriceDropSubscriptionsManagerUnitTest {
     @Rule
@@ -119,7 +119,7 @@
 
     @Before
     public void setUp() {
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
         MockitoAnnotations.initMocks(this);
         mTab1 = prepareTab(TAB1_ID, URL1, POSITION1, mCriticalPersistedTabData1);
         mTab2 = prepareTab(TAB2_ID, URL2, POSITION2, mCriticalPersistedTabData2);
diff --git a/chrome/browser/device_api/managed_configuration_api.cc b/chrome/browser/device_api/managed_configuration_api.cc
index d98e58f..78f7e518 100644
--- a/chrome/browser/device_api/managed_configuration_api.cc
+++ b/chrome/browser/device_api/managed_configuration_api.cc
@@ -296,7 +296,7 @@
     const url::Origin& origin,
     const std::string& url_hash,
     const data_decoder::DataDecoder::ValueOrError decoding_result) {
-  if (!decoding_result.value || !decoding_result.value->is_dict()) {
+  if (!decoding_result.has_value() || !decoding_result->is_dict()) {
     VLOG(1) << "Could not fetch managed configuration for app with origin = "
             << origin.Serialize();
     PostStoreConfiguration(origin, base::Value::Dict());
@@ -308,7 +308,7 @@
 
   // We need to transform each value into a string.
   base::Value::Dict result_dict;
-  for (auto item : decoding_result.value->DictItems()) {
+  for (auto item : decoding_result->GetDict()) {
     std::string result;
     JSONStringValueSerializer serializer(&result);
     serializer.Serialize(item.second);
diff --git a/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/DownloadLaterMetricsUnitTest.java b/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/DownloadLaterMetricsUnitTest.java
index d2790c8..6d6f9530 100644
--- a/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/DownloadLaterMetricsUnitTest.java
+++ b/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/DownloadLaterMetricsUnitTest.java
@@ -12,7 +12,8 @@
 import org.junit.runner.RunWith;
 import org.robolectric.annotation.Config;
 
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.browser.download.DownloadLaterMetrics.DownloadLaterUiEvent;
 import org.chromium.chrome.browser.download.dialogs.DownloadLaterDialogChoice;
@@ -21,18 +22,18 @@
  * Unit test for {@link DownloadLaterMetrics}.
  */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(manifest = Config.NONE, shadows = {ShadowRecordHistogram.class})
+@Config(manifest = Config.NONE)
 public class DownloadLaterMetricsUnitTest {
     private static final String UI_EVENT_METRIC_NAME = "Download.Later.UI.Events";
 
     @Before
     public void setUp() {
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
     }
 
     @After
     public void tearDown() {
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
     }
 
     @Test
@@ -40,7 +41,7 @@
         DownloadLaterMetrics.recordDownloadLaterUiEvent(
                 DownloadLaterUiEvent.DOWNLOAD_LATER_DIALOG_EDIT_CLICKED);
         assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(UI_EVENT_METRIC_NAME,
+                RecordHistogram.getHistogramValueCountForTesting(UI_EVENT_METRIC_NAME,
                         DownloadLaterUiEvent.DOWNLOAD_LATER_DIALOG_EDIT_CLICKED));
     }
 
@@ -48,20 +49,20 @@
     public void testRecordDownloadLaterDialogChoice() {
         DownloadLaterMetrics.recordDownloadLaterDialogChoice(DownloadLaterDialogChoice.ON_WIFI, -1);
         assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Download.Later.UI.DialogChoice.Main", DownloadLaterDialogChoice.ON_WIFI));
         assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         UI_EVENT_METRIC_NAME, DownloadLaterUiEvent.DOWNLOAD_LATER_DIALOG_COMPLETE));
 
         DownloadLaterMetrics.recordDownloadLaterDialogChoice(
                 DownloadLaterDialogChoice.DOWNLOAD_LATER, 1024);
         assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Download.Later.UI.DialogChoice.Main",
                         DownloadLaterDialogChoice.DOWNLOAD_LATER));
         assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Download.Later.ScheduledDownloadSize", 0));
     }
 
@@ -70,11 +71,11 @@
         DownloadLaterMetrics.recordDownloadHomeChangeScheduleChoice(
                 DownloadLaterDialogChoice.DOWNLOAD_LATER);
         assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Download.Later.UI.DialogChoice.DownloadHome",
                         DownloadLaterDialogChoice.DOWNLOAD_LATER));
         assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(UI_EVENT_METRIC_NAME,
+                RecordHistogram.getHistogramValueCountForTesting(UI_EVENT_METRIC_NAME,
                         DownloadLaterUiEvent.DOWNLOAD_HOME_CHANGE_SCHEDULE_COMPLETE));
     }
 
@@ -83,11 +84,11 @@
         DownloadLaterMetrics.recordInfobarChangeScheduleChoice(
                 DownloadLaterDialogChoice.DOWNLOAD_NOW);
         assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Download.Later.UI.DialogChoice.Infobar",
                         DownloadLaterDialogChoice.DOWNLOAD_NOW));
         assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(UI_EVENT_METRIC_NAME,
+                RecordHistogram.getHistogramValueCountForTesting(UI_EVENT_METRIC_NAME,
                         DownloadLaterUiEvent.DOWNLOAD_INFOBAR_CHANGE_SCHEDULE_COMPLETE));
     }
 }
diff --git a/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/DownloadLocationDialogMetricsUnitTest.java b/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/DownloadLocationDialogMetricsUnitTest.java
index cd575fe..0e3924f 100644
--- a/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/DownloadLocationDialogMetricsUnitTest.java
+++ b/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/DownloadLocationDialogMetricsUnitTest.java
@@ -12,7 +12,8 @@
 import org.junit.runner.RunWith;
 import org.robolectric.annotation.Config;
 
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.browser.download.DownloadLocationDialogMetrics.DownloadLocationSuggestionEvent;
 
@@ -20,7 +21,7 @@
  * Unit test for {@link DownloadLocationDialogMetrics}.
  */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(manifest = Config.NONE, shadows = {ShadowRecordHistogram.class})
+@Config(manifest = Config.NONE)
 public class DownloadLocationDialogMetricsUnitTest {
     private static final String EVENT_METRIC_NAME =
             "MobileDownload.Location.Dialog.Suggestion.Events";
@@ -29,12 +30,12 @@
 
     @Before
     public void setUp() {
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
     }
 
     @After
     public void tearDown() {
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
     }
 
     @Test
@@ -42,14 +43,13 @@
         DownloadLocationDialogMetrics.recordDownloadLocationSuggestionEvent(
                 DownloadLocationSuggestionEvent.NOT_ENOUGH_SPACE_SHOWN);
         assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         EVENT_METRIC_NAME, DownloadLocationSuggestionEvent.NOT_ENOUGH_SPACE_SHOWN));
     }
 
     @Test
     public void testRecordDownloadLocationSuggestionChoice() {
         DownloadLocationDialogMetrics.recordDownloadLocationSuggestionChoice(true);
-        assertEquals(
-                1, ShadowRecordHistogram.getHistogramTotalCountForTesting(SELECTED_METRIC_NAME));
+        assertEquals(1, RecordHistogram.getHistogramTotalCountForTesting(SELECTED_METRIC_NAME));
     }
 }
diff --git a/chrome/browser/enterprise/connectors/analysis/local_binary_upload_service.cc b/chrome/browser/enterprise/connectors/analysis/local_binary_upload_service.cc
new file mode 100644
index 0000000..5e24e2ec
--- /dev/null
+++ b/chrome/browser/enterprise/connectors/analysis/local_binary_upload_service.cc
@@ -0,0 +1,145 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/enterprise/connectors/analysis/local_binary_upload_service.h"
+
+#include "base/notreached.h"
+#include "base/task/thread_pool.h"
+#include "base/threading/scoped_blocking_call.h"
+#include "chrome/browser/enterprise/connectors/analysis/analysis_settings.h"
+#include "content/public/browser/browser_thread.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/content_analysis_sdk/src/browser/include/content_analysis/sdk/analysis_client.h"
+
+namespace enterprise_connectors {
+namespace {
+
+// Convert enterprise connector ContentAnalysisRequest into the SDK equivalent.
+// SDK ContentAnalysisRequest is a strict subset of the enterprise connector
+// version, therefore the function should always work.
+content_analysis::sdk::ContentAnalysisRequest ConvertChromeRequestToSDKRequest(
+    const ContentAnalysisRequest& req) {
+  content_analysis::sdk::ContentAnalysisRequest request;
+
+  // TODO(b/226679912): Add unit tests to
+  // components/enterprise/common/proto/connectors_unittest.cc to ensure the
+  // conversion methods here and below always work.
+  if (!request.ParseFromString(req.SerializeAsString())) {
+    return content_analysis::sdk::ContentAnalysisRequest();
+  }
+
+  return request;
+}
+
+// Convert SDK ContentAnalysisResponse into the enterprise connector equivalent.
+// SDK ContentAnalysisResponse is a strict subset of the enterprise connector
+// version, therefore the function should always work.
+ContentAnalysisResponse ConvertSDKResponseToChromeResponse(
+    const content_analysis::sdk::ContentAnalysisResponse& res) {
+  ContentAnalysisResponse response;
+
+  if (!response.ParseFromString(res.SerializeAsString())) {
+    return ContentAnalysisResponse();
+  }
+
+  return response;
+}
+
+absl::optional<content_analysis::sdk::ContentAnalysisResponse> SendRequestToSDK(
+    content_analysis::sdk::Client* client,
+    content_analysis::sdk::ContentAnalysisRequest
+        local_content_analysis_request) {
+  base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
+                                                base::BlockingType::MAY_BLOCK);
+  content_analysis::sdk::ContentAnalysisResponse response;
+  client->Send(local_content_analysis_request, &response);
+
+  return response;
+}
+
+}  // namespace
+
+LocalBinaryUploadService::LocalBinaryUploadService(
+    std::unique_ptr<AnalysisSettings> analysis_settings)
+    : analysis_settings_(std::move(analysis_settings)) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+}
+
+LocalBinaryUploadService::~LocalBinaryUploadService() = default;
+
+void LocalBinaryUploadService::OnSentRequestStatus(
+    std::unique_ptr<Request> request,
+    absl::optional<content_analysis::sdk::ContentAnalysisResponse> response) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  Result result;
+  ContentAnalysisResponse chrome_content_analysis_response;
+  if (response.has_value()) {
+    result = Result::SUCCESS;
+    chrome_content_analysis_response =
+        ConvertSDKResponseToChromeResponse(response.value());
+  } else {
+    result = Result::UPLOAD_FAILURE;
+    // Release old client when the status is not ok.
+    client_.reset();
+  }
+  request->FinishRequest(result, std::move(chrome_content_analysis_response));
+}
+
+void LocalBinaryUploadService::DoLocalContentAnalysis(
+    std::unique_ptr<Request> request,
+    Result result,
+    Request::Data data) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  if (result != Result::SUCCESS) {
+    request->FinishRequest(result, ContentAnalysisResponse());
+    return;
+  }
+
+  // TODO(b/226679912): Add logic to support OS-user-specific agents.
+  if (!client_) {
+    DCHECK(analysis_settings_->cloud_or_local_settings.is_local_analysis());
+    client_ = content_analysis::sdk::Client::Create(
+        {analysis_settings_->cloud_or_local_settings.local_settings()
+             .local_path});
+  }
+  content_analysis::sdk::ContentAnalysisRequest local_content_analysis_request =
+      ConvertChromeRequestToSDKRequest(request->content_analysis_request());
+
+  if (!data.contents.empty()) {
+    local_content_analysis_request.set_text_content(std::move(data.contents));
+  } else if (!data.path.empty()) {
+    local_content_analysis_request.set_file_path(data.path.AsUTF8Unsafe());
+  } else {
+    NOTREACHED();
+  }
+
+  // TODO(b/238897238): Manage SDK client pointer via
+  // ChromeBrowserPolicyConnector.
+  base::ThreadPool::PostTaskAndReplyWithResult(
+      FROM_HERE,
+      {base::TaskPriority::USER_VISIBLE, base::MayBlock(),
+       base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
+      // `client_` passed as naked pointer to avoid using `std::move()` which
+      // will reset `client_`, making chrome continually connecting and
+      // disconnecting from agent.
+      // Because LocalBinaryUploadService is a profile keyed service, the object
+      // should live for at least as long as the profile. Besides, the task is
+      // posted with `SKIP_ON_SHUTDOWN`, therefore if the task is pending on
+      // shutdown, it won't run.
+      base::BindOnce(&SendRequestToSDK, base::Unretained(client_.get()),
+                     std::move(local_content_analysis_request)),
+      base::BindOnce(&LocalBinaryUploadService::OnSentRequestStatus,
+                     weakptr_factory_.GetWeakPtr(), std::move(request)));
+}
+
+void LocalBinaryUploadService::MaybeUploadForDeepScanning(
+    std::unique_ptr<LocalBinaryUploadService::Request> request) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  Request* raw_request = request.get();
+  raw_request->GetRequestData(
+      base::BindOnce(&LocalBinaryUploadService::DoLocalContentAnalysis,
+                     weakptr_factory_.GetWeakPtr(), std::move(request)));
+}
+
+}  // namespace enterprise_connectors
diff --git a/chrome/browser/enterprise/connectors/analysis/local_binary_upload_service.h b/chrome/browser/enterprise/connectors/analysis/local_binary_upload_service.h
new file mode 100644
index 0000000..72c4f50
--- /dev/null
+++ b/chrome/browser/enterprise/connectors/analysis/local_binary_upload_service.h
@@ -0,0 +1,45 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_ENTERPRISE_CONNECTORS_ANALYSIS_LOCAL_BINARY_UPLOAD_SERVICE_H_
+#define CHROME_BROWSER_ENTERPRISE_CONNECTORS_ANALYSIS_LOCAL_BINARY_UPLOAD_SERVICE_H_
+
+#include <memory>
+
+#include "chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.h"
+#include "third_party/content_analysis_sdk/src/browser/include/content_analysis/sdk/analysis_client.h"
+
+namespace enterprise_connectors {
+
+// This class encapsulates the process of sending a file to local content
+// analysis agents for deep scanning and asynchronously retrieving a verdict.
+// This class runs on the UI thread.
+class LocalBinaryUploadService : public safe_browsing::BinaryUploadService {
+ public:
+  explicit LocalBinaryUploadService(
+      std::unique_ptr<AnalysisSettings> analysis_settings);
+  ~LocalBinaryUploadService() override;
+
+  // Send the given file contents to local partners for deep scanning.
+  void MaybeUploadForDeepScanning(std::unique_ptr<Request> request) override;
+
+ private:
+  void DoLocalContentAnalysis(std::unique_ptr<Request> request,
+                              Result result,
+                              Request::Data data);
+
+  // Updates response to Chrome based on whether the SDK request is successfully
+  // sent.
+  void OnSentRequestStatus(
+      std::unique_ptr<Request> request,
+      absl::optional<content_analysis::sdk::ContentAnalysisResponse> response);
+
+  std::unique_ptr<AnalysisSettings> analysis_settings_;
+  std::unique_ptr<content_analysis::sdk::Client> client_;
+  base::WeakPtrFactory<LocalBinaryUploadService> weakptr_factory_{this};
+};
+
+}  // namespace enterprise_connectors
+
+#endif  // CHROME_BROWSER_ENTERPRISE_CONNECTORS_ANALYSIS_LOCAL_BINARY_UPLOAD_SERVICE_H_
diff --git a/chrome/browser/enterprise/connectors/device_trust/device_trust_service.cc b/chrome/browser/enterprise/connectors/device_trust/device_trust_service.cc
index 08ececd1..775810f 100644
--- a/chrome/browser/enterprise/connectors/device_trust/device_trust_service.cc
+++ b/chrome/browser/enterprise/connectors/device_trust/device_trust_service.cc
@@ -24,13 +24,13 @@
 // service after it is validated and decoded.
 void OnJsonParsed(DeviceTrustService::ParseJsonChallengeCallback callback,
                   data_decoder::DataDecoder::ValueOrError result) {
-  if (!result.value) {
+  if (!result.has_value()) {
     std::move(callback).Run(std::string());
     return;
   }
 
   // Check if json is malformed or it doesn't include the needed field.
-  const std::string* challenge = result.value->FindStringPath("challenge");
+  const std::string* challenge = result->FindStringPath("challenge");
   if (!challenge) {
     std::move(callback).Run(std::string());
     return;
diff --git a/chrome/browser/enterprise/connectors/file_system/box_api_call_flow.cc b/chrome/browser/enterprise/connectors/file_system/box_api_call_flow.cc
index 77669f8..4302623 100644
--- a/chrome/browser/enterprise/connectors/file_system/box_api_call_flow.cc
+++ b/chrome/browser/enterprise/connectors/file_system/box_api_call_flow.cc
@@ -36,7 +36,8 @@
 
 #define LOG_PARSE_FAIL(severity, flow, result)                            \
   DLOG(severity) << "[BoxApiCallFlow] " << flow << "\nJson Parse Error: " \
-                 << (result.error ? result.error->data() : "<no error info>");
+                 << (!result.has_value() ? result.error().data()          \
+                                         : "<no error info>");
 
 #define LOG_PARSE_FAIL_IF(condition, severity, flow, result) \
   if (condition) {                                           \
@@ -118,11 +119,11 @@
 
 bool ExtractEntriesList(const Box::ParseResult& result,
                         base::Value::ConstListView* list) {
-  if (!result.value) {
+  if (!result.has_value()) {
     return false;
   }
 
-  const base::Value* entries = result.value->FindPath("entries");
+  const base::Value* entries = result->GetDict().Find("entries");
   if (!entries || !entries->is_list()) {
     return false;
   }
@@ -229,11 +230,10 @@
 
 // API reference: https://developer.box.com/reference/resources/client-error/
 void BoxApiCallFlow::OnFailureJsonParsed(int http_code, ParseResult result) {
-  DCHECK(result.value);
   base::Value *code = nullptr, *request_id = nullptr;
   auto response = Response{false, http_code};
-  if (result.value && (code = result.value->FindPath("code")) &&
-      (request_id = result.value->FindPath("request_id"))) {
+  if (result.has_value() && (code = result->FindPath("code")) &&
+      (request_id = result->FindPath("request_id"))) {
     response =
         MakeApiFailure(http_code, code->GetString(), request_id->GetString());
   }
@@ -320,8 +320,8 @@
 void BoxGetFileFolderApiCallFlow::OnSuccessJsonParsed(ParseResult result) {
   std::string folder_id;
 
-  if (result.value.has_value())
-    folder_id = ExtractParentId(result.value.value());
+  if (result.has_value())
+    folder_id = ExtractParentId(*result);
 
   std::move(callback_).Run(Response{!folder_id.empty(), net::HTTP_OK},
                            folder_id);
@@ -419,15 +419,15 @@
 void BoxCreateUpstreamFolderApiCallFlow::OnSuccessJsonParsed(
     int network_response_code,
     ParseResult result) {
-  DCHECK(result.value);
-  if (!result.value)
+  DCHECK(result.has_value());
+  if (!result.has_value())
     return OnFailureJsonParsed(network_response_code, std::move(result));
 
   std::string folder_id;
   absl::optional<base::Value> folder_info_dict;
 
   if (network_response_code == net::HTTP_CREATED) {
-    folder_info_dict = std::move(result.value);
+    folder_info_dict = std::move(*result);
   } else {
     // Right after a folder was created with a previous upload, the folder may
     // not be found via BoxFindUpstreamFolderApiCallFlow, therefore BoxUploader
@@ -435,9 +435,9 @@
     // folder is included in the response body so can also be extracted to
     // return a folder_id.
     DCHECK_EQ(network_response_code, net::HTTP_CONFLICT);
-    std::string* box_error_code = result.value->FindStringPath("code");
+    std::string* box_error_code = result->FindStringPath("code");
     base::Value* conflict_folders_list =
-        result.value->FindListPath("context_info.conflicts");
+        result->FindListPath("context_info.conflicts");
     if (box_error_code && *box_error_code == "item_name_in_use" &&
         conflict_folders_list &&
         conflict_folders_list->GetListDeprecated().size() > 0) {
@@ -487,24 +487,23 @@
 }
 
 void BoxGetCurrentUserApiCallFlow::OnJsonParsed(ParseResult result) {
-  if (!result.value.has_value()) {
+  if (!result.has_value()) {
     LOG_PARSE_FAIL(ERROR, "GetCurrentUser", result);
     std::move(callback_).Run(Response{false, net::HTTP_OK}, CreateEmptyDict());
     return;
   }
-  if (!result.value->is_dict() ||
-      !result.value->FindStringPath(kBoxEnterpriseIdFieldName) ||
-      !result.value->FindStringPath(kBoxLoginFieldName) ||
-      !result.value->FindStringPath(kBoxNameFieldName)) {
+  if (!result->is_dict() ||
+      !result->FindStringPath(kBoxEnterpriseIdFieldName) ||
+      !result->FindStringPath(kBoxLoginFieldName) ||
+      !result->FindStringPath(kBoxNameFieldName)) {
     LOG(ERROR)
         << "[BoxApiCallFlow] GetCurrentUser succeeded but "
            "response does not include all of enterprise_id, login, and name: "
-        << *result.value;
+        << *result;
     std::move(callback_).Run(Response{false, net::HTTP_OK}, CreateEmptyDict());
     return;
   }
-  std::move(callback_).Run(Response{true, net::HTTP_OK},
-                           std::move(result.value.value()));
+  std::move(callback_).Run(Response{true, net::HTTP_OK}, std::move(*result));
 }
 
 void BoxGetCurrentUserApiCallFlow::ProcessFailure(Response response) {
@@ -757,21 +756,19 @@
 
 void BoxCreateUploadSessionApiCallFlow::OnSuccessJsonParsed(
     ParseResult result) {
-  LOG_PARSE_FAIL_IF(!result.value.has_value(), ERROR, "CreateUploadSession",
-                    result);
+  LOG_PARSE_FAIL_IF(!result.has_value(), ERROR, "CreateUploadSession", result);
 
   const auto http_code = net::HTTP_CREATED;
   base::Value *endpoints = nullptr, *part_size = nullptr;
-  if (result.value.has_value() &&
-      (part_size = result.value->FindPath("part_size")) &&
-      (endpoints = result.value->FindPath("session_endpoints")) &&
+  if (result.has_value() && (part_size = result->FindPath("part_size")) &&
+      (endpoints = result->FindPath("session_endpoints")) &&
       endpoints->FindPath("upload_part") && endpoints->FindPath("commit") &&
       endpoints->FindPath("abort")) {
     std::move(callback_).Run(MakeSuccess(http_code), std::move(*endpoints),
                              part_size->GetInt());
     return;
   }
-  LOG_PARSE_FAIL_IF(!result.value, ERROR, "CreateUploadSession", result);
+  LOG_PARSE_FAIL_IF(!result.has_value(), ERROR, "CreateUploadSession", result);
   ProcessFailure(MakeApiFailure(http_code, "bad_response", "parse_fail"));
 }
 
@@ -861,13 +858,13 @@
 
 void BoxPartFileUploadApiCallFlow::OnSuccessJsonParsed(ParseResult result) {
   const auto http_code = net::HTTP_OK;
-  if (!result.value) {
+  if (!result.has_value()) {
     LOG_PARSE_FAIL(ERROR, "PartFileUpload", result);
     ProcessFailure(MakeApiFailure(http_code, "bad_response", "parse_fail"));
     return;
   }
 
-  base::Value* part = result.value->FindPath("part");
+  base::Value* part = result->FindPath("part");
   if (!part) {
     DLOG(ERROR) << "[BoxApiCallFlow] No info for uploaded part";
     ProcessFailure(MakeApiFailure(http_code, "bad_response", "parse_fail"));
diff --git a/chrome/browser/extensions/api/omnibox/suggestion_parser.cc b/chrome/browser/extensions/api/omnibox/suggestion_parser.cc
index 66ea40e..33de6e9 100644
--- a/chrome/browser/extensions/api/omnibox/suggestion_parser.cc
+++ b/chrome/browser/extensions/api/omnibox/suggestion_parser.cc
@@ -140,25 +140,25 @@
     std::move(callback).Run(std::move(result));
   };
 
-  if (value_or_error.error) {
-    run_callback_with_error(std::move(*value_or_error.error));
+  if (!value_or_error.has_value()) {
+    run_callback_with_error(std::move(value_or_error.error()));
     return;
   }
 
-  DCHECK(value_or_error.value);
+  DCHECK(value_or_error.has_value());
 
   // From this point on, we hope that everything is valid (e.g., that we don't
   // get non-dictionary values or unexpected top-level types. But, if we did,
   // emit a generic error.
   constexpr char kGenericError[] = "Invalid XML";
 
-  if (!value_or_error.value->is_dict()) {
+  if (!value_or_error->is_dict()) {
     run_callback_with_error(kGenericError);
     return;
   }
 
   std::vector<const base::Value*> entries;
-  const base::Value& root_node = *value_or_error.value;
+  const base::Value& root_node = *value_or_error;
   if (has_multiple_entries) {
     if (!PopulateEntriesFromNode(root_node, &entries)) {
       run_callback_with_error(kGenericError);
diff --git a/chrome/browser/extensions/api/web_authentication_proxy/web_authentication_proxy_service.cc b/chrome/browser/extensions/api/web_authentication_proxy/web_authentication_proxy_service.cc
index 9beda38..50f212b 100644
--- a/chrome/browser/extensions/api/web_authentication_proxy/web_authentication_proxy_service.cc
+++ b/chrome/browser/extensions/api/web_authentication_proxy/web_authentication_proxy_service.cc
@@ -214,13 +214,13 @@
     RequestId request_id,
     data_decoder::DataDecoder::ValueOrError value_or_error) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (value_or_error.error) {
+  if (!value_or_error.has_value()) {
     std::move(respond_callback)
-        .Run("Parsing responseJson failed: " + *value_or_error.error);
+        .Run("Parsing responseJson failed: " + value_or_error.error());
     return;
   }
   auto [response, error] =
-      webauthn_proxy::MakeCredentialResponseFromValue(*value_or_error.value);
+      webauthn_proxy::MakeCredentialResponseFromValue(*value_or_error);
   if (!response) {
     std::move(respond_callback).Run("Invalid responseJson: " + error);
     return;
@@ -245,13 +245,13 @@
     RequestId request_id,
     data_decoder::DataDecoder::ValueOrError value_or_error) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (value_or_error.error) {
+  if (!value_or_error.has_value()) {
     std::move(respond_callback)
-        .Run("Parsing responseJson failed: " + *value_or_error.error);
+        .Run("Parsing responseJson failed: " + value_or_error.error());
     return;
   }
   auto [response, error] =
-      webauthn_proxy::GetAssertionResponseFromValue(*value_or_error.value);
+      webauthn_proxy::GetAssertionResponseFromValue(*value_or_error);
   if (!response) {
     std::move(respond_callback).Run("Invalid responseJson: " + error);
     return;
diff --git a/chrome/browser/extensions/api/webstore_private/webstore_private_api.cc b/chrome/browser/extensions/api/webstore_private/webstore_private_api.cc
index 87bbf05..4a09461 100644
--- a/chrome/browser/extensions/api/webstore_private/webstore_private_api.cc
+++ b/chrome/browser/extensions/api/webstore_private/webstore_private_api.cc
@@ -1294,7 +1294,7 @@
 void WebstorePrivateGetExtensionStatusFunction::OnManifestParsed(
     const ExtensionId& extension_id,
     data_decoder::DataDecoder::ValueOrError result) {
-  if (!result.value || !result.value->is_dict()) {
+  if (!result.has_value() || !result->is_dict()) {
     Respond(Error(kWebstoreInvalidManifestError));
     return;
   }
@@ -1307,7 +1307,7 @@
   std::string error;
   auto dummy_extension =
       Extension::Create(base::FilePath(), mojom::ManifestLocation::kInternal,
-                        base::Value::AsDictionaryValue(*result.value),
+                        base::Value::AsDictionaryValue(*result),
                         Extension::FROM_WEBSTORE, extension_id, &error);
 
   if (!dummy_extension) {
diff --git a/chrome/browser/extensions/content_script_apitest.cc b/chrome/browser/extensions/content_script_apitest.cc
index 86405c70..b497879 100644
--- a/chrome/browser/extensions/content_script_apitest.cc
+++ b/chrome/browser/extensions/content_script_apitest.cc
@@ -2181,7 +2181,9 @@
   content::test::PrerenderTestHelper prerender_helper_;
 };
 
-IN_PROC_BROWSER_TEST_F(ContentScriptApiPrerenderingTest, Prerendering) {
+// TODO(crbug.com/1344548): Re-enable this test
+IN_PROC_BROWSER_TEST_F(ContentScriptApiPrerenderingTest,
+                       DISABLED_Prerendering) {
   ASSERT_TRUE(StartEmbeddedTestServer());
   ASSERT_TRUE(RunExtensionTest("content_scripts/prerendering")) << message_;
 }
diff --git a/chrome/browser/extensions/webstore_data_fetcher.cc b/chrome/browser/extensions/webstore_data_fetcher.cc
index bb41ddf..f871d211 100644
--- a/chrome/browser/extensions/webstore_data_fetcher.cc
+++ b/chrome/browser/extensions/webstore_data_fetcher.cc
@@ -121,12 +121,12 @@
 
 void WebstoreDataFetcher::OnJsonParsed(
     data_decoder::DataDecoder::ValueOrError result) {
-  if (!result.value) {
-    delegate_->OnWebstoreResponseParseFailure(id_, *result.error);
+  if (!result.has_value()) {
+    delegate_->OnWebstoreResponseParseFailure(id_, result.error());
     return;
   }
 
-  if (!result.value->is_dict()) {
+  if (!result->is_dict()) {
     delegate_->OnWebstoreResponseParseFailure(id_,
                                               kInvalidWebstoreResponseError);
     return;
@@ -134,7 +134,7 @@
 
   delegate_->OnWebstoreResponseParseSuccess(
       id_, base::DictionaryValue::From(
-               base::Value::ToUniquePtrValue(std::move(*result.value))));
+               base::Value::ToUniquePtrValue(std::move(*result))));
 }
 
 void WebstoreDataFetcher::OnSimpleLoaderComplete(
diff --git a/chrome/browser/extensions/webstore_install_helper.cc b/chrome/browser/extensions/webstore_install_helper.cc
index 5c36dcb..4fa1f78 100644
--- a/chrome/browser/extensions/webstore_install_helper.cc
+++ b/chrome/browser/extensions/webstore_install_helper.cc
@@ -113,11 +113,13 @@
     data_decoder::DataDecoder::ValueOrError result) {
   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   manifest_parse_complete_ = true;
-  if (result.value && result.value->is_dict()) {
+  if (result.has_value() && result->is_dict()) {
     parsed_manifest_ = base::DictionaryValue::From(
-        base::Value::ToUniquePtrValue(std::move(*result.value)));
+        base::Value::ToUniquePtrValue(std::move(*result)));
   } else {
-    error_ = result.error.value_or("Invalid JSON response");
+    error_ = (!result.has_value() || result.error().empty())
+                 ? "Invalid JSON response"
+                 : result.error();
     parse_error_ = Delegate::MANIFEST_ERROR;
   }
   ReportResultsIfComplete();
diff --git a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedStreamTest.java b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedStreamTest.java
index 877472d..b1cc0be 100644
--- a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedStreamTest.java
+++ b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedStreamTest.java
@@ -52,7 +52,6 @@
 
 import org.chromium.base.Callback;
 import org.chromium.base.FeatureList;
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
 import org.chromium.base.supplier.Supplier;
 import org.chromium.base.task.test.ShadowPostTask;
 import org.chromium.base.test.BaseRobolectricTestRunner;
@@ -89,8 +88,7 @@
 
 /** Unit tests for {@link FeedStream}. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(manifest = Config.NONE,
-        shadows = {ShadowPostTask.class, ShadowRecordHistogram.class, ShadowGURL.class})
+@Config(manifest = Config.NONE, shadows = {ShadowPostTask.class, ShadowGURL.class})
 // TODO(crbug.com/1210371): Rewrite using paused loop. See crbug for details.
 @LooperMode(LooperMode.Mode.LEGACY)
 public class FeedStreamTest {
diff --git a/chrome/browser/feedback/system_logs/about_system_logs_fetcher.cc b/chrome/browser/feedback/system_logs/about_system_logs_fetcher.cc
index d1e2480a..c00b7ff 100644
--- a/chrome/browser/feedback/system_logs/about_system_logs_fetcher.cc
+++ b/chrome/browser/feedback/system_logs/about_system_logs_fetcher.cc
@@ -11,11 +11,6 @@
 #include "chrome/browser/feedback/system_logs/log_sources/chrome_internal_log_source.h"
 #include "chrome/browser/feedback/system_logs/log_sources/memory_details_log_source.h"
 #include "components/feedback/system_logs/system_logs_fetcher.h"
-#include "net/net_buildflags.h"
-
-#if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
-#include "chrome/browser/feedback/system_logs/log_sources/chrome_root_store_log_source.h"
-#endif
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 #include "chrome/browser/ash/system_logs/bluetooth_log_source.h"
@@ -42,10 +37,6 @@
   fetcher->AddSource(std::make_unique<ChromeInternalLogSource>());
   fetcher->AddSource(std::make_unique<MemoryDetailsLogSource>());
 
-#if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
-  fetcher->AddSource(std::make_unique<ChromeRootStoreLogSource>());
-#endif
-
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   // These sources rely on scrubbing in SystemLogsFetcher.
   fetcher->AddSource(std::make_unique<BluetoothLogSource>());
diff --git a/chrome/browser/feedback/system_logs/log_sources/chrome_root_store_log_source.cc b/chrome/browser/feedback/system_logs/log_sources/chrome_root_store_log_source.cc
deleted file mode 100644
index 35390dba..0000000
--- a/chrome/browser/feedback/system_logs/log_sources/chrome_root_store_log_source.cc
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/feedback/system_logs/log_sources/chrome_root_store_log_source.h"
-
-#include "base/strings/string_number_conversions.h"
-#include "components/feedback/system_logs/system_logs_source.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/network_service_instance.h"
-#include "services/cert_verifier/cert_verifier_service_factory.h"
-#include "services/cert_verifier/public/mojom/cert_verifier_service_factory.mojom.h"
-
-namespace system_logs {
-
-namespace {
-
-constexpr char kChromeRootStoreKey[] = "chrome_root_store";
-
-void PopulateChromeRootStoreLogsAsync(
-    system_logs::SysLogsSourceCallback callback,
-    cert_verifier::mojom::ChromeRootStoreInfoPtr info) {
-  auto response = std::make_unique<system_logs::SystemLogsResponse>();
-
-  std::string entry;
-  entry += "version: " + base::NumberToString(info->version) + "\n\n";
-  for (auto const& cert_info : info->root_cert_info) {
-    entry += "hash: " + cert_info->sha256hash_hex +
-             "  name: " + cert_info->name + "\n";
-  }
-  response->emplace(kChromeRootStoreKey, std::move(entry));
-  std::move(callback).Run(std::move(response));
-}
-
-}  // namespace
-
-ChromeRootStoreLogSource::ChromeRootStoreLogSource()
-    : SystemLogsSource("ChromeRootStore") {}
-
-ChromeRootStoreLogSource::~ChromeRootStoreLogSource() {}
-
-void ChromeRootStoreLogSource::Fetch(
-    system_logs::SysLogsSourceCallback callback) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  DCHECK(!callback.is_null());
-
-  cert_verifier::mojom::CertVerifierServiceFactory* factory =
-      content::GetCertVerifierServiceFactory();
-  DCHECK(factory);
-  factory->GetChromeRootStoreInfo(
-      base::BindOnce(&PopulateChromeRootStoreLogsAsync, std::move(callback)));
-}
-
-}  // namespace system_logs
diff --git a/chrome/browser/feedback/system_logs/log_sources/chrome_root_store_log_source.h b/chrome/browser/feedback/system_logs/log_sources/chrome_root_store_log_source.h
deleted file mode 100644
index f5f5ef6..0000000
--- a/chrome/browser/feedback/system_logs/log_sources/chrome_root_store_log_source.h
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_FEEDBACK_SYSTEM_LOGS_LOG_SOURCES_CHROME_ROOT_STORE_LOG_SOURCE_H_
-#define CHROME_BROWSER_FEEDBACK_SYSTEM_LOGS_LOG_SOURCES_CHROME_ROOT_STORE_LOG_SOURCE_H_
-
-#include "components/feedback/system_logs/system_logs_source.h"
-
-namespace system_logs {
-
-// Get information about the contents of the Chrome Root Store in use.
-class ChromeRootStoreLogSource : public system_logs::SystemLogsSource {
- public:
-  ChromeRootStoreLogSource();
-
-  ChromeRootStoreLogSource(const ChromeRootStoreLogSource&) = delete;
-  ChromeRootStoreLogSource& operator=(const ChromeRootStoreLogSource&) = delete;
-
-  ~ChromeRootStoreLogSource() override;
-
-  // SystemLogsSource override.
-  void Fetch(system_logs::SysLogsSourceCallback callback) override;
-};
-
-}  // namespace system_logs
-
-#endif  // CHROME_BROWSER_FEEDBACK_SYSTEM_LOGS_LOG_SOURCES_CHROME_ROOT_STORE_LOG_SOURCE_H_
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 1633fd2..fb019649 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -2262,6 +2262,16 @@
     "expiry_milestone": 108
   },
   {
+    "name": "enable-get-display-media-set",
+    "owners": [ "simonha" ],
+    "expiry_milestone": 115
+  },
+  {
+    "name": "enable-get-display-media-set-auto-select-all-screens",
+    "owners": [ "simonha" ],
+    "expiry_milestone": 115
+  },
+  {
     "name": "enable-global-vaapi-lock",
     "owners": [
       "jeffcchen",
@@ -3475,11 +3485,6 @@
     "expiry_milestone": 105
   },
   {
-    "name": "force-60hz",
-    "owners": [ "sky" ],
-    "expiry_milestone": 106
-  },
-  {
     "name": "force-color-profile",
     "owners": [ "ccameron" ],
     // Used by developers and manual testing to test custom color profiles.
@@ -4517,11 +4522,6 @@
     "expiry_milestone": 105
   },
   {
-    "name": "ntp-modules",
-    "owners": [ "mahmadi", "tiborg" ],
-    "expiry_milestone": 105
-  },
-  {
     "name": "ntp-modules-drag-and-drop",
     "owners": [ "lydialam", "mahmadi", "tiborg" ],
     "expiry_milestone": 105
@@ -4822,7 +4822,7 @@
   },
   {
     "name": "omnibox-trigger-for-prerender2",
-    "owners": [ "//content/browser/prerender/OWNERS" ],
+    "owners": [ "//content/browser/preloading/prerender/OWNERS" ],
     "expiry_milestone": 106
   },
   {
@@ -5466,6 +5466,11 @@
     "expiry_milestone": 110
   },
   {
+    "name": "safe-mode-for-cached-flags",
+    "owners": ["hnakashima"],
+    "expiry_milestone": 115
+  },
+  {
     "name": "sameparty-cookies-considered-first-party",
     "owners": ["cfredric", "chrome-first-party-sets@chromium.org"],
     "expiry_milestone": 108
@@ -5531,7 +5536,7 @@
   },
   {
     "name": "search-suggestion-for-prerender2",
-    "owners": [ "//content/browser/prerender/OWNERS" ],
+    "owners": [ "//content/browser/preloading/prerender/OWNERS" ],
     "expiry_milestone": 106
   },
   {
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 73fa6d58..acf0128f 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -3695,6 +3695,11 @@
     " On tablets with small screens a mobile site will be requested by "
     "default.";
 
+const char kSafeModeForCachedFlagsName[] = "Safe Mode for Cached Flags";
+const char kSafeModeForCachedFlagsDescription[] =
+    "Attempts recovery from startup crash loops caused by a bad field trial "
+    "by rolling back to previous known safe flag values.";
+
 const char kScreenshotsForAndroidV2Name[] = "Screenshots for Android V2";
 const char kScreenshotsForAndroidV2Description[] =
     "Adds functionality to the share screenshot panel within Chrome Browser"
@@ -4778,6 +4783,17 @@
     "CRD, "
     "replacing the use of AuraDesktopCapturer";
 
+const char kGetDisplayMediaSetName[] = "GetDisplayMediaSet API";
+const char kGetDisplayMediaSetDescription[] =
+    "When enabled, the getDisplayMediaSet API for capturing multiple surfaces "
+    "at once is available.";
+
+const char kGetDisplayMediaSetAutoSelectAllScreensName[] =
+    "autoSelectAllScreens attribute for GetDisplayMediaSet";
+const char kGetDisplayMediaSetAutoSelectAllScreensDescription[] =
+    "When enabled, the autoSelectAllScreens attribute is available for usage "
+    "with the GetDisplayMediaSet API.";
+
 const char kMultiMonitorsInCrdName[] = "Multi monitor in CRD";
 const char kMultiMonitorsInCrdDescription[] =
     "Enables support for viewing multiple monitors connected to this ChromeOS "
@@ -6044,12 +6060,6 @@
     "there are undecryptable ones.";
 #endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC)
 
-#if BUILDFLAG(IS_MAC)
-const char kForce60HzName[] = "Use 60hz instead of 120hz";
-const char kForce60HzDescription[] =
-    "If enabled, on devices with a refresh rate of 120hz, 60hz is used.";
-#endif  // BUILDFLAG(IS_MAC)
-
 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX)
 const char kAsyncDnsName[] = "Async DNS resolver";
 const char kAsyncDnsDescription[] = "Enables the built-in DNS resolver.";
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 1c6834a3..e171126 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -2099,6 +2099,9 @@
 extern const char kRequestDesktopSiteForTabletsName[];
 extern const char kRequestDesktopSiteForTabletsDescription[];
 
+extern const char kSafeModeForCachedFlagsName[];
+extern const char kSafeModeForCachedFlagsDescription[];
+
 extern const char kScreenshotsForAndroidV2Name[];
 extern const char kScreenshotsForAndroidV2Description[];
 
@@ -2731,6 +2734,12 @@
 extern const char kFrameSinkDesktopCapturerInCrdName[];
 extern const char kFrameSinkDesktopCapturerInCrdDescription[];
 
+extern const char kGetDisplayMediaSetName[];
+extern const char kGetDisplayMediaSetDescription[];
+
+extern const char kGetDisplayMediaSetAutoSelectAllScreensName[];
+extern const char kGetDisplayMediaSetAutoSelectAllScreensDescription[];
+
 extern const char kMultiMonitorsInCrdName[];
 extern const char kMultiMonitorsInCrdDescription[];
 
@@ -3474,11 +3483,6 @@
 extern const char kSkipUndecryptablePasswordsDescription[];
 #endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC)
 
-#if BUILDFLAG(IS_MAC)
-extern const char kForce60HzName[];
-extern const char kForce60HzDescription[];
-#endif  // BUILDFLAG(IS_MAC)
-
 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX)
 extern const char kAsyncDnsName[];
 extern const char kAsyncDnsDescription[];
diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc
index e68b169..860ebbef 100644
--- a/chrome/browser/flags/android/chrome_feature_list.cc
+++ b/chrome/browser/flags/android/chrome_feature_list.cc
@@ -266,6 +266,7 @@
     &kRelatedSearchesInBar,
     &kRelatedSearchesSimplifiedUx,
     &kRelatedSearchesUi,
+    &kSafeModeForCachedFlags,
     &kSearchEnginePromoExistingDevice,
     &kSearchEnginePromoExistingDeviceV2,
     &kSearchEnginePromoNewDevice,
@@ -768,6 +769,9 @@
 const base::Feature kRelatedSearchesUi{"RelatedSearchesUi",
                                        base::FEATURE_DISABLED_BY_DEFAULT};
 
+const base::Feature kSafeModeForCachedFlags{"SafeModeForCachedFlags",
+                                            base::FEATURE_DISABLED_BY_DEFAULT};
+
 const base::Feature kShareButtonInTopToolbar{"ShareButtonInTopToolbar",
                                              base::FEATURE_DISABLED_BY_DEFAULT};
 
diff --git a/chrome/browser/flags/android/chrome_feature_list.h b/chrome/browser/flags/android/chrome_feature_list.h
index d44a3943..7941ec9 100644
--- a/chrome/browser/flags/android/chrome_feature_list.h
+++ b/chrome/browser/flags/android/chrome_feature_list.h
@@ -136,6 +136,7 @@
 extern const base::Feature kSharingHubLinkToggle;
 extern const base::Feature kShowScrollableMVTOnNTPAndroid;
 extern const base::Feature kFeedPositionAndroid;
+extern const base::Feature kSafeModeForCachedFlags;
 extern const base::Feature kSearchResumptionModuleAndroid;
 extern const base::Feature kSpannableInlineAutocomplete;
 extern const base::Feature kSpecialLocaleWrapper;
diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlags.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlags.java
index eb8bb57..a6f52d5 100644
--- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlags.java
+++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlags.java
@@ -258,6 +258,7 @@
      */
     public static void cacheAdditionalNativeFlags() {
         cacheNetworkServiceWarmUpEnabled();
+        sSafeMode.cacheSafeModeForCachedFlagsEnabled();
         cacheReachedCodeProfilerTrialGroup();
 
         // Propagate REACHED_CODE_PROFILER feature value to LibraryLoader. This can't be done in
@@ -510,6 +511,11 @@
         return swapped;
     }
 
+    @VisibleForTesting
+    static void setSafeModeExperimentEnabledForTesting(Boolean value) {
+        sSafeMode.setExperimentEnabledForTesting(value);
+    }
+
     @NativeMethods
     interface Natives {
         boolean isNetworkServiceWarmUpEnabled();
diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlagsSafeModeUnitTest.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlagsSafeModeUnitTest.java
index 1faf797..cf0999f 100644
--- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlagsSafeModeUnitTest.java
+++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlagsSafeModeUnitTest.java
@@ -41,6 +41,7 @@
 
     @Before
     public void setUp() {
+        CachedFeatureFlags.setSafeModeExperimentEnabledForTesting(true);
         CachedFeatureFlags.resetFlagsForTesting();
         Map<String, Boolean> defaults = makeFeatureMap(false, false);
         mDefaultsSwapped = CachedFeatureFlags.swapDefaultsForTesting(defaults);
@@ -48,6 +49,7 @@
 
     @After
     public void tearDown() {
+        CachedFeatureFlags.setSafeModeExperimentEnabledForTesting(null);
         CachedFeatureFlags.resetFlagsForTesting();
         CachedFeatureFlags.swapDefaultsForTesting(mDefaultsSwapped);
 
diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFlagsSafeMode.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFlagsSafeMode.java
index d29922f..e46826f0 100644
--- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFlagsSafeMode.java
+++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFlagsSafeMode.java
@@ -42,6 +42,9 @@
     @VisibleForTesting
     static final String PREF_SAFE_VALUES_VERSION = "Chrome.Flags.SafeValuesVersion";
 
+    private Boolean mSafeModeExperimentForcedForTesting;
+    private Boolean mSafeModeExperimentEnabled;
+
     // These values are persisted to logs. Entries should not be renumbered and numeric values
     // should never be reused.
     @VisibleForTesting
@@ -265,30 +268,59 @@
     }
 
     Boolean isEnabled(String featureName, String preferenceName) {
+        if (!isSafeModeExperimentEnabled()) return null;
+
         // TODO(crbug.com/1199069): Return safe values if safe mode is engaged.
         return null;
     }
 
     Boolean getBooleanFieldTrialParam(String preferenceName, boolean defaultValue) {
+        if (!isSafeModeExperimentEnabled()) return null;
+
         // TODO(crbug.com/1199069): Return safe values if safe mode is engaged.
         return null;
     }
 
     Integer getIntFieldTrialParam(String preferenceName, int defaultValue) {
+        if (!isSafeModeExperimentEnabled()) return null;
+
         // TODO(crbug.com/1199069): Return safe values if safe mode is engaged.
         return null;
     }
 
     Double getDoubleFieldTrialParam(String preferenceName, double defaultValue) {
+        if (!isSafeModeExperimentEnabled()) return null;
+
         // TODO(crbug.com/1199069): Return safe values if safe mode is engaged.
         return null;
     }
 
     String getStringFieldTrialParam(String preferenceName, String defaultValue) {
+        if (!isSafeModeExperimentEnabled()) return null;
+
         // TODO(crbug.com/1199069): Return safe values if safe mode is engaged.
         return null;
     }
 
+    void cacheSafeModeForCachedFlagsEnabled() {
+        SharedPreferencesManager.getInstance().writeBoolean(
+                ChromePreferenceKeys.FLAGS_SAFE_MODE_ENABLED,
+                ChromeFeatureList.isEnabled(ChromeFeatureList.SAFE_MODE_FOR_CACHED_FLAGS));
+    }
+
+    private boolean isSafeModeExperimentEnabled() {
+        if (mSafeModeExperimentForcedForTesting != null) {
+            return mSafeModeExperimentForcedForTesting;
+        }
+
+        if (mSafeModeExperimentEnabled == null) {
+            mSafeModeExperimentEnabled = SharedPreferencesManager.getInstance().readBoolean(
+                    ChromePreferenceKeys.FLAGS_SAFE_MODE_ENABLED, false);
+        }
+
+        return mSafeModeExperimentEnabled;
+    }
+
     @Behavior
     int getBehaviorForTesting() {
         return mBehavior.get();
@@ -298,10 +330,15 @@
         mBehavior.set(Behavior.UNKNOWN);
         mStartCheckpointWritten.set(false);
         mEndCheckpointWritten.set(false);
+        mSafeModeExperimentEnabled = null;
     }
 
     @SuppressLint({"ApplySharedPref"})
     static void clearDiskForTesting() {
         getSafeValuePreferences().edit().clear().commit();
     }
+
+    void setExperimentEnabledForTesting(Boolean value) {
+        mSafeModeExperimentForcedForTesting = value;
+    }
 }
diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
index 659ebd2..d1f1244 100644
--- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
+++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
@@ -487,6 +487,7 @@
     public static final String RELATED_SEARCHES_UI = "RelatedSearchesUi";
     public static final String REQUEST_DESKTOP_SITE_FOR_TABLETS = "RequestDesktopSiteForTablets";
     public static final String SAFE_BROWSING_DELAYED_WARNINGS = "SafeBrowsingDelayedWarnings";
+    public static final String SAFE_MODE_FOR_CACHED_FLAGS = "SafeModeForCachedFlags";
     public static final String SCREENSHOTS_FOR_ANDROID_V2 = "ScreenshotsForAndroidV2";
 
     public static final String SEARCH_ENGINE_PROMO_EXISTING_DEVICE =
diff --git a/chrome/browser/history_clusters/java/res/menu/history_clusters_menu.xml b/chrome/browser/history_clusters/java/res/menu/history_clusters_menu.xml
index 045b4d4..491a228 100644
--- a/chrome/browser/history_clusters/java/res/menu/history_clusters_menu.xml
+++ b/chrome/browser/history_clusters/java/res/menu/history_clusters_menu.xml
@@ -25,6 +25,10 @@
         android:title="@string/close"
         app:showAsAction="ifRoom"
         app:iconTint="@color/default_icon_color_secondary_tint_list" />
+    <item
+        android:id="@+id/optout_menu_id"
+        android:title="@string/history_clusters_disable_menu_item_label"
+        app:showAsAction="never" />
   </group>
   <group
       android:id="@+id/selection_mode_menu_group"
diff --git a/chrome/browser/history_clusters/java/src/org/chromium/chrome/browser/history_clusters/HistoryClustersCoordinator.java b/chrome/browser/history_clusters/java/src/org/chromium/chrome/browser/history_clusters/HistoryClustersCoordinator.java
index 3a0b15b..65e57de 100644
--- a/chrome/browser/history_clusters/java/src/org/chromium/chrome/browser/history_clusters/HistoryClustersCoordinator.java
+++ b/chrome/browser/history_clusters/java/src/org/chromium/chrome/browser/history_clusters/HistoryClustersCoordinator.java
@@ -229,6 +229,9 @@
             mMediator.deleteVisits(mSelectionDelegate.getSelectedItemsAsList());
             mSelectionDelegate.clearSelection();
             return true;
+        } else if (menuItem.getItemId() == R.id.optout_menu_id) {
+            mDelegate.onOptOut();
+            return true;
         }
         return false;
     }
diff --git a/chrome/browser/history_clusters/java/src/org/chromium/chrome/browser/history_clusters/HistoryClustersDelegate.java b/chrome/browser/history_clusters/java/src/org/chromium/chrome/browser/history_clusters/HistoryClustersDelegate.java
index e64b203b7..0f3358c 100644
--- a/chrome/browser/history_clusters/java/src/org/chromium/chrome/browser/history_clusters/HistoryClustersDelegate.java
+++ b/chrome/browser/history_clusters/java/src/org/chromium/chrome/browser/history_clusters/HistoryClustersDelegate.java
@@ -84,4 +84,9 @@
     default String getSearchEmptyString() {
         return "";
     }
+    /**
+     * Called when the user opts out of the Journeys feature to signal to the embedding component
+     * that it should remove the HistoryClusters UI.
+     */
+    default void onOptOut() {}
 }
diff --git a/chrome/browser/icon_loader_auralinux.cc b/chrome/browser/icon_loader_auralinux.cc
index 40d2a6e..61d1ad7 100644
--- a/chrome/browser/icon_loader_auralinux.cc
+++ b/chrome/browser/icon_loader_auralinux.cc
@@ -8,7 +8,7 @@
 #include "base/nix/mime_util_xdg.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
-#include "ui/views/linux_ui/linux_ui.h"
+#include "ui/linux/linux_ui.h"
 
 // static
 IconLoader::IconGroup IconLoader::GroupForFilepath(
@@ -18,7 +18,7 @@
 
 // static
 scoped_refptr<base::TaskRunner> IconLoader::GetReadIconTaskRunner() {
-  // ReadIcon() calls into views::LinuxUI and GTK code, so it must be on the UI
+  // ReadIcon() calls into ui::LinuxUi and GTK code, so it must be on the UI
   // thread.
   return content::GetUIThreadTaskRunner({});
 }
@@ -40,7 +40,7 @@
   }
 
   gfx::Image image;
-  views::LinuxUI* ui = views::LinuxUI::instance();
+  ui::LinuxUi* ui = ui::LinuxUi::instance();
   if (ui) {
     image = ui->GetIconForContentType(group_, size_pixels, scale_);
   }
diff --git a/chrome/browser/media/router/discovery/dial/safe_dial_app_info_parser.cc b/chrome/browser/media/router/discovery/dial/safe_dial_app_info_parser.cc
index dde8363e..18f0a0c1 100644
--- a/chrome/browser/media/router/discovery/dial/safe_dial_app_info_parser.cc
+++ b/chrome/browser/media/router/discovery/dial/safe_dial_app_info_parser.cc
@@ -103,7 +103,7 @@
 void SafeDialAppInfoParser::OnXmlParsingDone(
     SafeDialAppInfoParser::ParseCallback callback,
     data_decoder::DataDecoder::ValueOrError result) {
-  if (!result.value || !result.value->is_dict()) {
+  if (!result.has_value() || !result->is_dict()) {
     std::move(callback).Run(nullptr, ParsingResult::kInvalidXML);
     return;
   }
@@ -111,8 +111,8 @@
   // NOTE: enforce namespace check for <service> element in future. Namespace
   // value will be "urn:dial-multiscreen-org:schemas:dial".
   bool unique_service = true;
-  const base::Value* service_element = data_decoder::FindXmlElementPath(
-      *result.value, {"service"}, &unique_service);
+  const base::Value* service_element =
+      data_decoder::FindXmlElementPath(*result, {"service"}, &unique_service);
   if (!service_element || !unique_service) {
     std::move(callback).Run(nullptr, ParsingResult::kInvalidXML);
     return;
diff --git a/chrome/browser/media/router/discovery/dial/safe_dial_device_description_parser.cc b/chrome/browser/media/router/discovery/dial/safe_dial_device_description_parser.cc
index 827443f..7a37a64 100644
--- a/chrome/browser/media/router/discovery/dial/safe_dial_device_description_parser.cc
+++ b/chrome/browser/media/router/discovery/dial/safe_dial_device_description_parser.cc
@@ -53,7 +53,7 @@
     SafeDialDeviceDescriptionParser::ParseCallback callback,
     const GURL& app_url,
     data_decoder::DataDecoder::ValueOrError result) {
-  if (!result.value || !result.value->is_dict()) {
+  if (!result.has_value() || !result->is_dict()) {
     std::move(callback).Run(
         ParsedDialDeviceDescription(),
         SafeDialDeviceDescriptionParser::ParsingError::kInvalidXml);
@@ -62,7 +62,7 @@
 
   bool unique_device = true;
   const base::Value* device_element = data_decoder::FindXmlElementPath(
-      *result.value, {"root", "device"}, &unique_device);
+      *result, {"root", "device"}, &unique_device);
   if (!device_element) {
     NotifyParsingError(
         std::move(callback),
diff --git a/chrome/browser/media/router/providers/cast/cast_activity_manager.cc b/chrome/browser/media/router/providers/cast/cast_activity_manager.cc
index a59d6cd..c7a10818 100644
--- a/chrome/browser/media/router/providers/cast/cast_activity_manager.cc
+++ b/chrome/browser/media/router/providers/cast/cast_activity_manager.cc
@@ -114,11 +114,11 @@
     bool off_the_record,
     mojom::MediaRouteProvider::CreateRouteCallback callback,
     data_decoder::DataDecoder::ValueOrError result) {
-  if (!cast_source.app_params().empty() && result.error) {
-    logger_->LogError(
-        mojom::LogCategory::kRoute, kLoggerComponent,
-        base::StrCat({"Error parsing JSON data in appParams: ", *result.error}),
-        sink.id(), cast_source.source_id(), presentation_id);
+  if (!cast_source.app_params().empty() && !result.has_value()) {
+    logger_->LogError(mojom::LogCategory::kRoute, kLoggerComponent,
+                      base::StrCat({"Error parsing JSON data in appParams: ",
+                                    result.error()}),
+                      sink.id(), cast_source.source_id(), presentation_id);
     std::move(callback).Run(
         absl::nullopt, nullptr, std::string("Invalid JSON Format of appParams"),
         mojom::RouteRequestResultCode::NO_SUPPORTED_PROVIDER);
@@ -144,8 +144,15 @@
   route.set_media_sink_name(sink.sink().name());
   route.set_is_connecting(true);
 
+  // We either have a value, or an error, however `LaunchSession` calls this
+  // function is a default constructed `result`, which is supposed to be
+  // ignored.
+  absl::optional<base::Value> opt_result = absl::nullopt;
+  if (result.has_value() && !result->is_none())
+    opt_result = std::move(*result);
+
   DoLaunchSessionParams params(route, cast_source, sink, origin, tab_id,
-                               std::move(result.value), std::move(callback));
+                               std::move(opt_result), std::move(callback));
 
   // If there is currently a session on the sink, it must be terminated before
   // the new session can be launched.
@@ -656,17 +663,17 @@
     const std::string& media_route_id,
     const std::string& message,
     data_decoder::DataDecoder::ValueOrError result) {
-  if (result.error) {
+  if (!result.has_value()) {
     logger_->LogError(
         mojom::LogCategory::kRoute, kLoggerComponent,
         "Error parsing JSON data when sending route JSON message: " +
-            *result.error,
+            result.error(),
         "", MediaRoute::GetMediaSourceIdFromMediaRouteId(media_route_id),
         MediaRoute::GetPresentationIdFromMediaRouteId(media_route_id));
     return;
   }
 
-  const std::string* client_id = result.value->FindStringKey("clientId");
+  const std::string* client_id = result->FindStringKey("clientId");
   if (!client_id) {
     logger_->LogError(
         mojom::LogCategory::kRoute, kLoggerComponent,
diff --git a/chrome/browser/media/router/providers/cast/cast_session_client_impl.cc b/chrome/browser/media/router/providers/cast/cast_session_client_impl.cc
index cbfa464..71ca9b1b 100644
--- a/chrome/browser/media/router/providers/cast/cast_session_client_impl.cc
+++ b/chrome/browser/media/router/providers/cast/cast_session_client_impl.cc
@@ -145,19 +145,19 @@
 
 void CastSessionClientImpl::HandleParsedClientMessage(
     data_decoder::DataDecoder::ValueOrError result) {
-  if (!result.value) {
+  if (!result.has_value()) {
     ReportClientMessageParseError(activity_->route().media_route_id(),
-                                  *result.error);
+                                  result.error());
     return;
   }
 
   // NOTE(jrw): This step isn't part of the Cast protocol per se, but it's
   // required for backward compatibility.  There is one known case
   // (crbug.com/1129217) where not doing it breaks the Cast SDK.
-  RemoveNullFields(*result.value);
+  RemoveNullFields(*result);
 
   std::unique_ptr<CastInternalMessage> cast_message =
-      CastInternalMessage::From(std::move(*result.value));
+      CastInternalMessage::From(std::move(*result));
   if (!cast_message) {
     ReportClientMessageParseError(activity_->route().media_route_id(),
                                   "Not a Cast message");
diff --git a/chrome/browser/media/router/providers/cast/mirroring_activity.cc b/chrome/browser/media/router/providers/cast/mirroring_activity.cc
index 36fcb28..03d88afb 100644
--- a/chrome/browser/media/router/providers/cast/mirroring_activity.cc
+++ b/chrome/browser/media/router/providers/cast/mirroring_activity.cc
@@ -385,29 +385,29 @@
   CastSession* session = GetSession();
   DCHECK(session);
 
-  if (!result.value) {
+  if (!result.has_value()) {
     // TODO(crbug.com/905002): Record UMA metric for parse result.
     logger_->LogError(
         media_router::mojom::LogCategory::kMirroring, kLoggerComponent,
-        base::StrCat({"Failed to parse Cast client message:", *result.error}),
+        base::StrCat({"Failed to parse Cast client message:", result.error()}),
         route().media_sink_id(), route().media_source().id(),
         route().presentation_id());
     return;
   }
 
-  const std::string message_namespace = GetMirroringNamespace(*result.value);
+  const std::string message_namespace = GetMirroringNamespace(*result);
   if (message_namespace == mirroring::mojom::kWebRtcNamespace) {
     logger_->LogInfo(media_router::mojom::LogCategory::kMirroring,
                      kLoggerComponent,
                      base::StrCat({"WebRTC message received: ",
-                                   GetScrubbedLogMessage(*result.value)}),
+                                   GetScrubbedLogMessage(*result)}),
                      route().media_sink_id(), route().media_source().id(),
                      route().presentation_id());
   }
 
   cast::channel::CastMessage cast_message = cast_channel::CreateCastMessage(
-      message_namespace, std::move(*result.value),
-      message_handler_->sender_id(), session->transport_id());
+      message_namespace, std::move(*result), message_handler_->sender_id(),
+      session->transport_id());
   if (message_handler_->SendCastMessage(cast_data_.cast_channel_id,
                                         cast_message) == Result::kFailed) {
     logger_->LogError(
diff --git a/chrome/browser/media/router/providers/dial/dial_media_route_provider.cc b/chrome/browser/media/router/providers/dial/dial_media_route_provider.cc
index 94973814..b99ebfae 100644
--- a/chrome/browser/media/router/providers/dial/dial_media_route_provider.cc
+++ b/chrome/browser/media/router/providers/dial/dial_media_route_provider.cc
@@ -216,19 +216,19 @@
 void DialMediaRouteProvider::HandleParsedRouteMessage(
     const MediaRoute::Id& route_id,
     data_decoder::DataDecoder::ValueOrError result) {
-  if (!result.value) {
+  if (!result.has_value()) {
     logger_->LogError(
         mojom::LogCategory::kRoute, kLoggerComponent,
-        base::StrCat({"Failed to parse the route message. ", *result.error}),
+        base::StrCat({"Failed to parse the route message. ", result.error()}),
         "", MediaRoute::GetMediaSourceIdFromMediaRouteId(route_id),
         MediaRoute::GetPresentationIdFromMediaRouteId(route_id));
-    ReportParseError(DialParseMessageResult::kParseError, *result.error);
+    ReportParseError(DialParseMessageResult::kParseError, result.error());
     return;
   }
 
   std::string error;
   std::unique_ptr<DialInternalMessage> internal_message =
-      DialInternalMessage::From(std::move(*result.value), &error);
+      DialInternalMessage::From(std::move(*result), &error);
   if (!internal_message) {
     logger_->LogError(mojom::LogCategory::kRoute, kLoggerComponent,
                       base::StrCat({"Invalid route message. ", error}), "",
diff --git a/chrome/browser/new_tab_page/modules/drive/drive_service.cc b/chrome/browser/new_tab_page/modules/drive/drive_service.cc
index 33dfa75..24e15921 100644
--- a/chrome/browser/new_tab_page/modules/drive/drive_service.cc
+++ b/chrome/browser/new_tab_page/modules/drive/drive_service.cc
@@ -290,14 +290,14 @@
 
 void DriveService::OnJsonParsed(
     data_decoder::DataDecoder::ValueOrError result) {
-  if (!result.value) {
+  if (!result.has_value()) {
     for (auto& callback : callbacks_) {
       std::move(callback).Run(std::vector<drive::mojom::FilePtr>());
     }
     callbacks_.clear();
     return;
   }
-  auto* items = result.value->FindListPath("item");
+  auto* items = result->FindListPath("item");
   if (!items) {
     for (auto& callback : callbacks_) {
       std::move(callback).Run(std::vector<drive::mojom::FilePtr>());
diff --git a/chrome/browser/new_tab_page/modules/photos/photos_service.cc b/chrome/browser/new_tab_page/modules/photos/photos_service.cc
index 7d5057a..1f5e9b6 100644
--- a/chrome/browser/new_tab_page/modules/photos/photos_service.cc
+++ b/chrome/browser/new_tab_page/modules/photos/photos_service.cc
@@ -413,7 +413,7 @@
 void PhotosService::OnJsonParsed(
     const std::string& token,
     data_decoder::DataDecoder::ValueOrError result) {
-  if (!result.value) {
+  if (!result.has_value()) {
     for (auto& callback : callbacks_) {
       std::move(callback).Run(std::vector<photos::mojom::MemoryPtr>());
     }
@@ -421,7 +421,7 @@
     return;
   }
 
-  auto* memories = result.value->FindListPath("memory");
+  auto* memories = result->FindListPath("memory");
   if (!memories) {
     base::UmaHistogramCustomCounts("NewTabPage.Photos.DataResponseCount", 0, 0,
                                    10, 11);
diff --git a/chrome/browser/new_tab_page/modules/task_module/task_module_service.cc b/chrome/browser/new_tab_page/modules/task_module/task_module_service.cc
index f9d07d9..8e3a67696 100644
--- a/chrome/browser/new_tab_page/modules/task_module/task_module_service.cc
+++ b/chrome/browser/new_tab_page/modules/task_module/task_module_service.cc
@@ -223,15 +223,15 @@
 void TaskModuleService::OnJsonParsed(
     TaskModuleCallback callback,
     data_decoder::DataDecoder::ValueOrError result) {
-  if (!result.value) {
+  if (!result.has_value()) {
     std::move(callback).Run(nullptr);
     return;
   }
 
   // We receive a list of tasks ordered from highest to lowest priority. We only
   // support showing a single task though. Therefore, pick the first task.
-  auto* tasks = result.value->FindListPath(
-      base::StringPrintf("update.%s", GetTasksKey()));
+  auto* tasks =
+      result->FindListPath(base::StringPrintf("update.%s", GetTasksKey()));
   if (!tasks || tasks->GetListDeprecated().size() == 0) {
     std::move(callback).Run(nullptr);
     return;
diff --git a/chrome/browser/new_tab_page/new_tab_page_browsertest.cc b/chrome/browser/new_tab_page/new_tab_page_browsertest.cc
index 0a35555..a4fb205 100644
--- a/chrome/browser/new_tab_page/new_tab_page_browsertest.cc
+++ b/chrome/browser/new_tab_page/new_tab_page_browsertest.cc
@@ -42,7 +42,7 @@
   NewTabPageTest() {
     features_.InitWithFeatures(
         {}, {ntp_features::kNtpOneGoogleBar, ntp_features::kNtpShortcuts,
-             ntp_features::kNtpMiddleSlotPromo, ntp_features::kModules});
+             ntp_features::kNtpMiddleSlotPromo});
   }
 
   ~NewTabPageTest() override = default;
diff --git a/chrome/browser/new_tab_page/one_google_bar/one_google_bar_loader_impl.cc b/chrome/browser/new_tab_page/one_google_bar/one_google_bar_loader_impl.cc
index 8e4d22e..8715292 100644
--- a/chrome/browser/new_tab_page/one_google_bar/one_google_bar_loader_impl.cc
+++ b/chrome/browser/new_tab_page/one_google_bar/one_google_bar_loader_impl.cc
@@ -369,13 +369,13 @@
 
 void OneGoogleBarLoaderImpl::JsonParsed(
     data_decoder::DataDecoder::ValueOrError result) {
-  if (!result.value) {
-    DVLOG(1) << "Parsing JSON failed: " << *result.error;
+  if (!result.has_value()) {
+    DVLOG(1) << "Parsing JSON failed: " << result.error();
     Respond(Status::FATAL_ERROR, absl::nullopt);
     return;
   }
 
-  absl::optional<OneGoogleBarData> data = JsonToOGBData(*result.value);
+  absl::optional<OneGoogleBarData> data = JsonToOGBData(*result);
   Respond(data.has_value() ? Status::OK : Status::FATAL_ERROR, data);
 }
 
diff --git a/chrome/browser/new_tab_page/promos/promo_service.cc b/chrome/browser/new_tab_page/promos/promo_service.cc
index 049cc39..5791daa 100644
--- a/chrome/browser/new_tab_page/promos/promo_service.cc
+++ b/chrome/browser/new_tab_page/promos/promo_service.cc
@@ -201,8 +201,8 @@
 
 void PromoService::OnJsonParsed(
     data_decoder::DataDecoder::ValueOrError result) {
-  if (!result.value) {
-    DVLOG(1) << "Parsing JSON failed: " << *result.error;
+  if (!result.has_value()) {
+    DVLOG(1) << "Parsing JSON failed: " << result.error();
     PromoDataLoaded(Status::FATAL_ERROR, absl::nullopt);
     return;
   }
@@ -210,7 +210,7 @@
   absl::optional<PromoData> data;
   PromoService::Status status;
 
-  if (JsonToPromoData(*result.value, &data)) {
+  if (JsonToPromoData(*result, &data)) {
     bool is_blocked = IsBlockedAfterClearingExpired(data->promo_id);
     if (is_blocked)
       data = PromoData();
diff --git a/chrome/browser/notifications/android/java/src/org/chromium/chrome/browser/notifications/NotificationIntentInterceptorTest.java b/chrome/browser/notifications/android/java/src/org/chromium/chrome/browser/notifications/NotificationIntentInterceptorTest.java
index 2acacd6..5d1da83 100644
--- a/chrome/browser/notifications/android/java/src/org/chromium/chrome/browser/notifications/NotificationIntentInterceptorTest.java
+++ b/chrome/browser/notifications/android/java/src/org/chromium/chrome/browser/notifications/NotificationIntentInterceptorTest.java
@@ -30,7 +30,8 @@
 import org.robolectric.shadows.ShadowNotificationManager;
 import org.robolectric.shadows.ShadowPendingIntent;
 
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.browser.notifications.channels.ChromeChannelDefinitions;
 import org.chromium.components.browser_ui.notifications.NotificationManagerProxyImpl;
@@ -44,8 +45,7 @@
  * track metrics correctly.
  */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(shadows = {ShadowNotificationManager.class, ShadowPendingIntent.class,
-                ShadowRecordHistogram.class})
+@Config(shadows = {ShadowNotificationManager.class, ShadowPendingIntent.class})
 @LooperMode(LooperMode.Mode.LEGACY)
 public class NotificationIntentInterceptorTest {
     private static final String TEST_NOTIFICATION_TITLE = "Test notification title";
@@ -80,7 +80,7 @@
     @Before
     public void setUp() throws Exception {
         ShadowLog.stream = System.out;
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
         mContext = RuntimeEnvironment.application;
         mShadowNotificationManager = shadowOf(
                 (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE));
@@ -92,7 +92,7 @@
 
     @After
     public void tearDown() {
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
     }
 
     // Builds a simple notification used in tests.
@@ -157,7 +157,7 @@
         Assert.assertEquals(receivedIntent.getExtras().getInt(EXTRA_INTENT_TYPE),
                 NotificationIntentInterceptor.IntentType.CONTENT_INTENT);
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Mobile.SystemNotification.Content.Click",
                         NotificationUmaTracker.SystemNotificationType.DOWNLOAD_FILES));
     }
@@ -180,7 +180,7 @@
 
         // Verify the histogram.
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Mobile.SystemNotification.Dismiss",
                         NotificationUmaTracker.SystemNotificationType.DOWNLOAD_FILES));
         Assert.assertNull(mReceiver.intentReceived());
@@ -210,7 +210,7 @@
         Assert.assertEquals(NotificationIntentInterceptor.IntentType.ACTION_INTENT,
                 receivedIntent.getExtras().getInt(EXTRA_INTENT_TYPE));
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Mobile.SystemNotification.Action.Click",
                         NotificationUmaTracker.ActionType.DOWNLOAD_PAUSE));
     }
diff --git a/chrome/browser/notifications/android/java/src/org/chromium/chrome/browser/notifications/permissions/NotificationPermissionChangeReceiverTest.java b/chrome/browser/notifications/android/java/src/org/chromium/chrome/browser/notifications/permissions/NotificationPermissionChangeReceiverTest.java
index 435b1a77..3f1cee2 100644
--- a/chrome/browser/notifications/android/java/src/org/chromium/chrome/browser/notifications/permissions/NotificationPermissionChangeReceiverTest.java
+++ b/chrome/browser/notifications/android/java/src/org/chromium/chrome/browser/notifications/permissions/NotificationPermissionChangeReceiverTest.java
@@ -17,18 +17,18 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.metrics.RecordHistogram;
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 
 /**
  * Robolectric tests for {@link NotificationPermissionChangeReceiver}.
  */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(sdk = 30, manifest = Config.NONE, shadows = {ShadowRecordHistogram.class})
+@Config(sdk = 30, manifest = Config.NONE)
 public class NotificationPermissionChangeReceiverTest {
     @Before
     public void setUp() {
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
     }
 
     private void verifyPermissionChangeHistogramWasRecorded(boolean expectedPermissionState) {
diff --git a/chrome/browser/notifications/android/java/src/org/chromium/chrome/browser/notifications/permissions/NotificationPermissionControllerTest.java b/chrome/browser/notifications/android/java/src/org/chromium/chrome/browser/notifications/permissions/NotificationPermissionControllerTest.java
index 6358fc1..6796993 100644
--- a/chrome/browser/notifications/android/java/src/org/chromium/chrome/browser/notifications/permissions/NotificationPermissionControllerTest.java
+++ b/chrome/browser/notifications/android/java/src/org/chromium/chrome/browser/notifications/permissions/NotificationPermissionControllerTest.java
@@ -26,7 +26,7 @@
 import org.chromium.base.ContextUtils;
 import org.chromium.base.FeatureList;
 import org.chromium.base.metrics.RecordHistogram;
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.ShadowBuildInfo;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
@@ -48,7 +48,7 @@
  */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(sdk = 30, manifest = Config.NONE,
-        shadows = {ShadowBuildInfo.class, ShadowRecordHistogram.class, ShadowSystemClock.class},
+        shadows = {ShadowBuildInfo.class, ShadowSystemClock.class},
         // We use ShadowSystemClock to manipulate System.currentTimeMillis, but we must tell
         // Robolectric to instrument the code in the packages below so it can replace the original
         // implementation with the shadow one.
@@ -59,7 +59,7 @@
     @Before
     public void setUp() {
         ShadowBuildInfo.reset();
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
         ShadowSystemClock.reset();
         // Set a non-zero currentTimeMillis.
         ShadowSystemClock.advanceBy(Duration.ofDays(10));
diff --git a/chrome/browser/notifications/android/java/src/org/chromium/chrome/browser/notifications/permissions/NotificationPermissionRationaleDialogControllerTest.java b/chrome/browser/notifications/android/java/src/org/chromium/chrome/browser/notifications/permissions/NotificationPermissionRationaleDialogControllerTest.java
index eea1cd7..cb2a801 100644
--- a/chrome/browser/notifications/android/java/src/org/chromium/chrome/browser/notifications/permissions/NotificationPermissionRationaleDialogControllerTest.java
+++ b/chrome/browser/notifications/android/java/src/org/chromium/chrome/browser/notifications/permissions/NotificationPermissionRationaleDialogControllerTest.java
@@ -22,13 +22,12 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mockito;
-import org.robolectric.annotation.Config;
 
 import org.chromium.base.Callback;
 import org.chromium.base.FeatureList;
 import org.chromium.base.FeatureList.TestValues;
 import org.chromium.base.metrics.RecordHistogram;
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.notifications.NotificationUmaTracker.NotificationRationaleResult;
@@ -44,7 +43,6 @@
  * Tests for {@link NotificationPermissionRationaleDialogController}.
  */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(shadows = {ShadowRecordHistogram.class})
 @Features.DisableFeatures(ChromeFeatureList.NOTIFICATION_PERMISSION_VARIANT)
 public class NotificationPermissionRationaleDialogControllerTest {
     private ModalDialogManager mModalDialogManager;
@@ -52,7 +50,7 @@
 
     @Before
     public void setUp() {
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
         mModalDialogManager =
                 new ModalDialogManager(Mockito.mock(ModalDialogManager.Presenter.class), 0);
         mContext = ApplicationProvider.getApplicationContext();
diff --git a/chrome/browser/password_check/android/junit/src/org/chromium/chrome/browser/password_check/PasswordCheckControllerTest.java b/chrome/browser/password_check/android/junit/src/org/chromium/chrome/browser/password_check/PasswordCheckControllerTest.java
index 959af516..6b4ad73e 100644
--- a/chrome/browser/password_check/android/junit/src/org/chromium/chrome/browser/password_check/PasswordCheckControllerTest.java
+++ b/chrome/browser/password_check/android/junit/src/org/chromium/chrome/browser/password_check/PasswordCheckControllerTest.java
@@ -57,7 +57,7 @@
 import org.chromium.base.Callback;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.metrics.RecordHistogramJni;
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.JniMocker;
 import org.chromium.chrome.browser.device_reauth.ReauthenticatorBridge;
@@ -81,7 +81,7 @@
  * properly.
  */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(manifest = Config.NONE, shadows = {ShadowRecordHistogram.class})
+@Config(manifest = Config.NONE)
 @SuppressWarnings("DoNotMock") // Mocks GURL.
 public class PasswordCheckControllerTest {
     private static final CompromisedCredential ANA =
@@ -138,7 +138,7 @@
 
     @Before
     public void setUp() {
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
         MockitoAnnotations.initMocks(this);
         mJniMocker.mock(RecordHistogramJni.TEST_HOOKS, mRecordHistogramBridge);
         mModel = PasswordCheckProperties.createDefaultModel();
diff --git a/chrome/browser/password_entry_edit/android/internal/java/src/org/chromium/chrome/browser/password_entry_edit/CredentialEditControllerTest.java b/chrome/browser/password_entry_edit/android/internal/java/src/org/chromium/chrome/browser/password_entry_edit/CredentialEditControllerTest.java
index 33bcd54..e39bbff 100644
--- a/chrome/browser/password_entry_edit/android/internal/java/src/org/chromium/chrome/browser/password_entry_edit/CredentialEditControllerTest.java
+++ b/chrome/browser/password_entry_edit/android/internal/java/src/org/chromium/chrome/browser/password_entry_edit/CredentialEditControllerTest.java
@@ -53,7 +53,7 @@
 
 import org.chromium.base.Callback;
 import org.chromium.base.metrics.RecordHistogram;
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.browser.password_entry_edit.CredentialEditCoordinator.CredentialActionDelegate;
 import org.chromium.chrome.browser.password_entry_edit.CredentialEditMediator.CredentialEntryAction;
@@ -66,7 +66,7 @@
  * Tests verifying that the credential edit mediator modifies the model correctly.
  */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(manifest = Config.NONE, shadows = {ShadowRecordHistogram.class})
+@Config(manifest = Config.NONE)
 public class CredentialEditControllerTest {
     private static final String TEST_URL = "https://m.a.xyz/signin";
     private static final String TEST_USERNAME = "TestUsername";
@@ -99,6 +99,7 @@
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
+        UmaRecorderHolder.resetForTesting();
         mMediator = new CredentialEditMediator(mReauthenticationHelper, mDeleteDialogHelper,
                 mCredentialActionDelegate, mHelpLauncher, false);
         mModel = new PropertyModel.Builder(ALL_KEYS)
diff --git a/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordCheckupClientMetricsRecorderTest.java b/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordCheckupClientMetricsRecorderTest.java
index 930be5ce..e3ffaff0 100644
--- a/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordCheckupClientMetricsRecorderTest.java
+++ b/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordCheckupClientMetricsRecorderTest.java
@@ -16,7 +16,8 @@
 import org.robolectric.annotation.Config;
 import org.robolectric.shadows.ShadowSystemClock;
 
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.browser.password_manager.CredentialManagerLauncher.CredentialManagerError;
 import org.chromium.chrome.browser.password_manager.PasswordCheckupClientHelper.PasswordCheckBackendException;
@@ -28,13 +29,13 @@
  * Tests that metric reporter correctly writes the histograms depending on the operation and error.
  */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(manifest = Config.NONE, shadows = {ShadowRecordHistogram.class, ShadowSystemClock.class})
+@Config(manifest = Config.NONE, shadows = {ShadowSystemClock.class})
 public class PasswordCheckupClientMetricsRecorderTest {
     private static final String PASSWORD_CHECKUP_HISTOGRAM_BASE = "PasswordManager.PasswordCheckup";
 
     @Before
     public void setUp() {
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
     }
 
     private String getSuffixForOperation(@PasswordCheckOperation int operation) {
@@ -54,19 +55,15 @@
         final String nameWithSuffix =
                 PASSWORD_CHECKUP_HISTOGRAM_BASE + "." + getSuffixForOperation(operation);
         assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
-                        nameWithSuffix + ".Success", 1));
+                RecordHistogram.getHistogramValueCountForTesting(nameWithSuffix + ".Success", 1));
         assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
-                        nameWithSuffix + ".Latency", 0));
+                RecordHistogram.getHistogramValueCountForTesting(nameWithSuffix + ".Latency", 0));
         assertEquals(0,
-                ShadowRecordHistogram.getHistogramTotalCountForTesting(
-                        nameWithSuffix + ".ErrorLatency"));
-        assertEquals(0,
-                ShadowRecordHistogram.getHistogramTotalCountForTesting(nameWithSuffix + ".Error"));
-        assertEquals(0,
-                ShadowRecordHistogram.getHistogramTotalCountForTesting(
-                        nameWithSuffix + ".ApiError"));
+                RecordHistogram.getHistogramTotalCountForTesting(nameWithSuffix + ".ErrorLatency"));
+        assertEquals(
+                0, RecordHistogram.getHistogramTotalCountForTesting(nameWithSuffix + ".Error"));
+        assertEquals(
+                0, RecordHistogram.getHistogramTotalCountForTesting(nameWithSuffix + ".ApiError"));
     }
 
     private void checkHistogramsOnFailure(
@@ -74,24 +71,22 @@
         final String nameWithSuffix =
                 PASSWORD_CHECKUP_HISTOGRAM_BASE + "." + getSuffixForOperation(operation);
         assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
-                        nameWithSuffix + ".Success", 0));
-        assertEquals(0,
-                ShadowRecordHistogram.getHistogramTotalCountForTesting(
-                        nameWithSuffix + ".Latency"));
+                RecordHistogram.getHistogramValueCountForTesting(nameWithSuffix + ".Success", 0));
+        assertEquals(
+                0, RecordHistogram.getHistogramTotalCountForTesting(nameWithSuffix + ".Latency"));
         assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         nameWithSuffix + ".ErrorLatency", 0));
         assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         nameWithSuffix + ".Error", errorCode));
         apiErrorCode.ifPresentOrElse(apiError
                 -> assertEquals(1,
-                        ShadowRecordHistogram.getHistogramValueCountForTesting(
+                        RecordHistogram.getHistogramValueCountForTesting(
                                 nameWithSuffix + ".APIError", apiError)),
                 ()
                         -> assertEquals(0,
-                                ShadowRecordHistogram.getHistogramTotalCountForTesting(
+                                RecordHistogram.getHistogramTotalCountForTesting(
                                         nameWithSuffix + ".APIError")));
     }
 
diff --git a/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordManagerHelperTest.java b/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordManagerHelperTest.java
index ec03908..d0868ea6 100644
--- a/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordManagerHelperTest.java
+++ b/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordManagerHelperTest.java
@@ -44,7 +44,7 @@
 import org.chromium.base.CollectionUtil;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.metrics.RecordHistogram;
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.supplier.ObservableSupplier;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.Batch;
@@ -76,7 +76,7 @@
 
 /** Tests for password manager helper methods. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(manifest = Config.NONE, shadows = {ShadowSystemClock.class, ShadowRecordHistogram.class})
+@Config(manifest = Config.NONE, shadows = {ShadowSystemClock.class})
 @Batch(Batch.PER_CLASS)
 public class PasswordManagerHelperTest {
     private static final String TEST_EMAIL_ADDRESS = "test@email.com";
@@ -149,7 +149,7 @@
 
     @Before
     public void setUp() {
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
         MockitoAnnotations.initMocks(this);
         mJniMocker.mock(UserPrefsJni.TEST_HOOKS, mUserPrefsJniMock);
         Profile.setLastUsedProfileForTesting(mProfile);
@@ -1385,19 +1385,15 @@
         final String nameWithSuffix = PASSWORD_CHECKUP_HISTOGRAM_BASE + "."
                 + getPasswordCheckupHistogramSuffixForOperation(operation);
         assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
-                        nameWithSuffix + ".Success", 1));
+                RecordHistogram.getHistogramValueCountForTesting(nameWithSuffix + ".Success", 1));
         assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
-                        nameWithSuffix + ".Latency", 0));
+                RecordHistogram.getHistogramValueCountForTesting(nameWithSuffix + ".Latency", 0));
         assertEquals(0,
-                ShadowRecordHistogram.getHistogramTotalCountForTesting(
-                        nameWithSuffix + ".ErrorLatency"));
-        assertEquals(0,
-                ShadowRecordHistogram.getHistogramTotalCountForTesting(nameWithSuffix + ".Error"));
-        assertEquals(0,
-                ShadowRecordHistogram.getHistogramTotalCountForTesting(
-                        nameWithSuffix + ".ApiError"));
+                RecordHistogram.getHistogramTotalCountForTesting(nameWithSuffix + ".ErrorLatency"));
+        assertEquals(
+                0, RecordHistogram.getHistogramTotalCountForTesting(nameWithSuffix + ".Error"));
+        assertEquals(
+                0, RecordHistogram.getHistogramTotalCountForTesting(nameWithSuffix + ".ApiError"));
     }
 
     private void checkPasswordCheckupFailureHistogramsForOperation(
@@ -1405,24 +1401,22 @@
         final String nameWithSuffix = PASSWORD_CHECKUP_HISTOGRAM_BASE + "."
                 + getPasswordCheckupHistogramSuffixForOperation(operation);
         assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
-                        nameWithSuffix + ".Success", 0));
-        assertEquals(0,
-                ShadowRecordHistogram.getHistogramTotalCountForTesting(
-                        nameWithSuffix + ".Latency"));
+                RecordHistogram.getHistogramValueCountForTesting(nameWithSuffix + ".Success", 0));
+        assertEquals(
+                0, RecordHistogram.getHistogramTotalCountForTesting(nameWithSuffix + ".Latency"));
         assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         nameWithSuffix + ".ErrorLatency", 0));
         assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         nameWithSuffix + ".Error", errorCode));
         apiErrorCode.ifPresentOrElse(apiError
                 -> assertEquals(1,
-                        ShadowRecordHistogram.getHistogramValueCountForTesting(
+                        RecordHistogram.getHistogramValueCountForTesting(
                                 nameWithSuffix + ".APIError", apiError)),
                 ()
                         -> assertEquals(0,
-                                ShadowRecordHistogram.getHistogramTotalCountForTesting(
+                                RecordHistogram.getHistogramTotalCountForTesting(
                                         nameWithSuffix + ".APIError")));
     }
 
diff --git a/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordSettingsUpdaterBridgeTest.java b/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordSettingsUpdaterBridgeTest.java
index 59120461..3c3e555 100644
--- a/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordSettingsUpdaterBridgeTest.java
+++ b/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordSettingsUpdaterBridgeTest.java
@@ -27,7 +27,8 @@
 import org.robolectric.shadows.ShadowSystemClock;
 
 import org.chromium.base.Callback;
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.JniMocker;
@@ -41,7 +42,7 @@
  * callbacks in return.
  */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(manifest = Config.NONE, shadows = {ShadowRecordHistogram.class, ShadowSystemClock.class})
+@Config(manifest = Config.NONE, shadows = {ShadowSystemClock.class})
 @Batch(Batch.PER_CLASS)
 public class PasswordSettingsUpdaterBridgeTest {
     @Rule
@@ -64,7 +65,7 @@
 
     @Before
     public void setUp() {
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
         MockitoAnnotations.initMocks(this);
         mJniMocker.mock(PasswordSettingsUpdaterBridgeJni.TEST_HOOKS, mBridgeJniMock);
         mBridge = new PasswordSettingsUpdaterBridge(sDummyNativePointer, mAccessorMock);
@@ -74,20 +75,16 @@
         final String nameWithSuffixes =
                 HISTOGRAM_NAME_BASE + "." + functionSuffix + "." + settingSuffix;
         assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
-                        nameWithSuffixes + ".Success", 1));
+                RecordHistogram.getHistogramValueCountForTesting(nameWithSuffixes + ".Success", 1));
         assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
-                        nameWithSuffixes + ".Latency", 0));
+                RecordHistogram.getHistogramValueCountForTesting(nameWithSuffixes + ".Latency", 0));
         assertEquals(0,
-                ShadowRecordHistogram.getHistogramTotalCountForTesting(
+                RecordHistogram.getHistogramTotalCountForTesting(
                         nameWithSuffixes + ".ErrorLatency"));
         assertEquals(0,
-                ShadowRecordHistogram.getHistogramTotalCountForTesting(
-                        nameWithSuffixes + ".ErrorCode"));
+                RecordHistogram.getHistogramTotalCountForTesting(nameWithSuffixes + ".ErrorCode"));
         assertEquals(0,
-                ShadowRecordHistogram.getHistogramTotalCountForTesting(
-                        nameWithSuffixes + ".APIError1"));
+                RecordHistogram.getHistogramTotalCountForTesting(nameWithSuffixes + ".APIError1"));
     }
 
     private void checkFailureHistograms(
@@ -95,24 +92,22 @@
         final String nameWithSuffixes =
                 HISTOGRAM_NAME_BASE + "." + functionSuffix + "." + settingSuffix;
         assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
-                        nameWithSuffixes + ".Success", 0));
-        assertEquals(0,
-                ShadowRecordHistogram.getHistogramTotalCountForTesting(
-                        nameWithSuffixes + ".Latency"));
+                RecordHistogram.getHistogramValueCountForTesting(nameWithSuffixes + ".Success", 0));
+        assertEquals(
+                0, RecordHistogram.getHistogramTotalCountForTesting(nameWithSuffixes + ".Latency"));
         assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         nameWithSuffixes + ".ErrorLatency", 0));
         assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         nameWithSuffixes + ".ErrorCode", errorCode));
         apiErrorCode.ifPresentOrElse(apiError
                 -> assertEquals(1,
-                        ShadowRecordHistogram.getHistogramValueCountForTesting(
+                        RecordHistogram.getHistogramValueCountForTesting(
                                 nameWithSuffixes + ".APIError1", apiError)),
                 ()
                         -> assertEquals(0,
-                                ShadowRecordHistogram.getHistogramTotalCountForTesting(
+                                RecordHistogram.getHistogramTotalCountForTesting(
                                         nameWithSuffixes + ".APIError1")));
     }
 
diff --git a/chrome/browser/performance_manager/policies/background_tab_loading_policy.cc b/chrome/browser/performance_manager/policies/background_tab_loading_policy.cc
index eb76d9b..0ffb5cb 100644
--- a/chrome/browser/performance_manager/policies/background_tab_loading_policy.cc
+++ b/chrome/browser/performance_manager/policies/background_tab_loading_policy.cc
@@ -486,11 +486,8 @@
 size_t BackgroundTabLoadingPolicy::GetFreePhysicalMemoryMib() const {
   if (free_memory_mb_for_testing_ != 0)
     return free_memory_mb_for_testing_;
-  constexpr int64_t kMibibytesInBytes = 1 << 20;
-  int64_t free_mem =
-      base::SysInfo::AmountOfAvailablePhysicalMemory() / kMibibytesInBytes;
-  DCHECK_GE(free_mem, 0);
-  return free_mem;
+  constexpr uint64_t kMibibytesInBytes = 1 << 20;
+  return base::SysInfo::AmountOfAvailablePhysicalMemory() / kMibibytesInBytes;
 }
 
 void BackgroundTabLoadingPolicy::ErasePageNodeToLoadData(
diff --git a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java
index 65948f86..1148b477 100644
--- a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java
+++ b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java
@@ -294,6 +294,11 @@
             "Chrome.Flags.CrashStreakBeforeCache";
 
     /**
+     * Cached value of the native SafeModeForCachedFlags feature flag.
+     */
+    public static final String FLAGS_SAFE_MODE_ENABLED = "Chrome.Flags.SafeModeEnabled";
+
+    /**
      * How many runs of Safe Mode for Cached Flags are left before trying a normal run.
      */
     public static final String FLAGS_SAFE_MODE_RUNS_LEFT = "Chrome.Flags.SafeModeRunsLeft";
@@ -1011,6 +1016,7 @@
                 FLAGS_CRASH_STREAK_BEFORE_CACHE,
                 FLAGS_FIELD_TRIAL_PARAM_CACHED.pattern(),
                 FLAGS_LAST_CACHED_MINIMAL_BROWSER_FLAGS_TIME_MILLIS,
+                FLAGS_SAFE_MODE_ENABLED,
                 FLAGS_SAFE_MODE_RUNS_LEFT,
                 HOMEPAGE_LOCATION_POLICY,
                 HOMEPAGE_USE_CHROME_NTP,
diff --git a/chrome/browser/prerender/OWNERS b/chrome/browser/prerender/OWNERS
index 894bf54..03a1d5b 100644
--- a/chrome/browser/prerender/OWNERS
+++ b/chrome/browser/prerender/OWNERS
@@ -1 +1 @@
-file://content/browser/prerender/OWNERS
+file://content/browser/preloading/prerender/OWNERS
diff --git a/chrome/browser/profile_resetter/brandcode_config_fetcher.cc b/chrome/browser/profile_resetter/brandcode_config_fetcher.cc
index 3aa6ea2..3b41d8b 100644
--- a/chrome/browser/profile_resetter/brandcode_config_fetcher.cc
+++ b/chrome/browser/profile_resetter/brandcode_config_fetcher.cc
@@ -127,10 +127,10 @@
   // failure. The difference is whether |default_settings_| is populated.
   base::ScopedClosureRunner scoped_closure(std::move(fetch_callback_));
 
-  if (!value_or_error.value)
+  if (!value_or_error.has_value())
     return;
 
-  const base::Value* node = &value_or_error.value.value();
+  const base::Value* node = &*value_or_error;
   if (!data_decoder::IsXmlElementNamed(*node, "response"))
     return;
 
diff --git a/chrome/browser/renderer_preferences_util.cc b/chrome/browser/renderer_preferences_util.cc
index 1a17452..cc8ea097 100644
--- a/chrome/browser/renderer_preferences_util.cc
+++ b/chrome/browser/renderer_preferences_util.cc
@@ -37,10 +37,10 @@
 #include "ui/views/controls/textfield/textfield.h"
 #endif
 
-#if defined(USE_AURA) && (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS))
+#if defined(USE_AURA) && BUILDFLAG(IS_LINUX)
 #include "chrome/browser/themes/theme_service.h"
 #include "chrome/browser/themes/theme_service_factory.h"
-#include "ui/views/linux_ui/linux_ui.h"
+#include "ui/linux/linux_ui.h"
 #endif
 
 namespace {
@@ -159,8 +159,8 @@
   prefs->caret_blink_interval = views::Textfield::GetCaretBlinkInterval();
 #endif
 
-#if defined(USE_AURA) && (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS))
-  views::LinuxUI* linux_ui = views::LinuxUI::instance();
+#if defined(USE_AURA) && BUILDFLAG(IS_LINUX)
+  ui::LinuxUi* linux_ui = ui::LinuxUi::instance();
   if (linux_ui) {
     if (ThemeServiceFactory::GetForProfile(profile)->UsingSystemTheme()) {
       prefs->focus_ring_color = linux_ui->GetFocusRingColor();
diff --git a/chrome/browser/resource_coordinator/session_restore_policy.cc b/chrome/browser/resource_coordinator/session_restore_policy.cc
index 15ca094..6787763 100644
--- a/chrome/browser/resource_coordinator/session_restore_policy.cc
+++ b/chrome/browser/resource_coordinator/session_restore_policy.cc
@@ -68,11 +68,8 @@
   }
 
   size_t GetFreeMemoryMiB() const override {
-    constexpr int64_t kMibibytesInBytes = 1 << 20;
-    int64_t free_mem =
-        base::SysInfo::AmountOfAvailablePhysicalMemory() / kMibibytesInBytes;
-    DCHECK(free_mem >= 0);
-    return free_mem;
+    constexpr uint64_t kMibibytesInBytes = 1 << 20;
+    return base::SysInfo::AmountOfAvailablePhysicalMemory() / kMibibytesInBytes;
   }
 
   base::TimeTicks NowTicks() const override { return base::TimeTicks::Now(); }
diff --git a/chrome/browser/resources/bookmarks/BUILD.gn b/chrome/browser/resources/bookmarks/BUILD.gn
index e95bfbe5..d3f2006f 100644
--- a/chrome/browser/resources/bookmarks/BUILD.gn
+++ b/chrome/browser/resources/bookmarks/BUILD.gn
@@ -2,84 +2,54 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("//chrome/common/features.gni")
-import("//tools/grit/grit_rule.gni")
-import("//tools/grit/preprocess_if_expr.gni")
-import("//tools/polymer/css_to_wrapper.gni")
-import("//tools/polymer/html_to_wrapper.gni")
-import("//tools/typescript/ts_library.gni")
-import("//ui/webui/resources/tools/generate_grd.gni")
-import("../tools/optimize_webui.gni")
-import("bookmarks.gni")
+import("//chrome/browser/resources/tools/build_webui.gni")
 
-preprocess_folder = "preprocessed"
-tsc_folder = "tsc"
+build_webui("build") {
+  grd_prefix = "bookmarks"
 
-if (optimize_webui) {
-  build_manifest = "build_manifest.json"
-
-  optimize_webui("build") {
-    host = "bookmarks"
-    input = rebase_path("$target_gen_dir/$tsc_folder", root_build_dir)
-    js_out_files = [ "bookmarks.rollup.js" ]
-    js_module_in_files = [ "bookmarks.js" ]
-    out_manifest = "$target_gen_dir/$build_manifest"
-
-    deps = [
-      ":build_ts",
-      "../../../../ui/webui/resources:preprocess",
-    ]
-    excludes = [ "chrome://resources/js/cr.m.js" ]
-  }
-}
-
-preprocess_if_expr("preprocess") {
-  out_folder = "$target_gen_dir/$preprocess_folder"
-  in_files = ts_files
-}
-
-preprocess_if_expr("preprocess_generated") {
-  deps = [
-    ":css_wrapper_files",
-    ":html_wrapper_files",
+  static_files = [
+    "bookmarks.html",
+    "images/folder_open.svg",
   ]
-  in_folder = target_gen_dir
-  out_folder = "$target_gen_dir/$preprocess_folder"
-  in_files = html_wrapper_files + css_wrapper_files
-}
 
-css_to_wrapper("css_wrapper_files") {
-  in_files = css_files
-}
-
-html_to_wrapper("html_wrapper_files") {
-  in_files = html_files
-}
-
-grit("resources") {
-  defines = chrome_grit_defines
-
-  # These arguments are needed since the grd is generated at build time.
-  enable_input_discovery_for_gn_analyze = false
-  source = "$target_gen_dir/bookmarks_resources.grd"
-  deps = [ ":build_grd" ]
-
-  outputs = [
-    "grit/bookmarks_resources.h",
-    "grit/bookmarks_resources_map.cc",
-    "grit/bookmarks_resources_map.h",
-    "bookmarks_resources.pak",
+  # Files holding a Polymer element definition and have an equivalent .html file.
+  web_component_files = [
+    "app.ts",
+    "command_manager.ts",
+    "edit_dialog.ts",
+    "folder_node.ts",
+    "item.ts",
+    "list.ts",
+    "router.ts",
+    "toolbar.ts",
   ]
-  output_dir = "$root_gen_dir/chrome"
-}
 
-ts_library("build_ts") {
-  root_dir = "$target_gen_dir/$preprocess_folder"
-  out_dir = "$target_gen_dir/$tsc_folder"
-  composite = true
-  tsconfig_base = "tsconfig_base.json"
-  in_files = ts_files + css_wrapper_files + html_wrapper_files
-  definitions = [
+  non_web_component_files = [
+    "actions.ts",
+    "bookmarks.ts",
+    "api_listener.ts",
+    "browser_proxy.ts",
+    "constants.ts",
+    "debouncer.ts",
+    "dialog_focus_manager.ts",
+    "dnd_manager.ts",
+    "mouse_focus_behavior.ts",
+    "reducers.ts",
+    "store_client_mixin.ts",
+    "store.ts",
+    "types.ts",
+    "util.ts",
+  ]
+
+  # Files that are passed as input to css_to_wrapper().
+  css_files = [
+    "shared_style.css",
+    "shared_vars.css",
+  ]
+
+  ts_composite = true
+
+  ts_definitions = [
     "//tools/typescript/definitions/bookmarks.d.ts",
     "//tools/typescript/definitions/bookmark_manager_private.d.ts",
     "//tools/typescript/definitions/chrome_event.d.ts",
@@ -89,32 +59,19 @@
     "//tools/typescript/definitions/tabs.d.ts",
     "//tools/typescript/definitions/windows.d.ts",
   ]
-  deps = [
+
+  ts_deps = [
     "//third_party/polymer/v3_0:library",
     "//ui/webui/resources:library",
   ]
-  extra_deps = [
-    ":preprocess",
-    ":preprocess_generated",
-  ]
-}
 
-generate_grd("build_grd") {
-  input_files = [
-    "bookmarks.html",
-    "images/folder_open.svg",
-  ]
-  input_files_base_dir = rebase_path(".", "//")
-
-  if (optimize_webui) {
-    deps = [ ":build" ]
-    manifest_files = [ "$target_gen_dir/$build_manifest" ]
-    resource_path_rewrites = [ "bookmarks.rollup.js|bookmarks.js" ]
-  } else {
-    deps = [ ":build_ts" ]
-    manifest_files =
-        filter_include(get_target_outputs(":build_ts"), [ "*.manifest" ])
+  optimize = optimize_webui
+  if (optimize) {
+    optimize_webui_host = "bookmarks"
+    optimize_webui_out_files = [ "bookmarks.rollup.js" ]
+    optimize_webui_in_files = [ "bookmarks.js" ]
+    optimize_webui_excludes = [ "chrome://resources/js/cr.m.js" ]
+    optimize_webui_resource_paths_rewrites =
+        [ "bookmarks.rollup.js|bookmarks.js" ]
   }
-  grd_prefix = "bookmarks"
-  out_grd = "$target_gen_dir/${grd_prefix}_resources.grd"
 }
diff --git a/chrome/browser/resources/bookmarks/bookmarks.gni b/chrome/browser/resources/bookmarks/bookmarks.gni
deleted file mode 100644
index cc3d34c..0000000
--- a/chrome/browser/resources/bookmarks/bookmarks.gni
+++ /dev/null
@@ -1,56 +0,0 @@
-# Copyright 2021 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-# Files holding a Polymer element definition and have an equivalent .html file.
-web_component_files = [
-  "app.ts",
-  "command_manager.ts",
-  "edit_dialog.ts",
-  "folder_node.ts",
-  "item.ts",
-  "list.ts",
-  "router.ts",
-  "toolbar.ts",
-]
-
-# Files that are passed as input to html_to_wrapper().
-html_files = []
-foreach(f, web_component_files) {
-  html_files += [ string_replace(f, ".ts", ".html") ]
-}
-
-# Files that are generated by html_to_wrapper().
-html_wrapper_files = []
-foreach(f, html_files) {
-  html_wrapper_files += [ f + ".ts" ]
-}
-
-ts_files = [
-             "actions.ts",
-             "bookmarks.ts",
-             "api_listener.ts",
-             "browser_proxy.ts",
-             "constants.ts",
-             "debouncer.ts",
-             "dialog_focus_manager.ts",
-             "dnd_manager.ts",
-             "mouse_focus_behavior.ts",
-             "reducers.ts",
-             "store_client_mixin.ts",
-             "store.ts",
-             "types.ts",
-             "util.ts",
-           ] + web_component_files
-
-# Files that are passed as input to css_to_wrapper().
-css_files = [
-  "shared_style.css",
-  "shared_vars.css",
-]
-
-# Files that are generated by css_to_wrapper().
-css_wrapper_files = []
-foreach(f, css_files) {
-  css_wrapper_files += [ f + ".ts" ]
-}
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn b/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
index 9dd0cb0..9b2a8fb 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
@@ -24,7 +24,6 @@
   "../common/constants.js",
   "../common/cursors/recovery_strategy.js",
   "../common/key_code.js",
-  "../common/string_util.js",
   "../common/tree_walker.js",
   "background/braille/cursor_dots.js",
   "background/braille/liblouis.js",
@@ -43,7 +42,6 @@
   "common/extension_bridge.js",
   "common/log_types.js",
   "common/msgs.js",
-  "common/panel_bridge.js",
   "common/panel_menu_data.js",
   "common/spannable.js",
   "common/tree_dumper.js",
@@ -62,6 +60,7 @@
   "../common/cursors/range.js",
   "../common/event_generator.js",
   "../common/instance_checker.js",
+  "../common/string_util.js",
   "background/automation_object_constructor_installer.js",
   "background/auto_scroll_handler.js",
   "background/background.js",
@@ -138,6 +137,7 @@
   "common/key_util.js",
   "common/keyboard_handler.js",
   "common/locale_output_helper.js",
+  "common/panel_bridge.js",
   "common/panel_command.js",
   "common/tts_base.js",
   "learn_mode/learn_mode.js",
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille/braille_input_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille/braille_input_handler.js
index 6f01498a..ab5b45f 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille/braille_input_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille/braille_input_handler.js
@@ -7,7 +7,9 @@
  * text in an input field.  This class cooperates with the Braille IME
  * that is built into Chrome OS to do the actual text editing.
  */
+
 import {EventGenerator} from '../../../common/event_generator.js';
+import {StringUtil} from '../../../common/string_util.js';
 
 import {BrailleTranslatorManager} from './braille_translator_manager.js';
 import {ExpandingBrailleTranslator} from './expanding_braille_translator.js';
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/loader.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/loader.js
index e5438882..1e48fc2 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/loader.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/loader.js
@@ -38,14 +38,12 @@
 goog.require('OutputNodeSpan');
 goog.require('OutputSelectionSpan');
 goog.require('OutputSpeechProperties');
-goog.require('PanelBridge');
 goog.require('PanelNodeMenuData');
 goog.require('PanelTabMenuItemData');
 goog.require('QueueMode');
 goog.require('RecoveryStrategy');
 goog.require('Spannable');
 goog.require('SpeechLog');
-goog.require('StringUtil');
 goog.require('TextLog');
 goog.require('TreeDumper');
 goog.require('TreePathRecoveryStrategy');
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/panel/panel_background.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/panel/panel_background.js
index ced2585..c2c3b8c5 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/panel/panel_background.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/panel/panel_background.js
@@ -7,6 +7,7 @@
  * the background context.
  */
 import {CursorRange} from '../../../common/cursors/range.js';
+import {PanelBridge} from '../../common/panel_bridge.js';
 import {ChromeVoxState, ChromeVoxStateObserver} from '../chromevox_state.js';
 
 import {ISearch} from './i_search.js';
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/panel/panel_node_menu_background.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/panel/panel_node_menu_background.js
index 5da9809..eb09353 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/panel/panel_node_menu_background.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/panel/panel_node_menu_background.js
@@ -7,6 +7,7 @@
  * panel.
  */
 import {CursorRange} from '../../../common/cursors/range.js';
+import {PanelBridge} from '../../common/panel_bridge.js';
 import {ChromeVoxState} from '../chromevox_state.js';
 import {Output} from '../output/output.js';
 
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/common/panel_bridge.js b/chrome/browser/resources/chromeos/accessibility/chromevox/common/panel_bridge.js
index d3aa157e..baaecf96 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/common/panel_bridge.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/common/panel_bridge.js
@@ -7,25 +7,21 @@
  * the ChromeVox panel.
  */
 
-goog.provide('PanelBridge');
-
-goog.require('PanelNodeMenuItemData');
-
-PanelBridge = {
+export class PanelBridge {
   /**
    * @param {!PanelNodeMenuItemData} itemData
    * @return {!Promise}
    */
-  addMenuItem(itemData) {
+  static addMenuItem(itemData) {
     return BridgeHelper.sendMessage(
         BridgeConstants.Panel.TARGET,
         BridgeConstants.Panel.Action.ADD_MENU_ITEM, itemData);
-  },
+  }
 
   /** @return {!Promise} */
-  async onCurrentRangeChanged() {
+  static async onCurrentRangeChanged() {
     return BridgeHelper.sendMessage(
         BridgeConstants.Panel.TARGET,
         BridgeConstants.Panel.Action.ON_CURRENT_RANGE_CHANGED);
-  },
-};
+  }
+}
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel_loader.js b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel_loader.js
index 0b1380e..e940265 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel_loader.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel_loader.js
@@ -30,7 +30,6 @@
 goog.require('QueueMode');
 goog.require('RecoveryStrategy');
 goog.require('Spannable');
-goog.require('StringUtil');
 goog.require('TextLog');
 goog.require('TtsCategory');
 
diff --git a/chrome/browser/resources/chromeos/accessibility/common/cursors/cursor.js b/chrome/browser/resources/chromeos/accessibility/common/cursors/cursor.js
index 81ca54a..a9fa1e8 100644
--- a/chrome/browser/resources/chromeos/accessibility/common/cursors/cursor.js
+++ b/chrome/browser/resources/chromeos/accessibility/common/cursors/cursor.js
@@ -12,6 +12,7 @@
 const RoleType = chrome.automation.RoleType;
 const StateType = chrome.automation.StateType;
 
+import {StringUtil} from '../string_util.js';
 
 /**
  * The special index that represents a cursor pointing to a node without
diff --git a/chrome/browser/resources/chromeos/accessibility/common/string_util.js b/chrome/browser/resources/chromeos/accessibility/common/string_util.js
index 4d05788..d2d9fb0 100644
--- a/chrome/browser/resources/chromeos/accessibility/common/string_util.js
+++ b/chrome/browser/resources/chromeos/accessibility/common/string_util.js
@@ -6,9 +6,7 @@
  * @fileoverview Utilities for strings.
  */
 
-goog.provide('StringUtil');
-
-StringUtil = class {
+export class StringUtil {
   constructor() {}
 
   /**
@@ -84,8 +82,7 @@
     }
     return result;
   }
-};
-
+}
 
 /**
  * The last code point of the Unicode basic multilingual plane.
diff --git a/chrome/browser/resources/connectors_internals/BUILD.gn b/chrome/browser/resources/connectors_internals/BUILD.gn
index b9da289..166ee32 100644
--- a/chrome/browser/resources/connectors_internals/BUILD.gn
+++ b/chrome/browser/resources/connectors_internals/BUILD.gn
@@ -2,86 +2,23 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("//tools/grit/grit_rule.gni")
-import("//tools/polymer/html_to_wrapper.gni")
-import("//tools/typescript/ts_library.gni")
-import("//ui/webui/resources/tools/generate_grd.gni")
+import("//chrome/browser/resources/tools/build_webui.gni")
 
-resources_grd_file = "$target_gen_dir/resources.grd"
+build_webui("build") {
+  grd_prefix = "connectors_internals"
 
-web_component_files = [
-  "app.ts",
-  "connectors_tabs.ts",
-  "zero_trust_connector.ts",
-]
-
-# Files that are passed as input to html_to_wrapper().
-html_files = []
-foreach(f, web_component_files) {
-  html_files += [ string_replace(f, ".ts", ".html") ]
-}
-
-# Files that are generated by html_to_wrapper().
-html_wrapper_files = []
-foreach(f, html_files) {
-  html_wrapper_files += [ f + ".ts" ]
-}
-
-html_to_wrapper("html_wrapper_files") {
-  in_files = html_files
-  template = "native"
-}
-
-copy("copy_mojo") {
-  deps = [
+  static_files = [ "index.html" ]
+  web_component_files = [
+    "app.ts",
+    "connectors_tabs.ts",
+    "zero_trust_connector.ts",
+  ]
+  mojo_files = [ "$root_gen_dir/mojom-webui/chrome/browser/ui/webui/connectors_internals/connectors_internals.mojom-webui.js" ]
+  mojo_files_deps = [
     "//chrome/browser/ui/webui/connectors_internals:mojo_bindings_webui_js",
   ]
-  mojom_folder =
-      "$root_gen_dir/mojom-webui/chrome/browser/ui/webui/connectors_internals/"
-  sources = [ "$mojom_folder/connectors_internals.mojom-webui.js" ]
-  outputs = [ "$target_gen_dir/{{source_file_part}}" ]
-}
 
-copy("copy_src") {
-  sources = web_component_files
-  outputs = [ "$target_gen_dir/{{source_file_part}}" ]
-}
+  html_to_wrapper_template = "native"
 
-ts_library("build_ts") {
-  root_dir = target_gen_dir
-  out_dir = "$target_gen_dir/tsc"
-  tsconfig_base = "tsconfig_base.json"
-  in_files = web_component_files + html_wrapper_files +
-             [ "connectors_internals.mojom-webui.js" ]
-  deps = [ "//ui/webui/resources:library" ]
-  extra_deps = [
-    ":copy_mojo",
-    ":copy_src",
-    ":html_wrapper_files",
-  ]
-}
-
-generate_grd("build_grd") {
-  deps = [ ":build_ts" ]
-  input_files = [ "index.html" ]
-  input_files_base_dir = rebase_path(".", "//")
-  manifest_files =
-      filter_include(get_target_outputs(":build_ts"), [ "*.manifest" ])
-
-  grd_prefix = "connectors_internals"
-  out_grd = resources_grd_file
-}
-
-grit("resources") {
-  enable_input_discovery_for_gn_analyze = false
-  source = resources_grd_file
-  deps = [ ":build_grd" ]
-
-  outputs = [
-    "grit/connectors_internals_resources.h",
-    "grit/connectors_internals_resources_map.cc",
-    "grit/connectors_internals_resources_map.h",
-    "connectors_internals_resources.pak",
-  ]
-  output_dir = "$root_gen_dir/chrome"
+  ts_deps = [ "//ui/webui/resources:library" ]
 }
diff --git a/chrome/browser/resources/downloads/BUILD.gn b/chrome/browser/resources/downloads/BUILD.gn
index 1d56135..b356efa0 100644
--- a/chrome/browser/resources/downloads/BUILD.gn
+++ b/chrome/browser/resources/downloads/BUILD.gn
@@ -2,113 +2,58 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("//chrome/common/features.gni")
-import("//tools/grit/grit_rule.gni")
-import("//tools/grit/preprocess_if_expr.gni")
-import("//tools/polymer/html_to_wrapper.gni")
-import("//tools/typescript/ts_library.gni")
-import("//ui/webui/resources/tools/generate_grd.gni")
-import("../tools/optimize_webui.gni")
-import("downloads.gni")
+import("//chrome/browser/resources/tools/build_webui.gni")
 
 assert(!is_android)
 
-preprocess_folder = "preprocessed"
+build_webui("build") {
+  grd_prefix = "downloads"
 
-if (optimize_webui) {
-  build_manifest = "build_manifest.json"
-
-  optimize_webui("build") {
-    host = "downloads"
-    input = rebase_path("$target_gen_dir/tsc", root_build_dir)
-    js_out_files = [ "downloads.rollup.js" ]
-    js_module_in_files = [ "downloads.js" ]
-    out_manifest = "$target_gen_dir/$build_manifest"
-    excludes = [
-      "chrome://resources/js/cr.m.js",
-      "chrome://resources/mojo/mojo/public/js/bindings.js",
-    ]
-
-    deps = [
-      ":build_ts",
-      "../../../../ui/webui/resources:preprocess",
-    ]
-  }
-}
-
-preprocess_if_expr("preprocess") {
-  out_folder = "$target_gen_dir/$preprocess_folder"
-  in_files = ts_files
-}
-
-preprocess_if_expr("preprocess_generated") {
-  deps = [ ":html_wrapper_files" ]
-  in_folder = target_gen_dir
-  out_folder = "$target_gen_dir/$preprocess_folder"
-  in_files = html_wrapper_files
-}
-
-copy("copy_mojo") {
-  deps = [ "//chrome/browser/ui/webui/downloads:mojo_bindings_webui_js" ]
-  sources = [ "$root_gen_dir/mojom-webui/chrome/browser/ui/webui/downloads/downloads.mojom-webui.js" ]
-  outputs = [ "$target_gen_dir/$preprocess_folder/{{source_file_part}}" ]
-}
-
-grit("resources") {
-  defines = chrome_grit_defines
-
-  # These arguments are needed since the grd is generated at build time.
-  enable_input_discovery_for_gn_analyze = false
-  source = "$target_gen_dir/downloads_resources.grd"
-  deps = [ ":build_grd" ]
-
-  outputs = [
-    "grit/downloads_resources.h",
-    "grit/downloads_resources_map.cc",
-    "grit/downloads_resources_map.h",
-    "downloads_resources.pak",
-  ]
-  output_dir = "$root_gen_dir/chrome"
-}
-
-html_to_wrapper("html_wrapper_files") {
-  in_files = html_files + icons_html_files
-}
-
-ts_library("build_ts") {
-  root_dir = "$target_gen_dir/$preprocess_folder"
-  out_dir = "$target_gen_dir/tsc"
-  composite = true
-  tsconfig_base = "tsconfig_base.json"
-  in_files = ts_files + html_wrapper_files + [ "downloads.mojom-webui.js" ]
-  deps = [
-    "//third_party/polymer/v3_0:library",
-    "//ui/webui/resources:library",
-  ]
-  definitions = [ "//tools/typescript/definitions/chrome_send.d.ts" ]
-  extra_deps = [
-    ":copy_mojo",
-    ":preprocess",
-    ":preprocess_generated",
-  ]
-}
-
-generate_grd("build_grd") {
-  input_files = [
+  static_files = [
     "downloads.html",
     "images/incognito_marker.svg",
     "images/no_downloads.svg",
   ]
-  input_files_base_dir = rebase_path(".", "//")
-  if (optimize_webui) {
-    deps = [ ":build" ]
-    manifest_files = [ "$target_gen_dir/$build_manifest" ]
-    resource_path_rewrites = [ "downloads.rollup.js|downloads.js" ]
-  } else {
-    deps = [ ":build_ts" ]
-    manifest_files =
-        filter_include(get_target_outputs(":build_ts"), [ "*.manifest" ])
+
+  # Files holding a Polymer element definition and have an equivalent .html file.
+  web_component_files = [
+    "item.ts",
+    "manager.ts",
+    "toolbar.ts",
+  ]
+
+  non_web_component_files = [
+    "browser_proxy.ts",
+    "constants.ts",
+    "data.ts",
+    "downloads.ts",
+    "icon_loader.ts",
+    "search_service.ts",
+  ]
+
+  icons_html_files = [ "icons.html" ]
+
+  mojo_files_deps =
+      [ "//chrome/browser/ui/webui/downloads:mojo_bindings_webui_js" ]
+  mojo_files = [ "$root_gen_dir/mojom-webui/chrome/browser/ui/webui/downloads/downloads.mojom-webui.js" ]
+
+  ts_composite = true
+  ts_definitions = [ "//tools/typescript/definitions/chrome_send.d.ts" ]
+  ts_deps = [
+    "//third_party/polymer/v3_0:library",
+    "//ui/webui/resources:library",
+  ]
+
+  optimize = optimize_webui
+  if (optimize) {
+    optimize_webui_host = "downloads"
+    optimize_webui_out_files = [ "downloads.rollup.js" ]
+    optimize_webui_in_files = [ "downloads.js" ]
+    optimize_webui_excludes = [
+      "chrome://resources/js/cr.m.js",
+      "chrome://resources/mojo/mojo/public/js/bindings.js",
+    ]
+    optimize_webui_resource_paths_rewrites =
+        [ "downloads.rollup.js|downloads.js" ]
   }
-  grd_prefix = "downloads"
-  out_grd = "$target_gen_dir/${grd_prefix}_resources.grd"
 }
diff --git a/chrome/browser/resources/downloads/downloads.gni b/chrome/browser/resources/downloads/downloads.gni
deleted file mode 100644
index 5283a47..0000000
--- a/chrome/browser/resources/downloads/downloads.gni
+++ /dev/null
@@ -1,33 +0,0 @@
-# Copyright 2021 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-# Files holding a Polymer element definition and have an equivalent .html file.
-web_component_files = [
-  "item.ts",
-  "manager.ts",
-  "toolbar.ts",
-]
-
-# Files that are passed as input to html_to_wrapper().
-html_files = []
-foreach(f, web_component_files) {
-  html_files += [ string_replace(f, ".ts", ".html") ]
-}
-
-icons_html_files = [ "icons.html" ]
-
-# Files that are generated by html_to_wrapper().
-html_wrapper_files = []
-foreach(f, html_files + icons_html_files) {
-  html_wrapper_files += [ f + ".ts" ]
-}
-
-ts_files = [
-             "browser_proxy.ts",
-             "constants.ts",
-             "data.ts",
-             "downloads.ts",
-             "icon_loader.ts",
-             "search_service.ts",
-           ] + web_component_files
diff --git a/chrome/browser/resources/extensions/BUILD.gn b/chrome/browser/resources/extensions/BUILD.gn
index b5ddcf8..adb5e2d 100644
--- a/chrome/browser/resources/extensions/BUILD.gn
+++ b/chrome/browser/resources/extensions/BUILD.gn
@@ -3,86 +3,83 @@
 # found in the LICENSE file.
 
 import("//build/config/chromeos/ui_mode.gni")
-import("//chrome/common/features.gni")
+import("//chrome/browser/resources/tools/build_webui.gni")
 import("//extensions/buildflags/buildflags.gni")
-import("//tools/grit/grit_rule.gni")
-import("//tools/grit/preprocess_if_expr.gni")
-import("//tools/polymer/css_to_wrapper.gni")
-import("//tools/polymer/html_to_wrapper.gni")
-import("//tools/typescript/ts_library.gni")
-import("//ui/webui/resources/tools/generate_grd.gni")
-import("../tools/optimize_webui.gni")
-import("extensions.gni")
 
 assert(enable_extensions, "enable extensions check failed")
 
-preprocess_folder = "preprocessed"
+build_webui("build") {
+  grd_prefix = "extensions"
 
-if (optimize_webui) {
-  build_manifest = "build_manifest.json"
+  static_files = [ "extensions.html" ]
 
-  optimize_webui("build") {
-    host = "extensions"
-    input = rebase_path("$target_gen_dir/tsc", root_build_dir)
-    js_out_files = [ "extensions.rollup.js" ]
-    js_module_in_files = [ "extensions.js" ]
-    out_manifest = "$target_gen_dir/$build_manifest"
+  # Files holding a Polymer element definition and have an equivalent .html file.
+  web_component_files = [
+    "activity_log/activity_log.ts",
+    "activity_log/activity_log_history.ts",
+    "activity_log/activity_log_history_item.ts",
+    "activity_log/activity_log_stream.ts",
+    "activity_log/activity_log_stream_item.ts",
+    "code_section.ts",
+    "detail_view.ts",
+    "drop_overlay.ts",
+    "error_page.ts",
+    "host_permissions_toggle_list.ts",
+    "install_warnings_dialog.ts",
+    "item.ts",
+    "item_list.ts",
+    "keyboard_shortcuts.ts",
+    "load_error.ts",
+    "manager.ts",
+    "options_dialog.ts",
+    "pack_dialog.ts",
+    "pack_dialog_alert.ts",
+    "restricted_sites_dialog.ts",
+    "runtime_host_permissions.ts",
+    "runtime_hosts_dialog.ts",
+    "shortcut_input.ts",
+    "sidebar.ts",
+    "site_permissions_by_site.ts",
+    "site_permissions_edit_permissions_dialog.ts",
+    "site_permissions_edit_url_dialog.ts",
+    "site_permissions_list.ts",
+    "site_permissions_site_group.ts",
+    "site_permissions.ts",
+    "toggle_row.ts",
+    "toolbar.ts",
+  ]
 
-    deps = [
-      ":build_ts",
-      "../../../../ui/webui/resources:preprocess",
-    ]
-    excludes = [ "chrome://resources/js/cr.m.js" ]
+  if (is_chromeos_ash) {
+    web_component_files += [ "kiosk_dialog.ts" ]
   }
-}
 
-preprocess_if_expr("preprocess") {
-  out_folder = "$target_gen_dir/$preprocess_folder"
-  in_files = ts_files
-}
-
-preprocess_if_expr("preprocess_generated") {
-  public_deps = [
-    ":css_wrapper_files",
-    ":html_wrapper_files",
+  non_web_component_files = [
+    "drag_and_drop_handler.ts",
+    "extensions.ts",
+    "item_mixin.ts",
+    "item_util.ts",
+    "keyboard_shortcut_delegate.ts",
+    "navigation_helper.ts",
+    "service.ts",
+    "shortcut_util.ts",
+    "site_settings_mixin.ts",
+    "url_util.ts",
   ]
-  in_folder = target_gen_dir
-  out_folder = "$target_gen_dir/$preprocess_folder"
-  in_files = html_wrapper_files + css_wrapper_files
-}
 
-html_to_wrapper("html_wrapper_files") {
-  in_files = html_files + icons_html_files
-}
+  if (is_chromeos_ash) {
+    non_web_component_files += [ "kiosk_browser_proxy.ts" ]
+  }
 
-css_to_wrapper("css_wrapper_files") {
-  in_files = css_files
-}
+  icons_html_files = [ "icons.html" ]
 
-grit("resources") {
-  defines = chrome_grit_defines
-
-  # These arguments are needed since the grd is generated at build time.
-  enable_input_discovery_for_gn_analyze = false
-  source = "$target_gen_dir/extensions_resources.grd"
-  deps = [ ":build_grd" ]
-
-  outputs = [
-    "grit/extensions_resources.h",
-    "grit/extensions_resources_map.cc",
-    "grit/extensions_resources_map.h",
-    "extensions_resources.pak",
+  # Files that are passed as input to css_to_wrapper().
+  css_files = [
+    "shared_style.css",
+    "shared_vars.css",
   ]
-  output_dir = "$root_gen_dir/chrome"
-}
 
-ts_library("build_ts") {
-  root_dir = "$target_gen_dir/$preprocess_folder"
-  out_dir = "$target_gen_dir/tsc"
-  composite = true
-  tsconfig_base = "tsconfig_base.json"
-  in_files = ts_files + css_wrapper_files + html_wrapper_files
-  definitions = [
+  ts_composite = true
+  ts_definitions = [
     "//tools/typescript/definitions/activity_log_private.d.ts",
     "//tools/typescript/definitions/chrome_event.d.ts",
     "//tools/typescript/definitions/developer_private.d.ts",
@@ -91,34 +88,21 @@
     "//tools/typescript/definitions/pending.d.ts",
     "//tools/typescript/definitions/runtime.d.ts",
   ]
-
   if (is_chromeos_ash) {
-    definitions += [ "//tools/typescript/definitions/chrome_send.d.ts" ]
+    ts_definitions += [ "//tools/typescript/definitions/chrome_send.d.ts" ]
   }
-
-  deps = [
+  ts_deps = [
     "//third_party/polymer/v3_0:library",
     "//ui/webui/resources:library",
   ]
-  extra_deps = [
-    ":preprocess",
-    ":preprocess_generated",
-  ]
-}
 
-generate_grd("build_grd") {
-  grd_prefix = "extensions"
-  out_grd = "$target_gen_dir/${grd_prefix}_resources.grd"
-  input_files = [ "extensions.html" ]
-  input_files_base_dir = rebase_path(".", "//")
-
-  if (optimize_webui) {
-    deps = [ ":build" ]
-    resource_path_rewrites = [ "extensions.rollup.js|extensions.js" ]
-    manifest_files = [ "$target_gen_dir/$build_manifest" ]
-  } else {
-    deps = [ ":build_ts" ]
-    manifest_files =
-        filter_include(get_target_outputs(":build_ts"), [ "*.manifest" ])
+  optimize = optimize_webui
+  if (optimize) {
+    optimize_webui_host = "extensions"
+    optimize_webui_out_files = [ "extensions.rollup.js" ]
+    optimize_webui_in_files = [ "extensions.js" ]
+    optimize_webui_excludes = [ "chrome://resources/js/cr.m.js" ]
+    optimize_webui_resource_paths_rewrites =
+        [ "extensions.rollup.js|extensions.js" ]
   }
 }
diff --git a/chrome/browser/resources/extensions/extensions.gni b/chrome/browser/resources/extensions/extensions.gni
deleted file mode 100644
index 7eabb83..0000000
--- a/chrome/browser/resources/extensions/extensions.gni
+++ /dev/null
@@ -1,90 +0,0 @@
-# Copyright 2021 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//build/config/chromeos/ui_mode.gni")
-
-non_web_component_files = [
-  "drag_and_drop_handler.ts",
-  "extensions.ts",
-  "item_mixin.ts",
-  "item_util.ts",
-  "keyboard_shortcut_delegate.ts",
-  "navigation_helper.ts",
-  "service.ts",
-  "shortcut_util.ts",
-  "site_settings_mixin.ts",
-  "url_util.ts",
-]
-
-if (is_chromeos_ash) {
-  non_web_component_files += [ "kiosk_browser_proxy.ts" ]
-}
-
-# Files holding a Polymer element definition and have an equivalent .html file.
-web_component_files = [
-  "activity_log/activity_log.ts",
-  "activity_log/activity_log_history.ts",
-  "activity_log/activity_log_history_item.ts",
-  "activity_log/activity_log_stream.ts",
-  "activity_log/activity_log_stream_item.ts",
-  "code_section.ts",
-  "detail_view.ts",
-  "drop_overlay.ts",
-  "error_page.ts",
-  "host_permissions_toggle_list.ts",
-  "install_warnings_dialog.ts",
-  "item.ts",
-  "item_list.ts",
-  "keyboard_shortcuts.ts",
-  "load_error.ts",
-  "manager.ts",
-  "options_dialog.ts",
-  "pack_dialog.ts",
-  "pack_dialog_alert.ts",
-  "restricted_sites_dialog.ts",
-  "runtime_host_permissions.ts",
-  "runtime_hosts_dialog.ts",
-  "shortcut_input.ts",
-  "sidebar.ts",
-  "site_permissions.ts",
-  "site_permissions_by_site.ts",
-  "site_permissions_edit_permissions_dialog.ts",
-  "site_permissions_edit_url_dialog.ts",
-  "site_permissions_list.ts",
-  "site_permissions_site_group.ts",
-  "toggle_row.ts",
-  "toolbar.ts",
-]
-
-if (is_chromeos_ash) {
-  web_component_files += [ "kiosk_dialog.ts" ]
-}
-
-# Files that are passed as input to html_to_wrapper().
-html_files = []
-foreach(f, web_component_files) {
-  html_files += [ string_replace(f, ".ts", ".html") ]
-}
-
-icons_html_files = [ "icons.html" ]
-
-# Files that are generated by html_to_wrapper().
-html_wrapper_files = []
-foreach(f, html_files + icons_html_files) {
-  html_wrapper_files += [ f + ".ts" ]
-}
-
-# Files that are passed as input to css_to_wrapper().
-css_files = [
-  "shared_style.css",
-  "shared_vars.css",
-]
-
-# Files that are generated by css_to_wrapper().
-css_wrapper_files = []
-foreach(f, css_files) {
-  css_wrapper_files += [ f + ".ts" ]
-}
-
-ts_files = web_component_files + non_web_component_files
diff --git a/chrome/browser/resources/history/BUILD.gn b/chrome/browser/resources/history/BUILD.gn
index af87f665..cbfe95d9 100644
--- a/chrome/browser/resources/history/BUILD.gn
+++ b/chrome/browser/resources/history/BUILD.gn
@@ -2,130 +2,82 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("//chrome/common/features.gni")
-import("//tools/grit/grit_rule.gni")
-import("//tools/grit/preprocess_if_expr.gni")
-import("//tools/polymer/css_to_wrapper.gni")
-import("//tools/polymer/html_to_wrapper.gni")
-import("//tools/typescript/ts_library.gni")
-import("//ui/webui/resources/tools/generate_grd.gni")
-import("../tools/optimize_webui.gni")
-import("history.gni")
+import("//chrome/browser/resources/tools/build_webui.gni")
+import("//extensions/buildflags/buildflags.gni")
 
-preprocess_folder = "preprocessed"
-
-if (optimize_webui) {
-  build_manifest = "build_manifest.json"
-
-  optimize_webui("build") {
-    host = "history"
-    js_module_in_files = [
-      "history.js",
-      "lazy_load.js",
-    ]
-    input = rebase_path("$target_gen_dir/tsc", root_build_dir)
-    js_out_files = [
-      "history.rollup.js",
-      "lazy_load.rollup.js",
-      "shared.rollup.js",
-    ]
-    out_manifest = "$target_gen_dir/$build_manifest"
-
-    deps = [
-      ":build_ts",
-      "//ui/webui/resources:preprocess",
-    ]
-    excludes = [
-      "chrome://resources/js/cr.m.js",
-      "chrome://resources/mojo/mojo/public/js/bindings.js",
-    ]
-  }
-}
-
-preprocess_if_expr("preprocess") {
-  out_folder = "$target_gen_dir/$preprocess_folder"
-  in_files = ts_files
-}
-
-preprocess_if_expr("preprocess_generated") {
-  deps = [
-    ":css_wrapper_files",
-    ":html_wrapper_files",
-  ]
-  in_folder = target_gen_dir
-  out_folder = "$target_gen_dir/$preprocess_folder"
-  in_files = html_wrapper_files + css_wrapper_files
-}
-
-grit("resources") {
-  defines = chrome_grit_defines
-
-  # These arguments are needed since the grd is generated at build time.
-  enable_input_discovery_for_gn_analyze = false
-  source = "$target_gen_dir/history_resources.grd"
-  deps = [ ":build_grd" ]
-
-  outputs = [
-    "grit/history_resources.h",
-    "grit/history_resources_map.cc",
-    "grit/history_resources_map.h",
-    "history_resources.pak",
-  ]
-  output_dir = "$root_gen_dir/chrome"
-}
-
-css_to_wrapper("css_wrapper_files") {
-  in_files = css_files
-}
-
-html_to_wrapper("html_wrapper_files") {
-  in_files = html_files + icons_html_files
-}
-
-ts_library("build_ts") {
-  root_dir = "$target_gen_dir/$preprocess_folder"
-  out_dir = "$target_gen_dir/tsc"
-  composite = true
-  tsconfig_base = "tsconfig_base.json"
-  in_files = ts_files + css_wrapper_files + html_wrapper_files
-  deps = [
-    "//third_party/polymer/v3_0:library",
-    "//ui/webui/resources:library",
-    "//ui/webui/resources/cr_components/history_clusters:build_ts",
-    "//ui/webui/resources/mojo:library",
-  ]
-  definitions = [
-    "//tools/typescript/definitions/chrome_send.d.ts",
-    "//tools/typescript/definitions/metrics_private.d.ts",
-  ]
-  extra_deps = [
-    ":preprocess",
-    ":preprocess_generated",
-  ]
-}
-
-generate_grd("build_grd") {
+build_webui("build") {
   grd_prefix = "history"
-  out_grd = "$target_gen_dir/${grd_prefix}_resources.grd"
-  input_files = [
+
+  static_files = [
     "history.html",
     "images/journeys.svg",
     "images/list.svg",
     "images/sign_in_promo_dark.svg",
     "images/sign_in_promo.svg",
   ]
-  input_files_base_dir = rebase_path(".", "//")
 
-  if (optimize_webui) {
-    deps = [ ":build" ]
-    manifest_files = [ "$target_gen_dir/$build_manifest" ]
-    resource_path_rewrites = [
+  # Files holding a Polymer element definition and have an equivalent .html file.
+  web_component_files = [
+    "app.ts",
+    "history_item.ts",
+    "history_list.ts",
+    "history_toolbar.ts",
+    "router.ts",
+    "side_bar.ts",
+    "synced_device_card.ts",
+    "synced_device_manager.ts",
+  ]
+
+  non_web_component_files = [
+    "browser_service.ts",
+    "constants.ts",
+    "externs.ts",
+    "history.ts",
+    "lazy_load.ts",
+    "query_manager.ts",
+    "searched_label.ts",
+  ]
+
+  # Files that are passed as input to css_to_wrapper().
+  css_files = [
+    "shared_style.css",
+    "shared_vars.css",
+  ]
+
+  icons_html_files = [ "shared_icons.html" ]
+
+  ts_composite = true
+  ts_definitions = [
+    "//tools/typescript/definitions/chrome_send.d.ts",
+    "//tools/typescript/definitions/metrics_private.d.ts",
+  ]
+
+  ts_deps = [
+    "//third_party/polymer/v3_0:library",
+    "//ui/webui/resources:library",
+    "//ui/webui/resources/cr_components/history_clusters:build_ts",
+    "//ui/webui/resources/mojo:library",
+  ]
+
+  optimize = optimize_webui
+  if (optimize) {
+    optimize_webui_host = "history"
+    optimize_webui_out_files = [
+      "history.rollup.js",
+      "lazy_load.rollup.js",
+      "shared.rollup.js",
+    ]
+    optimize_webui_in_files = [
+      "history.js",
+      "lazy_load.js",
+    ]
+    optimize_webui_excludes = [
+      "chrome://resources/js/cr.m.js",
+      "chrome://resources/mojo/mojo/public/js/bindings.js",
+    ]
+    optimize_webui_resource_paths_rewrites = [
       "history.rollup.js|history.js",
       "lazy_load.rollup.js|lazy_load.js",
     ]
-  } else {
-    deps = [ ":build_ts" ]
-    manifest_files =
-        filter_include(get_target_outputs(":build_ts"), [ "*.manifest" ])
   }
 }
diff --git a/chrome/browser/resources/history/history.gni b/chrome/browser/resources/history/history.gni
deleted file mode 100644
index 8a1cd99..0000000
--- a/chrome/browser/resources/history/history.gni
+++ /dev/null
@@ -1,51 +0,0 @@
-# Copyright 2021 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-# Files holding a Polymer element definition AND have an equivalent .html file.
-web_component_files = [
-  "app.ts",
-  "history_item.ts",
-  "history_list.ts",
-  "history_toolbar.ts",
-  "router.ts",
-  "side_bar.ts",
-  "synced_device_card.ts",
-  "synced_device_manager.ts",
-]
-
-# Files that are passed as input to html_to_wrapper().
-html_files = []
-foreach(f, web_component_files) {
-  html_files += [ string_replace(f, ".ts", ".html") ]
-}
-
-icons_html_files = [ "shared_icons.html" ]
-
-# Files that are generated by html_to_wrapper().
-html_wrapper_files = []
-foreach(f, html_files + icons_html_files) {
-  html_wrapper_files += [ f + ".ts" ]
-}
-
-# Files that are passed as input to css_to_wrapper().
-css_files = [
-  "shared_style.css",
-  "shared_vars.css",
-]
-
-# Files that are generated by css_to_wrapper().
-css_wrapper_files = []
-foreach(f, css_files) {
-  css_wrapper_files += [ f + ".ts" ]
-}
-
-ts_files = [
-             "browser_service.ts",
-             "constants.ts",
-             "externs.ts",
-             "history.ts",
-             "lazy_load.ts",
-             "query_manager.ts",
-             "searched_label.ts",
-           ] + web_component_files
diff --git a/chrome/browser/resources/nearby_share/shared/BUILD.gn b/chrome/browser/resources/nearby_share/shared/BUILD.gn
index 275d5ea..495922d 100644
--- a/chrome/browser/resources/nearby_share/shared/BUILD.gn
+++ b/chrome/browser/resources/nearby_share/shared/BUILD.gn
@@ -4,7 +4,7 @@
 
 import("//third_party/closure_compiler/compile_js.gni")
 import("//tools/grit/preprocess_if_expr.gni")
-import("//tools/polymer/html_to_js.gni")
+import("//tools/polymer/html_to_wrapper.gni")
 import("//ui/webui/resources/tools/generate_grd.gni")
 import("./nearby_shared.gni")
 
@@ -15,6 +15,43 @@
 preprocess_gen_v3_manifest = "preprocessed_gen_v3_manifest.json"
 preprocess_v3_manifest = "preprocessed_v3_manifest.json"
 
+web_component_files = [
+  "nearby_contact_visibility.js",
+  "nearby_device_icon.js",
+  "nearby_device.js",
+  "nearby_onboarding_one_page.js",
+  "nearby_onboarding_page.js",
+  "nearby_page_template.js",
+  "nearby_preview.js",
+  "nearby_progress.js",
+  "nearby_visibility_page.js",
+]
+
+# Files that are passed as input to html_to_wrapper().
+html_files = []
+foreach(f, web_component_files) {
+  html_files += [ string_replace(f, ".js", ".html") ]
+}
+
+icons_html_files = [
+  "nearby_shared_icons.html",
+  "nearby_shared_share_type_icons.html",
+]
+
+# Files that are generated by html_to_wrapper().
+html_wrapper_files = []
+foreach(f, html_files + icons_html_files) {
+  html_wrapper_files += [ f + ".js" ]
+}
+
+non_web_component_files = [
+  "nearby_contact_manager.js",
+  "nearby_metrics_logger.js",
+  "nearby_share_settings_behavior.js",
+  "nearby_share_settings.js",
+  "types.js",
+]
+
 generate_grd("build_v3_grdp") {
   grd_prefix = "nearby_share"
   out_grd = "$target_gen_dir/${grd_prefix}_resources_v3.grdp"
@@ -30,37 +67,24 @@
 }
 
 preprocess_if_expr("preprocess_gen_v3") {
-  deps = [ ":web_components" ]
+  deps = [ ":html_wrapper_files" ]
   in_folder = target_gen_dir
-  in_files = [
-    "nearby_contact_visibility.js",
-    "nearby_device_icon.js",
-    "nearby_device.js",
-    "nearby_onboarding_one_page.js",
-    "nearby_onboarding_page.js",
-    "nearby_page_template.js",
-    "nearby_preview.js",
-    "nearby_progress.js",
-    "nearby_shared_icons.js",
-    "nearby_shared_share_type_icons.js",
-    "nearby_visibility_page.js",
-  ]
+  in_files = html_wrapper_files
   out_folder = "$os_settings_dir/$preprocess_folder"
   out_manifest = "$target_gen_dir/$preprocess_gen_v3_manifest"
 }
 
 preprocess_if_expr("preprocess_v3") {
-  in_files = [
-    "nearby_contact_manager.js",
-    "nearby_metrics_logger.js",
-    "nearby_share_settings.js",
-    "nearby_share_settings_behavior.js",
-    "types.js",
-  ]
+  in_files = web_component_files + non_web_component_files
   out_folder = "$os_settings_dir/$preprocess_folder"
   out_manifest = "$target_gen_dir/$preprocess_v3_manifest"
 }
 
+html_to_wrapper("html_wrapper_files") {
+  in_files = html_files + icons_html_files
+  use_js = true
+}
+
 js_type_check("closure_compile_module") {
   is_polymer3 = true
   closure_flags =
@@ -199,19 +223,3 @@
 js_library("types") {
   deps = []
 }
-
-html_to_js("web_components") {
-  js_files = [
-    "nearby_contact_visibility.js",
-    "nearby_device_icon.js",
-    "nearby_device.js",
-    "nearby_onboarding_one_page.js",
-    "nearby_onboarding_page.js",
-    "nearby_page_template.js",
-    "nearby_preview.js",
-    "nearby_progress.js",
-    "nearby_shared_icons.js",
-    "nearby_shared_share_type_icons.js",
-    "nearby_visibility_page.js",
-  ]
-}
diff --git a/chrome/browser/resources/nearby_share/shared/nearby_contact_visibility.js b/chrome/browser/resources/nearby_share/shared/nearby_contact_visibility.js
index 6e8aef62..17e331e 100644
--- a/chrome/browser/resources/nearby_share/shared/nearby_contact_visibility.js
+++ b/chrome/browser/resources/nearby_share/shared/nearby_contact_visibility.js
@@ -16,7 +16,7 @@
 import 'chrome://resources/polymer/v3_0/iron-icon/iron-icon.js';
 import 'chrome://resources/polymer/v3_0/iron-media-query/iron-media-query.js';
 import './nearby_page_template.js';
-import './nearby_shared_icons.js';
+import './nearby_shared_icons.html.js';
 
 import {assert, assertNotReached} from 'chrome://resources/js/assert.m.js';
 import {sendWithPromise} from 'chrome://resources/js/cr.m.js';
@@ -25,6 +25,7 @@
 import {html, mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {getContactManager, observeContactManager} from './nearby_contact_manager.js';
+import {getTemplate} from './nearby_contact_visibility.html.js';
 import {NearbySettings} from './nearby_share_settings_behavior.js';
 
 /** @enum {string} */
@@ -109,7 +110,7 @@
   }
 
   static get template() {
-    return html`{__html_template__}`;
+    return getTemplate();
   }
 
   static get properties() {
diff --git a/chrome/browser/resources/nearby_share/shared/nearby_device.js b/chrome/browser/resources/nearby_share/shared/nearby_device.js
index 18216497..b68f4d4f 100644
--- a/chrome/browser/resources/nearby_share/shared/nearby_device.js
+++ b/chrome/browser/resources/nearby_share/shared/nearby_device.js
@@ -10,10 +10,12 @@
 import 'chrome://resources/cr_elements/shared_style_css.m.js';
 import 'chrome://resources/cr_elements/cr_icons_css.m.js';
 import 'chrome://resources/polymer/v3_0/iron-icon/iron-icon.js';
-import './nearby_shared_icons.js';
+import './nearby_shared_icons.html.js';
 import './nearby_device_icon.js';
 
-import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {getTemplate} from './nearby_device.html.js';
 
 /** @polymer */
 export class NearbyDeviceElement extends PolymerElement {
@@ -22,7 +24,7 @@
   }
 
   static get template() {
-    return html`{__html_template__}`;
+    return getTemplate();
   }
 
   static get properties() {
diff --git a/chrome/browser/resources/nearby_share/shared/nearby_device_icon.js b/chrome/browser/resources/nearby_share/shared/nearby_device_icon.js
index ca78cf4..3e24870 100644
--- a/chrome/browser/resources/nearby_share/shared/nearby_device_icon.js
+++ b/chrome/browser/resources/nearby_share/shared/nearby_device_icon.js
@@ -11,9 +11,11 @@
 import 'chrome://resources/cr_elements/shared_style_css.m.js';
 import 'chrome://resources/cr_elements/cr_icons_css.m.js';
 import 'chrome://resources/polymer/v3_0/iron-icon/iron-icon.js';
-import './nearby_shared_icons.js';
+import './nearby_shared_icons.html.js';
 
-import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {getTemplate} from './nearby_device_icon.html.js';
 
 /** @polymer */
 export class NearbyDeviceIconElement extends PolymerElement {
@@ -22,7 +24,7 @@
   }
 
   static get template() {
-    return html`{__html_template__}`;
+    return getTemplate();
   }
 
   static get properties() {
diff --git a/chrome/browser/resources/nearby_share/shared/nearby_onboarding_one_page.js b/chrome/browser/resources/nearby_share/shared/nearby_onboarding_one_page.js
index 140d427..1546777 100644
--- a/chrome/browser/resources/nearby_share/shared/nearby_onboarding_one_page.js
+++ b/chrome/browser/resources/nearby_share/shared/nearby_onboarding_one_page.js
@@ -20,6 +20,7 @@
 import {html, mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {NearbyShareOnboardingFinalState, processOnePageOnboardingCancelledMetrics, processOnePageOnboardingCompleteMetrics, processOnePageOnboardingInitiatedMetrics, processOnePageOnboardingVisibilityButtonOnInitialPageClickedMetrics} from './nearby_metrics_logger.js';
+import {getTemplate} from './nearby_onboarding_one_page.html.js';
 import {getNearbyShareSettings} from './nearby_share_settings.js';
 import {NearbySettings} from './nearby_share_settings_behavior.js';
 
@@ -51,7 +52,7 @@
   }
 
   static get template() {
-    return html`{__html_template__}`;
+    return getTemplate();
   }
 
   static get properties() {
diff --git a/chrome/browser/resources/nearby_share/shared/nearby_onboarding_page.js b/chrome/browser/resources/nearby_share/shared/nearby_onboarding_page.js
index a6c7ed4..bdfb0922 100644
--- a/chrome/browser/resources/nearby_share/shared/nearby_onboarding_page.js
+++ b/chrome/browser/resources/nearby_share/shared/nearby_onboarding_page.js
@@ -19,6 +19,7 @@
 import {html, mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {NearbyShareOnboardingFinalState, processOnboardingCancelledMetrics, processOnboardingInitiatedMetrics} from './nearby_metrics_logger.js';
+import {getTemplate} from './nearby_onboarding_page.html.js';
 import {getNearbyShareSettings} from './nearby_share_settings.js';
 import {NearbySettings} from './nearby_share_settings_behavior.js';
 
@@ -50,7 +51,7 @@
   }
 
   static get template() {
-    return html`{__html_template__}`;
+    return getTemplate();
   }
 
   static get properties() {
diff --git a/chrome/browser/resources/nearby_share/shared/nearby_page_template.js b/chrome/browser/resources/nearby_share/shared/nearby_page_template.js
index a1775e2..599a2a9 100644
--- a/chrome/browser/resources/nearby_share/shared/nearby_page_template.js
+++ b/chrome/browser/resources/nearby_share/shared/nearby_page_template.js
@@ -10,8 +10,9 @@
 
 import 'chrome://resources/cr_elements/shared_style_css.m.js';
 
-import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
+import {getTemplate} from './nearby_page_template.html.js';
 import {CloseReason} from './types.js';
 
 /** @polymer */
@@ -21,7 +22,7 @@
   }
 
   static get template() {
-    return html`{__html_template__}`;
+    return getTemplate();
   }
 
   static get properties() {
diff --git a/chrome/browser/resources/nearby_share/shared/nearby_preview.js b/chrome/browser/resources/nearby_share/shared/nearby_preview.js
index 8ee969b..bcf9499f 100644
--- a/chrome/browser/resources/nearby_share/shared/nearby_preview.js
+++ b/chrome/browser/resources/nearby_share/shared/nearby_preview.js
@@ -10,13 +10,15 @@
 import 'chrome://resources/cr_elements/shared_style_css.m.js';
 import 'chrome://resources/cr_elements/cr_icons_css.m.js';
 import 'chrome://resources/polymer/v3_0/iron-icon/iron-icon.js';
-import './nearby_shared_icons.js';
-import './nearby_shared_share_type_icons.js';
+import './nearby_shared_icons.html.js';
+import './nearby_shared_share_type_icons.html.js';
 
 import {assertNotReached} from 'chrome://resources/js/assert.m.js';
 import {I18nBehavior, I18nBehaviorInterface} from 'chrome://resources/js/i18n_behavior.m.js';
 import {html, mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
+import {getTemplate} from './nearby_preview.html.js';
+
 /**
  * @constructor
  * @extends {PolymerElement}
@@ -31,7 +33,7 @@
   }
 
   static get template() {
-    return html`{__html_template__}`;
+    return getTemplate();
   }
 
   static get properties() {
diff --git a/chrome/browser/resources/nearby_share/shared/nearby_progress.js b/chrome/browser/resources/nearby_share/shared/nearby_progress.js
index 275805a..6204017 100644
--- a/chrome/browser/resources/nearby_share/shared/nearby_progress.js
+++ b/chrome/browser/resources/nearby_share/shared/nearby_progress.js
@@ -12,10 +12,12 @@
 import 'chrome://resources/cr_elements/cr_auto_img/cr_auto_img.js';
 import 'chrome://resources/cr_elements/shared_style_css.m.js';
 import 'chrome://resources/cr_elements/cr_icons_css.m.js';
-import './nearby_shared_icons.js';
+import './nearby_shared_icons.html.js';
 import './nearby_device_icon.js';
 
-import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {getTemplate} from './nearby_progress.html.js';
 
 /** @polymer */
 export class NearbyProgressElement extends PolymerElement {
@@ -24,7 +26,7 @@
   }
 
   static get template() {
-    return html`{__html_template__}`;
+    return getTemplate();
   }
 
   static get properties() {
diff --git a/chrome/browser/resources/nearby_share/shared/nearby_shared_icons.js b/chrome/browser/resources/nearby_share/shared/nearby_shared_icons.js
deleted file mode 100644
index 13e2c69..0000000
--- a/chrome/browser/resources/nearby_share/shared/nearby_shared_icons.js
+++ /dev/null
@@ -1,10 +0,0 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'chrome://resources/polymer/v3_0/iron-iconset-svg/iron-iconset-svg.js';
-
-import {html} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-
-const template = html`{__html_template__}`;
-document.head.appendChild(template.content);
diff --git a/chrome/browser/resources/nearby_share/shared/nearby_shared_share_type_icons.js b/chrome/browser/resources/nearby_share/shared/nearby_shared_share_type_icons.js
deleted file mode 100644
index 13e2c69..0000000
--- a/chrome/browser/resources/nearby_share/shared/nearby_shared_share_type_icons.js
+++ /dev/null
@@ -1,10 +0,0 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'chrome://resources/polymer/v3_0/iron-iconset-svg/iron-iconset-svg.js';
-
-import {html} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-
-const template = html`{__html_template__}`;
-document.head.appendChild(template.content);
diff --git a/chrome/browser/resources/nearby_share/shared/nearby_visibility_page.js b/chrome/browser/resources/nearby_share/shared/nearby_visibility_page.js
index c5505323..5b6ec942 100644
--- a/chrome/browser/resources/nearby_share/shared/nearby_visibility_page.js
+++ b/chrome/browser/resources/nearby_share/shared/nearby_visibility_page.js
@@ -21,6 +21,7 @@
 import {NearbyContactVisibilityElement} from './nearby_contact_visibility.js';
 import {NearbyShareOnboardingFinalState, processOnboardingCancelledMetrics, processOnboardingCompleteMetrics, processOnePageOnboardingCancelledMetrics, processOnePageOnboardingCompleteMetrics, processOnePageOnboardingManageContactsMetrics, processOnePageOnboardingVisibilityPageShownMetrics} from './nearby_metrics_logger.js';
 import {NearbySettings} from './nearby_share_settings_behavior.js';
+import {getTemplate} from './nearby_visibility_page.html.js';
 
 /**
  * @constructor
@@ -38,7 +39,7 @@
   }
 
   static get template() {
-    return html`{__html_template__}`;
+    return getTemplate();
   }
 
   static get properties() {
diff --git a/chrome/browser/resources/print_preview/BUILD.gn b/chrome/browser/resources/print_preview/BUILD.gn
index 9df77c9..92091a2 100644
--- a/chrome/browser/resources/print_preview/BUILD.gn
+++ b/chrome/browser/resources/print_preview/BUILD.gn
@@ -3,125 +3,144 @@
 # found in the LICENSE file.
 
 import("//build/config/chromeos/ui_mode.gni")
-import("//chrome/common/features.gni")
 import("//printing/buildflags/buildflags.gni")
-import("//tools/grit/grit_rule.gni")
-import("//tools/grit/preprocess_if_expr.gni")
-import("//tools/polymer/css_to_wrapper.gni")
-import("//tools/polymer/html_to_wrapper.gni")
-import("//tools/typescript/ts_library.gni")
-import("//ui/webui/resources/tools/generate_grd.gni")
-import("../tools/optimize_webui.gni")
-import("./print_preview.gni")
+
+import("//chrome/browser/resources/tools/build_webui.gni")
 
 assert(enable_print_preview, "enable_print_preview check failed")
 
-preprocess_folder = "preprocessed"
+build_webui("build") {
+  grd_prefix = "print_preview"
 
-if (optimize_webui) {
-  build_manifest = "build_manifest.json"
+  extra_grdp_deps = [ "../pdf:build_print_preview_grdp" ]
+  extra_grdp_files = [ "$root_gen_dir/chrome/browser/resources/pdf/print_preview_pdf_resources.grdp" ]
 
-  optimize_webui("build") {
-    host = "print"
-    input = rebase_path("$target_gen_dir/tsc", root_build_dir)
-    js_out_files = [ "print_preview.rollup.js" ]
-    js_module_in_files = [ "print_preview.js" ]
-    out_manifest = "$target_gen_dir/$build_manifest"
+  static_files = [ "print_preview.html" ]
 
-    deps = [
-      ":build_ts",
-      "../../../../ui/webui/resources:preprocess",
+  # Files holding a Polymer element definition and have an equivalent .html file.
+  web_component_files = [
+    "ui/advanced_options_settings.ts",
+    "ui/advanced_settings_dialog.ts",
+    "ui/advanced_settings_item.ts",
+    "ui/app.ts",
+    "ui/button_strip.ts",
+    "ui/color_settings.ts",
+    "ui/copies_settings.ts",
+    "ui/destination_list.ts",
+    "ui/destination_settings.ts",
+    "ui/dpi_settings.ts",
+    "ui/duplex_settings.ts",
+    "ui/header.ts",
+    "ui/layout_settings.ts",
+    "ui/margin_control.ts",
+    "ui/margin_control_container.ts",
+    "ui/margins_settings.ts",
+    "ui/media_size_settings.ts",
+    "ui/more_settings.ts",
+    "ui/number_settings_section.ts",
+    "ui/other_options_settings.ts",
+    "ui/pages_per_sheet_settings.ts",
+    "ui/pages_settings.ts",
+    "ui/preview_area.ts",
+    "ui/print_preview_search_box.ts",
+    "ui/scaling_settings.ts",
+    "ui/settings_section.ts",
+    "ui/settings_select.ts",
+    "ui/sidebar.ts",
+  ]
+
+  if (is_chromeos) {
+    web_component_files += [
+      "ui/destination_dropdown_cros.ts",
+      "ui/destination_list_item_cros.ts",
+      "ui/destination_select_cros.ts",
+      "ui/pin_settings.ts",
+      "ui/provisional_destination_resolver.ts",
+      "ui/destination_dialog_cros.ts",
     ]
-    excludes = [
-      "chrome://resources/js/cr.m.js",
-      "pdf/pdf_scripting_api.js",
+  } else {
+    web_component_files += [
+      "ui/destination_select.ts",
+      "ui/link_container.ts",
+      "ui/destination_dialog.ts",
+      "ui/destination_list_item.ts",
     ]
   }
-}
 
-css_to_wrapper("css_wrapper_files") {
-  in_files = css_files
-}
-
-html_to_wrapper("html_wrapper_files") {
-  in_files = html_files + icons_html_files
-}
-
-preprocess_if_expr("preprocess") {
-  out_folder = "$target_gen_dir/$preprocess_folder"
-  in_files = ts_files
-}
-
-preprocess_if_expr("preprocess_generated") {
-  deps = [
-    ":css_wrapper_files",
-    ":html_wrapper_files",
-  ]
-  in_folder = target_gen_dir
-  out_folder = "$target_gen_dir/$preprocess_folder"
-  in_files = html_wrapper_files + css_wrapper_files
-}
-
-grit("resources") {
-  defines = chrome_grit_defines
-
-  # These arguments are needed since the grd is generated at build time.
-  enable_input_discovery_for_gn_analyze = false
-  source = "$target_gen_dir/print_preview_resources.grd"
-  deps = [ ":build_grd" ]
-
-  outputs = [
-    "grit/print_preview_resources.h",
-    "grit/print_preview_resources_map.cc",
-    "grit/print_preview_resources_map.h",
-    "print_preview_resources.pak",
-  ]
-  output_dir = "$root_gen_dir/chrome"
-}
-
-ts_library("build_ts") {
-  root_dir = "$target_gen_dir/$preprocess_folder"
-  out_dir = "$target_gen_dir/tsc"
-  composite = true
-  tsconfig_base = "tsconfig_base.json"
-  in_files = ts_files + css_wrapper_files + html_wrapper_files
-
-  deps = [
-    "//chrome/browser/resources/pdf:build_ts",
-    "//third_party/polymer/v3_0:library",
-    "//ui/webui/resources:library",
+  non_web_component_files = [
+    "dark_mode_mixin.ts",
+    "metrics.ts",
+    "native_layer.ts",
+    "print_preview.ts",
+    "print_preview_utils.ts",
+    "data/cdd.ts",
+    "data/coordinate2d.ts",
+    "data/destination.ts",
+    "data/destination_match.ts",
+    "data/destination_store.ts",
+    "data/document_info.ts",
+    "data/local_parsers.ts",
+    "data/margins.ts",
+    "data/measurement_system.ts",
+    "data/model.ts",
+    "data/printable_area.ts",
+    "data/scaling.ts",
+    "data/size.ts",
+    "data/state.ts",
+    "ui/highlight_utils.ts",
+    "ui/input_mixin.ts",
+    "ui/plugin_proxy.ts",
+    "ui/select_mixin.ts",
+    "ui/settings_mixin.ts",
   ]
 
-  definitions = [
+  if (is_chromeos) {
+    non_web_component_files += [
+      "data/print_server_store.ts",
+      "data/printer_status_cros.ts",
+      "native_layer_cros.ts",
+    ]
+  }
+
+  # Files that are passed as input to css_to_wrapper().
+  css_files = [
+    "ui/destination_dialog_style.css",
+    "ui/destination_list_item_style.css",
+    "ui/destination_select_style.css",
+    "ui/print_preview_shared.css",
+    "ui/print_preview_vars.css",
+    "ui/throbber.css",
+  ]
+
+  icons_html_files = [ "ui/icons.html" ]
+
+  ts_composite = true
+  ts_definitions = [
     "//tools/typescript/definitions/chrome_send.d.ts",
     "//tools/typescript/definitions/mime_handler_private.d.ts",
     "//tools/typescript/definitions/pending.d.ts",
   ]
 
-  path_mappings = [ "chrome://print/pdf/*|" + rebase_path(
-                        "$root_gen_dir/chrome/browser/resources/pdf/tsc/*",
-                        target_gen_dir) ]
-
-  extra_deps = [
-    ":preprocess",
-    ":preprocess_generated",
+  ts_deps = [
+    "//chrome/browser/resources/pdf:build_ts",
+    "//third_party/polymer/v3_0:library",
+    "//ui/webui/resources:library",
   ]
-}
 
-generate_grd("build_grd") {
-  input_files = [ "print_preview.html" ]
-  input_files_base_dir = rebase_path(".", "//")
-  deps = [ "../pdf:build_print_preview_grdp" ]
-  grdp_files = [ "$root_gen_dir/chrome/browser/resources/pdf/print_preview_pdf_resources.grdp" ]
-  if (optimize_webui) {
-    deps += [ ":build" ]
-    resource_path_rewrites = [ "print_preview.rollup.js|print_preview.js" ]
-    manifest_files = [ "$target_gen_dir/$build_manifest" ]
-  } else {
-    deps += [ ":build_ts" ]
-    manifest_files =
-        filter_include(get_target_outputs(":build_ts"), [ "*.manifest" ])
+  ts_path_mappings = [ "chrome://print/pdf/*|" + rebase_path(
+                           "$root_gen_dir/chrome/browser/resources/pdf/tsc/*",
+                           target_gen_dir) ]
+
+  optimize = optimize_webui
+  if (optimize) {
+    optimize_webui_host = "print"
+    optimize_webui_out_files = [ "print_preview.rollup.js" ]
+    optimize_webui_in_files = [ "print_preview.js" ]
+    optimize_webui_excludes = [
+      "chrome://resources/js/cr.m.js",
+      "pdf/pdf_scripting_api.js",
+    ]
+    optimize_webui_resource_paths_rewrites =
+        [ "print_preview.rollup.js|print_preview.js" ]
   }
-  grd_prefix = "print_preview"
-  out_grd = "$target_gen_dir/${grd_prefix}_resources.grd"
 }
diff --git a/chrome/browser/resources/print_preview/print_preview.gni b/chrome/browser/resources/print_preview/print_preview.gni
deleted file mode 100644
index 616aa52..0000000
--- a/chrome/browser/resources/print_preview/print_preview.gni
+++ /dev/null
@@ -1,118 +0,0 @@
-# Copyright 2021 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-# Files holding a Polymer element definition and have an equivalent .html file.
-web_component_files = [
-  "ui/advanced_options_settings.ts",
-  "ui/advanced_settings_dialog.ts",
-  "ui/advanced_settings_item.ts",
-  "ui/app.ts",
-  "ui/button_strip.ts",
-  "ui/color_settings.ts",
-  "ui/copies_settings.ts",
-  "ui/destination_list.ts",
-  "ui/destination_settings.ts",
-  "ui/dpi_settings.ts",
-  "ui/duplex_settings.ts",
-  "ui/header.ts",
-  "ui/layout_settings.ts",
-  "ui/margin_control.ts",
-  "ui/margin_control_container.ts",
-  "ui/margins_settings.ts",
-  "ui/media_size_settings.ts",
-  "ui/more_settings.ts",
-  "ui/number_settings_section.ts",
-  "ui/other_options_settings.ts",
-  "ui/pages_per_sheet_settings.ts",
-  "ui/pages_settings.ts",
-  "ui/preview_area.ts",
-  "ui/print_preview_search_box.ts",
-  "ui/scaling_settings.ts",
-  "ui/settings_section.ts",
-  "ui/settings_select.ts",
-  "ui/sidebar.ts",
-]
-
-if (is_chromeos) {
-  web_component_files += [
-    "ui/destination_dropdown_cros.ts",
-    "ui/destination_list_item_cros.ts",
-    "ui/destination_select_cros.ts",
-    "ui/pin_settings.ts",
-    "ui/provisional_destination_resolver.ts",
-    "ui/destination_dialog_cros.ts",
-  ]
-} else {
-  web_component_files += [
-    "ui/destination_select.ts",
-    "ui/link_container.ts",
-    "ui/destination_dialog.ts",
-    "ui/destination_list_item.ts",
-  ]
-}
-
-# Files that are passed as input to html_to_wrapper().
-html_files = []
-foreach(f, web_component_files) {
-  html_files += [ string_replace(f, ".ts", ".html") ]
-}
-
-icons_html_files = [ "ui/icons.html" ]
-
-# Files that are generated by html_to_wrapper().
-html_wrapper_files = []
-foreach(f, html_files + icons_html_files) {
-  html_wrapper_files += [ f + ".ts" ]
-}
-
-ts_files = [
-             "dark_mode_mixin.ts",
-             "metrics.ts",
-             "native_layer.ts",
-             "print_preview.ts",
-             "print_preview_utils.ts",
-             "data/cdd.ts",
-             "data/coordinate2d.ts",
-             "data/destination.ts",
-             "data/destination_match.ts",
-             "data/destination_store.ts",
-             "data/document_info.ts",
-             "data/local_parsers.ts",
-             "data/margins.ts",
-             "data/measurement_system.ts",
-             "data/model.ts",
-             "data/printable_area.ts",
-             "data/scaling.ts",
-             "data/size.ts",
-             "data/state.ts",
-             "ui/highlight_utils.ts",
-             "ui/input_mixin.ts",
-             "ui/plugin_proxy.ts",
-             "ui/select_mixin.ts",
-             "ui/settings_mixin.ts",
-           ] + web_component_files
-
-if (is_chromeos) {
-  ts_files += [
-    "data/print_server_store.ts",
-    "data/printer_status_cros.ts",
-    "native_layer_cros.ts",
-  ]
-}
-
-# Files that are passed as input to css_to_wrapper().
-css_files = [
-  "ui/destination_dialog_style.css",
-  "ui/destination_list_item_style.css",
-  "ui/destination_select_style.css",
-  "ui/print_preview_shared.css",
-  "ui/print_preview_vars.css",
-  "ui/throbber.css",
-]
-
-# Files that are generated by css_to_wrapper().
-css_wrapper_files = []
-foreach(f, css_files) {
-  css_wrapper_files += [ f + ".ts" ]
-}
diff --git a/chrome/browser/resources/settings/chromeos/nearby_share_page/nearby_share_high_visibility_page.js b/chrome/browser/resources/settings/chromeos/nearby_share_page/nearby_share_high_visibility_page.js
index a7b6d5a..a67486d 100644
--- a/chrome/browser/resources/settings/chromeos/nearby_share_page/nearby_share_high_visibility_page.js
+++ b/chrome/browser/resources/settings/chromeos/nearby_share_page/nearby_share_high_visibility_page.js
@@ -12,7 +12,7 @@
 import 'chrome://resources/polymer/v3_0/iron-icon/iron-icon.js';
 import 'chrome://resources/cr_components/localized_link/localized_link.js';
 import '../../shared/nearby_page_template.js';
-import '../../shared/nearby_shared_icons.js';
+import '../../shared/nearby_shared_icons.html.js';
 
 import {I18nBehavior, I18nBehaviorInterface} from 'chrome://resources/js/i18n_behavior.m.js';
 import {html, mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
diff --git a/chrome/browser/resources/welcome/BUILD.gn b/chrome/browser/resources/welcome/BUILD.gn
index 044e47c..acfe0329 100644
--- a/chrome/browser/resources/welcome/BUILD.gn
+++ b/chrome/browser/resources/welcome/BUILD.gn
@@ -2,19 +2,12 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//chrome/browser/resources/tools/build_webui.gni")
 import("//chrome/common/features.gni")
-import("//tools/grit/grit_rule.gni")
-import("//tools/grit/preprocess_if_expr.gni")
-import("//tools/polymer/css_to_wrapper.gni")
-import("//tools/polymer/html_to_wrapper.gni")
-import("//tools/typescript/ts_library.gni")
 import("//ui/webui/resources/tools/generate_grd.gni")
-import("welcome.gni")
 
 assert(!is_chromeos_ash && !is_android)
 
-preprocess_folder = "preprocessed"
-
 if (is_chrome_branded) {
   generate_grd("build_icons_grdp") {
     grd_prefix = "welcome_images"
@@ -38,72 +31,10 @@
   }
 }
 
-preprocess_if_expr("preprocess") {
-  out_folder = "$target_gen_dir/$preprocess_folder"
-  in_files = ts_files
-}
-
-preprocess_if_expr("preprocess_generated") {
-  deps = [
-    ":css_wrapper_files",
-    ":html_wrapper_files",
-  ]
-  in_folder = target_gen_dir
-  out_folder = "$target_gen_dir/$preprocess_folder"
-  in_files = html_wrapper_files + css_wrapper_files
-}
-
-grit("resources") {
-  defines = chrome_grit_defines
-
-  # These arguments are needed since the grd is generated at build time.
-  enable_input_discovery_for_gn_analyze = false
-  source = "$target_gen_dir/welcome_resources.grd"
-  deps = [ ":build_grd" ]
-
-  outputs = [
-    "grit/welcome_resources.h",
-    "grit/welcome_resources_map.cc",
-    "grit/welcome_resources_map.h",
-    "welcome_resources.pak",
-  ]
-  output_dir = "$root_gen_dir/chrome"
-}
-
-css_to_wrapper("css_wrapper_files") {
-  in_files = css_files
-}
-
-html_to_wrapper("html_wrapper_files") {
-  in_files = html_files
-}
-
-ts_library("build_ts") {
-  root_dir = "$target_gen_dir/$preprocess_folder"
-  out_dir = "$target_gen_dir/tsc"
-  composite = true
-  tsconfig_base = "tsconfig_base.json"
-  in_files = ts_files + css_wrapper_files + html_wrapper_files
-  definitions = [
-    "//tools/typescript/definitions/bookmarks.d.ts",
-    "//tools/typescript/definitions/chrome_event.d.ts",
-    "//tools/typescript/definitions/chrome_send.d.ts",
-    "//tools/typescript/definitions/metrics_private.d.ts",
-  ]
-  deps = [
-    "//third_party/polymer/v3_0:library",
-    "//ui/webui/resources:library",
-  ]
-  extra_deps = [
-    ":preprocess",
-    ":preprocess_generated",
-  ]
-}
-
-generate_grd("build_grd") {
+build_webui("build") {
   grd_prefix = "welcome"
-  out_grd = "$target_gen_dir/${grd_prefix}_resources.grd"
-  input_files = [
+
+  static_files = [
     "images/background_svgs/bookmarks_background.svg",
     "images/background_svgs/bookmarks_foreground.svg",
     "images/background_svgs/devices_check.svg",
@@ -119,14 +50,59 @@
     "welcome.html",
     "welcome.css",
   ]
-  input_files_base_dir = rebase_path(".", "//")
-
-  deps = [ ":build_ts" ]
-  manifest_files =
-      filter_include(get_target_outputs(":build_ts"), [ "*.manifest" ])
 
   if (is_chrome_branded) {
-    deps += [ ":build_icons_grdp" ]
-    grdp_files = [ "$target_gen_dir/icon_resources.grdp" ]
+    # Additional static files that need to be passed separately since they have
+    # a different |input_files_base_dir|
+    extra_grdp_deps = [ ":build_icons_grdp" ]
+    extra_grdp_files = [ "$target_gen_dir/icon_resources.grdp" ]
   }
+
+  # Files holding a Polymer element definition AND have an equivalent .html file.
+  web_component_files = [
+    "google_apps/nux_google_apps.ts",
+    "landing_view.ts",
+    "ntp_background/nux_ntp_background.ts",
+    "set_as_default/nux_set_as_default.ts",
+    "shared/onboarding_background.ts",
+    "shared/step_indicator.ts",
+    "signin_view.ts",
+    "welcome_app.ts",
+  ]
+
+  non_web_component_files = [
+    "google_apps/google_app_proxy.ts",
+    "google_apps/google_apps_metrics_proxy.ts",
+    "landing_view_proxy.ts",
+    "navigation_mixin.ts",
+    "ntp_background/ntp_background_metrics_proxy.ts",
+    "ntp_background/ntp_background_proxy.ts",
+    "set_as_default/nux_set_as_default_proxy.ts",
+    "shared/bookmark_proxy.ts",
+    "shared/module_metrics_proxy.ts",
+    "shared/nux_types.ts",
+    "signin_view_proxy.ts",
+    "welcome_browser_proxy.ts",
+  ]
+
+  # Files that are passed as input to css_to_wrapper().
+  css_files = [
+    "shared/action_link_style.css",
+    "shared/animations.css",
+    "shared/chooser_shared.css",
+    "shared/navi_colors.css",
+    "shared/splash_pages_shared.css",
+  ]
+
+  ts_composite = true
+  ts_definitions = [
+    "//tools/typescript/definitions/bookmarks.d.ts",
+    "//tools/typescript/definitions/chrome_event.d.ts",
+    "//tools/typescript/definitions/chrome_send.d.ts",
+    "//tools/typescript/definitions/metrics_private.d.ts",
+  ]
+  ts_deps = [
+    "//third_party/polymer/v3_0:library",
+    "//ui/webui/resources:library",
+  ]
 }
diff --git a/chrome/browser/resources/welcome/welcome.gni b/chrome/browser/resources/welcome/welcome.gni
deleted file mode 100644
index d94cf37..0000000
--- a/chrome/browser/resources/welcome/welcome.gni
+++ /dev/null
@@ -1,57 +0,0 @@
-# Copyright 2021 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-# Files holding a Polymer element definition AND have an equivalent .html file.
-web_component_files = [
-  "google_apps/nux_google_apps.ts",
-  "landing_view.ts",
-  "ntp_background/nux_ntp_background.ts",
-  "set_as_default/nux_set_as_default.ts",
-  "shared/onboarding_background.ts",
-  "shared/step_indicator.ts",
-  "signin_view.ts",
-  "welcome_app.ts",
-]
-
-# Files that are passed as input to html_to_wrapper().
-html_files = []
-foreach(f, web_component_files) {
-  html_files += [ string_replace(f, ".ts", ".html") ]
-}
-
-# Files that are generated by html_to_wrapper().
-html_wrapper_files = []
-foreach(f, html_files) {
-  html_wrapper_files += [ f + ".ts" ]
-}
-
-ts_files = [
-             "google_apps/google_app_proxy.ts",
-             "google_apps/google_apps_metrics_proxy.ts",
-             "landing_view_proxy.ts",
-             "navigation_mixin.ts",
-             "ntp_background/ntp_background_metrics_proxy.ts",
-             "ntp_background/ntp_background_proxy.ts",
-             "set_as_default/nux_set_as_default_proxy.ts",
-             "shared/bookmark_proxy.ts",
-             "shared/module_metrics_proxy.ts",
-             "shared/nux_types.ts",
-             "signin_view_proxy.ts",
-             "welcome_browser_proxy.ts",
-           ] + web_component_files
-
-# Files that are passed as input to css_to_wrapper().
-css_files = [
-  "shared/action_link_style.css",
-  "shared/animations.css",
-  "shared/chooser_shared.css",
-  "shared/navi_colors.css",
-  "shared/splash_pages_shared.css",
-]
-
-# Files that are generated by css_to_wrapper().
-css_wrapper_files = []
-foreach(f, css_files) {
-  css_wrapper_files += [ f + ".ts" ]
-}
diff --git a/chrome/browser/resources/whats_new/BUILD.gn b/chrome/browser/resources/whats_new/BUILD.gn
index bd987f8e..033d6da 100644
--- a/chrome/browser/resources/whats_new/BUILD.gn
+++ b/chrome/browser/resources/whats_new/BUILD.gn
@@ -2,70 +2,18 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("//chrome/common/features.gni")
-import("//tools/grit/grit_rule.gni")
-import("//tools/polymer/html_to_wrapper.gni")
-import("//tools/typescript/ts_library.gni")
-import("//ui/webui/resources/tools/generate_grd.gni")
+import("//chrome/browser/resources/tools/build_webui.gni")
 
-tsc_folder = "tsc"
+build_webui("build") {
+  grd_prefix = "whats_new"
+  static_files = [ "whats_new.html" ]
+  web_component_files = [ "whats_new_app.ts" ]
+  non_web_component_files = [ "whats_new_proxy.ts" ]
 
-grit("resources") {
-  defines = chrome_grit_defines
-
-  # These arguments are needed since the grd is generated at build time.
-  enable_input_discovery_for_gn_analyze = false
-  source = "$target_gen_dir/resources.grd"
-  deps = [ ":build_grd" ]
-
-  outputs = [
-    "grit/whats_new_resources.h",
-    "grit/whats_new_resources_map.cc",
-    "grit/whats_new_resources_map.h",
-    "whats_new_resources.pak",
-  ]
-  output_dir = "$root_gen_dir/chrome"
-}
-
-html_to_wrapper("html_wrapper_files") {
-  in_files = [ "whats_new_app.html" ]
-}
-
-copy("copy_ts_files") {
-  sources = [
-    "whats_new_app.ts",
-    "whats_new_proxy.ts",
-  ]
-  outputs = [ "$target_gen_dir/{{source_file_part}}" ]
-}
-
-ts_library("build_ts") {
-  root_dir = target_gen_dir
-  out_dir = "$target_gen_dir/$tsc_folder"
-  composite = true
-  tsconfig_base = "tsconfig_base.json"
-  in_files = [
-    "whats_new_app.ts",
-    "whats_new_app.html.ts",
-    "whats_new_proxy.ts",
-  ]
-  deps = [
+  ts_composite = true
+  ts_deps = [
     "//third_party/polymer/v3_0:library",
     "//ui/webui/resources:library",
     "//ui/webui/resources/js/browser_command:build_ts",
   ]
-  extra_deps = [
-    ":copy_ts_files",
-    ":html_wrapper_files",
-  ]
-}
-
-generate_grd("build_grd") {
-  grd_prefix = "whats_new"
-  out_grd = "$target_gen_dir/resources.grd"
-  deps = [ ":build_ts" ]
-  manifest_files =
-      filter_include(get_target_outputs(":build_ts"), [ "*.manifest" ])
-  input_files = [ "whats_new.html" ]
-  input_files_base_dir = rebase_path(".", "//")
 }
diff --git a/chrome/browser/safe_browsing/incident_reporting/platform_state_store_unittest.cc b/chrome/browser/safe_browsing/incident_reporting/platform_state_store_unittest.cc
index 2638282..b7df9c5 100644
--- a/chrome/browser/safe_browsing/incident_reporting/platform_state_store_unittest.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/platform_state_store_unittest.cc
@@ -71,7 +71,7 @@
   PlatformStateStoreLoadResult load_result =
       DeserializeIncidentsSent(data, deserialized.get());
   ASSERT_EQ(PlatformStateStoreLoadResult::SUCCESS, load_result);
-  ASSERT_TRUE(deserialized->Equals(incidents_sent.get()));
+  EXPECT_EQ(*incidents_sent, *deserialized);
 }
 
 }  // namespace platform_state_store
diff --git a/chrome/browser/safe_xml_parser_browsertest.cc b/chrome/browser/safe_xml_parser_browsertest.cc
index e349e54b..69b8080 100644
--- a/chrome/browser/safe_xml_parser_browsertest.cc
+++ b/chrome/browser/safe_xml_parser_browsertest.cc
@@ -63,13 +63,11 @@
                       data_decoder::DataDecoder::ValueOrError result) {
     base::ScopedClosureRunner runner(std::move(quit_loop_closure));
     if (!expected_value) {
-      EXPECT_FALSE(result.value);
-      EXPECT_TRUE(result.error);
+      EXPECT_FALSE(result.has_value());
       return;
     }
-    EXPECT_FALSE(result.error);
-    ASSERT_TRUE(result.value);
-    EXPECT_EQ(*expected_value, *result.value);
+    ASSERT_TRUE(result.has_value());
+    EXPECT_EQ(*expected_value, *result);
   }
 };
 
diff --git a/chrome/browser/safety_check/android/javatests/src/org/chromium/chrome/browser/safety_check/SafetyCheckMediatorTest.java b/chrome/browser/safety_check/android/javatests/src/org/chromium/chrome/browser/safety_check/SafetyCheckMediatorTest.java
index 63746bd..74f06c7 100644
--- a/chrome/browser/safety_check/android/javatests/src/org/chromium/chrome/browser/safety_check/SafetyCheckMediatorTest.java
+++ b/chrome/browser/safety_check/android/javatests/src/org/chromium/chrome/browser/safety_check/SafetyCheckMediatorTest.java
@@ -39,7 +39,7 @@
 import org.chromium.base.Callback;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.metrics.RecordHistogram;
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.test.util.JniMocker;
 import org.chromium.chrome.browser.password_check.PasswordCheck;
 import org.chromium.chrome.browser.password_check.PasswordCheckFactory;
@@ -66,7 +66,7 @@
 
 /** Unit tests for {@link SafetyCheckMediator}. */
 @RunWith(ParameterizedRobolectricTestRunner.class)
-@Config(manifest = Config.NONE, shadows = {ShadowRecordHistogram.class})
+@Config(manifest = Config.NONE)
 public class SafetyCheckMediatorTest {
     private static final String SAFETY_CHECK_INTERACTIONS_HISTOGRAM =
             "Settings.SafetyCheck.Interactions";
@@ -252,7 +252,7 @@
         // User is always signed in unless the test specifies otherwise.
         doReturn(true).when(mSafetyCheckBridge).userSignedIn(any(BrowserContextHandle.class));
         // Reset the histogram count.
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
     }
 
     @Test
diff --git a/chrome/browser/search/ntp_features_unittest.cc b/chrome/browser/search/ntp_features_unittest.cc
index 43e2ae5..a60c945 100644
--- a/chrome/browser/search/ntp_features_unittest.cc
+++ b/chrome/browser/search/ntp_features_unittest.cc
@@ -18,7 +18,9 @@
 
   // The default value can be overridden.
   scoped_feature_list_.InitWithFeaturesAndParameters(
-      {{kModules, {{kNtpModulesLoadTimeoutMillisecondsParam, "123"}}}}, {});
+      {{kNtpModulesLoadTimeoutMilliseconds,
+        {{kNtpModulesLoadTimeoutMillisecondsParam, "123"}}}},
+      {});
   base::TimeDelta timeout = GetModulesLoadTimeout();
   EXPECT_EQ(123, timeout.InMilliseconds());
 
@@ -26,7 +28,9 @@
   // used.
   scoped_feature_list_.Reset();
   scoped_feature_list_.InitWithFeaturesAndParameters(
-      {{kModules, {{kNtpModulesLoadTimeoutMillisecondsParam, "j"}}}}, {});
+      {{kNtpModulesLoadTimeoutMilliseconds,
+        {{kNtpModulesLoadTimeoutMillisecondsParam, "j"}}}},
+      {});
   timeout = GetModulesLoadTimeout();
   EXPECT_EQ(3, timeout.InSeconds());
 }
@@ -36,13 +40,13 @@
 
   // Can process list.
   scoped_feature_list_.InitWithFeaturesAndParameters(
-      {{kModules, {{kNtpModulesOrderParam, "foo,bar"}}}}, {});
+      {{kNtpModulesOrder, {{kNtpModulesOrderParam, "foo,bar"}}}}, {});
   EXPECT_THAT(GetModulesOrder(), ElementsAre("foo", "bar"));
 
   // Can process empty param.
   scoped_feature_list_.Reset();
   scoped_feature_list_.InitWithFeaturesAndParameters(
-      {{kModules, {{kNtpModulesOrderParam, ""}}}}, {});
+      {{kNtpModulesOrder, {{kNtpModulesOrderParam, ""}}}}, {});
   EXPECT_TRUE(GetModulesOrder().empty());
 }
 
diff --git a/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/screenshot/ScreenshotShareSheetMediatorUnitTest.java b/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/screenshot/ScreenshotShareSheetMediatorUnitTest.java
index 26cabcfd..083744c 100644
--- a/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/screenshot/ScreenshotShareSheetMediatorUnitTest.java
+++ b/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/screenshot/ScreenshotShareSheetMediatorUnitTest.java
@@ -26,7 +26,8 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.Callback;
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.browser.share.share_sheet.ChromeOptionShareCallback;
 import org.chromium.chrome.test.util.browser.Features;
@@ -38,7 +39,7 @@
  * Tests for {@link ScreenshotShareSheetMediator}.
  */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(manifest = Config.NONE, shadows = {ShadowRecordHistogram.class})
+@Config(manifest = Config.NONE)
 // clang-format off
 public class ScreenshotShareSheetMediatorUnitTest {
     // clang-format on
@@ -92,7 +93,7 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
 
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
 
         doNothing().when(mDeleteRunnable).run();
 
@@ -114,7 +115,7 @@
 
         verify(mDeleteRunnable).run();
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Sharing.ScreenshotFallback.Action",
                         ScreenshotShareSheetMetrics.ScreenshotShareSheetAction.DELETE));
     }
@@ -127,7 +128,7 @@
 
         verify(mSaveRunnable).run();
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Sharing.ScreenshotFallback.Action",
                         ScreenshotShareSheetMetrics.ScreenshotShareSheetAction.SAVE));
     }
@@ -141,7 +142,7 @@
         Assert.assertTrue(mMediator.generateTemporaryUriFromBitmapCalled());
         verify(mDeleteRunnable).run();
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Sharing.ScreenshotFallback.Action",
                         ScreenshotShareSheetMetrics.ScreenshotShareSheetAction.SHARE));
     }
@@ -154,7 +155,7 @@
 
         verify(mInstallRunnable).onResult(any());
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Sharing.ScreenshotFallback.Action",
                         ScreenshotShareSheetMetrics.ScreenshotShareSheetAction.EDIT));
     }
diff --git a/chrome/browser/themes/theme_helper.cc b/chrome/browser/themes/theme_helper.cc
index 43f84619..b8aeb79 100644
--- a/chrome/browser/themes/theme_helper.cc
+++ b/chrome/browser/themes/theme_helper.cc
@@ -19,7 +19,7 @@
 #include "ui/native_theme/native_theme.h"
 
 #if BUILDFLAG(IS_LINUX)
-#include "ui/views/linux_ui/linux_ui.h"
+#include "ui/linux/linux_ui.h"
 #endif
 
 namespace {
@@ -193,7 +193,7 @@
   ui::NativeTheme const* native_theme =
       ui::NativeTheme::GetInstanceForNativeUi();
 #if BUILDFLAG(IS_LINUX)
-  if (const auto* linux_ui = views::LinuxUI::instance()) {
+  if (const auto* linux_ui = ui::LinuxUi::instance()) {
     // We rely on the fact that the system theme is in use iff `theme_supplier`
     // is non-null, but this is cheating. In the future this might not hold
     // after we fully migrate to the color provider and remove SystemThemeLinux.
diff --git a/chrome/browser/themes/theme_service.cc b/chrome/browser/themes/theme_service.cc
index 66acda8..57b51e4 100644
--- a/chrome/browser/themes/theme_service.cc
+++ b/chrome/browser/themes/theme_service.cc
@@ -73,8 +73,8 @@
 #endif
 
 #if BUILDFLAG(IS_LINUX)
+#include "ui/linux/linux_ui.h"
 #include "ui/ozone/public/ozone_platform.h"
-#include "ui/views/linux_ui/linux_ui.h"
 #endif
 
 using TP = ThemeProperties;
diff --git a/chrome/browser/themes/theme_service_aura_linux.cc b/chrome/browser/themes/theme_service_aura_linux.cc
index eb132114..e5cb99f2 100644
--- a/chrome/browser/themes/theme_service_aura_linux.cc
+++ b/chrome/browser/themes/theme_service_aura_linux.cc
@@ -11,8 +11,8 @@
 #include "chrome/common/pref_names.h"
 #include "components/prefs/pref_service.h"
 #include "ui/gfx/image/image.h"
+#include "ui/linux/linux_ui.h"
 #include "ui/native_theme/native_theme_aura.h"
-#include "ui/views/linux_ui/linux_ui.h"
 
 namespace {
 
@@ -35,13 +35,13 @@
   ~SystemThemeLinux() override;
 
   // These pointers are not owned by us.
-  const raw_ptr<views::LinuxUI> linux_ui_;
+  const raw_ptr<ui::LinuxUi> linux_ui_;
   const raw_ptr<PrefService> pref_service_;
 };
 
 SystemThemeLinux::SystemThemeLinux(PrefService* pref_service)
     : CustomThemeSupplier(ThemeType::kNativeX11),
-      linux_ui_(views::LinuxUI::instance()),
+      linux_ui_(ui::LinuxUi::instance()),
       pref_service_(pref_service) {}
 
 void SystemThemeLinux::StartUsingTheme() {
diff --git a/chrome/browser/themes/theme_service_browsertest.cc b/chrome/browser/themes/theme_service_browsertest.cc
index 9f654e73..e4d52cf 100644
--- a/chrome/browser/themes/theme_service_browsertest.cc
+++ b/chrome/browser/themes/theme_service_browsertest.cc
@@ -21,7 +21,7 @@
 #include "ui/color/color_provider.h"
 
 #if BUILDFLAG(USE_GTK)
-#include "ui/views/linux_ui/linux_ui.h"
+#include "ui/linux/linux_ui.h"
 #endif
 
 namespace {
@@ -160,7 +160,7 @@
   // true in dark mode and GTK.
   ui::NativeTheme::GetInstanceForNativeUi()->set_use_dark_colors(false);
 #if BUILDFLAG(USE_GTK)
-  views::LinuxUI::instance()->SetUseSystemThemeCallback(
+  ui::LinuxUi::instance()->SetUseSystemThemeCallback(
       base::BindRepeating([](aura::Window* window) { return false; }));
 #endif  // BUILDFLAG(USE_GTK)
   ui::NativeTheme::GetInstanceForNativeUi()->NotifyOnNativeThemeUpdated();
diff --git a/chrome/browser/themes/theme_service_factory.cc b/chrome/browser/themes/theme_service_factory.cc
index db3162c7..45a388f 100644
--- a/chrome/browser/themes/theme_service_factory.cc
+++ b/chrome/browser/themes/theme_service_factory.cc
@@ -27,7 +27,7 @@
 // of lacros-chrome is complete.
 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
 #include "chrome/browser/themes/theme_service_aura_linux.h"
-#include "ui/views/linux_ui/linux_ui.h"
+#include "ui/linux/linux_ui.h"
 #endif
 
 namespace {
@@ -99,7 +99,7 @@
 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
   bool default_uses_system_theme = false;
 
-  const views::LinuxUI* linux_ui = views::LinuxUI::instance();
+  const ui::LinuxUi* linux_ui = ui::LinuxUi::instance();
   if (linux_ui)
     default_uses_system_theme = linux_ui->GetDefaultUsesSystemTheme();
 
diff --git a/chrome/browser/themes/theme_service_unittest.cc b/chrome/browser/themes/theme_service_unittest.cc
index 2a02794..ebab1c5e 100644
--- a/chrome/browser/themes/theme_service_unittest.cc
+++ b/chrome/browser/themes/theme_service_unittest.cc
@@ -52,9 +52,9 @@
 #include "ui/views/views_features.h"
 
 #if BUILDFLAG(IS_LINUX)
+#include "ui/linux/linux_ui.h"
+#include "ui/linux/linux_ui_factory.h"  // nogncheck
 #include "ui/ozone/public/ozone_platform.h"
-#include "ui/views/linux_ui/linux_ui.h"
-#include "ui/views/linux_ui/linux_ui_factory.h"  // nogncheck
 #endif
 
 #if BUILDFLAG(ENABLE_SUPERVISED_USERS)
@@ -198,14 +198,14 @@
     static bool initialized_mixers = false;
     if (!initialized_mixers) {
 #if BUILDFLAG(IS_LINUX)
-      // Ensures LinuxUI is configured on supported linux platforms.
+      // Ensures LinuxUi is configured on supported linux platforms.
       // Initializing the toolkit also adds the native toolkit ColorMixers.
       ui::OzonePlatform::InitParams ozone_params;
       ozone_params.single_process = true;
       ui::OzonePlatform::InitializeForUI(ozone_params);
-      auto linux_ui = CreateLinuxUi();
+      auto linux_ui = ui::CreateLinuxUi();
       ASSERT_TRUE(linux_ui);
-      views::LinuxUI::SetInstance(std::move(linux_ui));
+      ui::LinuxUi::SetInstance(std::move(linux_ui));
 #endif  // BUILDFLAG(IS_LINUX)
 
       // Add the Chrome ColorMixers after native ColorMixers.
@@ -215,7 +215,7 @@
       initialized_mixers = true;
     }
 #if BUILDFLAG(IS_LINUX)
-    views::LinuxUI::instance()->SetUseSystemThemeCallback(base::BindRepeating(
+    ui::LinuxUi::instance()->SetUseSystemThemeCallback(base::BindRepeating(
         [](bool use_system_theme, aura::Window* window) {
           return use_system_theme;
         },
@@ -232,7 +232,7 @@
     native_theme_ = ui::NativeTheme::GetInstanceForNativeUi();
 #if BUILDFLAG(IS_LINUX)
     if (system_theme == SystemTheme::kCustom) {
-      const auto* linux_ui = views::LinuxUI::instance();
+      const auto* linux_ui = ui::LinuxUi::instance();
       native_theme_ = linux_ui->GetNativeTheme(nullptr);
     }
 #endif
diff --git a/chrome/browser/touch_to_fill/android/junit/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillControllerTest.java b/chrome/browser/touch_to_fill/android/junit/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillControllerTest.java
index 1a2371a5..a9ef9a537 100644
--- a/chrome/browser/touch_to_fill/android/junit/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillControllerTest.java
+++ b/chrome/browser/touch_to_fill/android/junit/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillControllerTest.java
@@ -47,7 +47,7 @@
 
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.metrics.RecordHistogramJni;
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.JniMocker;
 import org.chromium.chrome.browser.touch_to_fill.TouchToFillComponent.UserAction;
@@ -75,7 +75,7 @@
  * properly.
  */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(manifest = Config.NONE, shadows = {ShadowRecordHistogram.class})
+@Config(manifest = Config.NONE)
 public class TouchToFillControllerTest {
     private static final GURL TEST_URL = JUnitTestGURLs.getGURL(JUnitTestGURLs.EXAMPLE_URL);
     private static final String TEST_SUBDOMAIN_URL = "https://subdomain.example.xyz";
@@ -111,7 +111,7 @@
 
     @Before
     public void setUp() {
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
         MockitoAnnotations.initMocks(this);
         mJniMocker.mock(UrlFormatterJni.TEST_HOOKS, mUrlFormatterJniMock);
         mJniMocker.mock(RecordHistogramJni.TEST_HOOKS, mMockRecordHistogram);
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index d0f7398..b28169d 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -3931,6 +3931,7 @@
       "views/window_sizer_linux.cc",
       "views/window_sizer_linux.h",
     ]
+    deps += [ "//ui/linux:linux_ui" ]
   }
 
   if (is_linux || is_chromeos_lacros || is_fuchsia) {
@@ -3948,8 +3949,8 @@
       "//ui/base:wm_role_names",
       "//ui/base/ime",
       "//ui/events:dom_keycode_converter",
+      "//ui/linux:linux_ui_factory",
       "//ui/platform_window",
-      "//ui/views/linux_ui:linux_ui_factory",
     ]
 
     if (use_dbus) {
diff --git a/chrome/browser/ui/android/night_mode/java/src/org/chromium/chrome/browser/night_mode/WebContentsDarkModeControllerUnitTest.java b/chrome/browser/ui/android/night_mode/java/src/org/chromium/chrome/browser/night_mode/WebContentsDarkModeControllerUnitTest.java
index ccaf39b..a678e83 100644
--- a/chrome/browser/ui/android/night_mode/java/src/org/chromium/chrome/browser/night_mode/WebContentsDarkModeControllerUnitTest.java
+++ b/chrome/browser/ui/android/night_mode/java/src/org/chromium/chrome/browser/night_mode/WebContentsDarkModeControllerUnitTest.java
@@ -23,7 +23,7 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.metrics.RecordHistogram;
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.JniMocker;
 import org.chromium.chrome.browser.profiles.Profile;
@@ -37,7 +37,7 @@
 
 /** Unit tests for {@link WebContentsDarkModeController}. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(manifest = Config.NONE, shadows = {ShadowRecordHistogram.class, ShadowColorUtils.class})
+@Config(manifest = Config.NONE, shadows = {ShadowColorUtils.class})
 @SuppressWarnings("DoNotMock") // Mocking GURL
 public class WebContentsDarkModeControllerUnitTest {
     @Rule
@@ -62,7 +62,7 @@
         mJniMocker.mock(WebsitePreferenceBridgeJni.TEST_HOOKS, mMockWebsitePreferenceBridgeJni);
 
         Profile.setLastUsedProfileForTesting(mMockProfile);
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
 
         Mockito.doAnswer(invocation -> {
                    mIsGlobalSettingsEnabled = (boolean) invocation.getArguments()[2];
@@ -95,7 +95,7 @@
         Profile.setLastUsedProfileForTesting(null);
         ShadowColorUtils.sInNightMode = false;
 
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
     }
 
     @Test
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/SearchEngineLogoUtilsUnitTest.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/SearchEngineLogoUtilsUnitTest.java
index 62371ec8..c8de80a 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/SearchEngineLogoUtilsUnitTest.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/SearchEngineLogoUtilsUnitTest.java
@@ -38,7 +38,8 @@
 import org.robolectric.shadow.api.Shadow;
 
 import org.chromium.base.Callback;
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.JniMocker;
 import org.chromium.chrome.R;
@@ -59,7 +60,7 @@
  * Tests for SearchEngineLogoUtils.
  */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(manifest = Config.NONE, shadows = {ShadowRecordHistogram.class})
+@Config(manifest = Config.NONE)
 public class SearchEngineLogoUtilsUnitTest {
     private static final String LOGO_URL = JUnitTestGURLs.URL_1;
     private static final String EVENTS_HISTOGRAM = "AndroidSearchEngineLogo.Events";
@@ -116,7 +117,7 @@
 
     @After
     public void tearDown() {
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
         SearchEngineLogoUtils.resetForTesting();
     }
 
@@ -132,7 +133,7 @@
         mSearchEngineLogoUtils.recordEvent(
                 SearchEngineLogoUtils.Events.FETCH_NON_GOOGLE_LOGO_REQUEST);
         assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(EVENTS_HISTOGRAM,
+                RecordHistogram.getHistogramValueCountForTesting(EVENTS_HISTOGRAM,
                         SearchEngineLogoUtils.Events.FETCH_NON_GOOGLE_LOGO_REQUEST));
     }
 
@@ -150,10 +151,10 @@
 
         verify(mCallback).onResult(expected);
         assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(EVENTS_HISTOGRAM,
+                RecordHistogram.getHistogramValueCountForTesting(EVENTS_HISTOGRAM,
                         SearchEngineLogoUtils.Events.FETCH_NON_GOOGLE_LOGO_REQUEST));
         assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         EVENTS_HISTOGRAM, SearchEngineLogoUtils.Events.FETCH_SUCCESS));
     }
 
@@ -198,13 +199,13 @@
 
         verify(mCallback, times(2)).onResult(expected);
         assertEquals(2,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(EVENTS_HISTOGRAM,
+                RecordHistogram.getHistogramValueCountForTesting(EVENTS_HISTOGRAM,
                         SearchEngineLogoUtils.Events.FETCH_NON_GOOGLE_LOGO_REQUEST));
         assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         EVENTS_HISTOGRAM, SearchEngineLogoUtils.Events.FETCH_SUCCESS));
         assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         EVENTS_HISTOGRAM, SearchEngineLogoUtils.Events.FETCH_SUCCESS_CACHE_HIT));
     }
 
@@ -220,10 +221,10 @@
 
         verify(mCallback).onResult(eq(expected));
         assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(EVENTS_HISTOGRAM,
+                RecordHistogram.getHistogramValueCountForTesting(EVENTS_HISTOGRAM,
                         SearchEngineLogoUtils.Events.FETCH_NON_GOOGLE_LOGO_REQUEST));
         assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         EVENTS_HISTOGRAM, SearchEngineLogoUtils.Events.FETCH_FAILED_NULL_URL));
     }
 
@@ -242,10 +243,10 @@
 
         verify(mCallback).onResult(expected);
         assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(EVENTS_HISTOGRAM,
+                RecordHistogram.getHistogramValueCountForTesting(EVENTS_HISTOGRAM,
                         SearchEngineLogoUtils.Events.FETCH_NON_GOOGLE_LOGO_REQUEST));
         assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(EVENTS_HISTOGRAM,
+                RecordHistogram.getHistogramValueCountForTesting(EVENTS_HISTOGRAM,
                         SearchEngineLogoUtils.Events.FETCH_FAILED_FAVICON_HELPER_ERROR));
     }
 
@@ -265,10 +266,10 @@
 
         verify(mCallback).onResult(expected);
         assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(EVENTS_HISTOGRAM,
+                RecordHistogram.getHistogramValueCountForTesting(EVENTS_HISTOGRAM,
                         SearchEngineLogoUtils.Events.FETCH_NON_GOOGLE_LOGO_REQUEST));
         assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(EVENTS_HISTOGRAM,
+                RecordHistogram.getHistogramValueCountForTesting(EVENTS_HISTOGRAM,
                         SearchEngineLogoUtils.Events.FETCH_FAILED_RETURNED_BITMAP_NULL));
     }
 
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/mostvisited/MostVisitedTilesProcessorUnitTest.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/mostvisited/MostVisitedTilesProcessorUnitTest.java
index aac16034..79b0f33 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/mostvisited/MostVisitedTilesProcessorUnitTest.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/mostvisited/MostVisitedTilesProcessorUnitTest.java
@@ -36,7 +36,8 @@
 import org.robolectric.annotation.Config;
 import org.robolectric.shadows.ShadowLog;
 
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
@@ -64,7 +65,7 @@
  * Tests for {@link MostVisitedTilesProcessor}.
  */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(manifest = Config.NONE, shadows = {ShadowRecordHistogram.class})
+@Config(manifest = Config.NONE)
 public final class MostVisitedTilesProcessorUnitTest {
     private static final GURL NAV_URL = JUnitTestGURLs.getGURL(JUnitTestGURLs.URL_1);
     private static final GURL NAV_URL_2 = JUnitTestGURLs.getGURL(JUnitTestGURLs.URL_2);
@@ -88,6 +89,8 @@
 
     @Before
     public void setUp() {
+        UmaRecorderHolder.resetForTesting();
+
         // Enable logs to be printed along with possible test failures.
         ShadowLog.stream = System.out;
         mActivity = Robolectric.buildActivity(Activity.class).setup().get();
@@ -178,19 +181,19 @@
 
         // Verify histogram increased for delete attempt.
         assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Omnibox.SuggestTiles.SelectedTileType", SuggestTileType.SEARCH));
         assertEquals(2,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Omnibox.SuggestTiles.SelectedTileType", SuggestTileType.URL));
         assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Omnibox.SuggestTiles.SelectedTileIndex", 0));
         assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Omnibox.SuggestTiles.SelectedTileIndex", 1));
         assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Omnibox.SuggestTiles.SelectedTileIndex", 2));
     }
 
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/voice/AssistantVoiceSearchServiceUnitTest.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/voice/AssistantVoiceSearchServiceUnitTest.java
index 8ca5338..ff1e6fc 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/voice/AssistantVoiceSearchServiceUnitTest.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/voice/AssistantVoiceSearchServiceUnitTest.java
@@ -36,7 +36,8 @@
 import org.chromium.base.BaseSwitches;
 import org.chromium.base.Promise;
 import org.chromium.base.SysUtils;
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.task.test.CustomShadowAsyncTask;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.CommandLineFlags;
@@ -62,8 +63,7 @@
  * Tests for AssistantVoiceSearchService.
  */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(manifest = Config.NONE,
-        shadows = {CustomShadowAsyncTask.class, ShadowRecordHistogram.class})
+@Config(manifest = Config.NONE, shadows = {CustomShadowAsyncTask.class})
 @LooperMode(LooperMode.Mode.LEGACY)
 @Features.EnableFeatures(ChromeFeatureList.OMNIBOX_ASSISTANT_VOICE_SEARCH)
 @CommandLineFlags.Add(BaseSwitches.DISABLE_LOW_END_DEVICE_MODE)
@@ -110,7 +110,7 @@
 
     @Before
     public void setUp() {
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
         SysUtils.resetForTesting();
         MockitoAnnotations.initMocks(this);
         DeferredStartupHandler.setInstanceForTests(new TestDeferredStartupHandler());
@@ -337,28 +337,28 @@
     public void testReportUserEligibility() {
         mAssistantVoiceSearchService.reportMicPressUserEligibility();
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         AssistantVoiceSearchService.USER_ELIGIBILITY_HISTOGRAM, /* eligible= */ 1));
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         AssistantVoiceSearchService.AGSA_VERSION_HISTOGRAM, AGSA_VERSION_NUMBER));
 
         doReturn(true).when(mGsaState).isAgsaVersionBelowMinimum(any(), any());
         doReturn(false).when(mIdentityManager).hasPrimaryAccount(anyInt());
         mAssistantVoiceSearchService.reportMicPressUserEligibility();
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         AssistantVoiceSearchService.USER_ELIGIBILITY_HISTOGRAM, /* eligible= */ 0));
         Assert.assertEquals(2,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         AssistantVoiceSearchService.AGSA_VERSION_HISTOGRAM, AGSA_VERSION_NUMBER));
 
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         AssistantVoiceSearchService.USER_ELIGIBILITY_FAILURE_REASON_HISTOGRAM,
                         AssistantVoiceSearchService.EligibilityFailureReason.NO_CHROME_ACCOUNT));
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         AssistantVoiceSearchService.USER_ELIGIBILITY_FAILURE_REASON_HISTOGRAM,
                         AssistantVoiceSearchService.EligibilityFailureReason.NO_CHROME_ACCOUNT));
     }
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/adaptive/AdaptiveToolbarButtonControllerTest.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/adaptive/AdaptiveToolbarButtonControllerTest.java
index 605158b..1325d6f 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/adaptive/AdaptiveToolbarButtonControllerTest.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/adaptive/AdaptiveToolbarButtonControllerTest.java
@@ -37,7 +37,7 @@
 
 import org.chromium.base.Callback;
 import org.chromium.base.library_loader.LibraryLoader;
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
@@ -63,8 +63,7 @@
 
 /** Unit tests for the {@link AdaptiveToolbarButtonController} */
 @Config(manifest = Config.NONE,
-        shadows = {ShadowRecordHistogram.class,
-                AdaptiveToolbarButtonControllerTest.ShadowChromeFeatureList.class,
+        shadows = {AdaptiveToolbarButtonControllerTest.ShadowChromeFeatureList.class,
                 AdaptiveToolbarButtonControllerTest.ShadowVoiceRecognitionHandler.class})
 @RunWith(BaseRobolectricTestRunner.class)
 @EnableFeatures(ChromeFeatureList.ADAPTIVE_BUTTON_IN_TOP_TOOLBAR_CUSTOMIZATION_V2)
@@ -115,7 +114,7 @@
         MockitoAnnotations.initMocks(this);
         LibraryLoader.getInstance().setLibrariesLoadedForNativeTests();
         ShadowChromeFeatureList.sParamValues.clear();
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
         ShadowVoiceRecognitionHandler.sIsVoiceRecognitionEnabled = true;
         AdaptiveToolbarFeatures.clearParsedParamsForTesting();
         mButtonData = new ButtonDataImpl(
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonCoordinator.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonCoordinator.java
index 98e9728a..944dccc6c 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonCoordinator.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonCoordinator.java
@@ -151,7 +151,11 @@
     }
 
     /**
-     * Updates the color filter of the background to match the current theme/website color.
+     * Updates the color filter of the background to match the current address bar background color.
+     * This color is only used when showing a contextual action button (when {@link
+     * #updateButton(ButtonData)} is called with a {@link
+     * org.chromium.chrome.browser.toolbar.ButtonData.ButtonSpec} where {@code isDynamicAction()} is
+     * true).
      * @param backgroundColor
      */
     public void setBackgroundColorFilter(int backgroundColor) {
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonCoordinatorTest.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonCoordinatorTest.java
index 69bc675..cc15a16 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonCoordinatorTest.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonCoordinatorTest.java
@@ -17,6 +17,7 @@
 import android.content.res.ColorStateList;
 import android.graphics.Color;
 import android.graphics.drawable.Drawable;
+import android.transition.Transition;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.view.View.OnLongClickListener;
@@ -55,6 +56,8 @@
     private OptionalButtonView mMockOptionalButtonView;
     @Mock
     private UserEducationHelper mMockUserEducationHelper;
+    @Mock
+    private Callback<Transition> mMockBeginDelayedTransition;
 
     @Captor
     ArgumentCaptor<Callback<Integer>> mCallbackArgumentCaptor;
@@ -217,8 +220,7 @@
 
         // Button should be disabled.
         verify(mockButtonView).setEnabled(false);
-        // Animation should only happen at the first call.
-        verify(mMockOptionalButtonView, times(1)).updateButtonWithAnimation(buttonData);
+        verify(mMockOptionalButtonView, times(2)).updateButtonWithAnimation(buttonData);
     }
 
     @Test
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonProperties.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonProperties.java
index 882c6da..86685a5 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonProperties.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonProperties.java
@@ -16,8 +16,12 @@
 import org.chromium.ui.modelutil.PropertyModel.WritableObjectPropertyKey;
 
 class OptionalButtonProperties {
+    // We skip equality checks because some controllers update their button by changing the
+    // ButtonSpec value on the same ButtonData instance. In addition we don't split this into
+    // BUTTON_SPEC and CAN_SHOW because it would be hard to avoid two animations when both the spec
+    // and visibility change at the same time.
     public static final WritableObjectPropertyKey<ButtonData> BUTTON_DATA =
-            new WritableObjectPropertyKey<>();
+            new WritableObjectPropertyKey<>(/* skipEquality= */ true);
     public static final WritableBooleanPropertyKey IS_ENABLED = new WritableBooleanPropertyKey();
     public static final WritableObjectPropertyKey<Callback<Integer>> TRANSITION_STARTED_CALLBACK =
             new WritableObjectPropertyKey<>();
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonView.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonView.java
index 9abaef8..10789d7 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonView.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonView.java
@@ -36,6 +36,7 @@
 import org.chromium.chrome.browser.toolbar.ButtonData;
 import org.chromium.chrome.browser.toolbar.ButtonData.ButtonSpec;
 import org.chromium.chrome.browser.toolbar.R;
+import org.chromium.chrome.browser.toolbar.adaptive.AdaptiveToolbarFeatures.AdaptiveToolbarButtonVariant;
 import org.chromium.chrome.browser.toolbar.optional_button.OptionalButtonConstants.TransitionType;
 import org.chromium.components.browser_ui.widget.listmenu.ListMenuButton;
 
@@ -66,10 +67,14 @@
     private String mActionChipLabelString;
     private int mBackgroundColorFilter;
     private Runnable mOnBeforeHideTransitionCallback;
+    private Callback<Transition> mFakeBeginTransitionForTesting;
     private Handler mHandlerForTesting;
 
     private @State int mState;
 
+    private @AdaptiveToolbarButtonVariant int mCurrentButtonVariant =
+            AdaptiveToolbarButtonVariant.NONE;
+    private boolean mCanCurrentButtonShow;
     private @ButtonType int mCurrentButtonType;
     private @ButtonType int mNextButtonType;
 
@@ -147,6 +152,13 @@
      *         attributes. If null then this view starts a hide transition.
      */
     void updateButtonWithAnimation(@Nullable ButtonData buttonData) {
+        // If we receive the same button with the same visibility then there's no need to update.
+        if (buttonData != null
+                && mCurrentButtonVariant == buttonData.getButtonSpec().getButtonVariant()
+                && mCanCurrentButtonShow == buttonData.canShow()) {
+            return;
+        }
+
         if (mTransitionRoot == null || mIsAnimationAllowedPredicate == null) {
             throw new IllegalStateException(
                     "Both transitionRoot and animationAllowedPredicate must be set before starting "
@@ -174,11 +186,15 @@
         }
 
         if (buttonData == null || !buttonData.canShow()) {
+            mCurrentButtonVariant = AdaptiveToolbarButtonVariant.NONE;
+            mCanCurrentButtonShow = false;
             hide(canAnimate);
             return;
         }
 
         ButtonSpec buttonSpec = buttonData.getButtonSpec();
+        mCurrentButtonVariant = buttonSpec.getButtonVariant();
+        mCanCurrentButtonShow = buttonData.canShow();
 
         mIconDrawable = buttonSpec.getDrawable();
         mNextButtonType = buttonSpec.isDynamicAction() ? ButtonType.DYNAMIC : ButtonType.STATIC;
@@ -195,9 +211,7 @@
         mContentDescription =
                 getContext().getResources().getString(buttonSpec.getContentDescriptionResId());
 
-        if (mIconDrawable == null) {
-            hide(canAnimate);
-        } else if (mState == State.HIDDEN && mActionChipLabelString == null) {
+        if (mState == State.HIDDEN && mActionChipLabelString == null) {
             showIcon(canAnimate);
         } else if (canAnimate && mActionChipLabelString != null) {
             animateActionChipExpansion();
@@ -505,7 +519,7 @@
 
         // Begin a transition, all layout changes after this call will be animated. The animation
         // starts at the next frame.
-        TransitionManager.beginDelayedTransition(mTransitionRoot, createSwapIconTransition());
+        beginDelayedTransition(createSwapIconTransition());
 
         // Default transition.
         if (!isRevertingToStatic) {
@@ -553,7 +567,7 @@
 
         // Begin a transition, all layout changes after this call will be animated. The animation
         // starts at the next frame.
-        TransitionManager.beginDelayedTransition(mTransitionRoot, createActionChipTransition());
+        beginDelayedTransition(createActionChipTransition());
 
         mButton.setVisibility(VISIBLE);
         mAnimationImage.setVisibility(GONE);
@@ -574,7 +588,7 @@
     private void animateActionChipCollapse() {
         // Begin a transition, all layout changes after this call will be animated. The animation
         // starts at the next frame.
-        TransitionManager.beginDelayedTransition(mTransitionRoot, createActionChipTransition());
+        beginDelayedTransition(createActionChipTransition());
 
         mActionChipLabel.setVisibility(GONE);
         setWidth(mCollapsedStateWidthPx);
@@ -589,7 +603,7 @@
         }
         // Begin a transition, all layout changes after this call will be animated. The animation
         // starts at the next frame.
-        TransitionManager.beginDelayedTransition(mTransitionRoot, transition);
+        beginDelayedTransition(transition);
 
         mButton.setVisibility(GONE);
         mBackground.setVisibility(GONE);
@@ -620,7 +634,7 @@
 
         // Begin a transition, all layout changes after this call will be animated. The animation
         // starts at the next frame.
-        TransitionManager.beginDelayedTransition(mTransitionRoot, transition);
+        beginDelayedTransition(transition);
 
         setWidth(mCollapsedStateWidthPx);
         mButton.setVisibility(VISIBLE);
@@ -629,4 +643,19 @@
 
         mState = State.RUNNING_SHOW_TRANSITION;
     }
+
+    @VisibleForTesting
+    public void setFakeBeginDelayedTransitionForTesting(
+            Callback<Transition> fakeBeginDelayedTransition) {
+        mFakeBeginTransitionForTesting = fakeBeginDelayedTransition;
+    }
+
+    private void beginDelayedTransition(Transition transition) {
+        if (mFakeBeginTransitionForTesting != null) {
+            mFakeBeginTransitionForTesting.onResult(transition);
+            return;
+        }
+
+        TransitionManager.beginDelayedTransition(mTransitionRoot, transition);
+    }
 }
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonViewTest.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonViewTest.java
index f1a2b98..d18feff 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonViewTest.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonViewTest.java
@@ -20,6 +20,7 @@
 import android.graphics.drawable.Drawable;
 import android.os.Handler;
 import android.os.Looper;
+import android.transition.Transition;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.View.OnClickListener;
@@ -64,6 +65,8 @@
     private ImageView mButtonBackground;
     private ShadowLooper mShadowLooper;
     private BooleanSupplier mMockAnimationChecker;
+    private Callback<Transition> mMockBeginDelayedTransition;
+    private int mBeginTransitionCount;
 
     @Before
     public void setUp() {
@@ -77,11 +80,17 @@
                 new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
         mOptionalButtonView.setIsAnimationAllowedPredicate(mMockAnimationChecker);
 
+        mMockBeginDelayedTransition = Mockito.mock(Callback.class);
+
         mShadowLooper = Shadows.shadowOf(Looper.getMainLooper());
         Handler handler = new Handler(Looper.getMainLooper());
         // This handler is used to schedule the action chip collapse.
         mOptionalButtonView.setHandlerForTesting(handler);
 
+        // This function replaces calls to TransitionManager.beginDelayedTransition which is hard to
+        // mock as it's static.
+        mOptionalButtonView.setFakeBeginDelayedTransitionForTesting(mMockBeginDelayedTransition);
+
         mInnerButton = (ImageButton) mOptionalButtonView.getButtonView();
         mActionChipLabel = mOptionalButtonView.findViewById(R.id.action_chip_label);
         mButtonBackground =
@@ -106,7 +115,7 @@
         return buttonData;
     }
 
-    private ButtonDataImpl getDataForDynamicPriceTrackingIconButton() {
+    private ButtonDataImpl getDataForPriceTrackingIconButton() {
         Drawable iconDrawable = AppCompatResources.getDrawable(mActivity, R.drawable.btn_mic);
         OnClickListener clickListener = mock(OnClickListener.class);
         OnLongClickListener longClickListener = mock(OnLongClickListener.class);
@@ -125,7 +134,7 @@
         return buttonData;
     }
 
-    private ButtonDataImpl getDataForShareIconActionChip() {
+    private ButtonDataImpl getDataForPriceTrackingActionChip() {
         Drawable iconDrawable = AppCompatResources.getDrawable(mActivity, R.drawable.new_tab_icon);
         OnClickListener clickListener = mock(OnClickListener.class);
         OnLongClickListener longClickListener = mock(OnLongClickListener.class);
@@ -146,7 +155,7 @@
 
     @Test
     public void testSetButtonEnabled() {
-        ButtonDataImpl disabledButton = getDataForDynamicPriceTrackingIconButton();
+        ButtonDataImpl disabledButton = getDataForPriceTrackingIconButton();
         disabledButton.setEnabled(false);
         ViewGroup transitionRoot = mock(ViewGroup.class);
 
@@ -180,7 +189,7 @@
 
     @Test
     public void testSetIconDrawableWithAnimation_fromHiddenToIcon() {
-        ButtonData buttonData = getDataForDynamicPriceTrackingIconButton();
+        ButtonData buttonData = getDataForPriceTrackingIconButton();
         String contentDescriptionString = mActivity.getResources().getString(
                 buttonData.getButtonSpec().getContentDescriptionResId());
         ViewGroup transitionRoot = mock(ViewGroup.class);
@@ -203,7 +212,7 @@
 
     @Test
     public void testSetIconDrawableWithAnimation_swapIcons() {
-        ButtonData firstButtonData = getDataForDynamicPriceTrackingIconButton();
+        ButtonData firstButtonData = getDataForPriceTrackingIconButton();
         ButtonData secondButtonData = getDataForStaticNewTabIconButton();
 
         ViewGroup transitionRoot = mock(ViewGroup.class);
@@ -244,7 +253,7 @@
 
     @Test
     public void testSetIconDrawableWithAnimation_expandActionChipFromHidden() {
-        ButtonData actionChipButtonData = getDataForShareIconActionChip();
+        ButtonData actionChipButtonData = getDataForPriceTrackingActionChip();
         String actionChipLabel = mActivity.getResources().getString(
                 actionChipButtonData.getButtonSpec().getActionChipLabelResId());
 
@@ -273,7 +282,7 @@
 
     @Test
     public void testSetIconDrawableWithAnimation_expandAndCollapseActionChipFromHidden() {
-        ButtonData actionChipButtonData = getDataForShareIconActionChip();
+        ButtonData actionChipButtonData = getDataForPriceTrackingActionChip();
 
         ViewGroup transitionRoot = mock(ViewGroup.class);
         mOptionalButtonView.setTransitionRoot(transitionRoot);
@@ -307,7 +316,7 @@
     @Test
     public void testSetIconDrawableWithAnimation_expandActionChipFromAnotherIcon() {
         ButtonData staticButtonData = getDataForStaticNewTabIconButton();
-        ButtonData actionChipButtonData = getDataForShareIconActionChip();
+        ButtonData actionChipButtonData = getDataForPriceTrackingActionChip();
 
         ViewGroup transitionRoot = mock(ViewGroup.class);
         mOptionalButtonView.setTransitionRoot(transitionRoot);
@@ -372,8 +381,8 @@
     @Test
     public void testTransitionCallbacks() {
         ButtonData firstButton = getDataForStaticNewTabIconButton();
-        ButtonData secondButton = getDataForDynamicPriceTrackingIconButton();
-        ButtonData actionChipButton = getDataForShareIconActionChip();
+        ButtonData secondButton = getDataForPriceTrackingIconButton();
+        ButtonData actionChipButton = getDataForPriceTrackingActionChip();
 
         Runnable beforeHideTransitionCallback = mock(Runnable.class);
         Callback<Integer> transitionStartedCallback = mock(Callback.class);
@@ -399,6 +408,11 @@
         mOptionalButtonView.onTransitionStart(null);
         mOptionalButtonView.onTransitionEnd(null);
 
+        // Transition back to firstButton.
+        mOptionalButtonView.updateButtonWithAnimation(firstButton);
+        mOptionalButtonView.onTransitionStart(null);
+        mOptionalButtonView.onTransitionEnd(null);
+
         // Transition from secondButton to actionChipButton
         mOptionalButtonView.updateButtonWithAnimation(actionChipButton);
         // Run callbacks for expansion transition.
@@ -421,6 +435,8 @@
         inOrder.verify(transitionFinishedCallback).onResult(TransitionType.SHOWING);
         inOrder.verify(transitionStartedCallback).onResult(TransitionType.SWAPPING);
         inOrder.verify(transitionFinishedCallback).onResult(TransitionType.SWAPPING);
+        inOrder.verify(transitionStartedCallback).onResult(TransitionType.SWAPPING);
+        inOrder.verify(transitionFinishedCallback).onResult(TransitionType.SWAPPING);
         inOrder.verify(transitionStartedCallback).onResult(TransitionType.EXPANDING_ACTION_CHIP);
         inOrder.verify(transitionFinishedCallback).onResult(TransitionType.EXPANDING_ACTION_CHIP);
         inOrder.verify(transitionStartedCallback).onResult(TransitionType.COLLAPSING_ACTION_CHIP);
@@ -433,8 +449,8 @@
     @Test
     public void testTransitionCallbacks_withAnimationDisabled() {
         ButtonData firstButton = getDataForStaticNewTabIconButton();
-        ButtonData secondButton = getDataForDynamicPriceTrackingIconButton();
-        ButtonData actionChipButton = getDataForShareIconActionChip();
+        ButtonData secondButton = getDataForPriceTrackingIconButton();
+        ButtonData actionChipButton = getDataForPriceTrackingActionChip();
         when(mMockAnimationChecker.getAsBoolean()).thenReturn(false);
 
         Runnable beforeHideTransitionCallback = mock(Runnable.class);
@@ -461,6 +477,11 @@
         mOptionalButtonView.onTransitionStart(null);
         mOptionalButtonView.onTransitionEnd(null);
 
+        // Transition back to firstButton.
+        mOptionalButtonView.updateButtonWithAnimation(firstButton);
+        mOptionalButtonView.onTransitionStart(null);
+        mOptionalButtonView.onTransitionEnd(null);
+
         // Transition from secondButton to actionChipButton, when animations are disabled we don't
         // expand the action chip, as the width change would look jarring. Instead we just update
         // its icon.
@@ -482,8 +503,89 @@
         inOrder.verify(transitionFinishedCallback).onResult(TransitionType.SHOWING);
         inOrder.verify(transitionStartedCallback).onResult(TransitionType.SHOWING);
         inOrder.verify(transitionFinishedCallback).onResult(TransitionType.SHOWING);
+        inOrder.verify(transitionStartedCallback).onResult(TransitionType.SHOWING);
+        inOrder.verify(transitionFinishedCallback).onResult(TransitionType.SHOWING);
         inOrder.verify(beforeHideTransitionCallback).run();
         inOrder.verify(transitionStartedCallback).onResult(TransitionType.HIDING);
         inOrder.verify(transitionFinishedCallback).onResult(TransitionType.HIDING);
     }
+
+    @Test
+    public void testUpdateButton_earlyReturnIfNothingChanged() {
+        ButtonData firstButton = getDataForStaticNewTabIconButton();
+
+        ViewGroup transitionRoot = mock(ViewGroup.class);
+        mOptionalButtonView.setTransitionRoot(transitionRoot);
+
+        mOptionalButtonView.updateButtonWithAnimation(firstButton);
+        mOptionalButtonView.onTransitionStart(null);
+        mOptionalButtonView.onTransitionEnd(null);
+
+        mOptionalButtonView.updateButtonWithAnimation(firstButton);
+
+        // Calling updateButtonWithAnimation with the same button many times shouldn't begin
+        // repeated animations.
+        verify(mMockBeginDelayedTransition).onResult(any());
+    }
+
+    @Test
+    public void testUpdateButton_earlyReturnIfSameVariant() {
+        ButtonData priceTrackingButtonData = getDataForPriceTrackingIconButton();
+        ButtonData priceTrackingActionChipData = getDataForPriceTrackingActionChip();
+
+        ViewGroup transitionRoot = mock(ViewGroup.class);
+        mOptionalButtonView.setTransitionRoot(transitionRoot);
+
+        mOptionalButtonView.updateButtonWithAnimation(priceTrackingButtonData);
+        mOptionalButtonView.onTransitionStart(null);
+        mOptionalButtonView.onTransitionEnd(null);
+
+        mOptionalButtonView.updateButtonWithAnimation(priceTrackingActionChipData);
+
+        // Calling updateButtonWithAnimation with the same button variant many times shouldn't begin
+        // repeated animations.
+        verify(mMockBeginDelayedTransition).onResult(any());
+    }
+
+    @Test
+    public void testUpdateButton_sameButtonWithDifferentSpecTriggersTransition() {
+        ButtonDataImpl buttonData = getDataForStaticNewTabIconButton();
+
+        ViewGroup transitionRoot = mock(ViewGroup.class);
+        mOptionalButtonView.setTransitionRoot(transitionRoot);
+
+        mOptionalButtonView.updateButtonWithAnimation(buttonData);
+        mOptionalButtonView.onTransitionStart(null);
+        mOptionalButtonView.onTransitionEnd(null);
+
+        // Keep the same ButtonData instance, but use a different spec.
+        buttonData.setButtonSpec(getDataForPriceTrackingIconButton().getButtonSpec());
+
+        mOptionalButtonView.updateButtonWithAnimation(buttonData);
+
+        // Calling updateButtonWithAnimation with the same ButtonData instance but with a different
+        // variant many times should begin a new animation.
+        verify(mMockBeginDelayedTransition, times(2)).onResult(any());
+    }
+
+    @Test
+    public void testUpdateButton_sameButtonWithDifferentVisibilityTriggersTransition() {
+        ButtonDataImpl buttonData = getDataForStaticNewTabIconButton();
+
+        ViewGroup transitionRoot = mock(ViewGroup.class);
+        mOptionalButtonView.setTransitionRoot(transitionRoot);
+
+        mOptionalButtonView.updateButtonWithAnimation(buttonData);
+        mOptionalButtonView.onTransitionStart(null);
+        mOptionalButtonView.onTransitionEnd(null);
+
+        // Keep the same ButtonData instance, but set it to not show.
+        buttonData.setCanShow(false);
+
+        mOptionalButtonView.updateButtonWithAnimation(buttonData);
+
+        // Calling updateButtonWithAnimation with the same ButtonData instance but with a different
+        // visibility many times should begin a new animation.
+        verify(mMockBeginDelayedTransition, times(2)).onResult(any());
+    }
 }
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarControlContainerTest.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarControlContainerTest.java
index 9d0c379..77d8c23 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarControlContainerTest.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarControlContainerTest.java
@@ -17,7 +17,8 @@
 import org.mockito.junit.MockitoRule;
 import org.robolectric.annotation.Config;
 
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.browser.toolbar.top.CaptureReadinessResult.TopToolbarAllowCaptureReason;
 import org.chromium.chrome.browser.toolbar.top.CaptureReadinessResult.TopToolbarBlockCaptureReason;
@@ -26,8 +27,7 @@
 
 /** Unit tests for ToolbarControlContainer. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(shadows = {HomeButtonCoordinatorTest.ShadowChromeFeatureList.class,
-                ShadowRecordHistogram.class})
+@Config(shadows = {HomeButtonCoordinatorTest.ShadowChromeFeatureList.class})
 public class ToolbarControlContainerTest {
     @Rule
     public MockitoRule rule = MockitoJUnit.rule();
@@ -39,7 +39,7 @@
 
     @Before
     public void before() {
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
         Mockito.when(mToolbarContainer.getWidth()).thenReturn(1);
         Mockito.when(mToolbarContainer.getHeight()).thenReturn(1);
     }
@@ -51,25 +51,25 @@
         adapter.setOnResourceReadyCallback((resource) -> {});
 
         Assert.assertEquals(0,
-                ShadowRecordHistogram.getHistogramTotalCountForTesting(
+                RecordHistogram.getHistogramTotalCountForTesting(
                         "Android.TopToolbar.BlockCaptureReason"));
         Assert.assertEquals(0,
-                ShadowRecordHistogram.getHistogramTotalCountForTesting(
+                RecordHistogram.getHistogramTotalCountForTesting(
                         "Android.TopToolbar.AllowCaptureReason"));
         Assert.assertEquals(0,
-                ShadowRecordHistogram.getHistogramTotalCountForTesting(
+                RecordHistogram.getHistogramTotalCountForTesting(
                         "Android.TopToolbar.SnapshotDifference"));
 
         Assert.assertFalse(adapter.isDirty());
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.TopToolbar.BlockCaptureReason",
                         TopToolbarBlockCaptureReason.TOOLBAR_OR_RESULT_NULL));
 
         adapter.setToolbar(mToolbar);
         Assert.assertFalse(adapter.isDirty());
         Assert.assertEquals(2,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.TopToolbar.BlockCaptureReason",
                         TopToolbarBlockCaptureReason.TOOLBAR_OR_RESULT_NULL));
 
@@ -77,32 +77,32 @@
                 .thenReturn(CaptureReadinessResult.unknown(true));
         Assert.assertTrue(adapter.isDirty());
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.TopToolbar.AllowCaptureReason",
                         TopToolbarBlockCaptureReason.UNKNOWN));
 
         adapter.triggerBitmapCapture();
         Assert.assertFalse(adapter.isDirty());
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.TopToolbar.BlockCaptureReason",
                         TopToolbarBlockCaptureReason.VIEW_NOT_DIRTY));
         Assert.assertEquals(0,
-                ShadowRecordHistogram.getHistogramTotalCountForTesting(
+                RecordHistogram.getHistogramTotalCountForTesting(
                         "Android.TopToolbar.SnapshotDifference"));
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.TopToolbar.AllowCaptureReason",
                         TopToolbarBlockCaptureReason.UNKNOWN));
 
         adapter.forceInvalidate();
         Assert.assertTrue(adapter.isDirty());
         Assert.assertEquals(2,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.TopToolbar.AllowCaptureReason",
                         TopToolbarBlockCaptureReason.UNKNOWN));
         Assert.assertEquals(0,
-                ShadowRecordHistogram.getHistogramTotalCountForTesting(
+                RecordHistogram.getHistogramTotalCountForTesting(
                         "Android.TopToolbar.SnapshotDifference"));
     }
 
@@ -116,7 +116,7 @@
                         TopToolbarBlockCaptureReason.SNAPSHOT_SAME));
         Assert.assertFalse(adapter.isDirty());
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.TopToolbar.BlockCaptureReason",
                         TopToolbarBlockCaptureReason.SNAPSHOT_SAME));
     }
@@ -130,11 +130,11 @@
                 .thenReturn(CaptureReadinessResult.readyForced());
         Assert.assertTrue(adapter.isDirty());
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.TopToolbar.AllowCaptureReason",
                         TopToolbarAllowCaptureReason.FORCE_CAPTURE));
         Assert.assertEquals(0,
-                ShadowRecordHistogram.getHistogramTotalCountForTesting(
+                RecordHistogram.getHistogramTotalCountForTesting(
                         "Android.TopToolbar.SnapshotDifference"));
     }
 
@@ -148,12 +148,12 @@
                         ToolbarSnapshotDifference.URL_TEXT));
         Assert.assertTrue(adapter.isDirty());
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.TopToolbar.AllowCaptureReason",
                         TopToolbarAllowCaptureReason.SNAPSHOT_DIFFERENCE));
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.TopToolbar.SnapshotDifference",
                         ToolbarSnapshotDifference.URL_TEXT));
     }
-}
\ No newline at end of file
+}
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java
index 73c5aa9..45f05e78 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java
@@ -2545,6 +2545,9 @@
             mOptionalButton = new OptionalButtonCoordinator(optionalButton, userEducationHelper,
                     /* transitionRoot= */ this, isAnimationAllowedPredicate);
 
+            // Set the button's background to the same color as the URL bar background. This color
+            // is only used when showing dynamic actions.
+            mOptionalButton.setBackgroundColorFilter(mCurrentLocationBarColor);
             mOptionalButton.setOnBeforeHideTransitionCallback(
                     () -> mLayoutLocationBarWithoutExtraButton = true);
 
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTablet.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTablet.java
index 8f70e11..a7bdba7e 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTablet.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTablet.java
@@ -31,6 +31,7 @@
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.base.supplier.BooleanSupplier;
 import org.chromium.base.supplier.ObservableSupplier;
+import org.chromium.chrome.browser.flags.BooleanCachedFieldTrialParameter;
 import org.chromium.chrome.browser.flags.CachedFeatureFlags;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.omnibox.LocationBar;
@@ -839,8 +840,9 @@
     }
 
     private boolean isTabletGridTabSwitcherPolishEnabled() {
-        return ChromeFeatureList.getFieldTrialParamByFeatureAsBoolean(
-                ChromeFeatureList.GRID_TAB_SWITCHER_FOR_TABLETS, "enable_launch_polish", false);
+        return new BooleanCachedFieldTrialParameter(
+                ChromeFeatureList.GRID_TAB_SWITCHER_FOR_TABLETS, "enable_launch_polish", false)
+                .getValue();
     }
 
     @VisibleForTesting
diff --git a/chrome/browser/ui/app_list/search/files/item_suggest_cache.cc b/chrome/browser/ui/app_list/search/files/item_suggest_cache.cc
index 9b59e0f..2303e180 100644
--- a/chrome/browser/ui/app_list/search/files/item_suggest_cache.cc
+++ b/chrome/browser/ui/app_list/search/files/item_suggest_cache.cc
@@ -352,7 +352,7 @@
     data_decoder::DataDecoder::ValueOrError result) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  if (!result.value) {
+  if (!result.has_value()) {
     LogStatus(Status::kJsonParseFailure);
     return;
   }
@@ -360,7 +360,7 @@
   // Convert the JSON value into a Results object. If the conversion fails, or
   // if the conversion contains no results, we shouldn't update the stored
   // results.
-  const auto& results = ConvertResults(&result.value.value());
+  const auto& results = ConvertResults(&*result);
   if (!results) {
     LogStatus(Status::kJsonConversionFailure);
   } else if (results->results.empty()) {
diff --git a/chrome/browser/ui/app_list/search/search_result_ranker/recurrence_ranker_util.cc b/chrome/browser/ui/app_list/search/search_result_ranker/recurrence_ranker_util.cc
index 590baac..34e4292 100644
--- a/chrome/browser/ui/app_list/search/search_result_ranker/recurrence_ranker_util.cc
+++ b/chrome/browser/ui/app_list/search/search_result_ranker/recurrence_ranker_util.cc
@@ -254,7 +254,7 @@
     const std::string& model_identifier,
     data_decoder::DataDecoder::ValueOrError result) {
   RecurrenceRankerConfigProto proto;
-  if (result.value && ConvertRecurrenceRanker(&result.value.value(), &proto)) {
+  if (result.has_value() && ConvertRecurrenceRanker(&*result, &proto)) {
     std::move(callback).Run(std::move(proto));
   } else {
     std::move(callback).Run(absl::nullopt);
diff --git a/chrome/browser/ui/ash/assistant/search_and_assistant_enabled_checker.cc b/chrome/browser/ui/ash/assistant/search_and_assistant_enabled_checker.cc
index a9bbf2a..59c92894 100644
--- a/chrome/browser/ui/ash/assistant/search_and_assistant_enabled_checker.cc
+++ b/chrome/browser/ui/ash/assistant/search_and_assistant_enabled_checker.cc
@@ -83,14 +83,14 @@
 
 void SearchAndAssistantEnabledChecker::OnJsonParsed(
     data_decoder::DataDecoder::ValueOrError response) {
-  if (!response.value) {
-    LOG(ERROR) << "JSON parsing failed: " << *response.error;
+  if (!response.has_value()) {
+    LOG(ERROR) << "JSON parsing failed: " << response.error();
     delegate_->OnError();
     return;
   }
 
   // |result| is true if the Search and Assistant bit is disabled.
-  auto is_disabled = response.value->FindBoolPath("result");
+  auto is_disabled = response->FindBoolPath("result");
 
   delegate_->OnSearchAndAssistantStateReceived(is_disabled.value());
 }
diff --git a/chrome/browser/ui/ash/keyboard_shortcut_viewer_metadata_unittest.cc b/chrome/browser/ui/ash/keyboard_shortcut_viewer_metadata_unittest.cc
index d8354be..3f405b8e 100644
--- a/chrome/browser/ui/ash/keyboard_shortcut_viewer_metadata_unittest.cc
+++ b/chrome/browser/ui/ash/keyboard_shortcut_viewer_metadata_unittest.cc
@@ -24,9 +24,9 @@
 namespace {
 
 // The total number of Ash accelerators.
-constexpr int kAshAcceleratorsTotalNum = 141;
+constexpr int kAshAcceleratorsTotalNum = 142;
 // The hash of Ash accelerators.
-constexpr char kAshAcceleratorsHash[] = "a6012851a03e0b0a1653b9a066185d3c";
+constexpr char kAshAcceleratorsHash[] = "3e10b263d3b80fcbd36b5e4ca77bf61f";
 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
 // Internal builds add an extra accelerator for the Feedback app.
 // The total number of Chrome accelerators (available on Chrome OS).
diff --git a/chrome/browser/ui/ash/thumbnail_loader.cc b/chrome/browser/ui/ash/thumbnail_loader.cc
index 732641f0..ed732aa5 100644
--- a/chrome/browser/ui/ash/thumbnail_loader.cc
+++ b/chrome/browser/ui/ash/thumbnail_loader.cc
@@ -185,21 +185,21 @@
     const std::string& request_id,
     ThumbnailDataCallback callback,
     data_decoder::DataDecoder::ValueOrError result) {
-  if (!result.value) {
-    VLOG(2) << "Failed to parse request response " << *result.error;
+  if (!result.has_value()) {
+    VLOG(2) << "Failed to parse request response " << result.error();
     std::move(callback).Run("");
     return;
   }
 
-  if (!result.value->is_dict()) {
+  if (!result->is_dict()) {
     VLOG(2) << "Invalid response format";
     std::move(callback).Run("");
     return;
   }
 
   const std::string* received_request_id =
-      result.value->GetDict().FindString("taskId");
-  const std::string* data = result.value->GetDict().FindString("data");
+      result->GetDict().FindString("taskId");
+  const std::string* data = result->GetDict().FindString("data");
 
   if (!data || !received_request_id || *received_request_id != request_id) {
     std::move(callback).Run("");
diff --git a/chrome/browser/ui/autofill/payments/autofill_progress_dialog_controller_impl.cc b/chrome/browser/ui/autofill/payments/autofill_progress_dialog_controller_impl.cc
index 0c2082da..e8fbb3d 100644
--- a/chrome/browser/ui/autofill/payments/autofill_progress_dialog_controller_impl.cc
+++ b/chrome/browser/ui/autofill/payments/autofill_progress_dialog_controller_impl.cc
@@ -35,9 +35,7 @@
       AutofillProgressDialogView::CreateAndShow(this);
 
   if (autofill_progress_dialog_view_)
-    // TODO(crbug.com/1261529): Pass in the flow type once we have another use
-    // case so that the progress dialog can be shared across multiple use cases.
-    AutofillMetrics::LogProgressDialogShown();
+    AutofillMetrics::LogProgressDialogShown(autofill_progress_dialog_type);
 }
 
 void AutofillProgressDialogControllerImpl::DismissDialog(
@@ -54,12 +52,11 @@
     bool is_canceled_by_user) {
   // Dialog is being dismissed so set the pointer to nullptr.
   autofill_progress_dialog_view_ = nullptr;
-  autofill_progress_dialog_type_ = AutofillProgressDialogType::kUnspecified;
   if (is_canceled_by_user)
     std::move(cancel_callback_).Run();
-  // TODO(crbug.com/1261529): Pass in the flow type once we have another use
-  // case so that the progress dialog can be shared across multiple use cases.
-  AutofillMetrics::LogProgressDialogResultMetric(is_canceled_by_user);
+  AutofillMetrics::LogProgressDialogResultMetric(
+      is_canceled_by_user, autofill_progress_dialog_type_);
+  autofill_progress_dialog_type_ = AutofillProgressDialogType::kUnspecified;
   cancel_callback_.Reset();
 }
 
diff --git a/chrome/browser/ui/autofill/payments/autofill_progress_dialog_controller_impl_unittest.cc b/chrome/browser/ui/autofill/payments/autofill_progress_dialog_controller_impl_unittest.cc
new file mode 100644
index 0000000..5f088b26
--- /dev/null
+++ b/chrome/browser/ui/autofill/payments/autofill_progress_dialog_controller_impl_unittest.cc
@@ -0,0 +1,67 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/autofill/payments/autofill_progress_dialog_controller_impl.h"
+
+#include "base/test/metrics/histogram_tester.h"
+#include "chrome/browser/ui/autofill/payments/autofill_progress_dialog_controller.h"
+#include "chrome/browser/ui/autofill/payments/autofill_progress_dialog_view.h"
+#include "chrome/test/base/chrome_render_view_host_test_harness.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace autofill {
+
+class AutofillProgressDialogControllerImplTest
+    : public ChromeRenderViewHostTestHarness {
+ public:
+  AutofillProgressDialogControllerImplTest() = default;
+};
+
+TEST_F(AutofillProgressDialogControllerImplTest,
+       ShowDialogWithConfirmationTest) {
+  base::HistogramTester histogram_tester;
+  AutofillProgressDialogControllerImpl controller(web_contents());
+
+  controller.ShowDialog(AutofillProgressDialogType::kAndroidFIDOProgressDialog,
+                        base::DoNothing());
+  histogram_tester.ExpectUniqueSample(
+      "Autofill.ProgressDialog.AndroidFIDO.Shown", true, 1);
+
+  controller.DismissDialog(/*show_confirmation_before_closing=*/true);
+  histogram_tester.ExpectUniqueSample(
+      "Autofill.ProgressDialog.AndroidFIDO.Result", false, 1);
+}
+
+TEST_F(AutofillProgressDialogControllerImplTest,
+       ShowDialogWithoutConfirmationTest) {
+  base::HistogramTester histogram_tester;
+  AutofillProgressDialogControllerImpl controller(web_contents());
+
+  controller.ShowDialog(AutofillProgressDialogType::kAndroidFIDOProgressDialog,
+                        base::DoNothing());
+  histogram_tester.ExpectUniqueSample(
+      "Autofill.ProgressDialog.AndroidFIDO.Shown", true, 1);
+
+  controller.DismissDialog(/*show_confirmation_before_closing=*/false);
+  histogram_tester.ExpectUniqueSample(
+      "Autofill.ProgressDialog.AndroidFIDO.Result", false, 1);
+}
+
+TEST_F(AutofillProgressDialogControllerImplTest,
+       ShowDialogAndCancelledByUserTest) {
+  base::HistogramTester histogram_tester;
+  AutofillProgressDialogControllerImpl controller(web_contents());
+
+  controller.ShowDialog(AutofillProgressDialogType::kAndroidFIDOProgressDialog,
+                        base::DoNothing());
+  histogram_tester.ExpectUniqueSample(
+      "Autofill.ProgressDialog.AndroidFIDO.Shown", true, 1);
+
+  controller.OnDismissed(/*is_canceled_by_user=*/true);
+  histogram_tester.ExpectUniqueSample(
+      "Autofill.ProgressDialog.AndroidFIDO.Result", true, 1);
+}
+
+}  // namespace autofill
diff --git a/chrome/browser/ui/browser_element_identifiers.cc b/chrome/browser/ui/browser_element_identifiers.cc
index a5c93ec..798cc53 100644
--- a/chrome/browser/ui/browser_element_identifiers.cc
+++ b/chrome/browser/ui/browser_element_identifiers.cc
@@ -18,6 +18,7 @@
 DEFINE_ELEMENT_IDENTIFIER_VALUE(kOmniboxElementId);
 DEFINE_ELEMENT_IDENTIFIER_VALUE(kReadLaterButtonElementId);
 DEFINE_ELEMENT_IDENTIFIER_VALUE(kReadLaterSidePanelWebViewElementId);
+DEFINE_ELEMENT_IDENTIFIER_VALUE(kSidePanelCloseButtonElementId);
 DEFINE_ELEMENT_IDENTIFIER_VALUE(kSavePasswordComboboxElementId);
 DEFINE_ELEMENT_IDENTIFIER_VALUE(kSideSearchButtonElementId);
 DEFINE_ELEMENT_IDENTIFIER_VALUE(kTabAlertIndicatorButtonElementId);
diff --git a/chrome/browser/ui/browser_element_identifiers.h b/chrome/browser/ui/browser_element_identifiers.h
index f13a2b3..6a8d99a5 100644
--- a/chrome/browser/ui/browser_element_identifiers.h
+++ b/chrome/browser/ui/browser_element_identifiers.h
@@ -27,6 +27,7 @@
 DECLARE_ELEMENT_IDENTIFIER_VALUE(kOmniboxElementId);
 DECLARE_ELEMENT_IDENTIFIER_VALUE(kReadLaterButtonElementId);
 DECLARE_ELEMENT_IDENTIFIER_VALUE(kReadLaterSidePanelWebViewElementId);
+DECLARE_ELEMENT_IDENTIFIER_VALUE(kSidePanelCloseButtonElementId);
 DECLARE_ELEMENT_IDENTIFIER_VALUE(kSavePasswordComboboxElementId);
 DECLARE_ELEMENT_IDENTIFIER_VALUE(kSideSearchButtonElementId);
 DECLARE_ELEMENT_IDENTIFIER_VALUE(kTabAlertIndicatorButtonElementId);
diff --git a/chrome/browser/ui/extensions/extension_image_util_browsertest.cc b/chrome/browser/ui/extensions/extension_image_util_browsertest.cc
index 5f125bb..af626e8 100644
--- a/chrome/browser/ui/extensions/extension_image_util_browsertest.cc
+++ b/chrome/browser/ui/extensions/extension_image_util_browsertest.cc
@@ -13,7 +13,7 @@
 #include "ui/native_theme/native_theme.h"
 
 #if BUILDFLAG(USE_GTK)
-#include "ui/views/linux_ui/linux_ui.h"
+#include "ui/linux/linux_ui.h"
 #endif
 
 namespace {
@@ -33,7 +33,7 @@
   // This test relies on being run with the default light mode system theme.
   ui::NativeTheme::GetInstanceForNativeUi()->set_use_dark_colors(false);
 #if BUILDFLAG(USE_GTK)
-  views::LinuxUI::instance()->SetUseSystemThemeCallback(
+  ui::LinuxUi::instance()->SetUseSystemThemeCallback(
       base::BindRepeating([](aura::Window* window) { return false; }));
 #endif  // BUILDFLAG(USE_GTK)
   ui::NativeTheme::GetInstanceForNativeUi()->NotifyOnNativeThemeUpdated();
diff --git a/chrome/browser/ui/views/chrome_browser_main_extra_parts_views_linux.cc b/chrome/browser/ui/views/chrome_browser_main_extra_parts_views_linux.cc
index 246591230..5693345 100644
--- a/chrome/browser/ui/views/chrome_browser_main_extra_parts_views_linux.cc
+++ b/chrome/browser/ui/views/chrome_browser_main_extra_parts_views_linux.cc
@@ -12,19 +12,19 @@
 #include "ui/base/ime/input_method.h"
 #include "ui/base/ime/linux/fake_input_method_context_factory.h"
 #include "ui/base/linux/linux_ui_delegate.h"
+#include "ui/linux/linux_ui.h"
+#include "ui/linux/linux_ui_factory.h"
 #include "ui/ozone/public/ozone_platform.h"
-#include "ui/views/linux_ui/linux_ui.h"
-#include "ui/views/linux_ui/linux_ui_factory.h"
 
 namespace {
 
-std::unique_ptr<views::LinuxUI> BuildLinuxUI() {
+std::unique_ptr<ui::LinuxUi> BuildLinuxUI() {
   // If the ozone backend hasn't provided a LinuxUiDelegate, don't try to create
   // a LinuxUi instance as this may result in a crash in toolkit initialization.
   if (!ui::LinuxUiDelegate::GetInstance())
     return nullptr;
 
-  return CreateLinuxUi();
+  return ui::CreateLinuxUi();
 }
 
 }  // namespace
@@ -46,7 +46,7 @@
           return ThemeServiceAuraLinux::ShouldUseSystemThemeForProfile(
               GetThemeProfileForWindow(window));
         }));
-    views::LinuxUI::SetInstance(std::move(linux_ui));
+    ui::LinuxUi::SetInstance(std::move(linux_ui));
 
     // Cursor theme changes are tracked by LinuxUI (via a CursorThemeManager
     // implementation). Start observing them once it's initialized.
diff --git a/chrome/browser/ui/views/chrome_views_delegate_linux.cc b/chrome/browser/ui/views/chrome_views_delegate_linux.cc
index cdca5c5..9afa7ba 100644
--- a/chrome/browser/ui/views/chrome_views_delegate_linux.cc
+++ b/chrome/browser/ui/views/chrome_views_delegate_linux.cc
@@ -12,7 +12,7 @@
 #include "chrome/grit/chrome_unscaled_resources.h"
 #include "components/version_info/channel.h"
 #include "ui/base/resource/resource_bundle.h"
-#include "ui/views/linux_ui/linux_ui.h"
+#include "ui/linux/linux_ui.h"
 
 namespace {
 
diff --git a/chrome/browser/ui/views/frame/browser_desktop_window_tree_host_linux.cc b/chrome/browser/ui/views/frame/browser_desktop_window_tree_host_linux.cc
index 696e49d..65b4780 100644
--- a/chrome/browser/ui/views/frame/browser_desktop_window_tree_host_linux.cc
+++ b/chrome/browser/ui/views/frame/browser_desktop_window_tree_host_linux.cc
@@ -62,7 +62,7 @@
                                     : views::Widget::FrameType::kForceNative);
 
   theme_observation_.Observe(ui::NativeTheme::GetInstanceForNativeUi());
-  if (auto* linux_ui = views::LinuxUI::instance())
+  if (auto* linux_ui = ui::LinuxUi::instance())
     scale_observation_.Observe(linux_ui);
 }
 
diff --git a/chrome/browser/ui/views/frame/browser_desktop_window_tree_host_linux.h b/chrome/browser/ui/views/frame/browser_desktop_window_tree_host_linux.h
index a24d1f0..17704d2 100644
--- a/chrome/browser/ui/views/frame/browser_desktop_window_tree_host_linux.h
+++ b/chrome/browser/ui/views/frame/browser_desktop_window_tree_host_linux.h
@@ -8,8 +8,8 @@
 #include "base/memory/raw_ptr.h"
 #include "build/build_config.h"
 #include "chrome/browser/ui/views/frame/browser_desktop_window_tree_host.h"
-#include "ui/views/linux_ui/device_scale_factor_observer.h"
-#include "ui/views/linux_ui/linux_ui.h"
+#include "ui/linux/device_scale_factor_observer.h"
+#include "ui/linux/linux_ui.h"
 #include "ui/views/widget/desktop_aura/desktop_window_tree_host_linux.h"  // nogncheck
 
 #if defined(USE_DBUS_MENU)
@@ -29,7 +29,7 @@
     : public BrowserDesktopWindowTreeHost,
       public views::DesktopWindowTreeHostLinux,
       ui::NativeThemeObserver,
-      views::DeviceScaleFactorObserver {
+      ui::DeviceScaleFactorObserver {
  public:
   BrowserDesktopWindowTreeHostLinux(
       views::internal::NativeWidgetDelegate* native_widget_delegate,
@@ -102,10 +102,10 @@
 
   base::ScopedObservation<ui::NativeTheme, ui::NativeThemeObserver>
       theme_observation_{this};
-  base::ScopedObservation<views::LinuxUI,
-                          views::DeviceScaleFactorObserver,
-                          &views::LinuxUI::AddDeviceScaleFactorObserver,
-                          &views::LinuxUI::RemoveDeviceScaleFactorObserver>
+  base::ScopedObservation<ui::LinuxUi,
+                          ui::DeviceScaleFactorObserver,
+                          &ui::LinuxUi::AddDeviceScaleFactorObserver,
+                          &ui::LinuxUi::RemoveDeviceScaleFactorObserver>
       scale_observation_{this};
 };
 
diff --git a/chrome/browser/ui/views/frame/browser_frame.cc b/chrome/browser/ui/views/frame/browser_frame.cc
index 95d3053..f3cc4af3 100644
--- a/chrome/browser/ui/views/frame/browser_frame.cc
+++ b/chrome/browser/ui/views/frame/browser_frame.cc
@@ -54,7 +54,7 @@
 #endif
 
 #if BUILDFLAG(IS_LINUX)
-#include "ui/views/linux_ui/linux_ui.h"
+#include "ui/linux/linux_ui.h"
 #endif
 
 namespace {
@@ -403,7 +403,7 @@
   }
 
 #if BUILDFLAG(IS_LINUX)
-  const views::LinuxUI* linux_ui = views::LinuxUI::instance();
+  const ui::LinuxUi* linux_ui = ui::LinuxUi::instance();
   // Ignore GTK+ for web apps with window-controls-overlay as the
   // display_override so the web contents can blend with the overlay by using
   // the developer-provided theme color for a better experience. Context:
diff --git a/chrome/browser/ui/views/frame/browser_frame_view_layout_linux_native.cc b/chrome/browser/ui/views/frame/browser_frame_view_layout_linux_native.cc
index 1550173a..265a3c7 100644
--- a/chrome/browser/ui/views/frame/browser_frame_view_layout_linux_native.cc
+++ b/chrome/browser/ui/views/frame/browser_frame_view_layout_linux_native.cc
@@ -4,11 +4,11 @@
 
 #include "chrome/browser/ui/views/frame/browser_frame_view_layout_linux_native.h"
 
-#include "ui/views/linux_ui/nav_button_provider.h"
+#include "ui/linux/nav_button_provider.h"
 
 BrowserFrameViewLayoutLinuxNative::BrowserFrameViewLayoutLinuxNative(
-    views::NavButtonProvider* nav_button_provider,
-    views::WindowFrameProvider* window_frame_provider)
+    ui::NavButtonProvider* nav_button_provider,
+    ui::WindowFrameProvider* window_frame_provider)
     : nav_button_provider_(nav_button_provider),
       window_frame_provider_(window_frame_provider) {}
 
@@ -54,20 +54,20 @@
   return spacing;
 }
 
-views::NavButtonProvider::FrameButtonDisplayType
+ui::NavButtonProvider::FrameButtonDisplayType
 BrowserFrameViewLayoutLinuxNative::GetButtonDisplayType(
     views::FrameButton button_id) const {
   switch (button_id) {
     case views::FrameButton::kMinimize:
-      return views::NavButtonProvider::FrameButtonDisplayType::kMinimize;
+      return ui::NavButtonProvider::FrameButtonDisplayType::kMinimize;
     case views::FrameButton::kMaximize:
       return delegate_->IsMaximized()
-                 ? views::NavButtonProvider::FrameButtonDisplayType::kRestore
-                 : views::NavButtonProvider::FrameButtonDisplayType::kMaximize;
+                 ? ui::NavButtonProvider::FrameButtonDisplayType::kRestore
+                 : ui::NavButtonProvider::FrameButtonDisplayType::kMaximize;
     case views::FrameButton::kClose:
-      return views::NavButtonProvider::FrameButtonDisplayType::kClose;
+      return ui::NavButtonProvider::FrameButtonDisplayType::kClose;
     default:
       NOTREACHED();
-      return views::NavButtonProvider::FrameButtonDisplayType::kClose;
+      return ui::NavButtonProvider::FrameButtonDisplayType::kClose;
   }
 }
diff --git a/chrome/browser/ui/views/frame/browser_frame_view_layout_linux_native.h b/chrome/browser/ui/views/frame/browser_frame_view_layout_linux_native.h
index b98c6a4..ca0c76f 100644
--- a/chrome/browser/ui/views/frame/browser_frame_view_layout_linux_native.h
+++ b/chrome/browser/ui/views/frame/browser_frame_view_layout_linux_native.h
@@ -7,16 +7,16 @@
 
 #include "base/memory/raw_ptr.h"
 #include "chrome/browser/ui/views/frame/browser_frame_view_layout_linux.h"
-#include "ui/views/linux_ui/nav_button_provider.h"
-#include "ui/views/linux_ui/window_frame_provider.h"
+#include "ui/linux/nav_button_provider.h"
+#include "ui/linux/window_frame_provider.h"
 
 // A specialization of BrowserFrameViewLayoutLinux that is also able
 // to layout frame buttons that were rendered by the native toolkit.
 class BrowserFrameViewLayoutLinuxNative : public BrowserFrameViewLayoutLinux {
  public:
   explicit BrowserFrameViewLayoutLinuxNative(
-      views::NavButtonProvider* nav_button_provider,
-      views::WindowFrameProvider* window_frame_provider);
+      ui::NavButtonProvider* nav_button_provider,
+      ui::WindowFrameProvider* window_frame_provider);
 
   BrowserFrameViewLayoutLinuxNative(const BrowserFrameViewLayoutLinuxNative&) =
       delete;
@@ -39,12 +39,12 @@
  private:
   // Converts a FrameButton to a FrameButtonDisplayType, taking into
   // consideration the maximized state of the browser window.
-  views::NavButtonProvider::FrameButtonDisplayType GetButtonDisplayType(
+  ui::NavButtonProvider::FrameButtonDisplayType GetButtonDisplayType(
       views::FrameButton button_id) const;
 
   // Owned by BrowserFrameViewLinuxNative.
-  const raw_ptr<views::NavButtonProvider> nav_button_provider_;
-  const raw_ptr<views::WindowFrameProvider> window_frame_provider_;
+  const raw_ptr<ui::NavButtonProvider> nav_button_provider_;
+  const raw_ptr<ui::WindowFrameProvider> window_frame_provider_;
 };
 
 #endif  // CHROME_BROWSER_UI_VIEWS_FRAME_BROWSER_FRAME_VIEW_LAYOUT_LINUX_NATIVE_H_
diff --git a/chrome/browser/ui/views/frame/browser_frame_view_layout_linux_native_unittest.cc b/chrome/browser/ui/views/frame/browser_frame_view_layout_linux_native_unittest.cc
index 057bc7f..d59f7fd 100644
--- a/chrome/browser/ui/views/frame/browser_frame_view_layout_linux_native_unittest.cc
+++ b/chrome/browser/ui/views/frame/browser_frame_view_layout_linux_native_unittest.cc
@@ -9,10 +9,10 @@
 
 #include "chrome/browser/ui/layout_constants.h"
 #include "chrome/test/views/chrome_views_test_base.h"
+#include "ui/linux/nav_button_provider.h"
+#include "ui/linux/window_frame_provider.h"
 #include "ui/views/background.h"
 #include "ui/views/controls/button/image_button.h"
-#include "ui/views/linux_ui/nav_button_provider.h"
-#include "ui/views/linux_ui/window_frame_provider.h"
 
 namespace {
 
@@ -78,7 +78,7 @@
   bool ShouldDrawRestoredFrameShadow() const override { return true; }
 };
 
-class TestNavButtonProvider : public views::NavButtonProvider {
+class TestNavButtonProvider : public ui::NavButtonProvider {
  public:
   TestNavButtonProvider() = default;
 
@@ -88,14 +88,15 @@
     ASSERT_EQ(false, maximized);  // This only tests the restored state.
   }
 
-  gfx::ImageSkia GetImage(views::NavButtonProvider::FrameButtonDisplayType type,
-                          views::Button::ButtonState state) const override {
+  gfx::ImageSkia GetImage(
+      ui::NavButtonProvider::FrameButtonDisplayType type,
+      ui::NavButtonProvider::ButtonState state) const override {
     switch (type) {
-      case views::NavButtonProvider::FrameButtonDisplayType::kClose:
+      case ui::NavButtonProvider::FrameButtonDisplayType::kClose:
         return GetTestImageForSize(kCloseButtonSize);
-      case views::NavButtonProvider::FrameButtonDisplayType::kMaximize:
+      case ui::NavButtonProvider::FrameButtonDisplayType::kMaximize:
         return GetTestImageForSize(kMaximizeButtonSize);
-      case views::NavButtonProvider::FrameButtonDisplayType::kMinimize:
+      case ui::NavButtonProvider::FrameButtonDisplayType::kMinimize:
         return GetTestImageForSize(kMinimizeButtonSize);
       default:
         NOTREACHED();
@@ -104,13 +105,13 @@
   }
 
   gfx::Insets GetNavButtonMargin(
-      views::NavButtonProvider::FrameButtonDisplayType type) const override {
+      ui::NavButtonProvider::FrameButtonDisplayType type) const override {
     switch (type) {
-      case views::NavButtonProvider::FrameButtonDisplayType::kClose:
+      case ui::NavButtonProvider::FrameButtonDisplayType::kClose:
         return kCloseButtonMargin;
-      case views::NavButtonProvider::FrameButtonDisplayType::kMaximize:
+      case ui::NavButtonProvider::FrameButtonDisplayType::kMaximize:
         return kMaximizeButtonMargin;
-      case views::NavButtonProvider::FrameButtonDisplayType::kMinimize:
+      case ui::NavButtonProvider::FrameButtonDisplayType::kMinimize:
         return kMinimizeButtonMargin;
       default:
         NOTREACHED();
@@ -125,13 +126,13 @@
   }
 };
 
-class TestFrameProvider : public views::WindowFrameProvider {
+class TestFrameProvider : public ui::WindowFrameProvider {
  public:
   TestFrameProvider() = default;
 
   ~TestFrameProvider() override = default;
 
-  // views::WindowFrameProvider:
+  // ui::WindowFrameProvider:
   int GetTopCornerRadiusDip() override { return 0; }
   gfx::Insets GetFrameThicknessDip() override { return {}; }
   void PaintWindowFrame(gfx::Canvas* canvas,
@@ -188,18 +189,34 @@
   }
 
   void ResetNativeNavButtonImagesFromButtonProvider() {
-    std::vector<views::ImageButton*> buttons{close_button_, maximize_button_,
-                                             minimize_button_};
-    std::vector<views::NavButtonProvider::FrameButtonDisplayType> button_types{
-        views::NavButtonProvider::FrameButtonDisplayType::kClose,
-        views::NavButtonProvider::FrameButtonDisplayType::kMaximize,
-        views::NavButtonProvider::FrameButtonDisplayType::kMinimize};
-    for (size_t i = 0; i < buttons.size(); i++) {
-      for (views::Button::ButtonState state :
-           {views::Button::STATE_NORMAL, views ::Button::STATE_HOVERED,
-            views::Button::STATE_PRESSED}) {
-        buttons[i]->SetImage(
-            state, nav_button_provider_->GetImage(button_types[i], state));
+    struct {
+      views::ImageButton* button;
+      ui::NavButtonProvider::FrameButtonDisplayType type;
+    } const kButtons[] = {
+        {minimize_button_,
+         ui::NavButtonProvider::FrameButtonDisplayType::kMinimize},
+        {maximize_button_,
+         ui::NavButtonProvider::FrameButtonDisplayType::kMaximize},
+        {close_button_, ui::NavButtonProvider::FrameButtonDisplayType::kClose},
+    };
+    struct {
+      views::Button::ButtonState button_state;
+      ui::NavButtonProvider::ButtonState nav_button_provider_state;
+    } const kStates[] = {
+        {views::Button::STATE_NORMAL,
+         ui::NavButtonProvider::ButtonState::kNormal},
+        {views::Button::STATE_HOVERED,
+         ui::NavButtonProvider::ButtonState::kHovered},
+        {views::Button::STATE_PRESSED,
+         ui::NavButtonProvider::ButtonState::kPressed},
+    };
+
+    for (const auto& button : kButtons) {
+      for (const auto& state : kStates) {
+        button.button->SetImage(
+            state.button_state,
+            nav_button_provider_->GetImage(button.type,
+                                           state.nav_button_provider_state));
       }
     }
   }
@@ -213,8 +230,8 @@
   raw_ptr<views::View> root_view_ = nullptr;
   raw_ptr<BrowserFrameViewLayoutLinuxNative> layout_manager_ = nullptr;
   std::unique_ptr<TestLayoutDelegate> delegate_;
-  std::unique_ptr<views::NavButtonProvider> nav_button_provider_;
-  std::unique_ptr<views::WindowFrameProvider> frame_provider_;
+  std::unique_ptr<ui::NavButtonProvider> nav_button_provider_;
+  std::unique_ptr<ui::WindowFrameProvider> frame_provider_;
 
   // Widgets:
   raw_ptr<views::ImageButton> minimize_button_ = nullptr;
diff --git a/chrome/browser/ui/views/frame/browser_frame_view_linux.cc b/chrome/browser/ui/views/frame/browser_frame_view_linux.cc
index 22648b0..53ba58b 100644
--- a/chrome/browser/ui/views/frame/browser_frame_view_linux.cc
+++ b/chrome/browser/ui/views/frame/browser_frame_view_linux.cc
@@ -21,14 +21,14 @@
     BrowserFrameViewLayoutLinux* layout)
     : OpaqueBrowserFrameView(frame, browser_view, layout), layout_(layout) {
   layout->set_view(this);
-  if (views::LinuxUI* ui = views::LinuxUI::instance()) {
+  if (ui::LinuxUi* ui = ui::LinuxUi::instance()) {
     ui->AddWindowButtonOrderObserver(this);
     OnWindowButtonOrderingChange();
   }
 }
 
 BrowserFrameViewLinux::~BrowserFrameViewLinux() {
-  if (views::LinuxUI* ui = views::LinuxUI::instance())
+  if (ui::LinuxUi* ui = ui::LinuxUi::instance())
     ui->RemoveWindowButtonOrderObserver(this);
 }
 
diff --git a/chrome/browser/ui/views/frame/browser_frame_view_linux.h b/chrome/browser/ui/views/frame/browser_frame_view_linux.h
index 1290cf2..c481452 100644
--- a/chrome/browser/ui/views/frame/browser_frame_view_linux.h
+++ b/chrome/browser/ui/views/frame/browser_frame_view_linux.h
@@ -8,12 +8,12 @@
 #include "base/memory/raw_ptr.h"
 #include "chrome/browser/ui/views/frame/browser_frame_view_layout_linux.h"
 #include "chrome/browser/ui/views/frame/opaque_browser_frame_view.h"
-#include "ui/views/linux_ui/window_button_order_observer.h"
+#include "ui/linux/window_button_order_observer.h"
 
 // A specialization of OpaqueBrowserFrameView that is also able to
 // render client side decorations (shadow, border, and rounded corners).
 class BrowserFrameViewLinux : public OpaqueBrowserFrameView,
-                              public views::WindowButtonOrderObserver {
+                              public ui::WindowButtonOrderObserver {
  public:
   BrowserFrameViewLinux(BrowserFrame* frame,
                         BrowserView* browser_view,
@@ -36,7 +36,7 @@
   static gfx::ShadowValues GetShadowValues();
 
  protected:
-  // views::WindowButtonOrderObserver:
+  // ui::WindowButtonOrderObserver:
   void OnWindowButtonOrderingChange() override;
 
   // views::View:
diff --git a/chrome/browser/ui/views/frame/browser_frame_view_linux_native.cc b/chrome/browser/ui/views/frame/browser_frame_view_linux_native.cc
index 26d449c..fedb981d 100644
--- a/chrome/browser/ui/views/frame/browser_frame_view_linux_native.cc
+++ b/chrome/browser/ui/views/frame/browser_frame_view_linux_native.cc
@@ -4,11 +4,35 @@
 
 #include "chrome/browser/ui/views/frame/browser_frame_view_linux_native.h"
 
+#include "base/notreached.h"
 #include "chrome/browser/ui/views/frame/browser_frame_view_layout_linux_native.h"
+#include "ui/linux/linux_ui.h"
 #include "ui/views/controls/button/image_button.h"
-#include "ui/views/linux_ui/linux_ui.h"
 #include "ui/views/window/frame_background.h"
 
+namespace {
+
+ui::NavButtonProvider::ButtonState ButtonStateToNavButtonProviderState(
+    views::Button::ButtonState state) {
+  switch (state) {
+    case views::Button::STATE_NORMAL:
+      return ui::NavButtonProvider::ButtonState::kNormal;
+    case views::Button::STATE_HOVERED:
+      return ui::NavButtonProvider::ButtonState::kHovered;
+    case views::Button::STATE_PRESSED:
+      return ui::NavButtonProvider::ButtonState::kPressed;
+    case views::Button::STATE_DISABLED:
+      return ui::NavButtonProvider::ButtonState::kDisabled;
+
+    case views::Button::STATE_COUNT:
+    default:
+      NOTREACHED();
+      return ui::NavButtonProvider::ButtonState::kNormal;
+  }
+}
+
+}  // namespace
+
 bool BrowserFrameViewLinuxNative::DrawFrameButtonParams::operator==(
     const DrawFrameButtonParams& other) const {
   return top_area_height == other.top_area_height &&
@@ -19,8 +43,8 @@
     BrowserFrame* frame,
     BrowserView* browser_view,
     BrowserFrameViewLayoutLinux* layout,
-    std::unique_ptr<views::NavButtonProvider> nav_button_provider,
-    views::WindowFrameProvider* window_frame_provider)
+    std::unique_ptr<ui::NavButtonProvider> nav_button_provider,
+    ui::WindowFrameProvider* window_frame_provider)
     : BrowserFrameViewLinux(frame, browser_view, layout),
       nav_button_provider_(std::move(nav_button_provider)),
       window_frame_provider_(window_frame_provider) {}
@@ -61,11 +85,11 @@
   nav_button_provider_->RedrawImages(params.top_area_height, params.maximized,
                                      params.active);
   for (auto type : {
-           views::NavButtonProvider::FrameButtonDisplayType::kMinimize,
+           ui::NavButtonProvider::FrameButtonDisplayType::kMinimize,
            IsMaximized()
-               ? views::NavButtonProvider::FrameButtonDisplayType::kRestore
-               : views::NavButtonProvider::FrameButtonDisplayType::kMaximize,
-           views::NavButtonProvider::FrameButtonDisplayType::kClose,
+               ? ui::NavButtonProvider::FrameButtonDisplayType::kRestore
+               : ui::NavButtonProvider::FrameButtonDisplayType::kMaximize,
+           ui::NavButtonProvider::FrameButtonDisplayType::kClose,
        }) {
     for (size_t state = 0; state < views::Button::STATE_COUNT; state++) {
       views::Button::ButtonState button_state =
@@ -74,21 +98,23 @@
       DCHECK_EQ(std::string(views::ImageButton::kViewClassName),
                 button->GetClassName());
       static_cast<views::ImageButton*>(button)->SetImage(
-          button_state, nav_button_provider_->GetImage(type, button_state));
+          button_state,
+          nav_button_provider_->GetImage(
+              type, ButtonStateToNavButtonProviderState(button_state)));
     }
   }
 }
 
 views::Button* BrowserFrameViewLinuxNative::GetButtonFromDisplayType(
-    views::NavButtonProvider::FrameButtonDisplayType type) {
+    ui::NavButtonProvider::FrameButtonDisplayType type) {
   switch (type) {
-    case views::NavButtonProvider::FrameButtonDisplayType::kMinimize:
+    case ui::NavButtonProvider::FrameButtonDisplayType::kMinimize:
       return minimize_button();
-    case views::NavButtonProvider::FrameButtonDisplayType::kMaximize:
+    case ui::NavButtonProvider::FrameButtonDisplayType::kMaximize:
       return maximize_button();
-    case views::NavButtonProvider::FrameButtonDisplayType::kRestore:
+    case ui::NavButtonProvider::FrameButtonDisplayType::kRestore:
       return restore_button();
-    case views::NavButtonProvider::FrameButtonDisplayType::kClose:
+    case ui::NavButtonProvider::FrameButtonDisplayType::kClose:
       return close_button();
     default:
       NOTREACHED();
diff --git a/chrome/browser/ui/views/frame/browser_frame_view_linux_native.h b/chrome/browser/ui/views/frame/browser_frame_view_linux_native.h
index e0b7de0..b0933c7 100644
--- a/chrome/browser/ui/views/frame/browser_frame_view_linux_native.h
+++ b/chrome/browser/ui/views/frame/browser_frame_view_linux_native.h
@@ -7,8 +7,8 @@
 
 #include "base/memory/raw_ptr.h"
 #include "chrome/browser/ui/views/frame/browser_frame_view_linux.h"
-#include "ui/views/linux_ui/nav_button_provider.h"
-#include "ui/views/linux_ui/window_frame_provider.h"
+#include "ui/linux/nav_button_provider.h"
+#include "ui/linux/window_frame_provider.h"
 
 // A specialization of BrowserFrameViewLinux that is also able to
 // render frame buttons using the native toolkit.
@@ -18,8 +18,8 @@
       BrowserFrame* frame,
       BrowserView* browser_view,
       BrowserFrameViewLayoutLinux* layout,
-      std::unique_ptr<views::NavButtonProvider> nav_button_provider,
-      views::WindowFrameProvider* window_frame_provider);
+      std::unique_ptr<ui::NavButtonProvider> nav_button_provider,
+      ui::WindowFrameProvider* window_frame_provider);
 
   BrowserFrameViewLinuxNative(const BrowserFrameViewLinuxNative&) = delete;
   BrowserFrameViewLinuxNative& operator=(const BrowserFrameViewLinuxNative&) =
@@ -54,11 +54,11 @@
   // Returns one of |{minimize,maximize,restore,close}_button_|
   // corresponding to |type|.
   views::Button* GetButtonFromDisplayType(
-      views::NavButtonProvider::FrameButtonDisplayType type);
+      ui::NavButtonProvider::FrameButtonDisplayType type);
 
-  std::unique_ptr<views::NavButtonProvider> nav_button_provider_;
+  std::unique_ptr<ui::NavButtonProvider> nav_button_provider_;
 
-  const raw_ptr<views::WindowFrameProvider> window_frame_provider_;
+  const raw_ptr<ui::WindowFrameProvider> window_frame_provider_;
 
   DrawFrameButtonParams cache_{0, false, false};
 };
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_factory_views.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_factory_views.cc
index 0bdc0bc..e8c76fb 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_factory_views.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_factory_views.cc
@@ -22,8 +22,8 @@
 #include "chrome/browser/ui/views/frame/browser_frame_view_linux_native.h"
 #include "chrome/browser/ui/views/frame/desktop_browser_frame_aura_linux.h"
 #include "chrome/browser/ui/web_applications/app_browser_controller.h"
-#include "ui/views/linux_ui/linux_ui.h"
-#include "ui/views/linux_ui/nav_button_provider.h"
+#include "ui/linux/linux_ui.h"
+#include "ui/linux/nav_button_provider.h"
 #endif
 
 namespace chrome {
@@ -34,7 +34,7 @@
     BrowserFrame* frame,
     BrowserView* browser_view) {
 #if BUILDFLAG(IS_LINUX)
-  auto* linux_ui = views::LinuxUI::instance();
+  auto* linux_ui = ui::LinuxUi::instance();
   auto* profile = browser_view->browser()->profile();
   auto* theme_service_factory = ThemeServiceFactory::GetForProfile(profile);
   auto* app_controller = browser_view->browser()->app_controller();
diff --git a/chrome/browser/ui/views/frame/opaque_browser_frame_view.h b/chrome/browser/ui/views/frame/opaque_browser_frame_view.h
index aa7ae66..45ae28c 100644
--- a/chrome/browser/ui/views/frame/opaque_browser_frame_view.h
+++ b/chrome/browser/ui/views/frame/opaque_browser_frame_view.h
@@ -15,8 +15,8 @@
 #include "chrome/browser/ui/views/frame/opaque_browser_frame_view_layout_delegate.h"
 #include "chrome/browser/ui/views/tab_icon_view_model.h"
 #include "ui/base/metadata/metadata_header_macros.h"
+#include "ui/linux/linux_ui.h"
 #include "ui/views/controls/button/button.h"
-#include "ui/views/linux_ui/linux_ui.h"
 #include "ui/views/window/caption_button_types.h"
 #include "ui/views/window/non_client_view.h"
 
diff --git a/chrome/browser/ui/views/frame/opaque_browser_frame_view_browsertest.cc b/chrome/browser/ui/views/frame/opaque_browser_frame_view_browsertest.cc
index 15ad1402..e3a67bd 100644
--- a/chrome/browser/ui/views/frame/opaque_browser_frame_view_browsertest.cc
+++ b/chrome/browser/ui/views/frame/opaque_browser_frame_view_browsertest.cc
@@ -37,7 +37,7 @@
 #include "ui/views/view_utils.h"
 
 #if BUILDFLAG(IS_LINUX)
-#include "ui/views/linux_ui/linux_ui.h"
+#include "ui/linux/linux_ui.h"
 #endif
 
 // Tests web-app windows that use the OpaqueBrowserFrameView implementation
@@ -58,7 +58,7 @@
   void SetUpOnMainThread() override {
     SetThemeMode(ThemeMode::kDefault);
 #if BUILDFLAG(IS_LINUX)
-    views::LinuxUI::instance()->SetUseSystemThemeCallback(
+    ui::LinuxUi::instance()->SetUseSystemThemeCallback(
         base::BindRepeating([](aura::Window* window) { return false; }));
 #endif
   }
diff --git a/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view_browsertest.cc b/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view_browsertest.cc
index 6434853..66192da 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view_browsertest.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view_browsertest.cc
@@ -39,7 +39,7 @@
 #include "ui/views/widget/widget.h"
 
 #if BUILDFLAG(USE_GTK)
-#include "ui/views/linux_ui/linux_ui.h"
+#include "ui/linux/linux_ui.h"
 #endif
 
 #if defined(USE_AURA)
@@ -219,7 +219,7 @@
     // NativeThemeGtk instance will always be returned.
     // TODO(crbug.com/1304441): Remove this once GTK passthrough is fully
     // supported.
-    views::LinuxUI::instance()->SetUseSystemThemeCallback(
+    ui::LinuxUi::instance()->SetUseSystemThemeCallback(
         base::BindRepeating([](aura::Window* window) { return false; }));
     ui::NativeTheme::GetInstanceForNativeUi()->NotifyOnNativeThemeUpdated();
 
diff --git a/chrome/browser/ui/views/qrcode_generator/qrcode_generator_bubble.cc b/chrome/browser/ui/views/qrcode_generator/qrcode_generator_bubble.cc
index 3c035ba..ff4927b0 100644
--- a/chrome/browser/ui/views/qrcode_generator/qrcode_generator_bubble.cc
+++ b/chrome/browser/ui/views/qrcode_generator/qrcode_generator_bubble.cc
@@ -129,6 +129,7 @@
 void QRCodeGeneratorBubble::UpdateQRContent() {
   if (textfield_url_->GetText().empty()) {
     DisplayPlaceholderImage();
+    HideErrors(false);
     return;
   }
 
@@ -154,9 +155,7 @@
     return;
   }
 
-  ShrinkAndHideDisplay(center_error_label_);
-  bottom_error_label_->SetVisible(false);
-  download_button_->SetEnabled(true);
+  HideErrors(true);
   UpdateQRImage(AddQRCodeQuietZone(
       gfx::ImageSkia::CreateFrom1xBitmap(response->bitmap), response->data_size,
       GetColorProvider()->GetColor(kColorQrCodeBackground)));
@@ -190,6 +189,12 @@
   center_error_label_->SetVisible(true);
 }
 
+void QRCodeGeneratorBubble::HideErrors(bool enable_download_button) {
+  ShrinkAndHideDisplay(center_error_label_);
+  bottom_error_label_->SetVisible(false);
+  download_button_->SetEnabled(enable_download_button);
+}
+
 void QRCodeGeneratorBubble::ShrinkAndHideDisplay(views::View* view) {
   view->SetPreferredSize(gfx::Size(0, 0));
   view->SetVisible(false);
diff --git a/chrome/browser/ui/views/qrcode_generator/qrcode_generator_bubble.h b/chrome/browser/ui/views/qrcode_generator/qrcode_generator_bubble.h
index fc6fe630..5abce97 100644
--- a/chrome/browser/ui/views/qrcode_generator/qrcode_generator_bubble.h
+++ b/chrome/browser/ui/views/qrcode_generator/qrcode_generator_bubble.h
@@ -71,6 +71,8 @@
 
   views::ImageView* image_for_testing() { return qr_code_image_; }
   views::Textfield* textfield_for_testing() { return textfield_url_; }
+  views::Label* error_label_for_testing() { return bottom_error_label_; }
+  views::LabelButton* download_button_for_testing() { return download_button_; }
 
   void SetQRCodeServiceForTesting(
       mojo::Remote<mojom::QRCodeGeneratorService>&& remote);
@@ -88,6 +90,9 @@
   // Shows an error message.
   void DisplayError(mojom::QRCodeGeneratorError error);
 
+  // Hides all error messages and enables or disables download button.
+  void HideErrors(bool enable_download_button);
+
   // Shrinks the view and sets it not visible.
   void ShrinkAndHideDisplay(views::View* view);
 
diff --git a/chrome/browser/ui/views/qrcode_generator/qrcode_generator_bubble_unittest.cc b/chrome/browser/ui/views/qrcode_generator/qrcode_generator_bubble_unittest.cc
index 6cc2081..aa1fda6 100644
--- a/chrome/browser/ui/views/qrcode_generator/qrcode_generator_bubble_unittest.cc
+++ b/chrome/browser/ui/views/qrcode_generator/qrcode_generator_bubble_unittest.cc
@@ -4,12 +4,14 @@
 
 #include "chrome/browser/ui/views/qrcode_generator/qrcode_generator_bubble.h"
 
+#include "base/logging.h"
 #include "base/memory/raw_ptr.h"
 #include "base/scoped_observation.h"
 #include "chrome/services/qrcode_generator/public/cpp/qrcode_generator_service.h"
 #include "chrome/test/views/chrome_views_test_base.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/views/controls/button/label_button.h"
 #include "ui/views/controls/image_view.h"
 #include "ui/views/controls/textfield/textfield.h"
 #include "ui/views/view_observer.h"
@@ -144,6 +146,28 @@
   QRCodeGeneratorBubble* bubble() { return bubble_; }
   views::ImageView* image() { return bubble_->image_for_testing(); }
   views::Textfield* textfield() { return bubble_->textfield_for_testing(); }
+  views::Label* error_label() { return bubble_->error_label_for_testing(); }
+  views::LabelButton* download_button() {
+    return bubble_->download_button_for_testing();
+  }
+
+  bool ImageShowing() {
+    return image()->GetVisible() && image()->GetPreferredSize().height() > 0 &&
+           image()->GetPreferredSize().width() > 0;
+  }
+
+  bool ImagePlaceholderShowing() {
+    return ImageShowing() && image()->GetImage().height() > 128 &&
+           image()->GetImage().width() > 128 &&
+           image()->GetImage().bitmap()->getColor(128, 128) ==
+               SK_ColorTRANSPARENT;
+  }
+
+  bool ErrorLabelShowing() {
+    return error_label()->GetVisible() &&
+           error_label()->GetPreferredSize().height() > 0 &&
+           error_label()->GetPreferredSize().width() > 0;
+  }
 
   FakeQRCodeGeneratorService* service() { return &fake_service_; }
 
@@ -165,26 +189,21 @@
 TEST_F(QRCodeGeneratorBubbleUITest, ImageShowsAfterErrorState) {
   bubble()->Show();
 
-  auto image_showing = [&]() {
-    return image()->GetVisible() && image()->GetPreferredSize().height() > 0 &&
-           image()->GetPreferredSize().width() > 0;
-  };
-
-  EXPECT_TRUE(image_showing());
+  EXPECT_TRUE(ImageShowing());
 
   service()->WaitForRequest();
   ASSERT_TRUE(service()->HasPendingRequest());
   auto error_response = mojom::GenerateQRCodeResponse::New();
   error_response->error_code = mojom::QRCodeGeneratorError::UNKNOWN_ERROR;
 
-  EXPECT_TRUE(image_showing());
+  EXPECT_TRUE(ImageShowing());
 
   {
     ViewVisibilityWaiter waiter(image());
     service()->DeliverResponse(std::move(error_response));
     waiter.Wait();
 
-    EXPECT_FALSE(image_showing());
+    EXPECT_FALSE(ImageShowing());
   }
 
   // The UI regenerates the QR code when the user types new text, so synthesize
@@ -203,10 +222,110 @@
     service()->DeliverResponse(std::move(ok_response));
     waiter.Wait();
 
-    EXPECT_TRUE(image_showing());
+    EXPECT_TRUE(ImageShowing());
   }
 }
 
+TEST_F(QRCodeGeneratorBubbleUITest,
+       PlaceholderImageShowsAfterTextFieldEmptied) {
+  bubble()->Show();
+
+  EXPECT_TRUE(ImagePlaceholderShowing());
+
+  service()->WaitForRequest();
+  ASSERT_TRUE(service()->HasPendingRequest());
+  auto error_response = mojom::GenerateQRCodeResponse::New();
+  error_response->error_code = mojom::QRCodeGeneratorError::UNKNOWN_ERROR;
+
+  EXPECT_TRUE(ImagePlaceholderShowing());
+
+  {
+    ViewVisibilityWaiter waiter(image());
+    service()->DeliverResponse(std::move(error_response));
+    waiter.Wait();
+
+    EXPECT_FALSE(ImageShowing());
+  }
+
+  auto ok_response = mojom::GenerateQRCodeResponse::New();
+  ok_response->error_code = mojom::QRCodeGeneratorError::NONE;
+  ok_response->bitmap.allocN32Pixels(16, 16);
+  ok_response->bitmap.eraseColor(SK_ColorRED);
+  ok_response->data.resize(16 * 16);
+  ok_response->data_size = gfx::Size(16, 16);
+
+  // The UI regenerates the QR code when the user types new text, so synthesize
+  // that.
+  textfield()->InsertOrReplaceText(u"https://www.chromium.org/b");
+  service()->WaitForRequest();
+
+  {
+    ViewVisibilityWaiter waiter(image());
+    service()->DeliverResponse(std::move(ok_response));
+    waiter.Wait();
+
+    EXPECT_TRUE(ImageShowing());
+    EXPECT_FALSE(ImagePlaceholderShowing());
+    EXPECT_TRUE(download_button()->GetEnabled());
+  }
+
+  textfield()->SelectAll(false);
+  textfield()->DeleteRange(textfield()->GetSelectedRange());
+
+  EXPECT_TRUE(ImageShowing());
+  EXPECT_TRUE(ImagePlaceholderShowing());
+  EXPECT_FALSE(download_button()->GetEnabled());
+}
+
+TEST_F(QRCodeGeneratorBubbleUITest, LabelHidesAfterErrorState) {
+  bubble()->Show();
+
+  EXPECT_TRUE(ImagePlaceholderShowing());
+  EXPECT_FALSE(ErrorLabelShowing());
+
+  service()->WaitForRequest();
+  ASSERT_TRUE(service()->HasPendingRequest());
+  auto error_response = mojom::GenerateQRCodeResponse::New();
+  error_response->error_code = mojom::QRCodeGeneratorError::UNKNOWN_ERROR;
+
+  EXPECT_TRUE(ImagePlaceholderShowing());
+
+  {
+    ViewVisibilityWaiter waiter(image());
+    service()->DeliverResponse(std::move(error_response));
+    waiter.Wait();
+
+    EXPECT_FALSE(ImageShowing());
+  }
+
+  auto too_long_response = mojom::GenerateQRCodeResponse::New();
+  too_long_response->error_code = mojom::QRCodeGeneratorError::INPUT_TOO_LONG;
+
+  // The UI regenerates the QR code when the user types new text, so synthesize
+  // that.
+  textfield()->InsertOrReplaceText(u"https://www.chromium.org/b");
+  service()->WaitForRequest();
+
+  {
+    ViewVisibilityWaiter waiter(image());
+    service()->DeliverResponse(std::move(too_long_response));
+    waiter.Wait();
+
+    EXPECT_TRUE(ImageShowing());
+    EXPECT_TRUE(ImagePlaceholderShowing());
+    EXPECT_TRUE(ErrorLabelShowing());
+    EXPECT_FALSE(download_button()->GetEnabled());
+  }
+
+  textfield()->SelectAll(false);
+  textfield()->DeleteRange(textfield()->GetSelectedRange());
+
+  EXPECT_TRUE(ImageShowing());
+  EXPECT_TRUE(ImagePlaceholderShowing());
+  EXPECT_FALSE(ErrorLabelShowing());
+  EXPECT_FALSE(download_button()->GetEnabled());
+}
+
 }  // namespace
 
 }  // namespace qrcode_generator
diff --git a/chrome/browser/ui/views/side_panel/lens/lens_side_panel_coordinator.cc b/chrome/browser/ui/views/side_panel/lens/lens_side_panel_coordinator.cc
index 4e31c3b2..5100e4f 100644
--- a/chrome/browser/ui/views/side_panel/lens/lens_side_panel_coordinator.cc
+++ b/chrome/browser/ui/views/side_panel/lens/lens_side_panel_coordinator.cc
@@ -6,6 +6,8 @@
 #include <iostream>
 
 #include "base/callback.h"
+#include "base/metrics/user_metrics.h"
+#include "base/metrics/user_metrics_action.h"
 #include "base/scoped_observation.h"
 #include "chrome/browser/ui/ui_features.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
@@ -44,31 +46,62 @@
       ->side_panel_coordinator()
       ->GetGlobalSidePanelRegistry()
       ->Deregister(SidePanelEntry::Id::kLens);
+  base::RecordAction(
+      base::UserMetricsAction("LensUnifiedSidePanel.HideSidePanel"));
+}
+
+void LensSidePanelCoordinator::OnEntryShown(SidePanelEntry* entry) {
+  base::RecordAction(
+      base::UserMetricsAction("LensUnifiedSidePanel.LensEntryShown"));
+}
+
+void LensSidePanelCoordinator::OnEntryHidden(SidePanelEntry* entry) {
+  base::RecordAction(
+      base::UserMetricsAction("LensUnifiedSidePanel.LensEntryHidden"));
 }
 
 void LensSidePanelCoordinator::RegisterEntryAndShow(
     const content::OpenURLParams& params) {
+  base::RecordAction(base::UserMetricsAction("LensUnifiedSidePanel.LensQuery"));
   auto* side_panel_coordinator = GetBrowserView()->side_panel_coordinator();
   auto* global_registry = side_panel_coordinator->GetGlobalSidePanelRegistry();
 
   // check if the view is already registered
   if (global_registry->GetEntryForId(SidePanelEntry::Id::kLens) != nullptr &&
       lens_side_panel_view_ != nullptr) {
+    // The user issued a follow-up Lens query.
+    base::RecordAction(
+        base::UserMetricsAction("LensUnifiedSidePanel.LensQuery_Followup"));
     lens_side_panel_view_->OpenUrl(params);
   } else {
-    global_registry->Register(std::make_unique<SidePanelEntry>(
+    base::RecordAction(
+        base::UserMetricsAction("LensUnifiedSidePanel.LensQuery_New"));
+    auto entry = std::make_unique<SidePanelEntry>(
         SidePanelEntry::Id::kLens,
         l10n_util::GetStringUTF16(IDS_SIDE_PANEL_COMBO_BOX_GOOGLE_LENS_LABEL),
         // leaving the star icon for this CL, will change this to lens icon in a
         // different CL.
         ui::ImageModel::FromVectorIcon(omnibox::kStarIcon, ui::kColorIcon),
         base::BindRepeating(&LensSidePanelCoordinator::CreateLensWebView,
-                            base::Unretained(this), params)));
+                            base::Unretained(this), params));
+    entry->AddObserver(this);
+    global_registry->Register(std::move(entry));
   }
 
   if (side_panel_coordinator->GetCurrentEntryId() !=
       SidePanelEntry::Id::kLens) {
+    if (!side_panel_coordinator->IsSidePanelShowing()) {
+      base::RecordAction(base::UserMetricsAction(
+          "LensUnifiedSidePanel.LensQuery_SidePanelClosed"));
+    } else {
+      base::RecordAction(base::UserMetricsAction(
+          "LensUnifiedSidePanel.LensQuery_SidePanelOpenNonLens"));
+    }
+
     side_panel_coordinator->Show(SidePanelEntry::Id::kLens);
+  } else {
+    base::RecordAction(base::UserMetricsAction(
+        "LensUnifiedSidePanel.LensQuery_SidePanelOpenLens"));
   }
 }
 
diff --git a/chrome/browser/ui/views/side_panel/lens/lens_side_panel_coordinator.h b/chrome/browser/ui/views/side_panel/lens/lens_side_panel_coordinator.h
index 340839b..f44124c 100644
--- a/chrome/browser/ui/views/side_panel/lens/lens_side_panel_coordinator.h
+++ b/chrome/browser/ui/views/side_panel/lens/lens_side_panel_coordinator.h
@@ -11,6 +11,7 @@
 #include "chrome/browser/ui/browser_user_data.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/side_panel/lens/lens_unified_side_panel_view.h"
+#include "chrome/browser/ui/views/side_panel/side_panel_entry_observer.h"
 #include "chrome/browser/ui/views/side_panel/side_panel_view_state_observer.h"
 
 class Browser;
@@ -19,7 +20,8 @@
 // LensUnifiedSidePanelEntry.
 class LensSidePanelCoordinator
     : public BrowserUserData<LensSidePanelCoordinator>,
-      public SidePanelViewStateObserver {
+      public SidePanelViewStateObserver,
+      public SidePanelEntryObserver {
  public:
   explicit LensSidePanelCoordinator(Browser* browser);
   LensSidePanelCoordinator(const LensSidePanelCoordinator&) = delete;
@@ -37,6 +39,10 @@
  private:
   friend class BrowserUserData<LensSidePanelCoordinator>;
 
+  // SidePanelEntryObserver:
+  void OnEntryShown(SidePanelEntry* entry) override;
+  void OnEntryHidden(SidePanelEntry* entry) override;
+
   BrowserView* GetBrowserView();
 
   // SidePanelViewStateObserver
diff --git a/chrome/browser/ui/views/side_panel/lens/lens_side_panel_coordinator_browsertest.cc b/chrome/browser/ui/views/side_panel/lens/lens_side_panel_coordinator_browsertest.cc
index e19bf51..af2f19c 100644
--- a/chrome/browser/ui/views/side_panel/lens/lens_side_panel_coordinator_browsertest.cc
+++ b/chrome/browser/ui/views/side_panel/lens/lens_side_panel_coordinator_browsertest.cc
@@ -6,6 +6,7 @@
 #include "base/files/file_util.h"
 #include "base/memory/raw_ptr.h"
 #include "base/strings/strcat.h"
+#include "base/test/metrics/user_action_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
@@ -47,6 +48,8 @@
 
 namespace {
 
+constexpr char kCloseAction[] = "LensUnifiedSidePanel.HideSidePanel";
+
 // Maintains image search test state. In particular, note that |menu_observer_|
 // must live until the right-click completes asynchronously.
 class SearchImageWithUnifiedSidePanel : public InProcessBrowserTest {
@@ -167,6 +170,7 @@
   }
 
   std::unique_ptr<ContextMenuNotificationObserver> menu_observer_;
+  base::UserActionTester user_action_tester;
 };
 
 IN_PROC_BROWSER_TEST_F(SearchImageWithUnifiedSidePanel,
@@ -189,7 +193,7 @@
 }
 
 IN_PROC_BROWSER_TEST_F(SearchImageWithUnifiedSidePanel,
-                       ClosingSidePanelDeregistersLensView) {
+                       ClosingSidePanelDeregistersLensViewAndLogsCloseMetric) {
   SetupUnifiedSidePanel();
   EXPECT_TRUE(GetRightAlignedSidePanel()->GetVisible());
 
@@ -203,6 +207,7 @@
       GetSidePanelCoordinator()->GetGlobalSidePanelRegistry()->GetEntryForId(
           SidePanelEntry::Id::kLens),
       nullptr);
+  EXPECT_EQ(1, user_action_tester.GetActionCount(kCloseAction));
 }
 
 IN_PROC_BROWSER_TEST_F(SearchImageWithUnifiedSidePanel,
diff --git a/chrome/browser/ui/views/side_panel/lens/lens_side_panel_coordinator_unittest.cc b/chrome/browser/ui/views/side_panel/lens/lens_side_panel_coordinator_unittest.cc
index 4f44de4..ab1b4c33 100644
--- a/chrome/browser/ui/views/side_panel/lens/lens_side_panel_coordinator_unittest.cc
+++ b/chrome/browser/ui/views/side_panel/lens/lens_side_panel_coordinator_unittest.cc
@@ -20,6 +20,20 @@
 
 namespace {
 
+constexpr char kNewLensQueryAction[] = "LensUnifiedSidePanel.LensQuery_New";
+constexpr char kLensQueryAction[] = "LensUnifiedSidePanel.LensQuery";
+constexpr char kLensEntryHiddenAction[] =
+    "LensUnifiedSidePanel.LensEntryHidden";
+constexpr char kLensEntryShownAction[] = "LensUnifiedSidePanel.LensEntryShown";
+constexpr char kLensQueryFollowupAction[] =
+    "LensUnifiedSidePanel.LensQuery_Followup";
+constexpr char kLensQuerySidePanelClosedAction[] =
+    "LensUnifiedSidePanel.LensQuery_SidePanelClosed";
+constexpr char kLensQuerySidePanelOpenNonLensAction[] =
+    "LensUnifiedSidePanel.LensQuery_SidePanelOpenNonLens";
+constexpr char kLensQuerySidePanelOpenLensAction[] =
+    "LensUnifiedSidePanel.LensQuery_SidePanelOpenLens";
+
 class LensSidePanelCoordinatorTest : public TestWithBrowserView {
  public:
   void SetUp() override {
@@ -61,6 +75,7 @@
 TEST_F(LensSidePanelCoordinatorTest,
        OpenWithUrlShowsUnifiedSidePanelWithLensSelected) {
   base::UserActionTester user_action_tester;
+  EXPECT_EQ(0, user_action_tester.GetActionCount(kNewLensQueryAction));
 
   lens_side_panel_coordinator_->RegisterEntryAndShow(
       content::OpenURLParams(GURL("http://foo.com"), content::Referrer(),
@@ -71,5 +86,95 @@
   EXPECT_EQ(
       GetSidePanelCoordinator()->GetCurrentSidePanelEntryForTesting()->id(),
       SidePanelEntry::Id::kLens);
+  EXPECT_EQ(1, user_action_tester.GetActionCount(kLensQueryAction));
+  EXPECT_EQ(1, user_action_tester.GetActionCount(kNewLensQueryAction));
+  EXPECT_EQ(1,
+            user_action_tester.GetActionCount(kLensQuerySidePanelClosedAction));
 }
+
+TEST_F(LensSidePanelCoordinatorTest, OpenWithUrlWhenSidePanelOpenShowsLens) {
+  base::UserActionTester user_action_tester;
+  GetSidePanelCoordinator()->Show(SidePanelEntry::Id::kBookmarks);
+  EXPECT_EQ(0, user_action_tester.GetActionCount(kNewLensQueryAction));
+
+  lens_side_panel_coordinator_->RegisterEntryAndShow(
+      content::OpenURLParams(GURL("http://foo.com"), content::Referrer(),
+                             WindowOpenDisposition::NEW_FOREGROUND_TAB,
+                             ui::PAGE_TRANSITION_LINK, false));
+
+  EXPECT_TRUE(GetRightAlignedSidePanel()->GetVisible());
+  EXPECT_EQ(
+      GetSidePanelCoordinator()->GetCurrentSidePanelEntryForTesting()->id(),
+      SidePanelEntry::Id::kLens);
+  EXPECT_EQ(1, user_action_tester.GetActionCount(kNewLensQueryAction));
+  EXPECT_EQ(1, user_action_tester.GetActionCount(
+                   kLensQuerySidePanelOpenNonLensAction));
+}
+
+TEST_F(LensSidePanelCoordinatorTest,
+       CallingRegisterTwiceOpensNewUrlAndLogsAction) {
+  base::UserActionTester user_action_tester;
+
+  lens_side_panel_coordinator_->RegisterEntryAndShow(
+      content::OpenURLParams(GURL("http://foo.com"), content::Referrer(),
+                             WindowOpenDisposition::NEW_FOREGROUND_TAB,
+                             ui::PAGE_TRANSITION_LINK, false));
+  lens_side_panel_coordinator_->RegisterEntryAndShow(
+      content::OpenURLParams(GURL("http://bar.com"), content::Referrer(),
+                             WindowOpenDisposition::NEW_FOREGROUND_TAB,
+                             ui::PAGE_TRANSITION_LINK, false));
+
+  EXPECT_TRUE(GetRightAlignedSidePanel()->GetVisible());
+  EXPECT_EQ(
+      GetSidePanelCoordinator()->GetCurrentSidePanelEntryForTesting()->id(),
+      SidePanelEntry::Id::kLens);
+  EXPECT_EQ(2, user_action_tester.GetActionCount(kLensQueryAction));
+  EXPECT_EQ(1,
+            user_action_tester.GetActionCount(kLensQuerySidePanelClosedAction));
+  EXPECT_EQ(
+      1, user_action_tester.GetActionCount(kLensQuerySidePanelOpenLensAction));
+  EXPECT_EQ(1, user_action_tester.GetActionCount(kLensQueryFollowupAction));
+}
+
+TEST_F(LensSidePanelCoordinatorTest, SwitchToDifferentItemTriggersHideEvent) {
+  base::UserActionTester user_action_tester;
+  lens_side_panel_coordinator_->RegisterEntryAndShow(
+      content::OpenURLParams(GURL("http://foo.com"), content::Referrer(),
+                             WindowOpenDisposition::NEW_FOREGROUND_TAB,
+                             ui::PAGE_TRANSITION_LINK, false));
+
+  GetSidePanelCoordinator()->Show(SidePanelEntry::Id::kBookmarks);
+
+  EXPECT_TRUE(GetRightAlignedSidePanel()->GetVisible());
+  EXPECT_EQ(
+      GetSidePanelCoordinator()->GetCurrentSidePanelEntryForTesting()->id(),
+      SidePanelEntry::Id::kBookmarks);
+  EXPECT_EQ(1,
+            user_action_tester.GetActionCount(kLensQuerySidePanelClosedAction));
+  EXPECT_EQ(1, user_action_tester.GetActionCount(kLensEntryHiddenAction));
+  EXPECT_EQ(1, user_action_tester.GetActionCount(kLensEntryShownAction));
+}
+
+TEST_F(LensSidePanelCoordinatorTest, SwitchBackToLensTriggersShowEvent) {
+  base::UserActionTester user_action_tester;
+  lens_side_panel_coordinator_->RegisterEntryAndShow(
+      content::OpenURLParams(GURL("http://foo.com"), content::Referrer(),
+                             WindowOpenDisposition::NEW_FOREGROUND_TAB,
+                             ui::PAGE_TRANSITION_LINK, false));
+
+  GetSidePanelCoordinator()->Show(SidePanelEntry::Id::kBookmarks);
+  GetSidePanelCoordinator()->Show(SidePanelEntry::Id::kLens);
+
+  EXPECT_TRUE(GetRightAlignedSidePanel()->GetVisible());
+  EXPECT_EQ(
+      GetSidePanelCoordinator()->GetCurrentSidePanelEntryForTesting()->id(),
+      SidePanelEntry::Id::kLens);
+  EXPECT_EQ(1, user_action_tester.GetActionCount(kLensQueryAction));
+  EXPECT_EQ(1, user_action_tester.GetActionCount(kNewLensQueryAction));
+  EXPECT_EQ(1,
+            user_action_tester.GetActionCount(kLensQuerySidePanelClosedAction));
+  EXPECT_EQ(1, user_action_tester.GetActionCount(kLensEntryHiddenAction));
+  EXPECT_EQ(2, user_action_tester.GetActionCount(kLensEntryShownAction));
+}
+
 }  // namespace
diff --git a/chrome/browser/ui/views/side_panel/lens/lens_unified_side_panel_view.cc b/chrome/browser/ui/views/side_panel/lens/lens_unified_side_panel_view.cc
index 6a1dde6..bb8fd8ee 100644
--- a/chrome/browser/ui/views/side_panel/lens/lens_unified_side_panel_view.cc
+++ b/chrome/browser/ui/views/side_panel/lens/lens_unified_side_panel_view.cc
@@ -5,6 +5,8 @@
 #include "chrome/browser/ui/views/side_panel/lens/lens_unified_side_panel_view.h"
 
 #include "base/bind.h"
+#include "base/metrics/user_metrics.h"
+#include "base/metrics/user_metrics_action.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/themes/theme_properties.h"
 #include "chrome/browser/ui/browser.h"
@@ -121,6 +123,8 @@
                                 ui::PAGE_TRANSITION_TYPED,
                                 /*is_renderer_initiated=*/false);
   browser_view_->browser()->OpenURL(params);
+  base::RecordAction(
+      base::UserMetricsAction("LensUnifiedSidePanel.LoadResultsInNewTab"));
   browser_view_->side_panel_coordinator()->Close();
 }
 
@@ -157,6 +161,8 @@
     params.initiator_origin = url::Origin::Create(url);
 
   browser_view_->browser()->OpenURL(params);
+  base::RecordAction(
+      base::UserMetricsAction("LensUnifiedSidePanel.ResultLinkClick"));
 }
 
 void LensUnifiedSidePanelView::CreateAndInstallFooter() {
diff --git a/chrome/browser/ui/views/side_panel/side_panel_coordinator.cc b/chrome/browser/ui/views/side_panel/side_panel_coordinator.cc
index ed9aca56..01d45a0 100644
--- a/chrome/browser/ui/views/side_panel/side_panel_coordinator.cc
+++ b/chrome/browser/ui/views/side_panel/side_panel_coordinator.cc
@@ -10,6 +10,7 @@
 #include "base/callback_forward.h"
 #include "chrome/browser/feature_engagement/tracker_factory.h"
 #include "chrome/browser/themes/theme_properties.h"
+#include "chrome/browser/ui/browser_element_identifiers.h"
 #include "chrome/browser/ui/color/chrome_color_id.h"
 #include "chrome/browser/ui/views/chrome_layout_provider.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
@@ -51,6 +52,7 @@
     const gfx::VectorIcon& icon,
     const gfx::Insets& margin_insets,
     const std::u16string& tooltip_text,
+    ui::ElementIdentifier view_id,
     int dip_size) {
   auto button = views::CreateVectorImageButtonWithNativeTheme(pressed_callback,
                                                               icon, dip_size);
@@ -65,6 +67,7 @@
   button->SetProperty(
       views::kFlexBehaviorKey,
       views::FlexSpecification().WithAlignment(views::LayoutAlignment::kEnd));
+  button->SetProperty(views::kElementIdentifierKey, view_id);
 
   return button;
 }
@@ -473,6 +476,7 @@
       base::BindRepeating(&SidePanelCoordinator::Close, base::Unretained(this)),
       views::kIcCloseIcon, gfx::Insets(),
       l10n_util::GetStringUTF16(IDS_ACCNAME_CLOSE),
+      kSidePanelCloseButtonElementId,
       ChromeLayoutProvider::Get()->GetDistanceMetric(
           ChromeDistanceMetric::DISTANCE_SIDE_PANEL_HEADER_VECTOR_ICON_SIZE)));
 
diff --git a/chrome/browser/ui/views/side_search/side_search_browser_controller.cc b/chrome/browser/ui/views/side_search/side_search_browser_controller.cc
index e2d9b926..bdf7e0c4 100644
--- a/chrome/browser/ui/views/side_search/side_search_browser_controller.cc
+++ b/chrome/browser/ui/views/side_search/side_search_browser_controller.cc
@@ -223,8 +223,8 @@
     close_button_ = AddChildView(std::make_unique<HeaderButton>(
         vector_icons::kCloseIcon, std::move(callback)));
     views::InstallCircleHighlightPathGenerator(close_button_);
-    close_button_->SetID(SideSearchBrowserController::SideSearchViewID::
-                             VIEW_ID_SIDE_PANEL_CLOSE_BUTTON);
+    close_button_->SetProperty(views::kElementIdentifierKey,
+                               kSidePanelCloseButtonElementId);
     close_button_->SetAccessibleName(
         l10n_util::GetStringUTF16(IDS_ACCNAME_SIDE_SEARCH_CLOSE_BUTTON));
     close_button_->SetTooltipText(
diff --git a/chrome/browser/ui/views/side_search/side_search_browser_controller.h b/chrome/browser/ui/views/side_search/side_search_browser_controller.h
index 8e9a70c..459ce42 100644
--- a/chrome/browser/ui/views/side_search/side_search_browser_controller.h
+++ b/chrome/browser/ui/views/side_search/side_search_browser_controller.h
@@ -34,7 +34,6 @@
  public:
   enum SideSearchViewID {
     VIEW_ID_NONE = 0,
-    VIEW_ID_SIDE_PANEL_CLOSE_BUTTON,
     VIEW_ID_SIDE_PANEL_TITLE_LABEL,
   };
 
diff --git a/chrome/browser/ui/views/side_search/side_search_browser_controller_interactive_uitest.cc b/chrome/browser/ui/views/side_search/side_search_browser_controller_interactive_uitest.cc
index b398a07..f2ca1a40 100644
--- a/chrome/browser/ui/views/side_search/side_search_browser_controller_interactive_uitest.cc
+++ b/chrome/browser/ui/views/side_search/side_search_browser_controller_interactive_uitest.cc
@@ -216,10 +216,13 @@
     }
   }
 
-  views::ImageButton* GetSideButtonClosePanelFor(Browser* browser) {
-    return static_cast<views::ImageButton*>(
-        GetSidePanelFor(browser)->GetViewByID(static_cast<int>(
-            SideSearchBrowserController::VIEW_ID_SIDE_PANEL_CLOSE_BUTTON)));
+  views::Button* GetSideButtonClosePanelFor(Browser* browser) {
+    views::View* button_view =
+        views::ElementTrackerViews::GetInstance()->GetFirstMatchingView(
+            kSidePanelCloseButtonElementId,
+            browser->window()->GetElementContext());
+    return button_view ? views::AsViewClass<views::Button>(button_view)
+                       : nullptr;
   }
 
   virtual SidePanel* GetSidePanelFor(Browser* browser) {
diff --git a/chrome/browser/ui/views/status_icons/status_icon_button_linux.h b/chrome/browser/ui/views/status_icons/status_icon_button_linux.h
index 0513007..9371827 100644
--- a/chrome/browser/ui/views/status_icons/status_icon_button_linux.h
+++ b/chrome/browser/ui/views/status_icons/status_icon_button_linux.h
@@ -9,10 +9,10 @@
 
 #include "base/memory/raw_ptr.h"
 #include "ui/base/metadata/metadata_header_macros.h"
+#include "ui/linux/status_icon_linux.h"
 #include "ui/views/context_menu_controller.h"
 #include "ui/views/controls/button/button.h"
 #include "ui/views/controls/menu/menu_runner.h"
-#include "ui/views/linux_ui/status_icon_linux.h"
 #include "ui/views/widget/widget.h"
 
 namespace aura {
@@ -22,7 +22,7 @@
 // A button that is internally mapped as a status icon if the underlaying
 // platform supports that kind of windows. Otherwise, calls
 // OnImplInitializationFailed.
-class StatusIconButtonLinux : public views::StatusIconLinux,
+class StatusIconButtonLinux : public ui::StatusIconLinux,
                               public views::Button,
                               public views::ContextMenuController {
  public:
diff --git a/chrome/browser/ui/views/status_icons/status_icon_linux_dbus.cc b/chrome/browser/ui/views/status_icons/status_icon_linux_dbus.cc
index 639810163..ce4d9f00 100644
--- a/chrome/browser/ui/views/status_icons/status_icon_linux_dbus.cc
+++ b/chrome/browser/ui/views/status_icons/status_icon_linux_dbus.cc
@@ -41,7 +41,7 @@
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/image/image_skia.h"
-#include "ui/views/linux_ui/status_icon_linux.h"
+#include "ui/linux/status_icon_linux.h"
 
 namespace {
 
diff --git a/chrome/browser/ui/views/status_icons/status_icon_linux_dbus.h b/chrome/browser/ui/views/status_icons/status_icon_linux_dbus.h
index c9bf135c..6a0c6e47 100644
--- a/chrome/browser/ui/views/status_icons/status_icon_linux_dbus.h
+++ b/chrome/browser/ui/views/status_icons/status_icon_linux_dbus.h
@@ -19,8 +19,8 @@
 #include "dbus/message.h"
 #include "dbus/object_proxy.h"
 #include "ui/base/models/simple_menu_model.h"
+#include "ui/linux/status_icon_linux.h"
 #include "ui/views/controls/menu/menu_runner.h"
-#include "ui/views/linux_ui/status_icon_linux.h"
 
 namespace gfx {
 class ImageSkia;
@@ -31,7 +31,7 @@
 
 // A status icon following the StatusNotifierItem specification.
 // https://www.freedesktop.org/wiki/Specifications/StatusNotifierItem/StatusNotifierItem/
-class StatusIconLinuxDbus : public views::StatusIconLinux,
+class StatusIconLinuxDbus : public ui::StatusIconLinux,
                             public ui::SimpleMenuModel::Delegate,
                             public base::RefCounted<StatusIconLinuxDbus> {
  public:
diff --git a/chrome/browser/ui/views/status_icons/status_icon_linux_wrapper.cc b/chrome/browser/ui/views/status_icons/status_icon_linux_wrapper.cc
index 48f849f..8e74922 100644
--- a/chrome/browser/ui/views/status_icons/status_icon_linux_wrapper.cc
+++ b/chrome/browser/ui/views/status_icons/status_icon_linux_wrapper.cc
@@ -36,11 +36,10 @@
 
 }  // namespace
 
-StatusIconLinuxWrapper::StatusIconLinuxWrapper(
-    views::StatusIconLinux* status_icon,
-    StatusIconType status_icon_type,
-    const gfx::ImageSkia& image,
-    const std::u16string& tool_tip)
+StatusIconLinuxWrapper::StatusIconLinuxWrapper(ui::StatusIconLinux* status_icon,
+                                               StatusIconType status_icon_type,
+                                               const gfx::ImageSkia& image,
+                                               const std::u16string& tool_tip)
     : status_icon_(status_icon),
       status_icon_type_(status_icon_type),
       image_(GetBestImageRep(image)),
@@ -60,7 +59,7 @@
 #endif
 
 StatusIconLinuxWrapper::StatusIconLinuxWrapper(
-    std::unique_ptr<views::StatusIconLinux> status_icon,
+    std::unique_ptr<ui::StatusIconLinux> status_icon,
     StatusIconType status_icon_type,
     const gfx::ImageSkia& image,
     const std::u16string& tool_tip)
diff --git a/chrome/browser/ui/views/status_icons/status_icon_linux_wrapper.h b/chrome/browser/ui/views/status_icons/status_icon_linux_wrapper.h
index 65cbe081..acbb28d9 100644
--- a/chrome/browser/ui/views/status_icons/status_icon_linux_wrapper.h
+++ b/chrome/browser/ui/views/status_icons/status_icon_linux_wrapper.h
@@ -12,14 +12,14 @@
 #include "chrome/browser/status_icons/desktop_notification_balloon.h"
 #include "chrome/browser/status_icons/status_icon.h"
 #include "ui/gfx/image/image_skia.h"
-#include "ui/views/linux_ui/status_icon_linux.h"
+#include "ui/linux/status_icon_linux.h"
 
 class StatusIconLinuxDbus;
 
 // Wrapper class for StatusIconLinux that implements the standard StatusIcon
 // interface. Also handles callbacks from StatusIconLinux.
 class StatusIconLinuxWrapper : public StatusIcon,
-                               public views::StatusIconLinux::Delegate,
+                               public ui::StatusIconLinux::Delegate,
                                public StatusIconMenuModel::Observer {
  public:
   StatusIconLinuxWrapper(const StatusIconLinuxWrapper&) = delete;
@@ -66,7 +66,7 @@
 
   // A status icon wrapper should only be created by calling
   // CreateWrappedStatusIcon().
-  StatusIconLinuxWrapper(views::StatusIconLinux* status_icon,
+  StatusIconLinuxWrapper(ui::StatusIconLinux* status_icon,
                          StatusIconType status_icon_type,
                          const gfx::ImageSkia& image,
                          const std::u16string& tool_tip);
@@ -75,7 +75,7 @@
                          const gfx::ImageSkia& image,
                          const std::u16string& tool_tip);
 #endif
-  StatusIconLinuxWrapper(std::unique_ptr<views::StatusIconLinux> status_icon,
+  StatusIconLinuxWrapper(std::unique_ptr<ui::StatusIconLinux> status_icon,
                          StatusIconType status_icon_type,
                          const gfx::ImageSkia& image,
                          const std::u16string& tool_tip);
@@ -89,8 +89,8 @@
 #if defined(USE_DBUS)
   scoped_refptr<StatusIconLinuxDbus> status_icon_dbus_;
 #endif
-  std::unique_ptr<views::StatusIconLinux> status_icon_linux_;
-  raw_ptr<views::StatusIconLinux> status_icon_;
+  std::unique_ptr<ui::StatusIconLinux> status_icon_linux_;
+  raw_ptr<ui::StatusIconLinux> status_icon_;
   StatusIconType status_icon_type_;
 
   gfx::ImageSkia image_;
diff --git a/chrome/browser/ui/views/tabs/tab_strip_scroll_container.cc b/chrome/browser/ui/views/tabs/tab_strip_scroll_container.cc
index 8b53177..a38a807 100644
--- a/chrome/browser/ui/views/tabs/tab_strip_scroll_container.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip_scroll_container.cc
@@ -91,9 +91,12 @@
   // views::View overrides:
   void OnPaint(gfx::Canvas* canvas) override {
     // TODO(tbergquist): Handle themes with titlebar background images.
-    SkColor frame_color = tab_strip_->controller()->GetFrameColor(
-        BrowserFrameActiveState::kUseCurrent);
-    SkColor shadow_color = GetColorProvider()->GetColor(ui::kColorShadowBase);
+    // TODO(crbug/1308932): Remove FromColor and make all SkColor4f.
+    SkColor4f frame_color =
+        SkColor4f::FromColor(tab_strip_->controller()->GetFrameColor(
+            BrowserFrameActiveState::kUseCurrent));
+    SkColor4f shadow_color = SkColor4f::FromColor(
+        GetColorProvider()->GetColor(ui::kColorShadowBase));
 
     // Mirror how the indicator is painted for the right vs left sides.
     SkPoint points[2];
@@ -105,7 +108,7 @@
       points[1].iset(GetContentsBounds().origin().x(), GetContentsBounds().y());
     }
 
-    SkColor colors[5];
+    SkColor4f colors[5];
     SkScalar color_positions[5];
     // Paint an opaque region on the outside.
     colors[0] = frame_color;
@@ -116,7 +119,8 @@
     // Paint a shadow-like gradient on the inside.
     colors[2] = shadow_color;
     colors[3] = shadow_color;
-    colors[4] = SkColorSetA(shadow_color, SK_AlphaTRANSPARENT);
+    colors[4] = shadow_color;
+    colors[4].fA = 0.0f;
     color_positions[2] = static_cast<float>(kOpaqueWidth) / kTotalWidth;
     color_positions[3] =
         static_cast<float>(kOpaqueWidth + kShadowSpread) / kTotalWidth;
diff --git a/chrome/browser/ui/views/tabs/tab_style_views.cc b/chrome/browser/ui/views/tabs/tab_style_views.cc
index a87ea872..385ed3e7 100644
--- a/chrome/browser/ui/views/tabs/tab_style_views.cc
+++ b/chrome/browser/ui/views/tabs/tab_style_views.cc
@@ -169,7 +169,10 @@
                    const SkPoint& p,
                    SkScalar radius,
                    SkColor color) {
-  const SkColor colors[2] = {color, SkColorSetA(color, SK_AlphaTRANSPARENT)};
+  // TODO(crbug/1308932): Remove FromColor and make all SkColor4f.
+  const SkColor4f colors[2] = {
+      SkColor4f::FromColor(color),
+      SkColor4f::FromColor(SkColorSetA(color, SK_AlphaTRANSPARENT))};
   cc::PaintFlags flags;
   flags.setAntiAlias(true);
   flags.setShader(cc::PaintShader::MakeRadialGradient(
diff --git a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
index 32d39af..375d8fcc5 100644
--- a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
+++ b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
@@ -1593,6 +1593,12 @@
       GURL(chrome::kChromeUIPowerUrl),
       GURL(chrome::kChromeUIPrintManagementUrl),
       GURL(ash::multidevice::kChromeUIProximityAuthURL),
+      GURL(ash::multidevice::kOsUIProximityAuthURL),
+      GURL(chrome::kChromeUINearbyInternalsURL),
+      GURL(chrome::kOsUINearbyInternalsURL),
+      GURL(chrome::kChromeUIMultiDeviceInternalsURL),
+      GURL(chrome::kOsUIMultiDeviceInternalsURL),
+      GURL(chrome::kOsUIRestartURL),
       GURL(chrome::kChromeUIScanningAppURL),
       GURL(chrome::kOsUIConnectivityDiagnosticsAppURL),
       GURL(chrome::kOsUIDiagnosticsAppURL),
diff --git a/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler_unittest.cc b/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler_unittest.cc
index 5d09602..2382982 100644
--- a/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler_unittest.cc
+++ b/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler_unittest.cc
@@ -665,7 +665,7 @@
   EXPECT_CALL(callback, Run(_)).Times(1).WillOnce(SaveArg<0>(&module_ids));
   base::test::ScopedFeatureList features;
   features.InitWithFeaturesAndParameters(
-      {{ntp_features::kModules,
+      {{ntp_features::kNtpModulesOrder,
         {{ntp_features::kNtpModulesOrderParam, "bar,baz"}}},
        {ntp_features::kNtpModulesDragAndDrop, {}}},
       {});
diff --git a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc
index 94cd9983..83ad66c 100644
--- a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc
+++ b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc
@@ -92,6 +92,16 @@
 constexpr char kPrevNavigationTimePrefName[] = "NewTabPage.PrevNavigationTime";
 constexpr char kSignedOutNtpModulesSwitch[] = "signed-out-ntp-modules";
 
+const std::pair<const char*, const base::Feature&> kModuleFeatures[] = {
+#if !defined(OFFICIAL_BUILD)
+    {"dummyModulesEnabled", ntp_features::kNtpDummyModules},
+#endif
+    {"recipeTasksModuleEnabled", ntp_features::kNtpRecipeTasksModule},
+    {"chromeCartModuleEnabled", ntp_features::kNtpChromeCartModule},
+    {"photosModuleEnabled", ntp_features::kNtpPhotosModule},
+    {"feedModuleEnabled", ntp_features::kNtpFeedModule},
+};
+
 void AddRawStringOrDefault(content::WebUIDataSource* source,
                            const char key[],
                            const std::string str,
@@ -423,20 +433,11 @@
                                IDS_NTP_MODULES_CART_DISCOUNT_CONSENT_CONTENT);
   }
 
-#if !defined(OFFICIAL_BUILD)
-  source->AddBoolean(
-      "dummyModulesEnabled",
-      base::FeatureList::IsEnabled(ntp_features::kNtpDummyModules));
-#endif
-  source->AddBoolean(
-      "recipeTasksModuleEnabled",
-      base::FeatureList::IsEnabled(ntp_features::kNtpRecipeTasksModule));
-  source->AddBoolean(
-      "chromeCartModuleEnabled",
-      base::FeatureList::IsEnabled(ntp_features::kNtpChromeCartModule));
-  source->AddBoolean(
-      "photosModuleEnabled",
-      base::FeatureList::IsEnabled(ntp_features::kNtpPhotosModule));
+  for (const auto& nameFeature : kModuleFeatures) {
+    source->AddBoolean(nameFeature.first,
+                       base::FeatureList::IsEnabled(nameFeature.second));
+  }
+
   source->AddString("photosModuleCustomArtWork",
                     base::GetFieldTrialParamValueByFeature(
                         ntp_features::kNtpPhotosModuleCustomizedOptInArtWork,
@@ -456,8 +457,6 @@
   source->AddBoolean(
       "modulesRedesignedLayoutEnabled",
       base::FeatureList::IsEnabled(ntp_features::kNtpModulesRedesignedLayout));
-  source->AddBoolean("feedModuleEnabled", base::FeatureList::IsEnabled(
-                                              ntp_features::kNtpFeedModule));
 
   std::vector<std::string> splitExperimentGroup = base::SplitString(
       base::GetFieldTrialParamValueByFeature(
@@ -822,17 +821,22 @@
 void NewTabPageUI::OnLoad() {
   base::Value::Dict update;
   update.Set("navigationStartTime", navigation_start_time_.ToJsTime());
+  bool driveModuleEnabled = NewTabPageUI::IsDriveModuleEnabled(profile_);
+  bool anyModuleEnabled = driveModuleEnabled;
+  for (const auto& nameFeature : kModuleFeatures) {
+    anyModuleEnabled |= base::FeatureList::IsEnabled(nameFeature.second);
+  }
   // Only enable modules if account credentials are available as most modules
   // won't have data to render otherwise. We can override this behavior with the
   // "--signed-out-ntp-modules" command line switch, e.g. to allow modules in
   // perf tests, which do not support sign-in.
   update.Set("modulesEnabled",
-             base::FeatureList::IsEnabled(ntp_features::kModules) &&
+             anyModuleEnabled &&
+                 !base::FeatureList::IsEnabled(ntp_features::kNtpModulesLoad) &&
                  (base::CommandLine::ForCurrentProcess()->HasSwitch(
                       kSignedOutNtpModulesSwitch) ||
                   HasCredentials(profile_)));
-  update.Set("driveModuleEnabled",
-             NewTabPageUI::IsDriveModuleEnabled(profile_));
+  update.Set("driveModuleEnabled", driveModuleEnabled);
   content::WebUIDataSource::Update(profile_, chrome::kChromeUINewTabPageHost,
                                    std::move(update));
 }
diff --git a/chrome/browser/ui/webui/settings/chromeos/fast_pair_saved_devices_handler.cc b/chrome/browser/ui/webui/settings/chromeos/fast_pair_saved_devices_handler.cc
index 4e399de..4f2d0d0 100644
--- a/chrome/browser/ui/webui/settings/chromeos/fast_pair_saved_devices_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/fast_pair_saved_devices_handler.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ui/webui/settings/chromeos/fast_pair_saved_devices_handler.h"
 
+#include "ash/quick_pair/common/fast_pair/fast_pair_metrics.h"
 #include "ash/quick_pair/common/logging.h"
 #include "ash/quick_pair/repository/fast_pair/fast_pair_image_decoder_impl.h"
 #include "ash/quick_pair/repository/fast_pair_repository.h"
@@ -86,6 +87,7 @@
 
 void FastPairSavedDevicesHandler::OnSavedDeviceDeleted(bool success) {
   QP_LOG(INFO) << __func__ << ": " << (success ? "success" : "failed");
+  ash::quick_pair::RecordSavedDevicesRemoveResult(/*success=*/success);
 }
 
 void FastPairSavedDevicesHandler::HandleLoadSavedDevicePage(
diff --git a/chrome/browser/ui/webui/settings/chromeos/fast_pair_saved_devices_handler_unittest.cc b/chrome/browser/ui/webui/settings/chromeos/fast_pair_saved_devices_handler_unittest.cc
index 78abec8d..8b81f61c 100644
--- a/chrome/browser/ui/webui/settings/chromeos/fast_pair_saved_devices_handler_unittest.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/fast_pair_saved_devices_handler_unittest.cc
@@ -20,6 +20,7 @@
 #include "base/task/thread_pool/thread_pool_instance.h"
 #include "base/test/bind.h"
 #include "base/test/gmock_callback_support.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/mock_callback.h"
 #include "base/test/task_environment.h"
 #include "content/public/test/test_web_ui.h"
@@ -84,6 +85,9 @@
                                            0xF7, 0xB8, 0xCF, 0x5E, 0x3F, 0x45,
                                            0x61, 0xC3, 0x36, 0x1D};
 
+const char kSavedDeviceRemoveResultMetricName[] =
+    "Bluetooth.ChromeOS.FastPair.SavedDevices.Remove.Result";
+
 nearby::fastpair::FastPairDevice CreateFastPairDevice(
     const std::string device_name,
     const std::string image_bytes,
@@ -265,8 +269,11 @@
                                          &base::Value::AsListValue(args));
   }
 
+  base::HistogramTester& histogram_tester() { return histogram_tester_; }
+
  protected:
   base::test::TaskEnvironment task_environment_;
+  base::HistogramTester histogram_tester_;
   ash::quick_pair::FakeFastPairRepository fast_pair_repository_;
   gfx::Image test_image_;
   ash::quick_pair::MockFastPairImageDecoder* mock_decoder_;
@@ -520,6 +527,10 @@
       /*account_key2=*/kAccountKey2, /*device_name3=*/kDeviceName3,
       /*expected_device_url3=*/kDisplayUrlBase64,
       /*account_key3=*/kAccountKey3);
+  histogram_tester().ExpectBucketCount(kSavedDeviceRemoveResultMetricName,
+                                       /*success=*/true, 0);
+  histogram_tester().ExpectBucketCount(kSavedDeviceRemoveResultMetricName,
+                                       /*success=*/false, 0);
 
   RemoveDevice(kAccountKey3);
   base::RunLoop().RunUntilIdle();
@@ -541,6 +552,11 @@
                          /*expected_device_name=*/kDeviceName2,
                          /*expected_base64_image_url=*/kDisplayUrlBase64,
                          /*expected_account_key=*/kAccountKey2));
+
+  histogram_tester().ExpectBucketCount(kSavedDeviceRemoveResultMetricName,
+                                       /*success=*/true, 1);
+  histogram_tester().ExpectBucketCount(kSavedDeviceRemoveResultMetricName,
+                                       /*success=*/false, 0);
 }
 
 }  // namespace chromeos::settings
diff --git a/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/PlaybackStateObserverUnitTest.java b/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/PlaybackStateObserverUnitTest.java
index ee2b52d..5829354 100644
--- a/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/PlaybackStateObserverUnitTest.java
+++ b/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/PlaybackStateObserverUnitTest.java
@@ -13,7 +13,7 @@
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.browser.video_tutorials.PlaybackStateObserver.WatchStateInfo;
 import org.chromium.chrome.browser.video_tutorials.PlaybackStateObserver.WatchStateInfo.State;
@@ -36,7 +36,7 @@
 
     @Before
     public void setUp() {
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
         MockitoAnnotations.initMocks(this);
 
         mPlaybackStateObserver =
diff --git a/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/languages/LanguagePickerMediatorUnitTest.java b/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/languages/LanguagePickerMediatorUnitTest.java
index 9412ea9..1757710 100644
--- a/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/languages/LanguagePickerMediatorUnitTest.java
+++ b/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/languages/LanguagePickerMediatorUnitTest.java
@@ -17,7 +17,7 @@
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.browser.video_tutorials.FeatureType;
 import org.chromium.chrome.browser.video_tutorials.LanguageInfoProvider;
@@ -46,7 +46,7 @@
 
     @Before
     public void setUp() {
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
         MockitoAnnotations.initMocks(this);
 
         mModel = new PropertyModel(LanguagePickerProperties.ALL_KEYS);
diff --git a/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/metrics/VideoTutorialMetricsUnitTest.java b/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/metrics/VideoTutorialMetricsUnitTest.java
index 188cc41..9bb2175d 100644
--- a/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/metrics/VideoTutorialMetricsUnitTest.java
+++ b/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/metrics/VideoTutorialMetricsUnitTest.java
@@ -9,9 +9,9 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
 
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.browser.video_tutorials.FeatureType;
 import org.chromium.chrome.browser.video_tutorials.metrics.VideoTutorialMetrics.UserAction;
@@ -21,16 +21,15 @@
  * Tests for {@link VideoTutorialMetrics}.
  */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(shadows = {ShadowRecordHistogram.class})
 public class VideoTutorialMetricsUnitTest {
     @Before
     public void setUp() {
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
     }
 
     @After
     public void tearDown() {
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
     }
 
     @Test
@@ -44,23 +43,23 @@
         VideoTutorialMetrics.recordUserAction(
                 FeatureType.VOICE_SEARCH, UserAction.BACK_PRESS_WHEN_SHOWING_VIDEO_PLAYER);
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "VideoTutorials.ChromeIntro.Player.UserAction", UserAction.SHARE));
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "VideoTutorials.Download.Player.UserAction", UserAction.CLOSE));
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "VideoTutorials.Search.Player.UserAction", UserAction.CHANGE_LANGUAGE));
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "VideoTutorials.VoiceSearch.Player.UserAction",
                         UserAction.WATCH_NEXT_VIDEO));
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "VideoTutorials.Search.Player.UserAction", UserAction.TRY_NOW));
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "VideoTutorials.VoiceSearch.Player.UserAction",
                         UserAction.BACK_PRESS_WHEN_SHOWING_VIDEO_PLAYER));
     }
@@ -75,19 +74,19 @@
         VideoTutorialMetrics.recordWatchStateUpdate(FeatureType.CHROME_INTRO, WatchState.RESUMED);
         VideoTutorialMetrics.recordWatchStateUpdate(FeatureType.CHROME_INTRO, WatchState.WATCHED);
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "VideoTutorials.ChromeIntro.Player.Progress", WatchState.STARTED));
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "VideoTutorials.ChromeIntro.Player.Progress", WatchState.COMPLETED));
         Assert.assertEquals(2,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "VideoTutorials.ChromeIntro.Player.Progress", WatchState.PAUSED));
         Assert.assertEquals(2,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "VideoTutorials.ChromeIntro.Player.Progress", WatchState.RESUMED));
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "VideoTutorials.ChromeIntro.Player.Progress", WatchState.WATCHED));
     }
 }
diff --git a/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/player/VideoPlayerMediatorUnitTest.java b/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/player/VideoPlayerMediatorUnitTest.java
index 77007f1e..05ae9efb 100644
--- a/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/player/VideoPlayerMediatorUnitTest.java
+++ b/chrome/browser/video_tutorials/internal/android/java/src/org/chromium/chrome/browser/video_tutorials/player/VideoPlayerMediatorUnitTest.java
@@ -24,7 +24,7 @@
 import org.mockito.MockitoAnnotations;
 
 import org.chromium.base.Callback;
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.browser.video_tutorials.FeatureType;
 import org.chromium.chrome.browser.video_tutorials.PlaybackStateObserver;
@@ -71,7 +71,7 @@
 
     @Before
     public void setUp() {
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
         MockitoAnnotations.initMocks(this);
 
         Mockito.doReturn(mNavigationController).when(mWebContents).getNavigationController();
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index af8c9fe30..85fe3c20 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1657799781-a01447279cac714c4f98702cdc6aba4e280774a3.profdata
+chrome-linux-main-1657821506-6c419cda5e54292d3d9a3ca03a0ead4e7c462714.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 16ff78c..0e1939a1 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1657799781-c64dfe6a5c2e378641e259e3b6fa2497ebeba521.profdata
+chrome-mac-main-1657821506-a179f92b0df65c063a21ffe27bfb71d5f0e21ee6.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 20127f6f..f808f7b3 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1657799781-bdb9803ecc4b0b098e660a5b7f5c84e99210c2eb.profdata
+chrome-win32-main-1657809902-2584e57de6db21c3a9d49b665847507731b82758.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 8474fed..4a8cafe 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1657799781-8abf5c58805234b19f6ff14d70e561c65e92b7a8.profdata
+chrome-win64-main-1657809902-d9943c8ff2ee9aa21b84a79c4cadeb564eb3ecc9.profdata
diff --git a/chrome/common/webui_url_constants.cc b/chrome/common/webui_url_constants.cc
index 1d2cb98..dc6e56b 100644
--- a/chrome/common/webui_url_constants.cc
+++ b/chrome/common/webui_url_constants.cc
@@ -243,6 +243,7 @@
 #else
 const char kChromeUIAppServiceInternalsHost[] = "app-service-internals";
 const char kChromeUINearbyInternalsHost[] = "nearby-internals";
+const char kChromeUINearbyInternalsURL[] = "chrome://nearby-internals";
 const char kChromeUIBookmarksSidePanelHost[] =
     "bookmarks-side-panel.top-chrome";
 const char kChromeUIBookmarksSidePanelURL[] =
@@ -330,6 +331,8 @@
 const char kChromeUIMobileSetupHost[] = "mobilesetup";
 const char kChromeUIMobileSetupURL[] = "chrome://mobilesetup/";
 const char kChromeUIMultiDeviceInternalsHost[] = "multidevice-internals";
+const char kChromeUIMultiDeviceInternalsURL[] =
+    "chrome://multidevice-internals";
 const char kChromeUIMultiDeviceSetupHost[] = "multidevice-setup";
 const char kChromeUIMultiDeviceSetupUrl[] = "chrome://multidevice-setup";
 const char kChromeUINetworkHost[] = "network";
@@ -391,6 +394,8 @@
 const char kOsUIHistogramsURL[] = "os://histograms";
 const char kOsUIInvalidationsURL[] = "os://invalidations";
 const char kOsUILockScreenNetworkURL[] = "os://lock-network";
+const char kOsUIMultiDeviceInternalsURL[] = "os://multidevice-internals";
+const char kOsUINearbyInternalsURL[] = "os://nearby-internals";
 const char kOsUINetworkURL[] = "os://network";
 const char kOsUINetExportURL[] = "os://net-export";
 const char kOsUIRestartURL[] = "os://restart";
diff --git a/chrome/common/webui_url_constants.h b/chrome/common/webui_url_constants.h
index b45428c..3fc2ab5 100644
--- a/chrome/common/webui_url_constants.h
+++ b/chrome/common/webui_url_constants.h
@@ -232,6 +232,7 @@
 #else
 extern const char kChromeUIAppServiceInternalsHost[];
 extern const char kChromeUINearbyInternalsHost[];
+extern const char kChromeUINearbyInternalsURL[];
 extern const char kChromeUIBookmarksSidePanelHost[];
 extern const char kChromeUIBookmarksSidePanelURL[];
 extern const char kChromeUICustomizeChromeSidePanelHost[];
@@ -308,6 +309,7 @@
 extern const char kChromeUIMobileSetupHost[];
 extern const char kChromeUIMobileSetupURL[];
 extern const char kChromeUIMultiDeviceInternalsHost[];
+extern const char kChromeUIMultiDeviceInternalsURL[];
 extern const char kChromeUIMultiDeviceSetupHost[];
 extern const char kChromeUIMultiDeviceSetupUrl[];
 extern const char kChromeUINetworkHost[];
@@ -365,6 +367,8 @@
 extern const char kOsUIInvalidationsURL[];
 extern const char kOsUILockScreenNetworkURL[];
 extern const char kOsUINetExportURL[];
+extern const char kOsUIMultiDeviceInternalsURL[];
+extern const char kOsUINearbyInternalsURL[];
 extern const char kOsUINetworkURL[];
 extern const char kOsUIRestartURL[];
 extern const char kOsUISettingsURL[];
diff --git a/chrome/install_static/chromium_install_modes.cc b/chrome/install_static/chromium_install_modes.cc
index d0977d70..3f8426125 100644
--- a/chrome/install_static/chromium_install_modes.cc
+++ b/chrome/install_static/chromium_install_modes.cc
@@ -22,8 +22,6 @@
 
 const char kSafeBrowsingName[] = "chromium";
 
-const char kDeviceManagementServerHostName[] = "";
-
 const InstallConstants kInstallModes[] = {
     // The primary (and only) install mode for Chromium.
     {
diff --git a/chrome/install_static/google_chrome_install_modes.cc b/chrome/install_static/google_chrome_install_modes.cc
index df34b96..0972cc9 100644
--- a/chrome/install_static/google_chrome_install_modes.cc
+++ b/chrome/install_static/google_chrome_install_modes.cc
@@ -22,8 +22,6 @@
 
 const char kSafeBrowsingName[] = "googlechrome";
 
-const char kDeviceManagementServerHostName[] = "m.google.com";
-
 const InstallConstants kInstallModes[] = {
     // The primary install mode for stable Google Chrome.
     {
diff --git a/chrome/install_static/install_modes.h b/chrome/install_static/install_modes.h
index 2238d3e..7b3a61c5 100644
--- a/chrome/install_static/install_modes.h
+++ b/chrome/install_static/install_modes.h
@@ -60,10 +60,6 @@
 // The brand-specific safe browsing client name.
 extern const char kSafeBrowsingName[];
 
-// The brand-specific device management server hostname, or an empty string if
-// the brand does not restrict runtime-configured server URLs.
-extern const char kDeviceManagementServerHostName[];
-
 // A brand's collection of install modes.
 extern const InstallConstants kInstallModes[];
 
diff --git a/chrome/install_static/install_util.cc b/chrome/install_static/install_util.cc
index 8d56a2b3..8b9f9020 100644
--- a/chrome/install_static/install_util.cc
+++ b/chrome/install_static/install_util.cc
@@ -410,21 +410,6 @@
   return kSafeBrowsingName;
 }
 
-const char* GetDeviceManagementServerHostName() {
-  static constexpr char kNoRestriction[] = "";
-
-  // If this brand doesn't specify a restriction, return an empty string.
-  if (!*kDeviceManagementServerHostName)
-    return kNoRestriction;
-
-  // Stable and extended stable Google Chrome are restricted to one host.
-  if (GetChromeChannel() == version_info::Channel::STABLE)
-    return kDeviceManagementServerHostName;
-
-  // Otherwise, return an empty string to indicate no restriction.
-  return kNoRestriction;
-}
-
 bool GetCollectStatsConsent() {
   bool enabled = true;
 
diff --git a/chrome/install_static/install_util.h b/chrome/install_static/install_util.h
index 676b7f8..0a53d20 100644
--- a/chrome/install_static/install_util.h
+++ b/chrome/install_static/install_util.h
@@ -164,10 +164,6 @@
 // Returns the brand-specific safe browsing client name.
 std::string GetSafeBrowsingName();
 
-// Returns the hostname of the device management server url or an empty string
-// if the brand does not restrict runtime-configured server URLs.
-const char* GetDeviceManagementServerHostName();
-
 // Returns true if usage stats collecting is enabled for this user for the
 // current executable.
 bool GetCollectStatsConsent();
diff --git a/chrome/renderer/extensions/app_hooks_delegate.cc b/chrome/renderer/extensions/app_hooks_delegate.cc
index d3bc2cc..b48ca31 100644
--- a/chrome/renderer/extensions/app_hooks_delegate.cc
+++ b/chrome/renderer/extensions/app_hooks_delegate.cc
@@ -155,7 +155,7 @@
       base::Value::ToUniquePtrValue(extension->manifest()->value()->Clone()));
   manifest_copy->SetStringKey("id", extension->id());
   return content::V8ValueConverter::Create()->ToV8Value(
-      manifest_copy.get(), script_context->v8_context());
+      *manifest_copy, script_context->v8_context());
 }
 
 void AppHooksDelegate::GetInstallState(ScriptContext* script_context,
diff --git a/chrome/renderer/sandbox_status_extension_android.cc b/chrome/renderer/sandbox_status_extension_android.cc
index baa8043..39b4431f 100644
--- a/chrome/renderer/sandbox_status_extension_android.cc
+++ b/chrome/renderer/sandbox_status_extension_android.cc
@@ -8,6 +8,7 @@
 
 #include "base/android/build_info.h"
 #include "base/bind.h"
+#include "base/check.h"
 #include "base/files/file_util.h"
 #include "base/task/thread_pool.h"
 #include "chrome/common/url_constants.h"
@@ -151,6 +152,8 @@
   if (!render_frame())
     return;
 
+  CHECK(status);
+
   v8::Isolate* isolate = blink::MainThreadIsolate();
   v8::HandleScope handle_scope(isolate);
   v8::Local<v8::Context> context =
@@ -160,7 +163,7 @@
       v8::Local<v8::Function>::New(isolate, *callback);
 
   v8::Local<v8::Value> argv[] = {
-      content::V8ValueConverter::Create()->ToV8Value(status.get(), context)};
+      content::V8ValueConverter::Create()->ToV8Value(*status, context)};
   render_frame()->GetWebFrame()->CallFunctionEvenIfScriptDisabled(
       callback_local, v8::Object::New(isolate), 1, argv);
 }
diff --git a/chrome/services/system_signals/linux/linux_system_signals_service_unittest.cc b/chrome/services/system_signals/linux/linux_system_signals_service_unittest.cc
index ddbf64d..85de08f3 100644
--- a/chrome/services/system_signals/linux/linux_system_signals_service_unittest.cc
+++ b/chrome/services/system_signals/linux/linux_system_signals_service_unittest.cc
@@ -41,7 +41,7 @@
 
 // Tests that GetFileSystemSignals forwards the signal collection to
 // FileSystemService.
-TEST_F(LinuxSystemSignalsServiceTest, GetFileSystemSignals) {
+TEST_F(LinuxSystemSignalsServiceTest, DISABLED_GetFileSystemSignals) {
   device_signals::GetFileSystemInfoOptions options;
   options.file_path = base::FilePath::FromUTF8Unsafe("/some/file/path");
 
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 044951e..719b51e 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -2445,6 +2445,7 @@
         "../browser/ozone_platform_browsertest.cc",
         "../browser/ui/views/frame/desktop_browser_frame_aura_linux_browsertest.cc",
       ]
+      deps += [ "//ui/linux:linux_ui" ]
     }
 
     if (is_linux || is_chromeos) {
@@ -6272,6 +6273,7 @@
       "../browser/ui/android/tab_model/tab_model_list_unittest.cc",
       "../browser/ui/android/toolbar/location_bar_model_android_unittest.cc",
       "../browser/ui/autofill/payments/autofill_error_dialog_controller_impl_unittest.cc",
+      "../browser/ui/autofill/payments/autofill_progress_dialog_controller_impl_unittest.cc",
       "../browser/ui/autofill/payments/autofill_snackbar_controller_impl_unittest.cc",
     ]
 
@@ -6870,7 +6872,7 @@
       sources += [ "../browser/process_singleton_posix_unittest.cc" ]
     }
     if (is_linux) {
-      deps += [ "//ui/views/linux_ui:linux_ui_factory" ]
+      deps += [ "//ui/linux:linux_ui_factory" ]
     }
     if (use_ozone) {
       deps += [ "//ui/ozone" ]
@@ -8545,6 +8547,7 @@
     ]
     if (is_linux) {
       sources += [ "../browser/ui/views/frame/browser_frame_view_layout_linux_native_unittest.cc" ]
+      deps += [ "//ui/linux:linux_ui" ]
     }
     if (enable_plugins) {
       sources += [ "../browser/ui/views/hung_plugin_tab_helper_unittest.cc" ]
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/webapps/WebApkIntentDataProviderBuilder.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/webapps/WebApkIntentDataProviderBuilder.java
index a1111f4a..8f6a5b98 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/webapps/WebApkIntentDataProviderBuilder.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/webapps/WebApkIntentDataProviderBuilder.java
@@ -26,6 +26,7 @@
     private @DisplayMode.EnumType int mDisplayMode = DisplayMode.STANDALONE;
     private String mManifestUrl;
     private int mWebApkVersionCode;
+    private String mManifestId;
 
     public WebApkIntentDataProviderBuilder(String webApkPackageName, String url) {
         mWebApkPackageName = webApkPackageName;
@@ -48,6 +49,17 @@
         mWebApkVersionCode = versionCode;
     }
 
+    public void setWebApkManifestId(String manifestId) {
+        mManifestId = manifestId;
+    }
+
+    private String manifestId() {
+        if (mManifestId == null) {
+            return mUrl;
+        }
+        return mManifestId;
+    }
+
     /**
      * Builds {@link BrowserServicesIntentDataProvider} object using options that have been set.
      */
@@ -56,8 +68,8 @@
                 null, mDisplayMode, ScreenOrientationLockType.DEFAULT, ShortcutSource.UNKNOWN,
                 ColorUtils.INVALID_COLOR, ColorUtils.INVALID_COLOR, Color.WHITE,
                 false /* isPrimaryIconMaskable */, false /* isSplashIconMaskable */,
-                mWebApkPackageName, /* shellApkVersion */ 1, mManifestUrl, mUrl,
-                null /* manifestId*/, null /* appId*/, WebApkDistributor.BROWSER,
+                mWebApkPackageName, /* shellApkVersion */ 1, mManifestUrl, mUrl, manifestId(),
+                null /*appKey*/, WebApkDistributor.BROWSER,
                 new HashMap<String, String>() /* iconUrlToMurmur2HashMap */, null,
                 false /* forceNavigation */, false /* isSplashProvidedByWebApk */, null,
                 new ArrayList<>() /* shortcutItems */, mWebApkVersionCode);
diff --git a/chrome/test/base/chromeos/fake_ash_test_chrome_browser_main_extra_parts.cc b/chrome/test/base/chromeos/fake_ash_test_chrome_browser_main_extra_parts.cc
index 2d9ad525..05ac6f6 100644
--- a/chrome/test/base/chromeos/fake_ash_test_chrome_browser_main_extra_parts.cc
+++ b/chrome/test/base/chromeos/fake_ash_test_chrome_browser_main_extra_parts.cc
@@ -4,6 +4,7 @@
 
 #include "fake_ash_test_chrome_browser_main_extra_parts.h"
 
+#include "ash/multi_device_setup/multi_device_notification_presenter.h"
 #include "ash/test/ui_controls_factory_ash.h"
 #include "base/command_line.h"
 #include "base/files/file_path.h"
@@ -12,6 +13,7 @@
 #include "chrome/browser/ash/crosapi/crosapi_ash.h"
 #include "chrome/browser/ash/crosapi/crosapi_manager.h"
 #include "chrome/browser/ash/crosapi/test_controller_ash.h"
+#include "chrome/browser/ash/login/signin/signin_error_notifier.h"
 #include "chromeos/services/machine_learning/public/cpp/fake_service_connection.h"
 #include "ui/base/test/ui_controls.h"
 #include "ui/views/input_event_activation_protector.h"
@@ -67,6 +69,11 @@
   crosapi::BrowserManager::Get()->DisableAutoLaunchForTesting();
   views::InputEventActivationProtector::DisableForTesting();
 
+  ignore_signin_errors_ =
+      ash::SigninErrorNotifier::IgnoreSyncErrorsForTesting();
+  ignore_multi_device_notifications_ =
+      ash::MultiDeviceNotificationPresenter::DisableNotificationsForTesting();
+
   // Call this at the end of PostBrowserStart().
   AshIsReadyForTesting();
 }
diff --git a/chrome/test/base/chromeos/fake_ash_test_chrome_browser_main_extra_parts.h b/chrome/test/base/chromeos/fake_ash_test_chrome_browser_main_extra_parts.h
index ab410960..9e8980c 100644
--- a/chrome/test/base/chromeos/fake_ash_test_chrome_browser_main_extra_parts.h
+++ b/chrome/test/base/chromeos/fake_ash_test_chrome_browser_main_extra_parts.h
@@ -7,6 +7,7 @@
 
 #include <memory>
 
+#include "base/auto_reset.h"
 #include "chrome/browser/chrome_browser_main_extra_parts.h"
 
 namespace crosapi {
@@ -30,6 +31,11 @@
   void PostMainMessageLoopRun() override;
 
  private:
+  // Signin errors create a notification. That can interfere with tests.
+  std::unique_ptr<base::AutoReset<bool>> ignore_signin_errors_;
+  // Multi-device notifications are created on first login.
+  std::unique_ptr<base::AutoReset<bool>> ignore_multi_device_notifications_;
+
   std::unique_ptr<crosapi::TestControllerAsh> test_controller_ash_;
 };
 
diff --git a/chromecast/renderer/cast_demo_bindings.cc b/chromecast/renderer/cast_demo_bindings.cc
index 3d955b5e..3febeb3 100644
--- a/chromecast/renderer/cast_demo_bindings.cc
+++ b/chromecast/renderer/cast_demo_bindings.cc
@@ -260,7 +260,7 @@
   std::unique_ptr<content::V8ValueConverter> v8_converter =
       content::V8ValueConverter::Create();
   v8::Local<v8::Value> v8_value =
-      v8_converter->ToV8Value(&network_list, context);
+      v8_converter->ToV8Value(network_list, context);
 
   resolver.Get(isolate)
       ->Resolve(context, gin::ConvertToV8(isolate, v8_value))
@@ -296,7 +296,7 @@
 
   std::unique_ptr<content::V8ValueConverter> v8_converter =
       content::V8ValueConverter::Create();
-  v8::Local<v8::Value> v8_value = v8_converter->ToV8Value(&status, context);
+  v8::Local<v8::Value> v8_value = v8_converter->ToV8Value(status, context);
 
   resolver.Get(isolate)
       ->Resolve(context, gin::ConvertToV8(isolate, v8_value))
diff --git a/chromeos/ash/components/network/network_metadata_store.cc b/chromeos/ash/components/network/network_metadata_store.cc
index 5e24a1d..ad1b3bd6 100644
--- a/chromeos/ash/components/network/network_metadata_store.cc
+++ b/chromeos/ash/components/network/network_metadata_store.cc
@@ -4,6 +4,7 @@
 
 #include "chromeos/ash/components/network/network_metadata_store.h"
 
+#include "ash/constants/ash_features.h"
 #include "base/bind.h"
 #include "base/callback_helpers.h"
 #include "base/location.h"
@@ -26,9 +27,9 @@
 namespace chromeos {
 
 namespace {
-
 const char kNetworkMetadataPref[] = "network_metadata";
 const char kLastConnectedTimestampPref[] = "last_connected_timestamp";
+const char kCreationTimestamp[] = "creation_timestamp";
 const char kIsFromSync[] = "is_from_sync";
 const char kOwner[] = "owner";
 const char kExternalModifications[] = "external_modifications";
@@ -41,6 +42,10 @@
 const char kDayOfTrafficCountersAutoReset[] =
     "day_of_traffic_counters_auto_reset";
 
+// Wait two weeks before overwriting the creation timestamp for a given
+// network
+constexpr base::TimeDelta kTwoWeeks = base::Days(14);
+
 std::string GetPath(const std::string& guid, const std::string& subkey) {
   return base::StringPrintf("%s.%s", guid.c_str(), subkey.c_str());
 }
@@ -389,6 +394,37 @@
   SetPref(network_guid, kLastConnectedTimestampPref, base::Value(timestamp_f));
 }
 
+base::Time NetworkMetadataStore::UpdateAndRetrieveWiFiTimestamp(
+    const std::string& network_guid) {
+  DCHECK(base::FeatureList::IsEnabled(ash::features::kHiddenNetworkMigration));
+
+  const NetworkState* network =
+      network_state_handler_->GetNetworkStateFromGuid(network_guid);
+
+  if (!network || network->GetNetworkTechnologyType() !=
+                      NetworkState::NetworkTechnologyType::kWiFi) {
+    return base::Time::UnixEpoch();
+  }
+
+  const base::Value* creation_timestamp =
+      GetPref(network_guid, kCreationTimestamp);
+  const base::Time current_timestamp = base::Time::Now().UTCMidnight();
+
+  if (creation_timestamp &&
+      base::Time::FromDoubleT(creation_timestamp->GetDouble()) + kTwoWeeks <=
+          current_timestamp) {
+    SetPref(network_guid, kCreationTimestamp,
+            base::Value(base::Time::UnixEpoch().ToDoubleT()));
+    return base::Time::UnixEpoch();
+  }
+  if (!creation_timestamp) {
+    SetPref(network_guid, kCreationTimestamp,
+            base::Value(current_timestamp.ToDoubleT()));
+  }
+
+  return current_timestamp;
+}
+
 bool NetworkMetadataStore::GetIsConfiguredBySync(
     const std::string& network_guid) {
   const base::Value* is_from_sync = GetPref(network_guid, kIsFromSync);
diff --git a/chromeos/ash/components/network/network_metadata_store.h b/chromeos/ash/components/network/network_metadata_store.h
index c5f6fd5e..ce02edd3 100644
--- a/chromeos/ash/components/network/network_metadata_store.h
+++ b/chromeos/ash/components/network/network_metadata_store.h
@@ -84,6 +84,12 @@
   void SetLastConnectedTimestamp(const std::string& network_guid,
                                  const base::TimeDelta& timestamp);
 
+  // Returns the timestamp when the network was created, rounded to the
+  // nearest day. Will clear the timestamp of WiFi networks two weeks
+  // after creation and always return base::Time::UnixEpoch() for non-
+  // WiFi networks.
+  base::Time UpdateAndRetrieveWiFiTimestamp(const std::string& network_guid);
+
   // Networks which were added directly from sync data will return true.
   bool GetIsConfiguredBySync(const std::string& network_guid);
 
diff --git a/chromeos/ash/components/network/network_metadata_store_unittest.cc b/chromeos/ash/components/network/network_metadata_store_unittest.cc
index 48a09c5f..480bcee 100644
--- a/chromeos/ash/components/network/network_metadata_store_unittest.cc
+++ b/chromeos/ash/components/network/network_metadata_store_unittest.cc
@@ -4,8 +4,10 @@
 
 #include <memory>
 
+#include "ash/constants/ash_features.h"
 #include "base/callback_helpers.h"
 #include "base/test/metrics/histogram_tester.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
 #include "chromeos/ash/components/network/network_configuration_handler.h"
 #include "chromeos/ash/components/network/network_connection_handler.h"
@@ -166,6 +168,11 @@
   NetworkStateHandler* network_state_handler() {
     return network_state_handler_;
   }
+
+  base::test::SingleThreadTaskEnvironment* task_environment() {
+    return &task_environment_;
+  }
+
   void ResetStore() {
     metadata_store_ = std::make_unique<NetworkMetadataStore>(
         network_configuration_handler_.get(), network_connection_handler_.get(),
@@ -180,7 +187,8 @@
   const user_manager::User* secondary_user_;
 
  private:
-  base::test::SingleThreadTaskEnvironment task_environment_;
+  base::test::SingleThreadTaskEnvironment task_environment_{
+      base::test::TaskEnvironment::TimeSource::MOCK_TIME};
   NetworkStateTestHelper helper_{false /* use_default_devices_and_services */};
   std::unique_ptr<NetworkConfigurationHandler> network_configuration_handler_;
   std::unique_ptr<NetworkConnectionHandler> network_connection_handler_;
@@ -195,6 +203,7 @@
 namespace {
 const char* kGuid = "wifi0";
 const char* kGuid1 = "wifi1";
+const char* kGuid3 = "eth0";
 const char* kConfigWifi0Connectable =
     "{ \"GUID\": \"wifi0\", \"Type\": \"wifi\", \"State\": \"idle\", "
     "  \"Connectable\": true }";
@@ -209,6 +218,9 @@
 const char* kConfigWifi1Shared =
     "{ \"GUID\": \"wifi0\", \"Type\": \"wifi\", \"State\": \"idle\", "
     "  \"Connectable\": true, \"Profile\": \"/profile/default\" }";
+const char* kConfigEthernet =
+    "{ \"GUID\": \"eth0\", \"Type\": \"ethernet\", \"State\": \"idle\", "
+    "  \"Connectable\": true }";
 const char kHasFixedHiddenNetworks[] =
     "metadata_store.has_fixed_hidden_networks";
 }  // namespace
@@ -447,6 +459,52 @@
   ASSERT_FALSE(metadata_store()->GetIsCreatedByUser(kGuid));
 }
 
+TEST_F(NetworkMetadataStoreTest, NetworkCreationTimestampDefault) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(
+      ash::features::kHiddenNetworkMigration);
+  ConfigureService(kConfigWifi0Connectable);
+  EXPECT_EQ(metadata_store()->UpdateAndRetrieveWiFiTimestamp(kGuid),
+            base::Time::Now().UTCMidnight());
+}
+
+TEST_F(NetworkMetadataStoreTest,
+       NetworkCreationTimestampIsEventuallyOverwritten) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(
+      ash::features::kHiddenNetworkMigration);
+  ConfigureService(kConfigWifi0Connectable);
+  EXPECT_EQ(metadata_store()->UpdateAndRetrieveWiFiTimestamp(kGuid),
+            base::Time::Now().UTCMidnight());
+  // Fast forward 2 weeks to check that creation timestamp is
+  // overwritten to avoid permanently tracking networks.
+  task_environment()->FastForwardBy(base::Days(14));
+  EXPECT_EQ(metadata_store()->UpdateAndRetrieveWiFiTimestamp(kGuid),
+            base::Time::UnixEpoch());
+}
+
+TEST_F(NetworkMetadataStoreTest, NetworkCreationTimestampNonWifi) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(
+      ash::features::kHiddenNetworkMigration);
+  ConfigureService(kConfigEthernet);
+  EXPECT_EQ(metadata_store()->UpdateAndRetrieveWiFiTimestamp(kGuid3),
+            base::Time::UnixEpoch());
+}
+
+TEST_F(NetworkMetadataStoreTest, NetworkCreationTimestampNonExistentNetwork) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(
+      ash::features::kHiddenNetworkMigration);
+  EXPECT_EQ(metadata_store()->UpdateAndRetrieveWiFiTimestamp(kGuid),
+            base::Time::UnixEpoch());
+  // Fast forward 2 weeks to check that creation timestamp is always
+  // base::Time::UnixEpoch() for non-existent networks.
+  task_environment()->FastForwardBy(base::Days(14));
+  EXPECT_EQ(metadata_store()->UpdateAndRetrieveWiFiTimestamp(kGuid),
+            base::Time::UnixEpoch());
+}
+
 TEST_F(NetworkMetadataStoreTest, FixSyncedHiddenNetworks) {
   std::string service_path = ConfigureService(kConfigWifi0HiddenUser);
   metadata_store()->OnConfigurationCreated(service_path, kGuid);
diff --git a/chromeos/ash/components/oobe_quick_start/connectivity/BUILD.gn b/chromeos/ash/components/oobe_quick_start/connectivity/BUILD.gn
index 9462e6d..a4c74ace 100644
--- a/chromeos/ash/components/oobe_quick_start/connectivity/BUILD.gn
+++ b/chromeos/ash/components/oobe_quick_start/connectivity/BUILD.gn
@@ -20,6 +20,12 @@
     "target_device_connection_broker_factory.h",
     "target_device_connection_broker_impl.cc",
     "target_device_connection_broker_impl.h",
+    "target_fido_controller.cc",
+    "target_fido_controller.h",
+    "target_fido_controller_factory.cc",
+    "target_fido_controller_factory.h",
+    "target_fido_controller_impl.cc",
+    "target_fido_controller_impl.h",
   ]
 }
 
@@ -44,5 +50,6 @@
   sources = [
     "fast_pair_advertiser_unittest.cc",
     "target_device_connection_broker_impl_unittest.cc",
+    "target_fido_controller_impl_unittest.cc",
   ]
 }
diff --git a/chromeos/ash/components/oobe_quick_start/connectivity/target_device_connection_broker.cc b/chromeos/ash/components/oobe_quick_start/connectivity/target_device_connection_broker.cc
index 3e90bba..ed83e61 100644
--- a/chromeos/ash/components/oobe_quick_start/connectivity/target_device_connection_broker.cc
+++ b/chromeos/ash/components/oobe_quick_start/connectivity/target_device_connection_broker.cc
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "chromeos/ash/components/oobe_quick_start/connectivity/target_device_connection_broker.h"
+
 namespace ash::quick_start {
 
 // TODO impl
diff --git a/chromeos/ash/components/oobe_quick_start/connectivity/target_fido_controller.cc b/chromeos/ash/components/oobe_quick_start/connectivity/target_fido_controller.cc
new file mode 100644
index 0000000..3e90bba
--- /dev/null
+++ b/chromeos/ash/components/oobe_quick_start/connectivity/target_fido_controller.cc
@@ -0,0 +1,9 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+namespace ash::quick_start {
+
+// TODO impl
+
+}  // namespace ash::quick_start
diff --git a/chromeos/ash/components/oobe_quick_start/connectivity/target_fido_controller.h b/chromeos/ash/components/oobe_quick_start/connectivity/target_fido_controller.h
new file mode 100644
index 0000000..1abd8795
--- /dev/null
+++ b/chromeos/ash/components/oobe_quick_start/connectivity/target_fido_controller.h
@@ -0,0 +1,30 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_ASH_COMPONENTS_OOBE_QUICK_START_CONNECTIVITY_TARGET_FIDO_CONTROLLER_H_
+#define CHROMEOS_ASH_COMPONENTS_OOBE_QUICK_START_CONNECTIVITY_TARGET_FIDO_CONTROLLER_H_
+
+#include "base/callback.h"
+
+namespace ash::quick_start {
+
+// TargetFidoController initializes the FidoDeviceAuthenticator and the
+// GetAssertionRequestHandler to begin the FIDO CTAP2 Assertion Flow. This class
+// is also responsible for preparing the GetAssertionRequest and dispatching the
+// request.
+class TargetFidoController {
+ public:
+  using ResultCallback = base::OnceCallback<void(bool success)>;
+
+  TargetFidoController() = default;
+
+  virtual ~TargetFidoController() = default;
+
+  virtual void RequestAssertion(const std::string& challenge_bytes,
+                                ResultCallback callback) = 0;
+};
+
+}  // namespace ash::quick_start
+
+#endif  // CHROMEOS_ASH_COMPONENTS_OOBE_QUICK_START_CONNECTIVITY_TARGET_FIDO_CONTROLLER_H_
\ No newline at end of file
diff --git a/chromeos/ash/components/oobe_quick_start/connectivity/target_fido_controller_factory.cc b/chromeos/ash/components/oobe_quick_start/connectivity/target_fido_controller_factory.cc
new file mode 100644
index 0000000..c4b73d1
--- /dev/null
+++ b/chromeos/ash/components/oobe_quick_start/connectivity/target_fido_controller_factory.cc
@@ -0,0 +1,31 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/ash/components/oobe_quick_start/connectivity/target_fido_controller_factory.h"
+
+#include "chromeos/ash/components/oobe_quick_start/connectivity/target_fido_controller_impl.h"
+
+namespace ash::quick_start {
+
+// static
+std::unique_ptr<TargetFidoController> TargetFidoControllerFactory::Create(
+    const NearbyConnectionsManager* nearby_connections_manager) {
+  if (test_factory_) {
+    return test_factory_->CreateInstance(nearby_connections_manager);
+  }
+
+  return std::make_unique<TargetFidoControllerImpl>(nearby_connections_manager);
+}
+
+// static
+void TargetFidoControllerFactory::SetFactoryForTesting(
+    TargetFidoControllerFactory* test_factory) {
+  test_factory_ = test_factory;
+}
+
+// static
+TargetFidoControllerFactory* TargetFidoControllerFactory::test_factory_ =
+    nullptr;
+
+}  // namespace ash::quick_start
\ No newline at end of file
diff --git a/chromeos/ash/components/oobe_quick_start/connectivity/target_fido_controller_factory.h b/chromeos/ash/components/oobe_quick_start/connectivity/target_fido_controller_factory.h
new file mode 100644
index 0000000..6af4195
--- /dev/null
+++ b/chromeos/ash/components/oobe_quick_start/connectivity/target_fido_controller_factory.h
@@ -0,0 +1,32 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_ASH_COMPONENTS_OOBE_QUICK_START_CONNECTIVITY_TARGET_FIDO_CONTROLLER_FACTORY_H_
+#define CHROMEOS_ASH_COMPONENTS_OOBE_QUICK_START_CONNECTIVITY_TARGET_FIDO_CONTROLLER_FACTORY_H_
+
+#include "chromeos/ash/components/oobe_quick_start/connectivity/target_fido_controller.h"
+
+namespace ash::quick_start {
+
+class NearbyConnectionsManager;
+
+class TargetFidoControllerFactory {
+ public:
+  static std::unique_ptr<TargetFidoController> Create(
+      const NearbyConnectionsManager* nearby_connections_manager);
+
+  static void SetFactoryForTesting(TargetFidoControllerFactory* test_factory);
+
+ protected:
+  virtual ~TargetFidoControllerFactory() = default;
+  virtual std::unique_ptr<TargetFidoController> CreateInstance(
+      const NearbyConnectionsManager* nearby_connections_manager) = 0;
+
+ private:
+  static TargetFidoControllerFactory* test_factory_;
+};
+
+}  // namespace ash::quick_start
+
+#endif  // CHROMEOS_ASH_COMPONENTS_OOBE_QUICK_START_CONNECTIVITY_TARGET_FIDO_CONTROLLER_FACTORY_H_
diff --git a/chromeos/ash/components/oobe_quick_start/connectivity/target_fido_controller_impl.cc b/chromeos/ash/components/oobe_quick_start/connectivity/target_fido_controller_impl.cc
new file mode 100644
index 0000000..19a0dad
--- /dev/null
+++ b/chromeos/ash/components/oobe_quick_start/connectivity/target_fido_controller_impl.cc
@@ -0,0 +1,27 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/ash/components/oobe_quick_start/connectivity/target_fido_controller_impl.h"
+
+namespace ash::quick_start {
+
+TargetFidoControllerImpl::TargetFidoControllerImpl(
+    const NearbyConnectionsManager* nearby_connections_manager)
+    : nearby_connections_manager_(nearby_connections_manager) {
+  // TODO(jasonrhee@): Uncomment the following line after
+  // NearbyConnectionsManager defined.
+
+  // CHECK(nearby_connections_manager_);
+}
+
+TargetFidoControllerImpl::~TargetFidoControllerImpl() = default;
+
+void TargetFidoControllerImpl::RequestAssertion(
+    const std::string& challenge_bytes,
+    ResultCallback callback) {
+  // TODO(b/234655072): This is a stub and not real logic. Add the actual logic.
+  std::move(callback).Run(/*success=*/true);
+}
+
+}  // namespace ash::quick_start
\ No newline at end of file
diff --git a/chromeos/ash/components/oobe_quick_start/connectivity/target_fido_controller_impl.h b/chromeos/ash/components/oobe_quick_start/connectivity/target_fido_controller_impl.h
new file mode 100644
index 0000000..ccba225
--- /dev/null
+++ b/chromeos/ash/components/oobe_quick_start/connectivity/target_fido_controller_impl.h
@@ -0,0 +1,36 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_ASH_COMPONENTS_OOBE_QUICK_START_CONNECTIVITY_TARGET_FIDO_CONTROLLER_IMPL_H_
+#define CHROMEOS_ASH_COMPONENTS_OOBE_QUICK_START_CONNECTIVITY_TARGET_FIDO_CONTROLLER_IMPL_H_
+
+#include "chromeos/ash/components/oobe_quick_start/connectivity/target_fido_controller.h"
+
+namespace ash::quick_start {
+
+class NearbyConnectionsManager;
+
+class TargetFidoControllerImpl : public TargetFidoController {
+ public:
+  using ResultCallback = TargetFidoController::ResultCallback;
+
+  explicit TargetFidoControllerImpl(
+      const NearbyConnectionsManager* nearby_connections_manager);
+  TargetFidoControllerImpl(const TargetFidoControllerImpl&) = delete;
+  TargetFidoControllerImpl& operator=(const TargetFidoControllerImpl&) = delete;
+  ~TargetFidoControllerImpl() override;
+
+  // TargetFidoController:
+  void RequestAssertion(const std::string& challenge_bytes,
+                        ResultCallback callback) override;
+
+ private:
+  // TODO(b/234655072): Remove maybe_unused tag after NearbyConnectionsManager
+  // defined.
+  [[maybe_unused]] const NearbyConnectionsManager* nearby_connections_manager_;
+};
+
+}  // namespace ash::quick_start
+
+#endif  // CHROMEOS_ASH_COMPONENTS_OOBE_QUICK_START_CONNECTIVITY_TARGET_FIDO_CONTROLLER_IMPL_H_
\ No newline at end of file
diff --git a/chromeos/ash/components/oobe_quick_start/connectivity/target_fido_controller_impl_unittest.cc b/chromeos/ash/components/oobe_quick_start/connectivity/target_fido_controller_impl_unittest.cc
new file mode 100644
index 0000000..ef98cee
--- /dev/null
+++ b/chromeos/ash/components/oobe_quick_start/connectivity/target_fido_controller_impl_unittest.cc
@@ -0,0 +1,55 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/ash/components/oobe_quick_start/connectivity/target_fido_controller_impl.h"
+
+#include "chromeos/ash/components/oobe_quick_start/connectivity/target_fido_controller_factory.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+using TargetFidoController = ash::quick_start::TargetFidoController;
+using TargetFidoControllerImpl = ash::quick_start::TargetFidoControllerImpl;
+using NearbyConnectionsManager = ash::quick_start::NearbyConnectionsManager;
+
+const std::string kChallengeBytes = "testchallenge";
+}  // namespace
+
+class TargetFidoControllerImplTest : public testing::Test {
+ public:
+  TargetFidoControllerImplTest() = default;
+  TargetFidoControllerImplTest(TargetFidoControllerImplTest&) = delete;
+  TargetFidoControllerImplTest& operator=(TargetFidoControllerImplTest&) =
+      delete;
+  ~TargetFidoControllerImplTest() override = default;
+
+  // TODO(b/234655072): Pass in FakeNearbyConnectionsManager when available.
+  void SetUp() override { CreateFidoController(nullptr); }
+
+  void CreateFidoController(
+      const NearbyConnectionsManager* nearby_connections_manager) {
+    fido_controller_ = ash::quick_start::TargetFidoControllerFactory::Create(
+        nearby_connections_manager);
+  }
+
+  void OnRequestAssertion(bool success) {
+    request_assertion_callback_called_ = true;
+    request_assertion_success_ = success;
+  }
+
+ protected:
+  std::unique_ptr<TargetFidoController> fido_controller_;
+  bool request_assertion_callback_called_ = false;
+  bool request_assertion_success_ = false;
+  base::WeakPtrFactory<TargetFidoControllerImplTest> weak_ptr_factory_{this};
+};
+
+TEST_F(TargetFidoControllerImplTest,
+       StartGetAssertionFlow_NoNearbyConnectionsManager) {
+  fido_controller_->RequestAssertion(
+      kChallengeBytes,
+      base::BindOnce(&TargetFidoControllerImplTest::OnRequestAssertion,
+                     weak_ptr_factory_.GetWeakPtr()));
+  EXPECT_TRUE(request_assertion_callback_called_);
+  EXPECT_TRUE(request_assertion_success_);
+}
\ No newline at end of file
diff --git a/chromeos/components/quick_answers/search_result_parsers/search_response_parser.cc b/chromeos/components/quick_answers/search_result_parsers/search_response_parser.cc
index 3cb104d..560b5aa9 100644
--- a/chromeos/components/quick_answers/search_result_parsers/search_response_parser.cc
+++ b/chromeos/components/quick_answers/search_result_parsers/search_response_parser.cc
@@ -49,14 +49,14 @@
     data_decoder::DataDecoder::ValueOrError result) {
   DCHECK(complete_callback_);
 
-  if (!result.value) {
-    LOG(ERROR) << "JSON parsing failed: " << *result.error;
+  if (!result.has_value()) {
+    LOG(ERROR) << "JSON parsing failed: " << result.error();
     std::move(complete_callback_).Run(nullptr);
     return;
   }
 
   // Get the first result.
-  const Value* entries = result.value->FindListPath("results");
+  const Value* entries = result->FindListPath("results");
   if (!entries) {
     std::move(complete_callback_).Run(nullptr);
     return;
diff --git a/chromeos/components/quick_answers/translation_response_parser.cc b/chromeos/components/quick_answers/translation_response_parser.cc
index 48215bf..52c497b2 100644
--- a/chromeos/components/quick_answers/translation_response_parser.cc
+++ b/chromeos/components/quick_answers/translation_response_parser.cc
@@ -36,13 +36,13 @@
     data_decoder::DataDecoder::ValueOrError result) {
   DCHECK(complete_callback_);
 
-  if (!result.value) {
-    LOG(ERROR) << "JSON parsing failed: " << *result.error;
+  if (!result.has_value()) {
+    LOG(ERROR) << "JSON parsing failed: " << result.error();
     std::move(complete_callback_).Run(nullptr);
     return;
   }
 
-  auto* translations = result.value->FindListPath("data.translations");
+  auto* translations = result->FindListPath("data.translations");
   if (!translations) {
     LOG(ERROR) << "Can't find translations result list.";
     std::move(complete_callback_).Run(nullptr);
diff --git a/chromeos/ui/base/window_properties.cc b/chromeos/ui/base/window_properties.cc
index aecb0790..2217222 100644
--- a/chromeos/ui/base/window_properties.cc
+++ b/chromeos/ui/base/window_properties.cc
@@ -55,6 +55,4 @@
                              kWindowStateTypeKey,
                              WindowStateType::kDefault)
 
-DEFINE_UI_CLASS_PROPERTY_KEY(bool, kWindowToggleFloatKey, false)
-
 }  // namespace chromeos
diff --git a/chromeos/ui/base/window_properties.h b/chromeos/ui/base/window_properties.h
index 31c7119..2ad44ea 100644
--- a/chromeos/ui/base/window_properties.h
+++ b/chromeos/ui/base/window_properties.h
@@ -116,10 +116,6 @@
 COMPONENT_EXPORT(CHROMEOS_UI_BASE)
 extern const ui::ClassProperty<WindowStateType>* const kWindowStateTypeKey;
 
-// A property key to toggle Float window state change.
-COMPONENT_EXPORT(CHROMEOS_UI_BASE)
-extern const ui::ClassProperty<bool>* const kWindowToggleFloatKey;
-
 // A property key whose value is shown in alt-tab/overview mode. If non-value
 // is set, the window's title is used.
 COMPONENT_EXPORT(CHROMEOS_UI_BASE)
diff --git a/chromeos/ui/frame/BUILD.gn b/chromeos/ui/frame/BUILD.gn
index 16a88ba..2fbb753 100644
--- a/chromeos/ui/frame/BUILD.gn
+++ b/chromeos/ui/frame/BUILD.gn
@@ -45,6 +45,8 @@
     "immersive/immersive_revealed_lock.h",
     "interior_resize_handler_targeter.cc",
     "interior_resize_handler_targeter.h",
+    "multitask_menu/float_controller_base.cc",
+    "multitask_menu/float_controller_base.h",
     "multitask_menu/multitask_menu.cc",
     "multitask_menu/multitask_menu.h",
   ]
diff --git a/chromeos/ui/frame/caption_buttons/frame_caption_button_container_view.cc b/chromeos/ui/frame/caption_buttons/frame_caption_button_container_view.cc
index 2a7d853..e65a7901 100644
--- a/chromeos/ui/frame/caption_buttons/frame_caption_button_container_view.cc
+++ b/chromeos/ui/frame/caption_buttons/frame_caption_button_container_view.cc
@@ -19,8 +19,8 @@
 #include "chromeos/ui/frame/caption_buttons/frame_size_button.h"
 #include "chromeos/ui/frame/caption_buttons/snap_controller.h"
 #include "chromeos/ui/frame/frame_header.h"
+#include "chromeos/ui/frame/multitask_menu/float_controller_base.h"
 #include "chromeos/ui/vector_icons/vector_icons.h"
-#include "chromeos/ui/wm/window_util.h"
 #include "ui/aura/window_tree_host.h"
 #include "ui/base/hit_test.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -532,9 +532,8 @@
   DCHECK(chromeos::wm::features::IsFloatWindowEnabled());
   aura::Window* window = GetWidget()->GetNativeWindow();
   WindowStateType old_state = window->GetProperty(kWindowStateTypeKey);
-
-  // Float current window.
-  ToggleFloating(window);
+  // Toggle float current window.
+  FloatControllerBase::Get()->ToggleFloat(window);
   UpdateCaptionButtonState(true);
 
   // Update the tooltip if float/unfloat has been successful.
diff --git a/chromeos/ui/frame/multitask_menu/float_controller_base.cc b/chromeos/ui/frame/multitask_menu/float_controller_base.cc
new file mode 100644
index 0000000..732c273
--- /dev/null
+++ b/chromeos/ui/frame/multitask_menu/float_controller_base.cc
@@ -0,0 +1,32 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/ui/frame/multitask_menu/float_controller_base.h"
+
+#include "base/check_op.h"
+
+namespace chromeos {
+
+namespace {
+
+FloatControllerBase* g_instance = nullptr;
+
+}  // namespace
+
+FloatControllerBase::~FloatControllerBase() {
+  DCHECK_EQ(this, g_instance);
+  g_instance = nullptr;
+}
+
+// static
+FloatControllerBase* FloatControllerBase::Get() {
+  return g_instance;
+}
+
+FloatControllerBase::FloatControllerBase() {
+  DCHECK_EQ(nullptr, g_instance);
+  g_instance = this;
+}
+
+}  // namespace chromeos
diff --git a/chromeos/ui/frame/multitask_menu/float_controller_base.h b/chromeos/ui/frame/multitask_menu/float_controller_base.h
new file mode 100644
index 0000000..5685178b
--- /dev/null
+++ b/chromeos/ui/frame/multitask_menu/float_controller_base.h
@@ -0,0 +1,33 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_UI_FRAME_MULTITASK_MENU_FLOAT_CONTROLLER_BASE_H_
+#define CHROMEOS_UI_FRAME_MULTITASK_MENU_FLOAT_CONTROLLER_BASE_H_
+
+#include "base/component_export.h"
+
+namespace aura {
+class Window;
+}
+
+namespace chromeos {
+
+// This interface handles float/unfloat a window.
+// The singleton that implements the interface is provided by Ash.
+class COMPONENT_EXPORT(CHROMEOS_UI_FRAME) FloatControllerBase {
+ public:
+  virtual ~FloatControllerBase();
+
+  static FloatControllerBase* Get();
+
+  // Float the `window` if it's not floated, otherwise unfloat it.
+  virtual void ToggleFloat(aura::Window* window) = 0;
+
+ protected:
+  FloatControllerBase();
+};
+
+}  // namespace chromeos
+
+#endif  // CHROMEOS_UI_FRAME_MULTITASK_MENU_FLOAT_CONTROLLER_BASE_H_
diff --git a/chromeos/ui/wm/BUILD.gn b/chromeos/ui/wm/BUILD.gn
index c1d8fce..22c1059a 100644
--- a/chromeos/ui/wm/BUILD.gn
+++ b/chromeos/ui/wm/BUILD.gn
@@ -16,8 +16,6 @@
     "desks/desks_helper.h",
     "features.cc",
     "features.h",
-    "window_util.cc",
-    "window_util.h",
   ]
 
   deps = [
diff --git a/chromeos/ui/wm/window_util.cc b/chromeos/ui/wm/window_util.cc
deleted file mode 100644
index 8563ad04..0000000
--- a/chromeos/ui/wm/window_util.cc
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chromeos/ui/wm/window_util.h"
-
-#include "chromeos/ui/base/window_properties.h"
-#include "ui/aura/window.h"
-#include "ui/base/class_property.h"
-
-namespace chromeos {
-
-void ToggleFloating(aura::Window* window) {
-  DCHECK(window);
-  const bool window_float_state = window->GetProperty(kWindowToggleFloatKey);
-  window->SetProperty(kWindowToggleFloatKey, !window_float_state);
-}
-
-}  // namespace chromeos
diff --git a/chromeos/ui/wm/window_util.h b/chromeos/ui/wm/window_util.h
deleted file mode 100644
index d072a8e4..0000000
--- a/chromeos/ui/wm/window_util.h
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROMEOS_UI_WM_WINDOW_UTIL_H_
-#define CHROMEOS_UI_WM_WINDOW_UTIL_H_
-
-#include "base/component_export.h"
-
-namespace aura {
-class Window;
-}
-
-namespace chromeos {
-
-// Toggles the float state of `window`.
-COMPONENT_EXPORT(CHROMEOS_UI_BASE)
-void ToggleFloating(aura::Window* window);
-
-}  // namespace chromeos
-
-#endif  // CHROMEOS_UI_WM_WINDOW_UTIL_H_
diff --git a/components/autofill/core/browser/metrics/autofill_metrics.cc b/components/autofill/core/browser/metrics/autofill_metrics.cc
index 7c413d5d..a02cffe 100644
--- a/components/autofill/core/browser/metrics/autofill_metrics.cc
+++ b/components/autofill/core/browser/metrics/autofill_metrics.cc
@@ -1308,13 +1308,42 @@
       "Autofill.OfferNotificationInfoBarOffer.CardLinkedOffer", true);
 }
 
-void AutofillMetrics::LogProgressDialogResultMetric(bool is_canceled_by_user) {
-  base::UmaHistogramBoolean("Autofill.ProgressDialog.CardUnmask.Result",
-                            is_canceled_by_user);
+void AutofillMetrics::LogProgressDialogResultMetric(
+    bool is_canceled_by_user,
+    AutofillProgressDialogType autofill_progress_dialog_type) {
+  std::string dialog_type;
+  switch (autofill_progress_dialog_type) {
+    case AutofillProgressDialogType::kAndroidFIDOProgressDialog:
+      dialog_type = "AndroidFIDO";
+      break;
+    case AutofillProgressDialogType::kVirtualCardUnmaskProgressDialog:
+      dialog_type = "CardUnmask";
+      break;
+    case AutofillProgressDialogType::kUnspecified:
+      NOTREACHED();
+      return;
+  }
+  base::UmaHistogramBoolean(
+      "Autofill.ProgressDialog." + dialog_type + ".Result",
+      is_canceled_by_user);
 }
 
-void AutofillMetrics::LogProgressDialogShown() {
-  base::UmaHistogramBoolean("Autofill.ProgressDialog.CardUnmask.Shown", true);
+void AutofillMetrics::LogProgressDialogShown(
+    AutofillProgressDialogType autofill_progress_dialog_type) {
+  std::string dialog_type;
+  switch (autofill_progress_dialog_type) {
+    case AutofillProgressDialogType::kAndroidFIDOProgressDialog:
+      dialog_type = "AndroidFIDO";
+      break;
+    case AutofillProgressDialogType::kVirtualCardUnmaskProgressDialog:
+      dialog_type = "CardUnmask";
+      break;
+    case AutofillProgressDialogType::kUnspecified:
+      NOTREACHED();
+      return;
+  }
+  base::UmaHistogramBoolean("Autofill.ProgressDialog." + dialog_type + ".Shown",
+                            true);
 }
 
 // static
diff --git a/components/autofill/core/browser/metrics/autofill_metrics.h b/components/autofill/core/browser/metrics/autofill_metrics.h
index 97b4ba35c..65e4895 100644
--- a/components/autofill/core/browser/metrics/autofill_metrics.h
+++ b/components/autofill/core/browser/metrics/autofill_metrics.h
@@ -18,6 +18,7 @@
 #include "base/time/time.h"
 #include "components/autofill/core/browser/autofill_client.h"
 #include "components/autofill/core/browser/autofill_profile_import_process.h"
+#include "components/autofill/core/browser/autofill_progress_dialog_type.h"
 #include "components/autofill/core/browser/data_model/autofill_offer_data.h"
 #include "components/autofill/core/browser/data_model/autofill_profile.h"
 #include "components/autofill/core/browser/field_types.h"
@@ -1458,8 +1459,11 @@
   static void LogOfferNotificationInfoBarResultMetric(
       OfferNotificationInfoBarResultMetric metric);
   static void LogOfferNotificationInfoBarShown();
-  static void LogProgressDialogResultMetric(bool is_canceled_by_user);
-  static void LogProgressDialogShown();
+  static void LogProgressDialogResultMetric(
+      bool is_canceled_by_user,
+      AutofillProgressDialogType autofill_progress_dialog_type);
+  static void LogProgressDialogShown(
+      AutofillProgressDialogType autofill_progress_dialog_type);
   static void LogVirtualCardManualFallbackBubbleShown(bool is_reshow);
   static void LogVirtualCardManualFallbackBubbleResultMetric(
       VirtualCardManualFallbackBubbleResultMetric metric,
diff --git a/components/autofill/core/common/autofill_features.cc b/components/autofill/core/common/autofill_features.cc
index 5892e6d..f331a5fd 100644
--- a/components/autofill/core/common/autofill_features.cc
+++ b/components/autofill/core/common/autofill_features.cc
@@ -274,7 +274,7 @@
 // TODO(crbug.com/1098943): Remove once launched.
 const base::Feature kAutofillEnableSupportForMoreStructureInNames{
     "AutofillEnableSupportForMoreStructureInNames",
-    base::FEATURE_DISABLED_BY_DEFAULT};
+    base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Controls if Autofill supports new structure in addresses.
 // TODO(crbug.com/1098943): Remove once launched.
diff --git a/components/autofill_assistant/browser/actions/collect_user_data_action_unittest.cc b/components/autofill_assistant/browser/actions/collect_user_data_action_unittest.cc
index 3ea9f2ff..b8728402 100644
--- a/components/autofill_assistant/browser/actions/collect_user_data_action_unittest.cc
+++ b/components/autofill_assistant/browser/actions/collect_user_data_action_unittest.cc
@@ -2966,11 +2966,6 @@
             mappings,
             IsSupersetOf({Pair(field_formatter::Key(7), "John Doe"),
                           Pair(field_formatter::Key(14), "+11234567890")}));
-        // Note: Phone number is still getting split, even if it's added with
-        // "raw=true".
-        EXPECT_THAT(mappings,
-                    Not(AnyOf(Contains(Key(field_formatter::Key(3))),
-                              Contains(Key(field_formatter::Key(5))))));
 
         std::move(collect_user_data_options->confirm_callback)
             .Run(&user_data_, &user_model_);
diff --git a/components/cast_channel/cast_message_handler.cc b/components/cast_channel/cast_message_handler.cc
index 32515fcd..29324d4 100644
--- a/components/cast_channel/cast_message_handler.cc
+++ b/components/cast_channel/cast_message_handler.cc
@@ -391,12 +391,12 @@
     const std::string& destination_id,
     const std::string& namespace_,
     data_decoder::DataDecoder::ValueOrError parse_result) {
-  if (!parse_result.value) {
-    ReportParseError(*parse_result.error);
+  if (!parse_result.has_value()) {
+    ReportParseError(parse_result.error());
     return;
   }
 
-  base::Value& payload = *parse_result.value;
+  base::Value& payload = *parse_result;
   if (!payload.is_dict()) {
     ReportParseError("Parsed message not a dictionary");
     return;
diff --git a/components/cast_channel/cast_message_handler_unittest.cc b/components/cast_channel/cast_message_handler_unittest.cc
index c5e2abd..976b493 100644
--- a/components/cast_channel/cast_message_handler_unittest.cc
+++ b/components/cast_channel/cast_message_handler_unittest.cc
@@ -60,7 +60,7 @@
 
 data_decoder::DataDecoder::ValueOrError ParseJsonLikeDataDecoder(
     base::StringPiece json) {
-  return data_decoder::DataDecoder::ValueOrError::Value(ParseJson(json));
+  return ParseJson(json);
 }
 
 absl::optional<base::Value::Dict> GetDictionaryFromCastMessage(
diff --git a/components/desks_storage/BUILD.gn b/components/desks_storage/BUILD.gn
index 194c07a7..88428c1 100644
--- a/components/desks_storage/BUILD.gn
+++ b/components/desks_storage/BUILD.gn
@@ -62,6 +62,7 @@
     "core/desk_model_wrapper_unittests.cc",
     "core/desk_sync_bridge_unittest.cc",
     "core/desk_template_conversion_unittests.cc",
+    "core/desk_template_semantics_unittests.cc",
     "core/desk_template_util_unittests.cc",
     "core/desk_test_util_unittests.cc",
     "core/local_desk_data_manager_unittests.cc",
diff --git a/components/desks_storage/core/desk_template_conversion.cc b/components/desks_storage/core/desk_template_conversion.cc
index 67efeec..252d19da 100644
--- a/components/desks_storage/core/desk_template_conversion.cc
+++ b/components/desks_storage/core/desk_template_conversion.cc
@@ -571,10 +571,12 @@
 void FillWindowInfoFromJson(const base::Value& app,
                             app_restore::WindowInfo* out_window_info) {
   std::string window_state;
+  chromeos::WindowStateType cros_window_state =
+      chromeos::WindowStateType::kDefault;
   if (GetString(app, kWindowState, &window_state) &&
       IsValidWindowState(window_state)) {
-    out_window_info->window_state_type.emplace(
-        ToChromeOsWindowState(window_state));
+    cros_window_state = ToChromeOsWindowState(window_state);
+    out_window_info->window_state_type.emplace(cros_window_state);
   }
 
   std::string app_type;
@@ -608,7 +610,8 @@
 
   std::string pre_minimized_window_state;
   if (GetString(app, kPreMinimizedWindowState, &pre_minimized_window_state) &&
-      IsValidWindowState(pre_minimized_window_state)) {
+      IsValidWindowState(pre_minimized_window_state) &&
+      cros_window_state == chromeos::WindowStateType::kMinimized) {
     out_window_info->pre_minimized_show_state_type.emplace(
         ToUiWindowState(pre_minimized_window_state));
   }
@@ -857,9 +860,11 @@
     app_data.SetKey(kTitle, base::Value(base::UTF16ToUTF8(app->title.value())));
   }
 
+  chromeos::WindowStateType window_state = chromeos::WindowStateType::kDefault;
   if (app->window_state_type.has_value()) {
-    app_data.SetKey(kWindowState, base::Value(ChromeOsWindowStateToString(
-                                      app->window_state_type.value())));
+    window_state = app->window_state_type.value();
+    app_data.SetKey(kWindowState,
+                    base::Value(ChromeOsWindowStateToString(window_state)));
   }
 
   // TODO(crbug.com/1311801): Add support for actual event_flag values.
@@ -908,7 +913,8 @@
                     base::Value(base::NumberToString(app->display_id.value())));
   }
 
-  if (app->pre_minimized_show_state_type.has_value()) {
+  if (app->pre_minimized_show_state_type.has_value() &&
+      window_state == chromeos::WindowStateType::kMinimized) {
     app_data.SetKey(kPreMinimizedWindowState,
                     base::Value(UiWindowStateToString(
                         app->pre_minimized_show_state_type.value())));
diff --git a/components/desks_storage/core/desk_template_conversion_unittests.cc b/components/desks_storage/core/desk_template_conversion_unittests.cc
index fd69f30d..10eabed 100644
--- a/components/desks_storage/core/desk_template_conversion_unittests.cc
+++ b/components/desks_storage/core/desk_template_conversion_unittests.cc
@@ -123,6 +123,64 @@
   EXPECT_EQ(ali->tab_group_infos.value()[0], MakeSampleTabGroup());
   EXPECT_TRUE(wi->window_state_type.has_value());
   EXPECT_EQ(wi->window_state_type.value(), chromeos::WindowStateType::kNormal);
+  EXPECT_TRUE(wi->current_bounds.has_value());
+  EXPECT_EQ(wi->current_bounds.value().x(), 0);
+  EXPECT_EQ(wi->current_bounds.value().y(), 1);
+  EXPECT_EQ(wi->current_bounds.value().height(), 121);
+  EXPECT_EQ(wi->current_bounds.value().width(), 120);
+}
+
+TEST_F(DeskTemplateConversionTest, ParseBrowserTemplateMinimized) {
+  base::StringPiece raw_json =
+      base::StringPiece(desk_test_util::kValidPolicyTemplateBrowserMinimized);
+  auto parsed_json = base::JSONReader::ReadAndReturnValueWithError(raw_json);
+
+  EXPECT_TRUE(parsed_json.has_value());
+  EXPECT_TRUE(parsed_json->is_dict());
+
+  std::unique_ptr<ash::DeskTemplate> dt =
+      desk_template_conversion::ParseDeskTemplateFromSource(
+          *parsed_json, ash::DeskTemplateSource::kPolicy);
+
+  EXPECT_TRUE(dt != nullptr);
+  EXPECT_EQ(dt->uuid(), base::GUID::ParseCaseInsensitive(kTestUuidBrowser));
+  EXPECT_EQ(dt->created_time(),
+            desk_template_conversion::ProtoTimeToTime(1633535632));
+  EXPECT_EQ(dt->template_name(), base::UTF8ToUTF16(kBrowserTemplateName));
+
+  const app_restore::RestoreData* rd = dt->desk_restore_data();
+
+  EXPECT_TRUE(rd != nullptr);
+  EXPECT_EQ(rd->app_id_to_launch_list().size(), 1UL);
+  EXPECT_NE(rd->app_id_to_launch_list().find(app_constants::kChromeAppId),
+            rd->app_id_to_launch_list().end());
+
+  const app_restore::AppRestoreData* ard =
+      rd->GetAppRestoreData(app_constants::kChromeAppId, 0);
+  EXPECT_TRUE(ard != nullptr);
+  EXPECT_TRUE(ard->display_id.has_value());
+  EXPECT_EQ(ard->display_id.value(), 100L);
+  std::unique_ptr<app_restore::AppLaunchInfo> ali =
+      ard->GetAppLaunchInfo(app_constants::kChromeAppId, 0);
+  std::unique_ptr<app_restore::WindowInfo> wi = ard->GetWindowInfo();
+  EXPECT_TRUE(ali != nullptr);
+  EXPECT_TRUE(wi != nullptr);
+  EXPECT_TRUE(ali->window_id.has_value());
+  EXPECT_EQ(ali->window_id.value(), 0);
+  EXPECT_TRUE(ali->display_id.has_value());
+  EXPECT_EQ(ali->display_id.value(), 100L);
+  EXPECT_TRUE(ali->active_tab_index.has_value());
+  EXPECT_EQ(ali->active_tab_index.value(), 1);
+  EXPECT_TRUE(ali->first_non_pinned_tab_index.has_value());
+  EXPECT_EQ(ali->first_non_pinned_tab_index.value(), 1);
+  EXPECT_TRUE(ali->urls.has_value());
+  EXPECT_EQ(ali->urls.value()[0].spec(), kBrowserUrl1);
+  EXPECT_EQ(ali->urls.value()[1].spec(), kBrowserUrl2);
+  EXPECT_TRUE(ali->tab_group_infos.has_value());
+  EXPECT_EQ(ali->tab_group_infos.value()[0], MakeSampleTabGroup());
+  EXPECT_TRUE(wi->window_state_type.has_value());
+  EXPECT_EQ(wi->window_state_type.value(),
+            chromeos::WindowStateType::kMinimized);
   EXPECT_TRUE(wi->pre_minimized_show_state_type.has_value());
   EXPECT_EQ(wi->pre_minimized_show_state_type.value(),
             ui::WindowShowState::SHOW_STATE_NORMAL);
@@ -197,9 +255,6 @@
   EXPECT_TRUE(wi_chrome->window_state_type.has_value());
   EXPECT_EQ(wi_chrome->window_state_type.value(),
             chromeos::WindowStateType::kPrimarySnapped);
-  EXPECT_TRUE(wi_chrome->pre_minimized_show_state_type.has_value());
-  EXPECT_EQ(wi_chrome->pre_minimized_show_state_type.value(),
-            ui::WindowShowState::SHOW_STATE_NORMAL);
   EXPECT_TRUE(wi_chrome->current_bounds.has_value());
   EXPECT_EQ(wi_chrome->current_bounds.value().x(), 200);
   EXPECT_EQ(wi_chrome->current_bounds.value().y(), 200);
@@ -210,9 +265,6 @@
   EXPECT_TRUE(wi_pwa->window_state_type.has_value());
   EXPECT_EQ(wi_pwa->window_state_type.value(),
             chromeos::WindowStateType::kNormal);
-  EXPECT_TRUE(wi_pwa->pre_minimized_show_state_type.has_value());
-  EXPECT_EQ(wi_pwa->pre_minimized_show_state_type.value(),
-            ui::WindowShowState::SHOW_STATE_NORMAL);
   EXPECT_TRUE(wi_pwa->current_bounds.has_value());
   EXPECT_EQ(wi_pwa->current_bounds.value().x(), 0);
   EXPECT_EQ(wi_pwa->current_bounds.value().y(), 0);
diff --git a/components/desks_storage/core/desk_template_semantics_unittests.cc b/components/desks_storage/core/desk_template_semantics_unittests.cc
new file mode 100644
index 0000000..29e0fa6
--- /dev/null
+++ b/components/desks_storage/core/desk_template_semantics_unittests.cc
@@ -0,0 +1,103 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <string>
+
+#include "ash/public/cpp/desk_template.h"
+#include "base/json/json_reader.h"
+#include "base/test/task_environment.h"
+#include "components/account_id/account_id.h"
+#include "components/desks_storage/core/desk_sync_bridge.h"
+#include "components/desks_storage/core/desk_template_conversion.h"
+#include "components/desks_storage/core/desk_test_util.h"
+#include "components/services/app_service/public/cpp/app_registry_cache.h"
+#include "components/sync/protocol/workspace_desk_specifics.pb.h"
+#include "components/sync/test/model/mock_model_type_change_processor.h"
+#include "components/sync/test/model/model_type_store_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace desks_storage {
+
+namespace {
+
+base::Value PerformPolicyRoundtrip(const base::Value& expected,
+                                   DeskSyncBridge* bridge,
+                                   apps::AppRegistryCache* cache) {
+  std::unique_ptr<ash::DeskTemplate> policy_dt =
+      desk_template_conversion::ParseDeskTemplateFromSource(
+          expected, ash::DeskTemplateSource::kPolicy);
+
+  EXPECT_TRUE(policy_dt != nullptr);
+
+  sync_pb::WorkspaceDeskSpecifics proto_desk =
+      bridge->ToSyncProto(policy_dt.get());
+
+  // Convert back to original format.
+  std::unique_ptr<ash::DeskTemplate> got_dt =
+      DeskSyncBridge::FromSyncProto(proto_desk);
+  return desk_template_conversion::SerializeDeskTemplateAsPolicy(got_dt.get(),
+                                                                 cache);
+}
+
+}  // namespace
+
+class DeskTemplateSemanticsTest : public testing::TestWithParam<std::string> {
+ public:
+  DeskTemplateSemanticsTest(const DeskTemplateSemanticsTest&) = delete;
+  DeskTemplateSemanticsTest& operator=(const DeskTemplateSemanticsTest&) =
+      delete;
+
+ protected:
+  DeskTemplateSemanticsTest()
+      : cache_(std::make_unique<apps::AppRegistryCache>()),
+        account_id_(AccountId::FromUserEmail("test@gmail.com")),
+        store_(syncer::ModelTypeStoreTestUtil::CreateInMemoryStoreForTest()) {}
+
+  // testing::test.
+  void SetUp() override {
+    ON_CALL(mock_processor_, IsTrackingMetadata())
+        .WillByDefault(testing::Return(true));
+    bridge_ = std::make_unique<DeskSyncBridge>(
+        mock_processor_.CreateForwardingProcessor(),
+        syncer::ModelTypeStoreTestUtil::FactoryForForwardingStore(store_.get()),
+        account_id_);
+    desk_test_util::PopulateAppRegistryCache(account_id_, cache_.get());
+  }
+
+  DeskSyncBridge* bridge() { return bridge_.get(); }
+
+  apps::AppRegistryCache* app_cache() { return cache_.get(); }
+
+ private:
+  // In memory model type store needs to be able to post tasks.
+  base::test::TaskEnvironment task_environment_;
+  testing::NiceMock<syncer::MockModelTypeChangeProcessor> mock_processor_;
+  std::unique_ptr<apps::AppRegistryCache> cache_;
+  AccountId account_id_;
+  std::unique_ptr<DeskSyncBridge> bridge_;
+  std::unique_ptr<syncer::ModelTypeStore> store_;
+};
+
+TEST_P(DeskTemplateSemanticsTest, PolicyTemplateSemanticallyEquivalentToProto) {
+  base::StringPiece raw_json = base::StringPiece(GetParam());
+  auto expected_json = base::JSONReader::ReadAndReturnValueWithError(raw_json);
+
+  EXPECT_TRUE(expected_json.has_value());
+  EXPECT_TRUE(expected_json->is_dict());
+
+  base::Value got_json =
+      PerformPolicyRoundtrip(*expected_json, bridge(), app_cache());
+
+  EXPECT_EQ(*expected_json, got_json);
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    PolicySemanticsEquivalencyTest,
+    DeskTemplateSemanticsTest,
+    ::testing::Values(
+        desk_test_util::kValidPolicyTemplateBrowser,
+        desk_test_util::kValidPolicyTemplateBrowserMinimized,
+        desk_test_util::kValidPolicyTemplateChromeAndProgressive));
+
+}  // namespace desks_storage
\ No newline at end of file
diff --git a/components/desks_storage/core/desk_test_util.h b/components/desks_storage/core/desk_test_util.h
index ffd3674..d8d7a800 100644
--- a/components/desks_storage/core/desk_test_util.h
+++ b/components/desks_storage/core/desk_test_util.h
@@ -45,6 +45,23 @@
     "index\":1,\"last_index\":2,\"title\":\"sample_tab_"
     "group\",\"color\":\"GREY\",\"is_collapsed\":false}],\"active_tab_index\":"
     "1,\"first_non_pinned_tab_index\":1,\"window_id\":0,"
+    "\"display_id\":\"100\",\"event_flag\":0}]}}";
+
+const std::string kValidPolicyTemplateBrowserMinimized =
+    "{\"version\":1,\"uuid\":\"" + kTestUuidBrowser + "\",\"name\":\"" +
+    kBrowserTemplateName +
+    "\",\"created_time_usec\":\"1633535632\",\"updated_time_usec\": "
+    "\"1633535632\",\"desk_type\":\"TEMPLATE\",\"desk\":{\"apps\":[{\"window_"
+    "bound\":{\"left\":0,\"top\":1,\"height\":121,\"width\":120},\"window_"
+    "state\":\"MINIMIZED\",\"z_index\":1,\"app_type\":\"BROWSER\",\"tabs\":[{"
+    "\"url\":\"" +
+    kBrowserUrl +
+    "\"},{\"url\":\"https://"
+    "example.com/"
+    "2\"}],\"tab_groups\":[{\"first_"
+    "index\":1,\"last_index\":2,\"title\":\"sample_tab_"
+    "group\",\"color\":\"GREY\",\"is_collapsed\":false}],\"active_tab_index\":"
+    "1,\"first_non_pinned_tab_index\":1,\"window_id\":0,"
     "\"display_id\":\"100\",\"event_flag\":0,\"pre_minimized_window_state\":"
     "\"NORMAL\"}]}}";
 
@@ -59,14 +76,14 @@
     "\"PRIMARY_SNAPPED\",\"z_index\":2,\"app_type\":\"CHROME_APP\",\"app_id\":"
     "\"" +
     desk_test_util::kTestChromeAppId1 +
-    "\",\"window_id\":0,\"display_id\":\"100\",\"event_flag\":0,\"pre_"
-    "minimized_window_state\":\"NORMAL\", \"snap_percent\":75},{\"window_"
+    "\",\"window_id\":0,\"display_id\":\"100\",\"event_flag\":0, "
+    "\"snap_percent\":75},{\"window_"
     "bound\":{\"left\":0,\"top\":0,\"height\":120,\"width\":120},\"window_"
     "state\":\"NORMAL\",\"z_index\":1,\"app_type\":\"CHROME_APP\",\"app_id\":"
     "\"" +
     desk_test_util::kTestPwaAppId1 +
     "\",\"window_id\":1,\"display_id\":"
-    "\"100\",\"event_flag\":0,\"pre_minimized_window_state\":\"NORMAL\"}]}}";
+    "\"100\",\"event_flag\":0}]}}";
 
 const std::string kPolicyTemplateWithoutType =
     "{\"version\":1,\"uuid\":\"" + kTestUuidBrowser + "\",\"name\":\"" +
@@ -81,8 +98,7 @@
     "index\":1,\"last_index\":2,\"title\":\"sample_tab_"
     "group\",\"color\":\"GREY\",\"is_collapsed\":false}],\"active_tab_index\":"
     "1,\"first_non_pinned_tab_index\":1,\"window_id\":0,"
-    "\"display_id\":\"100\",\"event_flag\":0,\"pre_minimized_window_state\":"
-    "\"NORMAL\"}]}}";
+    "\"display_id\":\"100\",\"event_flag\":0}]}}";
 
 // Populates the given cache with test app information.
 void PopulateAppRegistryCache(AccountId account_id,
diff --git a/components/digital_asset_links/digital_asset_links_handler.cc b/components/digital_asset_links/digital_asset_links_handler.cc
index 34ba3a9a..c0a683b 100644
--- a/components/digital_asset_links/digital_asset_links_handler.cc
+++ b/components/digital_asset_links/digital_asset_links_handler.cc
@@ -175,16 +175,16 @@
     absl::optional<std::string> fingerprint,
     std::map<std::string, std::set<std::string>> target_values,
     data_decoder::DataDecoder::ValueOrError result) {
-  if (!result.value) {
+  if (!result.has_value()) {
     AddMessageToConsole(
         web_contents_.get(),
         "Digital Asset Links response parsing failed with message: " +
-            *result.error);
+            result.error());
     std::move(callback_).Run(RelationshipCheckResult::kFailure);
     return;
   }
 
-  auto& statement_list = *result.value;
+  auto& statement_list = *result;
   if (!statement_list.is_list()) {
     std::move(callback_).Run(RelationshipCheckResult::kFailure);
     AddMessageToConsole(web_contents_.get(), "Statement List is not a list.");
diff --git a/components/discardable_memory/service/discardable_shared_memory_manager.cc b/components/discardable_memory/service/discardable_shared_memory_manager.cc
index ad058b8..61c8a45a 100644
--- a/components/discardable_memory/service/discardable_shared_memory_manager.cc
+++ b/components/discardable_memory/service/discardable_shared_memory_manager.cc
@@ -158,20 +158,20 @@
 // Returns the default memory limit to use for discardable memory, taking
 // the amount physical memory available and other platform specific constraints
 // into account.
-int64_t GetDefaultMemoryLimit() {
-  const int kMegabyte = 1024 * 1024;
+uint64_t GetDefaultMemoryLimit() {
+  const uint64_t kMegabyte = 1024ull * 1024;
 
 #if BUILDFLAG(IS_CASTOS) || BUILDFLAG(IS_CAST_ANDROID)
   // Bypass IsLowEndDevice() check and fix max_default_memory_limit to 64MB on
   // Chromecast devices. Set value here as IsLowEndDevice() is used on some, but
   // not all Chromecast devices.
-  int64_t max_default_memory_limit = 64 * kMegabyte;
+  uint64_t max_default_memory_limit = 64 * kMegabyte;
 #else
 #if BUILDFLAG(IS_ANDROID)
   // Limits the number of FDs used to 32, assuming a 4MB allocation size.
-  int64_t max_default_memory_limit = 128 * kMegabyte;
+  uint64_t max_default_memory_limit = 128 * kMegabyte;
 #else
-  int64_t max_default_memory_limit = 512 * kMegabyte;
+  uint64_t max_default_memory_limit = 512 * kMegabyte;
 #endif
 
   // Use 1/8th of discardable memory on low-end devices.
@@ -201,7 +201,8 @@
 
     // Allow 1/2 of available shmem dir space to be used for discardable memory.
     max_default_memory_limit =
-        std::min(max_default_memory_limit, shmem_dir_amount_of_free_space / 2);
+        std::min(max_default_memory_limit,
+                 static_cast<uint64_t>(shmem_dir_amount_of_free_space / 2));
   }
 #endif
 
diff --git a/components/history_clusters_strings.grdp b/components/history_clusters_strings.grdp
index 03a1215..5e05faa 100644
--- a/components/history_clusters_strings.grdp
+++ b/components/history_clusters_strings.grdp
@@ -6,10 +6,12 @@
   <message name="IDS_HISTORY_CLUSTERS_ACTION_MENU_DESCRIPTION" desc="Text used to identify the history clusters drop-down menu for screen readers">
     Actions
   </message>
-  <message name="IDS_HISTORY_CLUSTERS_DISABLE_MENU_ITEM_LABEL" desc="A label for the menu item to turn off the Journeys feature, which collects different search and browsing activities related to a single topic into a single place, and organizes them by topic. See glossary entry for meaning of 'Journeys'.">
+  <message name="IDS_HISTORY_CLUSTERS_DISABLE_MENU_ITEM_LABEL" desc="A label for the menu item to turn off the Journeys feature, which collects different search and browsing activities related to a single topic into a single place, and organizes them by topic. See glossary entry for meaning of 'Journeys'."
+    formatter_data="android_java">
     Turn off Journeys
   </message>
-  <message name="IDS_HISTORY_CLUSTERS_ENABLE_MENU_ITEM_LABEL" desc="A label for the menu item to turn on the Journeys feature, which collects different search and browsing activities related to a single topic into a single place, and organizes them by topic. See glossary entry for meaning of 'Journeys'.">
+  <message name="IDS_HISTORY_CLUSTERS_ENABLE_MENU_ITEM_LABEL" desc="A label for the menu item to turn on the Journeys feature, which collects different search and browsing activities related to a single topic into a single place, and organizes them by topic. See glossary entry for meaning of 'Journeys'."
+    formatter_data="android_java">
     Turn on Journeys
   </message>
   <message name="IDS_HISTORY_CLUSTERS_JOURNEYS_TAB_LABEL" desc="A label for the section in Chrome History (chrome://history) where Journeys are collected and shown to the user. See glossary entry for meaning of 'Journeys'." formatter_data="android_java">
diff --git a/components/invalidation/impl/per_user_topic_subscription_request.cc b/components/invalidation/impl/per_user_topic_subscription_request.cc
index 1539bfc..5bf8e5d 100644
--- a/components/invalidation/impl/per_user_topic_subscription_request.cc
+++ b/components/invalidation/impl/per_user_topic_subscription_request.cc
@@ -189,7 +189,7 @@
 
 void PerUserTopicSubscriptionRequest::OnJsonParse(
     data_decoder::DataDecoder::ValueOrError result) {
-  if (!result.value) {
+  if (!result.has_value()) {
     RecordRequestStatus(SubscriptionStatus::kParsingFailure, type_, topic_);
     RunCompletedCallbackAndMaybeDie(
         Status(StatusCode::FAILED, base::StringPrintf("Body parse error")),
@@ -198,7 +198,7 @@
     return;
   }
 
-  const std::string* topic_name = GetTopicName(*result.value);
+  const std::string* topic_name = GetTopicName(*result);
   if (topic_name) {
     RecordRequestStatus(SubscriptionStatus::kSuccess, type_, topic_);
     RunCompletedCallbackAndMaybeDie(Status(StatusCode::SUCCESS, std::string()),
diff --git a/components/language/android/java/src/org/chromium/components/language/LanguageProfileControllerUnitTest.java b/components/language/android/java/src/org/chromium/components/language/LanguageProfileControllerUnitTest.java
index 34e84ef1..95c97fe 100644
--- a/components/language/android/java/src/org/chromium/components/language/LanguageProfileControllerUnitTest.java
+++ b/components/language/android/java/src/org/chromium/components/language/LanguageProfileControllerUnitTest.java
@@ -14,7 +14,7 @@
 
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.metrics.RecordHistogram;
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 
 import java.util.ArrayList;
@@ -25,11 +25,11 @@
  * Tests for LanguageProfileController.
  */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(manifest = Config.NONE, shadows = {ShadowRecordHistogram.class})
+@Config(manifest = Config.NONE)
 public class LanguageProfileControllerUnitTest {
     @Before
     public void setUp() {
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
         ThreadUtils.setThreadAssertsDisabledForTesting(true);
         mController = new LanguageProfileController(mDelegate);
     }
diff --git a/components/media_message_center/media_notification_background_impl.cc b/components/media_message_center/media_notification_background_impl.cc
index 128b88f..5b6b47c 100644
--- a/components/media_message_center/media_notification_background_impl.cc
+++ b/components/media_message_center/media_notification_background_impl.cc
@@ -300,8 +300,10 @@
     // Draw a gradient to fade the color background and the image together.
     gfx::Rect draw_bounds = GetGradientBounds(*view);
 
-    const SkColor colors[2] = {
-        background_color, SkColorSetA(background_color, SK_AlphaTRANSPARENT)};
+    // TODO(crbug/1308932): Remove FromColor and make all SkColor4f.
+    const SkColor4f colors[2] = {SkColor4f::FromColor(background_color),
+                                 SkColor4f::FromColor(SkColorSetA(
+                                     background_color, SK_AlphaTRANSPARENT))};
     const SkPoint points[2] = {GetGradientStartPoint(draw_bounds),
                                GetGradientEndPoint(draw_bounds)};
 
@@ -319,8 +321,10 @@
     // and the image together.
     gfx::Rect draw_bounds = GetBottomGradientBounds(*view);
 
-    const SkColor colors[2] = {
-        background_color, SkColorSetA(background_color, SK_AlphaTRANSPARENT)};
+    // TODO(crbug/1308932): Remove FromColor and make all SkColor4f.
+    const SkColor4f colors[2] = {SkColor4f::FromColor(background_color),
+                                 SkColor4f::FromColor(SkColorSetA(
+                                     background_color, SK_AlphaTRANSPARENT))};
     const SkPoint points[2] = {gfx::PointToSkPoint(draw_bounds.bottom_center()),
                                gfx::PointToSkPoint(draw_bounds.top_center())};
 
diff --git a/components/messages/android/internal/java/src/org/chromium/components/messages/MessageQueueManagerTest.java b/components/messages/android/internal/java/src/org/chromium/components/messages/MessageQueueManagerTest.java
index a63828a..01ce78a 100644
--- a/components/messages/android/internal/java/src/org/chromium/components/messages/MessageQueueManagerTest.java
+++ b/components/messages/android/internal/java/src/org/chromium/components/messages/MessageQueueManagerTest.java
@@ -31,7 +31,7 @@
 
 import org.chromium.base.ActivityState;
 import org.chromium.base.metrics.RecordHistogram;
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.DisableIf;
 import org.chromium.components.messages.MessageQueueManager.MessageState;
@@ -44,7 +44,7 @@
  * Unit tests for MessageQueueManager.
  */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(manifest = Config.NONE, shadows = {ShadowRecordHistogram.class})
+@Config(manifest = Config.NONE)
 public class MessageQueueManagerTest {
     private MessageQueueDelegate mEmptyDelegate = new MessageQueueDelegate() {
         @Override
@@ -103,7 +103,7 @@
 
     @Before
     public void setUp() {
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
     }
 
     /**
diff --git a/components/metal_util/hdr_copier_layer.mm b/components/metal_util/hdr_copier_layer.mm
index 2914cd0..d49491d 100644
--- a/components/metal_util/hdr_copier_layer.mm
+++ b/components/metal_util/hdr_copier_layer.mm
@@ -146,6 +146,10 @@
       return MTLPixelFormatRGBA16Float;
     case kCVPixelFormatType_ARGB2101010LEPacked:
       return MTLPixelFormatBGR10A2Unorm;
+    case kCVPixelFormatType_32BGRA:
+      return MTLPixelFormatBGRA8Unorm;
+    case kCVPixelFormatType_32RGBA:
+      return MTLPixelFormatRGBA8Unorm;
     default:
       break;
   }
@@ -207,6 +211,7 @@
 API_AVAILABLE(macos(10.15))
 @interface HDRCopierLayer : CAMetalLayer {
   base::scoped_nsprotocol<id<MTLRenderPipelineState>> _render_pipeline_state;
+  gfx::ColorSpace _color_space;
 }
 - (id)init;
 - (void)setHDRContents:(IOSurfaceRef)buffer
@@ -239,10 +244,30 @@
     return;
   }
 
+  // Set metadata for tone mapping.
+  if (_color_space != color_space) {
+    CAEDRMetadata* edr_metadata = nil;
+    switch (color_space.GetTransferID()) {
+      case gfx::ColorSpace::TransferID::PQ:
+        edr_metadata = [CAEDRMetadata HDR10MetadataWithMinLuminance:0
+                                                       maxLuminance:10000
+                                                 opticalOutputScale:100];
+        break;
+      case gfx::ColorSpace::TransferID::HLG:
+        edr_metadata = [CAEDRMetadata HLGMetadata];
+        break;
+      default:
+        [self setEDRMetadata:nil];
+        break;
+    }
+    [self setEDRMetadata:edr_metadata];
+    _color_space = color_space;
+  }
+
   // Migrate to the MTLDevice on which the CAMetalLayer is being composited, if
   // known.
   if ([self respondsToSelector:@selector(preferredDevice)]) {
-    id<MTLDevice> preferred_device = nil;
+    id<MTLDevice> preferred_device = [self preferredDevice];
     if (preferred_device)
       [self setDevice:preferred_device];
   }
diff --git a/components/module_installer/android/junit/src/org/chromium/components/module_installer/logger/SplitInstallFailureLoggerTest.java b/components/module_installer/android/junit/src/org/chromium/components/module_installer/logger/SplitInstallFailureLoggerTest.java
index 134b3dc..e906e711 100644
--- a/components/module_installer/android/junit/src/org/chromium/components/module_installer/logger/SplitInstallFailureLoggerTest.java
+++ b/components/module_installer/android/junit/src/org/chromium/components/module_installer/logger/SplitInstallFailureLoggerTest.java
@@ -13,14 +13,14 @@
 import org.junit.runner.RunWith;
 import org.robolectric.annotation.Config;
 
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 
 /**
  * Test suite for the SplitInstallFailureLogger class.
  */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(manifest = Config.NONE, shadows = {ShadowRecordHistogram.class})
+@Config(manifest = Config.NONE)
 public class SplitInstallFailureLoggerTest {
     private SplitInstallFailureLogger mFailureLogger;
 
@@ -44,7 +44,7 @@
 
     @Before
     public void setUp() {
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
         mFailureLogger = new SplitInstallFailureLogger();
     }
 
@@ -96,14 +96,14 @@
 
         // Act & Assert.
         for (int[] tuple : mErrorCodeMapping) {
-            ShadowRecordHistogram.reset();
+            UmaRecorderHolder.resetForTesting();
             int expectedOutputCode = tuple[0];
             int inputCode = tuple[1];
             mFailureLogger.logStatusFailure(moduleName, inputCode);
             assertEquals(expectedOutputCode, getHistogramStatus(moduleName));
         }
 
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
         mFailureLogger.logStatusFailure(moduleName, unknownCode);
         assertEquals(expectedUnknownCode, getHistogramStatus(moduleName));
     }
@@ -117,14 +117,14 @@
 
         // Act & Assert.
         for (int[] tuple : mErrorCodeMapping) {
-            ShadowRecordHistogram.reset();
+            UmaRecorderHolder.resetForTesting();
             int expectedOutputCode = tuple[0];
             int inputCode = tuple[1];
             mFailureLogger.logRequestFailure(moduleName, inputCode);
             assertEquals(expectedOutputCode, getHistogramStatus(moduleName));
         }
 
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
         mFailureLogger.logRequestFailure(moduleName, unknownCode);
         assertEquals(expectedUnknownCode, getHistogramStatus(moduleName));
     }
diff --git a/components/module_installer/android/junit/src/org/chromium/components/module_installer/logger/SplitInstallStatusLoggerTest.java b/components/module_installer/android/junit/src/org/chromium/components/module_installer/logger/SplitInstallStatusLoggerTest.java
index a477fa6..edf7d61 100644
--- a/components/module_installer/android/junit/src/org/chromium/components/module_installer/logger/SplitInstallStatusLoggerTest.java
+++ b/components/module_installer/android/junit/src/org/chromium/components/module_installer/logger/SplitInstallStatusLoggerTest.java
@@ -13,20 +13,20 @@
 import org.junit.runner.RunWith;
 import org.robolectric.annotation.Config;
 
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 
 /**
  * Test suite for the SplitInstallStatusLogger class.
  */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(manifest = Config.NONE, shadows = {ShadowRecordHistogram.class})
+@Config(manifest = Config.NONE)
 public class SplitInstallStatusLoggerTest {
     private SplitInstallStatusLogger mStatusLogger;
 
     @Before
     public void setUp() {
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
         mStatusLogger = new SplitInstallStatusLogger();
     }
 
@@ -77,7 +77,7 @@
     }
 
     private int logStatusChange(String moduleName, int status) {
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
         mStatusLogger.logStatusChange(moduleName, status);
         return getHistogramStatus(moduleName);
     }
diff --git a/components/ntp_tiles/popular_sites_impl.cc b/components/ntp_tiles/popular_sites_impl.cc
index 5b5b9d67..b2b100a 100644
--- a/components/ntp_tiles/popular_sites_impl.cc
+++ b/components/ntp_tiles/popular_sites_impl.cc
@@ -475,13 +475,13 @@
 
 void PopularSitesImpl::OnJsonParsed(
     data_decoder::DataDecoder::ValueOrError result) {
-  if (!result.value) {
-    DLOG(WARNING) << "JSON parsing failed: " << *result.error;
+  if (!result.has_value()) {
+    DLOG(WARNING) << "JSON parsing failed: " << result.error();
     OnDownloadFailed();
     return;
   }
 
-  base::Value list = std::move(*result.value);
+  base::Value list = std::move(*result);
   if (!list.is_list()) {
     DLOG(WARNING) << "JSON is not a list";
     OnDownloadFailed();
diff --git a/components/omnibox/browser/base_search_provider.h b/components/omnibox/browser/base_search_provider.h
index e6f7d78..77a4960 100644
--- a/components/omnibox/browser/base_search_provider.h
+++ b/components/omnibox/browser/base_search_provider.h
@@ -133,7 +133,8 @@
   static const char kShouldPrefetchKey[];
 
   // Indicates whether the server said a match should be prerendered by
-  // Prerender2. See content/browser/prerender/README.md for more information.
+  // Prerender2. See content/browser/preloading/prerender/README.md for more
+  // information.
   static const char kShouldPrerenderKey[];
 
   // Used to store metadata from the server response, which is needed for
diff --git a/components/omnibox/browser/search_suggestion_parser.h b/components/omnibox/browser/search_suggestion_parser.h
index 9f8eae94..3beb8b2 100644
--- a/components/omnibox/browser/search_suggestion_parser.h
+++ b/components/omnibox/browser/search_suggestion_parser.h
@@ -231,7 +231,7 @@
     bool should_prefetch_;
 
     // Should this result trigger Prerender2? See
-    // content/browser/prerender/README.md for more information.
+    // content/browser/preloading/prerender/README.md for more information.
     bool should_prerender_;
   };
 
diff --git a/components/page_load_metrics/browser/observers/prerender_page_load_metrics_observer.h b/components/page_load_metrics/browser/observers/prerender_page_load_metrics_observer.h
index 9f02cca..e02a454f 100644
--- a/components/page_load_metrics/browser/observers/prerender_page_load_metrics_observer.h
+++ b/components/page_load_metrics/browser/observers/prerender_page_load_metrics_observer.h
@@ -24,7 +24,7 @@
 
 }  // namespace internal
 
-// Prerender2 (content/browser/prerender/README.md):
+// Prerender2 (content/browser/preloading/prerender/README.md):
 // Records custom page load timing metrics for prerendered page loads.
 class PrerenderPageLoadMetricsObserver
     : public page_load_metrics::PageLoadMetricsObserver {
diff --git a/components/payments/content/android/BUILD.gn b/components/payments/content/android/BUILD.gn
index a6fb090..324c1d5 100644
--- a/components/payments/content/android/BUILD.gn
+++ b/components/payments/content/android/BUILD.gn
@@ -195,6 +195,7 @@
     ":feature_list_java",
     ":java_resources",
     ":minimal_java",
+    ":pre_purchase_query_enum_java",
     ":service_java",
     "//base:base_java",
     "//base:jni_java",
@@ -222,12 +223,16 @@
   ]
   srcjar_deps = [
     ":payments_journey_logger_enum_javagen",
-    ":pre_purchase_query_enum_javagen",
     ":prefs_strings_generated_srcjar",
   ]
   resources_package = "org.chromium.components.payments"
 }
 
+android_library("pre_purchase_query_enum_java") {
+  deps = [ "//third_party/androidx:androidx_annotation_annotation_java" ]
+  srcjar_deps = [ ":pre_purchase_query_enum_javagen" ]
+}
+
 # Minimal target that only includes what downstream code depends on.
 android_library("minimal_java") {
   annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
diff --git a/components/payments/content/utility/payment_manifest_parser.cc b/components/payments/content/utility/payment_manifest_parser.cc
index e50db4c..18de436 100644
--- a/components/payments/content/utility/payment_manifest_parser.cc
+++ b/components/payments/content/utility/payment_manifest_parser.cc
@@ -657,12 +657,12 @@
   std::vector<GURL> web_app_manifest_urls;
   std::vector<url::Origin> supported_origins;
 
-  if (result.value) {
+  if (result.has_value()) {
     ParsePaymentMethodManifestIntoVectors(
-        manifest_url, base::Value::ToUniquePtrValue(std::move(*result.value)),
-        *log_, &web_app_manifest_urls, &supported_origins);
+        manifest_url, base::Value::ToUniquePtrValue(std::move(*result)), *log_,
+        &web_app_manifest_urls, &supported_origins);
   } else {
-    log_->Error(*result.error);
+    log_->Error(result.error());
   }
 
   // Can trigger synchronous deletion of this object, so can't access any of
@@ -676,12 +676,11 @@
   parse_webapp_callback_counter_--;
 
   std::vector<WebAppManifestSection> manifest;
-  if (result.value) {
+  if (result.has_value()) {
     ParseWebAppManifestIntoVector(
-        base::Value::ToUniquePtrValue(std::move(*result.value)), *log_,
-        &manifest);
+        base::Value::ToUniquePtrValue(std::move(*result)), *log_, &manifest);
   } else {
-    log_->Error(*result.error);
+    log_->Error(result.error());
   }
 
   // Can trigger synchronous deletion of this object, so can't access any of
@@ -695,17 +694,17 @@
   std::unique_ptr<WebAppInstallationInfo> installation_info;
   std::unique_ptr<std::vector<WebAppIcon>> icons;
 
-  if (result.value) {
+  if (result.has_value()) {
     installation_info = std::make_unique<WebAppInstallationInfo>();
     icons = std::make_unique<std::vector<WebAppIcon>>();
     if (!ParseWebAppInstallationInfoIntoStructs(
-            base::Value::ToUniquePtrValue(std::move(*result.value)), *log_,
+            base::Value::ToUniquePtrValue(std::move(*result)), *log_,
             installation_info.get(), icons.get())) {
       installation_info.reset();
       icons.reset();
     }
   } else {
-    log_->Error(*result.error);
+    log_->Error(result.error());
   }
 
   // Can trigger synchronous deletion of this object, so can't access any of
diff --git a/components/pdf/renderer/pdf_view_web_plugin_client.cc b/components/pdf/renderer/pdf_view_web_plugin_client.cc
index d5c0436..0beee3a 100644
--- a/components/pdf/renderer/pdf_view_web_plugin_client.cc
+++ b/components/pdf/renderer/pdf_view_web_plugin_client.cc
@@ -91,9 +91,8 @@
   DCHECK_EQ(isolate_, context->GetIsolate());
   v8::Context::Scope context_scope(context);
 
-  base::Value message_as_value(std::move(message));
   v8::Local<v8::Value> converted_message =
-      v8_value_converter_->ToV8Value(&message_as_value, context);
+      v8_value_converter_->ToV8Value(message, context);
 
   plugin_container_->EnqueueMessageEvent(
       blink::WebSerializedScriptValue::Serialize(isolate_, converted_message));
diff --git a/components/plugins/renderer/plugin_placeholder.cc b/components/plugins/renderer/plugin_placeholder.cc
index 5b6fcaa..8156295 100644
--- a/components/plugins/renderer/plugin_placeholder.cc
+++ b/components/plugins/renderer/plugin_placeholder.cc
@@ -132,7 +132,7 @@
       blink::WebSerializedScriptValue::Serialize(
           blink::MainThreadIsolate(),
           content::V8ValueConverter::Create()->ToV8Value(
-              &value,
+              value,
               element.GetDocument().GetFrame()->MainWorldScriptContext()));
   blink::WebDOMMessageEvent msg_event(message_data);
 
diff --git a/components/policy/core/common/cloud/user_cloud_policy_store_unittest.cc b/components/policy/core/common/cloud/user_cloud_policy_store_unittest.cc
index 32a7caf..db71b19a 100644
--- a/components/policy/core/common/cloud/user_cloud_policy_store_unittest.cc
+++ b/components/policy/core/common/cloud/user_cloud_policy_store_unittest.cc
@@ -109,8 +109,7 @@
     const PolicyMap::Entry* entry =
         store->policy_map().Get(key::kSearchSuggestEnabled);
     ASSERT_TRUE(entry);
-    EXPECT_TRUE(
-        base::Value(true).Equals(entry->value(base::Value::Type::BOOLEAN)));
+    EXPECT_EQ(true, *entry->value(base::Value::Type::BOOLEAN));
     ASSERT_TRUE(store->policy_map().Get(key::kURLBlocklist));
   }
 
diff --git a/components/policy/core/common/mac_util_unittest.cc b/components/policy/core/common/mac_util_unittest.cc
index dcfa118..107cdbe0 100644
--- a/components/policy/core/common/mac_util_unittest.cc
+++ b/components/policy/core/common/mac_util_unittest.cc
@@ -56,7 +56,7 @@
   ASSERT_TRUE(property);
   std::unique_ptr<base::Value> value = PropertyToValue(property);
   ASSERT_TRUE(value);
-  EXPECT_TRUE(root.Equals(value.get()));
+  EXPECT_EQ(root, *value);
 }
 
 }  // namespace policy
diff --git a/components/query_tiles/android/java/src/org/chromium/components/query_tiles/TileUmaLoggerTest.java b/components/query_tiles/android/java/src/org/chromium/components/query_tiles/TileUmaLoggerTest.java
index 9f022ef0..452c3e7 100644
--- a/components/query_tiles/android/java/src/org/chromium/components/query_tiles/TileUmaLoggerTest.java
+++ b/components/query_tiles/android/java/src/org/chromium/components/query_tiles/TileUmaLoggerTest.java
@@ -12,21 +12,21 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.metrics.RecordHistogram;
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 
 /**
  * Tests for TileUmaLogger.
  */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(manifest = Config.NONE, shadows = {ShadowRecordHistogram.class})
+@Config(manifest = Config.NONE)
 public class TileUmaLoggerTest {
     private TileUmaLogger mTileUmaLogger;
     private TestTileProvider mTileProvider;
 
     @Before
     public void setUp() {
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
         mTileProvider = new TestTileProvider(2, 12);
         mTileUmaLogger = new TileUmaLogger("TestUiSurface");
     }
diff --git a/components/search/ntp_features.cc b/components/search/ntp_features.cc
index 7fd644e..23650bf 100644
--- a/components/search/ntp_features.cc
+++ b/components/search/ntp_features.cc
@@ -82,8 +82,13 @@
 const base::Feature kNtpMiddleSlotPromoDismissal{
     "NtpMiddleSlotPromoDismissal", base::FEATURE_ENABLED_BY_DEFAULT};
 
-// If enabled, modules will be shown.
-const base::Feature kModules{"NtpModules", base::FEATURE_DISABLED_BY_DEFAULT};
+// Dummy feature to set param "NtpModulesLoadTimeoutMillisecondsParam".
+const base::Feature kNtpModulesLoadTimeoutMilliseconds{
+    "NtpModulesLoadTimeoutMilliseconds", base::FEATURE_DISABLED_BY_DEFAULT};
+
+// Dummy feature to set param "NtpModulesOrderParam".
+const base::Feature kNtpModulesOrder{"NtpModulesOrder",
+                                     base::FEATURE_DISABLED_BY_DEFAULT};
 
 // If enabled, modules will be able to be reordered via dragging and dropping
 const base::Feature kNtpModulesDragAndDrop{"NtpModulesDragAndDrop",
@@ -93,9 +98,9 @@
 const base::Feature kNtpModulesFirstRunExperience{
     "NtpModulesFirstRunExperience", base::FEATURE_DISABLED_BY_DEFAULT};
 
-// If enabled, modules will be loaded even if kModules is disabled. This is
-// useful to determine if a user would have seen modules in order to
-// counterfactually log or trigger.
+// If enabled, modules will be loaded but not shown. This is useful to determine
+// if a user would have seen modules in order to counterfactually log or
+// trigger.
 const base::Feature kNtpModulesLoad{"NtpModulesLoad",
                                     base::FEATURE_DISABLED_BY_DEFAULT};
 
@@ -189,7 +194,8 @@
 
 base::TimeDelta GetModulesLoadTimeout() {
   std::string param_value = base::GetFieldTrialParamValueByFeature(
-      kModules, kNtpModulesLoadTimeoutMillisecondsParam);
+      kNtpModulesLoadTimeoutMilliseconds,
+      kNtpModulesLoadTimeoutMillisecondsParam);
   // If the field trial param is not found or cannot be parsed to an unsigned
   // integer, return the default value.
   unsigned int param_value_as_int = 0;
@@ -200,10 +206,10 @@
 }
 
 std::vector<std::string> GetModulesOrder() {
-  return base::SplitString(
-      base::GetFieldTrialParamValueByFeature(kModules, kNtpModulesOrderParam),
-      ",:;", base::WhitespaceHandling::TRIM_WHITESPACE,
-      base::SplitResult::SPLIT_WANT_NONEMPTY);
+  return base::SplitString(base::GetFieldTrialParamValueByFeature(
+                               kNtpModulesOrder, kNtpModulesOrderParam),
+                           ",:;", base::WhitespaceHandling::TRIM_WHITESPACE,
+                           base::SplitResult::SPLIT_WANT_NONEMPTY);
 }
 
 }  // namespace ntp_features
diff --git a/components/search/ntp_features.h b/components/search/ntp_features.h
index 04063b1..6759c5a0 100644
--- a/components/search/ntp_features.h
+++ b/components/search/ntp_features.h
@@ -37,7 +37,8 @@
 extern const base::Feature kNtpLogo;
 extern const base::Feature kNtpMiddleSlotPromo;
 extern const base::Feature kNtpMiddleSlotPromoDismissal;
-extern const base::Feature kModules;
+extern const base::Feature kNtpModulesLoadTimeoutMilliseconds;
+extern const base::Feature kNtpModulesOrder;
 extern const base::Feature kNtpModulesDragAndDrop;
 extern const base::Feature kNtpModulesFirstRunExperience;
 extern const base::Feature kNtpModulesLoad;
diff --git a/components/search_engines/template_url_parser.cc b/components/search_engines/template_url_parser.cc
index 4ac09746..073ec6f2 100644
--- a/components/search_engines/template_url_parser.cc
+++ b/components/search_engines/template_url_parser.cc
@@ -167,13 +167,13 @@
 
 void SafeTemplateURLParser::OnXmlParseComplete(
     data_decoder::DataDecoder::ValueOrError value_or_error) {
-  if (value_or_error.error) {
-    DLOG(ERROR) << "Failed to parse XML: " << *value_or_error.error;
+  if (!value_or_error.has_value()) {
+    DLOG(ERROR) << "Failed to parse XML: " << value_or_error.error();
     std::move(callback_).Run(nullptr);
     return;
   }
 
-  const base::Value& root = *value_or_error.value;
+  const base::Value& root = *value_or_error;
 
   // Get the namespaces used in the XML document, which will be used
   // to access nodes by tag name in GetChildElementsByTag().
diff --git a/components/test/data/autofill/merge_structured_names/output/validation.out b/components/test/data/autofill/merge_structured_names/output/validation.out
index 7c28a5b22..67ea0b65 100644
--- a/components/test/data/autofill/merge_structured_names/output/validation.out
+++ b/components/test/data/autofill/merge_structured_names/output/validation.out
@@ -23,5 +23,5 @@
 ADDRESS_HOME_CITY: Mountain View
 ADDRESS_HOME_STATE: CA
 ADDRESS_HOME_ZIP: 94043
-ADDRESS_HOME_COUNTRY:
+ADDRESS_HOME_COUNTRY: US
 PHONE_HOME_WHOLE_NUMBER:
diff --git a/components/update_client/protocol_serializer.cc b/components/update_client/protocol_serializer.cc
index 16bd70b..408e99b 100644
--- a/components/update_client/protocol_serializer.cc
+++ b/components/update_client/protocol_serializer.cc
@@ -37,9 +37,7 @@
 
 // Returns the amount of physical memory in GB, rounded to the nearest GB.
 int GetPhysicalMemoryGB() {
-  const double kOneGB = 1024 * 1024 * 1024;
-  const int64_t phys_mem = base::SysInfo::AmountOfPhysicalMemory();
-  return static_cast<int>(std::floor(0.5 + phys_mem / kOneGB));
+  return base::ClampRound(base::SysInfo::AmountOfPhysicalMemoryMB() / 1024.0f);
 }
 
 std::string GetOSVersion() {
diff --git a/components/variations/android/junit/src/org/chromium/components/variations/VariationsCompressionUtilsTest.java b/components/variations/android/junit/src/org/chromium/components/variations/VariationsCompressionUtilsTest.java
index 3508e80..fba4377 100644
--- a/components/variations/android/junit/src/org/chromium/components/variations/VariationsCompressionUtilsTest.java
+++ b/components/variations/android/junit/src/org/chromium/components/variations/VariationsCompressionUtilsTest.java
@@ -14,7 +14,6 @@
 import org.junit.runner.RunWith;
 import org.robolectric.annotation.Config;
 
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.components.variations.VariationsCompressionUtils.InstanceManipulations;
 
@@ -25,7 +24,7 @@
  * Tests for VariationsCompressionUtils
  */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(manifest = Config.NONE, shadows = {ShadowRecordHistogram.class})
+@Config(manifest = Config.NONE)
 public class VariationsCompressionUtilsTest {
     /**
      * Test method for successful {@link VariationsSeedFetcher#getInstanceManipulations}
diff --git a/components/variations/android/junit/src/org/chromium/components/variations/firstrun/VariationsSeedFetcherTest.java b/components/variations/android/junit/src/org/chromium/components/variations/firstrun/VariationsSeedFetcherTest.java
index 6153bb40..ec66b04 100644
--- a/components/variations/android/junit/src/org/chromium/components/variations/firstrun/VariationsSeedFetcherTest.java
+++ b/components/variations/android/junit/src/org/chromium/components/variations/firstrun/VariationsSeedFetcherTest.java
@@ -36,7 +36,7 @@
 import org.chromium.base.ContextUtils;
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.metrics.RecordHistogram;
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.components.variations.VariationsCompressionUtils;
@@ -57,7 +57,7 @@
  * Tests for VariationsSeedFetcher
  */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(manifest = Config.NONE, shadows = {ShadowRecordHistogram.class})
+@Config(manifest = Config.NONE)
 public class VariationsSeedFetcherTest {
     private HttpURLConnection mConnection;
     private VariationsSeedFetcher mFetcher;
@@ -87,7 +87,7 @@
                 .getServerConnection(VariationsSeedFetcher.VariationsPlatform.ANDROID, sRestrict,
                         sMilestone, sChannel);
         mPrefs = ContextUtils.getAppSharedPreferences();
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
     }
 
     @After
diff --git a/components/viz/service/display/renderer_pixeltest.cc b/components/viz/service/display/renderer_pixeltest.cc
index c0dd75d6..db371b71 100644
--- a/components/viz/service/display/renderer_pixeltest.cc
+++ b/components/viz/service/display/renderer_pixeltest.cc
@@ -4797,6 +4797,7 @@
     }
 
     gfx::ColorTransform::Options options;
+    options.tone_map_pq_and_hlg_to_dst = true;
     options.sdr_max_luminance_nits = gfx::ColorSpace::kDefaultSDRWhiteLevel;
     std::unique_ptr<gfx::ColorTransform> transform =
         gfx::ColorTransform::NewColorTransform(this->src_color_space_,
diff --git a/components/viz/service/display/skia_renderer.cc b/components/viz/service/display/skia_renderer.cc
index 94d62ed..427879fa 100644
--- a/components/viz/service/display/skia_renderer.cc
+++ b/components/viz/service/display/skia_renderer.cc
@@ -2160,7 +2160,8 @@
   const gfx::ColorSpace& src_color_space =
       resource_provider()->GetColorSpace(quad->resource_id());
   const bool needs_color_conversion_filter =
-      quad->is_video_frame && src_color_space.IsHDR();
+      (quad->is_video_frame && src_color_space.IsHDR()) ||
+      src_color_space.IsToneMappedByDefault();
 
   sk_sp<SkColorSpace> override_color_space;
   if (needs_color_conversion_filter)
@@ -2282,6 +2283,16 @@
     }
   }
 
+  if (needs_color_conversion_filter) {
+    // Skia won't perform color conversion.
+    const gfx::ColorSpace dst_color_space = CurrentRenderPassColorSpace();
+    DCHECK(SkColorSpace::Equals(image->colorSpace(),
+                                CurrentRenderPassSkColorSpace().get()));
+    sk_sp<SkColorFilter> color_filter = GetColorSpaceConversionFilter(
+        src_color_space, quad->hdr_metadata, dst_color_space);
+    paint.setColorFilter(color_filter->makeComposed(paint.refColorFilter()));
+  }
+
   // From gl_renderer, the final src color will be
   // vertexAlpha * (textureColor + backgroundColor * (1 - textureAlpha)), where
   // vertexAlpha is the quad's alpha * interpolated per-vertex alpha
@@ -2301,16 +2312,6 @@
       paint.setColorFilter(cf->makeComposed(paint.refColorFilter()));
   }
 
-  if (needs_color_conversion_filter) {
-    // Skia won't perform color conversion.
-    const gfx::ColorSpace dst_color_space = CurrentRenderPassColorSpace();
-    DCHECK(SkColorSpace::Equals(image->colorSpace(),
-                                CurrentRenderPassSkColorSpace().get()));
-    sk_sp<SkColorFilter> color_filter = GetColorSpaceConversionFilter(
-        src_color_space, quad->hdr_metadata, dst_color_space);
-    paint.setColorFilter(color_filter->makeComposed(paint.refColorFilter()));
-  }
-
   if (!rpdq_params) {
     // Reset the paint's alpha, since it started as params.opacity and that
     // is now applied outside of the paint's alpha.
@@ -2593,10 +2594,8 @@
     const gfx::ColorSpace& dst,
     float resource_offset,
     float resource_multiplier) {
-  // TODO(https://crbug.com/1325384): Use `src_hdr_metadata` in the color
-  // filter.
   return color_filter_cache_.Get(
-      src, dst, resource_offset, resource_multiplier,
+      src, dst, resource_offset, resource_multiplier, src_hdr_metadata,
       current_frame()->display_color_spaces.GetSDRMaxLuminanceNits(),
       current_frame()->display_color_spaces.GetHDRMaxLuminanceRelative());
 }
diff --git a/components/webapps/browser/android/shortcut_info.cc b/components/webapps/browser/android/shortcut_info.cc
index 496375b..a241056 100644
--- a/components/webapps/browser/android/shortcut_info.cc
+++ b/components/webapps/browser/android/shortcut_info.cc
@@ -23,17 +23,6 @@
 // https://developer.android.com/guide/topics/ui/shortcuts#shortcut-limitations
 constexpr size_t kMaxShortcuts = 4;
 
-std::string GetManifestId(const blink::mojom::Manifest& manifest) {
-  if (manifest.id.has_value()) {
-    // Generate the formatted id by <start_url_origin>/<manifest_id>.
-    GURL manifest_id(manifest.start_url.DeprecatedGetOriginAsURL().spec() +
-                     base::UTF16ToUTF8(manifest.id.value()));
-    DCHECK(manifest_id.is_valid());
-    return manifest_id.spec();
-  }
-  return manifest.start_url.spec();
-}
-
 }  // namespace
 
 using blink::mojom::DisplayMode;
@@ -232,4 +221,17 @@
   source = new_source;
 }
 
+// static
+std::string ShortcutInfo::GetManifestId(
+    const blink::mojom::Manifest& manifest) {
+  if (manifest.id.has_value()) {
+    // Generate the formatted id by <start_url_origin>/<manifest_id>.
+    GURL manifest_id(manifest.start_url.DeprecatedGetOriginAsURL().spec() +
+                     base::UTF16ToUTF8(manifest.id.value()));
+    DCHECK(manifest_id.is_valid());
+    return manifest_id.spec();
+  }
+  return manifest.start_url.spec();
+}
+
 }  // namespace webapps
diff --git a/components/webapps/browser/android/shortcut_info.h b/components/webapps/browser/android/shortcut_info.h
index c402327..011e76d 100644
--- a/components/webapps/browser/android/shortcut_info.h
+++ b/components/webapps/browser/android/shortcut_info.h
@@ -136,6 +136,9 @@
   // |splash_image_url| and |best_shortcut_icon_urls| if they are not empty
   std::set<GURL> GetWebApkIcons();
 
+  // Generate the formatted id field from web manifest.
+  static std::string GetManifestId(const blink::mojom::Manifest& manifest);
+
   GURL manifest_url;
   GURL url;
   GURL scope;
diff --git a/components/webapps/browser/android/webapk/webapk_proto_builder.cc b/components/webapps/browser/android/webapk/webapk_proto_builder.cc
index 82f7a373..e8719ba7 100644
--- a/components/webapps/browser/android/webapk/webapk_proto_builder.cc
+++ b/components/webapps/browser/android/webapk/webapk_proto_builder.cc
@@ -305,6 +305,7 @@
 bool StoreUpdateRequestToFileInBackground(
     const base::FilePath& update_request_path,
     const webapps::ShortcutInfo& shortcut_info,
+    const std::string& app_key,
     const std::string& primary_icon_data,
     bool is_primary_icon_maskable,
     const std::string& splash_icon_data,
@@ -317,10 +318,9 @@
   base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
                                                 base::BlockingType::MAY_BLOCK);
 
-  // TODO(crbug.com/1342070): Implement the app_key for updates.
   std::unique_ptr<std::string> proto = BuildProtoInBackground(
-      shortcut_info, "" /*app_key*/, primary_icon_data,
-      is_primary_icon_maskable, splash_icon_data, package_name, version,
+      shortcut_info, app_key, primary_icon_data, is_primary_icon_maskable,
+      splash_icon_data, package_name, version,
       std::move(icon_url_to_murmur2_hash), is_manifest_stale,
       is_app_identity_update_supported, std::move(update_reasons));
 
diff --git a/components/webapps/browser/android/webapk/webapk_proto_builder.h b/components/webapps/browser/android/webapk/webapk_proto_builder.h
index 6efccf2..807a718 100644
--- a/components/webapps/browser/android/webapk/webapk_proto_builder.h
+++ b/components/webapps/browser/android/webapk/webapk_proto_builder.h
@@ -61,6 +61,7 @@
 bool StoreUpdateRequestToFileInBackground(
     const base::FilePath& update_request_path,
     const webapps::ShortcutInfo& shortcut_info,
+    const std::string& app_key,
     const std::string& primary_icon_data,
     bool is_primary_icon_maskable,
     const std::string& splash_icon_data,
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index eb09730..4fe2de2 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -1440,6 +1440,8 @@
     "preloading/prerender/prerender_navigation_utils.h",
     "preloading/prerender/prerender_subframe_navigation_throttle.cc",
     "preloading/prerender/prerender_subframe_navigation_throttle.h",
+    "preloading/speculation_rules/speculation_host_impl.cc",
+    "preloading/speculation_rules/speculation_host_impl.h",
     "presentation/presentation_service_impl.cc",
     "presentation/presentation_service_impl.h",
     "private_aggregation/private_aggregation_budget_key.cc",
@@ -1959,8 +1961,6 @@
     "sms/user_consent_handler.h",
     "sms/webotp_service.cc",
     "sms/webotp_service.h",
-    "speculation_rules/speculation_host_impl.cc",
-    "speculation_rules/speculation_host_impl.h",
     "speech/speech_recognition_dispatcher_host.cc",
     "speech/speech_recognition_dispatcher_host.h",
     "speech/speech_recognition_manager_impl.cc",
diff --git a/content/browser/aggregation_service/aggregation_service_network_fetcher_impl.cc b/content/browser/aggregation_service/aggregation_service_network_fetcher_impl.cc
index f457832..1d04457 100644
--- a/content/browser/aggregation_service/aggregation_service_network_fetcher_impl.cc
+++ b/content/browser/aggregation_service/aggregation_service_network_fetcher_impl.cc
@@ -224,14 +224,13 @@
     base::Time fetch_time,
     base::Time expiry_time,
     data_decoder::DataDecoder::ValueOrError result) {
-  if (!result.value) {
+  if (!result.has_value()) {
     OnError(url, std::move(callback), FetchStatus::kJsonParseError,
-            /*error_msg=*/*result.error);
+            /*error_msg=*/result.error());
     return;
   }
 
-  std::vector<PublicKey> keys =
-      aggregation_service::GetPublicKeys(result.value.value());
+  std::vector<PublicKey> keys = aggregation_service::GetPublicKeys(*result);
   if (keys.empty()) {
     OnError(url, std::move(callback), FetchStatus::kInvalidKeyError,
             /*error_msg=*/"Public key parsing failed");
diff --git a/content/browser/attribution_reporting/attribution_data_host_manager_impl.cc b/content/browser/attribution_reporting/attribution_data_host_manager_impl.cc
index 0effeead..ef5b212 100644
--- a/content/browser/attribution_reporting/attribution_data_host_manager_impl.cc
+++ b/content/browser/attribution_reporting/attribution_data_host_manager_impl.cc
@@ -719,9 +719,9 @@
   registrations.pending_source_data--;
 
   absl::optional<StorableSource> source;
-  if (result.value && result.value->is_dict()) {
+  if (result.has_value() && result->is_dict()) {
     source = ParseSourceRegistration(
-        std::move(result.value->GetDict()), /*source_time=*/base::Time::Now(),
+        std::move(result->GetDict()), /*source_time=*/base::Time::Now(),
         std::move(reporting_origin), registrations.source_origin,
         AttributionSourceType::kNavigation);
   }
diff --git a/content/browser/blob_storage/blob_registry_wrapper.cc b/content/browser/blob_storage/blob_registry_wrapper.cc
index ea2fa5ef..3c4bf43 100644
--- a/content/browser/blob_storage/blob_registry_wrapper.cc
+++ b/content/browser/blob_storage/blob_registry_wrapper.cc
@@ -11,7 +11,6 @@
 #include "content/public/common/content_features.h"
 #include "storage/browser/blob/blob_registry_impl.h"
 #include "storage/browser/blob/blob_storage_context.h"
-#include "storage/browser/file_system/file_system_context.h"
 
 namespace content {
 
@@ -27,9 +26,6 @@
   bool CanReadFile(const base::FilePath& file) override {
     return security_policy_handle_.CanReadFile(file);
   }
-  bool CanReadFileSystemFile(const storage::FileSystemURL& url) override {
-    return security_policy_handle_.CanReadFileSystemFile(url);
-  }
   bool CanAccessDataForOrigin(const url::Origin& origin) override {
     return security_policy_handle_.CanAccessDataForOrigin(origin);
   }
@@ -43,13 +39,11 @@
 // static
 scoped_refptr<BlobRegistryWrapper> BlobRegistryWrapper::Create(
     scoped_refptr<ChromeBlobStorageContext> blob_storage_context,
-    scoped_refptr<storage::FileSystemContext> file_system_context,
     base::WeakPtr<storage::BlobUrlRegistry> blob_url_registry) {
   scoped_refptr<BlobRegistryWrapper> result(new BlobRegistryWrapper());
   GetIOThreadTaskRunner({})->PostTask(
       FROM_HERE, base::BindOnce(&BlobRegistryWrapper::InitializeOnIOThread,
                                 result, std::move(blob_storage_context),
-                                std::move(file_system_context),
                                 std::move(blob_url_registry)));
   return result;
 }
@@ -72,13 +66,11 @@
 
 void BlobRegistryWrapper::InitializeOnIOThread(
     scoped_refptr<ChromeBlobStorageContext> blob_storage_context,
-    scoped_refptr<storage::FileSystemContext> file_system_context,
     base::WeakPtr<storage::BlobUrlRegistry> blob_url_registry) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   blob_registry_ = std::make_unique<storage::BlobRegistryImpl>(
       blob_storage_context->context()->AsWeakPtr(),
-      std::move(blob_url_registry), GetUIThreadTaskRunner({}),
-      std::move(file_system_context));
+      std::move(blob_url_registry), GetUIThreadTaskRunner({}));
 }
 
 }  // namespace content
diff --git a/content/browser/blob_storage/blob_registry_wrapper.h b/content/browser/blob_storage/blob_registry_wrapper.h
index dd65a0c..2116346c 100644
--- a/content/browser/blob_storage/blob_registry_wrapper.h
+++ b/content/browser/blob_storage/blob_registry_wrapper.h
@@ -12,7 +12,6 @@
 namespace storage {
 class BlobRegistryImpl;
 class BlobUrlRegistry;
-class FileSystemContext;
 }  // namespace storage
 
 namespace content {
@@ -30,7 +29,6 @@
  public:
   static scoped_refptr<BlobRegistryWrapper> Create(
       scoped_refptr<ChromeBlobStorageContext> blob_storage_context,
-      scoped_refptr<storage::FileSystemContext> file_system_context,
       base::WeakPtr<storage::BlobUrlRegistry> blob_url_registry);
 
   void Bind(int process_id,
@@ -44,7 +42,6 @@
 
   void InitializeOnIOThread(
       scoped_refptr<ChromeBlobStorageContext> blob_storage_context,
-      scoped_refptr<storage::FileSystemContext> file_system_context,
       base::WeakPtr<storage::BlobUrlRegistry> blob_url_registry);
 
   std::unique_ptr<storage::BlobRegistryImpl> blob_registry_;
diff --git a/content/browser/browser_interface_binders.cc b/content/browser/browser_interface_binders.cc
index 78e1d230..fcd0ce3 100644
--- a/content/browser/browser_interface_binders.cc
+++ b/content/browser/browser_interface_binders.cc
@@ -37,6 +37,7 @@
 #include "content/browser/picture_in_picture/picture_in_picture_service_impl.h"
 #include "content/browser/preloading/prerender/prerender_internals.mojom.h"
 #include "content/browser/preloading/prerender/prerender_internals_ui.h"
+#include "content/browser/preloading/speculation_rules/speculation_host_impl.h"
 #include "content/browser/process_internals/process_internals.mojom.h"
 #include "content/browser/process_internals/process_internals_ui.h"
 #include "content/browser/quota/quota_context.h"
@@ -49,7 +50,6 @@
 #include "content/browser/renderer_host/render_frame_host_impl.h"
 #include "content/browser/renderer_host/render_process_host_impl.h"
 #include "content/browser/service_worker/service_worker_host.h"
-#include "content/browser/speculation_rules/speculation_host_impl.h"
 #include "content/browser/speech/speech_recognition_dispatcher_host.h"
 #include "content/browser/wake_lock/wake_lock_service_impl.h"
 #include "content/browser/web_contents/file_chooser_impl.h"
diff --git a/content/browser/data_decoder_browsertest.cc b/content/browser/data_decoder_browsertest.cc
index 5ba0d868..8bd04564 100644
--- a/content/browser/data_decoder_browsertest.cc
+++ b/content/browser/data_decoder_browsertest.cc
@@ -317,8 +317,9 @@
       {{0x1, 0x1, 0x1, 0x1, 0x1, 0x1}},
       base::BindOnce(
           [](bool* got_callback, base::ScopedClosureRunner quit_closure_runner,
-             data_decoder::DataDecoder::ResultOrError<mojo_base::BigBuffer>
-                 result) { *got_callback = true; },
+             base::expected<mojo_base::BigBuffer, std::string> result) {
+            *got_callback = true;
+          },
           // Pass the quit closure as a ScopedClosureRunner, so that the loop
           // is quit if the callback is destroyed un-run or after it runs.
           &got_callback, base::ScopedClosureRunner(run_loop.QuitClosure())));
diff --git a/content/browser/devtools/protocol/page_handler.cc b/content/browser/devtools/protocol/page_handler.cc
index bee63208..9f8887b 100644
--- a/content/browser/devtools/protocol/page_handler.cc
+++ b/content/browser/devtools/protocol/page_handler.cc
@@ -1899,9 +1899,9 @@
   auto reasons = std::make_unique<
       protocol::Array<Page::BackForwardCacheNotRestoredExplanation>>();
 
-  for (BackForwardCacheMetrics::NotRestoredReason reason :
+  for (BackForwardCacheMetrics::NotRestoredReason not_restored_reason :
        not_restored_reasons) {
-    if (reason ==
+    if (not_restored_reason ==
         BackForwardCacheMetrics::NotRestoredReason::kBlocklistedFeatures) {
       DCHECK(!blocklisted_features.Empty());
       for (blink::scheduler::WebSchedulerTrackedFeature feature :
@@ -1912,8 +1912,9 @@
                 .SetReason(BlocklistedFeatureToProtocol(feature))
                 .Build());
       }
-    } else if (reason == BackForwardCacheMetrics::NotRestoredReason::
-                             kDisableForRenderFrameHostCalled) {
+    } else if (not_restored_reason ==
+               BackForwardCacheMetrics::NotRestoredReason::
+                   kDisableForRenderFrameHostCalled) {
       for (auto disabled_reason : disabled_reasons) {
         auto reason =
             Page::BackForwardCacheNotRestoredExplanation::Create()
@@ -1929,8 +1930,8 @@
     } else {
       reasons->emplace_back(
           Page::BackForwardCacheNotRestoredExplanation::Create()
-              .SetType(MapNotRestoredReasonToType(reason))
-              .SetReason(NotRestoredReasonToProtocol(reason))
+              .SetType(MapNotRestoredReasonToType(not_restored_reason))
+              .SetReason(NotRestoredReasonToProtocol(not_restored_reason))
               .Build());
     }
   }
diff --git a/content/browser/indexed_db/indexed_db_context_impl.cc b/content/browser/indexed_db/indexed_db_context_impl.cc
index 7c443cf..946c8839 100644
--- a/content/browser/indexed_db/indexed_db_context_impl.cc
+++ b/content/browser/indexed_db/indexed_db_context_impl.cc
@@ -252,13 +252,26 @@
 }
 
 void IndexedDBContextImpl::GetUsageImpl(GetUsageCallback usage_callback) {
-  std::vector<storage::mojom::StorageUsageInfoPtr> result;
+  // TODO(https://crbug.com/1199077): Pass the real StorageKey when
+  // StorageUsageInfo is converted.
+  std::map<url::Origin, storage::mojom::StorageUsageInfoPtr> usage_map;
   for (const auto& bucket_locator : GetAllBuckets()) {
-    // TODO(https://crbug.com/1199077): Pass the real StorageKey when
-    // StorageUsageInfo is converted.
-    result.emplace_back(storage::mojom::StorageUsageInfo::New(
-        bucket_locator.storage_key.origin(), GetBucketDiskUsage(bucket_locator),
-        GetBucketLastModified(bucket_locator)));
+    const auto& origin = bucket_locator.storage_key.origin();
+    if (usage_map.find(origin) != usage_map.end()) {
+      usage_map[origin]->total_size_bytes += GetBucketDiskUsage(bucket_locator);
+      const auto& last_modified = GetBucketLastModified(bucket_locator);
+      if (usage_map[origin]->last_modified < last_modified) {
+        usage_map[origin]->last_modified = last_modified;
+      }
+    } else {
+      usage_map[origin] = storage::mojom::StorageUsageInfo::New(
+          origin, GetBucketDiskUsage(bucket_locator),
+          GetBucketLastModified(bucket_locator));
+    }
+  }
+  std::vector<storage::mojom::StorageUsageInfoPtr> result;
+  for (const auto& it : usage_map) {
+    result.emplace_back(it.second->Clone());
   }
   std::move(usage_callback).Run(std::move(result));
 }
@@ -1093,6 +1106,11 @@
       .Append(indexed_db::GetLevelDBFileName(bucket_locator));
 }
 
+base::FilePath IndexedDBContextImpl::GetLevelDBPathForTesting(
+    const storage::BucketLocator& bucket_locator) const {
+  return GetLevelDBPath(bucket_locator);
+}
+
 int64_t IndexedDBContextImpl::ReadUsageFromDisk(
     const storage::BucketLocator& bucket_locator) const {
   if (is_incognito()) {
diff --git a/content/browser/indexed_db/indexed_db_context_impl.h b/content/browser/indexed_db/indexed_db_context_impl.h
index 00b053ae..e002853 100644
--- a/content/browser/indexed_db/indexed_db_context_impl.h
+++ b/content/browser/indexed_db/indexed_db_context_impl.h
@@ -222,6 +222,11 @@
   void RegisterBucketLocatorToSkipQuotaLookupForTesting(
       const storage::BucketLocator& bucket_locator);
 
+  // In unit tests where you want to verify usage, this is an easy way to get
+  // the path to populate data at.
+  base::FilePath GetLevelDBPathForTesting(
+      const storage::BucketLocator& bucket_locator) const;
+
  private:
   friend class base::RefCountedThreadSafe<IndexedDBContextImpl>;
 
diff --git a/content/browser/indexed_db/indexed_db_factory_unittest.cc b/content/browser/indexed_db/indexed_db_factory_unittest.cc
index 21347951..d3894bd 100644
--- a/content/browser/indexed_db/indexed_db_factory_unittest.cc
+++ b/content/browser/indexed_db/indexed_db_factory_unittest.cc
@@ -21,6 +21,7 @@
 #include "base/test/test_simple_task_runner.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "base/time/default_clock.h"
+#include "components/services/storage/filesystem_proxy_factory.h"
 #include "components/services/storage/indexed_db/leveldb/fake_leveldb_factory.h"
 #include "components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_database.h"
 #include "components/services/storage/privileged/mojom/indexed_db_control.mojom-test-utils.h"
@@ -42,6 +43,7 @@
 #include "storage/browser/test/mock_quota_manager_proxy.h"
 #include "storage/browser/test/mock_special_storage_policy.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/common/indexeddb/web_idb_types.h"
 #include "third_party/blink/public/common/storage_key/storage_key.h"
 #include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom.h"
@@ -209,7 +211,7 @@
 
   storage::MockQuotaManager* quota_manager() { return quota_manager_.get(); }
 
- private:
+ protected:
   std::unique_ptr<base::test::TaskEnvironment> task_environment_;
 
   base::ScopedTempDir temp_dir_;
@@ -231,20 +233,69 @@
       const IndexedDBFactoryTestWithMockTime&) = delete;
 };
 
-TEST_F(IndexedDBFactoryTest, BasicFactoryCreationAndTearDown) {
+class IndexedDBFactoryTestWithStoragePartitioning
+    : public IndexedDBFactoryTest,
+      public testing::WithParamInterface<bool> {
+ public:
+  IndexedDBFactoryTestWithStoragePartitioning() {
+    feature_list_.InitWithFeatureState(
+        blink::features::kThirdPartyStoragePartitioning,
+        IsThirdPartyStoragePartitioningEnabled());
+  }
+
+  bool IsThirdPartyStoragePartitioningEnabled() { return GetParam(); }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+INSTANTIATE_TEST_SUITE_P(
+    /* no prefix */,
+    IndexedDBFactoryTestWithStoragePartitioning,
+    testing::Bool());
+
+TEST_P(IndexedDBFactoryTestWithStoragePartitioning,
+       BasicFactoryCreationAndTearDown) {
+  auto filesystem_proxy = storage::CreateFilesystemProxy();
   SetupContext();
 
   const blink::StorageKey storage_key_1 =
       blink::StorageKey::CreateFromStringForTesting("http://localhost:81");
   auto bucket_locator_1 = storage::BucketLocator();
   bucket_locator_1.storage_key = storage_key_1;
+  auto file_1 = context_->GetLevelDBPathForTesting(bucket_locator_1)
+                    .AppendASCII("1.json");
+  ASSERT_TRUE(CreateDirectory(file_1.DirName()));
+  ASSERT_TRUE(base::WriteFile(file_1, std::string(10, 'a')));
   const blink::StorageKey storage_key_2 =
       blink::StorageKey::CreateFromStringForTesting("http://localhost:82");
   auto bucket_locator_2 = storage::BucketLocator();
   bucket_locator_2.storage_key = storage_key_2;
+  auto file_2 = context_->GetLevelDBPathForTesting(bucket_locator_2)
+                    .AppendASCII("2.json");
+  ASSERT_TRUE(CreateDirectory(file_2.DirName()));
+  ASSERT_TRUE(base::WriteFile(file_2, std::string(100, 'a')));
+  const blink::StorageKey storage_key_3 =
+      blink::StorageKey::CreateFromStringForTesting("http://localhost2:82");
+  auto bucket_locator_3 = storage::BucketLocator();
+  bucket_locator_3.storage_key = storage_key_3;
+  auto file_3 = context_->GetLevelDBPathForTesting(bucket_locator_3)
+                    .AppendASCII("3.json");
+  ASSERT_TRUE(CreateDirectory(file_3.DirName()));
+  ASSERT_TRUE(base::WriteFile(file_3, std::string(1000, 'a')));
+  const blink::StorageKey storage_key_4 =
+      blink::StorageKey(storage_key_1.origin(), storage_key_3.origin());
+  auto bucket_locator_4 = storage::BucketLocator();
+  bucket_locator_4.storage_key = storage_key_4;
+  auto file_4 = context_->GetLevelDBPathForTesting(bucket_locator_4)
+                    .AppendASCII("4.json");
+  ASSERT_TRUE(CreateDirectory(file_4.DirName()));
+  ASSERT_TRUE(base::WriteFile(file_4, std::string(10000, 'a')));
 
   IndexedDBBucketStateHandle bucket_state1_handle;
   IndexedDBBucketStateHandle bucket_state2_handle;
+  IndexedDBBucketStateHandle bucket_state3_handle;
+  IndexedDBBucketStateHandle bucket_state4_handle;
   leveldb::Status s;
 
   std::tie(bucket_state1_handle, s, std::ignore, std::ignore, std::ignore) =
@@ -261,12 +312,62 @@
   EXPECT_TRUE(bucket_state2_handle.IsHeld()) << s.ToString();
   EXPECT_TRUE(s.ok()) << s.ToString();
 
+  std::tie(bucket_state3_handle, s, std::ignore, std::ignore, std::ignore) =
+      factory()->GetOrOpenBucketFactory(
+          bucket_locator_3, context()->GetDataPath(bucket_locator_3),
+          /*create_if_missing=*/true);
+  EXPECT_TRUE(bucket_state3_handle.IsHeld()) << s.ToString();
+  EXPECT_TRUE(s.ok()) << s.ToString();
+
+  std::tie(bucket_state4_handle, s, std::ignore, std::ignore, std::ignore) =
+      factory()->GetOrOpenBucketFactory(
+          bucket_locator_4, context()->GetDataPath(bucket_locator_4),
+          /*create_if_missing=*/true);
+  EXPECT_TRUE(bucket_state4_handle.IsHeld()) << s.ToString();
+  EXPECT_TRUE(s.ok()) << s.ToString();
+
   std::vector<storage::mojom::StorageUsageInfoPtr> origin_info;
   storage::mojom::IndexedDBControlAsyncWaiter sync_control(context());
   sync_control.GetUsage(&origin_info);
 
-  EXPECT_EQ(2ul, origin_info.size());
-  EXPECT_EQ(2ul, factory()->GetOpenBuckets().size());
+  int64_t bucket_size_1 =
+      filesystem_proxy->ComputeDirectorySize(file_1.DirName());
+  int64_t bucket_size_2 =
+      filesystem_proxy->ComputeDirectorySize(file_2.DirName());
+  int64_t bucket_size_3 =
+      filesystem_proxy->ComputeDirectorySize(file_3.DirName());
+  int64_t bucket_size_4 =
+      filesystem_proxy->ComputeDirectorySize(file_4.DirName());
+
+  // Since buckets 1 and 4 have the same origin, they merge when calling
+  // GetUsage.
+  EXPECT_EQ(3ul, origin_info.size());
+  for (const auto& info : origin_info) {
+    if (info->origin == bucket_locator_1.storage_key.origin()) {
+      // This is the size of the 10 and 10000 character files (buckets 1 and 4).
+      if (IsThirdPartyStoragePartitioningEnabled()) {
+        // If third party storage partitioning is on, additional space is taken
+        // by supporting files for the independent buckets.
+        EXPECT_EQ(info->total_size_bytes, bucket_size_1 + bucket_size_4);
+      } else {
+        EXPECT_EQ(bucket_size_1, bucket_size_4);
+        EXPECT_EQ(info->total_size_bytes, bucket_size_1);
+      }
+    } else if (info->origin == bucket_locator_2.storage_key.origin()) {
+      // This is the size of the 100 character file (bucket 2).
+      EXPECT_EQ(info->total_size_bytes, bucket_size_2);
+    } else if (info->origin == bucket_locator_3.storage_key.origin()) {
+      // This is the size of the 1000 character file (bucket 3).
+      EXPECT_EQ(info->total_size_bytes, bucket_size_3);
+    } else {
+      NOTREACHED();
+    }
+  }
+  if (IsThirdPartyStoragePartitioningEnabled()) {
+    EXPECT_EQ(4ul, factory()->GetOpenBuckets().size());
+  } else {
+    EXPECT_EQ(3ul, factory()->GetOpenBuckets().size());
+  }
 }
 
 TEST_F(IndexedDBFactoryTest, CloseSequenceStarts) {
diff --git a/content/browser/interest_group/auction_runner.cc b/content/browser/interest_group/auction_runner.cc
index 290d6a3a..7dec9e8c 100644
--- a/content/browser/interest_group/auction_runner.cc
+++ b/content/browser/interest_group/auction_runner.cc
@@ -602,58 +602,80 @@
 void AuctionRunner::Auction::OnInterestGroupRead(
     std::vector<StorageInterestGroup> interest_groups) {
   ++num_owners_loaded_;
-  if (!interest_groups.empty()) {
-    size_t size_limit = config_->non_shared_params.all_buyers_group_limit;
-    const url::Origin& owner = interest_groups[0].interest_group.owner;
-    post_auction_update_owners_.push_back(owner);
-    const auto limit_iter =
-        config_->non_shared_params.per_buyer_group_limits.find(owner);
-    if (limit_iter !=
-        config_->non_shared_params.per_buyer_group_limits.cend()) {
-      size_limit = static_cast<size_t>(limit_iter->second);
+  if (interest_groups.empty()) {
+    OnOneLoadCompleted();
+    return;
+  }
+  const url::Origin& owner = interest_groups[0].interest_group.owner;
+  post_auction_update_owners_.push_back(owner);
+  for (const auto& bidder : interest_groups) {
+    // Report freshness metrics.
+    if (bidder.interest_group.daily_update_url.has_value()) {
+      UMA_HISTOGRAM_CUSTOM_COUNTS(
+          "Ads.InterestGroup.Auction.GroupFreshness.WithDailyUpdates",
+          (base::Time::Now() - bidder.last_updated).InMinutes(),
+          kGroupFreshnessMin.InMinutes(), kGroupFreshnessMax.InMinutes(),
+          kGroupFreshnessBuckets);
+    } else {
+      UMA_HISTOGRAM_CUSTOM_COUNTS(
+          "Ads.InterestGroup.Auction.GroupFreshness.NoDailyUpdates",
+          (base::Time::Now() - bidder.last_updated).InMinutes(),
+          kGroupFreshnessMin.InMinutes(), kGroupFreshnessMax.InMinutes(),
+          kGroupFreshnessBuckets);
     }
-    StorageInterestGroupDescByPriority cmp;
-    std::sort(interest_groups.begin(), interest_groups.end(), cmp);
-    // Randomize order of interest groups with lowest allowed priority. This
-    // effectively performs a random sample among interest groups with the same
-    // priority.
-    size_limit = std::min(interest_groups.size(), size_limit);
-    if (size_limit > 0) {
-      double min_priority =
-          interest_groups[size_limit - 1].interest_group.priority.value();
-      auto rand_begin = std::lower_bound(
-          interest_groups.begin(), interest_groups.end(), min_priority, cmp);
-      auto rand_end = std::upper_bound(rand_begin, interest_groups.end(),
-                                       min_priority, cmp);
-      base::RandomShuffle(rand_begin, rand_end);
-    }
-    interest_groups.resize(size_limit);
-    for (auto& bidder : interest_groups) {
-      // Report freshness metrics.
-      if (bidder.interest_group.daily_update_url.has_value()) {
-        UMA_HISTOGRAM_CUSTOM_COUNTS(
-            "Ads.InterestGroup.Auction.GroupFreshness.WithDailyUpdates",
-            (base::Time::Now() - bidder.last_updated).InMinutes(),
-            kGroupFreshnessMin.InMinutes(), kGroupFreshnessMax.InMinutes(),
-            kGroupFreshnessBuckets);
-      } else {
-        UMA_HISTOGRAM_CUSTOM_COUNTS(
-            "Ads.InterestGroup.Auction.GroupFreshness.NoDailyUpdates",
-            (base::Time::Now() - bidder.last_updated).InMinutes(),
-            kGroupFreshnessMin.InMinutes(), kGroupFreshnessMax.InMinutes(),
-            kGroupFreshnessBuckets);
-      }
+  }
 
-      // Ignore interest groups with no bidding script or no ads.
-      if (!bidder.interest_group.bidding_url)
-        continue;
-      if (bidder.interest_group.ads->empty())
-        continue;
-      bidder.interest_group.priority.reset();
-      bid_states_.emplace_back(BidState());
-      bid_states_.back().bidder = std::move(bidder);
-    }
-    ++num_owners_with_interest_groups_;
+  // Ignore interest groups with no bidding script or no ads.
+  interest_groups.erase(
+      std::remove_if(interest_groups.begin(), interest_groups.end(),
+                     [](const StorageInterestGroup& bidder) {
+                       return !bidder.interest_group.bidding_url ||
+                              bidder.interest_group.ads->empty();
+                     }),
+      interest_groups.end());
+
+  // If there are no interest groups with both a bidding script and ads, nothing
+  // else to do.
+  if (interest_groups.empty()) {
+    OnOneLoadCompleted();
+    return;
+  }
+
+  // Only count owners with interest groups theoretically capable of making
+  // bids as participating in this auction.
+  ++num_owners_with_interest_groups_;
+
+  size_t size_limit = config_->non_shared_params.all_buyers_group_limit;
+  const auto limit_iter =
+      config_->non_shared_params.per_buyer_group_limits.find(owner);
+  if (limit_iter != config_->non_shared_params.per_buyer_group_limits.cend()) {
+    size_limit = static_cast<size_t>(limit_iter->second);
+  }
+  size_limit = std::min(interest_groups.size(), size_limit);
+  if (size_limit == 0) {
+    OnOneLoadCompleted();
+    return;
+  }
+
+  StorageInterestGroupDescByPriority cmp;
+  std::sort(interest_groups.begin(), interest_groups.end(), cmp);
+  // Randomize order of interest groups with lowest allowed priority. This
+  // effectively performs a random sample among interest groups with the same
+  // priority.
+  double min_priority =
+      interest_groups[size_limit - 1].interest_group.priority.value();
+  auto rand_begin = std::lower_bound(interest_groups.begin(),
+                                     interest_groups.end(), min_priority, cmp);
+  auto rand_end =
+      std::upper_bound(rand_begin, interest_groups.end(), min_priority, cmp);
+  base::RandomShuffle(rand_begin, rand_end);
+  interest_groups.resize(size_limit);
+
+  // Set up remaining interest groups to generate bids.
+  for (auto& bidder : interest_groups) {
+    bidder.interest_group.priority.reset();
+    bid_states_.emplace_back();
+    bid_states_.back().bidder = std::move(bidder);
   }
   OnOneLoadCompleted();
 }
diff --git a/content/browser/interest_group/auction_runner_unittest.cc b/content/browser/interest_group/auction_runner_unittest.cc
index 19be0a2a..41a7ea1 100644
--- a/content/browser/interest_group/auction_runner_unittest.cc
+++ b/content/browser/interest_group/auction_runner_unittest.cc
@@ -99,9 +99,9 @@
                           int num_ad_components,
                           const url::Origin& interest_group_owner,
                           const std::string& interest_group_name,
-                          bool has_signals,
-                          const std::string& signal_key,
-                          const std::string& signal_val,
+                          bool has_signals = false,
+                          const std::string& signal_key = "",
+                          const std::string& signal_val = "",
                           bool report_post_auction_signals = false,
                           const std::string& debug_loss_report_url = "",
                           const std::string& debug_win_report_url = "") {
@@ -2171,7 +2171,7 @@
   EXPECT_EQ(-1, result_.bidder2_bid_count);
   EXPECT_EQ(0u, result_.bidder2_prev_wins.size());
   CheckHistograms(AuctionRunner::AuctionResult::kNoInterestGroups,
-                  /*expected_interest_groups=*/0, /*expected_owners=*/1,
+                  /*expected_interest_groups=*/0, /*expected_owners=*/0,
                   /*expected_sellers=*/0);
 }
 
@@ -2195,7 +2195,7 @@
   EXPECT_EQ(-1, result_.bidder2_bid_count);
   EXPECT_EQ(0u, result_.bidder2_prev_wins.size());
   CheckHistograms(AuctionRunner::AuctionResult::kNoInterestGroups,
-                  /*expected_interest_groups=*/0, /*expected_owners=*/1,
+                  /*expected_interest_groups=*/0, /*expected_owners=*/0,
                   /*expected_sellers=*/0);
 }
 
@@ -2219,7 +2219,7 @@
   EXPECT_EQ(-1, result_.bidder2_bid_count);
   EXPECT_EQ(0u, result_.bidder2_prev_wins.size());
   CheckHistograms(AuctionRunner::AuctionResult::kNoInterestGroups,
-                  /*expected_interest_groups=*/0, /*expected_owners=*/1,
+                  /*expected_interest_groups=*/0, /*expected_owners=*/0,
                   /*expected_sellers=*/0);
 }
 
@@ -6893,6 +6893,38 @@
   }
 }
 
+// Test the case where there's one IG with two groups, a size limit of 1, and
+// the highest priority group has no bid script. The lower priority group should
+// get a chance to bid, rather than being filtered out.
+TEST_F(AuctionRunnerTest, SizeLimitHighestPriorityGroupHasNoBidScript) {
+  auction_worklet::AddJavascriptResponse(
+      &url_loader_factory_, kBidder1Url,
+      MakeBidScript(kSeller, "1", "https://ad1.com/", /*num_ad_components=*/0,
+                    kBidder1, kBidder1Name));
+  auction_worklet::AddJavascriptResponse(&url_loader_factory_, kSellerUrl,
+                                         MakeAuctionScript());
+
+  std::vector<StorageInterestGroup> bidders;
+  // Low priority group with a bidding URL.
+  bidders.emplace_back(MakeInterestGroup(
+      kBidder1, kBidder1Name, kBidder1Url,
+      /*trusted_bidding_signals_url=*/absl::nullopt,
+      /*trusted_bidding_signals_keys=*/{}, GURL("https://ad1.com")));
+  bidders.back().interest_group.priority = 0;
+
+  // High priority group without a bidding URL.
+  bidders.emplace_back(MakeInterestGroup(
+      kBidder1, "other-interest-group-name", /*bidding_url=*/absl::nullopt,
+      /*trusted_bidding_signals_url=*/absl::nullopt,
+      /*trusted_bidding_signals_keys=*/{}, GURL("https://ad2.com")));
+  bidders.back().interest_group.priority = 10;
+
+  RunAuctionAndWait(kSellerUrl, std::move(bidders));
+  EXPECT_THAT(result_.errors, testing::ElementsAre());
+  EXPECT_EQ(InterestGroupKey(kBidder1, kBidder1Name), result_.winning_group_id);
+  EXPECT_EQ(GURL("https://ad1.com/"), result_.ad_url);
+}
+
 // Enable and test forDebuggingOnly.reportAdAuctionLoss() and
 // forDebuggingOnly.reportAdAuctionWin() APIs.
 class AuctionRunnerBiddingAndScoringDebugReportingAPIEnabledTest
diff --git a/content/browser/interest_group/interest_group_permissions_checker.cc b/content/browser/interest_group/interest_group_permissions_checker.cc
index be4fe7f..f40bd72 100644
--- a/content/browser/interest_group/interest_group_permissions_checker.cc
+++ b/content/browser/interest_group/interest_group_permissions_checker.cc
@@ -186,15 +186,15 @@
 void InterestGroupPermissionsChecker::OnJsonParsed(
     ActiveRequestMap::iterator active_request,
     data_decoder::DataDecoder::ValueOrError result) {
-  if (result.error || !result.value->is_dict()) {
+  if (!result.has_value() || !result->is_dict()) {
     OnActiveRequestComplete(active_request, Permissions());
     return;
   }
 
   absl::optional<bool> can_join =
-      result.value->GetDict().FindBool("joinAdInterestGroup");
+      result->GetDict().FindBool("joinAdInterestGroup");
   absl::optional<bool> can_leave =
-      result.value->GetDict().FindBool("leaveAdInterestGroup");
+      result->GetDict().FindBool("leaveAdInterestGroup");
   Permissions permissions{/*can_join=*/can_join.value_or(false),
                           /*can_leave=*/can_leave.value_or(false)};
   OnActiveRequestComplete(active_request, permissions);
diff --git a/content/browser/interest_group/interest_group_update_manager.cc b/content/browser/interest_group/interest_group_update_manager.cc
index 399b444..5cb38c4 100644
--- a/content/browser/interest_group/interest_group_update_manager.cc
+++ b/content/browser/interest_group/interest_group_update_manager.cc
@@ -200,10 +200,10 @@
     const std::string& name,
     const data_decoder::DataDecoder::ValueOrError& result) {
   // TODO(crbug.com/1186444): Report to devtools.
-  if (result.error) {
+  if (!result.has_value()) {
     return absl::nullopt;
   }
-  const base::Value::Dict* dict = result.value->GetIfDict();
+  const base::Value::Dict* dict = result->GetIfDict();
   if (!dict) {
     return absl::nullopt;
   }
diff --git a/content/browser/mojo_binder_policy_applier.h b/content/browser/mojo_binder_policy_applier.h
index d98f7d01..444f015 100644
--- a/content/browser/mojo_binder_policy_applier.h
+++ b/content/browser/mojo_binder_policy_applier.h
@@ -23,7 +23,8 @@
 // The action to take for each interface is specified in the given
 // `MojoBinderPolicyMap`, and kDefer is used when no policy is specified.
 //
-// See content/browser/prerender/README.md for more about capability control.
+// See content/browser/preloading/prerender/README.md for more about capability
+// control.
 class CONTENT_EXPORT MojoBinderPolicyApplier {
  public:
   enum class Mode {
diff --git a/content/browser/preloading/prefetch/prefetch_service.cc b/content/browser/preloading/prefetch/prefetch_service.cc
index e0ac2a7..f8e0b0a9 100644
--- a/content/browser/preloading/prefetch/prefetch_service.cc
+++ b/content/browser/preloading/prefetch/prefetch_service.cc
@@ -893,7 +893,8 @@
     base::WeakPtr<PrefetchContainer> prefetch_container) const {
   bool matching_prefetch = false;
   for (const auto& prefetch_iter : all_prefetches_) {
-    if (prefetch_iter.second->GetURL() == prefetch_container->GetURL() &&
+    if (prefetch_iter.second &&
+        prefetch_iter.second->GetURL() == prefetch_container->GetURL() &&
         prefetch_iter.second->GetReferringRenderFrameHostId() !=
             prefetch_container->GetReferringRenderFrameHostId()) {
       matching_prefetch = true;
diff --git a/content/browser/preloading/prerender/prerender_host_registry_unittest.cc b/content/browser/preloading/prerender/prerender_host_registry_unittest.cc
index dae9af2..9fd569f 100644
--- a/content/browser/preloading/prerender/prerender_host_registry_unittest.cc
+++ b/content/browser/preloading/prerender/prerender_host_registry_unittest.cc
@@ -7,9 +7,9 @@
 #include "base/test/bind.h"
 #include "base/test/scoped_feature_list.h"
 #include "content/browser/preloading/prerender/prerender_host.h"
+#include "content/browser/preloading/speculation_rules/speculation_host_impl.h"
 #include "content/browser/renderer_host/render_frame_host_impl.h"
 #include "content/browser/site_instance_impl.h"
-#include "content/browser/speculation_rules/speculation_host_impl.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/storage_partition.h"
 #include "content/public/test/prerender_test_util.h"
diff --git a/content/browser/speculation_rules/DIR_METADATA b/content/browser/preloading/speculation_rules/DIR_METADATA
similarity index 97%
rename from content/browser/speculation_rules/DIR_METADATA
rename to content/browser/preloading/speculation_rules/DIR_METADATA
index eaf73c7..b30be6d5 100644
--- a/content/browser/speculation_rules/DIR_METADATA
+++ b/content/browser/preloading/speculation_rules/DIR_METADATA
@@ -1,3 +1,3 @@
 monorail: {
   component: "Internals>Preload"
-}
+}
\ No newline at end of file
diff --git a/content/browser/speculation_rules/OWNERS b/content/browser/preloading/speculation_rules/OWNERS
similarity index 98%
rename from content/browser/speculation_rules/OWNERS
rename to content/browser/preloading/speculation_rules/OWNERS
index 88908ea..375341e 100644
--- a/content/browser/speculation_rules/OWNERS
+++ b/content/browser/preloading/speculation_rules/OWNERS
@@ -1,3 +1,3 @@
 jbroman@chromium.org
 lingqi@chromium.org
-ryansturm@chromium.org
+ryansturm@chromium.org
\ No newline at end of file
diff --git a/content/browser/speculation_rules/README.md b/content/browser/preloading/speculation_rules/README.md
similarity index 100%
rename from content/browser/speculation_rules/README.md
rename to content/browser/preloading/speculation_rules/README.md
diff --git a/content/browser/speculation_rules/speculation_host_impl.cc b/content/browser/preloading/speculation_rules/speculation_host_impl.cc
similarity index 98%
rename from content/browser/speculation_rules/speculation_host_impl.cc
rename to content/browser/preloading/speculation_rules/speculation_host_impl.cc
index 32e7cfa7..d3075ad5 100644
--- a/content/browser/speculation_rules/speculation_host_impl.cc
+++ b/content/browser/preloading/speculation_rules/speculation_host_impl.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/browser/speculation_rules/speculation_host_impl.h"
+#include "content/browser/preloading/speculation_rules/speculation_host_impl.h"
 #include <functional>
 
 #include "base/containers/span.h"
diff --git a/content/browser/speculation_rules/speculation_host_impl.h b/content/browser/preloading/speculation_rules/speculation_host_impl.h
similarity index 90%
rename from content/browser/speculation_rules/speculation_host_impl.h
rename to content/browser/preloading/speculation_rules/speculation_host_impl.h
index e426b0c..888cfc44 100644
--- a/content/browser/speculation_rules/speculation_host_impl.h
+++ b/content/browser/preloading/speculation_rules/speculation_host_impl.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CONTENT_BROWSER_SPECULATION_RULES_SPECULATION_HOST_IMPL_H_
-#define CONTENT_BROWSER_SPECULATION_RULES_SPECULATION_HOST_IMPL_H_
+#ifndef CONTENT_BROWSER_PRELOADING_SPECULATION_RULES_SPECULATION_HOST_IMPL_H_
+#define CONTENT_BROWSER_PRELOADING_SPECULATION_RULES_SPECULATION_HOST_IMPL_H_
 
 #include <vector>
 
@@ -65,4 +65,4 @@
 
 }  // namespace content
 
-#endif  // CONTENT_BROWSER_SPECULATION_RULES_SPECULATION_HOST_IMPL_H_
+#endif  // CONTENT_BROWSER_PRELOADING_SPECULATION_RULES_SPECULATION_HOST_IMPL_H_
diff --git a/content/browser/speculation_rules/speculation_host_impl_unittest.cc b/content/browser/preloading/speculation_rules/speculation_host_impl_unittest.cc
similarity index 98%
rename from content/browser/speculation_rules/speculation_host_impl_unittest.cc
rename to content/browser/preloading/speculation_rules/speculation_host_impl_unittest.cc
index 506d09b..bd25a44 100644
--- a/content/browser/speculation_rules/speculation_host_impl_unittest.cc
+++ b/content/browser/preloading/speculation_rules/speculation_host_impl_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/browser/speculation_rules/speculation_host_impl.h"
+#include "content/browser/preloading/speculation_rules/speculation_host_impl.h"
 
 #include "base/memory/raw_ptr.h"
 #include "base/test/bind.h"
diff --git a/content/browser/renderer_host/media/media_stream_manager.cc b/content/browser/renderer_host/media/media_stream_manager.cc
index 4052cab..df54fe9 100644
--- a/content/browser/renderer_host/media/media_stream_manager.cc
+++ b/content/browser/renderer_host/media/media_stream_manager.cc
@@ -2607,10 +2607,11 @@
     }
     const blink::MediaStreamDevice& new_device = new_device_ptr->value();
     MediaStreamDevice old_device;
-    auto& old_devices = old_devices_by_type[static_cast<int>(new_device.type)];
-    if (!old_devices.empty()) {
-      old_device = old_devices.back();
-      old_devices.pop_back();
+    auto& old_devices_of_new_device_type =
+        old_devices_by_type[static_cast<int>(new_device.type)];
+    if (!old_devices_of_new_device_type.empty()) {
+      old_device = old_devices_of_new_device_type.back();
+      old_devices_of_new_device_type.pop_back();
     }
 
     request->device_changed_cb.Run(label, old_device, new_device);
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index bce2ee5c4..813a6c26 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -1031,10 +1031,10 @@
     case features::SubframeShutdownDelayType::kMemoryBased: {
       // See subframe-reuse design doc for more detail on these values.
       // docs.google.com/document/d/1x_h4Gg4ForILEj8A4rMBX6d84uHWyQ9RSXmGVqMlBTk
-      static constexpr int64_t kHighMemoryThreshold = 8000000000;
-      static constexpr int64_t kMaxMemoryThreshold = 16000000000;
+      static constexpr uint64_t kHighMemoryThreshold = 8'000'000'000;
+      static constexpr uint64_t kMaxMemoryThreshold = 16'000'000'000;
 
-      const int64_t available_memory =
+      const uint64_t available_memory =
           base::SysInfo::AmountOfAvailablePhysicalMemory();
       if (available_memory <= kHighMemoryThreshold)
         return kShortDelay;
@@ -1043,7 +1043,7 @@
 
       // Scale delay linearly based on where |available_memory| lies between
       // |kHighMemoryThreshold| and |kMaxMemoryThreshold|.
-      const int64_t available_memory_factor =
+      const uint64_t available_memory_factor =
           (available_memory - kHighMemoryThreshold) /
           (kMaxMemoryThreshold - kHighMemoryThreshold);
       return kShortDelay + (kLongDelay - kShortDelay) * available_memory_factor;
@@ -7081,21 +7081,21 @@
   // inside the MPArch renderer process, so we need to set it here.
   // TODO(crbug.com/1315802): Refactor _unfencedTop handling.
   if (params->is_unfenced_top_navigation) {
-    GURL validated_url = params->url;
+    const GURL validated_params_url = params->url;
 
     // Check that the IPC parameters are valid and that the navigation
     // is allowed.
     // TODO(crbug.com/1315802): When this handling is refactored into a separate
     // IPC, make sure that the checks from VerifyOpenURLParams above are not
     // unintentionally weakened.
-    if (!ValidateUnfencedTopNavigation(this, validated_url,
+    if (!ValidateUnfencedTopNavigation(this, validated_params_url,
                                        GetProcess()->GetID(), params->post_body,
                                        params->user_gesture)) {
       return;
     }
 
     TRACE_EVENT1("navigation", "RenderFrameHostImpl::OpenURL(_unfencedTop)",
-                 "url", validated_url.possibly_invalid_spec());
+                 "url", validated_params_url.possibly_invalid_spec());
 
     // There are some relevant parameter changes to note:
     // Change the navigation target to the outermost frame.
@@ -7129,7 +7129,7 @@
     blink::NavigationDownloadPolicy download_policy;
 
     target_frame->frame_tree_node()->navigator().NavigateFromFrameProxy(
-        target_frame, validated_url,
+        target_frame, validated_params_url,
         base::OptionalOrNullptr(params->initiator_frame_token),
         GetProcess()->GetID(), params->initiator_origin, GetSiteInstance(),
         content::Referrer(), ui::PAGE_TRANSITION_LINK,
diff --git a/content/browser/resources/prerender/OWNERS b/content/browser/resources/prerender/OWNERS
index 894bf54..03a1d5b 100644
--- a/content/browser/resources/prerender/OWNERS
+++ b/content/browser/resources/prerender/OWNERS
@@ -1 +1 @@
-file://content/browser/prerender/OWNERS
+file://content/browser/preloading/prerender/OWNERS
diff --git a/content/browser/speech/tts_controller_impl.cc b/content/browser/speech/tts_controller_impl.cc
index 644dcef..1f35ba94 100644
--- a/content/browser/speech/tts_controller_impl.cc
+++ b/content/browser/speech/tts_controller_impl.cc
@@ -705,13 +705,13 @@
     data_decoder::DataDecoder::ValueOrError result) {
   // Error checks.
   // If invalid xml, return original utterance text.
-  if (!result.value) {
+  if (!result.has_value()) {
     std::move(on_ssml_parsed).Run(utterance);
     return;
   }
 
   std::string root_tag_name;
-  data_decoder::GetXmlElementTagName(*result.value, &root_tag_name);
+  data_decoder::GetXmlElementTagName(*result, &root_tag_name);
   // Root element must be <speak>.
   if (root_tag_name.compare("speak") != 0) {
     std::move(on_ssml_parsed).Run(utterance);
@@ -720,7 +720,7 @@
 
   std::string parsed_text;
   // Change from unique_ptr to base::Value* so recursion will work.
-  PopulateParsedText(&parsed_text, &(*result.value));
+  PopulateParsedText(&parsed_text, &*result);
 
   // Run with parsed_text.
   std::move(on_ssml_parsed).Run(parsed_text);
diff --git a/content/browser/storage_partition_impl.cc b/content/browser/storage_partition_impl.cc
index 47c137b..ba1b3b9 100644
--- a/content/browser/storage_partition_impl.cc
+++ b/content/browser/storage_partition_impl.cc
@@ -1301,8 +1301,8 @@
           ? fallback_for_blob_urls->GetBlobUrlRegistry()->AsWeakPtr()
           : nullptr);
 
-  blob_registry_ = BlobRegistryWrapper::Create(
-      blob_context, filesystem_context_, blob_url_registry_->AsWeakPtr());
+  blob_registry_ = BlobRegistryWrapper::Create(blob_context,
+                                               blob_url_registry_->AsWeakPtr());
 
   prefetch_url_loader_service_ =
       std::make_unique<PrefetchURLLoaderService>(browser_context_);
diff --git a/content/browser/webauth/authenticator_common.cc b/content/browser/webauth/authenticator_common.cc
index 4c80888..9897bc3 100644
--- a/content/browser/webauth/authenticator_common.cc
+++ b/content/browser/webauth/authenticator_common.cc
@@ -513,22 +513,26 @@
 
 void AuthenticatorCommon::OnLargeBlobCompressed(
     uint64_t original_size,
-    data_decoder::DataDecoder::ResultOrError<mojo_base::BigBuffer> result) {
-  if (result.value) {
+    base::expected<mojo_base::BigBuffer, std::string> result) {
+  if (result.has_value()) {
     ctap_get_assertion_request_->large_blob_write = device::LargeBlob(
-        device::fido_parsing_utils::Materialize(*result.value), original_size);
+        device::fido_parsing_utils::Materialize(*result), original_size);
   }
   StartGetAssertionRequest(/*allow_skipping_pin_touch=*/true);
 }
 
 void AuthenticatorCommon::OnLargeBlobUncompressed(
     device::AuthenticatorGetAssertionResponse response,
-    data_decoder::DataDecoder::ResultOrError<mojo_base::BigBuffer> result) {
+    base::expected<mojo_base::BigBuffer, std::string> result) {
+  absl::optional<mojo_base::BigBuffer> value;
+  if (result.has_value())
+    value = std::move(*result);
+
   CompleteGetAssertionRequest(
       blink::mojom::AuthenticatorStatus::SUCCESS,
       CreateGetAssertionResponse(
           std::move(response),
-          device::fido_parsing_utils::MaterializeOrNull(result.value)));
+          device::fido_parsing_utils::MaterializeOrNull(value)));
 }
 
 // mojom::Authenticator
@@ -600,11 +604,11 @@
   absl::optional<std::string> appid_exclude;
   if (options->appid_exclude) {
     appid_exclude = "";
-    auto status = security_checker_->ValidateAppIdExtension(
+    auto add_id_status = security_checker_->ValidateAppIdExtension(
         *options->appid_exclude, caller_origin,
         options->remote_desktop_client_override, &appid_exclude.value());
-    if (status != blink::mojom::AuthenticatorStatus::SUCCESS) {
-      CompleteMakeCredentialRequest(status);
+    if (add_id_status != blink::mojom::AuthenticatorStatus::SUCCESS) {
+      CompleteMakeCredentialRequest(add_id_status);
       return;
     }
     // `ValidateAppidExtension` must have set a value to use. If not, it would
@@ -918,11 +922,11 @@
   if (options->appid) {
     requested_extensions_.insert(RequestExtension::kAppID);
     std::string app_id;
-    auto status = security_checker_->ValidateAppIdExtension(
+    auto add_id_status = security_checker_->ValidateAppIdExtension(
         *options->appid, caller_origin, options->remote_desktop_client_override,
         &app_id);
-    if (status != blink::mojom::AuthenticatorStatus::SUCCESS) {
-      CompleteGetAssertionRequest(status);
+    if (add_id_status != blink::mojom::AuthenticatorStatus::SUCCESS) {
+      CompleteGetAssertionRequest(add_id_status);
       return;
     }
     // `ValidateAppidExtension` must have set a value to use. If not, it would
diff --git a/content/browser/webauth/authenticator_common.h b/content/browser/webauth/authenticator_common.h
index 3be0e11..6a0fd01 100644
--- a/content/browser/webauth/authenticator_common.h
+++ b/content/browser/webauth/authenticator_common.h
@@ -155,13 +155,13 @@
   // start a request.
   void OnLargeBlobCompressed(
       uint64_t original_size,
-      data_decoder::DataDecoder::ResultOrError<mojo_base::BigBuffer> result);
+      base::expected<mojo_base::BigBuffer, std::string> result);
 
   // Callback to handle the large blob being uncompressed before completing a
   // request.
   void OnLargeBlobUncompressed(
       device::AuthenticatorGetAssertionResponse response,
-      data_decoder::DataDecoder::ResultOrError<mojo_base::BigBuffer> result);
+      base::expected<mojo_base::BigBuffer, std::string> result);
 
   // Callback to handle the async response from a U2fDevice.
   void OnRegisterResponse(
diff --git a/content/browser/webauth/virtual_authenticator.cc b/content/browser/webauth/virtual_authenticator.cc
index ee443432..b5527c39 100644
--- a/content/browser/webauth/virtual_authenticator.cc
+++ b/content/browser/webauth/virtual_authenticator.cc
@@ -215,28 +215,31 @@
 
 void VirtualAuthenticator::OnLargeBlobUncompressed(
     GetLargeBlobCallback callback,
-    data_decoder::DataDecoder::ResultOrError<mojo_base::BigBuffer> result) {
-  std::move(callback).Run(
-      device::fido_parsing_utils::MaterializeOrNull(result.value));
+    base::expected<mojo_base::BigBuffer, std::string> result) {
+  absl::optional<mojo_base::BigBuffer> value;
+  if (result.has_value())
+    value = std::move(*result);
+
+  std::move(callback).Run(device::fido_parsing_utils::MaterializeOrNull(value));
 }
 
 void VirtualAuthenticator::OnLargeBlobCompressed(
     base::span<const uint8_t> key_handle,
     uint64_t original_size,
     SetLargeBlobCallback callback,
-    data_decoder::DataDecoder::ResultOrError<mojo_base::BigBuffer> result) {
+    base::expected<mojo_base::BigBuffer, std::string> result) {
   auto registration = state_->registrations.find(key_handle);
   if (registration == state_->registrations.end()) {
     std::move(callback).Run(false);
     return;
   }
-  if (!result.value) {
+  if (!result.has_value()) {
     std::move(callback).Run(false);
     return;
   }
   state_->InjectLargeBlob(
       &registration->second,
-      device::LargeBlob(device::fido_parsing_utils::Materialize(*result.value),
+      device::LargeBlob(device::fido_parsing_utils::Materialize(*result),
                         original_size));
   std::move(callback).Run(true);
 }
diff --git a/content/browser/webauth/virtual_authenticator.h b/content/browser/webauth/virtual_authenticator.h
index ad1fe86c..e9cdc226 100644
--- a/content/browser/webauth/virtual_authenticator.h
+++ b/content/browser/webauth/virtual_authenticator.h
@@ -118,12 +118,12 @@
  private:
   void OnLargeBlobUncompressed(
       GetLargeBlobCallback callback,
-      data_decoder::DataDecoder::ResultOrError<mojo_base::BigBuffer> result);
+      base::expected<mojo_base::BigBuffer, std::string> result);
   void OnLargeBlobCompressed(
       base::span<const uint8_t> key_handle,
       uint64_t original_size,
       SetLargeBlobCallback callback,
-      data_decoder::DataDecoder::ResultOrError<mojo_base::BigBuffer> result);
+      base::expected<mojo_base::BigBuffer, std::string> result);
 
   const device::ProtocolVersion protocol_;
   const device::Ctap2Version ctap2_version_;
diff --git a/content/browser/webid/fedcm_metrics.h b/content/browser/webid/fedcm_metrics.h
index 601bbb8..de7fd13 100644
--- a/content/browser/webid/fedcm_metrics.h
+++ b/content/browser/webid/fedcm_metrics.h
@@ -28,9 +28,9 @@
   kManifestHttpNotFound,
   kManifestNoResponse,
   kManifestInvalidResponse,
-  kClientMetadataHttpNotFound,
-  kClientMetadataNoResponse,
-  kClientMetadataInvalidResponse,
+  kClientMetadataHttpNotFound,     // obsolete
+  kClientMetadataNoResponse,       // obsolete
+  kClientMetadataInvalidResponse,  // obsolete
   kAccountsHttpNotFound,
   kAccountsNoResponse,
   kAccountsInvalidResponse,
@@ -38,7 +38,7 @@
   kIdTokenNoResponse,
   kIdTokenInvalidResponse,
   kIdTokenInvalidRequest,
-  kClientMetadataMissingPrivacyPolicyUrl,
+  kClientMetadataMissingPrivacyPolicyUrl,  // obsolete
   kThirdPartyCookiesBlocked,
   kDisabledInSettings,
   kDisabledInFlags,
@@ -48,7 +48,7 @@
   kManifestNotInManifestList,
   kManifestListTooBig,
   kDisabledEmbargo,
-  kUserInterfaceTimedOut,
+  kUserInterfaceTimedOut,  // obsolete
 
   kMaxValue = kUserInterfaceTimedOut
 };
diff --git a/content/browser/webid/federated_auth_request_impl.cc b/content/browser/webid/federated_auth_request_impl.cc
index 6a89b550..e9258d5 100644
--- a/content/browser/webid/federated_auth_request_impl.cc
+++ b/content/browser/webid/federated_auth_request_impl.cc
@@ -6,6 +6,7 @@
 
 #include "base/callback.h"
 #include "base/command_line.h"
+#include "base/rand_util.h"
 #include "base/ranges/algorithm.h"
 #include "base/strings/string_piece.h"
 #include "base/threading/sequenced_task_runner_handle.h"
@@ -42,13 +43,7 @@
 
 namespace {
 static constexpr base::TimeDelta kDefaultTokenRequestDelay = base::Seconds(3);
-// TODO(yigu): We need to make sure the delay is greater than the time required
-// for a successful flow based on `Blink.FedCm.Timing.TurnaroundTime`.
-// https://crbug.com/1298316.
-// TODO(crbug.com/1329633): We temporarily use 120s to make the UI more
-// accessible. We should try not to dismiss it automatically if a user is
-// interacting with it using keyboard or accessibility tools.
-static constexpr base::TimeDelta kRequestRejectionDelay = base::Seconds(120);
+static constexpr base::TimeDelta kMaxRejectionTime = base::Seconds(60);
 
 // Maximum number of provider URLs in the manifest list.
 // TODO(cbiesinger): Determine what the right number is.
@@ -222,16 +217,18 @@
   }
 }
 
+// TODO(crbug.com/1344150): Use normal distribution after sufficient data is
+// collected.
+base::TimeDelta GetRandomRejectionTime() {
+  return kMaxRejectionTime * base::RandDouble();
+}
+
 }  // namespace
 
 FederatedAuthRequestImpl::FederatedAuthRequestImpl(
     RenderFrameHost& host,
     mojo::PendingReceiver<blink::mojom::FederatedAuthRequest> receiver)
     : DocumentService(host, std::move(receiver)),
-      delay_timer_(FROM_HERE,
-                   kRequestRejectionDelay,
-                   this,
-                   &FederatedAuthRequestImpl::OnRejectRequest),
       token_request_delay_(kDefaultTokenRequestDelay) {}
 
 FederatedAuthRequestImpl::~FederatedAuthRequestImpl() {
@@ -293,8 +290,6 @@
   nonce_ = nonce;
   prefer_auto_sign_in_ = prefer_auto_sign_in && IsFedCmAutoSigninEnabled();
   start_time_ = base::TimeTicks::Now();
-  if (!ShouldCompleteRequestImmediately())
-    delay_timer_.Reset();
 
   if (!GetApiPermissionContext()) {
     CompleteRequest(FederatedAuthRequestResult::kError, "",
@@ -771,8 +766,11 @@
       GetApiPermissionContext()->RecordDismissAndEmbargo(origin());
     }
 
+    // Reject the promise immediately if the UI is dismissed without selecting
+    // an account. Meanwhile, we fuzz the rejection time for other failures to
+    // make it indistinguishable.
     CompleteRequest(FederatedAuthRequestResult::kError, "",
-                    /*should_call_callback=*/false);
+                    /*should_call_callback=*/true);
     return;
   }
 
@@ -962,6 +960,12 @@
     RequestTokenStatus status =
         FederatedAuthRequestResultToRequestTokenStatus(result);
     std::move(auth_request_callback_).Run(status, id_token);
+  } else {
+    base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
+        FROM_HERE,
+        base::BindOnce(&FederatedAuthRequestImpl::OnRejectRequest,
+                       weak_ptr_factory_.GetWeakPtr()),
+        GetRandomRejectionTime());
   }
 }
 
@@ -1108,18 +1112,7 @@
 void FederatedAuthRequestImpl::OnRejectRequest() {
   if (auth_request_callback_) {
     DCHECK(!logout_callback_);
-    // If we have completed the request, e.g. when the token is returned or the
-    // API is aborted, `auth_request_callback_` would be null so we won't double
-    // count. If the request was failed but we have not yet rejected the
-    // promise, e.g. when the user has declined the permission or the API is
-    // disabled etc., we have already reported the errors to console. i.e.
-    // `errors_logged_to_console_` would be true so we won't double count
-    // either. We record `kUserInterfaceTimedOut` only when the UI is displayed
-    // and then time out without user interaction.
-    if (!errors_logged_to_console_) {
-      fedcm_metrics_->RecordRequestTokenStatus(
-          TokenStatus::kUserInterfaceTimedOut);
-    }
+    DCHECK(errors_logged_to_console_);
     CompleteRequest(FederatedAuthRequestResult::kError, "",
                     /*should_call_callback=*/true);
   }
diff --git a/content/browser/webid/federated_auth_request_impl.h b/content/browser/webid/federated_auth_request_impl.h
index d6c63716..544f3b8 100644
--- a/content/browser/webid/federated_auth_request_impl.h
+++ b/content/browser/webid/federated_auth_request_impl.h
@@ -13,7 +13,6 @@
 #include "base/containers/queue.h"
 #include "base/memory/raw_ptr.h"
 #include "base/time/time.h"
-#include "base/timer/timer.h"
 #include "content/browser/webid/fedcm_metrics.h"
 #include "content/browser/webid/idp_network_request_manager.h"
 #include "content/common/content_export.h"
@@ -203,7 +202,6 @@
   base::TimeTicks show_accounts_dialog_time_;
   base::TimeTicks select_account_time_;
   base::TimeTicks token_response_time_;
-  base::DelayTimer delay_timer_;
   base::TimeDelta token_request_delay_;
   bool errors_logged_to_console_{false};
   RequestTokenCallback auth_request_callback_;
diff --git a/content/browser/webid/federated_auth_request_impl_unittest.cc b/content/browser/webid/federated_auth_request_impl_unittest.cc
index 9459e98..243c867 100644
--- a/content/browser/webid/federated_auth_request_impl_unittest.cc
+++ b/content/browser/webid/federated_auth_request_impl_unittest.cc
@@ -1283,8 +1283,9 @@
   ExpectRequestTokenStatusUKM(TokenStatus::kSuccess);
 }
 
-// Test that request fails if user does not select an account.
-TEST_F(BasicFederatedAuthRequestImplTest, MetricsForNotSelectingAccount) {
+// Test that request fails if UI is dismissed without an account being selected.
+TEST_F(BasicFederatedAuthRequestImplTest,
+       MetricsForUIDismissedWithoutSelectingAccount) {
   base::HistogramTester histogram_tester_;
 
   AccountList displayed_accounts;
@@ -1309,6 +1310,7 @@
 
   EXPECT_EQ(kConfigurationValid.accounts.size(), 1u);
   MockConfiguration configuration = kConfigurationValid;
+  configuration.wait_for_callback = false;
   configuration.customized_dialog = true;
   RequestExpectations expectations = {
       RequestTokenStatus::kError, FederatedAuthRequestResult::kError,
@@ -1339,6 +1341,54 @@
   ExpectRequestTokenStatusUKM(TokenStatus::kNotSelectAccount);
 }
 
+// Test that request is not completed if user ignores the UI.
+TEST_F(BasicFederatedAuthRequestImplTest, UIIsIgnored) {
+  base::HistogramTester histogram_tester_;
+
+  // The UI will not be destroyed during the test.
+  EXPECT_CALL(*mock_dialog_controller(), DestructorCalled()).Times(0);
+
+  AccountList displayed_accounts;
+  EXPECT_CALL(*mock_dialog_controller(),
+              ShowAccountsDialog(_, _, _, _, _, _, _))
+      .WillOnce(Invoke(
+          [&](content::WebContents* rp_web_contents, const GURL& idp_signin_url,
+              base::span<const content::IdentityRequestAccount> accounts,
+              const IdentityProviderMetadata& idp_metadata,
+              const ClientIdData& client_id_data, SignInMode sign_in_mode,
+              IdentityRequestDialogController::AccountSelectionCallback
+                  on_selected) {
+            displayed_accounts = AccountList(accounts.begin(), accounts.end());
+            // Pretends that the user ignored the UI by not selecting an
+            // account.
+          }));
+
+  MockConfiguration configuration = kConfigurationValid;
+  configuration.wait_for_callback = false;
+  configuration.customized_dialog = true;
+  RequestExpectations expectations = {
+      /*return_status=*/absl::nullopt,
+      /*devtools_issue_status=*/absl::nullopt,
+      FETCH_ENDPOINT_ALL_REQUEST_TOKEN & ~FetchedEndpoint::TOKEN};
+  RunAuthTest(kDefaultRequestParameters, expectations, configuration);
+  task_environment()->FastForwardBy(base::Minutes(10));
+
+  EXPECT_FALSE(auth_helper_.was_callback_called());
+  ASSERT_FALSE(displayed_accounts.empty());
+
+  // Only the time to show the account dialog gets recorded.
+  histogram_tester_.ExpectTotalCount("Blink.FedCm.Timing.ShowAccountsDialog",
+                                     1);
+  histogram_tester_.ExpectTotalCount("Blink.FedCm.Timing.ContinueOnDialog", 0);
+  histogram_tester_.ExpectTotalCount("Blink.FedCm.Timing.CancelOnDialog", 0);
+  histogram_tester_.ExpectTotalCount("Blink.FedCm.Timing.IdTokenResponse", 0);
+  histogram_tester_.ExpectTotalCount("Blink.FedCm.Timing.TurnaroundTime", 0);
+  histogram_tester_.ExpectTotalCount("Blink.FedCm.Status.RequestIdToken", 0);
+
+  // The UI will be destroyed after the test is done.
+  EXPECT_CALL(*mock_dialog_controller(), DestructorCalled()).Times(1);
+}
+
 TEST_F(BasicFederatedAuthRequestImplTest, MetricsForWebContentsVisible) {
   base::HistogramTester histogram_tester;
   // Sets the WebContents to visible
@@ -1524,28 +1574,6 @@
               kConfigurationValid);
 }
 
-// Test that the request completes eventually in the case that the token request
-// times out.
-TEST_F(BasicFederatedAuthRequestImplTest, TokenRequestTimesOut) {
-  MockConfiguration configuration = kConfigurationValid;
-  configuration.delay_token_response = true;
-  RequestExpectations expectations = {RequestTokenStatus::kError,
-                                      FederatedAuthRequestResult::kError,
-                                      FETCH_ENDPOINT_ALL_REQUEST_TOKEN};
-  // RunAuthTest() fast forwards time by a sufficient amount to cause the
-  // request to timeout. `MockConfiguration::delay_token_response` disables
-  // the auto-run logic for the token request response and enables emulating
-  // the server being very slow to return a token request response.
-  RunAuthTest(kDefaultRequestParameters, expectations, configuration);
-
-  // Resolve token request. The callback should not be called.
-  test_network_request_manager_->RunDelayedCallbacks();
-
-  histogram_tester_.ExpectUniqueSample("Blink.FedCm.Status.RequestIdToken",
-                                       TokenStatus::kUserInterfaceTimedOut, 1);
-  ExpectRequestTokenStatusUKM(TokenStatus::kUserInterfaceTimedOut);
-}
-
 class FederatedAuthRequestImplTestCancelConsistency
     : public FederatedAuthRequestImplTest,
       public ::testing::WithParamInterface<int> {};
diff --git a/content/browser/webid/idp_network_request_manager.cc b/content/browser/webid/idp_network_request_manager.cc
index 20481fc..43df879 100644
--- a/content/browser/webid/idp_network_request_manager.cc
+++ b/content/browser/webid/idp_network_request_manager.cc
@@ -314,10 +314,10 @@
 
 FetchStatus GetParsingError(
     const data_decoder::DataDecoder::ValueOrError& result) {
-  if (!result.value)
+  if (!result.has_value())
     return FetchStatus::kInvalidResponseError;
 
-  auto& response = *result.value;
+  auto& response = *result;
   if (!response.is_dict())
     return FetchStatus::kInvalidResponseError;
 
@@ -375,7 +375,7 @@
     return;
   }
 
-  const base::Value::Dict* dict = result.value->GetIfDict();
+  const base::Value::Dict* dict = result->GetIfDict();
   if (!dict) {
     std::move(callback).Run(FetchStatus::kInvalidResponseError, urls);
     return;
@@ -649,7 +649,7 @@
     return;
   }
 
-  auto& response = *result.value;
+  auto& response = *result;
   auto ExtractEndpoint = [&](const char* key) {
     const base::Value* endpoint = response.FindKey(key);
     if (!endpoint || !endpoint->is_string()) {
@@ -687,7 +687,7 @@
   }
 
   AccountList account_list;
-  auto& response = *result.value;
+  auto& response = *result;
   const base::Value* accounts = response.FindKey(kAccountsKey);
   bool accounts_present =
       accounts && ParseAccounts(accounts, account_list, client_id);
@@ -709,7 +709,7 @@
     return;
   }
 
-  auto& response = *result.value;
+  auto& response = *result;
   const base::Value* token = response.FindKey(kTokenKey);
   bool token_present = token && token->is_string();
 
@@ -762,7 +762,7 @@
     return;
   }
 
-  auto& response = *result.value;
+  auto& response = *result;
   auto ExtractUrl = [&](const char* key) {
     const base::Value* endpoint = response.FindKey(key);
     if (!endpoint || !endpoint->is_string()) {
diff --git a/content/browser/webid/test/mock_identity_request_dialog_controller.cc b/content/browser/webid/test/mock_identity_request_dialog_controller.cc
index db21285c..43d1512 100644
--- a/content/browser/webid/test/mock_identity_request_dialog_controller.cc
+++ b/content/browser/webid/test/mock_identity_request_dialog_controller.cc
@@ -11,7 +11,8 @@
 MockIdentityRequestDialogController::MockIdentityRequestDialogController() =
     default;
 
-MockIdentityRequestDialogController::~MockIdentityRequestDialogController() =
-    default;
+MockIdentityRequestDialogController::~MockIdentityRequestDialogController() {
+  DestructorCalled();
+}
 
 }  // namespace content
diff --git a/content/browser/webid/test/mock_identity_request_dialog_controller.h b/content/browser/webid/test/mock_identity_request_dialog_controller.h
index 64d1322e..63fe1c0 100644
--- a/content/browser/webid/test/mock_identity_request_dialog_controller.h
+++ b/content/browser/webid/test/mock_identity_request_dialog_controller.h
@@ -32,6 +32,7 @@
                     const ClientIdData&,
                     IdentityRequestAccount::SignInMode,
                     AccountSelectionCallback));
+  MOCK_METHOD0(DestructorCalled, void());
 };
 
 }  // namespace content
diff --git a/content/common/partition_alloc_support.cc b/content/common/partition_alloc_support.cc
index 66d91e4..eafb934 100644
--- a/content/common/partition_alloc_support.cc
+++ b/content/common/partition_alloc_support.cc
@@ -420,7 +420,7 @@
 #if BUILDFLAG(IS_ANDROID) && defined(ARCH_CPU_32_BITS)
     // Devices almost always report less physical memory than what they actually
     // have, so anything above 3GiB will catch 4GiB and above.
-    if (base::SysInfo::AmountOfPhysicalMemory() <= int64_t{3500} * 1024 * 1024)
+    if (base::SysInfo::AmountOfPhysicalMemoryMB() <= 3500)
       largest_cached_size_ =
           ::partition_alloc::ThreadCacheLimits::kDefaultSizeThreshold;
 #endif  // BUILDFLAG(IS_ANDROID) && !defined(ARCH_CPU_64_BITS)
diff --git a/content/public/android/java/src/org/chromium/content/browser/GestureListenerManagerImpl.java b/content/public/android/java/src/org/chromium/content/browser/GestureListenerManagerImpl.java
index 9be06fa..2e6dfd6 100644
--- a/content/public/android/java/src/org/chromium/content/browser/GestureListenerManagerImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/GestureListenerManagerImpl.java
@@ -44,8 +44,6 @@
                 GestureListenerManagerImpl::new;
     }
 
-    private static GestureListenerManagerImpl sInstanceForTesting;
-
     private final WebContentsImpl mWebContents;
     private final ObserverList<GestureStateListener> mListeners;
     private final RewindableIterator<GestureStateListener> mIterator;
@@ -74,19 +72,11 @@
      *         Creates one if not present.
      */
     public static GestureListenerManagerImpl fromWebContents(WebContents webContents) {
-        if (sInstanceForTesting != null) return sInstanceForTesting;
         return ((WebContentsImpl) webContents)
                 .getOrSetUserData(
                         GestureListenerManagerImpl.class, UserDataFactoryLazyHolder.INSTANCE);
     }
 
-    // TODO(https://crbug.com/1340593): Mocking |#fromWebContents()| may be a better option, when
-    // available.
-    @VisibleForTesting
-    public static void setInstanceForTesting(GestureListenerManagerImpl instance) {
-        sInstanceForTesting = instance;
-    }
-
     public GestureListenerManagerImpl(WebContents webContents) {
         mWebContents = (WebContentsImpl) webContents;
         mListeners = new ObserverList<GestureStateListener>();
diff --git a/content/public/android/junit/src/org/chromium/content/browser/ChildProcessConnectionMetricsUnitTest.java b/content/public/android/junit/src/org/chromium/content/browser/ChildProcessConnectionMetricsUnitTest.java
index 148ff19..d768a2dd 100644
--- a/content/public/android/junit/src/org/chromium/content/browser/ChildProcessConnectionMetricsUnitTest.java
+++ b/content/public/android/junit/src/org/chromium/content/browser/ChildProcessConnectionMetricsUnitTest.java
@@ -13,10 +13,10 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.Config;
 
 import org.chromium.base.ChildBindingState;
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.process_launcher.ChildProcessConnection;
 import org.chromium.base.process_launcher.TestChildProcessConnection;
 import org.chromium.base.test.BaseRobolectricTestRunner;
@@ -27,7 +27,6 @@
  * Unit test for {@link ChildProcessConnectionMetrics}.
  */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(shadows = {ShadowRecordHistogram.class})
 public class ChildProcessConnectionMetricsUnitTest {
     private LinkedList<ChildProcessConnection> mRanking;
     private BindingManager mBindingManager;
@@ -35,7 +34,7 @@
 
     @Before
     public void setUp() {
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
         LauncherThread.setCurrentThreadAsLauncherThread();
         mRanking = new LinkedList<ChildProcessConnection>();
         mBindingManager = new BindingManager(RuntimeEnvironment.application, mRanking);
@@ -52,45 +51,45 @@
 
         mConnectionMetrics.emitMetrics();
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.ChildProcessBinding.TotalConnections", 0));
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.ChildProcessBinding.StrongConnections", 0));
         Assert.assertEquals(0,
-                ShadowRecordHistogram.getHistogramTotalCountForTesting(
+                RecordHistogram.getHistogramTotalCountForTesting(
                         "Android.ChildProcessBinding.PercentageStrongConnections_LessThan3Connections"));
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.ChildProcessBinding.ModerateConnections", 0));
         Assert.assertEquals(0,
-                ShadowRecordHistogram.getHistogramTotalCountForTesting(
+                RecordHistogram.getHistogramTotalCountForTesting(
                         "Android.ChildProcessBinding.PercentageModerateConnections_LessThan3Connections"));
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.ChildProcessBinding.WaivedConnections", 0));
         Assert.assertEquals(0,
-                ShadowRecordHistogram.getHistogramTotalCountForTesting(
+                RecordHistogram.getHistogramTotalCountForTesting(
                         "Android.ChildProcessBinding.PercentageWaivedConnections_LessThan3Connections"));
 
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.ChildProcessBinding.ContentModerateConnections", 0));
         Assert.assertEquals(0,
-                ShadowRecordHistogram.getHistogramTotalCountForTesting(
+                RecordHistogram.getHistogramTotalCountForTesting(
                         "Android.ChildProcessBinding.PercentageContentModerateConnections_LessThan3Connections"));
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.ChildProcessBinding.ContentWaivedConnections", 0));
         Assert.assertEquals(0,
-                ShadowRecordHistogram.getHistogramTotalCountForTesting(
+                RecordHistogram.getHistogramTotalCountForTesting(
                         "Android.ChildProcessBinding.PercentageContentWaivedConnections_LessThan3Connections"));
 
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.ChildProcessBinding.WaivableConnections", 0));
         Assert.assertEquals(0,
-                ShadowRecordHistogram.getHistogramTotalCountForTesting(
+                RecordHistogram.getHistogramTotalCountForTesting(
                         "Android.ChildProcessBinding.PercentageWaivableConnections_LessThan3Connections"));
     }
 
@@ -110,50 +109,50 @@
         mConnectionMetrics.emitMetrics();
 
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.ChildProcessBinding.TotalConnections", 6));
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.ChildProcessBinding.StrongConnections", 1));
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.ChildProcessBinding.PercentageStrongConnections_6To10Connections",
                         17));
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.ChildProcessBinding.ModerateConnections", 4));
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.ChildProcessBinding.PercentageModerateConnections_6To10Connections",
                         67));
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.ChildProcessBinding.WaivedConnections", 1));
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.ChildProcessBinding.PercentageWaivedConnections_6To10Connections",
                         17));
 
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.ChildProcessBinding.ContentModerateConnections", 1));
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.ChildProcessBinding.PercentageContentModerateConnections_6To10Connections",
                         17));
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.ChildProcessBinding.ContentWaivedConnections", 4));
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.ChildProcessBinding.PercentageContentWaivedConnections_6To10Connections",
                         67));
 
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.ChildProcessBinding.WaivableConnections", 3));
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.ChildProcessBinding.PercentageWaivableConnections_6To10Connections",
                         50));
     }
@@ -171,50 +170,50 @@
         mConnectionMetrics.emitMetrics();
 
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.ChildProcessBinding.TotalConnections", 2));
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.ChildProcessBinding.StrongConnections", 0));
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.ChildProcessBinding.PercentageStrongConnections_LessThan3Connections",
                         0));
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.ChildProcessBinding.ModerateConnections", 2));
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.ChildProcessBinding.PercentageModerateConnections_LessThan3Connections",
                         100));
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.ChildProcessBinding.WaivedConnections", 0));
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.ChildProcessBinding.PercentageWaivedConnections_LessThan3Connections",
                         0));
 
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.ChildProcessBinding.ContentModerateConnections", 2));
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.ChildProcessBinding.PercentageContentModerateConnections_LessThan3Connections",
                         100));
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.ChildProcessBinding.ContentWaivedConnections", 0));
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.ChildProcessBinding.PercentageContentWaivedConnections_LessThan3Connections",
                         0));
 
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.ChildProcessBinding.WaivableConnections", 0));
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.ChildProcessBinding.PercentageWaivableConnections_LessThan3Connections",
                         0));
 
@@ -223,50 +222,50 @@
         mConnectionMetrics.emitMetrics();
 
         Assert.assertEquals(2,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.ChildProcessBinding.TotalConnections", 2));
         Assert.assertEquals(2,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.ChildProcessBinding.StrongConnections", 0));
         Assert.assertEquals(2,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.ChildProcessBinding.PercentageStrongConnections_LessThan3Connections",
                         0));
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.ChildProcessBinding.ModerateConnections", 1));
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.ChildProcessBinding.PercentageModerateConnections_LessThan3Connections",
                         50));
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.ChildProcessBinding.WaivedConnections", 1));
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.ChildProcessBinding.PercentageWaivedConnections_LessThan3Connections",
                         50));
 
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.ChildProcessBinding.ContentModerateConnections", 0));
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.ChildProcessBinding.PercentageContentModerateConnections_LessThan3Connections",
                         0));
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.ChildProcessBinding.ContentWaivedConnections", 2));
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.ChildProcessBinding.PercentageContentWaivedConnections_LessThan3Connections",
                         100));
 
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.ChildProcessBinding.WaivableConnections", 1));
         Assert.assertEquals(1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(
+                RecordHistogram.getHistogramValueCountForTesting(
                         "Android.ChildProcessBinding.PercentageWaivableConnections_LessThan3Connections",
                         50));
     }
diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h
index a787de76..cadbd44 100644
--- a/content/public/browser/content_browser_client.h
+++ b/content/public/browser/content_browser_client.h
@@ -1183,8 +1183,8 @@
   // frame that is being prerendered.
   //
   // Prerender2 limits inactivated pages' capabilities by controlling when to
-  // bind Mojo interfaces. See content/browser/prerender/README.md for more
-  // about capability control.
+  // bind Mojo interfaces. See content/browser/preloading/prerender/README.md
+  // for more about capability control.
   //
   // The embedder can add entries to `policy_map` for interfaces that it
   // registers in `RegisterBrowserInterfaceBindersForFrame()` and
diff --git a/content/public/browser/render_frame_host.h b/content/public/browser/render_frame_host.h
index 349d14c..2e7fd65 100644
--- a/content/public/browser/render_frame_host.h
+++ b/content/public/browser/render_frame_host.h
@@ -379,13 +379,14 @@
   //
   // In the other direction, a RenderFrameHost can also transfer to a different
   // FrameTreeNode! Prior to the advent of prerendered pages
-  // (content/browser/prerender/README.md), that was not true, and it could be
-  // assumed that the return value of RenderFrameHost::GetFrameTreeNodeId() was
-  // constant over the lifetime of the RenderFrameHost. But with prerender
-  // activations, the main frame of the prerendered page transfers to a new
-  // FrameTreeNode, so newer code should no longer make that assumption. This
-  // transfer only happens for main frames (currently only during a prerender
-  // activation navigation) and never happens for subframes.
+  // (content/browser/preloading/prerender/README.md), that was not true, and it
+  // could be assumed that the return value of
+  // RenderFrameHost::GetFrameTreeNodeId() was constant over the lifetime of the
+  // RenderFrameHost. But with prerender activations, the main frame of the
+  // prerendered page transfers to a new FrameTreeNode, so newer code should no
+  // longer make that assumption. This transfer only happens for main frames
+  // (currently only during a prerender activation navigation) and never happens
+  // for subframes.
   //
   // If a stable identifier is needed, GetGlobalId() always refers to this
   // RenderFrameHost, while this RenderFrameHost might host multiple documents
diff --git a/content/public/browser/web_contents_delegate.h b/content/public/browser/web_contents_delegate.h
index 348dcde0..b088cb3 100644
--- a/content/public/browser/web_contents_delegate.h
+++ b/content/public/browser/web_contents_delegate.h
@@ -688,8 +688,8 @@
   // indication that the cache will be used.
   virtual bool IsBackForwardCacheSupported();
 
-  // Returns true if Prerender2 (see content/browser/prerender/README.md for
-  // details) is supported.
+  // Returns true if Prerender2 (see
+  // content/browser/preloading/prerender/README.md for details) is supported.
   virtual bool IsPrerender2Supported(WebContents& web_contents);
 
   // Requests the delegate to replace |predecessor_contents| with
diff --git a/content/public/renderer/v8_value_converter.h b/content/public/renderer/v8_value_converter.h
index f67a947..56feb32 100644
--- a/content/public/renderer/v8_value_converter.h
+++ b/content/public/renderer/v8_value_converter.h
@@ -14,6 +14,7 @@
 
 namespace base {
 class Value;
+class ValueView;
 }
 
 namespace content {
@@ -106,8 +107,7 @@
   // Unsupported types are replaced with null.  If an array or object throws
   // while setting a value, that property or item is skipped, leaving a hole in
   // the case of arrays.
-  // TODO(dcheng): This should just take a const reference.
-  v8::Local<v8::Value> ToV8Value(const base::Value* value,
+  v8::Local<v8::Value> ToV8Value(base::ValueView value,
                                  v8::Local<v8::Context> context) override = 0;
 
   // Converts a v8::Value to base::Value.
diff --git a/content/renderer/gpu_benchmarking_extension.cc b/content/renderer/gpu_benchmarking_extension.cc
index 949eab55..8168714b 100644
--- a/content/renderer/gpu_benchmarking_extension.cc
+++ b/content/renderer/gpu_benchmarking_extension.cc
@@ -269,7 +269,7 @@
   if (frame && !callback.IsEmpty()) {
     if (value.has_value()) {
       v8::Local<v8::Value> v8_value =
-          V8ValueConverter::Create()->ToV8Value(&value.value(), context);
+          V8ValueConverter::Create()->ToV8Value(*value, context);
       v8::Local<v8::Value> argv[] = {v8_value};
       frame->CallFunctionEvenIfScriptDisabled(
           callback, v8::Object::New(isolate), /*argc=*/1, argv);
diff --git a/content/renderer/java/gin_java_bridge_value_converter.cc b/content/renderer/java/gin_java_bridge_value_converter.cc
index 6104f94..93b0308 100644
--- a/content/renderer/java/gin_java_bridge_value_converter.cc
+++ b/content/renderer/java/gin_java_bridge_value_converter.cc
@@ -9,6 +9,7 @@
 
 #include <cmath>
 
+#include "base/check.h"
 #include "base/memory/ptr_util.h"
 #include "base/values.h"
 #include "content/common/android/gin_java_bridge_value.h"
@@ -32,7 +33,8 @@
 v8::Local<v8::Value> GinJavaBridgeValueConverter::ToV8Value(
     const base::Value* value,
     v8::Local<v8::Context> context) const {
-  return converter_->ToV8Value(value, context);
+  CHECK(value);
+  return converter_->ToV8Value(*value, context);
 }
 
 std::unique_ptr<base::Value> GinJavaBridgeValueConverter::FromV8Value(
diff --git a/content/renderer/skia_benchmarking_extension.cc b/content/renderer/skia_benchmarking_extension.cc
index 9f7625b..318335b 100644
--- a/content/renderer/skia_benchmarking_extension.cc
+++ b/content/renderer/skia_benchmarking_extension.cc
@@ -7,6 +7,8 @@
 #include <stddef.h>
 #include <stdint.h>
 
+#include <utility>
+
 #include "base/base64.h"
 #include "base/time/time.h"
 #include "base/values.h"
@@ -85,16 +87,13 @@
 std::unique_ptr<Picture> ParsePictureHash(v8::Isolate* isolate,
                                           v8::Local<v8::Value> arg) {
   std::unique_ptr<base::Value> picture_value = ParsePictureArg(isolate, arg);
-  if (!picture_value)
-    return nullptr;
-  const base::DictionaryValue* value = nullptr;
-  if (!picture_value->GetAsDictionary(&value))
+  if (!picture_value || !picture_value->is_dict())
     return nullptr;
   // Decode the picture from base64.
-  std::string encoded;
-  if (!value->GetString("skp64", &encoded))
+  std::string* encoded = picture_value->GetDict().FindString("skp64");
+  if (!encoded)
     return nullptr;
-  return CreatePictureFromEncodedString(encoded);
+  return CreatePictureFromEncodedString(std::move(*encoded));
 }
 
 class PicturePlaybackController : public SkPicture::AbortCallback {
@@ -185,14 +184,13 @@
     std::unique_ptr<base::Value> params_value =
         content::V8ValueConverter::Create()->FromV8Value(params, context);
 
-    const base::DictionaryValue* params_dict = nullptr;
-    if (params_value.get() && params_value->GetAsDictionary(&params_dict)) {
-      scale = params_dict->FindDoubleKey("scale").value_or(scale);
-      if (absl::optional<int> stop = params_dict->FindIntKey("stop"))
+    if (params_value && params_value->is_dict()) {
+      const base::Value::Dict& params_dict = params_value->GetDict();
+      scale = params_dict.FindDouble("scale").value_or(scale);
+      if (absl::optional<int> stop = params_dict.FindInt("stop"))
         stop_index = *stop;
 
-      const base::Value* clip_value = nullptr;
-      if (params_dict->Get("clip", &clip_value))
+      if (const base::Value* clip_value = params_dict.Find("clip"))
         cc::MathUtil::FromValue(clip_value, &clip_rect);
     }
   }
@@ -258,7 +256,7 @@
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
   args->Return(content::V8ValueConverter::Create()->ToV8Value(
-      &benchmarking_canvas.Commands(), context));
+      benchmarking_canvas.Commands(), context));
 }
 
 void SkiaBenchmarking::GetOpTimings(gin::Arguments* args) {
diff --git a/content/renderer/skia_benchmarking_extension_unittest.cc b/content/renderer/skia_benchmarking_extension_unittest.cc
index 70598f6..6a0f1aa 100644
--- a/content/renderer/skia_benchmarking_extension_unittest.cc
+++ b/content/renderer/skia_benchmarking_extension_unittest.cc
@@ -14,10 +14,10 @@
 
 namespace {
 
-testing::AssertionResult HasArg(const base::ListValue* args,
+testing::AssertionResult HasArg(const base::Value::List& args,
                                 const char name[]) {
-  for (size_t i = 0; i < args->GetListDeprecated().size(); ++i) {
-    const base::Value& arg = args->GetListDeprecated()[i];
+  for (size_t i = 0; i < args.size(); ++i) {
+    const base::Value& arg = args[i];
     if (!arg.is_dict() || arg.DictSize() != 1) {
       return testing::AssertionFailure() << " malformed argument for index "
                                          << i;
@@ -59,60 +59,70 @@
   benchmarking_canvas.restore();
 
   // Verify the recorded commands.
-  const base::ListValue& ops = benchmarking_canvas.Commands();
-  ASSERT_EQ(ops.GetListDeprecated().size(), static_cast<size_t>(5));
+  const base::Value::List& ops = benchmarking_canvas.Commands();
+  ASSERT_EQ(ops.size(), static_cast<size_t>(5));
 
   size_t index = 0;
   const base::Value* value;
-  const base::DictionaryValue* op;
-  const base::ListValue* op_args;
-  std::string op_name;
+  const base::Value::Dict* op;
+  const base::Value::List* op_args;
+  const std::string* op_name;
 
-  value = &ops.GetListDeprecated()[index++];
+  value = &ops[index++];
   ASSERT_TRUE(value->is_dict());
-  op = static_cast<const base::DictionaryValue*>(value);
-  EXPECT_TRUE(op->GetString("cmd_string", &op_name));
-  EXPECT_EQ(op_name, "Save");
-  ASSERT_TRUE(op->GetList("info", &op_args));
-  EXPECT_EQ(op_args->GetListDeprecated().size(), static_cast<size_t>(0));
+  op = &value->GetDict();
+  op_name = op->FindString("cmd_string");
+  ASSERT_TRUE(op_name);
+  EXPECT_EQ(*op_name, "Save");
+  op_args = op->FindList("info");
+  ASSERT_TRUE(op_args);
+  EXPECT_TRUE(op_args->empty());
 
-  value = &ops.GetListDeprecated()[index++];
+  value = &ops[index++];
   ASSERT_TRUE(value->is_dict());
-  op = static_cast<const base::DictionaryValue*>(value);
-  EXPECT_TRUE(op->GetString("cmd_string", &op_name));
-  EXPECT_EQ(op_name, "ClipRect");
-  ASSERT_TRUE(op->GetList("info", &op_args));
-  EXPECT_EQ(op_args->GetListDeprecated().size(), static_cast<size_t>(3));
-  EXPECT_TRUE(HasArg(op_args, "rect"));
-  EXPECT_TRUE(HasArg(op_args, "op"));
-  EXPECT_TRUE(HasArg(op_args, "anti-alias"));
+  op = &value->GetDict();
+  op_name = op->FindString("cmd_string");
+  ASSERT_TRUE(op_name);
+  EXPECT_EQ(*op_name, "ClipRect");
+  op_args = op->FindList("info");
+  ASSERT_TRUE(op_args);
+  EXPECT_EQ(op_args->size(), static_cast<size_t>(3));
+  EXPECT_TRUE(HasArg(*op_args, "rect"));
+  EXPECT_TRUE(HasArg(*op_args, "op"));
+  EXPECT_TRUE(HasArg(*op_args, "anti-alias"));
 
-  value = &ops.GetListDeprecated()[index++];
+  value = &ops[index++];
   ASSERT_TRUE(value->is_dict());
-  op = static_cast<const base::DictionaryValue*>(value);
-  EXPECT_TRUE(op->GetString("cmd_string", &op_name));
-  EXPECT_EQ(op_name, "SetMatrix");
-  ASSERT_TRUE(op->GetList("info", &op_args));
-  EXPECT_EQ(op_args->GetListDeprecated().size(), static_cast<size_t>(1));
-  EXPECT_TRUE(HasArg(op_args, "matrix"));
+  op = &value->GetDict();
+  op_name = op->FindString("cmd_string");
+  ASSERT_TRUE(op_name);
+  EXPECT_EQ(*op_name, "SetMatrix");
+  op_args = op->FindList("info");
+  ASSERT_TRUE(op_args);
+  EXPECT_EQ(op_args->size(), static_cast<size_t>(1));
+  EXPECT_TRUE(HasArg(*op_args, "matrix"));
 
-  value = &ops.GetListDeprecated()[index++];
+  value = &ops[index++];
   ASSERT_TRUE(value->is_dict());
-  op = static_cast<const base::DictionaryValue*>(value);
-  EXPECT_TRUE(op->GetString("cmd_string", &op_name));
-  EXPECT_EQ(op_name, "DrawRect");
-  ASSERT_TRUE(op->GetList("info", &op_args));
-  EXPECT_EQ(op_args->GetListDeprecated().size(), static_cast<size_t>(2));
-  EXPECT_TRUE(HasArg(op_args, "rect"));
-  EXPECT_TRUE(HasArg(op_args, "paint"));
+  op = &value->GetDict();
+  op_name = op->FindString("cmd_string");
+  ASSERT_TRUE(op_name);
+  EXPECT_EQ(*op_name, "DrawRect");
+  op_args = op->FindList("info");
+  ASSERT_TRUE(op_args);
+  EXPECT_EQ(op_args->size(), static_cast<size_t>(2));
+  EXPECT_TRUE(HasArg(*op_args, "rect"));
+  EXPECT_TRUE(HasArg(*op_args, "paint"));
 
-  value = &ops.GetListDeprecated()[index++];
+  value = &ops[index++];
   ASSERT_TRUE(value->is_dict());
-  op = static_cast<const base::DictionaryValue*>(value);
-  EXPECT_TRUE(op->GetString("cmd_string", &op_name));
-  EXPECT_EQ(op_name, "Restore");
-  ASSERT_TRUE(op->GetList("info", &op_args));
-  EXPECT_EQ(op_args->GetListDeprecated().size(), static_cast<size_t>(0));
+  op = &value->GetDict();
+  op_name = op->FindString("cmd_string");
+  ASSERT_TRUE(op_name);
+  EXPECT_EQ(*op_name, "Restore");
+  op_args = op->FindList("info");
+  ASSERT_TRUE(op_args);
+  EXPECT_TRUE(op_args->empty());
 }
 
 } // namespace content
diff --git a/content/renderer/v8_value_converter_impl.cc b/content/renderer/v8_value_converter_impl.cc
index 565f368..c7352cab 100644
--- a/content/renderer/v8_value_converter_impl.cc
+++ b/content/renderer/v8_value_converter_impl.cc
@@ -219,7 +219,7 @@
 }
 
 v8::Local<v8::Value> V8ValueConverterImpl::ToV8Value(
-    const base::Value* value,
+    base::ValueView value,
     v8::Local<v8::Context> context) {
   v8::Context::Scope context_scope(context);
   v8::EscapableHandleScope handle_scope(context->GetIsolate());
@@ -239,65 +239,66 @@
 v8::Local<v8::Value> V8ValueConverterImpl::ToV8ValueImpl(
     v8::Isolate* isolate,
     v8::Local<v8::Object> creation_context,
-    const base::Value* value) const {
-  CHECK(value);
-  switch (value->type()) {
-    case base::Value::Type::NONE:
+    base::ValueView value) const {
+  struct Visitor {
+    const V8ValueConverterImpl* converter;
+    v8::Isolate* isolate;
+    v8::Local<v8::Object> creation_context;
+
+    v8::Local<v8::Value> operator()(absl::monostate value) {
       return v8::Null(isolate);
-
-    case base::Value::Type::BOOLEAN:
-      return v8::Boolean::New(isolate, value->GetBool());
-
-    case base::Value::Type::INTEGER: {
-      return v8::Integer::New(isolate, value->GetInt());
     }
 
-    case base::Value::Type::DOUBLE: {
-      return v8::Number::New(isolate, value->GetDouble());
+    v8::Local<v8::Value> operator()(bool value) {
+      return v8::Boolean::New(isolate, value);
     }
 
-    case base::Value::Type::STRING: {
-      const std::string* val = value->GetIfString();
-      CHECK(val);
-      return v8::String::NewFromUtf8(isolate, val->c_str(),
-                                     v8::NewStringType::kNormal, val->length())
+    v8::Local<v8::Value> operator()(int value) {
+      return v8::Integer::New(isolate, value);
+    }
+
+    v8::Local<v8::Value> operator()(double value) {
+      return v8::Number::New(isolate, value);
+    }
+
+    v8::Local<v8::Value> operator()(base::StringPiece value) {
+      return v8::String::NewFromUtf8(isolate, value.data(),
+                                     v8::NewStringType::kNormal, value.length())
           .ToLocalChecked();
     }
 
-    case base::Value::Type::LIST:
-      return ToV8Array(isolate,
-                       creation_context,
-                       static_cast<const base::ListValue*>(value));
+    v8::Local<v8::Value> operator()(const base::Value::BlobStorage& value) {
+      return converter->ToArrayBuffer(isolate, creation_context, value);
+    }
 
-    case base::Value::Type::DICTIONARY:
-      return ToV8Object(isolate,
-                        creation_context,
-                        static_cast<const base::DictionaryValue*>(value));
+    v8::Local<v8::Value> operator()(const base::Value::Dict& value) {
+      return converter->ToV8Object(isolate, creation_context, value);
+    }
 
-    case base::Value::Type::BINARY:
-      return ToArrayBuffer(isolate, creation_context, value);
+    v8::Local<v8::Value> operator()(const base::Value::List& value) {
+      return converter->ToV8Array(isolate, creation_context, value);
+    }
+  };
 
-    default:
-      LOG(ERROR) << "Unexpected value type: " << value->type();
-      return v8::Null(isolate);
-  }
+  return value.Visit(Visitor{.converter = this,
+                             .isolate = isolate,
+                             .creation_context = creation_context});
 }
 
 v8::Local<v8::Value> V8ValueConverterImpl::ToV8Array(
     v8::Isolate* isolate,
     v8::Local<v8::Object> creation_context,
-    const base::ListValue* val) const {
-  v8::Local<v8::Array> result(
-      v8::Array::New(isolate, val->GetListDeprecated().size()));
+    const base::Value::List& val) const {
+  v8::Local<v8::Array> result(v8::Array::New(isolate, val.size()));
 
   // TODO(robwu): Callers should pass in the context.
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
-  for (size_t i = 0; i < val->GetListDeprecated().size(); ++i) {
-    const base::Value& child = val->GetListDeprecated()[i];
+  for (size_t i = 0; i < val.size(); ++i) {
+    const base::Value& child = val[i];
 
     v8::Local<v8::Value> child_v8 =
-        ToV8ValueImpl(isolate, creation_context, &child);
+        ToV8ValueImpl(isolate, creation_context, child);
     CHECK(!child_v8.IsEmpty());
 
     v8::Maybe<bool> maybe =
@@ -312,17 +313,15 @@
 v8::Local<v8::Value> V8ValueConverterImpl::ToV8Object(
     v8::Isolate* isolate,
     v8::Local<v8::Object> creation_context,
-    const base::DictionaryValue* val) const {
+    const base::Value::Dict& val) const {
   v8::Local<v8::Object> result(v8::Object::New(isolate));
 
   // TODO(robwu): Callers should pass in the context.
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
-  for (base::DictionaryValue::Iterator iter(*val);
-       !iter.IsAtEnd(); iter.Advance()) {
-    const std::string& key = iter.key();
+  for (const auto [key, value] : val) {
     v8::Local<v8::Value> child_v8 =
-        ToV8ValueImpl(isolate, creation_context, &iter.value());
+        ToV8ValueImpl(isolate, creation_context, value);
     CHECK(!child_v8.IsEmpty());
 
     v8::Maybe<bool> maybe = result->CreateDataProperty(
@@ -341,13 +340,12 @@
 v8::Local<v8::Value> V8ValueConverterImpl::ToArrayBuffer(
     v8::Isolate* isolate,
     v8::Local<v8::Object> creation_context,
-    const base::Value* value) const {
+    const base::Value::BlobStorage& value) const {
   DCHECK(creation_context->GetCreationContextChecked() ==
          isolate->GetCurrentContext());
   v8::Local<v8::ArrayBuffer> buffer =
-      v8::ArrayBuffer::New(isolate, value->GetBlob().size());
-  memcpy(buffer->GetBackingStore()->Data(), value->GetBlob().data(),
-         value->GetBlob().size());
+      v8::ArrayBuffer::New(isolate, value.size());
+  memcpy(buffer->GetBackingStore()->Data(), value.data(), value.size());
   return buffer;
 }
 
diff --git a/content/renderer/v8_value_converter_impl.h b/content/renderer/v8_value_converter_impl.h
index f0484c1..5b8c427 100644
--- a/content/renderer/v8_value_converter_impl.h
+++ b/content/renderer/v8_value_converter_impl.h
@@ -7,15 +7,10 @@
 
 #include <map>
 
+#include "base/values.h"
 #include "content/common/content_export.h"
 #include "content/public/renderer/v8_value_converter.h"
 
-namespace base {
-class DictionaryValue;
-class ListValue;
-class Value;
-}
-
 namespace content {
 
 class CONTENT_EXPORT V8ValueConverterImpl : public V8ValueConverter {
@@ -32,7 +27,7 @@
   void SetStripNullFromObjects(bool val) override;
   void SetConvertNegativeZeroToInt(bool val) override;
   void SetStrategy(Strategy* strategy) override;
-  v8::Local<v8::Value> ToV8Value(const base::Value* value,
+  v8::Local<v8::Value> ToV8Value(base::ValueView value,
                                  v8::Local<v8::Context> context) override;
   std::unique_ptr<base::Value> FromV8Value(
       v8::Local<v8::Value> value,
@@ -46,17 +41,17 @@
 
   v8::Local<v8::Value> ToV8ValueImpl(v8::Isolate* isolate,
                                      v8::Local<v8::Object> creation_context,
-                                     const base::Value* value) const;
+                                     base::ValueView value) const;
   v8::Local<v8::Value> ToV8Array(v8::Isolate* isolate,
+                                 v8::Local<v8::Object> creation_context,
+                                 const base::Value::List& list) const;
+  v8::Local<v8::Value> ToV8Object(v8::Isolate* isolate,
                                   v8::Local<v8::Object> creation_context,
-                                  const base::ListValue* list) const;
-  v8::Local<v8::Value> ToV8Object(
+                                  const base::Value::Dict& dictionary) const;
+  v8::Local<v8::Value> ToArrayBuffer(
       v8::Isolate* isolate,
       v8::Local<v8::Object> creation_context,
-      const base::DictionaryValue* dictionary) const;
-  v8::Local<v8::Value> ToArrayBuffer(v8::Isolate* isolate,
-                                     v8::Local<v8::Object> creation_context,
-                                     const base::Value* value) const;
+      const base::Value::BlobStorage& value) const;
 
   std::unique_ptr<base::Value> FromV8ValueImpl(FromV8ValueState* state,
                                                v8::Local<v8::Value> value,
diff --git a/content/renderer/v8_value_converter_impl_unittest.cc b/content/renderer/v8_value_converter_impl_unittest.cc
index a4bab8f..64980ac 100644
--- a/content/renderer/v8_value_converter_impl_unittest.cc
+++ b/content/renderer/v8_value_converter_impl_unittest.cc
@@ -289,6 +289,7 @@
       "  \"list\": [ \"bar\", \"foo\" ], \n"
       "  \"empty-list\": [], \n"
       "}");
+  ASSERT_TRUE(original_root);
 
   v8::HandleScope handle_scope(isolate_);
   v8::Local<v8::Context> context =
@@ -297,7 +298,7 @@
 
   V8ValueConverterImpl converter;
   v8::Local<v8::Object> v8_object =
-      converter.ToV8Value(original_root.get(), context).As<v8::Object>();
+      converter.ToV8Value(*original_root, context).As<v8::Object>();
   ASSERT_FALSE(v8_object.IsEmpty());
 
   EXPECT_EQ(original_root->DictSize(),
@@ -410,6 +411,7 @@
 TEST_F(V8ValueConverterImplTest, KeysWithDots) {
   std::unique_ptr<base::Value> original =
       base::test::ParseJsonDeprecated("{ \"foo.bar\": \"baz\" }");
+  ASSERT_TRUE(original);
 
   v8::HandleScope handle_scope(isolate_);
   v8::Local<v8::Context> context =
@@ -417,8 +419,8 @@
   v8::Context::Scope context_scope(context);
 
   V8ValueConverterImpl converter;
-  std::unique_ptr<base::Value> copy(converter.FromV8Value(
-      converter.ToV8Value(original.get(), context), context));
+  std::unique_ptr<base::Value> copy(
+      converter.FromV8Value(converter.ToV8Value(*original, context), context));
 
   EXPECT_TRUE(*original == *copy);
 }
@@ -450,7 +452,7 @@
   V8ValueConverterImpl converter;
   std::unique_ptr<base::DictionaryValue> converted(
       base::DictionaryValue::From(converter.FromV8Value(object, context)));
-  EXPECT_TRUE(converted.get());
+  ASSERT_TRUE(converted.get());
   // http://code.google.com/p/v8/issues/detail?id=1342
   // EXPECT_EQ(2u, converted->size());
   // EXPECT_TRUE(IsNull(converted.get(), "foo"));
@@ -460,7 +462,7 @@
   // Converting to v8 value should not trigger the setter.
   converted->SetString("foo", "foo");
   v8::Local<v8::Object> copy =
-      converter.ToV8Value(converted.get(), context).As<v8::Object>();
+      converter.ToV8Value(*converted, context).As<v8::Object>();
   EXPECT_FALSE(copy.IsEmpty());
   EXPECT_EQ(2u, copy->GetPropertyNames(context).ToLocalChecked()->Length());
   EXPECT_EQ("foo", GetString(copy, "foo"));
@@ -502,7 +504,7 @@
   converted.reset(static_cast<base::ListValue*>(
       base::test::ParseJsonDeprecated("[ \"foo\", \"bar\" ]").release()));
   v8::Local<v8::Array> copy =
-      converter.ToV8Value(converted.get(), context).As<v8::Array>();
+      converter.ToV8Value(*converted, context).As<v8::Array>();
   ASSERT_FALSE(copy.IsEmpty());
   EXPECT_EQ(2u, copy->Length());
   EXPECT_EQ("foo", GetString(copy, 0));
@@ -566,6 +568,7 @@
 TEST_F(V8ValueConverterImplTest, ObjectPrototypeSetter) {
   std::unique_ptr<base::Value> original =
       base::test::ParseJsonDeprecated("{ \"foo\": \"good value\" }");
+  ASSERT_TRUE(original);
 
   v8::HandleScope handle_scope(isolate_);
   v8::Local<v8::Context> context =
@@ -595,7 +598,7 @@
 
   V8ValueConverterImpl converter;
   v8::Local<v8::Object> converted =
-      converter.ToV8Value(original.get(), context).As<v8::Object>();
+      converter.ToV8Value(*original, context).As<v8::Object>();
   EXPECT_FALSE(converted.IsEmpty());
 
   // Getters/setters shouldn't be triggered.
@@ -611,10 +614,10 @@
   EXPECT_EQ(1, GetInt(result, "setters"));
 
   // Repeat the same exercise with a dictionary without the key.
-  base::DictionaryValue missing_key_dict;
-  missing_key_dict.SetString("otherkey", "hello");
+  base::Value::Dict missing_key_dict;
+  missing_key_dict.Set("otherkey", "hello");
   v8::Local<v8::Object> converted2 =
-      converter.ToV8Value(&missing_key_dict, context).As<v8::Object>();
+      converter.ToV8Value(missing_key_dict, context).As<v8::Object>();
   EXPECT_FALSE(converted2.IsEmpty());
 
   // Getters/setters shouldn't be triggered.
@@ -634,6 +637,7 @@
 TEST_F(V8ValueConverterImplTest, ArrayPrototypeSetter) {
   std::unique_ptr<base::Value> original =
       base::test::ParseJsonDeprecated("[100, 200, 300]");
+  ASSERT_TRUE(original);
 
   v8::HandleScope handle_scope(isolate_);
   v8::Local<v8::Context> context =
@@ -663,7 +667,7 @@
 
   V8ValueConverterImpl converter;
   v8::Local<v8::Array> converted =
-      converter.ToV8Value(original.get(), context).As<v8::Array>();
+      converter.ToV8Value(*original, context).As<v8::Array>();
   EXPECT_FALSE(converted.IsEmpty());
 
   // Getters/setters shouldn't be triggered during the conversion.
@@ -680,10 +684,10 @@
   EXPECT_EQ(1, GetInt(result, "setters"));
 
   // Try again, using an array without the index.
-  base::ListValue one_item_list;
+  base::Value::List one_item_list;
   one_item_list.Append(123456);
   v8::Local<v8::Array> converted2 =
-      converter.ToV8Value(&one_item_list, context).As<v8::Array>();
+      converter.ToV8Value(one_item_list, context).As<v8::Array>();
   EXPECT_FALSE(converted2.IsEmpty());
 
   // Getters/setters shouldn't be triggered during the conversion.
diff --git a/content/shell/BUILD.gn b/content/shell/BUILD.gn
index dfbb544..0322e7a 100644
--- a/content/shell/BUILD.gn
+++ b/content/shell/BUILD.gn
@@ -308,7 +308,7 @@
   }
 
   if (is_linux) {
-    deps += [ "//ui/views/linux_ui:linux_ui_factory" ]
+    deps += [ "//ui/linux:linux_ui_factory" ]
   }
 
   if (is_mac) {
diff --git a/content/shell/browser/DEPS b/content/shell/browser/DEPS
index 1a83b0ee..11d4b26 100644
--- a/content/shell/browser/DEPS
+++ b/content/shell/browser/DEPS
@@ -8,11 +8,6 @@
   "+services/device/public/cpp",
   "+services/network/public",
   "+services/service_manager/public/cpp",
+  "+ui/linux",
   "+ui/ozone/public",
 ]
-
-specific_include_rules = {
-  "shell_browser_main_parts\.cc": [
-    "+ui/gtk",
-  ],
-}
diff --git a/content/shell/browser/shell_browser_main_parts.cc b/content/shell/browser/shell_browser_main_parts.cc
index a04b658..278ce3e 100644
--- a/content/shell/browser/shell_browser_main_parts.cc
+++ b/content/shell/browser/shell_browser_main_parts.cc
@@ -66,8 +66,8 @@
 #endif
 
 #if BUILDFLAG(IS_LINUX)
-#include "ui/views/linux_ui/linux_ui.h"  // nogncheck
-#include "ui/views/linux_ui/linux_ui_factory.h"  // nogncheck
+#include "ui/linux/linux_ui.h"          // nogncheck
+#include "ui/linux/linux_ui_factory.h"  // nogncheck
 #endif
 
 namespace content {
@@ -154,7 +154,7 @@
     return;
 
 #if BUILDFLAG(IS_LINUX)
-  views::LinuxUI::SetInstance(CreateLinuxUi());
+  ui::LinuxUi::SetInstance(ui::CreateLinuxUi());
 #endif
 }
 
@@ -197,7 +197,7 @@
   browser_context_.reset();
   off_the_record_browser_context_.reset();
 #if BUILDFLAG(IS_LINUX)
-  views::LinuxUI::SetInstance(nullptr);
+  ui::LinuxUi::SetInstance(nullptr);
 #endif
   performance_manager_lifetime_.reset();
 }
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 7c4597a..f8e48aee 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -2283,6 +2283,7 @@
     "../browser/preloading/prefetch/prefetch_url_loader_interceptor_unittest.cc",
     "../browser/preloading/prerender/prerender_host_registry_unittest.cc",
     "../browser/preloading/prerender/prerender_host_unittest.cc",
+    "../browser/preloading/speculation_rules/speculation_host_impl_unittest.cc",
     "../browser/presentation/presentation_service_impl_unittest.cc",
     "../browser/private_aggregation/private_aggregation_budget_key_unittest.cc",
     "../browser/private_aggregation/private_aggregation_budget_storage_unittest.cc",
@@ -2411,7 +2412,6 @@
     "../browser/sms/sms_parser_unittest.cc",
     "../browser/sms/user_consent_handler_unittest.cc",
     "../browser/sms/webotp_service_unittest.cc",
-    "../browser/speculation_rules/speculation_host_impl_unittest.cc",
     "../browser/speech/tts_controller_unittest.cc",
     "../browser/startup_task_runner_unittest.cc",
     "../browser/storage_partition_config_unittest.cc",
@@ -2628,6 +2628,7 @@
     "//components/power_scheduler",
     "//components/services/quarantine/public/mojom",
     "//components/services/storage",
+    "//components/services/storage:filesystem_proxy_factory",
     "//components/services/storage:test_support",
     "//components/services/storage/dom_storage:local_storage_proto",
     "//components/services/storage/public/cpp",
diff --git a/content/web_test/renderer/web_ax_object_proxy.cc b/content/web_test/renderer/web_ax_object_proxy.cc
index a2630c3..a9a5476 100644
--- a/content/web_test/renderer/web_ax_object_proxy.cc
+++ b/content/web_test/renderer/web_ax_object_proxy.cc
@@ -1604,11 +1604,10 @@
 
 std::string WebAXObjectProxy::NameFrom() {
   UpdateLayout();
-  ax::mojom::NameFrom name_from = ax::mojom::NameFrom::kUninitialized;
+  ax::mojom::NameFrom name_from = ax::mojom::NameFrom::kNone;
   blink::WebVector<blink::WebAXObject> name_objects;
   accessibility_object_.GetName(name_from, name_objects);
   switch (name_from) {
-    case ax::mojom::NameFrom::kUninitialized:
     case ax::mojom::NameFrom::kNone:
       return "";
     default:
diff --git a/device/bluetooth/bluetooth_adapter_mac_unittest.mm b/device/bluetooth/bluetooth_adapter_mac_unittest.mm
index bceca7a1..5987f43fd 100644
--- a/device/bluetooth/bluetooth_adapter_mac_unittest.mm
+++ b/device/bluetooth/bluetooth_adapter_mac_unittest.mm
@@ -38,12 +38,6 @@
 #import <IOBluetooth/IOBluetooth.h>
 #endif  // BUILDFLAG(IS_IOS)
 
-// List of undocumented IOBluetooth APIs used for BluetoothAdapterMac.
-extern "C" {
-int IOBluetoothPreferenceGetControllerPowerState();
-void IOBluetoothPreferenceSetControllerPowerState(int state);
-}
-
 namespace {
 
 const char kTestPropertyListFileName[] = "test_property_list_file.plist";
@@ -232,22 +226,6 @@
   base::FilePath test_property_list_file_path_;
 };
 
-// Test if private IOBluetooth APIs are callable on all supported macOS
-// versions.
-// TODO(crbug.com/1344137): This test is flaky on all Mac builders and timing
-// out frequently on Mac11.
-#if BUILDFLAG(IS_MAC)
-#define MAYBE_IOBluetoothPrivateAPIs DISABLED_IOBluetoothPrivateAPIs
-#else
-#define MAYBE_IOBluetoothPrivateAPIs IOBluetoothPrivateAPIs
-#endif
-TEST_F(BluetoothAdapterMacTest, MAYBE_IOBluetoothPrivateAPIs) {
-  // Obtain current power state, toggle it, and reset it to it's original value.
-  int previous_state = IOBluetoothPreferenceGetControllerPowerState();
-  IOBluetoothPreferenceSetControllerPowerState(!previous_state);
-  IOBluetoothPreferenceSetControllerPowerState(previous_state);
-}
-
 TEST_F(BluetoothAdapterMacTest, Poll) {
   PollAdapter();
   EXPECT_TRUE(ui_task_runner_->HasPendingTask());
diff --git a/extensions/browser/api/declarative_net_request/file_backed_ruleset_source.cc b/extensions/browser/api/declarative_net_request/file_backed_ruleset_source.cc
index 872ac0f..46acdb3 100644
--- a/extensions/browser/api/declarative_net_request/file_backed_ruleset_source.cc
+++ b/extensions/browser/api/declarative_net_request/file_backed_ruleset_source.cc
@@ -218,15 +218,15 @@
     uint8_t parse_flags,
     FileBackedRulesetSource::IndexAndPersistJSONRulesetCallback callback,
     data_decoder::DataDecoder::ValueOrError result) {
-  if (!result.value) {
+  if (!result.has_value()) {
     std::move(callback).Run(IndexAndPersistJSONRulesetResult::CreateErrorResult(
-        GetErrorWithFilename(json_path, *result.error)));
+        GetErrorWithFilename(json_path, result.error())));
     return;
   }
 
   base::ElapsedTimer timer;
   ReadJSONRulesResult read_result = ParseRulesFromJSON(
-      source.id(), json_path, *result.value, source.rule_count_limit(),
+      source.id(), json_path, *result, source.rule_count_limit(),
       source.is_dynamic_ruleset());
 
   std::move(callback).Run(IndexAndPersistRuleset(source, std::move(read_result),
diff --git a/extensions/browser/api/management/management_api.cc b/extensions/browser/api/management/management_api.cc
index 7f501cd6..edb0f1c 100644
--- a/extensions/browser/api/management/management_api.cc
+++ b/extensions/browser/api/management/management_api.cc
@@ -347,8 +347,8 @@
 }
 void ManagementGetPermissionWarningsByManifestFunction::OnParse(
     data_decoder::DataDecoder::ValueOrError result) {
-  if (!result.value) {
-    Respond(Error(*result.error));
+  if (!result.has_value()) {
+    Respond(Error(result.error()));
 
     // Matched with AddRef() in Run().
     Release();
@@ -356,7 +356,7 @@
   }
 
   const base::DictionaryValue* parsed_manifest;
-  if (!result.value->GetAsDictionary(&parsed_manifest)) {
+  if (!result->GetAsDictionary(&parsed_manifest)) {
     Respond(Error(keys::kManifestParseError));
     Release();
     return;
diff --git a/extensions/browser/sandboxed_unpacker.cc b/extensions/browser/sandboxed_unpacker.cc
index 92ed57f..ea5fa4e 100644
--- a/extensions/browser/sandboxed_unpacker.cc
+++ b/extensions/browser/sandboxed_unpacker.cc
@@ -384,9 +384,9 @@
 
 void SandboxedUnpacker::OnVerifiedContentsUncompressed(
     const base::FilePath& unzip_dir,
-    data_decoder::DataDecoder::ResultOrError<mojo_base::BigBuffer> result) {
+    base::expected<mojo_base::BigBuffer, std::string> result) {
   DCHECK(unpacker_io_task_runner_->RunsTasksInCurrentSequence());
-  if (!result.value) {
+  if (!result.has_value()) {
     ReportFailure(SandboxedUnpackerFailureReason::
                       CRX_HEADER_VERIFIED_CONTENTS_UNCOMPRESSING_FAILURE,
                   l10n_util::GetStringFUTF16(
@@ -396,9 +396,8 @@
   }
   // Make a copy, since |result| may store data in shared memory, accessible by
   // some other processes.
-  std::vector<uint8_t> verified_contents(
-      result.value.value().data(),
-      result.value.value().data() + result.value.value().size());
+  std::vector<uint8_t> verified_contents(result->data(),
+                                         result->data() + result->size());
 
   client_->GetContentVerifierKey(
       base::BindOnce(&SandboxedUnpacker::StoreVerifiedContentsInExtensionDir,
diff --git a/extensions/browser/sandboxed_unpacker.h b/extensions/browser/sandboxed_unpacker.h
index cdd4e0c..c774dd2 100644
--- a/extensions/browser/sandboxed_unpacker.h
+++ b/extensions/browser/sandboxed_unpacker.h
@@ -187,7 +187,7 @@
   // Callback which is called after the verified contents are uncompressed.
   void OnVerifiedContentsUncompressed(
       const base::FilePath& unzip_dir,
-      data_decoder::DataDecoder::ResultOrError<mojo_base::BigBuffer> result);
+      base::expected<mojo_base::BigBuffer, std::string> result);
 
   // Verifies the decompressed verified contents fetched from the header of CRX
   // and stores them if the verification of these contents is successful.
diff --git a/extensions/browser/updater/safe_manifest_parser.cc b/extensions/browser/updater/safe_manifest_parser.cc
index f953452..28f62df8 100644
--- a/extensions/browser/updater/safe_manifest_parser.cc
+++ b/extensions/browser/updater/safe_manifest_parser.cc
@@ -168,15 +168,15 @@
 void ParseXmlDone(ParseUpdateManifestCallback callback,
                   data_decoder::DataDecoder::ValueOrError result) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  if (!result.value) {
-    ManifestParseFailure failure("Failed to parse XML: " + *result.error,
+  if (!result.has_value()) {
+    ManifestParseFailure failure("Failed to parse XML: " + result.error(),
                                  ManifestInvalidError::XML_PARSING_FAILED);
     ReportError(std::move(callback), failure);
     return;
   }
 
   auto results = std::make_unique<UpdateManifestResults>();
-  base::Value& root = *result.value;
+  base::Value& root = *result;
 
   // Look for the required namespace declaration.
   std::string gupdate_ns;
diff --git a/extensions/common/api/automation.idl b/extensions/common/api/automation.idl
index 90c6b51..89203f3 100644
--- a/extensions/common/api/automation.idl
+++ b/extensions/common/api/automation.idl
@@ -463,7 +463,6 @@
 
   // Where the node's name is from.
   enum NameFromType {
-    uninitialized,
     attribute,
     attributeExplicitlyEmpty,
     caption,
diff --git a/extensions/renderer/bindings/api_event_handler.cc b/extensions/renderer/bindings/api_event_handler.cc
index bb2d056..baf7158 100644
--- a/extensions/renderer/bindings/api_event_handler.cc
+++ b/extensions/renderer/bindings/api_event_handler.cc
@@ -236,7 +236,7 @@
   std::vector<v8::Local<v8::Value>> v8_args;
   v8_args.reserve(args.size());
   for (const auto& arg : args)
-    v8_args.push_back(converter->ToV8Value(&arg, context));
+    v8_args.push_back(converter->ToV8Value(arg, context));
 
   FireEventInContext(event_name, context, &v8_args, std::move(filter),
                      JSRunner::ResultCallback());
diff --git a/extensions/renderer/bindings/api_request_handler.cc b/extensions/renderer/bindings/api_request_handler.cc
index dd89d03..5a2fcd5 100644
--- a/extensions/renderer/bindings/api_request_handler.cc
+++ b/extensions/renderer/bindings/api_request_handler.cc
@@ -67,7 +67,7 @@
         content::V8ValueConverter::Create();
     v8_arguments_.reserve(base_arguments_->size());
     for (const auto& arg : *base_arguments_)
-      v8_arguments_.push_back(converter->ToV8Value(&arg, context));
+      v8_arguments_.push_back(converter->ToV8Value(arg, context));
   }
 
   return v8_arguments_;
diff --git a/extensions/renderer/dispatcher.cc b/extensions/renderer/dispatcher.cc
index 604a5e1f3..c9810316 100644
--- a/extensions/renderer/dispatcher.cc
+++ b/extensions/renderer/dispatcher.cc
@@ -153,7 +153,7 @@
 
   std::vector<v8::Local<v8::Value>> arguments;
   for (const auto& arg : *args) {
-    arguments.push_back(converter->ToV8Value(&arg, context->v8_context()));
+    arguments.push_back(converter->ToV8Value(arg, context->v8_context()));
   }
 
   context->module_system()->CallModuleMethodSafe(
diff --git a/extensions/renderer/guest_view/mime_handler_view/post_message_support.cc b/extensions/renderer/guest_view/mime_handler_view/post_message_support.cc
index 87841a5d..94a01017 100644
--- a/extensions/renderer/guest_view/mime_handler_view/post_message_support.cc
+++ b/extensions/renderer/guest_view/mime_handler_view/post_message_support.cc
@@ -160,9 +160,8 @@
   v8::Isolate* isolate = v8::Isolate::GetCurrent();
   v8::HandleScope handle_scope(isolate);
   v8::Context::Scope context_scope(frame->MainWorldScriptContext());
-  PostJavaScriptMessage(isolate,
-                        content::V8ValueConverter::Create()->ToV8Value(
-                            &message, frame->MainWorldScriptContext()));
+  PostJavaScriptMessage(isolate, content::V8ValueConverter::Create()->ToV8Value(
+                                     message, frame->MainWorldScriptContext()));
 }
 
 void PostMessageSupport::SetActive() {
diff --git a/extensions/renderer/native_renderer_messaging_service.cc b/extensions/renderer/native_renderer_messaging_service.cc
index 1ca9356..52cf160f 100644
--- a/extensions/renderer/native_renderer_messaging_service.cc
+++ b/extensions/renderer/native_renderer_messaging_service.cc
@@ -456,7 +456,7 @@
   if (extension) {
     if (!source->tab.DictEmpty() && !extension->is_platform_app()) {
       sender_builder.Set("tab", content::V8ValueConverter::Create()->ToV8Value(
-                                    &source->tab, v8_context));
+                                    source->tab, v8_context));
     }
 
     ExternallyConnectableInfo* externally_connectable =
diff --git a/extensions/renderer/runtime_hooks_delegate.cc b/extensions/renderer/runtime_hooks_delegate.cc
index 04d8d846..e98a975 100644
--- a/extensions/renderer/runtime_hooks_delegate.cc
+++ b/extensions/renderer/runtime_hooks_delegate.cc
@@ -223,12 +223,14 @@
 RequestResult RuntimeHooksDelegate::HandleGetManifest(
     ScriptContext* script_context,
     const APISignature::V8ParseResult& parse_result) {
-  DCHECK(script_context->extension());
   DCHECK_EQ(binding::AsyncResponseType::kNone, parse_result.async_type);
+  CHECK(script_context->extension());
+  CHECK(script_context->extension()->manifest());
+  CHECK(script_context->extension()->manifest()->value());
 
   RequestResult result(RequestResult::HANDLED);
   result.return_value = content::V8ValueConverter::Create()->ToV8Value(
-      script_context->extension()->manifest()->value(),
+      *script_context->extension()->manifest()->value(),
       script_context->v8_context());
 
   return result;
diff --git a/extensions/renderer/test_features_native_handler.cc b/extensions/renderer/test_features_native_handler.cc
index 6857f0b..adc1da33 100644
--- a/extensions/renderer/test_features_native_handler.cc
+++ b/extensions/renderer/test_features_native_handler.cc
@@ -27,7 +27,7 @@
   std::unique_ptr<JSONFeatureProviderSource> source(
       ExtensionsClient::Get()->CreateAPIFeatureSource());
   args.GetReturnValue().Set(content::V8ValueConverter::Create()->ToV8Value(
-      &source->dictionary(), context()->v8_context()));
+      source->dictionary(), context()->v8_context()));
 }
 
 }  // namespace extensions
diff --git a/extensions/renderer/web_request_hooks.cc b/extensions/renderer/web_request_hooks.cc
index 858f89b..a08dea9 100644
--- a/extensions/renderer/web_request_hooks.cc
+++ b/extensions/renderer/web_request_hooks.cc
@@ -69,7 +69,7 @@
   const base::ListValue* extra_params = nullptr;
   CHECK(event_spec->GetList("extraParameters", &extra_params));
   v8::Local<v8::Value> extra_parameters_spec =
-      content::V8ValueConverter::Create()->ToV8Value(extra_params, context);
+      content::V8ValueConverter::Create()->ToV8Value(*extra_params, context);
 
   v8::Local<v8::Function> get_event = get_event_value.As<v8::Function>();
   v8::Local<v8::Value> args[] = {
diff --git a/fuchsia_web/shell/BUILD.gn b/fuchsia_web/shell/BUILD.gn
index 7a14ffb9..5b2b7ac 100644
--- a/fuchsia_web/shell/BUILD.gn
+++ b/fuchsia_web/shell/BUILD.gn
@@ -19,6 +19,14 @@
   deps = [ ":web_engine_shell" ]
 }
 
+source_set("remote_debugging_port") {
+  sources = [
+    "remote_debugging_port.cc",
+    "remote_debugging_port.h",
+  ]
+  deps = [ "//base" ]
+}
+
 fuchsia_component("web_engine_shell_component") {
   testonly = true
   manifest = "web_engine_shell.cmx"
@@ -56,6 +64,7 @@
   data = [ "data" ]
 
   deps = [
+    ":remote_debugging_port",
     "//base",
     "//fuchsia_web/common",
     "//fuchsia_web/webinstance_host/",
diff --git a/fuchsia_web/shell/remote_debugging_port.cc b/fuchsia_web/shell/remote_debugging_port.cc
new file mode 100644
index 0000000..001537a4
--- /dev/null
+++ b/fuchsia_web/shell/remote_debugging_port.cc
@@ -0,0 +1,29 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "fuchsia_web/shell/remote_debugging_port.h"
+
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+
+constexpr char kRemoteDebuggingPortSwitch[] = "remote-debugging-port";
+
+absl::optional<uint16_t> GetRemoteDebuggingPort(
+    const base::CommandLine& command_line) {
+  if (!command_line.HasSwitch(kRemoteDebuggingPortSwitch)) {
+    return 0;
+  } else {
+    std::string port_str =
+        command_line.GetSwitchValueNative(kRemoteDebuggingPortSwitch);
+    int port_parsed;
+    if (!base::StringToInt(port_str, &port_parsed) || port_parsed < 0 ||
+        port_parsed > 65535) {
+      LOG(ERROR) << "Invalid value for --remote-debugging-port (must be in the "
+                    "range 0-65535).";
+      return absl::nullopt;
+    }
+    return static_cast<uint16_t>(port_parsed);
+  }
+}
diff --git a/fuchsia_web/shell/remote_debugging_port.h b/fuchsia_web/shell/remote_debugging_port.h
new file mode 100644
index 0000000..03ef395
--- /dev/null
+++ b/fuchsia_web/shell/remote_debugging_port.h
@@ -0,0 +1,25 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef FUCHSIA_WEB_SHELL_REMOTE_DEBUGGING_PORT_H_
+#define FUCHSIA_WEB_SHELL_REMOTE_DEBUGGING_PORT_H_
+
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+extern const char kRemoteDebuggingPortSwitch[];
+
+namespace base {
+
+class CommandLine;
+
+}  // namespace base
+
+// Return default value of 0 if |command_line| does not have remote debugging
+// port switch. If |command_line| contains the appropriate switch, returns the
+// remote debugging port specified in the |command_line| or nullopt on parsing
+// failure.
+absl::optional<uint16_t> GetRemoteDebuggingPort(
+    const base::CommandLine& command_line);
+
+#endif  // FUCHSIA_WEB_SHELL_REMOTE_DEBUGGING_PORT_H_
diff --git a/fuchsia_web/shell/web_engine_shell.cc b/fuchsia_web/shell/web_engine_shell.cc
index b706944..a4f8b8b9 100644
--- a/fuchsia_web/shell/web_engine_shell.cc
+++ b/fuchsia_web/shell/web_engine_shell.cc
@@ -23,12 +23,12 @@
 #include "base/message_loop/message_pump_type.h"
 #include "base/path_service.h"
 #include "base/run_loop.h"
-#include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
 #include "base/task/single_thread_task_executor.h"
 #include "base/values.h"
 #include "build/build_config.h"
 #include "fuchsia_web/common/init_logging.h"
+#include "fuchsia_web/shell/remote_debugging_port.h"
 #include "fuchsia_web/webinstance_host/web_instance_host.h"
 #include "url/gurl.h"
 
@@ -36,7 +36,6 @@
 
 namespace {
 
-constexpr char kRemoteDebuggingPortSwitch[] = "remote-debugging-port";
 constexpr char kHeadlessSwitch[] = "headless";
 constexpr char kEnableProtectedMediaIdentifier[] =
     "enable-protected-media-identifier";
@@ -59,20 +58,6 @@
             << "WebEngine to be processed." << std::endl;
 }
 
-absl::optional<uint16_t> ParseRemoteDebuggingPort(
-    const base::CommandLine& command_line) {
-  std::string port_str =
-      command_line.GetSwitchValueNative(kRemoteDebuggingPortSwitch);
-  int port_parsed;
-  if (!base::StringToInt(port_str, &port_parsed) || port_parsed < 0 ||
-      port_parsed > 65535) {
-    LOG(ERROR) << "Invalid value for --remote-debugging-port (must be in the "
-                  "range 0-65535).";
-    return absl::nullopt;
-  }
-  return (uint16_t)port_parsed;
-}
-
 GURL GetUrlFromArgs(const base::CommandLine::StringVector& args) {
   if (args.empty()) {
     LOG(ERROR) << "No URL provided.";
@@ -136,13 +121,11 @@
   CHECK(InitLoggingFromCommandLineDefaultingToStderrForTest(  // IN-TEST
       command_line));
 
-  absl::optional<uint16_t> remote_debugging_port;
-  if (command_line->HasSwitch(kRemoteDebuggingPortSwitch)) {
-    remote_debugging_port = ParseRemoteDebuggingPort(*command_line);
-    if (!remote_debugging_port) {
-      PrintUsage();
-      return 1;
-    }
+  absl::optional<uint16_t> remote_debugging_port =
+      GetRemoteDebuggingPort(*command_line);
+  if (!remote_debugging_port) {
+    PrintUsage();
+    return 1;
   }
 
   const bool is_headless = command_line->HasSwitch(kHeadlessSwitch);
@@ -204,8 +187,7 @@
     features |= fuchsia::web::ContextFeatureFlags::VULKAN;
 
   create_context_params.set_features(features);
-  if (remote_debugging_port)
-    create_context_params.set_remote_debugging_port(*remote_debugging_port);
+  create_context_params.set_remote_debugging_port(*remote_debugging_port);
 
   // DRM services require cdm_data_directory to be populated, so create a
   // directory under /data and use that as the cdm_data_directory.
@@ -266,8 +248,7 @@
 
   // Create the browser |frame| which will contain the webpage.
   fuchsia::web::CreateFrameParams frame_params;
-  if (remote_debugging_port)
-    frame_params.set_enable_remote_debugging(true);
+  frame_params.set_enable_remote_debugging(true);
 
   fuchsia::web::FramePtr frame;
   context->CreateFrameWithParams(std::move(frame_params), frame.NewRequest());
@@ -281,19 +262,17 @@
   settings.set_autoplay_policy(fuchsia::web::AutoplayPolicy::ALLOW);
   frame->SetContentAreaSettings(std::move(settings));
 
-  // Log the debugging port, if debugging is requested.
-  if (remote_debugging_port) {
-    context->GetRemoteDebuggingPort(
-        [](fuchsia::web::Context_GetRemoteDebuggingPort_Result result) {
-          if (result.is_err()) {
-            LOG(ERROR) << "Remote debugging service was not opened.";
-            return;
-          }
-          // Telemetry expects this exact format of log line output to retrieve
-          // the remote debugging port.
-          LOG(INFO) << "Remote debugging port: " << result.response().port;
-        });
-  }
+  // Log the debugging port.
+  context->GetRemoteDebuggingPort(
+      [](fuchsia::web::Context_GetRemoteDebuggingPort_Result result) {
+        if (result.is_err()) {
+          LOG(ERROR) << "Remote debugging service was not opened.";
+          return;
+        }
+        // Telemetry expects this exact format of log line output to retrieve
+        // the remote debugging port.
+        LOG(INFO) << "Remote debugging port: " << result.response().port;
+      });
 
   // Navigate |frame| to |url|.
   fuchsia::web::LoadUrlParams load_params;
diff --git a/fuchsia_web/webengine/BUILD.gn b/fuchsia_web/webengine/BUILD.gn
index d40151f..47073133 100644
--- a/fuchsia_web/webengine/BUILD.gn
+++ b/fuchsia_web/webengine/BUILD.gn
@@ -341,6 +341,7 @@
     ":switches",
     ":web_engine_core",
     "//base",
+    "//components/fuchsia_component_support",
     "//content/public/app",
   ]
   sources = [ "web_engine_main.cc" ]
diff --git a/fuchsia_web/webengine/browser/web_engine_browser_main.cc b/fuchsia_web/webengine/browser/web_engine_browser_main.cc
index 00216b4..73c21cd 100644
--- a/fuchsia_web/webengine/browser/web_engine_browser_main.cc
+++ b/fuchsia_web/webengine/browser/web_engine_browser_main.cc
@@ -8,21 +8,9 @@
 
 #include "base/logging.h"
 #include "build/build_config.h"
-#include "components/fuchsia_component_support/config_reader.h"
 #include "content/public/browser/browser_main_runner.h"
-#include "fuchsia_web/webengine/browser/web_engine_config.h"
 
 int WebEngineBrowserMain(content::MainFunctionParams parameters) {
-  // Process package config-data if any.
-  const absl::optional<base::Value>& config =
-      fuchsia_component_support::LoadPackageConfig();
-  if (config) {
-    bool config_valid = UpdateCommandLineFromConfigFile(
-        config.value(), base::CommandLine::ForCurrentProcess());
-    if (!config_valid)
-      LOG(FATAL) << "WebEngine config is invalid.";
-  }
-
   std::unique_ptr<content::BrowserMainRunner> main_runner =
       content::BrowserMainRunner::Create();
   int exit_code = main_runner->Initialize(std::move(parameters));
diff --git a/fuchsia_web/webengine/browser/web_engine_config.cc b/fuchsia_web/webengine/browser/web_engine_config.cc
index 9639366..3439d4a0 100644
--- a/fuchsia_web/webengine/browser/web_engine_config.cc
+++ b/fuchsia_web/webengine/browser/web_engine_config.cc
@@ -8,6 +8,7 @@
 #include "base/command_line.h"
 #include "base/containers/contains.h"
 #include "base/logging.h"
+#include "base/metrics/field_trial.h"
 #include "base/strings/strcat.h"
 #include "base/values.h"
 #include "cc/base/switches.h"
@@ -123,6 +124,9 @@
 
 bool UpdateCommandLineFromConfigFile(const base::Value& config,
                                      base::CommandLine* command_line) {
+  // The FieldTrialList should be initialized only after config is loaded.
+  DCHECK(base::FieldTrialList::GetInstance());
+
   if (!AddCommandLineArgsFromConfig(config, command_line))
     return false;
 
diff --git a/fuchsia_web/webengine/web_engine_main.cc b/fuchsia_web/webengine/web_engine_main.cc
index 0569a7e..e73e09f9 100644
--- a/fuchsia_web/webengine/web_engine_main.cc
+++ b/fuchsia_web/webengine/web_engine_main.cc
@@ -3,18 +3,41 @@
 // found in the LICENSE file.
 
 #include "base/command_line.h"
+#include "base/logging.h"
+#include "components/fuchsia_component_support/config_reader.h"
 #include "content/public/app/content_main.h"
+#include "content/public/common/content_switches.h"
+#include "fuchsia_web/webengine/browser/web_engine_config.h"
 #include "fuchsia_web/webengine/context_provider_main.h"
 #include "fuchsia_web/webengine/switches.h"
 #include "fuchsia_web/webengine/web_engine_main_delegate.h"
 
+static void LoadConfigAndUpdateCommandLine(base::CommandLine* command_line) {
+  // Config file needs to be loaded only in the browser process.
+  bool is_browser_process =
+      command_line->GetSwitchValueASCII(switches::kProcessType).empty();
+  if (!is_browser_process)
+    return;
+
+  const absl::optional<base::Value>& config =
+      fuchsia_component_support::LoadPackageConfig();
+  if (!config)
+    return;
+
+  bool config_valid =
+      UpdateCommandLineFromConfigFile(config.value(), command_line);
+  if (!config_valid)
+    LOG(FATAL) << "WebEngine config is invalid.";
+}
+
 int main(int argc, const char** argv) {
   base::CommandLine::Init(argc, argv);
 
-  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kContextProvider)) {
+  auto* const command_line = base::CommandLine::ForCurrentProcess();
+  if (command_line->HasSwitch(switches::kContextProvider))
     return ContextProviderMain();
-  }
+
+  LoadConfigAndUpdateCommandLine(command_line);
 
   WebEngineMainDelegate delegate;
   content::ContentMainParams params(&delegate);
diff --git a/gpu/command_buffer/client/shared_memory_limits.h b/gpu/command_buffer/client/shared_memory_limits.h
index 238785d..d3e13c08 100644
--- a/gpu/command_buffer/client/shared_memory_limits.h
+++ b/gpu/command_buffer/client/shared_memory_limits.h
@@ -21,7 +21,7 @@
     // Do not use more than 5% of extra shared memory, and do not use any extra
     // for memory contrained devices (<=1GB).
     max_mapped_memory_for_texture_upload =
-        base::SysInfo::AmountOfPhysicalMemory() > 1024 * 1024 * 1024
+        base::SysInfo::AmountOfPhysicalMemory() > 1024ULL * 1024 * 1024
             ? base::saturated_cast<uint32_t>(
                   base::SysInfo::AmountOfPhysicalMemory() / 20)
             : 0;
diff --git a/gpu/command_buffer/service/shared_image_backing_factory_ozone.cc b/gpu/command_buffer/service/shared_image_backing_factory_ozone.cc
index 4535360..b8ed63c 100644
--- a/gpu/command_buffer/service/shared_image_backing_factory_ozone.cc
+++ b/gpu/command_buffer/service/shared_image_backing_factory_ozone.cc
@@ -84,6 +84,7 @@
                                                  gfx::BufferUsage::GPU_READ);
   }
   if (!pixmap) {
+    DLOG(ERROR) << "Failed to create native pixmap";
     return nullptr;
   }
   return std::make_unique<SharedImageBackingOzone>(
@@ -124,9 +125,12 @@
       CreateSharedImageInternal(mailbox, format, surface_handle, size,
                                 color_space, surface_origin, alpha_type, usage);
 
+  if (!backing) {
+    return nullptr;
+  }
   if (!pixel_data.empty() &&
-      !backing->WritePixels(pixel_data, shared_context_state_, format, size,
-                            alpha_type)) {
+      !backing->WritePixels(pixel_data, shared_context_state_.get(), format,
+                            size, alpha_type)) {
     return nullptr;
   }
 
@@ -176,8 +180,11 @@
     backing = CreateSharedImageInternal(mailbox, format, surface_handle, size,
                                         color_space, surface_origin, alpha_type,
                                         usage);
+    if (!backing) {
+      return nullptr;
+    }
     if (!backing->WritePixels(shm_wrapper.GetMemoryAsSpan(),
-                              shared_context_state_, format, size,
+                              shared_context_state_.get(), format, size,
                               alpha_type)) {
       DLOG(ERROR) << "Failed to write pixels for shared memory.";
       return nullptr;
diff --git a/gpu/config/skia_limits.cc b/gpu/config/skia_limits.cc
index ee23325..390f820 100644
--- a/gpu/config/skia_limits.cc
+++ b/gpu/config/skia_limits.cc
@@ -30,7 +30,7 @@
   // Limits for glyph cache textures.
   constexpr size_t kMaxLowEndGlyphCacheTextureBytes = 1024 * 512 * 4;
   // High-end / low-end memory cutoffs.
-  constexpr int64_t kHighEndMemoryThreshold = 4096LL * 1024 * 1024;
+  constexpr uint64_t kHighEndMemoryThreshold = 4096ULL * 1024 * 1024;
 
   if (base::SysInfo::IsLowEndDevice()) {
     *max_resource_cache_bytes = kMaxLowEndGaneshResourceCacheBytes;
diff --git a/infra/config/generated/builders/ci/fuchsia-fyi-arm64-cfv2-script/properties.json b/infra/config/generated/builders/ci/fuchsia-fyi-arm64-cfv2-script/properties.json
new file mode 100644
index 0000000..fe8821f8
--- /dev/null
+++ b/infra/config/generated/builders/ci/fuchsia-fyi-arm64-cfv2-script/properties.json
@@ -0,0 +1,62 @@
+{
+  "$build/chromium_tests_builder_config": {
+    "builder_config": {
+      "builder_db": {
+        "entries": [
+          {
+            "builder_id": {
+              "bucket": "ci",
+              "builder": "fuchsia-fyi-arm64-cfv2-script",
+              "project": "chromium"
+            },
+            "builder_spec": {
+              "build_gs_bucket": "chromium-fuchsia-archive",
+              "builder_group": "chromium.fyi",
+              "execution_mode": "COMPILE_AND_TEST",
+              "legacy_chromium_config": {
+                "apply_configs": [
+                  "mb"
+                ],
+                "build_config": "Release",
+                "config": "chromium",
+                "target_arch": "arm",
+                "target_bits": 64,
+                "target_platform": "fuchsia"
+              },
+              "legacy_gclient_config": {
+                "apply_configs": [
+                  "fuchsia_arm64",
+                  "fuchsia_arm64_host"
+                ],
+                "config": "chromium"
+              },
+              "run_tests_serially": true
+            }
+          }
+        ]
+      },
+      "builder_ids": [
+        {
+          "bucket": "ci",
+          "builder": "fuchsia-fyi-arm64-cfv2-script",
+          "project": "chromium"
+        }
+      ]
+    }
+  },
+  "$build/goma": {
+    "enable_ats": true,
+    "rpc_extra_params": "?prod",
+    "server_host": "goma.chromium.org",
+    "use_luci_auth": true
+  },
+  "$recipe_engine/resultdb/test_presentation": {
+    "column_keys": [],
+    "grouping_keys": [
+      "status",
+      "v.test_suite"
+    ]
+  },
+  "builder_group": "chromium.fyi",
+  "recipe": "chromium"
+}
\ No newline at end of file
diff --git a/infra/config/generated/luci/cr-buildbucket.cfg b/infra/config/generated/luci/cr-buildbucket.cfg
index eca4ca8..6d39ef91 100644
--- a/infra/config/generated/luci/cr-buildbucket.cfg
+++ b/infra/config/generated/luci/cr-buildbucket.cfg
@@ -30424,6 +30424,86 @@
       }
     }
     builders {
+      name: "fuchsia-fyi-arm64-cfv2-script"
+      swarming_host: "chromium-swarm.appspot.com"
+      dimensions: "builderless:1"
+      dimensions: "cores:8"
+      dimensions: "cpu:x86-64"
+      dimensions: "free_space:standard"
+      dimensions: "os:Ubuntu-18.04"
+      dimensions: "pool:luci.chromium.ci"
+      dimensions: "ssd:0"
+      exe {
+        cipd_package: "infra/chromium/bootstrapper/${platform}"
+        cipd_version: "latest"
+        cmd: "bootstrapper"
+      }
+      properties:
+        '{'
+        '  "$bootstrap/exe": {'
+        '    "exe": {'
+        '      "cipd_package": "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build",'
+        '      "cipd_version": "refs/heads/main",'
+        '      "cmd": ['
+        '        "luciexe"'
+        '      ]'
+        '    }'
+        '  },'
+        '  "$bootstrap/properties": {'
+        '    "properties_file": "infra/config/generated/builders/ci/fuchsia-fyi-arm64-cfv2-script/properties.json",'
+        '    "top_level_project": {'
+        '      "ref": "refs/heads/main",'
+        '      "repo": {'
+        '        "host": "chromium.googlesource.com",'
+        '        "project": "chromium/src"'
+        '      }'
+        '    }'
+        '  },'
+        '  "builder_group": "chromium.fyi",'
+        '  "led_builder_is_bootstrapped": true,'
+        '  "recipe": "chromium"'
+        '}'
+      execution_timeout_secs: 36000
+      build_numbers: YES
+      service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
+      experiments {
+        key: "luci.recipes.use_python3"
+        value: 100
+      }
+      resultdb {
+        enable: true
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "ci_test_results"
+          test_results {}
+        }
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "gpu_ci_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "ninja://chrome/test:telemetry_gpu_integration_test[^/]*/.+"
+            }
+          }
+        }
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "blink_web_tests_ci_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "ninja://[^/]*blink_web_tests/.+"
+            }
+          }
+        }
+        history_options {
+          use_invocation_timestamp: true
+        }
+      }
+    }
+    builders {
       name: "fuchsia-fyi-arm64-dbg"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
diff --git a/infra/config/generated/luci/luci-milo.cfg b/infra/config/generated/luci/luci-milo.cfg
index 93ef6e59..50324fd 100644
--- a/infra/config/generated/luci/luci-milo.cfg
+++ b/infra/config/generated/luci/luci-milo.cfg
@@ -7803,6 +7803,11 @@
     short_name: "dbg"
   }
   builders {
+    name: "buildbucket/luci.chromium.ci/fuchsia-fyi-arm64-cfv2-script"
+    category: "fuchsia|a64"
+    short_name: "cfv2"
+  }
+  builders {
     name: "buildbucket/luci.chromium.ci/fuchsia-fyi-arm64-emu-arg"
     category: "fuchsia|a64"
     short_name: "emu-arg"
diff --git a/infra/config/generated/luci/luci-scheduler.cfg b/infra/config/generated/luci/luci-scheduler.cfg
index 124df4a..6c0975f 100644
--- a/infra/config/generated/luci/luci-scheduler.cfg
+++ b/infra/config/generated/luci/luci-scheduler.cfg
@@ -5110,6 +5110,16 @@
   }
 }
 job {
+  id: "fuchsia-fyi-arm64-cfv2-script"
+  realm: "ci"
+  acl_sets: "ci"
+  buildbucket {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "ci"
+    builder: "fuchsia-fyi-arm64-cfv2-script"
+  }
+}
+job {
   id: "fuchsia-fyi-arm64-dbg"
   realm: "ci"
   acl_sets: "ci"
@@ -7375,6 +7385,7 @@
   triggers: "chromeos-octopus-rel"
   triggers: "fuchsia-angle-builder"
   triggers: "fuchsia-arm64-cast"
+  triggers: "fuchsia-fyi-arm64-cfv2-script"
   triggers: "fuchsia-fyi-arm64-dbg"
   triggers: "fuchsia-fyi-arm64-emu-arg"
   triggers: "fuchsia-fyi-arm64-rel"
diff --git a/infra/config/subprojects/chromium/ci/chromium.fyi.star b/infra/config/subprojects/chromium/ci/chromium.fyi.star
index e6a4133..e9ac00a9 100644
--- a/infra/config/subprojects/chromium/ci/chromium.fyi.star
+++ b/infra/config/subprojects/chromium/ci/chromium.fyi.star
@@ -254,6 +254,38 @@
 )
 
 ci.builder(
+    name = "fuchsia-fyi-arm64-cfv2-script",
+    builder_spec = builder_config.builder_spec(
+        gclient_config = builder_config.gclient_config(
+            config = "chromium",
+            apply_configs = [
+                "fuchsia_arm64",
+                "fuchsia_arm64_host",
+            ],
+        ),
+        chromium_config = builder_config.chromium_config(
+            config = "chromium",
+            apply_configs = [
+                "mb",
+            ],
+            build_config = builder_config.build_config.RELEASE,
+            target_arch = builder_config.target_arch.ARM,
+            target_bits = 64,
+            target_platform = "fuchsia",
+        ),
+        build_gs_bucket = "chromium-fuchsia-archive",
+        run_tests_serially = True,
+    ),
+    console_view_entry = [
+        consoles.console_view_entry(
+            category = "fuchsia|a64",
+            short_name = "cfv2",
+        ),
+    ],
+    os = os.LINUX_DEFAULT,
+)
+
+ci.builder(
     name = "fuchsia-fyi-x64-rel",
     console_view_entry = [
         consoles.console_view_entry(
diff --git a/ios/chrome/browser/policy/policy_egtest.mm b/ios/chrome/browser/policy/policy_egtest.mm
index c4da832c..12e7928 100644
--- a/ios/chrome/browser/policy/policy_egtest.mm
+++ b/ios/chrome/browser/policy/policy_egtest.mm
@@ -131,7 +131,7 @@
 
 @implementation PolicyTestCase {
   BOOL _settingsOpened;
-  std::unique_ptr<policy::EmbeddedPolicyTestServer> test_server_;
+  std::unique_ptr<policy::EmbeddedPolicyTestServer> _server;
 }
 
 - (void)tearDown {
@@ -465,8 +465,8 @@
 
 // Tests the chrome://management page when there are machine level policies.
 - (void)testManagementPageManagedWithCBCM {
-  test_server_ = std::make_unique<policy::EmbeddedPolicyTestServer>();
-  test_server_->Start();
+  _server = std::make_unique<policy::EmbeddedPolicyTestServer>();
+  _server->Start();
 
   // Enable machine level (browser) cloud policies.
   AppLaunchConfiguration config;
@@ -481,7 +481,7 @@
   // Use the embedded test server as the policy server.
   config.additional_args.push_back(
       base::StrCat({"--", policy::switches::kDeviceManagementUrl, "=",
-                    test_server_->GetServiceURL().spec()}));
+                    _server->GetServiceURL().spec()}));
   [[AppLaunchManager sharedManager] ensureAppLaunchedWithConfiguration:config];
 
   [PolicyAppInterface setBrowserCloudPolicyDataWithDomain:kDomain1];
@@ -509,8 +509,8 @@
 // Tests the chrome://management page when there are machine level policies and
 // user level policies from the same domain.
 - (void)testManagementPageManagedWithCBCMAndUserPolicyDifferentDomains {
-  test_server_ = std::make_unique<policy::EmbeddedPolicyTestServer>();
-  test_server_->Start();
+  _server = std::make_unique<policy::EmbeddedPolicyTestServer>();
+  _server->Start();
 
   // Enable browser cloud policies.
   AppLaunchConfiguration config;
@@ -525,7 +525,7 @@
   // Use the embedded test server as the policy server.
   config.additional_args.push_back(
       base::StrCat({"--", policy::switches::kDeviceManagementUrl, "=",
-                    test_server_->GetServiceURL().spec()}));
+                    _server->GetServiceURL().spec()}));
   [[AppLaunchManager sharedManager] ensureAppLaunchedWithConfiguration:config];
 
   [PolicyAppInterface setBrowserCloudPolicyDataWithDomain:kDomain1];
@@ -544,8 +544,8 @@
 // Tests the chrome://management page when there are machine level policies and
 // user level policies from different domains.
 - (void)testManagementPageManagedWithCBCMAndUserPolicySameDomains {
-  test_server_ = std::make_unique<policy::EmbeddedPolicyTestServer>();
-  test_server_->Start();
+  _server = std::make_unique<policy::EmbeddedPolicyTestServer>();
+  _server->Start();
 
   // Enable browser cloud policies.
   AppLaunchConfiguration config;
@@ -560,7 +560,7 @@
   // Use the embedded test server as the policy server.
   config.additional_args.push_back(
       base::StrCat({"--", policy::switches::kDeviceManagementUrl, "=",
-                    test_server_->GetServiceURL().spec()}));
+                    _server->GetServiceURL().spec()}));
   [[AppLaunchManager sharedManager] ensureAppLaunchedWithConfiguration:config];
 
   [PolicyAppInterface setBrowserCloudPolicyDataWithDomain:kDomain1];
diff --git a/ios/chrome/browser/ui/alert_coordinator/loading_alert_coordinator.mm b/ios/chrome/browser/ui/alert_coordinator/loading_alert_coordinator.mm
index 58c9380..94873f4 100644
--- a/ios/chrome/browser/ui/alert_coordinator/loading_alert_coordinator.mm
+++ b/ios/chrome/browser/ui/alert_coordinator/loading_alert_coordinator.mm
@@ -121,8 +121,8 @@
 
   // Title.
   NSMutableDictionary* attrsDictionary = [NSMutableDictionary
-      dictionaryWithObject:[[MDCTypography fontLoader]
-                               mediumFontOfSize:kTitleLabelFontSize]
+      dictionaryWithObject:[UIFont systemFontOfSize:kTitleLabelFontSize
+                                             weight:UIFontWeightMedium]
                     forKey:NSFontAttributeName];
   [attrsDictionary setObject:UIColorFromRGB(kTitleLabelFontColor)
                       forKey:NSForegroundColorAttributeName];
diff --git a/ios/chrome/browser/ui/autofill/cells/cvc_item.mm b/ios/chrome/browser/ui/autofill/cells/cvc_item.mm
index 877d602..9bed9c3 100644
--- a/ios/chrome/browser/ui/autofill/cells/cvc_item.mm
+++ b/ios/chrome/browser/ui/autofill/cells/cvc_item.mm
@@ -113,8 +113,8 @@
     UIView* contentView = self.contentView;
 
     _instructionsTextLabel = [[UILabel alloc] init];
-    _instructionsTextLabel.font =
-        [[MDCTypography fontLoader] mediumFontOfSize:14];
+    _instructionsTextLabel.font = [UIFont systemFontOfSize:14
+                                                    weight:UIFontWeightMedium];
     _instructionsTextLabel.textColor = [UIColor colorNamed:kTextSecondaryColor];
     _instructionsTextLabel.numberOfLines = 0;
     _instructionsTextLabel.lineBreakMode = NSLineBreakByWordWrapping;
@@ -135,7 +135,7 @@
     [contentView addSubview:googlePayBadge];
 
     _errorLabel = [[UILabel alloc] init];
-    _errorLabel.font = [[MDCTypography fontLoader] regularFontOfSize:12];
+    _errorLabel.font = [UIFont systemFontOfSize:12 weight:UIFontWeightRegular];
     _errorLabel.textColor = [UIColor colorNamed:kRedColor];
     _errorLabel.numberOfLines = 0;
     _errorLabel.lineBreakMode = NSLineBreakByWordWrapping;
@@ -190,7 +190,7 @@
 
     _buttonForNewCard = [UIButton buttonWithType:UIButtonTypeCustom];
     _buttonForNewCard.titleLabel.font =
-        [[MDCTypography fontLoader] regularFontOfSize:12];
+        [UIFont systemFontOfSize:12 weight:UIFontWeightRegular];
     [_buttonForNewCard
         setTitle:l10n_util::GetNSString(IDS_AUTOFILL_CARD_UNMASK_NEW_CARD_LINK)
         forState:UIControlStateNormal];
diff --git a/ios/chrome/browser/ui/autofill/cells/legacy_autofill_edit_item.mm b/ios/chrome/browser/ui/autofill/cells/legacy_autofill_edit_item.mm
index a6447e02..dabd9ad 100644
--- a/ios/chrome/browser/ui/autofill/cells/legacy_autofill_edit_item.mm
+++ b/ios/chrome/browser/ui/autofill/cells/legacy_autofill_edit_item.mm
@@ -171,11 +171,12 @@
 
 - (void)updateWithFontScaling:(BOOL)withFontScaling {
   MaybeSetUILabelScaledFont(withFontScaling, self.textLabel,
-                            [[MDCTypography fontLoader] mediumFontOfSize:14]);
+                            [UIFont systemFontOfSize:14
+                                              weight:UIFontWeightMedium]);
   self.textLabel.textColor = [UIColor colorNamed:kTextPrimaryColor];
-  MaybeSetUITextFieldScaledFont(
-      withFontScaling, self.textField,
-      [[MDCTypography fontLoader] lightFontOfSize:16]);
+  MaybeSetUITextFieldScaledFont(withFontScaling, self.textField,
+                                [UIFont systemFontOfSize:16
+                                                  weight:UIFontWeightLight]);
   self.textField.textColor = [UIColor colorNamed:kTextSecondaryColor];
 }
 
diff --git a/ios/chrome/browser/ui/autofill/cells/status_item.mm b/ios/chrome/browser/ui/autofill/cells/status_item.mm
index 8bcbaa2..caff6c8 100644
--- a/ios/chrome/browser/ui/autofill/cells/status_item.mm
+++ b/ios/chrome/browser/ui/autofill/cells/status_item.mm
@@ -88,7 +88,7 @@
     [verticalCenteringView addSubview:_errorImageView];
 
     _textLabel = [[UILabel alloc] init];
-    _textLabel.font = [[MDCTypography fontLoader] mediumFontOfSize:16];
+    _textLabel.font = [UIFont systemFontOfSize:16 weight:UIFontWeightMedium];
     _textLabel.numberOfLines = 0;
     _textLabel.lineBreakMode = NSLineBreakByWordWrapping;
     // The label's position will be centered with Auto Layout but this ensures
diff --git a/ios/chrome/browser/ui/collection_view/cells/collection_view_account_item.mm b/ios/chrome/browser/ui/collection_view/cells/collection_view_account_item.mm
index 865fbd1..19c90caf 100644
--- a/ios/chrome/browser/ui/collection_view/cells/collection_view_account_item.mm
+++ b/ios/chrome/browser/ui/collection_view/cells/collection_view_account_item.mm
@@ -138,13 +138,14 @@
 
   _textLabel = [[UILabel alloc] init];
   _textLabel.translatesAutoresizingMaskIntoConstraints = NO;
-  _textLabel.font = [[MDCTypography fontLoader] mediumFontOfSize:14];
+  _textLabel.font = [UIFont systemFontOfSize:14 weight:UIFontWeightMedium];
   _textLabel.textColor = [UIColor colorNamed:kTextPrimaryColor];
   [contentView addSubview:_textLabel];
 
   _detailTextLabel = [[UILabel alloc] init];
   _detailTextLabel.translatesAutoresizingMaskIntoConstraints = NO;
-  _detailTextLabel.font = [[MDCTypography fontLoader] regularFontOfSize:14];
+  _detailTextLabel.font = [UIFont systemFontOfSize:14
+                                            weight:UIFontWeightRegular];
   _detailTextLabel.textColor = [UIColor colorNamed:kTextSecondaryColor];
   [contentView addSubview:_detailTextLabel];
 }
diff --git a/ios/chrome/browser/ui/collection_view/cells/collection_view_switch_item.mm b/ios/chrome/browser/ui/collection_view/cells/collection_view_switch_item.mm
index 204c53c..90ece039 100644
--- a/ios/chrome/browser/ui/collection_view/cells/collection_view_switch_item.mm
+++ b/ios/chrome/browser/ui/collection_view/cells/collection_view_switch_item.mm
@@ -110,7 +110,8 @@
 
 - (void)useScaledFont:(BOOL)useScaledFont {
   MaybeSetUILabelScaledFont(useScaledFont, _textLabel,
-                            [[MDCTypography fontLoader] mediumFontOfSize:14]);
+                            [UIFont systemFontOfSize:14
+                                              weight:UIFontWeightMedium]);
 }
 
 + (UIColor*)defaultTextColorForState:(UIControlState)state {
diff --git a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_whats_new_item.mm b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_whats_new_item.mm
index 3eb9982..d29b5fe 100644
--- a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_whats_new_item.mm
+++ b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_whats_new_item.mm
@@ -123,8 +123,8 @@
 
 // Configures the `promoLabel` with the `text`.
 + (void)configureLabel:(UILabel*)promoLabel withText:(NSString*)text {
-  promoLabel.font =
-      [[MDCTypography fontLoader] regularFontOfSize:kLabelFontSize];
+  promoLabel.font = [UIFont systemFontOfSize:kLabelFontSize
+                                      weight:UIFontWeightRegular];
   promoLabel.textColor = [UIColor colorNamed:kTextPrimaryColor];
   promoLabel.numberOfLines = 0;
 
diff --git a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_whats_new_view.mm b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_whats_new_view.mm
index 654108e..3ada7bc 100644
--- a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_whats_new_view.mm
+++ b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_whats_new_view.mm
@@ -86,8 +86,8 @@
 
 // Configures `promoLabel` with `text`.
 - (void)configureLabelWithText:(NSString*)text {
-  _promoLabel.font =
-      [[MDCTypography fontLoader] regularFontOfSize:kLabelFontSize];
+  _promoLabel.font = [UIFont systemFontOfSize:kLabelFontSize
+                                       weight:UIFontWeightRegular];
   _promoLabel.textColor = [UIColor colorNamed:kTextPrimaryColor];
   _promoLabel.numberOfLines = 0;
 
diff --git a/ios/chrome/browser/ui/first_run/welcome_to_chrome_view.mm b/ios/chrome/browser/ui/first_run/welcome_to_chrome_view.mm
index 04678d7..81dca09 100644
--- a/ios/chrome/browser/ui/first_run/welcome_to_chrome_view.mm
+++ b/ios/chrome/browser/ui/first_run/welcome_to_chrome_view.mm
@@ -593,8 +593,9 @@
 }
 
 - (void)configureTitleLabel {
-  self.titleLabel.font = [[MDCTypography fontLoader]
-      regularFontOfSize:kTitleLabelFontSize[[self widthSizeClassIdiom]]];
+  self.titleLabel.font =
+      [UIFont systemFontOfSize:kTitleLabelFontSize[[self widthSizeClassIdiom]]
+                        weight:UIFontWeightRegular];
 }
 
 - (void)configureImageView {
@@ -626,8 +627,9 @@
   NSRange fullRange = NSMakeRange(0, parsedString.string.length);
   NSURL* URL =
       [NSURL URLWithString:base::SysUTF8ToNSString(kTermsOfServiceUrl)];
-  UIFont* font = [[MDCTypography fontLoader]
-      regularFontOfSize:kTOSTOSTextViewFontSize[[self widthSizeClassIdiom]]];
+  UIFont* font = [UIFont
+      systemFontOfSize:kTOSTOSTextViewFontSize[[self widthSizeClassIdiom]]
+                weight:UIFontWeightRegular];
   NSMutableParagraphStyle* style =
       [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
   style.alignment = NSTextAlignmentCenter;
@@ -648,15 +650,17 @@
 }
 
 - (void)configureOptInLabel {
-  self.optInLabel.font = [[MDCTypography fontLoader]
-      regularFontOfSize:kOptInLabelFontSize[[self widthSizeClassIdiom]]];
+  self.optInLabel.font =
+      [UIFont systemFontOfSize:kOptInLabelFontSize[[self widthSizeClassIdiom]]
+                        weight:UIFontWeightRegular];
   SetLabelLineHeight(self.optInLabel,
                      kOptInLabelLineHeight[[self widthSizeClassIdiom]]);
 }
 
 - (void)configureManagedLabel {
-  self.managedLabel.font = [[MDCTypography fontLoader]
-      regularFontOfSize:kManagedLabelFontSize[[self widthSizeClassIdiom]]];
+  self.managedLabel.font =
+      [UIFont systemFontOfSize:kManagedLabelFontSize[[self widthSizeClassIdiom]]
+                        weight:UIFontWeightRegular];
   self.managedLabel.textColor = [UIColor colorNamed:kTextSecondaryColor];
   SetLabelLineHeight(self.managedLabel,
                      kManagedLabelLineHeight[[self widthSizeClassIdiom]]);
@@ -672,8 +676,9 @@
 }
 
 - (void)configureOKButton {
-  UIFont* font = [[MDCTypography fontLoader]
-      mediumFontOfSize:kOKButtonTitleLabelFontSize[[self widthSizeClassIdiom]]];
+  UIFont* font = [UIFont
+      systemFontOfSize:kOKButtonTitleLabelFontSize[[self widthSizeClassIdiom]]
+                weight:UIFontWeightMedium];
   [self.OKButton setTitleFont:font forState:UIControlStateNormal];
   CGSize size = [self.OKButton
       sizeThatFits:CGSizeMake(CGFLOAT_MAX,
diff --git a/ios/chrome/browser/ui/sad_tab/sad_tab_view.mm b/ios/chrome/browser/ui/sad_tab/sad_tab_view.mm
index f235255b..cbbf91e 100644
--- a/ios/chrome/browser/ui/sad_tab/sad_tab_view.mm
+++ b/ios/chrome/browser/ui/sad_tab/sad_tab_view.mm
@@ -310,8 +310,8 @@
     [_titleLabel setLineBreakMode:NSLineBreakByWordWrapping];
     [_titleLabel setNumberOfLines:0];
     [_titleLabel setTextColor:[UIColor colorNamed:kTextPrimaryColor]];
-    [_titleLabel setFont:[[MDCTypography fontLoader]
-                             regularFontOfSize:kTitleLabelFontSize]];
+    [_titleLabel setFont:[UIFont systemFontOfSize:kTitleLabelFontSize
+                                           weight:UIFontWeightRegular]];
   }
   return _titleLabel;
 }
@@ -324,8 +324,8 @@
 
     // Set base text styling for footer.
     NSDictionary<NSAttributedStringKey, id>* footerAttributes = @{
-      NSFontAttributeName :
-          [[MDCTypography fontLoader] regularFontOfSize:kFooterLabelFontSize],
+      NSFontAttributeName : [UIFont systemFontOfSize:kFooterLabelFontSize
+                                              weight:UIFontWeightRegular],
       NSForegroundColorAttributeName : [UIColor colorNamed:kTextSecondaryColor],
     };
     NSMutableAttributedString* footerText =
@@ -540,8 +540,8 @@
     [_messageTextView setAttributedText:[self messageTextViewAttributedText]];
     _messageTextView.textContainer.lineFragmentPadding = 0.0f;
     [_messageTextView setTextColor:[UIColor colorNamed:kTextSecondaryColor]];
-    [_messageTextView setFont:[[MDCTypography fontLoader]
-                                  regularFontOfSize:kMessageTextViewFontSize]];
+    [_messageTextView setFont:[UIFont systemFontOfSize:kMessageTextViewFontSize
+                                                weight:UIFontWeightRegular]];
     [_messageTextView setUserInteractionEnabled:NO];
   }
   return _messageTextView;
diff --git a/ios/web/web_state/web_state_impl_unittest.mm b/ios/web/web_state/web_state_impl_unittest.mm
index 787825e..4a9d665 100644
--- a/ios/web/web_state/web_state_impl_unittest.mm
+++ b/ios/web/web_state/web_state_impl_unittest.mm
@@ -148,7 +148,7 @@
                          bool user_is_interacting,
                          web::WebFrame* sender_frame) {
   *is_called = true;
-  EXPECT_TRUE(expected_value->Equals(&value));
+  EXPECT_EQ(*expected_value, value);
   EXPECT_EQ(expected_url, url);
   EXPECT_EQ(expected_user_is_interacting, user_is_interacting);
   EXPECT_EQ(expected_sender_frame, sender_frame);
diff --git a/net/cert/internal/trust_store_chrome.cc b/net/cert/internal/trust_store_chrome.cc
index 6b824d6..926c1207 100644
--- a/net/cert/internal/trust_store_chrome.cc
+++ b/net/cert/internal/trust_store_chrome.cc
@@ -73,12 +73,10 @@
   for (const auto& cert_info : certs) {
     bssl::UniquePtr<CRYPTO_BUFFER> cert;
     if (certs_are_static) {
-      // TODO(mattm,hchao): Ensure the static data crypto_buffers for the
-      // compiled-in roots are kept alive, so that roots from the component
-      // updater data will de-dupe against them. This currently works if the
-      // new components roots are the same as the compiled in roots, but
-      // fails if a component update drops a root and then the next component
-      // update readds the root without a restart.
+      // TODO(mattm,hchao): When the component updater is implemented, ensure
+      // the static data crypto_buffers for the compiled-in roots are kept
+      // alive, so that roots from the component updater data will de-dupe
+      // against them.
       cert = x509_util::CreateCryptoBufferFromStaticDataUnsafe(
           cert_info.root_cert_der);
     } else {
@@ -88,6 +86,8 @@
     auto parsed = ParsedCertificate::Create(
         std::move(cert), x509_util::DefaultParseCertificateOptions(), &errors);
     DCHECK(parsed);
+    // TODO(hchao): Figure out how to fail gracefully when the Chrome Root Store
+    // gets a bad component update.
     trust_store_.AddTrustAnchor(parsed);
   }
   version_ = version;
@@ -130,20 +130,4 @@
   return kRootStoreVersion;
 }
 
-ParsedCertificateList CompiledChromeRootStoreAnchors() {
-  ParsedCertificateList parsed_cert_list;
-  for (const auto& cert_info : kChromeRootCertList) {
-    bssl::UniquePtr<CRYPTO_BUFFER> cert =
-        x509_util::CreateCryptoBufferFromStaticDataUnsafe(
-            cert_info.root_cert_der);
-    CertErrors errors;
-    auto parsed = ParsedCertificate::Create(
-        std::move(cert), x509_util::DefaultParseCertificateOptions(), &errors);
-    DCHECK(parsed);
-    parsed_cert_list.push_back(parsed);
-  }
-
-  return parsed_cert_list;
-}
-
 }  // namespace net
diff --git a/net/cert/internal/trust_store_chrome.h b/net/cert/internal/trust_store_chrome.h
index 48835f8..5f59e2c 100644
--- a/net/cert/internal/trust_store_chrome.h
+++ b/net/cert/internal/trust_store_chrome.h
@@ -89,10 +89,6 @@
 // binary.
 NET_EXPORT int64_t CompiledChromeRootStoreVersion();
 
-// Returns the anchors of the Chrome Root Store that were compiled into the
-// binary.
-NET_EXPORT ParsedCertificateList CompiledChromeRootStoreAnchors();
-
 }  // namespace net
 
 #endif  // NET_CERT_INTERNAL_TRUST_STORE_CHROME_H_
diff --git a/net/disk_cache/blockfile/backend_impl.cc b/net/disk_cache/blockfile/backend_impl.cc
index 4b13920..25d81dc 100644
--- a/net/disk_cache/blockfile/backend_impl.cc
+++ b/net/disk_cache/blockfile/backend_impl.cc
@@ -2111,18 +2111,17 @@
 }
 
 int BackendImpl::MaxBuffersSize() {
-  static int64_t total_memory = base::SysInfo::AmountOfPhysicalMemory();
+  static uint64_t total_memory = base::SysInfo::AmountOfPhysicalMemory();
   static bool done = false;
 
   if (!done) {
-    const int kMaxBuffersSize = 30 * 1024 * 1024;
-
-    // We want to use up to 2% of the computer's memory.
-    total_memory = total_memory * 2 / 100;
-    if (total_memory > kMaxBuffersSize || total_memory <= 0)
-      total_memory = kMaxBuffersSize;
-
     done = true;
+
+    // We want to use up to 2% of the computer's memory, limit 30 MB.
+    total_memory = total_memory * 2 / 100;
+    constexpr uint64_t kMaxBuffersSize = 30 * 1024 * 1024;
+    if (total_memory > kMaxBuffersSize || total_memory == 0)
+      total_memory = kMaxBuffersSize;
   }
 
   return static_cast<int>(total_memory);
diff --git a/net/disk_cache/memory/mem_backend_impl.cc b/net/disk_cache/memory/mem_backend_impl.cc
index 951427d..ab9f041 100644
--- a/net/disk_cache/memory/mem_backend_impl.cc
+++ b/net/disk_cache/memory/mem_backend_impl.cc
@@ -76,9 +76,9 @@
   if (max_size_)
     return true;
 
-  int64_t total_memory = base::SysInfo::AmountOfPhysicalMemory();
+  uint64_t total_memory = base::SysInfo::AmountOfPhysicalMemory();
 
-  if (total_memory <= 0) {
+  if (total_memory == 0) {
     max_size_ = kDefaultInMemoryCacheSize;
     return true;
   }
@@ -86,7 +86,7 @@
   // We want to use up to 2% of the computer's memory, with a limit of 50 MB,
   // reached on system with more than 2.5 GB of RAM.
   total_memory = total_memory * 2 / 100;
-  if (total_memory > kDefaultInMemoryCacheSize * 5)
+  if (total_memory > static_cast<uint64_t>(kDefaultInMemoryCacheSize) * 5)
     max_size_ = kDefaultInMemoryCacheSize * 5;
   else
     max_size_ = static_cast<int32_t>(total_memory);
diff --git a/sandbox/policy/win/sandbox_win.cc b/sandbox/policy/win/sandbox_win.cc
index f7b0de8..f83aa27 100644
--- a/sandbox/policy/win/sandbox_win.cc
+++ b/sandbox/policy/win/sandbox_win.cc
@@ -543,13 +543,13 @@
   size_t memory_limit = static_cast<size_t>(kDataSizeLimit);
 
   if (sandbox_type == Sandbox::kGpu || sandbox_type == Sandbox::kRenderer) {
-    int64_t GB = 1024 * 1024 * 1024;
+    constexpr uint64_t GB = 1024 * 1024 * 1024;
     // Allow the GPU/RENDERER process's sandbox to access more physical memory
     // if it's available on the system.
     //
     // Renderer processes are allowed to access 16 GB; the GPU process, up
     // to 64 GB.
-    int64_t physical_memory = base::SysInfo::AmountOfPhysicalMemory();
+    uint64_t physical_memory = base::SysInfo::AmountOfPhysicalMemory();
     if (sandbox_type == Sandbox::kGpu && physical_memory > 64 * GB) {
       memory_limit = 64 * GB;
     } else if (sandbox_type == Sandbox::kGpu && physical_memory > 32 * GB) {
diff --git a/services/cert_verifier/cert_verifier_service_factory.cc b/services/cert_verifier/cert_verifier_service_factory.cc
index b2e5a49..3608393 100644
--- a/services/cert_verifier/cert_verifier_service_factory.cc
+++ b/services/cert_verifier/cert_verifier_service_factory.cc
@@ -26,8 +26,6 @@
 
 #if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
 #include "mojo/public/cpp/base/big_buffer.h"
-#include "net/cert/internal/parse_name.h"
-#include "net/cert/internal/parsed_certificate.h"
 #include "net/cert/internal/trust_store_chrome.h"
 #include "net/cert/root_store_proto_lite/root_store.pb.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -74,26 +72,6 @@
                                                std::move(cert_net_fetcher));
 }
 
-#if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
-std::string GetName(scoped_refptr<net::ParsedCertificate> cert) {
-  net::RDNSequence subject_rdn;
-  if (!net::ParseName(cert->subject_tlv(), &subject_rdn)) {
-    return "UNKNOWN";
-  }
-  std::string subject_string;
-  if (!net::ConvertToRFC2253(subject_rdn, &subject_string)) {
-    return "UNKNOWN";
-  }
-  return subject_string;
-}
-
-std::string GetHash(scoped_refptr<net::ParsedCertificate> cert) {
-  net::SHA256HashValue hash =
-      net::X509Certificate::CalculateFingerprint256(cert->cert_buffer());
-  return base::HexEncode(hash.data, std::size(hash.data));
-}
-#endif
-
 }  // namespace
 
 CertVerifierServiceFactoryImpl::CertVerifierServiceFactoryImpl(
@@ -170,25 +148,6 @@
   // instances will start with the updated store.
   root_store_data_ = std::move(root_store_data);
 }
-
-void CertVerifierServiceFactoryImpl::GetChromeRootStoreInfo(
-    GetChromeRootStoreInfoCallback callback) {
-  mojom::ChromeRootStoreInfoPtr info_ptr = mojom::ChromeRootStoreInfo::New();
-  if (root_store_data_) {
-    info_ptr->version = root_store_data_->version();
-    for (auto cert : root_store_data_->anchors()) {
-      info_ptr->root_cert_info.push_back(
-          mojom::ChromeRootCertInfo::New(GetName(cert), GetHash(cert)));
-    }
-  } else {
-    info_ptr->version = net::CompiledChromeRootStoreVersion();
-    for (auto cert : net::CompiledChromeRootStoreAnchors()) {
-      info_ptr->root_cert_info.push_back(
-          mojom::ChromeRootCertInfo::New(GetName(cert), GetHash(cert)));
-    }
-  }
-  std::move(callback).Run(std::move(info_ptr));
-}
 #endif
 
 void CertVerifierServiceFactoryImpl::RemoveService(
diff --git a/services/cert_verifier/cert_verifier_service_factory.h b/services/cert_verifier/cert_verifier_service_factory.h
index ccbe6f1..c1e582fa 100644
--- a/services/cert_verifier/cert_verifier_service_factory.h
+++ b/services/cert_verifier/cert_verifier_service_factory.h
@@ -51,7 +51,6 @@
 #if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
   // mojom::CertVerifierServiceFactory implementation:
   void UpdateChromeRootStore(mojom::ChromeRootStorePtr new_root_store) override;
-  void GetChromeRootStoreInfo(GetChromeRootStoreInfoCallback callback) override;
 #endif
 
   // Remove a CertVerifyService from needing updates to the Chrome Root Store.
diff --git a/services/cert_verifier/cert_verifier_service_factory_unittest.cc b/services/cert_verifier/cert_verifier_service_factory_unittest.cc
index 4f0f328..3a971d1 100644
--- a/services/cert_verifier/cert_verifier_service_factory_unittest.cc
+++ b/services/cert_verifier/cert_verifier_service_factory_unittest.cc
@@ -33,10 +33,8 @@
 
 #if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
 #include "mojo/public/cpp/base/big_buffer.h"
-#include "net/cert/internal/parse_name.h"
 #include "net/cert/internal/trust_store_chrome.h"
 #include "net/cert/root_store_proto_lite/root_store.pb.h"
-#include "net/der/input.h"
 #endif
 
 namespace cert_verifier {
@@ -448,80 +446,6 @@
   }
 }
 
-void GetRootStoreInfo(cert_verifier::mojom::ChromeRootStoreInfoPtr* return_ptr,
-                      base::RepeatingClosure quit_closure,
-                      cert_verifier::mojom::ChromeRootStoreInfoPtr info) {
-  *return_ptr = std::move(info);
-  quit_closure.Run();
-}
-
-TEST(CertVerifierServiceFactoryTest, RootStoreInfoWithUpdatedRootStore) {
-  // Create leaf and root certs.
-  base::test::TaskEnvironment task_environment;
-  std::unique_ptr<net::CertBuilder> leaf, root;
-  net::CertBuilder::CreateSimpleChain(&leaf, &root);
-
-  base::Time now = base::Time::Now();
-  leaf->SetValidity(now - base::Days(1), now + base::Days(1));
-
-  // Create updated Chrome Root Store with just the root cert from above.
-  chrome_root_store::RootStore root_store_proto;
-  root_store_proto.set_version_major(net::CompiledChromeRootStoreVersion() + 1);
-  chrome_root_store::TrustAnchor* anchor = root_store_proto.add_trust_anchors();
-  anchor->set_der(root->GetDER());
-  std::string proto_serialized;
-  root_store_proto.SerializeToString(&proto_serialized);
-  cert_verifier::mojom::ChromeRootStorePtr root_store_ptr =
-      cert_verifier::mojom::ChromeRootStore::New(
-          base::as_bytes(base::make_span(proto_serialized)));
-
-  mojo::Remote<mojom::CertVerifierServiceFactory> cv_service_factory_remote;
-  CertVerifierServiceFactoryImpl cv_service_factory_impl(
-      cv_service_factory_remote.BindNewPipeAndPassReceiver());
-
-  // Feed factory the new Chrome Root Store.
-  cv_service_factory_impl.UpdateChromeRootStore(std::move(root_store_ptr));
-
-  cert_verifier::mojom::ChromeRootStoreInfoPtr info_ptr;
-  base::RunLoop request_completed_run_loop;
-  cv_service_factory_remote->GetChromeRootStoreInfo(base::BindOnce(
-      &GetRootStoreInfo, &info_ptr, request_completed_run_loop.QuitClosure()));
-  request_completed_run_loop.Run();
-  ASSERT_TRUE(info_ptr);
-  EXPECT_EQ(info_ptr->version, root_store_proto.version_major());
-  ASSERT_EQ(info_ptr->root_cert_info.size(), static_cast<std::size_t>(1));
-
-  net::der::Input subject_tlv(&root->GetSubject());
-  net::RDNSequence subject_rdn;
-  DCHECK(net::ParseName(subject_tlv, &subject_rdn));
-  std::string subject_string;
-  DCHECK(net::ConvertToRFC2253(subject_rdn, &subject_string));
-  EXPECT_EQ(info_ptr->root_cert_info[0]->name, subject_string);
-
-  net::SHA256HashValue root_hash =
-      net::X509Certificate::CalculateFingerprint256(root->GetCertBuffer());
-  EXPECT_EQ(info_ptr->root_cert_info[0]->sha256hash_hex,
-            base::HexEncode(root_hash.data, std::size(root_hash.data)));
-}
-
-TEST(CertVerifierServiceFactoryTest, RootStoreInfoWithCompiledRootStore) {
-  base::test::TaskEnvironment task_environment;
-  net::ParsedCertificateList anchors = net::CompiledChromeRootStoreAnchors();
-
-  mojo::Remote<mojom::CertVerifierServiceFactory> cv_service_factory_remote;
-  CertVerifierServiceFactoryImpl cv_service_factory_impl(
-      cv_service_factory_remote.BindNewPipeAndPassReceiver());
-  cert_verifier::mojom::ChromeRootStoreInfoPtr info_ptr;
-  base::RunLoop request_completed_run_loop;
-  cv_service_factory_remote->GetChromeRootStoreInfo(base::BindOnce(
-      &GetRootStoreInfo, &info_ptr, request_completed_run_loop.QuitClosure()));
-  request_completed_run_loop.Run();
-
-  ASSERT_TRUE(info_ptr);
-  EXPECT_EQ(info_ptr->version, net::CompiledChromeRootStoreVersion());
-  EXPECT_EQ(info_ptr->root_cert_info.size(), anchors.size());
-}
-
 #endif
 
 }  // namespace cert_verifier
diff --git a/services/cert_verifier/public/mojom/cert_verifier_service_factory.mojom b/services/cert_verifier/public/mojom/cert_verifier_service_factory.mojom
index 38a218a..8cc5e7c8 100644
--- a/services/cert_verifier/public/mojom/cert_verifier_service_factory.mojom
+++ b/services/cert_verifier/public/mojom/cert_verifier_service_factory.mojom
@@ -75,19 +75,6 @@
   mojo_base.mojom.BigBuffer serialized_proto_root_store;
 };
 
-// Information about a certificate in the Chrome Root Store
-struct ChromeRootCertInfo {
-  // Human-readable name for the certificate.
-  string name;
-  string sha256hash_hex;
-};
-
-// Information about the Chrome Root Store
-struct ChromeRootStoreInfo {
-  int64 version;
-  array<ChromeRootCertInfo> root_cert_info;
-};
-
 // Parent interface for the CertVerifierProcess. Hands out new
 // CertVerifierService's, which have their own underlying CertVerifier's
 // underneath.
@@ -101,8 +88,4 @@
   // new version.
   [EnableIf=is_chrome_root_store_supported]
   UpdateChromeRootStore(ChromeRootStore new_root_store);
-
-  // Returns information about the current Chrome Root Store.
-  [EnableIf=is_chrome_root_store_supported]
-  GetChromeRootStoreInfo() => (ChromeRootStoreInfo root_store_info);
 };
diff --git a/services/cert_verifier/test_cert_verifier_service_factory.cc b/services/cert_verifier/test_cert_verifier_service_factory.cc
index 139d98f..363f6bda1 100644
--- a/services/cert_verifier/test_cert_verifier_service_factory.cc
+++ b/services/cert_verifier/test_cert_verifier_service_factory.cc
@@ -48,13 +48,6 @@
 #if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
 void TestCertVerifierServiceFactoryImpl::UpdateChromeRootStore(
     mojom::ChromeRootStorePtr new_root_store) {}
-
-void TestCertVerifierServiceFactoryImpl::GetChromeRootStoreInfo(
-    GetChromeRootStoreInfoCallback callback) {
-  mojom::ChromeRootStoreInfoPtr info_ptr = mojom::ChromeRootStoreInfo::New();
-  info_ptr->version = 42;
-  std::move(callback).Run(std::move(info_ptr));
-}
 #endif
 
 void TestCertVerifierServiceFactoryImpl::ReleaseAllCertVerifierParams() {
diff --git a/services/cert_verifier/test_cert_verifier_service_factory.h b/services/cert_verifier/test_cert_verifier_service_factory.h
index 6ff8a30..f256214 100644
--- a/services/cert_verifier/test_cert_verifier_service_factory.h
+++ b/services/cert_verifier/test_cert_verifier_service_factory.h
@@ -48,7 +48,6 @@
 
 #if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
   void UpdateChromeRootStore(mojom::ChromeRootStorePtr new_root_store) override;
-  void GetChromeRootStoreInfo(GetChromeRootStoreInfoCallback callback) override;
 #endif
 
   // Pops the first request off the back of the list and forwards it to the
diff --git a/services/data_decoder/public/cpp/data_decoder.cc b/services/data_decoder/public/cpp/data_decoder.cc
index 325cd8e7..e5f42e0b4 100644
--- a/services/data_decoder/public/cpp/data_decoder.cc
+++ b/services/data_decoder/public/cpp/data_decoder.cc
@@ -72,11 +72,11 @@
     if (!callback() || is_cancelled_->data)
       return;
 
-    DataDecoder::ResultOrError<V> result;
+    base::expected<V, std::string> result;
     if (value)
-      result.value = std::move(value);
+      result = std::move(*value);
     else
-      result.error = error.value_or("unknown error");
+      result = base::unexpected(error.value_or("unknown error"));
 
     // Copy the callback onto the stack before resetting the Remote, as that may
     // delete |this|.
@@ -102,8 +102,7 @@
 
     if (callback()) {
       std::move(callback())
-          .Run(DataDecoder::ResultOrError<V>::Error(
-              "Data Decoder terminated unexpectedly"));
+          .Run(base::unexpected("Data Decoder terminated unexpectedly"));
     }
   }
 
@@ -138,11 +137,9 @@
     return;
 
   if (!value_with_error.has_value()) {
-    std::move(callback).Run(
-        DataDecoder::ValueOrError::Error(value_with_error.error().message));
+    std::move(callback).Run(base::unexpected(value_with_error.error().message));
   } else {
-    std::move(callback).Run(
-        DataDecoder::ValueOrError::Value(std::move(*value_with_error)));
+    std::move(callback).Run(std::move(*value_with_error));
   }
 }
 
@@ -209,7 +206,7 @@
                     return;
 
                   if (!result.value) {
-                    std::move(callback).Run(ValueOrError::Error(*result.error));
+                    std::move(callback).Run(base::unexpected(*result.error));
                     return;
                   }
 
diff --git a/services/data_decoder/public/cpp/data_decoder.h b/services/data_decoder/public/cpp/data_decoder.h
index cea5b7f8..e5b83f1 100644
--- a/services/data_decoder/public/cpp/data_decoder.h
+++ b/services/data_decoder/public/cpp/data_decoder.h
@@ -10,6 +10,7 @@
 #include "base/callback_forward.h"
 #include "base/memory/ref_counted.h"
 #include "base/time/time.h"
+#include "base/types/expected.h"
 #include "base/values.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
@@ -58,38 +59,12 @@
 
   ~DataDecoder();
 
-  // The result of a service call that can return either a value of type T or an
-  // error string. Exactly one of either |value| or |error| will have a value
-  // when returned by either operation.
+  using ValueOrError = base::expected<base::Value, std::string>;
   template <typename T>
-  struct ResultOrError {
-    ResultOrError() = default;
-    ResultOrError(ResultOrError&&) = default;
-    ~ResultOrError() = default;
-
-    static ResultOrError Value(T value) {
-      ResultOrError<T> result;
-      result.value = std::move(value);
-      return result;
-    }
-    static ResultOrError Error(const std::string& error) {
-      ResultOrError<T> result;
-      result.error = error;
-      return result;
-    }
-
-    absl::optional<T> value;
-    absl::optional<std::string> error;
-  };
-
-  using ValueOrError = ResultOrError<base::Value>;
-
-  template <typename T>
-  using ResultCallback = base::OnceCallback<void(ResultOrError<T>)>;
-  using ValueParseCallback = base::OnceCallback<void(ValueOrError)>;
-  using GzipperCallback =
-      base::OnceCallback<void(ResultOrError<mojo_base::BigBuffer>)>;
-
+  using ResultCallback =
+      base::OnceCallback<void(base::expected<T, std::string>)>;
+  using ValueParseCallback = ResultCallback<base::Value>;
+  using GzipperCallback = ResultCallback<mojo_base::BigBuffer>;
   using CancellationFlag = base::RefCountedData<bool>;
 
   // Returns a raw interface to the service instance. This launches an instance
diff --git a/services/data_decoder/public/cpp/data_decoder_unittest.cc b/services/data_decoder/public/cpp/data_decoder_unittest.cc
index 0fc6dcf5..e0de610 100644
--- a/services/data_decoder/public/cpp/data_decoder_unittest.cc
+++ b/services/data_decoder/public/cpp/data_decoder_unittest.cc
@@ -79,18 +79,18 @@
   // a process.
   base::RunLoop run_loop;
   DataDecoder decoder;
-  absl::optional<base::Value> result;
+  DataDecoder::ValueOrError result;
   decoder.ParseJson(
       // The magic 122.416294033786585 number comes from
       // https://github.com/serde-rs/json/issues/707
       "[ 122.416294033786585 ]",
       base::BindLambdaForTesting(
           [&run_loop, &result](DataDecoder::ValueOrError value_or_error) {
-            result = std::move(value_or_error.value);
+            result = std::move(value_or_error);
             run_loop.Quit();
           }));
   run_loop.Run();
-  EXPECT_TRUE(result);
+  ASSERT_TRUE(result.has_value());
   ASSERT_TRUE(result->is_list());
   base::Value::List& list = result->GetList();
   ASSERT_EQ(1u, list.size());
diff --git a/services/data_decoder/public/cpp/json_sanitizer_non_android.cc b/services/data_decoder/public/cpp/json_sanitizer_non_android.cc
index 5e39858..d686f76 100644
--- a/services/data_decoder/public/cpp/json_sanitizer_non_android.cc
+++ b/services/data_decoder/public/cpp/json_sanitizer_non_android.cc
@@ -19,12 +19,12 @@
       json,
       base::BindOnce(
           [](Callback callback, DataDecoder::ValueOrError parse_result) {
-            if (!parse_result.value) {
-              std::move(callback).Run(Result::Error(*parse_result.error));
+            if (!parse_result.has_value()) {
+              std::move(callback).Run(Result::Error(parse_result.error()));
               return;
             }
 
-            const base::Value::Type type = parse_result.value->type();
+            const base::Value::Type type = parse_result->type();
             if (type != base::Value::Type::DICTIONARY &&
                 type != base::Value::Type::LIST) {
               std::move(callback).Run(Result::Error("Invalid top-level type"));
@@ -32,7 +32,7 @@
             }
 
             std::string safe_json;
-            if (!base::JSONWriter::Write(*parse_result.value, &safe_json)) {
+            if (!base::JSONWriter::Write(*parse_result, &safe_json)) {
               std::move(callback).Run(Result::Error("Encoding error"));
               return;
             }
diff --git a/services/device/serial/bluetooth_serial_device_enumerator.cc b/services/device/serial/bluetooth_serial_device_enumerator.cc
index 272385a..195d67d 100644
--- a/services/device/serial/bluetooth_serial_device_enumerator.cc
+++ b/services/device/serial/bluetooth_serial_device_enumerator.cc
@@ -51,7 +51,7 @@
 
 void BluetoothSerialDeviceEnumerator::AdapterHelper::OnGotClassicAdapter(
     scoped_refptr<device::BluetoothAdapter> adapter) {
-  SEQUENCE_CHECKER(sequence_checker_);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(adapter);
 
   BluetoothAdapter::DeviceList devices = adapter->GetDevices();
diff --git a/services/network/proxy_resolving_client_socket_factory.h b/services/network/proxy_resolving_client_socket_factory.h
index e8af271..fa53ddf2 100644
--- a/services/network/proxy_resolving_client_socket_factory.h
+++ b/services/network/proxy_resolving_client_socket_factory.h
@@ -44,8 +44,8 @@
   // established to. The full URL will be only used for proxy resolution. Caller
   // doesn't need to explicitly sanitize the url, any sensitive data (like
   // embedded usernames and passwords), and local data (i.e. reference fragment)
-  // will be sanitized by net::ProxyService::ResolveProxyHelper() before the url
-  // is disclosed to the proxy.
+  // will be sanitized by net::ProxyResolutionService before the url is
+  // disclosed to the PAC script.
   //
   // |network_isolation_key| indicates the network shard to use for storing
   // shared network state (DNS cache entries, shared H2/QUIC proxy connections,
diff --git a/skia/ext/benchmarking_canvas.cc b/skia/ext/benchmarking_canvas.cc
index 4aecb9a9..42c6928af 100644
--- a/skia/ext/benchmarking_canvas.cc
+++ b/skia/ext/benchmarking_canvas.cc
@@ -12,6 +12,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/memory/raw_ptr.h"
 #include "base/time/time.h"
+#include "base/values.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "third_party/skia/include/core/SkColorFilter.h"
 #include "third_party/skia/include/core/SkImage.h"
@@ -49,155 +50,137 @@
   std::ostringstream oss_;
 };
 
-std::unique_ptr<base::Value> AsValue(bool b) {
-  std::unique_ptr<base::Value> val(new base::Value(b));
-
-  return val;
+base::Value AsValue(bool b) {
+  return base::Value(b);
 }
 
-std::unique_ptr<base::Value> AsValue(SkScalar scalar) {
-  std::unique_ptr<base::Value> val(new base::Value(scalar));
-
-  return val;
+base::Value AsValue(SkScalar scalar) {
+  return base::Value(scalar);
 }
 
-std::unique_ptr<base::Value> AsValue(const SkSize& size) {
-  std::unique_ptr<base::DictionaryValue> val(new base::DictionaryValue());
-  val->SetKey("width", base::Value::FromUniquePtrValue(AsValue(size.width())));
-  val->SetKey("height",
-              base::Value::FromUniquePtrValue(AsValue(size.height())));
+base::Value AsValue(const SkSize& size) {
+  base::Value::Dict val;
+  val.Set("width", AsValue(size.width()));
+  val.Set("height", AsValue(size.height()));
 
-  return std::move(val);
+  return base::Value(std::move(val));
 }
 
-std::unique_ptr<base::Value> AsValue(const SkPoint& point) {
-  std::unique_ptr<base::DictionaryValue> val(new base::DictionaryValue());
-  val->SetKey("x", base::Value::FromUniquePtrValue(AsValue(point.x())));
-  val->SetKey("y", base::Value::FromUniquePtrValue(AsValue(point.y())));
+base::Value AsValue(const SkPoint& point) {
+  base::Value::Dict val;
+  val.Set("x", AsValue(point.x()));
+  val.Set("y", AsValue(point.y()));
 
-  return std::move(val);
+  return base::Value(std::move(val));
 }
 
-std::unique_ptr<base::Value> AsValue(const SkRect& rect) {
-  std::unique_ptr<base::DictionaryValue> val(new base::DictionaryValue());
-  val->SetKey("left", base::Value::FromUniquePtrValue(AsValue(rect.fLeft)));
-  val->SetKey("top", base::Value::FromUniquePtrValue(AsValue(rect.fTop)));
-  val->SetKey("right", base::Value::FromUniquePtrValue(AsValue(rect.fRight)));
-  val->SetKey("bottom", base::Value::FromUniquePtrValue(AsValue(rect.fBottom)));
+base::Value AsValue(const SkRect& rect) {
+  base::Value::Dict val;
+  val.Set("left", AsValue(rect.fLeft));
+  val.Set("top", AsValue(rect.fTop));
+  val.Set("right", AsValue(rect.fRight));
+  val.Set("bottom", AsValue(rect.fBottom));
 
-  return std::move(val);
+  return base::Value(std::move(val));
 }
 
-std::unique_ptr<base::Value> AsValue(const SkRRect& rrect) {
-  base::Value radii_val(base::Value::Type::DICTIONARY);
-  radii_val.SetKey("upper-left", base::Value::FromUniquePtrValue(AsValue(
-                                     rrect.radii(SkRRect::kUpperLeft_Corner))));
-  radii_val.SetKey("upper-right",
-                   base::Value::FromUniquePtrValue(
-                       AsValue(rrect.radii(SkRRect::kUpperRight_Corner))));
-  radii_val.SetKey("lower-right",
-                   base::Value::FromUniquePtrValue(
-                       AsValue(rrect.radii(SkRRect::kLowerRight_Corner))));
-  radii_val.SetKey("lower-left", base::Value::FromUniquePtrValue(AsValue(
-                                     rrect.radii(SkRRect::kLowerLeft_Corner))));
+base::Value AsValue(const SkRRect& rrect) {
+  base::Value::Dict radii_val;
+  radii_val.Set("upper-left", AsValue(rrect.radii(SkRRect::kUpperLeft_Corner)));
+  radii_val.Set("upper-right",
+                AsValue(rrect.radii(SkRRect::kUpperRight_Corner)));
+  radii_val.Set("lower-right",
+                AsValue(rrect.radii(SkRRect::kLowerRight_Corner)));
+  radii_val.Set("lower-left", AsValue(rrect.radii(SkRRect::kLowerLeft_Corner)));
 
-  std::unique_ptr<base::DictionaryValue> val(new base::DictionaryValue());
-  val->SetKey("rect", base::Value::FromUniquePtrValue(AsValue(rrect.rect())));
-  val->SetKey("radii", std::move(radii_val));
+  base::Value::Dict val;
+  val.Set("rect", AsValue(rrect.rect()));
+  val.Set("radii", std::move(radii_val));
 
-  return std::move(val);
+  return base::Value(std::move(val));
 }
 
-std::unique_ptr<base::Value> AsValue(const SkMatrix& matrix) {
-  std::unique_ptr<base::ListValue> val(new base::ListValue());
+base::Value AsValue(const SkMatrix& matrix) {
+  base::Value::List val;
   for (int i = 0; i < 9; ++i)
-    val->Append(base::Value::FromUniquePtrValue(AsValue(matrix[i])));
+    val.Append(AsValue(matrix[i]));
 
-  return std::move(val);
+  return base::Value(std::move(val));
 }
 
-std::unique_ptr<base::Value> AsValue(SkColor color) {
-  std::unique_ptr<base::DictionaryValue> val(new base::DictionaryValue());
-  val->SetInteger("a", SkColorGetA(color));
-  val->SetInteger("r", SkColorGetR(color));
-  val->SetInteger("g", SkColorGetG(color));
-  val->SetInteger("b", SkColorGetB(color));
+base::Value AsValue(SkColor color) {
+  base::Value::Dict val;
+  val.Set("a", int{SkColorGetA(color)});
+  val.Set("r", int{SkColorGetR(color)});
+  val.Set("g", int{SkColorGetG(color)});
+  val.Set("b", int{SkColorGetB(color)});
 
-  return std::move(val);
+  return base::Value(std::move(val));
 }
 
-std::unique_ptr<base::Value> AsValue(SkBlendMode mode) {
-  std::unique_ptr<base::Value> val(new base::Value(SkBlendMode_Name(mode)));
-
-  return val;
+base::Value AsValue(SkBlendMode mode) {
+  return base::Value(SkBlendMode_Name(mode));
 }
 
-std::unique_ptr<base::Value> AsValue(SkCanvas::PointMode mode) {
+base::Value AsValue(SkCanvas::PointMode mode) {
   static const char* gModeStrings[] = { "Points", "Lines", "Polygon" };
   DCHECK_LT(static_cast<size_t>(mode), SK_ARRAY_COUNT(gModeStrings));
 
-  std::unique_ptr<base::Value> val(new base::Value(gModeStrings[mode]));
-
-  return val;
+  return base::Value(gModeStrings[mode]);
 }
 
-std::unique_ptr<base::Value> AsValue(const SkColorFilter& filter) {
-  std::unique_ptr<base::DictionaryValue> val(new base::DictionaryValue());
+base::Value AsValue(const SkColorFilter& filter) {
+  base::Value::Dict val;
 
   if (filter.isAlphaUnchanged()) {
     FlagsBuilder builder('|');
     builder.addFlag(true, "kAlphaUnchanged_Flag");
 
-    val->SetString("flags", builder.str());
+    val.Set("flags", builder.str());
   }
 
   SkScalar color_matrix[20];
   if (filter.asAColorMatrix(color_matrix)) {
-    std::unique_ptr<base::ListValue> color_matrix_val(new base::ListValue());
+    base::Value::List color_matrix_val;
     for (unsigned i = 0; i < 20; ++i) {
-      color_matrix_val->Append(
-          base::Value::FromUniquePtrValue(AsValue(color_matrix[i])));
+      color_matrix_val.Append(AsValue(color_matrix[i]));
     }
 
-    val->SetKey("color_matrix",
-                base::Value::FromUniquePtrValue(std::move(color_matrix_val)));
+    val.Set("color_matrix", std::move(color_matrix_val));
   }
 
-  return std::move(val);
+  return base::Value(std::move(val));
 }
 
-std::unique_ptr<base::Value> AsValue(const SkImageFilter& filter) {
-  std::unique_ptr<base::DictionaryValue> val(new base::DictionaryValue());
-  val->SetInteger("inputs", filter.countInputs());
+base::Value AsValue(const SkImageFilter& filter) {
+  base::Value::Dict val;
+  val.Set("inputs", filter.countInputs());
 
   SkColorFilter* color_filter;
   if (filter.asColorFilter(&color_filter)) {
-    val->SetKey("color_filter",
-                base::Value::FromUniquePtrValue(AsValue(*color_filter)));
+    val.Set("color_filter", AsValue(*color_filter));
     SkSafeUnref(color_filter); // ref'd in asColorFilter
   }
 
-  return std::move(val);
+  return base::Value(std::move(val));
 }
 
-std::unique_ptr<base::Value> AsValue(const SkPaint& paint) {
-  std::unique_ptr<base::DictionaryValue> val(new base::DictionaryValue());
+base::Value AsValue(const SkPaint& paint) {
+  base::Value::Dict val;
   SkPaint default_paint;
 
   if (paint.getColor() != default_paint.getColor())
-    val->SetKey("Color",
-                base::Value::FromUniquePtrValue(AsValue(paint.getColor())));
+    val.Set("Color", AsValue(paint.getColor()));
 
   if (paint.getStyle() != default_paint.getStyle()) {
     static const char* gStyleStrings[] = { "Fill", "Stroke", "StrokeFill" };
     DCHECK_LT(static_cast<size_t>(paint.getStyle()),
               SK_ARRAY_COUNT(gStyleStrings));
-    val->SetString("Style", gStyleStrings[paint.getStyle()]);
+    val.Set("Style", gStyleStrings[paint.getStyle()]);
   }
 
   if (paint.asBlendMode() != default_paint.asBlendMode()) {
-    val->SetKey("Xfermode", base::Value::FromUniquePtrValue(AsValue(
-                                paint.getBlendMode_or(SkBlendMode::kSrcOver))));
+    val.Set("Xfermode", AsValue(paint.getBlendMode_or(SkBlendMode::kSrcOver)));
   }
 
   if (paint.isAntiAlias() || paint.isDither()) {
@@ -205,28 +188,23 @@
     builder.addFlag(paint.isAntiAlias(), "AntiAlias");
     builder.addFlag(paint.isDither(), "Dither");
 
-    val->SetString("Flags", builder.str());
+    val.Set("Flags", builder.str());
   }
 
   if (paint.getColorFilter())
-    val->SetKey("ColorFilter", base::Value::FromUniquePtrValue(
-                                   AsValue(*paint.getColorFilter())));
+    val.Set("ColorFilter", AsValue(*paint.getColorFilter()));
 
   if (paint.getImageFilter())
-    val->SetKey("ImageFilter", base::Value::FromUniquePtrValue(
-                                   AsValue(*paint.getImageFilter())));
+    val.Set("ImageFilter", AsValue(*paint.getImageFilter()));
 
-  return std::move(val);
+  return base::Value(std::move(val));
 }
 
-std::unique_ptr<base::Value> SaveLayerFlagsAsValue(
-    SkCanvas::SaveLayerFlags flags) {
-  std::unique_ptr<base::Value> val(new base::Value(static_cast<int>(flags)));
-
-  return val;
+base::Value SaveLayerFlagsAsValue(SkCanvas::SaveLayerFlags flags) {
+  return base::Value(int{flags});
 }
 
-std::unique_ptr<base::Value> AsValue(SkClipOp op) {
+base::Value AsValue(SkClipOp op) {
   static const char* gOpStrings[] = { "Difference",
                                       "Intersect",
                                       "Union",
@@ -236,46 +214,41 @@
                                     };
   size_t index = static_cast<size_t>(op);
   DCHECK_LT(index, SK_ARRAY_COUNT(gOpStrings));
-  std::unique_ptr<base::Value> val(new base::Value(gOpStrings[index]));
-  return val;
+  return base::Value(gOpStrings[index]);
 }
 
-std::unique_ptr<base::Value> AsValue(const SkRegion& region) {
-  std::unique_ptr<base::DictionaryValue> val(new base::DictionaryValue());
-  val->SetKey("bounds", base::Value::FromUniquePtrValue(
-                            AsValue(SkRect::Make(region.getBounds()))));
+base::Value AsValue(const SkRegion& region) {
+  base::Value::Dict val;
+  val.Set("bounds", AsValue(SkRect::Make(region.getBounds())));
 
-  return std::move(val);
+  return base::Value(std::move(val));
 }
 
-std::unique_ptr<base::Value> AsValue(const SkImage& image) {
-  std::unique_ptr<base::DictionaryValue> val(new base::DictionaryValue());
-  val->SetKey("size", base::Value::FromUniquePtrValue(AsValue(
-                          SkSize::Make(image.width(), image.height()))));
+base::Value AsValue(const SkImage& image) {
+  base::Value::Dict val;
+  val.Set("size", AsValue(SkSize::Make(image.width(), image.height())));
 
-  return std::move(val);
+  return base::Value(std::move(val));
 }
 
-std::unique_ptr<base::Value> AsValue(const SkTextBlob& blob) {
-  std::unique_ptr<base::DictionaryValue> val(new base::DictionaryValue());
-  val->SetKey("bounds",
-              base::Value::FromUniquePtrValue(AsValue(blob.bounds())));
+base::Value AsValue(const SkTextBlob& blob) {
+  base::Value::Dict val;
+  val.Set("bounds", AsValue(blob.bounds()));
 
-  return std::move(val);
+  return base::Value(std::move(val));
 }
 
-std::unique_ptr<base::Value> AsValue(const SkPath& path) {
-  std::unique_ptr<base::DictionaryValue> val(new base::DictionaryValue());
+base::Value AsValue(const SkPath& path) {
+  base::Value::Dict val;
 
   static const char* gFillStrings[] =
       { "winding", "even-odd", "inverse-winding", "inverse-even-odd" };
   size_t index = static_cast<size_t>(path.getFillType());
   DCHECK_LT(index, SK_ARRAY_COUNT(gFillStrings));
-  val->SetString("fill-type", gFillStrings[index]);
-  val->SetBoolean("convex", path.isConvex());
-  val->SetBoolean("is-rect", path.isRect(nullptr));
-  val->SetKey("bounds",
-              base::Value::FromUniquePtrValue(AsValue(path.getBounds())));
+  val.Set("fill-type", gFillStrings[index]);
+  val.Set("convex", path.isConvex());
+  val.Set("is-rect", path.isRect(nullptr));
+  val.Set("bounds", AsValue(path.getBounds()));
 
   static const char* gVerbStrings[] =
       { "move", "line", "quad", "conic", "cubic", "close", "done" };
@@ -291,7 +264,7 @@
       SK_ARRAY_COUNT(gVerbStrings) == SK_ARRAY_COUNT(gPtOffsetPerVerb),
       "gPtOffsetPerVerb size mismatch");
 
-  base::Value verbs_val(base::Value::Type::LIST);
+  base::Value::List verbs_val;
   SkPath::RawIter iter(const_cast<SkPath&>(path));
   SkPoint points[4];
 
@@ -299,34 +272,32 @@
        verb = iter.next(points)) {
     DCHECK_LT(static_cast<size_t>(verb), SK_ARRAY_COUNT(gVerbStrings));
 
-    base::Value verb_val(base::Value::Type::DICTIONARY);
-    base::Value pts_val(base::Value::Type::LIST);
+    base::Value::Dict verb_val;
+    base::Value::List pts_val;
 
     for (int i = 0; i < gPtsPerVerb[verb]; ++i)
-      pts_val.Append(base::Value::FromUniquePtrValue(
-          AsValue(points[i + gPtOffsetPerVerb[verb]])));
+      pts_val.Append(AsValue(points[i + gPtOffsetPerVerb[verb]]));
 
-    verb_val.SetKey(gVerbStrings[verb], std::move(pts_val));
+    verb_val.Set(gVerbStrings[verb], std::move(pts_val));
 
     if (SkPath::kConic_Verb == verb)
-      verb_val.SetKey("weight", base::Value::FromUniquePtrValue(
-                                    AsValue(iter.conicWeight())));
+      verb_val.Set("weight", AsValue(iter.conicWeight()));
 
     verbs_val.Append(std::move(verb_val));
   }
-  val->SetKey("verbs", std::move(verbs_val));
+  val.Set("verbs", std::move(verbs_val));
 
-  return std::move(val);
+  return base::Value(std::move(val));
 }
 
 template <typename T>
-std::unique_ptr<base::Value> AsListValue(const T array[], size_t count) {
-  std::unique_ptr<base::ListValue> val(new base::ListValue());
+base::Value AsListValue(const T array[], size_t count) {
+  base::Value::List val;
 
   for (size_t i = 0; i < count; ++i)
-    val->Append(base::Value::FromUniquePtrValue(AsValue(array[i])));
+    val.Append(AsValue(array[i]));
 
-  return std::move(val);
+  return base::Value(std::move(val));
 }
 
 } // namespace
@@ -340,13 +311,15 @@
  AutoOp(BenchmarkingCanvas* canvas,
         const char op_name[],
         const SkPaint* paint = nullptr)
-     : canvas_(canvas), op_record_(new base::DictionaryValue()) {
+     : canvas_(canvas) {
    DCHECK(canvas);
    DCHECK(op_name);
 
-   op_record_->SetString("cmd_string", op_name);
-   op_params_ =
-       op_record_->SetList("info", std::make_unique<base::ListValue>());
+   op_record_.Set("cmd_string", op_name);
+   base::Value* op_params = op_record_.Set("info", base::Value::List());
+   DCHECK(op_params);
+   DCHECK(op_params->is_list());
+   op_params_ = &op_params->GetList();
 
    if (paint) {
      this->addParam("paint", AsValue(*paint));
@@ -354,29 +327,28 @@
    }
 
    start_ticks_ = base::TimeTicks::Now();
-  }
+ }
 
   ~AutoOp() {
     base::TimeDelta ticks = base::TimeTicks::Now() - start_ticks_;
-    op_record_->SetDouble("cmd_time", ticks.InMillisecondsF());
+    op_record_.Set("cmd_time", ticks.InMillisecondsF());
 
-    canvas_->op_records_.Append(
-        base::Value::FromUniquePtrValue(std::move(op_record_)));
+    canvas_->op_records_.Append(std::move(op_record_));
   }
 
-  void addParam(const char name[], std::unique_ptr<base::Value> value) {
-    std::unique_ptr<base::DictionaryValue> param(new base::DictionaryValue());
-    param->SetKey(name, base::Value::FromUniquePtrValue(std::move(value)));
+  void addParam(const char name[], base::Value value) {
+    base::Value::Dict param;
+    param.Set(name, std::move(value));
 
-    op_params_->Append(base::Value::FromUniquePtrValue(std::move(param)));
+    op_params_->Append(std::move(param));
   }
 
   const SkPaint* paint() const { return &filtered_paint_; }
 
 private:
  raw_ptr<BenchmarkingCanvas> canvas_;
- std::unique_ptr<base::DictionaryValue> op_record_;
- raw_ptr<base::ListValue> op_params_;
+ base::Value::Dict op_record_;
+ raw_ptr<base::Value::List> op_params_;
  base::TimeTicks start_ticks_;
 
  SkPaint filtered_paint_;
@@ -391,18 +363,18 @@
 BenchmarkingCanvas::~BenchmarkingCanvas() = default;
 
 size_t BenchmarkingCanvas::CommandCount() const {
-  return op_records_.GetListDeprecated().size();
+  return op_records_.size();
 }
 
-const base::ListValue& BenchmarkingCanvas::Commands() const {
+const base::Value::List& BenchmarkingCanvas::Commands() const {
   return op_records_;
 }
 
 double BenchmarkingCanvas::GetTime(size_t index) {
-  const base::Value& op = op_records_.GetListDeprecated()[index];
+  const base::Value& op = op_records_[index];
   if (!op.is_dict())
     return 0;
-  return op.FindDoubleKey("cmd_time").value_or(0);
+  return op.GetDict().FindDouble("cmd_time").value_or(0);
 }
 
 void BenchmarkingCanvas::willSave() {
diff --git a/skia/ext/benchmarking_canvas.h b/skia/ext/benchmarking_canvas.h
index 32758813..78c3f16 100644
--- a/skia/ext/benchmarking_canvas.h
+++ b/skia/ext/benchmarking_canvas.h
@@ -21,7 +21,7 @@
   size_t CommandCount() const;
 
   // Returns the list of executed draw commands.
-  const base::ListValue& Commands() const;
+  const base::Value::List& Commands() const;
 
   // Return the recorded render time (milliseconds) for a draw command index.
   double GetTime(size_t index);
@@ -73,7 +73,7 @@
 
   class AutoOp;
 
-  base::ListValue op_records_;
+  base::Value::List op_records_;
 };
 
 }
diff --git a/storage/browser/blob/blob_memory_controller.cc b/storage/browser/blob/blob_memory_controller.cc
index f5d1e16..87c87f83 100644
--- a/storage/browser/blob/blob_memory_controller.cc
+++ b/storage/browser/blob/blob_memory_controller.cc
@@ -82,11 +82,11 @@
 BlobStorageLimits CalculateBlobStorageLimitsImpl(
     const FilePath& storage_dir,
     bool disk_enabled,
-    absl::optional<int64_t> optional_memory_size_for_testing) {
+    absl::optional<uint64_t> optional_memory_size_for_testing) {
   int64_t disk_size = 0ull;
-  int64_t memory_size = optional_memory_size_for_testing
-                            ? optional_memory_size_for_testing.value()
-                            : base::SysInfo::AmountOfPhysicalMemory();
+  uint64_t memory_size = optional_memory_size_for_testing
+                             ? optional_memory_size_for_testing.value()
+                             : base::SysInfo::AmountOfPhysicalMemory();
   if (disk_enabled && CreateBlobDirectory(storage_dir) == base::File::FILE_OK)
     disk_size = base::SysInfo::AmountOfTotalDiskSpace(storage_dir);
 
@@ -99,9 +99,9 @@
     constexpr size_t kTwoGigabytes = 2ull * 1024 * 1024 * 1024;
     limits.max_blob_in_memory_space = kTwoGigabytes;
 #elif BUILDFLAG(IS_ANDROID)
-    limits.max_blob_in_memory_space = static_cast<size_t>(memory_size / 100ll);
+    limits.max_blob_in_memory_space = static_cast<size_t>(memory_size / 100);
 #else
-    limits.max_blob_in_memory_space = static_cast<size_t>(memory_size / 5ll);
+    limits.max_blob_in_memory_space = static_cast<size_t>(memory_size / 5);
 #endif
   }
   // Devices just on the edge (RAM == 256MB) should not fail because
diff --git a/storage/browser/blob/blob_memory_controller.h b/storage/browser/blob/blob_memory_controller.h
index ecb449b..168c100 100644
--- a/storage/browser/blob/blob_memory_controller.h
+++ b/storage/browser/blob/blob_memory_controller.h
@@ -204,7 +204,7 @@
   // synchronously.
   void CallWhenStorageLimitsAreKnown(base::OnceClosure callback);
 
-  void set_amount_of_physical_memory_for_testing(int64_t amount_of_memory) {
+  void set_amount_of_physical_memory_for_testing(uint64_t amount_of_memory) {
     amount_of_memory_for_testing_ = amount_of_memory;
   }
 
@@ -283,7 +283,7 @@
   bool did_calculate_storage_limits_ = false;
   std::vector<base::OnceClosure> on_calculate_limits_callbacks_;
 
-  absl::optional<int64_t> amount_of_memory_for_testing_;
+  absl::optional<uint64_t> amount_of_memory_for_testing_;
 
   // Memory bookkeeping. These numbers are all disjoint.
   // This is the amount of memory we're using for blobs in RAM, including the
diff --git a/storage/browser/blob/blob_registry_impl.cc b/storage/browser/blob/blob_registry_impl.cc
index 5285bab..9388dc9 100644
--- a/storage/browser/blob/blob_registry_impl.cc
+++ b/storage/browser/blob/blob_registry_impl.cc
@@ -51,7 +51,6 @@
     ElementEntry& operator=(ElementEntry&& other) = default;
 
     blink::mojom::DataElementPtr element;
-    FileSystemURL filesystem_url;
     mojo::Remote<blink::mojom::BytesProvider> bytes_provider;
     mojo::Remote<blink::mojom::Blob> blob;
   };
@@ -385,13 +384,6 @@
       builder_->AppendFile(
           f->path, f->offset, f->length,
           f->expected_modification_time.value_or(base::Time()));
-    } else if (element->is_file_filesystem()) {
-      DCHECK(entry.filesystem_url.is_valid());
-      const auto& f = element->get_file_filesystem();
-      builder_->AppendFileSystemFile(
-          entry.filesystem_url, f->offset, f->length,
-          f->expected_modification_time.value_or(base::Time()),
-          blob_registry_->file_system_context_);
     } else if (element->is_blob()) {
       DCHECK(blob_uuid_it != referenced_blob_uuids_.end());
       const std::string& blob_uuid = *blob_uuid_it++;
@@ -501,10 +493,8 @@
 BlobRegistryImpl::BlobRegistryImpl(
     base::WeakPtr<BlobStorageContext> context,
     base::WeakPtr<BlobUrlRegistry> url_registry,
-    scoped_refptr<base::TaskRunner> url_registry_runner,
-    scoped_refptr<FileSystemContext> file_system_context)
+    scoped_refptr<base::TaskRunner> url_registry_runner)
     : context_(std::move(context)),
-      file_system_context_(std::move(file_system_context)),
       url_registry_(std::move(url_registry)),
       url_registry_runner_(std::move(url_registry_runner)) {}
 
@@ -558,25 +548,6 @@
         std::move(callback).Run();
         return;
       }
-    } else if (entry.element->is_file_filesystem()) {
-      const GURL crack_url = entry.element->get_file_filesystem()->url;
-      // TODO(https://crbug.com/1221308): determine whether StorageKey should be
-      // replaced with a more meaningful value
-      const blink::StorageKey crack_storage_key =
-          blink::StorageKey(url::Origin::Create(crack_url));
-      entry.filesystem_url =
-          file_system_context_->CrackURL(crack_url, crack_storage_key);
-      if (!entry.filesystem_url.is_valid() ||
-          !file_system_context_->GetFileSystemBackend(
-              entry.filesystem_url.type()) ||
-          !delegate->CanReadFileSystemFile(entry.filesystem_url)) {
-        std::unique_ptr<BlobDataHandle> handle = context_->AddBrokenBlob(
-            uuid, content_type, content_disposition,
-            BlobStatus::ERR_REFERENCED_FILE_UNAVAILABLE);
-        BlobImpl::Create(std::move(handle), std::move(blob));
-        std::move(callback).Run();
-        return;
-      }
     }
     element_entries.push_back(std::move(entry));
   }
diff --git a/storage/browser/blob/blob_registry_impl.h b/storage/browser/blob/blob_registry_impl.h
index 866dac0..1ce62bd 100644
--- a/storage/browser/blob/blob_registry_impl.h
+++ b/storage/browser/blob/blob_registry_impl.h
@@ -12,16 +12,18 @@
 #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"
-#include "storage/browser/file_system/file_system_context.h"
 #include "third_party/blink/public/mojom/blob/blob_registry.mojom.h"
 #include "url/origin.h"
 
+namespace base {
+class FilePath;
+}
+
 namespace storage {
 
 class BlobBuilderFromStream;
 class BlobDataHandle;
 class BlobStorageContext;
-class FileSystemURL;
 
 class COMPONENT_EXPORT(STORAGE_BROWSER) BlobRegistryImpl
     : public blink::mojom::BlobRegistry {
@@ -32,14 +34,12 @@
    public:
     virtual ~Delegate() {}
     virtual bool CanReadFile(const base::FilePath& file) = 0;
-    virtual bool CanReadFileSystemFile(const FileSystemURL& url) = 0;
     virtual bool CanAccessDataForOrigin(const url::Origin& origin) = 0;
   };
 
   BlobRegistryImpl(base::WeakPtr<BlobStorageContext> context,
                    base::WeakPtr<BlobUrlRegistry> url_registry,
-                   scoped_refptr<base::TaskRunner> url_registry_runner,
-                   scoped_refptr<FileSystemContext> file_system_context);
+                   scoped_refptr<base::TaskRunner> url_registry_runner);
 
   BlobRegistryImpl(const BlobRegistryImpl&) = delete;
   BlobRegistryImpl& operator=(const BlobRegistryImpl&) = delete;
@@ -96,7 +96,6 @@
                          std::unique_ptr<BlobDataHandle> result);
 
   base::WeakPtr<BlobStorageContext> context_;
-  scoped_refptr<FileSystemContext> file_system_context_;
 
   // `url_registry_` should only be accessed on `url_registry_runner_`.
   base::WeakPtr<BlobUrlRegistry> url_registry_;
diff --git a/storage/browser/blob/blob_registry_impl_unittest.cc b/storage/browser/blob/blob_registry_impl_unittest.cc
index 112ef40..30d8db2 100644
--- a/storage/browser/blob/blob_registry_impl_unittest.cc
+++ b/storage/browser/blob/blob_registry_impl_unittest.cc
@@ -74,19 +74,9 @@
         data_dir_.GetPath(), data_dir_.GetPath(),
         base::ThreadPool::CreateTaskRunner({base::MayBlock()}));
     auto storage_policy = base::MakeRefCounted<MockSpecialStoragePolicy>();
-    file_system_context_ = FileSystemContext::Create(
-        base::ThreadTaskRunnerHandle::Get(),
-        base::ThreadTaskRunnerHandle::Get(),
-        /*external_mount_points=*/nullptr, std::move(storage_policy),
-        /*quota_manager_proxy=*/nullptr,
-        std::vector<std::unique_ptr<FileSystemBackend>>(),
-        std::vector<URLRequestAutoMountHandler>(), data_dir_.GetPath(),
-        FileSystemOptions(FileSystemOptions::PROFILE_MODE_INCOGNITO,
-                          /*force_in_memory=*/false,
-                          std::vector<std::string>()));
     registry_impl_ = std::make_unique<BlobRegistryImpl>(
         context_->AsWeakPtr(), url_registry_.AsWeakPtr(),
-        base::SequencedTaskRunnerHandle::Get(), file_system_context_);
+        base::SequencedTaskRunnerHandle::Get());
     auto delegate = std::make_unique<MockBlobRegistryDelegate>();
     delegate_ptr_ = delegate.get();
     registry_impl_->Bind(registry_.BindNewPipeAndPassReceiver(),
@@ -194,7 +184,6 @@
   base::test::TaskEnvironment task_environment_;
   absl::optional<base::ScopedDisallowBlocking> disallow_blocking_;
   std::unique_ptr<BlobStorageContext> context_;
-  scoped_refptr<FileSystemContext> file_system_context_;
   BlobUrlRegistry url_registry_;
   std::unique_ptr<BlobRegistryImpl> registry_impl_;
   mojo::Remote<blink::mojom::BlobRegistry> registry_;
@@ -630,82 +619,6 @@
   EXPECT_EQ(0u, BlobsUnderConstruction());
 }
 
-TEST_F(BlobRegistryImplTest, Register_FileSystemFile_InvalidScheme) {
-  const std::string kId = "id";
-
-  std::vector<blink::mojom::DataElementPtr> elements;
-  elements.push_back(blink::mojom::DataElement::NewFileFilesystem(
-      blink::mojom::DataElementFilesystemURL::New(GURL("http://foobar.com/"), 0,
-                                                  16, absl::nullopt)));
-
-  mojo::PendingRemote<blink::mojom::Blob> blob;
-  EXPECT_TRUE(registry_->Register(blob.InitWithNewPipeAndPassReceiver(), kId,
-                                  "", "", std::move(elements)));
-  EXPECT_TRUE(bad_messages_.empty());
-
-  std::unique_ptr<BlobDataHandle> handle = context_->GetBlobDataFromUUID(kId);
-  WaitForBlobCompletion(handle.get());
-
-  EXPECT_TRUE(handle->IsBroken());
-  EXPECT_EQ(BlobStatus::ERR_REFERENCED_FILE_UNAVAILABLE,
-            handle->GetBlobStatus());
-  EXPECT_EQ(0u, BlobsUnderConstruction());
-}
-
-TEST_F(BlobRegistryImplTest, Register_FileSystemFile_UnreadableFile) {
-  delegate_ptr_->can_read_file_system_file_result = false;
-
-  const std::string kId = "id";
-  const GURL url("filesystem:http://example.com/temporary/myfile.png");
-
-  std::vector<blink::mojom::DataElementPtr> elements;
-  elements.push_back(blink::mojom::DataElement::NewFileFilesystem(
-      blink::mojom::DataElementFilesystemURL::New(url, 0, 16, absl::nullopt)));
-
-  mojo::PendingRemote<blink::mojom::Blob> blob;
-  EXPECT_TRUE(registry_->Register(blob.InitWithNewPipeAndPassReceiver(), kId,
-                                  "", "", std::move(elements)));
-  EXPECT_TRUE(bad_messages_.empty());
-
-  std::unique_ptr<BlobDataHandle> handle = context_->GetBlobDataFromUUID(kId);
-  WaitForBlobCompletion(handle.get());
-
-  EXPECT_TRUE(handle->IsBroken());
-  EXPECT_EQ(BlobStatus::ERR_REFERENCED_FILE_UNAVAILABLE,
-            handle->GetBlobStatus());
-  EXPECT_EQ(0u, BlobsUnderConstruction());
-}
-
-TEST_F(BlobRegistryImplTest, Register_FileSystemFile_Valid) {
-  delegate_ptr_->can_read_file_system_file_result = true;
-
-  const std::string kId = "id";
-  const GURL url("filesystem:http://example.com/temporary/myfile.png");
-
-  std::vector<blink::mojom::DataElementPtr> elements;
-  elements.push_back(blink::mojom::DataElement::NewFileFilesystem(
-      blink::mojom::DataElementFilesystemURL::New(url, 0, 16, absl::nullopt)));
-
-  mojo::PendingRemote<blink::mojom::Blob> blob;
-  EXPECT_TRUE(registry_->Register(blob.InitWithNewPipeAndPassReceiver(), kId,
-                                  "", "", std::move(elements)));
-  EXPECT_TRUE(bad_messages_.empty());
-
-  std::unique_ptr<BlobDataHandle> handle = context_->GetBlobDataFromUUID(kId);
-  WaitForBlobCompletion(handle.get());
-
-  EXPECT_FALSE(handle->IsBroken());
-  ASSERT_EQ(BlobStatus::DONE, handle->GetBlobStatus());
-
-  BlobDataBuilder expected_blob_data(kId);
-  expected_blob_data.AppendFileSystemFile(
-      file_system_context_->CrackURLInFirstPartyContext(url), 0, 16,
-      base::Time(), file_system_context_);
-
-  EXPECT_EQ(expected_blob_data, *handle->CreateSnapshot());
-  EXPECT_EQ(0u, BlobsUnderConstruction());
-}
-
 TEST_F(BlobRegistryImplTest, Register_BytesInvalidEmbeddedData) {
   const std::string kId = "id";
 
diff --git a/storage/browser/file_system/obfuscated_file_util_memory_delegate.cc b/storage/browser/file_system/obfuscated_file_util_memory_delegate.cc
index 326e9d85d..e9db5bb9 100644
--- a/storage/browser/file_system/obfuscated_file_util_memory_delegate.cc
+++ b/storage/browser/file_system/obfuscated_file_util_memory_delegate.cc
@@ -10,6 +10,7 @@
 #include "base/allocator/partition_allocator/partition_alloc_constants.h"
 #include "base/files/file_util.h"
 #include "base/numerics/checked_math.h"
+#include "base/numerics/safe_conversions.h"
 #include "base/system/sys_info.h"
 #include "build/build_config.h"
 #include "net/base/io_buffer.h"
@@ -26,14 +27,14 @@
 // crash possibility.
 // Note that quota assignment is the same for on-disk filesystem and the
 // assigned quota is not guaranteed to be allocatable later.
-bool IsMemoryAvailable(int64_t required_memory) {
+bool IsMemoryAvailable(size_t required_memory) {
 #if BUILDFLAG(IS_FUCHSIA)
   // This function is not implemented on FUCHSIA, yet. (crbug.com/986608)
   return true;
 #else
-  int64_t max_allocatable =
+  uint64_t max_allocatable =
       std::min(base::SysInfo::AmountOfAvailablePhysicalMemory(),
-               static_cast<int64_t>(partition_alloc::MaxDirectMapped()));
+               static_cast<uint64_t>(partition_alloc::MaxDirectMapped()));
 
   return max_allocatable >= required_memory;
 #endif
@@ -335,12 +336,13 @@
     return base::File::FILE_ERROR_NOT_FOUND;
 
   // Fail if enough memory is not available.
-  if (static_cast<size_t>(length) > dp->entry->file_content.capacity() &&
-      !IsMemoryAvailable(length)) {
+  if (!base::IsValueInRangeForNumericType<size_t>(length) ||
+      (static_cast<size_t>(length) > dp->entry->file_content.capacity() &&
+       !IsMemoryAvailable(static_cast<size_t>(length)))) {
     return base::File::FILE_ERROR_NO_SPACE;
   }
 
-  dp->entry->file_content.resize(length);
+  dp->entry->file_content.resize(static_cast<size_t>(length));
   return base::File::FILE_OK;
 }
 
@@ -620,7 +622,7 @@
   }
 
   // Fail if enough memory is not available.
-  if (!IsMemoryAvailable(source_info.size))
+  if (!IsMemoryAvailable(static_cast<size_t>(source_info.size)))
     return base::File::FILE_ERROR_NO_SPACE;
 
   // Create file.
diff --git a/storage/browser/quota/quota_device_info_helper.cc b/storage/browser/quota/quota_device_info_helper.cc
index 8f41544..cf12c20 100644
--- a/storage/browser/quota/quota_device_info_helper.cc
+++ b/storage/browser/quota/quota_device_info_helper.cc
@@ -17,7 +17,7 @@
   return disk_space;
 }
 
-int64_t QuotaDeviceInfoHelper::AmountOfPhysicalMemory() const {
+uint64_t QuotaDeviceInfoHelper::AmountOfPhysicalMemory() const {
   return base::SysInfo::AmountOfPhysicalMemory();
 }
 
diff --git a/storage/browser/quota/quota_device_info_helper.h b/storage/browser/quota/quota_device_info_helper.h
index 2ce8b16..1f865d8 100644
--- a/storage/browser/quota/quota_device_info_helper.h
+++ b/storage/browser/quota/quota_device_info_helper.h
@@ -25,7 +25,7 @@
 
   virtual int64_t AmountOfTotalDiskSpace(const base::FilePath& path) const;
 
-  virtual int64_t AmountOfPhysicalMemory() const;
+  virtual uint64_t AmountOfPhysicalMemory() const;
 };  // class QuotaDeviceInfoHelper
 
 }  // namespace storage
diff --git a/storage/browser/quota/quota_settings.cc b/storage/browser/quota/quota_settings.cc
index c2a5a20..718e6cf 100644
--- a/storage/browser/quota/quota_settings.cc
+++ b/storage/browser/quota/quota_settings.cc
@@ -37,7 +37,7 @@
 }
 
 QuotaSettings CalculateIncognitoDynamicSettings(
-    int64_t physical_memory_amount) {
+    uint64_t physical_memory_amount) {
   // The incognito pool size is a fraction of the amount of system memory.
   double incognito_pool_size_ratio =
       kIncognitoQuotaRatioLowerBound +
diff --git a/storage/browser/quota/quota_settings_unittest.cc b/storage/browser/quota/quota_settings_unittest.cc
index ef2bf9a..84e03cb 100644
--- a/storage/browser/quota/quota_settings_unittest.cc
+++ b/storage/browser/quota/quota_settings_unittest.cc
@@ -8,6 +8,7 @@
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/files/scoped_temp_dir.h"
+#include "base/numerics/safe_conversions.h"
 #include "base/run_loop.h"
 #include "base/test/bind.h"
 #include "base/test/scoped_feature_list.h"
@@ -22,8 +23,8 @@
 
 namespace {
 
-constexpr int64_t kLowPhysicalMemory = 1024 * 1024;
-constexpr int64_t kHighPhysicalMemory = 65536 * kLowPhysicalMemory;
+constexpr uint64_t kLowPhysicalMemory = 1024 * 1024;
+constexpr uint64_t kHighPhysicalMemory = 65536 * kLowPhysicalMemory;
 
 }  // namespace
 
@@ -33,7 +34,7 @@
  public:
   MockQuotaDeviceInfoHelper() = default;
   MOCK_CONST_METHOD1(AmountOfTotalDiskSpace, int64_t(const base::FilePath&));
-  MOCK_CONST_METHOD0(AmountOfPhysicalMemory, int64_t());
+  MOCK_CONST_METHOD0(AmountOfPhysicalMemory, uint64_t());
 };
 
 class QuotaSettingsTest : public testing::Test {
@@ -71,23 +72,25 @@
 
  protected:
   void SetUpDeviceInfoHelper(const int expected_calls,
-                             const int64_t physical_memory_amount) {
+                             const uint64_t physical_memory_amount) {
     ON_CALL(device_info_helper_, AmountOfPhysicalMemory())
         .WillByDefault(::testing::Return(physical_memory_amount));
     EXPECT_CALL(device_info_helper_, AmountOfPhysicalMemory())
         .Times(expected_calls);
   }
 
-  void GetAndTestSettings(const int64_t physical_memory_amount) {
+  void GetAndTestSettings(const uint64_t physical_memory_amount) {
     absl::optional<QuotaSettings> settings =
         GetSettings(true, &device_info_helper_);
     ASSERT_TRUE(settings.has_value());
+    const uint64_t pool_size =
+        base::checked_cast<uint64_t>(settings->pool_size);
     EXPECT_LE(
         physical_memory_amount * GetIncognitoQuotaRatioLowerBound_ForTesting(),
-        settings->pool_size);
+        pool_size);
     EXPECT_GE(
         physical_memory_amount * GetIncognitoQuotaRatioUpperBound_ForTesting(),
-        settings->pool_size);
+        pool_size);
   }
 
  private:
diff --git a/storage/browser/test/mock_blob_registry_delegate.cc b/storage/browser/test/mock_blob_registry_delegate.cc
index d0316c8f..47a065a 100644
--- a/storage/browser/test/mock_blob_registry_delegate.cc
+++ b/storage/browser/test/mock_blob_registry_delegate.cc
@@ -9,9 +9,7 @@
 bool MockBlobRegistryDelegate::CanReadFile(const base::FilePath& file) {
   return can_read_file_result;
 }
-bool MockBlobRegistryDelegate::CanReadFileSystemFile(const FileSystemURL& url) {
-  return can_read_file_system_file_result;
-}
+
 bool MockBlobRegistryDelegate::CanAccessDataForOrigin(
     const url::Origin& origin) {
   return can_access_data_for_origin;
diff --git a/storage/browser/test/mock_blob_registry_delegate.h b/storage/browser/test/mock_blob_registry_delegate.h
index c8ec096..9fbcbdbd 100644
--- a/storage/browser/test/mock_blob_registry_delegate.h
+++ b/storage/browser/test/mock_blob_registry_delegate.h
@@ -15,11 +15,9 @@
   ~MockBlobRegistryDelegate() override = default;
 
   bool CanReadFile(const base::FilePath& file) override;
-  bool CanReadFileSystemFile(const FileSystemURL& url) override;
   bool CanAccessDataForOrigin(const url::Origin& origin) override;
 
   bool can_read_file_result = true;
-  bool can_read_file_system_file_result = true;
   bool can_access_data_for_origin = true;
 };
 
diff --git a/testing/buildbot/chrome.json b/testing/buildbot/chrome.json
index e770c451..3e559ab 100644
--- a/testing/buildbot/chrome.json
+++ b/testing/buildbot/chrome.json
@@ -2197,186 +2197,20 @@
         "variant_id": "JACUZZI_RELEASE_BETA"
       },
       {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/lacros-arm.ozone_unittests.filter"
-        ],
-        "cros_board": "hana",
-        "cros_img": "hana-release/R105-14959.0.0",
-        "name": "ozone_unittests HANA_RELEASE_LKGM",
-        "swarming": {},
-        "test": "ozone_unittests",
-        "test_id_prefix": "ninja://ui/ozone:ozone_unittests/",
-        "timeout_sec": 3600,
-        "variant_id": "HANA_RELEASE_LKGM"
-      },
-      {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/lacros-arm.ozone_unittests.filter"
-        ],
-        "cros_board": "hana",
-        "cros_img": "hana-release/R105-14961.0.0",
-        "name": "ozone_unittests HANA_RELEASE_DEV",
-        "swarming": {},
-        "test": "ozone_unittests",
-        "test_id_prefix": "ninja://ui/ozone:ozone_unittests/",
-        "timeout_sec": 3600,
-        "variant_id": "HANA_RELEASE_DEV"
-      },
-      {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/lacros-arm.ozone_unittests.filter"
-        ],
-        "cros_board": "hana",
-        "cros_img": "hana-release/R104-14909.26.0",
-        "name": "ozone_unittests HANA_RELEASE_BETA",
-        "swarming": {},
-        "test": "ozone_unittests",
-        "test_id_prefix": "ninja://ui/ozone:ozone_unittests/",
-        "timeout_sec": 3600,
-        "variant_id": "HANA_RELEASE_BETA"
-      },
-      {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/lacros-arm.ozone_unittests.filter"
-        ],
-        "cros_board": "hana",
-        "cros_img": "hana-release/R103-14816.99.0",
-        "name": "ozone_unittests HANA_RELEASE_STABLE",
-        "swarming": {},
-        "test": "ozone_unittests",
-        "test_id_prefix": "ninja://ui/ozone:ozone_unittests/",
-        "timeout_sec": 3600,
-        "variant_id": "HANA_RELEASE_STABLE"
-      },
-      {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/lacros-arm.ozone_unittests.filter"
-        ],
+        "args": [],
         "cros_board": "jacuzzi",
-        "cros_img": "jacuzzi-release/R105-14959.0.0",
-        "name": "ozone_unittests JACUZZI_RELEASE_LKGM",
+        "cros_img": "jacuzzi-release/R103-14816.99.0",
+        "name": "lacros_all_tast_tests JACUZZI_RELEASE_STABLE",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
         "swarming": {},
-        "test": "ozone_unittests",
-        "test_id_prefix": "ninja://ui/ozone:ozone_unittests/",
-        "timeout_sec": 3600,
-        "variant_id": "JACUZZI_RELEASE_LKGM"
-      },
-      {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/lacros-arm.ozone_unittests.filter"
-        ],
-        "cros_board": "jacuzzi",
-        "cros_img": "jacuzzi-release/R105-14943.0.0",
-        "name": "ozone_unittests JACUZZI_RELEASE_DEV",
-        "swarming": {},
-        "test": "ozone_unittests",
-        "test_id_prefix": "ninja://ui/ozone:ozone_unittests/",
-        "timeout_sec": 3600,
-        "variant_id": "JACUZZI_RELEASE_DEV"
-      },
-      {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/lacros-arm.ozone_unittests.filter"
-        ],
-        "cros_board": "jacuzzi",
-        "cros_img": "jacuzzi-release/R104-14909.26.0",
-        "name": "ozone_unittests JACUZZI_RELEASE_BETA",
-        "swarming": {},
-        "test": "ozone_unittests",
-        "test_id_prefix": "ninja://ui/ozone:ozone_unittests/",
-        "timeout_sec": 3600,
-        "variant_id": "JACUZZI_RELEASE_BETA"
-      },
-      {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/lacros-arm.viz_unittests.filter"
-        ],
-        "cros_board": "hana",
-        "cros_img": "hana-release/R105-14959.0.0",
-        "name": "viz_unittests HANA_RELEASE_LKGM",
-        "swarming": {},
-        "test": "viz_unittests",
-        "test_id_prefix": "ninja://components/viz:viz_unittests/",
-        "timeout_sec": 3600,
-        "variant_id": "HANA_RELEASE_LKGM"
-      },
-      {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/lacros-arm.viz_unittests.filter"
-        ],
-        "cros_board": "hana",
-        "cros_img": "hana-release/R105-14961.0.0",
-        "name": "viz_unittests HANA_RELEASE_DEV",
-        "swarming": {},
-        "test": "viz_unittests",
-        "test_id_prefix": "ninja://components/viz:viz_unittests/",
-        "timeout_sec": 3600,
-        "variant_id": "HANA_RELEASE_DEV"
-      },
-      {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/lacros-arm.viz_unittests.filter"
-        ],
-        "cros_board": "hana",
-        "cros_img": "hana-release/R104-14909.26.0",
-        "name": "viz_unittests HANA_RELEASE_BETA",
-        "swarming": {},
-        "test": "viz_unittests",
-        "test_id_prefix": "ninja://components/viz:viz_unittests/",
-        "timeout_sec": 3600,
-        "variant_id": "HANA_RELEASE_BETA"
-      },
-      {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/lacros-arm.viz_unittests.filter"
-        ],
-        "cros_board": "hana",
-        "cros_img": "hana-release/R103-14816.99.0",
-        "name": "viz_unittests HANA_RELEASE_STABLE",
-        "swarming": {},
-        "test": "viz_unittests",
-        "test_id_prefix": "ninja://components/viz:viz_unittests/",
-        "timeout_sec": 3600,
-        "variant_id": "HANA_RELEASE_STABLE"
-      },
-      {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/lacros-arm.viz_unittests.filter"
-        ],
-        "cros_board": "jacuzzi",
-        "cros_img": "jacuzzi-release/R105-14959.0.0",
-        "name": "viz_unittests JACUZZI_RELEASE_LKGM",
-        "swarming": {},
-        "test": "viz_unittests",
-        "test_id_prefix": "ninja://components/viz:viz_unittests/",
-        "timeout_sec": 3600,
-        "variant_id": "JACUZZI_RELEASE_LKGM"
-      },
-      {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/lacros-arm.viz_unittests.filter"
-        ],
-        "cros_board": "jacuzzi",
-        "cros_img": "jacuzzi-release/R105-14943.0.0",
-        "name": "viz_unittests JACUZZI_RELEASE_DEV",
-        "swarming": {},
-        "test": "viz_unittests",
-        "test_id_prefix": "ninja://components/viz:viz_unittests/",
-        "timeout_sec": 3600,
-        "variant_id": "JACUZZI_RELEASE_DEV"
-      },
-      {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/lacros-arm.viz_unittests.filter"
-        ],
-        "cros_board": "jacuzzi",
-        "cros_img": "jacuzzi-release/R104-14909.26.0",
-        "name": "viz_unittests JACUZZI_RELEASE_BETA",
-        "swarming": {},
-        "test": "viz_unittests",
-        "test_id_prefix": "ninja://components/viz:viz_unittests/",
-        "timeout_sec": 3600,
-        "variant_id": "JACUZZI_RELEASE_BETA"
+        "tast_expr": "(\"group:mainline\" && \"dep:lacros\" && !informational)",
+        "test": "lacros_all_tast_tests",
+        "test_id_prefix": "ninja://chromeos/lacros:lacros_all_tast_tests/",
+        "timeout_sec": 10800,
+        "variant_id": "JACUZZI_RELEASE_STABLE"
       }
     ]
   },
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json
index 6f9b8637..92d35de2 100644
--- a/testing/buildbot/chromium.chromiumos.json
+++ b/testing/buildbot/chromium.chromiumos.json
@@ -5755,21 +5755,21 @@
       {
         "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_v105.0.5179.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5180.0/test_ash_chrome"
         ],
         "isolate_profile_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "interactive_ui_tests Lacros version skew testing ash 105.0.5179.0",
+        "name": "interactive_ui_tests Lacros version skew testing ash 105.0.5180.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v105.0.5179.0",
-              "revision": "version:105.0.5179.0"
+              "location": "lacros_version_skew_tests_v105.0.5180.0",
+              "revision": "version:105.0.5180.0"
             }
           ],
           "dimension_sets": [
@@ -5782,7 +5782,7 @@
         },
         "test": "interactive_ui_tests",
         "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/",
-        "variant_id": "Lacros version skew testing ash 105.0.5179.0"
+        "variant_id": "Lacros version skew testing ash 105.0.5180.0"
       },
       {
         "isolate_profile_data": true,
@@ -5920,21 +5920,21 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5179.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5180.0/test_ash_chrome"
         ],
         "isolate_profile_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests Lacros version skew testing ash 105.0.5179.0",
+        "name": "lacros_chrome_browsertests Lacros version skew testing ash 105.0.5180.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v105.0.5179.0",
-              "revision": "version:105.0.5179.0"
+              "location": "lacros_version_skew_tests_v105.0.5180.0",
+              "revision": "version:105.0.5180.0"
             }
           ],
           "dimension_sets": [
@@ -5946,7 +5946,7 @@
         },
         "test": "lacros_chrome_browsertests",
         "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests/",
-        "variant_id": "Lacros version skew testing ash 105.0.5179.0"
+        "variant_id": "Lacros version skew testing ash 105.0.5180.0"
       },
       {
         "args": [
@@ -6066,21 +6066,21 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5179.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5180.0/test_ash_chrome"
         ],
         "isolate_profile_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 105.0.5179.0",
+        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 105.0.5180.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v105.0.5179.0",
-              "revision": "version:105.0.5179.0"
+              "location": "lacros_version_skew_tests_v105.0.5180.0",
+              "revision": "version:105.0.5180.0"
             }
           ],
           "dimension_sets": [
@@ -6092,7 +6092,7 @@
         },
         "test": "lacros_chrome_browsertests_run_in_series",
         "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests_run_in_series/",
-        "variant_id": "Lacros version skew testing ash 105.0.5179.0"
+        "variant_id": "Lacros version skew testing ash 105.0.5180.0"
       },
       {
         "isolate_profile_data": true,
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 89455c7..9d816cf 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -21191,6 +21191,1331 @@
       }
     ]
   },
+  "fuchsia-fyi-arm64-cfv2-script": {
+    "gtest_tests": [
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "absl_hardening_tests",
+        "test_id_prefix": "ninja://third_party/abseil-cpp:absl_hardening_tests/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "accessibility_unittests",
+        "test_id_prefix": "ninja://ui/accessibility:accessibility_unittests/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "aura_unittests",
+        "test_id_prefix": "ninja://ui/aura:aura_unittests/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "base_unittests",
+        "test_id_prefix": "ninja://base:base_unittests/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "blink_common_unittests",
+        "test_id_prefix": "ninja://third_party/blink/common:blink_common_unittests/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "blink_fuzzer_unittests",
+        "test_id_prefix": "ninja://third_party/blink/renderer/platform:blink_fuzzer_unittests/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "blink_heap_unittests",
+        "test_id_prefix": "ninja://third_party/blink/renderer/platform/heap:blink_heap_unittests/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "blink_platform_unittests",
+        "test_id_prefix": "ninja://third_party/blink/renderer/platform:blink_platform_unittests/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "blink_unittests",
+        "test_id_prefix": "ninja://third_party/blink/renderer/controller:blink_unittests/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "boringssl_crypto_tests",
+        "test_id_prefix": "ninja://third_party/boringssl:boringssl_crypto_tests/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "boringssl_ssl_tests",
+        "test_id_prefix": "ninja://third_party/boringssl:boringssl_ssl_tests/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "capture_unittests",
+        "test_id_prefix": "ninja://media/capture:capture_unittests/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "cast_runner_browsertests",
+        "test_id_prefix": "ninja://fuchsia_web/runners:cast_runner_browsertests/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "cast_runner_integration_tests_cfv1",
+        "test_id_prefix": "ninja://fuchsia_web/runners:cast_runner_integration_tests_cfv1/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "cast_runner_unittests",
+        "test_id_prefix": "ninja://fuchsia_web/runners:cast_runner_unittests/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "cc_unittests",
+        "test_id_prefix": "ninja://cc:cc_unittests/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "color_unittests",
+        "test_id_prefix": "ninja://ui/color:color_unittests/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "components_browsertests",
+        "test_id_prefix": "ninja://components:components_browsertests/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "components_unittests",
+        "test_id_prefix": "ninja://components:components_unittests/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "compositor_unittests",
+        "test_id_prefix": "ninja://ui/compositor:compositor_unittests/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "content_unittests",
+        "test_id_prefix": "ninja://content/test:content_unittests/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "courgette_unittests",
+        "test_id_prefix": "ninja://courgette:courgette_unittests/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "crypto_unittests",
+        "test_id_prefix": "ninja://crypto:crypto_unittests/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "display_unittests",
+        "test_id_prefix": "ninja://ui/display:display_unittests/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "events_unittests",
+        "test_id_prefix": "ninja://ui/events:events_unittests/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "extensions_unittests",
+        "test_id_prefix": "ninja://extensions:extensions_unittests/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "filesystem_service_unittests",
+        "test_id_prefix": "ninja://components/services/filesystem:filesystem_service_unittests/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "fuchsia_mojo_unittests",
+        "test_id_prefix": "ninja://mojo/public/cpp/base/fuchsia:fuchsia_mojo_unittests/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "gcm_unit_tests",
+        "test_id_prefix": "ninja://google_apis/gcm:gcm_unit_tests/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "gfx_unittests",
+        "test_id_prefix": "ninja://ui/gfx:gfx_unittests/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "gin_unittests",
+        "test_id_prefix": "ninja://gin:gin_unittests/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "google_apis_unittests",
+        "test_id_prefix": "ninja://google_apis:google_apis_unittests/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "gpu_unittests",
+        "test_id_prefix": "ninja://gpu:gpu_unittests/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "gwp_asan_unittests",
+        "test_id_prefix": "ninja://components/gwp_asan:gwp_asan_unittests/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "headless_browsertests",
+        "test_id_prefix": "ninja://headless:headless_browsertests/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "headless_unittests",
+        "test_id_prefix": "ninja://headless:headless_unittests/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "ipc_tests",
+        "test_id_prefix": "ninja://ipc:ipc_tests/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "latency_unittests",
+        "test_id_prefix": "ninja://ui/latency:latency_unittests/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "media_unittests",
+        "test_id_prefix": "ninja://media:media_unittests/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "message_center_unittests",
+        "test_id_prefix": "ninja://ui/message_center:message_center_unittests/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "midi_unittests",
+        "test_id_prefix": "ninja://media/midi:midi_unittests/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "mojo_core_unittests",
+        "test_id_prefix": "ninja://mojo/core:mojo_core_unittests/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "mojo_unittests",
+        "test_id_prefix": "ninja://mojo:mojo_unittests/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "native_theme_unittests",
+        "test_id_prefix": "ninja://ui/native_theme:native_theme_unittests/"
+      },
+      {
+        "args": [
+          "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.net_unittests.filter"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 4
+        },
+        "test": "net_unittests",
+        "test_id_prefix": "ninja://net:net_unittests/"
+      },
+      {
+        "args": [
+          "--",
+          "--ozone-platform=headless"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "ozone_gl_unittests",
+        "test_id_prefix": "ninja://ui/ozone/gl:ozone_gl_unittests/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "ozone_unittests",
+        "test_id_prefix": "ninja://ui/ozone:ozone_unittests/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "perfetto_unittests",
+        "test_id_prefix": "ninja://third_party/perfetto:perfetto_unittests/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "service_manager_unittests",
+        "test_id_prefix": "ninja://services/service_manager/tests:service_manager_unittests/"
+      },
+      {
+        "args": [
+          "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.services_unittests.filter"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "services_unittests",
+        "test_id_prefix": "ninja://services:services_unittests/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "shell_dialogs_unittests",
+        "test_id_prefix": "ninja://ui/shell_dialogs:shell_dialogs_unittests/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "skia_unittests",
+        "test_id_prefix": "ninja://skia:skia_unittests/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "snapshot_unittests",
+        "test_id_prefix": "ninja://ui/snapshot:snapshot_unittests/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "sql_unittests",
+        "test_id_prefix": "ninja://sql:sql_unittests/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "storage_unittests",
+        "test_id_prefix": "ninja://storage:storage_unittests/"
+      },
+      {
+        "args": [
+          "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.ui_base_unittests.filter"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "ui_base_unittests",
+        "test_id_prefix": "ninja://ui/base:ui_base_unittests/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "ui_touch_selection_unittests",
+        "test_id_prefix": "ninja://ui/touch_selection:ui_touch_selection_unittests/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "url_unittests",
+        "test_id_prefix": "ninja://url:url_unittests/"
+      },
+      {
+        "args": [
+          "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.views_examples_unittests.filter"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "views_examples_unittests",
+        "test_id_prefix": "ninja://ui/views/examples:views_examples_unittests/"
+      },
+      {
+        "args": [
+          "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.views_unittests.filter"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "views_unittests",
+        "test_id_prefix": "ninja://ui/views:views_unittests/"
+      },
+      {
+        "args": [
+          "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.viz_unittests.filter"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "viz_unittests",
+        "test_id_prefix": "ninja://components/viz:viz_unittests/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "web_engine_browsertests",
+        "test_id_prefix": "ninja://fuchsia_web/webengine:web_engine_browsertests/"
+      },
+      {
+        "args": [
+          "--",
+          "--vmodule=test_navigation_listener=1"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "web_engine_integration_tests",
+        "test_id_prefix": "ninja://fuchsia_web/webengine:web_engine_integration_tests/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "web_engine_unittests",
+        "test_id_prefix": "ninja://fuchsia_web/webengine:web_engine_unittests/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "web_runner_integration_tests",
+        "test_id_prefix": "ninja://fuchsia_web/runners:web_runner_integration_tests/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "wm_unittests",
+        "test_id_prefix": "ninja://ui/wm:wm_unittests/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "wtf_unittests",
+        "test_id_prefix": "ninja://third_party/blink/renderer/platform/wtf:wtf_unittests/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "zlib_unittests",
+        "test_id_prefix": "ninja://third_party/zlib:zlib_unittests/"
+      }
+    ]
+  },
   "fuchsia-fyi-arm64-emu-arg": {
     "additional_compile_targets": [
       "all"
@@ -48356,7 +49681,7 @@
   },
   "ios-simulator-cronet (reclient shadow)": {
     "additional_compile_targets": [
-      "all"
+      "cronet_test"
     ]
   },
   "ios-simulator-multi-window": {
@@ -93240,21 +94565,21 @@
       {
         "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_v105.0.5179.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5180.0/test_ash_chrome"
         ],
         "isolate_profile_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "interactive_ui_tests Lacros version skew testing ash 105.0.5179.0",
+        "name": "interactive_ui_tests Lacros version skew testing ash 105.0.5180.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v105.0.5179.0",
-              "revision": "version:105.0.5179.0"
+              "location": "lacros_version_skew_tests_v105.0.5180.0",
+              "revision": "version:105.0.5180.0"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -93262,7 +94587,7 @@
         },
         "test": "interactive_ui_tests",
         "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/",
-        "variant_id": "Lacros version skew testing ash 105.0.5179.0"
+        "variant_id": "Lacros version skew testing ash 105.0.5180.0"
       },
       {
         "isolate_profile_data": true,
@@ -93375,28 +94700,28 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5179.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5180.0/test_ash_chrome"
         ],
         "isolate_profile_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests Lacros version skew testing ash 105.0.5179.0",
+        "name": "lacros_chrome_browsertests Lacros version skew testing ash 105.0.5180.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v105.0.5179.0",
-              "revision": "version:105.0.5179.0"
+              "location": "lacros_version_skew_tests_v105.0.5180.0",
+              "revision": "version:105.0.5180.0"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "lacros_chrome_browsertests",
         "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests/",
-        "variant_id": "Lacros version skew testing ash 105.0.5179.0"
+        "variant_id": "Lacros version skew testing ash 105.0.5180.0"
       },
       {
         "args": [
@@ -93496,28 +94821,28 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5179.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5180.0/test_ash_chrome"
         ],
         "isolate_profile_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 105.0.5179.0",
+        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 105.0.5180.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v105.0.5179.0",
-              "revision": "version:105.0.5179.0"
+              "location": "lacros_version_skew_tests_v105.0.5180.0",
+              "revision": "version:105.0.5180.0"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test": "lacros_chrome_browsertests_run_in_series",
         "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests_run_in_series/",
-        "variant_id": "Lacros version skew testing ash 105.0.5179.0"
+        "variant_id": "Lacros version skew testing ash 105.0.5180.0"
       },
       {
         "isolate_profile_data": true,
@@ -94855,20 +96180,20 @@
       {
         "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_v105.0.5179.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5180.0/test_ash_chrome"
         ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "interactive_ui_tests Lacros version skew testing ash 105.0.5179.0",
+        "name": "interactive_ui_tests Lacros version skew testing ash 105.0.5180.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v105.0.5179.0",
-              "revision": "version:105.0.5179.0"
+              "location": "lacros_version_skew_tests_v105.0.5180.0",
+              "revision": "version:105.0.5180.0"
             }
           ],
           "dimension_sets": [
@@ -94882,7 +96207,7 @@
         },
         "test": "interactive_ui_tests",
         "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/",
-        "variant_id": "Lacros version skew testing ash 105.0.5179.0"
+        "variant_id": "Lacros version skew testing ash 105.0.5180.0"
       },
       {
         "merge": {
@@ -95020,20 +96345,20 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5179.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5180.0/test_ash_chrome"
         ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests Lacros version skew testing ash 105.0.5179.0",
+        "name": "lacros_chrome_browsertests Lacros version skew testing ash 105.0.5180.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v105.0.5179.0",
-              "revision": "version:105.0.5179.0"
+              "location": "lacros_version_skew_tests_v105.0.5180.0",
+              "revision": "version:105.0.5180.0"
             }
           ],
           "dimension_sets": [
@@ -95046,7 +96371,7 @@
         },
         "test": "lacros_chrome_browsertests",
         "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests/",
-        "variant_id": "Lacros version skew testing ash 105.0.5179.0"
+        "variant_id": "Lacros version skew testing ash 105.0.5180.0"
       },
       {
         "args": [
@@ -95166,20 +96491,20 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5179.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5180.0/test_ash_chrome"
         ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 105.0.5179.0",
+        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 105.0.5180.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v105.0.5179.0",
-              "revision": "version:105.0.5179.0"
+              "location": "lacros_version_skew_tests_v105.0.5180.0",
+              "revision": "version:105.0.5180.0"
             }
           ],
           "dimension_sets": [
@@ -95192,7 +96517,7 @@
         },
         "test": "lacros_chrome_browsertests_run_in_series",
         "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests_run_in_series/",
-        "variant_id": "Lacros version skew testing ash 105.0.5179.0"
+        "variant_id": "Lacros version skew testing ash 105.0.5180.0"
       },
       {
         "merge": {
@@ -96688,20 +98013,20 @@
       {
         "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_v105.0.5179.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5180.0/test_ash_chrome"
         ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "interactive_ui_tests Lacros version skew testing ash 105.0.5179.0",
+        "name": "interactive_ui_tests Lacros version skew testing ash 105.0.5180.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v105.0.5179.0",
-              "revision": "version:105.0.5179.0"
+              "location": "lacros_version_skew_tests_v105.0.5180.0",
+              "revision": "version:105.0.5180.0"
             }
           ],
           "dimension_sets": [
@@ -96715,7 +98040,7 @@
         },
         "test": "interactive_ui_tests",
         "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/",
-        "variant_id": "Lacros version skew testing ash 105.0.5179.0"
+        "variant_id": "Lacros version skew testing ash 105.0.5180.0"
       },
       {
         "merge": {
@@ -96853,20 +98178,20 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5179.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5180.0/test_ash_chrome"
         ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests Lacros version skew testing ash 105.0.5179.0",
+        "name": "lacros_chrome_browsertests Lacros version skew testing ash 105.0.5180.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v105.0.5179.0",
-              "revision": "version:105.0.5179.0"
+              "location": "lacros_version_skew_tests_v105.0.5180.0",
+              "revision": "version:105.0.5180.0"
             }
           ],
           "dimension_sets": [
@@ -96879,7 +98204,7 @@
         },
         "test": "lacros_chrome_browsertests",
         "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests/",
-        "variant_id": "Lacros version skew testing ash 105.0.5179.0"
+        "variant_id": "Lacros version skew testing ash 105.0.5180.0"
       },
       {
         "args": [
@@ -96999,20 +98324,20 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5179.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5180.0/test_ash_chrome"
         ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 105.0.5179.0",
+        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 105.0.5180.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v105.0.5179.0",
-              "revision": "version:105.0.5179.0"
+              "location": "lacros_version_skew_tests_v105.0.5180.0",
+              "revision": "version:105.0.5180.0"
             }
           ],
           "dimension_sets": [
@@ -97025,7 +98350,7 @@
         },
         "test": "lacros_chrome_browsertests_run_in_series",
         "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests_run_in_series/",
-        "variant_id": "Lacros version skew testing ash 105.0.5179.0"
+        "variant_id": "Lacros version skew testing ash 105.0.5180.0"
       },
       {
         "merge": {
@@ -97760,20 +99085,20 @@
       {
         "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_v105.0.5179.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5180.0/test_ash_chrome"
         ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "interactive_ui_tests Lacros version skew testing ash 105.0.5179.0",
+        "name": "interactive_ui_tests Lacros version skew testing ash 105.0.5180.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v105.0.5179.0",
-              "revision": "version:105.0.5179.0"
+              "location": "lacros_version_skew_tests_v105.0.5180.0",
+              "revision": "version:105.0.5180.0"
             }
           ],
           "dimension_sets": [
@@ -97786,7 +99111,7 @@
         },
         "test": "interactive_ui_tests",
         "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/",
-        "variant_id": "Lacros version skew testing ash 105.0.5179.0"
+        "variant_id": "Lacros version skew testing ash 105.0.5180.0"
       }
     ]
   },
diff --git a/testing/buildbot/chromium.memory.json b/testing/buildbot/chromium.memory.json
index 4e76d76..7f20e53 100644
--- a/testing/buildbot/chromium.memory.json
+++ b/testing/buildbot/chromium.memory.json
@@ -20875,21 +20875,21 @@
       {
         "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_v105.0.5179.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5180.0/test_ash_chrome"
         ],
         "isolate_profile_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "interactive_ui_tests Lacros version skew testing ash 105.0.5179.0",
+        "name": "interactive_ui_tests Lacros version skew testing ash 105.0.5180.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v105.0.5179.0",
-              "revision": "version:105.0.5179.0"
+              "location": "lacros_version_skew_tests_v105.0.5180.0",
+              "revision": "version:105.0.5180.0"
             }
           ],
           "dimension_sets": [
@@ -20902,7 +20902,7 @@
         },
         "test": "interactive_ui_tests",
         "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/",
-        "variant_id": "Lacros version skew testing ash 105.0.5179.0"
+        "variant_id": "Lacros version skew testing ash 105.0.5180.0"
       },
       {
         "isolate_profile_data": true,
@@ -21040,21 +21040,21 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5179.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5180.0/test_ash_chrome"
         ],
         "isolate_profile_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests Lacros version skew testing ash 105.0.5179.0",
+        "name": "lacros_chrome_browsertests Lacros version skew testing ash 105.0.5180.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v105.0.5179.0",
-              "revision": "version:105.0.5179.0"
+              "location": "lacros_version_skew_tests_v105.0.5180.0",
+              "revision": "version:105.0.5180.0"
             }
           ],
           "dimension_sets": [
@@ -21066,7 +21066,7 @@
         },
         "test": "lacros_chrome_browsertests",
         "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests/",
-        "variant_id": "Lacros version skew testing ash 105.0.5179.0"
+        "variant_id": "Lacros version skew testing ash 105.0.5180.0"
       },
       {
         "args": [
@@ -21186,21 +21186,21 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5179.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5180.0/test_ash_chrome"
         ],
         "isolate_profile_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 105.0.5179.0",
+        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 105.0.5180.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v105.0.5179.0",
-              "revision": "version:105.0.5179.0"
+              "location": "lacros_version_skew_tests_v105.0.5180.0",
+              "revision": "version:105.0.5180.0"
             }
           ],
           "dimension_sets": [
@@ -21212,7 +21212,7 @@
         },
         "test": "lacros_chrome_browsertests_run_in_series",
         "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests_run_in_series/",
-        "variant_id": "Lacros version skew testing ash 105.0.5179.0"
+        "variant_id": "Lacros version skew testing ash 105.0.5180.0"
       },
       {
         "isolate_profile_data": true,
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index 3a593892..f564556 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -4186,16 +4186,6 @@
           'has_native_resultdb_integration',
         ],
       },
-      'ozone_unittests': {
-        'timeout_sec': 3600,
-        'args': [
-          '--test-launcher-filter-file=../../testing/buildbot/filters/lacros-arm.ozone_unittests.filter']
-      },
-      'viz_unittests': {
-        'timeout_sec': 3600,
-        'args': [
-          '--test-launcher-filter-file=../../testing/buildbot/filters/lacros-arm.viz_unittests.filter']
-      },
     },
 
     # create this temporary lacros arm test suites that runs on skylab
@@ -7558,6 +7548,7 @@
           'CROS_JACUZZI_RELEASE_LKGM',
           'CROS_JACUZZI_RELEASE_DEV',
           'CROS_JACUZZI_RELEASE_BETA',
+          'CROS_JACUZZI_RELEASE_STABLE',
         ]
       },
     },
diff --git a/testing/buildbot/variants.pyl b/testing/buildbot/variants.pyl
index 80d414556..cfe2fc13 100644
--- a/testing/buildbot/variants.pyl
+++ b/testing/buildbot/variants.pyl
@@ -22,15 +22,15 @@
   },
   'LACROS_VERSION_SKEW_CANARY': {
     'args': [
-      '--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5179.0/test_ash_chrome',
+      '--ash-chrome-path-override=../../lacros_version_skew_tests_v105.0.5180.0/test_ash_chrome',
     ],
-    'identifier': 'Lacros version skew testing ash 105.0.5179.0',
+    'identifier': 'Lacros version skew testing ash 105.0.5180.0',
     'swarming': {
       'cipd_packages': [
         {
           'cipd_package': 'chromium/testing/linux-ash-chromium/x86_64/ash.zip',
-          'location': 'lacros_version_skew_tests_v105.0.5179.0',
-          'revision': 'version:105.0.5179.0',
+          'location': 'lacros_version_skew_tests_v105.0.5180.0',
+          'revision': 'version:105.0.5180.0',
         },
       ],
     },
@@ -1103,6 +1103,15 @@
     'enabled': True,
     'identifier': 'JACUZZI_RELEASE_BETA',
   },
+  'CROS_JACUZZI_RELEASE_STABLE': {
+    'skylab': {
+      'cros_board': 'jacuzzi',
+      'cros_chrome_version': '103.0.5060.114',
+      'cros_img': 'jacuzzi-release/R103-14816.99.0',
+    },
+    'enabled': True,
+    'identifier': 'JACUZZI_RELEASE_STABLE',
+  },
   'CROS_OCTOPUS_FULL': {
     'skylab': {
       'cros_board': 'octopus',
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index d249a8e..bb675b8 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -3135,6 +3135,18 @@
           'linux-bionic',
         ],
       },
+      # A temporary builder to test new Fuchsia testing scripts on arm64.
+      'fuchsia-fyi-arm64-cfv2-script': {
+        'os_type': 'fuchsia',
+        'mixins': [
+          'arm64',
+          'docker',
+          'linux-focal',
+        ],
+        'test_suites': {
+          'gtest_tests': 'fuchsia_gtests',
+        },
+      },
       # A temporary builder to test the changes to the emulator arguments on
       # arm64.
       'fuchsia-fyi-arm64-emu-arg': {
@@ -3339,7 +3351,7 @@
       },
       'ios-simulator-cronet (reclient shadow)': {
         'additional_compile_targets': [
-          'all',
+          'cronet_test',
         ],
         'mixins': [
           'has_native_resultdb_integration',
diff --git a/testing/scripts/check_network_annotations.py b/testing/scripts/check_network_annotations.py
index 7d05b49a..d9f6c71 100755
--- a/testing/scripts/check_network_annotations.py
+++ b/testing/scripts/check_network_annotations.py
@@ -6,7 +6,12 @@
 """//testing/scripts wrapper for the network traffic annotations checks.
 This script is used to run check_annotations.py on the trybots to ensure that
 all network traffic annotations have correct syntax and semantics, and all
-functions requiring annotations have one."""
+functions requiring annotations have one.
+
+This is a wrapper around tools/traffic_annotation/scripts/auditor.py.
+
+See tools/traffic_annotation/scripts/auditor/README.md for instructions on
+running locally."""
 
 import json
 import os
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 5e7f8a7..7d4ae303 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -917,80 +917,6 @@
             ]
         }
     ],
-    "AutofillEnableSupportForMoreStructureInAddresses": [
-        {
-            "platforms": [
-                "android",
-                "chromeos",
-                "chromeos_lacros",
-                "ios",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled_2021-12-01",
-                    "enable_features": [
-                        "AutofillEnableSupportForMoreStructureInAddresses"
-                    ]
-                }
-            ]
-        }
-    ],
-    "AutofillEnableSupportForMoreStructureInAddressesOnWebView": [
-        {
-            "platforms": [
-                "android_webview",
-                "android_weblayer"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "AutofillEnableSupportForMoreStructureInAddresses"
-                    ]
-                }
-            ]
-        }
-    ],
-    "AutofillEnableSupportForMoreStructureInNames": [
-        {
-            "platforms": [
-                "android",
-                "chromeos",
-                "chromeos_lacros",
-                "ios",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled_2021-12-01",
-                    "enable_features": [
-                        "AutofillEnableSupportForMoreStructureInNames"
-                    ]
-                }
-            ]
-        }
-    ],
-    "AutofillEnableSupportForMoreStructureInNamesOnWebView": [
-        {
-            "platforms": [
-                "android_webview",
-                "android_weblayer"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "AutofillEnableSupportForMoreStructureInNames"
-                    ]
-                }
-            ]
-        }
-    ],
     "AutofillEnableSupportForParsingWithSharedLabels": [
         {
             "platforms": [
@@ -3231,7 +3157,7 @@
                     "enable_features": [
                         "NtpChromeCartModule",
                         "NtpDriveModule",
-                        "NtpModules",
+                        "NtpModulesLoadTimeoutMilliseconds",
                         "NtpPhotosModule",
                         "NtpRecipeTasksModule"
                     ],
@@ -3254,8 +3180,8 @@
                     "enable_features": [
                         "NtpChromeCartModule",
                         "NtpDriveModule",
-                        "NtpModules",
                         "NtpModulesFirstRunExperience",
+                        "NtpModulesLoadTimeoutMilliseconds",
                         "NtpRecipeTasksModule"
                     ],
                     "disable_features": [
@@ -3276,7 +3202,7 @@
                     "enable_features": [
                         "NtpChromeCartModule",
                         "NtpDriveModule",
-                        "NtpModules",
+                        "NtpModulesLoadTimeoutMilliseconds",
                         "NtpRecipeTasksModule"
                     ],
                     "disable_features": [
@@ -3298,8 +3224,8 @@
                     "enable_features": [
                         "NtpChromeCartModule",
                         "NtpDriveModule",
-                        "NtpModules",
                         "NtpModulesFirstRunExperience",
+                        "NtpModulesLoadTimeoutMilliseconds",
                         "NtpRecipeTasksModule"
                     ],
                     "disable_features": [
@@ -3320,8 +3246,8 @@
                     "enable_features": [
                         "NtpChromeCartModule",
                         "NtpDriveModule",
-                        "NtpModules",
                         "NtpModulesFirstRunExperience",
+                        "NtpModulesLoadTimeoutMilliseconds",
                         "NtpRecipeTasksModule"
                     ],
                     "disable_features": [
@@ -4166,21 +4092,6 @@
             ]
         }
     ],
-    "Force60Hz": [
-        {
-            "platforms": [
-                "mac"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "Force60Hz"
-                    ]
-                }
-            ]
-        }
-    ],
     "GMSCoreEmoji": [
         {
             "platforms": [
diff --git a/third_party/blink/public/mojom/blob/data_element.mojom b/third_party/blink/public/mojom/blob/data_element.mojom
index 580559c2..171ca59 100644
--- a/third_party/blink/public/mojom/blob/data_element.mojom
+++ b/third_party/blink/public/mojom/blob/data_element.mojom
@@ -16,8 +16,6 @@
   DataElementBytes bytes;
   // A reference to a file on disk.
   DataElementFile file;
-  // A reference to a file as a filesystem URL.
-  DataElementFilesystemURL file_filesystem;
   // A reference to another blob.
   DataElementBlob blob;
 };
@@ -84,21 +82,6 @@
   mojo_base.mojom.Time? expected_modification_time;
 };
 
-// A reference to a slice of a file as a filesystem URL.
-struct DataElementFilesystemURL {
-  // URL of the file.
-  url.mojom.Url url;
-  // Offset inside the file.
-  uint64 offset;
-  // Length of the slice. Can be uint64_max if the length is unknown. If this is
-  // the case the offset is always 0 and this DataElement should be the only
-  // element making up the blob.
-  uint64 length;
-  // Expected modification time of the file being referenced. Can be null if the
-  // modification time is unknown.
-  mojo_base.mojom.Time? expected_modification_time;
-};
-
 // A reference to a slice of another blob.
 struct DataElementBlob {
   // The blob being referenced.
diff --git a/third_party/blink/public/mojom/prerender/OWNERS b/third_party/blink/public/mojom/prerender/OWNERS
index 066d342..7f561c4 100644
--- a/third_party/blink/public/mojom/prerender/OWNERS
+++ b/third_party/blink/public/mojom/prerender/OWNERS
@@ -1,5 +1,5 @@
 file://chrome/browser/prefetch/no_state_prefetch/OWNERS
-file://content/browser/prerender/OWNERS
+file://content/browser/preloading/prerender/OWNERS
 
 per-file *.mojom=set noparent
 per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/third_party/blink/public/platform/web_v8_value_converter.h b/third_party/blink/public/platform/web_v8_value_converter.h
index e5663f7..a622e9b9 100644
--- a/third_party/blink/public/platform/web_v8_value_converter.h
+++ b/third_party/blink/public/platform/web_v8_value_converter.h
@@ -13,6 +13,7 @@
 
 namespace base {
 class Value;
+class ValueView;
 }
 
 namespace blink {
@@ -44,7 +45,7 @@
   // while setting a value, that property or item is skipped, leaving a hole in
   // the case of arrays.
   // TODO(dcheng): This should just take a const reference.
-  virtual v8::Local<v8::Value> ToV8Value(const base::Value* value,
+  virtual v8::Local<v8::Value> ToV8Value(base::ValueView value,
                                          v8::Local<v8::Context> context) = 0;
 
   // Converts a v8::Value to base::Value.
diff --git a/third_party/blink/public/web/web_view.h b/third_party/blink/public/web/web_view.h
index 99f0ceb..b01562eb 100644
--- a/third_party/blink/public/web/web_view.h
+++ b/third_party/blink/public/web/web_view.h
@@ -100,7 +100,7 @@
   // clients may be null, but should both be null or not together.
   // |is_hidden| defines the initial visibility of the page.
   // |is_prerendering| defines whether the page is being prerendered by the
-  // Prerender2 feature (see content/browser/prerender/README.md).
+  // Prerender2 feature (see content/browser/preloading/prerender/README.md).
   // [is_inside_portal] defines whether the page is inside_portal.
   // [is_fenced_frame] defines whether the page is for a fenced frame.
   // |compositing_enabled| dictates whether accelerated compositing should be
diff --git a/third_party/blink/renderer/bindings/core/v8/serialization/serialized_color_params.cc b/third_party/blink/renderer/bindings/core/v8/serialization/serialized_color_params.cc
index 2e9d726ba..e29187c 100644
--- a/third_party/blink/renderer/bindings/core/v8/serialization/serialized_color_params.cc
+++ b/third_party/blink/renderer/bindings/core/v8/serialization/serialized_color_params.cc
@@ -108,7 +108,9 @@
 
 SerializedImageBitmapSettings::SerializedImageBitmapSettings() = default;
 
-SerializedImageBitmapSettings::SerializedImageBitmapSettings(SkImageInfo info)
+SerializedImageBitmapSettings::SerializedImageBitmapSettings(
+    SkImageInfo info,
+    ImageOrientationEnum image_orientation)
     : sk_color_space_(kSerializedParametricColorSpaceLength) {
   auto color_space =
       info.colorSpace() ? info.refColorSpace() : SkColorSpace::MakeSRGB();
@@ -167,6 +169,33 @@
       is_premultiplied_ = true;
       break;
   }
+
+  switch (image_orientation) {
+    case ImageOrientationEnum::kOriginTopLeft:
+      image_orientation_ = SerializedImageOrientation::kTopLeft;
+      break;
+    case ImageOrientationEnum::kOriginTopRight:
+      image_orientation_ = SerializedImageOrientation::kTopRight;
+      break;
+    case ImageOrientationEnum::kOriginBottomRight:
+      image_orientation_ = SerializedImageOrientation::kBottomRight;
+      break;
+    case ImageOrientationEnum::kOriginBottomLeft:
+      image_orientation_ = SerializedImageOrientation::kBottomLeft;
+      break;
+    case ImageOrientationEnum::kOriginLeftTop:
+      image_orientation_ = SerializedImageOrientation::kLeftTop;
+      break;
+    case ImageOrientationEnum::kOriginRightTop:
+      image_orientation_ = SerializedImageOrientation::kRightTop;
+      break;
+    case ImageOrientationEnum::kOriginRightBottom:
+      image_orientation_ = SerializedImageOrientation::kRightBottom;
+      break;
+    case ImageOrientationEnum::kOriginLeftBottom:
+      image_orientation_ = SerializedImageOrientation::kLeftBottom;
+      break;
+  }
 }
 
 SerializedImageBitmapSettings::SerializedImageBitmapSettings(
@@ -174,12 +203,14 @@
     const Vector<double>& sk_color_space,
     SerializedPixelFormat pixel_format,
     SerializedOpacityMode opacity_mode,
-    uint32_t is_premultiplied)
+    uint32_t is_premultiplied,
+    SerializedImageOrientation image_orientation)
     : color_space_(color_space),
       sk_color_space_(sk_color_space),
       pixel_format_(pixel_format),
       opacity_mode_(opacity_mode),
-      is_premultiplied_(is_premultiplied) {}
+      is_premultiplied_(is_premultiplied),
+      image_orientation_(image_orientation) {}
 
 SkImageInfo SerializedImageBitmapSettings::GetSkImageInfo(
     uint32_t width,
@@ -235,4 +266,28 @@
                            std::move(sk_color_space));
 }
 
+ImageOrientationEnum SerializedImageBitmapSettings::GetImageOrientation()
+    const {
+  switch (image_orientation_) {
+    case SerializedImageOrientation::kTopLeft:
+      return ImageOrientationEnum::kOriginTopLeft;
+    case SerializedImageOrientation::kTopRight:
+      return ImageOrientationEnum::kOriginTopRight;
+    case SerializedImageOrientation::kBottomRight:
+      return ImageOrientationEnum::kOriginBottomRight;
+    case SerializedImageOrientation::kBottomLeft:
+      return ImageOrientationEnum::kOriginBottomLeft;
+    case SerializedImageOrientation::kLeftTop:
+      return ImageOrientationEnum::kOriginLeftTop;
+    case SerializedImageOrientation::kRightTop:
+      return ImageOrientationEnum::kOriginRightTop;
+    case SerializedImageOrientation::kRightBottom:
+      return ImageOrientationEnum::kOriginRightBottom;
+    case SerializedImageOrientation::kLeftBottom:
+      return ImageOrientationEnum::kOriginLeftBottom;
+  }
+  NOTREACHED();
+  return ImageOrientationEnum::kOriginTopLeft;
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/bindings/core/v8/serialization/serialized_color_params.h b/third_party/blink/renderer/bindings/core/v8/serialization/serialized_color_params.h
index a503d93..88c4e8e 100644
--- a/third_party/blink/renderer/bindings/core/v8/serialization/serialized_color_params.h
+++ b/third_party/blink/renderer/bindings/core/v8/serialization/serialized_color_params.h
@@ -7,6 +7,7 @@
 
 #include "third_party/blink/renderer/core/html/canvas/image_data.h"
 #include "third_party/blink/renderer/platform/graphics/canvas_color_params.h"
+#include "third_party/blink/renderer/platform/graphics/image_orientation.h"
 
 namespace blink {
 
@@ -41,8 +42,10 @@
   // row-major order that converts an r,g,b triple in the linear color space to
   // x,y,z in the XYZ D50 color space.
   kParametricColorSpaceTag = 7,
+  // followed by a SerializedImageOrientation enum, used only for ImageBitmap.
+  kImageOrientationTag = 8,
 
-  kLast = kParametricColorSpaceTag,
+  kLast = kImageOrientationTag,
 };
 
 // This enumeration specifies the values used to serialize PredefinedColorSpace.
@@ -89,6 +92,18 @@
   kLast = kOpaque,
 };
 
+enum class SerializedImageOrientation : uint32_t {
+  kTopLeft = 0,
+  kTopRight = 1,
+  kBottomRight = 2,
+  kBottomLeft = 3,
+  kLeftTop = 4,
+  kRightTop = 5,
+  kRightBottom = 6,
+  kLeftBottom = 7,
+  kLast = kLeftBottom,
+};
+
 class SerializedImageDataSettings {
  public:
   SerializedImageDataSettings(PredefinedColorSpace, ImageDataStorageFormat);
@@ -120,14 +135,16 @@
 class SerializedImageBitmapSettings {
  public:
   SerializedImageBitmapSettings();
-  explicit SerializedImageBitmapSettings(SkImageInfo);
+  explicit SerializedImageBitmapSettings(SkImageInfo, ImageOrientationEnum);
   SerializedImageBitmapSettings(SerializedPredefinedColorSpace,
                                 const Vector<double>& sk_color_space,
                                 SerializedPixelFormat,
                                 SerializedOpacityMode,
-                                uint32_t is_premultiplied);
+                                uint32_t is_premultiplied,
+                                SerializedImageOrientation);
 
   SkImageInfo GetSkImageInfo(uint32_t width, uint32_t height) const;
+  ImageOrientationEnum GetImageOrientation() const;
 
   const Vector<double>& GetSerializedSkColorSpace() { return sk_color_space_; }
   SerializedPixelFormat GetSerializedPixelFormat() const {
@@ -137,6 +154,9 @@
     return opacity_mode_;
   }
   uint32_t IsPremultiplied() const { return is_premultiplied_; }
+  SerializedImageOrientation GetSerializedImageOrientation() const {
+    return image_orientation_;
+  }
 
  private:
   SerializedPredefinedColorSpace color_space_ =
@@ -145,6 +165,8 @@
   SerializedPixelFormat pixel_format_ = SerializedPixelFormat::kRGBA8;
   SerializedOpacityMode opacity_mode_ = SerializedOpacityMode::kNonOpaque;
   bool is_premultiplied_ = true;
+  SerializedImageOrientation image_orientation_ =
+      SerializedImageOrientation::kTopLeft;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_deserializer.cc b/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_deserializer.cc
index f290381..138b341 100644
--- a/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_deserializer.cc
+++ b/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_deserializer.cc
@@ -323,6 +323,8 @@
           SerializedPixelFormat::kNative8_LegacyObsolete;
       SerializedOpacityMode canvas_opacity_mode =
           SerializedOpacityMode::kOpaque;
+      SerializedImageOrientation image_orientation =
+          SerializedImageOrientation::kTopLeft;
       uint32_t origin_clean = 0, is_premultiplied = 0, width = 0, height = 0,
                byte_length = 0;
       const void* pixels = nullptr;
@@ -366,6 +368,11 @@
               if (!ReadUint32(&is_premultiplied) || is_premultiplied > 1)
                 return nullptr;
               break;
+            case ImageSerializationTag::kImageOrientationTag:
+              if (!ReadUint32Enum<SerializedImageOrientation>(
+                      &image_orientation))
+                return nullptr;
+              break;
             case ImageSerializationTag::kImageDataStorageFormatTag:
               // Does not apply to ImageBitmap.
               return nullptr;
@@ -378,11 +385,10 @@
       if (!ReadUint32(&width) || !ReadUint32(&height) ||
           !ReadUint32(&byte_length) || !ReadRawBytes(byte_length, &pixels))
         return nullptr;
-      SkImageInfo info =
-          SerializedImageBitmapSettings(predefined_color_space, sk_color_space,
-                                        canvas_pixel_format,
-                                        canvas_opacity_mode, is_premultiplied)
-              .GetSkImageInfo(width, height);
+      SerializedImageBitmapSettings settings(
+          predefined_color_space, sk_color_space, canvas_pixel_format,
+          canvas_opacity_mode, is_premultiplied, image_orientation);
+      SkImageInfo info = settings.GetSkImageInfo(width, height);
       base::CheckedNumeric<uint32_t> computed_byte_length =
           info.computeMinByteSize();
       if (!computed_byte_length.IsValid() ||
@@ -394,7 +400,8 @@
         return nullptr;
       }
       SkPixmap pixmap(info, pixels, info.minRowBytes());
-      return MakeGarbageCollected<ImageBitmap>(pixmap, origin_clean);
+      return MakeGarbageCollected<ImageBitmap>(pixmap, origin_clean,
+                                               settings.GetImageOrientation());
     }
     case kImageBitmapTransferTag: {
       uint32_t index = 0;
@@ -437,6 +444,7 @@
             case ImageSerializationTag::kIsPremultipliedTag:
             case ImageSerializationTag::kCanvasOpacityModeTag:
             case ImageSerializationTag::kParametricColorSpaceTag:
+            case ImageSerializationTag::kImageOrientationTag:
               // Does not apply to ImageData.
               return nullptr;
           }
diff --git a/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_serializer.cc b/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_serializer.cc
index c3a86c4..cd242a1 100644
--- a/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_serializer.cc
+++ b/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_serializer.cc
@@ -466,27 +466,36 @@
     }
     WriteTag(kImageBitmapTag);
     SkImageInfo info = image_bitmap->GetBitmapSkImageInfo();
-    SerializedImageBitmapSettings color_params(info);
+    SerializedImageBitmapSettings bitmap_settings(
+        info, image_bitmap->ImageOrientation());
     WriteUint32Enum(ImageSerializationTag::kParametricColorSpaceTag);
-    DCHECK_EQ(color_params.GetSerializedSkColorSpace().size(),
+    DCHECK_EQ(bitmap_settings.GetSerializedSkColorSpace().size(),
               kSerializedParametricColorSpaceLength);
-    for (const auto& value : color_params.GetSerializedSkColorSpace())
+    for (const auto& value : bitmap_settings.GetSerializedSkColorSpace())
       WriteDouble(value);
     WriteUint32Enum(ImageSerializationTag::kCanvasPixelFormatTag);
-    WriteUint32Enum(color_params.GetSerializedPixelFormat());
+    WriteUint32Enum(bitmap_settings.GetSerializedPixelFormat());
     WriteUint32Enum(ImageSerializationTag::kCanvasOpacityModeTag);
-    WriteUint32Enum(color_params.GetSerializedOpacityMode());
+    WriteUint32Enum(bitmap_settings.GetSerializedOpacityMode());
     WriteUint32Enum(ImageSerializationTag::kOriginCleanTag);
     WriteUint32(image_bitmap->OriginClean());
     WriteUint32Enum(ImageSerializationTag::kIsPremultipliedTag);
-    WriteUint32(color_params.IsPremultiplied());
+    WriteUint32(bitmap_settings.IsPremultiplied());
+    WriteUint32Enum(ImageSerializationTag::kImageOrientationTag);
+    WriteUint32Enum(bitmap_settings.GetSerializedImageOrientation());
     WriteUint32Enum(ImageSerializationTag::kEndTag);
-    WriteUint32(image_bitmap->width());
-    WriteUint32(image_bitmap->height());
-    Vector<uint8_t> pixels = image_bitmap->CopyBitmapData(info, false);
-    // Check if we succeeded to copy the BitmapData.
-    if (image_bitmap->width() != 0 && image_bitmap->height() != 0 &&
-        pixels.size() == 0) {
+    // Obtain size disregarding image orientation since the image orientation
+    // will be applied at deserialization time.
+    Image::SizeConfig size_config;
+    size_config.apply_orientation = false;
+    gfx::Size bitmap_size =
+        image_bitmap->BitmapImage()->SizeWithConfig(size_config);
+    WriteUint32(bitmap_size.width());
+    WriteUint32(bitmap_size.height());
+    Vector<uint8_t> pixels =
+        image_bitmap->CopyBitmapData(info, /*apply_orientation=*/false);
+    // Check if we succeeded to copy the bitmap data.
+    if (!bitmap_size.IsEmpty() && pixels.size() == 0) {
       exception_state.ThrowDOMException(
           DOMExceptionCode::kDataCloneError,
           "An ImageBitmap could not be read successfully.");
diff --git a/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_serializer_test.cc b/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_serializer_test.cc
index c52092b..a13d7eac 100644
--- a/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_serializer_test.cc
+++ b/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_serializer_test.cc
@@ -1069,6 +1069,37 @@
   ASSERT_THAT(pixel, testing::ElementsAre(255, 0, 0, 255));
 }
 
+TEST(V8ScriptValueSerializerTest, ImageBitmapEXIFImageOrientation) {
+  // More complete end-to-end testing is provided by WPT test
+  // imagebitmap-replication-exif-orientation.html.
+  // The purpose of this complementary test is to get complete code coverage
+  // for all possible values of ImageOrientationEnum.
+  V8TestingScope scope;
+  const uint32_t kImageWidth = 10;
+  const uint32_t kImageHeight = 5;
+  for (uint8_t i = static_cast<uint8_t>(ImageOrientationEnum::kOriginTopLeft);
+       i <= static_cast<uint8_t>(ImageOrientationEnum::kMaxValue); i++) {
+    ImageOrientationEnum orientation = static_cast<ImageOrientationEnum>(i);
+    sk_sp<SkSurface> surface =
+        SkSurface::MakeRasterN32Premul(kImageWidth, kImageHeight);
+    auto static_image =
+        UnacceleratedStaticBitmapImage::Create(surface->makeImageSnapshot());
+    static_image->SetOrientation(orientation);
+    auto* image_bitmap = MakeGarbageCollected<ImageBitmap>(static_image);
+    ASSERT_TRUE(image_bitmap->BitmapImage());
+    // Serialize and deserialize it.
+    v8::Local<v8::Value> wrapper = ToV8(image_bitmap, scope.GetScriptState());
+    v8::Local<v8::Value> result = RoundTrip(wrapper, scope);
+    ASSERT_TRUE(V8ImageBitmap::HasInstance(result, scope.GetIsolate()));
+    ImageBitmap* new_image_bitmap =
+        V8ImageBitmap::ToImpl(result.As<v8::Object>());
+    ASSERT_TRUE(new_image_bitmap->BitmapImage());
+    // Ensure image orientation did not confuse (e.g transpose) the image size
+    ASSERT_EQ(new_image_bitmap->Size(), image_bitmap->Size());
+    ASSERT_EQ(new_image_bitmap->ImageOrientation(), orientation);
+  }
+}
+
 TEST(V8ScriptValueSerializerTest, RoundTripImageBitmapWithColorSpaceInfo) {
   sk_sp<SkColorSpace> p3 =
       SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kDisplayP3);
@@ -1152,6 +1183,9 @@
           SkImageInfo::Make(2, 1, kRGBA_8888_SkColorType, kPremul_SkAlphaType),
           &pixels, 8, 0, 0));
   ASSERT_THAT(pixels, testing::ElementsAre(255, 0, 0, 255, 0, 255, 0, 255));
+  // Check that orientation is top left (default).
+  ASSERT_EQ(new_image_bitmap->ImageOrientation(),
+            ImageOrientationEnum::kOriginTopLeft);
 }
 
 TEST(V8ScriptValueSerializerTest, DecodeImageBitmapV18) {
@@ -1187,6 +1221,133 @@
   // in half floats by Skia).
   ASSERT_THAT(pixel, testing::ElementsAre(0x94, 0x3A, 0x3F, 0x28, 0x5F, 0x24,
                                           0x0, 0x3C));
+  // Check that orientation is top left (default).
+  ASSERT_EQ(new_image_bitmap->ImageOrientation(),
+            ImageOrientationEnum::kOriginTopLeft);
+}
+
+TEST(V8ScriptValueSerializerTest, DecodeImageBitmapV20WithoutImageOrientation) {
+  V8TestingScope scope;
+  ScriptState* script_state = scope.GetScriptState();
+  scoped_refptr<SerializedScriptValue> input = SerializedValue({
+      0xff,  // kVersionTag
+      0x14,  // 20
+      0xff,  // Value serializer header
+      0x0f,  // Value serializer version 15 (varint format)
+      0x5c,  // kHostObjectTag
+      0x67,  // kImageBitmapTag
+      0x07,  // kParametricColorSpaceTag
+      // srgb colorspace
+      0x00, 0x00, 0x00, 0x40, 0x33, 0x33, 0x03, 0x40, 0x00, 0x00, 0x00, 0xc0,
+      0xed, 0x54, 0xee, 0x3f, 0x00, 0x00, 0x00, 0x20, 0x23, 0xb1, 0xaa, 0x3f,
+      0x00, 0x00, 0x00, 0x20, 0x72, 0xd0, 0xb3, 0x3f, 0x00, 0x00, 0x00, 0xc0,
+      0xdc, 0xb5, 0xa4, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x80, 0xe8, 0xdb, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x40, 0xa6, 0xd8, 0x3f,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0xc2, 0x3f, 0x00, 0x00, 0x00, 0x00,
+      0x80, 0x7a, 0xcc, 0x3f, 0x00, 0x00, 0x00, 0x00, 0xa0, 0xf0, 0xe6, 0x3f,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0xaf, 0x3f, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x80, 0x8c, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0xda, 0xb8, 0x3f,
+      0x00, 0x00, 0x00, 0x00, 0xe0, 0xd9, 0xe6, 0x3f, 0x02,
+      0x03,        // kCanvasPixelFormatTag kBGRA8
+      0x06, 0x00,  // kCanvasOpacityModeTag
+      0x04, 0x01,  // kOriginCleanTag
+      0x05, 0x01,  // kIsPremultipliedTag
+      // Image orientation omitted
+      0x0,         // kEndTag
+      0x01, 0x01,  // width, height (varint format)
+      0x04,        // pixel size (varint format)
+      0xee, 0xaa, 0x77, 0xff,
+      0x00  // padding: even number of bytes for endianness swapping.
+  });
+
+  v8::Local<v8::Value> result =
+      V8ScriptValueDeserializer(script_state, input).Deserialize();
+  ASSERT_TRUE(V8ImageBitmap::HasInstance(result, scope.GetIsolate()));
+  ImageBitmap* new_image_bitmap =
+      V8ImageBitmap::ToImpl(result.As<v8::Object>());
+  // Check image size
+  ASSERT_EQ(gfx::Size(1, 1), new_image_bitmap->Size());
+  // Check the color settings.
+  SkImageInfo bitmap_info = new_image_bitmap->GetBitmapSkImageInfo();
+  EXPECT_EQ(kBGRA_8888_SkColorType, bitmap_info.colorType());
+  sk_sp<SkColorSpace> srgb =
+      SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kSRGB);
+  EXPECT_TRUE(SkColorSpace::Equals(srgb.get(), bitmap_info.colorSpace()));
+  // Check that orientation is bottom left.
+  ASSERT_EQ(new_image_bitmap->ImageOrientation(),
+            ImageOrientationEnum::kOriginTopLeft);
+  // Check pixel value
+  SkImageInfo info = SkImageInfo::Make(1, 1, kRGBA_8888_SkColorType,
+                                       kPremul_SkAlphaType, srgb);
+  uint8_t pixel[4] = {};
+  ASSERT_TRUE(
+      new_image_bitmap->BitmapImage()->PaintImageForCurrentFrame().readPixels(
+          info, &pixel, 4, 0, 0));
+  // BGRA encoding, swapped to RGBA
+  ASSERT_THAT(pixel, testing::ElementsAre(0x77, 0xaa, 0xee, 0xff));
+}
+
+TEST(V8ScriptValueSerializerTest, DecodeImageBitmapV20WithImageOrientation) {
+  V8TestingScope scope;
+  ScriptState* script_state = scope.GetScriptState();
+  scoped_refptr<SerializedScriptValue> input = SerializedValue({
+      0xff,  // kVersionTag
+      0x14,  // 20
+      0xff,  // Value serializer header
+      0x0f,  // Value serializer version 15, varint encoding
+      0x5c,  // kHostObjectTag
+      0x67,  // kImageBitmapTag
+      0x07,  // kParametricColorSpaceTag
+      // srgb colorspace
+      0x00, 0x00, 0x00, 0x40, 0x33, 0x33, 0x03, 0x40, 0x00, 0x00, 0x00, 0xc0,
+      0xed, 0x54, 0xee, 0x3f, 0x00, 0x00, 0x00, 0x20, 0x23, 0xb1, 0xaa, 0x3f,
+      0x00, 0x00, 0x00, 0x20, 0x72, 0xd0, 0xb3, 0x3f, 0x00, 0x00, 0x00, 0xc0,
+      0xdc, 0xb5, 0xa4, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x80, 0xe8, 0xdb, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x40, 0xa6, 0xd8, 0x3f,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0xc2, 0x3f, 0x00, 0x00, 0x00, 0x00,
+      0x80, 0x7a, 0xcc, 0x3f, 0x00, 0x00, 0x00, 0x00, 0xa0, 0xf0, 0xe6, 0x3f,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0xaf, 0x3f, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x80, 0x8c, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0xda, 0xb8, 0x3f,
+      0x00, 0x00, 0x00, 0x00, 0xe0, 0xd9, 0xe6, 0x3f, 0x02,
+      0x03,        // kCanvasPixelFormatTag kBGRA8
+      0x06, 0x00,  // kCanvasOpacityModeTag
+      0x04, 0x01,  // kOriginCleanTag
+      0x05, 0x01,  // kIsPremultipliedTag
+      0x08, 0x03,  // kImageOrientationTag -> kBottomLeft
+      0x0,         // kEndTag
+      0x01, 0x01,  // width, height (varint format)
+      0x04,        // pixel size (varint format)
+      0xee, 0xaa, 0x77, 0xff,
+      0x00  // padding: even number of bytes for endianness swapping.
+  });
+
+  v8::Local<v8::Value> result =
+      V8ScriptValueDeserializer(script_state, input).Deserialize();
+  ASSERT_TRUE(V8ImageBitmap::HasInstance(result, scope.GetIsolate()));
+  ImageBitmap* new_image_bitmap =
+      V8ImageBitmap::ToImpl(result.As<v8::Object>());
+  // Check image size
+  ASSERT_EQ(gfx::Size(1, 1), new_image_bitmap->Size());
+  // Check the color settings.
+  SkImageInfo bitmap_info = new_image_bitmap->GetBitmapSkImageInfo();
+  EXPECT_EQ(kBGRA_8888_SkColorType, bitmap_info.colorType());
+  sk_sp<SkColorSpace> srgb =
+      SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kSRGB);
+  EXPECT_TRUE(SkColorSpace::Equals(srgb.get(), bitmap_info.colorSpace()));
+  // Check that orientation is bottom left.
+  ASSERT_EQ(new_image_bitmap->ImageOrientation(),
+            ImageOrientationEnum::kOriginBottomLeft);
+  // Check pixel value
+  SkImageInfo info = SkImageInfo::Make(1, 1, kRGBA_8888_SkColorType,
+                                       kPremul_SkAlphaType, srgb);
+  uint8_t pixel[4] = {};
+  ASSERT_TRUE(
+      new_image_bitmap->BitmapImage()->PaintImageForCurrentFrame().readPixels(
+          info, &pixel, 4, 0, 0));
+  // BGRA encoding, swapped to RGBA
+  ASSERT_THAT(pixel, testing::ElementsAre(0x77, 0xaa, 0xee, 0xff));
 }
 
 TEST(V8ScriptValueSerializerTest, InvalidImageBitmapDecode) {
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_initializer.cc b/third_party/blink/renderer/bindings/core/v8/v8_initializer.cc
index a5aee790..db5bd67 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_initializer.cc
+++ b/third_party/blink/renderer/bindings/core/v8/v8_initializer.cc
@@ -737,10 +737,8 @@
   ArrayBufferAllocator() : total_allocation_(0) {
     // size_t may be equivalent to uint32_t or uint64_t, cast all values to
     // uint64_t to compare.
-    uint64_t virtual_size =
-        static_cast<uint64_t>(base::SysInfo::AmountOfVirtualMemory());
-    uint64_t size_t_max =
-        static_cast<uint64_t>(std::numeric_limits<std::size_t>::max());
+    uint64_t virtual_size = base::SysInfo::AmountOfVirtualMemory();
+    uint64_t size_t_max = std::numeric_limits<std::size_t>::max();
     DCHECK(virtual_size < size_t_max);
     // If AmountOfVirtualMemory() returns 0, there is no limit on virtual
     // memory, do not limit the total allocation. Otherwise, Limit the total
diff --git a/third_party/blink/renderer/controller/user_level_memory_pressure_signal_generator.cc b/third_party/blink/renderer/controller/user_level_memory_pressure_signal_generator.cc
index 8e2ba7b..dec4baf 100644
--- a/third_party/blink/renderer/controller/user_level_memory_pressure_signal_generator.cc
+++ b/third_party/blink/renderer/controller/user_level_memory_pressure_signal_generator.cc
@@ -69,21 +69,15 @@
 }
 
 double MemoryThresholdParam() {
-  int64_t physical_memory = base::SysInfo::AmountOfPhysicalMemory();
-  double memory_threshold_mb = kDefaultMemoryThresholdMB;
-
-  if (physical_memory > 3.1 * 1024 * 1024 * 1024)
-    memory_threshold_mb = MemoryThresholdParamOf4GbDevices();
-  else if (physical_memory > 2.1 * 1024 * 1024 * 1024)
-    memory_threshold_mb = MemoryThresholdParamOf3GbDevices();
-  else if (physical_memory > 1.1 * 1024 * 1024 * 1024)
-    memory_threshold_mb = MemoryThresholdParamOf2GbDevices();
-  else if (physical_memory > 600 * 1024 * 1024)
-    memory_threshold_mb = MemoryThresholdParamOf1GbDevices();
-  else
-    memory_threshold_mb = MemoryThresholdParamOf512MbDevices();
-
-  return memory_threshold_mb;
+  int physical_memory_mb = base::SysInfo::AmountOfPhysicalMemoryMB();
+  if (physical_memory_mb > 3.1 * 1024)
+    return MemoryThresholdParamOf4GbDevices();
+  if (physical_memory_mb > 2.1 * 1024)
+    return MemoryThresholdParamOf3GbDevices();
+  if (physical_memory_mb > 1.1 * 1024)
+    return MemoryThresholdParamOf2GbDevices();
+  return (physical_memory_mb > 600) ? MemoryThresholdParamOf1GbDevices()
+                                    : MemoryThresholdParamOf512MbDevices();
 }
 
 }  // namespace
diff --git a/third_party/blink/renderer/core/animation/animation.cc b/third_party/blink/renderer/core/animation/animation.cc
index b8a421d..a460769 100644
--- a/third_party/blink/renderer/core/animation/animation.cc
+++ b/third_party/blink/renderer/core/animation/animation.cc
@@ -91,7 +91,7 @@
                                     1000;
 }
 
-enum PseudoPriority { kNone, kMarker, kBefore, kOther, kAfter };
+enum class PseudoPriority { kNone, kMarker, kBefore, kOther, kAfter };
 
 unsigned NextSequenceNumber() {
   static unsigned next = 0;
@@ -595,7 +595,7 @@
   // notified yet.
   if (!compositor_property_animations_had_no_effect && start_on_compositor &&
       should_cancel && should_start && compositor_state_ &&
-      compositor_state_->pending_action == kStart &&
+      compositor_state_->pending_action == CompositorAction::kStart &&
       !compositor_state_->effect_changed) {
     // Restarting but still waiting for a start time.
     return false;
@@ -654,14 +654,16 @@
 void Animation::PostCommit() {
   compositor_pending_ = false;
 
-  if (!compositor_state_ || compositor_state_->pending_action == kNone)
+  if (!compositor_state_ ||
+      compositor_state_->pending_action == CompositorAction::kNone) {
     return;
+  }
 
-  DCHECK_EQ(kStart, compositor_state_->pending_action);
+  DCHECK_EQ(CompositorAction::kStart, compositor_state_->pending_action);
   if (compositor_state_->start_time) {
     DCHECK(IsWithinAnimationTimeEpsilon(start_time_.value().InSecondsF(),
                                         compositor_state_->start_time.value()));
-    compositor_state_->pending_action = kNone;
+    compositor_state_->pending_action = CompositorAction::kNone;
   }
 }
 
@@ -722,7 +724,7 @@
 
     // The following if statement is not reachable, but the implementation
     // matches the specification for composite ordering
-    if (priority1 == kOther && pseudo1 != pseudo2) {
+    if (priority1 == PseudoPriority::kOther && pseudo1 != pseudo2) {
       return CodeUnitCompareLessThan(
           PseudoElement::PseudoElementNameForEvents(pseudo1),
           PseudoElement::PseudoElementNameForEvents(pseudo2));
@@ -764,9 +766,10 @@
   else if (pending_pause_)
     CommitPendingPause(ready_time);
 
-  if (compositor_state_ && compositor_state_->pending_action == kStart) {
+  if (compositor_state_ &&
+      compositor_state_->pending_action == CompositorAction::kStart) {
     DCHECK(!compositor_state_->start_time);
-    compositor_state_->pending_action = kNone;
+    compositor_state_->pending_action = CompositorAction::kNone;
     compositor_state_->start_time =
         start_time_ ? absl::make_optional(start_time_.value().InSecondsF())
                     : absl::nullopt;
diff --git a/third_party/blink/renderer/core/animation/animation.h b/third_party/blink/renderer/core/animation/animation.h
index e0519e4..c35810ab 100644
--- a/third_party/blink/renderer/core/animation/animation.h
+++ b/third_party/blink/renderer/core/animation/animation.h
@@ -474,7 +474,7 @@
 
   // TODO(crbug.com/960944): Consider reintroducing kPause and cleanup use of
   // mutually exclusive pending_play_ and pending_pause_ flags.
-  enum CompositorAction { kNone, kStart };
+  enum class CompositorAction { kNone, kStart };
 
   class CompositorState {
     USING_FAST_MALLOC(CompositorState);
@@ -493,7 +493,8 @@
                         : absl::nullopt),
           playback_rate(animation.EffectivePlaybackRate()),
           effect_changed(false),
-          pending_action(animation.start_time_ ? kNone : kStart) {}
+          pending_action(animation.start_time_ ? CompositorAction::kNone
+                                               : CompositorAction::kStart) {}
     CompositorState(const CompositorState&) = delete;
     CompositorState& operator=(const CompositorState&) = delete;
 
diff --git a/third_party/blink/renderer/core/animation/interpolable_filter.cc b/third_party/blink/renderer/core/animation/interpolable_filter.cc
index f9cbc49..4be4657 100644
--- a/third_party/blink/renderer/core/animation/interpolable_filter.cc
+++ b/third_party/blink/renderer/core/animation/interpolable_filter.cc
@@ -16,18 +16,18 @@
 namespace {
 double ClampParameter(double value, FilterOperation::OperationType type) {
   switch (type) {
-    case FilterOperation::kBrightness:
-    case FilterOperation::kContrast:
-    case FilterOperation::kSaturate:
+    case FilterOperation::OperationType::kBrightness:
+    case FilterOperation::OperationType::kContrast:
+    case FilterOperation::OperationType::kSaturate:
       return ClampTo<double>(value, 0);
 
-    case FilterOperation::kGrayscale:
-    case FilterOperation::kInvert:
-    case FilterOperation::kOpacity:
-    case FilterOperation::kSepia:
+    case FilterOperation::OperationType::kGrayscale:
+    case FilterOperation::OperationType::kInvert:
+    case FilterOperation::OperationType::kOpacity:
+    case FilterOperation::OperationType::kSepia:
       return ClampTo<double>(value, 0, 1);
 
-    case FilterOperation::kHueRotate:
+    case FilterOperation::OperationType::kHueRotate:
       return value;
 
     default:
@@ -44,33 +44,33 @@
   std::unique_ptr<InterpolableValue> value;
   FilterOperation::OperationType type = filter.GetType();
   switch (type) {
-    case FilterOperation::kGrayscale:
-    case FilterOperation::kHueRotate:
-    case FilterOperation::kSaturate:
-    case FilterOperation::kSepia:
+    case FilterOperation::OperationType::kGrayscale:
+    case FilterOperation::OperationType::kHueRotate:
+    case FilterOperation::OperationType::kSaturate:
+    case FilterOperation::OperationType::kSepia:
       value = std::make_unique<InterpolableNumber>(
           To<BasicColorMatrixFilterOperation>(filter).Amount());
       break;
 
-    case FilterOperation::kBrightness:
-    case FilterOperation::kContrast:
-    case FilterOperation::kInvert:
-    case FilterOperation::kOpacity:
+    case FilterOperation::OperationType::kBrightness:
+    case FilterOperation::OperationType::kContrast:
+    case FilterOperation::OperationType::kInvert:
+    case FilterOperation::OperationType::kOpacity:
       value = std::make_unique<InterpolableNumber>(
           To<BasicComponentTransferFilterOperation>(filter).Amount());
       break;
 
-    case FilterOperation::kBlur:
+    case FilterOperation::OperationType::kBlur:
       value = InterpolableLength::MaybeConvertLength(
           To<BlurFilterOperation>(filter).StdDeviation(), zoom);
       break;
 
-    case FilterOperation::kDropShadow:
+    case FilterOperation::OperationType::kDropShadow:
       value = InterpolableShadow::Create(
           To<DropShadowFilterOperation>(filter).Shadow(), zoom);
       break;
 
-    case FilterOperation::kReference:
+    case FilterOperation::OperationType::kReference:
       return nullptr;
 
     default:
@@ -96,25 +96,25 @@
   FilterOperation::OperationType type =
       FilterOperationResolver::FilterOperationForType(filter.FunctionType());
   switch (type) {
-    case FilterOperation::kBrightness:
-    case FilterOperation::kContrast:
-    case FilterOperation::kGrayscale:
-    case FilterOperation::kInvert:
-    case FilterOperation::kOpacity:
-    case FilterOperation::kSaturate:
-    case FilterOperation::kSepia:
-    case FilterOperation::kHueRotate:
+    case FilterOperation::OperationType::kBrightness:
+    case FilterOperation::OperationType::kContrast:
+    case FilterOperation::OperationType::kGrayscale:
+    case FilterOperation::OperationType::kInvert:
+    case FilterOperation::OperationType::kOpacity:
+    case FilterOperation::OperationType::kSaturate:
+    case FilterOperation::OperationType::kSepia:
+    case FilterOperation::OperationType::kHueRotate:
       value = std::make_unique<InterpolableNumber>(
           FilterOperationResolver::ResolveNumericArgumentForFunction(filter));
       break;
 
-    case FilterOperation::kBlur:
+    case FilterOperation::OperationType::kBlur:
       value = filter.length() > 0
                   ? InterpolableLength::MaybeConvertCSSValue(filter.Item(0))
                   : InterpolableLength::CreateNeutral();
       break;
 
-    case FilterOperation::kDropShadow:
+    case FilterOperation::OperationType::kDropShadow:
       value = InterpolableShadow::MaybeConvertCSSValue(filter.Item(0));
       break;
 
@@ -135,25 +135,25 @@
   // mapping of OperationType to initial value.
   std::unique_ptr<InterpolableValue> value;
   switch (type) {
-    case FilterOperation::kGrayscale:
-    case FilterOperation::kInvert:
-    case FilterOperation::kSepia:
-    case FilterOperation::kHueRotate:
+    case FilterOperation::OperationType::kGrayscale:
+    case FilterOperation::OperationType::kInvert:
+    case FilterOperation::OperationType::kSepia:
+    case FilterOperation::OperationType::kHueRotate:
       value = std::make_unique<InterpolableNumber>(0);
       break;
 
-    case FilterOperation::kBrightness:
-    case FilterOperation::kContrast:
-    case FilterOperation::kOpacity:
-    case FilterOperation::kSaturate:
+    case FilterOperation::OperationType::kBrightness:
+    case FilterOperation::OperationType::kContrast:
+    case FilterOperation::OperationType::kOpacity:
+    case FilterOperation::OperationType::kSaturate:
       value = std::make_unique<InterpolableNumber>(1);
       break;
 
-    case FilterOperation::kBlur:
+    case FilterOperation::OperationType::kBlur:
       value = InterpolableLength::CreateNeutral();
       break;
 
-    case FilterOperation::kDropShadow:
+    case FilterOperation::OperationType::kDropShadow:
       value = InterpolableShadow::CreateNeutral();
       break;
 
@@ -168,33 +168,33 @@
 FilterOperation* InterpolableFilter::CreateFilterOperation(
     const StyleResolverState& state) const {
   switch (type_) {
-    case FilterOperation::kGrayscale:
-    case FilterOperation::kHueRotate:
-    case FilterOperation::kSaturate:
-    case FilterOperation::kSepia: {
+    case FilterOperation::OperationType::kGrayscale:
+    case FilterOperation::OperationType::kHueRotate:
+    case FilterOperation::OperationType::kSaturate:
+    case FilterOperation::OperationType::kSepia: {
       double value =
           ClampParameter(To<InterpolableNumber>(*value_).Value(), type_);
       return MakeGarbageCollected<BasicColorMatrixFilterOperation>(value,
                                                                    type_);
     }
 
-    case FilterOperation::kBrightness:
-    case FilterOperation::kContrast:
-    case FilterOperation::kInvert:
-    case FilterOperation::kOpacity: {
+    case FilterOperation::OperationType::kBrightness:
+    case FilterOperation::OperationType::kContrast:
+    case FilterOperation::OperationType::kInvert:
+    case FilterOperation::OperationType::kOpacity: {
       double value =
           ClampParameter(To<InterpolableNumber>(*value_).Value(), type_);
       return MakeGarbageCollected<BasicComponentTransferFilterOperation>(value,
                                                                          type_);
     }
 
-    case FilterOperation::kBlur: {
+    case FilterOperation::OperationType::kBlur: {
       Length std_deviation = To<InterpolableLength>(*value_).CreateLength(
           state.CssToLengthConversionData(), Length::ValueRange::kNonNegative);
       return MakeGarbageCollected<BlurFilterOperation>(std_deviation);
     }
 
-    case FilterOperation::kDropShadow: {
+    case FilterOperation::OperationType::kDropShadow: {
       ShadowData shadow_data =
           To<InterpolableShadow>(*value_).CreateShadowData(state);
       if (shadow_data.GetColor().IsCurrentColor())
@@ -213,13 +213,13 @@
   // The following types have an initial value of 1, so addition for them is
   // one-based: result = value_ + other.value_ - 1
   switch (type_) {
-    case FilterOperation::kBrightness:
-    case FilterOperation::kContrast:
-    case FilterOperation::kGrayscale:
-    case FilterOperation::kInvert:
-    case FilterOperation::kOpacity:
-    case FilterOperation::kSaturate:
-    case FilterOperation::kSepia:
+    case FilterOperation::OperationType::kBrightness:
+    case FilterOperation::OperationType::kContrast:
+    case FilterOperation::OperationType::kGrayscale:
+    case FilterOperation::OperationType::kInvert:
+    case FilterOperation::OperationType::kOpacity:
+    case FilterOperation::OperationType::kSaturate:
+    case FilterOperation::OperationType::kSepia:
       value_->Add(*std::make_unique<InterpolableNumber>(-1));
       break;
     default:
diff --git a/third_party/blink/renderer/core/css/css_properties.json5 b/third_party/blink/renderer/core/css/css_properties.json5
index 3ab5abe..be7b814 100644
--- a/third_party/blink/renderer/core/css/css_properties.json5
+++ b/third_party/blink/renderer/core/css/css_properties.json5
@@ -1371,7 +1371,6 @@
       typedom_types: ["Keyword"],
       converter: "ConvertStyleColor",
       style_builder_template: "color",
-      runtime_flag: "CSSAccentColor",
       default_value: "StyleAutoColor::AutoColor()",
       style_builder_custom_functions: ["initial", "inherit", "value"],
       computable: true,
diff --git a/third_party/blink/renderer/core/css/parser/css_parser_token.h b/third_party/blink/renderer/core/css/parser/css_parser_token.h
index 2744e51..a6e6222 100644
--- a/third_party/blink/renderer/core/css/parser/css_parser_token.h
+++ b/third_party/blink/renderer/core/css/parser/css_parser_token.h
@@ -159,6 +159,21 @@
 
   CSSParserToken CopyWithUpdatedString(const StringView&) const;
 
+  static CSSParserTokenType ClosingTokenType(CSSParserTokenType opening_type) {
+    switch (opening_type) {
+      case kFunctionToken:
+      case kLeftParenthesisToken:
+        return kRightParenthesisToken;
+      case kLeftBracketToken:
+        return kRightBracketToken;
+      case kLeftBraceToken:
+        return kRightBraceToken;
+      default:
+        NOTREACHED();
+        return kEOFToken;
+    }
+  }
+
  private:
   void InitValueFromStringView(StringView string) {
     value_length_ = string.length();
diff --git a/third_party/blink/renderer/core/css/parser/css_parser_token_range.cc b/third_party/blink/renderer/core/css/parser/css_parser_token_range.cc
index 6a9e080..30bf7bfa 100644
--- a/third_party/blink/renderer/core/css/parser/css_parser_token_range.cc
+++ b/third_party/blink/renderer/core/css/parser/css_parser_token_range.cc
@@ -60,7 +60,8 @@
 String CSSParserTokenRange::Serialize() const {
   // We're supposed to insert comments between certain pairs of token types
   // as per spec, but since this is currently only used for @supports CSSOM
-  // we just get these cases wrong and avoid the additional complexity.
+  // and CSS Paint API arguments we just get these cases wrong and avoid the
+  // additional complexity.
   StringBuilder builder;
   for (const CSSParserToken* it = first_; it != last_; ++it)
     it->Serialize(builder);
diff --git a/third_party/blink/renderer/core/css/properties/computed_style_utils.cc b/third_party/blink/renderer/core/css/properties/computed_style_utils.cc
index 30643a7..45a9771f 100644
--- a/third_party/blink/renderer/core/css/properties/computed_style_utils.cc
+++ b/third_party/blink/renderer/core/css/properties/computed_style_utils.cc
@@ -2478,40 +2478,40 @@
   for (const auto& operation : filter_operations.Operations()) {
     FilterOperation* filter_operation = operation.Get();
     switch (filter_operation->GetType()) {
-      case FilterOperation::kReference:
+      case FilterOperation::OperationType::kReference:
         filter_value = MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kUrl);
         filter_value->Append(*MakeGarbageCollected<CSSStringValue>(
             To<ReferenceFilterOperation>(filter_operation)->Url()));
         break;
-      case FilterOperation::kGrayscale:
+      case FilterOperation::OperationType::kGrayscale:
         filter_value =
             MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kGrayscale);
         filter_value->Append(*CSSNumericLiteralValue::Create(
             To<BasicColorMatrixFilterOperation>(filter_operation)->Amount(),
             CSSPrimitiveValue::UnitType::kNumber));
         break;
-      case FilterOperation::kSepia:
+      case FilterOperation::OperationType::kSepia:
         filter_value =
             MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kSepia);
         filter_value->Append(*CSSNumericLiteralValue::Create(
             To<BasicColorMatrixFilterOperation>(filter_operation)->Amount(),
             CSSPrimitiveValue::UnitType::kNumber));
         break;
-      case FilterOperation::kSaturate:
+      case FilterOperation::OperationType::kSaturate:
         filter_value =
             MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kSaturate);
         filter_value->Append(*CSSNumericLiteralValue::Create(
             To<BasicColorMatrixFilterOperation>(filter_operation)->Amount(),
             CSSPrimitiveValue::UnitType::kNumber));
         break;
-      case FilterOperation::kHueRotate:
+      case FilterOperation::OperationType::kHueRotate:
         filter_value =
             MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kHueRotate);
         filter_value->Append(*CSSNumericLiteralValue::Create(
             To<BasicColorMatrixFilterOperation>(filter_operation)->Amount(),
             CSSPrimitiveValue::UnitType::kDegrees));
         break;
-      case FilterOperation::kInvert:
+      case FilterOperation::OperationType::kInvert:
         filter_value =
             MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kInvert);
         filter_value->Append(*CSSNumericLiteralValue::Create(
@@ -2519,7 +2519,7 @@
                 ->Amount(),
             CSSPrimitiveValue::UnitType::kNumber));
         break;
-      case FilterOperation::kOpacity:
+      case FilterOperation::OperationType::kOpacity:
         filter_value =
             MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kOpacity);
         filter_value->Append(*CSSNumericLiteralValue::Create(
@@ -2527,7 +2527,7 @@
                 ->Amount(),
             CSSPrimitiveValue::UnitType::kNumber));
         break;
-      case FilterOperation::kBrightness:
+      case FilterOperation::OperationType::kBrightness:
         filter_value =
             MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kBrightness);
         filter_value->Append(*CSSNumericLiteralValue::Create(
@@ -2535,7 +2535,7 @@
                 ->Amount(),
             CSSPrimitiveValue::UnitType::kNumber));
         break;
-      case FilterOperation::kContrast:
+      case FilterOperation::OperationType::kContrast:
         filter_value =
             MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kContrast);
         filter_value->Append(*CSSNumericLiteralValue::Create(
@@ -2543,14 +2543,14 @@
                 ->Amount(),
             CSSPrimitiveValue::UnitType::kNumber));
         break;
-      case FilterOperation::kBlur:
+      case FilterOperation::OperationType::kBlur:
         filter_value =
             MakeGarbageCollected<CSSFunctionValue>(CSSValueID::kBlur);
         filter_value->Append(*ZoomAdjustedPixelValue(
             To<BlurFilterOperation>(filter_operation)->StdDeviation().Value(),
             style));
         break;
-      case FilterOperation::kDropShadow: {
+      case FilterOperation::OperationType::kDropShadow: {
         const auto& drop_shadow_operation =
             To<DropShadowFilterOperation>(*filter_operation);
         filter_value =
diff --git a/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc b/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc
index 3e64442..8ffd4d9 100644
--- a/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc
+++ b/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc
@@ -531,17 +531,21 @@
 Vector<CSSParserToken> ConsumeFunctionArgsOrNot(CSSParserTokenRange& args) {
   Vector<CSSParserToken> argument_tokens;
   if (args.Peek().GetBlockType() == CSSParserToken::kBlockStart) {
-    // Function block.
-    // Push the function name and initial right parenthesis.
-    // Since we don't have any upfront knowledge about the input argument types
-    // here, we should just leave the token as it is and resolve it later in
-    // the variable parsing phase.
+    // A block of some type (maybe a function, maybe (), [], or {}).
+    // Push the block start.
+    //
+    // For functions, we don't have any upfront knowledge about the input
+    // argument types here, we should just leave the token as it is and
+    // resolve it later in the variable parsing phase.
     argument_tokens.push_back(args.Peek());
+    CSSParserTokenType closing_type =
+        CSSParserToken::ClosingTokenType(args.Peek().GetType());
+
     CSSParserTokenRange contents = args.ConsumeBlock();
     while (!contents.AtEnd())
       argument_tokens.push_back(contents.Consume());
     argument_tokens.push_back(
-        CSSParserToken(kRightParenthesisToken, CSSParserToken::kBlockEnd));
+        CSSParserToken(closing_type, CSSParserToken::kBlockEnd));
 
   } else {
     argument_tokens.push_back(args.ConsumeIncludingWhitespace());
diff --git a/third_party/blink/renderer/core/css/resolver/filter_operation_resolver.cc b/third_party/blink/renderer/core/css/resolver/filter_operation_resolver.cc
index a14eb24..50ec59b 100644
--- a/third_party/blink/renderer/core/css/resolver/filter_operation_resolver.cc
+++ b/third_party/blink/renderer/core/css/resolver/filter_operation_resolver.cc
@@ -49,29 +49,29 @@
     CSSValueID type) {
   switch (type) {
     case CSSValueID::kGrayscale:
-      return FilterOperation::kGrayscale;
+      return FilterOperation::OperationType::kGrayscale;
     case CSSValueID::kSepia:
-      return FilterOperation::kSepia;
+      return FilterOperation::OperationType::kSepia;
     case CSSValueID::kSaturate:
-      return FilterOperation::kSaturate;
+      return FilterOperation::OperationType::kSaturate;
     case CSSValueID::kHueRotate:
-      return FilterOperation::kHueRotate;
+      return FilterOperation::OperationType::kHueRotate;
     case CSSValueID::kInvert:
-      return FilterOperation::kInvert;
+      return FilterOperation::OperationType::kInvert;
     case CSSValueID::kOpacity:
-      return FilterOperation::kOpacity;
+      return FilterOperation::OperationType::kOpacity;
     case CSSValueID::kBrightness:
-      return FilterOperation::kBrightness;
+      return FilterOperation::OperationType::kBrightness;
     case CSSValueID::kContrast:
-      return FilterOperation::kContrast;
+      return FilterOperation::OperationType::kContrast;
     case CSSValueID::kBlur:
-      return FilterOperation::kBlur;
+      return FilterOperation::OperationType::kBlur;
     case CSSValueID::kDropShadow:
-      return FilterOperation::kDropShadow;
+      return FilterOperation::OperationType::kDropShadow;
     default:
       NOTREACHED();
       // FIXME: We shouldn't have a type None since we never create them
-      return FilterOperation::kNone;
+      return FilterOperation::OperationType::kNone;
   }
 }
 
@@ -81,50 +81,50 @@
   // uninitialized.
   WebFeature feature = WebFeature::kNumberOfFeatures;
   switch (operation_type) {
-    case FilterOperation::kNone:
-    case FilterOperation::kBoxReflect:
-    case FilterOperation::kConvolveMatrix:
-    case FilterOperation::kComponentTransfer:
-    case FilterOperation::kTurbulence:
+    case FilterOperation::OperationType::kNone:
+    case FilterOperation::OperationType::kBoxReflect:
+    case FilterOperation::OperationType::kConvolveMatrix:
+    case FilterOperation::OperationType::kComponentTransfer:
+    case FilterOperation::OperationType::kTurbulence:
       NOTREACHED();
       return;
-    case FilterOperation::kReference:
+    case FilterOperation::OperationType::kReference:
       feature = WebFeature::kCSSFilterReference;
       break;
-    case FilterOperation::kGrayscale:
+    case FilterOperation::OperationType::kGrayscale:
       feature = WebFeature::kCSSFilterGrayscale;
       break;
-    case FilterOperation::kSepia:
+    case FilterOperation::OperationType::kSepia:
       feature = WebFeature::kCSSFilterSepia;
       break;
-    case FilterOperation::kSaturate:
+    case FilterOperation::OperationType::kSaturate:
       feature = WebFeature::kCSSFilterSaturate;
       break;
-    case FilterOperation::kHueRotate:
+    case FilterOperation::OperationType::kHueRotate:
       feature = WebFeature::kCSSFilterHueRotate;
       break;
-    case FilterOperation::kLuminanceToAlpha:
+    case FilterOperation::OperationType::kLuminanceToAlpha:
       feature = WebFeature::kCSSFilterLuminanceToAlpha;
       break;
-    case FilterOperation::kColorMatrix:
+    case FilterOperation::OperationType::kColorMatrix:
       feature = WebFeature::kCSSFilterColorMatrix;
       break;
-    case FilterOperation::kInvert:
+    case FilterOperation::OperationType::kInvert:
       feature = WebFeature::kCSSFilterInvert;
       break;
-    case FilterOperation::kOpacity:
+    case FilterOperation::OperationType::kOpacity:
       feature = WebFeature::kCSSFilterOpacity;
       break;
-    case FilterOperation::kBrightness:
+    case FilterOperation::OperationType::kBrightness:
       feature = WebFeature::kCSSFilterBrightness;
       break;
-    case FilterOperation::kContrast:
+    case FilterOperation::OperationType::kContrast:
       feature = WebFeature::kCSSFilterContrast;
       break;
-    case FilterOperation::kBlur:
+    case FilterOperation::OperationType::kBlur:
       feature = WebFeature::kCSSFilterBlur;
       break;
-    case FilterOperation::kDropShadow:
+    case FilterOperation::OperationType::kDropShadow:
       feature = WebFeature::kCSSFilterDropShadow;
       break;
   };
@@ -180,7 +180,8 @@
   for (auto& curr_value : To<CSSValueList>(in_value)) {
     if (const auto* url_value =
             DynamicTo<cssvalue::CSSURIValue>(curr_value.Get())) {
-      CountFilterUse(FilterOperation::kReference, state.GetDocument());
+      CountFilterUse(FilterOperation::OperationType::kReference,
+                     state.GetDocument());
 
       SVGResource* resource =
           state.GetElementStyleResources().GetSVGResourceFromValue(property_id,
diff --git a/third_party/blink/renderer/core/css/threaded/filter_operation_resolver_threaded_test.cc b/third_party/blink/renderer/core/css/threaded/filter_operation_resolver_threaded_test.cc
index a712bc2..8eb71f0c 100644
--- a/third_party/blink/renderer/core/css/threaded/filter_operation_resolver_threaded_test.cc
+++ b/third_party/blink/renderer/core/css/threaded/filter_operation_resolver_threaded_test.cc
@@ -29,7 +29,7 @@
         FilterOperationResolver::CreateOffscreenFilterOperations(*value, font);
     ASSERT_EQ(fo.size(), 1ul);
     EXPECT_EQ(*fo.at(0), *MakeGarbageCollected<BasicColorMatrixFilterOperation>(
-                             0.5, FilterOperation::kSepia));
+                             0.5, FilterOperation::OperationType::kSepia));
   });
 }
 
@@ -47,7 +47,7 @@
     ASSERT_EQ(fo.size(), 1ul);
     EXPECT_EQ(*fo.at(0),
               *MakeGarbageCollected<BasicComponentTransferFilterOperation>(
-                  0.5, FilterOperation::kBrightness));
+                  0.5, FilterOperation::OperationType::kBrightness));
   });
 }
 
@@ -101,10 +101,10 @@
     EXPECT_FALSE(fo.IsEmpty());
     ASSERT_EQ(fo.size(), 2ul);
     EXPECT_EQ(*fo.at(0), *MakeGarbageCollected<BasicColorMatrixFilterOperation>(
-                             0.5, FilterOperation::kSepia));
+                             0.5, FilterOperation::OperationType::kSepia));
     EXPECT_EQ(*fo.at(1),
               *MakeGarbageCollected<BasicComponentTransferFilterOperation>(
-                  0.5, FilterOperation::kBrightness));
+                  0.5, FilterOperation::OperationType::kBrightness));
   });
 }
 
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index 9c1bdaa1..13c8067 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -4987,7 +4987,7 @@
     // and other non-user internal interventions.
     if (params.type != mojom::blink::FocusType::kNone &&
         params.type != mojom::blink::FocusType::kScript)
-      last_focus_type_ = params.type;
+      SetLastFocusType(params.type);
 
     for (auto& observer : focused_element_change_observers_)
       observer->DidChangeFocus();
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc
index ac91a4c..e88b83e 100644
--- a/third_party/blink/renderer/core/dom/element.cc
+++ b/third_party/blink/renderer/core/dom/element.cc
@@ -2996,8 +2996,8 @@
   DCHECK(RuntimeEnabledFeatures::HTMLPopupAttributeEnabled());
   if (event.GetEventPath().IsEmpty())
     return;
-  DCHECK_NE(Event::kNone, event.eventPhase());
-  if (event.eventPhase() == Event::kBubblingPhase)
+  DCHECK_NE(Event::PhaseType::kNone, event.eventPhase());
+  if (event.eventPhase() == Event::PhaseType::kBubblingPhase)
     return;
   // Ensure that shadow DOM event retargeting is considered when computing
   // the event target node.
diff --git a/third_party/blink/renderer/core/dom/events/add_event_listener_options.idl b/third_party/blink/renderer/core/dom/events/add_event_listener_options.idl
index db453f8..f4878cb6 100644
--- a/third_party/blink/renderer/core/dom/events/add_event_listener_options.idl
+++ b/third_party/blink/renderer/core/dom/events/add_event_listener_options.idl
@@ -7,5 +7,5 @@
 dictionary AddEventListenerOptions : EventListenerOptions {
     boolean passive;
     boolean once = false;
-    [RuntimeEnabled=AddEventListenerAbortSignal] AbortSignal signal;
+    AbortSignal signal;
 };
diff --git a/third_party/blink/renderer/core/dom/events/event.cc b/third_party/blink/renderer/core/dom/events/event.cc
index b09ab62..1821c3a7 100644
--- a/third_party/blink/renderer/core/dom/events/event.cc
+++ b/third_party/blink/renderer/core/dom/events/event.cc
@@ -88,7 +88,7 @@
       fire_only_non_capture_listeners_at_target_(false),
       copy_event_path_from_underlying_event_(false),
       handling_passive_(PassiveMode::kNotPassiveDefault),
-      event_phase_(0),
+      event_phase_(Event::PhaseType::kNone),
       current_target_(nullptr),
       platform_time_stamp_(platform_time_stamp) {}
 
@@ -305,7 +305,7 @@
 HeapVector<Member<EventTarget>> Event::PathInternal(ScriptState* script_state,
                                                     EventPathMode mode) const {
   if (!current_target_) {
-    DCHECK_EQ(Event::kNone, event_phase_);
+    DCHECK_EQ(Event::PhaseType::kNone, event_phase_);
     if (!event_path_) {
       // Before dispatching the event
       return HeapVector<Member<EventTarget>>();
diff --git a/third_party/blink/renderer/core/dom/events/event.h b/third_party/blink/renderer/core/dom/events/event.h
index c1e091f4..96fd605 100644
--- a/third_party/blink/renderer/core/dom/events/event.h
+++ b/third_party/blink/renderer/core/dom/events/event.h
@@ -60,7 +60,7 @@
     kNo,
   };
 
-  enum PhaseType {
+  enum class PhaseType {
     kNone = 0,
     kCapturingPhase = 1,
     kAtTarget = 2,
@@ -157,19 +157,19 @@
 
   void SetRelatedTargetIfExists(EventTarget* related_target);
 
-  uint8_t eventPhase() const { return event_phase_; }
-  void SetEventPhase(uint8_t event_phase) { event_phase_ = event_phase; }
+  PhaseType eventPhase() const { return event_phase_; }
+  void SetEventPhase(PhaseType event_phase) { event_phase_ = event_phase; }
 
   void SetFireOnlyCaptureListenersAtTarget(
       bool fire_only_capture_listeners_at_target) {
-    DCHECK_EQ(event_phase_, kAtTarget);
+    DCHECK_EQ(event_phase_, PhaseType::kAtTarget);
     fire_only_capture_listeners_at_target_ =
         fire_only_capture_listeners_at_target;
   }
 
   void SetFireOnlyNonCaptureListenersAtTarget(
       bool fire_only_non_capture_listeners_at_target) {
-    DCHECK_EQ(event_phase_, kAtTarget);
+    DCHECK_EQ(event_phase_, PhaseType::kAtTarget);
     fire_only_non_capture_listeners_at_target_ =
         fire_only_non_capture_listeners_at_target;
   }
@@ -266,7 +266,7 @@
   ScriptValue path(ScriptState*) const;
   HeapVector<Member<EventTarget>> composedPath(ScriptState*) const;
 
-  bool IsBeingDispatched() const { return eventPhase(); }
+  bool IsBeingDispatched() const { return eventPhase() != PhaseType::kNone; }
 
   // Events that must not leak across isolated world, similar to how
   // ErrorEvent behaves, can override this method.
@@ -353,7 +353,7 @@
   unsigned copy_event_path_from_underlying_event_ : 1;
 
   PassiveMode handling_passive_;
-  uint8_t event_phase_;
+  PhaseType event_phase_;
   probe::AsyncTaskContext async_task_context_;
 
   Member<EventTarget> current_target_;
diff --git a/third_party/blink/renderer/core/dom/events/event_dispatcher.cc b/third_party/blink/renderer/core/dom/events/event_dispatcher.cc
index 3f34e513..1e87c33f 100644
--- a/third_party/blink/renderer/core/dom/events/event_dispatcher.cc
+++ b/third_party/blink/renderer/core/dom/events/event_dispatcher.cc
@@ -292,7 +292,7 @@
   // Trigger capturing event handlers, starting at the top and working our way
   // down. When we get to the last one, the target, change the event phase to
   // AT_TARGET and fire only the capture listeners on it.
-  event_->SetEventPhase(Event::kCapturingPhase);
+  event_->SetEventPhase(Event::PhaseType::kCapturingPhase);
 
   if (event_->GetEventPath().GetWindowEventContext().HandleLocalEvents(
           *event_) &&
@@ -302,12 +302,12 @@
   for (wtf_size_t i = event_->GetEventPath().size(); i > 0; --i) {
     const NodeEventContext& event_context = event_->GetEventPath()[i - 1];
     if (event_context.CurrentTargetSameAsTarget()) {
-      event_->SetEventPhase(Event::kAtTarget);
+      event_->SetEventPhase(Event::PhaseType::kAtTarget);
       event_->SetFireOnlyCaptureListenersAtTarget(true);
       event_context.HandleLocalEvents(*event_);
       event_->SetFireOnlyCaptureListenersAtTarget(false);
     } else {
-      event_->SetEventPhase(Event::kCapturingPhase);
+      event_->SetEventPhase(Event::PhaseType::kCapturingPhase);
       event_context.HandleLocalEvents(*event_);
     }
     if (event_->PropagationStopped())
@@ -326,12 +326,12 @@
     const NodeEventContext& event_context = event_->GetEventPath()[i];
     if (event_context.CurrentTargetSameAsTarget()) {
       // TODO(hayato): Need to check cancelBubble() also here?
-      event_->SetEventPhase(Event::kAtTarget);
+      event_->SetEventPhase(Event::PhaseType::kAtTarget);
       event_->SetFireOnlyNonCaptureListenersAtTarget(true);
       event_context.HandleLocalEvents(*event_);
       event_->SetFireOnlyNonCaptureListenersAtTarget(false);
     } else if (event_->bubbles() && !event_->cancelBubble()) {
-      event_->SetEventPhase(Event::kBubblingPhase);
+      event_->SetEventPhase(Event::PhaseType::kBubblingPhase);
       event_context.HandleLocalEvents(*event_);
     } else {
       continue;
@@ -340,7 +340,7 @@
       return;
   }
   if (event_->bubbles() && !event_->cancelBubble()) {
-    event_->SetEventPhase(Event::kBubblingPhase);
+    event_->SetEventPhase(Event::PhaseType::kBubblingPhase);
     event_->GetEventPath().GetWindowEventContext().HandleLocalEvents(*event_);
   }
 }
@@ -355,7 +355,7 @@
   event_->SetStopPropagation(false);
   event_->SetStopImmediatePropagation(false);
   // 15. Set event’s eventPhase attribute to NONE.
-  event_->SetEventPhase(0);
+  event_->SetEventPhase(Event::PhaseType::kNone);
   // TODO(rakina): investigate this and move it to the bottom of step 16
   // 17. Set event’s currentTarget attribute to null.
   event_->SetCurrentTarget(nullptr);
diff --git a/third_party/blink/renderer/core/dom/events/event_target.cc b/third_party/blink/renderer/core/dom/events/event_target.cc
index 748bbfe..273da6d 100644
--- a/third_party/blink/renderer/core/dom/events/event_target.cc
+++ b/third_party/blink/renderer/core/dom/events/event_target.cc
@@ -692,9 +692,9 @@
 DispatchEventResult EventTarget::DispatchEventInternal(Event& event) {
   event.SetTarget(this);
   event.SetCurrentTarget(this);
-  event.SetEventPhase(Event::kAtTarget);
+  event.SetEventPhase(Event::PhaseType::kAtTarget);
   DispatchEventResult dispatch_result = FireEventListeners(event);
-  event.SetEventPhase(0);
+  event.SetEventPhase(Event::PhaseType::kNone);
   return dispatch_result;
 }
 
diff --git a/third_party/blink/renderer/core/dom/events/registered_event_listener.cc b/third_party/blink/renderer/core/dom/events/registered_event_listener.cc
index baab2521..efe9f3c 100644
--- a/third_party/blink/renderer/core/dom/events/registered_event_listener.cc
+++ b/third_party/blink/renderer/core/dom/events/registered_event_listener.cc
@@ -87,16 +87,16 @@
 
 bool RegisteredEventListener::ShouldFire(const Event& event) const {
   if (event.FireOnlyCaptureListenersAtTarget()) {
-    DCHECK_EQ(event.eventPhase(), Event::kAtTarget);
+    DCHECK_EQ(event.eventPhase(), Event::PhaseType::kAtTarget);
     return Capture();
   }
   if (event.FireOnlyNonCaptureListenersAtTarget()) {
-    DCHECK_EQ(event.eventPhase(), Event::kAtTarget);
+    DCHECK_EQ(event.eventPhase(), Event::PhaseType::kAtTarget);
     return !Capture();
   }
-  if (event.eventPhase() == Event::kCapturingPhase)
+  if (event.eventPhase() == Event::PhaseType::kCapturingPhase)
     return Capture();
-  if (event.eventPhase() == Event::kBubblingPhase)
+  if (event.eventPhase() == Event::PhaseType::kBubblingPhase)
     return !Capture();
   return true;
 }
diff --git a/third_party/blink/renderer/core/fileapi/file.cc b/third_party/blink/renderer/core/fileapi/file.cc
index 279c9ca..596879a9 100644
--- a/third_party/blink/renderer/core/fileapi/file.cc
+++ b/third_party/blink/renderer/core/fileapi/file.cc
@@ -113,23 +113,6 @@
   return blob_data;
 }
 
-static std::unique_ptr<BlobData> CreateBlobDataForFileSystemURL(
-    const KURL& file_system_url,
-    const FileMetadata& metadata) {
-  std::unique_ptr<BlobData> blob_data;
-  if (metadata.length == BlobData::kToEndOfFile) {
-    blob_data = BlobData::CreateForFileSystemURLWithUnknownSize(
-        file_system_url, metadata.modification_time);
-  } else {
-    blob_data = std::make_unique<BlobData>();
-    blob_data->AppendFileSystemURL(file_system_url, 0, metadata.length,
-                                   metadata.modification_time);
-  }
-  blob_data->SetContentType(GetContentTypeFromFileName(
-      file_system_url.GetPath(), File::kWellKnownContentTypes));
-  return blob_data;
-}
-
 // static
 File* File::Create(ExecutionContext* context,
                    const HeapVector<Member<V8BlobPart>>& file_bits,
@@ -299,22 +282,6 @@
   DCHECK_GE(metadata.length, 0);
 }
 
-File::File(const KURL& file_system_url,
-           const FileMetadata& metadata,
-           UserVisibility user_visibility)
-    : Blob(BlobDataHandle::Create(
-          CreateBlobDataForFileSystemURL(file_system_url, metadata),
-          metadata.length)),
-      has_backing_file_(false),
-      user_visibility_(user_visibility),
-      name_(DecodeURLEscapeSequences(file_system_url.LastPathComponent(),
-                                     DecodeURLMode::kUTF8OrIsomorphic)),
-      file_system_url_(file_system_url),
-      snapshot_size_(metadata.length),
-      snapshot_modification_time_(metadata.modification_time) {
-  DCHECK_GE(metadata.length, 0);
-}
-
 File::File(const File& other)
     : Blob(other.GetBlobDataHandle()),
       has_backing_file_(other.has_backing_file_),
diff --git a/third_party/blink/renderer/core/fileapi/file.h b/third_party/blink/renderer/core/fileapi/file.h
index 72d6b7e..c061962 100644
--- a/third_party/blink/renderer/core/fileapi/file.h
+++ b/third_party/blink/renderer/core/fileapi/file.h
@@ -154,7 +154,6 @@
        const absl::optional<base::Time>& modification_time,
        scoped_refptr<BlobDataHandle>);
   File(const String& name, const FileMetadata&, UserVisibility);
-  File(const KURL& file_system_url, const FileMetadata&, UserVisibility);
   File(const KURL& file_system_url,
        const FileMetadata& metadata,
        UserVisibility user_visibility,
diff --git a/third_party/blink/renderer/core/frame/frame_test_helpers.h b/third_party/blink/renderer/core/frame/frame_test_helpers.h
index a818532..2ed99e1 100644
--- a/third_party/blink/renderer/core/frame/frame_test_helpers.h
+++ b/third_party/blink/renderer/core/frame/frame_test_helpers.h
@@ -289,6 +289,7 @@
   bool ShouldAutoDetermineCompositingToLCDTextSetting() override {
     return false;
   }
+  bool AllowsScrollResampling() override { return false; }
 
  private:
   cc::FakeLayerTreeFrameSink* last_created_frame_sink_ = nullptr;
diff --git a/third_party/blink/renderer/core/frame/local_dom_window.cc b/third_party/blink/renderer/core/frame/local_dom_window.cc
index 2b9a90b0..ec10f16 100644
--- a/third_party/blink/renderer/core/frame/local_dom_window.cc
+++ b/third_party/blink/renderer/core/frame/local_dom_window.cc
@@ -1971,7 +1971,7 @@
   event.SetTrusted(true);
   event.SetTarget(target ? target : this);
   event.SetCurrentTarget(this);
-  event.SetEventPhase(Event::kAtTarget);
+  event.SetEventPhase(Event::PhaseType::kAtTarget);
 
   DEVTOOLS_TIMELINE_TRACE_EVENT("EventDispatch",
                                 inspector_event_dispatch_event::Data, event);
diff --git a/third_party/blink/renderer/core/frame/local_frame_mojo_handler.cc b/third_party/blink/renderer/core/frame/local_frame_mojo_handler.cc
index 98cc08b..8b44dee 100644
--- a/third_party/blink/renderer/core/frame/local_frame_mojo_handler.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_mojo_handler.cc
@@ -206,7 +206,7 @@
   v8::Context::Scope context_scope(context);
   WTF::Vector<v8::Local<v8::Value>> args;
   for (const auto& argument : arguments) {
-    args.push_back(converter->ToV8Value(&argument, context));
+    args.push_back(converter->ToV8Value(argument, context));
   }
 
   v8::Local<v8::Value> object;
diff --git a/third_party/blink/renderer/core/html/forms/html_form_element.cc b/third_party/blink/renderer/core/html/forms/html_form_element.cc
index 753538c..66709078 100644
--- a/third_party/blink/renderer/core/html/forms/html_form_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_form_element.cc
@@ -183,7 +183,7 @@
 
 void HTMLFormElement::HandleLocalEvents(Event& event) {
   Node* target_node = event.target()->ToNode();
-  if (event.eventPhase() != Event::kCapturingPhase && target_node &&
+  if (event.eventPhase() != Event::PhaseType::kCapturingPhase && target_node &&
       target_node != this &&
       (event.type() == event_type_names::kSubmit ||
        event.type() == event_type_names::kReset)) {
diff --git a/third_party/blink/renderer/core/imagebitmap/image_bitmap.cc b/third_party/blink/renderer/core/imagebitmap/image_bitmap.cc
index 087e125..6f25b9d5 100644
--- a/third_party/blink/renderer/core/imagebitmap/image_bitmap.cc
+++ b/third_party/blink/renderer/core/imagebitmap/image_bitmap.cc
@@ -611,7 +611,8 @@
 }
 
 ImageBitmap::ImageBitmap(const SkPixmap& pixmap,
-                         bool is_image_bitmap_origin_clean) {
+                         bool is_image_bitmap_origin_clean,
+                         ImageOrientationEnum image_orientation) {
   sk_sp<SkImage> raster_copy = SkImage::MakeRasterCopy(pixmap);
   if (!raster_copy)
     return;
@@ -619,6 +620,7 @@
   if (!image_)
     return;
   image_->SetOriginClean(is_image_bitmap_origin_clean);
+  image_->SetOrientation(image_orientation);
   UpdateImageBitmapMemoryUsage();
 }
 
diff --git a/third_party/blink/renderer/core/imagebitmap/image_bitmap.h b/third_party/blink/renderer/core/imagebitmap/image_bitmap.h
index 44da2982..1bc5336 100644
--- a/third_party/blink/renderer/core/imagebitmap/image_bitmap.h
+++ b/third_party/blink/renderer/core/imagebitmap/image_bitmap.h
@@ -72,7 +72,9 @@
   // This constructor may called by structured-cloning an ImageBitmap.
   // isImageBitmapOriginClean indicates whether the original ImageBitmap is
   // origin clean or not.
-  ImageBitmap(const SkPixmap& pixmap, bool is_image_bitmap_origin_clean);
+  ImageBitmap(const SkPixmap& pixmap,
+              bool is_image_bitmap_origin_clean,
+              ImageOrientationEnum);
 
   // Type and helper function required by CallbackPromiseAdapter:
   using WebType = sk_sp<SkImage>;
@@ -94,6 +96,9 @@
   bool IsNeutered() const override { return is_neutered_; }
   bool OriginClean() const { return image_->OriginClean(); }
   bool IsPremultiplied() const { return image_->IsPremultiplied(); }
+  ImageOrientationEnum ImageOrientation() const {
+    return image_->CurrentFrameOrientation().Orientation();
+  }
   scoped_refptr<StaticBitmapImage> Transfer();
   void close();
 
diff --git a/third_party/blink/renderer/core/input/mouse_event_manager.cc b/third_party/blink/renderer/core/input/mouse_event_manager.cc
index b8b34dd0..f86fc1e 100644
--- a/third_party/blink/renderer/core/input/mouse_event_manager.cc
+++ b/third_party/blink/renderer/core/input/mouse_event_manager.cc
@@ -615,10 +615,10 @@
 bool MouseEventManager::SlideFocusOnShadowHostIfNecessary(
     const Element& element) {
   if (Element* delegated_target = element.GetFocusableArea()) {
-    // Use FocusTypeForward instead of FocusTypeMouse here to mean the
-    // focus has slided.
+    // Use FocusType::kMouse instead of FocusType::kForward
+    // in order to prevent :focus-visible from being set
     delegated_target->Focus(FocusParams(SelectionBehaviorOnFocus::kReset,
-                                        mojom::blink::FocusType::kForward,
+                                        mojom::blink::FocusType::kMouse,
                                         nullptr));
     return true;
   }
diff --git a/third_party/blink/renderer/core/input/overscroll_behavior_test.cc b/third_party/blink/renderer/core/input/overscroll_behavior_test.cc
index 73d7722..b57e748 100644
--- a/third_party/blink/renderer/core/input/overscroll_behavior_test.cc
+++ b/third_party/blink/renderer/core/input/overscroll_behavior_test.cc
@@ -18,7 +18,7 @@
  protected:
   void SetUp() override;
 
-  void SetInnerOverscrollBehavior(EOverscrollBehavior, EOverscrollBehavior);
+  void SetInnerOverscrollBehavior(String, String);
 
   void ScrollBegin(double hint_x, double hint_y);
   void ScrollUpdate(double x, double y);
@@ -30,16 +30,20 @@
 void OverscrollBehaviorTest::SetUp() {
   SimTest::SetUp();
   v8::HandleScope HandleScope(v8::Isolate::GetCurrent());
-  WebView().MainFrameViewWidget()->Resize(gfx::Size(400, 400));
+  ResizeView(gfx::Size(400, 400));
   SimRequest request("https://example.com/test.html", "text/html");
   LoadURL("https://example.com/test.html");
   request.Complete(R"HTML(
-    <div id='outer' style='height: 300px; width: 300px; overflow:
-    scroll;'>
-      <div id='inner' style='height: 500px; width: 500px; overflow:
-    scroll;'>
+    <style>
+      #outer { height: 300px; width: 300px; overflow: scroll; }
+      #inner { height: 500px; width: 500px; overflow: scroll; }
+    </style>
+    <div id='outer'>
+      <div id='inner'>
         <div id='content' style='height: 700px; width: 700px;'>
-    </div></div></div>
+        </div>
+      </div>
+    </div>
   )HTML");
 
   Compositor().BeginFrame();
@@ -59,15 +63,12 @@
   ASSERT_EQ(inner->scrollTop(), 0);
 }
 
-void OverscrollBehaviorTest::SetInnerOverscrollBehavior(EOverscrollBehavior x,
-                                                        EOverscrollBehavior y) {
-  Element* inner = GetDocument().getElementById("inner");
-  scoped_refptr<ComputedStyle> modified_style =
-      ComputedStyle::Clone(*inner->GetComputedStyle());
-  modified_style->SetOverscrollBehaviorX(x);
-  modified_style->SetOverscrollBehaviorY(y);
-  inner->GetLayoutObject()->SetStyle(std::move(modified_style),
-                                     LayoutObject::ApplyStyleChanges::kNo);
+void OverscrollBehaviorTest::SetInnerOverscrollBehavior(String x, String y) {
+  GetDocument().getElementById("inner")->setAttribute(
+      html_names::kStyleAttr,
+      AtomicString(
+          String::Format("overscroll-behavior-x: %s; overscroll-behavior-y: %s",
+                         x.Utf8().c_str(), y.Utf8().c_str())));
 }
 
 void OverscrollBehaviorTest::ScrollBegin(double hint_x, double hint_y) {
@@ -80,7 +81,7 @@
   event.data.scroll_begin.delta_y_hint = -hint_y;
   event.data.scroll_begin.pointer_count = 1;
   event.SetFrameScale(1);
-  GetDocument().GetFrame()->GetEventHandler().HandleGestureScrollEvent(event);
+  GetWebFrameWidget().DispatchThroughCcInputHandler(event);
 }
 
 void OverscrollBehaviorTest::ScrollUpdate(double delta_x, double delta_y) {
@@ -92,7 +93,7 @@
   event.data.scroll_update.delta_x = -delta_x;
   event.data.scroll_update.delta_y = -delta_y;
   event.SetFrameScale(1);
-  GetDocument().GetFrame()->GetEventHandler().HandleGestureScrollEvent(event);
+  GetWebFrameWidget().DispatchThroughCcInputHandler(event);
 }
 
 void OverscrollBehaviorTest::ScrollEnd() {
@@ -101,18 +102,23 @@
                         WebGestureDevice::kTouchscreen);
   event.SetPositionInWidget(gfx::PointF(20, 20));
   event.SetPositionInScreen(gfx::PointF(20, 20));
-  GetDocument().GetFrame()->GetEventHandler().HandleGestureScrollEvent(event);
+  GetWebFrameWidget().DispatchThroughCcInputHandler(event);
 }
 
 void OverscrollBehaviorTest::Scroll(double x, double y) {
+  // Commits property tree state, so cc sees updated overscroll-behavior.
+  Compositor().BeginFrame();
+
   ScrollBegin(x, y);
   ScrollUpdate(x, y);
   ScrollEnd();
+
+  // Applies viewport deltas, so main sees the new scroll offset.
+  Compositor().BeginFrame();
 }
 
 TEST_F(OverscrollBehaviorTest, AutoAllowsPropagation) {
-  SetInnerOverscrollBehavior(EOverscrollBehavior::kAuto,
-                             EOverscrollBehavior::kAuto);
+  SetInnerOverscrollBehavior("auto", "auto");
   Scroll(-100.0, -100.0);
   Element* outer = GetDocument().getElementById("outer");
   ASSERT_EQ(outer->scrollLeft(), 100);
@@ -120,8 +126,7 @@
 }
 
 TEST_F(OverscrollBehaviorTest, ContainOnXPreventsPropagationsOnX) {
-  SetInnerOverscrollBehavior(EOverscrollBehavior::kContain,
-                             EOverscrollBehavior::kAuto);
+  SetInnerOverscrollBehavior("contain", "auto");
   Scroll(-100, 0.0);
   Element* outer = GetDocument().getElementById("outer");
   ASSERT_EQ(outer->scrollLeft(), 200);
@@ -129,8 +134,7 @@
 }
 
 TEST_F(OverscrollBehaviorTest, ContainOnXAllowsPropagationsOnY) {
-  SetInnerOverscrollBehavior(EOverscrollBehavior::kContain,
-                             EOverscrollBehavior::kAuto);
+  SetInnerOverscrollBehavior("contain", "auto");
   Scroll(0.0, -100.0);
   Element* outer = GetDocument().getElementById("outer");
   ASSERT_EQ(outer->scrollLeft(), 200);
@@ -138,8 +142,7 @@
 }
 
 TEST_F(OverscrollBehaviorTest, ContainOnXPreventsDiagonalPropagations) {
-  SetInnerOverscrollBehavior(EOverscrollBehavior::kContain,
-                             EOverscrollBehavior::kAuto);
+  SetInnerOverscrollBehavior("contain", "auto");
   Scroll(-100.0, -100.0);
   Element* outer = GetDocument().getElementById("outer");
   ASSERT_EQ(outer->scrollLeft(), 200);
@@ -147,8 +150,7 @@
 }
 
 TEST_F(OverscrollBehaviorTest, ContainOnYPreventsPropagationsOnY) {
-  SetInnerOverscrollBehavior(EOverscrollBehavior::kAuto,
-                             EOverscrollBehavior::kContain);
+  SetInnerOverscrollBehavior("auto", "contain");
   Scroll(0.0, -100.0);
   Element* outer = GetDocument().getElementById("outer");
   ASSERT_EQ(outer->scrollLeft(), 200);
@@ -156,8 +158,7 @@
 }
 
 TEST_F(OverscrollBehaviorTest, ContainOnYAllowsPropagationsOnX) {
-  SetInnerOverscrollBehavior(EOverscrollBehavior::kAuto,
-                             EOverscrollBehavior::kContain);
+  SetInnerOverscrollBehavior("auto", "contain");
   Scroll(-100.0, 0.0);
   Element* outer = GetDocument().getElementById("outer");
   ASSERT_EQ(outer->scrollLeft(), 100);
@@ -165,8 +166,7 @@
 }
 
 TEST_F(OverscrollBehaviorTest, ContainOnYPreventsDiagonalPropagations) {
-  SetInnerOverscrollBehavior(EOverscrollBehavior::kAuto,
-                             EOverscrollBehavior::kContain);
+  SetInnerOverscrollBehavior("auto", "contain");
   Scroll(-100.0, -100.0);
   Element* outer = GetDocument().getElementById("outer");
   ASSERT_EQ(outer->scrollLeft(), 200);
@@ -174,14 +174,28 @@
 }
 
 TEST_F(OverscrollBehaviorTest, LatchToTheElementPreventedByOverscrollBehavior) {
-  SetInnerOverscrollBehavior(EOverscrollBehavior::kNone,
-                             EOverscrollBehavior::kNone);
+  SetInnerOverscrollBehavior("none", "none");
+  Compositor().BeginFrame();
   ScrollBegin(-100, 0);
+
+  // Always call BeginFrame between updates to force the last update to be
+  // handled via InputHandlerProxy::DeliverInputForBeginFrame.  This avoids
+  // interference from event coalescing in CompositorThreadEventQueue::Queue.
+  //
+  // Note: this test also requires ScrollPredictor to be disabled; that happens
+  // via TestWebFrameWidget::AllowsScrollResampling.
+  //
   ScrollUpdate(-100, 0);
+  Compositor().BeginFrame();
   ScrollUpdate(100, 0);
+  Compositor().BeginFrame();
   ScrollUpdate(0, -100);
+  Compositor().BeginFrame();
   ScrollUpdate(0, 100);
+  Compositor().BeginFrame();
+
   ScrollEnd();
+  Compositor().BeginFrame();
 
   Element* inner = GetDocument().getElementById("inner");
   ASSERT_EQ(inner->scrollLeft(), 100);
diff --git a/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc b/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc
index 308eba4..a4d4ccc 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc
@@ -524,9 +524,26 @@
     }
   }
 
-  if (oof_data->oof_positioned_fragmentainer_descendants.IsEmpty())
+  PropagateOOFFragmentainerDescendants(fragment, offset, relative_offset,
+                                       containing_block_adjustment,
+                                       fixedpos_containing_block);
+}
+
+void NGContainerFragmentBuilder::PropagateOOFFragmentainerDescendants(
+    const NGPhysicalFragment& fragment,
+    LogicalOffset offset,
+    LogicalOffset relative_offset,
+    LayoutUnit containing_block_adjustment,
+    const NGContainingBlock<LogicalOffset>* fixedpos_containing_block) {
+  NGFragmentedOutOfFlowData* oof_data = fragment.FragmentedOutOfFlowData();
+  if (!oof_data || oof_data->oof_positioned_fragmentainer_descendants.IsEmpty())
     return;
 
+  const WritingModeConverter converter(GetWritingDirection(), fragment.Size());
+  const NGPhysicalBoxFragment* box_fragment =
+      DynamicTo<NGPhysicalBoxFragment>(&fragment);
+  bool is_column_spanner = box_fragment && box_fragment->IsColumnSpanAll();
+
   auto& out_of_flow_fragmentainer_descendants =
       oof_data->oof_positioned_fragmentainer_descendants;
   wtf_size_t next_idx;
diff --git a/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h b/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h
index 67e14a0..a05ae89 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h
@@ -263,6 +263,14 @@
       const NGInlineContainer<LogicalOffset>* fixedpos_inline_container =
           nullptr,
       LogicalOffset additional_fixedpos_offset = LogicalOffset());
+  // Same as PropagateOOFPositionedInfo(), but only performs the propagation of
+  // OOF fragmentainer descendants.
+  void PropagateOOFFragmentainerDescendants(
+      const NGPhysicalFragment& fragment,
+      LogicalOffset offset,
+      LogicalOffset relative_offset,
+      LayoutUnit containing_block_adjustment,
+      const NGContainingBlock<LogicalOffset>* fixedpos_containing_block);
 
   void SetIsSelfCollapsing() { is_self_collapsing_ = true; }
 
diff --git a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc
index c61d922..0b1abf7 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc
@@ -1193,13 +1193,14 @@
     ClearCollectionScope<HeapVector<NodeToLayout>> fragmented_descendants_scope(
         &fragmented_descendants);
     fragmentainer_consumed_block_size_ = LayoutUnit();
-    wtf_size_t num_children = container_builder_->Children().size();
+    auto& children = FragmentationContextChildren();
+    wtf_size_t num_children = children.size();
 
     // Layout the OOF descendants in order of fragmentainer index.
     for (wtf_size_t index = 0; index < descendants_to_layout.size(); index++) {
       const NGPhysicalFragment* fragment = nullptr;
       if (index < num_children)
-        fragment = container_builder_->Children()[index].fragment;
+        fragment = children[index].fragment;
 
       // Skip over any column spanners.
       if (!fragment || fragment->IsFragmentainerBox()) {
@@ -1210,7 +1211,7 @@
                                   &fragmented_descendants);
         // Retrieve the updated or newly added fragmentainer, and add its block
         // contribution to the consumed block size.
-        fragment = container_builder_->Children()[index].fragment;
+        fragment = children[index].fragment;
         fragmentainer_consumed_block_size_ +=
             fragment->Size()
                 .ConvertToLogical(container_builder_->Style().GetWritingMode())
@@ -1680,7 +1681,8 @@
     wtf_size_t index,
     LogicalOffset fragmentainer_progression,
     HeapVector<NodeToLayout>* fragmented_descendants) {
-  wtf_size_t num_children = container_builder_->Children().size();
+  auto& children = FragmentationContextChildren();
+  wtf_size_t num_children = children.size();
   bool is_new_fragment = index >= num_children;
 
   DCHECK(fragmented_descendants);
@@ -1701,14 +1703,13 @@
 
   // If we are a new fragment, find a non-spanner fragmentainer as a basis.
   wtf_size_t original_index = index;
-  while (
-      index >= num_children ||
-      !container_builder_->Children()[index].fragment->IsFragmentainerBox()) {
+  while (index >= num_children ||
+         !children[index].fragment->IsFragmentainerBox()) {
     DCHECK_GT(num_children, 0u);
     index--;
   }
 
-  const auto& fragmentainer = container_builder_->Children()[index];
+  const auto& fragmentainer = children[index];
   DCHECK(fragmentainer.fragment->IsFragmentainerBox());
   const NGBlockNode& node = container_builder_->Node();
   const auto* fragment =
@@ -1847,7 +1848,7 @@
       // https://www.w3.org/TR/CSS22/page.html#page-box
       DCHECK(container_builder_->Node().IsPaginatedRoot());
       container = To<NGPhysicalBoxFragment>(
-          container_builder_->Children()[0].fragment.Get());
+          FragmentationContextChildren()[0].fragment.Get());
     }
 
     LogicalOffset legacy_offset =
@@ -1907,12 +1908,13 @@
     LogicalOffset fragmentainer_progression,
     bool create_new_fragment) {
   if (create_new_fragment) {
-    wtf_size_t num_children = container_builder_->Children().size();
-    if (index != num_children - 1 && !container_builder_->Children()[index + 1]
-                                          .fragment->IsFragmentainerBox()) {
+    auto& children = FragmentationContextChildren();
+    wtf_size_t num_children = children.size();
+    if (index != num_children - 1 &&
+        !children[index + 1].fragment->IsFragmentainerBox()) {
       // If we are a new fragment and are separated from other columns by a
       // spanner, compute the correct column offset to use.
-      const auto& spanner = container_builder_->Children()[index + 1];
+      const auto& spanner = children[index + 1];
       DCHECK(spanner.fragment->IsColumnSpanAll());
 
       offset = spanner.offset;
@@ -1929,7 +1931,8 @@
 
 NGConstraintSpace NGOutOfFlowLayoutPart::GetFragmentainerConstraintSpace(
     wtf_size_t index) {
-  wtf_size_t num_children = container_builder_->Children().size();
+  auto& children = FragmentationContextChildren();
+  wtf_size_t num_children = children.size();
   bool is_new_fragment = index >= num_children;
   // Allow margins to be discarded if this is not the first column in the
   // multicol container, and we're not right after a spanner.
@@ -1939,19 +1942,18 @@
   // spanner), and this is the first column in the next outer fragmentainer, we
   // should still discard margins, since there is no explicit break involved.
   bool allow_discard_start_margin =
-      is_new_fragment || (index > 0 && container_builder_->Children()[index - 1]
-                                           .fragment->IsFragmentainerBox());
+      is_new_fragment ||
+      (index > 0 && children[index - 1].fragment->IsFragmentainerBox());
 
   // If we are a new fragment, find a non-spanner fragmentainer to base our
   // constraint space off of.
-  while (
-      index >= num_children ||
-      !container_builder_->Children()[index].fragment->IsFragmentainerBox()) {
+  while (index >= num_children ||
+         !children[index].fragment->IsFragmentainerBox()) {
     DCHECK_GT(num_children, 0u);
     index--;
   }
 
-  const auto& fragmentainer = container_builder_->Children()[index];
+  const auto& fragmentainer = children[index];
   DCHECK(fragmentainer.fragment->IsFragmentainerBox());
   const auto& fragment = To<NGPhysicalBoxFragment>(*fragmentainer.fragment);
   const WritingMode container_writing_mode =
@@ -1963,8 +1965,7 @@
   // spanner, compute the correct column block size to use.
   if (is_new_fragment && index != num_children - 1 &&
       original_column_block_size_ != kIndefiniteSize &&
-      !container_builder_->Children()[index + 1]
-           .fragment->IsFragmentainerBox()) {
+      !children[index + 1].fragment->IsFragmentainerBox()) {
     column_size.block_size =
         original_column_block_size_ -
         container_builder_->BlockOffsetForAdditionalColumns();
@@ -2004,9 +2005,10 @@
   LayoutUnit current_max_block_size;
   // The block size for the last fragmentainer we encountered.
   LayoutUnit fragmentainer_block_size;
+  auto& children = FragmentationContextChildren();
   // TODO(bebeaudr): There is a possible performance improvement here as we'll
   // repeat this for each abspos in a same fragmentainer.
-  for (auto& child : container_builder_->Children()) {
+  for (auto& child : children) {
     if (child.fragment->IsFragmentainerBox()) {
       fragmentainer_block_size = child.fragment->Size()
                                      .ConvertToLogical(default_writing_mode)
@@ -2040,8 +2042,7 @@
   // If we are a new fragment and are separated from other columns by a
   // spanner, compute the correct fragmentainer_block_size.
   if (original_column_block_size_ != kIndefiniteSize &&
-      !container_builder_->Children()[child_index - 1]
-           .fragment->IsFragmentainerBox()) {
+      !children[child_index - 1].fragment->IsFragmentainerBox()) {
     fragmentainer_block_size =
         original_column_block_size_ -
         container_builder_->BlockOffsetForAdditionalColumns();
@@ -2134,8 +2135,8 @@
     DCHECK(!box.IsColumnSpanAll());
     // We're currently laying out |containing_block|, and it's a multicol
     // container. Search inside fragmentainer children in the builder.
-    for (const NGContainerFragmentBuilder::ChildWithOffset& child :
-         container_builder_->Children()) {
+    auto& children = FragmentationContextChildren();
+    for (const NGContainerFragmentBuilder::ChildWithOffset& child : children) {
       if (ReplaceFragmentainerChild(*child.fragment))
         return;
     }
diff --git a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h
index 533b661a..f3eff214 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h
@@ -342,6 +342,12 @@
   NGLogicalStaticPosition ToStaticPositionForLegacy(
       NGLogicalStaticPosition position) const;
 
+  const NGContainerFragmentBuilder::ChildrenVector&
+  FragmentationContextChildren() const {
+    DCHECK(container_builder_->IsBlockFragmentationContextRoot());
+    return container_builder_->Children();
+  }
+
   NGBoxFragmentBuilder* container_builder_;
   ContainingBlockInfo default_containing_block_info_for_absolute_;
   ContainingBlockInfo default_containing_block_info_for_fixed_;
diff --git a/third_party/blink/renderer/core/layout/svg/svg_layout_tree_as_text.cc b/third_party/blink/renderer/core/layout/svg/svg_layout_tree_as_text.cc
index fd0b696..3ba73158 100644
--- a/third_party/blink/renderer/core/layout/svg/svg_layout_tree_as_text.cc
+++ b/third_party/blink/renderer/core/layout/svg/svg_layout_tree_as_text.cc
@@ -766,7 +766,8 @@
     DCHECK(style.HasFilter());
     DCHECK_EQ(style.Filter().size(), 1u);
     const FilterOperation& filter_operation = *style.Filter().at(0);
-    DCHECK_EQ(filter_operation.GetType(), FilterOperation::kReference);
+    DCHECK_EQ(filter_operation.GetType(),
+              FilterOperation::OperationType::kReference);
     const auto& reference_filter_operation =
         To<ReferenceFilterOperation>(filter_operation);
     WriteSVGResourceReferencePrefix(ts, "filter", filter,
diff --git a/third_party/blink/renderer/core/page/page.h b/third_party/blink/renderer/core/page/page.h
index 96fd41a..9cfb780 100644
--- a/third_party/blink/renderer/core/page/page.h
+++ b/third_party/blink/renderer/core/page/page.h
@@ -527,7 +527,7 @@
   bool inside_portal_ = false;
 
   // Whether the page is being prerendered by the Prerender2
-  // feature. See content/browser/prerender/README.md.
+  // feature. See content/browser/preloading/prerender/README.md.
   //
   // This is ordinarily initialized by WebViewImpl immediately after creating
   // this Page. Once initialized, it can only transition from true to false on
diff --git a/third_party/blink/renderer/core/paint/filter_effect_builder.cc b/third_party/blink/renderer/core/paint/filter_effect_builder.cc
index 3a829376..d7da69c 100644
--- a/third_party/blink/renderer/core/paint/filter_effect_builder.cc
+++ b/third_party/blink/renderer/core/paint/filter_effect_builder.cc
@@ -147,7 +147,7 @@
   for (FilterOperation* filter_operation : operations.Operations()) {
     FilterEffect* effect = nullptr;
     switch (filter_operation->GetType()) {
-      case FilterOperation::kReference: {
+      case FilterOperation::OperationType::kReference: {
         auto& reference_operation =
             To<ReferenceFilterOperation>(*filter_operation);
         Filter* reference_filter =
@@ -163,7 +163,7 @@
         reference_operation.SetFilter(reference_filter);
         break;
       }
-      case FilterOperation::kGrayscale: {
+      case FilterOperation::OperationType::kGrayscale: {
         Vector<float> input_parameters = GrayscaleMatrix(
             To<BasicColorMatrixFilterOperation>(filter_operation)->Amount());
         effect = MakeGarbageCollected<FEColorMatrix>(
@@ -171,7 +171,7 @@
             std::move(input_parameters));
         break;
       }
-      case FilterOperation::kSepia: {
+      case FilterOperation::OperationType::kSepia: {
         Vector<float> input_parameters = SepiaMatrix(
             To<BasicColorMatrixFilterOperation>(filter_operation)->Amount());
         effect = MakeGarbageCollected<FEColorMatrix>(
@@ -179,7 +179,7 @@
             std::move(input_parameters));
         break;
       }
-      case FilterOperation::kSaturate: {
+      case FilterOperation::OperationType::kSaturate: {
         Vector<float> input_parameters;
         input_parameters.push_back(ClampTo<float>(
             To<BasicColorMatrixFilterOperation>(filter_operation)->Amount()));
@@ -188,7 +188,7 @@
             std::move(input_parameters));
         break;
       }
-      case FilterOperation::kHueRotate: {
+      case FilterOperation::OperationType::kHueRotate: {
         Vector<float> input_parameters;
         input_parameters.push_back(ClampTo<float>(
             To<BasicColorMatrixFilterOperation>(filter_operation)->Amount()));
@@ -197,14 +197,14 @@
             std::move(input_parameters));
         break;
       }
-      case FilterOperation::kLuminanceToAlpha: {
+      case FilterOperation::OperationType::kLuminanceToAlpha: {
         Vector<float> input_parameters;
         effect = MakeGarbageCollected<FEColorMatrix>(
             parent_filter, FECOLORMATRIX_TYPE_LUMINANCETOALPHA,
             std::move(input_parameters));
         break;
       }
-      case FilterOperation::kColorMatrix: {
+      case FilterOperation::OperationType::kColorMatrix: {
         Vector<float> input_parameters =
             To<ColorMatrixFilterOperation>(filter_operation)->Values();
         effect = MakeGarbageCollected<FEColorMatrix>(
@@ -212,7 +212,7 @@
             std::move(input_parameters));
         break;
       }
-      case FilterOperation::kInvert: {
+      case FilterOperation::OperationType::kInvert: {
         BasicComponentTransferFilterOperation* component_transfer_operation =
             To<BasicComponentTransferFilterOperation>(filter_operation);
         ComponentTransferFunction transfer_function;
@@ -230,7 +230,7 @@
             transfer_function, null_function);
         break;
       }
-      case FilterOperation::kOpacity: {
+      case FilterOperation::OperationType::kOpacity: {
         ComponentTransferFunction transfer_function;
         transfer_function.type = FECOMPONENTTRANSFER_TYPE_TABLE;
         Vector<float> transfer_parameters;
@@ -246,7 +246,7 @@
             transfer_function);
         break;
       }
-      case FilterOperation::kBrightness: {
+      case FilterOperation::OperationType::kBrightness: {
         ComponentTransferFunction transfer_function;
         transfer_function.type = FECOMPONENTTRANSFER_TYPE_LINEAR;
         transfer_function.slope = ClampTo<float>(
@@ -260,7 +260,7 @@
             transfer_function, null_function);
         break;
       }
-      case FilterOperation::kContrast: {
+      case FilterOperation::OperationType::kContrast: {
         ComponentTransferFunction transfer_function;
         transfer_function.type = FECOMPONENTTRANSFER_TYPE_LINEAR;
         float amount = ClampTo<float>(
@@ -275,7 +275,7 @@
             transfer_function, null_function);
         break;
       }
-      case FilterOperation::kBlur: {
+      case FilterOperation::OperationType::kBlur: {
         float std_deviation = FloatValueForLength(
             To<BlurFilterOperation>(filter_operation)->StdDeviation(), 0);
         std_deviation *= shorthand_scale_;
@@ -283,7 +283,7 @@
             parent_filter, std_deviation, std_deviation);
         break;
       }
-      case FilterOperation::kDropShadow: {
+      case FilterOperation::OperationType::kDropShadow: {
         const ShadowData& shadow =
             To<DropShadowFilterOperation>(*filter_operation).Shadow();
         gfx::PointF offset =
@@ -294,14 +294,14 @@
             shadow.GetColor().GetColor(), 1);
         break;
       }
-      case FilterOperation::kBoxReflect: {
+      case FilterOperation::OperationType::kBoxReflect: {
         BoxReflectFilterOperation* box_reflect_operation =
             To<BoxReflectFilterOperation>(filter_operation);
         effect = MakeGarbageCollected<FEBoxReflect>(
             parent_filter, box_reflect_operation->Reflection());
         break;
       }
-      case FilterOperation::kConvolveMatrix: {
+      case FilterOperation::OperationType::kConvolveMatrix: {
         ConvolveMatrixFilterOperation* convolve_matrix_operation =
             To<ConvolveMatrixFilterOperation>(filter_operation);
         effect = MakeGarbageCollected<FEConvolveMatrix>(
@@ -314,7 +314,7 @@
             convolve_matrix_operation->KernelMatrix());
         break;
       }
-      case FilterOperation::kComponentTransfer: {
+      case FilterOperation::OperationType::kComponentTransfer: {
         ComponentTransferFilterOperation* component_transfer_operation =
             To<ComponentTransferFilterOperation>(filter_operation);
         effect = MakeGarbageCollected<FEComponentTransfer>(
@@ -324,7 +324,7 @@
             component_transfer_operation->AlphaFunc());
         break;
       }
-      case FilterOperation::kTurbulence: {
+      case FilterOperation::OperationType::kTurbulence: {
         TurbulenceFilterOperation* turbulence_filter_operation =
             To<TurbulenceFilterOperation>(filter_operation);
         effect = MakeGarbageCollected<FETurbulence>(
@@ -341,7 +341,8 @@
     }
 
     if (effect) {
-      if (filter_operation->GetType() != FilterOperation::kReference) {
+      if (filter_operation->GetType() !=
+          FilterOperation::OperationType::kReference) {
         // Unlike SVG, filters applied here should not clip to their primitive
         // subregions.
         effect->SetClipsToBounds(false);
@@ -363,7 +364,7 @@
   CompositorFilterOperations filters;
   for (FilterOperation* op : operations.Operations()) {
     switch (op->GetType()) {
-      case FilterOperation::kReference: {
+      case FilterOperation::OperationType::kReference: {
         auto& reference_operation = To<ReferenceFilterOperation>(*op);
         Filter* reference_filter =
             BuildReferenceFilter(reference_operation, nullptr);
@@ -384,22 +385,22 @@
         reference_operation.SetFilter(reference_filter);
         break;
       }
-      case FilterOperation::kGrayscale:
-      case FilterOperation::kSepia:
-      case FilterOperation::kSaturate:
-      case FilterOperation::kHueRotate: {
+      case FilterOperation::OperationType::kGrayscale:
+      case FilterOperation::OperationType::kSepia:
+      case FilterOperation::OperationType::kSaturate:
+      case FilterOperation::OperationType::kHueRotate: {
         float amount = To<BasicColorMatrixFilterOperation>(*op).Amount();
         switch (op->GetType()) {
-          case FilterOperation::kGrayscale:
+          case FilterOperation::OperationType::kGrayscale:
             filters.AppendGrayscaleFilter(amount);
             break;
-          case FilterOperation::kSepia:
+          case FilterOperation::OperationType::kSepia:
             filters.AppendSepiaFilter(amount);
             break;
-          case FilterOperation::kSaturate:
+          case FilterOperation::OperationType::kSaturate:
             filters.AppendSaturateFilter(amount);
             break;
-          case FilterOperation::kHueRotate:
+          case FilterOperation::OperationType::kHueRotate:
             filters.AppendHueRotateFilter(amount);
             break;
           default:
@@ -407,35 +408,35 @@
         }
         break;
       }
-      case FilterOperation::kLuminanceToAlpha:
-      case FilterOperation::kConvolveMatrix:
-      case FilterOperation::kComponentTransfer:
-      case FilterOperation::kTurbulence:
+      case FilterOperation::OperationType::kLuminanceToAlpha:
+      case FilterOperation::OperationType::kConvolveMatrix:
+      case FilterOperation::OperationType::kComponentTransfer:
+      case FilterOperation::OperationType::kTurbulence:
         // These filter types only exist for Canvas filters.
         NOTREACHED();
         break;
-      case FilterOperation::kColorMatrix: {
+      case FilterOperation::OperationType::kColorMatrix: {
         Vector<float> matrix_values =
             To<ColorMatrixFilterOperation>(*op).Values();
         filters.AppendColorMatrixFilter(matrix_values);
         break;
       }
-      case FilterOperation::kInvert:
-      case FilterOperation::kOpacity:
-      case FilterOperation::kBrightness:
-      case FilterOperation::kContrast: {
+      case FilterOperation::OperationType::kInvert:
+      case FilterOperation::OperationType::kOpacity:
+      case FilterOperation::OperationType::kBrightness:
+      case FilterOperation::OperationType::kContrast: {
         float amount = To<BasicComponentTransferFilterOperation>(*op).Amount();
         switch (op->GetType()) {
-          case FilterOperation::kInvert:
+          case FilterOperation::OperationType::kInvert:
             filters.AppendInvertFilter(amount);
             break;
-          case FilterOperation::kOpacity:
+          case FilterOperation::OperationType::kOpacity:
             filters.AppendOpacityFilter(amount);
             break;
-          case FilterOperation::kBrightness:
+          case FilterOperation::OperationType::kBrightness:
             filters.AppendBrightnessFilter(amount);
             break;
-          case FilterOperation::kContrast:
+          case FilterOperation::OperationType::kContrast:
             filters.AppendContrastFilter(amount);
             break;
           default:
@@ -443,14 +444,14 @@
         }
         break;
       }
-      case FilterOperation::kBlur: {
+      case FilterOperation::OperationType::kBlur: {
         float pixel_radius =
             To<BlurFilterOperation>(*op).StdDeviation().GetFloatValue();
         pixel_radius *= shorthand_scale_;
         filters.AppendBlurFilter(pixel_radius, blur_tile_mode_);
         break;
       }
-      case FilterOperation::kDropShadow: {
+      case FilterOperation::OperationType::kDropShadow: {
         const ShadowData& shadow = To<DropShadowFilterOperation>(*op).Shadow();
         gfx::Point floored_offset = gfx::ToFlooredPoint(
             gfx::ScalePoint(shadow.Location(), shorthand_scale_));
@@ -459,7 +460,7 @@
                                        shadow.GetColor().GetColor());
         break;
       }
-      case FilterOperation::kBoxReflect: {
+      case FilterOperation::OperationType::kBoxReflect: {
         // TODO(jbroman): Consider explaining box reflect to the compositor,
         // instead of calling this a "reference filter".
         const auto& reflection =
@@ -468,7 +469,7 @@
             paint_filter_builder::BuildBoxReflectFilter(reflection, nullptr));
         break;
       }
-      case FilterOperation::kNone:
+      case FilterOperation::OperationType::kNone:
         break;
     }
   }
diff --git a/third_party/blink/renderer/core/paint/theme_painter_default.cc b/third_party/blink/renderer/core/paint/theme_painter_default.cc
index 2cb3552d..ce1f5e6 100644
--- a/third_party/blink/renderer/core/paint/theme_painter_default.cc
+++ b/third_party/blink/renderer/core/paint/theme_painter_default.cc
@@ -154,9 +154,6 @@
 }
 
 absl::optional<SkColor> GetAccentColor(const ComputedStyle& style) {
-  if (!RuntimeEnabledFeatures::CSSAccentColorEnabled())
-    return absl::nullopt;
-
   absl::optional<Color> css_accent_color = style.AccentColorResolved();
   if (css_accent_color)
     return css_accent_color->Rgb();
diff --git a/third_party/blink/renderer/core/style/filter_operation.cc b/third_party/blink/renderer/core/style/filter_operation.cc
index 7a840db..12d48168 100644
--- a/third_party/blink/renderer/core/style/filter_operation.cc
+++ b/third_party/blink/renderer/core/style/filter_operation.cc
@@ -49,7 +49,9 @@
 
 ReferenceFilterOperation::ReferenceFilterOperation(const AtomicString& url,
                                                    SVGResource* resource)
-    : FilterOperation(kReference), url_(url), resource_(resource) {}
+    : FilterOperation(OperationType::kReference),
+      url_(url),
+      resource_(resource) {}
 
 void ReferenceFilterOperation::AddClient(SVGResourceClient& client) {
   if (resource_)
diff --git a/third_party/blink/renderer/core/style/filter_operation.h b/third_party/blink/renderer/core/style/filter_operation.h
index a622006b..f030792 100644
--- a/third_party/blink/renderer/core/style/filter_operation.h
+++ b/third_party/blink/renderer/core/style/filter_operation.h
@@ -50,7 +50,7 @@
 
 class CORE_EXPORT FilterOperation : public GarbageCollected<FilterOperation> {
  public:
-  enum OperationType {
+  enum class OperationType {
     kReference,  // url(#somefilter)
     kGrayscale,
     kSepia,
@@ -73,26 +73,26 @@
 
   static bool CanInterpolate(FilterOperation::OperationType type) {
     switch (type) {
-      case kGrayscale:
-      case kSepia:
-      case kSaturate:
-      case kHueRotate:
-      case kLuminanceToAlpha:
-      case kInvert:
-      case kOpacity:
-      case kBrightness:
-      case kContrast:
-      case kBlur:
-      case kDropShadow:
-      case kColorMatrix:
-      case kTurbulence:
+      case OperationType::kGrayscale:
+      case OperationType::kSepia:
+      case OperationType::kSaturate:
+      case OperationType::kHueRotate:
+      case OperationType::kLuminanceToAlpha:
+      case OperationType::kInvert:
+      case OperationType::kOpacity:
+      case OperationType::kBrightness:
+      case OperationType::kContrast:
+      case OperationType::kBlur:
+      case OperationType::kDropShadow:
+      case OperationType::kColorMatrix:
+      case OperationType::kTurbulence:
         return true;
-      case kReference:
-      case kComponentTransfer:
-      case kConvolveMatrix:
-      case kBoxReflect:
+      case OperationType::kReference:
+      case OperationType::kComponentTransfer:
+      case OperationType::kConvolveMatrix:
+      case OperationType::kBoxReflect:
         return false;
-      case kNone:
+      case OperationType::kNone:
         break;
     }
     NOTREACHED();
@@ -167,7 +167,7 @@
 template <>
 struct DowncastTraits<ReferenceFilterOperation> {
   static bool AllowFrom(const FilterOperation& op) {
-    return op.GetType() == FilterOperation::kReference;
+    return op.GetType() == FilterOperation::OperationType::kReference;
   }
 };
 
@@ -214,11 +214,11 @@
 inline bool IsBasicColorMatrixFilterOperation(
     const FilterOperation& operation) {
   FilterOperation::OperationType type = operation.GetType();
-  return type == FilterOperation::kGrayscale ||
-         type == FilterOperation::kSepia ||
-         type == FilterOperation::kSaturate ||
-         type == FilterOperation::kHueRotate ||
-         type == FilterOperation::kLuminanceToAlpha;
+  return type == FilterOperation::OperationType::kGrayscale ||
+         type == FilterOperation::OperationType::kSepia ||
+         type == FilterOperation::OperationType::kSaturate ||
+         type == FilterOperation::OperationType::kHueRotate ||
+         type == FilterOperation::OperationType::kLuminanceToAlpha;
 }
 
 template <>
@@ -231,7 +231,7 @@
 template <>
 struct DowncastTraits<ColorMatrixFilterOperation> {
   static bool AllowFrom(const FilterOperation& op) {
-    return op.GetType() == FilterOperation::kColorMatrix;
+    return op.GetType() == FilterOperation::OperationType::kColorMatrix;
   }
 };
 
@@ -245,7 +245,9 @@
 
   double Amount() const { return amount_; }
 
-  bool AffectsOpacity() const override { return type_ == kOpacity; }
+  bool AffectsOpacity() const override {
+    return type_ == OperationType::kOpacity;
+  }
 
  protected:
   bool IsEqualAssumingSameType(const FilterOperation& o) const override {
@@ -261,10 +263,10 @@
 inline bool IsBasicComponentTransferFilterOperation(
     const FilterOperation& operation) {
   FilterOperation::OperationType type = operation.GetType();
-  return type == FilterOperation::kInvert ||
-         type == FilterOperation::kOpacity ||
-         type == FilterOperation::kBrightness ||
-         type == FilterOperation::kContrast;
+  return type == FilterOperation::OperationType::kInvert ||
+         type == FilterOperation::OperationType::kOpacity ||
+         type == FilterOperation::OperationType::kBrightness ||
+         type == FilterOperation::OperationType::kContrast;
 }
 
 template <>
@@ -277,7 +279,7 @@
 class CORE_EXPORT BlurFilterOperation : public FilterOperation {
  public:
   explicit BlurFilterOperation(const Length& std_deviation)
-      : FilterOperation(kBlur), std_deviation_(std_deviation) {}
+      : FilterOperation(OperationType::kBlur), std_deviation_(std_deviation) {}
 
   const Length& StdDeviation() const { return std_deviation_; }
 
@@ -299,14 +301,14 @@
 template <>
 struct DowncastTraits<BlurFilterOperation> {
   static bool AllowFrom(const FilterOperation& op) {
-    return op.GetType() == FilterOperation::kBlur;
+    return op.GetType() == FilterOperation::OperationType::kBlur;
   }
 };
 
 class CORE_EXPORT DropShadowFilterOperation : public FilterOperation {
  public:
   explicit DropShadowFilterOperation(const ShadowData& shadow)
-      : FilterOperation(kDropShadow), shadow_(shadow) {}
+      : FilterOperation(OperationType::kDropShadow), shadow_(shadow) {}
 
   const ShadowData& Shadow() const { return shadow_; }
 
@@ -328,14 +330,14 @@
 template <>
 struct DowncastTraits<DropShadowFilterOperation> {
   static bool AllowFrom(const FilterOperation& op) {
-    return op.GetType() == FilterOperation::kDropShadow;
+    return op.GetType() == FilterOperation::OperationType::kDropShadow;
   }
 };
 
 class CORE_EXPORT BoxReflectFilterOperation : public FilterOperation {
  public:
   explicit BoxReflectFilterOperation(const BoxReflection& reflection)
-      : FilterOperation(kBoxReflect), reflection_(reflection) {}
+      : FilterOperation(OperationType::kBoxReflect), reflection_(reflection) {}
 
   const BoxReflection& Reflection() const { return reflection_; }
 
@@ -353,7 +355,7 @@
 template <>
 struct DowncastTraits<BoxReflectFilterOperation> {
   static bool AllowFrom(const FilterOperation& op) {
-    return op.GetType() == FilterOperation::kBoxReflect;
+    return op.GetType() == FilterOperation::OperationType::kBoxReflect;
   }
 };
 
@@ -366,7 +368,7 @@
                                 FEConvolveMatrix::EdgeModeType edge_mode,
                                 bool preserve_alpha,
                                 const Vector<float>& kernel_matrix)
-      : FilterOperation(kConvolveMatrix),
+      : FilterOperation(OperationType::kConvolveMatrix),
         kernel_size_(kernel_size),
         divisor_(divisor),
         bias_(bias),
@@ -408,7 +410,7 @@
 template <>
 struct DowncastTraits<ConvolveMatrixFilterOperation> {
   static bool AllowFrom(const FilterOperation& op) {
-    return op.GetType() == FilterOperation::kConvolveMatrix;
+    return op.GetType() == FilterOperation::OperationType::kConvolveMatrix;
   }
 };
 
@@ -418,7 +420,7 @@
                                    const ComponentTransferFunction& green_func,
                                    const ComponentTransferFunction& blue_func,
                                    const ComponentTransferFunction& alpha_func)
-      : FilterOperation(kComponentTransfer),
+      : FilterOperation(OperationType::kComponentTransfer),
         red_func_(red_func),
         green_func_(green_func),
         blue_func_(blue_func),
@@ -448,7 +450,7 @@
 template <>
 struct DowncastTraits<ComponentTransferFilterOperation> {
   static bool AllowFrom(const FilterOperation& op) {
-    return op.GetType() == FilterOperation::kComponentTransfer;
+    return op.GetType() == FilterOperation::OperationType::kComponentTransfer;
   }
 };
 
@@ -460,7 +462,7 @@
                             int num_octaves,
                             float seed,
                             bool stitch_tiles)
-      : FilterOperation(kTurbulence),
+      : FilterOperation(OperationType::kTurbulence),
         type_(type),
         base_frequency_x_(base_frequency_x),
         base_frequency_y_(base_frequency_y),
@@ -498,7 +500,7 @@
 template <>
 struct DowncastTraits<TurbulenceFilterOperation> {
   static bool AllowFrom(const FilterOperation& op) {
-    return op.GetType() == FilterOperation::kTurbulence;
+    return op.GetType() == FilterOperation::OperationType::kTurbulence;
   }
 };
 
diff --git a/third_party/blink/renderer/core/style/filter_operations.cc b/third_party/blink/renderer/core/style/filter_operations.cc
index de0c65cd..7f0983f 100644
--- a/third_party/blink/renderer/core/style/filter_operations.cc
+++ b/third_party/blink/renderer/core/style/filter_operations.cc
@@ -92,22 +92,23 @@
 }
 
 bool FilterOperations::HasReferenceFilter() const {
-  return std::any_of(
-      operations_.begin(), operations_.end(), [](const auto& operation) {
-        return operation->GetType() == FilterOperation::kReference;
-      });
+  return std::any_of(operations_.begin(), operations_.end(),
+                     [](const auto& operation) {
+                       return operation->GetType() ==
+                              FilterOperation::OperationType::kReference;
+                     });
 }
 
 void FilterOperations::AddClient(SVGResourceClient& client) const {
   for (FilterOperation* operation : operations_) {
-    if (operation->GetType() == FilterOperation::kReference)
+    if (operation->GetType() == FilterOperation::OperationType::kReference)
       To<ReferenceFilterOperation>(*operation).AddClient(client);
   }
 }
 
 void FilterOperations::RemoveClient(SVGResourceClient& client) const {
   for (FilterOperation* operation : operations_) {
-    if (operation->GetType() == FilterOperation::kReference)
+    if (operation->GetType() == FilterOperation::OperationType::kReference)
       To<ReferenceFilterOperation>(*operation).RemoveClient(client);
   }
 }
diff --git a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
index b079dd8..0dddd51 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
@@ -3350,7 +3350,7 @@
     }
   }
 
-  name_from = ax::mojom::blink::NameFrom::kUninitialized;
+  name_from = ax::mojom::blink::NameFrom::kNone;
 
   if (name_sources && found_text_alternative) {
     for (NameSource& name_source : *name_sources) {
@@ -3387,7 +3387,6 @@
   // spec and with what is done in other user agents.
   switch (last_used_name_from) {
     case ax::mojom::blink::NameFrom::kNone:
-    case ax::mojom::blink::NameFrom::kUninitialized:
     case ax::mojom::blink::NameFrom::kAttributeExplicitlyEmpty:
     case ax::mojom::blink::NameFrom::kContents:
       break;
@@ -3401,7 +3400,6 @@
   }
   switch (name_from) {
     case ax::mojom::blink::NameFrom::kNone:
-    case ax::mojom::blink::NameFrom::kUninitialized:
     case ax::mojom::blink::NameFrom::kAttributeExplicitlyEmpty:
     case ax::mojom::blink::NameFrom::kContents:
       break;
@@ -3462,7 +3460,7 @@
   StringBuilder accumulated_text;
   AXObject* previous = nullptr;
   ax::mojom::blink::NameFrom last_used_name_from =
-      ax::mojom::blink::NameFrom::kUninitialized;
+      ax::mojom::blink::NameFrom::kNone;
 
   // Ensure that if this node needs to invalidate its children (e.g. due to
   // included in tree status change), that we do it now, rather than while
@@ -3490,7 +3488,7 @@
     }
 
     ax::mojom::blink::NameFrom child_name_from =
-        ax::mojom::blink::NameFrom::kUninitialized;
+        ax::mojom::blink::NameFrom::kNone;
     String result;
     if (child->IsPresentational()) {
       result = child->TextFromDescendants(visited,
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.cc b/third_party/blink/renderer/modules/accessibility/ax_object.cc
index 7c098c16..466839a 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object.cc
@@ -2864,7 +2864,7 @@
   event.SetTarget(target);
 
   // Capturing phase.
-  event.SetEventPhase(Event::kCapturingPhase);
+  event.SetEventPhase(Event::PhaseType::kCapturingPhase);
   for (int i = static_cast<int>(event_path.size()) - 1; i >= 0; i--) {
     // Don't call capturing event listeners on the target. Note that
     // the target may not necessarily be in the event path which is why
@@ -2879,14 +2879,14 @@
   }
 
   // Targeting phase.
-  event.SetEventPhase(Event::kAtTarget);
+  event.SetEventPhase(Event::PhaseType::kAtTarget);
   event.SetCurrentTarget(event_path[0]);
   event_path[0]->FireEventListeners(event);
   if (event.PropagationStopped())
     return true;
 
   // Bubbling phase.
-  event.SetEventPhase(Event::kBubblingPhase);
+  event.SetEventPhase(Event::PhaseType::kBubblingPhase);
   for (wtf_size_t i = 1; i < event_path.size(); i++) {
     event.SetCurrentTarget(event_path[i]);
     event_path[i]->FireEventListeners(event);
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.h b/third_party/blink/renderer/modules/accessibility/ax_object.h
index 44523fcc..7a902a6 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_object.h
@@ -122,7 +122,7 @@
   String text;
   bool superseded = false;
   bool invalid = false;
-  ax::mojom::blink::NameFrom type = ax::mojom::blink::NameFrom::kUninitialized;
+  ax::mojom::blink::NameFrom type = ax::mojom::blink::NameFrom::kNone;
   const QualifiedName& attribute;
   AtomicString attribute_value;
   AXTextSource native_source = kAXTextFromNativeSourceUninitialized;
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_filter_operation_resolver.cc b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_filter_operation_resolver.cc
index 844d747..a1bd8f5 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_filter_operation_resolver.cc
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_filter_operation_resolver.cc
@@ -50,7 +50,7 @@
   }
 
   return MakeGarbageCollected<ColorMatrixFilterOperation>(
-      *values, FilterOperation::kColorMatrix);
+      *values, FilterOperation::OperationType::kColorMatrix);
 }
 
 struct KernelMatrix {
@@ -303,17 +303,17 @@
             filter_dict.Get<IDLDouble>("values", exception_state).value_or(0);
         operations.Operations().push_back(
             MakeGarbageCollected<BasicColorMatrixFilterOperation>(
-                amount, FilterOperation::kHueRotate));
+                amount, FilterOperation::OperationType::kHueRotate));
       } else if (type == "saturate") {
         double amount =
             filter_dict.Get<IDLDouble>("values", exception_state).value_or(0);
         operations.Operations().push_back(
             MakeGarbageCollected<BasicColorMatrixFilterOperation>(
-                amount, FilterOperation::kSaturate));
+                amount, FilterOperation::OperationType::kSaturate));
       } else if (type == "luminanceToAlpha") {
         operations.Operations().push_back(
             MakeGarbageCollected<BasicColorMatrixFilterOperation>(
-                0, FilterOperation::kLuminanceToAlpha));
+                0, FilterOperation::OperationType::kLuminanceToAlpha));
       } else if (auto* color_matrix_operation =
                      ResolveColorMatrix(filter_dict, exception_state)) {
         operations.Operations().push_back(color_matrix_operation);
diff --git a/third_party/blink/renderer/modules/exported/web_ax_object.cc b/third_party/blink/renderer/modules/exported/web_ax_object.cc
index 75bb7ae2..f1b68f30 100644
--- a/third_party/blink/renderer/modules/exported/web_ax_object.cc
+++ b/third_party/blink/renderer/modules/exported/web_ax_object.cc
@@ -786,7 +786,7 @@
 
 WebString WebAXObject::GetName(ax::mojom::NameFrom& out_name_from,
                                WebVector<WebAXObject>& out_name_objects) const {
-  out_name_from = ax::mojom::blink::NameFrom::kUninitialized;
+  out_name_from = ax::mojom::blink::NameFrom::kNone;
 
   if (IsDetached())
     return WebString();
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_event_dispatcher.cc b/third_party/blink/renderer/modules/indexeddb/idb_event_dispatcher.cc
index 5504e12..c3d07f5c 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_event_dispatcher.cc
+++ b/third_party/blink/renderer/modules/indexeddb/idb_event_dispatcher.cc
@@ -40,7 +40,7 @@
   wtf_size_t size = event_targets.size();
   DCHECK(size);
 
-  event.SetEventPhase(Event::kCapturingPhase);
+  event.SetEventPhase(Event::PhaseType::kCapturingPhase);
   for (wtf_size_t i = size - 1; i; --i) {  // Don't do the first element.
     event.SetCurrentTarget(event_targets[i].Get());
     event_targets[i]->FireEventListeners(event);
@@ -48,13 +48,13 @@
       goto doneDispatching;
   }
 
-  event.SetEventPhase(Event::kAtTarget);
+  event.SetEventPhase(Event::PhaseType::kAtTarget);
   event.SetCurrentTarget(event_targets[0].Get());
   event_targets[0]->FireEventListeners(event);
   if (event.PropagationStopped() || !event.bubbles() || event.cancelBubble())
     goto doneDispatching;
 
-  event.SetEventPhase(Event::kBubblingPhase);
+  event.SetEventPhase(Event::PhaseType::kBubblingPhase);
   for (wtf_size_t i = 1; i < size; ++i) {  // Don't do the first element.
     event.SetCurrentTarget(event_targets[i].Get());
     event_targets[i]->FireEventListeners(event);
@@ -64,7 +64,7 @@
 
 doneDispatching:
   event.SetCurrentTarget(nullptr);
-  event.SetEventPhase(Event::kNone);
+  event.SetEventPhase(Event::PhaseType::kNone);
   return EventTarget::GetDispatchEventResult(event);
 }
 
diff --git a/third_party/blink/renderer/modules/payments/payment_request_update_event_test.cc b/third_party/blink/renderer/modules/payments/payment_request_update_event_test.cc
index e0bd03b..d6e4d4ea 100644
--- a/third_party/blink/renderer/modules/payments/payment_request_update_event_test.cc
+++ b/third_party/blink/renderer/modules/payments/payment_request_update_event_test.cc
@@ -48,7 +48,7 @@
   MockPaymentRequest* request = MakeGarbageCollected<MockPaymentRequest>();
   event->SetTrusted(true);
   event->SetPaymentRequest(request);
-  event->SetEventPhase(Event::kCapturingPhase);
+  event->SetEventPhase(Event::PhaseType::kCapturingPhase);
   auto* payment_details =
       MakeGarbageCollected<ScriptPromiseResolver>(scope.GetScriptState());
   event->updateWith(scope.GetScriptState(), payment_details->Promise(),
@@ -68,7 +68,7 @@
   MockPaymentRequest* request = MakeGarbageCollected<MockPaymentRequest>();
   event->SetTrusted(true);
   event->SetPaymentRequest(request);
-  event->SetEventPhase(Event::kCapturingPhase);
+  event->SetEventPhase(Event::PhaseType::kCapturingPhase);
   auto* payment_details =
       MakeGarbageCollected<ScriptPromiseResolver>(scope.GetScriptState());
   event->updateWith(scope.GetScriptState(), payment_details->Promise(),
@@ -103,7 +103,7 @@
   MockPaymentRequest* request = MakeGarbageCollected<MockPaymentRequest>();
   event->SetTrusted(true);
   event->SetPaymentRequest(request);
-  event->SetEventPhase(Event::kCapturingPhase);
+  event->SetEventPhase(Event::PhaseType::kCapturingPhase);
   event->updateWith(
       scope.GetScriptState(),
       MakeGarbageCollected<ScriptPromiseResolver>(scope.GetScriptState())
@@ -224,7 +224,7 @@
       scope.GetExecutionContext(), event_type_names::kShippingaddresschange);
   event->SetTrusted(true);
   event->SetPaymentRequest(request);
-  event->SetEventPhase(Event::kCapturingPhase);
+  event->SetEventPhase(Event::PhaseType::kCapturingPhase);
 
   LocalFrame::NotifyUserActivation(
       &scope.GetFrame(), mojom::UserActivationNotificationType::kTest);
@@ -261,7 +261,7 @@
       scope.GetExecutionContext(), event_type_names::kShippingoptionchange);
   event->SetTrusted(true);
   event->SetPaymentRequest(request);
-  event->SetEventPhase(Event::kCapturingPhase);
+  event->SetEventPhase(Event::PhaseType::kCapturingPhase);
 
   LocalFrame::NotifyUserActivation(
       &scope.GetFrame(), mojom::UserActivationNotificationType::kTest);
diff --git a/third_party/blink/renderer/modules/serial/serial_port.cc b/third_party/blink/renderer/modules/serial/serial_port.cc
index 135b345..71bda2a 100644
--- a/third_party/blink/renderer/modules/serial/serial_port.cc
+++ b/third_party/blink/renderer/modules/serial/serial_port.cc
@@ -559,25 +559,25 @@
   event.SetTarget(this);
 
   // Events fired on a SerialPort instance bubble to the parent Serial instance.
-  event.SetEventPhase(Event::kCapturingPhase);
+  event.SetEventPhase(Event::PhaseType::kCapturingPhase);
   event.SetCurrentTarget(parent_);
   parent_->FireEventListeners(event);
   if (event.PropagationStopped())
     goto doneDispatching;
 
-  event.SetEventPhase(Event::kAtTarget);
+  event.SetEventPhase(Event::PhaseType::kAtTarget);
   event.SetCurrentTarget(this);
   FireEventListeners(event);
   if (event.PropagationStopped() || !event.bubbles())
     goto doneDispatching;
 
-  event.SetEventPhase(Event::kBubblingPhase);
+  event.SetEventPhase(Event::PhaseType::kBubblingPhase);
   event.SetCurrentTarget(parent_);
   parent_->FireEventListeners(event);
 
 doneDispatching:
   event.SetCurrentTarget(nullptr);
-  event.SetEventPhase(Event::kNone);
+  event.SetEventPhase(Event::PhaseType::kNone);
   return EventTarget::GetDispatchEventResult(event);
 }
 
diff --git a/third_party/blink/renderer/platform/blob/blob_data.cc b/third_party/blink/renderer/platform/blob/blob_data.cc
index e1e3a80..4c61bb6 100644
--- a/third_party/blink/renderer/platform/blob/blob_data.cc
+++ b/third_party/blink/renderer/platform/blob/blob_data.cc
@@ -61,7 +61,6 @@
 using mojom::blink::DataElementBytes;
 using mojom::blink::DataElementBytesPtr;
 using mojom::blink::DataElementFile;
-using mojom::blink::DataElementFilesystemURL;
 using mojom::blink::DataElementPtr;
 
 namespace {
@@ -135,17 +134,6 @@
   return data;
 }
 
-std::unique_ptr<BlobData> BlobData::CreateForFileSystemURLWithUnknownSize(
-    const KURL& file_system_url,
-    const absl::optional<base::Time>& expected_modification_time) {
-  std::unique_ptr<BlobData> data = base::WrapUnique(
-      new BlobData(FileCompositionStatus::kSingleUnknownSizeFile));
-  data->elements_.push_back(DataElement::NewFileFilesystem(
-      DataElementFilesystemURL::New(file_system_url, 0, BlobData::kToEndOfFile,
-                                    expected_modification_time)));
-  return data;
-}
-
 void BlobData::SetContentType(const String& content_type) {
   if (IsValidBlobType(content_type))
     content_type_ = content_type;
@@ -191,22 +179,6 @@
       DataElementBlob::New(data_handle->CloneBlobRemote(), offset, length)));
 }
 
-void BlobData::AppendFileSystemURL(
-    const KURL& url,
-    int64_t offset,
-    int64_t length,
-    const absl::optional<base::Time>& expected_modification_time) {
-  DCHECK_EQ(file_composition_, FileCompositionStatus::kNoUnknownSizeFiles)
-      << "Blobs with a unknown-size file cannot have other items.";
-  DCHECK_GE(length, 0);
-  // Skip zero-byte items, as they don't matter for the contents of the blob.
-  if (length == 0)
-    return;
-  elements_.push_back(
-      DataElement::NewFileFilesystem(DataElementFilesystemURL::New(
-          url, offset, length, expected_modification_time)));
-}
-
 void BlobData::AppendText(const String& text,
                           bool do_normalize_line_endings_to_native) {
   DCHECK_EQ(file_composition_, FileCompositionStatus::kNoUnknownSizeFiles)
@@ -246,9 +218,6 @@
       case DataElement::Tag::kFile:
         length += element->get_file()->length;
         break;
-      case DataElement::Tag::kFileFilesystem:
-        length += element->get_file_filesystem()->length;
-        break;
       case DataElement::Tag::kBlob:
         length += element->get_blob()->length;
         break;
diff --git a/third_party/blink/renderer/platform/blob/blob_data.h b/third_party/blink/renderer/platform/blob/blob_data.h
index ab237cf..8ef6a22 100644
--- a/third_party/blink/renderer/platform/blob/blob_data.h
+++ b/third_party/blink/renderer/platform/blob/blob_data.h
@@ -119,9 +119,6 @@
   static std::unique_ptr<BlobData> CreateForFileWithUnknownSize(
       const String& path,
       const absl::optional<base::Time>& expected_modification_time);
-  static std::unique_ptr<BlobData> CreateForFileSystemURLWithUnknownSize(
-      const KURL& file_system_url,
-      const absl::optional<base::Time>& expected_modification_time);
 
   const String& ContentType() const { return content_type_; }
   void SetContentType(const String&);
@@ -143,11 +140,6 @@
   void AppendBlob(scoped_refptr<BlobDataHandle>,
                   int64_t offset,
                   int64_t length);
-  void AppendFileSystemURL(
-      const KURL&,
-      int64_t offset,
-      int64_t length,
-      const absl::optional<base::Time>& expected_modification_time);
   void AppendText(const String&, bool normalize_line_endings_to_native);
 
   // The value of the size property for a Blob who has this data.
diff --git a/third_party/blink/renderer/platform/blob/blob_data_test.cc b/third_party/blink/renderer/platform/blob/blob_data_test.cc
index e61ba30f..502e43a2b 100644
--- a/third_party/blink/renderer/platform/blob/blob_data_test.cc
+++ b/third_party/blink/renderer/platform/blob/blob_data_test.cc
@@ -31,7 +31,6 @@
 using mojom::blink::DataElementBlob;
 using mojom::blink::DataElementBytes;
 using mojom::blink::DataElementFile;
-using mojom::blink::DataElementFilesystemURL;
 using mojom::blink::DataElementPtr;
 
 namespace {
@@ -62,14 +61,6 @@
         DataElementFile::New(WebStringToFilePath(path), offset, length, time))};
   }
 
-  static ExpectedElement FileFilesystem(const KURL& url,
-                                        uint64_t offset,
-                                        uint64_t length,
-                                        base::Time time) {
-    return ExpectedElement{DataElement::NewFileFilesystem(
-        DataElementFilesystemURL::New(url, offset, length, time))};
-  }
-
   static ExpectedElement Blob(const String& uuid,
                               uint64_t offset,
                               uint64_t length) {
@@ -187,16 +178,6 @@
         EXPECT_EQ(expected->get_file()->offset, actual->get_file()->offset);
         EXPECT_EQ(expected->get_file()->expected_modification_time,
                   actual->get_file()->expected_modification_time);
-      } else if (expected->is_file_filesystem()) {
-        ASSERT_TRUE(actual->is_file_filesystem());
-        EXPECT_EQ(expected->get_file_filesystem()->url,
-                  actual->get_file_filesystem()->url);
-        EXPECT_EQ(expected->get_file_filesystem()->length,
-                  actual->get_file_filesystem()->length);
-        EXPECT_EQ(expected->get_file_filesystem()->offset,
-                  actual->get_file_filesystem()->offset);
-        EXPECT_EQ(expected->get_file_filesystem()->expected_modification_time,
-                  actual->get_file_filesystem()->expected_modification_time);
       } else if (expected->is_blob()) {
         ASSERT_TRUE(actual->is_blob());
         EXPECT_EQ(expected->get_blob()->length, actual->get_blob()->length);
@@ -287,7 +268,6 @@
   data->AppendBytes(small_test_data_.data(), 0);
   data->AppendBlob(empty_blob_, 0, 0);
   data->AppendFile("path", 0, 0, base::Time::UnixEpoch());
-  data->AppendFileSystemURL(NullURL(), 0, 0, base::Time::UnixEpoch());
 
   TestCreateBlob(std::move(data), {});
 }
@@ -360,22 +340,6 @@
   TestCreateBlob(std::move(data), std::move(expected_elements));
 }
 
-TEST_F(BlobDataHandleTest, CreateFromFileAndFileSystemURL) {
-  base::Time timestamp1 = base::Time::Now();
-  base::Time timestamp2 = timestamp1 + base::Seconds(1);
-  KURL url(NullURL(), "http://example.com/");
-  auto data = std::make_unique<BlobData>();
-  data->AppendFile("path", 4, 32, timestamp1);
-  data->AppendFileSystemURL(url, 15, 876, timestamp2);
-
-  Vector<ExpectedElement> expected_elements;
-  expected_elements.push_back(ExpectedElement::File("path", 4, 32, timestamp1));
-  expected_elements.push_back(
-      ExpectedElement::FileFilesystem(url, 15, 876, timestamp2));
-
-  TestCreateBlob(std::move(data), std::move(expected_elements));
-}
-
 TEST_F(BlobDataHandleTest, CreateFromFileWithUnknownSize) {
   Vector<ExpectedElement> expected_elements;
   expected_elements.push_back(
@@ -385,18 +349,6 @@
                  std::move(expected_elements));
 }
 
-TEST_F(BlobDataHandleTest, CreateFromFilesystemFileWithUnknownSize) {
-  base::Time timestamp = base::Time::Now();
-  KURL url(NullURL(), "http://example.com/");
-  Vector<ExpectedElement> expected_elements;
-  expected_elements.push_back(
-      ExpectedElement::FileFilesystem(url, 0, uint64_t(-1), timestamp));
-
-  TestCreateBlob(
-      BlobData::CreateForFileSystemURLWithUnknownSize(url, timestamp),
-      std::move(expected_elements));
-}
-
 TEST_F(BlobDataHandleTest, CreateFromBlob) {
   auto data = std::make_unique<BlobData>();
   data->AppendBlob(test_blob_, 13, 765);
diff --git a/third_party/blink/renderer/platform/graphics/filters/paint_filter_effect.cc b/third_party/blink/renderer/platform/graphics/filters/paint_filter_effect.cc
index e671dbb..9696bb34 100644
--- a/third_party/blink/renderer/platform/graphics/filters/paint_filter_effect.cc
+++ b/third_party/blink/renderer/platform/graphics/filters/paint_filter_effect.cc
@@ -31,7 +31,7 @@
   } else {
     // ShaderPaintFilter requires shader to be non-null
     return sk_make_sp<ShaderPaintFilter>(
-        cc::PaintShader::MakeColor(flags_.getColor()), 255,
+        cc::PaintShader::MakeColor(flags_.getColor4f()), 255,
         flags_.getFilterQuality(), dither);
   }
 }
diff --git a/third_party/blink/renderer/platform/graphics/gradient.cc b/third_party/blink/renderer/platform/graphics/gradient.cc
index b1acf04..84ba0e58 100644
--- a/third_party/blink/renderer/platform/graphics/gradient.cc
+++ b/third_party/blink/renderer/platform/graphics/gradient.cc
@@ -228,9 +228,15 @@
     }
 
     SkPoint pts[2] = {FloatPointToSkPoint(p0_), FloatPointToSkPoint(p1_)};
+    // TODO(crbug/1308932): Remove this helper vector colors4f and make all
+    // SkColor4f.
+    std::vector<SkColor4f> colors4f;
+    colors4f.reserve(colors.size());
+    for (auto& color : colors)
+      colors4f.push_back(SkColor4f::FromColor(color));
     return PaintShader::MakeLinearGradient(
-        pts, colors.data(), pos.data(), static_cast<int>(colors.size()),
-        tile_mode, flags, &local_matrix, fallback_color);
+        pts, colors4f.data(), pos.data(), static_cast<int>(colors4f.size()),
+        tile_mode, flags, &local_matrix, SkColor4f::FromColor(fallback_color));
   }
 
  private:
@@ -286,10 +292,16 @@
       return PaintShader::MakeEmpty();
     }
 
+    // TODO(crbug/1308932): Remove this helper vector colors4f and make all
+    // SkColor4f.
+    std::vector<SkColor4f> colors4f;
+    colors4f.reserve(colors.size());
+    for (auto& color : colors)
+      colors4f.push_back(SkColor4f::FromColor(color));
     return PaintShader::MakeTwoPointConicalGradient(
         FloatPointToSkPoint(p0_), radius0, FloatPointToSkPoint(p1_), radius1,
-        colors.data(), pos.data(), static_cast<int>(colors.size()), tile_mode,
-        flags, matrix, fallback_color);
+        colors4f.data(), pos.data(), static_cast<int>(colors4f.size()),
+        tile_mode, flags, matrix, SkColor4f::FromColor(fallback_color));
   }
 
  private:
@@ -341,10 +353,16 @@
       matrix = &*adjusted_local_matrix;
     }
 
+    // TODO(crbug/1308932): Remove this helper vector colors4f and make all
+    // SkColor4f.
+    std::vector<SkColor4f> colors4f;
+    colors4f.reserve(colors.size());
+    for (auto& color : colors)
+      colors4f.push_back(SkColor4f::FromColor(color));
     return PaintShader::MakeSweepGradient(
-        position_.x(), position_.y(), colors.data(), pos.data(),
-        static_cast<int>(colors.size()), tile_mode, start_angle_, end_angle_,
-        flags, matrix, fallback_color);
+        position_.x(), position_.y(), colors4f.data(), pos.data(),
+        static_cast<int>(colors4f.size()), tile_mode, start_angle_, end_angle_,
+        flags, matrix, SkColor4f::FromColor(fallback_color));
   }
 
  private:
diff --git a/third_party/blink/renderer/platform/graphics/image_pattern.cc b/third_party/blink/renderer/platform/graphics/image_pattern.cc
index 5b06140..ed5c3b48 100644
--- a/third_party/blink/renderer/platform/graphics/image_pattern.cc
+++ b/third_party/blink/renderer/platform/graphics/image_pattern.cc
@@ -21,7 +21,7 @@
 sk_sp<PaintShader> ImagePattern::CreateShader(
     const SkMatrix& local_matrix) const {
   if (!tile_image_) {
-    return PaintShader::MakeColor(SK_ColorTRANSPARENT);
+    return PaintShader::MakeColor(SkColors::kTransparent);
   }
 
   return PaintShader::MakeImage(
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 08005de..a46138d 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -153,10 +153,6 @@
       name: "AccessibilityUseAXPositionForDocumentMarkers",
     },
     {
-      name: "AddEventListenerAbortSignal",
-      status: "stable",
-    },
-    {
       name: "AddressSpace",
       status: "experimental",
       implied_by: ["CorsRFC1918"],
@@ -486,10 +482,6 @@
       status: "experimental",
     },
     {
-      name: "CSSAccentColor",
-      status: "stable",
-    },
-    {
       // Allows positioning a positioned element relative to another one.
       // https://tabatkins.github.io/specs/css-anchor-position/
       name: "CSSAnchorPositioning",
diff --git a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
index 03e861d..0d77a7a 100644
--- a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
+++ b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
@@ -11,6 +11,8 @@
 # Tests that fail in legacy but pass in NG
 
 # ====== New tests from wpt-importer added here ======
+crbug.com/626703 external/wpt/css/css-sizing/aspect-ratio/block-aspect-ratio-046.html [ Failure ]
+crbug.com/626703 external/wpt/css/css-sizing/aspect-ratio/block-aspect-ratio-048.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-display/display-flow-root-002.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-sizing/aspect-ratio/block-aspect-ratio-040.html [ Failure ]
 crbug.com/626703 external/wpt/service-workers/cache-storage/crashtests/cache-response-clone.https.html [ Timeout ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 34449eb7..f19f90a7 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -3478,6 +3478,8 @@
 crbug.com/626703 [ Win ] virtual/partitioned-cookies/http/tests/inspector-protocol/network/disabled-cache-navigation.js [ Failure ]
 
 # ====== New tests from wpt-importer added here ======
+crbug.com/626703 external/wpt/css/css-images/gradient/gradient-eval-001.tentative.html [ Failure ]
+crbug.com/626703 [ Mac12 ] virtual/off-main-thread-css-paint/external/wpt/css/css-paint-api/paint2d-composite.https.html [ Failure ]
 crbug.com/626703 [ Mac11 ] external/wpt/html/browsers/origin/cross-origin-objects/location-properties-smoke-test.html [ Timeout ]
 crbug.com/626703 [ Mac11 ] virtual/off-main-thread-css-paint/external/wpt/css/css-paint-api/geometry-background-image-tiled-001.https.html [ Failure ]
 crbug.com/626703 external/wpt/html/canvas/element/manual/drawing-text-to-the-canvas/canvas.2d.disconnected.html [ Failure ]
@@ -7033,7 +7035,7 @@
 crbug.com/1137003 fast/dom/timer-throttling-background-page-near-alignment-interval.html [ Failure Pass ]
 
 # Sheriff 2022-04-29
-crbug.com/1321243 accessibility/aom-reparenting-crash.html [ Skip ]
+crbug.com/1321243 accessibility/aom-reparenting-crash.html [ Pass Timeout ]
 
 # Sheriff 2022-05-02
 crbug.com/1321293 fast/forms/calendar-picker/week-picker-choose-default-value-after-set-value.html [ Failure Pass ]
@@ -7178,6 +7180,10 @@
 crbug.com/1180274 virtual/first-party-sets/http/tests/inspector-protocol/network/navigate-iframe-out2in.js [ Skip ]
 crbug.com/1180274 virtual/partitioned-cookies/http/tests/inspector-protocol/network/navigate-iframe-out2in.js [ Skip ]
 
+# Disabled test to land a DevTools change
+crbug.com/1344541 http/tests/devtools/security/security-details-updated-with-security-state.js [ Failure Pass ]
+crbug.com/1344541 http/tests/devtools/security/origin-view-ct-compliance.js [ Failure Pass ]
+
 # Sheriff 2022-07-11
 crbug.com/1343262 [ Win11 ] compositing/overlap-blending/reflection-opacity-huge.html [ Failure ]
 crbug.com/1343262 [ Win11 ] compositing/reflections/nested-reflection-opacity.html [ Failure ]
diff --git a/third_party/blink/web_tests/external/Version b/third_party/blink/web_tests/external/Version
index e7ce08b..983f617 100644
--- a/third_party/blink/web_tests/external/Version
+++ b/third_party/blink/web_tests/external/Version
@@ -1 +1 @@
-Version: 2781ee1ecb863fe628daf493b5a1f411e218397c
+Version: 961c5a18f3550b830df68c87221f5200bbebe821
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 f610187..da81015 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
@@ -127945,6 +127945,66 @@
        {}
       ]
      ],
+     "gradient": {
+      "gradient-eval-001.tentative.html": [
+       "384f46da293777f7aa52c37eb673188da742df65",
+       [
+        null,
+        [
+         [
+          "/css/css-images/gradient/gradient-eval-001-ref.html",
+          "=="
+         ]
+        ],
+        {
+         "fuzzy": [
+          [
+           null,
+           [
+            [
+             1,
+             2
+            ],
+            [
+             0,
+             10000
+            ]
+           ]
+          ]
+         ]
+        }
+       ]
+      ],
+      "gradient-eval-002.tentative.html": [
+       "e393c4c69d0858e856833cf30ba4f68722fbeed3",
+       [
+        null,
+        [
+         [
+          "/css/css-images/gradient/gradient-eval-002-ref.html",
+          "=="
+         ]
+        ],
+        {
+         "fuzzy": [
+          [
+           null,
+           [
+            [
+             1,
+             2
+            ],
+            [
+             0,
+             10000
+            ]
+           ]
+          ]
+         ]
+        }
+       ]
+      ]
+     },
      "gradient-border-box.html": [
       "2938c5ea99e1e41c64c1d58e69806270318bccf9",
       [
@@ -159174,6 +159234,84 @@
         {}
        ]
       ],
+      "block-aspect-ratio-044.html": [
+       "e78a1f1f12d51d62cd62e971725f9da626ed3d06",
+       [
+        null,
+        [
+         [
+          "/css/reference/ref-filled-green-100px-square.xht",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "block-aspect-ratio-045.html": [
+       "2c86bd4417bfb7b0ef3df305d07928fc3b76aef9",
+       [
+        null,
+        [
+         [
+          "/css/reference/ref-filled-green-100px-square.xht",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "block-aspect-ratio-046.html": [
+       "3deb786fcb963881b18cec7e237139578ad1e280",
+       [
+        null,
+        [
+         [
+          "/css/reference/ref-filled-green-100px-square.xht",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "block-aspect-ratio-047.html": [
+       "5f7cad5803cb98b1392ac69b16bd0727fe416f77",
+       [
+        null,
+        [
+         [
+          "/css/reference/ref-filled-green-100px-square.xht",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "block-aspect-ratio-048.html": [
+       "12ae22c26badc8b2070af11aec390c7a4d13fd32",
+       [
+        null,
+        [
+         [
+          "/css/reference/ref-filled-green-100px-square.xht",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "block-aspect-ratio-049.html": [
+       "7e25b6e626fa6a3f6b865b411515bedc033a3b01",
+       [
+        null,
+        [
+         [
+          "/css/reference/ref-filled-green-100px-square.xht",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
       "block-aspect-ratio-with-margin-collapsing-001.html": [
        "78a0418fe285da58030cdd863460add0533e6af5",
        [
@@ -278159,6 +278297,16 @@
       "f3193036766e428bf946e81b7df72bfc7f4901db",
       []
      ],
+     "gradient": {
+      "gradient-eval-001-ref.html": [
+       "37c9cf8d8be5d5f59227722e560adb0713e439cb",
+       []
+      ],
+      "gradient-eval-002-ref.html": [
+       "199cf5a369d35fcb53cf5bc990f20da685ec5edb",
+       []
+      ]
+     },
      "gradient-border-box-ref.html": [
       "5b219cea6c20ba46ebba7d7bb890af3923d90a54",
       []
diff --git a/third_party/blink/web_tests/external/wpt/css/css-conditional/js/supports-conditionText.html b/third_party/blink/web_tests/external/wpt/css/css-conditional/js/supports-conditionText.html
new file mode 100644
index 0000000..ea74077
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-conditional/js/supports-conditionText.html
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML>
+<meta charset=UTF-8>
+<title>CSSGroupingRule Conditional Rules Test</title>
+<link rel="author" title="L. David Baron" href="https://dbaron.org/">
+<link rel="help" href="https://www.w3.org/TR/css-conditional-3/#the-cssconditionrule-interface">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style id="style"></style>
+
+<script>
+
+function check_condition_text(text) {
+  test(function() {
+    let style_element = document.getElementById("style");
+    style_element.textContent = `@supports ${text} {}`;
+    let rules = style_element.sheet.cssRules;
+    assert_equals(rules.length, 1);
+    assert_equals(rules[0].conditionText, text);
+  }, `conditionText getter for @supports ${text}`);
+}
+
+check_condition_text("(color: red)");
+check_condition_text("(color : red) or ( color:blue )");
+check_condition_text("not (color: red)");
+check_condition_text("()");
+check_condition_text("func()");
+check_condition_text("([])");
+check_condition_text("({})");
+check_condition_text("(())");
+check_condition_text("(func())");
+check_condition_text("(x)");
+check_condition_text("func(x)");
+check_condition_text("([x])");
+check_condition_text("({x})");
+check_condition_text("((x))");
+check_condition_text("(func(x))");
+
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-images/gradient/gradient-eval-001-ref.html b/third_party/blink/web_tests/external/wpt/css/css-images/gradient/gradient-eval-001-ref.html
new file mode 100644
index 0000000..37c9cf8
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/gradient/gradient-eval-001-ref.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+ <head>
+  <style>
+   .test {
+       width: 100px;
+       height: 100px;
+       background: #080;
+   }
+ </style>
+ </head>
+ <body>
+  <div class="test"></div>
+ </body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-images/gradient/gradient-eval-001.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-images/gradient/gradient-eval-001.tentative.html
new file mode 100644
index 0000000..384f46d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/gradient/gradient-eval-001.tentative.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+ <head>
+  <title>Gradient interpolation</title>
+  <meta name="fuzzy" content="maxDifference=1-2;totalPixels=0-10000">
+  <link rel="author" title="Mike Bremford" href="mailto:mike@bfo.com">
+  <link rel="help" href="https://www.w3.org/TR/css-color-4/#interpolation">
+  <meta name="assert" content="A narrow segment of a wide gradient is essentially a single color, allowing for anti-aliasing">
+  <link rel="match" href="gradient-eval-001-ref.html">
+  <style>
+   :root {
+       --start: #000;
+       --end: #0F0;
+       --t: 0.5;
+       --big: 131070000px;
+   }
+   .test {
+       width: 100px;
+       height: 100px;
+       background: linear-gradient(var(--start) calc(var(--big) * (0 - var(--t))), var(--end) calc(var(--big) * (1 - var(--t))));
+   }
+ </style>
+ </head>
+ <body>
+  <div class="test"></div>
+ </body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-images/gradient/gradient-eval-002-ref.html b/third_party/blink/web_tests/external/wpt/css/css-images/gradient/gradient-eval-002-ref.html
new file mode 100644
index 0000000..199cf5a3
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/gradient/gradient-eval-002-ref.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+ <head>
+  <title>Gradient interpolation</title>
+  <link rel="author" title="Mike Bremford" href="mailto:mike@bfo.com">
+  <style>
+   .test {
+       width: 100px;
+       height: 100px;
+       background: lab(50% 0 0);
+   }
+ </style>
+ </head>
+ <body>
+  <div class="test"></div>
+ </body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-images/gradient/gradient-eval-002.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-images/gradient/gradient-eval-002.tentative.html
new file mode 100644
index 0000000..e393c4c6
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/gradient/gradient-eval-002.tentative.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+ <head>
+  <title>Gradient interpolation</title>
+  <meta name="fuzzy" content="maxDifference=1-2;totalPixels=0-10000">
+  <link rel="author" title="Mike Bremford" href="mailto:mike@bfo.com">
+  <link rel="help" href="https://www.w3.org/TR/css-color-4/#interpolation">
+  <meta name="assert" content="A narrow segment of a wide gradient is essentially a single color, allowing for anti-aliasing">
+  <link rel="match" href="gradient-eval-002-ref.html">
+  <style>
+   :root {
+       --start: lab(50% 0 80);
+       --end: lab(50% 0 -80);
+       --t: 0.5;
+       --big: 131070000px;
+   }
+   .test {
+       width: 100px;
+       height: 100px;
+       background: linear-gradient(in lab, var(--start) calc(var(--big) * (0 - var(--t))), var(--end) calc(var(--big) * (1 - var(--t))));
+   }
+ </style>
+ </head>
+ <body>
+  <div class="test"></div>
+ </body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-paint-api/parsing/paint-function-valid.https.html b/third_party/blink/web_tests/external/wpt/css/css-paint-api/parsing/paint-function-valid.https.html
index b67ab35..187bd9e 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-paint-api/parsing/paint-function-valid.https.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-paint-api/parsing/paint-function-valid.https.html
@@ -37,6 +37,9 @@
   test_valid_value("background-image", "paint(mypaint ", "paint(mypaint)");
   test_valid_value("background-image", "paint( mypaint", "paint(mypaint)");
   test_valid_value("background-image", "paint(mypaint, blue)");
+  test_valid_value("background-image", "paint(mypaint, {})");
+  test_valid_value("background-image", "paint(mypaint, [])");
+  test_valid_value("background-image", "paint(mypaint, ())");
 
   done();
 });
diff --git a/third_party/blink/web_tests/external/wpt/css/css-sizing/aspect-ratio/block-aspect-ratio-044.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/aspect-ratio/block-aspect-ratio-044.html
new file mode 100644
index 0000000..e78a1f1
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/aspect-ratio/block-aspect-ratio-044.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<title>CSS aspect-ratio: The definite max-height should win the transferred minimum height</title>
+<link rel="author" title="Cathie Chen" href="mailto:cchen@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-sizing-4/#aspect-ratio-size-transfers">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht" />
+<meta name="assert" content="CSS aspect-ratio: The definite max-height should win the transferred minimum height.">
+
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+
+<div style="background: green; width: 50px; aspect-ratio: 1 / 2; min-width: 100px; max-height: 100px;"></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-sizing/aspect-ratio/block-aspect-ratio-045.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/aspect-ratio/block-aspect-ratio-045.html
new file mode 100644
index 0000000..2c86bd4
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/aspect-ratio/block-aspect-ratio-045.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<title>CSS aspect-ratio: The definite max-width should win the transferred minimum width</title>
+<link rel="author" title="Cathie Chen" href="mailto:cchen@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-sizing-4/#aspect-ratio-size-transfers">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht" />
+<meta name="assert" content="CSS aspect-ratio: The definite max-width should win the transferred minimum width.">
+
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+
+<div style="background: green; height: 50px; aspect-ratio: 2 / 1; min-height: 100px; max-width: 100px;"></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-sizing/aspect-ratio/block-aspect-ratio-046.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/aspect-ratio/block-aspect-ratio-046.html
new file mode 100644
index 0000000..3deb786f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/aspect-ratio/block-aspect-ratio-046.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<title>CSS aspect-ratio: The definite min-height should win the transferred minimum height</title>
+<link rel="author" title="Cathie Chen" href="mailto:cchen@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-sizing-4/#aspect-ratio-size-transfers">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht" />
+<meta name="assert" content="CSS aspect-ratio: The definite min-height should win the transferred minimum height.">
+
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+
+<div style="background: green; width: 50px; aspect-ratio: 2 / 1; min-width: 100px; min-height: 100px;"></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-sizing/aspect-ratio/block-aspect-ratio-047.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/aspect-ratio/block-aspect-ratio-047.html
new file mode 100644
index 0000000..5f7cad5
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/aspect-ratio/block-aspect-ratio-047.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<title>CSS aspect-ratio: The definite min-width should win the transferred minimum width</title>
+<link rel="author" title="Cathie Chen" href="mailto:cchen@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-sizing-4/#aspect-ratio-size-transfers">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht" />
+<meta name="assert" content="CSS aspect-ratio: The definite min-width should win the transferred minimum width.">
+
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+
+<div style="background: green; height: 50px; aspect-ratio: 1 / 2; min-height: 100px; min-width: 100px;"></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-sizing/aspect-ratio/block-aspect-ratio-048.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/aspect-ratio/block-aspect-ratio-048.html
new file mode 100644
index 0000000..12ae22c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/aspect-ratio/block-aspect-ratio-048.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<title>CSS aspect-ratio: The definite min-height should win the transferred maximum height</title>
+<link rel="author" title="Cathie Chen" href="mailto:cchen@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-sizing-4/#aspect-ratio-size-transfers">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht" />
+<meta name="assert" content="CSS aspect-ratio: The definite min-height should win the transferred maximum height.">
+
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+
+<div style="background: green; width: 100px; aspect-ratio: 2 / 1; max-width: 150px; min-height: 100px;"></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-sizing/aspect-ratio/block-aspect-ratio-049.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/aspect-ratio/block-aspect-ratio-049.html
new file mode 100644
index 0000000..7e25b6e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/aspect-ratio/block-aspect-ratio-049.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<title>CSS aspect-ratio: The definite min-width should win the transferred maximum width</title>
+<link rel="author" title="Cathie Chen" href="mailto:cchen@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-sizing-4/#aspect-ratio-size-transfers">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht" />
+<meta name="assert" content="CSS aspect-ratio: The definite min-width should win the transferred maximum width.">
+
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+
+<div style="background: green; height: 100px; aspect-ratio: 1 / 2; max-height: 150px; min-width: 100px;"></div>
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/imagebitmap/imagebitmap-replication-exif-orientation.html b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/imagebitmap/imagebitmap-replication-exif-orientation.html
new file mode 100644
index 0000000..b04a761
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/imagebitmap/imagebitmap-replication-exif-orientation.html
@@ -0,0 +1,146 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Verify that image orientation is propagated when ImageBitmap objects are replicated.</title>
+<link rel="author" title="Justin Novosad" href="mailto:junov@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+
+<body>
+<script>
+// This test is most relevant for browser implementations that apply EXIF image
+// orientation lazily. That is to say that the transform is applied at rasterization
+// time rather than at image decode time. This implies that image orientation metadata
+// is stored internally in the decoded image's data structure.  This test ensures
+// that the orientation metadata is correctly carried over when ImageBitmap objects
+// are replicated (serialized/deserialized, copied or transferred).
+
+function checkImageBitmapRotated(bitmap) {
+    assert_equals(bitmap.width, 320, 'Bitmap width');
+    assert_equals(bitmap.height, 160, 'Bitmap height');
+
+    const canvas = document.createElement('canvas');
+    const ctx = canvas.getContext('2d');
+
+    const expected_colors = [
+        // row, col, r, g, b, a
+        [0, 0, 255, 0, 0, 255],
+        [0, 1, 0, 255, 0, 255],
+        [0, 2, 0, 0, 255, 255],
+        [0, 3, 0, 0, 0, 255],
+        [1, 0, 255, 128, 128, 255],
+        [1, 1, 128, 255, 128, 255],
+        [1, 2, 128, 128, 255, 255],
+        [1, 3, 128, 128, 128, 255],
+    ];
+
+    canvas.width = bitmap.width;
+    canvas.height = bitmap.height;
+    ctx.drawImage(bitmap, 0, 0);
+
+    let data = ctx.getImageData(0, 0, canvas.width, canvas.height).data;
+    for (let [row, col, r, g, b, a] of expected_colors) {
+        let x = col * 80 + 40;
+        let y = row * 80 + 40;
+        let i = (x + y * canvas.width) * 4;
+        let expected = [r, g, b, a];
+        let actual = [data[i], data[i + 1], data[i + 2], data[i + 3]];
+        assert_array_approx_equals(actual, expected, 1, `Pixel value at (${x},${y}) ${expected} =~ ${actual}.`);
+    }
+}
+
+promise_test(async t => {
+    const response = await fetch("resources/squares.jpg");
+    const blob = await response.blob();
+    const image = await createImageBitmap(blob)
+    const image_copy = structuredClone(image);
+    checkImageBitmapRotated(image_copy);
+}, "ImageBitmap from file with EXIF rotation, duplicated via structuredClone");
+
+promise_test(async t => {
+    const image = new Image();
+    image.src = "resources/squares.jpg"
+    await new Promise(resolve => image.onload = resolve);
+    const image_copy = await createImageBitmap(image);
+    checkImageBitmapRotated(image_copy);
+}, "ImageBitmap from file with EXIF rotation, loaded via <img>");
+
+promise_test(async t => {
+    const image = new Image();
+    image.src = "resources/squares.jpg"
+    // The following has no effect because the image's style is not
+    // processed unless the element is connected to the DOM.
+    image.style.imageOrientation = "none";
+    await new Promise(resolve => image.onload = resolve);
+    const image_copy = await createImageBitmap(image);
+    checkImageBitmapRotated(image_copy);
+}, "ImageBitmap from file with EXIF rotation, loaded via <img> not in DOM, imageOrientation = none");
+
+promise_test(async t => {
+    const image = new Image();
+    document.body.appendChild(image);
+    image.src = "resources/squares.jpg"
+    // The style is being processed in this case, but the imageOrientation
+    // CSS property must still have no effect because createImageBitmap
+    // accesses the element's underlying media directly, without being
+    // affected by the image's style (unlike drawImage).
+    image.style.imageOrientation = "none";
+    await new Promise(resolve => image.onload = resolve);
+    const image_copy = await createImageBitmap(image);
+    checkImageBitmapRotated(image_copy);
+}, "ImageBitmap from file with EXIF rotation, loaded via <img> in DOM, imageOrientation = none");
+
+
+promise_test(async t => {
+    const response = await fetch("resources/squares.jpg");
+    const blob = await response.blob();
+    const image = await createImageBitmap(blob);
+    const image_copy = await createImageBitmap(image);
+    checkImageBitmapRotated(image_copy);
+}, "ImageBitmap from file with EXIF rotation, duplicated via createImageBitmap");
+
+promise_test(async t => {
+    const worker = new Worker("serialize-worker.js");
+    const response = await fetch("resources/squares.jpg");
+    const blob = await response.blob()
+    const image = await createImageBitmap(blob);
+    worker.postMessage({bitmap: image});
+    const bitmap = (await new Promise(resolve => {worker.addEventListener("message", resolve)})).data.bitmap;
+    checkImageBitmapRotated(bitmap);
+}, "ImageBitmap from file with EXIF rotation, duplicated via worker serialization round-trip");
+
+promise_test(async t => {
+    const worker = new Worker("transfer-worker.js");
+    let response = await fetch("resources/squares.jpg");
+    let blob = await response.blob();
+    let image = await createImageBitmap(blob);
+    worker.postMessage({bitmap: image}, [image]);
+    const bitmap = (await new Promise(resolve => {worker.addEventListener("message", resolve)})).data.bitmap;
+    checkImageBitmapRotated(bitmap);
+}, "ImageBitmap from file with EXIF rotation, duplicated via worker transfer round-trip");
+
+promise_test(async t => {
+    // This test variant ensures additional code coverage.
+    // By creating a canvas pattern, a reference to the ImageBitmap's
+    // underlying pixel data is held in the source realm.  This forces
+    // implementations that do lazy copying to duplicate the pixel
+    // data at transfer time. This test verifies that the lazy
+    // duplication code path (if applicable) carries over the image
+    // orientation metadata.
+    const worker = new Worker("transfer-worker.js");
+    let response = await fetch("resources/squares.jpg");
+    let blob = await response.blob();
+    let image = await createImageBitmap(blob);
+    const canvas = document.createElement('canvas');
+    const ctx = canvas.getContext('2d');
+    const pattern = ctx.createPattern(image, 'repeat');
+    worker.postMessage({bitmap: image}, [image]);
+    const bitmap = (await new Promise(resolve => {worker.addEventListener("message", resolve)})).data.bitmap;
+    checkImageBitmapRotated(bitmap);
+}, "ImageBitmap from file with EXIF rotation, duplicated via worker transfer round-trip, while referenced by a CanvasPattern");
+
+
+</script>
+</body>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/imagebitmap/serialize-worker.js b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/imagebitmap/serialize-worker.js
new file mode 100644
index 0000000..a76537c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/imagebitmap/serialize-worker.js
@@ -0,0 +1,3 @@
+addEventListener('message', evt => {
+    postMessage(evt.data);
+});
diff --git a/third_party/blink/web_tests/external/wpt/shadow-dom/focus/focus-click-on-shadow-host.html b/third_party/blink/web_tests/external/wpt/shadow-dom/focus/focus-click-on-shadow-host.html
new file mode 100644
index 0000000..7a318a0
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/shadow-dom/focus/focus-click-on-shadow-host.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: click on shadow host with delegatesFocus</title>
+<link rel="author" href="mailto:dizhangg@chromium.org">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1327136">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<body>
+  <div id="host"></div>
+</body>
+
+<script>
+const host = document.getElementById("host");
+
+const shadowRoot = host.attachShadow({ mode: "open", delegatesFocus: true });
+// Add an unfocusable spacer, because test_driver.click will click on the
+// center point of #host, and we don't want the click to land on focusableDiv
+const spacer = document.createElement("div");
+spacer.style = "height: 1000px;";
+shadowRoot.appendChild(spacer);
+
+const focusableDiv = document.createElement("div");
+focusableDiv.tabIndex = 0;
+shadowRoot.appendChild(focusableDiv);
+
+promise_test(async () => {
+  assert_equals(document.activeElement, document.body);
+  // Mouse click
+  await test_driver.click(host);
+  assert_equals(document.activeElement, host);
+  assert_equals(shadowRoot.activeElement, focusableDiv);
+  assert_true(host.matches(':focus'));
+  assert_true(focusableDiv.matches(':focus'));
+  assert_false(focusableDiv.matches(':focus-visible'));
+}, ":focus should be applied to the host and the child node when the focus is moved by mouse click");
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/shadow-dom/focus/focus-tab-on-shadow-host.html b/third_party/blink/web_tests/external/wpt/shadow-dom/focus/focus-tab-on-shadow-host.html
index 354ed0e..0dffc01 100644
--- a/third_party/blink/web_tests/external/wpt/shadow-dom/focus/focus-tab-on-shadow-host.html
+++ b/third_party/blink/web_tests/external/wpt/shadow-dom/focus/focus-tab-on-shadow-host.html
@@ -25,5 +25,7 @@
   assert_equals(document.activeElement, host);
   assert_equals(shadowRoot.activeElement, input);
   assert_true(host.matches(':focus'));
-}, ":focus should be applied to the host when the focus is moved by <tab>");
+  assert_true(input.matches(':focus'));
+  assert_true(input.matches(':focus-visible'));
+}, ":focus should be applied to the host and :focus-visible should be applied to the child node when the focus is moved by <tab>");
 </script>
diff --git a/third_party/blink/web_tests/platform/generic/external/wpt/css/css-conditional/js/supports-conditionText-expected.txt b/third_party/blink/web_tests/platform/generic/external/wpt/css/css-conditional/js/supports-conditionText-expected.txt
new file mode 100644
index 0000000..8c41303f
--- /dev/null
+++ b/third_party/blink/web_tests/platform/generic/external/wpt/css/css-conditional/js/supports-conditionText-expected.txt
@@ -0,0 +1,18 @@
+This is a testharness.js-based test.
+PASS conditionText getter for @supports (color: red)
+PASS conditionText getter for @supports (color : red) or ( color:blue )
+PASS conditionText getter for @supports not (color: red)
+FAIL conditionText getter for @supports () assert_equals: expected 1 but got 0
+FAIL conditionText getter for @supports func() assert_equals: expected 1 but got 0
+PASS conditionText getter for @supports ([])
+PASS conditionText getter for @supports ({})
+FAIL conditionText getter for @supports (()) assert_equals: expected 1 but got 0
+FAIL conditionText getter for @supports (func()) assert_equals: expected 1 but got 0
+PASS conditionText getter for @supports (x)
+PASS conditionText getter for @supports func(x)
+PASS conditionText getter for @supports ([x])
+PASS conditionText getter for @supports ({x})
+PASS conditionText getter for @supports ((x))
+PASS conditionText getter for @supports (func(x))
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/generic/storage/websql/test-authorizer-expected.txt b/third_party/blink/web_tests/platform/generic/storage/websql/test-authorizer-expected.txt
index 1f3a673..d11d2d1a 100644
--- a/third_party/blink/web_tests/platform/generic/storage/websql/test-authorizer-expected.txt
+++ b/third_party/blink/web_tests/platform/generic/storage/websql/test-authorizer-expected.txt
@@ -20,7 +20,7 @@
 SQLITE_ALTER_TABLE statement succeeded.
 SQLITE_ALTER_TABLE statement succeeded.
 SQLITE_ADD_COLUMN statement succeeded.
-SQLITE_DROP_COLUMN statement failed: could not prepare statement (1 SQL logic error)
+SQLITE_DROP_COLUMN statement failed: could not prepare statement (1 not authorized to use function: sqlite_drop_column)
 SQLITE_TRANSACTION statement failed: could not prepare statement (23 not authorized)
 SQLITE_ATTACH statement failed: could not prepare statement (23 not authorized)
 SQLITE_DETACH statement failed: could not prepare statement (23 not authorized)
diff --git a/third_party/blink/web_tests/platform/mac/external/wpt/webmessaging/with-ports/020-expected.txt b/third_party/blink/web_tests/platform/mac/external/wpt/webmessaging/with-ports/020-expected.txt
new file mode 100644
index 0000000..bd17790a
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/external/wpt/webmessaging/with-ports/020-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL cross-origin test Uncaught Error: assert_equals: expected 1 but got 2
+Harness: the test ran to completion.
+
diff --git a/third_party/closure_compiler/externs/automation.js b/third_party/closure_compiler/externs/automation.js
index 703cccf..634f948 100644
--- a/third_party/closure_compiler/externs/automation.js
+++ b/third_party/closure_compiler/externs/automation.js
@@ -444,7 +444,6 @@
  * @see https://developer.chrome.com/extensions/automation#type-NameFromType
  */
 chrome.automation.NameFromType = {
-  UNINITIALIZED: 'uninitialized',
   ATTRIBUTE: 'attribute',
   ATTRIBUTE_EXPLICITLY_EMPTY: 'attributeExplicitlyEmpty',
   CAPTION: 'caption',
diff --git a/third_party/crashpad/README.chromium b/third_party/crashpad/README.chromium
index 37720365..73a50abb 100644
--- a/third_party/crashpad/README.chromium
+++ b/third_party/crashpad/README.chromium
@@ -2,7 +2,7 @@
 Short Name: crashpad
 URL: https://crashpad.chromium.org/
 Version: unknown
-Revision: 14246325920a4dc4c5d2862a93721cc8a9590044
+Revision: df86075acc33314e611b351b33bf1c671b8cbc2f
 License: Apache 2.0
 License File: crashpad/LICENSE
 Security Critical: yes
diff --git a/third_party/crashpad/crashpad/client/crash_report_database.h b/third_party/crashpad/crashpad/client/crash_report_database.h
index fea49853..4404959f 100644
--- a/third_party/crashpad/crashpad/client/crash_report_database.h
+++ b/third_party/crashpad/crashpad/client/crash_report_database.h
@@ -328,7 +328,8 @@
   virtual OperationStatus GetCompletedReports(std::vector<Report>* reports) = 0;
 
   //! \brief Obtains and locks a report object for uploading to a collection
-  //!     server.
+  //!     server. On iOS the file lock is released and mutual-exclusion is kept
+  //!     via a file attribute.
   //!
   //! Callers should upload the crash report using the FileReader provided.
   //! Callers should then call RecordUploadComplete() to record a successful
@@ -336,6 +337,13 @@
   //! be recorded as unsuccessful and the report lock released when \a report is
   //! destroyed.
   //!
+  //! On iOS, holding a lock during a slow upload can lead to watchdog kills if
+  //! the app is suspended mid-upload. Instead, if the client can obtain the
+  //! lock, the database sets a lock-time file attribute and releases the lock.
+  //! The attribute is cleared when the upload is completed. The lock-time
+  //! attribute can be used to prevent file access from other processes, or to
+  //! discard reports that likely were terminated mid-upload.
+  //!
   //! \param[in] uuid The unique identifier for the crash report record.
   //! \param[out] report A crash report record for the report to be uploaded.
   //!     Only valid if this returns #kNoError.
diff --git a/third_party/crashpad/crashpad/client/crash_report_database_mac.mm b/third_party/crashpad/crashpad/client/crash_report_database_mac.mm
index e33b932..813e5b8f 100644
--- a/third_party/crashpad/crashpad/client/crash_report_database_mac.mm
+++ b/third_party/crashpad/crashpad/client/crash_report_database_mac.mm
@@ -71,6 +71,9 @@
 constexpr char kXattrCollectorID[] = "id";
 constexpr char kXattrCreationTime[] = "creation_time";
 constexpr char kXattrIsUploaded[] = "uploaded";
+#if BUILDFLAG(IS_IOS)
+constexpr char kXattrUploadStartTime[] = "upload_start_time";
+#endif
 constexpr char kXattrLastUploadTime[] = "last_upload_time";
 constexpr char kXattrUploadAttemptCount[] = "upload_count";
 constexpr char kXattrIsUploadExplicitlyRequested[] =
@@ -189,10 +192,11 @@
     //! \brief Obtain a background task assertion while a flock is in use.
     //!     Ensure this is defined first so it is destroyed last.
     internal::ScopedBackgroundTask ios_background_task{"UploadReportMac"};
-#endif  // BUILDFLAG(IS_IOS)
+#else
     //! \brief Stores the flock of the file for the duration of
     //!     GetReportForUploading() and RecordUploadAttempt().
     base::ScopedFD lock_fd;
+#endif  // BUILDFLAG(IS_IOS)
   };
 
   //! \brief Locates a crash report in the database by UUID.
@@ -474,12 +478,50 @@
   if (!ReadReportMetadataLocked(upload_report->file_path, upload_report.get()))
     return kDatabaseError;
 
+#if BUILDFLAG(IS_IOS)
+  time_t upload_start_time = 0;
+  if (ReadXattrTimeT(upload_report->file_path,
+                     XattrName(kXattrUploadStartTime),
+                     &upload_start_time) == XattrStatus::kOtherError) {
+    return kDatabaseError;
+  }
+
+  time_t now = time(nullptr);
+  if (upload_start_time) {
+    // If we were able to ObtainReportLock but kXattrUploadStartTime is set,
+    // either another client is uploading this report or a client was terminated
+    // during an upload. CrashReportUploadThread sets the timeout to 20 seconds
+    // for iOS. If kXattrUploadStartTime is less than  5 minutes ago, consider
+    // the report locked and return kBusyError. Otherwise, consider the upload a
+    // failure and skip the report.
+    if (upload_start_time > now - 15 * internal::kUploadReportTimeoutSeconds) {
+      return kBusyError;
+    } else {
+      // SkipReportUpload expects an unlocked report.
+      lock.reset();
+      CrashReportDatabase::OperationStatus os = SkipReportUpload(
+          upload_report->uuid, Metrics::CrashSkippedReason::kUploadFailed);
+      if (os != kNoError) {
+        return kDatabaseError;
+      }
+      return kReportNotFound;
+    }
+  }
+
+  if (!WriteXattrTimeT(
+          upload_report->file_path, XattrName(kXattrUploadStartTime), now)) {
+    return kDatabaseError;
+  }
+#endif
+
   if (!upload_report->Initialize(upload_report->file_path, this)) {
     return kFileSystemError;
   }
 
   upload_report->database_ = this;
+#if !BUILDFLAG(IS_IOS)
   upload_report->lock_fd.reset(lock.release());
+#endif
   upload_report->report_metrics_ = report_metrics;
   report->reset(upload_report.release());
   return kNoError;
@@ -510,6 +552,13 @@
       return os;
   }
 
+#if BUILDFLAG(IS_IOS)
+  if (RemoveXattr(report_path, XattrName(kXattrUploadStartTime)) ==
+      XattrStatus::kOtherError) {
+    return kDatabaseError;
+  }
+#endif
+
   if (!WriteXattrBool(report_path, XattrName(kXattrIsUploaded), successful)) {
     return kDatabaseError;
   }
@@ -554,6 +603,13 @@
   if (!lock.is_valid())
     return kBusyError;
 
+#if BUILDFLAG(IS_IOS)
+  if (RemoveXattr(report_path, XattrName(kXattrUploadStartTime)) ==
+      XattrStatus::kOtherError) {
+    return kDatabaseError;
+  }
+#endif
+
   return MarkReportCompletedLocked(report_path, nullptr);
 }
 
diff --git a/third_party/crashpad/crashpad/client/crash_report_database_test.cc b/third_party/crashpad/crashpad/client/crash_report_database_test.cc
index bdb6dc24..67b5edc 100644
--- a/third_party/crashpad/crashpad/client/crash_report_database_test.cc
+++ b/third_party/crashpad/crashpad/client/crash_report_database_test.cc
@@ -24,6 +24,10 @@
 #include "util/file/file_io.h"
 #include "util/file/filesystem.h"
 
+#if BUILDFLAG(IS_IOS)
+#include "util/mac/xattr.h"
+#endif
+
 namespace crashpad {
 namespace test {
 namespace {
@@ -511,6 +515,46 @@
             CrashReportDatabase::kNoError);
 }
 
+#if BUILDFLAG(IS_IOS)
+TEST_F(CrashReportDatabaseTest, InterruptedIOSUploads) {
+  CrashReportDatabase::Report report;
+  CreateCrashReport(&report);
+
+  std::unique_ptr<const CrashReportDatabase::UploadReport> upload_report;
+  EXPECT_EQ(db()->GetReportForUploading(report.uuid, &upload_report),
+            CrashReportDatabase::kNoError);
+
+  // Set upload_start_time to 10 minutes ago.
+  time_t ten_minutes_ago = time(nullptr) - 10 * 60;
+  ASSERT_TRUE(
+      WriteXattrTimeT(report.file_path,
+                      "org.chromium.crashpad.database.upload_start_time",
+                      ten_minutes_ago));
+
+  std::vector<CrashReportDatabase::Report> reports;
+  EXPECT_EQ(db()->GetPendingReports(&reports), CrashReportDatabase::kNoError);
+  ASSERT_EQ(reports.size(), 1u);
+  reports.clear();
+  EXPECT_EQ(db()->GetCompletedReports(&reports), CrashReportDatabase::kNoError);
+  EXPECT_TRUE(reports.empty());
+
+  // Getting a stale report will automatically skip it.
+  std::unique_ptr<const CrashReportDatabase::UploadReport> upload_report_2;
+  EXPECT_EQ(db()->GetReportForUploading(report.uuid, &upload_report_2),
+            CrashReportDatabase::kReportNotFound);
+  EXPECT_FALSE(upload_report_2);
+
+  // Confirm report was moved from pending to completed.
+  EXPECT_EQ(db()->GetPendingReports(&reports), CrashReportDatabase::kNoError);
+  EXPECT_TRUE(reports.empty());
+  EXPECT_EQ(db()->GetCompletedReports(&reports), CrashReportDatabase::kNoError);
+  ASSERT_EQ(reports.size(), 1u);
+
+  EXPECT_EQ(db()->RecordUploadComplete(std::move(upload_report), std::string()),
+            CrashReportDatabase::kReportNotFound);
+}
+#endif
+
 TEST_F(CrashReportDatabaseTest, UploadAlreadyUploaded) {
   CrashReportDatabase::Report report;
   CreateCrashReport(&report);
diff --git a/third_party/crashpad/crashpad/client/ios_handler/in_process_intermediate_dump_handler.cc b/third_party/crashpad/crashpad/client/ios_handler/in_process_intermediate_dump_handler.cc
index e7e9003..6b43158 100644
--- a/third_party/crashpad/crashpad/client/ios_handler/in_process_intermediate_dump_handler.cc
+++ b/third_party/crashpad/crashpad/client/ios_handler/in_process_intermediate_dump_handler.cc
@@ -118,7 +118,7 @@
 //! \param[in] writer The dump writer
 //! \param[in] key The key to write.
 //! \param[in] value Memory to be written.
-//! \param[in] count Length of \a data.
+//! \param[in] value_length Length of \a data.
 void WritePropertyBytes(IOSIntermediateDumpWriter* writer,
                         IntermediateDumpKey key,
                         const void* value,
@@ -127,6 +127,20 @@
     WriteError(key);
 }
 
+//! \brief Call AddPropertyCString with raw error log.
+//!
+//! \param[in] writer The dump writer
+//! \param[in] key The key to write.
+//! \param[in] max_length The maximum string length.
+//! \param[in] value Memory to be written.
+void WritePropertyCString(IOSIntermediateDumpWriter* writer,
+                          IntermediateDumpKey key,
+                          size_t max_length,
+                          const char* value) {
+  if (!writer->AddPropertyCString(key, max_length, value))
+    WriteError(key);
+}
+
 kern_return_t MachVMRegionRecurseDeepest(task_t task,
                                          vm_address_t* address,
                                          vm_size_t* size,
@@ -498,80 +512,6 @@
   }
 }
 
-void WriteDyldErrorStringAnnotation(
-    IOSIntermediateDumpWriter* writer,
-    const uint64_t address,
-    const symtab_command* symtab_command_ptr,
-    const dysymtab_command* dysymtab_command_ptr,
-    const segment_command_64* text_seg_ptr,
-    const segment_command_64* linkedit_seg_ptr,
-    vm_size_t slide) {
-  if (text_seg_ptr == nullptr || linkedit_seg_ptr == nullptr ||
-      symtab_command_ptr == nullptr) {
-    return;
-  }
-
-  ScopedVMRead<symtab_command> symtab_command;
-  ScopedVMRead<dysymtab_command> dysymtab_command;
-  ScopedVMRead<segment_command_64> text_seg;
-  ScopedVMRead<segment_command_64> linkedit_seg;
-  if (!symtab_command.Read(symtab_command_ptr) ||
-      !text_seg.Read(text_seg_ptr) || !linkedit_seg.Read(linkedit_seg_ptr) ||
-      (dysymtab_command_ptr && !dysymtab_command.Read(dysymtab_command_ptr))) {
-    CRASHPAD_RAW_LOG("Unable to load dyld symbol table.");
-  }
-
-  uint64_t file_slide =
-      (linkedit_seg->vmaddr - text_seg->vmaddr) - linkedit_seg->fileoff;
-  uint64_t strings = address + (symtab_command->stroff + file_slide);
-  nlist_64* symbol_ptr = reinterpret_cast<nlist_64*>(
-      address + (symtab_command->symoff + file_slide));
-
-  // If a dysymtab is present, use it to filter the symtab for just the
-  // portion used for extdefsym. If no dysymtab is present, the entire symtab
-  // will need to be consulted.
-  uint32_t symbol_count = symtab_command->nsyms;
-  if (dysymtab_command_ptr) {
-    symbol_ptr += dysymtab_command->iextdefsym;
-    symbol_count = dysymtab_command->nextdefsym;
-  }
-
-  for (uint32_t i = 0; i < symbol_count; i++, symbol_ptr++) {
-    ScopedVMRead<nlist_64> symbol;
-    if (!symbol.Read(symbol_ptr)) {
-      CRASHPAD_RAW_LOG("Unable to load dyld symbol table symbol.");
-      return;
-    }
-
-    if (!symbol->n_value)
-      continue;
-
-    ScopedVMRead<const char> symbol_name;
-    if (!symbol_name.Read(strings + symbol->n_un.n_strx)) {
-      CRASHPAD_RAW_LOG("Unable to load dyld symbol name.");
-    }
-
-    if (strcmp(symbol_name.get(), "_error_string") == 0) {
-      ScopedVMRead<const char> symbol_value;
-      if (!symbol_value.Read(symbol->n_value + slide)) {
-        CRASHPAD_RAW_LOG("Unable to load dyld symbol value.");
-      }
-      // 1024 here is distinct from kMaxMessageSize above, because it refers to
-      // a precisely-sized buffer inside dyld.
-      const size_t value_len = strnlen(symbol_value.get(), 1024);
-      if (value_len) {
-        WriteProperty(writer,
-                      IntermediateDumpKey::kAnnotationsDyldErrorString,
-                      symbol_value.get(),
-                      value_len);
-      }
-      return;
-    }
-
-    continue;
-  }
-}
-
 }  // namespace
 
 // static
@@ -980,10 +920,8 @@
     }
 
     if (image->imageFilePath) {
-      WriteProperty(writer,
-                    IntermediateDumpKey::kName,
-                    image->imageFilePath,
-                    strlen(image->imageFilePath));
+      WritePropertyCString(
+          writer, IntermediateDumpKey::kName, PATH_MAX, image->imageFilePath);
     }
     uint64_t address = FromPointerCast<uint64_t>(image->imageLoadAddress);
     WriteProperty(writer, IntermediateDumpKey::kAddress, &address);
@@ -995,10 +933,8 @@
   {
     IOSIntermediateDumpWriter::ScopedArrayMap modules(writer);
     if (image_infos->dyldPath) {
-      WriteProperty(writer,
-                    IntermediateDumpKey::kName,
-                    image_infos->dyldPath,
-                    strlen(image_infos->dyldPath));
+      WritePropertyCString(
+          writer, IntermediateDumpKey::kName, PATH_MAX, image_infos->dyldPath);
     }
     uint64_t address =
         FromPointerCast<uint64_t>(image_infos->dyldImageLoadAddress);
@@ -1129,10 +1065,6 @@
   // Make sure that the basic load command structure doesn’t overflow the
   // space allotted for load commands, as well as iterating through ncmds.
   vm_size_t slide = 0;
-  const symtab_command* symtab_command = nullptr;
-  const dysymtab_command* dysymtab_command = nullptr;
-  const segment_command_64* linkedit_seg = nullptr;
-  const segment_command_64* text_seg = nullptr;
   for (uint32_t cmd_index = 0, cumulative_cmd_size = 0;
        cmd_index <= header->ncmds && cumulative_cmd_size < header->sizeofcmds;
        ++cmd_index, cumulative_cmd_size += command->cmdsize) {
@@ -1145,20 +1077,11 @@
       const segment_command_64* segment_ptr =
           reinterpret_cast<const segment_command_64*>(command_ptr);
       if (strcmp(segment->segname, SEG_TEXT) == 0) {
-        text_seg = segment_ptr;
         WriteProperty(writer, IntermediateDumpKey::kSize, &segment->vmsize);
         slide = address - segment->vmaddr;
       } else if (strcmp(segment->segname, SEG_DATA) == 0) {
         WriteDataSegmentAnnotations(writer, segment_ptr, slide);
-      } else if (strcmp(segment->segname, SEG_LINKEDIT) == 0) {
-        linkedit_seg = segment_ptr;
       }
-    } else if (command->cmd == LC_SYMTAB) {
-      symtab_command =
-          reinterpret_cast<const struct symtab_command*>(command_ptr);
-    } else if (command->cmd == LC_DYSYMTAB) {
-      dysymtab_command =
-          reinterpret_cast<const struct dysymtab_command*>(command_ptr);
     } else if (command->cmd == LC_ID_DYLIB) {
       ScopedVMRead<dylib_command> dylib;
       if (!dylib.Read(command_ptr)) {
@@ -1195,16 +1118,6 @@
   }
 
   WriteProperty(writer, IntermediateDumpKey::kFileType, &header->filetype);
-
-  if (is_dyld && header->filetype == MH_DYLINKER) {
-    WriteDyldErrorStringAnnotation(writer,
-                                   address,
-                                   symtab_command,
-                                   dysymtab_command,
-                                   text_seg,
-                                   linkedit_seg,
-                                   slide);
-  }
 }
 
 void InProcessIntermediateDumpHandler::WriteDataSegmentAnnotations(
@@ -1286,12 +1199,10 @@
     }
 
     IOSIntermediateDumpWriter::ScopedArrayMap annotation_map(writer);
-    const size_t name_len = strnlen(reinterpret_cast<const char*>(node->name()),
-                                    Annotation::kNameMaxLength);
-    WritePropertyBytes(writer,
-                       IntermediateDumpKey::kAnnotationName,
-                       reinterpret_cast<const void*>(node->name()),
-                       name_len);
+    WritePropertyCString(writer,
+                         IntermediateDumpKey::kAnnotationName,
+                         Annotation::kNameMaxLength,
+                         reinterpret_cast<const char*>(node->name()));
     WritePropertyBytes(writer,
                        IntermediateDumpKey::kAnnotationValue,
                        reinterpret_cast<const void*>(node->value()),
diff --git a/third_party/crashpad/crashpad/client/settings.h b/third_party/crashpad/crashpad/client/settings.h
index 8ad8a2b..e4a5ced 100644
--- a/third_party/crashpad/crashpad/client/settings.h
+++ b/third_party/crashpad/crashpad/client/settings.h
@@ -37,6 +37,14 @@
   static void Free(FileHandle handle);
 };
 
+// TODO(mark): The timeout should be configurable by the client.
+#if BUILDFLAG(IS_IOS)
+// iOS background assertions only last 30 seconds, keep the timeout shorter.
+constexpr double kUploadReportTimeoutSeconds = 20;
+#else
+constexpr double kUploadReportTimeoutSeconds = 60;
+#endif
+
 }  // namespace internal
 
 //! \brief An interface for accessing and modifying the settings of a
diff --git a/third_party/crashpad/crashpad/handler/crash_report_upload_thread.cc b/third_party/crashpad/crashpad/handler/crash_report_upload_thread.cc
index 138cf800..4d1548d 100644
--- a/third_party/crashpad/crashpad/handler/crash_report_upload_thread.cc
+++ b/third_party/crashpad/crashpad/handler/crash_report_upload_thread.cc
@@ -311,13 +311,7 @@
   }
   http_transport->SetBodyStream(http_multipart_builder.GetBodyStream());
   // TODO(mark): The timeout should be configurable by the client.
-#if BUILDFLAG(IS_IOS)
-  // iOS background assertions only last 30 seconds, keep the timeout shorter.
-  double timeout_seconds = 20;
-#else
-  double timeout_seconds = 60;
-#endif
-  http_transport->SetTimeout(timeout_seconds);
+  http_transport->SetTimeout(internal::kUploadReportTimeoutSeconds);
 
   std::string url = url_;
   if (options_.identify_client_via_url) {
diff --git a/third_party/crashpad/crashpad/util/ios/ios_intermediate_dump_writer.cc b/third_party/crashpad/crashpad/util/ios/ios_intermediate_dump_writer.cc
index 0fd3851..bfbb596c 100644
--- a/third_party/crashpad/crashpad/util/ios/ios_intermediate_dump_writer.cc
+++ b/third_party/crashpad/crashpad/util/ios/ios_intermediate_dump_writer.cc
@@ -15,8 +15,10 @@
 #include "util/ios/ios_intermediate_dump_writer.h"
 
 #include <fcntl.h>
+#include <mach/mach.h>
 #include <unistd.h>
 
+#include <algorithm>
 #include <ostream>
 
 #include "base/check.h"
@@ -86,6 +88,73 @@
   return RawLoggingCloseFile(fd);
 }
 
+bool IOSIntermediateDumpWriter::AddPropertyCString(IntermediateDumpKey key,
+                                                   size_t max_length,
+                                                   const char* value) {
+  constexpr size_t kMaxStringBytes = 1024;
+  if (max_length > kMaxStringBytes) {
+    CRASHPAD_RAW_LOG("AddPropertyCString max_length too large");
+    return false;
+  }
+
+  char buffer[kMaxStringBytes];
+  size_t string_length;
+  if (ReadCStringInternal(value, buffer, max_length, &string_length)) {
+    return Property(key, buffer, string_length);
+  }
+  return false;
+}
+
+bool IOSIntermediateDumpWriter::ReadCStringInternal(const char* value,
+                                                    char* buffer,
+                                                    size_t max_length,
+                                                    size_t* string_length) {
+  size_t length = 0;
+  while (length < max_length) {
+    vm_address_t data_address = reinterpret_cast<vm_address_t>(value + length);
+    // Calculate bytes to read past `data_address`, either the number of bytes
+    // to the end of the page, or the remaining bytes in `buffer`, whichever is
+    // smaller.
+    size_t data_to_end_of_page =
+        getpagesize() - (data_address - trunc_page(data_address));
+    size_t remaining_bytes_in_buffer = max_length - length;
+    size_t bytes_to_read =
+        std::min(data_to_end_of_page, remaining_bytes_in_buffer);
+
+    char* buffer_start = buffer + length;
+    size_t bytes_read = 0;
+    kern_return_t kr =
+        vm_read_overwrite(mach_task_self(),
+                          data_address,
+                          bytes_to_read,
+                          reinterpret_cast<vm_address_t>(buffer_start),
+                          &bytes_read);
+    if (kr != KERN_SUCCESS || bytes_read <= 0) {
+      CRASHPAD_RAW_LOG("ReadCStringInternal vm_read_overwrite failed");
+      return false;
+    }
+
+    char* nul = static_cast<char*>(memchr(buffer_start, '\0', bytes_read));
+    if (nul != nullptr) {
+      length += nul - buffer_start;
+      *string_length = length;
+      return true;
+    }
+    length += bytes_read;
+  }
+  CRASHPAD_RAW_LOG("unterminated string");
+  return false;
+}
+
+bool IOSIntermediateDumpWriter::AddPropertyInternal(IntermediateDumpKey key,
+                                                    const char* value,
+                                                    size_t value_length) {
+  ScopedVMRead<char> vmread;
+  if (!vmread.Read(value, value_length))
+    return false;
+  return Property(key, vmread.get(), value_length);
+}
+
 bool IOSIntermediateDumpWriter::ArrayMapStart() {
   const CommandType command_type = CommandType::kMapStart;
   return RawLoggingWriteFile(fd_, &command_type, sizeof(command_type));
@@ -123,17 +192,14 @@
   return RawLoggingWriteFile(fd_, &command_type, sizeof(command_type));
 }
 
-bool IOSIntermediateDumpWriter::AddPropertyInternal(IntermediateDumpKey key,
-                                                    const char* value,
-                                                    size_t value_length) {
-  ScopedVMRead<char> vmread;
-  if (!vmread.Read(value, value_length))
-    return false;
+bool IOSIntermediateDumpWriter::Property(IntermediateDumpKey key,
+                                         const void* value,
+                                         size_t value_length) {
   const CommandType command_type = CommandType::kProperty;
   return RawLoggingWriteFile(fd_, &command_type, sizeof(command_type)) &&
          RawLoggingWriteFile(fd_, &key, sizeof(key)) &&
          RawLoggingWriteFile(fd_, &value_length, sizeof(size_t)) &&
-         RawLoggingWriteFile(fd_, vmread.get(), value_length);
+         RawLoggingWriteFile(fd_, value, value_length);
 }
 
 }  // namespace internal
diff --git a/third_party/crashpad/crashpad/util/ios/ios_intermediate_dump_writer.h b/third_party/crashpad/crashpad/util/ios/ios_intermediate_dump_writer.h
index d4f7a7b..ea178353 100644
--- a/third_party/crashpad/crashpad/util/ios/ios_intermediate_dump_writer.h
+++ b/third_party/crashpad/crashpad/util/ios/ios_intermediate_dump_writer.h
@@ -175,9 +175,27 @@
         key, reinterpret_cast<const char*>(value), value_length);
   }
 
+  //! \return Returns `true` if able to vm_read a string of \a value  and write
+  //!     a kProperty command with the \a key \a value up to a NUL byte.
+  //!     The string cannot be longer than \a max_length with a maximum string
+  //!     length of 1024.
+  bool AddPropertyCString(IntermediateDumpKey key,
+                          size_t max_length,
+                          const char* value);
+
  private:
-  //! \return Returns `true` if able to write a kProperty command  with the
-  //!     \a key \a value \a count tuple.
+  //! \return Returns `true` if able to vm_read_overwrite \a value into
+  //!     \a buffer while only reading one page at a time up to a NUL byte.
+  //!     Sets the final length of \a buffer to \a string_length.
+  //!     Returns `false` if unable to vm_read \a value or when no NUL byte can
+  //!     be found within /a max_length (unterminated).
+  bool ReadCStringInternal(const char* value,
+                           char* buffer,
+                           size_t max_length,
+                           size_t* string_length);
+
+  //! \return Returns `true` if able to vm_read \a value \a count and write a
+  //!     kProperty command  with the \a key \a value \a count tuple.
   bool AddPropertyInternal(IntermediateDumpKey key,
                            const char* value,
                            size_t value_length);
@@ -205,6 +223,12 @@
   //! \return Returns `true` if able to write a kRootMapEnd command.
   bool RootMapEnd();
 
+  //! \return Returns `true` if able to write a kProperty command with the
+  //!     \a key \a value \a value_length tuple.
+  bool Property(IntermediateDumpKey key,
+                const void* value,
+                size_t value_length);
+
   int fd_;
 };
 
diff --git a/third_party/crashpad/crashpad/util/ios/ios_intermediate_dump_writer_test.cc b/third_party/crashpad/crashpad/util/ios/ios_intermediate_dump_writer_test.cc
index 055ae70..5e70899 100644
--- a/third_party/crashpad/crashpad/util/ios/ios_intermediate_dump_writer_test.cc
+++ b/third_party/crashpad/crashpad/util/ios/ios_intermediate_dump_writer_test.cc
@@ -15,8 +15,10 @@
 #include "util/ios/ios_intermediate_dump_writer.h"
 
 #include <fcntl.h>
+#include <mach/mach.h>
 
 #include "base/files/scoped_file.h"
+#include "base/mac/scoped_mach_vm.h"
 #include "base/posix/eintr_wrapper.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
@@ -102,6 +104,78 @@
   ASSERT_EQ(contents, result);
 }
 
+TEST_F(IOSIntermediateDumpWriterTest, PropertyString) {
+  EXPECT_TRUE(writer_->Open(path()));
+  EXPECT_TRUE(writer_->AddPropertyCString(Key::kVersion, 64, "version"));
+
+  std::string contents;
+  ASSERT_TRUE(LoggingReadEntireFile(path(), &contents));
+  std::string result("\5\1\0\a\0\0\0\0\0\0\0version", 18);
+  ASSERT_EQ(contents, result);
+}
+
+TEST_F(IOSIntermediateDumpWriterTest, PropertyStringShort) {
+  EXPECT_TRUE(writer_->Open(path()));
+  EXPECT_FALSE(
+      writer_->AddPropertyCString(Key::kVersion, 7, "versionnnnnnnnnnnn"));
+}
+
+TEST_F(IOSIntermediateDumpWriterTest, PropertyStringLong) {
+  EXPECT_TRUE(writer_->Open(path()));
+
+  char* bad_string = nullptr;
+  EXPECT_FALSE(writer_->AddPropertyCString(Key::kVersion, 1025, bad_string));
+}
+
+TEST_F(IOSIntermediateDumpWriterTest, MissingPropertyString) {
+  char* region;
+  vm_size_t page_size = getpagesize();
+  vm_size_t region_size = page_size * 2;
+  ASSERT_EQ(vm_allocate(mach_task_self(),
+                        reinterpret_cast<vm_address_t*>(&region),
+                        region_size,
+                        VM_FLAGS_ANYWHERE),
+            0);
+  base::mac::ScopedMachVM vm_owner(reinterpret_cast<vm_address_t>(region),
+                                   region_size);
+
+  // Fill first page with 'A' and second with 'B'.
+  memset(region, 'A', page_size);
+  memset(region + page_size, 'B', page_size);
+
+  // Drop a NUL 10 bytes from the end of the first page and into the second
+  // page.
+  region[page_size - 10] = '\0';
+  region[page_size + 10] = '\0';
+
+  // Read a string that spans two pages.
+  EXPECT_TRUE(writer_->Open(path()));
+  EXPECT_TRUE(
+      writer_->AddPropertyCString(Key::kVersion, 64, region + page_size - 5));
+  std::string contents;
+  ASSERT_TRUE(LoggingReadEntireFile(path(), &contents));
+  std::string result("\x5\x1\0\xF\0\0\0\0\0\0\0AAAAABBBBBBBBBB", 26);
+  ASSERT_EQ(contents, result);
+
+  // Dealloc second page.
+  ASSERT_EQ(vm_deallocate(mach_task_self(),
+                          reinterpret_cast<vm_address_t>(region + page_size),
+                          page_size),
+            0);
+
+  // Reading the same string should fail when the next page is dealloc-ed.
+  EXPECT_FALSE(
+      writer_->AddPropertyCString(Key::kVersion, 64, region + page_size - 5));
+
+  // Ensure we can read the first string without loading the second page.
+  EXPECT_TRUE(writer_->Open(path()));
+  EXPECT_TRUE(
+      writer_->AddPropertyCString(Key::kVersion, 64, region + page_size - 20));
+  ASSERT_TRUE(LoggingReadEntireFile(path(), &contents));
+  result.assign("\x5\x1\0\n\0\0\0\0\0\0\0AAAAAAAAAA", 21);
+  ASSERT_EQ(contents, result);
+}
+
 TEST_F(IOSIntermediateDumpWriterTest, BadProperty) {
   EXPECT_TRUE(writer_->Open(path()));
   ASSERT_FALSE(writer_->AddProperty(Key::kVersion, "version", -1));
diff --git a/third_party/crashpad/crashpad/util/misc/metrics.cc b/third_party/crashpad/crashpad/util/misc/metrics.cc
index 2e8459c7..dac7300 100644
--- a/third_party/crashpad/crashpad/util/misc/metrics.cc
+++ b/third_party/crashpad/crashpad/util/misc/metrics.cc
@@ -78,6 +78,13 @@
   UMA_HISTOGRAM_BOOLEAN("Crashpad.CrashUpload.AttemptSuccessful", successful);
 }
 
+#if BUILDFLAG(IS_APPLE)
+// static
+void Metrics::CrashUploadErrorCode(int error_code) {
+  base::UmaHistogramSparse("Crashpad.CrashUpload.ErrorCode", error_code);
+}
+#endif
+
 // static
 void Metrics::CrashUploadSkipped(CrashSkippedReason reason) {
   UMA_HISTOGRAM_ENUMERATION(
diff --git a/third_party/crashpad/crashpad/util/misc/metrics.h b/third_party/crashpad/crashpad/util/misc/metrics.h
index c870b3a2..e11fcc1 100644
--- a/third_party/crashpad/crashpad/util/misc/metrics.h
+++ b/third_party/crashpad/crashpad/util/misc/metrics.h
@@ -63,6 +63,12 @@
   //! \brief Reports on a crash upload attempt, and if it succeeded.
   static void CrashUploadAttempted(bool successful);
 
+#if BUILDFLAG(IS_APPLE) || DOXYGEN
+  //! \brief Records error codes from
+  //!     `+[NSURLConnection sendSynchronousRequest:returningResponse:error:]`.
+  static void CrashUploadErrorCode(int error_code);
+#endif
+
   //! \brief Values for CrashUploadSkipped().
   //!
   //! \note These are used as metrics enumeration values, so new values should
diff --git a/third_party/crashpad/crashpad/util/net/http_transport_mac.mm b/third_party/crashpad/crashpad/util/net/http_transport_mac.mm
index 50b9206..445e895 100644
--- a/third_party/crashpad/crashpad/util/net/http_transport_mac.mm
+++ b/third_party/crashpad/crashpad/util/net/http_transport_mac.mm
@@ -25,6 +25,7 @@
 #include "package.h"
 #include "util/file/file_io.h"
 #include "util/misc/implicit_cast.h"
+#include "util/misc/metrics.h"
 #include "util/net/http_body.h"
 
 // An implementation of NSInputStream that reads from a
@@ -257,6 +258,7 @@
 #pragma clang diagnostic pop
 
     if (error) {
+      Metrics::CrashUploadErrorCode(error.code);
       LOG(ERROR) << [[error localizedDescription] UTF8String] << " ("
                  << [[error domain] UTF8String] << " " << [error code] << ")";
       return false;
diff --git a/third_party/eigen3/README.chromium b/third_party/eigen3/README.chromium
index 8d90f24..e319a4f 100644
--- a/third_party/eigen3/README.chromium
+++ b/third_party/eigen3/README.chromium
@@ -1,8 +1,8 @@
 Name: Eigen
 Short Name: eigen3
 URL: http://eigen.tuxfamily.org/
-Version: b02c384ef4e8eba7b8bdef16f9dc6f8f4d6a6b2b
-Date: 2022/05/02
+Version: 0e187141679fdb91da33249d18cb79a011c0e2ea
+Date: 2022/07/14
 License: MPL 2
 License File: LICENSE
 Security Critical: Yes
diff --git a/third_party/ipcz/src/ipcz/node_link.cc b/third_party/ipcz/src/ipcz/node_link.cc
index fe89b0b..7e08c643 100644
--- a/third_party/ipcz/src/ipcz/node_link.cc
+++ b/third_party/ipcz/src/ipcz/node_link.cc
@@ -266,6 +266,19 @@
       sublink->router_link->GetType(), route_closed.params().sequence_length);
 }
 
+bool NodeLink::OnSetRouterLinkState(msg::SetRouterLinkState& set) {
+  if (set.params().descriptor.is_null()) {
+    return false;
+  }
+
+  if (absl::optional<Sublink> sublink = GetSublink(set.params().sublink)) {
+    auto fragment = memory().GetFragment(set.params().descriptor);
+    sublink->router_link->SetLinkState(
+        memory().AdoptFragmentRef<RouterLinkState>(fragment));
+  }
+  return true;
+}
+
 bool NodeLink::OnFlushRouter(msg::FlushRouter& flush) {
   if (Ref<Router> router = GetRouter(flush.params().sublink)) {
     router->Flush();
diff --git a/third_party/ipcz/src/ipcz/node_link.h b/third_party/ipcz/src/ipcz/node_link.h
index 54fad52..d7d2292 100644
--- a/third_party/ipcz/src/ipcz/node_link.h
+++ b/third_party/ipcz/src/ipcz/node_link.h
@@ -135,6 +135,7 @@
   bool OnAddBlockBuffer(msg::AddBlockBuffer& add) override;
   bool OnAcceptParcel(msg::AcceptParcel& accept) override;
   bool OnRouteClosed(msg::RouteClosed& route_closed) override;
+  bool OnSetRouterLinkState(msg::SetRouterLinkState& set) override;
   bool OnFlushRouter(msg::FlushRouter& flush) override;
   void OnTransportError() override;
 
diff --git a/third_party/ipcz/src/ipcz/node_link_memory.cc b/third_party/ipcz/src/ipcz/node_link_memory.cc
index 74f73585..6bf92d05 100644
--- a/third_party/ipcz/src/ipcz/node_link_memory.cc
+++ b/third_party/ipcz/src/ipcz/node_link_memory.cc
@@ -282,6 +282,39 @@
   return buffer_pool_.FreeBlock(fragment);
 }
 
+FragmentRef<RouterLinkState> NodeLinkMemory::TryAllocateRouterLinkState() {
+  Fragment fragment = buffer_pool_.AllocateBlock(sizeof(RouterLinkState));
+  if (!fragment.is_null()) {
+    return InitializeRouterLinkStateFragment(fragment);
+  }
+
+  // Unlike with the more generic AllocateFragment(), we unconditionally lobby
+  // for additional capacity when RouterLinkState allocation fails.
+  RequestBlockCapacity(sizeof(RouterLinkState), [](bool ok) {});
+  return {};
+}
+
+void NodeLinkMemory::AllocateRouterLinkState(RouterLinkStateCallback callback) {
+  Fragment fragment = buffer_pool_.AllocateBlock(sizeof(RouterLinkState));
+  if (!fragment.is_null()) {
+    callback(InitializeRouterLinkStateFragment(fragment));
+    return;
+  }
+
+  RequestBlockCapacity(sizeof(RouterLinkState),
+                       [memory = WrapRefCounted(this),
+                        callback = std::move(callback)](bool ok) mutable {
+                         if (!ok) {
+                           DLOG(ERROR)
+                               << "Could not allocate a new RouterLinkState";
+                           callback({});
+                           return;
+                         }
+
+                         memory->AllocateRouterLinkState(std::move(callback));
+                       });
+}
+
 void NodeLinkMemory::WaitForBufferAsync(
     BufferId id,
     BufferPool::WaitForBufferCallback callback) {
@@ -368,4 +401,13 @@
   }
 }
 
+FragmentRef<RouterLinkState> NodeLinkMemory::InitializeRouterLinkStateFragment(
+    const Fragment& fragment) {
+  ABSL_ASSERT(!fragment.is_null());
+  FragmentRef<RouterLinkState> ref =
+      AdoptFragmentRef<RouterLinkState>(fragment);
+  RouterLinkState::Initialize(ref.get());
+  return ref;
+}
+
 }  // namespace ipcz
diff --git a/third_party/ipcz/src/ipcz/node_link_memory.h b/third_party/ipcz/src/ipcz/node_link_memory.h
index 0390719..be6770b2 100644
--- a/third_party/ipcz/src/ipcz/node_link_memory.h
+++ b/third_party/ipcz/src/ipcz/node_link_memory.h
@@ -17,6 +17,7 @@
 #include "ipcz/fragment_descriptor.h"
 #include "ipcz/fragment_ref.h"
 #include "ipcz/ipcz.h"
+#include "ipcz/ref_counted_fragment.h"
 #include "ipcz/router_link_state.h"
 #include "ipcz/sublink_id.h"
 #include "third_party/abseil-cpp/absl/container/flat_hash_map.h"
@@ -101,6 +102,15 @@
   // with the same BufferId and dimensions as `descriptor`.
   Fragment GetFragment(const FragmentDescriptor& descriptor);
 
+  // Adopts an existing reference to a RefCountedFragment within `fragment`.
+  // This does NOT increment the ref count of the RefCountedFragment.
+  template <typename T>
+  FragmentRef<T> AdoptFragmentRef(const Fragment& fragment) {
+    ABSL_ASSERT(sizeof(T) <= fragment.size());
+    return FragmentRef<T>(RefCountedFragment::kAdoptExistingRef,
+                          WrapRefCounted(this), fragment);
+  }
+
   // Adds a new buffer to the underlying BufferPool to use as additional
   // allocation capacity for blocks of size `block_size`. Note that the
   // contents of the mapped region must already be initialized as a
@@ -118,6 +128,21 @@
   // allocated fragment within this NodeLinkMemory.
   bool FreeFragment(const Fragment& fragment);
 
+  // Allocates a fragment to store a new RouterLinkState and initializes a new
+  // RouterLinkState instance there. If no capacity is currently available to
+  // allocate an appropriate fragment, this may return null.
+  FragmentRef<RouterLinkState> TryAllocateRouterLinkState();
+
+  // Allocates a fragment to store a new RouterLinkState and initializes a new
+  // RouterLinkState instance there. Calls `callback` with a reference to the
+  // new fragment once allocated. Unlike TryAllocateRouterLinkState(), this
+  // allocation always succeeds eventually unless driver memory allocation
+  // itself begins to fail unrecoverably. If the allocation can succeed
+  // synchronously, `callback` may be called before this method returns.
+  using RouterLinkStateCallback =
+      std::function<void(FragmentRef<RouterLinkState>)>;
+  void AllocateRouterLinkState(RouterLinkStateCallback callback);
+
   // Runs `callback` as soon as the identified buffer is added to the underlying
   // BufferPool. If the buffer is already present here, `callback` is run
   // immediately.
@@ -142,6 +167,10 @@
                             RequestBlockCapacityCallback callback);
   void OnCapacityRequestComplete(size_t block_size, bool success);
 
+  // Initializes `fragment` as a new RouterLinkState and returns a ref to it.
+  FragmentRef<RouterLinkState> InitializeRouterLinkStateFragment(
+      const Fragment& fragment);
+
   const Ref<Node> node_;
 
   // The underlying BufferPool. Note that this object is itself thread-safe, so
diff --git a/third_party/ipcz/src/ipcz/node_messages_generator.h b/third_party/ipcz/src/ipcz/node_messages_generator.h
index 5f4d2e096..6cdbf72 100644
--- a/third_party/ipcz/src/ipcz/node_messages_generator.h
+++ b/third_party/ipcz/src/ipcz/node_messages_generator.h
@@ -117,6 +117,14 @@
   IPCZ_MSG_PARAM(SequenceNumber, sequence_length)
 IPCZ_MSG_END()
 
+// Notifies a node that the Router it has bound to `sublink` (on the
+// transmitting NodeLink) now has an allocated RouterLinkState in the fragment
+// identified by `descriptor`.
+IPCZ_MSG_BEGIN(SetRouterLinkState, IPCZ_MSG_ID(23), IPCZ_MSG_VERSION(0))
+  IPCZ_MSG_PARAM(SublinkId, sublink)
+  IPCZ_MSG_PARAM(FragmentDescriptor, descriptor)
+IPCZ_MSG_END()
+
 // Hints to the target router that it should flush its state. Generally sent to
 // catalyze route reduction or elicit some other state change which was blocked
 // on some other work being done first by the sender of this message.
diff --git a/third_party/ipcz/src/ipcz/remote_router_link.cc b/third_party/ipcz/src/ipcz/remote_router_link.cc
index ec47513..6fba386 100644
--- a/third_party/ipcz/src/ipcz/remote_router_link.cc
+++ b/third_party/ipcz/src/ipcz/remote_router_link.cc
@@ -9,6 +9,7 @@
 
 #include "ipcz/box.h"
 #include "ipcz/node_link.h"
+#include "ipcz/node_link_memory.h"
 #include "ipcz/node_messages.h"
 #include "ipcz/portal.h"
 #include "ipcz/router.h"
@@ -24,9 +25,11 @@
     : node_link_(std::move(node_link)),
       sublink_(sublink),
       type_(type),
-      side_(side),
-      link_state_(std::move(link_state)) {
-  ABSL_ASSERT(link_state_.is_null() || link_state_.is_addressable());
+      side_(side) {
+  ABSL_ASSERT(type.is_central() || link_state.is_null());
+  if (type.is_central()) {
+    SetLinkState(std::move(link_state));
+  }
 }
 
 RemoteRouterLink::~RemoteRouterLink() = default;
@@ -42,12 +45,67 @@
                                        std::move(link_state), type, side));
 }
 
+void RemoteRouterLink::SetLinkState(FragmentRef<RouterLinkState> state) {
+  ABSL_ASSERT(type_.is_central());
+  if (state.is_null()) {
+    // By convention, if a central link has no RouterLinkState at construction
+    // time, side A is responsible for allocating a new one and sharing it with
+    // side B eventually. Side B lives with a null RouterLinkState until then.
+    if (side_.is_side_a()) {
+      AllocateAndShareLinkState();
+    }
+    return;
+  }
+
+  if (state.is_pending()) {
+    // By convention, side A should never be given a pending RouterLinkState
+    // fragment.
+    ABSL_ASSERT(side_.is_side_b());
+
+    // Side B on the other hand may obtain a RouterLinkState fragment which it
+    // can't address yet, and in this case, we wait for the fragment's buffer to
+    // be mapped locally.
+    Ref<NodeLinkMemory> memory = WrapRefCounted(&node_link()->memory());
+    FragmentDescriptor descriptor = state.fragment().descriptor();
+    memory->WaitForBufferAsync(
+        descriptor.buffer_id(),
+        [self = WrapRefCounted(this), memory, descriptor] {
+          auto fragment = memory->GetFragment(descriptor);
+          self->SetLinkState(
+              memory->AdoptFragmentRef<RouterLinkState>(fragment));
+        });
+    return;
+  }
+
+  ABSL_ASSERT(state.is_addressable());
+
+  // SetLinkState() must be called with an addressable fragment only once.
+  ABSL_ASSERT(link_state_.load(std::memory_order_acquire) == nullptr);
+
+  // The release when storing `link_state_` is balanced by an acquire in
+  // GetLinkState().
+  link_state_fragment_ = std::move(state);
+  link_state_.store(link_state_fragment_.get(), std::memory_order_release);
+
+  // If this side of the link was already marked stable before the
+  // RouterLinkState was available, `side_is_stable_` will be true. In that
+  // case, set the stable bit in RouterLinkState immediately. This may unblock
+  // some routing work. The acquire here is balanced by a release in
+  // MarkSideStable().
+  if (side_is_stable_.load(std::memory_order_acquire)) {
+    MarkSideStable();
+  }
+  if (Ref<Router> router = node_link()->GetRouter(sublink_)) {
+    router->Flush();
+  }
+}
+
 LinkType RemoteRouterLink::GetType() const {
   return type_;
 }
 
 RouterLinkState* RemoteRouterLink::GetLinkState() const {
-  return link_state_.get();
+  return link_state_.load(std::memory_order_acquire);
 }
 
 bool RemoteRouterLink::HasLocalPeer(const Router& router) {
@@ -233,4 +291,21 @@
   return ss.str();
 }
 
+void RemoteRouterLink::AllocateAndShareLinkState() {
+  node_link()->memory().AllocateRouterLinkState(
+      [self = WrapRefCounted(this)](FragmentRef<RouterLinkState> state) {
+        if (state.is_null()) {
+          DLOG(ERROR) << "Unable to allocate RouterLinkState.";
+          return;
+        }
+        ABSL_ASSERT(state.is_addressable());
+        self->SetLinkState(state);
+
+        msg::SetRouterLinkState set;
+        set.params().sublink = self->sublink();
+        set.params().descriptor = state.release().descriptor();
+        self->node_link()->Transmit(set);
+      });
+}
+
 }  // namespace ipcz
diff --git a/third_party/ipcz/src/ipcz/remote_router_link.h b/third_party/ipcz/src/ipcz/remote_router_link.h
index 9967fa56..a5c3aeb 100644
--- a/third_party/ipcz/src/ipcz/remote_router_link.h
+++ b/third_party/ipcz/src/ipcz/remote_router_link.h
@@ -48,6 +48,23 @@
   const Ref<NodeLink>& node_link() const { return node_link_; }
   SublinkId sublink() const { return sublink_; }
 
+  // Sets this link's RouterLinkState.
+  //
+  // If `state` is null and this link is on side B, this call is a no-op. If
+  // `state` is null and this link is on side A, this call will kick off an
+  // asynchronous allocation of a new RouterLinkState. When that completes, the
+  // new state will be adopted by side A and shared with side B.
+  //
+  // If `state` references a pending fragment and this link is on side A, the
+  // call is a no-op. If `state` references a pending fragment and this link
+  // is on side B, this operation will be automatically deferred until the
+  // NodeLink acquires a mapping of the buffer referenced by `state` and the
+  // fragment can be resolved to an addressable one.
+  //
+  // Finally, if `state` references a valid, addressable fragment, it is
+  // adopted as-is.
+  void SetLinkState(FragmentRef<RouterLinkState> state);
+
   // RouterLink:
   LinkType GetType() const override;
   RouterLinkState* GetLinkState() const override;
@@ -73,6 +90,8 @@
 
   ~RemoteRouterLink() override;
 
+  void AllocateAndShareLinkState();
+
   const Ref<NodeLink> node_link_;
   const SublinkId sublink_;
   const LinkType type_;
@@ -87,7 +106,19 @@
   // shared by both ends of this RouterLink. Always null for non-central links,
   // and may be null for a central links if its RouterLinkState has not yet been
   // allocated or shared.
-  FragmentRef<RouterLinkState> link_state_;
+  //
+  // Must be set at most once and is only retained by this object to keep the
+  // fragment allocated. Access is unguarded and is restricted to
+  // SetLinkState(), and only allowed while `link_state_` below is still null.
+  // Any other access is unsafe. Use GetLinkState() to get a usable reference to
+  // the RouterLinkState instance.
+  FragmentRef<RouterLinkState> link_state_fragment_;
+
+  // Cached address of the shared RouterLinkState referenced by
+  // `link_state_fragment_`. Once this is set to a non-null value it retains
+  // that value indefinitely, so any non-null value loaded from this field is
+  // safe to dereference for the duration of the RemoteRouterLink's lifetime.
+  std::atomic<RouterLinkState*> link_state_{nullptr};
 };
 
 }  // namespace ipcz
diff --git a/third_party/ipcz/src/ipcz/router_link_test.cc b/third_party/ipcz/src/ipcz/router_link_test.cc
index 3790412..38057ba 100644
--- a/third_party/ipcz/src/ipcz/router_link_test.cc
+++ b/third_party/ipcz/src/ipcz/router_link_test.cc
@@ -5,7 +5,10 @@
 #include "ipcz/router_link.h"
 
 #include <tuple>
+#include <utility>
+#include <vector>
 
+#include "ipcz/driver_memory.h"
 #include "ipcz/driver_transport.h"
 #include "ipcz/ipcz.h"
 #include "ipcz/link_side.h"
@@ -36,6 +39,87 @@
 constexpr NodeName kTestPeer1Name(3, 4);
 constexpr NodeName kTestPeer2Name(4, 5);
 
+// A helper for the tests in this module, TestNodePair creates one broker node
+// and one non-broker node and interconnects them using the synchronous
+// reference driver. This class exposes the NodeLinkMemory on either end of the
+// connection and provides some additional facilities which tests can use to
+// poke at node and router state.
+class TestNodePair {
+ public:
+  TestNodePair() {
+    auto transports = DriverTransport::CreatePair(kTestDriver);
+    auto alloc = NodeLinkMemory::Allocate(node_a_);
+    node_link_a_ =
+        NodeLink::Create(node_a_, LinkSide::kA, kTestBrokerName,
+                         kTestNonBrokerName, Node::Type::kNormal, 0,
+                         transports.first, std::move(alloc.node_link_memory));
+    node_link_b_ = NodeLink::Create(
+        node_b_, LinkSide::kB, kTestNonBrokerName, kTestBrokerName,
+        Node::Type::kBroker, 0, transports.second,
+        NodeLinkMemory::Adopt(node_b_, std::move(alloc.primary_buffer_memory)));
+    node_a_->AddLink(kTestNonBrokerName, node_link_a_);
+    node_b_->AddLink(kTestBrokerName, node_link_b_);
+  }
+
+  ~TestNodePair() {
+    node_b_->Close();
+    node_a_->Close();
+  }
+
+  NodeLinkMemory& memory_a() const { return node_link_a_->memory(); }
+  NodeLinkMemory& memory_b() const { return node_link_b_->memory(); }
+
+  // Activates both of the test nodes' NodeLink transports. Tests can defer this
+  // activation as a means of deferring NodeLink communications in general.
+  void ActivateTransports() {
+    node_link_a_->transport()->Activate();
+    node_link_b_->transport()->Activate();
+  }
+
+  // Establishes new RemoteRouterLinks between `a` and `b`. Different initial
+  // RouterLinkState references may be provided for the link on either side in
+  // order to mimic various production scenarios.
+  RouterLink::Pair LinkRemoteRouters(Ref<Router> a,
+                                     FragmentRef<RouterLinkState> a_state,
+                                     Ref<Router> b,
+                                     FragmentRef<RouterLinkState> b_state) {
+    const SublinkId sublink = node_link_a_->memory().AllocateSublinkIds(1);
+    Ref<RemoteRouterLink> a_link = node_link_a_->AddRemoteRouterLink(
+        sublink, std::move(a_state), LinkType::kCentral, LinkSide::kA, a);
+    Ref<RemoteRouterLink> b_link = node_link_b_->AddRemoteRouterLink(
+        sublink, std::move(b_state), LinkType::kCentral, LinkSide::kB, b);
+    a->SetOutwardLink(a_link);
+    b->SetOutwardLink(b_link);
+    return {a_link, b_link};
+  }
+
+  // Depletes the available supply of RouterLinkState fragments and returns
+  // references to all of them. Note that one side effect of this call is that
+  // memory_a() will expand its RouterLinkState fragment capacity, so subsequent
+  // allocation requests will still succeed.
+  std::vector<FragmentRef<RouterLinkState>> AllocateAllRouterLinkStates() {
+    std::vector<FragmentRef<RouterLinkState>> fragments;
+    for (;;) {
+      FragmentRef<RouterLinkState> fragment =
+          memory_a().TryAllocateRouterLinkState();
+      if (fragment.is_null()) {
+        return fragments;
+      }
+      fragments.push_back(std::move(fragment));
+    }
+  }
+
+ private:
+  const Ref<Node> node_a_{MakeRefCounted<Node>(Node::Type::kBroker,
+                                               kTestDriver,
+                                               IPCZ_INVALID_DRIVER_HANDLE)};
+  const Ref<Node> node_b_{MakeRefCounted<Node>(Node::Type::kNormal,
+                                               kTestDriver,
+                                               IPCZ_INVALID_DRIVER_HANDLE)};
+  Ref<NodeLink> node_link_a_;
+  Ref<NodeLink> node_link_b_;
+};
+
 class RouterLinkTest : public testing::Test,
                        public testing::WithParamInterface<RouterLinkTestMode> {
  public:
@@ -47,48 +131,24 @@
         break;
 
       case RouterLinkTestMode::kRemote: {
-        auto transports = DriverTransport::CreatePair(kTestDriver);
-        auto alloc = NodeLinkMemory::Allocate(broker_);
-        broker_node_link_ = NodeLink::Create(
-            broker_, LinkSide::kA, kTestBrokerName, kTestNonBrokerName,
-            Node::Type::kNormal, 0, transports.first,
-            std::move(alloc.node_link_memory));
-        non_broker_node_link_ = NodeLink::Create(
-            non_broker_, LinkSide::kB, kTestNonBrokerName, kTestBrokerName,
-            Node::Type::kBroker, 0, transports.second,
-            NodeLinkMemory::Adopt(non_broker_,
-                                  std::move(alloc.primary_buffer_memory)));
-        broker_->AddLink(kTestNonBrokerName, broker_node_link_);
-        non_broker_->AddLink(kTestBrokerName, non_broker_node_link_);
-
-        auto fragment = broker_node_link_->memory().AllocateFragment(
-            sizeof(RouterLinkState));
-        auto link_state = FragmentRef<RouterLinkState>(
-            RefCountedFragment::kAdoptExistingRef,
-            WrapRefCounted(&broker_node_link_->memory()), fragment);
-        RouterLinkState::Initialize(link_state.get());
-        a_link_ = broker_node_link_->AddRemoteRouterLink(
-            SublinkId{0}, link_state, LinkType::kCentral, LinkSide::kA, a_);
-        b_link_ = non_broker_node_link_->AddRemoteRouterLink(
-            SublinkId{0}, link_state, LinkType::kCentral, LinkSide::kB, b_);
-        a_->SetOutwardLink(a_link_);
-        b_->SetOutwardLink(b_link_);
-
-        broker_node_link_->transport()->Activate();
-        non_broker_node_link_->transport()->Activate();
+        auto link_state = nodes_.memory_a().TryAllocateRouterLinkState();
+        ABSL_ASSERT(link_state.is_addressable());
+        link_state_ = link_state.get();
+        std::tie(a_link_, b_link_) =
+            nodes_.LinkRemoteRouters(a_, link_state, b_, link_state);
         break;
       }
     }
 
     ASSERT_EQ(a_link_->GetLinkState(), b_link_->GetLinkState());
     link_state_ = a_link_->GetLinkState();
+
+    nodes_.ActivateTransports();
   }
 
   void TearDown() override {
     a_->CloseRoute();
     b_->CloseRoute();
-    broker_->Close();
-    non_broker_->Close();
   }
 
   Router& a() { return *a_; }
@@ -99,15 +159,7 @@
   RouterLinkState::Status link_status() { return link_state_->status; }
 
  private:
-  const Ref<Node> broker_{MakeRefCounted<Node>(Node::Type::kBroker,
-                                               kTestDriver,
-                                               IPCZ_INVALID_DRIVER_HANDLE)};
-  const Ref<Node> non_broker_{MakeRefCounted<Node>(Node::Type::kNormal,
-                                                   kTestDriver,
-                                                   IPCZ_INVALID_DRIVER_HANDLE)};
-  Ref<NodeLink> broker_node_link_;
-  Ref<NodeLink> non_broker_node_link_;
-
+  TestNodePair nodes_;
   const Ref<Router> a_{MakeRefCounted<Router>()};
   const Ref<Router> b_{MakeRefCounted<Router>()};
   Ref<RouterLink> a_link_;
@@ -186,6 +238,149 @@
   EXPECT_EQ(RouterLinkState::kStable, link_status());
 }
 
+class RemoteRouterLinkTest : public testing::Test {
+ public:
+  TestNodePair& nodes() { return nodes_; }
+
+  std::vector<Router::Pair> CreateTestRouterPairs(size_t n) {
+    std::vector<Router::Pair> pairs;
+    pairs.reserve(n);
+    for (size_t i = 0; i < n; ++i) {
+      pairs.emplace_back(MakeRefCounted<Router>(), MakeRefCounted<Router>());
+    }
+    return pairs;
+  }
+
+  void CloseRoutes(const std::vector<Router::Pair>& routers) {
+    for (const auto& pair : routers) {
+      pair.first->CloseRoute();
+      pair.second->CloseRoute();
+    }
+  }
+
+  BufferId GenerateBufferId() {
+    return nodes().memory_a().AllocateNewBufferId();
+  }
+
+ private:
+  TestNodePair nodes_;
+};
+
+TEST_F(RemoteRouterLinkTest, NewLinkWithAddressableState) {
+  nodes().ActivateTransports();
+
+  std::vector<FragmentRef<RouterLinkState>> fragments =
+      nodes().AllocateAllRouterLinkStates();
+  std::vector<Router::Pair> router_pairs =
+      CreateTestRouterPairs(fragments.size());
+  std::vector<RouterLink::Pair> links;
+  for (size_t i = 0; i < fragments.size(); ++i) {
+    auto [a, b] = router_pairs[i];
+    auto [a_link, b_link] =
+        nodes().LinkRemoteRouters(a, fragments[i], b, fragments[i]);
+    a_link->MarkSideStable();
+    b_link->MarkSideStable();
+    links.emplace_back(std::move(a_link), std::move(b_link));
+  }
+
+  // We should be able to lock all links from either side, implying that both
+  // sides have a valid reference to the same RouterLinkState.
+  for (const auto& [a_link, b_link] : links) {
+    EXPECT_TRUE(a_link->TryLockForClosure());
+    a_link->Unlock();
+    EXPECT_TRUE(b_link->TryLockForClosure());
+  }
+
+  CloseRoutes(router_pairs);
+}
+
+TEST_F(RemoteRouterLinkTest, NewLinkWithPendingState) {
+  // Occupy all fragments in the primary buffer so they aren't usable.
+  std::vector<FragmentRef<RouterLinkState>> unused_fragments =
+      nodes().AllocateAllRouterLinkStates();
+
+  // Now allocate another batch of fragments which must be in a newly allocated
+  // buffer on node A. Because the nodes' transports are not active yet, there
+  // is no way for node B to have had this buffer shared with it yet. Hence all
+  // of these fragments will be seen as pending on node B.
+  std::vector<FragmentRef<RouterLinkState>> fragments =
+      nodes().AllocateAllRouterLinkStates();
+
+  std::vector<Router::Pair> router_pairs =
+      CreateTestRouterPairs(fragments.size());
+  std::vector<RouterLink::Pair> links;
+  for (size_t i = 0; i < fragments.size(); ++i) {
+    auto [a, b] = router_pairs[i];
+    auto a_state = fragments[i];
+    auto b_fragment =
+        nodes().memory_b().GetFragment(fragments[i].fragment().descriptor());
+    auto b_state =
+        nodes().memory_b().AdoptFragmentRef<RouterLinkState>(b_fragment);
+    ASSERT_TRUE(a_state.is_addressable());
+    ASSERT_TRUE(b_state.is_pending());
+    auto [a_link, b_link] =
+        nodes().LinkRemoteRouters(a, std::move(a_state), b, std::move(b_state));
+    a_link->MarkSideStable();
+    b_link->MarkSideStable();
+    links.emplace_back(std::move(a_link), std::move(b_link));
+  }
+
+  // Because side B of these links still cannot resolve its RouterLinkState,
+  // the link still cannot be stabilized or locked yet.
+  for (const auto& [a_link, b_link] : links) {
+    EXPECT_FALSE(a_link->TryLockForClosure());
+    EXPECT_FALSE(b_link->TryLockForClosure());
+  }
+
+  // We're using the synchronous driver, so as soon as we activate our
+  // transports, all pending NodeLink communications will complete before this
+  // call returns. This also means side B of each link will resolve its
+  // RouterLinkState.
+  nodes().ActivateTransports();
+
+  // Now all links should be lockable from either side, implying that both
+  // sides have a valid reference to the same RouterLinkState.
+  for (const auto& [a_link, b_link] : links) {
+    EXPECT_TRUE(a_link->TryLockForClosure());
+    a_link->Unlock();
+    EXPECT_TRUE(b_link->TryLockForClosure());
+  }
+
+  CloseRoutes(router_pairs);
+}
+
+TEST_F(RemoteRouterLinkTest, NewLinkWithNullState) {
+  // Occupy all fragments in the primary buffer so they aren't usable.
+  std::vector<FragmentRef<RouterLinkState>> unused_fragments =
+      nodes().AllocateAllRouterLinkStates();
+
+  constexpr size_t kNumIterations = 15000;
+  std::vector<Router::Pair> router_pairs =
+      CreateTestRouterPairs(kNumIterations);
+  std::vector<RouterLink::Pair> links;
+  for (size_t i = 0; i < kNumIterations; ++i) {
+    auto [a, b] = router_pairs[i];
+    auto [a_link, b_link] = nodes().LinkRemoteRouters(a, nullptr, b, nullptr);
+    a_link->MarkSideStable();
+    b_link->MarkSideStable();
+    EXPECT_FALSE(a_link->TryLockForClosure());
+    EXPECT_FALSE(b_link->TryLockForClosure());
+    links.emplace_back(std::move(a_link), std::move(b_link));
+  }
+
+  // Since we're using the synchronous driver, by the time transport activation
+  // completes, side A of each link must have already dynamically allocated a
+  // RouterLinkState and shared it with side B.
+  nodes().ActivateTransports();
+  for (const auto& [a_link, b_link] : links) {
+    EXPECT_TRUE(a_link->TryLockForClosure());
+    a_link->Unlock();
+    EXPECT_TRUE(b_link->TryLockForClosure());
+  }
+
+  CloseRoutes(router_pairs);
+}
+
 INSTANTIATE_TEST_SUITE_P(,
                          RouterLinkTest,
                          ::testing::Values(RouterLinkTestMode::kLocal,
diff --git a/third_party/sqlite/README.chromium b/third_party/sqlite/README.chromium
index a12bb2a..e9fd7ca 100644
--- a/third_party/sqlite/README.chromium
+++ b/third_party/sqlite/README.chromium
@@ -1,7 +1,7 @@
 Name: sqlite
 URL: https://sqlite.org/
-Version: 3.39.0
-CPEPrefix: cpe:/a:sqlite:sqlite:3.39.0
+Version: 3.39.1
+CPEPrefix: cpe:/a:sqlite:sqlite:3.39.1
 Included In Release: Yes
 Security Critical: Yes
 License: Public domain
diff --git a/third_party/tflite/README.chromium b/third_party/tflite/README.chromium
index fc341ceff..272b6184 100644
--- a/third_party/tflite/README.chromium
+++ b/third_party/tflite/README.chromium
@@ -1,8 +1,8 @@
 Name: TensorFlow Lite
 Short Name: tflite
 URL: https://github.com/tensorflow/tensorflow
-Version: 77b5b086f95d3d20b90704ead451923f9dad0014
-Date: 2022/07/06
+Version: ca77d58bb625ee21bc33660c074c624659b8a3c2
+Date: 2022/07/14
 License: Apache 2.0
 License File: LICENSE
 Security Critical: Yes
diff --git a/tools/gritsettings/resource_ids.spec b/tools/gritsettings/resource_ids.spec
index 84e9b4d3..9a8d321 100644
--- a/tools/gritsettings/resource_ids.spec
+++ b/tools/gritsettings/resource_ids.spec
@@ -108,7 +108,7 @@
     "META": {"sizes": {"includes": [5],}},
     "includes": [1280],
   },
-  "<(SHARED_INTERMEDIATE_DIR)/chrome/browser/resources/bookmarks/bookmarks_resources.grd": {
+  "<(SHARED_INTERMEDIATE_DIR)/chrome/browser/resources/bookmarks/resources.grd": {
     "META": {"sizes": {"includes": [50],}},
     "includes": [1300],
   },
@@ -184,15 +184,15 @@
     "META": {"sizes": {"includes": [20],}},
     "includes": [1585],
   },
-  "<(SHARED_INTERMEDIATE_DIR)/chrome/browser/resources/downloads/downloads_resources.grd": {
+  "<(SHARED_INTERMEDIATE_DIR)/chrome/browser/resources/downloads/resources.grd": {
     "META": {"sizes": {"includes": [50],}},
     "includes": [1590],
   },
-  "<(SHARED_INTERMEDIATE_DIR)/chrome/browser/resources/extensions/extensions_resources.grd": {
+  "<(SHARED_INTERMEDIATE_DIR)/chrome/browser/resources/extensions/resources.grd": {
     "META": {"sizes": {"includes": [80],}},
     "includes": [1600],
   },
-  "<(SHARED_INTERMEDIATE_DIR)/chrome/browser/resources/history/history_resources.grd": {
+  "<(SHARED_INTERMEDIATE_DIR)/chrome/browser/resources/history/resources.grd": {
     "META": {"sizes": {"includes": [40]}},
     "includes": [1620],
   },
@@ -255,7 +255,7 @@
     "META": {"sizes": {"includes": [200]}},
     "includes": [1880],
   },
-  "<(SHARED_INTERMEDIATE_DIR)/chrome/browser/resources/print_preview/print_preview_resources.grd": {
+  "<(SHARED_INTERMEDIATE_DIR)/chrome/browser/resources/print_preview/resources.grd": {
     "META": {"sizes": {"includes": [500],}},
     "includes": [1900],
   },
@@ -303,7 +303,7 @@
     "META": {"sizes": {"includes": [20]}},
     "includes": [2040],
   },
-  "<(SHARED_INTERMEDIATE_DIR)/chrome/browser/resources/welcome/welcome_resources.grd": {
+  "<(SHARED_INTERMEDIATE_DIR)/chrome/browser/resources/welcome/resources.grd": {
     "META": {"sizes": {"includes": [30]}},
     "includes": [2060],
   },
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index e3328e3..a7ac49d 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -393,6 +393,7 @@
       'chromeos-amd64-generic-rel (reclient compare)': 'chromeos_amd64-generic-vm_use_fake_dbus_clients_reclient',
       'chromeos-amd64-generic-rel (reclient)': 'chromeos_amd64-generic-vm_use_fake_dbus_clients_reclient',
       'fuchsia-code-coverage': 'fuchsia_clang_code_coverage',
+      'fuchsia-fyi-arm64-cfv2-script': 'release_bot_fuchsia_cfv2_script_arm64',
       'fuchsia-fyi-arm64-emu-arg': 'release_trybot_fuchsia_arm64',
       'fuchsia-fyi-arm64-rel': 'release_bot_fuchsia_arm64',
       'fuchsia-fyi-cfv2-script': 'release_bot_fuchsia_cfv2_script',
@@ -3350,6 +3351,10 @@
       'release_bot', 'fuchsia', 'fuchsia_cfv2_script',
     ],
 
+    'release_bot_fuchsia_cfv2_script_arm64': [
+      'release_bot', 'fuchsia', 'arm64', 'arm64_host', 'fuchsia_cfv2_script',
+    ],
+
     'release_bot_mac_strip_minimal_symbols': [
       'release_bot', 'mac_strip', 'minimal_symbols',
     ],
diff --git a/tools/mb/mb_config_expectations/chromium.fyi.json b/tools/mb/mb_config_expectations/chromium.fyi.json
index 1a4d6eb..f78e174 100644
--- a/tools/mb/mb_config_expectations/chromium.fyi.json
+++ b/tools/mb/mb_config_expectations/chromium.fyi.json
@@ -756,6 +756,18 @@
       "use_goma": true
     }
   },
+  "fuchsia-fyi-arm64-cfv2-script": {
+    "gn_args": {
+      "dcheck_always_on": false,
+      "is_component_build": false,
+      "is_debug": false,
+      "target_cpu": "arm64",
+      "target_os": "fuchsia",
+      "test_host_cpu": "arm64",
+      "use_cfv2_script": true,
+      "use_goma": true
+    }
+  },
   "fuchsia-fyi-arm64-emu-arg": {
     "gn_args": {
       "dcheck_always_on": true,
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index 6a7d5eb..8b1694117 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -15474,6 +15474,120 @@
   <description>The Lens side panel went from hidden to showing.</description>
 </action>
 
+<action name="LensUnifiedSidePanel.HideSidePanel">
+  <owner>stanfield@google.com</owner>
+  <owner>benwgold@google.com</owner>
+  <owner>juanmojica@google.com</owner>
+  <owner>schechter@google.com</owner>
+  <owner>apalanki@google.com</owner>
+  <description>The side panel is hidden when lens was an entry.</description>
+</action>
+
+<action name="LensUnifiedSidePanel.LensEntryHidden">
+  <owner>stanfield@google.com</owner>
+  <owner>benwgold@google.com</owner>
+  <owner>juanmojica@google.com</owner>
+  <owner>schechter@google.com</owner>
+  <owner>apalanki@google.com</owner>
+  <description>
+    The lens entry was hidden in the side panel. User navigated to a different
+    entry in the side panel.
+  </description>
+</action>
+
+<action name="LensUnifiedSidePanel.LensEntryShown">
+  <owner>stanfield@google.com</owner>
+  <owner>benwgold@google.com</owner>
+  <owner>juanmojica@google.com</owner>
+  <owner>schechter@google.com</owner>
+  <owner>apalanki@google.com</owner>
+  <description>The lens entry was shown in the side panel.</description>
+</action>
+
+<action name="LensUnifiedSidePanel.LensQuery">
+  <owner>stanfield@google.com</owner>
+  <owner>benwgold@google.com</owner>
+  <owner>juanmojica@google.com</owner>
+  <owner>schechter@google.com</owner>
+  <owner>apalanki@google.com</owner>
+  <description>The user issued a Lens query.</description>
+</action>
+
+<action name="LensUnifiedSidePanel.LensQuery_Followup">
+  <owner>stanfield@google.com</owner>
+  <owner>benwgold@google.com</owner>
+  <owner>juanmojica@google.com</owner>
+  <owner>schechter@google.com</owner>
+  <owner>apalanki@google.com</owner>
+  <description>The user issued a follow Lens query.</description>
+</action>
+
+<action name="LensUnifiedSidePanel.LensQuery_New">
+  <owner>stanfield@google.com</owner>
+  <owner>benwgold@google.com</owner>
+  <owner>juanmojica@google.com</owner>
+  <owner>schechter@google.com</owner>
+  <owner>apalanki@google.com</owner>
+  <description>The user issued a new Lens query.</description>
+</action>
+
+<action name="LensUnifiedSidePanel.LensQuery_SidePanelClosed">
+  <owner>stanfield@google.com</owner>
+  <owner>benwgold@google.com</owner>
+  <owner>juanmojica@google.com</owner>
+  <owner>schechter@google.com</owner>
+  <owner>apalanki@google.com</owner>
+  <description>
+    Lens query was issued when the side panel is closed. So opening side panel
+    and focusing on lens.
+  </description>
+</action>
+
+<action name="LensUnifiedSidePanel.LensQuery_SidePanelOpenLens">
+  <owner>stanfield@google.com</owner>
+  <owner>benwgold@google.com</owner>
+  <owner>juanmojica@google.com</owner>
+  <owner>schechter@google.com</owner>
+  <owner>apalanki@google.com</owner>
+  <description>
+    Lens query was issued when the side panel is open and lens is focused.
+  </description>
+</action>
+
+<action name="LensUnifiedSidePanel.LensQuery_SidePanelOpenNonLens">
+  <owner>stanfield@google.com</owner>
+  <owner>benwgold@google.com</owner>
+  <owner>juanmojica@google.com</owner>
+  <owner>schechter@google.com</owner>
+  <owner>apalanki@google.com</owner>
+  <description>
+    Lens query was issued when the side panel is open. So focusing on the lens
+    entry in the side panel.
+  </description>
+</action>
+
+<action name="LensUnifiedSidePanel.LoadResultsInNewTab">
+  <owner>stanfield@google.com</owner>
+  <owner>benwgold@google.com</owner>
+  <owner>juanmojica@google.com</owner>
+  <owner>schechter@google.com</owner>
+  <owner>apalanki@google.com</owner>
+  <description>
+    The side panel button to expand results to new tab was clicked.
+  </description>
+</action>
+
+<action name="LensUnifiedSidePanel.ResultLinkClick">
+  <owner>stanfield@google.com</owner>
+  <owner>benwgold@google.com</owner>
+  <owner>juanmojica@google.com</owner>
+  <owner>schechter@google.com</owner>
+  <owner>apalanki@google.com</owner>
+  <description>
+    The user clicked a result link in the Lens entry in the side panel.
+  </description>
+</action>
+
 <action name="LinkNavigationOpenedInForegroundTab">
   <owner>meiliang@chromium.org</owner>
   <owner>sbirch@chromium.org</owner>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 1cc8b68b..98463d6 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -22541,6 +22541,27 @@
   <int value="4" label="Checksum out of sync"/>
 </enum>
 
+<enum name="CryptohomeCryptoError">
+  <summary>
+    The error enum defined in platform2/cryptohome/crypto_error.h
+  </summary>
+  <int value="0" label="No error"/>
+  <int value="1" label="TPM fatal error"/>
+  <int value="2" label="TPM communication failure"/>
+  <int value="3" label="TPM is in the defense mode"/>
+  <int value="4" label="TPM crypto error"/>
+  <int value="5" label="TPM state requires reboot"/>
+  <int value="6" label="Scrypt failed"/>
+  <int value="7" label="Other fatal error"/>
+  <int value="8" label="Other crypto error"/>
+  <int value="9" label="No TPM public key hash"/>
+  <int value="10"
+      label="Low Entropy (LE) credential protection is not supported"/>
+  <int value="11" label="The LE secret provided during decryption is invalid"/>
+  <int value="12" label="Flags and policy mismatch"/>
+  <int value="13" label="The LE credential had been locked"/>
+</enum>
+
 <enum name="CryptohomeDeprecatedApiCalled">
   <int value="0" label="InitializeCastKey"/>
   <int value="1" label="GetBootAttribute"/>
@@ -24583,6 +24604,13 @@
   <int value="4" label="RollbackDisallowed"/>
 </enum>
 
+<enum name="DetailedViewSection">
+  <int value="0" label="WiFi section shown"/>
+  <int value="1" label="Mobile section shown"/>
+  <int value="2" label="Ethernet section shown"/>
+  <int value="3" label="Detailed view shown"/>
+</enum>
+
 <enum name="DeviceActiveClientPsmResponse">
 <!-- This must be kept current with DeviceActivityClient::PsmResponse
         located in ash/components/device_activity/device_activity_client.h -->
@@ -58436,6 +58464,7 @@
   <int value="-159877930" label="MaterialDesignUserManager:disabled"/>
   <int value="-158938746" label="NtpRealboxPedals:enabled"/>
   <int value="-158549277" label="enable-embeddedsearch-api"/>
+  <int value="-158161384" label="SafeModeForCachedFlags:disabled"/>
   <int value="-153299083" label="NewProfilePicker:disabled"/>
   <int value="-152677714" label="AsmJsToWebAssembly:enabled"/>
   <int value="-152632720" label="RuntimeHostPermissions:enabled"/>
@@ -58703,7 +58732,6 @@
   <int value="7444737" label="NTPSuggestionsStandaloneUI:disabled"/>
   <int value="7533886" label="disable-offer-store-unmasked-wallet-cards"/>
   <int value="7642326" label="hdrnet-override"/>
-  <int value="7804608" label="Force60Hz:enabled"/>
   <int value="8891567" label="CaptionSettings:enabled"/>
   <int value="9609535" label="BrowsingDataLifetimeManager:disabled"/>
   <int value="10405060" label="BluetoothSessionizedMetrics:disabled"/>
@@ -59058,6 +59086,7 @@
   <int value="255375615" label="stop-non-timers-in-background:enabled"/>
   <int value="255765456"
       label="ActivateMetricsReportingEnabledPolicyAndroid:disabled"/>
+  <int value="255806443" label="GetDisplayMediaSet:disabled"/>
   <int value="256753680" label="ChromeKioskEnableLacros:disabled"/>
   <int value="257206347" label="GuestOSGenericInstaller:disabled"/>
   <int value="258621334"
@@ -59494,6 +59523,7 @@
   <int value="517568645" label="AnimatedAppMenuIcon:disabled"/>
   <int value="518419320" label="RemoteCopyProgressNotification:disabled"/>
   <int value="519140642" label="SendWebUIJavaScriptErrorReports:enabled"/>
+  <int value="519623921" label="SafeModeForCachedFlags:enabled"/>
   <int value="520738365" label="OmniboxPedalsBatch2NonEnglish:enabled"/>
   <int value="520982116" label="BuiltInModuleAll:enabled"/>
   <int value="521992655" label="LauncherPlayStoreSearch:disabled"/>
@@ -59568,6 +59598,7 @@
   <int value="575441361" label="crostini-container-install-version"/>
   <int value="575829120" label="shelf-hotseat"/>
   <int value="576701073" label="WebPaymentsJustInTimePaymentApp:disabled"/>
+  <int value="576730168" label="GetDisplayMediaSet:enabled"/>
   <int value="576878329" label="enable-background-blur"/>
   <int value="578695119" label="ImprovedCookieControls:disabled"/>
   <int value="578894559" label="IPH_Snooze:enabled"/>
@@ -59919,6 +59950,8 @@
   <int value="788130042" label="PrivacySandboxSettings2:enabled"/>
   <int value="790411499" label="PageInfoStoreInfo:disabled"/>
   <int value="791541863" label="NtpPhotosModule:disabled"/>
+  <int value="791979491"
+      label="GetDisplayMediaSetAutoSelectAllScreens:disabled"/>
   <int value="792307132"
       label="KeyboardBasedDisplayArrangementInSettings:enabled"/>
   <int value="792884862" label="EnableSharedImageForWebview:enabled"/>
@@ -61188,7 +61221,6 @@
       label="AutofillAlwaysReturnCloudTokenizedCard:enabled"/>
   <int value="1634897915" label="password-import-export:enabled"/>
   <int value="1634980726" label="VrShellExperimentalRendering:disabled"/>
-  <int value="1635519293" label="Force60Hz:disabled"/>
   <int value="1635695770" label="CSSContainerQueries:disabled"/>
   <int value="1635755615" label="ScanAppMultiPageScan:disabled"/>
   <int value="1635986352" label="ContextMenuSearchWithGoogleLens:enabled"/>
@@ -61727,6 +61759,8 @@
   <int value="1980011075" label="debug-packed-apps"/>
   <int value="1980648371" label="PointerEventV1SpecCapturing:enabled"/>
   <int value="1985584246" label="PageInfoHistory:enabled"/>
+  <int value="1985952454"
+      label="GetDisplayMediaSetAutoSelectAllScreens:enabled"/>
   <int value="1986031201" label="SwipingFromLeftEdgeToGoBack:disabled"/>
   <int value="1988099368" label="DropInputEventsBeforeFirstPaint:disabled"/>
   <int value="1988506961" label="EnableManualSaving:enabled"/>
diff --git a/tools/metrics/histograms/metadata/autofill/histograms.xml b/tools/metrics/histograms/metadata/autofill/histograms.xml
index 9eed5c3..2e95cc8 100644
--- a/tools/metrics/histograms/metadata/autofill/histograms.xml
+++ b/tools/metrics/histograms/metadata/autofill/histograms.xml
@@ -108,6 +108,8 @@
 </variants>
 
 <variants name="Autofill.ProgressDialog.FlowType">
+  <variant name="AndroidFIDO"
+      summary="Progress dialog while authenticating with FIDO on Android"/>
   <variant name="CardUnmask"
       summary="Progress dialog for the VCN Card Unmask Flow"/>
 </variants>
diff --git a/tools/metrics/histograms/metadata/bluetooth/histograms.xml b/tools/metrics/histograms/metadata/bluetooth/histograms.xml
index 738245e0..71440d9 100644
--- a/tools/metrics/histograms/metadata/bluetooth/histograms.xml
+++ b/tools/metrics/histograms/metadata/bluetooth/histograms.xml
@@ -864,6 +864,59 @@
   </summary>
 </histogram>
 
+<histogram
+    name="Bluetooth.ChromeOS.FastPair.SavedDevices.GetSavedDevices.Result"
+    enum="BooleanSuccess" expires_after="2022-09-20">
+  <owner>julietlevesque@google.com</owner>
+  <owner>chromeos-cross-device-eng@google.com</owner>
+  <summary>
+    Records the success or failure of fetching a user's list of Saved Devices
+    and opt-in status from the Footprints server. The fetching is initiated when
+    a user opens the Saved Device Bluetooth sub-age. This metric is emitted
+    following the fetching data attempt in the FastPairRepository.
+  </summary>
+</histogram>
+
+<histogram name="Bluetooth.ChromeOS.FastPair.SavedDevices.Remove.Result"
+    enum="BooleanSuccess" expires_after="2022-09-20">
+  <owner>julietlevesque@google.com</owner>
+  <owner>chromeos-cross-device-eng@google.com</owner>
+  <summary>
+    Records the success or failure of removing a Fast Pair device from the
+    Footprints server. Emitted each time a remove attempt completes following
+    user action to &quot;remove&quot; in the Saved Devices Bluetooth Settings
+    sub-page.
+  </summary>
+</histogram>
+
+<histogram
+    name="Bluetooth.ChromeOS.FastPair.SavedDevices.UpdateOptInStatus.Result.{FastPairPairingProtocol}"
+    enum="BooleanSuccess" expires_after="2022-09-20">
+  <owner>julietlevesque@google.com</owner>
+  <owner>chromeos-cross-device-eng@google.com</owner>
+  <summary>
+    Records the success or failure of updating a user's opt-in status to
+    STATUS_OPTED_IN. Emitted each time an update attempt completes following
+    user pairing via a Fast Pair protocol: initial, subsequent, or retroactive.
+  </summary>
+  <token key="FastPairPairingProtocol">
+    <variant name="InitialPairingProtocol"
+        summary="the first time a user pairs a device via Fast Pair, device
+                 needs to be in pairing mode, account key written to the
+                 device to save it to their account, discovered via BLE
+                 scanning"/>
+    <variant name="RetroactivePairingProtocol"
+        summary="user elects to save a device paired via classic Bluetooth
+                 settings to their Google account, account key written to
+                 device"/>
+    <variant name="SubsequentPairingProtocol"
+        summary="device is already saved to the user's account and is
+                 discovered via not-discoverable advertisement, which means
+                 the device does not have to be in pairing mode, no account
+                 key saved to device, discovered via BLE scanning"/>
+  </token>
+</histogram>
+
 <histogram name="Bluetooth.ChromeOS.FastPair.Scanner.StartSession.Result"
     enum="BooleanSuccess" expires_after="2022-09-20">
   <owner>shanefitz@google.com</owner>
@@ -978,7 +1031,7 @@
 
 <histogram
     name="Bluetooth.ChromeOS.Pairing.Result.FailureReason{BluetoothTransportTypes}"
-    enum="BluetoothConnectionFailureReason" expires_after="2022-08-01">
+    enum="BluetoothConnectionFailureReason" expires_after="2023-06-01">
   <owner>khorimoto@chromium.org</owner>
   <owner>cros-connectivity@google.com</owner>
   <summary>
@@ -995,7 +1048,7 @@
 </histogram>
 
 <histogram name="Bluetooth.ChromeOS.Pairing.Result{BluetoothTransportTypes}"
-    enum="BooleanSuccess" expires_after="2022-08-01">
+    enum="BooleanSuccess" expires_after="2023-06-01">
   <owner>khorimoto@chromium.org</owner>
   <owner>cros-connectivity@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/chromeos/histograms.xml b/tools/metrics/histograms/metadata/chromeos/histograms.xml
index 19d9dbb..0ff3e16 100644
--- a/tools/metrics/histograms/metadata/chromeos/histograms.xml
+++ b/tools/metrics/histograms/metadata/chromeos/histograms.xml
@@ -1686,6 +1686,19 @@
   </summary>
 </histogram>
 
+<histogram name="ChromeOS.SystemTray.Network.SectionShown"
+    enum="DetailedViewSection" expires_after="2023-06-20">
+  <owner>khorimoto@chromium.org</owner>
+  <owner>tjohnsonkanu@chromium.org</owner>
+  <owner>chadduffin@chromium.org</owner>
+  <owner>cros-connectivity@google.com</owner>
+  <summary>
+    Records when different sections within the detailed network view in the
+    system tray shown. This metric is emitted each time a user opens the system
+    tray and each time a different network section is newly shown.
+  </summary>
+</histogram>
+
 <histogram name="ChromeOS.SystemTray.NotificationsRemovedByClearAll"
     units="notifications" expires_after="2023-04-01">
   <owner>amehfooz@chromium.org</owner>
@@ -1832,6 +1845,50 @@
   <summary>Timing and number of USB devices attached.</summary>
 </histogram>
 
+<histogram base="true"
+    name="ChromeOS.USB.ExternalDeviceAttached.{USBDeviceRecognized}.{USBDeviceClass}"
+    enum="ChromeOSUsbEventTiming" expires_after="2022-12-31">
+  <owner>wonchung@google.com</owner>
+  <owner>allenwebb@chromium.org</owner>
+  <owner>chromeos-usb@google.com</owner>
+  <summary>
+    Timing and number of external USB devices attached. This is emitted each
+    time an external device is attached via USB and when user logs in with an
+    external device attached.
+  </summary>
+  <token key="USBDeviceRecognized">
+    <variant name="Recognized"
+        summary="A device that was already added to the list of trusted
+                 devices"/>
+    <variant name="Unrecognized"
+        summary="A device that was not in the list of trusted devices yet"/>
+  </token>
+  <token key="USBDeviceClass">
+    <variant name="App"
+        summary="A device with only the application specific interface class"/>
+    <variant name="Audio" summary="An audio device"/>
+    <variant name="AV" summary="An audio/video device"/>
+    <variant name="Card" summary="A smart card reader"/>
+    <variant name="Comm" summary="A communication device"/>
+    <variant name="Health" summary="A personal healthcare device"/>
+    <variant name="HID" summary="A human-interface device"/>
+    <variant name="Hub" summary="A USB hub"/>
+    <variant name="Image" summary="A still image capture device"/>
+    <variant name="Misc"
+        summary="A device with only the misc. interface class"/>
+    <variant name="Other"
+        summary="All devices not covered by the other groups"/>
+    <variant name="Phys" summary="A physical device"/>
+    <variant name="Print" summary="A printer"/>
+    <variant name="Sec" summary="A security device (e.g. fingerprint reader)"/>
+    <variant name="Storage" summary="A storage device"/>
+    <variant name="Vendor"
+        summary="A device with only the vendor specific interface class"/>
+    <variant name="Video" summary="A video device"/>
+    <variant name="Wireless" summary="A wireless controller device"/>
+  </token>
+</histogram>
+
 <histogram name="ChromeOS.UserTypeByDeviceType.LogSegment"
     enum="UserDeviceMatrix" expires_after="2023-04-24">
   <owner>bmalcolm@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/cryptohome/histograms.xml b/tools/metrics/histograms/metadata/cryptohome/histograms.xml
index 1b71681..12358f15 100644
--- a/tools/metrics/histograms/metadata/cryptohome/histograms.xml
+++ b/tools/metrics/histograms/metadata/cryptohome/histograms.xml
@@ -22,6 +22,18 @@
 
 <histograms>
 
+<variants name="CryptohomeAuthBlockTypes">
+  <variant name="ChallengeCredential"/>
+  <variant name="CryptohomeRecovery"/>
+  <variant name="DoubleWrappedCompat"/>
+  <variant name="LibScryptCompat"/>
+  <variant name="PinWeaver"/>
+  <variant name="Scrypt"/>
+  <variant name="TpmBoundToPcr"/>
+  <variant name="TpmEcc"/>
+  <variant name="TpmNotBoundToPcr"/>
+</variants>
+
 <histogram name="Cryptohome.AsyncDBusRequest" units="ms"
     expires_after="2022-01-02">
   <owner>zuan@chromium.org</owner>
@@ -902,6 +914,31 @@
   </token>
 </histogram>
 
+<histogram name="Cryptohome.{AuthBlockType}.CredentialRevocationResult"
+    enum="CryptohomeLECredError" expires_after="2023-01-01">
+  <owner>anastasiian@chromium.org</owner>
+  <owner>cros-lurs@google.com</owner>
+  <owner>cros-hwsec+uma@chromium.org</owner>
+  <summary>
+    Records the result of credentials revocation Revoke() method, which removes
+    the credential label from PinWeaver. Reported when an auth factor which uses
+    credentials revocation is deleted.
+  </summary>
+  <token key="AuthBlockType" variants="CryptohomeAuthBlockTypes"/>
+</histogram>
+
+<histogram name="Cryptohome.{AuthBlockType}.PrepareForRemovalResult"
+    enum="CryptohomeCryptoError" expires_after="2023-01-01">
+  <owner>anastasiian@chromium.org</owner>
+  <owner>cros-lurs@google.com</owner>
+  <owner>cros-hwsec+uma@chromium.org</owner>
+  <summary>
+    Records the result of PrepareForRemoval() call of an auth factor. Reported
+    when an auth factor is deleted.
+  </summary>
+  <token key="AuthBlockType" variants="CryptohomeAuthBlockTypes"/>
+</histogram>
+
 <histogram name="Cryptohome.{AuthSessionFunction}.{AuthBlockType}" units="ms"
     expires_after="2023-05-01">
   <owner>thomascedeno@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/page/histograms.xml b/tools/metrics/histograms/metadata/page/histograms.xml
index fbb6346..7cebec1 100644
--- a/tools/metrics/histograms/metadata/page/histograms.xml
+++ b/tools/metrics/histograms/metadata/page/histograms.xml
@@ -2318,12 +2318,9 @@
 </histogram>
 
 <histogram name="PageLoad.LayoutInstability.CumulativeShiftScore"
-    units="scorex10" expires_after="never">
-<!-- expires-never: guiding metric (internal: go/chrome-browser-guiding-metrics) -->
-
+    units="scorex10" expires_after="2023-07-13">
   <owner>bmcquade@chromium.org</owner>
   <owner>skobes@chromium.org</owner>
-  <owner>chrome-analysis-team@google.com</owner>
   <summary>
     Measures the cumulative layout shift score (bit.ly/lsm-explainer) that has
     occurred on the page (including all subframes). Higher values correspond to
@@ -2332,9 +2329,6 @@
     closed. Stable since M79; previous versions are expermental and subject to
     fluctuation between releases.
 
-    This histogram is of special interest to the chrome-analysis-team@. Do not
-    change its semantics or retire it without talking to them first.
-
     Bucketing for this histogram should be kept in sync with bucketing for
     PageLoad.LayoutInstability.CumulativeShiftScore.AfterBackForwardCacheRestore.
     These two histograms will be aggregated on the server to form the
diff --git a/tools/perf/chrome-health-presets.yaml b/tools/perf/chrome-health-presets.yaml
index c0b8a78e..7d1690c 100644
--- a/tools/perf/chrome-health-presets.yaml
+++ b/tools/perf/chrome-health-presets.yaml
@@ -66,7 +66,7 @@
           - win-10-perf
           - android-pixel4-perf
         stories:
-          - Jetstream2
+          - JetStream2
   chrome_health_pgo:
     telemetry_batch_experiment:
       - benchmark: speedometer2-chrome-health
@@ -126,7 +126,7 @@
           - win-10-perf-pgo
           - android-pixel4-perf
         stories:
-          - Jetstream2
+          - JetStream2
   speedometer2:
     telemetry_batch_experiment:
       - benchmark: speedometer2-chrome-health
@@ -192,7 +192,18 @@
           - win-10-perf
           - android-pixel4-perf
         stories:
-          - Jetstream2
+          - JetStream2
+  jetstream2-pgo:
+    telemetry_batch_experiment:
+      - benchmark: jetstream2
+        configs:
+          - mac-laptop_low_end-perf-pgo
+          - mac-m1_mini_2020-perf-pgo
+          - linux-perf-pgo
+          - win-10-perf-pgo
+          - android-pixel4-perf
+        stories:
+          - JetStream2
   cwv-ch:
     telemetry_batch_experiment:
       - benchmark: loading.desktop
diff --git a/tools/traffic_annotation/scripts/auditor/README.md b/tools/traffic_annotation/scripts/auditor/README.md
index 7700669..d919ce69 100644
--- a/tools/traffic_annotation/scripts/auditor/README.md
+++ b/tools/traffic_annotation/scripts/auditor/README.md
@@ -7,6 +7,20 @@
 Please see `docs/network_traffic_annotations.md` for an introduction to network
 traffic annotations.
 
+## Before Running
+
+Before running, you need to build the `traffic_annotation_proto` target, and
+pass the build path to the executable. For instance:
+
+```
+$ autoninja -C out/Debug traffic_annotation_proto
+$ vpython3 ./tools/traffic_annotation/scripts/auditor/auditor.py --build-path=out/Debug
+```
+
+`traffic_annotation_proto` is a dependency of the `chrome` target, as well. So
+if you regularly use this build directory for development, you can probably skip
+the first step.
+
 ## Usage
 
 `vpython3 ./tools/traffic_annotation/scripts/auditor/auditor.py [OPTIONS]... [path_filter]...`
@@ -19,11 +33,6 @@
 Example:
   `vpython3 ./tools/traffic_annotation/scripts/auditor/auditor.py --build-path=out/Debug`
 
-## Running
-
-Before running, you need to build the `chrome` target, and pass the build path
-to the executable.
-
 ## Safe List
 
 If there are files, paths, or specific functions that need to be exempted from
diff --git a/ui/accessibility/ax_computed_node_data.cc b/ui/accessibility/ax_computed_node_data.cc
index cbb3f6f..f08b4472 100644
--- a/ui/accessibility/ax_computed_node_data.cc
+++ b/ui/accessibility/ax_computed_node_data.cc
@@ -392,7 +392,6 @@
   if (owner_->IsLeaf() && !is_atomic_text_field_with_descendants) {
     switch (owner_->data().GetNameFrom()) {
       case ax::mojom::NameFrom::kNone:
-      case ax::mojom::NameFrom::kUninitialized:
       // The accessible name is not displayed on screen, e.g. aria-label, or is
       // not displayed directly inside the node, e.g. an associated label
       // element.
diff --git a/ui/accessibility/ax_enum_util.cc b/ui/accessibility/ax_enum_util.cc
index 3a82bcb7..885f60f 100644
--- a/ui/accessibility/ax_enum_util.cc
+++ b/ui/accessibility/ax_enum_util.cc
@@ -1552,8 +1552,6 @@
   switch (name_from) {
     case ax::mojom::NameFrom::kNone:
       return "none";
-    case ax::mojom::NameFrom::kUninitialized:
-      return "uninitialized";
     case ax::mojom::NameFrom::kAttribute:
       return "attribute";
     case ax::mojom::NameFrom::kAttributeExplicitlyEmpty:
diff --git a/ui/accessibility/ax_enums.mojom b/ui/accessibility/ax_enums.mojom
index 671d00e..98ecd88 100644
--- a/ui/accessibility/ax_enums.mojom
+++ b/ui/accessibility/ax_enums.mojom
@@ -1172,7 +1172,6 @@
 
 enum NameFrom {
   kNone,
-  kUninitialized,
   kAttribute,  // E.g. aria-label.
   kAttributeExplicitlyEmpty,
   kCaption,  // E.g. in the case of a table, from a caption element.
diff --git a/ui/android/junit/src/org/chromium/ui/dragdrop/DragAndDropDelegateImplUnitTest.java b/ui/android/junit/src/org/chromium/ui/dragdrop/DragAndDropDelegateImplUnitTest.java
index 03f0ded..05d12538 100644
--- a/ui/android/junit/src/org/chromium/ui/dragdrop/DragAndDropDelegateImplUnitTest.java
+++ b/ui/android/junit/src/org/chromium/ui/dragdrop/DragAndDropDelegateImplUnitTest.java
@@ -41,7 +41,8 @@
 import org.robolectric.shadows.ShadowAccessibilityManager;
 
 import org.chromium.base.compat.ApiHelperForN;
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.ui.dragdrop.DragAndDropDelegateImpl.DragTargetType;
 import org.chromium.url.JUnitTestGURLs;
@@ -50,7 +51,6 @@
  * Unit tests for {@link DragAndDropDelegateImpl}.
  */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(shadows = {ShadowRecordHistogram.class})
 public class DragAndDropDelegateImplUnitTest {
     /** Using a window size of 1000*600 for the ease of dp / pixel calculation. */
     private static final int WINDOW_WIDTH = 1000;
@@ -98,7 +98,7 @@
     @After
     public void tearDown() {
         DropDataContentProvider.onDragEnd(false);
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
         ShadowApiHelperForN.sLastDragShadowBuilder = null;
     }
 
@@ -462,8 +462,8 @@
     private void assertDragTypeRecorded(@DragTargetType int type) {
         final String histogram = "Android.DragDrop.FromWebContent.TargetType";
         final String errorMsg = "<" + histogram + "> is not recorded correctly.";
-        Assert.assertEquals(errorMsg, 1,
-                ShadowRecordHistogram.getHistogramValueCountForTesting(histogram, type));
+        Assert.assertEquals(
+                errorMsg, 1, RecordHistogram.getHistogramValueCountForTesting(histogram, type));
     }
 
     private void assertDragOutsideWebContentHistogramsRecorded(boolean dropResult) {
@@ -496,8 +496,7 @@
     private void assertHistogramRecorded(String histogram, boolean recorded, String reason) {
         Assert.assertEquals(
                 String.format("<%s> is not recorded correctly. Reason: %s", histogram, reason),
-                recorded ? 1 : 0,
-                ShadowRecordHistogram.getHistogramTotalCountForTesting(histogram));
+                recorded ? 1 : 0, RecordHistogram.getHistogramTotalCountForTesting(histogram));
     }
 
     private DragAndDropBrowserDelegate createDragAndDropBrowserDelegate(
diff --git a/ui/android/junit/src/org/chromium/ui/dragdrop/DropDataContentProviderTest.java b/ui/android/junit/src/org/chromium/ui/dragdrop/DropDataContentProviderTest.java
index d9ffd2f..d83abac 100644
--- a/ui/android/junit/src/org/chromium/ui/dragdrop/DropDataContentProviderTest.java
+++ b/ui/android/junit/src/org/chromium/ui/dragdrop/DropDataContentProviderTest.java
@@ -18,10 +18,10 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
 import org.robolectric.shadows.ShadowLooper;
 
-import org.chromium.base.metrics.test.ShadowRecordHistogram;
+import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 
 import java.io.FileNotFoundException;
@@ -31,7 +31,6 @@
  * Test basic functionality of {@link DropDataContentProvider}.
  */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(shadows = {ShadowRecordHistogram.class})
 public class DropDataContentProviderTest {
     private static final byte[] IMAGE_DATA_A = new byte[100];
     private static final byte[] IMAGE_DATA_B = new byte[50];
@@ -59,7 +58,7 @@
     public void tearDown() {
         DropDataContentProvider.clearCache();
         DropDataContentProvider.clearLastUriCreatedTimestampForTesting();
-        ShadowRecordHistogram.reset();
+        UmaRecorderHolder.resetForTesting();
     }
 
     @Test
@@ -194,42 +193,42 @@
     private void assertImageSizeRecorded(int expectedCnt) {
         final String histogram = "Android.DragDrop.Image.Size";
         final String errorMsg = "<" + histogram + "> is not recorded properly.";
-        Assert.assertEquals(errorMsg, expectedCnt,
-                ShadowRecordHistogram.getHistogramTotalCountForTesting(histogram));
+        Assert.assertEquals(
+                errorMsg, expectedCnt, RecordHistogram.getHistogramTotalCountForTesting(histogram));
     }
 
     private void assertImageUriCreatedIntervalRecorded(int expectedCnt) {
         final String histogram = "Android.DragDrop.Image.UriCreatedInterval";
         final String errorMsg = "<" + histogram + "> is not recorded properly.";
-        Assert.assertEquals(errorMsg, expectedCnt,
-                ShadowRecordHistogram.getHistogramTotalCountForTesting(histogram));
+        Assert.assertEquals(
+                errorMsg, expectedCnt, RecordHistogram.getHistogramTotalCountForTesting(histogram));
     }
 
     private void assertImageFirstOpenFileRecorded(int expectedCnt) {
         final String histogram = "Android.DragDrop.Image.OpenFileTime.FirstAttempt";
         final String errorMsg = "<" + histogram + "> is not recorded properly.";
-        Assert.assertEquals(errorMsg, expectedCnt,
-                ShadowRecordHistogram.getHistogramTotalCountForTesting(histogram));
+        Assert.assertEquals(
+                errorMsg, expectedCnt, RecordHistogram.getHistogramTotalCountForTesting(histogram));
     }
 
     private void assertImageLastOpenFileRecorded(int expectedCnt) {
         final String histogram = "Android.DragDrop.Image.OpenFileTime.LastAttempt";
         final String errorMsg = "<" + histogram + "> is not recorded properly.";
-        Assert.assertEquals(errorMsg, expectedCnt,
-                ShadowRecordHistogram.getHistogramTotalCountForTesting(histogram));
+        Assert.assertEquals(
+                errorMsg, expectedCnt, RecordHistogram.getHistogramTotalCountForTesting(histogram));
     }
 
     private void assertImageFirstExpiredOpenFileRecorded(int expectedCnt) {
         final String histogram = "Android.DragDrop.Image.OpenFileTime.FirstExpired";
         final String errorMsg = "<" + histogram + "> is not recorded properly.";
-        Assert.assertEquals(errorMsg, expectedCnt,
-                ShadowRecordHistogram.getHistogramTotalCountForTesting(histogram));
+        Assert.assertEquals(
+                errorMsg, expectedCnt, RecordHistogram.getHistogramTotalCountForTesting(histogram));
     }
 
     private void assertImageAllExpiredOpenFileRecorded(int expectedCnt) {
         final String histogram = "Android.DragDrop.Image.OpenFileTime.AllExpired";
         final String errorMsg = "<" + histogram + "> is not recorded properly.";
-        Assert.assertEquals(errorMsg, expectedCnt,
-                ShadowRecordHistogram.getHistogramTotalCountForTesting(histogram));
+        Assert.assertEquals(
+                errorMsg, expectedCnt, RecordHistogram.getHistogramTotalCountForTesting(histogram));
     }
 }
diff --git a/ui/display/display_features.cc b/ui/display/display_features.cc
index da0c774..bd0acae 100644
--- a/ui/display/display_features.cc
+++ b/ui/display/display_features.cc
@@ -62,9 +62,5 @@
   return base::FeatureList::IsEnabled(kRequireHdcpKeyProvisioning);
 }
 
-#if BUILDFLAG(IS_MAC)
-const base::Feature kForce60Hz{"Force60Hz", base::FEATURE_DISABLED_BY_DEFAULT};
-#endif
-
 }  // namespace features
 }  // namespace display
diff --git a/ui/display/display_features.h b/ui/display/display_features.h
index d0137b9..c278d07 100644
--- a/ui/display/display_features.h
+++ b/ui/display/display_features.h
@@ -31,11 +31,6 @@
 DISPLAY_EXPORT extern const base::Feature kRequireHdcpKeyProvisioning;
 DISPLAY_EXPORT bool IsHdcpKeyProvisioningRequired();
 
-#if BUILDFLAG(IS_MAC)
-// If enabled, vsync of 120 is forced to 60.
-DISPLAY_EXPORT extern const base::Feature kForce60Hz;
-#endif
-
 }  // namespace features
 }  // namespace display
 
diff --git a/ui/display/mac/display_link_mac.cc b/ui/display/mac/display_link_mac.cc
index 977939e2..564cf86 100644
--- a/ui/display/mac/display_link_mac.cc
+++ b/ui/display/mac/display_link_mac.cc
@@ -7,7 +7,6 @@
 #include <stdint.h>
 
 #include "base/bind.h"
-#include "base/command_line.h"
 #include "base/location.h"
 #include "base/logging.h"
 #include "base/no_destructor.h"
@@ -15,7 +14,6 @@
 #include "base/task/single_thread_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/trace_event/trace_event.h"
-#include "ui/display/display_features.h"
 
 namespace base {
 
@@ -162,9 +160,7 @@
 DisplayLinkMac::DisplayLinkMac(
     CGDirectDisplayID display_id,
     base::ScopedTypeRef<CVDisplayLinkRef> display_link)
-    : display_id_(display_id),
-      display_link_(display_link),
-      force_60hz_(base::FeatureList::IsEnabled(display::features::kForce60Hz)) {
+    : display_id_(display_id), display_link_(display_link) {
   DisplayLinkMap& all_display_links = GetAllDisplayLinks();
   DCHECK(all_display_links.find(display_id) == all_display_links.end());
   if (all_display_links.empty()) {
@@ -236,11 +232,6 @@
 
   timebase_ = base::TimeTicks::FromMachAbsoluteTime(cv_time.hostTime);
   interval_ = base::Microseconds(int64_t{interval_us.ValueOrDie()});
-  // Use a range as interval is not always exactly 120.
-  if (force_60hz_ && interval_ <= base::Hertz(115) &&
-      interval_ >= base::Hertz(125)) {
-    interval_ *= 2;
-  }
   timebase_and_interval_valid_ = true;
 
   // Don't restart the display link for 10 seconds.
diff --git a/ui/display/mac/display_link_mac.h b/ui/display/mac/display_link_mac.h
index 8b56dfe..6a4d7e2 100644
--- a/ui/display/mac/display_link_mac.h
+++ b/ui/display/mac/display_link_mac.h
@@ -95,9 +95,6 @@
   base::TimeTicks recalculate_time_;
 
   std::vector<VSyncUpdatedCallback> vsync_updated_callbacks_;
-
-  // TODO(sky): temporary, will remove after doing some analysis.
-  const bool force_60hz_;
 };
 
 }  // namespace ui
diff --git a/ui/gfx/BUILD.gn b/ui/gfx/BUILD.gn
index 84e1b57..c492e236 100644
--- a/ui/gfx/BUILD.gn
+++ b/ui/gfx/BUILD.gn
@@ -419,6 +419,8 @@
     "color_transform.h",
     "display_color_spaces.cc",
     "display_color_spaces.h",
+    "hdr_metadata.cc",
+    "hdr_metadata.h",
     "hdr_static_metadata.cc",
     "hdr_static_metadata.h",
     "icc_profile.cc",
@@ -543,8 +545,6 @@
     "gpu_fence.h",
     "gpu_fence_handle.cc",
     "gpu_fence_handle.h",
-    "hdr_metadata.cc",
-    "hdr_metadata.h",
     "native_pixmap.h",
     "overlay_priority_hint.h",
     "overlay_transform.h",
diff --git a/ui/gfx/color_conversion_sk_filter_cache.cc b/ui/gfx/color_conversion_sk_filter_cache.cc
index 56b035c..6c9a3a32c 100644
--- a/ui/gfx/color_conversion_sk_filter_cache.cc
+++ b/ui/gfx/color_conversion_sk_filter_cache.cc
@@ -36,6 +36,7 @@
     const Key& other) const {
   return src == other.src && dst == other.dst &&
          sdr_max_luminance_nits == other.sdr_max_luminance_nits &&
+         src_hdr_metadata == other.src_hdr_metadata &&
          dst_max_luminance_relative == other.dst_max_luminance_relative;
 }
 
@@ -50,12 +51,15 @@
                   other.dst_max_luminance_relative);
 }
 
-ColorConversionSkFilterCache::Key::Key(const gfx::ColorSpace& src,
-                                       const gfx::ColorSpace& dst,
-                                       float sdr_max_luminance_nits,
-                                       float dst_max_luminance_relative)
+ColorConversionSkFilterCache::Key::Key(
+    const gfx::ColorSpace& src,
+    const gfx::ColorSpace& dst,
+    absl::optional<gfx::HDRMetadata> src_hdr_metadata,
+    float sdr_max_luminance_nits,
+    float dst_max_luminance_relative)
     : src(src),
       dst(dst),
+      src_hdr_metadata(src_hdr_metadata),
       sdr_max_luminance_nits(sdr_max_luminance_nits),
       dst_max_luminance_relative(dst_max_luminance_relative) {}
 
@@ -64,14 +68,16 @@
     const gfx::ColorSpace& dst,
     float resource_offset,
     float resource_multiplier,
+    absl::optional<gfx::HDRMetadata> src_hdr_metadata,
     float sdr_max_luminance_nits,
     float dst_max_luminance_relative) {
   // Set unused parameters to bogus values, so that they do not result in
   // different keys for the same conversion.
   if (!src.IsToneMappedByDefault()) {
-    // If the source is not going to be tone mapped, then
-    // `dst_max_luminance_relative` will not be used, so set it to a nonsense
-    // value.
+    // If the source is not going to be tone mapped, then `src_hdr_metadata`
+    // and `dst_max_luminance_relative` will not be used, so set them nonsense
+    // values.
+    src_hdr_metadata = absl::nullopt;
     dst_max_luminance_relative = 0;
 
     // If neither source nor destination will use `sdr_max_luminance_nits`, then
@@ -81,7 +87,8 @@
     }
   }
 
-  const Key key(src, dst, sdr_max_luminance_nits, dst_max_luminance_relative);
+  const Key key(src, dst, src_hdr_metadata, sdr_max_luminance_nits,
+                dst_max_luminance_relative);
   sk_sp<SkRuntimeEffect>& effect = cache_[key];
 
   if (!effect) {
@@ -93,6 +100,7 @@
     // SkShaderSource not depend on that parameter (rather, that it be left
     // as a uniform in the shader). If that is not the case, then it will need
     // to be part of the key.
+    options.src_hdr_metadata = src_hdr_metadata;
     options.dst_max_luminance_relative = dst_max_luminance_relative;
     std::unique_ptr<gfx::ColorTransform> transform =
         gfx::ColorTransform::NewColorTransform(src, dst, options);
@@ -194,7 +202,8 @@
   sk_sp<SkColorFilter> filter =
       Get(image_color_space, gfx::ColorSpace(*target_color_space),
           /*resource_offset=*/0, /*resource_multiplier=*/1,
-          sdr_max_luminance_nits, dst_max_luminance_relative);
+          /*src_hdr_metadata=*/absl::nullopt, sdr_max_luminance_nits,
+          dst_max_luminance_relative);
   SkPaint paint;
   paint.setBlendMode(SkBlendMode::kSrc);
   paint.setColorFilter(filter);
diff --git a/ui/gfx/color_conversion_sk_filter_cache.h b/ui/gfx/color_conversion_sk_filter_cache.h
index 1688cae..add7773f 100644
--- a/ui/gfx/color_conversion_sk_filter_cache.h
+++ b/ui/gfx/color_conversion_sk_filter_cache.h
@@ -6,10 +6,12 @@
 #define UI_GFX_COLOR_CONVERSION_SK_FILTER_CACHE_H_
 
 #include "base/containers/flat_map.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/skia/include/core/SkRefCnt.h"
 #include "ui/gfx/color_space.h"
 #include "ui/gfx/color_space_export.h"
 #include "ui/gfx/gfx_export.h"
+#include "ui/gfx/hdr_metadata.h"
 
 class GrDirectContext;
 class SkImage;
@@ -27,14 +29,15 @@
   ~ColorConversionSkFilterCache();
 
   // Retrieve an SkColorFilter to transform `src` to `dst`. The filter also
-  // applies the offset `resource_offset` and then scales by
-  // `resource_multiplier`.
-  // TODO(https://crbug.com/1286076): Apply tone mapping using
-  // `sdr_max_luminance_nits` and `dst_max_luminance_relative`.
+  // applies the offset `src_resource_offset` and then scales by
+  // `src_resource_multiplier`. Apply tone mapping of `src` is HLG or PQ,
+  // using `sdr_max_luminance_nits`, `src_hdr_metadata`, and
+  // `dst_max_luminance_relative` as parameters.
   sk_sp<SkColorFilter> Get(const gfx::ColorSpace& src,
                            const gfx::ColorSpace& dst,
                            float resource_offset,
                            float resource_multiplier,
+                           absl::optional<gfx::HDRMetadata> src_hdr_metadata,
                            float sdr_max_luminance_nits,
                            float dst_max_luminance_relative);
 
@@ -57,11 +60,13 @@
   struct Key {
     Key(const gfx::ColorSpace& src,
         const gfx::ColorSpace& dst,
+        absl::optional<gfx::HDRMetadata> src_hdr_metadata,
         float sdr_max_luminance_nits,
         float dst_max_luminance_relative);
 
     gfx::ColorSpace src;
     gfx::ColorSpace dst;
+    absl::optional<gfx::HDRMetadata> src_hdr_metadata;
     float sdr_max_luminance_nits = 0.f;
     float dst_max_luminance_relative = 0.f;
 
@@ -74,6 +79,7 @@
                           float resource_offset,
                           float resource_multiplier,
                           float sdr_max_luminance_nits,
+                          absl::optional<gfx::HDRMetadata> src_hdr_metadata,
                           float dst_max_luminance_relative);
 
   base::flat_map<Key, sk_sp<SkRuntimeEffect>> cache_;
diff --git a/ui/gfx/color_transform.cc b/ui/gfx/color_transform.cc
index 9fd6a00..86bbceb 100644
--- a/ui/gfx/color_transform.cc
+++ b/ui/gfx/color_transform.cc
@@ -144,6 +144,21 @@
   return 0;
 }
 
+// Return the maximum luminance to be used for tone mapping a PQ signal, with
+// the indicated metadata.
+float GetToneMapPQMaxLuminanceNits(
+    const absl::optional<gfx::HDRMetadata>& hdr_metadata) {
+  if (hdr_metadata) {
+    if (hdr_metadata->max_content_light_level > 0)
+      return hdr_metadata->max_content_light_level;
+    if (hdr_metadata->color_volume_metadata.luminance_max > 0.f) {
+      return hdr_metadata->color_volume_metadata.luminance_max;
+    }
+  }
+  // The maximum value that ColorTransformPQToLinear can produce.
+  return 10000.f;
+}
+
 }  // namespace
 
 class ColorTransformMatrix;
@@ -1119,9 +1134,9 @@
         break;
       }
       case ColorSpace::TransferID::PQ: {
-        // The maximum value that ColorTransformPQToLinear can produce.
         const float src_max_luminance_relative =
-            10000 / options.sdr_max_luminance_nits;
+            GetToneMapPQMaxLuminanceNits(options.src_hdr_metadata) /
+            options.sdr_max_luminance_nits;
         if (src_max_luminance_relative > options.dst_max_luminance_relative) {
           const ColorSpace rec2020_linear(
               ColorSpace::PrimaryID::BT2020, ColorSpace::TransferID::LINEAR,
diff --git a/ui/gfx/color_transform.h b/ui/gfx/color_transform.h
index d3f6e5d..2bc8d22 100644
--- a/ui/gfx/color_transform.h
+++ b/ui/gfx/color_transform.h
@@ -8,9 +8,11 @@
 #include <memory>
 #include <string>
 
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/gfx/color_space.h"
 #include "ui/gfx/color_space_export.h"
 #include "ui/gfx/geometry/point3_f.h"
+#include "ui/gfx/hdr_metadata.h"
 
 namespace gfx {
 
@@ -33,6 +35,9 @@
     // TODO(https://crbug.com/1286082): Use this value in the transform.
     float sdr_max_luminance_nits = ColorSpace::kDefaultSDRWhiteLevel;
 
+    // Used for tone mapping PQ sources.
+    absl::optional<gfx::HDRMetadata> src_hdr_metadata;
+
     // The maximum luminance value for the destination, as a multiple of
     // `sdr_max_luminance_nits` (so this is 1 for SDR displays).
     // TODO(https://crbug.com/1286076): Use this value for transforming
diff --git a/ui/gfx/gpu_memory_buffer.h b/ui/gfx/gpu_memory_buffer.h
index 6c5c523..307efc1a 100644
--- a/ui/gfx/gpu_memory_buffer.h
+++ b/ui/gfx/gpu_memory_buffer.h
@@ -14,7 +14,6 @@
 #include "ui/gfx/generic_shared_memory_id.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/gfx_export.h"
-#include "ui/gfx/hdr_metadata.h"
 
 #if defined(USE_OZONE) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
 #include "ui/gfx/native_pixmap_handle.h"
diff --git a/ui/gfx/hdr_metadata.h b/ui/gfx/hdr_metadata.h
index c06f9e2..a50da18 100644
--- a/ui/gfx/hdr_metadata.h
+++ b/ui/gfx/hdr_metadata.h
@@ -5,13 +5,13 @@
 #ifndef UI_GFX_HDR_METADATA_H_
 #define UI_GFX_HDR_METADATA_H_
 
+#include "ui/gfx/color_space_export.h"
 #include "ui/gfx/geometry/point_f.h"
-#include "ui/gfx/gfx_export.h"
 
 namespace gfx {
 
 // SMPTE ST 2086 color volume metadata.
-struct GFX_EXPORT ColorVolumeMetadata {
+struct COLOR_SPACE_EXPORT ColorVolumeMetadata {
   using Chromaticity = PointF;
   Chromaticity primary_r;
   Chromaticity primary_g;
@@ -33,7 +33,7 @@
 };
 
 // HDR metadata common for HDR10 and WebM/VP9-based HDR formats.
-struct GFX_EXPORT HDRMetadata {
+struct COLOR_SPACE_EXPORT HDRMetadata {
   ColorVolumeMetadata color_volume_metadata;
   // Max content light level (CLL), i.e. maximum brightness level present in the
   // stream), in nits.
diff --git a/ui/gfx/render_text.cc b/ui/gfx/render_text.cc
index 9db4aaff7..69ee0aee 100644
--- a/ui/gfx/render_text.cc
+++ b/ui/gfx/render_text.cc
@@ -144,9 +144,15 @@
 
   const SkPoint points[2] = { PointToSkPoint(text_rect.origin()),
                               PointToSkPoint(text_rect.top_right()) };
+  // TODO(crbug/1308932): Remove this helper vector colors4f and make all
+  // SkColor4f.
+  std::vector<SkColor4f> colors4f;
+  colors4f.reserve(colors.size());
+  for (auto& c : colors)
+    colors4f.push_back(SkColor4f::FromColor(c));
   return cc::PaintShader::MakeLinearGradient(
-      &points[0], &colors[0], &positions[0], static_cast<int>(colors.size()),
-      SkTileMode::kClamp);
+      &points[0], &colors4f[0], &positions[0],
+      static_cast<int>(colors4f.size()), SkTileMode::kClamp);
 }
 
 // Converts a FontRenderParams::Hinting value to the corresponding
diff --git a/ui/gfx/skia_paint_util.cc b/ui/gfx/skia_paint_util.cc
index 7bc2d44..b25b305 100644
--- a/ui/gfx/skia_paint_util.cc
+++ b/ui/gfx/skia_paint_util.cc
@@ -61,7 +61,9 @@
                                             const gfx::Point& end_point,
                                             SkColor start_color,
                                             SkColor end_color) {
-  SkColor grad_colors[2] = {start_color, end_color};
+  // TODO(crbug/1308932): Remove FromColor and make all SkColor4f.
+  SkColor4f grad_colors[2] = {SkColor4f::FromColor(start_color),
+                              SkColor4f::FromColor(end_color)};
   SkPoint grad_points[2] = {gfx::PointToSkPoint(start_point),
                             gfx::PointToSkPoint(end_point)};
 
diff --git a/ui/gtk/BUILD.gn b/ui/gtk/BUILD.gn
index 1ae7c0e7..7f00a119 100644
--- a/ui/gtk/BUILD.gn
+++ b/ui/gtk/BUILD.gn
@@ -69,7 +69,7 @@
 }
 
 component("gtk") {
-  visibility = [ "//ui/views/linux_ui:linux_ui_factory" ]
+  visibility = [ "//ui/linux:linux_ui_factory" ]
   public = [ "gtk_ui_factory.h" ]
 
   sources = [
@@ -138,6 +138,7 @@
     "//ui/gfx",
     "//ui/gfx:native_widget_types",
     "//ui/gfx/geometry",
+    "//ui/linux:linux_ui",
     "//ui/native_theme",
     "//ui/shell_dialogs",
     "//ui/strings",
diff --git a/ui/gtk/DEPS b/ui/gtk/DEPS
index 53d53a5..21af089 100644
--- a/ui/gtk/DEPS
+++ b/ui/gtk/DEPS
@@ -9,6 +9,7 @@
   "+ui/display",
   "+ui/events",
   "+ui/gfx",
+  "+ui/linux",
   "+ui/native_theme",
   "+ui/shell_dialogs",
   "+ui/strings",
diff --git a/ui/gtk/gtk_ui.cc b/ui/gtk/gtk_ui.cc
index 202d142..86ee61e5 100644
--- a/ui/gtk/gtk_ui.cc
+++ b/ui/gtk/gtk_ui.cc
@@ -64,14 +64,14 @@
 #include "ui/gtk/select_file_dialog_linux_gtk.h"
 #include "ui/gtk/settings_provider_gtk.h"
 #include "ui/gtk/window_frame_provider_gtk.h"
+#include "ui/linux/device_scale_factor_observer.h"
+#include "ui/linux/nav_button_provider.h"
+#include "ui/linux/window_button_order_observer.h"
 #include "ui/native_theme/native_theme.h"
 #include "ui/ozone/buildflags.h"
 #include "ui/ozone/public/ozone_platform.h"
 #include "ui/shell_dialogs/select_file_dialog_linux.h"
 #include "ui/shell_dialogs/select_file_policy.h"
-#include "ui/views/linux_ui/device_scale_factor_observer.h"
-#include "ui/views/linux_ui/nav_button_provider.h"
-#include "ui/views/linux_ui/window_button_order_observer.h"
 #include "ui/views/window/window_button_order_provider.h"
 
 #if defined(USE_GIO)
@@ -167,9 +167,9 @@
   return params;
 }
 
-views::LinuxUI::WindowFrameAction GetDefaultMiddleClickAction() {
+ui::LinuxUi::WindowFrameAction GetDefaultMiddleClickAction() {
   if (GtkCheckVersion(3, 14))
-    return views::LinuxUI::WindowFrameAction::kNone;
+    return ui::LinuxUi::WindowFrameAction::kNone;
   std::unique_ptr<base::Environment> env(base::Environment::Create());
   switch (base::nix::GetDesktopEnvironment(env.get())) {
     case base::nix::DESKTOP_ENVIRONMENT_KDE4:
@@ -178,9 +178,9 @@
       // middle mouse button to create tab groups. We don't support that in
       // Chrome, but at least avoid lowering windows in response to middle
       // clicks to avoid surprising users who expect the KDE behavior.
-      return views::LinuxUI::WindowFrameAction::kNone;
+      return ui::LinuxUi::WindowFrameAction::kNone;
     default:
-      return views::LinuxUI::WindowFrameAction::kLower;
+      return ui::LinuxUi::WindowFrameAction::kLower;
   }
 }
 
@@ -259,8 +259,8 @@
   // so this must be done after to avoid the race condition.
   ShellDialogLinux::Initialize();
 
-  using Action = views::LinuxUI::WindowFrameAction;
-  using ActionSource = views::LinuxUI::WindowFrameActionSource;
+  using Action = ui::LinuxUi::WindowFrameAction;
+  using ActionSource = ui::LinuxUi::WindowFrameActionSource;
   window_frame_actions_ = {
       {ActionSource::kDoubleClick, Action::kToggleMaximize},
       {ActionSource::kMiddleClick, GetDefaultMiddleClickAction()},
@@ -463,10 +463,8 @@
   views::WindowButtonOrderProvider::GetInstance()->SetWindowButtonOrder(
       leading_buttons, trailing_buttons);
 
-  for (views::WindowButtonOrderObserver& observer :
-       window_button_order_observer_list()) {
+  for (auto& observer : window_button_order_observer_list())
     observer.OnWindowButtonOrderingChange();
-  }
 }
 
 void GtkUi::SetWindowFrameAction(WindowFrameActionSource source,
@@ -502,7 +500,7 @@
   return new SelectFileDialogLinuxGtk(listener, std::move(policy));
 }
 
-views::LinuxUI::WindowFrameAction GtkUi::GetWindowFrameAction(
+ui::LinuxUi::WindowFrameAction GtkUi::GetWindowFrameAction(
     WindowFrameActionSource source) {
   return window_frame_actions_[source];
 }
@@ -521,13 +519,13 @@
   return animations_enabled;
 }
 
-std::unique_ptr<views::NavButtonProvider> GtkUi::CreateNavButtonProvider() {
+std::unique_ptr<ui::NavButtonProvider> GtkUi::CreateNavButtonProvider() {
   if (GtkCheckVersion(3, 14))
     return std::make_unique<gtk::NavButtonProviderGtk>();
   return nullptr;
 }
 
-views::WindowFrameProvider* GtkUi::GetWindowFrameProvider(bool solid_frame) {
+ui::WindowFrameProvider* GtkUi::GetWindowFrameProvider(bool solid_frame) {
   if (!GtkCheckVersion(3, 14))
     return nullptr;
   auto& provider =
@@ -942,7 +940,7 @@
   float old_device_scale_factor = device_scale_factor_;
   device_scale_factor_ = GetRawDeviceScaleFactor();
   if (device_scale_factor_ != old_device_scale_factor) {
-    for (views::DeviceScaleFactorObserver& observer :
+    for (ui::DeviceScaleFactorObserver& observer :
          device_scale_factor_observer_list()) {
       observer.OnDeviceScaleFactorChanged();
     }
diff --git a/ui/gtk/gtk_ui.h b/ui/gtk/gtk_ui.h
index e63ec134..162fb07 100644
--- a/ui/gtk/gtk_ui.h
+++ b/ui/gtk/gtk_ui.h
@@ -15,8 +15,8 @@
 #include "ui/base/glib/glib_signal.h"
 #include "ui/gfx/color_utils.h"
 #include "ui/gtk/gtk_ui_platform.h"
-#include "ui/views/linux_ui/linux_ui.h"
-#include "ui/views/linux_ui/window_frame_provider.h"
+#include "ui/linux/linux_ui.h"
+#include "ui/linux/window_frame_provider.h"
 #include "ui/views/window/frame_buttons.h"
 
 #if BUILDFLAG(ENABLE_PRINTING)
@@ -36,7 +36,7 @@
 class SettingsProvider;
 
 // Interface to GTK desktop features.
-class GtkUi : public views::LinuxUI {
+class GtkUi : public ui::LinuxUi {
  public:
   GtkUi();
 
@@ -74,7 +74,7 @@
       ui::SelectFileDialog::Listener* listener,
       std::unique_ptr<ui::SelectFilePolicy> policy) const override;
 
-  // views::LinuxUI:
+  // ui::LinuxUi:
   bool Initialize() override;
   bool GetColor(int id, SkColor* color, bool use_custom_frame) const override;
   bool GetDisplayProperty(int id, int* result) const override;
@@ -92,8 +92,8 @@
   float GetDeviceScaleFactor() const override;
   bool PreferDarkTheme() const override;
   bool AnimationsEnabled() const override;
-  std::unique_ptr<views::NavButtonProvider> CreateNavButtonProvider() override;
-  views::WindowFrameProvider* GetWindowFrameProvider(bool solid_frame) override;
+  std::unique_ptr<ui::NavButtonProvider> CreateNavButtonProvider() override;
+  ui::WindowFrameProvider* GetWindowFrameProvider(bool solid_frame) override;
   base::flat_map<std::string, std::string> GetKeyboardLayoutMap() override;
   std::string GetCursorThemeName() override;
   int GetCursorThemeSize() override;
@@ -197,8 +197,8 @@
   // Paints a native window frame.  Typically only one of these will be
   // non-null.  The exception is when the user starts or stops their compositor
   // while Chrome is running.
-  std::unique_ptr<views::WindowFrameProvider> solid_frame_provider_;
-  std::unique_ptr<views::WindowFrameProvider> transparent_frame_provider_;
+  std::unique_ptr<ui::WindowFrameProvider> solid_frame_provider_;
+  std::unique_ptr<ui::WindowFrameProvider> transparent_frame_provider_;
 };
 
 }  // namespace gtk
diff --git a/ui/gtk/gtk_ui_factory.cc b/ui/gtk/gtk_ui_factory.cc
index 06cd8306a..051d1c3 100644
--- a/ui/gtk/gtk_ui_factory.cc
+++ b/ui/gtk/gtk_ui_factory.cc
@@ -6,6 +6,6 @@
 
 #include "ui/gtk/gtk_ui.h"
 
-std::unique_ptr<views::LinuxUI> BuildGtkUi() {
+std::unique_ptr<ui::LinuxUi> BuildGtkUi() {
   return std::make_unique<gtk::GtkUi>();
 }
diff --git a/ui/gtk/gtk_ui_factory.h b/ui/gtk/gtk_ui_factory.h
index f076602..dd808515 100644
--- a/ui/gtk/gtk_ui_factory.h
+++ b/ui/gtk/gtk_ui_factory.h
@@ -9,13 +9,13 @@
 
 #include "base/component_export.h"
 
-namespace views {
-class LinuxUI;
+namespace ui {
+class LinuxUi;
 }
 
 // Access point to the GTK desktop system.  This should be the only symbol
 // exported from this component.
 COMPONENT_EXPORT(GTK)
-std::unique_ptr<views::LinuxUI> BuildGtkUi();
+std::unique_ptr<ui::LinuxUi> BuildGtkUi();
 
 #endif  // UI_GTK_GTK_UI_FACTORY_H_
diff --git a/ui/gtk/gtk_util.cc b/ui/gtk/gtk_util.cc
index 54b8dd6..6f10b3f 100644
--- a/ui/gtk/gtk_util.cc
+++ b/ui/gtk/gtk_util.cc
@@ -27,9 +27,9 @@
 #include "ui/gtk/gtk_compat.h"
 #include "ui/gtk/gtk_ui.h"
 #include "ui/gtk/gtk_ui_platform.h"
+#include "ui/linux/linux_ui.h"
 #include "ui/native_theme/common_theme.h"
 #include "ui/ozone/public/ozone_platform.h"
-#include "ui/views/linux_ui/linux_ui.h"
 
 namespace gtk {
 
@@ -683,7 +683,7 @@
 }
 
 float GetDeviceScaleFactor() {
-  views::LinuxUI* linux_ui = views::LinuxUI::instance();
+  ui::LinuxUi* linux_ui = ui::LinuxUi::instance();
   return linux_ui ? linux_ui->GetDeviceScaleFactor() : 1;
 }
 
diff --git a/ui/gtk/input_method_context_impl_gtk.cc b/ui/gtk/input_method_context_impl_gtk.cc
index 5694965..fdd18e0 100644
--- a/ui/gtk/input_method_context_impl_gtk.cc
+++ b/ui/gtk/input_method_context_impl_gtk.cc
@@ -20,7 +20,7 @@
 #include "ui/gtk/gtk_ui.h"
 #include "ui/gtk/gtk_ui_platform.h"
 #include "ui/gtk/gtk_util.h"
-#include "ui/views/linux_ui/linux_ui.h"
+#include "ui/linux/linux_ui.h"
 
 namespace gtk {
 
diff --git a/ui/gtk/nav_button_provider_gtk.cc b/ui/gtk/nav_button_provider_gtk.cc
index e5926028..46d2a67c 100644
--- a/ui/gtk/nav_button_provider_gtk.cc
+++ b/ui/gtk/nav_button_provider_gtk.cc
@@ -12,6 +12,7 @@
 #include "ui/gfx/image/image_skia_source.h"
 #include "ui/gtk/gtk_compat.h"
 #include "ui/gtk/gtk_util.h"
+#include "ui/linux/nav_button_provider.h"
 #include "ui/views/widget/widget.h"
 
 namespace gtk {
@@ -33,14 +34,14 @@
 const int kHeaderSpacing = 6;
 
 const char* ButtonStyleClassFromButtonType(
-    views::NavButtonProvider::FrameButtonDisplayType type) {
+    ui::NavButtonProvider::FrameButtonDisplayType type) {
   switch (type) {
-    case views::NavButtonProvider::FrameButtonDisplayType::kMinimize:
+    case ui::NavButtonProvider::FrameButtonDisplayType::kMinimize:
       return "minimize";
-    case views::NavButtonProvider::FrameButtonDisplayType::kMaximize:
-    case views::NavButtonProvider::FrameButtonDisplayType::kRestore:
+    case ui::NavButtonProvider::FrameButtonDisplayType::kMaximize:
+    case ui::NavButtonProvider::FrameButtonDisplayType::kRestore:
       return "maximize";
-    case views::NavButtonProvider::FrameButtonDisplayType::kClose:
+    case ui::NavButtonProvider::FrameButtonDisplayType::kClose:
       return "close";
     default:
       NOTREACHED();
@@ -48,16 +49,17 @@
   }
 }
 
-GtkStateFlags GtkStateFlagsFromButtonState(views::Button::ButtonState state) {
+GtkStateFlags GtkStateFlagsFromButtonState(
+    ui::NavButtonProvider::ButtonState state) {
   switch (state) {
-    case views::Button::STATE_NORMAL:
+    case ui::NavButtonProvider::ButtonState::kNormal:
       return GTK_STATE_FLAG_NORMAL;
-    case views::Button::STATE_HOVERED:
+    case ui::NavButtonProvider::ButtonState::kHovered:
       return GTK_STATE_FLAG_PRELIGHT;
-    case views::Button::STATE_PRESSED:
+    case ui::NavButtonProvider::ButtonState::kPressed:
       return static_cast<GtkStateFlags>(GTK_STATE_FLAG_PRELIGHT |
                                         GTK_STATE_FLAG_ACTIVE);
-    case views::Button::STATE_DISABLED:
+    case ui::NavButtonProvider::ButtonState::kDisabled:
       return GTK_STATE_FLAG_INSENSITIVE;
     default:
       NOTREACHED();
@@ -66,15 +68,15 @@
 }
 
 const char* IconNameFromButtonType(
-    views::NavButtonProvider::FrameButtonDisplayType type) {
+    ui::NavButtonProvider::FrameButtonDisplayType type) {
   switch (type) {
-    case views::NavButtonProvider::FrameButtonDisplayType::kMinimize:
+    case ui::NavButtonProvider::FrameButtonDisplayType::kMinimize:
       return "window-minimize-symbolic";
-    case views::NavButtonProvider::FrameButtonDisplayType::kMaximize:
+    case ui::NavButtonProvider::FrameButtonDisplayType::kMaximize:
       return "window-maximize-symbolic";
-    case views::NavButtonProvider::FrameButtonDisplayType::kRestore:
+    case ui::NavButtonProvider::FrameButtonDisplayType::kRestore:
       return "window-restore-symbolic";
-    case views::NavButtonProvider::FrameButtonDisplayType::kClose:
+    case ui::NavButtonProvider::FrameButtonDisplayType::kClose:
       return "window-close-symbolic";
     default:
       NOTREACHED();
@@ -82,11 +84,10 @@
   }
 }
 
-gfx::Size LoadNavButtonIcon(
-    views::NavButtonProvider::FrameButtonDisplayType type,
-    GtkStyleContext* button_context,
-    int scale,
-    NavButtonIcon* icon = nullptr) {
+gfx::Size LoadNavButtonIcon(ui::NavButtonProvider::FrameButtonDisplayType type,
+                            GtkStyleContext* button_context,
+                            int scale,
+                            NavButtonIcon* icon = nullptr) {
   const char* icon_name = IconNameFromButtonType(type);
   if (!GtkCheckVersion(4)) {
     auto icon_info = TakeGObject(gtk_icon_theme_lookup_icon_for_scale(
@@ -186,7 +187,7 @@
 }
 
 void CalculateUnscaledButtonSize(
-    views::NavButtonProvider::FrameButtonDisplayType type,
+    ui::NavButtonProvider::FrameButtonDisplayType type,
     bool maximized,
     gfx::Size* button_size,
     gfx::Insets* button_margin) {
@@ -213,8 +214,8 @@
 
 class NavButtonImageSource : public gfx::ImageSkiaSource {
  public:
-  NavButtonImageSource(views::NavButtonProvider::FrameButtonDisplayType type,
-                       views::Button::ButtonState state,
+  NavButtonImageSource(ui::NavButtonProvider::FrameButtonDisplayType type,
+                       ui::NavButtonProvider::ButtonState state,
                        bool maximized,
                        bool active,
                        gfx::Size button_size)
@@ -339,8 +340,8 @@
   bool HasRepresentationAtAllScales() const override { return true; }
 
  private:
-  views::NavButtonProvider::FrameButtonDisplayType type_;
-  views::Button::ButtonState state_;
+  ui::NavButtonProvider::FrameButtonDisplayType type_;
+  ui::NavButtonProvider::ButtonState state_;
   bool maximized_;
   bool active_;
   gfx::Size button_size_;
@@ -359,15 +360,15 @@
   auto header_padding = GtkStyleContextGetPadding(header_context);
 
   double scale = 1.0f;
-  std::map<views::NavButtonProvider::FrameButtonDisplayType, gfx::Size>
+  std::map<ui::NavButtonProvider::FrameButtonDisplayType, gfx::Size>
       button_sizes;
-  std::map<views::NavButtonProvider::FrameButtonDisplayType, gfx::Insets>
+  std::map<ui::NavButtonProvider::FrameButtonDisplayType, gfx::Insets>
       button_margins;
-  std::vector<views::NavButtonProvider::FrameButtonDisplayType> display_types{
-      views::NavButtonProvider::FrameButtonDisplayType::kMinimize,
-      maximized ? views::NavButtonProvider::FrameButtonDisplayType::kRestore
-                : views::NavButtonProvider::FrameButtonDisplayType::kMaximize,
-      views::NavButtonProvider::FrameButtonDisplayType::kClose,
+  std::vector<ui::NavButtonProvider::FrameButtonDisplayType> display_types{
+      ui::NavButtonProvider::FrameButtonDisplayType::kMinimize,
+      maximized ? ui::NavButtonProvider::FrameButtonDisplayType::kRestore
+                : ui::NavButtonProvider::FrameButtonDisplayType::kMaximize,
+      ui::NavButtonProvider::FrameButtonDisplayType::kClose,
   };
   for (auto type : display_types) {
     CalculateUnscaledButtonSize(type, maximized, &button_sizes[type],
@@ -413,26 +414,32 @@
 
     button_margins_[type] = margin;
 
-    for (size_t state = 0; state < views::Button::STATE_COUNT; state++) {
-      button_images_[type][state] = gfx::ImageSkia(
-          std::make_unique<NavButtonImageSource>(
-              type, static_cast<views::Button::ButtonState>(state), maximized,
-              active, size),
-          size);
+    for (auto state : {
+             ui::NavButtonProvider::ButtonState::kNormal,
+             ui::NavButtonProvider::ButtonState::kHovered,
+             ui::NavButtonProvider::ButtonState::kPressed,
+             ui::NavButtonProvider::ButtonState::kDisabled,
+         }) {
+      button_images_[type][state] =
+          gfx::ImageSkia(std::make_unique<NavButtonImageSource>(
+                             type, state, maximized, active, size),
+                         size);
     }
   }
 }
 
 gfx::ImageSkia NavButtonProviderGtk::GetImage(
-    views::NavButtonProvider::FrameButtonDisplayType type,
-    views::Button::ButtonState state) const {
+    ui::NavButtonProvider::FrameButtonDisplayType type,
+    ui::NavButtonProvider::ButtonState state) const {
   auto it = button_images_.find(type);
   DCHECK(it != button_images_.end());
-  return it->second[state];
+  auto it2 = it->second.find(state);
+  DCHECK(it2 != it->second.end());
+  return it2->second;
 }
 
 gfx::Insets NavButtonProviderGtk::GetNavButtonMargin(
-    views::NavButtonProvider::FrameButtonDisplayType type) const {
+    ui::NavButtonProvider::FrameButtonDisplayType type) const {
   auto it = button_margins_.find(type);
   DCHECK(it != button_margins_.end());
   return it->second;
diff --git a/ui/gtk/nav_button_provider_gtk.h b/ui/gtk/nav_button_provider_gtk.h
index ffedf019..8fd2c49 100644
--- a/ui/gtk/nav_button_provider_gtk.h
+++ b/ui/gtk/nav_button_provider_gtk.h
@@ -7,31 +7,32 @@
 
 #include <map>
 
+#include "base/containers/flat_map.h"
+#include "ui/gfx/geometry/insets.h"
 #include "ui/gfx/image/image_skia.h"
-#include "ui/views/controls/button/button.h"
-#include "ui/views/linux_ui/nav_button_provider.h"
+#include "ui/linux/nav_button_provider.h"
 
 namespace gtk {
 
-class NavButtonProviderGtk : public views::NavButtonProvider {
+class NavButtonProviderGtk : public ui::NavButtonProvider {
  public:
   NavButtonProviderGtk();
   ~NavButtonProviderGtk() override;
 
-  // views::NavButtonProvider:
+  // ui::NavButtonProvider:
   void RedrawImages(int top_area_height, bool maximized, bool active) override;
-  gfx::ImageSkia GetImage(views::NavButtonProvider::FrameButtonDisplayType type,
-                          views::Button::ButtonState state) const override;
+  gfx::ImageSkia GetImage(ui::NavButtonProvider::FrameButtonDisplayType type,
+                          ButtonState state) const override;
   gfx::Insets GetNavButtonMargin(
-      views::NavButtonProvider::FrameButtonDisplayType type) const override;
+      ui::NavButtonProvider::FrameButtonDisplayType type) const override;
   gfx::Insets GetTopAreaSpacing() const override;
   int GetInterNavButtonSpacing() const override;
 
  private:
-  std::map<views::NavButtonProvider::FrameButtonDisplayType,
-           gfx::ImageSkia[views::Button::STATE_COUNT]>
+  std::map<ui::NavButtonProvider::FrameButtonDisplayType,
+           base::flat_map<ui::NavButtonProvider::ButtonState, gfx::ImageSkia>>
       button_images_;
-  std::map<views::NavButtonProvider::FrameButtonDisplayType, gfx::Insets>
+  std::map<ui::NavButtonProvider::FrameButtonDisplayType, gfx::Insets>
       button_margins_;
   gfx::Insets top_area_spacing_;
   int inter_button_spacing_;
diff --git a/ui/gtk/settings_provider_gsettings.cc b/ui/gtk/settings_provider_gsettings.cc
index 6498b74..9ac11213 100644
--- a/ui/gtk/settings_provider_gsettings.cc
+++ b/ui/gtk/settings_provider_gsettings.cc
@@ -116,22 +116,22 @@
   GtkUi::WindowFrameAction action;
 
   if (click_action == "none") {
-    action = views::LinuxUI::WindowFrameAction::kNone;
+    action = ui::LinuxUi::WindowFrameAction::kNone;
   } else if (click_action == "lower") {
-    action = views::LinuxUI::WindowFrameAction::kLower;
+    action = ui::LinuxUi::WindowFrameAction::kLower;
   } else if (click_action == "minimize") {
-    action = views::LinuxUI::WindowFrameAction::kMinimize;
+    action = ui::LinuxUi::WindowFrameAction::kMinimize;
   } else if (click_action == "toggle-maximize") {
-    action = views::LinuxUI::WindowFrameAction::kToggleMaximize;
+    action = ui::LinuxUi::WindowFrameAction::kToggleMaximize;
   } else {
     // While we want to have the default state be lower if there isn't a
     // value, we want to default to no action if the user has explicitly
     // chose an action that we don't implement.
-    action = views::LinuxUI::WindowFrameAction::kNone;
+    action = ui::LinuxUi::WindowFrameAction::kNone;
   }
 
   delegate_->SetWindowFrameAction(
-      views::LinuxUI::WindowFrameActionSource::kMiddleClick, action);
+      ui::LinuxUi::WindowFrameActionSource::kMiddleClick, action);
 }
 
 }  // namespace gtk
diff --git a/ui/gtk/settings_provider_gtk.cc b/ui/gtk/settings_provider_gtk.cc
index 0163f4e..76f51c7 100644
--- a/ui/gtk/settings_provider_gtk.cc
+++ b/ui/gtk/settings_provider_gtk.cc
@@ -32,15 +32,15 @@
 void ParseActionString(const std::string& value,
                        GtkUi::WindowFrameAction* action) {
   if (value == "none")
-    *action = views::LinuxUI::WindowFrameAction::kNone;
+    *action = ui::LinuxUi::WindowFrameAction::kNone;
   else if (value == "lower")
-    *action = views::LinuxUI::WindowFrameAction::kLower;
+    *action = ui::LinuxUi::WindowFrameAction::kLower;
   else if (value == "minimize")
-    *action = views::LinuxUI::WindowFrameAction::kMinimize;
+    *action = ui::LinuxUi::WindowFrameAction::kMinimize;
   else if (value == "toggle-maximize")
-    *action = views::LinuxUI::WindowFrameAction::kToggleMaximize;
+    *action = ui::LinuxUi::WindowFrameAction::kToggleMaximize;
   else if (value == "menu")
-    *action = views::LinuxUI::WindowFrameAction::kMenu;
+    *action = ui::LinuxUi::WindowFrameAction::kMenu;
 }
 
 }  // namespace
@@ -48,8 +48,8 @@
 SettingsProviderGtk::FrameActionSettingWatcher::FrameActionSettingWatcher(
     SettingsProviderGtk* settings_provider,
     const std::string& setting_name,
-    views::LinuxUI::WindowFrameActionSource action_type,
-    views::LinuxUI::WindowFrameAction default_action)
+    ui::LinuxUi::WindowFrameActionSource action_type,
+    ui::LinuxUi::WindowFrameAction default_action)
     : settings_provider_(settings_provider),
       setting_name_(setting_name),
       action_type_(action_type),
@@ -91,18 +91,18 @@
     frame_action_setting_watchers_.push_back(
         std::make_unique<FrameActionSettingWatcher>(
             this, "gtk-titlebar-middle-click",
-            views::LinuxUI::WindowFrameActionSource::kMiddleClick,
-            views::LinuxUI::WindowFrameAction::kNone));
+            ui::LinuxUi::WindowFrameActionSource::kMiddleClick,
+            ui::LinuxUi::WindowFrameAction::kNone));
     frame_action_setting_watchers_.push_back(
         std::make_unique<FrameActionSettingWatcher>(
             this, "gtk-titlebar-double-click",
-            views::LinuxUI::WindowFrameActionSource::kDoubleClick,
-            views::LinuxUI::WindowFrameAction::kToggleMaximize));
+            ui::LinuxUi::WindowFrameActionSource::kDoubleClick,
+            ui::LinuxUi::WindowFrameAction::kToggleMaximize));
     frame_action_setting_watchers_.push_back(
         std::make_unique<FrameActionSettingWatcher>(
             this, "gtk-titlebar-right-click",
-            views::LinuxUI::WindowFrameActionSource::kRightClick,
-            views::LinuxUI::WindowFrameAction::kMenu));
+            ui::LinuxUi::WindowFrameActionSource::kRightClick,
+            ui::LinuxUi::WindowFrameAction::kMenu));
   } else {
     signal_id_decoration_layout_ =
         g_signal_connect_after(settings, "notify::gtk-theme-name",
diff --git a/ui/gtk/settings_provider_gtk.h b/ui/gtk/settings_provider_gtk.h
index 34639c2..bb6c7e6 100644
--- a/ui/gtk/settings_provider_gtk.h
+++ b/ui/gtk/settings_provider_gtk.h
@@ -12,7 +12,7 @@
 #include "base/memory/raw_ptr.h"
 #include "ui/base/glib/glib_signal.h"
 #include "ui/gtk/settings_provider.h"
-#include "ui/views/linux_ui/linux_ui.h"
+#include "ui/linux/linux_ui.h"
 
 typedef struct _GParamSpec GParamSpec;
 typedef struct _GtkSettings GtkSettings;
@@ -33,11 +33,10 @@
  private:
   class FrameActionSettingWatcher {
    public:
-    FrameActionSettingWatcher(
-        SettingsProviderGtk* settings_provider,
-        const std::string& setting_name,
-        views::LinuxUI::WindowFrameActionSource action_type,
-        views::LinuxUI::WindowFrameAction default_action);
+    FrameActionSettingWatcher(SettingsProviderGtk* settings_provider,
+                              const std::string& setting_name,
+                              ui::LinuxUi::WindowFrameActionSource action_type,
+                              ui::LinuxUi::WindowFrameAction default_action);
 
     FrameActionSettingWatcher(const FrameActionSettingWatcher&) = delete;
     FrameActionSettingWatcher& operator=(const FrameActionSettingWatcher&) =
@@ -54,8 +53,8 @@
    private:
     raw_ptr<SettingsProviderGtk> settings_provider_;
     std::string setting_name_;
-    views::LinuxUI::WindowFrameActionSource action_type_;
-    views::LinuxUI::WindowFrameAction default_action_;
+    ui::LinuxUi::WindowFrameActionSource action_type_;
+    ui::LinuxUi::WindowFrameAction default_action_;
     unsigned long signal_id_;
   };
 
diff --git a/ui/gtk/window_frame_provider_gtk.h b/ui/gtk/window_frame_provider_gtk.h
index d3039d7..da0f7c3 100644
--- a/ui/gtk/window_frame_provider_gtk.h
+++ b/ui/gtk/window_frame_provider_gtk.h
@@ -7,12 +7,12 @@
 
 #include "base/containers/flat_map.h"
 #include "third_party/skia/include/core/SkBitmap.h"
-#include "ui/views/linux_ui/linux_ui.h"
-#include "ui/views/linux_ui/window_frame_provider.h"
+#include "ui/linux/linux_ui.h"
+#include "ui/linux/window_frame_provider.h"
 
 namespace gtk {
 
-class WindowFrameProviderGtk : public views::WindowFrameProvider {
+class WindowFrameProviderGtk : public ui::WindowFrameProvider {
  public:
   explicit WindowFrameProviderGtk(bool solid_frame);
 
@@ -21,7 +21,7 @@
 
   ~WindowFrameProviderGtk() override;
 
-  // views::WindowFrameProvider:
+  // ui::WindowFrameProvider:
   int GetTopCornerRadiusDip() override;
   gfx::Insets GetFrameThicknessDip() override;
   void PaintWindowFrame(gfx::Canvas* canvas,
diff --git a/ui/linux/BUILD.gn b/ui/linux/BUILD.gn
new file mode 100644
index 0000000..ca135e1
--- /dev/null
+++ b/ui/linux/BUILD.gn
@@ -0,0 +1,55 @@
+# Copyright 2022 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/linux/gtk/gtk.gni")
+import("//printing/buildflags/buildflags.gni")
+import("//ui/qt/qt.gni")
+
+component("linux_ui") {
+  defines = [ "IS_LINUX_UI_IMPL" ]
+  public = [
+    "device_scale_factor_observer.h",
+    "linux_ui.h",
+    "nav_button_provider.h",
+    "status_icon_linux.h",
+    "window_button_order_observer.h",
+    "window_frame_provider.h",
+  ]
+  sources = [
+    "linux_ui.cc",
+    "status_icon_linux.cc",
+  ]
+  deps = [
+    "//build:chromecast_buildflags",
+    "//ui/base/ime/linux",
+    "//ui/native_theme",
+    "//ui/shell_dialogs",
+  ]
+  if (enable_basic_printing) {
+    deps += [ "//printing" ]
+  }
+  public_deps = [
+    "//printing/buildflags",
+    "//skia",
+    "//ui/base/cursor:theme_manager",
+  ]
+}
+
+source_set("linux_ui_factory") {
+  sources = [
+    "linux_ui_factory.cc",
+    "linux_ui_factory.h",
+  ]
+
+  public_deps = [ ":linux_ui" ]
+
+  deps = [ "//ui/base:buildflags" ]
+  if (use_gtk) {
+    # This is the only component that can interact with gtk.
+    deps += [ "//ui/gtk" ]
+  }
+  if (use_qt) {
+    deps += [ "//ui/qt" ]
+  }
+}
diff --git a/ui/linux/DEPS b/ui/linux/DEPS
new file mode 100644
index 0000000..592db66
--- /dev/null
+++ b/ui/linux/DEPS
@@ -0,0 +1,17 @@
+include_rules = [
+  "+printing",
+  "+third_party/skia",
+  "+ui/base",
+  "+ui/base/cursor",
+  "+ui/base/ime",
+  "+ui/gfx",
+  "+ui/native_theme",
+  "+ui/shell_dialogs",
+]
+
+specific_include_rules = {
+  "linux_ui_factory.cc": [
+    "+ui/gtk",
+    "+ui/qt",
+  ],
+}
diff --git a/ui/views/linux_ui/OWNERS b/ui/linux/OWNERS
similarity index 100%
rename from ui/views/linux_ui/OWNERS
rename to ui/linux/OWNERS
diff --git a/ui/linux/device_scale_factor_observer.h b/ui/linux/device_scale_factor_observer.h
new file mode 100644
index 0000000..d09b39f3
--- /dev/null
+++ b/ui/linux/device_scale_factor_observer.h
@@ -0,0 +1,19 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_LINUX_DEVICE_SCALE_FACTOR_OBSERVER_H_
+#define UI_LINUX_DEVICE_SCALE_FACTOR_OBSERVER_H_
+
+namespace ui {
+
+class DeviceScaleFactorObserver {
+ public:
+  virtual ~DeviceScaleFactorObserver() = default;
+
+  virtual void OnDeviceScaleFactorChanged() = 0;
+};
+
+}  // namespace ui
+
+#endif  // UI_LINUX_DEVICE_SCALE_FACTOR_OBSERVER_H_
diff --git a/ui/views/linux_ui/linux_ui.cc b/ui/linux/linux_ui.cc
similarity index 70%
rename from ui/views/linux_ui/linux_ui.cc
rename to ui/linux/linux_ui.cc
index f5a54ff..261dd9f 100644
--- a/ui/views/linux_ui/linux_ui.cc
+++ b/ui/linux/linux_ui.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ui/views/linux_ui/linux_ui.h"
+#include "ui/linux/linux_ui.h"
 
 #include <cstdio>
 #include <utility>
@@ -11,18 +11,17 @@
 #include "base/environment.h"
 #include "base/nix/xdg_util.h"
 #include "build/build_config.h"
-#include "ui/base/ime/linux/linux_input_method_context_factory.h"
-#include "ui/gfx/skia_font_delegate.h"
+#include "ui/native_theme/native_theme.h"
 
 namespace {
 
-views::LinuxUI* g_linux_ui = nullptr;
+ui::LinuxUi* g_linux_ui = nullptr;
 
 }  // namespace
 
-namespace views {
+namespace ui {
 
-void LinuxUI::SetInstance(std::unique_ptr<LinuxUI> instance) {
+void LinuxUi::SetInstance(std::unique_ptr<LinuxUi> instance) {
   delete g_linux_ui;
   g_linux_ui = instance.release();
 
@@ -42,57 +41,57 @@
   // context factory.
 }
 
-LinuxUI* LinuxUI::instance() {
+LinuxUi* LinuxUi::instance() {
   return g_linux_ui;
 }
 
-LinuxUI::LinuxUI() = default;
+LinuxUi::LinuxUi() = default;
 
-LinuxUI::~LinuxUI() = default;
+LinuxUi::~LinuxUi() = default;
 
-LinuxUI::CmdLineArgs::CmdLineArgs() = default;
+LinuxUi::CmdLineArgs::CmdLineArgs() = default;
 
-LinuxUI::CmdLineArgs::CmdLineArgs(CmdLineArgs&&) = default;
+LinuxUi::CmdLineArgs::CmdLineArgs(CmdLineArgs&&) = default;
 
-LinuxUI::CmdLineArgs& LinuxUI::CmdLineArgs::operator=(CmdLineArgs&&) = default;
+LinuxUi::CmdLineArgs& LinuxUi::CmdLineArgs::operator=(CmdLineArgs&&) = default;
 
-LinuxUI::CmdLineArgs::~CmdLineArgs() = default;
+LinuxUi::CmdLineArgs::~CmdLineArgs() = default;
 
-void LinuxUI::AddWindowButtonOrderObserver(
-    views::WindowButtonOrderObserver* observer) {
+void LinuxUi::AddWindowButtonOrderObserver(
+    WindowButtonOrderObserver* observer) {
   window_button_order_observer_list_.AddObserver(observer);
 }
 
-void LinuxUI::RemoveWindowButtonOrderObserver(
-    views::WindowButtonOrderObserver* observer) {
+void LinuxUi::RemoveWindowButtonOrderObserver(
+    WindowButtonOrderObserver* observer) {
   window_button_order_observer_list_.RemoveObserver(observer);
 }
 
-void LinuxUI::AddDeviceScaleFactorObserver(
-    views::DeviceScaleFactorObserver* observer) {
+void LinuxUi::AddDeviceScaleFactorObserver(
+    DeviceScaleFactorObserver* observer) {
   device_scale_factor_observer_list_.AddObserver(observer);
 }
 
-void LinuxUI::RemoveDeviceScaleFactorObserver(
-    views::DeviceScaleFactorObserver* observer) {
+void LinuxUi::RemoveDeviceScaleFactorObserver(
+    DeviceScaleFactorObserver* observer) {
   device_scale_factor_observer_list_.RemoveObserver(observer);
 }
 
-ui::NativeTheme* LinuxUI::GetNativeTheme(aura::Window* window) const {
+ui::NativeTheme* LinuxUi::GetNativeTheme(aura::Window* window) const {
   return GetNativeTheme(use_system_theme_callback_.is_null() ||
                         use_system_theme_callback_.Run(window));
 }
 
-ui::NativeTheme* LinuxUI::GetNativeTheme(bool use_system_theme) const {
+ui::NativeTheme* LinuxUi::GetNativeTheme(bool use_system_theme) const {
   return use_system_theme ? GetNativeTheme()
                           : ui::NativeTheme::GetInstanceForNativeUi();
 }
 
-void LinuxUI::SetUseSystemThemeCallback(UseSystemThemeCallback callback) {
+void LinuxUi::SetUseSystemThemeCallback(UseSystemThemeCallback callback) {
   use_system_theme_callback_ = std::move(callback);
 }
 
-bool LinuxUI::GetDefaultUsesSystemTheme() const {
+bool LinuxUi::GetDefaultUsesSystemTheme() const {
   std::unique_ptr<base::Environment> env = base::Environment::Create();
 
   // TODO(https://crbug.com/1317782): This logic won't be necessary after
@@ -115,7 +114,7 @@
 }
 
 // static
-LinuxUI::CmdLineArgs LinuxUI::CopyCmdLine(
+LinuxUi::CmdLineArgs LinuxUi::CopyCmdLine(
     const base::CommandLine& command_line) {
   const auto& argv = command_line.argv();
   size_t args_chars = 0;
@@ -135,4 +134,4 @@
   return cmd_line;
 }
 
-}  // namespace views
+}  // namespace ui
diff --git a/ui/views/linux_ui/linux_ui.h b/ui/linux/linux_ui.h
similarity index 85%
rename from ui/views/linux_ui/linux_ui.h
rename to ui/linux/linux_ui.h
index 4a8b3ee..bf0bdd18 100644
--- a/ui/views/linux_ui/linux_ui.h
+++ b/ui/linux/linux_ui.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef UI_VIEWS_LINUX_UI_LINUX_UI_H_
-#define UI_VIEWS_LINUX_UI_LINUX_UI_H_
+#ifndef UI_LINUX_LINUX_UI_H_
+#define UI_LINUX_LINUX_UI_H_
 
 #include <memory>
 #include <string>
@@ -11,6 +11,8 @@
 
 #include "base/callback.h"
 #include "base/command_line.h"
+#include "base/component_export.h"
+#include "base/containers/flat_map.h"
 #include "build/buildflag.h"
 #include "build/chromecast_buildflags.h"
 #include "printing/buildflags/buildflags.h"
@@ -20,9 +22,6 @@
 #include "ui/base/ime/linux/text_edit_key_bindings_delegate_auralinux.h"
 #include "ui/gfx/animation/animation_settings_provider_linux.h"
 #include "ui/gfx/skia_font_delegate.h"
-#include "ui/views/buildflags.h"
-#include "ui/views/controls/button/button.h"
-#include "ui/views/views_export.h"
 
 #if BUILDFLAG(IS_LINUX) && !BUILDFLAG(IS_CASTOS)
 #include "ui/shell_dialogs/shell_dialog_linux.h"
@@ -47,25 +46,28 @@
 class Image;
 }
 
-namespace views {
+namespace ui {
+
 class DeviceScaleFactorObserver;
+class NativeTheme;
 class NavButtonProvider;
 class WindowButtonOrderObserver;
 class WindowFrameProvider;
 
 // Adapter class with targets to render like different toolkits. Set by any
 // project that wants to do linux desktop native rendering.
-class VIEWS_EXPORT LinuxUI : public ui::LinuxInputMethodContextFactory,
-                             public gfx::SkiaFontDelegate,
+class COMPONENT_EXPORT(LINUX_UI) LinuxUi
+    : public ui::LinuxInputMethodContextFactory,
+      public gfx::SkiaFontDelegate,
 #if BUILDFLAG(IS_LINUX) && !BUILDFLAG(IS_CASTOS)
-                             public ui::ShellDialogLinux,
+      public ui::ShellDialogLinux,
 #endif
 #if BUILDFLAG(ENABLE_PRINTING)
-                             public printing::PrintingContextLinuxDelegate,
+      public printing::PrintingContextLinuxDelegate,
 #endif
-                             public ui::TextEditKeyBindingsDelegateAuraLinux,
-                             public ui::CursorThemeManager,
-                             public gfx::AnimationSettingsProviderLinux {
+      public ui::TextEditKeyBindingsDelegateAuraLinux,
+      public ui::CursorThemeManager,
+      public gfx::AnimationSettingsProviderLinux {
  public:
   using UseSystemThemeCallback =
       base::RepeatingCallback<bool(aura::Window* window)>;
@@ -87,19 +89,19 @@
     kRightClick,
   };
 
-  LinuxUI(const LinuxUI&) = delete;
-  LinuxUI& operator=(const LinuxUI&) = delete;
-  ~LinuxUI() override;
+  LinuxUi(const LinuxUi&) = delete;
+  LinuxUi& operator=(const LinuxUi&) = delete;
+  ~LinuxUi() override;
 
   // Sets the dynamically loaded singleton that draws the desktop native UI.
-  static void SetInstance(std::unique_ptr<LinuxUI> instance);
+  static void SetInstance(std::unique_ptr<LinuxUi> instance);
 
   // Returns a LinuxUI instance for the toolkit used in the user's desktop
   // environment.
   //
   // Can return NULL, in case no toolkit has been set. (For example, if we're
   // running with the "--ash" flag.)
-  static LinuxUI* instance();
+  static LinuxUi* instance();
 
   // Notifies the observer about changes about how window buttons should be
   // laid out.
@@ -202,16 +204,16 @@
     std::vector<char> args;
   };
 
-  LinuxUI();
+  LinuxUi();
 
   static CmdLineArgs CopyCmdLine(const base::CommandLine& command_line);
 
-  const base::ObserverList<views::WindowButtonOrderObserver>::Unchecked&
+  const base::ObserverList<WindowButtonOrderObserver>::Unchecked&
   window_button_order_observer_list() const {
     return window_button_order_observer_list_;
   }
 
-  const base::ObserverList<views::DeviceScaleFactorObserver>::Unchecked&
+  const base::ObserverList<DeviceScaleFactorObserver>::Unchecked&
   device_scale_factor_observer_list() const {
     return device_scale_factor_observer_list_;
   }
@@ -225,14 +227,14 @@
   UseSystemThemeCallback use_system_theme_callback_;
 
   // Objects to notify when the window frame button order changes.
-  base::ObserverList<views::WindowButtonOrderObserver>::Unchecked
+  base::ObserverList<WindowButtonOrderObserver>::Unchecked
       window_button_order_observer_list_;
 
   // Objects to notify when the device scale factor changes.
-  base::ObserverList<views::DeviceScaleFactorObserver>::Unchecked
+  base::ObserverList<DeviceScaleFactorObserver>::Unchecked
       device_scale_factor_observer_list_;
 };
 
-}  // namespace views
+}  // namespace ui
 
-#endif  // UI_VIEWS_LINUX_UI_LINUX_UI_H_
+#endif  // UI_LINUX_LINUX_UI_H_
diff --git a/ui/views/linux_ui/linux_ui_factory.cc b/ui/linux/linux_ui_factory.cc
similarity index 81%
rename from ui/views/linux_ui/linux_ui_factory.cc
rename to ui/linux/linux_ui_factory.cc
index 2cebfc0..e43758bb 100644
--- a/ui/views/linux_ui/linux_ui_factory.cc
+++ b/ui/linux/linux_ui_factory.cc
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ui/views/linux_ui/linux_ui_factory.h"
+#include "ui/linux/linux_ui_factory.h"
 
 #include <utility>
 
 #include "ui/base/buildflags.h"
-#include "ui/views/linux_ui/linux_ui.h"
+#include "ui/linux/linux_ui.h"
 
 #if BUILDFLAG(USE_GTK)
 #include "ui/gtk/gtk_ui_factory.h"
@@ -16,12 +16,14 @@
 #include "ui/qt/qt_ui.h"
 #endif
 
-std::unique_ptr<views::LinuxUI> CreateLinuxUi() {
+namespace ui {
+
+std::unique_ptr<LinuxUi> CreateLinuxUi() {
   // TODO(thomasanderson): LinuxUI backend should be chosen depending on the
   // environment.
 #if BUILDFLAG(USE_QT)
   {
-    std::unique_ptr<views::LinuxUI> fallback_linux_ui;
+    std::unique_ptr<LinuxUi> fallback_linux_ui;
 #if BUILDFLAG(USE_GTK)
     fallback_linux_ui = BuildGtkUi();
     if (!fallback_linux_ui->Initialize())
@@ -41,3 +43,5 @@
 #endif
   return nullptr;
 }
+
+}  // namespace ui
\ No newline at end of file
diff --git a/ui/linux/linux_ui_factory.h b/ui/linux/linux_ui_factory.h
new file mode 100644
index 0000000..72f665e
--- /dev/null
+++ b/ui/linux/linux_ui_factory.h
@@ -0,0 +1,20 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_LINUX_LINUX_UI_FACTORY_H_
+#define UI_LINUX_LINUX_UI_FACTORY_H_
+
+#include <memory>
+
+namespace ui {
+
+class LinuxUi;
+
+// Returns a new LinuxUI based on a Linux toolkit.  May return nullptr if the
+// preferred toolkits are unavailable.
+std::unique_ptr<LinuxUi> CreateLinuxUi();
+
+}  // namespace ui
+
+#endif  // UI_LINUX_LINUX_UI_FACTORY_H_
diff --git a/ui/views/linux_ui/nav_button_provider.h b/ui/linux/nav_button_provider.h
similarity index 74%
rename from ui/views/linux_ui/nav_button_provider.h
rename to ui/linux/nav_button_provider.h
index 1c07d4f..aee2714 100644
--- a/ui/views/linux_ui/nav_button_provider.h
+++ b/ui/linux/nav_button_provider.h
@@ -2,12 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef UI_VIEWS_LINUX_UI_NAV_BUTTON_PROVIDER_H_
-#define UI_VIEWS_LINUX_UI_NAV_BUTTON_PROVIDER_H_
-
-#include "build/buildflag.h"
-#include "ui/views/buildflags.h"
-#include "ui/views/controls/button/button.h"
+#ifndef UI_LINUX_NAV_BUTTON_PROVIDER_H_
+#define UI_LINUX_NAV_BUTTON_PROVIDER_H_
 
 namespace chrome {
 enum class FrameButtonDisplayType;
@@ -18,7 +14,7 @@
 class Insets;
 }  // namespace gfx
 
-namespace views {
+namespace ui {
 
 class NavButtonProvider {
  public:
@@ -34,7 +30,15 @@
     kClose,
   };
 
-  virtual ~NavButtonProvider() {}
+  // This enum is based on views::Button::ButtonState.
+  enum class ButtonState {
+    kNormal,
+    kHovered,
+    kPressed,
+    kDisabled,
+  };
+
+  virtual ~NavButtonProvider() = default;
 
   // Redraws all images and updates all size state.  |top_area_height|
   // is the total available height to render the buttons, and buttons
@@ -45,15 +49,13 @@
                             bool active) = 0;
 
   // Gets the cached button image corresponding to |type| and |state|.
-  virtual gfx::ImageSkia GetImage(
-      views::NavButtonProvider::FrameButtonDisplayType type,
-      views::Button::ButtonState state) const = 0;
+  virtual gfx::ImageSkia GetImage(FrameButtonDisplayType type,
+                                  ButtonState state) const = 0;
 
   // Gets the external margin around each button.  The left inset
   // represents the leading margin, and the right inset represents the
   // trailing margin.
-  virtual gfx::Insets GetNavButtonMargin(
-      views::NavButtonProvider::FrameButtonDisplayType type) const = 0;
+  virtual gfx::Insets GetNavButtonMargin(FrameButtonDisplayType type) const = 0;
 
   // Gets the internal spacing (padding + border) of the top area.
   // The left inset represents the leading spacing, and the right
@@ -64,6 +66,6 @@
   virtual int GetInterNavButtonSpacing() const = 0;
 };
 
-}  // namespace views
+}  // namespace ui
 
-#endif  // UI_VIEWS_LINUX_UI_NAV_BUTTON_PROVIDER_H_
+#endif  // UI_LINUX_NAV_BUTTON_PROVIDER_H_
diff --git a/ui/views/linux_ui/status_icon_linux.cc b/ui/linux/status_icon_linux.cc
similarity index 84%
rename from ui/views/linux_ui/status_icon_linux.cc
rename to ui/linux/status_icon_linux.cc
index e861e157..3e8f5c9 100644
--- a/ui/views/linux_ui/status_icon_linux.cc
+++ b/ui/linux/status_icon_linux.cc
@@ -2,9 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ui/views/linux_ui/status_icon_linux.h"
+#include "ui/linux/status_icon_linux.h"
 
-namespace views {
+namespace ui {
 
 StatusIconLinux::Delegate::~Delegate() = default;
 
@@ -21,4 +21,4 @@
   OnSetDelegate();
 }
 
-}  // namespace views
+}  // namespace ui
diff --git a/ui/views/linux_ui/status_icon_linux.h b/ui/linux/status_icon_linux.h
similarity index 84%
rename from ui/views/linux_ui/status_icon_linux.h
rename to ui/linux/status_icon_linux.h
index 58458bd4..7f2d2bd0 100644
--- a/ui/views/linux_ui/status_icon_linux.h
+++ b/ui/linux/status_icon_linux.h
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef UI_VIEWS_LINUX_UI_STATUS_ICON_LINUX_H_
-#define UI_VIEWS_LINUX_UI_STATUS_ICON_LINUX_H_
+#ifndef UI_LINUX_STATUS_ICON_LINUX_H_
+#define UI_LINUX_STATUS_ICON_LINUX_H_
 
 #include <string>
 
+#include "base/component_export.h"
 #include "base/memory/raw_ptr.h"
-#include "ui/views/views_export.h"
 
 namespace gfx {
 class ImageSkia;
@@ -18,13 +18,13 @@
 class MenuModel;
 }  // namespace ui
 
-namespace views {
+namespace ui {
 
-// Since liblinux_ui cannot have dependencies on any chrome browser components
+// Since linux_ui cannot have dependencies on any chrome browser components
 // we cannot inherit from StatusIcon. So we implement the necessary methods
 // and let a wrapper class implement the StatusIcon interface and defer the
 // callbacks to a delegate. For the same reason, do not use StatusIconMenuModel.
-class VIEWS_EXPORT StatusIconLinux {
+class COMPONENT_EXPORT(LINUX_UI) StatusIconLinux {
  public:
   class Delegate {
    public:
@@ -68,6 +68,6 @@
   raw_ptr<Delegate> delegate_ = nullptr;
 };
 
-}  // namespace views
+}  // namespace ui
 
-#endif  // UI_VIEWS_LINUX_UI_STATUS_ICON_LINUX_H_
+#endif  // UI_LINUX_STATUS_ICON_LINUX_H_
diff --git a/ui/views/linux_ui/window_button_order_observer.h b/ui/linux/window_button_order_observer.h
similarity index 63%
rename from ui/views/linux_ui/window_button_order_observer.h
rename to ui/linux/window_button_order_observer.h
index 584dca33..ce26875 100644
--- a/ui/views/linux_ui/window_button_order_observer.h
+++ b/ui/linux/window_button_order_observer.h
@@ -2,12 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef UI_VIEWS_LINUX_UI_WINDOW_BUTTON_ORDER_OBSERVER_H_
-#define UI_VIEWS_LINUX_UI_WINDOW_BUTTON_ORDER_OBSERVER_H_
+#ifndef UI_LINUX_WINDOW_BUTTON_ORDER_OBSERVER_H_
+#define UI_LINUX_WINDOW_BUTTON_ORDER_OBSERVER_H_
 
-#include "ui/views/window/frame_buttons.h"
-
-namespace views {
+namespace ui {
 
 // Observer interface to receive the ordering of the min,max,close buttons.
 class WindowButtonOrderObserver {
@@ -19,6 +17,6 @@
   virtual ~WindowButtonOrderObserver() = default;
 };
 
-}  // namespace views
+}  // namespace ui
 
-#endif  // UI_VIEWS_LINUX_UI_WINDOW_BUTTON_ORDER_OBSERVER_H_
+#endif  // UI_LINUX_WINDOW_BUTTON_ORDER_OBSERVER_H_
diff --git a/ui/views/linux_ui/window_frame_provider.h b/ui/linux/window_frame_provider.h
similarity index 83%
rename from ui/views/linux_ui/window_frame_provider.h
rename to ui/linux/window_frame_provider.h
index c8124c3..4708e303 100644
--- a/ui/views/linux_ui/window_frame_provider.h
+++ b/ui/linux/window_frame_provider.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef UI_VIEWS_LINUX_UI_WINDOW_FRAME_PROVIDER_H_
-#define UI_VIEWS_LINUX_UI_WINDOW_FRAME_PROVIDER_H_
+#ifndef UI_LINUX_WINDOW_FRAME_PROVIDER_H_
+#define UI_LINUX_WINDOW_FRAME_PROVIDER_H_
 
 namespace gfx {
 class Canvas;
@@ -11,7 +11,7 @@
 class Rect;
 }  // namespace gfx
 
-namespace views {
+namespace ui {
 
 class WindowFrameProvider {
  public:
@@ -33,6 +33,6 @@
                                 bool focused) = 0;
 };
 
-}  // namespace views
+}  // namespace ui
 
-#endif  // UI_VIEWS_LINUX_UI_WINDOW_FRAME_PROVIDER_H_
+#endif  // UI_LINUX_WINDOW_FRAME_PROVIDER_H_
diff --git a/ui/native_theme/native_theme_mac.mm b/ui/native_theme/native_theme_mac.mm
index adf8dc37..d3c6ede 100644
--- a/ui/native_theme/native_theme_mac.mm
+++ b/ui/native_theme/native_theme_mac.mm
@@ -230,29 +230,29 @@
     ColorScheme color_scheme) const {
   gfx::Canvas paint_canvas(canvas, 1.0f);
   // Select colors.
-  std::vector<SkColor> gradient_colors;
+  std::vector<SkColor4f> gradient_colors;
   bool dark_mode = color_scheme == ColorScheme::kDark;
   if (extra_params.is_overlay) {
     if (dark_mode) {
-      gradient_colors = {SkColorSetARGB(0x28, 0xD8, 0xD8, 0xD8),
-                         SkColorSetARGB(0x26, 0xCC, 0xCC, 0xCC),
-                         SkColorSetARGB(0x26, 0xCC, 0xCC, 0xCC),
-                         SkColorSetARGB(0x26, 0xCC, 0xCC, 0xCC)};
+      gradient_colors = {SkColor4f{0.847f, 0.847f, 0.847f, 0.157f},
+                         SkColor4f{0.8f, 0.8f, 0.8f, 0.149f},
+                         SkColor4f{0.8f, 0.8f, 0.8f, 0.149f},
+                         SkColor4f{0.8f, 0.8f, 0.8f, 0.149f}};
     } else {
-      gradient_colors = {SkColorSetARGB(0xC6, 0xF8, 0xF8, 0xF8),
-                         SkColorSetARGB(0xC2, 0xF8, 0xF8, 0xF8),
-                         SkColorSetARGB(0xC2, 0xF8, 0xF8, 0xF8),
-                         SkColorSetARGB(0xC2, 0xF8, 0xF8, 0xF8)};
+      gradient_colors = {SkColor4f{0.973f, 0.973f, 0.973f, 0.776f},
+                         SkColor4f{0.973f, 0.973f, 0.973f, 0.761f},
+                         SkColor4f{0.973f, 0.973f, 0.973f, 0.761f},
+                         SkColor4f{0.973f, 0.973f, 0.973f, 0.761f}};
     }
   } else {
     // Non-overlay scroller track colors are not transparent. On Safari, they
     // are, but on all other macOS applications they are not.
     if (dark_mode) {
-      gradient_colors = {SkColorSetRGB(0x2D, 0x2D, 0x2D),
-                         SkColorSetRGB(0x2B, 0x2B, 0x2B)};
+      gradient_colors = {SkColor4f{0.176f, 0.176f, 0.176f, 1.0f},
+                         SkColor4f{0.169f, 0.169f, 0.169f, 1.0f}};
     } else {
-      gradient_colors = {SkColorSetRGB(0xFA, 0xFA, 0xFA),
-                         SkColorSetRGB(0xFA, 0xFA, 0xFA)};
+      gradient_colors = {SkColor4f{0.98f, 0.98f, 0.98f, 1.0f},
+                         SkColor4f{0.98f, 0.98f, 0.98f, 1.0f}};
     }
   }
 
diff --git a/ui/ozone/platform/wayland/emulate/wayland_input_emulate.cc b/ui/ozone/platform/wayland/emulate/wayland_input_emulate.cc
index 7a862b0..785010a 100644
--- a/ui/ozone/platform/wayland/emulate/wayland_input_emulate.cc
+++ b/ui/ozone/platform/wayland/emulate/wayland_input_emulate.cc
@@ -127,19 +127,6 @@
   DCHECK(wayland_proxy);
 
   auto* wlsurface = wayland_proxy->GetWlSurfaceForAcceleratedWidget(widget);
-
-  // If it's a toplevel window, activate it. This results in raising the the
-  // parent window and its children windows.
-  // TODO(oshima): This is probably not right because a inactive window can
-  // still receive mouse wheel event, and you may want to move a mouse pointer
-  // w/o activating a window. A window will be activated when a mouse is
-  // clicked.
-  auto window_type = wayland_proxy->GetWindowType(widget);
-  if (window_type != ui::PlatformWindowType::kTooltip &&
-      window_type != ui::PlatformWindowType::kMenu &&
-      !wayland_proxy->WindowHasPointerFocus(widget)) {
-    weston_test_activate_surface(weston_test_, wlsurface);
-  }
   bool screen_coordinates =
       wayland_proxy->GetWaylandWindowForAcceleratedWidget(widget)
           ->IsScreenCoordinatesEnabled();
diff --git a/ui/qt/BUILD.gn b/ui/qt/BUILD.gn
index 1c67c69..af3dc60 100644
--- a/ui/qt/BUILD.gn
+++ b/ui/qt/BUILD.gn
@@ -79,7 +79,7 @@
 }
 
 component("qt") {
-  visibility = [ "//ui/views/linux_ui:linux_ui_factory" ]
+  visibility = [ "//ui/linux:linux_ui_factory" ]
 
   defines = [ "IS_QT_IMPL" ]
 
@@ -94,6 +94,7 @@
     "//ui/color",
     "//ui/color:mixers",
     "//ui/gfx",
+    "//ui/linux:linux_ui",
     "//ui/native_theme",
     "//ui/shell_dialogs",
     "//ui/views",
diff --git a/ui/qt/DEPS b/ui/qt/DEPS
index 819fddb..1d8a7b31 100644
--- a/ui/qt/DEPS
+++ b/ui/qt/DEPS
@@ -5,6 +5,7 @@
   "+ui/base",
   "+ui/color",
   "+ui/gfx",
+  "+ui/linux",
   "+ui/native_theme",
   "+ui/shell_dialogs",
   "+ui/views",
diff --git a/ui/qt/qt_ui.cc b/ui/qt/qt_ui.cc
index 9bd43c48..4200b42c 100644
--- a/ui/qt/qt_ui.cc
+++ b/ui/qt/qt_ui.cc
@@ -27,12 +27,12 @@
 #include "ui/gfx/image/image.h"
 #include "ui/gfx/image/image_skia_rep.h"
 #include "ui/gfx/image/image_skia_source.h"
+#include "ui/linux/linux_ui.h"
 #include "ui/native_theme/native_theme_aura.h"
 #include "ui/native_theme/native_theme_base.h"
 #include "ui/qt/qt_interface.h"
 #include "ui/shell_dialogs/select_file_policy.h"
 #include "ui/views/controls/button/label_button_border.h"
-#include "ui/views/linux_ui/linux_ui.h"
 
 namespace qt {
 
@@ -115,7 +115,7 @@
   raw_ptr<QtInterface> const shim_;
 };
 
-QtUi::QtUi(std::unique_ptr<views::LinuxUI> fallback_linux_ui)
+QtUi::QtUi(std::unique_ptr<ui::LinuxUi> fallback_linux_ui)
     : fallback_linux_ui_(std::move(fallback_linux_ui)) {}
 
 QtUi::~QtUi() = default;
@@ -267,12 +267,12 @@
   return shim_->GetAnimationDurationMs() > 0;
 }
 
-std::unique_ptr<views::NavButtonProvider> QtUi::CreateNavButtonProvider() {
+std::unique_ptr<ui::NavButtonProvider> QtUi::CreateNavButtonProvider() {
   // QT prefers server-side decorations.
   return nullptr;
 }
 
-views::WindowFrameProvider* QtUi::GetWindowFrameProvider(bool solid_frame) {
+ui::WindowFrameProvider* QtUi::GetWindowFrameProvider(bool solid_frame) {
   // QT prefers server-side decorations.
   return nullptr;
 }
@@ -501,8 +501,8 @@
   }
 }
 
-std::unique_ptr<views::LinuxUI> CreateQtUi(
-    std::unique_ptr<views::LinuxUI> fallback_linux_ui) {
+std::unique_ptr<ui::LinuxUi> CreateQtUi(
+    std::unique_ptr<ui::LinuxUi> fallback_linux_ui) {
   return std::make_unique<QtUi>(std::move(fallback_linux_ui));
 }
 
diff --git a/ui/qt/qt_ui.h b/ui/qt/qt_ui.h
index 4b751e4..c45b2ae 100644
--- a/ui/qt/qt_ui.h
+++ b/ui/qt/qt_ui.h
@@ -10,8 +10,10 @@
 #include "base/component_export.h"
 #include "printing/buildflags/buildflags.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
+#include "ui/color/color_provider.h"
+#include "ui/color/color_provider_manager.h"
+#include "ui/linux/linux_ui.h"
 #include "ui/qt/qt_interface.h"
-#include "ui/views/linux_ui/linux_ui.h"
 
 #if BUILDFLAG(ENABLE_PRINTING)
 #include "printing/printing_context_linux.h"  // nogncheck
@@ -22,9 +24,9 @@
 class QtNativeTheme;
 
 // Interface to QT desktop features.
-class QtUi : public views::LinuxUI, QtInterface::Delegate {
+class QtUi : public ui::LinuxUi, QtInterface::Delegate {
  public:
-  explicit QtUi(std::unique_ptr<views::LinuxUI> fallback_linux_uik);
+  explicit QtUi(std::unique_ptr<ui::LinuxUi> fallback_linux_uik);
 
   QtUi(const QtUi&) = delete;
   QtUi& operator=(const QtUi&) = delete;
@@ -49,7 +51,7 @@
       ui::SelectFileDialog::Listener* listener,
       std::unique_ptr<ui::SelectFilePolicy> policy) const override;
 
-  // views::LinuxUI:
+  // ui::LinuxUi:
   bool Initialize() override;
   bool GetColor(int id, SkColor* color, bool use_custom_frame) const override;
   bool GetDisplayProperty(int id, int* result) const override;
@@ -67,8 +69,8 @@
   float GetDeviceScaleFactor() const override;
   bool PreferDarkTheme() const override;
   bool AnimationsEnabled() const override;
-  std::unique_ptr<views::NavButtonProvider> CreateNavButtonProvider() override;
-  views::WindowFrameProvider* GetWindowFrameProvider(bool solid_frame) override;
+  std::unique_ptr<ui::NavButtonProvider> CreateNavButtonProvider() override;
+  ui::WindowFrameProvider* GetWindowFrameProvider(bool solid_frame) override;
   base::flat_map<std::string, std::string> GetKeyboardLayoutMap() override;
   std::string GetCursorThemeName() override;
   int GetCursorThemeSize() override;
@@ -99,7 +101,7 @@
 
   // TODO(https://crbug.com/1317782): This is a fallback for any unimplemented
   // functionality in the QT backend and should eventually be removed.
-  std::unique_ptr<views::LinuxUI> fallback_linux_ui_;
+  std::unique_ptr<ui::LinuxUi> fallback_linux_ui_;
 
   // QT modifies argc and argv, and they must be kept alive while
   // `shim_` is alive.
@@ -120,8 +122,8 @@
 
 // This should be the only symbol exported from this component.
 COMPONENT_EXPORT(QT)
-std::unique_ptr<views::LinuxUI> CreateQtUi(
-    std::unique_ptr<views::LinuxUI> fallback_linux_ui);
+std::unique_ptr<ui::LinuxUi> CreateQtUi(
+    std::unique_ptr<ui::LinuxUi> fallback_linux_ui);
 
 }  // namespace qt
 
diff --git a/ui/views/BUILD.gn b/ui/views/BUILD.gn
index c9415dc..a5f5758b 100644
--- a/ui/views/BUILD.gn
+++ b/ui/views/BUILD.gn
@@ -8,7 +8,6 @@
 import("//build/config/ozone.gni")
 import("//build/config/ui.gni")
 import("//components/vector_icons/vector_icons.gni")
-import("//printing/buildflags/buildflags.gni")
 import("//skia/features.gni")
 import("//testing/test.gni")
 import("//ui/base/ui_features.gni")
@@ -575,31 +574,8 @@
   }
 
   if (is_linux || is_chromeos_lacros) {
-    public_deps += [
-      "//printing/buildflags",
-      "//ui/base/cursor:theme_manager",
-    ]
-    deps += [
-      "//build:chromecast_buildflags",
-      "//ui/base/ime/linux",
-      "//ui/shell_dialogs",
-    ]
-    if (enable_basic_printing) {
-      deps += [ "//printing" ]
-    }
-    public += [
-      "linux_ui/device_scale_factor_observer.h",
-      "linux_ui/linux_ui.h",
-      "linux_ui/nav_button_provider.h",
-      "linux_ui/status_icon_linux.h",
-      "linux_ui/window_button_order_observer.h",
-      "linux_ui/window_frame_provider.h",
-    ]
-    sources += [
-      "linux_ui/linux_ui.cc",
-      "linux_ui/status_icon_linux.cc",
-    ]
-
+    deps += [ "//ui/base/ime/linux" ]
+    public_deps += [ "//ui/linux:linux_ui" ]
     if (!is_chromeos_lacros) {
       sources += [ "controls/menu/menu_config_linux.cc" ]
     }
diff --git a/ui/views/DEPS b/ui/views/DEPS
index 82f0dba..d60b866 100644
--- a/ui/views/DEPS
+++ b/ui/views/DEPS
@@ -19,6 +19,7 @@
   "+ui/events",
   "+ui/gfx",
   "+ui/gl/test/gl_surface_test_support.h",  # To initialize GL for tests.
+  "+ui/linux",
   "+ui/lottie",
   "+ui/native_theme",
   "+ui/ozone/buildflags.h",
diff --git a/ui/views/color_chooser/color_chooser_view.cc b/ui/views/color_chooser/color_chooser_view.cc
index 51af8d0..30fe586 100644
--- a/ui/views/color_chooser/color_chooser_view.cc
+++ b/ui/views/color_chooser/color_chooser_view.cc
@@ -115,7 +115,9 @@
                       SkColor end_color,
                       bool is_horizontal,
                       gfx::Canvas* canvas) {
-  SkColor colors[2] = {start_color, end_color};
+  // TODO(crbug/1308932): Remove FromColor and make all SkColor4f.
+  SkColor4f colors[2] = {SkColor4f::FromColor(start_color),
+                         SkColor4f::FromColor(end_color)};
   SkPoint points[2];
   points[0].iset(0, 0);
   if (is_horizontal)
diff --git a/ui/views/examples/animation_example.cc b/ui/views/examples/animation_example.cc
index 25a7db5..c8a83f73 100644
--- a/ui/views/examples/animation_example.cc
+++ b/ui/views/examples/animation_example.cc
@@ -72,8 +72,10 @@
 void AnimatingSquare::OnPaint(gfx::Canvas* canvas) {
   View::OnPaint(canvas);
   const SkColor color = SkColorSetRGB((5 - index_) * 51, 0, index_ * 51);
-  const SkColor colors[2] = {color,
-                             color_utils::HSLShift(color, {-1.0, -1.0, 0.75})};
+  // TODO(crbug/1308932): Remove this FromColor and make all SkColor4f.
+  const SkColor4f colors[2] = {
+      SkColor4f::FromColor(color),
+      SkColor4f::FromColor(color_utils::HSLShift(color, {-1.0, -1.0, 0.75}))};
   cc::PaintFlags flags;
   gfx::Rect local_bounds = gfx::Rect(layer()->size());
   const float dsf = canvas->UndoDeviceScaleFactor();
diff --git a/ui/views/linux_ui/BUILD.gn b/ui/views/linux_ui/BUILD.gn
deleted file mode 100644
index 19593ab..0000000
--- a/ui/views/linux_ui/BUILD.gn
+++ /dev/null
@@ -1,22 +0,0 @@
-# Copyright 2022 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//build/config/linux/gtk/gtk.gni")
-import("//ui/qt/qt.gni")
-
-source_set("linux_ui_factory") {
-  sources = [
-    "linux_ui_factory.cc",
-    "linux_ui_factory.h",
-  ]
-
-  deps = [ "//ui/views" ]
-  if (use_gtk) {
-    # This is the only component that can interact with gtk.
-    deps += [ "//ui/gtk" ]
-  }
-  if (use_qt) {
-    deps += [ "//ui/qt" ]
-  }
-}
diff --git a/ui/views/linux_ui/DEPS b/ui/views/linux_ui/DEPS
deleted file mode 100644
index fe20be1..0000000
--- a/ui/views/linux_ui/DEPS
+++ /dev/null
@@ -1,11 +0,0 @@
-include_rules = [
-  "+printing",
-  "+ui/shell_dialogs",
-]
-
-specific_include_rules = {
-  "linux_ui_factory.cc": [
-    "+ui/gtk",
-    "+ui/qt",
-  ],
-}
\ No newline at end of file
diff --git a/ui/views/linux_ui/device_scale_factor_observer.h b/ui/views/linux_ui/device_scale_factor_observer.h
deleted file mode 100644
index c62bfeb..0000000
--- a/ui/views/linux_ui/device_scale_factor_observer.h
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef UI_VIEWS_LINUX_UI_DEVICE_SCALE_FACTOR_OBSERVER_H_
-#define UI_VIEWS_LINUX_UI_DEVICE_SCALE_FACTOR_OBSERVER_H_
-
-namespace views {
-
-class DeviceScaleFactorObserver {
- public:
-  virtual ~DeviceScaleFactorObserver() {}
-
-  virtual void OnDeviceScaleFactorChanged() = 0;
-};
-
-}  // namespace views
-
-#endif  // UI_VIEWS_LINUX_UI_DEVICE_SCALE_FACTOR_OBSERVER_H_
diff --git a/ui/views/linux_ui/linux_ui_factory.h b/ui/views/linux_ui/linux_ui_factory.h
deleted file mode 100644
index b2b2cb4..0000000
--- a/ui/views/linux_ui/linux_ui_factory.h
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 2022 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef UI_VIEWS_LINUX_UI_LINUX_UI_FACTORY_H_
-#define UI_VIEWS_LINUX_UI_LINUX_UI_FACTORY_H_
-
-#include <memory>
-
-namespace views {
-class LinuxUI;
-}
-
-// Returns a new LinuxUI based on a Linux toolkit.  May return nullptr if the
-// preferred toolkits are unavailable.
-std::unique_ptr<views::LinuxUI> CreateLinuxUi();
-
-#endif  // UI_VIEWS_LINUX_UI_LINUX_UI_FACTORY_H_
diff --git a/ui/views/widget/desktop_aura/desktop_screen_ozone_linux.cc b/ui/views/widget/desktop_aura/desktop_screen_ozone_linux.cc
index 8839e76b..6a27456 100644
--- a/ui/views/widget/desktop_aura/desktop_screen_ozone_linux.cc
+++ b/ui/views/widget/desktop_aura/desktop_screen_ozone_linux.cc
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "base/notreached.h"
+#include "base/scoped_observation.h"
+#include "ui/linux/device_scale_factor_observer.h"
+#include "ui/linux/linux_ui.h"
 #include "ui/ozone/public/platform_screen.h"
-#include "ui/views/linux_ui/device_scale_factor_observer.h"
-#include "ui/views/linux_ui/linux_ui.h"
 #include "ui/views/widget/desktop_aura/desktop_screen.h"
 #include "ui/views/widget/desktop_aura/desktop_screen_ozone.h"
 
@@ -14,7 +14,7 @@
 // Listens to device scale factor changes that can be provided via "external" to
 // Ozone sources such as toolkits, etc, and provides Ozone with new values.
 class DesktopScreenOzoneLinux : public DesktopScreenOzone,
-                                public DeviceScaleFactorObserver {
+                                public ui::DeviceScaleFactorObserver {
  public:
   DesktopScreenOzoneLinux() = default;
   ~DesktopScreenOzoneLinux() override = default;
@@ -23,7 +23,7 @@
   // DeviceScaleFactorObserver:
   void OnDeviceScaleFactorChanged() override {
     SetDeviceScaleFactorToPlatformScreen(
-        LinuxUI::instance()->GetDeviceScaleFactor());
+        ui::LinuxUi::instance()->GetDeviceScaleFactor());
   }
 
   void SetDeviceScaleFactorToPlatformScreen(float scale_factor) {
@@ -40,7 +40,7 @@
   // set the display scale factor as early as possible so that list of displays
   // have correct scale factor from the beginning.
   void OnBeforePlatformScreenInit() override {
-    auto* linux_ui = LinuxUI::instance();
+    auto* linux_ui = ui::LinuxUi::instance();
     if (linux_ui) {
       display_scale_factor_observer_.Observe(linux_ui);
       // Send current scale factor as starting to observe doesn't actually
@@ -49,10 +49,10 @@
     }
   }
 
-  base::ScopedObservation<LinuxUI,
+  base::ScopedObservation<ui::LinuxUi,
                           DeviceScaleFactorObserver,
-                          &LinuxUI::AddDeviceScaleFactorObserver,
-                          &LinuxUI::RemoveDeviceScaleFactorObserver>
+                          &ui::LinuxUi::AddDeviceScaleFactorObserver,
+                          &ui::LinuxUi::RemoveDeviceScaleFactorObserver>
       display_scale_factor_observer_{this};
 };
 
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.cc
index d8d30be..5cb499f 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.cc
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.cc
@@ -20,13 +20,13 @@
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
 #include "ui/events/event.h"
+#include "ui/linux/linux_ui.h"
 #include "ui/platform_window/extensions/desk_extension.h"
 #include "ui/platform_window/extensions/pinned_mode_extension.h"
 #include "ui/platform_window/extensions/wayland_extension.h"
 #include "ui/platform_window/extensions/x11_extension.h"
 #include "ui/platform_window/platform_window_init_properties.h"
 #include "ui/platform_window/wm/wm_move_resize_handler.h"
-#include "ui/views/linux_ui/linux_ui.h"
 #include "ui/views/views_delegate.h"
 #include "ui/views/widget/widget.h"
 
@@ -259,7 +259,7 @@
 void DesktopWindowTreeHostLinux::AddAdditionalInitProperties(
     const Widget::InitParams& params,
     ui::PlatformWindowInitProperties* properties) {
-  const views::LinuxUI* linux_ui = views::LinuxUI::instance();
+  const ui::LinuxUi* linux_ui = ui::LinuxUi::instance();
   properties->prefer_dark_theme = linux_ui && linux_ui->PreferDarkTheme();
 
   // Set the background color on startup to make the initial flickering
@@ -295,7 +295,7 @@
 
 base::flat_map<std::string, std::string>
 DesktopWindowTreeHostLinux::GetKeyboardLayoutMap() {
-  if (auto* linux_ui = LinuxUI::instance())
+  if (auto* linux_ui = ui::LinuxUi::instance())
     return linux_ui->GetKeyboardLayoutMap();
   return WindowTreeHostPlatform::GetKeyboardLayoutMap();
 }
diff --git a/ui/views/widget/desktop_aura/window_event_filter_linux.cc b/ui/views/widget/desktop_aura/window_event_filter_linux.cc
index ed48c4c..98e1761 100644
--- a/ui/views/widget/desktop_aura/window_event_filter_linux.cc
+++ b/ui/views/widget/desktop_aura/window_event_filter_linux.cc
@@ -14,8 +14,8 @@
 #include "ui/display/screen.h"
 #include "ui/events/event.h"
 #include "ui/events/event_utils.h"
+#include "ui/linux/linux_ui.h"
 #include "ui/platform_window/wm/wm_move_resize_handler.h"
-#include "ui/views/linux_ui/linux_ui.h"
 #include "ui/views/widget/desktop_aura/desktop_window_tree_host_platform.h"
 #include "ui/views/widget/native_widget_aura.h"
 #include "ui/views/widget/widget.h"
@@ -75,23 +75,23 @@
 
 void WindowEventFilterLinux::OnClickedCaption(ui::MouseEvent* event,
                                               int previous_click_component) {
-  LinuxUI* linux_ui = LinuxUI::instance();
+  ui::LinuxUi* linux_ui = ui::LinuxUi::instance();
 
-  views::LinuxUI::WindowFrameActionSource action_type;
-  views::LinuxUI::WindowFrameAction default_action;
+  ui::LinuxUi::WindowFrameActionSource action_type;
+  ui::LinuxUi::WindowFrameAction default_action;
 
   if (event->IsRightMouseButton()) {
-    action_type = LinuxUI::WindowFrameActionSource::kRightClick;
-    default_action = LinuxUI::WindowFrameAction::kMenu;
+    action_type = ui::LinuxUi::WindowFrameActionSource::kRightClick;
+    default_action = ui::LinuxUi::WindowFrameAction::kMenu;
   } else if (event->IsMiddleMouseButton()) {
-    action_type = LinuxUI::WindowFrameActionSource::kMiddleClick;
-    default_action = LinuxUI::WindowFrameAction::kNone;
+    action_type = ui::LinuxUi::WindowFrameActionSource::kMiddleClick;
+    default_action = ui::LinuxUi::WindowFrameAction::kNone;
   } else if (event->IsLeftMouseButton() &&
              event->flags() & ui::EF_IS_DOUBLE_CLICK) {
     click_component_ = HTNOWHERE;
     if (previous_click_component == HTCAPTION) {
-      action_type = LinuxUI::WindowFrameActionSource::kDoubleClick;
-      default_action = LinuxUI::WindowFrameAction::kToggleMaximize;
+      action_type = ui::LinuxUi::WindowFrameActionSource::kDoubleClick;
+      default_action = ui::LinuxUi::WindowFrameAction::kToggleMaximize;
     } else {
       return;
     }
@@ -101,24 +101,24 @@
   }
 
   auto* content_window = desktop_window_tree_host_->GetContentWindow();
-  LinuxUI::WindowFrameAction action =
+  ui::LinuxUi::WindowFrameAction action =
       linux_ui ? linux_ui->GetWindowFrameAction(action_type) : default_action;
   switch (action) {
-    case LinuxUI::WindowFrameAction::kNone:
+    case ui::LinuxUi::WindowFrameAction::kNone:
       break;
-    case LinuxUI::WindowFrameAction::kLower:
+    case ui::LinuxUi::WindowFrameAction::kLower:
       LowerWindow();
       event->SetHandled();
       break;
-    case LinuxUI::WindowFrameAction::kMinimize:
+    case ui::LinuxUi::WindowFrameAction::kMinimize:
       desktop_window_tree_host_->Minimize();
       event->SetHandled();
       break;
-    case LinuxUI::WindowFrameAction::kToggleMaximize:
+    case ui::LinuxUi::WindowFrameAction::kToggleMaximize:
       MaybeToggleMaximizedState(content_window);
       event->SetHandled();
       break;
-    case LinuxUI::WindowFrameAction::kMenu:
+    case ui::LinuxUi::WindowFrameAction::kMenu:
       views::Widget* widget =
           views::Widget::GetWidgetForNativeView(content_window);
       if (!widget)
diff --git a/ui/views/widget/widget.cc b/ui/views/widget/widget.cc
index 046d3cf..2cb71ae39 100644
--- a/ui/views/widget/widget.cc
+++ b/ui/views/widget/widget.cc
@@ -49,7 +49,7 @@
 #include "ui/views/window/dialog_delegate.h"
 
 #if BUILDFLAG(IS_LINUX)
-#include "ui/views/linux_ui/linux_ui.h"
+#include "ui/linux/linux_ui.h"
 #endif
 
 namespace views {
@@ -1820,7 +1820,7 @@
     return parent_->GetNativeTheme();
 
 #if BUILDFLAG(IS_LINUX)
-  if (const views::LinuxUI* linux_ui = views::LinuxUI::instance()) {
+  if (const ui::LinuxUi* linux_ui = ui::LinuxUi::instance()) {
     if (auto* native_theme = linux_ui->GetNativeTheme(GetNativeWindow()))
       return native_theme;
   }
diff --git a/ui/webui/resources/.eslintrc.js b/ui/webui/resources/.eslintrc.js
new file mode 100644
index 0000000..2ca8df37
--- /dev/null
+++ b/ui/webui/resources/.eslintrc.js
@@ -0,0 +1,9 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module.exports = {
+  'rules' : {
+    'comma-dangle' : ['error', 'always-multiline'],
+  },
+};
diff --git a/ui/webui/resources/cr_components/certificate_manager/certificate_provisioning_list.ts b/ui/webui/resources/cr_components/certificate_manager/certificate_provisioning_list.ts
index 93695aaf..32f1e6b 100644
--- a/ui/webui/resources/cr_components/certificate_manager/certificate_provisioning_list.ts
+++ b/ui/webui/resources/cr_components/certificate_manager/certificate_provisioning_list.ts
@@ -40,7 +40,7 @@
         type: Array,
         value() {
           return [];
-        }
+        },
       },
 
       /**
diff --git a/ui/webui/resources/cr_components/chromeos/bluetooth/bluetooth_battery_icon_percentage.js b/ui/webui/resources/cr_components/chromeos/bluetooth/bluetooth_battery_icon_percentage.js
index 6003ccf..309ea27 100644
--- a/ui/webui/resources/cr_components/chromeos/bluetooth/bluetooth_battery_icon_percentage.js
+++ b/ui/webui/resources/cr_components/chromeos/bluetooth/bluetooth_battery_icon_percentage.js
@@ -34,8 +34,20 @@
  * @type {Array<Array<number>>}
  */
 const BATTERY_ICONS_RANGES = [
-  [0, 7], [8, 14], [15, 21], [22, 28], [29, 35], [36, 42], [43, 49], [50, 56],
-  [57, 63], [64, 70], [71, 77], [78, 85], [86, 92], [93, 100]
+  [0, 7],
+  [8, 14],
+  [15, 21],
+  [22, 28],
+  [29, 35],
+  [36, 42],
+  [43, 49],
+  [50, 56],
+  [57, 63],
+  [64, 70],
+  [71, 77],
+  [78, 85],
+  [86, 92],
+  [93, 100],
 ];
 
 /**
diff --git a/ui/webui/resources/cr_components/chromeos/bluetooth/bluetooth_pairing_confirm_code_page.js b/ui/webui/resources/cr_components/chromeos/bluetooth/bluetooth_pairing_confirm_code_page.js
index f393a14..f540fb4 100644
--- a/ui/webui/resources/cr_components/chromeos/bluetooth/bluetooth_pairing_confirm_code_page.js
+++ b/ui/webui/resources/cr_components/chromeos/bluetooth/bluetooth_pairing_confirm_code_page.js
@@ -51,7 +51,7 @@
           cancel: ButtonState.ENABLED,
           pair: ButtonState.ENABLED,
         },
-      }
+      },
     };
   }
 
diff --git a/ui/webui/resources/cr_components/chromeos/bluetooth/bluetooth_pairing_enter_code_page.js b/ui/webui/resources/cr_components/chromeos/bluetooth/bluetooth_pairing_enter_code_page.js
index 041af85..b7804c8 100644
--- a/ui/webui/resources/cr_components/chromeos/bluetooth/bluetooth_pairing_enter_code_page.js
+++ b/ui/webui/resources/cr_components/chromeos/bluetooth/bluetooth_pairing_enter_code_page.js
@@ -81,7 +81,7 @@
       keys_: {
         type: Array,
         computed: 'computeKeys_(code)',
-      }
+      },
     };
   }
 
diff --git a/ui/webui/resources/cr_components/chromeos/bluetooth/bluetooth_pairing_request_code_page.js b/ui/webui/resources/cr_components/chromeos/bluetooth/bluetooth_pairing_request_code_page.js
index fe31fc7..3395b7d28 100644
--- a/ui/webui/resources/cr_components/chromeos/bluetooth/bluetooth_pairing_request_code_page.js
+++ b/ui/webui/resources/cr_components/chromeos/bluetooth/bluetooth_pairing_request_code_page.js
@@ -76,7 +76,7 @@
       pinCode_: {
         type: String,
         value: '',
-      }
+      },
     };
   }
 
diff --git a/ui/webui/resources/cr_components/chromeos/bluetooth/bluetooth_spinner_page.js b/ui/webui/resources/cr_components/chromeos/bluetooth/bluetooth_spinner_page.js
index 8ba04073..b071218 100644
--- a/ui/webui/resources/cr_components/chromeos/bluetooth/bluetooth_spinner_page.js
+++ b/ui/webui/resources/cr_components/chromeos/bluetooth/bluetooth_spinner_page.js
@@ -32,7 +32,7 @@
           cancel: ButtonState.ENABLED,
           pair: ButtonState.DISABLED,
         },
-      }
+      },
     };
   }
 }
diff --git a/ui/webui/resources/cr_components/chromeos/cellular_setup/activation_code_page.js b/ui/webui/resources/cr_components/chromeos/cellular_setup/activation_code_page.js
index 2421842..4376cfe3 100644
--- a/ui/webui/resources/cr_components/chromeos/cellular_setup/activation_code_page.js
+++ b/ui/webui/resources/cr_components/chromeos/cellular_setup/activation_code_page.js
@@ -153,7 +153,7 @@
     isActivationCodeInvalidFormat_: {
       type: Boolean,
       value: false,
-    }
+    },
   },
 
   /**
@@ -345,9 +345,9 @@
           video: {
             height: 130,
             width: 482,
-            facingMode: useUserFacingCamera ? 'user' : 'environment'
+            facingMode: useUserFacingCamera ? 'user' : 'environment',
           },
-          audio: false
+          audio: false,
         })
         .then(stream => {
           this.stream_ = stream;
@@ -431,7 +431,7 @@
     this.fire('activation-code-updated', {
       activationCode: this.validateActivationCode_(this.activationCode) ?
           this.activationCode :
-          null
+          null,
     });
   },
 
@@ -658,5 +658,5 @@
     // Because this string contains '<' and '>' characters, we cannot use i18n
     // methods.
     return loadTimeData.getString('scanQrCodeInputError');
-  }
+  },
 });
diff --git a/ui/webui/resources/cr_components/chromeos/cellular_setup/base_page.js b/ui/webui/resources/cr_components/chromeos/cellular_setup/base_page.js
index 3efd9ec..628fee7e 100644
--- a/ui/webui/resources/cr_components/chromeos/cellular_setup/base_page.js
+++ b/ui/webui/resources/cr_components/chromeos/cellular_setup/base_page.js
@@ -31,7 +31,7 @@
     messageIcon: {
       type: String,
       value: '',
-    }
+    },
   },
 
   /**
@@ -56,5 +56,5 @@
    */
   isMessageIconShown_() {
     return !!this.messageIcon;
-  }
+  },
 });
diff --git a/ui/webui/resources/cr_components/chromeos/cellular_setup/button_bar.js b/ui/webui/resources/cr_components/chromeos/cellular_setup/button_bar.js
index 4243c079..653bcf5 100644
--- a/ui/webui/resources/cr_components/chromeos/cellular_setup/button_bar.js
+++ b/ui/webui/resources/cr_components/chromeos/cellular_setup/button_bar.js
@@ -31,7 +31,7 @@
     forwardButtonLabel: {
       type: String,
       value: '',
-    }
+    },
   },
 
   /**
@@ -99,5 +99,5 @@
         assertNotReached();
         return cellularSetup.ButtonState.ENABLED;
     }
-  }
+  },
 });
diff --git a/ui/webui/resources/cr_components/chromeos/cellular_setup/cellular_setup.js b/ui/webui/resources/cr_components/chromeos/cellular_setup/cellular_setup.js
index e2c7b944..fc12412 100644
--- a/ui/webui/resources/cr_components/chromeos/cellular_setup/cellular_setup.js
+++ b/ui/webui/resources/cr_components/chromeos/cellular_setup/cellular_setup.js
@@ -75,7 +75,7 @@
      */
     forwardButtonLabel_: {
       type: String,
-    }
+    },
   },
 
   listeners: {
@@ -141,5 +141,5 @@
    */
   shouldShowEsimFlow_(currentPage) {
     return currentPage === cellularSetup.CellularSetupPageName.ESIM_FLOW_UI;
-  }
+  },
 });
diff --git a/ui/webui/resources/cr_components/chromeos/cellular_setup/mojo_interface_provider.js b/ui/webui/resources/cr_components/chromeos/cellular_setup/mojo_interface_provider.js
index 675fe58..f8061446 100644
--- a/ui/webui/resources/cr_components/chromeos/cellular_setup/mojo_interface_provider.js
+++ b/ui/webui/resources/cr_components/chromeos/cellular_setup/mojo_interface_provider.js
@@ -84,6 +84,6 @@
     getCellularSetupRemote,
     setESimManagerRemoteForTesting,
     getESimManagerRemote,
-    observeESimManager
+    observeESimManager,
   };
 });
diff --git a/ui/webui/resources/cr_components/chromeos/cellular_setup/profile_discovery_list_page.js b/ui/webui/resources/cr_components/chromeos/cellular_setup/profile_discovery_list_page.js
index 3b3c2b9c..3ba7bee 100644
--- a/ui/webui/resources/cr_components/chromeos/cellular_setup/profile_discovery_list_page.js
+++ b/ui/webui/resources/cr_components/chromeos/cellular_setup/profile_discovery_list_page.js
@@ -45,5 +45,5 @@
    */
   isProfileSelected_(profile) {
     return this.selectedProfile === profile;
-  }
+  },
 });
diff --git a/ui/webui/resources/cr_components/chromeos/cellular_setup/provisioning_page.js b/ui/webui/resources/cr_components/chromeos/cellular_setup/provisioning_page.js
index ace1aecc..20b49c2 100644
--- a/ui/webui/resources/cr_components/chromeos/cellular_setup/provisioning_page.js
+++ b/ui/webui/resources/cr_components/chromeos/cellular_setup/provisioning_page.js
@@ -199,7 +199,7 @@
             carrier: this.cellularMetadata.carrier,
             MEID: this.cellularMetadata.meid,
             IMEI: this.cellularMetadata.imei,
-            MDN: this.cellularMetadata.mdn
+            MDN: this.cellularMetadata.mdn,
           },
           this.cellularMetadata.paymentUrl.url);
       return;
diff --git a/ui/webui/resources/cr_components/chromeos/cellular_setup/psim_flow_ui.js b/ui/webui/resources/cr_components/chromeos/cellular_setup/psim_flow_ui.js
index 966cf79..fc9168b 100644
--- a/ui/webui/resources/cr_components/chromeos/cellular_setup/psim_flow_ui.js
+++ b/ui/webui/resources/cr_components/chromeos/cellular_setup/psim_flow_ui.js
@@ -605,6 +605,6 @@
   return {
     PSimPageName: PSimPageName,
     PSimUIState: PSimUIState,
-    getTimeoutMsForPSimUIState: getTimeoutMsForPSimUIState
+    getTimeoutMsForPSimUIState: getTimeoutMsForPSimUIState,
   };
 });
diff --git a/ui/webui/resources/cr_components/chromeos/cellular_setup/webview_post_util.js b/ui/webui/resources/cr_components/chromeos/cellular_setup/webview_post_util.js
index 0ec5e8c..e59f3885 100644
--- a/ui/webui/resources/cr_components/chromeos/cellular_setup/webview_post_util.js
+++ b/ui/webui/resources/cr_components/chromeos/cellular_setup/webview_post_util.js
@@ -84,7 +84,7 @@
       code: WEBVIEW_REDIRECT_SCRIPT + '(' +
           'document.getElementById(\'' + WEBVIEW_REDIRECT_FORM_ID + '\'),' +
           ' \'' + escape(paymentUrl) + '\',' +
-          ' \'' + escape(postData || '') + '\');'
+          ' \'' + escape(postData || '') + '\');',
     });
   }
 
diff --git a/ui/webui/resources/cr_components/chromeos/multidevice_setup/button_bar.js b/ui/webui/resources/cr_components/chromeos/multidevice_setup/button_bar.js
index c635763e..45d50cd 100644
--- a/ui/webui/resources/cr_components/chromeos/multidevice_setup/button_bar.js
+++ b/ui/webui/resources/cr_components/chromeos/multidevice_setup/button_bar.js
@@ -32,7 +32,7 @@
     shouldShowShadow: {
       type: Boolean,
       value: false,
-    }
+    },
   },
 
   /** @private */
diff --git a/ui/webui/resources/cr_components/chromeos/multidevice_setup/fake_mojo_service.js b/ui/webui/resources/cr_components/chromeos/multidevice_setup/fake_mojo_service.js
index f5603c93..56d2cd50 100644
--- a/ui/webui/resources/cr_components/chromeos/multidevice_setup/fake_mojo_service.js
+++ b/ui/webui/resources/cr_components/chromeos/multidevice_setup/fake_mojo_service.js
@@ -62,7 +62,7 @@
     for (let i = 0; i < this.deviceCount; i++) {
       const deviceName = deviceNames[i % 4];
       devices.push({
-        remoteDevice: {deviceName: deviceName, deviceId: deviceName + '--' + i}
+        remoteDevice: {deviceName: deviceName, deviceId: deviceName + '--' + i},
       });
     }
     return new Promise(function(resolve, reject) {
diff --git a/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup.js b/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup.js
index 87e201e..1ab5d4b 100644
--- a/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup.js
+++ b/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup.js
@@ -37,7 +37,7 @@
         type: Boolean,
         computed: 'shouldForwardButtonBeDisabled_(' +
             'passwordPageForwardButtonDisabled_, visiblePageName)',
-        notify: true
+        notify: true,
       },
 
       /**
diff --git a/ui/webui/resources/cr_components/chromeos/network/cr_policy_network_indicator_mojo.js b/ui/webui/resources/cr_components/chromeos/network/cr_policy_network_indicator_mojo.js
index 73ad2eaa..cd6830b9 100644
--- a/ui/webui/resources/cr_components/chromeos/network/cr_policy_network_indicator_mojo.js
+++ b/ui/webui/resources/cr_components/chromeos/network/cr_policy_network_indicator_mojo.js
@@ -76,5 +76,5 @@
     const matches = !!this.property &&
         this.property.activeValue === this.property.policyValue;
     return this.getIndicatorTooltip(this.indicatorType, '', matches);
-  }
+  },
 });
diff --git a/ui/webui/resources/cr_components/chromeos/network/network_apnlist.js b/ui/webui/resources/cr_components/chromeos/network/network_apnlist.js
index 6d74a50..4d1f28f 100644
--- a/ui/webui/resources/cr_components/chromeos/network/network_apnlist.js
+++ b/ui/webui/resources/cr_components/chromeos/network/network_apnlist.js
@@ -53,7 +53,7 @@
       type: Array,
       value() {
         return [];
-      }
+      },
     },
 
     /**
@@ -69,7 +69,7 @@
           accessPointName: kDefaultAccessPointName,
           name: kOtherAccessPointName,
         };
-      }
+      },
     },
 
     /**
@@ -81,7 +81,7 @@
       value() {
         return ['accessPointName', 'username', 'password'];
       },
-      readOnly: true
+      readOnly: true,
     },
 
     /**
@@ -94,10 +94,10 @@
         return {
           'accessPointName': 'String',
           'username': 'String',
-          'password': 'Password'
+          'password': 'Password',
         };
       },
-      readOnly: true
+      readOnly: true,
     },
 
     /** @private */
@@ -106,7 +106,7 @@
       value() {
         return loadTimeData.valueExists('useAttachApn') &&
             loadTimeData.getBoolean('useAttachApn');
-      }
+      },
     },
 
     /** @private */
@@ -392,5 +392,5 @@
    */
   isApnItemSelected_(item) {
     return item.accessPointName === this.selectedApn_;
-  }
+  },
 });
diff --git a/ui/webui/resources/cr_components/chromeos/network/network_choose_mobile.js b/ui/webui/resources/cr_components/chromeos/network/network_choose_mobile.js
index 021810d89..5badcff 100644
--- a/ui/webui/resources/cr_components/chromeos/network/network_choose_mobile.js
+++ b/ui/webui/resources/cr_components/chromeos/network/network_choose_mobile.js
@@ -48,7 +48,7 @@
       type: Array,
       value() {
         return [];
-      }
+      },
     },
   },
 
@@ -85,7 +85,7 @@
     this.mobileNetworkList_ = cellular.foundNetworks || [];
     if (!this.mobileNetworkList_.length) {
       this.mobileNetworkList_ = [
-        {networkId: 'none', longName: this.i18n('networkCellularNoNetworks')}
+        {networkId: 'none', longName: this.i18n('networkCellularNoNetworks')},
       ];
     }
     // Set selectedMobileNetworkId_ after the dom-repeat has been stamped.
diff --git a/ui/webui/resources/cr_components/chromeos/network/network_config.js b/ui/webui/resources/cr_components/chromeos/network/network_config.js
index 223deea3..7809fac 100644
--- a/ui/webui/resources/cr_components/chromeos/network/network_config.js
+++ b/ui/webui/resources/cr_components/chromeos/network/network_config.js
@@ -2393,5 +2393,5 @@
       // Reset error if user starts typing new password.
       this.setError_('');
     }
-  }
+  },
 });
diff --git a/ui/webui/resources/cr_components/chromeos/network/network_icon.js b/ui/webui/resources/cr_components/chromeos/network/network_icon.js
index e7159c2..bbaede4 100644
--- a/ui/webui/resources/cr_components/chromeos/network/network_icon.js
+++ b/ui/webui/resources/cr_components/chromeos/network/network_icon.js
@@ -59,7 +59,7 @@
     ariaLabel: {
       type: String,
       reflectToAttribute: true,
-      computed: 'computeAriaLabel_(locale, networkState)'
+      computed: 'computeAriaLabel_(locale, networkState)',
     },
   },
 
diff --git a/ui/webui/resources/cr_components/chromeos/network/network_ip_config.js b/ui/webui/resources/cr_components/chromeos/network/network_ip_config.js
index 63979d0..672e054 100644
--- a/ui/webui/resources/cr_components/chromeos/network/network_ip_config.js
+++ b/ui/webui/resources/cr_components/chromeos/network/network_ip_config.js
@@ -138,7 +138,7 @@
           'ipv6.ipAddress',
         ];
       },
-      readOnly: true
+      readOnly: true,
     },
 
     /**
@@ -252,7 +252,7 @@
       }
       if (!this.ipConfig_.ipv4) {
         this.ipConfig_.ipv4 = {
-          type: chromeos.networkConfig.mojom.IPConfigType.kIPv4
+          type: chromeos.networkConfig.mojom.IPConfigType.kIPv4,
         };
       }
       this.setIpv4Defaults_(this.ipConfig_.ipv4);
@@ -362,7 +362,7 @@
       // expects a ManagedProperty and routingPrefix has the same type as
       // netmask.
       'ipv4.netmask': this.getIPFieldEditType_(staticIpConfig.routingPrefix),
-      'ipv4.gateway': this.getIPFieldEditType_(staticIpConfig.gateway)
+      'ipv4.gateway': this.getIPFieldEditType_(staticIpConfig.gateway),
     };
   },
 
@@ -390,7 +390,7 @@
       field: 'staticIpConfig',
       value: this.ipConfig_.ipv4 ?
           this.getIPConfigProperties_(this.ipConfig_.ipv4) :
-          {}
+          {},
     });
   },
 
@@ -416,5 +416,5 @@
       classes += ' indented';
     }
     return classes;
-  }
+  },
 });
diff --git a/ui/webui/resources/cr_components/chromeos/network/network_list_item.js b/ui/webui/resources/cr_components/chromeos/network/network_list_item.js
index 74eab43..81f3c17 100644
--- a/ui/webui/resources/cr_components/chromeos/network/network_list_item.js
+++ b/ui/webui/resources/cr_components/chromeos/network/network_list_item.js
@@ -26,7 +26,7 @@
       reflectToAttribute: true,
       observer: 'disabledChanged_',
       computed: 'computeDisabled_(deviceState, deviceState.inhibitReason,' +
-          'disableItem)'
+          'disableItem)',
     },
 
     /**
diff --git a/ui/webui/resources/cr_components/chromeos/network/network_nameservers.js b/ui/webui/resources/cr_components/chromeos/network/network_nameservers.js
index f8433b51..b9799c99 100644
--- a/ui/webui/resources/cr_components/chromeos/network/network_nameservers.js
+++ b/ui/webui/resources/cr_components/chromeos/network/network_nameservers.js
@@ -13,7 +13,7 @@
 const NameserversType = {
   AUTOMATIC: 'automatic',
   CUSTOM: 'custom',
-  GOOGLE: 'google'
+  GOOGLE: 'google',
 };
 
 Polymer({
@@ -69,14 +69,14 @@
       value() {
         return this.i18nAdvanced(
             'networkNameserversGoogle', {substitutions: [], tags: ['a']});
-      }
+      },
     },
 
     /** @private */
     canChangeConfigType_: {
       type: Boolean,
       computed: 'computeCanChangeConfigType_(managedProperties)',
-    }
+    },
   },
 
   /** @const */
diff --git a/ui/webui/resources/cr_components/chromeos/network/network_property_list_mojo.js b/ui/webui/resources/cr_components/chromeos/network/network_property_list_mojo.js
index cfcd11b..c2b923e1 100644
--- a/ui/webui/resources/cr_components/chromeos/network/network_property_list_mojo.js
+++ b/ui/webui/resources/cr_components/chromeos/network/network_property_list_mojo.js
@@ -85,7 +85,7 @@
     hasAnyInputFocused_: {
       type: Boolean,
       value: false,
-    }
+    },
   },
 
   /** @private */
diff --git a/ui/webui/resources/cr_components/chromeos/network/network_proxy.js b/ui/webui/resources/cr_components/chromeos/network/network_proxy.js
index c3cb28a..850f089 100644
--- a/ui/webui/resources/cr_components/chromeos/network/network_proxy.js
+++ b/ui/webui/resources/cr_components/chromeos/network/network_proxy.js
@@ -73,7 +73,7 @@
     proxyTypes_: {
       type: Array,
       value: ['Direct', 'PAC', 'WPAD', 'Manual'],
-      readOnly: true
+      readOnly: true,
     },
 
     /**
@@ -233,7 +233,7 @@
       proxy.excludeDomains =
           proxy.excludeDomains || this.savedExcludeDomains_ || {
             activeValue: [],
-            policySource: chromeos.networkConfig.mojom.PolicySource.kNone
+            policySource: chromeos.networkConfig.mojom.PolicySource.kNone,
           };
     }
     return proxy;
diff --git a/ui/webui/resources/cr_components/chromeos/network/network_proxy_exclusions.js b/ui/webui/resources/cr_components/chromeos/network/network_proxy_exclusions.js
index d5296ed..f553994 100644
--- a/ui/webui/resources/cr_components/chromeos/network/network_proxy_exclusions.js
+++ b/ui/webui/resources/cr_components/chromeos/network/network_proxy_exclusions.js
@@ -28,8 +28,8 @@
       value() {
         return [];
       },
-      notify: true
-    }
+      notify: true,
+    },
   },
 
   /**
@@ -41,5 +41,5 @@
     const index = event.model.index;
     this.splice('exclusions', index, 1);
     this.fire('proxy-exclusions-change');
-  }
+  },
 });
diff --git a/ui/webui/resources/cr_components/chromeos/network/network_proxy_input.js b/ui/webui/resources/cr_components/chromeos/network/network_proxy_input.js
index 40c3f6f4..df15480 100644
--- a/ui/webui/resources/cr_components/chromeos/network/network_proxy_input.js
+++ b/ui/webui/resources/cr_components/chromeos/network/network_proxy_input.js
@@ -62,5 +62,5 @@
     }
     this.value.port.activeValue = port;
     this.fire('proxy-input-change', this.value);
-  }
+  },
 });
diff --git a/ui/webui/resources/cr_components/chromeos/network/network_siminfo.js b/ui/webui/resources/cr_components/chromeos/network/network_siminfo.js
index 5a529e4..c02f7758 100644
--- a/ui/webui/resources/cr_components/chromeos/network/network_siminfo.js
+++ b/ui/webui/resources/cr_components/chromeos/network/network_siminfo.js
@@ -20,7 +20,7 @@
  */
 const State = {
   SIM_LOCKED: 0,
-  SIM_UNLOCKED: 1
+  SIM_UNLOCKED: 1,
 };
 
 Polymer({
@@ -89,7 +89,7 @@
     isActiveSim_: {
       type: Boolean,
       value: false,
-      computed: 'computeIsActiveSim_(networkState, deviceState)'
+      computed: 'computeIsActiveSim_(networkState, deviceState)',
     },
 
     /** @private {!State} */
@@ -106,7 +106,7 @@
       value() {
         return loadTimeData.valueExists('isSimLockPolicyEnabled') &&
             loadTimeData.getBoolean('isSimLockPolicyEnabled');
-      }
+      },
     },
 
     /** @private {boolean} */
diff --git a/ui/webui/resources/cr_components/chromeos/network/onc_mojo.js b/ui/webui/resources/cr_components/chromeos/network/onc_mojo.js
index 439c848..2aef272 100644
--- a/ui/webui/resources/cr_components/chromeos/network/onc_mojo.js
+++ b/ui/webui/resources/cr_components/chromeos/network/onc_mojo.js
@@ -794,7 +794,7 @@
             signalStrength: 0,
             simLocked: false,
             supportNetworkScan: false,
-          }
+          },
         };
         break;
       case mojom.NetworkType.kEthernet:
@@ -809,7 +809,7 @@
             carrier: '',
             hasConnectedToHost: false,
             signalStrength: 0,
-          }
+          },
         };
         break;
       case mojom.NetworkType.kVPN:
@@ -818,7 +818,7 @@
             providerName: '',
             type: mojom.VpnType.kOpenVPN,
             openVpn: {},
-          }
+          },
         };
         break;
       case mojom.NetworkType.kWiFi:
@@ -831,7 +831,7 @@
             signalStrength: 0,
             isSyncable: false,
             isConfiguredByActiveUser: false,
-          }
+          },
         };
         break;
     }
@@ -863,9 +863,9 @@
           typeConfig: {
             wifi: {
               security: mojom.SecurityType.kNone,
-              hiddenSsid: mojom.HiddenSsidMode.kAutomatic
-            }
-          }
+              hiddenSsid: mojom.HiddenSsidMode.kAutomatic,
+            },
+          },
         };
         break;
     }
@@ -1116,7 +1116,7 @@
     return {
       activeValue: s,
       policySource: chromeos.networkConfig.mojom.PolicySource.kNone,
-      policyValue: undefined
+      policyValue: undefined,
     };
   }
 
@@ -1128,7 +1128,7 @@
     return {
       activeValue: n,
       policySource: chromeos.networkConfig.mojom.PolicySource.kNone,
-      policyValue: 0
+      policyValue: 0,
     };
   }
 
@@ -1140,7 +1140,7 @@
     return {
       activeValue: b,
       policySource: chromeos.networkConfig.mojom.PolicySource.kNone,
-      policyValue: false
+      policyValue: false,
     };
   }
 
@@ -1151,7 +1151,7 @@
     return {
       lastResetTime: null,
       autoReset: false,
-      userSpecifiedResetDay: 1
+      userSpecifiedResetDay: 1,
     };
   }
 
@@ -1409,7 +1409,7 @@
       }
       result.push(/* @type {!chromeos.networkConfig.mojom.SubjectAltName} */ {
         type: type,
-        value: value[0]
+        value: value[0],
       });
     }
     return result;
diff --git a/ui/webui/resources/cr_components/chromeos/network/sim_lock_dialogs.js b/ui/webui/resources/cr_components/chromeos/network/sim_lock_dialogs.js
index e1038a8..8870cc3 100644
--- a/ui/webui/resources/cr_components/chromeos/network/sim_lock_dialogs.js
+++ b/ui/webui/resources/cr_components/chromeos/network/sim_lock_dialogs.js
@@ -14,7 +14,7 @@
   INCORRECT_PUK: 'incorrect-puk',
   MISMATCHED_PIN: 'mismatched-pin',
   INVALID_PIN: 'invalid-pin',
-  INVALID_PUK: 'invalid-puk'
+  INVALID_PUK: 'invalid-puk',
 };
 
 const DIGITS_ONLY_REGEX = /^[0-9]+$/;
@@ -159,7 +159,7 @@
       value() {
         return loadTimeData.valueExists('isSimLockPolicyEnabled') &&
             loadTimeData.getBoolean('isSimLockPolicyEnabled');
-      }
+      },
     },
 
     /** @private {boolean} */
diff --git a/ui/webui/resources/cr_components/chromeos/network_health/network_diagnostics.js b/ui/webui/resources/cr_components/chromeos/network_health/network_diagnostics.js
index 6d0a42a..6aae159 100644
--- a/ui/webui/resources/cr_components/chromeos/network_health/network_diagnostics.js
+++ b/ui/webui/resources/cr_components/chromeos/network_health/network_diagnostics.js
@@ -53,7 +53,7 @@
       type: Boolean,
       value() {
         return loadTimeData.getBoolean('enableArcNetworkDiagnostics');
-      }
+      },
     },
 
     /**
@@ -72,7 +72,7 @@
                 type: RoutineType.kLanConnectivity,
                 func: () => getNetworkDiagnosticsService().runLanConnectivity(),
               },
-            ]
+            ],
           },
           {
             group: RoutineGroup.WIFI,
@@ -88,7 +88,7 @@
                 func: () =>
                     getNetworkDiagnosticsService().runHasSecureWiFiConnection(),
               },
-            ]
+            ],
           },
           {
             group: RoutineGroup.PORTAL,
@@ -98,7 +98,7 @@
                 type: RoutineType.kCaptivePortal,
                 func: () => getNetworkDiagnosticsService().runCaptivePortal(),
               },
-            ]
+            ],
           },
           {
             group: RoutineGroup.GATEWAY,
@@ -109,7 +109,7 @@
                 func: () =>
                     getNetworkDiagnosticsService().runGatewayCanBePinged(),
               },
-            ]
+            ],
           },
           {
             group: RoutineGroup.FIREWALL,
@@ -130,7 +130,7 @@
                 type: RoutineType.kHttpsLatency,
                 func: () => getNetworkDiagnosticsService().runHttpsLatency(),
               },
-            ]
+            ],
           },
           {
             group: RoutineGroup.DNS,
@@ -151,7 +151,7 @@
                 type: RoutineType.kDnsResolution,
                 func: () => getNetworkDiagnosticsService().runDnsResolution(),
               },
-            ]
+            ],
           },
           {
             group: RoutineGroup.GOOGLE_SERVICES,
@@ -164,8 +164,8 @@
                 func: () => getNetworkDiagnosticsService().runVideoConferencing(
                     /*stun_server_hostname=*/ null),
               },
-            ]
-          }
+            ],
+          },
         ];
         if (this.areArcNetworkingRoutinesEnabled_) {
           routineGroups.push({
@@ -187,7 +187,7 @@
                 func: () =>
                     getNetworkDiagnosticsService().runArcDnsResolution(),
               },
-            ]
+            ],
           });
         }
         const routines = [];
@@ -200,7 +200,7 @@
         }
 
         return routines;
-      }
+      },
     },
 
     /**
@@ -210,7 +210,7 @@
     RoutineGroup_: {
       type: Object,
       value: RoutineGroup,
-    }
+    },
   },
 
   /**
diff --git a/ui/webui/resources/cr_components/chromeos/network_health/network_diagnostics_types.js b/ui/webui/resources/cr_components/chromeos/network_health/network_diagnostics_types.js
index f6a23cb..217516b9 100644
--- a/ui/webui/resources/cr_components/chromeos/network_health/network_diagnostics_types.js
+++ b/ui/webui/resources/cr_components/chromeos/network_health/network_diagnostics_types.js
@@ -44,11 +44,11 @@
   FIREWALL: 4,
   DNS: 5,
   GOOGLE_SERVICES: 6,
-  ARC: 7
+  ARC: 7,
 };
 
 export const Icons = {
   TEST_FAILED: 'test_failed.png',
   TEST_NOT_RUN: 'test_not_run.png',
-  TEST_PASSED: 'test_passed.png'
+  TEST_PASSED: 'test_passed.png',
 };
diff --git a/ui/webui/resources/cr_components/chromeos/network_health/network_health_summary.js b/ui/webui/resources/cr_components/chromeos/network_health/network_health_summary.js
index 82c6037..8b6fd7e 100644
--- a/ui/webui/resources/cr_components/chromeos/network_health/network_health_summary.js
+++ b/ui/webui/resources/cr_components/chromeos/network_health/network_health_summary.js
@@ -194,8 +194,10 @@
    */
   showSettingsLink_(network) {
     const validStates = [
-      NetworkState.kConnected, NetworkState.kConnecting, NetworkState.kPortal,
-      NetworkState.kOnline
+      NetworkState.kConnected,
+      NetworkState.kConnecting,
+      NetworkState.kPortal,
+      NetworkState.kOnline,
     ];
     return validStates.includes(network.state);
   },
diff --git a/ui/webui/resources/cr_components/chromeos/quick_unlock/lock_screen_constants.js b/ui/webui/resources/cr_components/chromeos/quick_unlock/lock_screen_constants.js
index fd1606d2..7cc0d46b 100644
--- a/ui/webui/resources/cr_components/chromeos/quick_unlock/lock_screen_constants.js
+++ b/ui/webui/resources/cr_components/chromeos/quick_unlock/lock_screen_constants.js
@@ -22,7 +22,7 @@
     CHOOSE_PIN_OR_PASSWORD: 2,
     ENTER_PIN: 3,
     CONFIRM_PIN: 4,
-    MAX_BUCKET: 5
+    MAX_BUCKET: 5,
   };
   /**
    * Helper function to send the progress of the pin setup to be recorded in the
@@ -37,13 +37,15 @@
       return;
     }
     chrome.send('metricsHandler:recordInHistogram', [
-      PinUnlockUmaHistogramName, currentProgress, LockScreenProgress.MAX_BUCKET
+      PinUnlockUmaHistogramName,
+      currentProgress,
+      LockScreenProgress.MAX_BUCKET,
     ]);
   };
 
   // #cr_define_end
   return {
     recordLockScreenProgress: recordLockScreenProgress,
-    LockScreenProgress: LockScreenProgress
+    LockScreenProgress: LockScreenProgress,
   };
 });
diff --git a/ui/webui/resources/cr_components/chromeos/quick_unlock/setup_pin_keyboard.js b/ui/webui/resources/cr_components/chromeos/quick_unlock/setup_pin_keyboard.js
index c395db0..8d0fd164 100644
--- a/ui/webui/resources/cr_components/chromeos/quick_unlock/setup_pin_keyboard.js
+++ b/ui/webui/resources/cr_components/chromeos/quick_unlock/setup_pin_keyboard.js
@@ -25,7 +25,7 @@
 /** @enum {string} */
 /* #export */ const ProblemType = {
   WARNING: 'warning',
-  ERROR: 'error'
+  ERROR: 'error',
 };
 
 Polymer({
@@ -98,7 +98,7 @@
       type: Object,
       value() {
         return function() {};
-      }
+      },
     },
 
     /**
diff --git a/ui/webui/resources/cr_components/customize_themes/customize_themes.ts b/ui/webui/resources/cr_components/customize_themes/customize_themes.ts
index 033ea58df..61264f7 100644
--- a/ui/webui/resources/cr_components/customize_themes/customize_themes.ts
+++ b/ui/webui/resources/cr_components/customize_themes/customize_themes.ts
@@ -76,7 +76,7 @@
       showManagedThemeDialog_: {
         type: Boolean,
         value: false,
-      }
+      },
     };
   }
 
diff --git a/ui/webui/resources/cr_components/history_clusters/url_visit.ts b/ui/webui/resources/cr_components/history_clusters/url_visit.ts
index bd06876e..20bc583 100644
--- a/ui/webui/resources/cr_components/history_clusters/url_visit.ts
+++ b/ui/webui/resources/cr_components/history_clusters/url_visit.ts
@@ -115,7 +115,7 @@
       unusedUrlForDisplay_: {
         type: String,
         computed: 'computeUrlForDisplay_(visit)',
-      }
+      },
     };
   }
 
diff --git a/ui/webui/resources/cr_components/managed_footnote/managed_footnote.ts b/ui/webui/resources/cr_components/managed_footnote/managed_footnote.ts
index 38eb5486..9fe8edb 100644
--- a/ui/webui/resources/cr_components/managed_footnote/managed_footnote.ts
+++ b/ui/webui/resources/cr_components/managed_footnote/managed_footnote.ts
@@ -57,7 +57,7 @@
       showDeviceInfo: {
         type: Boolean,
         value: false,
-      }
+      },
     };
   }
 
diff --git a/ui/webui/resources/cr_elements/chromeos/cr_picture/png.js b/ui/webui/resources/cr_elements/chromeos/cr_picture/png.js
index 5c149a6..5cd62f7d 100644
--- a/ui/webui/resources/cr_elements/chromeos/cr_picture/png.js
+++ b/ui/webui/resources/cr_elements/chromeos/cr_picture/png.js
@@ -112,7 +112,7 @@
     0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
     0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693,
     0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
-    0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
+    0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D,
   ];
 
   /**
diff --git a/ui/webui/resources/cr_elements/cr_lottie/cr_lottie.js b/ui/webui/resources/cr_elements/cr_lottie/cr_lottie.js
index 0e35c77..53a1056 100644
--- a/ui/webui/resources/cr_elements/cr_lottie/cr_lottie.js
+++ b/ui/webui/resources/cr_elements/cr_lottie/cr_lottie.js
@@ -209,7 +209,7 @@
     const clientRect = canvasElement.getBoundingClientRect();
     const drawSize = {
       width: clientRect.width * devicePixelRatio,
-      height: clientRect.height * devicePixelRatio
+      height: clientRect.height * devicePixelRatio,
     };
     return drawSize;
   },
@@ -289,7 +289,7 @@
     const message = [{
       animationData,
       drawSize: this.getCanvasDrawBufferSize_(),
-      params: {loop: !this.singleLoop, autoplay: this.autoplay}
+      params: {loop: !this.singleLoop, autoplay: this.autoplay},
     }];
     if (!this.hasTransferredCanvas_) {
       message[0].canvas = this.offscreenCanvas_;
diff --git a/ui/webui/resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector.ts b/ui/webui/resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector.ts
index 232b02d..e4674e8 100644
--- a/ui/webui/resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector.ts
+++ b/ui/webui/resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector.ts
@@ -46,7 +46,7 @@
         type: Array,
         value() {
           return [];
-        }
+        },
       },
 
       /**
diff --git a/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_search_field.ts b/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_search_field.ts
index 628c5692..73f1344 100644
--- a/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_search_field.ts
+++ b/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_search_field.ts
@@ -62,7 +62,7 @@
 
       isSpinnerShown_: {
         type: Boolean,
-        computed: 'computeIsSpinnerShown_(spinnerActive, showingSearch)'
+        computed: 'computeIsSpinnerShown_(spinnerActive, showingSearch)',
       },
 
       searchFocused_: {type: Boolean, value: false},
diff --git a/ui/webui/resources/cr_elements/cr_view_manager/cr_view_manager.ts b/ui/webui/resources/cr_elements/cr_view_manager/cr_view_manager.ts
index ee6c31a32..7d1ff5dd 100644
--- a/ui/webui/resources/cr_elements/cr_view_manager/cr_view_manager.ts
+++ b/ui/webui/resources/cr_elements/cr_view_manager/cr_view_manager.ts
@@ -45,7 +45,7 @@
   const animation = element.animate(
       [
         {transform: 'translateX(-8px)', opacity: 0},
-        {transform: 'translateX(0)', opacity: 1}
+        {transform: 'translateX(0)', opacity: 1},
       ],
       {
         duration: 300,
@@ -60,7 +60,7 @@
   const animation = element.animate(
       [
         {transform: 'translateX(8px)', opacity: 0},
-        {transform: 'translateX(0)', opacity: 1}
+        {transform: 'translateX(0)', opacity: 1},
       ],
       {
         duration: 300,
diff --git a/ui/webui/resources/cr_elements/policy/cr_tooltip_icon.js b/ui/webui/resources/cr_elements/policy/cr_tooltip_icon.js
index ee069725..5d92759 100644
--- a/ui/webui/resources/cr_elements/policy/cr_tooltip_icon.js
+++ b/ui/webui/resources/cr_elements/policy/cr_tooltip_icon.js
@@ -16,7 +16,7 @@
     tooltipPosition: {
       type: String,
       value: 'top',
-    }
+    },
   },
 
   /** @return {!Element} */
diff --git a/ui/webui/resources/js/cr.js b/ui/webui/resources/js/cr.js
index feaf427..fb182dc 100644
--- a/ui/webui/resources/js/cr.js
+++ b/ui/webui/resources/js/cr.js
@@ -84,7 +84,7 @@
      * element has the attribute then the value is true.
      * Use only for properties of type {boolean}.
      */
-    BOOL_ATTR: 'boolAttr'
+    BOOL_ATTR: 'boolAttr',
   };
 
   /**
@@ -451,6 +451,6 @@
     /** Whether this is on iOS. */
     get isIOS() {
       return /CriOS/.test(navigator.userAgent);
-    }
+    },
   };
 }(this);
diff --git a/ui/webui/resources/js/cr.m.js b/ui/webui/resources/js/cr.m.js
index 4d77234..6496724 100644
--- a/ui/webui/resources/js/cr.m.js
+++ b/ui/webui/resources/js/cr.m.js
@@ -261,7 +261,7 @@
    * element has the attribute then the value is true.
    * Use only for properties of type {boolean}.
    */
-  BOOL_ATTR: 'boolAttr'
+  BOOL_ATTR: 'boolAttr',
 };
 
 /**
diff --git a/ui/webui/resources/js/cr/ui.js b/ui/webui/resources/js/cr/ui.js
index 4b67baf..00d2054 100644
--- a/ui/webui/resources/js/cr/ui.js
+++ b/ui/webui/resources/js/cr/ui.js
@@ -221,6 +221,6 @@
     define: define,
     limitInputWidth: limitInputWidth,
     toCssPx: toCssPx,
-    swallowDoubleClick: swallowDoubleClick
+    swallowDoubleClick: swallowDoubleClick,
   };
 });
diff --git a/ui/webui/resources/js/cr/ui/command.js b/ui/webui/resources/js/cr/ui/command.js
index 6351638..4421578 100644
--- a/ui/webui/resources/js/cr/ui/command.js
+++ b/ui/webui/resources/js/cr/ui/command.js
@@ -261,7 +261,7 @@
           }
         }
       }
-    }
+    },
   };
 
   /**
@@ -301,7 +301,7 @@
       this.canExecute_ = !!canExecute;
       this.stopPropagation();
       this.preventDefault();
-    }
+    },
   };
 
   // Export
diff --git a/ui/webui/resources/js/cr/ui/focus_manager.js b/ui/webui/resources/js/cr/ui/focus_manager.js
index ab0f22f..80a6ab2 100644
--- a/ui/webui/resources/js/cr/ui/focus_manager.js
+++ b/ui/webui/resources/js/cr/ui/focus_manager.js
@@ -70,7 +70,7 @@
 
               // Accept nodes that are non-hidden and focusable.
               return NodeFilter.FILTER_ACCEPT;
-            }
+            },
           }),
           false);
 
diff --git a/ui/webui/resources/js/cr/ui/grid.js b/ui/webui/resources/js/cr/ui/grid.js
index 08d2d99..c72f591 100644
--- a/ui/webui/resources/js/cr/ui/grid.js
+++ b/ui/webui/resources/js/cr/ui/grid.js
@@ -49,7 +49,7 @@
     decorate() {
       ListItem.prototype.decorate.apply(this, arguments);
       this.textContent = this.dataItem;
-    }
+    },
   };
 
   /**
@@ -356,7 +356,7 @@
       }
 
       List.prototype.redraw.call(this);
-    }
+    },
   };
 
   /**
@@ -445,7 +445,7 @@
         return -1;
       }
       return index + 1;
-    }
+    },
   };
 
   // #cr_define_end
@@ -453,6 +453,6 @@
   return {
     Grid: Grid,
     GridItem: GridItem,
-    GridSelectionController: GridSelectionController
+    GridSelectionController: GridSelectionController,
   };
 });
diff --git a/ui/webui/resources/js/cr/ui/list.js b/ui/webui/resources/js/cr/ui/list.js
index 1cd5998d..7f68c73d 100644
--- a/ui/webui/resources/js/cr/ui/list.js
+++ b/ui/webui/resources/js/cr/ui/list.js
@@ -441,7 +441,7 @@
           marginBottom: 0,
           width: 0,
           marginLeft: 0,
-          marginRight: 0
+          marginRight: 0,
         };
       }
       const item = opt_item || this.cachedMeasuredItem_ ||
@@ -490,7 +490,7 @@
         marginBottom: mb,
         width: Math.max(0, w),
         marginLeft: ml,
-        marginRight: mr
+        marginRight: mr,
       };
     },
 
@@ -1005,7 +1005,7 @@
         return {
           first: 0,
           length: this.dataModel.length,
-          last: this.dataModel.length
+          last: this.dataModel.length,
         };
       } else {
         const firstIndex = this.getIndexForListOffset_(scrollTop);
@@ -1014,7 +1014,7 @@
         return {
           first: firstIndex,
           length: lastIndex - firstIndex + 1,
-          last: lastIndex + 1
+          last: lastIndex + 1,
         };
       }
     },
diff --git a/ui/webui/resources/js/cr/ui/list_selection_controller.js b/ui/webui/resources/js/cr/ui/list_selection_controller.js
index f6ed0f99..c449db2 100644
--- a/ui/webui/resources/js/cr/ui/list_selection_controller.js
+++ b/ui/webui/resources/js/cr/ui/list_selection_controller.js
@@ -304,7 +304,7 @@
           e.preventDefault();
         }
       }
-    }
+    },
   };
 
   // #cr_define_end
diff --git a/ui/webui/resources/js/cr/ui/list_selection_model.js b/ui/webui/resources/js/cr/ui/list_selection_model.js
index f3774d6..e6e2d89 100644
--- a/ui/webui/resources/js/cr/ui/list_selection_model.js
+++ b/ui/webui/resources/js/cr/ui/list_selection_model.js
@@ -272,7 +272,7 @@
           e.changes = indexes.map(function(index) {
             return {
               index: Number(index),
-              selected: this.changedIndexes_[index]
+              selected: this.changedIndexes_[index],
             };
           }, this);
           this.dispatchEvent(e);
diff --git a/ui/webui/resources/js/cr/ui/menu.js b/ui/webui/resources/js/cr/ui/menu.js
index bc3d4c27..55444eb 100644
--- a/ui/webui/resources/js/cr/ui/menu.js
+++ b/ui/webui/resources/js/cr/ui/menu.js
@@ -368,7 +368,7 @@
           separatorRequired = true;
         }
       }
-    }
+    },
   };
 
   /** @suppress {globalThis} This standalone function is used like method. */
diff --git a/ui/webui/resources/js/cr/ui/menu_button.js b/ui/webui/resources/js/cr/ui/menu_button.js
index 347e5f0..8b3003e5 100644
--- a/ui/webui/resources/js/cr/ui/menu_button.js
+++ b/ui/webui/resources/js/cr/ui/menu_button.js
@@ -351,7 +351,7 @@
           this.hideMenu();
           break;
       }
-    }
+    },
   };
 
   // Export
diff --git a/ui/webui/resources/js/cr/ui/menu_item.js b/ui/webui/resources/js/cr/ui/menu_item.js
index 5970f30..b59cfd320 100644
--- a/ui/webui/resources/js/cr/ui/menu_item.js
+++ b/ui/webui/resources/js/cr/ui/menu_item.js
@@ -264,7 +264,7 @@
           this.checked = this.command.checked;
           break;
       }
-    }
+    },
   };
   /**
    * Whether the menu item is disabled or not.
diff --git a/ui/webui/resources/js/cr/ui/position_util.js b/ui/webui/resources/js/cr/ui/position_util.js
index 8357b57d..453944b5 100644
--- a/ui/webui/resources/js/cr/ui/position_util.js
+++ b/ui/webui/resources/js/cr/ui/position_util.js
@@ -41,7 +41,7 @@
      * The popop's top edge is aligned with the bottom edge of the anchor.
      * The popup's left edge is aligned with the left edge of the anchor.
      */
-    BELOW: 4  // p: top, a: bottom, p: left, a: left
+    BELOW: 4,  // p: top, a: bottom, p: left, a: left
   };
 
   /**
@@ -69,7 +69,7 @@
         top: 0,
         bottom: docElement.clientHeight,
         left: 0,
-        right: docElement.clientWidth
+        right: docElement.clientWidth,
       };
     } else {
       availRect = popupElement.offsetParent.getBoundingClientRect();
@@ -243,6 +243,6 @@
   return {
     AnchorType: AnchorType,
     positionPopupAroundElement: positionPopupAroundElement,
-    positionPopupAtPoint: positionPopupAtPoint
+    positionPopupAtPoint: positionPopupAtPoint,
   };
 });
diff --git a/ui/webui/resources/js/i18n_template_no_process.js b/ui/webui/resources/js/i18n_template_no_process.js
index aa25c05..8a90356 100644
--- a/ui/webui/resources/js/i18n_template_no_process.js
+++ b/ui/webui/resources/js/i18n_template_no_process.js
@@ -106,7 +106,7 @@
           element.setAttribute(propName, /** @type {string} */ (value));
         }
       });
-    }
+    },
   };
 
   const prefixes = [''];
diff --git a/ui/webui/resources/js/ios/mojo_api.js b/ui/webui/resources/js/ios/mojo_api.js
index b2540452..85151b80 100644
--- a/ui/webui/resources/js/ios/mojo_api.js
+++ b/ui/webui/resources/js/ios/mojo_api.js
@@ -56,8 +56,8 @@
     name: 'Mojo.bindInterface',
     args: {
       interfaceName: interfaceName,
-      requestHandle: requestHandle.takeNativeHandle_()
-    }
+      requestHandle: requestHandle.takeNativeHandle_(),
+    },
   });
 };
 
@@ -134,8 +134,8 @@
     args: {
       handle: this.nativeHandle_,
       signals: signalsValue,
-      callbackId: Mojo.internal.watchCallbacksHolder.getNextCallbackId()
-    }
+      callbackId: Mojo.internal.watchCallbacksHolder.getNextCallbackId(),
+    },
   });
   Mojo.internal.watchCallbacksHolder.addWatchCallback(watchId, callback);
 
@@ -160,7 +160,7 @@
       handle: this.nativeHandle_,
       buffer: buffer,
       handles: nativeHandles,
-    }
+    },
   });
 };
 
@@ -296,6 +296,6 @@
     callCallback: callCallback,
     getNextCallbackId: getNextCallbackId,
     addWatchCallback: addWatchCallback,
-    removeWatchCallback: removeWatchCallback
+    removeWatchCallback: removeWatchCallback,
   };
 })();
diff --git a/ui/webui/resources/js/ios/web_ui.js b/ui/webui/resources/js/ios/web_ui.js
index 3dff8352..e49caed 100644
--- a/ui/webui/resources/js/ios/web_ui.js
+++ b/ui/webui/resources/js/ios/web_ui.js
@@ -15,7 +15,7 @@
   __gCrWeb.message.invokeOnHost({
     'command': 'webui.chromeSend',
     'message': message,
-    'arguments': args || []
+    'arguments': args || [],
   });
 };
 
diff --git a/ui/webui/resources/js/metrics_reporter/metrics_reporter.ts b/ui/webui/resources/js/metrics_reporter/metrics_reporter.ts
index d44ec1f5..14b518f 100644
--- a/ui/webui/resources/js/metrics_reporter/metrics_reporter.ts
+++ b/ui/webui/resources/js/metrics_reporter/metrics_reporter.ts
@@ -77,7 +77,7 @@
     callbackRouter.onGetMark.addListener(
         (name: string) => ({
           markedTime:
-              this.marks_.has(name) ? timeToMojo(this.marks_.get(name)!) : null
+              this.marks_.has(name) ? timeToMojo(this.marks_.get(name)!) : null,
         }));
 
     callbackRouter.onClearMark.addListener(
diff --git a/ui/webui/resources/js/parse_html_subset.js b/ui/webui/resources/js/parse_html_subset.js
index 0bfd091..83159d2 100644
--- a/ui/webui/resources/js/parse_html_subset.js
+++ b/ui/webui/resources/js/parse_html_subset.js
@@ -59,7 +59,7 @@
         return node.tagName === 'A' &&
             (value.startsWith('chrome://') || value.startsWith('https://') ||
              value === '#');
-      }
+      },
     ],
     [
       'target',
@@ -67,7 +67,7 @@
         // Only allow a[target='_blank'].
         // TODO(dbeam): are there valid use cases for target !== '_blank'?
         return node.tagName === 'A' && value === '_blank';
-      }
+      },
     ],
   ]);
 
@@ -86,7 +86,7 @@
       (node, value) => {
         // Only allow img[src] starting with chrome://
         return node.tagName === 'IMG' && value.startsWith('chrome://');
-      }
+      },
     ],
     ['tabindex', allowAttribute],
     ['aria-hidden', allowAttribute],
diff --git a/ui/webui/resources/js/static_types.js b/ui/webui/resources/js/static_types.js
index 4fddfc8..84f9392e 100644
--- a/ui/webui/resources/js/static_types.js
+++ b/ui/webui/resources/js/static_types.js
@@ -53,7 +53,7 @@
 const rules = {
   createHTML: createTypes,
   createScript: createTypes,
-  createScriptURL: createTypes
+  createScriptURL: createTypes,
 };
 
 /**
diff --git a/weblayer/API_OWNERS b/weblayer/API_OWNERS
index 8098e861..4357e6a 100644
--- a/weblayer/API_OWNERS
+++ b/weblayer/API_OWNERS
@@ -3,5 +3,6 @@
 #
 blundell@chromium.org
 boliu@chromium.org
+cduvall@chromium.org
 jam@chromium.org
 sky@chromium.org