diff --git a/DEPS b/DEPS
index af17a5d9..80b86f85 100644
--- a/DEPS
+++ b/DEPS
@@ -235,7 +235,7 @@
   # luci-go CIPD package version.
   # Make sure the revision is uploaded by infra-packagers builder.
   # https://ci.chromium.org/p/infra-internal/g/infra-packagers/console
-  'luci_go': 'git_revision:4494da54d1ba43d9127c07630d452c11ba2c953e',
+  'luci_go': 'git_revision:3569ebf36f17a991aa4d26fd6e228cdf6e664d13',
 
   # This can be overridden, e.g. with custom_vars, to build clang from HEAD
   # instead of downloading the prebuilt pinned revision.
@@ -304,15 +304,15 @@
   # 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': '528ff00a3637f6fef419b69392c803f8f2a1e778',
+  'skia_revision': 'f7064a1861e13b87cdfaafb11410c7e68c1f440d',
   # 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': '5f7f064aee9828f22e29abdc45b63e6c9b854298',
+  'v8_revision': '27bc33ed3812654d8efaa1e640955adf6afd6632',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': 'fbb16d64636b352e2a6c68b8dfc8bbd5e5918585',
+  'angle_revision': '1ee27fcdbfe6ad6138907783cd43b83d6720f390',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -391,7 +391,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling devtools-frontend
   # and whatever else without interference from each other.
-  'devtools_frontend_revision': 'a09c4de27878ff6f526c3b78f53bf99e6495f1d3',
+  'devtools_frontend_revision': '9ba6ed7f9e83373c5395aa30c66dd6c81f0efec0',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libprotobuf-mutator
   # and whatever else without interference from each other.
@@ -431,7 +431,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': '86f01195ceaff80fc20498a7fb1a0ad70a6c366e',
+  'dawn_revision': 'df714941d3a24b7d7a6e7e2a5475c8e8a76a81f8',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -794,7 +794,7 @@
 
   'src/clank': {
     'url': Var('chrome_git') + '/clank/internal/apps.git' + '@' +
-    '3935d163b8c4d7775a19b87a678849992e18f255',
+    'cf8ffc879eb73497b886ddfe4e901c274b96c435',
     'condition': 'checkout_android and checkout_src_internal',
   },
 
@@ -983,7 +983,7 @@
     'packages': [
       {
           'package': 'chromium/third_party/androidx',
-          'version': 'S5-ILgSdnMEpl88ZRDuQOJ0Ds_HZR3JAKxZzSoj5toYC',
+          'version': 'zd34H0ZKUoP4415kneK7fwSS-MzZu0_hYScGJWzsVgkC',
       },
     ],
     'condition': 'checkout_android',
@@ -1234,13 +1234,13 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '33a8f072364d819b69cfb45958e17aca021150ad',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'df528a9d7a40d52f0cf0b61cf21e1e298f67860a',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
 
   'src/third_party/devtools-frontend-internal': {
-      'url': Var('chrome_git') + '/devtools/devtools-internal.git' + '@' + '1edfd44e2837c5219e24e004e31602c19ca6a9c2',
+      'url': Var('chrome_git') + '/devtools/devtools-internal.git' + '@' + '261f701a7f57b23ea170314c1925d3743858b496',
     'condition': 'checkout_src_internal',
   },
 
@@ -1891,7 +1891,7 @@
     Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '7e014d4d99cdacd0c08a711d379fcb7886ed66d2',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '90d5e7d65520656ddec367cd454bffc16d55c423',
+    Var('webrtc_git') + '/src.git' + '@' + 'ef5cd7d336ffa0b388a5b2140ef7a6ec31b69b75',
 
   # Wuffs' canonical repository is at github.com/google/wuffs, but we use
   # Skia's mirror of Wuffs, the same as in upstream Skia's DEPS file.
@@ -1968,7 +1968,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': Var('chrome_git') + '/chrome/src-internal.git@24d159fca4f5a3e4a816bfab61767356a578dfa5',
+    'url': Var('chrome_git') + '/chrome/src-internal.git@8f94ab55bf24a2399e85050aaaa74f172632d0cf',
     'condition': 'checkout_src_internal',
   },
 
@@ -2009,7 +2009,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/media_app/app',
-        'version': 'eidV3Okk2cLuns-cpL0H5ywKTXHCLHczvGCJXBYyPv4C',
+        'version': 'AvGNSo5SPRLnsrDFsxN4Yxo5F9rx1WPz0-DYmEEDkGMC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
diff --git a/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java b/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
index 695fd5b..ddfec4f 100644
--- a/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
+++ b/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
@@ -23,6 +23,7 @@
 import org.chromium.content_public.common.ContentSwitches;
 import org.chromium.gpu.config.GpuFeatures;
 import org.chromium.gpu.config.GpuSwitches;
+import org.chromium.net.NetFeatures;
 import org.chromium.services.network.NetworkServiceFeatures;
 import org.chromium.ui.accessibility.AccessibilityFeatures;
 
@@ -389,6 +390,9 @@
             Flag.baseFeature("LessChattyNetworkService"),
             Flag.baseFeature(BlinkFeatures.AUTOFILL_DETECT_REMOVED_FORM_CONTROLS,
                     "Enables Autofill to detect if form controls are removed from the DOM"),
+            Flag.baseFeature(NetFeatures.SUPPORT_PARTITIONED_BLOB_URL,
+                    "Enables the new Blob URL implementation needed for third-party storage"
+                            + " partitioning"),
             // Add new commandline switches and features above. The final entry should have a
             // trailing comma for cleaner diffs.
     };
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AcceptLanguageTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AcceptLanguageTest.java
index 0ad75e76..6970362 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AcceptLanguageTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AcceptLanguageTest.java
@@ -5,8 +5,8 @@
 package org.chromium.android_webview.test;
 
 import android.os.LocaleList;
-import android.support.test.InstrumentationRegistry;
 
+import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 
 import org.junit.After;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AndroidScrollIntegrationTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AndroidScrollIntegrationTest.java
index e65180d..0452090fe 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AndroidScrollIntegrationTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AndroidScrollIntegrationTest.java
@@ -9,8 +9,8 @@
 import android.annotation.SuppressLint;
 import android.content.Context;
 import android.graphics.Point;
-import android.support.test.InstrumentationRegistry;
 
+import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 
 import org.junit.After;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AndroidViewIntegrationTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AndroidViewIntegrationTest.java
index 593355c..ec925764 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AndroidViewIntegrationTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AndroidViewIntegrationTest.java
@@ -4,11 +4,11 @@
 
 package org.chromium.android_webview.test;
 
-import android.support.test.InstrumentationRegistry;
 import android.view.View;
 import android.view.ViewGroup.LayoutParams;
 import android.widget.LinearLayout;
 
+import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 
 import org.junit.Assert;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwActivityTestRule.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwActivityTestRule.java
index b29e8a2..6c90d32 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwActivityTestRule.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwActivityTestRule.java
@@ -6,12 +6,13 @@
 
 import android.content.Context;
 import android.content.Intent;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.lifecycle.Stage;
 import android.util.AndroidRuntimeException;
 import android.util.Base64;
 import android.view.ViewGroup;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.lifecycle.Stage;
+
 import org.junit.Assert;
 import org.junit.runner.Description;
 import org.junit.runners.model.Statement;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwAutofillTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwAutofillTest.java
index 04b702d..104d7b3e 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwAutofillTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwAutofillTest.java
@@ -19,7 +19,6 @@
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.SystemClock;
-import android.support.test.InstrumentationRegistry;
 import android.text.TextUtils;
 import android.util.Pair;
 import android.util.SparseArray;
@@ -29,6 +28,7 @@
 import android.view.autofill.AutofillValue;
 
 import androidx.annotation.RequiresApi;
+import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 
 import org.junit.After;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientCallbackHelperTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientCallbackHelperTest.java
index 4cbf2ea..40e1872 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientCallbackHelperTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientCallbackHelperTest.java
@@ -9,8 +9,8 @@
 import android.graphics.Picture;
 import android.os.Handler;
 import android.os.Looper;
-import android.support.test.InstrumentationRegistry;
 
+import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 
 import org.junit.Assert;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientFullScreenTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientFullScreenTest.java
index 06bfff1c..49e5d01 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientFullScreenTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientFullScreenTest.java
@@ -6,11 +6,11 @@
 
 import static org.chromium.android_webview.test.AwActivityTestRule.WAIT_TIMEOUT_MS;
 
-import android.support.test.InstrumentationRegistry;
 import android.view.KeyEvent;
 import android.view.View;
 import android.view.ViewGroup;
 
+import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.MediumTest;
 
 import org.hamcrest.Matchers;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientGetDefaultVideoPosterTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientGetDefaultVideoPosterTest.java
index cd1f54bc..f33553a 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientGetDefaultVideoPosterTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientGetDefaultVideoPosterTest.java
@@ -7,9 +7,9 @@
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
-import android.support.test.InstrumentationRegistry;
 import android.util.Log;
 
+import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 
 import org.junit.Assert;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientOnFormResubmissionTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientOnFormResubmissionTest.java
index 363ddce..92d6dc0 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientOnFormResubmissionTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientOnFormResubmissionTest.java
@@ -5,9 +5,9 @@
 package org.chromium.android_webview.test;
 
 import android.os.Message;
-import android.support.test.InstrumentationRegistry;
 import android.util.Base64;
 
+import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 
 import org.junit.After;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientShouldInterceptRequestTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientShouldInterceptRequestTest.java
index 9a06916..b205fe9b 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientShouldInterceptRequestTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientShouldInterceptRequestTest.java
@@ -6,10 +6,10 @@
 
 import static org.chromium.android_webview.test.AwActivityTestRule.SCALED_WAIT_TIMEOUT_MS;
 
-import android.support.test.InstrumentationRegistry;
 import android.util.Pair;
 import android.webkit.JavascriptInterface;
 
+import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 
 import com.google.common.util.concurrent.SettableFuture;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientShouldOverrideUrlLoadingTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientShouldOverrideUrlLoadingTest.java
index 85d1f72..21af506e 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientShouldOverrideUrlLoadingTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientShouldOverrideUrlLoadingTest.java
@@ -9,9 +9,9 @@
 
 import android.annotation.SuppressLint;
 import android.os.Build;
-import android.support.test.InstrumentationRegistry;
 import android.util.Pair;
 
+import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 
 import org.hamcrest.Matchers;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsGarbageCollectionTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsGarbageCollectionTest.java
index d8a9d9be..b5e7ab1 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsGarbageCollectionTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsGarbageCollectionTest.java
@@ -11,12 +11,12 @@
 import android.content.ContextWrapper;
 import android.os.Build;
 import android.os.ResultReceiver;
-import android.support.test.InstrumentationRegistry;
 import android.util.Pair;
 import android.view.Window;
 import android.view.WindowManager;
 import android.webkit.JavascriptInterface;
 
+import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.LargeTest;
 import androidx.test.filters.SmallTest;
 
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsRenderTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsRenderTest.java
index 71a6227b..45aaca1 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsRenderTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsRenderTest.java
@@ -6,9 +6,9 @@
 
 import android.graphics.Bitmap;
 import android.graphics.Color;
-import android.support.test.InstrumentationRegistry;
 import android.view.View;
 
+import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 
 import org.junit.Assert;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsStaticsTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsStaticsTest.java
index 5e54e59..a6763dd 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsStaticsTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsStaticsTest.java
@@ -4,8 +4,7 @@
 
 package org.chromium.android_webview.test;
 
-import android.support.test.InstrumentationRegistry;
-
+import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 
 import org.junit.Assert;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsTest.java
index ae0c64c..8194a8f3 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsTest.java
@@ -19,12 +19,12 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
-import android.support.test.InstrumentationRegistry;
 import android.util.Pair;
 import android.view.KeyEvent;
 import android.view.View;
 import android.webkit.JavascriptInterface;
 
+import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.LargeTest;
 import androidx.test.filters.MediumTest;
 import androidx.test.filters.SmallTest;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwFormDatabaseTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwFormDatabaseTest.java
index d99b9e36d..43d0b2b 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwFormDatabaseTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwFormDatabaseTest.java
@@ -4,8 +4,7 @@
 
 package org.chromium.android_webview.test;
 
-import android.support.test.InstrumentationRegistry;
-
+import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 
 import org.junit.Assert;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwJavaBridgeTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwJavaBridgeTest.java
index 1ccf94ba..5657d47b 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwJavaBridgeTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwJavaBridgeTest.java
@@ -4,9 +4,9 @@
 
 package org.chromium.android_webview.test;
 
-import android.support.test.InstrumentationRegistry;
 import android.webkit.JavascriptInterface;
 
+import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 
 import org.junit.Assert;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwLegacyQuirksTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwLegacyQuirksTest.java
index b654ceca..8cc1ccd 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwLegacyQuirksTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwLegacyQuirksTest.java
@@ -4,8 +4,7 @@
 
 package org.chromium.android_webview.test;
 
-import android.support.test.InstrumentationRegistry;
-
+import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.MediumTest;
 
 import org.junit.Assert;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwMetricsIntegrationTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwMetricsIntegrationTest.java
index 58d3f65..7f474706 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwMetricsIntegrationTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwMetricsIntegrationTest.java
@@ -12,8 +12,7 @@
 
 import static org.chromium.android_webview.test.OnlyRunIn.ProcessMode.MULTI_PROCESS;
 
-import android.support.test.InstrumentationRegistry;
-
+import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.MediumTest;
 
 import org.hamcrest.Description;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwNetworkConfigurationTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwNetworkConfigurationTest.java
index 864529ad..6289c9ef 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwNetworkConfigurationTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwNetworkConfigurationTest.java
@@ -7,9 +7,9 @@
 import static org.chromium.android_webview.test.AwActivityTestRule.WAIT_TIMEOUT_MS;
 
 import android.os.Build;
-import android.support.test.InstrumentationRegistry;
 import android.webkit.JavascriptInterface;
 
+import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 
 import com.google.common.util.concurrent.SettableFuture;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwQuotaManagerBridgeTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwQuotaManagerBridgeTest.java
index 8a495e0..ff8fb80 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwQuotaManagerBridgeTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwQuotaManagerBridgeTest.java
@@ -4,7 +4,7 @@
 
 package org.chromium.android_webview.test;
 
-import android.support.test.InstrumentationRegistry;
+import androidx.test.InstrumentationRegistry;
 
 import org.junit.After;
 import org.junit.Assert;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwRestrictSensitiveContentTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwRestrictSensitiveContentTest.java
index 93c8216..2381b36 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwRestrictSensitiveContentTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwRestrictSensitiveContentTest.java
@@ -5,9 +5,9 @@
 package org.chromium.android_webview.test;
 
 import android.content.Context;
-import android.support.test.InstrumentationRegistry;
 import android.util.Pair;
 
+import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 
 import org.junit.After;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwSecondBrowserProcessTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwSecondBrowserProcessTest.java
index 899a167..d9f21c3 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwSecondBrowserProcessTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwSecondBrowserProcessTest.java
@@ -13,7 +13,8 @@
 import android.os.Parcel;
 import android.os.Process;
 import android.os.RemoteException;
-import android.support.test.InstrumentationRegistry;
+
+import androidx.test.InstrumentationRegistry;
 
 import org.junit.After;
 import org.junit.Assert;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwSettingsTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwSettingsTest.java
index 3cd416c..5be64a7 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwSettingsTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwSettingsTest.java
@@ -11,11 +11,11 @@
 import android.net.http.SslError;
 import android.os.Build;
 import android.os.SystemClock;
-import android.support.test.InstrumentationRegistry;
 import android.view.WindowManager;
 import android.webkit.JavascriptInterface;
 import android.webkit.WebSettings;
 
+import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.LargeTest;
 import androidx.test.filters.MediumTest;
 import androidx.test.filters.SmallTest;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwStrictModeTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwStrictModeTest.java
index 3ea20cf9..493f77f 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwStrictModeTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwStrictModeTest.java
@@ -5,8 +5,8 @@
 package org.chromium.android_webview.test;
 
 import android.os.StrictMode;
-import android.support.test.InstrumentationRegistry;
 
+import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.LargeTest;
 
 import org.junit.After;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwUncaughtExceptionTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwUncaughtExceptionTest.java
index f8ebd22..ab5bfbe 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwUncaughtExceptionTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwUncaughtExceptionTest.java
@@ -7,10 +7,10 @@
 import static org.chromium.android_webview.test.AwActivityTestRule.SCALED_WAIT_TIMEOUT_MS;
 
 import android.os.Looper;
-import android.support.test.runner.lifecycle.ActivityLifecycleMonitor;
-import android.support.test.runner.lifecycle.ActivityLifecycleMonitorRegistry;
 
 import androidx.test.filters.MediumTest;
+import androidx.test.runner.lifecycle.ActivityLifecycleMonitor;
+import androidx.test.runner.lifecycle.ActivityLifecycleMonitorRegistry;
 
 import org.junit.After;
 import org.junit.Assert;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwWebContentsObserverTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwWebContentsObserverTest.java
index f8c36e2c..bf6abbe 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwWebContentsObserverTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwWebContentsObserverTest.java
@@ -4,8 +4,7 @@
 
 package org.chromium.android_webview.test;
 
-import android.support.test.InstrumentationRegistry;
-
+import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 
 import org.junit.Assert;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/ClearHistoryTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/ClearHistoryTest.java
index daf0d180..94cbe6f 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/ClearHistoryTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/ClearHistoryTest.java
@@ -4,8 +4,7 @@
 
 package org.chromium.android_webview.test;
 
-import android.support.test.InstrumentationRegistry;
-
+import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 
 import org.junit.Assert;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/ClientAddMessageToConsoleTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/ClientAddMessageToConsoleTest.java
index cf15c25..6956848 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/ClientAddMessageToConsoleTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/ClientAddMessageToConsoleTest.java
@@ -4,8 +4,7 @@
 
 package org.chromium.android_webview.test;
 
-import android.support.test.InstrumentationRegistry;
-
+import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 
 import org.junit.Assert;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/ClientHintsTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/ClientHintsTest.java
index 64fe0b0..4a5835e 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/ClientHintsTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/ClientHintsTest.java
@@ -6,8 +6,7 @@
 
 import static org.chromium.android_webview.test.AwActivityTestRule.WAIT_TIMEOUT_MS;
 
-import android.support.test.InstrumentationRegistry;
-
+import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 
 import org.json.JSONObject;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/ClientOnPageFinishedTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/ClientOnPageFinishedTest.java
index e25a02f..cf9714f 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/ClientOnPageFinishedTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/ClientOnPageFinishedTest.java
@@ -6,9 +6,9 @@
 
 import static org.chromium.android_webview.test.AwActivityTestRule.SCALED_WAIT_TIMEOUT_MS;
 
-import android.support.test.InstrumentationRegistry;
 import android.util.Pair;
 
+import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.MediumTest;
 
 import org.junit.Assert;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/ContentViewMiscTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/ContentViewMiscTest.java
index 1f10f2e..f2ab7ec 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/ContentViewMiscTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/ContentViewMiscTest.java
@@ -10,8 +10,8 @@
 import android.net.Proxy;
 import android.os.Handler;
 import android.os.Looper;
-import android.support.test.InstrumentationRegistry;
 
+import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 
 import org.junit.Assert;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/CookieManagerStartupTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/CookieManagerStartupTest.java
index f8b4428..20d1af9 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/CookieManagerStartupTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/CookieManagerStartupTest.java
@@ -5,8 +5,8 @@
 package org.chromium.android_webview.test;
 
 import android.os.Looper;
-import android.support.test.InstrumentationRegistry;
 
+import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.MediumTest;
 import androidx.test.filters.SmallTest;
 
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/CookieManagerTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/CookieManagerTest.java
index d46cc8e4..2e8bb2d 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/CookieManagerTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/CookieManagerTest.java
@@ -8,10 +8,10 @@
 import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.MatcherAssert.assertThat;
 
-import android.support.test.InstrumentationRegistry;
 import android.util.Pair;
 
 import androidx.annotation.IntDef;
+import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.MediumTest;
 import androidx.test.filters.SmallTest;
 
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/FencedFrameTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/FencedFrameTest.java
index efe08f2..116cfc4 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/FencedFrameTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/FencedFrameTest.java
@@ -7,11 +7,11 @@
 import static org.chromium.android_webview.test.AwActivityTestRule.WAIT_TIMEOUT_MS;
 
 import android.graphics.Color;
-import android.support.test.InstrumentationRegistry;
 import android.util.Pair;
 import android.webkit.JavascriptInterface;
 import android.webkit.WebView.HitTestResult;
 
+import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 
 import org.junit.After;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/GeolocationTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/GeolocationTest.java
index 3fc06184..ac929a4 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/GeolocationTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/GeolocationTest.java
@@ -5,8 +5,8 @@
 package org.chromium.android_webview.test;
 
 import android.content.Context;
-import android.support.test.InstrumentationRegistry;
 
+import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.MediumTest;
 import androidx.test.filters.SmallTest;
 
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/HttpAuthDatabaseTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/HttpAuthDatabaseTest.java
index a357f94..82a99ae 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/HttpAuthDatabaseTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/HttpAuthDatabaseTest.java
@@ -6,8 +6,7 @@
 
 import static org.chromium.android_webview.test.OnlyRunIn.ProcessMode.SINGLE_PROCESS;
 
-import android.support.test.InstrumentationRegistry;
-
+import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 
 import org.junit.After;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/LoadDataWithBaseUrlTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/LoadDataWithBaseUrlTest.java
index 76baaf2..f2355fc 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/LoadDataWithBaseUrlTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/LoadDataWithBaseUrlTest.java
@@ -5,9 +5,9 @@
 package org.chromium.android_webview.test;
 
 import android.graphics.Bitmap;
-import android.support.test.InstrumentationRegistry;
 import android.util.Pair;
 
+import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 
 import org.junit.Assert;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/LoadUrlTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/LoadUrlTest.java
index 7ee237c..8ee67c51 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/LoadUrlTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/LoadUrlTest.java
@@ -7,11 +7,11 @@
 import static org.chromium.android_webview.test.AwActivityTestRule.WAIT_TIMEOUT_MS;
 
 import android.content.Context;
-import android.support.test.InstrumentationRegistry;
 import android.util.Base64;
 import android.util.Pair;
 import android.view.ViewGroup;
 
+import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 
 import org.json.JSONArray;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/ManifestMetadataUtilTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/ManifestMetadataUtilTest.java
index 5065017..9a83d34f 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/ManifestMetadataUtilTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/ManifestMetadataUtilTest.java
@@ -6,8 +6,8 @@
 
 import android.content.ComponentName;
 import android.os.Bundle;
-import android.support.test.InstrumentationRegistry;
 
+import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 
 import org.junit.Assert;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/NavigationHistoryTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/NavigationHistoryTest.java
index 91b3a14..fd1d0b3 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/NavigationHistoryTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/NavigationHistoryTest.java
@@ -4,8 +4,7 @@
 
 package org.chromium.android_webview.test;
 
-import android.support.test.InstrumentationRegistry;
-
+import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 
 import org.junit.After;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/OnDiskFileTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/OnDiskFileTest.java
index 26d8e79..99c7a09 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/OnDiskFileTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/OnDiskFileTest.java
@@ -5,8 +5,8 @@
 package org.chromium.android_webview.test;
 
 import android.content.Context;
-import android.support.test.InstrumentationRegistry;
 
+import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 
 import org.junit.Assert;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/PolicyUrlFilteringTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/PolicyUrlFilteringTest.java
index 9164c497..086735505 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/PolicyUrlFilteringTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/PolicyUrlFilteringTest.java
@@ -6,9 +6,9 @@
 
 import static org.chromium.android_webview.test.OnlyRunIn.ProcessMode.SINGLE_PROCESS;
 
-import android.support.test.InstrumentationRegistry;
 import android.util.Pair;
 
+import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.MediumTest;
 import androidx.test.filters.SmallTest;
 
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/PopupWindowTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/PopupWindowTest.java
index 62c20d9..87c1dc4 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/PopupWindowTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/PopupWindowTest.java
@@ -6,9 +6,9 @@
 
 import android.graphics.Rect;
 import android.net.Uri;
-import android.support.test.InstrumentationRegistry;
 import android.webkit.JavascriptInterface;
 
+import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 
 import org.hamcrest.Matchers;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/PostMessageTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/PostMessageTest.java
index 67eca22..1eb3bb97 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/PostMessageTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/PostMessageTest.java
@@ -9,9 +9,9 @@
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Looper;
-import android.support.test.InstrumentationRegistry;
 import android.webkit.JavascriptInterface;
 
+import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 
 import org.hamcrest.Matchers;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/SafeBrowsingTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/SafeBrowsingTest.java
index 31c1772..f257ab4 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/SafeBrowsingTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/SafeBrowsingTest.java
@@ -13,9 +13,9 @@
 import android.graphics.Color;
 import android.net.Uri;
 import android.os.Build;
-import android.support.test.InstrumentationRegistry;
 import android.view.ViewGroup;
 
+import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 
 import org.hamcrest.Matchers;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/SaveRestoreStateTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/SaveRestoreStateTest.java
index 6da760824..316af86c 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/SaveRestoreStateTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/SaveRestoreStateTest.java
@@ -5,8 +5,8 @@
 package org.chromium.android_webview.test;
 
 import android.os.Bundle;
-import android.support.test.InstrumentationRegistry;
 
+import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 
 import org.junit.After;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/SslPreferencesTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/SslPreferencesTest.java
index 601ebeea..28b144fc 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/SslPreferencesTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/SslPreferencesTest.java
@@ -5,8 +5,8 @@
 package org.chromium.android_webview.test;
 
 import android.net.http.SslError;
-import android.support.test.InstrumentationRegistry;
 
+import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 
 import org.junit.Assert;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/VisualStateTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/VisualStateTest.java
index 80b264d..bd44f78 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/VisualStateTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/VisualStateTest.java
@@ -10,9 +10,9 @@
 import android.graphics.Bitmap;
 import android.graphics.Color;
 import android.graphics.Rect;
-import android.support.test.InstrumentationRegistry;
 import android.util.Base64;
 
+import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 
 import org.junit.Assert;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/WebKitHitTestTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/WebKitHitTestTest.java
index 1178499b..be4da908 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/WebKitHitTestTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/WebKitHitTestTest.java
@@ -9,10 +9,10 @@
 import android.os.Handler;
 import android.os.Message;
 import android.os.SystemClock;
-import android.support.test.InstrumentationRegistry;
 import android.view.KeyEvent;
 import android.webkit.WebView.HitTestResult;
 
+import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.LargeTest;
 import androidx.test.filters.SmallTest;
 
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/WebViewAsynchronousFindApisTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/WebViewAsynchronousFindApisTest.java
index 2ac0449..d1c1fe34 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/WebViewAsynchronousFindApisTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/WebViewAsynchronousFindApisTest.java
@@ -4,8 +4,7 @@
 
 package org.chromium.android_webview.test;
 
-import android.support.test.InstrumentationRegistry;
-
+import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 
 import org.junit.Assert;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/WebViewFindApisTestRule.java b/android_webview/javatests/src/org/chromium/android_webview/test/WebViewFindApisTestRule.java
index 8329fd1d..6154915 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/WebViewFindApisTestRule.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/WebViewFindApisTestRule.java
@@ -4,7 +4,7 @@
 
 package org.chromium.android_webview.test;
 
-import android.support.test.InstrumentationRegistry;
+import androidx.test.InstrumentationRegistry;
 
 import org.junit.runner.Description;
 import org.junit.runners.model.Statement;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/devui/ComponentsListFragmentTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/devui/ComponentsListFragmentTest.java
index 2e99028..100d36b 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/devui/ComponentsListFragmentTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/devui/ComponentsListFragmentTest.java
@@ -17,8 +17,8 @@
 
 import android.content.Context;
 import android.content.Intent;
-import android.support.test.InstrumentationRegistry;
 
+import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 
 import org.junit.After;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/devui/ComponentsListFragmentUpdateButtonTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/devui/ComponentsListFragmentUpdateButtonTest.java
index 44ec9ce..58e31f5 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/devui/ComponentsListFragmentUpdateButtonTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/devui/ComponentsListFragmentUpdateButtonTest.java
@@ -16,8 +16,8 @@
 
 import android.content.Context;
 import android.content.Intent;
-import android.support.test.InstrumentationRegistry;
 
+import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.MediumTest;
 
 import org.junit.After;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/devui/CrashesListFragmentTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/devui/CrashesListFragmentTest.java
index 711851246..fec8f2c17 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/devui/CrashesListFragmentTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/devui/CrashesListFragmentTest.java
@@ -41,11 +41,11 @@
 import android.graphics.Canvas;
 import android.graphics.drawable.Drawable;
 import android.os.Build;
-import android.support.test.InstrumentationRegistry;
 import android.view.View;
 import android.widget.ImageView;
 
 import androidx.annotation.IdRes;
+import androidx.test.InstrumentationRegistry;
 import androidx.test.espresso.DataInteraction;
 import androidx.test.espresso.intent.Intents;
 import androidx.test.espresso.intent.matcher.IntentMatchers;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/devui/DeveloperUiTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/devui/DeveloperUiTest.java
index 0abc802..cd73ba8 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/devui/DeveloperUiTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/devui/DeveloperUiTest.java
@@ -35,9 +35,9 @@
 import android.content.Intent;
 import android.content.SharedPreferences;
 import android.provider.Settings;
-import android.support.test.InstrumentationRegistry;
 import android.view.View;
 
+import androidx.test.InstrumentationRegistry;
 import androidx.test.espresso.DataInteraction;
 import androidx.test.espresso.intent.Intents;
 import androidx.test.espresso.intent.matcher.IntentMatchers;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/devui/FlagsFragmentTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/devui/FlagsFragmentTest.java
index d411bba..5b95c0f 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/devui/FlagsFragmentTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/devui/FlagsFragmentTest.java
@@ -34,7 +34,6 @@
 import android.graphics.Color;
 import android.graphics.drawable.Drawable;
 import android.os.SystemClock;
-import android.support.test.InstrumentationRegistry;
 import android.text.SpannableString;
 import android.text.style.BackgroundColorSpan;
 import android.view.MotionEvent;
@@ -44,6 +43,7 @@
 import android.widget.TextView;
 
 import androidx.annotation.IntDef;
+import androidx.test.InstrumentationRegistry;
 import androidx.test.espresso.DataInteraction;
 import androidx.test.espresso.Espresso;
 import androidx.test.espresso.NoMatchingRootException;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/devui/HomeFragmentTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/devui/HomeFragmentTest.java
index 20f740c..c7752178 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/devui/HomeFragmentTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/devui/HomeFragmentTest.java
@@ -31,8 +31,8 @@
 import android.content.pm.PackageInfo;
 import android.os.Build;
 import android.provider.Settings;
-import android.support.test.InstrumentationRegistry;
 
+import androidx.test.InstrumentationRegistry;
 import androidx.test.espresso.intent.Intents;
 import androidx.test.espresso.intent.matcher.IntentMatchers;
 import androidx.test.filters.MediumTest;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/util/AwQuotaManagerBridgeTestUtil.java b/android_webview/javatests/src/org/chromium/android_webview/test/util/AwQuotaManagerBridgeTestUtil.java
index 46430552..e2b367b 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/util/AwQuotaManagerBridgeTestUtil.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/util/AwQuotaManagerBridgeTestUtil.java
@@ -4,7 +4,7 @@
 
 package org.chromium.android_webview.test.util;
 
-import android.support.test.InstrumentationRegistry;
+import androidx.test.InstrumentationRegistry;
 
 import org.chromium.android_webview.AwQuotaManagerBridge;
 import org.chromium.base.test.util.CallbackHelper;
diff --git a/android_webview/test/BUILD.gn b/android_webview/test/BUILD.gn
index b6c6874e..18cd1b6 100644
--- a/android_webview/test/BUILD.gn
+++ b/android_webview/test/BUILD.gn
@@ -118,8 +118,9 @@
     "//components/heap_profiling/multi_process:heap_profiling_java_test_support",
     "//components/policy/android:policy_java_test_support",
     "//content/public/android:content_java",
-    "//third_party/android_support_test_runner:runner_java",
     "//third_party/androidx:androidx_annotation_annotation_java",
+    "//third_party/androidx:androidx_test_monitor_java",
+    "//third_party/androidx:androidx_test_runner_java",
     "//third_party/androidx_javascriptengine:javascriptengine_java",
     "//third_party/junit",
     "//ui/android:ui_java",
@@ -292,13 +293,12 @@
     "//third_party/android_deps:guava_android_java",
     "//third_party/android_deps:guava_android_java",
     "//third_party/android_deps:protobuf_lite_runtime_java",
-    "//third_party/android_support_test_runner:rules_java",
-    "//third_party/android_support_test_runner:runner_java",
     "//third_party/androidx:androidx_activity_activity_java",
     "//third_party/androidx:androidx_annotation_annotation_java",
     "//third_party/androidx:androidx_core_core_java",
     "//third_party/androidx:androidx_fragment_fragment_java",
     "//third_party/androidx:androidx_test_core_java",
+    "//third_party/androidx:androidx_test_monitor_java",
     "//third_party/androidx:androidx_test_rules_java",
     "//third_party/androidx:androidx_test_runner_java",
     "//third_party/androidx_javascriptengine:javascriptengine_java",
@@ -712,8 +712,8 @@
     "//content/public/android:content_full_java",
     "//content/public/test/android:content_java_test_support",
     "//third_party/android_deps:protobuf_lite_runtime_java",
-    "//third_party/android_support_test_runner:runner_java",
     "//third_party/androidx:androidx_test_core_java",
+    "//third_party/androidx:androidx_test_monitor_java",
     "//third_party/androidx:androidx_test_runner_java",
     "//third_party/blink/public/mojom:mojom_platform_java",
     "//url:gurl_java",
diff --git a/android_webview/tools/automated_ui_tests/BUILD.gn b/android_webview/tools/automated_ui_tests/BUILD.gn
index e8b6219..b641e52 100644
--- a/android_webview/tools/automated_ui_tests/BUILD.gn
+++ b/android_webview/tools/automated_ui_tests/BUILD.gn
@@ -60,8 +60,8 @@
     "//base:base_java_test_support",
     "//third_party/android_deps:espresso_java",
     "//third_party/android_sdk:android_test_base_java",
-    "//third_party/android_support_test_runner:rules_java",
-    "//third_party/android_support_test_runner:runner_java",
+    "//third_party/androidx:androidx_test_monitor_java",
+    "//third_party/androidx:androidx_test_rules_java",
     "//third_party/androidx:androidx_test_runner_java",
     "//third_party/androidx:androidx_test_uiautomator_uiautomator_java",
     "//third_party/hamcrest:hamcrest_java",
diff --git a/android_webview/tools/automated_ui_tests/javatests/src/org/chromium/webview_ui_test/test/ActionModeTest.java b/android_webview/tools/automated_ui_tests/javatests/src/org/chromium/webview_ui_test/test/ActionModeTest.java
index 40dda2a2..2db2377 100644
--- a/android_webview/tools/automated_ui_tests/javatests/src/org/chromium/webview_ui_test/test/ActionModeTest.java
+++ b/android_webview/tools/automated_ui_tests/javatests/src/org/chromium/webview_ui_test/test/ActionModeTest.java
@@ -42,9 +42,9 @@
 import android.app.Instrumentation;
 import android.content.Intent;
 import android.os.Build;
-import android.support.test.InstrumentationRegistry;
 import android.view.MenuItem;
 
+import androidx.test.InstrumentationRegistry;
 import androidx.test.espresso.NoMatchingViewException;
 import androidx.test.espresso.PerformException;
 import androidx.test.espresso.Root;
diff --git a/android_webview/tools/automated_ui_tests/javatests/src/org/chromium/webview_ui_test/test/util/WebViewSyncWrapper.java b/android_webview/tools/automated_ui_tests/javatests/src/org/chromium/webview_ui_test/test/util/WebViewSyncWrapper.java
index 049d8ab..d82840d5 100644
--- a/android_webview/tools/automated_ui_tests/javatests/src/org/chromium/webview_ui_test/test/util/WebViewSyncWrapper.java
+++ b/android_webview/tools/automated_ui_tests/javatests/src/org/chromium/webview_ui_test/test/util/WebViewSyncWrapper.java
@@ -4,7 +4,7 @@
 
 package org.chromium.webview_ui_test.test.util;
 
-import static android.support.test.InstrumentationRegistry.getInstrumentation;
+import static androidx.test.InstrumentationRegistry.getInstrumentation;
 
 import android.os.Looper;
 import android.webkit.ConsoleMessage;
diff --git a/android_webview/tools/system_webview_shell/BUILD.gn b/android_webview/tools/system_webview_shell/BUILD.gn
index adad2289..5cf9c11 100644
--- a/android_webview/tools/system_webview_shell/BUILD.gn
+++ b/android_webview/tools/system_webview_shell/BUILD.gn
@@ -125,8 +125,8 @@
     "//base:base_java_test_support",
     "//content/public/android:content_java",
     "//content/public/test/android:content_java_test_support",
-    "//third_party/android_support_test_runner:rules_java",
-    "//third_party/android_support_test_runner:runner_java",
+    "//third_party/androidx:androidx_test_monitor_java",
+    "//third_party/androidx:androidx_test_rules_java",
     "//third_party/androidx:androidx_test_runner_java",
     "//third_party/junit",
   ]
@@ -154,8 +154,8 @@
     "//base:base_java",
     "//base:base_java_test_support",
     "//third_party/android_sdk:android_test_base_java",
-    "//third_party/android_support_test_runner:rules_java",
-    "//third_party/android_support_test_runner:runner_java",
+    "//third_party/androidx:androidx_test_monitor_java",
+    "//third_party/androidx:androidx_test_rules_java",
     "//third_party/androidx:androidx_test_runner_java",
     "//third_party/junit",
   ]
diff --git a/android_webview/tools/system_webview_shell/layout_tests/src/org/chromium/webview_shell/test/WebPlatformTestsActivityTest.java b/android_webview/tools/system_webview_shell/layout_tests/src/org/chromium/webview_shell/test/WebPlatformTestsActivityTest.java
index 3840f5b..de6a972e 100644
--- a/android_webview/tools/system_webview_shell/layout_tests/src/org/chromium/webview_shell/test/WebPlatformTestsActivityTest.java
+++ b/android_webview/tools/system_webview_shell/layout_tests/src/org/chromium/webview_shell/test/WebPlatformTestsActivityTest.java
@@ -7,9 +7,9 @@
 import static org.chromium.base.test.util.ScalableTimeout.scaleTimeout;
 
 import android.os.Handler;
-import android.support.test.InstrumentationRegistry;
 import android.webkit.WebView;
 
+import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.MediumTest;
 
 import org.junit.Assert;
diff --git a/android_webview/tools/system_webview_shell/layout_tests/src/org/chromium/webview_shell/test/WebViewLayoutTest.java b/android_webview/tools/system_webview_shell/layout_tests/src/org/chromium/webview_shell/test/WebViewLayoutTest.java
index 5178782..1d25665 100644
--- a/android_webview/tools/system_webview_shell/layout_tests/src/org/chromium/webview_shell/test/WebViewLayoutTest.java
+++ b/android_webview/tools/system_webview_shell/layout_tests/src/org/chromium/webview_shell/test/WebViewLayoutTest.java
@@ -5,8 +5,8 @@
 package org.chromium.webview_shell.test;
 
 import android.os.Bundle;
-import android.support.test.InstrumentationRegistry;
 
+import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.MediumTest;
 
 import junit.framework.ComparisonFailure;
diff --git a/android_webview/tools/system_webview_shell/page_cycler/src/org/chromium/webview_shell/page_cycler/PageCyclerTest.java b/android_webview/tools/system_webview_shell/page_cycler/src/org/chromium/webview_shell/page_cycler/PageCyclerTest.java
index 0b0be3ca..b8f03fd 100644
--- a/android_webview/tools/system_webview_shell/page_cycler/src/org/chromium/webview_shell/page_cycler/PageCyclerTest.java
+++ b/android_webview/tools/system_webview_shell/page_cycler/src/org/chromium/webview_shell/page_cycler/PageCyclerTest.java
@@ -4,11 +4,11 @@
 
 package org.chromium.webview_shell.page_cycler;
 
-import android.support.test.InstrumentationRegistry;
 import android.webkit.WebSettings;
 import android.webkit.WebView;
 import android.webkit.WebViewClient;
 
+import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.LargeTest;
 
 import org.junit.Assert;
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd
index 2fec6d1..d8ae607 100644
--- a/ash/ash_strings.grd
+++ b/ash/ash_strings.grd
@@ -4813,7 +4813,7 @@
       </message>
 
       <message name="IDS_ASH_CALENDAR_JOIN_BUTTON_ACCESSIBLE_NAME" desc="The join meeting button accessible name on the event list item views.">
-        Hit enter to join event <ph name="event_summary">$1<ex>Lunch break</ex></ph>
+        Join <ph name="event_summary">$1<ex>Lunch break</ex></ph>
       </message>
 
       <message name="IDS_ASH_STATUS_TRAY_PROGRESS_BAR_ACCESSIBLE_NAME" desc="The accessible name for the progress bar shown in the status tray.">
diff --git a/ash/ash_strings_grd/IDS_ASH_CALENDAR_JOIN_BUTTON_ACCESSIBLE_NAME.png.sha1 b/ash/ash_strings_grd/IDS_ASH_CALENDAR_JOIN_BUTTON_ACCESSIBLE_NAME.png.sha1
index ed28750f..2c592260 100644
--- a/ash/ash_strings_grd/IDS_ASH_CALENDAR_JOIN_BUTTON_ACCESSIBLE_NAME.png.sha1
+++ b/ash/ash_strings_grd/IDS_ASH_CALENDAR_JOIN_BUTTON_ACCESSIBLE_NAME.png.sha1
@@ -1 +1 @@
-a897cbbf687238c380b5b0b3b641cc7c194fb4c8
\ No newline at end of file
+9103ecfa3006ec73595cd039ff5fd52bd09a4165
\ No newline at end of file
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc
index 910b5bc..9cc8deb2 100644
--- a/ash/constants/ash_features.cc
+++ b/ash/constants/ash_features.cc
@@ -1185,7 +1185,7 @@
 // Enable or disable a new header bar for the ChromeOS virtual keyboard.
 BASE_FEATURE(kVirtualKeyboardNewHeader,
              "VirtualKeyboardNewHeader",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 
 // If enabled, used to configure the heuristic rules for some advanced IME
 // features (e.g. auto-correct).
@@ -2016,7 +2016,7 @@
 // Enables the alternative emulator for the Terminal app.
 BASE_FEATURE(kTerminalAlternativeEmulator,
              "TerminalAlternativeEmulator",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 //
 // Enables Terminal System App to load from Downloads for developer testing.
 // Only works in dev and canary channels.
diff --git a/ash/shelf/drag_window_from_shelf_controller.cc b/ash/shelf/drag_window_from_shelf_controller.cc
index 2463423..18f66604 100644
--- a/ash/shelf/drag_window_from_shelf_controller.cc
+++ b/ash/shelf/drag_window_from_shelf_controller.cc
@@ -20,6 +20,7 @@
 #include "ash/wallpaper/wallpaper_constants.h"
 #include "ash/wallpaper/wallpaper_view.h"
 #include "ash/wallpaper/wallpaper_widget_controller.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_constants.h"
@@ -242,8 +243,20 @@
       other_window_copy_ = wm::RecreateLayers(other_window_);
       other_window_copy_->root()->SetVisible(true);
       other_window_copy_->root()->SetOpacity(1.f);
-      other_window_->layer()->parent()->StackAbove(other_window_copy_->root(),
-                                                   other_window_->layer());
+
+      // If `other_window_` is the floated window, we need to move the copy to
+      // the active desk container. The float container will be moved under the
+      // desk containers (see `ScopedFloatContainerStacker `), so that the
+      // overview item does not appear above the dragged window during the drag.
+      if (other_window_ == floated_window) {
+        ui::Layer* new_parent = desks_util::GetActiveDeskContainerForRoot(
+                                    Shell::GetPrimaryRootWindow())
+                                    ->layer();
+        new_parent->Add(other_window_copy_->root());
+      } else {
+        other_window_->layer()->parent()->StackAbove(other_window_copy_->root(),
+                                                     other_window_->layer());
+      }
     }
   }
 
@@ -628,12 +641,16 @@
     copy_scale = 1.f - base::clamp(copy_scale, 0.f, 1.f);
 
     other_window_copy_->root()->SetOpacity(copy_scale);
-    const float copy_transform_scale =
-        base::clamp(copy_scale, kOtherWindowMaxScale, 1.f);
-    const gfx::Transform copy_transform = gfx::GetScaleTransform(
-        other_window_copy_->root()->bounds().CenterPoint(),
-        copy_transform_scale);
-    other_window_copy_->root()->SetTransform(copy_transform);
+
+    CHECK(other_window_);
+    if (!WindowState::Get(other_window_)->IsFloated()) {
+      const float copy_transform_scale =
+          base::clamp(copy_scale, kOtherWindowMaxScale, 1.f);
+      const gfx::Transform copy_transform = gfx::GetScaleTransform(
+          other_window_copy_->root()->bounds().CenterPoint(),
+          copy_transform_scale);
+      other_window_copy_->root()->SetTransform(copy_transform);
+    }
   }
 }
 
diff --git a/ash/shelf/drag_window_from_shelf_controller_unittest.cc b/ash/shelf/drag_window_from_shelf_controller_unittest.cc
index bdecbff..5232dd3 100644
--- a/ash/shelf/drag_window_from_shelf_controller_unittest.cc
+++ b/ash/shelf/drag_window_from_shelf_controller_unittest.cc
@@ -1488,12 +1488,13 @@
   ui::Layer* other_window_copy_layer = GetOtherWindowCopyLayer();
   ASSERT_TRUE(other_window_copy_layer);
 
-  // To check if the copy is of the floated window, we check the parent and
-  // bounds.
-  EXPECT_EQ(floated_window->layer()->parent(),
-            other_window_copy_layer->parent());
+  // To check if the copy is of the floated window, we check the bounds. The
+  // float container gets stacked under the desk containers during overview, so
+  // the copy should be on a different parent.
   EXPECT_EQ(floated_window->layer()->bounds(),
             other_window_copy_layer->bounds());
+  EXPECT_NE(floated_window->layer()->parent(),
+            other_window_copy_layer->parent());
 
   Drag(gfx::Point(0, 200), 1.f, 1.f);
   EndDrag(shelf_bounds.CenterPoint(), /*velocity_y=*/absl::nullopt);
diff --git a/ash/style/ash_color_mixer.cc b/ash/style/ash_color_mixer.cc
index c0822c1..17123b2 100644
--- a/ash/style/ash_color_mixer.cc
+++ b/ash/style/ash_color_mixer.cc
@@ -242,18 +242,14 @@
       cros_tokens::kCrosSysOnPrimaryContainer};
 
   mixer[cros_tokens::kBgColor] = {cros_tokens::kCrosSysAppBase};
-  mixer[cros_tokens::kBgColorElevation1] = {
-      cros_tokens::kCrosSysAppBaseElevated};
+  mixer[cros_tokens::kBgColorElevation1] = {cros_tokens::kCrosSysBaseElevated};
   mixer[cros_tokens::kBgColorElevation2Light] = {
-      cros_tokens::kCrosSysAppBaseElevatedLight};
+      cros_tokens::kCrosSysBaseElevatedLight};
   mixer[cros_tokens::kBgColorElevation2Dark] = {
-      cros_tokens::kCrosSysAppBaseElevatedDark};
-  mixer[cros_tokens::kBgColorElevation3] = {
-      cros_tokens::kCrosSysAppBaseElevated};
-  mixer[cros_tokens::kBgColorElevation4] = {
-      cros_tokens::kCrosSysAppBaseElevated};
-  mixer[cros_tokens::kBgColorElevation5] = {
-      cros_tokens::kCrosSysAppBaseElevated};
+      cros_tokens::kCrosSysBaseElevatedDark};
+  mixer[cros_tokens::kBgColorElevation3] = {cros_tokens::kCrosSysBaseElevated};
+  mixer[cros_tokens::kBgColorElevation4] = {cros_tokens::kCrosSysBaseElevated};
+  mixer[cros_tokens::kBgColorElevation5] = {cros_tokens::kCrosSysBaseElevated};
   mixer[cros_tokens::kBgColorDroppedElevation1] = {
       cros_tokens::kCrosSysAppBaseShaded};
   mixer[cros_tokens::kBgColorDroppedElevation2] = {
@@ -299,7 +295,7 @@
       cros_tokens::kCrosSysRipplePrimary};
   mixer[cros_tokens::kHighlightColor] = {cros_tokens::kCrosSysPrimary};
   mixer[cros_tokens::kTextfieldBackgroundColor] = {
-      cros_tokens::kCrosSysInputFieldLight};
+      cros_tokens::kCrosSysInputFieldOnShaded};
   mixer[cros_tokens::kTextfieldLabelColor] = {cros_tokens::kCrosSysOnSurface};
 
   mixer[cros_tokens::kSliderColorActive] = {cros_tokens::kCrosSysPrimary};
@@ -567,7 +563,7 @@
 
   mixer[ui::kColorAshSystemUIMenuBackground] = {
       chromeos::features::IsJellyEnabled()
-          ? static_cast<ui::ColorId>(cros_tokens::kCrosSysAppBaseElevated)
+          ? static_cast<ui::ColorId>(cros_tokens::kCrosSysBaseElevated)
           : kColorAshShieldAndBase80};
   mixer[ui::kColorAshSystemUIMenuIcon] = {kColorAshIconColorPrimary};
   mixer[ui::kColorAshSystemUIMenuItemBackgroundSelected] = {kColorAshInkDrop};
diff --git a/ash/style/dark_light_mode_controller_impl.cc b/ash/style/dark_light_mode_controller_impl.cc
index e105f8f..ecd65d0 100644
--- a/ash/style/dark_light_mode_controller_impl.cc
+++ b/ash/style/dark_light_mode_controller_impl.cc
@@ -52,10 +52,14 @@
   native_theme->NotifyOnNativeThemeUpdated();
 
   auto* native_theme_web = ui::NativeTheme::GetInstanceForWeb();
-  native_theme_web->set_use_dark_colors(is_dark_mode_enabled);
-  native_theme_web->set_preferred_color_scheme(
-      is_dark_mode_enabled ? ui::NativeTheme::PreferredColorScheme::kDark
-                           : ui::NativeTheme::PreferredColorScheme::kLight);
+  if (!native_theme_web->IsForcedDarkMode()) {
+    // If we're in forced dark mode, leave the value alone to allow the tests to
+    // work.
+    native_theme_web->set_use_dark_colors(is_dark_mode_enabled);
+    native_theme_web->set_preferred_color_scheme(
+        is_dark_mode_enabled ? ui::NativeTheme::PreferredColorScheme::kDark
+                             : ui::NativeTheme::PreferredColorScheme::kLight);
+  }
   native_theme_web->set_user_color(themed_color);
   native_theme_web->NotifyOnNativeThemeUpdated();
 }
@@ -152,9 +156,6 @@
 }
 
 bool DarkLightModeControllerImpl::IsDarkModeEnabled() const {
-  if (!features::IsDarkLightModeEnabled() && override_light_mode_as_default_)
-    return false;
-
   // Dark mode is off during OOBE when the OobeDialogState is still unknown.
   // When the SessionState is OOBE, the OobeDialogState is HIDDEN until the
   // first screen is shown. This fixes a bug that caused dark colors to be
@@ -166,51 +167,54 @@
     return false;
   }
 
-  if (features::IsDarkLightModeEnabled()) {
-    if (is_dark_mode_enabled_in_oobe_for_testing_.has_value())
-      return is_dark_mode_enabled_in_oobe_for_testing_.value();
+  if (is_dark_mode_enabled_in_oobe_for_testing_.has_value()) {
+    return is_dark_mode_enabled_in_oobe_for_testing_.value();
+  }
 
-    if (oobe_state_ != OobeDialogState::HIDDEN) {
-      if (active_user_pref_service_) {
-        const PrefService::Preference* pref =
-            active_user_pref_service_->FindPreference(
-                prefs::kDarkModeScheduleType);
-        // Managed users do not see the theme selection screen, so to avoid
-        // confusion they should always see light colors during OOBE
-        if (pref->IsManaged() || pref->IsRecommended())
-          return false;
-
-        if (!active_user_pref_service_->GetBoolean(prefs::kDarkModeEnabled))
-          return false;
+  if (oobe_state_ != OobeDialogState::HIDDEN) {
+    if (active_user_pref_service_) {
+      const PrefService::Preference* pref =
+          active_user_pref_service_->FindPreference(
+              prefs::kDarkModeScheduleType);
+      // Managed users do not see the theme selection screen, so to avoid
+      // confusion they should always see light colors during OOBE
+      if (pref->IsManaged() || pref->IsRecommended()) {
+        return false;
       }
-      return base::Contains(kStatesSupportingDarkTheme, oobe_state_);
-    }
 
-    // On the login screen use the preference of the focused pod's user if they
-    // had the preference stored in the known_user and the pod is focused.
-    if (!active_user_pref_service_ &&
-        is_dark_mode_enabled_for_focused_pod_.has_value()) {
-      return is_dark_mode_enabled_for_focused_pod_.value();
+      if (!active_user_pref_service_->GetBoolean(prefs::kDarkModeEnabled)) {
+        return false;
+      }
     }
+    return base::Contains(kStatesSupportingDarkTheme, oobe_state_);
+  }
+
+  // On the login screen use the preference of the focused pod's user if they
+  // had the preference stored in the known_user and the pod is focused.
+  if (!active_user_pref_service_ &&
+      is_dark_mode_enabled_for_focused_pod_.has_value()) {
+    return is_dark_mode_enabled_for_focused_pod_.value();
   }
 
   // Keep the color mode as DARK in login screen or when dark/light mode feature
   // is not enabled.
-  if (!active_user_pref_service_ || !features::IsDarkLightModeEnabled())
+  if (!active_user_pref_service_) {
     return true;
+  }
 
   return active_user_pref_service_->GetBoolean(prefs::kDarkModeEnabled);
 }
 
 void DarkLightModeControllerImpl::SetDarkModeEnabledForTest(bool enabled) {
-  DCHECK(features::IsDarkLightModeEnabled());
   if (oobe_state_ != OobeDialogState::HIDDEN) {
     auto closure = GetNotifyOnDarkModeChangeClosure();
     is_dark_mode_enabled_in_oobe_for_testing_ = enabled;
     return;
   }
-  if (IsDarkModeEnabled() != enabled)
+
+  if (IsDarkModeEnabled() != enabled) {
     ToggleColorMode();
+  }
 }
 
 void DarkLightModeControllerImpl::OnOobeDialogStateChanged(
@@ -232,17 +236,11 @@
 }
 
 void DarkLightModeControllerImpl::OnWallpaperColorsChanged() {
-  if (!features::IsDarkLightModeEnabled())
-    return;
-
   RefreshColorsOnColorMode(IsDarkModeEnabled());
 }
 
 void DarkLightModeControllerImpl::OnActiveUserPrefServiceChanged(
     PrefService* prefs) {
-  if (!features::IsDarkLightModeEnabled())
-    return;
-
   active_user_pref_service_ = prefs;
   pref_change_registrar_ = std::make_unique<PrefChangeRegistrar>();
   pref_change_registrar_->Init(prefs);
@@ -260,8 +258,6 @@
 
 void DarkLightModeControllerImpl::OnSessionStateChanged(
     session_manager::SessionState state) {
-  if (!features::IsDarkLightModeEnabled())
-    return;
   if (state != session_manager::SessionState::OOBE &&
       state != session_manager::SessionState::LOGIN_PRIMARY) {
     oobe_state_ = OobeDialogState::HIDDEN;
diff --git a/ash/style/dark_light_mode_controller_unittests.cc b/ash/style/dark_light_mode_controller_unittests.cc
index ff2d3c03..3c0c974 100644
--- a/ash/style/dark_light_mode_controller_unittests.cc
+++ b/ash/style/dark_light_mode_controller_unittests.cc
@@ -4,7 +4,6 @@
 
 #include "ash/style/dark_light_mode_controller_impl.h"
 
-#include "ash/constants/ash_features.h"
 #include "ash/login/login_screen_controller.h"
 #include "ash/login/ui/login_test_base.h"
 #include "ash/public/cpp/login_types.h"
@@ -24,8 +23,6 @@
   auto* client = GetSessionControllerClient();
   auto* dark_light_mode_controller = DarkLightModeControllerImpl::Get();
 
-  base::test::ScopedFeatureList enable_dark_light;
-  enable_dark_light.InitAndEnableFeature(chromeos::features::kDarkLightMode);
   client->SetSessionState(session_manager::SessionState::UNKNOWN);
   // When dark/light mode is enabled. Color mode in non-active user sessions
   // (e.g, login page) should be DARK.
@@ -46,18 +43,6 @@
 
   dispatcher->NotifyOobeDialogState(OobeDialogState::GAIA_SIGNIN);
   EXPECT_FALSE(dark_light_mode_controller->IsDarkModeEnabled());
-
-  // When dark/light mode is disabled. Color mode in non-active user sessions
-  // (e.g, login page) should still be DARK.
-  base::test::ScopedFeatureList disable_dark_light;
-  disable_dark_light.InitWithFeatures(
-      /*enabled_features=*/{}, /*disabled_features=*/{
-          chromeos::features::kDarkLightMode, features::kNotificationsRefresh});
-  client->SetSessionState(session_manager::SessionState::UNKNOWN);
-  EXPECT_TRUE(dark_light_mode_controller->IsDarkModeEnabled());
-  client->SetSessionState(session_manager::SessionState::OOBE);
-  dispatcher->NotifyOobeDialogState(OobeDialogState::USER_CREATION);
-  EXPECT_TRUE(dark_light_mode_controller->IsDarkModeEnabled());
 }
 
 }  // namespace ash
diff --git a/ash/system/input_device_settings/input_device_settings_metrics_manager.cc b/ash/system/input_device_settings/input_device_settings_metrics_manager.cc
index 53b406a..652ae4b8 100644
--- a/ash/system/input_device_settings/input_device_settings_metrics_manager.cc
+++ b/ash/system/input_device_settings/input_device_settings_metrics_manager.cc
@@ -5,6 +5,8 @@
 #include "ash/system/input_device_settings/input_device_settings_metrics_manager.h"
 
 #include "ash/public/mojom/input_device_settings.mojom-forward.h"
+#include "ash/session/session_controller_impl.h"
+#include "ash/shell.h"
 #include "base/metrics/histogram_functions.h"
 
 namespace ash {
@@ -37,7 +39,17 @@
 
 void InputDeviceSettingsMetricsManager::RecordKeyboardInitialMetrics(
     const mojom::Keyboard& keyboard) {
-  // TODO(yyhyyh@): Only record the metrics once for each keyboard.
+  // Only record the metrics once for each keyboard.
+  const auto account_id =
+      Shell::Get()->session_controller()->GetActiveAccountId();
+  auto iter = recorded_keyboards_.find(account_id);
+
+  if (iter != recorded_keyboards_.end() &&
+      base::Contains(iter->second, keyboard.id)) {
+    return;
+  }
+  recorded_keyboards_[account_id].insert(keyboard.id);
+
   const KeyboardType keyboard_type = GetKeyboardType(keyboard);
   switch (keyboard_type) {
     case KeyboardType::kExternal:
diff --git a/ash/system/input_device_settings/input_device_settings_metrics_manager.h b/ash/system/input_device_settings/input_device_settings_metrics_manager.h
index 1ff9e4d..e41df29 100644
--- a/ash/system/input_device_settings/input_device_settings_metrics_manager.h
+++ b/ash/system/input_device_settings/input_device_settings_metrics_manager.h
@@ -7,6 +7,9 @@
 
 #include "ash/ash_export.h"
 #include "ash/public/mojom/input_device_settings.mojom.h"
+#include "base/containers/flat_map.h"
+#include "base/containers/flat_set.h"
+#include "components/account_id/account_id.h"
 
 namespace ash {
 
@@ -20,6 +23,9 @@
   ~InputDeviceSettingsMetricsManager();
 
   void RecordKeyboardInitialMetrics(const mojom::Keyboard& keyboard);
+
+ private:
+  base::flat_map<AccountId, base::flat_set<uint32_t>> recorded_keyboards_;
 };
 
 }  // namespace ash
diff --git a/ash/system/input_device_settings/input_device_settings_metrics_manager_unittest.cc b/ash/system/input_device_settings/input_device_settings_metrics_manager_unittest.cc
index 2d52eae..a065d2c1 100644
--- a/ash/system/input_device_settings/input_device_settings_metrics_manager_unittest.cc
+++ b/ash/system/input_device_settings/input_device_settings_metrics_manager_unittest.cc
@@ -15,6 +15,9 @@
 constexpr int kExternalKeyboardId = 1;
 constexpr int kExternalChromeOSKeyboardId = 2;
 constexpr int kInternalKeyboardId = 3;
+
+constexpr char kUser1[] = "user1@gmail.com";
+constexpr char kUser2[] = "user2@gmail.com";
 }  // namespace
 
 class InputDeviceSettingsMetricsManagerTest : public AshTestBase {
@@ -107,4 +110,48 @@
       /*expected_count=*/1u);
 }
 
+TEST_F(InputDeviceSettingsMetricsManagerTest, RecordMetricOncePerKeyboard) {
+  mojom::Keyboard keyboard_external;
+  keyboard_external.id = kExternalKeyboardId;
+  keyboard_external.is_external = true;
+  keyboard_external.meta_key = mojom::MetaKey::kCommand;
+  keyboard_external.settings = mojom::KeyboardSettings::New();
+  auto& settings_external = *keyboard_external.settings;
+  settings_external.top_row_are_fkeys = true;
+
+  mojom::Keyboard keyboard_internal;
+  keyboard_internal.id = kInternalKeyboardId;
+  keyboard_internal.is_external = false;
+  keyboard_internal.settings = mojom::KeyboardSettings::New();
+  auto& settings_internal = *keyboard_internal.settings;
+  settings_internal.top_row_are_fkeys = true;
+
+  base::HistogramTester histogram_tester;
+  SimulateUserLogin(kUser1);
+  manager_.get()->RecordKeyboardInitialMetrics(keyboard_external);
+  histogram_tester.ExpectTotalCount(
+      "ChromeOS.Settings.Device.Keyboard.External.TopRowAreFKeys.Initial",
+      /*expected_count=*/1u);
+
+  manager_.get()->RecordKeyboardInitialMetrics(keyboard_internal);
+  histogram_tester.ExpectTotalCount(
+      "ChromeOS.Settings.Device.Keyboard.Internal.TopRowAreFKeys.Initial",
+      /*expected_count=*/1u);
+
+  // Call RecordKeyboardInitialMetrics with the same user and same keyboard,
+  // ExpectTotalCount for Internal metric won't increase.
+  manager_.get()->RecordKeyboardInitialMetrics(keyboard_internal);
+  histogram_tester.ExpectTotalCount(
+      "ChromeOS.Settings.Device.Keyboard.Internal.TopRowAreFKeys.Initial",
+      /*expected_count=*/1u);
+
+  // Call RecordKeyboardInitialMetrics with the different user but same
+  // keyboard, ExpectTotalCount for Internal metric will increase.
+  SimulateUserLogin(kUser2);
+  manager_.get()->RecordKeyboardInitialMetrics(keyboard_internal);
+  histogram_tester.ExpectTotalCount(
+      "ChromeOS.Settings.Device.Keyboard.Internal.TopRowAreFKeys.Initial",
+      /*expected_count=*/2u);
+}
+
 }  // namespace ash
\ No newline at end of file
diff --git a/ash/touch/touch_selection_magnifier_runner_ash.cc b/ash/touch/touch_selection_magnifier_runner_ash.cc
index c48244b3..1303fe0 100644
--- a/ash/touch/touch_selection_magnifier_runner_ash.cc
+++ b/ash/touch/touch_selection_magnifier_runner_ash.cc
@@ -4,28 +4,76 @@
 
 #include "ash/touch/touch_selection_magnifier_runner_ash.h"
 
-#include "ash/constants/ash_features.h"
 #include "ash/public/cpp/shell_window_ids.h"
+#include "ash/style/color_util.h"
+#include "third_party/skia/include/core/SkDrawLooper.h"
 #include "ui/aura/window.h"
+#include "ui/chromeos/styles/cros_tokens_color_mappings.h"
+#include "ui/color/color_provider_source_observer.h"
 #include "ui/compositor/layer.h"
+#include "ui/compositor/paint_recorder.h"
+#include "ui/compositor/scoped_layer_animation_settings.h"
 #include "ui/gfx/geometry/point_conversions.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/size.h"
+#include "ui/gfx/shadow_value.h"
+#include "ui/gfx/skia_paint_util.h"
 
 namespace ash {
 
 namespace {
 
-// Gets the bounds of the magnifier when showing the specified point of
-// interest. `point_of_interest` and returned bounds are in root window
-// coordinates.
-gfx::Rect GetBounds(const gfx::Point& point_of_interest) {
-  const gfx::Size size = TouchSelectionMagnifierRunnerAsh::kMagnifierLayerSize;
+constexpr int kMagnifierRadius = 20;
+
+constexpr int kMagnifierBorderThickness = 1;
+
+const gfx::ShadowValues kMagnifierShadowValues =
+    gfx::ShadowValue::MakeChromeOSSystemUIShadowValues(3);
+
+// The space outside the zoom layer needed for shadows.
+const gfx::Outsets kMagnifierShadowOutsets =
+    gfx::ShadowValue::GetMargin(kMagnifierShadowValues).ToOutsets();
+
+// Bounds of the zoom layer in coordinates of its parent. These zoom layer
+// bounds are fixed since we only update the bounds of the parent magnifier
+// layer when the magnifier moves.
+const gfx::Rect kZoomLayerBounds =
+    gfx::Rect(kMagnifierShadowOutsets.left(),
+              kMagnifierShadowOutsets.top(),
+              TouchSelectionMagnifierRunnerAsh::kMagnifierSize.width(),
+              TouchSelectionMagnifierRunnerAsh::kMagnifierSize.height());
+
+// Size of the border layer, which includes space for the zoom layer and
+// surrounding border and shadows.
+const gfx::Size kBorderLayerSize =
+    TouchSelectionMagnifierRunnerAsh::kMagnifierSize +
+    kMagnifierShadowOutsets.size();
+
+// Duration of the animation when updating magnifier bounds.
+constexpr base::TimeDelta kMagnifierTransitionDuration = base::Milliseconds(50);
+
+// Gets the bounds of the magnifier layer for showing the specified point of
+// interest. These bounds include the magnifier border and shadows.
+// `point_of_interest` and returned bounds are in coordinates of the magnifier's
+// parent container.
+gfx::Rect GetMagnifierLayerBounds(const gfx::Point& point_of_interest) {
+  const gfx::Size& size = TouchSelectionMagnifierRunnerAsh::kMagnifierSize;
   const gfx::Point origin(
       point_of_interest.x() - size.width() / 2,
       point_of_interest.y() - size.height() / 2 +
           TouchSelectionMagnifierRunnerAsh::kMagnifierVerticalOffset);
-  return gfx::Rect(origin, size);
+  gfx::Rect magnifier_layer_bounds(origin, size);
+  magnifier_layer_bounds.Outset(kMagnifierShadowOutsets);
+  return magnifier_layer_bounds;
+}
+
+// Gets the border color using `color_provider_source`. Defaults to black if
+// `color_provider_source` is nullptr.
+SkColor GetBorderColor(const ui::ColorProviderSource* color_provider_source) {
+  return color_provider_source
+             ? color_provider_source->GetColorProvider()->GetColor(
+                   cros_tokens::kCrosSysSeparator)
+             : SkColorSetARGB(51, 0, 0, 0);
 }
 
 // Returns the child container in `root` that should parent the magnifier layer.
@@ -35,6 +83,59 @@
 
 }  // namespace
 
+// Delegate for drawing the magnifier border and shadows onto the border layer.
+class TouchSelectionMagnifierRunnerAsh::BorderRenderer
+    : public ui::LayerDelegate {
+ public:
+  explicit BorderRenderer(SkColor border_color) : border_color_(border_color) {}
+
+  BorderRenderer(const BorderRenderer&) = delete;
+  BorderRenderer& operator=(const BorderRenderer&) = delete;
+  ~BorderRenderer() override = default;
+
+  void set_border_color(SkColor border_color) { border_color_ = border_color; }
+
+  // ui::LayerDelegate:
+  void OnPaintLayer(const ui::PaintContext& context) override {
+    ui::PaintRecorder recorder(context, kBorderLayerSize);
+
+    // Draw shadows onto the border layer. These shadows should surround the
+    // magnified area, so we draw them around the zoom layer bounds.
+    cc::PaintFlags shadow_flags;
+    shadow_flags.setAntiAlias(true);
+    shadow_flags.setColor(SK_ColorTRANSPARENT);
+    shadow_flags.setLooper(gfx::CreateShadowDrawLooper(kMagnifierShadowValues));
+    recorder.canvas()->DrawRoundRect(kZoomLayerBounds, kMagnifierRadius,
+                                     shadow_flags);
+
+    // Since the border layer is stacked above the zoom layer (to prevent the
+    // magnifier border and shadows from being magnified), we now need to clear
+    // the parts of the shadow covering the zoom layer.
+    cc::PaintFlags mask_flags;
+    mask_flags.setAntiAlias(true);
+    mask_flags.setBlendMode(SkBlendMode::kClear);
+    mask_flags.setStyle(cc::PaintFlags::kFill_Style);
+    recorder.canvas()->DrawRoundRect(kZoomLayerBounds, kMagnifierRadius,
+                                     mask_flags);
+
+    // Draw the magnifier border onto the border layer, using the zoom layer
+    // bounds so that the border surrounds the magnified area.
+    cc::PaintFlags border_flags;
+    border_flags.setAntiAlias(true);
+    border_flags.setStyle(cc::PaintFlags::kStroke_Style);
+    border_flags.setStrokeWidth(kMagnifierBorderThickness);
+    border_flags.setColor(border_color_);
+    recorder.canvas()->DrawRoundRect(kZoomLayerBounds, kMagnifierRadius,
+                                     border_flags);
+  }
+
+  void OnDeviceScaleFactorChanged(float old_device_scale_factor,
+                                  float new_device_scale_factor) override {}
+
+ private:
+  SkColor border_color_;
+};
+
 TouchSelectionMagnifierRunnerAsh::TouchSelectionMagnifierRunnerAsh() = default;
 
 TouchSelectionMagnifierRunnerAsh::~TouchSelectionMagnifierRunnerAsh() = default;
@@ -50,26 +151,47 @@
 
   aura::Window* root_window = current_context_->GetRootWindow();
   DCHECK(root_window);
-  gfx::PointF position_in_root(position);
-  aura::Window::ConvertPointToTarget(context, root_window, &position_in_root);
+  aura::Window* parent_container =
+      GetMagnifierParentContainerForRoot(root_window);
+  gfx::PointF position_in_parent(position);
+  aura::Window::ConvertPointToTarget(context, parent_container,
+                                     &position_in_parent);
 
   if (!magnifier_layer_) {
-    CreateMagnifierLayer(root_window, position_in_root);
+    CreateMagnifierLayer(parent_container, position_in_parent);
   } else {
+    ui::ScopedLayerAnimationSettings settings(magnifier_layer_->GetAnimator());
+    settings.SetTransitionDuration(kMagnifierTransitionDuration);
+    settings.SetTweenType(gfx::Tween::LINEAR);
+    settings.SetPreemptionStrategy(
+        ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
     magnifier_layer_->SetBounds(
-        GetBounds(gfx::ToRoundedPoint(position_in_root)));
+        GetMagnifierLayerBounds(gfx::ToRoundedPoint(position_in_parent)));
   }
 }
 
 void TouchSelectionMagnifierRunnerAsh::CloseMagnifier() {
   current_context_ = nullptr;
   magnifier_layer_ = nullptr;
+  zoom_layer_ = nullptr;
+  border_layer_ = nullptr;
+  border_renderer_ = nullptr;
+  Observe(nullptr);
 }
 
 bool TouchSelectionMagnifierRunnerAsh::IsRunning() const {
   return current_context_ != nullptr;
 }
 
+void TouchSelectionMagnifierRunnerAsh::OnColorProviderChanged() {
+  if (border_renderer_) {
+    DCHECK(border_layer_);
+    border_renderer_->set_border_color(
+        GetBorderColor(GetColorProviderSource()));
+    border_layer_->SchedulePaint(gfx::Rect(border_layer_->size()));
+  }
+}
+
 const aura::Window*
 TouchSelectionMagnifierRunnerAsh::GetCurrentContextForTesting() const {
   return current_context_;
@@ -80,21 +202,44 @@
   return magnifier_layer_.get();
 }
 
-void TouchSelectionMagnifierRunnerAsh::CreateMagnifierLayer(
-    aura::Window* root_window,
-    const gfx::PointF& position_in_root) {
-  aura::Window* parent_container =
-      GetMagnifierParentContainerForRoot(root_window);
+const ui::Layer* TouchSelectionMagnifierRunnerAsh::GetZoomLayerForTesting()
+    const {
+  return zoom_layer_.get();
+}
 
+void TouchSelectionMagnifierRunnerAsh::CreateMagnifierLayer(
+    aura::Window* parent_container,
+    const gfx::PointF& position_in_parent) {
+  Observe(ColorUtil::GetColorProviderSourceForWindow(parent_container));
   ui::Layer* parent_layer = parent_container->layer();
-  magnifier_layer_ = std::make_unique<ui::Layer>(ui::LAYER_SOLID_COLOR);
-  magnifier_layer_->SetBounds(GetBounds(gfx::ToRoundedPoint(position_in_root)));
-  magnifier_layer_->SetBackgroundZoom(kMagnifierScale, 0);
-  magnifier_layer_->SetBackgroundOffset(
-      gfx::Point(0, kMagnifierVerticalOffset));
+
+  // Create the magnifier layer, which will parent the zoom layer and border
+  // layer.
+  magnifier_layer_ = std::make_unique<ui::Layer>(ui::LAYER_NOT_DRAWN);
+  magnifier_layer_->SetBounds(
+      GetMagnifierLayerBounds(gfx::ToRoundedPoint(position_in_parent)));
   magnifier_layer_->SetFillsBoundsOpaquely(false);
-  magnifier_layer_->SetRoundedCornerRadius(kMagnifierRoundedCorners);
   parent_layer->Add(magnifier_layer_.get());
+
+  // Create the zoom layer, which will show the magnified area.
+  zoom_layer_ = std::make_unique<ui::Layer>(ui::LAYER_SOLID_COLOR);
+  zoom_layer_->SetBounds(kZoomLayerBounds);
+  zoom_layer_->SetBackgroundZoom(kMagnifierScale, 0);
+  zoom_layer_->SetBackgroundOffset(gfx::Point(0, kMagnifierVerticalOffset));
+  zoom_layer_->SetFillsBoundsOpaquely(false);
+  zoom_layer_->SetRoundedCornerRadius(gfx::RoundedCornersF{kMagnifierRadius});
+  magnifier_layer_->Add(zoom_layer_.get());
+
+  // Create the border layer. This is stacked above the zoom layer so that the
+  // magnifier border and shadows aren't shown in the magnified area drawn by
+  // the zoom layer.
+  border_layer_ = std::make_unique<ui::Layer>();
+  border_layer_->SetBounds(gfx::Rect(kBorderLayerSize));
+  border_renderer_ = std::make_unique<BorderRenderer>(
+      GetBorderColor(GetColorProviderSource()));
+  border_layer_->set_delegate(border_renderer_.get());
+  border_layer_->SetFillsBoundsOpaquely(false);
+  magnifier_layer_->Add(border_layer_.get());
 }
 
 }  // namespace ash
diff --git a/ash/touch/touch_selection_magnifier_runner_ash.h b/ash/touch/touch_selection_magnifier_runner_ash.h
index 7f5a5ab..a5e3bf0 100644
--- a/ash/touch/touch_selection_magnifier_runner_ash.h
+++ b/ash/touch/touch_selection_magnifier_runner_ash.h
@@ -7,7 +7,7 @@
 
 #include "ash/ash_export.h"
 #include "base/memory/raw_ptr.h"
-#include "ui/gfx/geometry/rounded_corners_f.h"
+#include "ui/color/color_provider_source_observer.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/touch_selection/touch_selection_magnifier_runner.h"
 
@@ -23,7 +23,8 @@
 
 // Ash implementation for TouchSelectionMagnifierRunner.
 class ASH_EXPORT TouchSelectionMagnifierRunnerAsh
-    : public ui::TouchSelectionMagnifierRunner {
+    : public ui::TouchSelectionMagnifierRunner,
+      public ui::ColorProviderSourceObserver {
  public:
   TouchSelectionMagnifierRunnerAsh();
 
@@ -34,15 +35,14 @@
 
   ~TouchSelectionMagnifierRunnerAsh() override;
 
-  static constexpr float kMagnifierScale = 2.0f;
+  static constexpr float kMagnifierScale = 1.25f;
 
-  static constexpr gfx::Size kMagnifierLayerSize{100, 48};
-
-  static constexpr gfx::RoundedCornersF kMagnifierRoundedCorners{20};
+  // Size of the magnifier zoom layer, which excludes border and shadows.
+  static constexpr gfx::Size kMagnifierSize{100, 40};
 
   // Offset to apply so that the magnifier is shown vertically above the point
   // of interest. The offset specifies vertical displacement from the center of
-  // the text selection caret to the center of the magnifier bounds.
+  // the text selection caret to the center of the magnifier zoom layer.
   static constexpr int kMagnifierVerticalOffset = -32;
 
   // ui::TouchSelectionMagnifierRunner:
@@ -51,20 +51,37 @@
   void CloseMagnifier() override;
   bool IsRunning() const override;
 
+  // ui::ColorProviderSourceObserver:
+  void OnColorProviderChanged() override;
+
   const aura::Window* GetCurrentContextForTesting() const;
 
   const ui::Layer* GetMagnifierLayerForTesting() const;
 
+  const ui::Layer* GetZoomLayerForTesting() const;
+
  private:
-  void CreateMagnifierLayer(aura::Window* root_window,
+  class BorderRenderer;
+
+  void CreateMagnifierLayer(aura::Window* parent_container,
                             const gfx::PointF& position_in_root);
 
   // Current context window in which the magnifier is being shown, or `nullptr`
   // if no magnifier is running.
   raw_ptr<aura::Window> current_context_ = nullptr;
 
-  // The magnifier layer, which draws the background with a zoom filter applied.
+  // The magnifier layer is the parent of the zoom layer and border layer. The
+  // layer bounds should be updated when selection updates occur.
   std::unique_ptr<ui::Layer> magnifier_layer_;
+
+  // Draws the background with a zoom filter applied.
+  std::unique_ptr<ui::Layer> zoom_layer_;
+
+  // Draws a border and shadow. `border_layer_` must be ordered after
+  // `border_renderer_` so that it is destroyed before `border_renderer_`.
+  // Otherwise `border_layer_` will have a pointer to a deleted delegate.
+  std::unique_ptr<BorderRenderer> border_renderer_;
+  std::unique_ptr<ui::Layer> border_layer_;
 };
 
 }  // namespace ash
diff --git a/ash/touch/touch_selection_magnifier_runner_ash_unittest.cc b/ash/touch/touch_selection_magnifier_runner_ash_unittest.cc
index 56f88f8..d789003 100644
--- a/ash/touch/touch_selection_magnifier_runner_ash_unittest.cc
+++ b/ash/touch/touch_selection_magnifier_runner_ash_unittest.cc
@@ -43,6 +43,34 @@
     NoSessionAshTestBase::SetUp();
   }
 
+  // Verifies that the magnifier has the correct bounds given the point of
+  // interest in context window coordinates.
+  // TODO(b/273613374): For now, we assume that the context window, root
+  // window, and magnifier parent container have the same bounds, but in
+  // practice this might not always be the case. Rewrite these tests once the
+  // bounds related parts of the magnifier have been cleaned up.
+  void VerifyMagnifierBounds(gfx::PointF point_of_interest) {
+    TouchSelectionMagnifierRunnerAsh* magnifier_runner = GetMagnifierRunner();
+    ASSERT_TRUE(magnifier_runner);
+
+    const ui::Layer* magnifier_layer =
+        magnifier_runner->GetMagnifierLayerForTesting();
+    const ui::Layer* zoom_layer = magnifier_runner->GetZoomLayerForTesting();
+    ASSERT_TRUE(magnifier_layer);
+    ASSERT_TRUE(zoom_layer);
+
+    gfx::Rect zoom_layer_bounds_in_context =
+        zoom_layer->bounds() + magnifier_layer->bounds().OffsetFromOrigin();
+    EXPECT_EQ(zoom_layer_bounds_in_context.size(),
+              TouchSelectionMagnifierRunnerAsh::kMagnifierSize);
+    EXPECT_EQ(
+        zoom_layer_bounds_in_context.CenterPoint(),
+        gfx::Point(
+            point_of_interest.x(),
+            point_of_interest.y() +
+                TouchSelectionMagnifierRunnerAsh::kMagnifierVerticalOffset));
+  }
+
  private:
   base::test::ScopedFeatureList scoped_feature_list_;
 };
@@ -106,79 +134,34 @@
   EXPECT_FALSE(magnifier_runner->GetCurrentContextForTesting());
 }
 
-// Tests that the magnifier layer is created and destroyed.
-TEST_F(TouchSelectionMagnifierRunnerAshTest, Layer) {
+// Tests that the magnifier and zoom layers are created and destroyed.
+TEST_F(TouchSelectionMagnifierRunnerAshTest, CreatesAndDestroysLayers) {
   TouchSelectionMagnifierRunnerAsh* magnifier_runner = GetMagnifierRunner();
   ASSERT_TRUE(magnifier_runner);
 
   magnifier_runner->ShowMagnifier(GetContext(), gfx::PointF(300, 200));
   ASSERT_TRUE(magnifier_runner->GetMagnifierLayerForTesting());
+  ASSERT_TRUE(magnifier_runner->GetZoomLayerForTesting());
 
   magnifier_runner->CloseMagnifier();
   RunPendingMessages();
   EXPECT_FALSE(magnifier_runner->GetMagnifierLayerForTesting());
+  EXPECT_FALSE(magnifier_runner->GetZoomLayerForTesting());
 }
 
-// Tests that the magnifier layer is positioned with the correct bounds.
-TEST_F(TouchSelectionMagnifierRunnerAshTest, LayerBounds) {
+// Tests that the magnifier bounds are set correctly.
+TEST_F(TouchSelectionMagnifierRunnerAshTest, CorrectBounds) {
   TouchSelectionMagnifierRunnerAsh* magnifier_runner = GetMagnifierRunner();
   ASSERT_TRUE(magnifier_runner);
 
   gfx::PointF position(300, 200);
   magnifier_runner->ShowMagnifier(GetContext(), position);
-  const ui::Layer* magnifier_layer =
-      magnifier_runner->GetMagnifierLayerForTesting();
-  ASSERT_TRUE(magnifier_layer);
-
-  gfx::Rect bounds = magnifier_layer->bounds();
-  EXPECT_EQ(bounds.size(),
-            TouchSelectionMagnifierRunnerAsh::kMagnifierLayerSize);
-  EXPECT_EQ(
-      bounds.CenterPoint(),
-      gfx::Point(
-          position.x(),
-          position.y() +
-              TouchSelectionMagnifierRunnerAsh::kMagnifierVerticalOffset));
-
-  magnifier_runner->CloseMagnifier();
-  RunPendingMessages();
-}
-
-// Tests that the magnifier layer bounds update correctly.
-TEST_F(TouchSelectionMagnifierRunnerAshTest, LayerUpdatesBounds) {
-  TouchSelectionMagnifierRunnerAsh* magnifier_runner = GetMagnifierRunner();
-  ASSERT_TRUE(magnifier_runner);
-
-  gfx::PointF position(300, 200);
-  magnifier_runner->ShowMagnifier(GetContext(), position);
-  const ui::Layer* magnifier_layer =
-      magnifier_runner->GetMagnifierLayerForTesting();
-  ASSERT_TRUE(magnifier_layer);
-
-  gfx::Rect bounds = magnifier_layer->bounds();
-  EXPECT_EQ(bounds.size(),
-            TouchSelectionMagnifierRunnerAsh::kMagnifierLayerSize);
-  EXPECT_EQ(
-      bounds.CenterPoint(),
-      gfx::Point(
-          position.x(),
-          position.y() +
-              TouchSelectionMagnifierRunnerAsh::kMagnifierVerticalOffset));
+  VerifyMagnifierBounds(position);
 
   // Move the magnifier.
   position = gfx::PointF(400, 150);
   magnifier_runner->ShowMagnifier(GetContext(), position);
-  EXPECT_EQ(magnifier_layer, magnifier_runner->GetMagnifierLayerForTesting());
-
-  bounds = magnifier_layer->bounds();
-  EXPECT_EQ(bounds.size(),
-            TouchSelectionMagnifierRunnerAsh::kMagnifierLayerSize);
-  EXPECT_EQ(
-      bounds.CenterPoint(),
-      gfx::Point(
-          position.x(),
-          position.y() +
-              TouchSelectionMagnifierRunnerAsh::kMagnifierVerticalOffset));
+  VerifyMagnifierBounds(position);
 
   magnifier_runner->CloseMagnifier();
   RunPendingMessages();
diff --git a/ash/webui/personalization_app/.gitignore b/ash/webui/personalization_app/.gitignore
new file mode 100644
index 0000000..e6d297d
--- /dev/null
+++ b/ash/webui/personalization_app/.gitignore
@@ -0,0 +1,2 @@
+# Do not check in until they are ready to be made public.
+resources/time_of_day/*
diff --git a/ash/wm/float/float_controller_unittest.cc b/ash/wm/float/float_controller_unittest.cc
index 1ae16e60..234ce02 100644
--- a/ash/wm/float/float_controller_unittest.cc
+++ b/ash/wm/float/float_controller_unittest.cc
@@ -1429,6 +1429,28 @@
   EXPECT_TRUE(window->IsVisible());
 }
 
+// Tests that the floated window is visible when it is untucked.
+TEST_F(TabletWindowFloatTest, UntuckedWindowVisibility) {
+  Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
+  std::unique_ptr<aura::Window> window = CreateFloatedWindow();
+
+  // Long duration for tuck animation, to allow it to be interrupted.
+  ui::ScopedAnimationDurationScaleMode test_duration(
+      ui::ScopedAnimationDurationScaleMode::SLOW_DURATION);
+
+  // Fling to tuck the window. Press the untuck handle before the window
+  // finishes the tuck animation. Test that the window is visible.
+  FlingWindow(window.get(), /*left=*/false, /*up=*/false);
+  auto* float_controller = Shell::Get()->float_controller();
+  views::Widget* tuck_handle_widget =
+      float_controller->GetTuckHandleWidget(window.get());
+  ASSERT_TRUE(tuck_handle_widget);
+  GetEventGenerator()->GestureTapAt(
+      tuck_handle_widget->GetWindowBoundsInScreen().CenterPoint());
+  ASSERT_TRUE(window->IsVisible());
+  EXPECT_FALSE(float_controller->IsFloatedWindowTuckedForTablet(window.get()));
+}
+
 // Tests that the expected window gets activation after tucking a floated
 // window, and that on untucking the floated window, it gains activation.
 TEST_F(TabletWindowFloatTest, WindowActivationAfterTuckingUntucking) {
diff --git a/ash/wm/float/scoped_window_tucker.cc b/ash/wm/float/scoped_window_tucker.cc
index 7ed5d706..f8537f091 100644
--- a/ash/wm/float/scoped_window_tucker.cc
+++ b/ash/wm/float/scoped_window_tucker.cc
@@ -247,8 +247,6 @@
       left_ ? -kTuckOffscreenPaddingDp : kTuckOffscreenPaddingDp, 0);
 
   views::AnimationBuilder()
-      .OnAborted(base::BindOnce(&ScopedWindowTucker::OnAnimateTuckEnded,
-                                weak_factory_.GetWeakPtr()))
       .OnEnded(base::BindOnce(&ScopedWindowTucker::OnAnimateTuckEnded,
                               weak_factory_.GetWeakPtr()))
       .SetPreemptionStrategy(
diff --git a/ash/wm/snap_group/snap_group.cc b/ash/wm/snap_group/snap_group.cc
index 70ee667..e48c60c2 100644
--- a/ash/wm/snap_group/snap_group.cc
+++ b/ash/wm/snap_group/snap_group.cc
@@ -21,6 +21,19 @@
   return snap_group_controller &&
          snap_group_controller->IsArm1AutomaticallyLockEnabled();
 }
+
+bool IsStackedBelow(aura::Window* win1, aura::Window* win2) {
+  DCHECK_NE(win1, win2);
+  DCHECK_EQ(win1->parent(), win2->parent());
+
+  const auto& children = win1->parent()->children();
+  auto win1_iter = base::ranges::find(children, win1);
+  auto win2_iter = base::ranges::find(children, win2);
+  DCHECK(win1_iter != children.end());
+  DCHECK(win2_iter != children.end());
+  return win1_iter < win2_iter;
+}
+
 }  // namespace
 
 SnapGroup::SnapGroup(aura::Window* window1, aura::Window* window2) {
@@ -31,6 +44,32 @@
   StopObservingWindows();
 }
 
+void SnapGroup::OnWindowStackingChanged(aura::Window* window) {
+  // Update the stacking order of the other window in the snap group so that the
+  // two windows are always placed on top, both of which will be stacked below
+  // the `split_view_divider` if applicable afterwards.
+  aura::Window* top_window =
+      IsStackedBelow(window1_, window2_) ? window2_ : window1_;
+  aura::Window* target_window = top_window == window1_ ? window2_ : window1_;
+  auto* parent_container = target_window->parent();
+  parent_container->StackChildBelow(target_window, top_window);
+
+  if (ShouldConsiderDivider()) {
+    // Update the stacking order of the `split_view_divider` to be on top of the
+    // `top_window` which makes the overall stacking order become
+    // `divider_widget->GetNativeWindow()` --> `top_window` --> `target_window`.
+    aura::Window* root_window = window1_->GetRootWindow();
+    auto* split_view_controller = SplitViewController::Get(root_window);
+    DCHECK(split_view_controller);
+    auto* split_view_divider = split_view_controller->split_view_divider();
+    DCHECK(split_view_divider);
+    auto* divider_widget = split_view_divider->divider_widget();
+    DCHECK(divider_widget);
+    parent_container->StackChildAbove(divider_widget->GetNativeWindow(),
+                                      top_window);
+  }
+}
+
 void SnapGroup::OnWindowDestroying(aura::Window* window) {
   if (window != window1_ && window != window2_) {
     return;
@@ -40,37 +79,6 @@
   Shell::Get()->snap_group_controller()->RemoveSnapGroup(this);
 }
 
-void SnapGroup::OnWindowActivated(ActivationReason reason,
-                                  aura::Window* gained_active,
-                                  aura::Window* lost_active) {
-  if (gained_active != window1_ && gained_active != window2_) {
-    return;
-  }
-
-  // Update the stacking order of the other window in the snap group so that the
-  // two windows are always placed on top, both of which will be stacked below
-  // the `split_view_divider` if applicable afterwards.
-  aura::Window* target_window = gained_active == window1_ ? window2_ : window1_;
-  auto* parent_container = target_window->parent();
-  parent_container->StackChildBelow(target_window, gained_active);
-
-  // Update the stacking order of the `split_view_divider` to be on top of the
-  // `gained_active` window which makes the overall stacking order become
-  // `divider_widget->GetNativeWindow()` --> `gained_active` -->
-  // `target_window`.
-  if (ShouldConsiderDivider()) {
-    aura::Window* root_window = window1_->GetRootWindow();
-    auto* split_view_controller = SplitViewController::Get(root_window);
-    DCHECK(split_view_controller);
-    auto* split_view_divider = split_view_controller->split_view_divider();
-    DCHECK(split_view_divider);
-    auto* divider_widget = split_view_divider->divider_widget();
-    DCHECK(divider_widget);
-    parent_container->StackChildAbove(divider_widget->GetNativeWindow(),
-                                      gained_active);
-  }
-}
-
 void SnapGroup::StartObservingWindows(aura::Window* window1,
                                       aura::Window* window2) {
   DCHECK(window1);
@@ -79,8 +87,6 @@
   window1_->AddObserver(this);
   window2_ = window2;
   window2_->AddObserver(this);
-
-  Shell::Get()->activation_client()->AddObserver(this);
 }
 
 void SnapGroup::StopObservingWindows() {
@@ -93,8 +99,6 @@
     window2_->RemoveObserver(this);
     window2_ = nullptr;
   }
-
-  Shell::Get()->activation_client()->RemoveObserver(this);
 }
 
 }  // namespace ash
\ No newline at end of file
diff --git a/ash/wm/snap_group/snap_group.h b/ash/wm/snap_group/snap_group.h
index 39fc19ff..bd4ae78 100644
--- a/ash/wm/snap_group/snap_group.h
+++ b/ash/wm/snap_group/snap_group.h
@@ -6,7 +6,6 @@
 #define ASH_WM_SNAP_GROUP_SNAP_GROUP_H_
 
 #include "ui/aura/window_observer.h"
-#include "ui/wm/public/activation_change_observer.h"
 
 namespace aura {
 class Window;
@@ -16,7 +15,7 @@
 
 // Observes changes in the windows of the SnapGroup and manages the windows
 // accordingly.
-class SnapGroup : public aura::WindowObserver, wm::ActivationChangeObserver {
+class SnapGroup : public aura::WindowObserver {
  public:
   SnapGroup(aura::Window* window1, aura::Window* window2);
   SnapGroup(const SnapGroup&) = delete;
@@ -24,15 +23,10 @@
   ~SnapGroup() override;
 
   // aura::WindowObserver:
-  // TODO: Implement `OnWindowParentChanged` and maybe `OnWindowStackingChanged`
-  // in future.
+  // TODO: Implement `OnWindowParentChanged`.
+  void OnWindowStackingChanged(aura::Window* window) override;
   void OnWindowDestroying(aura::Window* window) override;
 
-  // wm::ActivationChangeObserver:
-  void OnWindowActivated(ActivationReason reason,
-                         aura::Window* gained_active,
-                         aura::Window* lost_active) override;
-
   aura::Window* window1() const { return window1_; }
   aura::Window* window2() const { return window2_; }
 
diff --git a/ash/wm/snap_group/snap_group_unittest.cc b/ash/wm/snap_group/snap_group_unittest.cc
index 26e17d3f..1b77543 100644
--- a/ash/wm/snap_group/snap_group_unittest.cc
+++ b/ash/wm/snap_group/snap_group_unittest.cc
@@ -34,6 +34,7 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/gfx/geometry/point.h"
 #include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/vector2d.h"
 #include "ui/gfx/image/image_skia.h"
 #include "ui/gfx/image/image_unittest_util.h"
 #include "ui/gfx/paint_vector_icon.h"
@@ -282,42 +283,18 @@
   EXPECT_FALSE(split_view_controller()->InSplitViewMode());
 }
 
-// Tests that after snapping two windows, resize one window will end the
-// split view mode.
-TEST_F(SnapGroupEntryPointArm1Test, ResizeOneWindowWillExitSnapGroup) {
-  std::unique_ptr<aura::Window> w1(CreateTestWindow());
-  std::unique_ptr<aura::Window> w2(CreateTestWindow());
-  SnapTwoTestWindowsInArm1(w1.get(), w2.get());
-
-  auto* event_generator = GetEventGenerator();
-  wm::ActivateWindow(w1.get());
-
-  gfx::Point hover_location = w1->GetBoundsInScreen().right_center();
-  hover_location.Offset(-1, 0);
-  const int distance_delta = work_area_bounds().width() / 4;
-  event_generator->MoveMouseTo(hover_location);
-  event_generator->PressLeftButton();
-  event_generator->MoveMouseTo(hover_location.x() + distance_delta,
-                               hover_location.y());
-  event_generator->ReleaseLeftButton();
-  EXPECT_FALSE(split_view_controller()->InSplitViewMode());
-  EXPECT_FALSE(split_view_divider());
-}
-
-// Tests the basic functionalities when dragging the windows in a snap group
-// with the split view divider.
-TEST_F(SnapGroupEntryPointArm1Test,
-       ResizeWithSplitViewDividerBasicFunctionalities) {
+// Tests the snap ratio is updated correctly when resizing the windows in a snap
+// group with the split view divider.
+TEST_F(SnapGroupEntryPointArm1Test, SnapRatioTest) {
   std::unique_ptr<aura::Window> w1(CreateTestWindow());
   std::unique_ptr<aura::Window> w2(CreateTestWindow());
   SnapTwoTestWindowsInArm1(w1.get(), w2.get());
 
   const gfx::Point hover_location =
       split_view_divider_bounds_in_screen().CenterPoint();
-  const int distance_delta = -work_area_bounds().width() / 6;
   split_view_controller()->StartResizeWithDivider(hover_location);
   const auto end_point =
-      gfx::Point(hover_location.x() + distance_delta, hover_location.y());
+      hover_location + gfx::Vector2d(-work_area_bounds().width() / 6, 0);
   split_view_controller()->ResizeWithDivider(end_point);
   split_view_controller()->EndResizeWithDivider(end_point);
   EXPECT_TRUE(split_view_controller()->InSplitViewMode());
@@ -341,9 +318,8 @@
     const gfx::Point hover_location =
         split_view_divider_bounds_in_screen().CenterPoint();
     split_view_controller()->StartResizeWithDivider(hover_location);
-    const auto end_location =
-        gfx::Point(hover_location.x() + distance_delta, hover_location.y());
-    split_view_controller()->ResizeWithDivider(end_location);
+    split_view_controller()->ResizeWithDivider(
+        hover_location + gfx::Vector2d(distance_delta, 0));
     EXPECT_TRUE(split_view_controller()->InSplitViewMode());
 
     EXPECT_EQ(w1_cached_bounds.width() + distance_delta,
@@ -483,6 +459,29 @@
   }
 }
 
+// Tests that the hit area of the split view divider can be outside of its
+// bounds with the extra insets with a value of `kSplitViewDividerExtraInset`.
+TEST_F(SnapGroupEntryPointArm1Test, SplitViewDividerEnlargedHitArea) {
+  std::unique_ptr<aura::Window> w1(CreateTestWindow());
+  std::unique_ptr<aura::Window> w2(CreateTestWindow());
+  SnapTwoTestWindowsInArm1(w1.get(), w2.get(), /*horizontal=*/true);
+
+  const gfx::Point cached_divider_center_point =
+      split_view_divider_bounds_in_screen().CenterPoint();
+  auto* event_generator = GetEventGenerator();
+  gfx::Point hover_location =
+      cached_divider_center_point -
+      gfx::Vector2d(kSplitViewDividerExtraInset.width(), 0);
+  event_generator->MoveMouseTo(hover_location);
+  event_generator->PressLeftButton();
+  const auto move_vector = -gfx::Vector2d(20, 0);
+  event_generator->MoveMouseTo(hover_location + move_vector);
+  event_generator->ReleaseLeftButton();
+  EXPECT_TRUE(split_view_controller()->InSplitViewMode());
+  EXPECT_EQ(split_view_divider_bounds_in_screen().CenterPoint(),
+            cached_divider_center_point + move_vector);
+}
+
 // A test fixture that tests the user-initiated snap group entry point. This
 // entry point is guarded by the feature flag `kSnapGroup` and will only be
 // enabled when the feature param `kAutomaticallyLockGroup` is false.
diff --git a/ash/wm/splitview/split_view_constants.h b/ash/wm/splitview/split_view_constants.h
index ea25e20..4206aa9 100644
--- a/ash/wm/splitview/split_view_constants.h
+++ b/ash/wm/splitview/split_view_constants.h
@@ -8,6 +8,7 @@
 #include "ash/ash_export.h"
 #include "base/time/time.h"
 #include "chromeos/ui/wm/constants.h"
+#include "ui/gfx/geometry/insets.h"
 
 namespace ash {
 
@@ -87,6 +88,10 @@
 constexpr int kMinCaretKeyboardDist = 16;
 constexpr float kMinDividerPositionRatio = 0.15f;
 
+// Extra insets used to increase the hit bounds of the split view divider to
+// make it easier to handle located event.
+constexpr gfx::Insets kSplitViewDividerExtraInset = gfx::Insets::VH(0, -2);
+
 }  // namespace ash
 
 #endif  // ASH_WM_SPLITVIEW_SPLIT_VIEW_CONSTANTS_H_
diff --git a/ash/wm/splitview/split_view_divider.cc b/ash/wm/splitview/split_view_divider.cc
index 23ce672..ddd974d 100644
--- a/ash/wm/splitview/split_view_divider.cc
+++ b/ash/wm/splitview/split_view_divider.cc
@@ -64,7 +64,7 @@
 }  // namespace
 
 // -----------------------------------------------------------------------------
-// DividerView:
+// AlwaysOnTopWindowTargeter:
 
 // The window targeter that is installed on the always on top container window
 // when the split view mode is active.
@@ -313,8 +313,9 @@
   for (auto snap_pos : {SplitViewController::SnapPosition::kPrimary,
                         SplitViewController::SnapPosition::kSecondary}) {
     auto* window = controller_->GetSnappedWindow(snap_pos);
-    if (window)
+    if (window) {
       AddObservedWindow(window);
+    }
   }
 }
 
@@ -554,7 +555,16 @@
   divider_view_ = divider_widget_->SetContentsView(
       std::make_unique<DividerView>(controller, this));
   divider_widget_->SetBounds(GetDividerBoundsInScreen(/*is_dragging=*/false));
-  divider_widget_->GetNativeWindow()->SetProperty(kLockedToRootKey, true);
+  auto* divider_widget_native_window = divider_widget_->GetNativeWindow();
+  divider_widget_native_window->SetProperty(kLockedToRootKey, true);
+
+  // Use a window targeter and enlarge the hit region to allow located events
+  // that are slightly outside the divider widget bounds be consumed by
+  // `divider_widget_`.
+  auto window_targeter = std::make_unique<aura::WindowTargeter>();
+  window_targeter->SetInsets(kSplitViewDividerExtraInset);
+  divider_widget_native_window->SetEventTargeter(std::move(window_targeter));
+
   divider_widget_->Show();
 }
 
diff --git a/ash/wm/splitview/split_view_divider.h b/ash/wm/splitview/split_view_divider.h
index 54b9209..3d4730d 100644
--- a/ash/wm/splitview/split_view_divider.h
+++ b/ash/wm/splitview/split_view_divider.h
@@ -52,7 +52,7 @@
       int divider_position,
       bool is_dragging);
 
-  views::Widget* divider_widget() { return divider_widget_; }
+  views::Widget* divider_widget() const { return divider_widget_; }
 
   // Do the divider spawning animation that adds a finishing touch to the
   // snapping animation of a window.
diff --git a/base/allocator/partition_allocator/address_pool_manager.h b/base/allocator/partition_allocator/address_pool_manager.h
index 30bb9341..a2366cb7 100644
--- a/base/allocator/partition_allocator/address_pool_manager.h
+++ b/base/allocator/partition_allocator/address_pool_manager.h
@@ -8,7 +8,6 @@
 #include <bitset>
 #include <limits>
 
-#include "base/allocator/partition_allocator/address_pool_manager_bitmap.h"
 #include "base/allocator/partition_allocator/address_pool_manager_types.h"
 #include "base/allocator/partition_allocator/partition_address_space.h"
 #include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h"
@@ -21,6 +20,10 @@
 #include "base/allocator/partition_allocator/partition_lock.h"
 #include "build/build_config.h"
 
+#if !BUILDFLAG(HAS_64_BIT_POINTERS)
+#include "base/allocator/partition_allocator/address_pool_manager_bitmap.h"
+#endif
+
 namespace partition_alloc {
 
 class AddressSpaceStatsDumper;
diff --git a/base/allocator/partition_allocator/partition_root.cc b/base/allocator/partition_allocator/partition_root.cc
index 93a9b51e..ff2d108 100644
--- a/base/allocator/partition_allocator/partition_root.cc
+++ b/base/allocator/partition_allocator/partition_root.cc
@@ -6,7 +6,6 @@
 
 #include <cstdint>
 
-#include "base/allocator/partition_allocator/address_pool_manager_bitmap.h"
 #include "base/allocator/partition_allocator/freeslot_bitmap.h"
 #include "base/allocator/partition_allocator/oom.h"
 #include "base/allocator/partition_allocator/page_allocator.h"
@@ -38,6 +37,10 @@
 #include "base/allocator/partition_allocator/starscan/pcscan.h"
 #endif
 
+#if !BUILDFLAG(HAS_64_BIT_POINTERS)
+#include "base/allocator/partition_allocator/address_pool_manager_bitmap.h"
+#endif
+
 #if BUILDFLAG(IS_WIN)
 #include <windows.h>
 #include "wow64apiset.h"
diff --git a/base/allocator/partition_allocator/pointers/raw_ptr_backup_ref_impl.h b/base/allocator/partition_allocator/pointers/raw_ptr_backup_ref_impl.h
index ac7edc6..bb4ce014 100644
--- a/base/allocator/partition_allocator/pointers/raw_ptr_backup_ref_impl.h
+++ b/base/allocator/partition_allocator/pointers/raw_ptr_backup_ref_impl.h
@@ -9,7 +9,6 @@
 
 #include <type_traits>
 
-#include "base/allocator/partition_allocator/address_pool_manager_bitmap.h"
 #include "base/allocator/partition_allocator/partition_address_space.h"
 #include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h"
 #include "base/allocator/partition_allocator/partition_alloc_base/component_export.h"
@@ -20,6 +19,10 @@
 #include "base/allocator/partition_allocator/partition_alloc_forward.h"
 #include "build/build_config.h"
 
+#if !BUILDFLAG(HAS_64_BIT_POINTERS)
+#include "base/allocator/partition_allocator/address_pool_manager_bitmap.h"
+#endif
+
 #if !BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT)
 #error "Included under wrong build option"
 #endif
diff --git a/base/allocator/partition_allocator/starscan/pcscan_internal.cc b/base/allocator/partition_allocator/starscan/pcscan_internal.cc
index 1e1d88d9..81df4405 100644
--- a/base/allocator/partition_allocator/starscan/pcscan_internal.cc
+++ b/base/allocator/partition_allocator/starscan/pcscan_internal.cc
@@ -18,7 +18,6 @@
 #include <vector>
 
 #include "base/allocator/partition_allocator/address_pool_manager.h"
-#include "base/allocator/partition_allocator/address_pool_manager_bitmap.h"
 #include "base/allocator/partition_allocator/allocation_guard.h"
 #include "base/allocator/partition_allocator/page_allocator.h"
 #include "base/allocator/partition_allocator/page_allocator_constants.h"
@@ -52,6 +51,10 @@
 #include "base/allocator/partition_allocator/thread_cache.h"
 #include "build/build_config.h"
 
+#if !BUILDFLAG(HAS_64_BIT_POINTERS)
+#include "base/allocator/partition_allocator/address_pool_manager_bitmap.h"
+#endif
+
 #if PA_CONFIG(STARSCAN_NOINLINE_SCAN_FUNCTIONS)
 #define PA_SCAN_INLINE PA_NOINLINE
 #else
diff --git a/base/android/javatests/OWNERS b/base/android/javatests/OWNERS
new file mode 100644
index 0000000..7d712d23
--- /dev/null
+++ b/base/android/javatests/OWNERS
@@ -0,0 +1 @@
+file://base/test/android/OWNERS
diff --git a/base/guid.cc b/base/guid.cc
index 548a40d4..a65b3ce5e 100644
--- a/base/guid.cc
+++ b/base/guid.cc
@@ -67,22 +67,10 @@
   return !GetCanonicalGUIDInternal(input, /*strict=*/false).empty();
 }
 
-bool IsValidGUID(StringPiece16 input) {
-  return !GetCanonicalGUIDInternal(input, /*strict=*/false).empty();
-}
-
 bool IsValidGUIDOutputString(StringPiece input) {
   return !GetCanonicalGUIDInternal(input, /*strict=*/true).empty();
 }
 
-std::string RandomDataToGUIDString(const uint64_t bytes[2]) {
-  return StringPrintf(
-      "%08x-%04x-%04x-%04x-%012llx", static_cast<uint32_t>(bytes[0] >> 32),
-      static_cast<uint32_t>((bytes[0] >> 16) & 0x0000ffff),
-      static_cast<uint32_t>(bytes[0] & 0x0000ffff),
-      static_cast<uint32_t>(bytes[1] >> 48), bytes[1] & 0x0000ffff'ffffffffULL);
-}
-
 // static
 GUID GUID::GenerateRandomV4() {
   uint8_t sixteen_bytes[kGuidV4InputLength];
@@ -125,7 +113,13 @@
   sixteen_bytes[1] |= 0x80000000'00000000ULL;
 
   GUID guid;
-  guid.lowercase_ = RandomDataToGUIDString(sixteen_bytes);
+  guid.lowercase_ =
+      StringPrintf("%08x-%04x-%04x-%04x-%012llx",
+                   static_cast<uint32_t>(sixteen_bytes[0] >> 32),
+                   static_cast<uint32_t>((sixteen_bytes[0] >> 16) & 0x0000ffff),
+                   static_cast<uint32_t>(sixteen_bytes[0] & 0x0000ffff),
+                   static_cast<uint32_t>(sixteen_bytes[1] >> 48),
+                   sixteen_bytes[1] & 0x0000ffff'ffffffffULL);
   return guid;
 }
 
diff --git a/base/guid.h b/base/guid.h
index 0e0b367..061810d 100644
--- a/base/guid.h
+++ b/base/guid.h
@@ -23,19 +23,17 @@
 
 namespace base {
 
-// DEPRECATED, use GUID::GenerateRandomV4() instead.
+// DEPRECATED(crbug.com/1195446): Use GUID::GenerateRandomV4() instead.
 BASE_EXPORT std::string GenerateGUID();
 
-// DEPRECATED, use GUID::ParseCaseInsensitive() and GUID::is_valid() instead.
+// DEPRECATED(crbug.com/1195446): Use GUID::ParseCaseInsensitive() and
+// GUID::is_valid() instead.
 BASE_EXPORT bool IsValidGUID(StringPiece input);
-BASE_EXPORT bool IsValidGUID(StringPiece16 input);
 
-// DEPRECATED, use GUID::ParseLowercase() and GUID::is_valid() instead.
+// DEPRECATED(crbug.com/1195446): Use GUID::ParseLowercase() and
+// GUID::is_valid() instead.
 BASE_EXPORT bool IsValidGUIDOutputString(StringPiece input);
 
-// For unit testing purposes only.  Do not use outside of tests.
-BASE_EXPORT std::string RandomDataToGUIDString(const uint64_t bytes[2]);
-
 class BASE_EXPORT GUID {
  public:
   // Length in bytes of the input required to format the input as a GUID in the
diff --git a/base/guid_unittest.cc b/base/guid_unittest.cc
index bf0ff60..e83cf9a3 100644
--- a/base/guid_unittest.cc
+++ b/base/guid_unittest.cc
@@ -16,19 +16,6 @@
 
 namespace base {
 
-TEST(GUIDTest, GUIDGeneratesAllZeroes) {
-  static constexpr uint64_t kBytes[] = {0, 0};
-  const std::string clientid = RandomDataToGUIDString(kBytes);
-  EXPECT_EQ("00000000-0000-0000-0000-000000000000", clientid);
-}
-
-TEST(GUIDTest, GUIDGeneratesCorrectly) {
-  static constexpr uint64_t kBytes[] = {0x0123456789ABCDEFULL,
-                                        0xFEDCBA9876543210ULL};
-  const std::string clientid = RandomDataToGUIDString(kBytes);
-  EXPECT_EQ("01234567-89ab-cdef-fedc-ba9876543210", clientid);
-}
-
 TEST(GUIDTest, DeprecatedGUIDCorrectlyFormatted) {
   constexpr int kIterations = 10;
   for (int i = 0; i < kIterations; ++i) {
@@ -128,20 +115,17 @@
   }
 }
 
-TEST(GUIDTest, Equality) {
-  static constexpr uint64_t kBytes[] = {0xDEADBEEFDEADBEEFULL,
-                                        0xDEADBEEFDEADBEEFULL};
-  const std::string clientid = RandomDataToGUIDString(kBytes);
+TEST(GUIDTest, EqualityAndRoundTrip) {
+  static constexpr char kCanonicalStr[] =
+      "deadbeef-dead-4eef-bead-beefdeadbeef";
 
-  static constexpr char kExpectedCanonicalStr[] =
-      "deadbeef-dead-beef-dead-beefdeadbeef";
-  ASSERT_EQ(kExpectedCanonicalStr, clientid);
+  const GUID from_lower =
+      GUID::ParseCaseInsensitive(ToLowerASCII(kCanonicalStr));
+  EXPECT_EQ(kCanonicalStr, from_lower.AsLowercaseString());
 
-  const GUID from_lower = GUID::ParseCaseInsensitive(ToLowerASCII(clientid));
-  EXPECT_EQ(kExpectedCanonicalStr, from_lower.AsLowercaseString());
-
-  const GUID from_upper = GUID::ParseCaseInsensitive(ToUpperASCII(clientid));
-  EXPECT_EQ(kExpectedCanonicalStr, from_upper.AsLowercaseString());
+  const GUID from_upper =
+      GUID::ParseCaseInsensitive(ToUpperASCII(kCanonicalStr));
+  EXPECT_EQ(kCanonicalStr, from_upper.AsLowercaseString());
 
   EXPECT_EQ(from_lower, from_upper);
 
diff --git a/build/config/rust.gni b/build/config/rust.gni
index 68949af..e50ed8e9 100644
--- a/build/config/rust.gni
+++ b/build/config/rust.gni
@@ -55,6 +55,10 @@
   # <home dir>/.rustup/toolchains/nightly-<something>-<something>
   rust_sysroot_absolute = ""
 
+  # If you're using an external Rust toolchain, set this to a Rust
+  # the output of rustc -V.
+  rustc_version = ""
+
   # If you're using a Rust toolchain as specified by rust_sysroot_absolute,
   # you can specify whether it supports nacl here.
   rust_toolchain_supports_nacl = false
@@ -136,12 +140,14 @@
 # (so e.g. rust targets are rebuilt, and the standard library is re-copied when
 # the toolchain changes). It is left empty for custom toolchains.
 rustc_revision = ""
-if (toolchain_has_rust && rust_sysroot_absolute == "") {
+if (toolchain_has_rust) {
   if (use_chromium_rust_toolchain) {
     update_rust_args = [ "--print-package-version" ]
     rustc_revision = exec_script("//tools/rust/update_rust.py",
                                  update_rust_args,
                                  "trim string")
+  } else if (rust_sysroot_absolute != "") {
+    rustc_revision = rustc_version
   } else {
     # Android toolchain version.
     rustc_revision = "rustc 1.64.0-dev (Android Rust Toolchain version 9099361)"
diff --git a/chrome/VERSION b/chrome/VERSION
index d9b20b75..b2291c7 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=114
 MINOR=0
-BUILD=5679
+BUILD=5680
 PATCH=0
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index c730308..93b94db 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -357,7 +357,6 @@
   "java/src/org/chromium/chrome/browser/compositor/layouts/eventfilter/OverlayPanelEventFilter.java",
   "java/src/org/chromium/chrome/browser/compositor/layouts/phone/SimpleAnimationLayout.java",
   "java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/StackScroller.java",
-  "java/src/org/chromium/chrome/browser/compositor/overlays/strip/CascadingStripStacker.java",
   "java/src/org/chromium/chrome/browser/compositor/overlays/strip/ScrollingStripStacker.java",
   "java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java",
   "java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManager.java",
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiFeatureUtilities.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiFeatureUtilities.java
index e9a2160..d469334 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiFeatureUtilities.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiFeatureUtilities.java
@@ -67,12 +67,6 @@
             new BooleanCachedFieldTrialParameter(ChromeFeatureList.TAB_GRID_LAYOUT_ANDROID,
                     SHOW_OPEN_IN_TAB_GROUP_MENU_ITEM_FIRST_PARAM, false);
 
-    // Field trial parameter for defining tab width for tab strip improvements.
-    private static final String TAB_STRIP_IMPROVEMENTS_TAB_WIDTH_PARAM = "min_tab_width";
-    public static final DoubleCachedFieldTrialParameter TAB_STRIP_TAB_WIDTH =
-            new DoubleCachedFieldTrialParameter(ChromeFeatureList.TAB_STRIP_IMPROVEMENTS,
-                    TAB_STRIP_IMPROVEMENTS_TAB_WIDTH_PARAM, 108.f);
-
     // Field trial parameter for controlling share tabs in TabSelectionEditorV2.
     private static final String TAB_SELECTION_EDITOR_V2_SHARE_PARAM = "enable_share";
     public static final BooleanCachedFieldTrialParameter ENABLE_TAB_SELECTION_EDITOR_V2_SHARE =
@@ -148,21 +142,11 @@
     }
 
     /**
-     * @return Whether the tab strip improvements are enabled.
-     * @param context The activity context.
-     */
-    public static boolean isTabStripImprovementsEnabled(Context context) {
-        return DeviceFormFactor.isNonMultiDisplayContextOnTablet(context)
-                && ChromeFeatureList.sTabStripImprovements.isEnabled();
-    }
-
-    /**
      * @return Whether tab groups are enabled for tablet.
      * @param context The activity context.
      */
     public static boolean isTabletTabGroupsEnabled(Context context) {
         return DeviceFormFactor.isNonMultiDisplayContextOnTablet(context)
-                && ChromeFeatureList.sTabStripImprovements.isEnabled()
                 && ChromeFeatureList.sTabGroupsForTablets.isEnabled()
                 && !DeviceClassManager.enableAccessibilityLayout(context);
     }
@@ -226,25 +210,7 @@
                 && !SysUtils.isLowEndDevice();
     }
 
-    private static Float sTabMinWidthForTesting;
-
-    /**
-     * Set the min tab width for testing.
-     */
-    public static void setTabMinWidthForTesting(@Nullable Float minWidth) {
-        sTabMinWidthForTesting = minWidth;
-    }
-
-    /**
-     * @return The min tab width.
-     */
-    public static float getTabMinWidth() {
-        if (sTabMinWidthForTesting != null) {
-            return sTabMinWidthForTesting;
-        }
-
-        return (float) TAB_STRIP_TAB_WIDTH.getValue();
-    }
+    public static Float sTabMinWidthForTesting;
 
     /**
      * @return Whether the "Open in new tab in group" context menu item should show before the
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogTest.java
index 92e72fe..3371a25 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogTest.java
@@ -40,7 +40,6 @@
 import static org.chromium.chrome.browser.flags.ChromeFeatureList.TAB_GRID_LAYOUT_ANDROID;
 import static org.chromium.chrome.browser.flags.ChromeFeatureList.TAB_GROUPS_ANDROID;
 import static org.chromium.chrome.browser.flags.ChromeFeatureList.TAB_GROUPS_FOR_TABLETS;
-import static org.chromium.chrome.browser.flags.ChromeFeatureList.TAB_STRIP_IMPROVEMENTS;
 import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.clickFirstCardFromTabSwitcher;
 import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.clickFirstTabInDialog;
 import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.clickNthTabInDialog;
@@ -143,7 +142,7 @@
 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
 @Restriction({Restriction.RESTRICTION_TYPE_NON_LOW_END_DEVICE})
 @Features.EnableFeatures({TAB_GRID_LAYOUT_ANDROID, TAB_GROUPS_ANDROID,
-    TAB_GROUPS_FOR_TABLETS, TAB_STRIP_IMPROVEMENTS})
+    TAB_GROUPS_FOR_TABLETS})
 @DoNotBatch(reason = "crbug.com/1380489")
 public class TabGridDialogTest {
     // clang-format on
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorMenuTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorMenuTest.java
index 0d18d1f9..8a0c4fe5e 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorMenuTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorMenuTest.java
@@ -18,7 +18,6 @@
 
 import static org.chromium.chrome.browser.flags.ChromeFeatureList.TAB_GROUPS_FOR_TABLETS;
 import static org.chromium.chrome.browser.flags.ChromeFeatureList.TAB_SELECTION_EDITOR_V2;
-import static org.chromium.chrome.browser.flags.ChromeFeatureList.TAB_STRIP_IMPROVEMENTS;
 import static org.chromium.ui.test.util.ViewUtils.onViewWaiting;
 
 import android.content.Context;
@@ -82,7 +81,7 @@
  */
 @RunWith(ParameterizedRunner.class)
 @ParameterAnnotations.UseRunnerDelegate(ChromeJUnit4RunnerDelegate.class)
-@Features.EnableFeatures({TAB_STRIP_IMPROVEMENTS, TAB_GROUPS_FOR_TABLETS, TAB_SELECTION_EDITOR_V2})
+@Features.EnableFeatures({TAB_GROUPS_FOR_TABLETS, TAB_SELECTION_EDITOR_V2})
 @Batch(Batch.PER_CLASS)
 public class TabSelectionEditorMenuTest extends BlankUiTestActivityTestCase {
     private static final int TAB_COUNT = 3;
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorTest.java
index 7a2a0dde..102f3823 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorTest.java
@@ -31,7 +31,6 @@
 import static org.chromium.base.test.util.Restriction.RESTRICTION_TYPE_NON_LOW_END_DEVICE;
 import static org.chromium.chrome.browser.flags.ChromeFeatureList.TAB_GROUPS_ANDROID;
 import static org.chromium.chrome.browser.flags.ChromeFeatureList.TAB_GROUPS_FOR_TABLETS;
-import static org.chromium.chrome.browser.flags.ChromeFeatureList.TAB_STRIP_IMPROVEMENTS;
 import static org.chromium.chrome.browser.flags.ChromeFeatureList.TAB_TO_GTS_ANIMATION;
 
 import android.content.Intent;
@@ -113,7 +112,8 @@
 @RunWith(ChromeJUnit4ClassRunner.class)
 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE, "force-fieldtrials=Study/Group",
         "force-fieldtrial-params=Study.Group:enable_launch_polish/true"})
-@EnableFeatures({TAB_GROUPS_ANDROID, TAB_STRIP_IMPROVEMENTS, TAB_GROUPS_FOR_TABLETS})
+@EnableFeatures({TAB_GROUPS_ANDROID + "<Study",
+        TAB_GROUPS_FOR_TABLETS})
 @DisableFeatures(TAB_TO_GTS_ANIMATION)
 @Batch(Batch.PER_CLASS)
 public class TabSelectionEditorTest {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/UndoRefocusHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/UndoRefocusHelper.java
index 93129c0..fa8eff7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/UndoRefocusHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/UndoRefocusHelper.java
@@ -12,7 +12,6 @@
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.base.supplier.ObservableSupplier;
 import org.chromium.chrome.browser.compositor.layouts.LayoutManagerImpl;
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.layouts.LayoutStateProvider;
 import org.chromium.chrome.browser.layouts.LayoutType;
 import org.chromium.chrome.browser.lifecycle.DestroyObserver;
@@ -56,8 +55,7 @@
     public static void initialize(Context context, TabModelSelector modelSelector,
             ObservableSupplier<LayoutManagerImpl> layoutManagerObservableSupplier,
             boolean isTablet) {
-        if (!DeviceFormFactor.isNonMultiDisplayContextOnTablet(context)
-                || !ChromeFeatureList.sTabStripImprovements.isEnabled()) {
+        if (!DeviceFormFactor.isNonMultiDisplayContextOnTablet(context)) {
             return;
         }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/flags/ChromeCachedFlags.java b/chrome/android/java/src/org/chromium/chrome/browser/app/flags/ChromeCachedFlags.java
index eede55e6..a38eee3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/app/flags/ChromeCachedFlags.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/app/flags/ChromeCachedFlags.java
@@ -121,7 +121,6 @@
                 ChromeFeatureList.sTabGroupsContinuationAndroid,
                 ChromeFeatureList.sTabGroupsForTablets,
                 ChromeFeatureList.sTabSelectionEditorV2,
-                ChromeFeatureList.sTabStripImprovements,
                 ChromeFeatureList.sTabToGTSAnimation,
                 ChromeFeatureList.sToolbarUseHardwareBitmapDraw,
                 ChromeFeatureList.sUseChimeAndroidSdk,
@@ -170,7 +169,6 @@
                 TabUiFeatureUtilities.ZOOMING_MIN_MEMORY,
                 TabUiFeatureUtilities.SKIP_SLOW_ZOOMING,
                 TabUiFeatureUtilities.THUMBNAIL_ASPECT_RATIO,
-                TabUiFeatureUtilities.TAB_STRIP_TAB_WIDTH,
                 TabUiFeatureUtilities.ENABLE_TAB_SELECTION_EDITOR_V2_LONGPRESS_ENTRY,
                 TabUiFeatureUtilities.ENABLE_TAB_SELECTION_EDITOR_V2_SHARE,
                 TabUiFeatureUtilities.ENABLE_TAB_SELECTION_EDITOR_V2_BOOKMARKS,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/CascadingStripStacker.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/CascadingStripStacker.java
deleted file mode 100644
index 2a46758..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/CascadingStripStacker.java
+++ /dev/null
@@ -1,131 +0,0 @@
-// Copyright 2016 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.compositor.overlays.strip;
-
-import org.chromium.base.MathUtils;
-import org.chromium.ui.base.LocalizationUtils;
-
-/**
- * A stacker that tells the {@link StripLayoutHelper} how to layer the tabs for the
- * {@link StaticLayout} when the available window width is >= 600dp. Tabs will be stacked with the
- * focused tab in front with tabs cascading back to each side.
- */
-public class CascadingStripStacker extends StripStacker {
-    @Override
-    public void setTabOffsets(int selectedIndex, StripLayoutTab[] indexOrderedTabs,
-            float tabStackWidth, int maxTabsToStack, float tabOverlapWidth, float stripLeftMargin,
-            float stripRightMargin, float stripWidth, boolean inReorderMode, boolean tabClosing,
-            boolean tabCreating, float cachedTabWidth) {
-        // 1. Calculate the size of the selected tab.  This is used later to figure out how
-        // occluded the tabs are.
-        final StripLayoutTab selTab = selectedIndex >= 0 ? indexOrderedTabs[selectedIndex] : null;
-        final float selTabWidth = selTab != null ? selTab.getWidth() : 0;
-        final float selTabVisibleSize = selTabWidth - tabStackWidth - tabOverlapWidth;
-
-        for (int i = 0; i < indexOrderedTabs.length; i++) {
-            StripLayoutTab tab = indexOrderedTabs[i];
-
-            float posX = tab.getIdealX();
-
-            // 2. Calculate how many tabs are stacked on the left or the right, giving us an idea
-            // of where we can stack this current tab.
-            int leftStackCount = (i < selectedIndex) ? Math.min(i, maxTabsToStack)
-                                                : Math.min(maxTabsToStack, selectedIndex)
-                            + Math.min(maxTabsToStack, i - selectedIndex);
-
-            int rightStackCount = (i >= selectedIndex)
-                    ? Math.min(indexOrderedTabs.length - 1 - i, maxTabsToStack)
-                    : Math.min(indexOrderedTabs.length - 1 - selectedIndex, maxTabsToStack)
-                            + Math.min(selectedIndex - i, maxTabsToStack);
-
-            if (LocalizationUtils.isLayoutRtl()) {
-                int oldLeft = leftStackCount;
-                leftStackCount = rightStackCount;
-                rightStackCount = oldLeft;
-            }
-
-            // 3. Calculate the proper draw position for the tab.  Clamp based on stacking
-            // rules.
-            float minDrawX = tabStackWidth * leftStackCount + stripLeftMargin;
-            float maxDrawX = stripWidth - tabStackWidth * rightStackCount - stripRightMargin;
-
-            float drawX =
-                    MathUtils.clamp(posX + tab.getOffsetX(), minDrawX, maxDrawX - tab.getWidth());
-
-            // TODO(dtrainor): Don't set drawX if the tab is closing?
-            tab.setDrawX(drawX);
-            tab.setDrawY(tab.getOffsetY());
-
-            // 4. Calculate how visible this tab is.
-            float visiblePercentage = 1.f;
-            if (i != selectedIndex) {
-                final float effectiveTabWidth = Math.max(tab.getWidth(), 1.f);
-                final boolean leftStack =
-                        LocalizationUtils.isLayoutRtl() ? i > selectedIndex : i < selectedIndex;
-                final float minVisible = !leftStack ? minDrawX + selTabVisibleSize : minDrawX;
-                final float maxVisible = leftStack ? maxDrawX - selTabVisibleSize : maxDrawX;
-
-                final float clippedTabWidth =
-                        Math.min(posX + effectiveTabWidth, maxVisible) - Math.max(posX, minVisible);
-                visiblePercentage = MathUtils.clamp(clippedTabWidth / effectiveTabWidth, 0.f, 1.f);
-            }
-            tab.setVisiblePercentage(visiblePercentage);
-
-            // 5. Calculate which index we start sliding content for.
-            // When reordering, we don't want to slide the content of the adjacent tabs.
-            int contentOffsetIndex = inReorderMode ? selectedIndex + 1 : selectedIndex;
-
-            // 6. Calculate how much the tab is overlapped on the left side or right for RTL.
-            float hiddenAmount = 0.f;
-            if (i > contentOffsetIndex && i > 0) {
-                // 6.a. Get the effective right edge of the previous tab.
-                final StripLayoutTab prevTab = indexOrderedTabs[i - 1];
-                final float prevLayoutWidth =
-                        (prevTab.getWidth() - tabOverlapWidth) * prevTab.getWidthWeight();
-                float prevTabRight = prevTab.getDrawX();
-                if (!LocalizationUtils.isLayoutRtl()) prevTabRight += prevLayoutWidth;
-
-                // 6.b. Subtract our current draw X from the previous tab's right edge and
-                // get the percentage covered.
-                hiddenAmount = Math.max(prevTabRight - drawX, 0);
-                if (LocalizationUtils.isLayoutRtl()) {
-                    // Invert The amount because we're RTL.
-                    hiddenAmount = prevLayoutWidth - hiddenAmount;
-                }
-            }
-
-            tab.setContentOffsetX(hiddenAmount);
-        }
-    }
-
-    @Override
-    public void performOcclusionPass(int selectedIndex, StripLayoutTab[] indexOrderedTabs,
-            float stripWidth) {
-        for (int i = 1; i < indexOrderedTabs.length; i++) {
-            StripLayoutTab prevTab = indexOrderedTabs[i - 1];
-            StripLayoutTab currTab = indexOrderedTabs[i];
-
-            if ((int) prevTab.getDrawY() == (int) currTab.getDrawY()
-                    && (int) prevTab.getDrawX() == (int) currTab.getDrawX()) {
-                if (i <= selectedIndex) {
-                    prevTab.setVisible(false);
-                } else if (i > selectedIndex) {
-                    currTab.setVisible(false);
-                }
-            } else if ((int) prevTab.getDrawX() != (int) currTab.getDrawX()) {
-                if (i <= selectedIndex) {
-                    prevTab.setVisible(true);
-                } else if (i > selectedIndex) {
-                    currTab.setVisible(true);
-                }
-            }
-
-            if (i == selectedIndex) currTab.setVisible(true);
-
-            // If index 0 is selected, this line is required to set its visibility correctly.
-            if (i - 1 == selectedIndex) prevTab.setVisible(true);
-        }
-    }
-}
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/ScrollingStripStacker.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/ScrollingStripStacker.java
index d4a1ed271..8a599fd 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/ScrollingStripStacker.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/ScrollingStripStacker.java
@@ -4,9 +4,6 @@
 
 package org.chromium.chrome.browser.compositor.overlays.strip;
 
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
-import org.chromium.ui.base.LocalizationUtils;
-
 /**
  * A stacker that tells the {@link StripLayoutHelper} how to layer the tabs for the
  * {@link StaticLayout} when the available window width is < 600dp. Tabs will be stacked side by
@@ -18,18 +15,13 @@
             float tabStackWidth, int maxTabsToStack, float tabOverlapWidth, float stripLeftMargin,
             float stripRightMargin, float stripWidth, boolean inReorderMode, boolean tabClosing,
             boolean tabCreating, float cachedTabWidth) {
-        boolean tabStripImpEnabled = ChromeFeatureList.sTabStripImprovements.isEnabled();
         for (int i = 0; i < indexOrderedTabs.length; i++) {
             StripLayoutTab tab = indexOrderedTabs[i];
-            if (tabStripImpEnabled) {
-                // When a tab is closed, drawX and width update will be animated so skip this.
-                if (!tabClosing) {
-                    tab.setDrawX(tab.getIdealX() + tab.getOffsetX());
-                    // When a tab is being created, all tabs are animating to their desired width.
-                    if (!tabCreating) tab.setWidth(cachedTabWidth);
-                }
-            } else {
-                tab.setDrawX(tab.getIdealX());
+            // When a tab is closed, drawX and width update will be animated so skip this.
+            if (!tabClosing) {
+                tab.setDrawX(tab.getIdealX() + tab.getOffsetX());
+                // When a tab is being created, all tabs are animating to their desired width.
+                if (!tabCreating) tab.setWidth(cachedTabWidth);
             }
             tab.setDrawY(tab.getOffsetY());
             tab.setVisiblePercentage(1.f);
@@ -38,78 +30,11 @@
     }
 
     @Override
-    public float computeNewTabButtonOffset(StripLayoutTab[] indexOrderedTabs, float tabOverlapWidth,
-            float stripLeftMargin, float stripRightMargin, float stripWidth, float buttonWidth,
-            float touchTargetOffset, float cachedTabWidth, boolean animate) {
-        if (ChromeFeatureList.sTabStripImprovements.isEnabled()) {
-            return super.computeNewTabButtonOffset(indexOrderedTabs, tabOverlapWidth,
-                    stripLeftMargin, stripRightMargin, stripWidth, buttonWidth, touchTargetOffset,
-                    cachedTabWidth, animate);
-        }
-        return LocalizationUtils.isLayoutRtl()
-                ? computeNewTabButtonOffsetRtl(indexOrderedTabs, tabOverlapWidth, stripRightMargin,
-                        stripWidth, buttonWidth)
-                : computeNewTabButtonOffsetLtr(indexOrderedTabs, tabOverlapWidth, stripLeftMargin);
-    }
-
-    @Override
-    public void performOcclusionPass(int selectedIndex, StripLayoutTab[] indexOrderedTabs,
-            float stripWidth) {
+    public void performOcclusionPass(
+            int selectedIndex, StripLayoutTab[] indexOrderedTabs, float stripWidth) {
         for (int i = 0; i < indexOrderedTabs.length; i++) {
             StripLayoutTab tab = indexOrderedTabs[i];
             tab.setVisible((tab.getDrawX() + tab.getWidth()) >= 0 && tab.getDrawX() <= stripWidth);
         }
     }
-
-    private float computeNewTabButtonOffsetLtr(
-            StripLayoutTab[] indexOrderedTabs, float tabOverlapWidth, float stripLeftMargin) {
-        float rightEdge = stripLeftMargin;
-
-        int numTabs = indexOrderedTabs.length;
-        // Need to look at the last two tabs to determine the new tab position in case the last
-        // tab is dying.
-        int i = numTabs > 0 ? ((numTabs >= 2) ? numTabs - 2 : numTabs - 1) : 0;
-        for (; i < numTabs; i++) {
-            StripLayoutTab tab = indexOrderedTabs[i];
-            float layoutWidth;
-            if (tab.isDying()) {
-                // If a tab is dying, adjust the tab width by the width weight so that the new
-                // tab button slides to the left with the closing tab.
-                layoutWidth = tab.getWidth() * tab.getWidthWeight();
-            } else {
-                // If a tab is being created, disregard its width weight so the new tab button
-                // doesn't end up positioned too far to the left. If a tab is neither being
-                // created or dying, its width width weight is 1.0 and can also be ignored.
-                layoutWidth = tab.getWidth();
-            }
-
-            rightEdge = Math.max(tab.getDrawX() + layoutWidth, rightEdge);
-        }
-
-        // Adjust the right edge by the tab overlap width so that the new tab button is nestled
-        // closer to the tab.
-        rightEdge -= tabOverlapWidth / 2;
-
-        // The draw X position for the new tab button is the rightEdge of the tab strip.
-        return rightEdge;
-    }
-
-    private float computeNewTabButtonOffsetRtl(StripLayoutTab[] indexOrderedTabs,
-            float tabOverlapWidth, float stripRightMargin, float stripWidth,
-            float newTabButtonWidth) {
-        float leftEdge = stripWidth - stripRightMargin;
-        int numTabs = indexOrderedTabs.length;
-        if (numTabs >= 1) {
-            StripLayoutTab tab = indexOrderedTabs[numTabs - 1];
-            leftEdge = Math.min(tab.getDrawX(), leftEdge);
-        }
-
-        // Adjust the left edge by the tab overlap width so that the new tab button is nestled
-        // closer to the tab.
-        leftEdge += tabOverlapWidth / 2;
-
-        // The draw X position for the new tab button is the left edge of the tab strip minus
-        // the new tab button width.
-        return leftEdge - newTabButtonWidth;
-    }
 }
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java
index 2cd2162..62aeba8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java
@@ -58,7 +58,6 @@
 import org.chromium.components.browser_ui.styles.ChromeColors;
 import org.chromium.components.browser_ui.styles.SemanticColorUtils;
 import org.chromium.components.browser_ui.widget.animation.Interpolators;
-import org.chromium.ui.base.DeviceFormFactor;
 import org.chromium.ui.base.LocalizationUtils;
 import org.chromium.ui.util.ColorUtils;
 
@@ -120,6 +119,8 @@
     private static final int ANIM_TAB_DIM_MS = 150;
     private static final int ANIM_BUTTONS_FADE_MS = 150;
     private static final int NEW_TAB_BUTTON_OFFSET_MOVE_MS = 250;
+    private static final int SCROLL_DISTANCE_SHORT = 960;
+    private static final int SCROLL_DISTANCE_MEDIUM = 1920;
     private static final long INVALID_TIME = 0L;
     static final long DROP_INTO_GROUP_MS = 300L;
 
@@ -156,6 +157,7 @@
     static final float BACKGROUND_TAB_BRIGHTNESS_DEFAULT = 1.f;
     static final float BACKGROUND_TAB_BRIGHTNESS_DIMMED = 0.65f;
     static final float FADE_FULL_OPACITY_THRESHOLD_DP = 24.f;
+    private static final float TAB_STRIP_TAB_WIDTH = 108.f;
 
     private static final int MESSAGE_RESIZE = 1;
     private static final int MESSAGE_UPDATE_SPINNER = 2;
@@ -171,9 +173,7 @@
     private TabModel mModel;
     private TabGroupModelFilter mTabGroupModelFilter;
     private TabCreator mTabCreator;
-    private StripStacker mStripStacker;
-    private CascadingStripStacker mCascadingStripStacker = new CascadingStripStacker();
-    private ScrollingStripStacker mScrollingStripStacker = new ScrollingStripStacker();
+    private StripStacker mStripStacker = new ScrollingStripStacker();
 
     // Internal State
     private StripLayoutTab[] mStripTabs = new StripLayoutTab[0];
@@ -230,8 +230,6 @@
     private float mNewTabButtonWithTabStripEndPadding;
 
     private final boolean mIncognito;
-    // Whether the CascadingStripStacker should be used.
-    private boolean mShouldCascadeTabs;
     private boolean mIsFirstLayoutPass;
     private boolean mAnimationsDisabledForTesting;
     // Whether tab strip scrolling is in progress
@@ -247,9 +245,6 @@
     private boolean mTabGroupMarginAnimRunning;
     private boolean mTabCreating;
 
-    // Experiment flags
-    private final boolean mTabStripImpEnabled;
-
     /**
      * Creates an instance of the {@link StripLayoutHelper}.
      * @param context         The current Android {@link Context}.
@@ -274,7 +269,6 @@
             mNewTabButtonWidth = NEW_TAB_BUTTON_WIDTH_DP;
         }
         mModelSelectorButton = modelSelectorButton;
-        mTabStripImpEnabled = ChromeFeatureList.sTabStripImprovements.isEnabled();
 
         if (ChromeFeatureList.sTabStripRedesign.isEnabled()) {
             mNewTabButtonWithTabStripEndPadding =
@@ -290,7 +284,7 @@
                 ? mNewTabButtonWithTabStripEndPadding + mNewTabButtonWidth
                 : 0;
 
-        mMinTabWidth = TabUiFeatureUtilities.getTabMinWidth();
+        mMinTabWidth = TAB_STRIP_TAB_WIDTH;
 
         mMaxTabWidth = TabUiThemeUtil.getMaxTabStripTabWidthDp();
         mReorderMoveStartThreshold = REORDER_MOVE_START_THRESHOLD_DP;
@@ -416,10 +410,6 @@
         int menuWidth = mContext.getResources().getDimensionPixelSize(R.dimen.menu_width);
         mTabMenu.setWidth(menuWidth);
         mTabMenu.setModal(true);
-
-        mShouldCascadeTabs =
-                DeviceFormFactor.isNonMultiDisplayContextOnTablet(context) && !mTabStripImpEnabled;
-        mStripStacker = mShouldCascadeTabs ? mCascadingStripStacker : mScrollingStripStacker;
         mIsFirstLayoutPass = true;
     }
 
@@ -499,8 +489,6 @@
      * @return The opacity to use for the fade.
      */
     private float getFadeOpacity(boolean isLeft) {
-        if (mShouldCascadeTabs) return 0.f;
-
         // In RTL, scroll position 0 is on the right side of the screen, whereas in LTR scroll
         // position 0 is on the left. Account for that in the offset calculation.
         boolean isRtl = LocalizationUtils.isLayoutRtl();
@@ -569,9 +557,8 @@
     public void onSizeChanged(float width, float height, boolean orientationChanged, long time) {
         if (mWidth == width && mHeight == height) return;
 
-        boolean wasSelectedTabVisible = mTabStripImpEnabled && orientationChanged && mModel != null
-                && mModel.index() >= 0 && mModel.index() < mStripTabs.length
-                && mStripTabs[mModel.index()].isVisible();
+        boolean wasSelectedTabVisible = orientationChanged && mModel != null && mModel.index() >= 0
+                && mModel.index() < mStripTabs.length && mStripTabs[mModel.index()].isVisible();
 
         boolean widthChanged = mWidth != width;
 
@@ -584,9 +571,6 @@
 
         if (widthChanged) {
             computeAndUpdateTabWidth(false, false);
-            boolean shouldCascade =
-                    width >= DeviceFormFactor.MINIMUM_TABLET_WIDTH_DP && !mTabStripImpEnabled;
-            setShouldCascadeTabs(shouldCascade);
         }
         if (mStripTabs.length > 0) mUpdateHost.requestUpdate();
 
@@ -597,33 +581,6 @@
     }
 
     /**
-     * Should be called when the viewport width crosses the 600dp threshold. The
-     * {@link CascadingStripStacker} should be used at 600dp+, otherwise the
-     * {@link ScrollingStripStacker} should be used.
-     * @param shouldCascadeTabs Whether the {@link CascadingStripStacker} should be used.
-     */
-    void setShouldCascadeTabs(boolean shouldCascadeTabs) {
-        if (mModel == null) return;
-
-        if (shouldCascadeTabs != mShouldCascadeTabs) {
-            mShouldCascadeTabs = shouldCascadeTabs;
-            setTabStacker(shouldCascadeTabs ? mCascadingStripStacker : mScrollingStripStacker);
-
-            // Scroll to make the selected tab visible and nearby tabs visible.
-            if (mModel.getTabAt(mModel.index()) != null) {
-                updateScrollOffsetLimits();
-                StripLayoutTab tab = findTabById(mModel.getTabAt(mModel.index()).getId());
-                float delta = calculateOffsetToMakeTabVisible(tab, true, true, true, true);
-                // During this resize, mMinScrollOffset will be changing, so the scroll effect
-                // cannot be properly animated. Jump to the new scroll offset instead.
-                mScrollOffset = mScrollOffset + delta;
-            }
-
-            updateStrip();
-        }
-    }
-
-    /**
      * Updates all internal resources and dimensions.
      * @param context The current Android {@link Context}.
      */
@@ -652,7 +609,6 @@
         if (mModel == model) return;
         mModel = model;
         mTabCreator = tabCreator;
-        setShouldCascadeTabs(mShouldCascadeTabs);
         computeAndUpdateTabOrders(false, false);
     }
 
@@ -823,42 +779,27 @@
 
         // 3. Figure out which tab needs to be visible.
         StripLayoutTab fastExpandTab = findTabById(prevId);
-        boolean allowLeftExpand = false;
-        boolean canExpandSelectedTab = false;
-        if (!selected) {
-            fastExpandTab = tab;
-            allowLeftExpand = true;
-        }
 
-        if (!mShouldCascadeTabs) {
-            int selIndex = mModel.index();
-            if (mTabStripImpEnabled && !selected && selIndex >= 0 && selIndex < mStripTabs.length) {
-                // Prioritize focusing on selected tab over newly created unselected tabs.
-                fastExpandTab = mStripTabs[selIndex];
-            } else {
-                fastExpandTab = tab;
-            }
-            allowLeftExpand = true;
-            canExpandSelectedTab = true;
+        int selIndex = mModel.index();
+        if (!selected && selIndex >= 0 && selIndex < mStripTabs.length) {
+            // Prioritize focusing on selected tab over newly created unselected tabs.
+            fastExpandTab = mStripTabs[selIndex];
+        } else {
+            fastExpandTab = tab;
         }
+        boolean allowLeftExpand = true;
+        boolean canExpandSelectedTab = true;
 
         // 4. Scroll the stack so that the fast expand tab is visible. Skip if tab was restored.
-        boolean skipAutoScroll = (mTabStripImpEnabled && closureCancelled) || onStartup;
+        boolean skipAutoScroll = closureCancelled || onStartup;
         if (fastExpandTab != null && !skipAutoScroll) {
             float delta = calculateOffsetToMakeTabVisible(
                     fastExpandTab, canExpandSelectedTab, allowLeftExpand, true, selected);
 
-            if (!mShouldCascadeTabs) {
-                // If the ScrollingStripStacker is being used and the new tab button is visible, go
-                // directly to the new scroll offset rather than animating. Animating the scroll
-                // causes the new tab button to disappear for a frame.
-                boolean shouldAnimate = (!mNewTabButton.isVisible() || mTabStripImpEnabled)
-                        && !mAnimationsDisabledForTesting;
-                setScrollForScrollingTabStacker(delta, shouldAnimate, time);
-            } else if (delta != 0.f) {
-                mScroller.startScroll(
-                        Math.round(mScrollOffset), 0, (int) delta, 0, time, getExpandDuration());
-            }
+            // If new tab button is visible, go directly to the new scroll offset rather than
+            // animating. Animating the scroll causes the new tab button to disappear for a frame.
+            boolean shouldAnimate = !mAnimationsDisabledForTesting;
+            setScrollForScrollingTabStacker(delta, shouldAnimate, time);
         }
 
         // 5. When restoring tabs through startup, ensure the selected tab is visible, as the newly
@@ -873,24 +814,15 @@
      * partially visible tabs at the edge of the tab strip when min tab width is set to >=156dp.
      */
     private void updateCloseButtons() {
-        if (!mTabStripImpEnabled) {
-            return;
-        }
-
         Tab selectedTab = mModel.getTabAt(mModel.index());
         final int count = mModel.getCount();
         if (selectedTab == null) return;
 
         for (int i = 0; i < count; i++) {
             final StripLayoutTab tab = mStripTabs[i];
-            if (mMinTabWidth == TAB_WIDTH_MEDIUM) {
-                boolean canShowCloseButton = shouldShowCloseButton(tab, i);
-                mStripTabs[i].setCanShowCloseButton(canShowCloseButton, !mIsFirstLayoutPass);
-            } else if (mMinTabWidth == TAB_WIDTH_SMALL) {
-                boolean canShowCloseButton = tab.getWidth() >= TAB_WIDTH_MEDIUM
-                        || (tab.getId() == selectedTab.getId() && shouldShowCloseButton(tab, i));
-                mStripTabs[i].setCanShowCloseButton(canShowCloseButton, !mIsFirstLayoutPass);
-            }
+            boolean canShowCloseButton = tab.getWidth() >= TAB_WIDTH_MEDIUM
+                    || (tab.getId() == selectedTab.getId() && shouldShowCloseButton(tab, i));
+            mStripTabs[i].setCanShowCloseButton(canShowCloseButton, !mIsFirstLayoutPass);
         }
     }
 
@@ -1084,42 +1016,13 @@
             // 2.b. Still scrolling, update the scroll destination here.
             mScroller.setFinalX((int) (mScroller.getFinalX() + deltaX));
         } else {
-            // 2.c. Not scrolling. Check if we need to fast expand.
-            float fastExpandDelta;
-            if (mShouldCascadeTabs) {
-                fastExpandDelta =
-                        calculateOffsetToMakeTabVisible(mInteractingTab, true, true, true, true);
-            } else {
-                // Non-cascaded tabs are never hidden behind each other, so there's no need to fast
-                // expand.
-                fastExpandDelta = 0.f;
+            // 2.c. Not scrolling.
+            if (!mIsStripScrollInProgress) {
+                mIsStripScrollInProgress = true;
+                RecordUserAction.record("MobileToolbarSlideTabs");
+                onStripScrollStart();
             }
-
-            if (mInteractingTab != null && fastExpandDelta != 0.f) {
-                if ((fastExpandDelta > 0 && deltaX > 0) || (fastExpandDelta < 0 && deltaX < 0)) {
-                    mScroller.startScroll(Math.round(mScrollOffset), 0, (int) fastExpandDelta, 0,
-                            time, getExpandDuration());
-                }
-            } else {
-                if (!mIsStripScrollInProgress) {
-                    mIsStripScrollInProgress = true;
-                    RecordUserAction.record("MobileToolbarSlideTabs");
-                    onStripScrollStart();
-                }
-                updateScrollOffsetPosition(mScrollOffset + deltaX);
-            }
-        }
-
-        // 3. Check if we should start the reorder mode. Disabling this for tab strip improvements
-        // experiments to prevent accidentally entering reorder mode when scrolling the tab strip.
-        if (!ChromeFeatureList.sTabStripImprovements.isEnabled() && !mInReorderMode) {
-            final float absTotalX = Math.abs(totalX);
-            final float absTotalY = Math.abs(totalY);
-            if (totalY > mReorderMoveStartThreshold && absTotalX < mReorderMoveStartThreshold * 2.f
-                    && (absTotalX > EPSILON
-                               && (absTotalY / absTotalX) > TAN_OF_REORDER_ANGLE_START_THRESHOLD)) {
-                startReorderMode(time, x, x - totalX);
-            }
+            updateScrollOffsetPosition(mScrollOffset + deltaX);
         }
 
         // If we're scrolling at all we aren't interacting with any particular tab.
@@ -1257,10 +1160,11 @@
         // Find out if we're closing the last tab to determine if we resize immediately.
         boolean lastTab =
                 mStripTabs.length == 0 || mStripTabs[mStripTabs.length - 1].getId() == tab.getId();
-        // For the improved tab strip design, when a tab is closed #resizeStripOnTabClose will run
-        // animations for the new tab offset and tab x offsets. When there is only 1 tab remaining,
-        // we do not need to run those animations, so #resizeTabStrip() is used instead.
-        boolean runImprovedTabAnimations = mTabStripImpEnabled && mStripTabs.length > 1;
+
+        // When a tab is closed #resizeStripOnTabClose will run animations for the new tab offset
+        // and tab x offsets. When there is only 1 tab remaining, we do not need to run those
+        // animations, so #resizeTabStrip() is used instead.
+        boolean runImprovedTabAnimations = mStripTabs.length > 1;
 
         // 1. Setup the close animation.
         CompositorAnimator tabClosingAnimator = CompositorAnimator.ofFloatProperty(
@@ -1505,27 +1409,19 @@
         }
     }
 
-    private void updateScrollOffsetLimits() {
+    @VisibleForTesting
+    void updateScrollOffsetLimits() {
         // 1. Compute the width of the available space for all tabs.
         float stripWidth = mWidth - mLeftMargin - mRightMargin;
 
         // 2. Compute the effective width of every tab.
-        float tabsWidth = 0.f;
-        if (mShouldCascadeTabs) {
+        float tabsWidth = mStripTabs.length * (mCachedTabWidth - mTabOverlapWidth);
+
+        if (mInReorderMode || mTabGroupMarginAnimRunning) {
+            tabsWidth += mStripStartMarginForReorder;
             for (int i = 0; i < mStripTabs.length; i++) {
                 final StripLayoutTab tab = mStripTabs[i];
-                tabsWidth += (tab.getWidth() - mTabOverlapWidth) * tab.getWidthWeight();
-            }
-        } else {
-            // When tabs aren't cascaded, they're non-animating width weight is always 1.0 so it
-            // doesn't need to be included in this calculation.
-            tabsWidth = mStripTabs.length * (mCachedTabWidth - mTabOverlapWidth);
-            if (mInReorderMode || mTabGroupMarginAnimRunning) {
-                tabsWidth += mStripStartMarginForReorder;
-                for (int i = 0; i < mStripTabs.length; i++) {
-                    final StripLayoutTab tab = mStripTabs[i];
-                    tabsWidth += tab.getTrailingMargin();
-                }
+                tabsWidth += tab.getTrailingMargin();
             }
         }
 
@@ -1605,8 +1501,7 @@
     private void pushStackerPropertiesToTab(StripLayoutTab tab) {
         // The close button is visible by default. If it should be hidden on tab creation, do not
         // animate the fade-out. See (https://crbug.com/1342654).
-        boolean shouldShowCloseButton =
-                (!mTabStripImpEnabled) || mCachedTabWidth >= TAB_WIDTH_MEDIUM;
+        boolean shouldShowCloseButton = mCachedTabWidth >= TAB_WIDTH_MEDIUM;
         tab.setCanShowCloseButton(
                 mStripStacker.canShowCloseButton() && shouldShowCloseButton, false);
         // TODO(dtrainor): Push more properties as they are added (title text slide, etc?)
@@ -1856,8 +1751,8 @@
         if (selIndex >= 0 && selIndex < mStripTabs.length) {
             currentlyFocusedTab = mStripTabs[selIndex];
         }
-        if (mTabStripImpEnabled && currentlyFocusedTab != null
-                && isSelectedTabCompletelyVisible(currentlyFocusedTab) && !selected) {
+        if (currentlyFocusedTab != null && isSelectedTabCompletelyVisible(currentlyFocusedTab)
+                && !selected) {
             // We want to prioritize keeping the currently selected tab in visible area if newly
             // created tab is not selected. If selected tab is already visible, no need to scroll.
             return 0.f;
@@ -1871,30 +1766,14 @@
 
         // 4. Return the proper deltaX that has to be applied to the current scroll to see the
         // tab.
-        if (mShouldCascadeTabs) {
-            if (mScrollOffset < optimalLeft && canExpandLeft) {
-                return optimalLeft - mScrollOffset;
-            } else if (mScrollOffset > optimalRight && canExpandRight) {
-                return optimalRight - mScrollOffset;
-            }
-        } else {
-            if (mTabStripImpEnabled) {
-                // Distance to make tab visible on the left edge and the right edge.
-                float offsetToOptimalLeft = optimalLeft - mScrollOffset;
-                float offsetToOptimalRight = optimalRight - mScrollOffset - mTabOverlapWidth;
-                // Scroll the minimum possible distance to bring the selected tab into visible area.
-                if (Math.abs(offsetToOptimalLeft) < Math.abs(offsetToOptimalRight)) {
-                    return offsetToOptimalLeft;
-                }
-                return offsetToOptimalRight;
-            }
-            // If tabs are not cascaded, the entire tab strip scrolls and the strip should be
-            // scrolled to the optimal left offset.
-            return optimalLeft - mScrollOffset;
+        // Distance to make tab visible on the left edge and the right edge.
+        float offsetToOptimalLeft = optimalLeft - mScrollOffset;
+        float offsetToOptimalRight = optimalRight - mScrollOffset - mTabOverlapWidth;
+        // Scroll the minimum possible distance to bring the selected tab into visible area.
+        if (Math.abs(offsetToOptimalLeft) < Math.abs(offsetToOptimalRight)) {
+            return offsetToOptimalLeft;
         }
-
-        // 5. We don't have to do anything.  Return no delta.
-        return 0.f;
+        return offsetToOptimalRight;
     }
 
     @VisibleForTesting
@@ -2029,14 +1908,9 @@
         }
         setCompositorButtonsVisible(false);
 
-        // 6. Fast expand to make sure this tab is visible. If tabs are not cascaded, the selected
-        //    tab will already be visible, so there's no need to fast expand to make it visible.
-        if (mShouldCascadeTabs) {
-            float fastExpandDelta =
-                    calculateOffsetToMakeTabVisible(mInteractingTab, true, true, true, true);
-            mScroller.startScroll(Math.round(mScrollOffset), 0, (int) fastExpandDelta, 0, time,
-                    getExpandDuration());
-        } else if (TabUiFeatureUtilities.isTabletTabGroupsEnabled(mContext)) {
+        // 6. the selected tab will already be visible, so update tab group and background
+        // container.
+        if (TabUiFeatureUtilities.isTabletTabGroupsEnabled(mContext)) {
             Tab tab = getTabById(mInteractingTab.getId());
             computeAndUpdateTabGroupMargins(true, animationList);
             if (ChromeFeatureList.sTabStripRedesign.isEnabled()) {
@@ -2801,27 +2675,20 @@
      * @return the duration in ms.
      */
     private int getExpandDuration() {
-        if (mTabStripImpEnabled) {
-            float scrollDistance = Math.abs(mScrollOffset);
-            if (scrollDistance <= 960) {
-                return EXPAND_DURATION_MS;
-            } else if (scrollDistance <= 1920) {
-                return EXPAND_DURATION_MS_MEDIUM;
-            } else {
-                return EXPAND_DURATION_MS_LONG;
-            }
+        float scrollDistance = Math.abs(mScrollOffset);
+        if (scrollDistance <= SCROLL_DISTANCE_SHORT) {
+            return EXPAND_DURATION_MS;
+        } else if (scrollDistance <= SCROLL_DISTANCE_MEDIUM) {
+            return EXPAND_DURATION_MS_MEDIUM;
+        } else {
+            return EXPAND_DURATION_MS_LONG;
         }
-
-        return EXPAND_DURATION_MS;
     }
 
     /**
      * Scrolls to the selected tab if it's not fully visible.
      */
     private void bringSelectedTabToVisibleArea(long time, boolean animate) {
-        // The selected tab is always visible in the CascadingStripStacker.
-        if (mShouldCascadeTabs) return;
-
         Tab selectedTab = mModel.getTabAt(mModel.index());
         if (selectedTab == null) return;
 
@@ -2833,20 +2700,15 @@
     }
 
     private boolean isSelectedTabCompletelyVisible(StripLayoutTab selectedTab) {
-        if (mTabStripImpEnabled) {
-            boolean isRtl = LocalizationUtils.isLayoutRtl();
-            if (isRtl) {
-                return selectedTab.isVisible()
-                        && selectedTab.getDrawX() >= getCloseBtnVisibilityThreshold(false)
-                        && selectedTab.getDrawX() + selectedTab.getWidth() <= mWidth;
-            } else {
-                return selectedTab.isVisible() && selectedTab.getDrawX() >= 0
-                        && selectedTab.getDrawX() + selectedTab.getWidth() <= getScrollableWidth();
-            }
+        boolean isRtl = LocalizationUtils.isLayoutRtl();
+        if (isRtl) {
+            return selectedTab.isVisible()
+                    && selectedTab.getDrawX() >= getCloseBtnVisibilityThreshold(false)
+                    && selectedTab.getDrawX() + selectedTab.getWidth() <= mWidth;
+        } else {
+            return selectedTab.isVisible() && selectedTab.getDrawX() >= 0
+                    && selectedTab.getDrawX() + selectedTab.getWidth() <= getScrollableWidth();
         }
-
-        return selectedTab.isVisible() && selectedTab.getDrawX() >= 0
-                && selectedTab.getDrawX() + selectedTab.getWidth() <= mWidth;
     }
 
     /**
@@ -2855,13 +2717,8 @@
      * @return the width of the tab strip that is scrollable.
      */
     private float getScrollableWidth() {
-        if (mTabStripImpEnabled) {
-            return mWidth - getCloseBtnVisibilityThreshold(false)
-                    - (LocalizationUtils.isLayoutRtl() ? mRightMargin : mLeftMargin);
-        }
-
-        // TODO(dtrainor): Use real tab widths here?
-        return mWidth - mLeftMargin - mRightMargin;
+        return mWidth - getCloseBtnVisibilityThreshold(false)
+                - (LocalizationUtils.isLayoutRtl() ? mRightMargin : mLeftMargin);
     }
 
     /**
@@ -2873,6 +2730,8 @@
      */
     private float getCloseBtnVisibilityThreshold(boolean start) {
         if (start) {
+            //@TODO(zheliooo) Add unit tests to cover start tab cases for testTabSelected in
+            // StripLayoutHelperTest
             // The start of the tab strip does not have the new tab and model selector button
             // so the threshold is constant.
             return CLOSE_BTN_VISIBILITY_THRESHOLD_START;
@@ -2900,14 +2759,6 @@
         mTabMenu.performItemClick(menuItemId);
     }
 
-    /**
-     * @return Whether the {@link CascadingStripStacker} is being used.
-     */
-    @VisibleForTesting
-    boolean shouldCascadeTabs() {
-        return mShouldCascadeTabs;
-    }
-
     @VisibleForTesting
     int getExpandDurationForTesting() {
         return getExpandDuration();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutTab.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutTab.java
index a388daa..3df441d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutTab.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutTab.java
@@ -36,9 +36,6 @@
 import org.chromium.components.browser_ui.styles.ChromeColors;
 import org.chromium.components.browser_ui.styles.SemanticColorUtils;
 import org.chromium.ui.base.LocalizationUtils;
-import org.chromium.ui.resources.AndroidResourceType;
-import org.chromium.ui.resources.LayoutResource;
-import org.chromium.ui.resources.ResourceManager;
 import org.chromium.ui.util.ColorUtils;
 
 import java.util.List;
@@ -181,8 +178,7 @@
     // Close Button Constants
     // Close button padding value comes from the built-in padding in the source png.
     private static final int CLOSE_BUTTON_PADDING_DP = 7;
-    private static final int CLOSE_BUTTON_WIDTH_DP = 36;
-    private static final int CLOSE_BUTTON_WIDTH_SCROLLING_STRIP_DP = 48;
+    private static final int CLOSE_BUTTON_WIDTH_DP = 48;
 
     // Tab strip content y offset
     private static final float FOLIO_CONTENT_OFFSET_Y = 8.f;
@@ -876,9 +872,7 @@
     }
 
     private RectF getCloseRect() {
-        boolean tabStripImprovementsEnabled = ChromeFeatureList.sTabStripImprovements.isEnabled();
-        int closeButtonWidth = tabStripImprovementsEnabled ? CLOSE_BUTTON_WIDTH_SCROLLING_STRIP_DP
-                                                           : CLOSE_BUTTON_WIDTH_DP;
+        int closeButtonWidth = CLOSE_BUTTON_WIDTH_DP;
         if (!LocalizationUtils.isLayoutRtl()) {
             mClosePlacement.left = getWidth() - closeButtonWidth;
             mClosePlacement.right = mClosePlacement.left + closeButtonWidth;
@@ -890,21 +884,7 @@
         mClosePlacement.top = 0;
         mClosePlacement.bottom = getHeight();
 
-        float xOffset = 0;
-        if (!tabStripImprovementsEnabled) {
-            ResourceManager manager = mRenderHost.getResourceManager();
-            if (manager != null) {
-                LayoutResource resource =
-                        manager.getResource(AndroidResourceType.STATIC, getResourceId());
-                if (resource != null) {
-                    xOffset = LocalizationUtils.isLayoutRtl()
-                            ? resource.getPadding().left
-                            : -(resource.getBitmapSize().width() - resource.getPadding().right);
-                }
-            }
-        }
-
-        mClosePlacement.offset(getDrawX() + xOffset, getDrawY());
+        mClosePlacement.offset(getDrawX(), getDrawY());
         return mClosePlacement;
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripStacker.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripStacker.java
index 19a09c7b5..4458a32 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripStacker.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripStacker.java
@@ -6,7 +6,6 @@
 
 import org.chromium.base.MathUtils;
 import org.chromium.chrome.browser.compositor.layouts.Layout;
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.ui.base.LocalizationUtils;
 
 /**
@@ -97,13 +96,11 @@
             float tabOverlapWidth, float stripLeftMargin, float stripRightMargin, float stripWidth,
             float touchTargetOffset, float cachedTabWidth, boolean animate) {
         float rightEdge = stripLeftMargin;
-        boolean tabStripImpEnabled = ChromeFeatureList.sTabStripImprovements.isEnabled();
-
         for (StripLayoutTab tab : indexOrderedTabs) {
             float tabWidth;
             float tabDrawX;
             float tabWidthWeight;
-            if (tabStripImpEnabled && animate) {
+            if (animate) {
                 // This value is set to 1.f to avoid the new tab button jitter for the improved tab
                 // strip design. The tab.width and tab.drawX may not reflect the final values before
                 // the tab closing animations are completed.
@@ -132,12 +129,10 @@
             float stripLeftMargin, float stripRightMargin, float stripWidth,
             float newTabButtonWidth, float touchTargetOffset, float cachedTabWidth,
             boolean animate) {
-        boolean tabStripImpEnabled = ChromeFeatureList.sTabStripImprovements.isEnabled();
-
         float leftEdge = stripWidth - stripRightMargin;
 
         for (StripLayoutTab tab : indexOrderedTabs) {
-            float drawX = (tabStripImpEnabled && animate) ? tab.getIdealX() : tab.getDrawX();
+            float drawX = animate ? tab.getIdealX() : tab.getDrawX();
             leftEdge = Math.min(drawX, leftEdge);
         }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabActivity.java
index 2b015ea8..ee43c71 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabActivity.java
@@ -487,8 +487,7 @@
             // CustomTabActivityNavigationController#FinishHandler. Pass the mode enum into
             // CustomTabActivityModule, so that it can provide the correct implementation.
             getComponent().resolveTwaFinishHandler().onFinish(defaultBehavior);
-        } else if (intentDataProvider.isPartialCustomTab()
-                && intentDataProvider.shouldAnimateOnFinish()) {
+        } else if (intentDataProvider.isPartialCustomTab()) {
             // WebContents is missing during the close animation due to android:windowIsTranslucent.
             // We let partial CCT handle the animation.
             mBaseCustomTabRootUiCoordinator.handleCloseAnimation(defaultBehavior);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/RequestDesktopUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/RequestDesktopUtils.java
index f5f5c159..309076b0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/RequestDesktopUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/RequestDesktopUtils.java
@@ -396,15 +396,7 @@
             return false;
         }
 
-        // If the smallest screen size in dp is below threshold, avoid default-enabling the setting.
-        if (context.getResources().getConfiguration().smallestScreenWidthDp
-                < ChromeFeatureList.getFieldTrialParamByFeatureAsInt(feature,
-                        PARAM_GLOBAL_SETTING_DEFAULT_ON_SMALLEST_SCREEN_WIDTH,
-                        DEFAULT_GLOBAL_SETTING_DEFAULT_ON_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP)) {
-            // TODO(shuyng): Add downgrade path support for smallestScreenWidthDp change.
-            return false;
-        }
-
+        // Check whether manufacturer is in allow list.
         if (sDefaultEnabledManufacturerAllowlist == null) {
             sDefaultEnabledManufacturerAllowlist = new HashSet<>();
             String allowListStr = ChromeFeatureList.getFieldTrialParamByFeature(
@@ -416,9 +408,17 @@
         if (!sDefaultEnabledManufacturerAllowlist.isEmpty()
                 && !sDefaultEnabledManufacturerAllowlist.contains(
                         Build.MANUFACTURER.toLowerCase(Locale.US))) {
+            updateNoLongerInCohort();
             return false;
         }
 
+        if (displaySizeInInches > MAX_RECORDED_SCREEN_SIZE_INCHES) {
+            silentlyReportingCrashes(
+                    context, displaySizeInInches, "Display size falls into overflow bucket");
+        }
+
+        // If it is not external display and the screen size in inches is below threshold, avoid
+        // default-enabling the setting.
         SharedPreferencesManager sharedPreferencesManager = SharedPreferencesManager.getInstance();
         boolean isOnExternalDisplay =
                 !ChromeFeatureList.getFieldTrialParamByFeatureAsBoolean(
@@ -427,12 +427,23 @@
         double screenSizeThreshold = ChromeFeatureList.getFieldTrialParamByFeatureAsDouble(feature,
                 PARAM_GLOBAL_SETTING_DEFAULT_ON_DISPLAY_SIZE_THRESHOLD_INCHES,
                 DEFAULT_GLOBAL_SETTING_DEFAULT_ON_DISPLAY_SIZE_THRESHOLD_INCHES);
-        if (displaySizeInInches < screenSizeThreshold && !isOnExternalDisplay
-                && sharedPreferencesManager.contains(
+        if (!isOnExternalDisplay && displaySizeInInches < screenSizeThreshold) {
+            if (sharedPreferencesManager.contains(
                         ChromePreferenceKeys.DEFAULT_ENABLE_DESKTOP_SITE_GLOBAL_SETTING_COHORT)) {
-            // TODO(shuyng): Add downgrade path support for displaySizeInInches change.
-            silentlyReportingCrashes(
-                    context, displaySizeInInches, "Display size falls below threshold");
+                silentlyReportingCrashes(
+                        context, displaySizeInInches, "Display size falls below threshold");
+            }
+            updateNoLongerInCohort();
+            return false;
+        }
+
+        // If the smallest screen size in dp is below threshold, avoid default-enabling the setting.
+        if (context.getResources().getConfiguration().smallestScreenWidthDp
+                < ChromeFeatureList.getFieldTrialParamByFeatureAsInt(feature,
+                        PARAM_GLOBAL_SETTING_DEFAULT_ON_SMALLEST_SCREEN_WIDTH,
+                        DEFAULT_GLOBAL_SETTING_DEFAULT_ON_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP)) {
+            updateNoLongerInCohort();
+            return false;
         }
 
         boolean previouslyDefaultEnabled = sharedPreferencesManager.readBoolean(
@@ -441,8 +452,7 @@
                 SingleCategorySettingsConstants
                         .USER_ENABLED_DESKTOP_SITE_GLOBAL_SETTING_PREFERENCE_KEY);
 
-        boolean inCohort = !previouslyUpdatedByUser && displaySizeInInches >= screenSizeThreshold
-                && !isOnExternalDisplay;
+        boolean inCohort = !previouslyUpdatedByUser && !isOnExternalDisplay;
         boolean wouldEnable = !previouslyDefaultEnabled && inCohort;
         if (wouldEnable) {
             // Store a SharedPreferences key to tag the device as qualified for the feature
@@ -452,11 +462,6 @@
             captureDisplaySpec(context, displaySizeInInches);
         }
 
-        if (displaySizeInInches > MAX_RECORDED_SCREEN_SIZE_INCHES) {
-            silentlyReportingCrashes(
-                    context, displaySizeInInches, "Display size falls into overflow bucket");
-        }
-
         if (inCohort
                 || sharedPreferencesManager.contains(
                         ChromePreferenceKeys.DEFAULT_ENABLE_DESKTOP_SITE_GLOBAL_SETTING_COHORT)) {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/overlays/strip/TabStripTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/overlays/strip/TabStripTest.java
index 21c62e9..38405f4 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/overlays/strip/TabStripTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/overlays/strip/TabStripTest.java
@@ -11,7 +11,6 @@
 
 import org.hamcrest.Matchers;
 import org.junit.Assert;
-import org.junit.Before;
 import org.junit.ClassRule;
 import org.junit.Rule;
 import org.junit.Test;
@@ -60,11 +59,6 @@
     public BlankCTATabInitialStateRule mBlankCTATabInitialStateRule =
             new BlankCTATabInitialStateRule(sActivityTestRule, false);
 
-    @Before
-    public void setUp() throws ExecutionException {
-        setShouldCascadeTabsAndCheckTabStrips(true);
-    }
-
     /**
      * Tests that the initial state of the system is good.  This is so the default TabStrips match
      * the TabModels and we do not have to test this in further tests.
@@ -524,27 +518,23 @@
     }
 
     /**
-     * Compares tab strips with models after switching between the ScrollingStripStacker and
-     * CascadingStripStacker when an incognito tab is present. Tests tapping the incognito
-     * button while the strip is using the ScrollingStripStacker (other tests cover tapping
-     * the button while using the CascadingStripStacker), and checks that switching between
-     * tab models scrolls to make the selected tab visible.
+     * Tests tapping the incognito button when an incognito tab is present and checks scrolls to
+     * make the selected tab visible.
      */
     @Test
     @LargeTest
     @Restriction(UiRestriction.RESTRICTION_TYPE_TABLET)
     @Feature({"TabStrip"})
-    public void testSwitchStripStackersWithIncognito() throws Exception {
+    public void testScrollingStripStackersWithIncognito() throws Exception {
         // Open an incognito tab to switch to the incognito model.
         sActivityTestRule.newIncognitoTabFromMenu();
 
-        // Open enough regular tabs to cause the tabs to cascade or the strip to scroll depending
-        // on which stacker is being used.
+        // Open enough regular tabs to cause the tab strip to scroll.
         ChromeTabUtils.newTabsFromMenu(
                 InstrumentationRegistry.getInstrumentation(), sActivityTestRule.getActivity(), 20);
 
-        // Switch to the ScrollingStripStacker.
-        setShouldCascadeTabsAndCheckTabStrips(false);
+        // Check scrolling tab strip
+        checkTabStrips();
 
         // Scroll so the selected tab is not visible.
         assertSetTabStripScrollOffset(0);
@@ -573,45 +563,35 @@
 
         // Wait for selected tab to be visible.
         helper.waitForCallback(0);
-
-        // Switch to the CascadingStripStacker.
-        setShouldCascadeTabsAndCheckTabStrips(true);
     }
 
     /**
-     * Compares tab strip with model after switching between the ScrollingStripStacker and
-     * CascadingStripStacker when the last tab is selected. This also verifies that the strip
-     * scrolls correctly and the correct index is selected after switching.
+     * This verifies that the strip scrolls correctly when last tab is selected.
      */
     @Test
     @LargeTest
     @Restriction(UiRestriction.RESTRICTION_TYPE_TABLET)
     @Feature({"TabStrip"})
-    public void testSwitchStripStackersWithLastTabSelected() throws Exception {
-        // Open enough regular tabs to cause the tabs to cascade or the strip to scroll depending
-        // on which stacker is being used.
+    public void testScrollingStripStackersWithLastTabSelected() throws Exception {
+        // Open enough regular tabs to cause the strip to scroll
         ChromeTabUtils.newTabsFromMenu(
                 InstrumentationRegistry.getInstrumentation(), sActivityTestRule.getActivity(), 20);
 
-        // Switch to the ScrollingStripStacker.
-        setShouldCascadeTabsAndCheckTabStrips(false);
-
-        // Switch to the CascadingStripStacker.
-        setShouldCascadeTabsAndCheckTabStrips(true);
+        // Check scrolling tab strip
+        checkTabStrips();
     }
 
     /**
-     * Compares tab strip with model after switching between the ScrollingStripStacker and
-     * CascadingStripStacker when the first tab is selected. This also verifies that the strip
-     * scrolls correctly and the correct index is selected after switching.
+     * Verifies that the strip scrolls correctly and the correct index is selected.
      */
     @Test
     @LargeTest
     @Restriction(UiRestriction.RESTRICTION_TYPE_TABLET)
     @Feature({"TabStrip"})
-    public void testSwitchStripStackersWithFirstTabSelected() throws Exception {
-        // Open enough regular tabs to cause the tabs to cascade or the strip to scroll depending
-        // on which stacker is being used.
+    @DisabledTest
+    // Disable due to flakiness in scrolling to hide the first tab.
+    public void testScrollingStripStackersWithFirstTabSelected() throws Exception {
+        // Open enough regular tabs to cause the tab strip to scroll.
         ChromeTabUtils.newTabsFromMenu(
                 InstrumentationRegistry.getInstrumentation(), sActivityTestRule.getActivity(), 20);
 
@@ -619,38 +599,50 @@
         // try to tap on it.
         ChromeTabUtils.switchTabInCurrentTabModel(sActivityTestRule.getActivity(), 0);
 
-        // Switch to the ScrollingStripStacker.
-        setShouldCascadeTabsAndCheckTabStrips(false);
+        // Check scrolling tab strip
+        checkTabStrips();
 
-        // Scroll so the first tab is off screen to verify that switching to the
-        // CascadingStripStacker makes it visible again. The selected tab should always be visible
-        // when using the CascadingStripStacker but may not be visible when using the
-        // ScrollingStripStacker.
-        assertSetTabStripScrollOffset(
-                (int) TabStripUtils.getActiveStripLayoutHelper(sActivityTestRule.getActivity())
-                        .getMinimumScrollOffset());
         StripLayoutTab selectedLayoutTab =
                 TabStripUtils.findStripLayoutTab(sActivityTestRule.getActivity(), false,
                         sActivityTestRule.getActivity().getCurrentTabModel().getTabAt(0).getId());
-        assertTabVisibility(false, selectedLayoutTab);
 
-        // Switch to the CascadingStripStacker.
-        setShouldCascadeTabsAndCheckTabStrips(true);
+        // Create visibility callback helper.
+        final CallbackHelper helper = new CallbackHelper();
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            selectedLayoutTab.addObserver(new StripLayoutTab.Observer() {
+                @Override
+                public void onVisibilityChanged(boolean visible) {
+                    // Notify the helper when tab becomes visible.
+                    if (!visible) helper.notifyCalled();
+                }
+            });
+        });
+
+        // Flaky in scrolling to hide the first tab.
+
+        // Scroll so the first tab is off screen and the selected tab may or may not be visible with
+        // the ScrollingStripStacker.
+        assertSetTabStripScrollOffset(
+                (int) TabStripUtils.getActiveStripLayoutHelper(sActivityTestRule.getActivity())
+                        .getMinimumScrollOffset());
+
+        // Tab should now be hidden.
+        helper.waitForCallback(0);
+
+        assertTabVisibility(false, selectedLayoutTab);
     }
 
     /**
-     * Compares tab strip with model after switching between the ScrollingStripStacker and
-     * CascadingStripStacker when a middle tab is selected. This also verifies that the strip
-     * scrolls correctly and the correct index is selected after switching.
+     * Verifies that the strip scrolls correctly and the correct index when a middle tab is
+     * selected.
      */
     @Test
     @LargeTest
     @Restriction(UiRestriction.RESTRICTION_TYPE_TABLET)
     @Feature({"TabStrip"})
     @DisabledTest(message = "crbug.com/1348310")
-    public void testSwitchStripStackersWithMiddleTabSelected() throws Exception {
-        // Open enough regular tabs to cause the tabs to cascade or the strip to scroll depending
-        // on which stacker is being used.
+    public void testScrollingStripStackersWithMiddleTabSelected() throws Exception {
+        // Open enough regular tabs to cause the tab strip to scroll.
         ChromeTabUtils.newTabsFromMenu(
                 InstrumentationRegistry.getInstrumentation(), sActivityTestRule.getActivity(), 10);
 
@@ -658,11 +650,8 @@
         // try to tap on it.
         ChromeTabUtils.switchTabInCurrentTabModel(sActivityTestRule.getActivity(), 5);
 
-        // Switch to the ScrollingStripStacker.
-        setShouldCascadeTabsAndCheckTabStrips(false);
-
-        // Switch to the CascadingStripStacker.
-        setShouldCascadeTabsAndCheckTabStrips(true);
+        // Check scrolling tab strip
+        checkTabStrips();
     }
 
     /**
@@ -675,8 +664,8 @@
     @Restriction(UiRestriction.RESTRICTION_TYPE_TABLET)
     @Feature({"TabStrip"})
     public void testScrollingStripStackerFadeOpacity() throws Exception {
-        // Switch to the ScrollingStripStacker.
-        setShouldCascadeTabsAndCheckTabStrips(false);
+        // Check scrolling tab strip
+        checkTabStrips();
 
         // Open enough regular tabs to cause the strip to scroll.
         ChromeTabUtils.newTabsFromMenu(
@@ -719,8 +708,8 @@
     @Restriction(UiRestriction.RESTRICTION_TYPE_TABLET)
     @Feature({"TabStrip"})
     public void testScrollingStripStackerScrollsToSelectedTab() throws Exception {
-        // Switch to the ScrollingStripStacker.
-        setShouldCascadeTabsAndCheckTabStrips(false);
+        // Check scrolling tab strip
+        checkTabStrips();
 
         // Open enough regular tabs to cause the strip to scroll.
         ChromeTabUtils.newTabsFromMenu(
@@ -761,8 +750,8 @@
     @Feature({"TabStrip"})
     @DisabledTest(message = "crbug.com/1348310")
     public void testScrollingStripStackerTabOffsets() throws Exception {
-        // Switch to the ScrollingStripStacker.
-        setShouldCascadeTabsAndCheckTabStrips(false);
+        // Check scrolling tab strip
+        checkTabStrips();
 
         // Open enough regular tabs to cause the strip to scroll and select the first tab.
         ChromeTabUtils.newTabsFromMenu(
@@ -995,17 +984,9 @@
                         == incognito) {
             Assert.assertTrue("ChromeTab is not in the proper selection state",
                     tabStrip.isForegroundTab(tabView));
-            if (tabStrip.shouldCascadeTabs()) {
-                Assert.assertEquals(
-                        "ChromeTab is not completely visible, but is selected. The selected "
-                                + "tab should be visible when the CascadingStripStacker is in use.",
-                        tabView.getVisiblePercentage(), 1.0f, 0);
-            }
         }
 
-        if (!tabStrip.shouldCascadeTabs()) {
-            assertTabVisibilityForScrollingStripStacker(tabStrip, tabView);
-        }
+        assertTabVisibilityForScrollingStripStacker(tabStrip, tabView);
 
         // TODO(dtrainor): Compare favicon bitmaps?  Only compare a few pixels.
     }
@@ -1061,39 +1042,27 @@
     }
 
     /**
-     * Sets whether the strip should cascade tabs and checks for validity.
-     *
-     * @param shouldCascadeTabs Whether the {@link CascadingStripStacker} should be used. If false,
-     *                          the {@link ScrollingStripStacker} will be used instead.
+     * Check scrolling tab strip validity and auto-scrolling.
+     * @throws ExecutionException
      */
-    private void setShouldCascadeTabsAndCheckTabStrips(final boolean shouldCascadeTabs)
-            throws ExecutionException {
+    private void checkTabStrips() throws ExecutionException {
         TabModel model = sActivityTestRule.getActivity().getCurrentTabModel();
         int selectedTabIndex = model.index();
 
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             TabStripUtils.getStripLayoutHelper(sActivityTestRule.getActivity(), true)
-                    .setShouldCascadeTabs(shouldCascadeTabs);
+                    .updateScrollOffsetLimits();
             TabStripUtils.getStripLayoutHelper(sActivityTestRule.getActivity(), false)
-                    .setShouldCascadeTabs(shouldCascadeTabs);
+                    .updateScrollOffsetLimits();
         });
 
-        // Assert that the correct StripStacker is being used.
-        Assert.assertEquals(shouldCascadeTabs
-                        ? "Expected CascadingStripStacker but was ScrollingStripStacker."
-                        : "Expected ScrollingStripStacker but was CascadingStripStacker.",
-                shouldCascadeTabs,
-                TabStripUtils.getActiveStripLayoutHelper(sActivityTestRule.getActivity())
-                        .shouldCascadeTabs());
-
         // Assert that the same tab is still selected.
         Assert.assertEquals("The correct tab is not selected.", selectedTabIndex, model.index());
 
         // Compare all TabStrips with corresponding TabModels.
         compareAllTabStripsWithModel();
 
-        // The selected tab should always be visible in the CascadingStripStacker and switching to
-        // the ScrollingStripStacker should auto-scroll to make the selected tab visible.
+        // The scrollingStripStacker should auto-scroll to make the selected tab visible.
         StripLayoutTab selectedLayoutTab =
                 TabStripUtils.findStripLayoutTab(sActivityTestRule.getActivity(),
                         model.isIncognito(), model.getTabAt(selectedTabIndex).getId());
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/media/PictureInPictureActivityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/media/PictureInPictureActivityTest.java
index 9643877..0aca051b 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/media/PictureInPictureActivityTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/media/PictureInPictureActivityTest.java
@@ -54,6 +54,7 @@
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.content_public.browser.test.util.WebContentsUtils;
 import org.chromium.media_session.mojom.MediaSessionAction;
+import org.chromium.ui.test.util.DeviceRestriction;
 
 import java.util.ArrayList;
 import java.util.concurrent.Callable;
@@ -65,6 +66,7 @@
 @RunWith(ChromeJUnit4ClassRunner.class)
 @Batch(Batch.PER_CLASS)
 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
+@Restriction(DeviceRestriction.RESTRICTION_TYPE_NON_AUTO)
 @RequiresApi(Build.VERSION_CODES.O)
 public class PictureInPictureActivityTest {
     @Rule
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/ui/SyncErrorMessageTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/ui/SyncErrorMessageTest.java
index 9600de1..ab86dcc 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/ui/SyncErrorMessageTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/ui/SyncErrorMessageTest.java
@@ -59,8 +59,7 @@
  */
 @RunWith(ChromeJUnit4ClassRunner.class)
 @DoNotBatch(reason = "TODO(crbug.com/1168590): SyncTestRule doesn't support batching.")
-@EnableFeatures({ChromeFeatureList.MESSAGES_FOR_ANDROID_INFRASTRUCTURE,
-        ChromeFeatureList.MESSAGES_FOR_ANDROID_SYNC_ERROR})
+@EnableFeatures({ChromeFeatureList.MESSAGES_FOR_ANDROID_INFRASTRUCTURE})
 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
 public class SyncErrorMessageTest {
     @Mock
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/ScrollingStripStackerUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/ScrollingStripStackerUnitTest.java
index 5c13ac9..ec849db 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/ScrollingStripStackerUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/ScrollingStripStackerUnitTest.java
@@ -4,8 +4,6 @@
 
 package org.chromium.chrome.browser.compositor.overlays.strip;
 
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.is;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
@@ -17,8 +15,6 @@
 import org.mockito.MockitoAnnotations;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
-import org.chromium.ui.base.LocalizationUtils;
 
 /** Tests for {@link ScrollingStripStacker}. */
 @RunWith(BaseRobolectricTestRunner.class)
@@ -59,27 +55,10 @@
             draw_x += TAB_WIDTH;
             ideal_x += TAB_WIDTH;
         }
-        setTabStripImprovementFeature(false);
     }
 
     @Test
-    public void testSetTabOffsets() {
-        mTarget.setTabOffsets(2, mInput, 0, 0, 0, 0, 0, 0, false, false, false, CACHED_TAB_WIDTH);
-
-        float expected_x = 0;
-        for (StripLayoutTab tab : mInput) {
-            verify(tab).setDrawX(expected_x);
-            verify(tab).setDrawY(TAB_OFFSET_Y);
-            verify(tab).setVisiblePercentage(1.f);
-            verify(tab).setContentOffsetX(0.f);
-            expected_x += TAB_WIDTH;
-        }
-    }
-
-    @Test
-    public void testSetTabOffsets_withTabStripImprovement() {
-        setTabStripImprovementFeature(true);
-
+    public void testSetTabOffsets_tabNotClosing() {
         mTarget.setTabOffsets(2, mInput, 0, 0, 0, 0, 0, 0, false, false, false, CACHED_TAB_WIDTH);
 
         float expected_x = 0;
@@ -94,9 +73,7 @@
     }
 
     @Test
-    public void testSetTabOffsets_withTabStripImprovement_tabClosing() {
-        setTabStripImprovementFeature(true);
-
+    public void testSetTabOffsets_tabClosing() {
         mTarget.setTabOffsets(
                 2, mInput, 0, 0, 0, 0, 0, STRIP_WIDTH, false, true, false, CACHED_TAB_WIDTH);
 
@@ -110,9 +87,7 @@
     }
 
     @Test
-    public void testSetTabOffsets_withTabStripImprovement_tabCreating() {
-        setTabStripImprovementFeature(true);
-
+    public void testSetTabOffsets_tabCreating() {
         mTarget.setTabOffsets(
                 2, mInput, 0, 0, 0, 0, 0, STRIP_WIDTH, false, false, true, CACHED_TAB_WIDTH);
 
@@ -142,23 +117,4 @@
             }
         }
     }
-
-    @Test
-    public void testComputeNewTabButtonOffset() {
-        float value = mTarget.computeNewTabButtonOffset(mInput, TAB_OVERLAP, STRIP_MARGIN,
-                STRIP_MARGIN, STRIP_WIDTH, BUTTON_WIDTH, 0, CACHED_TAB_WIDTH, true);
-        assertThat("Button offset does not match", value, is(96.5f));
-    }
-
-    @Test
-    public void testComputeNewTabButtonOffsetRTL() {
-        LocalizationUtils.setRtlForTesting(true);
-        float value = mTarget.computeNewTabButtonOffset(mInput, TAB_OVERLAP, STRIP_MARGIN,
-                STRIP_MARGIN, STRIP_WIDTH, BUTTON_WIDTH, 0, CACHED_TAB_WIDTH, true);
-        assertThat("Button offset does not match", value, is(66.5f));
-    }
-
-    private void setTabStripImprovementFeature(boolean value) {
-        ChromeFeatureList.sTabStripImprovements.setForTesting(value);
-    }
 }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperTest.java
index e99514f..8cf8c87 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperTest.java
@@ -57,7 +57,6 @@
 import org.chromium.chrome.browser.layouts.components.VirtualView;
 import org.chromium.chrome.browser.tasks.tab_groups.TabGroupModelFilter;
 import org.chromium.chrome.browser.tasks.tab_management.TabManagementFieldTrial;
-import org.chromium.chrome.browser.tasks.tab_management.TabUiFeatureUtilities;
 import org.chromium.chrome.test.util.browser.Features;
 import org.chromium.ui.base.LocalizationUtils;
 import org.chromium.ui.shadows.ShadowAppCompatResources;
@@ -68,8 +67,9 @@
 /** Tests for {@link StripLayoutHelper}. */
 @RunWith(BaseRobolectricTestRunner.class)
 // clang-format off
-@Features.EnableFeatures({ChromeFeatureList.TAB_STRIP_IMPROVEMENTS,
-        ChromeFeatureList.TAB_STRIP_REDESIGN, ChromeFeatureList.TAB_GROUPS_FOR_TABLETS})
+@Features.EnableFeatures({
+        ChromeFeatureList.TAB_STRIP_REDESIGN,
+        ChromeFeatureList.TAB_GROUPS_FOR_TABLETS})
 @Config(manifest = Config.NONE, qualifiers = "sw600dp", shadows = {ShadowAppCompatResources.class})
 
 public class StripLayoutHelperTest {
@@ -107,7 +107,7 @@
     private static final float TAB_WIDTH_SMALL = 108.f;
     private static final float TAB_OVERLAP_WIDTH = 28.f;
     private static final float TAB_WIDTH_MEDIUM = 156.f;
-    private static final float TAB_MARGIN_WIDTH = 95.f;
+    private static final float TAB_MARGIN_WIDTH = 54.f;
     private static final long TIMESTAMP = 5000;
     private static final float NEW_TAB_BTN_X = 700.f;
     private static final float NEW_TAB_BTN_Y = 1400.f;
@@ -130,7 +130,6 @@
 
         mActivity = Robolectric.setupActivity(Activity.class);
         mActivity.setTheme(org.chromium.chrome.R.style.Theme_BrowserUI);
-        TabUiFeatureUtilities.setTabMinWidthForTesting(190.f);
     }
 
     @After
@@ -139,8 +138,6 @@
             mStripLayoutHelper.stopReorderModeForTesting();
             mStripLayoutHelper.setTabAtPositionForTesting(null);
         }
-
-        TabUiFeatureUtilities.setTabMinWidthForTesting(null);
     }
 
     /**
@@ -151,7 +148,6 @@
     @Test
     @Feature({"Accessibility"})
     public void testSimpleTabOrder() {
-        TabUiFeatureUtilities.setTabMinWidthForTesting(TAB_WIDTH_MEDIUM);
         initializeTest(false, false, 0);
         mStripLayoutHelper.onSizeChanged(SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP);
         mStripLayoutHelper.updateLayout(TIMESTAMP);
@@ -168,7 +164,6 @@
     @Test
     @Feature({"Accessibility"})
     public void testTabOrderWithIndex() {
-        TabUiFeatureUtilities.setTabMinWidthForTesting(TAB_WIDTH_SMALL);
         initializeTest(false, false, 1);
         mStripLayoutHelper.onSizeChanged(SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP);
         mStripLayoutHelper.updateLayout(TIMESTAMP);
@@ -185,7 +180,6 @@
     @Test
     @Feature({"Accessibility"})
     public void testTabOrderRtl() {
-        TabUiFeatureUtilities.setTabMinWidthForTesting(TAB_WIDTH_SMALL);
         initializeTest(true, false, 0);
         mStripLayoutHelper.onSizeChanged(SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP);
         mStripLayoutHelper.updateLayout(TIMESTAMP);
@@ -203,7 +197,6 @@
     @Test
     @Feature({"Accessibility"})
     public void testIncognitoAccessibilityDescriptions() {
-        TabUiFeatureUtilities.setTabMinWidthForTesting(TAB_WIDTH_SMALL);
         initializeTest(false, true, 0);
         mStripLayoutHelper.onSizeChanged(SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP);
         mStripLayoutHelper.updateLayout(TIMESTAMP);
@@ -212,27 +205,6 @@
     }
 
     @Test
-    @Feature("Tab Strip Improvements")
-    @Config(qualifiers = "sw800dp")
-    public void testStripStacker_TabStripImprovementsEnabled_Scroll() {
-        initializeTest(false, true, 0);
-
-        // Assert
-        assertFalse(mStripLayoutHelper.shouldCascadeTabs());
-    }
-
-    @Test
-    @Feature("Tab Strip Improvements")
-    @Config(qualifiers = "sw800dp")
-    @Features.DisableFeatures(ChromeFeatureList.TAB_STRIP_IMPROVEMENTS)
-    public void testStripStacker_TabStripImprovementsDisabled_Cascade() {
-        initializeTest(false, true, 0);
-
-        // Assert
-        assertTrue(mStripLayoutHelper.shouldCascadeTabs());
-    }
-
-    @Test
     public void testAllTabsClosed() {
         initializeTest(false, false, 0);
         assertTrue(mStripLayoutHelper.getStripLayoutTabs().length == TEST_TAB_TITLES.length);
@@ -248,10 +220,8 @@
     }
 
     @Test
-    @Feature("Tab Strip Improvements")
-    public void testStripStacker_UpdateCloseButtons() {
-        // Set fourth tab as selected
-        TabUiFeatureUtilities.setTabMinWidthForTesting(TAB_WIDTH_SMALL);
+    // Test show selected tab(non-last tab) close button.
+    public void testTabSelected_SelectedTab_NonLastTab_ShowCloseBtn() {
         initializeTest(false, true, 3);
         mStripLayoutHelper.onSizeChanged(SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP);
         StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_1);
@@ -259,9 +229,9 @@
 
         mStripLayoutHelper.tabSelected(1, 3, 0, false);
 
-        // Close btn should be visible on the selected tab.
+        // Close btn is visible on the selected tab.
         Mockito.verify(tabs[3]).setCanShowCloseButton(true, false);
-        // Close btn is hidden on unselected tabs.
+        // Close btn is hidden for the rest of tabs.
         Mockito.verify(tabs[0]).setCanShowCloseButton(false, false);
         Mockito.verify(tabs[1]).setCanShowCloseButton(false, false);
         Mockito.verify(tabs[2]).setCanShowCloseButton(false, false);
@@ -269,10 +239,9 @@
     }
 
     @Test
-    @Feature("Tab Strip Improvements")
-    public void testTabSelected_SelectedTab_EdgeTab_HideCloseBtn() {
+    // Test hide selected tab(non-last tab) close button.
+    public void testTabSelected_SelectedTab_NonLastTab_HideCloseBtn() {
         // Set fourth tab as selected
-        TabUiFeatureUtilities.setTabMinWidthForTesting(TAB_WIDTH_SMALL);
         initializeTest(false, true, 3);
         mStripLayoutHelper.onSizeChanged(SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP);
         StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_1);
@@ -281,9 +250,9 @@
 
         mStripLayoutHelper.tabSelected(1, 3, 0, false);
 
-        // Close btn should be hidden on the selected tab as its an edge tab.
+        // Close btn is hidden on the selected tab.
         Mockito.verify(tabs[3]).setCanShowCloseButton(false, false);
-        // Close btn is hidden on unselected tabs.
+        // Close btn is hidden for the rest of tabs as well.
         Mockito.verify(tabs[0]).setCanShowCloseButton(false, false);
         Mockito.verify(tabs[1]).setCanShowCloseButton(false, false);
         Mockito.verify(tabs[2]).setCanShowCloseButton(false, false);
@@ -291,125 +260,66 @@
     }
 
     @Test
-    @Feature("Tab Strip Improvements")
-    public void testTabSelected_EdgeTab_Start_Ltr_HideCloseBtn() {
-        // Arrange
-        TabUiFeatureUtilities.setTabMinWidthForTesting(TAB_WIDTH_MEDIUM);
-        initializeTest(false, true, 3);
-        StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_2);
+    // Test show selected tab(last tab) close button.
+    public void testTabSelected_SelectedTab_LastTab_ShowCloseBtn() {
+        initializeTest(false, true, 4);
+        StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_1);
         // Set mWidth value to 800.f
         mStripLayoutHelper.onSizeChanged(SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP);
         mStripLayoutHelper.getNewTabButton().setX(600.f);
-        // The leftmost tab is partially hidden
-        when(tabs[0].getDrawX()).thenReturn(-80.f);
         mStripLayoutHelper.setStripLayoutTabsForTest(tabs);
 
         // Act
-        mStripLayoutHelper.tabSelected(1, 3, 0, false);
+        mStripLayoutHelper.tabSelected(1, 4, 0, false);
 
         // Assert
-        // Close btn should be hidden for the partially visible edge tab.
+        // Close btn is visible on the selected last tab.
+        Mockito.verify(tabs[4]).setCanShowCloseButton(true, false);
+        // Close button is hidden for the rest of tabs.
         Mockito.verify(tabs[0]).setCanShowCloseButton(false, false);
-        // Close button is visible for the rest of the tabs.
-        Mockito.verify(tabs[1]).setCanShowCloseButton(true, false);
-        Mockito.verify(tabs[2]).setCanShowCloseButton(true, false);
-        Mockito.verify(tabs[3]).setCanShowCloseButton(true, false);
-        Mockito.verify(tabs[4]).setCanShowCloseButton(true, false);
-    }
-
-    @Test
-    @Feature("Tab Strip Improvements")
-    public void testTabSelected_EdgeTab_End_Ltr_HideCloseBtn() {
-        // Arrange
-        TabUiFeatureUtilities.setTabMinWidthForTesting(TAB_WIDTH_MEDIUM);
-        initializeTest(false, false, 3);
-        StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_2);
-        // Set mWidth value to 800.f
-        mStripLayoutHelper.onSizeChanged(SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP);
-        mStripLayoutHelper.getNewTabButton().setX(700);
-        float drawXToHideCloseBtn = SCREEN_WIDTH - CLOSE_BTN_VISIBILITY_THRESHOLD_END_MODEL_SELECTOR
-                - TAB_WIDTH_2 + TAB_OVERLAP_WIDTH + 1; // 1 is to cross threshold
-        when(tabs[3].getDrawX()).thenReturn(drawXToHideCloseBtn);
-        mStripLayoutHelper.setStripLayoutTabsForTest(tabs);
-
-        // Act
-        mStripLayoutHelper.tabSelected(1, 3, 0, false);
-
-        // Assert
-        // Close button is visible for the rest of the tabs.
-        Mockito.verify(tabs[0]).setCanShowCloseButton(true, false);
-        Mockito.verify(tabs[1]).setCanShowCloseButton(true, false);
-        Mockito.verify(tabs[2]).setCanShowCloseButton(true, false);
-        Mockito.verify(tabs[4]).setCanShowCloseButton(true, false);
-        // Close btn should be hidden for the partially visible edge tab.
+        Mockito.verify(tabs[1]).setCanShowCloseButton(false, false);
+        Mockito.verify(tabs[2]).setCanShowCloseButton(false, false);
         Mockito.verify(tabs[3]).setCanShowCloseButton(false, false);
     }
 
     @Test
-    @Feature("Tab Strip Improvements")
-    public void testTabSelected_LastTab_ShowCloseBtn() {
-        TabUiFeatureUtilities.setTabMinWidthForTesting(TAB_WIDTH_MEDIUM);
-        initializeTest(false, false, 3);
-        StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_2);
+    // Test hide selected tab(last tab) close button.
+    public void testTabSelected_SelectedTab_LastTab_HideCloseBtn() {
+        initializeTest(false, true, 4);
+        StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_1);
         // Set mWidth value to 800.f
         mStripLayoutHelper.onSizeChanged(SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP);
         mStripLayoutHelper.getNewTabButton().setX(NEW_TAB_BTN_X);
-        // newTabBtn.X(700.f) - tab.width(160.f) + mTabOverlapWidth(24.f)
-        when(tabs[4].getDrawX()).thenReturn(564.f);
+        // mNewTabButton.getX(700.f) + TabOverlapWidth(28.f) - tab.getWidth(140) + 1 to cross
+        // threshold
+        when(tabs[4].getDrawX()).thenReturn(589.f);
         mStripLayoutHelper.setStripLayoutTabsForTest(tabs);
 
         // Act
-        mStripLayoutHelper.tabSelected(1, 3, 0, false);
+        mStripLayoutHelper.tabSelected(1, 4, 0, false);
 
         // Assert
-        // Close button is visible for all tabs.
-        Mockito.verify(tabs[0]).setCanShowCloseButton(true, false);
-        Mockito.verify(tabs[1]).setCanShowCloseButton(true, false);
-        Mockito.verify(tabs[2]).setCanShowCloseButton(true, false);
-        Mockito.verify(tabs[3]).setCanShowCloseButton(true, false);
-        Mockito.verify(tabs[4]).setCanShowCloseButton(true, false);
-    }
-
-    @Test
-    @Feature("Tab Strip Improvements")
-    public void testTabSelected_LastTab_EdgeTab_HideCloseBtn() {
-        TabUiFeatureUtilities.setTabMinWidthForTesting(TAB_WIDTH_MEDIUM);
-        initializeTest(false, false, 3);
-        StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_2);
-
-        // Set mWidth value to 800.f
-        mStripLayoutHelper.onSizeChanged(SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP);
-        mStripLayoutHelper.getNewTabButton().setX(NEW_TAB_BTN_X);
-        // newTabBtn.X(700.f) - tab.width(160.f) + mTabOverlapWidth(28.f) + 1
-        when(tabs[4].getDrawX()).thenReturn(569.f);
-        mStripLayoutHelper.setStripLayoutTabsForTest(tabs);
-
-        // Act
-        mStripLayoutHelper.tabSelected(1, 3, 0, false);
-
-        // Assert
-        // Close btn should be visible for rest of the tabs.
-        Mockito.verify(tabs[0]).setCanShowCloseButton(true, false);
-        Mockito.verify(tabs[1]).setCanShowCloseButton(true, false);
-        Mockito.verify(tabs[2]).setCanShowCloseButton(true, false);
-        Mockito.verify(tabs[3]).setCanShowCloseButton(true, false);
-        // Close btn should be hidden for the partially visible edge tab.
+        // Close btn is hidden on the selected last tab.
         Mockito.verify(tabs[4]).setCanShowCloseButton(false, false);
+        // Close button is hidden for the rest of tabs.
+        Mockito.verify(tabs[0]).setCanShowCloseButton(false, false);
+        Mockito.verify(tabs[1]).setCanShowCloseButton(false, false);
+        Mockito.verify(tabs[2]).setCanShowCloseButton(false, false);
+        Mockito.verify(tabs[3]).setCanShowCloseButton(false, false);
     }
 
     @Test
-    @Feature("Tab Strip Improvements")
-    public void testTabSelected_EdgeTab_End_Ltr_NoModelSelBtn_HideCloseBtn() {
+    // Test hide selected tab(non-last tab) close button when there is no model selector button.
+    public void testTabSelected_NonLastTab_Ltr_NoModelSelBtn_HideCloseBtn() {
         // Arrange
-        TabUiFeatureUtilities.setTabMinWidthForTesting(TAB_WIDTH_MEDIUM);
         when(mModelSelectorBtn.isVisible()).thenReturn(false);
 
         initializeTest(false, false, 3);
-        StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_2);
+        StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_1);
         // Set mWidth value to 800.f
         mStripLayoutHelper.onSizeChanged(SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP);
-        mStripLayoutHelper.getNewTabButton().setX(700);
-        float drawXToHideCloseBtn = SCREEN_WIDTH - CLOSE_BTN_VISIBILITY_THRESHOLD_END - TAB_WIDTH_2
+        mStripLayoutHelper.getNewTabButton().setX(NEW_TAB_BTN_X);
+        float drawXToHideCloseBtn = SCREEN_WIDTH - CLOSE_BTN_VISIBILITY_THRESHOLD_END - TAB_WIDTH_1
                 + TAB_OVERLAP_WIDTH + 1; // 1 is to cross threshold
         when(tabs[3].getDrawX()).thenReturn(drawXToHideCloseBtn);
         mStripLayoutHelper.setStripLayoutTabsForTest(tabs);
@@ -418,65 +328,135 @@
         mStripLayoutHelper.tabSelected(1, 3, 0, false);
 
         // Assert
-        // Close button is visible for the rest of the tabs.
-        Mockito.verify(tabs[0]).setCanShowCloseButton(true, false);
-        Mockito.verify(tabs[1]).setCanShowCloseButton(true, false);
-        Mockito.verify(tabs[2]).setCanShowCloseButton(true, false);
-        Mockito.verify(tabs[4]).setCanShowCloseButton(true, false);
-        // Close btn should be hidden for the partially visible edge tab.
+        // Close button is hidden for selected tab.
+        Mockito.verify(tabs[3]).setCanShowCloseButton(false, false);
+        // Close button is hidden for the rest of tabs as well.
+        Mockito.verify(tabs[0]).setCanShowCloseButton(false, false);
+        Mockito.verify(tabs[1]).setCanShowCloseButton(false, false);
+        Mockito.verify(tabs[2]).setCanShowCloseButton(false, false);
+        Mockito.verify(tabs[4]).setCanShowCloseButton(false, false);
+    }
+
+    @Test
+    // Test show selected tab(non-last tab) close button when there is no model selector button.
+    public void testTabSelected_NonLastTab_Ltr_NoModelSelBtn_ShowCloseBtn() {
+        // Arrange
+        when(mModelSelectorBtn.isVisible()).thenReturn(false);
+
+        initializeTest(false, false, 3);
+        StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_1);
+        // Set mWidth value to 800.f
+        mStripLayoutHelper.onSizeChanged(SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP);
+        mStripLayoutHelper.getNewTabButton().setX(NEW_TAB_BTN_X);
+        mStripLayoutHelper.setStripLayoutTabsForTest(tabs);
+
+        // Act
+        mStripLayoutHelper.tabSelected(1, 3, 0, false);
+
+        // Assert
+        // Close button is visible for selected tab
+        Mockito.verify(tabs[3]).setCanShowCloseButton(true, false);
+        // Close button is hidden for the rest of tabs.
+        Mockito.verify(tabs[0]).setCanShowCloseButton(false, false);
+        Mockito.verify(tabs[1]).setCanShowCloseButton(false, false);
+        Mockito.verify(tabs[2]).setCanShowCloseButton(false, false);
+        Mockito.verify(tabs[4]).setCanShowCloseButton(false, false);
+    }
+
+    @Test
+    // Test hide selected tab(last tab) close button when RTL.
+    public void testTabSelected_SelectedTab_LastTab_Rtl_HideCloseBtn() {
+        initializeTest(true, false, 4);
+        StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_1);
+        // Set mWidth value to 800.f
+        mStripLayoutHelper.onSizeChanged(SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP);
+        mStripLayoutHelper.getNewTabButton().setX(NEW_TAB_BTN_X);
+        mStripLayoutHelper.setStripLayoutTabsForTest(tabs);
+
+        // Act
+        mStripLayoutHelper.tabSelected(1, 4, 0, false);
+
+        // Assert
+        // Close button is hidden for the selected last tab.
+        Mockito.verify(tabs[4]).setCanShowCloseButton(false, false);
+        // Close button is hidden for the rest of tabs as well.
+        Mockito.verify(tabs[0]).setCanShowCloseButton(false, false);
+        Mockito.verify(tabs[1]).setCanShowCloseButton(false, false);
+        Mockito.verify(tabs[2]).setCanShowCloseButton(false, false);
         Mockito.verify(tabs[3]).setCanShowCloseButton(false, false);
     }
 
     @Test
-    @Feature("Tab Strip Improvements")
-    public void testTabSelected_EdgeTab_Start_Rtl_HideCloseBtn() {
-        // Arrange
-        TabUiFeatureUtilities.setTabMinWidthForTesting(TAB_WIDTH_MEDIUM);
-        initializeTest(true, true, 3);
-        StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_2, 150.f, 5);
-        // Set mWidth value to 800.f.
+    // Test show selected tab(last tab) close button when RTL.
+    public void testTabSelected_SelectedTab_LastTab_Rtl_ShowCloseBtn() {
+        initializeTest(true, false, 4);
+        StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_1);
+        // Set mWidth value to 800.f
         mStripLayoutHelper.onSizeChanged(SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP);
-        // The leftmost tab is partially hidden.
-        when(tabs[0].getDrawX()).thenReturn(60.f);
+        mStripLayoutHelper.getNewTabButton().setX(600.f);
+        // newTabBtn.X(600.f) + newTabBtn.width(38.f) - mTabOverlapWidth(28.f) + 1
+        when(tabs[4].getDrawX()).thenReturn(611.f);
         mStripLayoutHelper.setStripLayoutTabsForTest(tabs);
 
         // Act
-        mStripLayoutHelper.tabSelected(1, 3, 0, false);
+        mStripLayoutHelper.tabSelected(1, 4, 0, false);
 
-        // Assert
-        // Close btn should be hidden for the partially visible edge tab.
-        Mockito.verify(tabs[0]).setCanShowCloseButton(false, false);
-        // Close button is visible for the rest of the tabs.
-        Mockito.verify(tabs[1]).setCanShowCloseButton(true, false);
-        Mockito.verify(tabs[2]).setCanShowCloseButton(true, false);
-        Mockito.verify(tabs[3]).setCanShowCloseButton(true, false);
+        // Close button is visible for selected last tab.
         Mockito.verify(tabs[4]).setCanShowCloseButton(true, false);
+        // Assert
+        // Close button is hidden for the rest of tabs.
+        Mockito.verify(tabs[0]).setCanShowCloseButton(false, false);
+        Mockito.verify(tabs[1]).setCanShowCloseButton(false, false);
+        Mockito.verify(tabs[2]).setCanShowCloseButton(false, false);
+        Mockito.verify(tabs[3]).setCanShowCloseButton(false, false);
     }
 
     @Test
-    @Feature("Tab Strip Improvements")
-    public void testTabSelected_EdgeTab_End_Rtl_HideCloseBtn() {
-        // Arrange
-        TabUiFeatureUtilities.setTabMinWidthForTesting(TAB_WIDTH_MEDIUM);
-        initializeTest(true, true, 3);
-        StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_2, 150.f, 5);
+    // Test hide selected tab(non-last tab) close button when RTL.
+    public void testTabSelected_SelectedTab_NonLastTab_Rtl_HideCloseBtn() {
+        initializeTest(true, false, 3);
+        StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_1);
+
         // Set mWidth value to 800.f
         mStripLayoutHelper.onSizeChanged(SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP);
-        // To make rightmost tab partially hidden value should be gt than (SCREEN_WIDTH - 120.f -
-        // TAB_WIDTH_2).
-        when(tabs[4].getDrawX()).thenReturn(710.f);
+        mStripLayoutHelper.getNewTabButton().setX(NEW_TAB_BTN_X);
         mStripLayoutHelper.setStripLayoutTabsForTest(tabs);
 
         // Act
         mStripLayoutHelper.tabSelected(1, 3, 0, false);
 
         // Assert
-        // Close button is visible for the rest of the tabs.
-        Mockito.verify(tabs[0]).setCanShowCloseButton(true, false);
-        Mockito.verify(tabs[1]).setCanShowCloseButton(true, false);
-        Mockito.verify(tabs[2]).setCanShowCloseButton(true, false);
+        // Close btn is hidden for selected tab.
+        Mockito.verify(tabs[3]).setCanShowCloseButton(false, false);
+        // Close btn is hidden for all the rest of tabs as well.
+        Mockito.verify(tabs[0]).setCanShowCloseButton(false, false);
+        Mockito.verify(tabs[1]).setCanShowCloseButton(false, false);
+        Mockito.verify(tabs[2]).setCanShowCloseButton(false, false);
+        Mockito.verify(tabs[4]).setCanShowCloseButton(false, false);
+    }
+
+    @Test
+    // Test show selected tab(non-last tab) close button when RTL.
+    public void testTabSelected_SelectedTab_NonLastTab_Rtl_ShowCloseBtn() {
+        initializeTest(true, false, 3);
+        StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_1);
+        // Set mWidth value to 800.f.
+        mStripLayoutHelper.onSizeChanged(SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP);
+        mStripLayoutHelper.getNewTabButton().setX(NEW_TAB_BTN_X);
+        // getCloseBtnVisibilityThreshold(120.f) - mTabOverlapWidth(28.f) + 1
+        when(tabs[3].getDrawX()).thenReturn(93.f);
+        mStripLayoutHelper.setStripLayoutTabsForTest(tabs);
+
+        // Act
+        mStripLayoutHelper.tabSelected(1, 3, 0, false);
+
+        // Assert
+        // Close button is visible for the selected tab.
         Mockito.verify(tabs[3]).setCanShowCloseButton(true, false);
-        // Close btn should be hidden for the partially visible edge tab.
+        // Close button is hidden for the rest of tabs.
+        Mockito.verify(tabs[0]).setCanShowCloseButton(false, false);
+        Mockito.verify(tabs[1]).setCanShowCloseButton(false, false);
+        Mockito.verify(tabs[2]).setCanShowCloseButton(false, false);
         Mockito.verify(tabs[4]).setCanShowCloseButton(false, false);
     }
 
@@ -599,7 +579,6 @@
         // Setup
         TabManagementFieldTrial.TAB_STRIP_REDESIGN_ENABLE_FOLIO.setForTesting(true);
         int tabCount = 4;
-        TabUiFeatureUtilities.setTabMinWidthForTesting(TAB_WIDTH_MEDIUM);
         initializeTest(false, false, false, 3, tabCount);
         mStripLayoutHelper.onSizeChanged(SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP);
 
@@ -617,7 +596,6 @@
         // Setup
         TabManagementFieldTrial.TAB_STRIP_REDESIGN_ENABLE_FOLIO.setForTesting(true);
         int tabCount = 4;
-        TabUiFeatureUtilities.setTabMinWidthForTesting(TAB_WIDTH_MEDIUM);
         initializeTest(true, false, false, 3, tabCount);
 
         // Set New tab button position.
@@ -634,7 +612,6 @@
         // Setup
         TabManagementFieldTrial.TAB_STRIP_REDESIGN_ENABLE_FOLIO.setForTesting(true);
         int tabCount = 4;
-        TabUiFeatureUtilities.setTabMinWidthForTesting(TAB_WIDTH_MEDIUM);
         when(mModelSelectorBtn.getWidth()).thenReturn(MODEL_SELECTOR_BUTTON_BG_WIDTH_FOLIO);
         initializeTest(false, true, false, 3, tabCount);
         mStripLayoutHelper.onSizeChanged(SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP);
@@ -655,7 +632,6 @@
         // Setup
         int tabCount = 4;
         TabManagementFieldTrial.TAB_STRIP_REDESIGN_ENABLE_FOLIO.setForTesting(true);
-        TabUiFeatureUtilities.setTabMinWidthForTesting(TAB_WIDTH_MEDIUM);
         initializeTest(true, true, false, 3, tabCount);
         when(mModelSelectorBtn.getWidth()).thenReturn(MODEL_SELECTOR_BUTTON_BG_WIDTH_FOLIO);
         mStripLayoutHelper.onSizeChanged(SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP);
@@ -671,11 +647,9 @@
     }
 
     @Test
-    @Feature("Tab Strip Improvements")
     public void testScrollOffset_OnResume_StartOnLeft_SelectedRightmostTab() {
         // Arrange: Initialize tabs with last tab selected.
         initializeTest(false, true, false, 9, 10);
-        TabUiFeatureUtilities.setTabMinWidthForTesting(TAB_WIDTH_MEDIUM);
         StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_MEDIUM, 150.f, 10);
         mStripLayoutHelper.setStripLayoutTabsForTest(tabs);
 
@@ -684,18 +658,16 @@
 
         mStripLayoutHelper.scrollTabToView(TIMESTAMP, false);
 
-        int expectedFinalX = -968; // delta(optimalRight(-940) - scrollOffset(0)
+        int expectedFinalX = -148; // delta(optimalRight(-120) - scrollOffset(0)
                                    // - tabOverlapWidth(28)) + scrollOffset(0)
         assertEquals(expectedFinalX, mStripLayoutHelper.getScroller().getFinalX());
     }
 
     @Test
-    @Feature("Tab Strip Improvements")
     public void testScrollOffset_OnResume_StartOnLeft_NoModelSelBtn_SelectedRightmostTab() {
         // Arrange: Initialize tabs with last tab selected.
         when(mModelSelectorBtn.isVisible()).thenReturn(false);
         initializeTest(false, true, false, 9, 10);
-        TabUiFeatureUtilities.setTabMinWidthForTesting(TAB_WIDTH_MEDIUM);
         StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_MEDIUM, 150.f, 10);
         mStripLayoutHelper.setStripLayoutTabsForTest(tabs);
 
@@ -704,17 +676,16 @@
 
         mStripLayoutHelper.scrollTabToView(TIMESTAMP, false);
 
-        int expectedFinalX = -920; // delta(optimalRight(-892) - scrollOffset(0)
+        System.out.println();
+        int expectedFinalX = -100; // delta(optimalRight(-72) - scrollOffset(0)
                                    // - tabOverlapWidth(28)) + scrollOffset(0)
         assertEquals(expectedFinalX, mStripLayoutHelper.getScroller().getFinalX());
     }
 
     @Test
-    @Feature("Tab Strip Improvements")
     public void testScrollOffset_OnResume_StartOnRight_SelectedLeftmostTab() {
         // Arrange: Initialize tabs with first tab selected.
         initializeTest(false, true, false, 0, 10);
-        TabUiFeatureUtilities.setTabMinWidthForTesting(TAB_WIDTH_MEDIUM);
         StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_MEDIUM, 150.f, 10);
         mStripLayoutHelper.setStripLayoutTabsForTest(tabs);
         mStripLayoutHelper.testSetScrollOffset(-1200);
@@ -729,12 +700,10 @@
     }
 
     @Test
-    @Feature("Tab Strip Improvements")
     public void testScrollOffset_OnResume_StartOnRight_NoModelSelBtn_SelectedRightmostTab() {
         // Arrange: Initialize tabs with first tab selected.
         when(mModelSelectorBtn.isVisible()).thenReturn(false);
         initializeTest(false, true, false, 0, 10);
-        TabUiFeatureUtilities.setTabMinWidthForTesting(TAB_WIDTH_MEDIUM);
         StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_MEDIUM, 150.f, 10);
         mStripLayoutHelper.setStripLayoutTabsForTest(tabs);
         mStripLayoutHelper.testSetScrollOffset(-1200);
@@ -749,12 +718,10 @@
     }
 
     @Test
-    @Feature("Tab Strip Improvements")
     public void testScrollOffset_OnOrientationChange_SelectedTabVisible() {
         // Arrange: Initialize tabs with last tab selected.
         when(mModelSelectorBtn.isVisible()).thenReturn(false);
         initializeTest(false, true, false, 9, 10);
-        TabUiFeatureUtilities.setTabMinWidthForTesting(TAB_WIDTH_MEDIUM);
         StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_MEDIUM, 150.f, 10);
         when(tabs[9].isVisible()).thenReturn(true);
         mStripLayoutHelper.setStripLayoutTabsForTest(tabs);
@@ -764,7 +731,7 @@
         mStripLayoutHelper.onSizeChanged(SCREEN_WIDTH_LANDSCAPE, SCREEN_HEIGHT, false, TIMESTAMP);
 
         // Assert: finalX value before orientation change.
-        int initialFinalX = -1458;
+        int initialFinalX = -720;
         assertEquals(initialFinalX, mStripLayoutHelper.getScroller().getFinalX());
 
         // Act: change orientation.
@@ -772,18 +739,16 @@
         mStripLayoutHelper.onSizeChanged(SCREEN_WIDTH, SCREEN_HEIGHT, true, TIMESTAMP);
 
         // Assert: finalX value after orientation change.
-        int expectedFinalX = -920; // delta(optimalRight(-892) - tabOverlapWidth(28)) -
+        int expectedFinalX = -100; // delta(optimalRight(-72) - tabOverlapWidth(28)) -
                                    // scrollOffset(1000) + scrollOffset(1000)
         assertEquals(expectedFinalX, mStripLayoutHelper.getScroller().getFinalX());
     }
 
     @Test
-    @Feature("Tab Strip Improvements")
     public void testScrollOffset_OnOrientationChange_SelectedTabNotVisible() {
         // Arrange: Initialize tabs with last tab selected.
         when(mModelSelectorBtn.isVisible()).thenReturn(false);
         initializeTest(false, true, false, 9, 10);
-        TabUiFeatureUtilities.setTabMinWidthForTesting(TAB_WIDTH_MEDIUM);
         StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_MEDIUM, 150.f, 10);
         when(tabs[9].isVisible()).thenReturn(false);
         mStripLayoutHelper.setStripLayoutTabsForTest(tabs);
@@ -793,7 +758,7 @@
         mStripLayoutHelper.onSizeChanged(SCREEN_WIDTH_LANDSCAPE, SCREEN_HEIGHT, false, TIMESTAMP);
 
         // Assert: finalX value before orientation change.
-        int initialFinalX = -1458;
+        int initialFinalX = -720;
         assertEquals(initialFinalX, mStripLayoutHelper.getScroller().getFinalX());
 
         // Act: change orientation.
@@ -805,7 +770,6 @@
     }
 
     @Test
-    @Feature("Tab Strip Improvements")
     public void testTabSelected_AfterTabClose_SkipsAutoScroll() {
         initializeTest(false, true, 3);
         StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_MEDIUM);
@@ -821,7 +785,6 @@
     }
 
     @Test
-    @Feature("Tab Strip Improvements")
     public void testTabSelected_AfterSelectedTabClose_SkipsAutoScroll() {
         initializeTest(false, true, 3);
         StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_MEDIUM);
@@ -854,7 +817,6 @@
     }
 
     @Test
-    @Feature("Tab Strip Improvements")
     public void testTabCreated_RestoredTab_SkipsAutoscroll() {
         initializeTest(false, true, 3);
         StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_MEDIUM);
@@ -871,7 +833,6 @@
     }
 
     @Test
-    @Feature("Tab Strip Improvements")
     public void testTabCreated_NonRestoredTab_SkipsAutoscroll() {
         initializeTest(false, true, 3);
         StripLayoutTab[] tabs = getMockedStripLayoutTabs(TAB_WIDTH_MEDIUM);
@@ -888,9 +849,8 @@
     }
 
     @Test
-    @Feature("Tab Strip Improvements")
     public void testTabCreated_BringSelectedTabToVisibleArea_StartupRestoredUnselectedTab() {
-        initializeTest(false, false, true, 1, 10);
+        initializeTest(false, false, true, 1, 11);
         mStripLayoutHelper.onSizeChanged(SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP);
         // Set initial scroller position to -500.
         mStripLayoutHelper.testSetScrollOffset(-500);
@@ -899,17 +859,16 @@
         // Act: Tab was restored during startup.
         boolean selected = false;
         boolean onStartup = true;
-        mStripLayoutHelper.tabCreated(TIMESTAMP, 11, 11, selected, false, onStartup);
+        mStripLayoutHelper.tabCreated(TIMESTAMP, 12, 12, selected, false, onStartup);
 
         // Assert: We don't scroll to the created tab. The selected tab is not already visible, so
-        // we scroll to it. Offset = -(1 tab width) = -162.
-        float expectedOffset = -162f;
+        // we scroll to it. Offset = -(1 tab width) = -80.
+        float expectedOffset = -80f;
         assertEquals("We should scroll to the selected tab", expectedOffset,
                 mStripLayoutHelper.getScrollOffset(), EPSILON);
     }
 
     @Test
-    @Feature("Tab Strip Improvements")
     public void testScrollDuration() {
         initializeTest(false, true, 3);
 
@@ -921,9 +880,8 @@
     }
 
     @Test
-    @Feature("Tab Strip Improvements")
     public void testScrollDuration_Medium() {
-        initializeTest(false, true, false, 3, 10);
+        initializeTest(false, true, false, 3, 12);
 
         // Act: Set scroll offset between -960 and -1920.
         mStripLayoutHelper.testSetScrollOffset(-1000);
@@ -933,9 +891,8 @@
     }
 
     @Test
-    @Feature("Tab Strip Improvements")
     public void testScrollDuration_Large() {
-        initializeTest(false, true, false, 3, 15);
+        initializeTest(false, true, false, 3, 24);
 
         // Act: Set scroll offset less than -1920
         mStripLayoutHelper.testSetScrollOffset(-2000);
@@ -1345,8 +1302,9 @@
         mStripLayoutHelper.testSetScrollOffset(0);
 
         // Start reorder on tab to the right of groups. 2 margins to left of tab, so should scroll.
-        // Verify the scroll offset is 2 * (-marginWidth) + startMargin = 2 * -95 + -95 = -285
-        float expectedOffset = -285f;
+        // Verify the scroll offset is 2 * (-marginWidth) + startMargin = 2 * -54 + -54 = -162
+        // marginWidth is half of 0.5 * minTabWidth = 108 / 2 = 54.
+        float expectedOffset = -162f;
         mStripLayoutHelper.startReorderModeAtIndexForTesting(4);
         assertEquals("There are margins left of the selected tab, so we should scroll.",
                 expectedOffset, mStripLayoutHelper.getScrollOffset(), EPSILON);
@@ -1374,8 +1332,9 @@
                 mStripLayoutHelper.getScrollOffset(), EPSILON);
 
         // Finish animations.
-        // Verify the scroll offset is 2 * (-marginWidth) + startMargin = 2 * -95 + -95 = -285
-        float expectedOffset = -285f;
+        // Verify the scroll offset is 2 * (-marginWidth) + startMargin = 2 * -54 + -54 = -162
+        // marginWidth is half of 0.5 * minTabWidth = 108 / 2 = 54.
+        float expectedOffset = -162f;
         mStripLayoutHelper.getRunningAnimatorForTesting().end();
         assertEquals("The scroller has finished, so the offset should change.", expectedOffset,
                 mStripLayoutHelper.getScrollOffset(), EPSILON);
@@ -1624,7 +1583,7 @@
         // Start reorder mode on third tab. Drag between tabs in group.
         // -300 < -(tabWidth + marginWidth) = -(190 + 95) = -285
         mStripLayoutHelper.startReorderModeAtIndexForTesting(2);
-        float dragDistance = -300f;
+        float dragDistance = -200f;
         float startX = mStripLayoutHelper.getLastReorderX();
         mStripLayoutHelper.drag(TIMESTAMP, startX + dragDistance, 0f, dragDistance, 0f, 0f, 0f);
 
@@ -1703,13 +1662,11 @@
     }
 
     @Test
-    @Feature("Tab Strip Improvements")
     public void testTabClosing_NoTabResize() {
         // Arrange
-        int tabCount = 10;
-        TabUiFeatureUtilities.setTabMinWidthForTesting(TAB_WIDTH_MEDIUM);
-        initializeTest(false, false, false, 9, tabCount);
-        StripLayoutTab[] tabs = getRealStripLayoutTabs(TAB_WIDTH_MEDIUM, tabCount);
+        int tabCount = 15;
+        initializeTest(false, false, false, 14, tabCount);
+        StripLayoutTab[] tabs = getRealStripLayoutTabs(TAB_WIDTH_SMALL, tabCount);
         mStripLayoutHelper.setStripLayoutTabsForTest(tabs);
         mStripLayoutHelper.onSizeChanged(SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP);
         setupForAnimations();
@@ -1717,7 +1674,7 @@
         mStripLayoutHelper.updateLayout(TIMESTAMP);
 
         // Act: Call on close tab button handler.
-        mStripLayoutHelper.handleCloseButtonClick(tabs[9], TIMESTAMP);
+        mStripLayoutHelper.handleCloseButtonClick(tabs[14], TIMESTAMP);
 
         // Assert: Animations started.
         assertTrue("MultiStepAnimations should have started.",
@@ -1729,7 +1686,7 @@
         runningAnimator.end();
 
         // Assert: Tab is closed and animations are still running.
-        int expectedTabCount = 9;
+        int expectedTabCount = 14;
         assertEquals("Unexpected tabs count.", expectedTabCount,
                 mStripLayoutHelper.getStripLayoutTabs().length);
         assertTrue("MultiStepAnimations should still be running.",
@@ -1740,23 +1697,21 @@
 
         // Assert: Animations completed. The tab width is not resized and drawX does not change.
         float expectedDrawX =
-                -423.f; // Since we are focused on the last tab, start tabs are off screen.
+                -391.f; // Since we are focused on the last tab, start tabs are off screen.
         StripLayoutTab[] updatedTabs = mStripLayoutHelper.getStripLayoutTabs();
         for (StripLayoutTab stripTab : updatedTabs) {
-            assertEquals("Unexpected tab width after resize.", 156.f, stripTab.getWidth(), 0.0);
-            assertEquals("Unexpected tab position.", expectedDrawX, stripTab.getDrawX(), 0.0);
-            expectedDrawX += (TAB_WIDTH_MEDIUM - TAB_OVERLAP_WIDTH);
+            assertEquals("Unexpected tab width after resize.", 108.f, stripTab.getWidth(), 0);
+            assertEquals("Unexpected tab position.", expectedDrawX, stripTab.getDrawX(), 0);
+            expectedDrawX += TAB_WIDTH_SMALL - TAB_OVERLAP_WIDTH;
         }
         assertFalse("MultiStepAnimations should have stopped running.",
                 mStripLayoutHelper.isMultiStepCloseAnimationsRunning());
     }
 
     @Test
-    @Feature("Tab Strip Improvements")
     public void testTabClosing_NonLastTab_TabResize() {
         // Arrange
         int tabCount = 4;
-        TabUiFeatureUtilities.setTabMinWidthForTesting(TAB_WIDTH_MEDIUM);
         initializeTest(false, false, false, 3, tabCount);
         StripLayoutTab[] tabs = getRealStripLayoutTabs(TAB_WIDTH_MEDIUM, tabCount);
         mStripLayoutHelper.setStripLayoutTabsForTest(tabs);
@@ -1805,7 +1760,7 @@
     @Test
     public void testFlingLeft() {
         // Arrange
-        initializeTest(false, false, false, 9, 10);
+        initializeTest(false, false, false, 11, 12);
         mStripLayoutHelper.onSizeChanged(SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP);
         mStripLayoutHelper.updateLayout(TIMESTAMP);
         mStripLayoutHelper.testSetScrollOffset(-150);
@@ -1830,7 +1785,7 @@
     @Test
     public void testFlingRight() {
         // Arrange
-        initializeTest(false, false, false, 9, 10);
+        initializeTest(false, false, false, 10, 11);
         mStripLayoutHelper.onSizeChanged(SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP);
         // When updateLayout is called for the first time, bringSelectedTabToVisibleArea() method is
         // invoked. That also affects the scrollOffset value. So we call updateLayout before
@@ -1856,10 +1811,9 @@
     }
 
     @Test
-    @Feature("Tab Strip Improvements")
     public void testDrag_UpdatesScrollOffset_ScrollingStrip() {
         // Arrange
-        initializeTest(false, false, false, 9, 10);
+        initializeTest(false, false, false, 13, 14);
         mStripLayoutHelper.onSizeChanged(SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP);
         // When updateLayout is called for the first time, bringSelectedTabToVisibleArea() method is
         // invoked. That also affects the scrollOffset value. So we call updateLayout before
@@ -1880,60 +1834,6 @@
         assertFalse(mStripLayoutHelper.isInReorderModeForTesting());
     }
 
-    @Test
-    public void testDrag_UpdatesScrollOffset_CascadingStrip() {
-        // Arrange
-        ChromeFeatureList.sTabStripImprovements.setForTesting(false);
-        initializeTest(false, false, false, 0, 10);
-        mStripLayoutHelper.onSizeChanged(SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP);
-        // When updateLayout is called for the first time, bringSelectedTabToVisibleArea() method is
-        // invoked. That also affects the scrollOffset value. So we call updateLayout before
-        // performing a fling so that bringSelectedTabToVisible area isn't called after the fling.
-        mStripLayoutHelper.updateLayout(TIMESTAMP);
-        mStripLayoutHelper.testSetScrollOffset(-250);
-
-        // Act: Drag and update layout.
-        float dragDeltaX = -200.f;
-        mStripLayoutHelper.drag(
-                TIMESTAMP, 374.74f, 24.276f, dragDeltaX, -0.304f, -16.078f, -4.476f);
-
-        // Assert
-        float expectedOffset = -450; // mScrollOffset + dragDeltaX = -200 - 250 = -450
-        assertEquals("Unexpected scroll offset.", expectedOffset,
-                mStripLayoutHelper.getScrollOffset(), 0.0);
-        assertFalse("Reorder mode should not enabled when totalY <= 50.",
-                mStripLayoutHelper.isInReorderModeForTesting());
-    }
-
-    @Test
-    public void testDrag_ReorderMode_CascadingStrip() {
-        // Arrange
-        ChromeFeatureList.sTabStripImprovements.setForTesting(false);
-        initializeTest(false, false, false, 5, 10);
-        mStripLayoutHelper.onSizeChanged(SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP);
-        // When updateLayout is called for the first time, bringSelectedTabToVisibleArea() method is
-        // invoked. That also affects the scrollOffset value. So we call updateLayout before
-        // performing a fling so that bringSelectedTabToVisible area isn't called after the fling.
-        mStripLayoutHelper.updateLayout(TIMESTAMP);
-        mStripLayoutHelper.testSetScrollOffset(-250);
-
-        // Assert: Ensure reorder mode is disabled when starting drag.
-        assertFalse("Reorder mode should be disabled before drag.",
-                mStripLayoutHelper.isInReorderModeForTesting());
-
-        // Act
-        float dragDeltaX = -200.f;
-        float totalY = 75.f; // Drag with totalY > 50.f to cross reorder mode threshold.
-        mStripLayoutHelper.drag(TIMESTAMP, 374.74f, 24.276f, dragDeltaX, -0.304f, -16.078f, totalY);
-
-        // Assert: Reorder mode is enabled.
-        assertTrue("Reorder mode was not enabled after drag.",
-                mStripLayoutHelper.isInReorderModeForTesting());
-        float expectedOffset = -450; // mScrollOffset + dragDeltaX = -200 - 250 = -450
-        assertEquals("Unexpected scroll offset.", expectedOffset,
-                mStripLayoutHelper.getScrollOffset(), 0.0);
-    }
-
     private void setupForAnimations() {
         CompositorAnimationHandler mHandler = new CompositorAnimationHandler(() -> {});
         // CompositorAnimationHandler.setTestingMode(true);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripStackerUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripStackerUnitTest.java
index 1902463..08487fa 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripStackerUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripStackerUnitTest.java
@@ -9,7 +9,6 @@
 import static org.hamcrest.Matchers.is;
 import static org.mockito.Mockito.when;
 
-import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -19,7 +18,6 @@
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.DisabledTest;
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.ui.base.LocalizationUtils;
 
 /** Tests for {@link StripStacker}. */
@@ -61,12 +59,6 @@
             when(tab.getDrawX()).thenReturn(x);
             x += TAB_WIDTH;
         }
-        setTabStripImprovementFeature(false);
-    }
-
-    @After
-    public void tearDown() {
-        setTabStripImprovementFeature(false);
     }
 
     @Test
@@ -85,16 +77,6 @@
         float result = mTarget.computeNewTabButtonOffset(mInput, TAB_OVERLAP, STRIP_LEFT_MARGIN,
                 STRIP_RIGHT_MARGIN, STRIP_WIDTH, BUTTON_WIDTH, TOUCH_OFFSET, CACHED_TAB_WIDTH,
                 true);
-        assertThat("New Tab button offset does not match", result, is(130f));
-    }
-
-    @Test
-    @DisabledTest(message = "https://crbug.com/1385702")
-    public void testComputeNewTabButtonOffset_withTabStripImprovements() {
-        setTabStripImprovementFeature(true);
-        float result = mTarget.computeNewTabButtonOffset(mInput, TAB_OVERLAP, STRIP_LEFT_MARGIN,
-                STRIP_RIGHT_MARGIN, STRIP_WIDTH, BUTTON_WIDTH, TOUCH_OFFSET, CACHED_TAB_WIDTH,
-                true);
         assertThat("New Tab button offset does not match", result, is(35f));
     }
 
@@ -102,24 +84,6 @@
     public void testComputeNewTabButtonOffsetRTL() {
         LocalizationUtils.setRtlForTesting(true);
         float expected_res = 3f;
-        // Update drawX for RTL = ((mInput.length -1 ) * TAB_WIDTH) + TOUCH_OFFSET + BUTTON_WIDTH
-        // +expected_res = 4*25 + 5 + 10 +3
-        float draw_x = 118f;
-        for (StripLayoutTab tab : mInput) {
-            when(tab.getDrawX()).thenReturn(draw_x);
-            draw_x -= TAB_WIDTH;
-        }
-        float result = mTarget.computeNewTabButtonOffset(mInput, TAB_OVERLAP, STRIP_LEFT_MARGIN,
-                STRIP_RIGHT_MARGIN, STRIP_WIDTH, BUTTON_WIDTH, TOUCH_OFFSET, CACHED_TAB_WIDTH,
-                true);
-        assertThat("New Tab button offset does not match", result, is(expected_res));
-    }
-
-    @Test
-    public void testComputeNewTabButtonOffsetRTL_withTabStripImprovements() {
-        LocalizationUtils.setRtlForTesting(true);
-        setTabStripImprovementFeature(true);
-        float expected_res = 3f;
         // Update idealX for RTL = ((mInput.length -1 ) * TAB_WIDTH) + TOUCH_OFFSET + BUTTON_WIDTH +
         // expected_res = 4*25 + 5 + 10 + 3
         float ideal_x = 118f;
@@ -133,10 +97,6 @@
         assertThat("New Tab button offset does not match", result, is(expected_res));
     }
 
-    private void setTabStripImprovementFeature(boolean value) {
-        ChromeFeatureList.sTabStripImprovements.setForTesting(value);
-    }
-
     class DummyStacker extends StripStacker {
         @Override
         public void setTabOffsets(int selectedIndex, StripLayoutTab[] indexOrderedTabs,
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/tab/RequestDesktopUtilsUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/tab/RequestDesktopUtilsUnitTest.java
index 68575af7..7aa89c36 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/tab/RequestDesktopUtilsUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/tab/RequestDesktopUtilsUnitTest.java
@@ -925,6 +925,78 @@
     }
 
     @Test
+    public void testMaybeDisableGlobalSetting_FinchParamChanged_ScreenSizeInches() {
+        // Default-enable the global setting.
+        Map<String, String> params = new HashMap<>();
+        params.put(
+                RequestDesktopUtils.PARAM_GLOBAL_SETTING_DEFAULT_ON_DISPLAY_SIZE_THRESHOLD_INCHES,
+                "10.0");
+        enableFeatureWithParams(ChromeFeatureList.REQUEST_DESKTOP_SITE_DEFAULTS, params, true);
+        RequestDesktopUtils.maybeDefaultEnableGlobalSetting(
+                /*displaySizeInInches*/ 10.5, mProfile, mActivity);
+
+        // Update finch param and initiate downgrade.
+        params.put(
+                RequestDesktopUtils.PARAM_GLOBAL_SETTING_DEFAULT_ON_DISPLAY_SIZE_THRESHOLD_INCHES,
+                "11.0");
+        enableFeatureWithParams(ChromeFeatureList.REQUEST_DESKTOP_SITE_DEFAULTS, params, true);
+        RequestDesktopUtils.maybeDefaultEnableGlobalSetting(
+                /*displaySizeInInches*/ 10.5, mProfile, mActivity);
+        enableFeatureWithParams(
+                ChromeFeatureList.REQUEST_DESKTOP_SITE_DEFAULTS_DOWNGRADE, null, true);
+        boolean didDisable = RequestDesktopUtils.maybeDisableGlobalSetting(mProfile);
+
+        Assert.assertTrue(
+                "Desktop site global setting should be disabled on downgrade.", didDisable);
+        Assert.assertEquals("Desktop site content setting should be set correctly.",
+                ContentSettingValues.BLOCK, mRdsDefaultValue);
+        Assert.assertFalse(
+                "SharedPreference DEFAULT_ENABLED_DESKTOP_SITE_GLOBAL_SETTING should be removed.",
+                mSharedPreferencesManager.contains(
+                        ChromePreferenceKeys.DEFAULT_ENABLED_DESKTOP_SITE_GLOBAL_SETTING));
+        Assert.assertFalse(
+                "SharedPreference DEFAULT_ENABLE_DESKTOP_SITE_GLOBAL_SETTING_COHORT should be removed.",
+                mSharedPreferencesManager.contains(
+                        ChromePreferenceKeys.DEFAULT_ENABLE_DESKTOP_SITE_GLOBAL_SETTING_COHORT));
+    }
+
+    @Test
+    public void testMaybeDisableGlobalSetting_FinchParamChanged_ScreenWidthDp() {
+        // Default-enable the global setting.
+        Map<String, String> params = new HashMap<>();
+        params.put(
+                RequestDesktopUtils.PARAM_GLOBAL_SETTING_DEFAULT_ON_SMALLEST_SCREEN_WIDTH, "600");
+        enableFeatureWithParams(ChromeFeatureList.REQUEST_DESKTOP_SITE_DEFAULTS, params, true);
+        RequestDesktopUtils.maybeDefaultEnableGlobalSetting(
+                RequestDesktopUtils.DEFAULT_GLOBAL_SETTING_DEFAULT_ON_DISPLAY_SIZE_THRESHOLD_INCHES,
+                mProfile, mActivity);
+
+        // Update finch param and initiate downgrade.
+        params.put(
+                RequestDesktopUtils.PARAM_GLOBAL_SETTING_DEFAULT_ON_SMALLEST_SCREEN_WIDTH, "800");
+        enableFeatureWithParams(ChromeFeatureList.REQUEST_DESKTOP_SITE_DEFAULTS, params, true);
+        RequestDesktopUtils.maybeDefaultEnableGlobalSetting(
+                RequestDesktopUtils.DEFAULT_GLOBAL_SETTING_DEFAULT_ON_DISPLAY_SIZE_THRESHOLD_INCHES,
+                mProfile, mActivity);
+        enableFeatureWithParams(
+                ChromeFeatureList.REQUEST_DESKTOP_SITE_DEFAULTS_DOWNGRADE, null, true);
+        boolean didDisable = RequestDesktopUtils.maybeDisableGlobalSetting(mProfile);
+
+        Assert.assertTrue(
+                "Desktop site global setting should be disabled on downgrade.", didDisable);
+        Assert.assertEquals("Desktop site content setting should be set correctly.",
+                ContentSettingValues.BLOCK, mRdsDefaultValue);
+        Assert.assertFalse(
+                "SharedPreference DEFAULT_ENABLED_DESKTOP_SITE_GLOBAL_SETTING should be removed.",
+                mSharedPreferencesManager.contains(
+                        ChromePreferenceKeys.DEFAULT_ENABLED_DESKTOP_SITE_GLOBAL_SETTING));
+        Assert.assertFalse(
+                "SharedPreference DEFAULT_ENABLE_DESKTOP_SITE_GLOBAL_SETTING_COHORT should be removed.",
+                mSharedPreferencesManager.contains(
+                        ChromePreferenceKeys.DEFAULT_ENABLE_DESKTOP_SITE_GLOBAL_SETTING_COHORT));
+    }
+
+    @Test
     public void testShouldShowGlobalSettingOptInMessage_ExperimentControlGroup() {
         when(mTracker.wouldTriggerHelpUI(FeatureConstants.REQUEST_DESKTOP_SITE_OPT_IN_FEATURE))
                 .thenReturn(true);
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp
index ad28356..3d5ffedb 100644
--- a/chrome/app/chromeos_strings.grdp
+++ b/chrome/app/chromeos_strings.grdp
@@ -6236,6 +6236,18 @@
   <message name="IDS_PARENT_ACCESS_EXTENSION_APPROVALS_DISABLED_SUBTITLE" desc="Text telling the user that an extension cannot be installed because a parent has disallowed it.">
     Your parent has turned off "Permissions for sites, apps, and extensions" for Chrome. Adding this extension is not allowed.
   </message>
+  <message name="IDS_PARENT_ACCESS_EXTENSION_APPROVALS_ADD_EXTENSION_BEFORE_TITLE" desc="Title for the dialog asking a supervised user if they would like to ask for approval to install a new extension.">
+    Ask your parent to add this extension?
+  </message>
+  <message name="IDS_PARENT_ACCESS_EXTENSION_APPROVALS_ADD_EXTENSION_BEFORE_SUBTITLE" desc="Text indicating that a parent must approve the extension for the supervised user to gain access.">
+    A parent or guardian has to say that it's OK for you to add this extension
+  </message>
+  <message name="IDS_PARENT_ACCESS_EXTENSION_APPROVALS_ENABLE_EXTENSION_BEFORE_TITLE" desc="Title for the dialog asking a supervised user if they would like to ask for approval to enable an installed extension.">
+    Ask your parent to enable this extension?
+  </message>
+  <message name="IDS_PARENT_ACCESS_EXTENSION_APPROVALS_ENABLE_EXTENSION_BEFORE_SUBTITLE" desc="Text indicating that a parent must approve the extension for the supervised user to gain access.">
+    A parent or guardian has to say that it's OK for you to enable this extension
+  </message>
 
   <!-- Strings shared among supervised user experiences -->
   <message name="IDS_SUPERVISED_USER_ERROR_TITLE" desc="Title for the UI shown to supervised users when an error has occurred.">
diff --git a/chrome/app/chromeos_strings_grdp/IDS_PARENT_ACCESS_EXTENSION_APPROVALS_ADD_EXTENSION_BEFORE_SUBTITLE.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_PARENT_ACCESS_EXTENSION_APPROVALS_ADD_EXTENSION_BEFORE_SUBTITLE.png.sha1
new file mode 100644
index 0000000..017131a1
--- /dev/null
+++ b/chrome/app/chromeos_strings_grdp/IDS_PARENT_ACCESS_EXTENSION_APPROVALS_ADD_EXTENSION_BEFORE_SUBTITLE.png.sha1
@@ -0,0 +1 @@
+e464b2a56eace4c01dad1ca10031ecb5a4c0c72d
\ No newline at end of file
diff --git a/chrome/app/chromeos_strings_grdp/IDS_PARENT_ACCESS_EXTENSION_APPROVALS_ADD_EXTENSION_BEFORE_TITLE.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_PARENT_ACCESS_EXTENSION_APPROVALS_ADD_EXTENSION_BEFORE_TITLE.png.sha1
new file mode 100644
index 0000000..017131a1
--- /dev/null
+++ b/chrome/app/chromeos_strings_grdp/IDS_PARENT_ACCESS_EXTENSION_APPROVALS_ADD_EXTENSION_BEFORE_TITLE.png.sha1
@@ -0,0 +1 @@
+e464b2a56eace4c01dad1ca10031ecb5a4c0c72d
\ No newline at end of file
diff --git a/chrome/app/chromeos_strings_grdp/IDS_PARENT_ACCESS_EXTENSION_APPROVALS_ENABLE_EXTENSION_BEFORE_SUBTITLE.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_PARENT_ACCESS_EXTENSION_APPROVALS_ENABLE_EXTENSION_BEFORE_SUBTITLE.png.sha1
new file mode 100644
index 0000000..b62f0928
--- /dev/null
+++ b/chrome/app/chromeos_strings_grdp/IDS_PARENT_ACCESS_EXTENSION_APPROVALS_ENABLE_EXTENSION_BEFORE_SUBTITLE.png.sha1
@@ -0,0 +1 @@
+fd1669a881108e995f847161beaaabb9e20832cb
\ No newline at end of file
diff --git a/chrome/app/chromeos_strings_grdp/IDS_PARENT_ACCESS_EXTENSION_APPROVALS_ENABLE_EXTENSION_BEFORE_TITLE.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_PARENT_ACCESS_EXTENSION_APPROVALS_ENABLE_EXTENSION_BEFORE_TITLE.png.sha1
new file mode 100644
index 0000000..b62f0928
--- /dev/null
+++ b/chrome/app/chromeos_strings_grdp/IDS_PARENT_ACCESS_EXTENSION_APPROVALS_ENABLE_EXTENSION_BEFORE_TITLE.png.sha1
@@ -0,0 +1 @@
+fd1669a881108e995f847161beaaabb9e20832cb
\ No newline at end of file
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index d29092d..41ff792 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -12940,9 +12940,6 @@
 
     <if expr="not is_android">
       <!-- Device Chooser Prompt -->
-      <message name="IDS_BLUETOOTH_DEVICE_CHOOSER_PROMPT_EXTENSION_NAME" desc="The label that is used to introduce Bluetooth chooser details to the user in a popup when it is from a Chrome extension.">
-        "<ph name="CHROME_EXTENSION_NAME">$1<ex>Chrome Extension Name</ex></ph>" wants to pair
-      </message>
       <message name="IDS_BLUETOOTH_DEVICE_CHOOSER_TURN_ADAPTER_OFF" desc="Text of a link the user can click to get help information when Bluetooth adapter is turned off.">
         <ph name="TURN_ON_BLUETOOTH_LINK">$1<ex>Turn on Bluetooth</ex></ph> to allow pairing
       </message>
@@ -12971,9 +12968,6 @@
         <ph name="DEVICE_NAME">$1<ex>device name</ex></ph> - Paired
       </message>
     </if>
-    <message name="IDS_USB_DEVICE_CHOOSER_PROMPT_EXTENSION_NAME" desc="The label that is used to introduce USB chooser details to the user in a popup when it is from a Chrome extension.">
-      "<ph name="CHROME_EXTENSION_NAME">$1<ex>Chrome Extension Name</ex></ph>" wants to connect
-    </message>
     <message name="IDS_DEVICE_CHOOSER_ACCNAME_COMPATIBLE_DEVICES_LIST" desc="The accessible name for the list of compatible devices, read to screen reader users when navigating to the container.">
       Compatible devices
     </message>
@@ -13005,12 +12999,9 @@
 
     <!-- Serial port chooser -->
     <if expr="not is_android">
-      <message name="IDS_SERIAL_PORT_CHOOSER_PROMPT_ORIGIN" desc="The label that is used to introduce serial port chooser details to the user in a popup when it is from a website.">
+      <message name="IDS_SERIAL_PORT_CHOOSER_PROMPT" desc="The label that is used to introduce serial port chooser details to the user in a popup.">
         <ph name="Origin">$1<ex>www.google.com</ex></ph> wants to connect to a serial port
       </message>
-      <message name="IDS_SERIAL_PORT_CHOOSER_PROMPT_EXTENSION_NAME" desc="The label that is used to introduce serial port chooser details to the user in a popup when it is from a Chrome extension.">
-        "<ph name="CHROME_EXTENSION_NAME">$1<ex>Chrome Extension Name</ex></ph>" wants to connect to a serial port
-      </message>
       <message name="IDS_SERIAL_PORT_CHOOSER_NAME_WITH_PATH" desc="User option displaying a human-readable name for a serial port with the device path in parenthesis">
         <ph name="FRIENDLY_NAME">$1<ex>Virtual COM Port</ex></ph> (<ph name="DEVICE_PATH">$2<ex>COM1</ex></ph>)
       </message>
@@ -13045,12 +13036,9 @@
 
     <!-- HID (Human Interface Device) chooser -->
     <if expr="not is_android">
-      <message name="IDS_HID_CHOOSER_PROMPT_ORIGIN" desc="The label that is used to introduce the Human Interface Device (HID) chooser details to the user in a popup when it is from a website.">
+      <message name="IDS_HID_CHOOSER_PROMPT" desc="The label that is used to introduce the Human Interface Device (HID) chooser details to the user in a popup.">
         <ph name="Origin">$1<ex>www.google.com</ex></ph> wants to connect to a HID device
       </message>
-      <message name="IDS_HID_CHOOSER_PROMPT_EXTENSION_NAME" desc="The label that is used to introduce Human Interface Device (HID) chooser details to the user in a popup when it is from a Chrome extension.">
-        "<ph name="CHROME_EXTENSION_NAME">$1<ex>Chrome Extension Name</ex></ph>" wants to connect to a HID device
-      </message>
       <message name="IDS_HID_CHOOSER_ITEM_WITHOUT_NAME" desc="User option displaying the device IDs for a Human Interface Device (HID) without a device name.">
         Unknown Device (<ph name="DEVICE_ID">$1<ex>1234:abcd</ex></ph>)
       </message>
diff --git a/chrome/app/generated_resources_grd/IDS_HID_CHOOSER_PROMPT_ORIGIN.png.sha1 b/chrome/app/generated_resources_grd/IDS_HID_CHOOSER_PROMPT.png.sha1
similarity index 100%
rename from chrome/app/generated_resources_grd/IDS_HID_CHOOSER_PROMPT_ORIGIN.png.sha1
rename to chrome/app/generated_resources_grd/IDS_HID_CHOOSER_PROMPT.png.sha1
diff --git a/chrome/app/generated_resources_grd/IDS_HID_CHOOSER_PROMPT_EXTENSION_NAME.png.sha1 b/chrome/app/generated_resources_grd/IDS_HID_CHOOSER_PROMPT_EXTENSION_NAME.png.sha1
deleted file mode 100644
index 90a6bb6b..0000000
--- a/chrome/app/generated_resources_grd/IDS_HID_CHOOSER_PROMPT_EXTENSION_NAME.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-071dbff1f196a88a6bd8c193de1291d1dbdb77dd
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_SERIAL_PORT_CHOOSER_PROMPT_ORIGIN.png.sha1 b/chrome/app/generated_resources_grd/IDS_SERIAL_PORT_CHOOSER_PROMPT.png.sha1
similarity index 100%
rename from chrome/app/generated_resources_grd/IDS_SERIAL_PORT_CHOOSER_PROMPT_ORIGIN.png.sha1
rename to chrome/app/generated_resources_grd/IDS_SERIAL_PORT_CHOOSER_PROMPT.png.sha1
diff --git a/chrome/app/generated_resources_grd/IDS_SERIAL_PORT_CHOOSER_PROMPT_EXTENSION_NAME.png.sha1 b/chrome/app/generated_resources_grd/IDS_SERIAL_PORT_CHOOSER_PROMPT_EXTENSION_NAME.png.sha1
deleted file mode 100644
index e6f0756f..0000000
--- a/chrome/app/generated_resources_grd/IDS_SERIAL_PORT_CHOOSER_PROMPT_EXTENSION_NAME.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-c0b7a72549b53d5fbbcb195d7b429de21fd1d229
\ No newline at end of file
diff --git a/chrome/app/vector_icons/BUILD.gn b/chrome/app/vector_icons/BUILD.gn
index fd5e44a..d605d6e9b 100644
--- a/chrome/app/vector_icons/BUILD.gn
+++ b/chrome/app/vector_icons/BUILD.gn
@@ -184,7 +184,9 @@
     "webauthn/webauthn_error.icon",
     "webauthn/webauthn_error_dark.icon",
     "zoom_minus.icon",
+    "zoom_minus_chrome_refresh.icon",
     "zoom_plus.icon",
+    "zoom_plus_chrome_refresh.icon",
   ]
 
   if (is_mac) {
diff --git a/chrome/app/vector_icons/zoom_minus_chrome_refresh.icon b/chrome/app/vector_icons/zoom_minus_chrome_refresh.icon
new file mode 100644
index 0000000..01b8a423
--- /dev/null
+++ b/chrome/app/vector_icons/zoom_minus_chrome_refresh.icon
@@ -0,0 +1,134 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+CANVAS_DIMENSIONS, 32,
+MOVE_TO, 26.27f, 28,
+LINE_TO, 17.73f, 19.47f,
+CUBIC_TO, 17.07f, 20.02f, 16.3f, 20.46f, 15.43f, 20.77f,
+CUBIC_TO, 14.57f, 21.08f, 13.63f, 21.23f, 12.63f, 21.23f,
+CUBIC_TO, 10.23f, 21.23f, 8.19f, 20.4f, 6.5f, 18.73f,
+CUBIC_TO, 4.83f, 17.04f, 4, 15, 4, 12.6f,
+CUBIC_TO, 4, 10.2f, 4.83f, 8.17f, 6.5f, 6.5f,
+CUBIC_TO, 8.19f, 4.83f, 10.23f, 4, 12.63f, 4,
+CUBIC_TO, 15.03f, 4, 17.07f, 4.83f, 18.73f, 6.5f,
+CUBIC_TO, 20.4f, 8.17f, 21.23f, 10.2f, 21.23f, 12.6f,
+CUBIC_TO, 21.23f, 13.58f, 21.08f, 14.51f, 20.77f, 15.4f,
+CUBIC_TO, 20.46f, 16.27f, 20.02f, 17.04f, 19.47f, 17.73f,
+LINE_TO, 28, 26.27f,
+LINE_TO, 26.27f, 28,
+CLOSE,
+MOVE_TO, 12.63f, 18.8f,
+CUBIC_TO, 14.34f, 18.8f, 15.8f, 18.2f, 17, 17,
+CUBIC_TO, 18.2f, 15.78f, 18.8f, 14.31f, 18.8f, 12.6f,
+CUBIC_TO, 18.8f, 10.89f, 18.2f, 9.43f, 17, 8.23f,
+CUBIC_TO, 15.8f, 7.03f, 14.34f, 6.43f, 12.63f, 6.43f,
+CUBIC_TO, 10.92f, 6.43f, 9.46f, 7.03f, 8.23f, 8.23f,
+CUBIC_TO, 7.03f, 9.43f, 6.43f, 10.89f, 6.43f, 12.6f,
+CUBIC_TO, 6.43f, 14.31f, 7.03f, 15.78f, 8.23f, 17,
+CUBIC_TO, 9.46f, 18.2f, 10.92f, 18.8f, 12.63f, 18.8f,
+CLOSE,
+MOVE_TO, 9.27f, 13.83f,
+V_LINE_TO, 11.37f,
+H_LINE_TO, 15.97f,
+V_LINE_TO, 13.83f,
+H_LINE_TO, 9.27f,
+CLOSE,
+NEW_PATH
+
+
+
+CANVAS_DIMENSIONS, 24,
+MOVE_TO, 19.6f, 21,
+LINE_TO, 13.3f, 14.7f,
+CUBIC_TO, 12.8f, 15.1f, 12.23f, 15.42f, 11.58f, 15.65f,
+CUBIC_TO, 10.93f, 15.88f, 10.23f, 16, 9.5f, 16,
+CUBIC_TO, 7.68f, 16, 6.14f, 15.38f, 4.88f, 14.13f,
+CUBIC_TO, 3.63f, 12.86f, 3, 11.32f, 3, 9.5f,
+CUBIC_TO, 3, 7.68f, 3.63f, 6.15f, 4.88f, 4.9f,
+CUBIC_TO, 6.14f, 3.63f, 7.68f, 3, 9.5f, 3,
+CUBIC_TO, 11.32f, 3, 12.85f, 3.63f, 14.1f, 4.9f,
+CUBIC_TO, 15.37f, 6.15f, 16, 7.68f, 16, 9.5f,
+CUBIC_TO, 16, 10.23f, 15.88f, 10.93f, 15.65f, 11.58f,
+CUBIC_TO, 15.42f, 12.23f, 15.1f, 12.8f, 14.7f, 13.3f,
+LINE_TO, 21, 19.6f,
+LINE_TO, 19.6f, 21,
+CLOSE,
+MOVE_TO, 9.5f, 14,
+CUBIC_TO, 10.75f, 14, 11.81f, 13.57f, 12.68f, 12.7f,
+CUBIC_TO, 13.56f, 11.82f, 14, 10.75f, 14, 9.5f,
+CUBIC_TO, 14, 8.25f, 13.56f, 7.19f, 12.68f, 6.33f,
+CUBIC_TO, 11.81f, 5.44f, 10.75f, 5, 9.5f, 5,
+CUBIC_TO, 8.25f, 5, 7.18f, 5.44f, 6.3f, 6.33f,
+CUBIC_TO, 5.43f, 7.19f, 5, 8.25f, 5, 9.5f,
+CUBIC_TO, 5, 10.75f, 5.43f, 11.82f, 6.3f, 12.7f,
+CUBIC_TO, 7.18f, 13.57f, 8.25f, 14, 9.5f, 14,
+CLOSE,
+MOVE_TO, 7, 10.5f,
+V_LINE_TO, 8.5f,
+H_LINE_TO, 12,
+V_LINE_TO, 10.5f,
+H_LINE_TO, 7,
+CLOSE,
+NEW_PATH
+
+
+CANVAS_DIMENSIONS, 20,
+MOVE_TO, 15.94f, 17,
+LINE_TO, 10.96f, 12,
+CUBIC_TO, 10.54f, 12.32f, 10.08f, 12.57f, 9.58f, 12.75f,
+CUBIC_TO, 9.08f, 12.92f, 8.56f, 13, 8, 13,
+CUBIC_TO, 6.61f, 13, 5.43f, 12.51f, 4.46f, 11.54f,
+CUBIC_TO, 3.49f, 10.57f, 3, 9.39f, 3, 8,
+CUBIC_TO, 3, 6.61f, 3.49f, 5.43f, 4.46f, 4.46f,
+CUBIC_TO, 5.43f, 3.49f, 6.61f, 3, 8, 3,
+CUBIC_TO, 9.39f, 3, 10.57f, 3.49f, 11.54f, 4.46f,
+CUBIC_TO, 12.51f, 5.43f, 13, 6.61f, 13, 8,
+CUBIC_TO, 13, 8.56f, 12.91f, 9.08f, 12.73f, 9.58f,
+CUBIC_TO, 12.56f, 10.08f, 12.33f, 10.54f, 12.02f, 10.96f,
+LINE_TO, 17, 15.94f,
+LINE_TO, 15.94f, 17,
+CLOSE,
+MOVE_TO, 8, 11.5f,
+CUBIC_TO, 8.97f, 11.5f, 9.8f, 11.16f, 10.48f, 10.48f,
+CUBIC_TO, 11.16f, 9.8f, 11.5f, 8.97f, 11.5f, 8,
+CUBIC_TO, 11.5f, 7.03f, 11.16f, 6.2f, 10.48f, 5.52f,
+CUBIC_TO, 9.8f, 4.84f, 8.97f, 4.5f, 8, 4.5f,
+CUBIC_TO, 7.03f, 4.5f, 6.2f, 4.84f, 5.52f, 5.52f,
+CUBIC_TO, 4.84f, 6.2f, 4.5f, 7.03f, 4.5f, 8,
+CUBIC_TO, 4.5f, 8.97f, 4.84f, 9.8f, 5.52f, 10.48f,
+CUBIC_TO, 6.2f, 11.16f, 7.03f, 11.5f, 8, 11.5f,
+CLOSE,
+MOVE_TO, 6, 8.75f,
+V_LINE_TO, 7.25f,
+H_LINE_TO, 10,
+V_LINE_TO, 8.75f,
+H_LINE_TO, 6,
+CLOSE,
+NEW_PATH
+
+
+CANVAS_DIMENSIONS, 16,
+MOVE_TO, 10.16f, 9.1f,
+CUBIC_TO, 11.41f, 7.34f, 11.26f, 4.9f, 9.68f, 3.32f,
+CUBIC_TO, 7.92f, 1.56f, 5.07f, 1.56f, 3.32f, 3.32f,
+CUBIC_TO, 1.56f, 5.08f, 1.56f, 7.93f, 3.32f, 9.68f,
+CUBIC_TO, 4.9f, 11.26f, 7.34f, 11.41f, 9.1f, 10.16f,
+LINE_TO, 12.94f, 14,
+LINE_TO, 14, 12.94f,
+LINE_TO, 10.16f, 9.1f,
+CLOSE,
+MOVE_TO, 8.62f, 8.62f,
+CUBIC_TO, 7.45f, 9.79f, 5.55f, 9.79f, 4.38f, 8.62f,
+CUBIC_TO, 3.21f, 7.45f, 3.21f, 5.55f, 4.38f, 4.38f,
+CUBIC_TO, 5.55f, 3.21f, 7.45f, 3.21f, 8.62f, 4.38f,
+CUBIC_TO, 9.79f, 5.55f, 9.79f, 7.45f, 8.62f, 8.62f,
+CLOSE,
+NEW_PATH,
+MOVE_TO, 8.5f, 6,
+H_LINE_TO, 4.5f,
+V_LINE_TO, 7,
+H_LINE_TO, 8.5f,
+V_LINE_TO, 6,
+CLOSE,
+NEW_PATH
diff --git a/chrome/app/vector_icons/zoom_plus_chrome_refresh.icon b/chrome/app/vector_icons/zoom_plus_chrome_refresh.icon
new file mode 100644
index 0000000..1b47f83c
--- /dev/null
+++ b/chrome/app/vector_icons/zoom_plus_chrome_refresh.icon
@@ -0,0 +1,166 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+CANVAS_DIMENSIONS, 32,
+MOVE_TO, 26.27f, 28,
+LINE_TO, 17.73f, 19.47f,
+CUBIC_TO, 17.07f, 20.02f, 16.3f, 20.46f, 15.43f, 20.77f,
+CUBIC_TO, 14.57f, 21.08f, 13.63f, 21.23f, 12.63f, 21.23f,
+CUBIC_TO, 10.23f, 21.23f, 8.19f, 20.4f, 6.5f, 18.73f,
+CUBIC_TO, 4.83f, 17.04f, 4, 15, 4, 12.6f,
+CUBIC_TO, 4, 10.2f, 4.83f, 8.17f, 6.5f, 6.5f,
+CUBIC_TO, 8.19f, 4.83f, 10.23f, 4, 12.63f, 4,
+CUBIC_TO, 15.03f, 4, 17.07f, 4.83f, 18.73f, 6.5f,
+CUBIC_TO, 20.4f, 8.17f, 21.23f, 10.2f, 21.23f, 12.6f,
+CUBIC_TO, 21.23f, 13.58f, 21.08f, 14.51f, 20.77f, 15.4f,
+CUBIC_TO, 20.46f, 16.27f, 20.02f, 17.04f, 19.47f, 17.73f,
+LINE_TO, 28, 26.27f,
+LINE_TO, 26.27f, 28,
+CLOSE,
+MOVE_TO, 12.63f, 18.8f,
+CUBIC_TO, 14.34f, 18.8f, 15.8f, 18.2f, 17, 17,
+CUBIC_TO, 18.2f, 15.78f, 18.8f, 14.31f, 18.8f, 12.6f,
+CUBIC_TO, 18.8f, 10.89f, 18.2f, 9.43f, 17, 8.23f,
+CUBIC_TO, 15.8f, 7.03f, 14.34f, 6.43f, 12.63f, 6.43f,
+CUBIC_TO, 10.92f, 6.43f, 9.46f, 7.03f, 8.23f, 8.23f,
+CUBIC_TO, 7.03f, 9.43f, 6.43f, 10.89f, 6.43f, 12.6f,
+CUBIC_TO, 6.43f, 14.31f, 7.03f, 15.78f, 8.23f, 17,
+CUBIC_TO, 9.46f, 18.2f, 10.92f, 18.8f, 12.63f, 18.8f,
+CLOSE,
+MOVE_TO, 11.4f, 16.5f,
+V_LINE_TO, 13.83f,
+H_LINE_TO, 8.7f,
+V_LINE_TO, 11.37f,
+H_LINE_TO, 11.4f,
+V_LINE_TO, 8.7f,
+H_LINE_TO, 13.83f,
+V_LINE_TO, 11.37f,
+H_LINE_TO, 16.53f,
+V_LINE_TO, 13.83f,
+H_LINE_TO, 13.83f,
+V_LINE_TO, 16.5f,
+H_LINE_TO, 11.4f,
+CLOSE,
+NEW_PATH
+
+
+CANVAS_DIMENSIONS, 24,
+MOVE_TO, 19.6f, 21,
+LINE_TO, 13.3f, 14.7f,
+CUBIC_TO, 12.8f, 15.1f, 12.23f, 15.42f, 11.58f, 15.65f,
+CUBIC_TO, 10.93f, 15.88f, 10.23f, 16, 9.5f, 16,
+CUBIC_TO, 7.68f, 16, 6.14f, 15.38f, 4.88f, 14.13f,
+CUBIC_TO, 3.63f, 12.86f, 3, 11.32f, 3, 9.5f,
+CUBIC_TO, 3, 7.68f, 3.63f, 6.15f, 4.88f, 4.9f,
+CUBIC_TO, 6.14f, 3.63f, 7.68f, 3, 9.5f, 3,
+CUBIC_TO, 11.32f, 3, 12.85f, 3.63f, 14.1f, 4.9f,
+CUBIC_TO, 15.37f, 6.15f, 16, 7.68f, 16, 9.5f,
+CUBIC_TO, 16, 10.23f, 15.88f, 10.93f, 15.65f, 11.58f,
+CUBIC_TO, 15.42f, 12.23f, 15.1f, 12.8f, 14.7f, 13.3f,
+LINE_TO, 21, 19.6f,
+LINE_TO, 19.6f, 21,
+CLOSE,
+MOVE_TO, 9.5f, 14,
+CUBIC_TO, 10.75f, 14, 11.81f, 13.57f, 12.68f, 12.7f,
+CUBIC_TO, 13.56f, 11.82f, 14, 10.75f, 14, 9.5f,
+CUBIC_TO, 14, 8.25f, 13.56f, 7.19f, 12.68f, 6.33f,
+CUBIC_TO, 11.81f, 5.44f, 10.75f, 5, 9.5f, 5,
+CUBIC_TO, 8.25f, 5, 7.18f, 5.44f, 6.3f, 6.33f,
+CUBIC_TO, 5.43f, 7.19f, 5, 8.25f, 5, 9.5f,
+CUBIC_TO, 5, 10.75f, 5.43f, 11.82f, 6.3f, 12.7f,
+CUBIC_TO, 7.18f, 13.57f, 8.25f, 14, 9.5f, 14,
+CLOSE,
+MOVE_TO, 8.5f, 12.5f,
+V_LINE_TO, 10.5f,
+H_LINE_TO, 6.5f,
+V_LINE_TO, 8.5f,
+H_LINE_TO, 8.5f,
+V_LINE_TO, 6.5f,
+H_LINE_TO, 10.5f,
+V_LINE_TO, 8.5f,
+H_LINE_TO, 12.5f,
+V_LINE_TO, 10.5f,
+H_LINE_TO, 10.5f,
+V_LINE_TO, 12.5f,
+H_LINE_TO, 8.5f,
+CLOSE,
+NEW_PATH
+
+
+
+CANVAS_DIMENSIONS, 20,
+MOVE_TO, 15.94f, 17,
+LINE_TO, 10.96f, 12,
+CUBIC_TO, 10.54f, 12.32f, 10.08f, 12.57f, 9.58f, 12.75f,
+CUBIC_TO, 9.08f, 12.92f, 8.56f, 13, 8, 13,
+CUBIC_TO, 6.61f, 13, 5.43f, 12.51f, 4.46f, 11.54f,
+CUBIC_TO, 3.49f, 10.57f, 3, 9.39f, 3, 8,
+CUBIC_TO, 3, 6.61f, 3.49f, 5.43f, 4.46f, 4.46f,
+CUBIC_TO, 5.43f, 3.49f, 6.61f, 3, 8, 3,
+CUBIC_TO, 9.39f, 3, 10.57f, 3.49f, 11.54f, 4.46f,
+CUBIC_TO, 12.51f, 5.43f, 13, 6.61f, 13, 8,
+CUBIC_TO, 13, 8.56f, 12.91f, 9.08f, 12.73f, 9.58f,
+CUBIC_TO, 12.56f, 10.08f, 12.33f, 10.54f, 12.02f, 10.96f,
+LINE_TO, 17, 15.94f,
+LINE_TO, 15.94f, 17,
+CLOSE,
+MOVE_TO, 8, 11.5f,
+CUBIC_TO, 8.97f, 11.5f, 9.8f, 11.16f, 10.48f, 10.48f,
+CUBIC_TO, 11.16f, 9.8f, 11.5f, 8.97f, 11.5f, 8,
+CUBIC_TO, 11.5f, 7.03f, 11.16f, 6.2f, 10.48f, 5.52f,
+CUBIC_TO, 9.8f, 4.84f, 8.97f, 4.5f, 8, 4.5f,
+CUBIC_TO, 7.03f, 4.5f, 6.2f, 4.84f, 5.52f, 5.52f,
+CUBIC_TO, 4.84f, 6.2f, 4.5f, 7.03f, 4.5f, 8,
+CUBIC_TO, 4.5f, 8.97f, 4.84f, 9.8f, 5.52f, 10.48f,
+CUBIC_TO, 6.2f, 11.16f, 7.03f, 11.5f, 8, 11.5f,
+CLOSE,
+MOVE_TO, 7.25f, 10.25f,
+V_LINE_TO, 8.75f,
+H_LINE_TO, 5.75f,
+V_LINE_TO, 7.25f,
+H_LINE_TO, 7.25f,
+V_LINE_TO, 5.75f,
+H_LINE_TO, 8.75f,
+V_LINE_TO, 7.25f,
+H_LINE_TO, 10.25f,
+V_LINE_TO, 8.75f,
+H_LINE_TO, 8.75f,
+V_LINE_TO, 10.25f,
+H_LINE_TO, 7.25f,
+CLOSE,
+NEW_PATH
+
+
+CANVAS_DIMENSIONS, 16,
+MOVE_TO, 10.16f, 9.1f,
+CUBIC_TO, 11.41f, 7.34f, 11.26f, 4.9f, 9.68f, 3.32f,
+CUBIC_TO, 7.92f, 1.56f, 5.07f, 1.56f, 3.32f, 3.32f,
+CUBIC_TO, 1.56f, 5.08f, 1.56f, 7.93f, 3.32f, 9.68f,
+CUBIC_TO, 4.9f, 11.26f, 7.34f, 11.41f, 9.1f, 10.16f,
+LINE_TO, 12.94f, 14,
+LINE_TO, 14, 12.94f,
+LINE_TO, 10.16f, 9.1f,
+CLOSE,
+MOVE_TO, 8.62f, 8.62f,
+CUBIC_TO, 7.45f, 9.79f, 5.55f, 9.79f, 4.38f, 8.62f,
+CUBIC_TO, 3.21f, 7.45f, 3.21f, 5.55f, 4.38f, 4.38f,
+CUBIC_TO, 5.55f, 3.21f, 7.45f, 3.21f, 8.62f, 4.38f,
+CUBIC_TO, 9.79f, 5.55f, 9.79f, 7.45f, 8.62f, 8.62f,
+CLOSE,
+NEW_PATH,
+MOVE_TO, 7, 4.5f,
+H_LINE_TO, 6,
+V_LINE_TO, 6,
+H_LINE_TO, 4.5f,
+V_LINE_TO, 7,
+H_LINE_TO, 6,
+V_LINE_TO, 8.5f,
+H_LINE_TO, 7,
+V_LINE_TO, 7,
+H_LINE_TO, 8.5f,
+V_LINE_TO, 6,
+H_LINE_TO, 7,
+V_LINE_TO, 4.5f,
+CLOSE,
+NEW_PATH
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 23e1682..eae1562 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -2983,21 +2983,6 @@
 #endif  // BUILDFLAG(IS_ANDROID)
 
 #if BUILDFLAG(IS_ANDROID)
-const FeatureEntry::FeatureParam kTabStripImprovementsTabWidthShort[] = {
-    {"min_tab_width", "108"}};
-const FeatureEntry::FeatureParam kTabStripImprovementsTabWidthMedium[] = {
-    {"min_tab_width", "156"}};
-
-const FeatureEntry::FeatureVariation kTabStripImprovementsTabWidthVariations[] =
-    {
-        {"Short Tab Width", kTabStripImprovementsTabWidthShort,
-         std::size(kTabStripImprovementsTabWidthShort), nullptr},
-        {"Medium Tab Width", kTabStripImprovementsTabWidthMedium,
-         std::size(kTabStripImprovementsTabWidthMedium), nullptr},
-};
-#endif  // BUILDFLAG(IS_ANDROID)
-
-#if BUILDFLAG(IS_ANDROID)
 const FeatureEntry::FeatureParam kTabStripRedesignFolio[] = {
     {"enable_folio", "true"}};
 const FeatureEntry::FeatureParam kTabStripRedesignDetached[] = {
@@ -5736,6 +5721,11 @@
      flag_descriptions::kOmniboxGM3SteadyStateHeightDescription, kOsAll,
      FEATURE_VALUE_TYPE(omnibox::kOmniboxSteadyStateHeight)},
 
+    {"omnibox-gm3-steady-state-text-color",
+     flag_descriptions::kOmniboxGM3SteadyStateTextColorName,
+     flag_descriptions::kOmniboxGM3SteadyStateTextColorDescription, kOsAll,
+     FEATURE_VALUE_TYPE(omnibox::kOmniboxSteadyStateTextColor)},
+
     {"omnibox-gm3-steady-state-text-style",
      flag_descriptions::kOmniboxGM3SteadyStateTextStyleName,
      flag_descriptions::kOmniboxGM3SteadyStateTextStyleDescription, kOsAll,
@@ -6470,13 +6460,6 @@
      flag_descriptions::kTabEngagementReportingDescription, kOsAndroid,
      FEATURE_VALUE_TYPE(chrome::android::kTabEngagementReportingAndroid)},
 
-    {"enable-tab-strip-improvements",
-     flag_descriptions::kTabStripImprovementsAndroidName,
-     flag_descriptions::kTabStripImprovementsAndroidDescription, kOsAndroid,
-     FEATURE_WITH_PARAMS_VALUE_TYPE(chrome::android::kTabStripImprovements,
-                                    kTabStripImprovementsTabWidthVariations,
-                                    "TabStripImprovementsAndroid")},
-
     {"enable-discover-multi-column",
      flag_descriptions::kDiscoverFeedMultiColumnAndroidName,
      flag_descriptions::kDiscoverFeedMultiColumnAndroidDescription, kOsAndroid,
diff --git a/chrome/browser/android/compositor/layer/tab_handle_layer.cc b/chrome/browser/android/compositor/layer/tab_handle_layer.cc
index c2a9d3c..f7c49c0 100644
--- a/chrome/browser/android/compositor/layer/tab_handle_layer.cc
+++ b/chrome/browser/android/compositor/layer/tab_handle_layer.cc
@@ -161,12 +161,11 @@
   const float padding_left = tab_handle_resource->padding().x();
 
   float close_width = close_button_->bounds().width();
-  // For the min_tab_width experiments, if close button is not shown, fill
+
+  // If close button is not shown, fill
   // the remaining space with the title text
-  if (base::FeatureList::IsEnabled(chrome::android::kTabStripImprovements)) {
-    if (close_button_alpha == 0.f) {
-      close_width = 0.f;
-    }
+  if (close_button_alpha == 0.f) {
+    close_width = 0.f;
   }
 
   int divider_y;
diff --git a/chrome/browser/android/compositor/scene_layer/tab_strip_scene_layer.cc b/chrome/browser/android/compositor/scene_layer/tab_strip_scene_layer.cc
index 469afca7..d4d3c3c 100644
--- a/chrome/browser/android/compositor/scene_layer/tab_strip_scene_layer.cc
+++ b/chrome/browser/android/compositor/scene_layer/tab_strip_scene_layer.cc
@@ -51,12 +51,6 @@
   // while the incognito button and left/ride fade stay fixed. Put the new tab
   // button and tabs in a separate layer placed visually below the others.
   scrollable_strip_layer_->SetIsDrawable(true);
-  const bool tab_strip_improvements_enabled =
-      base::FeatureList::IsEnabled(chrome::android::kTabStripImprovements);
-  if (!tab_strip_improvements_enabled) {
-    scrollable_strip_layer_->AddChild(new_tab_button_);
-  }
-
   tab_strip_layer_->SetIsDrawable(true);
   tab_strip_layer_->AddChild(scrollable_strip_layer_);
 
@@ -65,12 +59,10 @@
   tab_strip_layer_->AddChild(model_selector_button_);
   tab_strip_layer_->AddChild(model_selector_button_background_);
   model_selector_button_background_->AddChild(model_selector_button_);
-  if (tab_strip_improvements_enabled) {
-    if (tab_strip_redesign_enabled) {
-      tab_strip_layer_->AddChild(new_tab_button_background_);
-    }
-    tab_strip_layer_->AddChild(new_tab_button_);
+  if (tab_strip_redesign_enabled) {
+    tab_strip_layer_->AddChild(new_tab_button_background_);
   }
+  tab_strip_layer_->AddChild(new_tab_button_);
   layer()->AddChild(tab_strip_layer_);
 }
 
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 1488b2b4..f684adf5 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
@@ -12,6 +12,8 @@
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.graphics.Color;
+import android.graphics.Insets;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.media.MediaPlayer;
 import android.net.Uri;
@@ -20,13 +22,14 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.text.TextUtils;
-import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.Pair;
+import android.view.Display;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.view.ViewGroup;
+import android.view.WindowInsets;
 import android.widget.AdapterView;
 import android.widget.ArrayAdapter;
 import android.widget.AutoCompleteTextView;
@@ -972,12 +975,17 @@
         @Px
         int res = 0;
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
-            Rect rect = this.getWindowManager().getMaximumWindowMetrics().getBounds();
-            res = Math.max(rect.width(), rect.height());
+            Insets navbarInsets =
+                    getWindowManager().getCurrentWindowMetrics().getWindowInsets().getInsets(
+                            WindowInsets.Type.navigationBars() | WindowInsets.Type.displayCutout());
+            int navbarWidth = navbarInsets.left + navbarInsets.right;
+            Rect windowBounds = getWindowManager().getCurrentWindowMetrics().getBounds();
+            res = windowBounds.width() - navbarWidth;
         } else {
-            DisplayMetrics displayMetrics = new DisplayMetrics();
-            this.getWindowManager().getDefaultDisplay().getRealMetrics(displayMetrics);
-            res = Math.max(displayMetrics.widthPixels, displayMetrics.heightPixels);
+            Display display = getWindowManager().getDefaultDisplay();
+            Point size = new Point();
+            display.getSize(size);
+            res = size.x;
         }
         return res;
     }
diff --git a/chrome/browser/apps/almanac_api_client/BUILD.gn b/chrome/browser/apps/almanac_api_client/BUILD.gn
index d421570e..c68a041 100644
--- a/chrome/browser/apps/almanac_api_client/BUILD.gn
+++ b/chrome/browser/apps/almanac_api_client/BUILD.gn
@@ -21,6 +21,7 @@
     "//chrome/browser/apps:user_type_filter",
     "//chrome/browser/profiles:profile",
     "//chrome/common:channel_info",
+    "//chromeos/ash/components/system",
     "//chromeos/version",
     "//components/language/core/browser",
     "//components/prefs",
diff --git a/chrome/browser/apps/almanac_api_client/device_info_manager.cc b/chrome/browser/apps/almanac_api_client/device_info_manager.cc
index afb4dfd..0717d16 100644
--- a/chrome/browser/apps/almanac_api_client/device_info_manager.cc
+++ b/chrome/browser/apps/almanac_api_client/device_info_manager.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/apps/almanac_api_client/device_info_manager.h"
 
 #include "base/functional/callback.h"
+#include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
 #include "base/task/task_traits.h"
 #include "base/task/thread_pool.h"
@@ -12,10 +13,12 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/channel_info.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "chromeos/version/version_loader.h"
 #include "components/language/core/browser/pref_names.h"
 #include "components/prefs/pref_service.h"
 #include "components/version_info/version_info.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace apps {
 
@@ -36,6 +39,7 @@
 //  - version_info.ash_chrome
 //  - user_type
 //  - channel
+//  - hardware_id
 // The method then asynchronously populates:
 //  - version_info.platform (OnPlatformVersionNumber)
 //  - model (OnModelInfo)
@@ -48,6 +52,12 @@
   device_info.user_type = apps::DetermineUserType(profile_);
   device_info.version_info.channel = chrome::GetChannel();
 
+  ash::system::StatisticsProvider* provider =
+      ash::system::StatisticsProvider::GetInstance();
+  absl::optional<base::StringPiece> hwid =
+      provider->GetMachineStatistic(ash::system::kHardwareClassKey);
+  device_info.hardware_id = std::string(hwid.value_or("unknown"));
+
   // Locale
   PrefService* prefs = profile_->GetPrefs();
   DCHECK(prefs);
@@ -89,6 +99,7 @@
   os << "Device Info: " << std::endl;
   os << "- Board: " << device_info.board << std::endl;
   os << "- Model: " << device_info.model << std::endl;
+  os << "- Hardware ID: " << device_info.hardware_id << std::endl;
   os << "- User Type: " << device_info.user_type << std::endl;
   os << "- Locale: " << device_info.locale << std::endl;
   os << device_info.version_info;
diff --git a/chrome/browser/apps/almanac_api_client/device_info_manager.h b/chrome/browser/apps/almanac_api_client/device_info_manager.h
index a9b9589..a10194f 100644
--- a/chrome/browser/apps/almanac_api_client/device_info_manager.h
+++ b/chrome/browser/apps/almanac_api_client/device_info_manager.h
@@ -41,6 +41,11 @@
   // The model of the device. e.g. "taniks"
   std::string model;
 
+  // The HWID which identifies the hardware configuration of the device. Set to
+  // "unknown" if not running on a ChromeOS device. e.g.
+  // "REDRIX-CLQY C4B-G4H-D3D-U7F-X54-I9N".
+  std::string hardware_id;
+
   // The user type of the profile currently running. e.g. "unmanaged"
   std::string user_type;
 
diff --git a/chrome/browser/apps/almanac_api_client/device_info_manager_unittest.cc b/chrome/browser/apps/almanac_api_client/device_info_manager_unittest.cc
index 816ec62..4e1ece8 100644
--- a/chrome/browser/apps/almanac_api_client/device_info_manager_unittest.cc
+++ b/chrome/browser/apps/almanac_api_client/device_info_manager_unittest.cc
@@ -11,6 +11,8 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/common/channel_info.h"
 #include "chrome/test/base/testing_profile.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
+#include "chromeos/ash/components/system/statistics_provider.h"
 #include "components/language/core/browser/pref_names.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/test/browser_task_environment.h"
@@ -28,12 +30,16 @@
   DeviceInfoManager* device_info_manager() {
     return device_info_manager_.get();
   }
+  ash::system::FakeStatisticsProvider* statistics_provider() {
+    return &fake_statistics_provider_;
+  }
 
  private:
   content::BrowserTaskEnvironment task_environment_;
   TestingProfile profile_;
 
   std::unique_ptr<DeviceInfoManager> device_info_manager_;
+  ash::system::ScopedFakeStatisticsProvider fake_statistics_provider_;
 };
 
 TEST_F(DeviceInfoManagerTest, CheckDeviceInfo) {
@@ -43,6 +49,9 @@
   )";
   base::test::ScopedChromeOSVersionInfo version(kLsbRelease, base::Time());
 
+  statistics_provider()->SetMachineStatistic(ash::system::kHardwareClassKey,
+                                             "FOOBAR D0G-F4N-C1UB");
+
   static constexpr char kTestLocale[] = "test_locale";
   profile()->GetPrefs()->SetString(language::prefs::kApplicationLocale,
                                    kTestLocale);
@@ -59,6 +68,7 @@
   ASSERT_FALSE(device_info.version_info.ash_chrome.empty());
   ASSERT_EQ(device_info.version_info.platform, "123.4.5");
   ASSERT_EQ(device_info.version_info.channel, chrome::GetChannel());
+  ASSERT_EQ(device_info.hardware_id, "FOOBAR D0G-F4N-C1UB");
   ASSERT_EQ(device_info.locale, kTestLocale);
 }
 
diff --git a/chrome/browser/apps/almanac_api_client/proto/client_context.proto b/chrome/browser/apps/almanac_api_client/proto/client_context.proto
index 9b97167..4498d44 100644
--- a/chrome/browser/apps/almanac_api_client/proto/client_context.proto
+++ b/chrome/browser/apps/almanac_api_client/proto/client_context.proto
@@ -63,8 +63,8 @@
   // The ChromeOS version information.
   optional Versions versions = 4;
 
-  // The SKU identifier for the device sending the request.
-  optional string sku_id = 5;
+  // The HWID identifier for the device sending the request.
+  optional string hardware_id = 5;
 }
 
 // Context about the user of the client device making this request.
diff --git a/chrome/browser/apps/app_preload_service/app_preload_server_connector.cc b/chrome/browser/apps/app_preload_service/app_preload_server_connector.cc
index 1920017..2435e68 100644
--- a/chrome/browser/apps/app_preload_service/app_preload_server_connector.cc
+++ b/chrome/browser/apps/app_preload_service/app_preload_server_connector.cc
@@ -103,6 +103,7 @@
       info.version_info.ash_chrome);
   device_context->mutable_versions()->set_chrome_os_platform(
       info.version_info.platform);
+  device_context->set_hardware_id(info.hardware_id);
 
   apps::proto::ClientUserContext* user_context =
       request_proto.mutable_user_context();
diff --git a/chrome/browser/apps/app_preload_service/app_preload_server_connector_unittest.cc b/chrome/browser/apps/app_preload_service/app_preload_server_connector_unittest.cc
index b61c74b2..955128b 100644
--- a/chrome/browser/apps/app_preload_service/app_preload_server_connector_unittest.cc
+++ b/chrome/browser/apps/app_preload_service/app_preload_server_connector_unittest.cc
@@ -57,6 +57,7 @@
   DeviceInfo device_info;
   device_info.board = "brya";
   device_info.model = "taniks";
+  device_info.hardware_id = "FOOBAR D0G-F4N-C1UB";
   device_info.user_type = "unmanaged";
   device_info.version_info.ash_chrome = "10.10.10";
   device_info.version_info.platform = "12345.0.0";
@@ -98,6 +99,7 @@
   EXPECT_EQ(request.device_context().versions().chrome_ash(), "10.10.10");
   EXPECT_EQ(request.device_context().versions().chrome_os_platform(),
             "12345.0.0");
+  EXPECT_EQ(request.device_context().hardware_id(), "FOOBAR D0G-F4N-C1UB");
 }
 
 TEST_F(AppPreloadServerConnectorTest, GetAppsForFirstLoginSuccessfulResponse) {
diff --git a/chrome/browser/apps/app_preload_service/app_preload_service_unittest.cc b/chrome/browser/apps/app_preload_service/app_preload_service_unittest.cc
index 20d43a3..dbfe6f7 100644
--- a/chrome/browser/apps/app_preload_service/app_preload_service_unittest.cc
+++ b/chrome/browser/apps/app_preload_service/app_preload_service_unittest.cc
@@ -20,6 +20,7 @@
 #include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/test/base/testing_profile.h"
+#include "chromeos/ash/components/system/fake_statistics_provider.h"
 #include "components/prefs/pref_service.h"
 #include "components/services/app_service/public/cpp/app_registry_cache.h"
 #include "components/services/app_service/public/cpp/app_update.h"
@@ -80,6 +81,7 @@
   base::test::ScopedFeatureList scoped_feature_list_;
   std::unique_ptr<TestingProfile> profile_;
   user_manager::ScopedUserManager scoped_user_manager_;
+  ash::system::ScopedFakeStatisticsProvider fake_statistics_provider_;
 };
 
 TEST_F(AppPreloadServiceTest, ServiceAccessPerProfile) {
diff --git a/chrome/browser/apps/guest_view/web_view_browsertest.cc b/chrome/browser/apps/guest_view/web_view_browsertest.cc
index c7a0f6c..8d615be 100644
--- a/chrome/browser/apps/guest_view/web_view_browsertest.cc
+++ b/chrome/browser/apps/guest_view/web_view_browsertest.cc
@@ -529,7 +529,7 @@
   std::unique_ptr<base::RunLoop> run_loop_;
 };
 
-class WebViewTestBase : public extensions::PlatformAppBrowserTest {
+class WebViewTest : public extensions::PlatformAppBrowserTest {
  protected:
   void SetUp() override {
     if (UsesFakeSpeech()) {
@@ -675,19 +675,19 @@
         return;
       }
       embedded_test_server()->RegisterRequestHandler(base::BindRepeating(
-          &WebViewTestBase::RedirectResponseHandler, kRedirectResponsePath,
+          &WebViewTest::RedirectResponseHandler, kRedirectResponsePath,
           embedded_test_server()->GetURL(kRedirectResponseFullPath)));
 
       embedded_test_server()->RegisterRequestHandler(base::BindRepeating(
-          &WebViewTestBase::EmptyResponseHandler, kEmptyResponsePath));
+          &WebViewTest::EmptyResponseHandler, kEmptyResponsePath));
 
       embedded_test_server()->RegisterRequestHandler(base::BindRepeating(
-          &WebViewTestBase::UserAgentResponseHandler,
+          &WebViewTest::UserAgentResponseHandler,
           kUserAgentRedirectResponsePath,
           embedded_test_server()->GetURL(kRedirectResponseFullPath)));
 
       embedded_test_server()->RegisterRequestHandler(base::BindRepeating(
-          &WebViewTestBase::CacheControlResponseHandler, kCacheResponsePath));
+          &WebViewTest::CacheControlResponseHandler, kCacheResponsePath));
 
       EmbeddedTestServerAcceptConnections();
     }
@@ -826,11 +826,11 @@
     return manager;
   }
 
-  WebViewTestBase() : guest_view_(nullptr), embedder_web_contents_(nullptr) {
+  WebViewTest() : guest_view_(nullptr), embedder_web_contents_(nullptr) {
     GuestViewManager::set_factory_for_testing(&factory_);
   }
 
-  ~WebViewTestBase() override = default;
+  ~WebViewTest() override = default;
 
   extensions::IdentifiabilityMetricsTestHelper
       identifiability_metrics_test_helper_;
@@ -855,64 +855,12 @@
   raw_ptr<content::WebContents, DanglingUntriaged> embedder_web_contents_;
 };
 
-class WebViewGuestSiteIsolationTest : public WebViewTestBase {
- public:
-  explicit WebViewGuestSiteIsolationTest(
-      bool site_isolation_for_guests_enabled) {
-    scoped_feature_list_.InitWithFeatureState(
-        features::kSiteIsolationForGuests, site_isolation_for_guests_enabled);
-  }
-  ~WebViewGuestSiteIsolationTest() override = default;
-
- private:
-  base::test::ScopedFeatureList scoped_feature_list_;
-};
-
-class WebViewTest : public WebViewGuestSiteIsolationTest,
-                    public testing::WithParamInterface<bool> {
- public:
-  WebViewTest()
-      : WebViewGuestSiteIsolationTest(
-            /*site_isolation_for_guests_enabled=*/GetParam()) {}
-
-  // Provides meaningful param names instead of /0 and /1.
-  static std::string DescribeParams(
-      const testing::TestParamInfo<ParamType>& info) {
-    return info.param ? "SiteIsolationForGuestsEnabled"
-                      : "SiteIsolationForGuestsDisabled";
-  }
-
- private:
-  base::test::ScopedFeatureList scoped_feature_list_;
-};
-
-INSTANTIATE_TEST_SUITE_P(WebViewTests,
-                         WebViewTest,
-                         testing::Bool(),
-                         WebViewTest::DescribeParams);
-
 // The following test suites are created to group tests based on specific
 // features of <webview>.
 using WebViewSizeTest = WebViewTest;
-INSTANTIATE_TEST_SUITE_P(WebViewTests,
-                         WebViewSizeTest,
-                         testing::Bool(),
-                         WebViewTest::DescribeParams);
 using WebViewVisibilityTest = WebViewTest;
-INSTANTIATE_TEST_SUITE_P(WebViewTests,
-                         WebViewVisibilityTest,
-                         testing::Bool(),
-                         WebViewTest::DescribeParams);
 using WebViewSpeechAPITest = WebViewTest;
-INSTANTIATE_TEST_SUITE_P(WebViewTests,
-                         WebViewSpeechAPITest,
-                         testing::Bool(),
-                         WebViewTest::DescribeParams);
 using WebViewAccessibilityTest = WebViewTest;
-INSTANTIATE_TEST_SUITE_P(WebViewTests,
-                         WebViewAccessibilityTest,
-                         testing::Bool(),
-                         WebViewTest::DescribeParams);
 
 // Used to test that enterprise policy can revert MPArch related changes. For
 // ease of testing, instead of further parameterizing the tests which actually
@@ -952,42 +900,22 @@
       extensions::AreWebviewMPArchBehaviorsEnabled(browser()->profile()));
 }
 
-class WebViewNewWindowTest
-    : public WebViewTestBase,
-      public testing::WithParamInterface<testing::tuple<bool, bool>> {
+class WebViewNewWindowTest : public WebViewTest,
+                             public testing::WithParamInterface<bool> {
  public:
   WebViewNewWindowTest() {
-    auto [is_site_isolation_enabled, mparch_newwindow_restriction] = GetParam();
-    std::vector<base::test::FeatureRef> enabled_features, disabled_features;
-    if (is_site_isolation_enabled) {
-      enabled_features.push_back(features::kSiteIsolationForGuests);
-    } else {
-      disabled_features.push_back(features::kSiteIsolationForGuests);
-    }
-
-    if (mparch_newwindow_restriction) {
-      enabled_features.push_back(
-          extensions_features::kWebviewTagMPArchBehavior);
-    } else {
-      disabled_features.push_back(
-          extensions_features::kWebviewTagMPArchBehavior);
-    }
-
-    scoped_feature_list_.InitWithFeatures(std::move(enabled_features),
-                                          std::move(disabled_features));
+    scoped_feature_list_.InitWithFeatureState(
+        extensions_features::kWebviewTagMPArchBehavior, /*enabled=*/GetParam());
   }
   ~WebViewNewWindowTest() override = default;
 
   static std::string DescribeParams(
       const testing::TestParamInfo<ParamType>& info) {
-    auto [is_site_isolation_enabled, mparch_newwindow_restriction] = info.param;
-    return base::StringPrintf(
-        "SiteIsolationForGuests%s_NewWindow%s",
-        is_site_isolation_enabled ? "Enabled" : "Disabled",
-        mparch_newwindow_restriction ? "Restricted" : "Legacy");
+    return base::StringPrintf("NewWindow%s",
+                              info.param ? "Restricted" : "Legacy");
   }
 
-  bool IsNewWindowRestricted() { return testing::get<1>(GetParam()); }
+  bool IsNewWindowRestricted() { return GetParam(); }
 
  private:
   base::test::ScopedFeatureList scoped_feature_list_;
@@ -995,7 +923,7 @@
 
 INSTANTIATE_TEST_SUITE_P(WebViewNewWindowTests,
                          WebViewNewWindowTest,
-                         testing::Combine(testing::Bool(), testing::Bool()),
+                         testing::Bool(),
                          WebViewNewWindowTest::DescribeParams);
 
 class WebViewDPITest : public WebViewTest {
@@ -1008,10 +936,6 @@
 
   static float scale() { return 2.0f; }
 };
-INSTANTIATE_TEST_SUITE_P(WebViewTests,
-                         WebViewDPITest,
-                         testing::Bool(),
-                         WebViewTest::DescribeParams);
 
 class WebContentsAudioMutedObserver : public content::WebContentsObserver {
  public:
@@ -1066,7 +990,7 @@
   std::unique_ptr<base::RunLoop> run_loop_;
 };
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, AudibilityStatePropagates) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, AudibilityStatePropagates) {
   ASSERT_TRUE(StartEmbeddedTestServer());  // For serving guest audio.
 
   LoadAppWithGuest("web_view/simple");
@@ -1108,7 +1032,7 @@
   EXPECT_FALSE(guest->IsCurrentlyAudible());
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, WebViewRespectsInsets) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, WebViewRespectsInsets) {
   LoadAppWithGuest("web_view/simple");
 
   content::RenderWidgetHostView* guest_host_view =
@@ -1124,7 +1048,7 @@
   EXPECT_EQ(expected.size(), size_after);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, AudioMutesWhileAttached) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, AudioMutesWhileAttached) {
   LoadAppWithGuest("web_view/simple");
 
   content::WebContents* embedder = GetEmbedderWebContents();
@@ -1142,7 +1066,7 @@
   EXPECT_FALSE(guest->IsAudioMuted());
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, AudioMutesOnAttach) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, AudioMutesOnAttach) {
   LoadAndLaunchPlatformApp("web_view/app_creates_webview",
                            "WebViewTest.LAUNCHED");
   content::WebContents* embedder = GetEmbedderWebContents();
@@ -1162,7 +1086,7 @@
   EXPECT_TRUE(guest->IsAudioMuted());
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, AudioStateJavascriptAPI) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, AudioStateJavascriptAPI) {
   base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
       switches::kAutoplayPolicy,
       switches::autoplay::kNoUserGestureRequiredPolicy);
@@ -1174,7 +1098,7 @@
 }
 
 // Test that WebView does not override autoplay policy.
-IN_PROC_BROWSER_TEST_P(WebViewTest, AutoplayPolicy) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, AutoplayPolicy) {
   base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
       switches::kAutoplayPolicy,
       switches::autoplay::kDocumentUserActivationRequiredPolicy);
@@ -1186,7 +1110,7 @@
 }
 
 // This test exercises the webview spatial navigation API
-IN_PROC_BROWSER_TEST_P(WebViewTest, SpatialNavigationJavascriptAPI) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, SpatialNavigationJavascriptAPI) {
   base::CommandLine::ForCurrentProcess()->AppendSwitch(
       switches::kEnableSpatialNavigation);
 
@@ -1239,7 +1163,7 @@
 
 // This test verifies that hiding the guest triggers visibility change
 // notifications.
-IN_PROC_BROWSER_TEST_P(WebViewVisibilityTest, GuestVisibilityChanged) {
+IN_PROC_BROWSER_TEST_F(WebViewVisibilityTest, GuestVisibilityChanged) {
   LoadAppWithGuest("web_view/visibility_changed");
 
   base::RunLoop run_loop;
@@ -1253,7 +1177,7 @@
 }
 
 // This test verifies that hiding the embedder also hides the guest.
-IN_PROC_BROWSER_TEST_P(WebViewVisibilityTest, EmbedderVisibilityChanged) {
+IN_PROC_BROWSER_TEST_F(WebViewVisibilityTest, EmbedderVisibilityChanged) {
   LoadAppWithGuest("web_view/visibility_changed");
 
   base::RunLoop run_loop;
@@ -1268,7 +1192,7 @@
 
 // This test verifies that reloading the embedder reloads the guest (and doest
 // not crash).
-IN_PROC_BROWSER_TEST_P(WebViewTest, ReloadEmbedder) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, ReloadEmbedder) {
   // Just load a guest from other test, we do not want to add a separate
   // platform_app for this test.
   LoadAppWithGuest("web_view/visibility_changed");
@@ -1281,21 +1205,21 @@
 
 // This test ensures JavaScript errors ("Cannot redefine property") do not
 // happen when a <webview> is removed from DOM and added back.
-IN_PROC_BROWSER_TEST_P(WebViewTest, AddRemoveWebView_AddRemoveWebView) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, AddRemoveWebView_AddRemoveWebView) {
   ASSERT_TRUE(StartEmbeddedTestServer());  // For serving guest pages.
   ASSERT_TRUE(RunExtensionTest("platform_apps/web_view/addremove",
                                {.launch_as_platform_app = true}))
       << message_;
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewSizeTest, AutoSize) {
+IN_PROC_BROWSER_TEST_F(WebViewSizeTest, AutoSize) {
   ASSERT_TRUE(RunExtensionTest("platform_apps/web_view/autosize",
                                {.launch_as_platform_app = true}))
       << message_;
 }
 
 // Test for http://crbug.com/419611.
-IN_PROC_BROWSER_TEST_P(WebViewTest, DisplayNoneSetSrc) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, DisplayNoneSetSrc) {
   LoadAndLaunchPlatformApp("web_view/display_none_set_src",
                            "WebViewTest.LAUNCHED");
   // Navigate the guest while it's in "display: none" state.
@@ -1313,65 +1237,65 @@
 
 // Checks that {allFrames: true} injects script correctly to subframes
 // inside <webview>.
-IN_PROC_BROWSER_TEST_P(WebViewTest, ExecuteScript) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, ExecuteScript) {
   ASSERT_TRUE(RunExtensionTest(
       "platform_apps/web_view/common",
       {.custom_arg = "execute_script", .launch_as_platform_app = true}))
       << message_;
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, ExecuteCode) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, ExecuteCode) {
   ASSERT_TRUE(RunExtensionTest(
       "platform_apps/web_view/common",
       {.custom_arg = "execute_code", .launch_as_platform_app = true}))
       << message_;
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewSizeTest, Shim_TestAutosizeAfterNavigation) {
+IN_PROC_BROWSER_TEST_F(WebViewSizeTest, Shim_TestAutosizeAfterNavigation) {
   TestHelper("testAutosizeAfterNavigation", "web_view/shim", NO_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestAllowTransparencyAttribute) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestAllowTransparencyAttribute) {
   TestHelper("testAllowTransparencyAttribute", "web_view/shim", NO_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewDPITest, Shim_TestAutosizeHeight) {
+IN_PROC_BROWSER_TEST_F(WebViewDPITest, Shim_TestAutosizeHeight) {
   TestHelper("testAutosizeHeight", "web_view/shim", NO_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewSizeTest, Shim_TestAutosizeHeight) {
+IN_PROC_BROWSER_TEST_F(WebViewSizeTest, Shim_TestAutosizeHeight) {
   TestHelper("testAutosizeHeight", "web_view/shim", NO_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewDPITest, Shim_TestAutosizeBeforeNavigation) {
+IN_PROC_BROWSER_TEST_F(WebViewDPITest, Shim_TestAutosizeBeforeNavigation) {
   TestHelper("testAutosizeBeforeNavigation", "web_view/shim", NO_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewSizeTest, Shim_TestAutosizeBeforeNavigation) {
+IN_PROC_BROWSER_TEST_F(WebViewSizeTest, Shim_TestAutosizeBeforeNavigation) {
   TestHelper("testAutosizeBeforeNavigation", "web_view/shim", NO_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewDPITest, Shim_TestAutosizeRemoveAttributes) {
+IN_PROC_BROWSER_TEST_F(WebViewDPITest, Shim_TestAutosizeRemoveAttributes) {
   TestHelper("testAutosizeRemoveAttributes", "web_view/shim", NO_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewSizeTest, Shim_TestAutosizeRemoveAttributes) {
+IN_PROC_BROWSER_TEST_F(WebViewSizeTest, Shim_TestAutosizeRemoveAttributes) {
   TestHelper("testAutosizeRemoveAttributes", "web_view/shim", NO_TEST_SERVER);
 }
 
 // This test is disabled due to being flaky. http://crbug.com/282116
-IN_PROC_BROWSER_TEST_P(WebViewSizeTest,
+IN_PROC_BROWSER_TEST_F(WebViewSizeTest,
                        DISABLED_Shim_TestAutosizeWithPartialAttributes) {
   TestHelper("testAutosizeWithPartialAttributes",
              "web_view/shim",
              NO_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestAPIMethodExistence) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestAPIMethodExistence) {
   TestHelper("testAPIMethodExistence", "web_view/shim", NO_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest,
+IN_PROC_BROWSER_TEST_F(WebViewTest,
                        Shim_TestCustomElementCallbacksInaccessible) {
   TestHelper("testCustomElementCallbacksInaccessible", "web_view/shim",
              NO_TEST_SERVER);
@@ -1379,23 +1303,23 @@
 
 // Tests the existence of WebRequest API event objects on the request
 // object, on the webview element, and hanging directly off webview.
-IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestWebRequestAPIExistence) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestWebRequestAPIExistence) {
   TestHelper("testWebRequestAPIExistence", "web_view/shim", NO_TEST_SERVER);
 }
 
 // Tests that addListener call succeeds on webview's WebRequest API events.
-IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestWebRequestAPIAddListener) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestWebRequestAPIAddListener) {
   TestHelper("testWebRequestAPIAddListener", "web_view/shim", NO_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestWebRequestAPIErrorOccurred) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestWebRequestAPIErrorOccurred) {
   TestHelper("testWebRequestAPIErrorOccurred", "web_view/shim", NO_TEST_SERVER);
 }
 
 #if defined(USE_AURA)
 // Test validates that select tag can be shown and hidden in webview safely
 // using quick touch.
-IN_PROC_BROWSER_TEST_P(WebViewTest, SelectShowHide) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, SelectShowHide) {
   LoadAppWithGuest("web_view/select");
 
   content::WebContents* embedder_contents = GetFirstAppWindowWebContents();
@@ -1428,23 +1352,23 @@
 }
 #endif
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestChromeExtensionURL) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestChromeExtensionURL) {
   TestHelper("testChromeExtensionURL", "web_view/shim", NO_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestChromeExtensionRelativePath) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestChromeExtensionRelativePath) {
   TestHelper("testChromeExtensionRelativePath",
              "web_view/shim",
              NO_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest,
+IN_PROC_BROWSER_TEST_F(WebViewTest,
                        Shim_TestContentInitiatedNavigationToDataUrlBlocked) {
   TestHelper("testContentInitiatedNavigationToDataUrlBlocked", "web_view/shim",
              NO_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestDisplayNoneWebviewLoad) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestDisplayNoneWebviewLoad) {
   TestHelper("testDisplayNoneWebviewLoad", "web_view/shim", NO_TEST_SERVER);
 }
 
@@ -1457,85 +1381,85 @@
   Shim_TestDisplayNoneWebviewRemoveChild
 #endif
 // Flaky on most desktop platforms: https://crbug.com/1115106.
-IN_PROC_BROWSER_TEST_P(WebViewTest,
+IN_PROC_BROWSER_TEST_F(WebViewTest,
                        MAYBE_Shim_TestDisplayNoneWebviewRemoveChild) {
   TestHelper("testDisplayNoneWebviewRemoveChild",
              "web_view/shim", NO_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestDisplayBlock) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestDisplayBlock) {
   TestHelper("testDisplayBlock", "web_view/shim", NO_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest,
+IN_PROC_BROWSER_TEST_F(WebViewTest,
                        Shim_TestInlineScriptFromAccessibleResources) {
   TestHelper("testInlineScriptFromAccessibleResources",
              "web_view/shim",
              NO_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestInvalidChromeExtensionURL) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestInvalidChromeExtensionURL) {
   TestHelper("testInvalidChromeExtensionURL", "web_view/shim", NO_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestEventName) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestEventName) {
   content::ScopedAllowRendererCrashes scoped_allow_renderer_crashes;
   TestHelper("testEventName", "web_view/shim", NO_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestOnEventProperty) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestOnEventProperty) {
   TestHelper("testOnEventProperties", "web_view/shim", NO_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestLoadProgressEvent) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestLoadProgressEvent) {
   TestHelper("testLoadProgressEvent", "web_view/shim", NO_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestDestroyOnEventListener) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestDestroyOnEventListener) {
   TestHelper("testDestroyOnEventListener", "web_view/shim", NO_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestCannotMutateEventName) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestCannotMutateEventName) {
   TestHelper("testCannotMutateEventName", "web_view/shim", NO_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestPartitionChangeAfterNavigation) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestPartitionChangeAfterNavigation) {
   TestHelper("testPartitionChangeAfterNavigation",
              "web_view/shim",
              NO_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest,
+IN_PROC_BROWSER_TEST_F(WebViewTest,
                        Shim_TestPartitionRemovalAfterNavigationFails) {
   TestHelper("testPartitionRemovalAfterNavigationFails",
              "web_view/shim",
              NO_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestAddContentScript) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestAddContentScript) {
   TestHelper("testAddContentScript", "web_view/shim", NEEDS_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestAddMultipleContentScripts) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestAddMultipleContentScripts) {
   TestHelper("testAddMultipleContentScripts", "web_view/shim",
              NEEDS_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(
+IN_PROC_BROWSER_TEST_F(
     WebViewTest,
     Shim_TestAddContentScriptWithSameNameShouldOverwriteTheExistingOne) {
   TestHelper("testAddContentScriptWithSameNameShouldOverwriteTheExistingOne",
              "web_view/shim", NEEDS_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(
+IN_PROC_BROWSER_TEST_F(
     WebViewTest,
     Shim_TestAddContentScriptToOneWebViewShouldNotInjectToTheOtherWebView) {
   TestHelper("testAddContentScriptToOneWebViewShouldNotInjectToTheOtherWebView",
              "web_view/shim", NEEDS_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestAddAndRemoveContentScripts) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestAddAndRemoveContentScripts) {
   TestHelper("testAddAndRemoveContentScripts", "web_view/shim",
              NEEDS_TEST_SERVER);
 }
@@ -1550,7 +1474,7 @@
 #endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC)
 }
 
-IN_PROC_BROWSER_TEST_P(
+IN_PROC_BROWSER_TEST_F(
     WebViewTest,
     Shim_TestContentScriptIsInjectedAfterTerminateAndReloadWebView) {
   content::ScopedAllowRendererCrashes scoped_allow_renderer_crashes;
@@ -1558,34 +1482,34 @@
              "web_view/shim", NEEDS_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest,
+IN_PROC_BROWSER_TEST_F(WebViewTest,
                        Shim_TestContentScriptExistsAsLongAsWebViewTagExists) {
   TestHelper("testContentScriptExistsAsLongAsWebViewTagExists", "web_view/shim",
              NEEDS_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestAddContentScriptWithCode) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestAddContentScriptWithCode) {
   TestHelper("testAddContentScriptWithCode", "web_view/shim",
              NEEDS_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(
+IN_PROC_BROWSER_TEST_F(
     WebViewTest,
     Shim_TestAddMultipleContentScriptsWithCodeAndCheckGeneratedScriptUrl) {
   TestHelper("testAddMultipleContentScriptsWithCodeAndCheckGeneratedScriptUrl",
              "web_view/shim", NEEDS_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestExecuteScriptFail) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestExecuteScriptFail) {
   TestHelper("testExecuteScriptFail", "web_view/shim", NEEDS_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestExecuteScript) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestExecuteScript) {
   TestHelper("testExecuteScript", "web_view/shim", NO_TEST_SERVER);
 }
 
 // Flaky and likely not testing the right assertion. https://crbug.com/703727
-IN_PROC_BROWSER_TEST_P(
+IN_PROC_BROWSER_TEST_F(
     WebViewTest,
     DISABLED_Shim_TestExecuteScriptIsAbortedWhenWebViewSourceIsChanged) {
   TestHelper("testExecuteScriptIsAbortedWhenWebViewSourceIsChanged",
@@ -1593,7 +1517,7 @@
              NO_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(
+IN_PROC_BROWSER_TEST_F(
     WebViewTest,
     Shim_TestExecuteScriptIsAbortedWhenWebViewSourceIsInvalid) {
   TestHelper("testExecuteScriptIsAbortedWhenWebViewSourceIsInvalid",
@@ -1601,32 +1525,32 @@
              NO_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestTerminateAfterExit) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestTerminateAfterExit) {
   content::ScopedAllowRendererCrashes scoped_allow_renderer_crashes;
   TestHelper("testTerminateAfterExit", "web_view/shim", NO_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestAssignSrcAfterCrash) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestAssignSrcAfterCrash) {
   content::ScopedAllowRendererCrashes scoped_allow_renderer_crashes;
   TestHelper("testAssignSrcAfterCrash", "web_view/shim", NO_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest,
+IN_PROC_BROWSER_TEST_F(WebViewTest,
                        Shim_TestNavOnConsecutiveSrcAttributeChanges) {
   TestHelper("testNavOnConsecutiveSrcAttributeChanges",
              "web_view/shim",
              NO_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestNavOnSrcAttributeChange) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestNavOnSrcAttributeChange) {
   TestHelper("testNavOnSrcAttributeChange", "web_view/shim", NO_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestNavigateAfterResize) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestNavigateAfterResize) {
   TestHelper("testNavigateAfterResize", "web_view/shim", NO_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestNestedCrossOriginSubframes) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestNestedCrossOriginSubframes) {
   TestHelper("testNestedCrossOriginSubframes",
              "web_view/shim", NEEDS_TEST_SERVER);
 }
@@ -1637,15 +1561,15 @@
 #else
 #define MAYBE_Shim_TestNestedSubframes Shim_TestNestedSubframes
 #endif
-IN_PROC_BROWSER_TEST_P(WebViewTest, MAYBE_Shim_TestNestedSubframes) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, MAYBE_Shim_TestNestedSubframes) {
   TestHelper("testNestedSubframes", "web_view/shim", NO_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestRemoveSrcAttribute) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestRemoveSrcAttribute) {
   TestHelper("testRemoveSrcAttribute", "web_view/shim", NO_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestReassignSrcAttribute) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestReassignSrcAttribute) {
   TestHelper("testReassignSrcAttribute", "web_view/shim", NO_TEST_SERVER);
 }
 
@@ -1708,15 +1632,8 @@
   EXPECT_EQ(guest_instance1->GetStoragePartitionConfig(),
             guest_instance2->GetStoragePartitionConfig());
 
-  // Without <webview> site isolation, both guests should be in the same
-  // SiteInstance, even in this `opener_suppressed` case which typically places
-  // the new window in a new BrowsingInstance. With site isolation, the new
-  // guest should be in a different BrowsingInstance.
-  if (content::SiteIsolationPolicy::IsSiteIsolationForGuestsEnabled()) {
-    EXPECT_FALSE(guest_instance1->IsRelatedSiteInstance(guest_instance2));
-  } else {
-    EXPECT_EQ(guest_instance1, guest_instance2);
-  }
+  // The new guest should be in a different BrowsingInstance.
+  EXPECT_FALSE(guest_instance1->IsRelatedSiteInstance(guest_instance2));
 
   // Check that the source SiteInstance used when the first guest opened the
   // new noreferrer window is also a guest SiteInstance in the same
@@ -1800,7 +1717,7 @@
 // with two iframes and a webview within each of the iframes. The
 // purpose of the test is to ensure that webRequest subevent names are
 // unique across all webviews within the app.
-IN_PROC_BROWSER_TEST_P(WebViewTest, TwoIframesWebRequest) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, TwoIframesWebRequest) {
   ASSERT_TRUE(StartEmbeddedTestServer());  // For serving webview pages.
   ExtensionTestMessageListener ready1("ready1", ReplyBehavior::kWillReply);
   ExtensionTestMessageListener ready2("ready2", ReplyBehavior::kWillReply);
@@ -2005,121 +1922,121 @@
             unattached_guest->web_contents()->GetResponsibleWebContents());
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestContentLoadEvent) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestContentLoadEvent) {
   TestHelper("testContentLoadEvent", "web_view/shim", NO_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestContentLoadEventWithDisplayNone) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestContentLoadEventWithDisplayNone) {
   TestHelper("testContentLoadEventWithDisplayNone",
              "web_view/shim",
              NO_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestDeclarativeWebRequestAPI) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestDeclarativeWebRequestAPI) {
   TestHelper("testDeclarativeWebRequestAPI",
              "web_view/shim",
              NEEDS_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest,
+IN_PROC_BROWSER_TEST_F(WebViewTest,
                        Shim_TestDeclarativeWebRequestAPISendMessage) {
   TestHelper("testDeclarativeWebRequestAPISendMessage",
              "web_view/shim",
              NEEDS_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(
+IN_PROC_BROWSER_TEST_F(
     WebViewTest,
     Shim_TestDeclarativeWebRequestAPISendMessageSecondWebView) {
   TestHelper("testDeclarativeWebRequestAPISendMessageSecondWebView",
              "web_view/shim", NEEDS_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestWebRequestAPI) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestWebRequestAPI) {
   TestHelper("testWebRequestAPI", "web_view/shim", NEEDS_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestWebRequestAPIOnlyForInstance) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestWebRequestAPIOnlyForInstance) {
   TestHelper("testWebRequestAPIOnlyForInstance", "web_view/shim",
              NEEDS_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestWebRequestAPIWithHeaders) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestWebRequestAPIWithHeaders) {
   TestHelper("testWebRequestAPIWithHeaders",
              "web_view/shim",
              NEEDS_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestWebRequestAPIGoogleProperty) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestWebRequestAPIGoogleProperty) {
   TestHelper("testWebRequestAPIGoogleProperty",
              "web_view/shim",
              NO_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest,
+IN_PROC_BROWSER_TEST_F(WebViewTest,
                        Shim_TestWebRequestListenerSurvivesReparenting) {
   TestHelper("testWebRequestListenerSurvivesReparenting",
              "web_view/shim",
              NEEDS_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestLoadStartLoadRedirect) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestLoadStartLoadRedirect) {
   TestHelper("testLoadStartLoadRedirect", "web_view/shim", NEEDS_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest,
+IN_PROC_BROWSER_TEST_F(WebViewTest,
                        Shim_TestLoadAbortChromeExtensionURLWrongPartition) {
   TestHelper("testLoadAbortChromeExtensionURLWrongPartition",
              "web_view/shim",
              NO_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestLoadAbortEmptyResponse) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestLoadAbortEmptyResponse) {
   TestHelper("testLoadAbortEmptyResponse", "web_view/shim", NEEDS_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestLoadAbortIllegalChromeURL) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestLoadAbortIllegalChromeURL) {
   TestHelper("testLoadAbortIllegalChromeURL",
              "web_view/shim",
              NO_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestLoadAbortIllegalFileURL) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestLoadAbortIllegalFileURL) {
   TestHelper("testLoadAbortIllegalFileURL", "web_view/shim", NO_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestLoadAbortIllegalJavaScriptURL) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestLoadAbortIllegalJavaScriptURL) {
   TestHelper("testLoadAbortIllegalJavaScriptURL",
              "web_view/shim",
              NO_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestLoadAbortInvalidNavigation) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestLoadAbortInvalidNavigation) {
   TestHelper("testLoadAbortInvalidNavigation", "web_view/shim", NO_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestLoadAbortNonWebSafeScheme) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestLoadAbortNonWebSafeScheme) {
   TestHelper("testLoadAbortNonWebSafeScheme", "web_view/shim", NO_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestReload) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestReload) {
   TestHelper("testReload", "web_view/shim", NO_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestReloadAfterTerminate) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestReloadAfterTerminate) {
   content::ScopedAllowRendererCrashes scoped_allow_renderer_crashes;
   TestHelper("testReloadAfterTerminate", "web_view/shim", NO_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestGetProcessId) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestGetProcessId) {
   TestHelper("testGetProcessId", "web_view/shim", NO_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewVisibilityTest, Shim_TestHiddenBeforeNavigation) {
+IN_PROC_BROWSER_TEST_F(WebViewVisibilityTest, Shim_TestHiddenBeforeNavigation) {
   TestHelper("testHiddenBeforeNavigation", "web_view/shim", NO_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestRemoveWebviewOnExit) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestRemoveWebviewOnExit) {
   content::ScopedAllowRendererCrashes scoped_allow_renderer_crashes;
   ASSERT_TRUE(StartEmbeddedTestServer());  // For serving guest pages.
 
@@ -2171,67 +2088,50 @@
 
 // Remove <webview> immediately after navigating it.
 // This is a regression test for http://crbug.com/276023.
-IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestRemoveWebviewAfterNavigation) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestRemoveWebviewAfterNavigation) {
   TestHelper("testRemoveWebviewAfterNavigation",
              "web_view/shim",
              NO_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestNavigationToExternalProtocol) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestNavigationToExternalProtocol) {
   TestHelper("testNavigationToExternalProtocol",
              "web_view/shim",
              NO_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewSizeTest,
+IN_PROC_BROWSER_TEST_F(WebViewSizeTest,
                        Shim_TestResizeWebviewWithDisplayNoneResizesContent) {
   TestHelper("testResizeWebviewWithDisplayNoneResizesContent",
              "web_view/shim",
              NO_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewSizeTest, Shim_TestResizeWebviewResizesContent) {
+IN_PROC_BROWSER_TEST_F(WebViewSizeTest, Shim_TestResizeWebviewResizesContent) {
   TestHelper("testResizeWebviewResizesContent",
              "web_view/shim",
              NO_TEST_SERVER);
 }
 
-class WebViewSSLErrorTest
-    : public WebViewTestBase,
-      public testing::WithParamInterface<testing::tuple<bool, bool>> {
+class WebViewSSLErrorTest : public WebViewTest,
+                            public testing::WithParamInterface<bool> {
  public:
   WebViewSSLErrorTest() {
-    auto [is_site_isolation_enabled, use_interstitials] = GetParam();
-    std::vector<base::test::FeatureRef> enabled_features, disabled_features;
-    if (is_site_isolation_enabled) {
-      enabled_features.push_back(features::kSiteIsolationForGuests);
-    } else {
-      disabled_features.push_back(features::kSiteIsolationForGuests);
-    }
-
-    if (use_interstitials) {
-      disabled_features.push_back(
-          extensions_features::kWebviewTagMPArchBehavior);
-    } else {
-      enabled_features.push_back(
-          extensions_features::kWebviewTagMPArchBehavior);
-    }
-
-    scoped_feature_list_.InitWithFeatures(std::move(enabled_features),
-                                          std::move(disabled_features));
+    bool use_interstitials = GetParam();
+    scoped_feature_list_.InitWithFeatureState(
+        extensions_features::kWebviewTagMPArchBehavior,
+        /*enabled=*/!use_interstitials);
   }
   ~WebViewSSLErrorTest() override = default;
 
   static std::string DescribeParams(
       const testing::TestParamInfo<ParamType>& info) {
-    auto [is_site_isolation_enabled, use_interstitials] = info.param;
-    return base::StringPrintf(
-        "SiteIsolationForGuests%s_Use%s",
-        is_site_isolation_enabled ? "Enabled" : "Disabled",
-        use_interstitials ? "Interstitial" : "ErrorPage");
+    bool use_interstitials = info.param;
+    return base::StringPrintf("Use%s",
+                              use_interstitials ? "Interstitial" : "ErrorPage");
   }
 
-  bool UseInterstitials() { return testing::get<1>(GetParam()); }
+  bool UseInterstitials() { return GetParam(); }
 
   // Loads the guest at "web_view/ssl/https_page.html" with an SSL error, and
   // asserts the security interstitial is not displayed for guest through the
@@ -2317,7 +2217,7 @@
 
 INSTANTIATE_TEST_SUITE_P(WebViewSSLErrorTests,
                          WebViewSSLErrorTest,
-                         testing::Combine(testing::Bool(), testing::Bool()),
+                         testing::Bool(),
                          WebViewSSLErrorTest::DescribeParams);
 
 // Test makes sure that an error document is shown in `<webview>` with an SSL
@@ -2449,12 +2349,7 @@
       safe_browsing_factory_;
 };
 
-INSTANTIATE_TEST_SUITE_P(WebViewTests,
-                         WebViewSafeBrowsingTest,
-                         testing::Bool(),
-                         WebViewTest::DescribeParams);
-
-IN_PROC_BROWSER_TEST_P(WebViewSafeBrowsingTest,
+IN_PROC_BROWSER_TEST_F(WebViewSafeBrowsingTest,
                        Shim_TestLoadAbortSafeBrowsing) {
   // We start the test server here, instead of in TestHelper, because we need
   // to know the URL to treat as dangerous before running the rest of the test.
@@ -2476,7 +2371,7 @@
 
 INSTANTIATE_TEST_SUITE_P(WebViewHttpsFirstModeTests,
                          WebViewHttpsFirstModeTest,
-                         testing::Combine(testing::Bool(), testing::Bool()),
+                         testing::Bool(),
                          WebViewHttpsFirstModeTest::DescribeParams);
 
 // Tests that loading an HTTPS page in a guest <webview> with HTTPS-First Mode
@@ -2528,7 +2423,7 @@
   ASSERT_FALSE(IsShowingInterstitial(GetFirstAppWindowWebContents()));
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, ShimSrcAttribute) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, ShimSrcAttribute) {
   ASSERT_TRUE(RunExtensionTest("platform_apps/web_view/src_attribute",
                                {.launch_as_platform_app = true}))
       << message_;
@@ -2537,7 +2432,7 @@
 // This test verifies that prerendering has been disabled inside <webview>.
 // This test is here rather than in PrerenderBrowserTest for testing convenience
 // only. If it breaks then this is a bug in the prerenderer.
-IN_PROC_BROWSER_TEST_P(WebViewTest, NoPrerenderer) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, NoPrerenderer) {
   ASSERT_TRUE(StartEmbeddedTestServer());
   LoadAndLaunchPlatformApp("web_view/noprerenderer", "guest-loaded");
   auto* guest_rfh =
@@ -2553,7 +2448,7 @@
 
 // Verify that existing <webview>'s are detected when the task manager starts
 // up.
-IN_PROC_BROWSER_TEST_P(WebViewTest, TaskManagerExistingWebView) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, TaskManagerExistingWebView) {
   ASSERT_TRUE(StartEmbeddedTestServer());
 
   LoadAndLaunchPlatformApp("web_view/task_manager", "guest-loaded");
@@ -2576,7 +2471,7 @@
 }
 
 // Verify that the task manager notices the creation of new <webview>'s.
-IN_PROC_BROWSER_TEST_P(WebViewTest, TaskManagerNewWebView) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, TaskManagerNewWebView) {
   ASSERT_TRUE(StartEmbeddedTestServer());
 
   chrome::ShowTaskManager(browser());  // Show task manager BEFORE guest loads.
@@ -2602,7 +2497,7 @@
 // the main browser window to a page that sets a cookie and loads an app with
 // multiple webview tags. Each tag sets a cookie and the test checks the proper
 // storage isolation is enforced.
-IN_PROC_BROWSER_TEST_P(WebViewTest, CookieIsolation) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, CookieIsolation) {
   ASSERT_TRUE(StartEmbeddedTestServer());
   // Navigate the browser to a page which writes a sample cookie
   // The cookie is "testCookie=1"
@@ -2628,7 +2523,7 @@
 
 // This tests that in-memory storage partitions are reset on browser restart,
 // but persistent ones maintain state for cookies and HTML5 storage.
-IN_PROC_BROWSER_TEST_P(WebViewTest, PRE_StoragePersistence) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, PRE_StoragePersistence) {
   ASSERT_TRUE(StartEmbeddedTestServer());
   // We don't care where the main browser is on this test.
   ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GURL("about:blank")));
@@ -2641,7 +2536,7 @@
 
 // This is the post-reset portion of the StoragePersistence test.  See
 // PRE_StoragePersistence for main comment.
-IN_PROC_BROWSER_TEST_P(WebViewTest, StoragePersistence) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, StoragePersistence) {
   ASSERT_TRUE(StartEmbeddedTestServer());
   // We don't care where the main browser is on this test.
   ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GURL("about:blank")));
@@ -2656,7 +2551,7 @@
 // loads an app with multiple webview tags and each tag sets DOM storage
 // entries, which the test checks to ensure proper storage isolation is
 // enforced.
-IN_PROC_BROWSER_TEST_P(WebViewTest, DOMStorageIsolation) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, DOMStorageIsolation) {
   ASSERT_TRUE(StartEmbeddedTestServer());
 
   GURL navigate_to_url = embedded_test_server()->GetURL(
@@ -2693,7 +2588,7 @@
 // in the same storage partition stopped being able to find each other).
 // This is also a regression test for https://crbug.com/802278 (setting of
 // a guestview as an opener should not leak any memory).
-IN_PROC_BROWSER_TEST_P(WebViewTest, FindabilityIsolation) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, FindabilityIsolation) {
   ASSERT_TRUE(StartEmbeddedTestServer());
 
   GURL navigate_to_url = embedded_test_server()->GetURL(
@@ -2710,7 +2605,7 @@
 // This tests IndexedDB isolation for packaged apps with webview tags. It loads
 // an app with multiple webview tags and each tag creates an IndexedDB record,
 // which the test checks to ensure proper storage isolation is enforced.
-IN_PROC_BROWSER_TEST_P(WebViewTest, IndexedDBIsolation) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, IndexedDBIsolation) {
   ASSERT_TRUE(StartEmbeddedTestServer());
   ASSERT_TRUE(RunExtensionTest("platform_apps/web_view/isolation_indexeddb",
                                {.launch_as_platform_app = true}))
@@ -2727,37 +2622,36 @@
 #else
 #define MAYBE_CloseOnLoadcommit CloseOnLoadcommit
 #endif
-IN_PROC_BROWSER_TEST_P(WebViewTest, MAYBE_CloseOnLoadcommit) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, MAYBE_CloseOnLoadcommit) {
   LoadAndLaunchPlatformApp("web_view/close_on_loadcommit",
                            "done-close-on-loadcommit");
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, MediaAccessAPIDeny_TestDeny) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, MediaAccessAPIDeny_TestDeny) {
   MediaAccessAPIDenyTestHelper("testDeny");
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest,
+IN_PROC_BROWSER_TEST_F(WebViewTest,
                        MediaAccessAPIDeny_TestDenyThenAllowThrows) {
   MediaAccessAPIDenyTestHelper("testDenyThenAllowThrows");
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest,
+IN_PROC_BROWSER_TEST_F(WebViewTest,
                        MediaAccessAPIDeny_TestDenyWithPreventDefault) {
   MediaAccessAPIDenyTestHelper("testDenyWithPreventDefault");
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest,
+IN_PROC_BROWSER_TEST_F(WebViewTest,
                        MediaAccessAPIDeny_TestNoListenersImplyDeny) {
   MediaAccessAPIDenyTestHelper("testNoListenersImplyDeny");
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest,
+IN_PROC_BROWSER_TEST_F(WebViewTest,
                        MediaAccessAPIDeny_TestNoPreventDefaultImpliesDeny) {
   MediaAccessAPIDenyTestHelper("testNoPreventDefaultImpliesDeny");
 }
 
-void WebViewTestBase::MediaAccessAPIAllowTestHelper(
-    const std::string& test_name) {
+void WebViewTest::MediaAccessAPIAllowTestHelper(const std::string& test_name) {
   ASSERT_TRUE(StartEmbeddedTestServer());  // For serving guest pages.
   LoadAndLaunchPlatformApp("web_view/media_access/allow", "Launched");
 
@@ -2778,7 +2672,7 @@
   mock->WaitForRequestMediaPermission();
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, OpenURLFromTab_CurrentTab_Abort) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, OpenURLFromTab_CurrentTab_Abort) {
   LoadAppWithGuest("web_view/simple");
 
   // Verify that OpenURLFromTab with a window disposition of CURRENT_TAB will
@@ -2802,7 +2696,7 @@
 
 // A navigation to a web-safe URL should succeed, even if it is not renderer-
 // initiated, such as a navigation from the PDF viewer.
-IN_PROC_BROWSER_TEST_P(WebViewTest, OpenURLFromTab_CurrentTab_Succeed) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, OpenURLFromTab_CurrentTab_Succeed) {
   LoadAppWithGuest("web_view/simple");
 
   // Verify that OpenURLFromTab with a window disposition of CURRENT_TAB will
@@ -2883,7 +2777,7 @@
   EXPECT_TRUE(content::NavigateToURLFromRenderer(guest2, coop_url));
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, ContextMenuInspectElement) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, ContextMenuInspectElement) {
   LoadAppWithGuest("web_view/context_menus/basic");
   content::RenderFrameHost* guest_rfh = GetGuestRenderFrameHost();
   ASSERT_TRUE(guest_rfh);
@@ -2900,7 +2794,7 @@
 // load chrome://settings/languages in a browser window. This is a browser-
 // initiated operation and so we expect this to succeed if the embedder is
 // allowed to perform the operation.
-IN_PROC_BROWSER_TEST_P(WebViewTest, ContextMenuLanguageSettings) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, ContextMenuLanguageSettings) {
   LoadAppWithGuest("web_view/context_menus/basic");
 
   content::WebContents* embedder = GetEmbedderWebContents();
@@ -2925,7 +2819,7 @@
             new_contents->GetVisibleURL());
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, ContextMenusAPI_Basic) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, ContextMenusAPI_Basic) {
   LoadAppWithGuest("web_view/context_menus/basic");
 
   content::WebContents* embedder = GetEmbedderWebContents();
@@ -2976,7 +2870,7 @@
   ASSERT_EQ(0u, items_after_all_removal.size());
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, ContextMenusAPI_PreventDefault) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, ContextMenusAPI_PreventDefault) {
   LoadAppWithGuest("web_view/context_menus/basic");
   auto* guest_main_frame =
       GetGuestViewManager()->GetLastGuestRenderFrameHostCreated();
@@ -3009,7 +2903,7 @@
 
 // Tests that a context menu is created when right-clicking in the webview. This
 // also tests that the 'contextmenu' event is handled correctly.
-IN_PROC_BROWSER_TEST_P(WebViewTest, TestContextMenu) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, TestContextMenu) {
   LoadAppWithGuest("web_view/context_menus/basic");
   auto* guest_main_frame =
       GetGuestViewManager()->WaitForSingleGuestRenderFrameHostCreated();
@@ -3034,23 +2928,23 @@
   run_loop.Run();
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, MediaAccessAPIAllow_TestAllow) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, MediaAccessAPIAllow_TestAllow) {
   MediaAccessAPIAllowTestHelper("testAllow");
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, MediaAccessAPIAllow_TestAllowAndThenDeny) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, MediaAccessAPIAllow_TestAllowAndThenDeny) {
   MediaAccessAPIAllowTestHelper("testAllowAndThenDeny");
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, MediaAccessAPIAllow_TestAllowTwice) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, MediaAccessAPIAllow_TestAllowTwice) {
   MediaAccessAPIAllowTestHelper("testAllowTwice");
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, MediaAccessAPIAllow_TestAllowAsync) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, MediaAccessAPIAllow_TestAllowAsync) {
   MediaAccessAPIAllowTestHelper("testAllowAsync");
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, MediaAccessAPIAllow_TestCheck) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, MediaAccessAPIAllow_TestCheck) {
   ASSERT_TRUE(StartEmbeddedTestServer());  // For serving guest pages.
   LoadAndLaunchPlatformApp("web_view/media_access/check", "Launched");
 
@@ -3072,7 +2966,7 @@
 
 // Checks that window.screenX/screenY/screenLeft/screenTop works correctly for
 // guests.
-IN_PROC_BROWSER_TEST_P(WebViewTest, ScreenCoordinates) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, ScreenCoordinates) {
   ASSERT_TRUE(RunExtensionTest(
       "platform_apps/web_view/common",
       {.custom_arg = "screen_coordinates", .launch_as_platform_app = true}))
@@ -3085,7 +2979,7 @@
 #else
 #define MAYBE_TearDownTest TearDownTest
 #endif
-IN_PROC_BROWSER_TEST_P(WebViewTest, MAYBE_TearDownTest) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, MAYBE_TearDownTest) {
   const extensions::Extension* extension =
       LoadAndLaunchPlatformApp("web_view/simple", "WebViewTest.LAUNCHED");
   extensions::AppWindow* window = nullptr;
@@ -3101,7 +2995,7 @@
 
 // Tests that an app can inject a content script into a webview, and that it can
 // send cross-origin requests with CORS headers.
-IN_PROC_BROWSER_TEST_P(WebViewTest, ContentScriptFetch) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, ContentScriptFetch) {
   TestHelper("testContentScriptFetch", "web_view/content_script_fetch",
              NEEDS_TEST_SERVER);
 }
@@ -3110,13 +3004,13 @@
 // platform app) does not have geolocation permission for this test.
 // No matter what the API does, geolocation permission would be denied.
 // Note that the test name prefix must be "GeolocationAPI".
-IN_PROC_BROWSER_TEST_P(WebViewTest, GeolocationAPIEmbedderHasNoAccessAllow) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, GeolocationAPIEmbedderHasNoAccessAllow) {
   TestHelper("testDenyDenies",
              "web_view/geolocation/embedder_has_no_permission",
              NEEDS_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, GeolocationAPIEmbedderHasNoAccessDeny) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, GeolocationAPIEmbedderHasNoAccessDeny) {
   TestHelper("testDenyDenies",
              "web_view/geolocation/embedder_has_no_permission",
              NEEDS_TEST_SERVER);
@@ -3130,14 +3024,14 @@
 // the tests become flaky.
 //
 // GeolocationAPI* test 1 of 3.
-IN_PROC_BROWSER_TEST_P(WebViewTest, GeolocationAPIEmbedderHasAccessAllow) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, GeolocationAPIEmbedderHasAccessAllow) {
   TestHelper("testAllow",
              "web_view/geolocation/embedder_has_permission",
              NEEDS_TEST_SERVER);
 }
 
 // GeolocationAPI* test 2 of 3.
-IN_PROC_BROWSER_TEST_P(WebViewTest, GeolocationAPIEmbedderHasAccessDeny) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, GeolocationAPIEmbedderHasAccessDeny) {
   TestHelper("testDeny",
              "web_view/geolocation/embedder_has_permission",
              NEEDS_TEST_SERVER);
@@ -3145,115 +3039,115 @@
 
 // GeolocationAPI* test 3 of 3.
 // Currently disabled until crbug.com/526788 is fixed.
-IN_PROC_BROWSER_TEST_P(WebViewTest,
+IN_PROC_BROWSER_TEST_F(WebViewTest,
                        GeolocationAPIEmbedderHasAccessMultipleBridgeIdAllow) {
   TestHelper("testMultipleBridgeIdAllow",
              "web_view/geolocation/embedder_has_permission", NEEDS_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest,
+IN_PROC_BROWSER_TEST_F(WebViewTest,
                        PermissionsAPIEmbedderHasAccessAllowGeolocation) {
   TestHelper("testAllowGeolocation",
              "web_view/permissions_test/embedder_has_permission",
              NEEDS_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest,
+IN_PROC_BROWSER_TEST_F(WebViewTest,
                        PermissionsAPIEmbedderHasAccessDenyGeolocation) {
   TestHelper("testDenyGeolocation",
              "web_view/permissions_test/embedder_has_permission",
              NEEDS_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest,
+IN_PROC_BROWSER_TEST_F(WebViewTest,
                        PermissionsAPIEmbedderHasAccessAllowCamera) {
   TestHelper("testAllowCamera",
              "web_view/permissions_test/embedder_has_permission",
              NEEDS_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, PermissionsAPIEmbedderHasAccessDenyCamera) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, PermissionsAPIEmbedderHasAccessDenyCamera) {
   TestHelper("testDenyCamera",
              "web_view/permissions_test/embedder_has_permission",
              NEEDS_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest,
+IN_PROC_BROWSER_TEST_F(WebViewTest,
                        PermissionsAPIEmbedderHasAccessAllowMicrophone) {
   TestHelper("testAllowMicrophone",
              "web_view/permissions_test/embedder_has_permission",
              NEEDS_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest,
+IN_PROC_BROWSER_TEST_F(WebViewTest,
                        PermissionsAPIEmbedderHasAccessDenyMicrophone) {
   TestHelper("testDenyMicrophone",
              "web_view/permissions_test/embedder_has_permission",
              NEEDS_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, PermissionsAPIEmbedderHasAccessAllowMedia) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, PermissionsAPIEmbedderHasAccessAllowMedia) {
   TestHelper("testAllowMedia",
              "web_view/permissions_test/embedder_has_permission",
              NEEDS_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, PermissionsAPIEmbedderHasAccessDenyMedia) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, PermissionsAPIEmbedderHasAccessDenyMedia) {
   TestHelper("testDenyMedia",
              "web_view/permissions_test/embedder_has_permission",
              NEEDS_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest,
+IN_PROC_BROWSER_TEST_F(WebViewTest,
                        PermissionsAPIEmbedderHasNoAccessAllowGeolocation) {
   TestHelper("testAllowGeolocation",
              "web_view/permissions_test/embedder_has_no_permission",
              NEEDS_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest,
+IN_PROC_BROWSER_TEST_F(WebViewTest,
                        PermissionsAPIEmbedderHasNoAccessDenyGeolocation) {
   TestHelper("testDenyGeolocation",
              "web_view/permissions_test/embedder_has_no_permission",
              NEEDS_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest,
+IN_PROC_BROWSER_TEST_F(WebViewTest,
                        PermissionsAPIEmbedderHasNoAccessAllowCamera) {
   TestHelper("testAllowCamera",
              "web_view/permissions_test/embedder_has_no_permission",
              NEEDS_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest,
+IN_PROC_BROWSER_TEST_F(WebViewTest,
                        PermissionsAPIEmbedderHasNoAccessDenyCamera) {
   TestHelper("testDenyCamera",
              "web_view/permissions_test/embedder_has_no_permission",
              NEEDS_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest,
+IN_PROC_BROWSER_TEST_F(WebViewTest,
                        PermissionsAPIEmbedderHasNoAccessAllowMicrophone) {
   TestHelper("testAllowMicrophone",
              "web_view/permissions_test/embedder_has_no_permission",
              NEEDS_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest,
+IN_PROC_BROWSER_TEST_F(WebViewTest,
                        PermissionsAPIEmbedderHasNoAccessDenyMicrophone) {
   TestHelper("testDenyMicrophone",
              "web_view/permissions_test/embedder_has_no_permission",
              NEEDS_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest,
+IN_PROC_BROWSER_TEST_F(WebViewTest,
                        PermissionsAPIEmbedderHasNoAccessAllowMedia) {
   TestHelper("testAllowMedia",
              "web_view/permissions_test/embedder_has_no_permission",
              NEEDS_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest,
+IN_PROC_BROWSER_TEST_F(WebViewTest,
                        PermissionsAPIEmbedderHasNoAccessDenyMedia) {
   TestHelper("testDenyMedia",
              "web_view/permissions_test/embedder_has_no_permission",
@@ -3263,7 +3157,7 @@
 // Tests that
 // BrowserPluginGeolocationPermissionContext::CancelGeolocationPermissionRequest
 // is handled correctly (and does not crash).
-IN_PROC_BROWSER_TEST_P(WebViewTest, GeolocationAPICancelGeolocation) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, GeolocationAPICancelGeolocation) {
   ASSERT_TRUE(StartEmbeddedTestServer());  // For serving guest pages.
   ASSERT_TRUE(
       RunExtensionTest("platform_apps/web_view/geolocation/cancel_request",
@@ -3271,7 +3165,7 @@
       << message_;
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, DISABLED_GeolocationRequestGone) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, DISABLED_GeolocationRequestGone) {
   ASSERT_TRUE(StartEmbeddedTestServer());  // For serving guest pages.
   ASSERT_TRUE(RunExtensionTest(
       "platform_apps/web_view/geolocation/geolocation_request_gone",
@@ -3282,17 +3176,17 @@
 // In following FilesystemAPIRequestFromMainThread* tests, guest request
 // filesystem access from main thread of the guest.
 // FileSystemAPIRequestFromMainThread* test 1 of 3
-IN_PROC_BROWSER_TEST_P(WebViewTest, FileSystemAPIRequestFromMainThreadAllow) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, FileSystemAPIRequestFromMainThreadAllow) {
   TestHelper("testAllow", "web_view/filesystem/main", NEEDS_TEST_SERVER);
 }
 
 // FileSystemAPIRequestFromMainThread* test 2 of 3.
-IN_PROC_BROWSER_TEST_P(WebViewTest, FileSystemAPIRequestFromMainThreadDeny) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, FileSystemAPIRequestFromMainThreadDeny) {
   TestHelper("testDeny", "web_view/filesystem/main", NEEDS_TEST_SERVER);
 }
 
 // FileSystemAPIRequestFromMainThread* test 3 of 3.
-IN_PROC_BROWSER_TEST_P(WebViewTest,
+IN_PROC_BROWSER_TEST_F(WebViewTest,
                        FileSystemAPIRequestFromMainThreadDefaultAllow) {
   TestHelper("testDefaultAllow", "web_view/filesystem/main", NEEDS_TEST_SERVER);
 }
@@ -3300,17 +3194,17 @@
 // In following FilesystemAPIRequestFromWorker* tests, guest create a worker
 // to request filesystem access from worker thread.
 // FileSystemAPIRequestFromWorker* test 1 of 3
-IN_PROC_BROWSER_TEST_P(WebViewTest, FileSystemAPIRequestFromWorkerAllow) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, FileSystemAPIRequestFromWorkerAllow) {
   TestHelper("testAllow", "web_view/filesystem/worker", NEEDS_TEST_SERVER);
 }
 
 // FileSystemAPIRequestFromWorker* test 2 of 3.
-IN_PROC_BROWSER_TEST_P(WebViewTest, FileSystemAPIRequestFromWorkerDeny) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, FileSystemAPIRequestFromWorkerDeny) {
   TestHelper("testDeny", "web_view/filesystem/worker", NEEDS_TEST_SERVER);
 }
 
 // FileSystemAPIRequestFromWorker* test 3 of 3.
-IN_PROC_BROWSER_TEST_P(WebViewTest,
+IN_PROC_BROWSER_TEST_F(WebViewTest,
                        FileSystemAPIRequestFromWorkerDefaultAllow) {
   TestHelper(
       "testDefaultAllow", "web_view/filesystem/worker", NEEDS_TEST_SERVER);
@@ -3320,7 +3214,7 @@
 // embedder contains a single webview guest. The guest creates a shared worker
 // to request filesystem access from worker thread.
 // FileSystemAPIRequestFromSharedWorkerOfSingleWebViewGuest* test 1 of 3
-IN_PROC_BROWSER_TEST_P(
+IN_PROC_BROWSER_TEST_F(
     WebViewTest,
     FileSystemAPIRequestFromSharedWorkerOfSingleWebViewGuestAllow) {
   TestHelper("testAllow",
@@ -3329,7 +3223,7 @@
 }
 
 // FileSystemAPIRequestFromSharedWorkerOfSingleWebViewGuest* test 2 of 3.
-IN_PROC_BROWSER_TEST_P(
+IN_PROC_BROWSER_TEST_F(
     WebViewTest,
     FileSystemAPIRequestFromSharedWorkerOfSingleWebViewGuestDeny) {
   TestHelper("testDeny",
@@ -3338,7 +3232,7 @@
 }
 
 // FileSystemAPIRequestFromSharedWorkerOfSingleWebViewGuest* test 3 of 3.
-IN_PROC_BROWSER_TEST_P(
+IN_PROC_BROWSER_TEST_F(
     WebViewTest,
     FileSystemAPIRequestFromSharedWorkerOfSingleWebViewGuestDefaultAllow) {
   TestHelper(
@@ -3351,7 +3245,7 @@
 // embedder contains mutiple webview guests. Each guest creates a shared worker
 // to request filesystem access from worker thread.
 // FileSystemAPIRequestFromSharedWorkerOfMultiWebViewGuests* test 1 of 3
-IN_PROC_BROWSER_TEST_P(
+IN_PROC_BROWSER_TEST_F(
     WebViewTest,
     FileSystemAPIRequestFromSharedWorkerOfMultiWebViewGuestsAllow) {
   TestHelper("testAllow",
@@ -3360,7 +3254,7 @@
 }
 
 // FileSystemAPIRequestFromSharedWorkerOfMultiWebViewGuests* test 2 of 3.
-IN_PROC_BROWSER_TEST_P(
+IN_PROC_BROWSER_TEST_F(
     WebViewTest,
     FileSystemAPIRequestFromSharedWorkerOfMultiWebViewGuestsDeny) {
   TestHelper("testDeny",
@@ -3369,7 +3263,7 @@
 }
 
 // FileSystemAPIRequestFromSharedWorkerOfMultiWebViewGuests* test 3 of 3.
-IN_PROC_BROWSER_TEST_P(
+IN_PROC_BROWSER_TEST_F(
     WebViewTest,
     FileSystemAPIRequestFromSharedWorkerOfMultiWebViewGuestsDefaultAllow) {
   TestHelper(
@@ -3378,7 +3272,7 @@
       NEEDS_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, ClearData) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, ClearData) {
   ASSERT_TRUE(StartEmbeddedTestServer());  // For serving guest pages.
   ASSERT_TRUE(RunExtensionTest(
       "platform_apps/web_view/common",
@@ -3386,7 +3280,7 @@
       << message_;
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, ClearSessionCookies) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, ClearSessionCookies) {
   ASSERT_TRUE(StartEmbeddedTestServer());  // For serving guest pages.
   ASSERT_TRUE(RunExtensionTest(
       "platform_apps/web_view/common",
@@ -3394,7 +3288,7 @@
       << message_;
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, ClearPersistentCookies) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, ClearPersistentCookies) {
   ASSERT_TRUE(StartEmbeddedTestServer());  // For serving guest pages.
   ASSERT_TRUE(RunExtensionTest(
       "platform_apps/web_view/common",
@@ -3403,7 +3297,7 @@
 }
 
 // Regression test for https://crbug.com/615429.
-IN_PROC_BROWSER_TEST_P(WebViewTest, ClearDataTwice) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, ClearDataTwice) {
   ASSERT_TRUE(StartEmbeddedTestServer());  // For serving guest pages.
   ASSERT_TRUE(RunExtensionTest(
       "platform_apps/web_view/common",
@@ -3418,18 +3312,18 @@
 #else
 #define MAYBE_ClearDataCache ClearDataCache
 #endif
-IN_PROC_BROWSER_TEST_P(WebViewTest, MAYBE_ClearDataCache) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, MAYBE_ClearDataCache) {
   TestHelper("testClearCache", "web_view/clear_data_cache", NEEDS_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, ConsoleMessage) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, ConsoleMessage) {
   ASSERT_TRUE(RunExtensionTest(
       "platform_apps/web_view/common",
       {.custom_arg = "console_messages", .launch_as_platform_app = true}))
       << message_;
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, DownloadPermission) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, DownloadPermission) {
   ASSERT_TRUE(StartEmbeddedTestServer());  // For serving guest pages.
   LoadAndLaunchPlatformApp("web_view/download", "guest-loaded");
   auto* guest_view_base =
@@ -3541,7 +3435,7 @@
 // Downloads initiated from isolated guest parititons should use their
 // respective cookie stores. In addition, if those downloads are resumed, they
 // should continue to use their respective cookie stores.
-IN_PROC_BROWSER_TEST_P(WebViewTest, DownloadCookieIsolation) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, DownloadCookieIsolation) {
   embedded_test_server()->RegisterRequestHandler(
       base::BindRepeating(&HandleDownloadRequestWithCookie));
   ASSERT_TRUE(StartEmbeddedTestServer());  // For serving guest pages.
@@ -3627,7 +3521,7 @@
   ASSERT_TRUE(cookies.find("cookie=second") != cookies.end());
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, PRE_DownloadCookieIsolation_CrossSession) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, PRE_DownloadCookieIsolation_CrossSession) {
   embedded_test_server()->RegisterRequestHandler(
       base::BindRepeating(&HandleDownloadRequestWithCookie));
   ASSERT_TRUE(StartEmbeddedTestServer());  // For serving guest pages.
@@ -3683,7 +3577,7 @@
   content::EnsureCookiesFlushed(profile());
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, DownloadCookieIsolation_CrossSession) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, DownloadCookieIsolation_CrossSession) {
   embedded_test_server()->RegisterRequestHandler(
       base::BindRepeating(&HandleDownloadRequestWithCookie));
   ASSERT_TRUE(StartEmbeddedTestServer());  // For serving guest pages.
@@ -3777,7 +3671,7 @@
 
 // This test makes sure loading <webview> does not crash when there is an
 // extension which has content script allowlisted/forced.
-IN_PROC_BROWSER_TEST_P(WebViewTest, AllowlistedContentScript) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, AllowlistedContentScript) {
   // Allowlist the extension for running content script we are going to load.
   extensions::ExtensionsClient::ScriptingAllowlist allowlist;
   const std::string extension_id = "imeongpbjoodlnmlakaldhlcmijmhpbb";
@@ -3796,7 +3690,7 @@
                            "TEST_PASSED");
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, SendMessageToExtensionFromGuest) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, SendMessageToExtensionFromGuest) {
   // Load the extension as a normal, non-component extension.
   const extensions::Extension* extension =
       LoadExtension(test_data_dir_.AppendASCII(
@@ -3807,7 +3701,7 @@
              NEEDS_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, SendMessageToComponentExtensionFromGuest) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, SendMessageToComponentExtensionFromGuest) {
   const extensions::Extension* component_extension =
       LoadExtensionAsComponent(test_data_dir_.AppendASCII(
           "platform_apps/web_view/extension_api/component_extension"));
@@ -3839,19 +3733,19 @@
       content::WebContents::FromRenderFrameHost(guest_rfh)));
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, SetPropertyOnDocumentReady) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, SetPropertyOnDocumentReady) {
   ASSERT_TRUE(RunExtensionTest("platform_apps/web_view/document_ready",
                                {.launch_as_platform_app = true}))
       << message_;
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, SetPropertyOnDocumentInteractive) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, SetPropertyOnDocumentInteractive) {
   ASSERT_TRUE(RunExtensionTest("platform_apps/web_view/document_interactive",
                                {.launch_as_platform_app = true}))
       << message_;
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewSpeechAPITest,
+IN_PROC_BROWSER_TEST_F(WebViewSpeechAPITest,
                        SpeechRecognitionAPI_HasPermissionAllow) {
   ASSERT_TRUE(RunExtensionTest(
       "platform_apps/web_view/speech_recognition_api",
@@ -3859,7 +3753,7 @@
       << message_;
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewSpeechAPITest,
+IN_PROC_BROWSER_TEST_F(WebViewSpeechAPITest,
                        SpeechRecognitionAPI_HasPermissionDeny) {
   ASSERT_TRUE(RunExtensionTest(
       "platform_apps/web_view/speech_recognition_api",
@@ -3867,7 +3761,7 @@
       << message_;
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewSpeechAPITest,
+IN_PROC_BROWSER_TEST_F(WebViewSpeechAPITest,
                        SpeechRecognitionAPI_NoPermission) {
   ASSERT_TRUE(
       RunExtensionTest("platform_apps/web_view/common",
@@ -3877,7 +3771,7 @@
 }
 
 // Tests overriding user agent.
-IN_PROC_BROWSER_TEST_P(WebViewTest, UserAgent) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, UserAgent) {
   ASSERT_TRUE(RunExtensionTest(
       "platform_apps/web_view/common",
       {.custom_arg = "useragent", .launch_as_platform_app = true}))
@@ -3897,41 +3791,41 @@
       << message_;
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, NoPermission) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, NoPermission) {
   ASSERT_TRUE(RunExtensionTest("platform_apps/web_view/nopermission",
                                {.launch_as_platform_app = true}))
       << message_;
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, Dialog_TestAlertDialog) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Dialog_TestAlertDialog) {
   TestHelper("testAlertDialog", "web_view/dialog", NO_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, TestConfirmDialog) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, TestConfirmDialog) {
   TestHelper("testConfirmDialog", "web_view/dialog", NO_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, Dialog_TestConfirmDialogCancel) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Dialog_TestConfirmDialogCancel) {
   TestHelper("testConfirmDialogCancel", "web_view/dialog", NO_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, Dialog_TestConfirmDialogDefaultCancel) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Dialog_TestConfirmDialogDefaultCancel) {
   TestHelper("testConfirmDialogDefaultCancel",
              "web_view/dialog",
              NO_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, Dialog_TestConfirmDialogDefaultGCCancel) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Dialog_TestConfirmDialogDefaultGCCancel) {
   TestHelper("testConfirmDialogDefaultGCCancel",
              "web_view/dialog",
              NO_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, Dialog_TestPromptDialog) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Dialog_TestPromptDialog) {
   TestHelper("testPromptDialog", "web_view/dialog", NO_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, NoContentSettingsAPI) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, NoContentSettingsAPI) {
   // Load the extension.
   const extensions::Extension* content_settings_extension =
       LoadExtension(
@@ -3951,11 +3845,6 @@
   }
 };
 
-INSTANTIATE_TEST_SUITE_P(WebViewTests,
-                         WebViewCaptureTest,
-                         testing::Bool(),
-                         WebViewTest::DescribeParams);
-
 // TODO(crbug.com/1087381): Flaky on mac
 // TODO(crbug.com/1052397): Revisit once build flag switch of lacros-chrome is
 // complete.
@@ -3966,11 +3855,11 @@
 #else
 #define MAYBE_Shim_TestZoomAPI Shim_TestZoomAPI
 #endif
-IN_PROC_BROWSER_TEST_P(WebViewTest, MAYBE_Shim_TestZoomAPI) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, MAYBE_Shim_TestZoomAPI) {
   TestHelper("testZoomAPI", "web_view/shim", NO_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestFindAPI) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestFindAPI) {
   TestHelper("testFindAPI", "web_view/shim", NO_TEST_SERVER);
 }
 
@@ -3980,48 +3869,45 @@
 #else
 #define MAYBE_Shim_TestFindAPI_findupdate Shim_TestFindAPI_findupdate
 #endif
-IN_PROC_BROWSER_TEST_P(WebViewTest, MAYBE_Shim_TestFindAPI_findupdate) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, MAYBE_Shim_TestFindAPI_findupdate) {
   TestHelper("testFindAPI_findupdate", "web_view/shim", NO_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_testFindInMultipleWebViews) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_testFindInMultipleWebViews) {
   TestHelper("testFindInMultipleWebViews", "web_view/shim", NO_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestLoadDataAPI) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestLoadDataAPI) {
   TestHelper("testLoadDataAPI", "web_view/shim", NEEDS_TEST_SERVER);
 
-  // Ensure that when site-isolated guests are enabled, the guest process is
-  // locked after the loadDataWithBaseURL navigation and is allowed to access
-  // resources belonging to the base URL's origin.
-  if (content::SiteIsolationPolicy::IsSiteIsolationForGuestsEnabled()) {
-    content::RenderFrameHost* guest_main_frame =
-        GetGuestViewManager()->WaitForSingleGuestRenderFrameHostCreated();
-    ASSERT_TRUE(guest_main_frame);
-    EXPECT_TRUE(
-        guest_main_frame->GetSiteInstance()->RequiresDedicatedProcess());
-    EXPECT_TRUE(
-        guest_main_frame->GetProcess()->IsProcessLockedToSiteForTesting());
+  // Ensure that the guest process is locked after the loadDataWithBaseURL
+  // navigation and is allowed to access resources belonging to the base URL's
+  // origin.
+  content::RenderFrameHost* guest_main_frame =
+      GetGuestViewManager()->WaitForSingleGuestRenderFrameHostCreated();
+  ASSERT_TRUE(guest_main_frame);
+  EXPECT_TRUE(guest_main_frame->GetSiteInstance()->RequiresDedicatedProcess());
+  EXPECT_TRUE(
+      guest_main_frame->GetProcess()->IsProcessLockedToSiteForTesting());
 
-    auto* security_policy = content::ChildProcessSecurityPolicy::GetInstance();
-    url::Origin base_origin = url::Origin::Create(GURL("http://localhost"));
-    EXPECT_TRUE(security_policy->CanAccessDataForOrigin(
-        guest_main_frame->GetProcess()->GetID(), base_origin));
+  auto* security_policy = content::ChildProcessSecurityPolicy::GetInstance();
+  url::Origin base_origin = url::Origin::Create(GURL("http://localhost"));
+  EXPECT_TRUE(security_policy->CanAccessDataForOrigin(
+      guest_main_frame->GetProcess()->GetID(), base_origin));
 
-    // Ensure the process doesn't have access to some other origin. This
-    // verifies that site isolation is enforced.
-    url::Origin another_origin = url::Origin::Create(GURL("http://foo.com"));
-    EXPECT_FALSE(security_policy->CanAccessDataForOrigin(
-        guest_main_frame->GetProcess()->GetID(), another_origin));
-  }
+  // Ensure the process doesn't have access to some other origin. This
+  // verifies that site isolation is enforced.
+  url::Origin another_origin = url::Origin::Create(GURL("http://foo.com"));
+  EXPECT_FALSE(security_policy->CanAccessDataForOrigin(
+      guest_main_frame->GetProcess()->GetID(), another_origin));
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestLoadDataAPIAccessibleResources) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestLoadDataAPIAccessibleResources) {
   TestHelper("testLoadDataAPIAccessibleResources", "web_view/shim",
              NEEDS_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, LoadDataAPINotRelativeToAnotherExtension) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, LoadDataAPINotRelativeToAnotherExtension) {
   ASSERT_TRUE(StartEmbeddedTestServer());
   const extensions::Extension* other_extension =
       LoadExtension(test_data_dir_.AppendASCII("simple_with_file"));
@@ -4048,24 +3934,24 @@
 }
 
 // This test verifies that the resize and contentResize events work correctly.
-IN_PROC_BROWSER_TEST_P(WebViewSizeTest, Shim_TestResizeEvents) {
+IN_PROC_BROWSER_TEST_F(WebViewSizeTest, Shim_TestResizeEvents) {
   TestHelper("testResizeEvents", "web_view/shim", NO_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestPerOriginZoomMode) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestPerOriginZoomMode) {
   TestHelper("testPerOriginZoomMode", "web_view/shim", NO_TEST_SERVER);
 }
 
 // TODO(crbug.com/935665): Test has flaky failures on all platforms.
-IN_PROC_BROWSER_TEST_P(WebViewTest, DISABLED_Shim_TestPerViewZoomMode) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, DISABLED_Shim_TestPerViewZoomMode) {
   TestHelper("testPerViewZoomMode", "web_view/shim", NO_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestDisabledZoomMode) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestDisabledZoomMode) {
   TestHelper("testDisabledZoomMode", "web_view/shim", NO_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestZoomBeforeNavigation) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestZoomBeforeNavigation) {
   TestHelper("testZoomBeforeNavigation", "web_view/shim", NO_TEST_SERVER);
 }
 
@@ -4163,13 +4049,8 @@
   net::EmbeddedTestServer https_server_{net::EmbeddedTestServer::TYPE_HTTPS};
 };
 
-INSTANTIATE_TEST_SUITE_P(WebViewTests,
-                         WebViewCertificateSelectorTest,
-                         testing::Bool(),
-                         WebViewTest::DescribeParams);
-
 // Ensure a guest triggering a client certificate dialog does not crash.
-IN_PROC_BROWSER_TEST_P(WebViewCertificateSelectorTest,
+IN_PROC_BROWSER_TEST_F(WebViewCertificateSelectorTest,
                        CertificateSelectorForGuest) {
   LoadAppWithGuest("web_view/simple");
   content::RenderFrameHost* guest_rfh = GetGuestRenderFrameHost();
@@ -4194,7 +4075,7 @@
 // happened multiple times for various dialogs and signin flows (see
 // https://crbug.com/1076696 and https://crbug.com/1306988 ), so let's test that
 // if we are in this situation, we at least don't crash.
-IN_PROC_BROWSER_TEST_P(WebViewCertificateSelectorTest,
+IN_PROC_BROWSER_TEST_F(WebViewCertificateSelectorTest,
                        CertificateSelectorForGuestMisconfigured) {
   LoadAppWithGuest("web_view/simple");
   content::WebContents* guest = GetGuestWebContents();
@@ -4222,34 +4103,24 @@
 }
 
 // Test fixture to run the test on multiple channels.
-class WebViewChannelTest : public WebViewTestBase,
-                           public testing::WithParamInterface<
-                               testing::tuple<bool, version_info::Channel>> {
+class WebViewChannelTest
+    : public WebViewTest,
+      public testing::WithParamInterface<version_info::Channel> {
  public:
-  WebViewChannelTest() : channel_(GetChannelParam()) {
-    scoped_feature_list_.InitWithFeatureState(
-        features::kSiteIsolationForGuests,
-        /*enabled=*/testing::get<0>(GetParam()));
-  }
+  WebViewChannelTest() : channel_(GetChannelParam()) {}
 
-  version_info::Channel GetChannelParam() {
-    return testing::get<1>(GetParam());
-  }
+  version_info::Channel GetChannelParam() { return GetParam(); }
+
   WebViewChannelTest(const WebViewChannelTest&) = delete;
   WebViewChannelTest& operator=(const WebViewChannelTest&) = delete;
 
   static std::string DescribeParams(
       const testing::TestParamInfo<ParamType>& info) {
-    auto [is_site_isolation_enabled, channel] = info.param;
-    return base::StringPrintf(
-        "SiteIsolationForGuests%s_%s",
-        is_site_isolation_enabled ? "Enabled" : "Disabled",
-        channel == version_info::Channel::STABLE ? "StableChannel"
-                                                 : "NonStableChannel");
+    return info.param == version_info::Channel::STABLE ? "StableChannel"
+                                                       : "NonStableChannel";
   }
 
  private:
-  base::test::ScopedFeatureList scoped_feature_list_;
   extensions::ScopedCurrentChannel channel_;
 };
 
@@ -4351,35 +4222,33 @@
             registry->rules_cache_delegate_for_testing()->type());
 }
 
-INSTANTIATE_TEST_SUITE_P(
-    WebViewTests,
-    WebViewChannelTest,
-    testing::Combine(testing::Bool(),
-                     testing::Values(version_info::Channel::UNKNOWN,
-                                     version_info::Channel::STABLE)),
-    WebViewChannelTest::DescribeParams);
+INSTANTIATE_TEST_SUITE_P(WebViewTests,
+                         WebViewChannelTest,
+                         testing::Values(version_info::Channel::UNKNOWN,
+                                         version_info::Channel::STABLE),
+                         WebViewChannelTest::DescribeParams);
 
 // This test verifies that webview.contentWindow works inside an iframe.
-IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestWebViewInsideFrame) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestWebViewInsideFrame) {
   LoadAppWithGuest("web_view/inside_iframe");
 }
 
 // <webview> screenshot capture fails with ubercomp.
 // See http://crbug.com/327035.
-IN_PROC_BROWSER_TEST_P(WebViewCaptureTest, DISABLED_Shim_ScreenshotCapture) {
+IN_PROC_BROWSER_TEST_F(WebViewCaptureTest, DISABLED_Shim_ScreenshotCapture) {
   TestHelper("testScreenshotCapture", "web_view/shim", NO_TEST_SERVER);
 }
 
 // Test is disabled because it times out often.
 // http://crbug.com/403325
-IN_PROC_BROWSER_TEST_P(WebViewTest, DISABLED_WebViewInBackgroundPage) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, DISABLED_WebViewInBackgroundPage) {
   ASSERT_TRUE(StartEmbeddedTestServer());
   ASSERT_TRUE(RunExtensionTest("platform_apps/web_view/background"))
       << message_;
 }
 
 // This test verifies that the allowtransparency attribute properly propagates.
-IN_PROC_BROWSER_TEST_P(WebViewTest, AllowTransparencyAndAllowScalingPropagate) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, AllowTransparencyAndAllowScalingPropagate) {
   LoadAppWithGuest("web_view/simple");
 
   ASSERT_TRUE(GetGuestView());
@@ -4389,7 +4258,7 @@
   ASSERT_TRUE(guest->allow_scaling());
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, BasicPostMessage) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, BasicPostMessage) {
   ASSERT_TRUE(StartEmbeddedTestServer());  // For serving guest pages.
   ASSERT_TRUE(RunExtensionTest("platform_apps/web_view/post_message/basic",
                                {.launch_as_platform_app = true}))
@@ -4397,12 +4266,12 @@
 }
 
 // Tests that webviews do get garbage collected.
-IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestGarbageCollect) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestGarbageCollect) {
   TestHelper("testGarbageCollect", "web_view/shim", NO_TEST_SERVER);
   GetGuestViewManager()->WaitForSingleViewGarbageCollected();
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestCloseNewWindowCleanup) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestCloseNewWindowCleanup) {
   TestHelper("testCloseNewWindowCleanup", "web_view/shim", NEEDS_TEST_SERVER);
   auto* gvm = GetGuestViewManager();
   gvm->WaitForLastGuestDeleted();
@@ -4411,18 +4280,14 @@
 
 // Ensure that focusing a WebView while it is already focused does not blur the
 // guest content.
-IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestFocusWhileFocused) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestFocusWhileFocused) {
   TestHelper("testFocusWhileFocused", "web_view/shim", NO_TEST_SERVER);
 }
 
 #if BUILDFLAG(ENABLE_PDF)
 using WebViewPdfTest = WebViewTest;
-INSTANTIATE_TEST_SUITE_P(WebViewTests,
-                         WebViewPdfTest,
-                         testing::Bool(),
-                         WebViewTest::DescribeParams);
 
-IN_PROC_BROWSER_TEST_P(WebViewPdfTest, NestedGuestContainerBounds) {
+IN_PROC_BROWSER_TEST_F(WebViewPdfTest, NestedGuestContainerBounds) {
   TestHelper("testPDFInWebview", "web_view/shim", NO_TEST_SERVER);
 
   std::vector<content::RenderFrameHost*> guest_rfh_list;
@@ -4446,7 +4311,7 @@
 
 // Test that context menu Back/Forward items in a MimeHandlerViewGuest affect
 // the embedder WebContents. See crbug.com/587355.
-IN_PROC_BROWSER_TEST_P(WebViewPdfTest, ContextMenuNavigationInMimeHandlerView) {
+IN_PROC_BROWSER_TEST_F(WebViewPdfTest, ContextMenuNavigationInMimeHandlerView) {
   TestHelper("testNavigateToPDFInWebview", "web_view/shim", NO_TEST_SERVER);
 
   GetGuestViewManager()->WaitForNumGuestsCreated(2u);
@@ -4484,18 +4349,18 @@
   EXPECT_EQ(GURL(url::kAboutBlankURL), web_view_rfh2->GetLastCommittedURL());
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewPdfTest, Shim_TestDialogInPdf) {
+IN_PROC_BROWSER_TEST_F(WebViewPdfTest, Shim_TestDialogInPdf) {
   TestHelper("testDialogInPdf", "web_view/shim", NO_TEST_SERVER);
 }
 #endif  // BUILDFLAG(ENABLE_PDF)
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestMailtoLink) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestMailtoLink) {
   TestHelper("testMailtoLink", "web_view/shim", NEEDS_TEST_SERVER);
 }
 
 // Tests that a renderer navigation from an unattached guest that results in a
 // server redirect works properly.
-IN_PROC_BROWSER_TEST_P(WebViewTest,
+IN_PROC_BROWSER_TEST_F(WebViewTest,
                        Shim_TestRendererNavigationRedirectWhileUnattached) {
   TestHelper("testRendererNavigationRedirectWhileUnattached",
              "web_view/shim", NEEDS_TEST_SERVER);
@@ -4505,25 +4370,25 @@
 // See https://crbug.com/652077.
 // Also tests that the embedder can't navigate to a blob URL created by a
 // WebView. See https://crbug.com/1106890.
-IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestBlobURL) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestBlobURL) {
   TestHelper("testBlobURL", "web_view/shim", NEEDS_TEST_SERVER);
 }
 
 // Tests that no error page is shown when WebRequest blocks a navigation.
-IN_PROC_BROWSER_TEST_P(WebViewTest, Shim_TestWebRequestBlockedNavigation) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestWebRequestBlockedNavigation) {
   TestHelper("testWebRequestBlockedNavigation", "web_view/shim",
              NEEDS_TEST_SERVER);
 }
 
 // Tests that a WebView accessible resource can actually be loaded from a
 // webpage in a WebView.
-IN_PROC_BROWSER_TEST_P(WebViewTest, LoadWebviewAccessibleResource) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, LoadWebviewAccessibleResource) {
   TestHelper("testLoadWebviewAccessibleResource",
              "web_view/load_webview_accessible_resource", NEEDS_TEST_SERVER);
 }
 
 // Tests that a WebView can be navigated to a WebView accessible resource.
-IN_PROC_BROWSER_TEST_P(WebViewTest, NavigateGuestToWebviewAccessibleResource) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, NavigateGuestToWebviewAccessibleResource) {
   TestHelper("testNavigateGuestToWebviewAccessibleResource",
              "web_view/load_webview_accessible_resource", NO_TEST_SERVER);
 
@@ -4551,7 +4416,7 @@
 
 // Tests that a WebView can reload a WebView accessible resource. See
 // https://crbug.com/691941.
-IN_PROC_BROWSER_TEST_P(WebViewTest, ReloadWebviewAccessibleResource) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, ReloadWebviewAccessibleResource) {
   TestHelper("testReloadWebviewAccessibleResource",
              "web_view/load_webview_accessible_resource", NEEDS_TEST_SERVER);
 
@@ -4570,7 +4435,7 @@
 
 // Tests that a WebView can navigate an iframe to a blob URL that it creates
 // while its main frame is at a WebView accessible resource.
-IN_PROC_BROWSER_TEST_P(WebViewTest, BlobInWebviewAccessibleResource) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, BlobInWebviewAccessibleResource) {
   TestHelper("testBlobInWebviewAccessibleResource",
              "web_view/load_webview_accessible_resource", NEEDS_TEST_SERVER);
 
@@ -4599,7 +4464,7 @@
 
 // Tests that a WebView cannot load a webview-inaccessible resource. See
 // https://crbug.com/640072.
-IN_PROC_BROWSER_TEST_P(WebViewTest, LoadWebviewInaccessibleResource) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, LoadWebviewInaccessibleResource) {
   TestHelper("testLoadWebviewInaccessibleResource",
              "web_view/load_webview_accessible_resource", NEEDS_TEST_SERVER);
 
@@ -4620,12 +4485,12 @@
 
 // Ensure that only app resources accessible to the webview can be loaded in a
 // webview even if the webview commits an app frame.
-IN_PROC_BROWSER_TEST_P(WebViewTest,
+IN_PROC_BROWSER_TEST_F(WebViewTest,
                        LoadAccessibleSubresourceInAppWebviewFrame) {
   TestHelper("testLoadAccessibleSubresourceInAppWebviewFrame",
              "web_view/load_webview_accessible_resource", NEEDS_TEST_SERVER);
 }
-IN_PROC_BROWSER_TEST_P(WebViewTest,
+IN_PROC_BROWSER_TEST_F(WebViewTest,
                        InaccessibleResourceDoesNotLoadInAppWebviewFrame) {
   TestHelper("testInaccessibleResourceDoesNotLoadInAppWebviewFrame",
              "web_view/load_webview_accessible_resource", NEEDS_TEST_SERVER);
@@ -4633,7 +4498,7 @@
 
 // Makes sure that a webview will display correctly after reloading it after a
 // crash.
-IN_PROC_BROWSER_TEST_P(WebViewTest, ReloadAfterCrash) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, ReloadAfterCrash) {
   // Load guest and wait for it to appear.
   LoadAppWithGuest("web_view/simple");
 
@@ -4683,17 +4548,12 @@
   }
 };
 
-INSTANTIATE_TEST_SUITE_P(WebViewTests,
-                         WebViewTestNoDomAutomationController,
-                         testing::Bool(),
-                         WebViewTest::DescribeParams);
-
 // Tests that a webview inside an iframe can load and that it is destroyed when
 // the iframe is detached.
 // We need to disable DomAutomationController because it forces the creation of
 // a script context. We want to test that we handle the case where there is no
 // script context for the iframe. See crbug.com/788914
-IN_PROC_BROWSER_TEST_P(WebViewTestNoDomAutomationController,
+IN_PROC_BROWSER_TEST_F(WebViewTestNoDomAutomationController,
                        LoadWebviewInsideIframe) {
   TestHelper("testLoadWebviewInsideIframe",
              "web_view/load_webview_inside_iframe", NEEDS_TEST_SERVER);
@@ -4708,7 +4568,7 @@
   GetGuestViewManager()->WaitForLastGuestDeleted();
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewAccessibilityTest, LoadWebViewAccessibility) {
+IN_PROC_BROWSER_TEST_F(WebViewAccessibilityTest, LoadWebViewAccessibility) {
   content::BrowserAccessibilityState::GetInstance()->EnableAccessibility();
   LoadAppWithGuest("web_view/focus_accessibility");
   content::WebContents* web_contents = GetFirstAppWindowWebContents();
@@ -4716,7 +4576,7 @@
                                                          "Guest button");
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewAccessibilityTest, FocusAccessibility) {
+IN_PROC_BROWSER_TEST_F(WebViewAccessibilityTest, FocusAccessibility) {
   content::BrowserAccessibilityState::GetInstance()->EnableAccessibility();
   LoadAppWithGuest("web_view/focus_accessibility");
   content::WebContents* web_contents = GetFirstAppWindowWebContents();
@@ -4750,7 +4610,7 @@
 // BrowserAccessibilityManager would not be updated due to how we were updating
 // the AXTreeData.
 // The test was disabled. See crbug.com/1141313.
-IN_PROC_BROWSER_TEST_P(WebViewAccessibilityTest,
+IN_PROC_BROWSER_TEST_F(WebViewAccessibilityTest,
                        DISABLED_FocusAccessibilityNestedFrame) {
   content::BrowserAccessibilityState::GetInstance()->EnableAccessibility();
   LoadAppWithGuest("web_view/focus_accessibility");
@@ -4833,7 +4693,7 @@
   size_t count_;
 };
 
-IN_PROC_BROWSER_TEST_P(WebViewAccessibilityTest, DISABLED_TouchAccessibility) {
+IN_PROC_BROWSER_TEST_F(WebViewAccessibilityTest, DISABLED_TouchAccessibility) {
   content::BrowserAccessibilityState::GetInstance()->EnableAccessibility();
   LoadAppWithGuest("web_view/touch_accessibility");
   content::WebContents* web_contents = GetFirstAppWindowWebContents();
@@ -4866,29 +4726,17 @@
   EXPECT_EQ(0U, main_event_watcher.count());
 }
 
-class WebViewGuestScrollTest
-    : public WebViewTestBase,
-      public testing::WithParamInterface<testing::tuple<bool, bool>> {
+class WebViewGuestScrollTest : public WebViewTest,
+                               public testing::WithParamInterface<bool> {
  public:
-  WebViewGuestScrollTest() {
-    scoped_feature_list_.InitWithFeatureState(
-        features::kSiteIsolationForGuests,
-        /*enabled=*/testing::get<0>(GetParam()));
-  }
-
-  bool GetScrollParam() { return testing::get<1>(GetParam()); }
+  bool GetScrollParam() { return GetParam(); }
 
   static std::string DescribeParams(
       const testing::TestParamInfo<ParamType>& info) {
-    auto [is_site_isolation_enabled, is_scroll_disabled] = info.param;
-    return base::StringPrintf(
-        "SiteIsolationForGuests%s_Scroll%s",
-        is_site_isolation_enabled ? "Enabled" : "Disabled",
-        is_scroll_disabled ? "Disabled" : "Enabled");
+    bool is_scroll_disabled = info.param;
+    return base::StringPrintf("Scroll%s",
+                              is_scroll_disabled ? "Disabled" : "Enabled");
   }
-
- private:
-  base::test::ScopedFeatureList scoped_feature_list_;
 };
 
 class WebViewGuestScrollTouchTest : public WebViewGuestScrollTest {
@@ -4908,7 +4756,7 @@
 // different ack results in between these two cases.
 INSTANTIATE_TEST_SUITE_P(WebViewScrollBubbling,
                          WebViewGuestScrollTest,
-                         testing::Combine(testing::Bool(), testing::Bool()),
+                         testing::Bool(),
                          WebViewGuestScrollTest::DescribeParams);
 
 IN_PROC_BROWSER_TEST_P(WebViewGuestScrollTest, TestGuestWheelScrollsBubble) {
@@ -5073,7 +4921,7 @@
 
 INSTANTIATE_TEST_SUITE_P(WebViewScrollBubbling,
                          WebViewGuestScrollTouchTest,
-                         testing::Combine(testing::Bool(), testing::Bool()),
+                         testing::Bool(),
                          WebViewGuestScrollTouchTest::DescribeParams);
 
 IN_PROC_BROWSER_TEST_P(WebViewGuestScrollTouchTest,
@@ -5182,14 +5030,9 @@
   }
 };
 
-INSTANTIATE_TEST_SUITE_P(WebViewTests,
-                         ChromeSignInWebViewTest,
-                         testing::Bool(),
-                         WebViewTest::DescribeParams);
-
 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
 // This verifies the fix for http://crbug.com/667708.
-IN_PROC_BROWSER_TEST_P(ChromeSignInWebViewTest,
+IN_PROC_BROWSER_TEST_F(ChromeSignInWebViewTest,
                        ClosingChromeSignInShouldNotCrash) {
   GURL signin_url{"chrome://chrome-signin/?reason=5"};
 
@@ -5216,7 +5059,7 @@
 #else
 #define MAYBE_NoFindInPageForUnattachedGuest NoFindInPageForUnattachedGuest
 #endif
-IN_PROC_BROWSER_TEST_P(ChromeSignInWebViewTest,
+IN_PROC_BROWSER_TEST_F(ChromeSignInWebViewTest,
                        MAYBE_NoFindInPageForUnattachedGuest) {
   GURL signin_url{"chrome://chrome-signin/?reason=5"};
   ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), signin_url));
@@ -5289,16 +5132,11 @@
   }
 };
 
-INSTANTIATE_TEST_SUITE_P(WebViewTests,
-                         IsolatedOriginWebViewTest,
-                         testing::Bool(),
-                         WebViewTest::DescribeParams);
-
 // Test isolated origins inside a WebView, and make sure that loading an
 // isolated origin in a regular tab's subframe doesn't reuse a WebView process
 // that had loaded it previously, which would result in renderer kills. See
 // https://crbug.com/751916 and https://crbug.com/751920.
-IN_PROC_BROWSER_TEST_P(IsolatedOriginWebViewTest, IsolatedOriginInWebview) {
+IN_PROC_BROWSER_TEST_F(IsolatedOriginWebViewTest, IsolatedOriginInWebview) {
   LoadAppWithGuest("web_view/simple");
   guest_view::GuestViewBase* guest = GetGuestView();
 
@@ -5330,22 +5168,14 @@
   EXPECT_TRUE(NavigateToURLFromRenderer(
       ChildFrameAt(guest->GetGuestMainFrame(), 0), isolated_url));
 
-  // If site isolation for <webview> is not used, the subframe will stay in the
-  // guest process and SiteInstance.  Otherwise, it will be in its own
+  // Since <webview> supports site isolation, the subframe will be in its own
   // SiteInstance and process.
   content::RenderFrameHost* webview_subframe =
       ChildFrameAt(guest->GetGuestMainFrame(), 0);
-  if (content::SiteIsolationPolicy::IsSiteIsolationForGuestsEnabled()) {
-    EXPECT_NE(webview_subframe->GetProcess(),
-              guest->GetGuestMainFrame()->GetProcess());
-    EXPECT_NE(webview_subframe->GetSiteInstance(),
-              guest->GetGuestMainFrame()->GetSiteInstance());
-  } else {
-    EXPECT_EQ(webview_subframe->GetProcess(),
-              guest->GetGuestMainFrame()->GetProcess());
-    EXPECT_EQ(webview_subframe->GetSiteInstance(),
-              guest->GetGuestMainFrame()->GetSiteInstance());
-  }
+  EXPECT_NE(webview_subframe->GetProcess(),
+            guest->GetGuestMainFrame()->GetProcess());
+  EXPECT_NE(webview_subframe->GetSiteInstance(),
+            guest->GetGuestMainFrame()->GetSiteInstance());
 
   // Load a page with subframe in a regular tab.
   ASSERT_TRUE(AddTabAtIndex(0, foo_url, ui::PAGE_TRANSITION_TYPED));
@@ -5380,7 +5210,7 @@
 // regular tab's subframe.  The isolated origin's subframe in the <webview>
 // subframe should not reuse the regular tab's subframe process.  See
 // https://crbug.com/751916 and https://crbug.com/751920.
-IN_PROC_BROWSER_TEST_P(IsolatedOriginWebViewTest,
+IN_PROC_BROWSER_TEST_F(IsolatedOriginWebViewTest,
                        LoadIsolatedOriginInWebviewAfterLoadingInRegularTab) {
   LoadAppWithGuest("web_view/simple");
   guest_view::GuestViewBase* guest = GetGuestView();
@@ -5410,22 +5240,14 @@
   EXPECT_TRUE(NavigateToURLFromRenderer(
       ChildFrameAt(guest->GetGuestMainFrame(), 0), isolated_url));
 
-  // If site isolation for <webview> is not used, the subframe will stay in the
-  // guest process and SiteInstance.  Otherwise, it will be in its own
+  // Since <webview> supports site isolation, the subframe will be in its own
   // SiteInstance and process.
   content::RenderFrameHost* webview_subframe =
       ChildFrameAt(guest->GetGuestMainFrame(), 0);
-  if (content::SiteIsolationPolicy::IsSiteIsolationForGuestsEnabled()) {
-    EXPECT_NE(webview_subframe->GetProcess(),
-              guest->GetGuestMainFrame()->GetProcess());
-    EXPECT_NE(webview_subframe->GetSiteInstance(),
-              guest->GetGuestMainFrame()->GetSiteInstance());
-  } else {
-    EXPECT_EQ(webview_subframe->GetProcess(),
-              guest->GetGuestMainFrame()->GetProcess());
-    EXPECT_EQ(webview_subframe->GetSiteInstance(),
-              guest->GetGuestMainFrame()->GetSiteInstance());
-  }
+  EXPECT_NE(webview_subframe->GetProcess(),
+            guest->GetGuestMainFrame()->GetProcess());
+  EXPECT_NE(webview_subframe->GetSiteInstance(),
+            guest->GetGuestMainFrame()->GetSiteInstance());
 
   // The isolated origin subframe in <webview> shouldn't share the process with
   // the isolated origin subframe in the regular tab.
@@ -5451,7 +5273,7 @@
 // Sends an auto-resize message to the RenderWidgetHost and ensures that the
 // auto-resize transaction is handled and produces a single response message
 // from guest to embedder.
-IN_PROC_BROWSER_TEST_P(WebViewTest, AutoResizeMessages) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, AutoResizeMessages) {
   LoadAppWithGuest("web_view/simple");
 
   // Helper function as this test requires inspecting a number of content::
@@ -5461,7 +5283,7 @@
 }
 
 // Test that a guest sees the synthetic wheel events of a touchpad pinch.
-IN_PROC_BROWSER_TEST_P(WebViewTest, TouchpadPinchSyntheticWheelEvents) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, TouchpadPinchSyntheticWheelEvents) {
   ASSERT_TRUE(StartEmbeddedTestServer());
   LoadAppWithGuest("web_view/touchpad_pinch");
 
@@ -5490,7 +5312,7 @@
 
 // Tests that we can open and close a devtools window that inspects a contents
 // containing a guest view without crashing.
-IN_PROC_BROWSER_TEST_P(WebViewTest, OpenAndCloseDevTools) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, OpenAndCloseDevTools) {
   LoadAppWithGuest("web_view/simple");
   content::WebContents* embedder = GetEmbedderWebContents();
   DevToolsWindow* devtools = DevToolsWindowTesting::OpenDevToolsWindowSync(
@@ -5501,7 +5323,7 @@
 // Tests that random extensions cannot inject content scripts into a platform
 // app's own webview, but the owner platform app can. Regression test for
 // crbug.com/1205675.
-IN_PROC_BROWSER_TEST_P(WebViewTest, NoExtensionScriptsInjectedInWebview) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, NoExtensionScriptsInjectedInWebview) {
   ASSERT_TRUE(StartEmbeddedTestServer());  // For serving guest pages.
 
   // Load an extension which injects a content script at document_end. The
@@ -5557,10 +5379,6 @@
  private:
   base::test::ScopedFeatureList features_;
 };
-INSTANTIATE_TEST_SUITE_P(WebViewTests,
-                         LocalNetworkAccessWebViewTest,
-                         testing::Bool(),
-                         WebViewTest::DescribeParams);
 
 // Verify that Local Network Access has the correct understanding of guests.
 // The chrome-guest:// scheme should only ever be used as a Site URL (and only
@@ -5571,7 +5389,7 @@
 // Note: This test is put in this file for convenience of reusing the entire
 // app testing infrastructure. Other similar tests that do not require that
 // infrastructure live in LocalNetworkAccessBrowserTest.*
-IN_PROC_BROWSER_TEST_P(LocalNetworkAccessWebViewTest,
+IN_PROC_BROWSER_TEST_F(LocalNetworkAccessWebViewTest,
                        SpecialSchemeChromeGuest) {
   LoadAppWithGuest("web_view/simple");
   content::RenderFrameHost* guest_frame_host = GetGuestRenderFrameHost();
@@ -5602,7 +5420,7 @@
 // Verify that navigating a <webview> subframe to a disallowed extension
 // resource (where the extension ID doesn't match the <webview> owner) doesn't
 // result in a renderer kill.  See https://crbug.com/1204094.
-IN_PROC_BROWSER_TEST_P(WebViewTest, LoadDisallowedExtensionURLInSubframe) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, LoadDisallowedExtensionURLInSubframe) {
   ASSERT_TRUE(StartEmbeddedTestServer());
   base::RunLoop run_loop;
   identifiability_metrics_test_helper_.PrepareForTest(&run_loop);
@@ -5659,7 +5477,7 @@
             entry->metrics.begin()->second);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewTest, InsertIntoDetachedIframe) {
+IN_PROC_BROWSER_TEST_F(WebViewTest, InsertIntoDetachedIframe) {
   TestHelper("testInsertIntoDetachedIframe", "web_view/shim",
              NEEDS_TEST_SERVER);
   // Round-trip to ensure the embedder did not crash.
@@ -5675,16 +5493,11 @@
   }
 };
 
-INSTANTIATE_TEST_SUITE_P(WebViewTests,
-                         WebViewPPAPITest,
-                         testing::Bool(),
-                         WebViewTest::DescribeParams);
-
-IN_PROC_BROWSER_TEST_P(WebViewPPAPITest, Shim_TestPlugin) {
+IN_PROC_BROWSER_TEST_F(WebViewPPAPITest, Shim_TestPlugin) {
   TestHelper("testPlugin", "web_view/shim", NO_TEST_SERVER);
 }
 
-IN_PROC_BROWSER_TEST_P(WebViewPPAPITest, Shim_TestPluginLoadPermission) {
+IN_PROC_BROWSER_TEST_F(WebViewPPAPITest, Shim_TestPluginLoadPermission) {
   TestHelper("testPluginLoadPermission", "web_view/shim", NO_TEST_SERVER);
 }
 #endif  // BUILDFLAG(ENABLE_PPAPI)
@@ -5697,17 +5510,13 @@
 constexpr char kWebstoreURLOverride[] = "https://webstore.override.test.com/";
 
 // Helper class for setting up and testing webview behavior with the Chrome
-// Webstore. The tuple param contains the Webstore URL under test and a boolen
-// that is passed to the base class to set if site isolation is enabled for
-// guests.
-class WebstoreWebViewTest
-    : public WebViewGuestSiteIsolationTest,
-      public testing::WithParamInterface<std::tuple<GURL, bool>> {
+// Webstore. The test param contains the Webstore URL to test.
+class WebstoreWebViewTest : public WebViewTest,
+                            public testing::WithParamInterface<GURL> {
  public:
   WebstoreWebViewTest()
-      : WebViewGuestSiteIsolationTest(::testing::get<1>(GetParam())),
-        https_server_(net::EmbeddedTestServer::TYPE_HTTPS),
-        webstore_url_(std::move(::testing::get<0>(GetParam()))) {}
+      : https_server_(net::EmbeddedTestServer::TYPE_HTTPS),
+        webstore_url_(GetParam()) {}
 
   WebstoreWebViewTest(const WebstoreWebViewTest&) = delete;
   WebstoreWebViewTest& operator=(const WebstoreWebViewTest&) = delete;
@@ -5730,22 +5539,22 @@
                                       kWebstoreURLOverride);
     }
     mock_cert_verifier_.SetUpCommandLine(command_line);
-    WebViewGuestSiteIsolationTest::SetUpCommandLine(command_line);
+    WebViewTest::SetUpCommandLine(command_line);
   }
 
   void SetUpOnMainThread() override {
     https_server_.StartAcceptingConnections();
     mock_cert_verifier_.mock_cert_verifier()->set_default_result(net::OK);
-    WebViewGuestSiteIsolationTest::SetUpOnMainThread();
+    WebViewTest::SetUpOnMainThread();
   }
 
   void SetUpInProcessBrowserTestFixture() override {
-    WebViewGuestSiteIsolationTest::SetUpInProcessBrowserTestFixture();
+    WebViewTest::SetUpInProcessBrowserTestFixture();
     mock_cert_verifier_.SetUpInProcessBrowserTestFixture();
   }
 
   void TearDownInProcessBrowserTestFixture() override {
-    WebViewGuestSiteIsolationTest::TearDownInProcessBrowserTestFixture();
+    WebViewTest::TearDownInProcessBrowserTestFixture();
     mock_cert_verifier_.TearDownInProcessBrowserTestFixture();
   }
 
@@ -5753,17 +5562,13 @@
   GURL webstore_url() { return webstore_url_; }
 
   // Provides meaningful param names.
-  static std::string DescribeParams(
-      const testing::TestParamInfo<std::tuple<GURL, bool>>& info) {
-    std::string guest_isolation = ::testing::get<1>(info.param)
-                                      ? "SiteIsolationForGuestsEnabled"
-                                      : "SiteIsolationForGuestsDisabled";
-    GURL webstore_url(std::move(::testing::get<0>(info.param)));
+  static std::string DescribeParams(const testing::TestParamInfo<GURL>& info) {
+    GURL webstore_url(info.param);
     if (webstore_url.spec() == kWebstoreURL)
-      return "OldWebstore_" + guest_isolation;
+      return "OldWebstore";
     if (webstore_url.spec() == kWebstoreURLOverride)
-      return "WebstoreOverride_" + guest_isolation;
-    return "NewWebstore_" + guest_isolation;
+      return "WebstoreOverride";
+    return "NewWebstore";
   }
 
  private:
@@ -5772,14 +5577,12 @@
   GURL webstore_url_;
 };
 
-INSTANTIATE_TEST_SUITE_P(
-    WebViewTests,
-    WebstoreWebViewTest,
-    testing::Combine(testing::Values(GURL(kWebstoreURL),
-                                     GURL(kWebstoreURLOverride),
-                                     GURL(kNewWebstoreURL)),
-                     testing::Bool()),
-    WebstoreWebViewTest::DescribeParams);
+INSTANTIATE_TEST_SUITE_P(WebViewTests,
+                         WebstoreWebViewTest,
+                         testing::Values(GURL(kWebstoreURL),
+                                         GURL(kWebstoreURLOverride),
+                                         GURL(kNewWebstoreURL)),
+                         WebstoreWebViewTest::DescribeParams);
 
 // Ensure that an attempt to load Chrome Web Store in a <webview> is blocked
 // and does not result in a renderer kill.  See https://crbug.com/1197674.
@@ -5817,21 +5620,9 @@
   EXPECT_EQ(false, content::EvalJs(guest, "!!chrome.dashboardPrivate"));
 }
 
-// This is a base class for tests that enable site isolation in <webview>
-// guests.
-class SitePerProcessWebViewTest : public WebViewTestBase {
- public:
-  SitePerProcessWebViewTest() {
-    feature_list_.InitAndEnableFeature(features::kSiteIsolationForGuests);
-  }
-  ~SitePerProcessWebViewTest() override = default;
-  SitePerProcessWebViewTest(const SitePerProcessWebViewTest&) = delete;
-  SitePerProcessWebViewTest& operator=(const SitePerProcessWebViewTest&) =
-      delete;
-
- private:
-  base::test::ScopedFeatureList feature_list_;
-};
+// This is a group of tests that check site isolation properties in <webview>
+// guests.  Note that site isolation in <webview> is always enabled.
+using SitePerProcessWebViewTest = WebViewTest;
 
 // Checks basic site isolation properties when a <webview> main frame and
 // subframe navigate cross-site.
@@ -6622,30 +6413,12 @@
   EXPECT_EQ(fenced_frame->GetProcess(), guest_rfh->GetProcess());
 }
 
-class WebViewFencedFrameTest
-    : public WebViewTestBase,
-      public testing::WithParamInterface<std::tuple<bool, bool>> {
+class WebViewFencedFrameTest : public WebViewTest,
+                               public testing::WithParamInterface<bool> {
  public:
   WebViewFencedFrameTest() {
-    bool should_enable_site_isolation_for_guests = std::get<0>(GetParam());
-    bool should_enable_process_isolation_for_fenced_frames =
-        std::get<1>(GetParam());
-    std::vector<base::test::FeatureRef> enabled_features, disabled_features;
-
-    if (should_enable_site_isolation_for_guests) {
-      enabled_features.push_back(features::kSiteIsolationForGuests);
-    } else {
-      disabled_features.push_back(features::kSiteIsolationForGuests);
-    }
-
-    if (should_enable_process_isolation_for_fenced_frames) {
-      enabled_features.push_back(features::kIsolateFencedFrames);
-    } else {
-      disabled_features.push_back(features::kIsolateFencedFrames);
-    }
-
-    scoped_feature_list_.InitWithFeatures(std::move(enabled_features),
-                                          std::move(disabled_features));
+    scoped_feature_list_.InitWithFeatureState(features::kIsolateFencedFrames,
+                                              /*enabled=*/GetParam());
   }
   ~WebViewFencedFrameTest() override = default;
 
@@ -6655,12 +6428,8 @@
 
   static std::string DescribeParams(
       const testing::TestParamInfo<ParamType>& info) {
-    return base::StringPrintf(
-        "%s_%s",
-        std::get<0>(info.param) ? "SiteIsolationForGuestsEnabled"
-                                : "SiteIsolationForGuestsDisabled",
-        std::get<1>(info.param) ? "IsolateFencedFramesEnabled"
-                                : "IsolateFencedFramesDisabled");
+    return info.param ? "IsolateFencedFramesEnabled"
+                      : "IsolateFencedFramesDisabled";
   }
 
  private:
@@ -6670,7 +6439,7 @@
 
 INSTANTIATE_TEST_SUITE_P(WebViewTests,
                          WebViewFencedFrameTest,
-                         testing::Combine(testing::Bool(), testing::Bool()),
+                         testing::Bool(),
                          WebViewFencedFrameTest::DescribeParams);
 
 IN_PROC_BROWSER_TEST_P(WebViewFencedFrameTest,
@@ -6692,10 +6461,8 @@
             guest_rfh->GetSiteInstance()->GetStoragePartitionConfig());
 
   // The fenced frame will be in a different process from the embedding guest
-  // only if both Site Isolation for Guests and Process Isolation for Fenced
-  // Frames are enabled.
-  if (content::SiteIsolationPolicy::IsSiteIsolationForGuestsEnabled() &&
-      content::SiteIsolationPolicy::
+  // only if Process Isolation for Fenced Frames is enabled.
+  if (content::SiteIsolationPolicy::
           IsProcessIsolationForFencedFramesEnabled()) {
     EXPECT_NE(ff_rfh->GetProcess(), guest_rfh->GetProcess());
   } else {
@@ -6723,12 +6490,7 @@
   base::test::ScopedFeatureList scoped_feature_list_;
 };
 
-INSTANTIATE_TEST_SUITE_P(WebViewTests,
-                         WebViewPortalTest,
-                         testing::Bool(),
-                         WebViewTest::DescribeParams);
-
 // Creates and activates a <portal> element inside a <webview>.
-IN_PROC_BROWSER_TEST_P(WebViewPortalTest, PortalActivationInGuest) {
+IN_PROC_BROWSER_TEST_F(WebViewPortalTest, PortalActivationInGuest) {
   TestHelper("testActivatePortal", "web_view/shim", NEEDS_TEST_SERVER);
 }
diff --git a/chrome/browser/apps/guest_view/web_view_interactive_browsertest.cc b/chrome/browser/apps/guest_view/web_view_interactive_browsertest.cc
index 185ffa6..a0cd986 100644
--- a/chrome/browser/apps/guest_view/web_view_interactive_browsertest.cc
+++ b/chrome/browser/apps/guest_view/web_view_interactive_browsertest.cc
@@ -153,6 +153,11 @@
     command_line->AppendSwitch(blink::switches::kAllowPreCommitInput);
   }
 
+  void SetUpOnMainThread() override {
+    host_resolver()->AddRule("*", "127.0.0.1");
+    extensions::PlatformAppBrowserTest::SetUpOnMainThread();
+  }
+
   TestGuestViewManager* GetGuestViewManager() {
     TestGuestViewManager* manager = static_cast<TestGuestViewManager*>(
         TestGuestViewManager::FromBrowserContext(browser()->profile()));
@@ -1658,34 +1663,9 @@
 }
 #endif
 
-// Base class for interactive tests that enable site isolation in <webview>
-// guests.
-class SitePerProcessWebViewInteractiveTest : public WebViewInteractiveTest {
- public:
-  SitePerProcessWebViewInteractiveTest() = default;
-  ~SitePerProcessWebViewInteractiveTest() override = default;
-  SitePerProcessWebViewInteractiveTest(
-      const SitePerProcessWebViewInteractiveTest&) = delete;
-  SitePerProcessWebViewInteractiveTest& operator=(
-      const SitePerProcessWebViewInteractiveTest&) = delete;
-
-  void SetUp() override {
-    feature_list_.InitAndEnableFeature(features::kSiteIsolationForGuests);
-    WebViewInteractiveTest::SetUp();
-  }
-
-  void SetUpOnMainThread() override {
-    host_resolver()->AddRule("*", "127.0.0.1");
-    WebViewInteractiveTest::SetUpOnMainThread();
-  }
-
- private:
-  base::test::ScopedFeatureList feature_list_;
-};
-
 // Check that when a focused <webview> navigates cross-process, the focus
 // is preserved in the new page. See https://crbug.com/1358210.
-IN_PROC_BROWSER_TEST_F(SitePerProcessWebViewInteractiveTest,
+IN_PROC_BROWSER_TEST_F(WebViewInteractiveTest,
                        FocusPreservedAfterCrossProcessNavigation) {
   // Load and show a platform app with a <webview> on a data: URL.
   ASSERT_TRUE(StartEmbeddedTestServer());
diff --git a/chrome/browser/ash/BUILD.gn b/chrome/browser/ash/BUILD.gn
index aa1ae38..a268f58 100644
--- a/chrome/browser/ash/BUILD.gn
+++ b/chrome/browser/ash/BUILD.gn
@@ -983,6 +983,8 @@
     "dbus/vm/vm_permission_service_provider.h",
     "dbus/vm/vm_sk_forwarding_service_provider.cc",
     "dbus/vm/vm_sk_forwarding_service_provider.h",
+    "dbus/vm/vm_wl_service_provider.cc",
+    "dbus/vm/vm_wl_service_provider.h",
     "device_name/device_name_applier.h",
     "device_name/device_name_applier_impl.cc",
     "device_name/device_name_applier_impl.h",
@@ -2495,6 +2497,8 @@
     "policy/reporting/metrics_reporting/apps/app_platform_metrics_retriever.h",
     "policy/reporting/metrics_reporting/apps/app_usage_collector.cc",
     "policy/reporting/metrics_reporting/apps/app_usage_collector.h",
+    "policy/reporting/metrics_reporting/apps/app_usage_telemetry_sampler.cc",
+    "policy/reporting/metrics_reporting/apps/app_usage_telemetry_sampler.h",
     "policy/reporting/metrics_reporting/audio/audio_events_observer.cc",
     "policy/reporting/metrics_reporting/audio/audio_events_observer.h",
     "policy/reporting/metrics_reporting/cros_healthd_metric_sampler.cc",
@@ -3419,6 +3423,7 @@
     "//chromeos/ash/components/dbus:metrics_event_proto",
     "//chromeos/ash/components/dbus:vm_applications_apps_proto",
     "//chromeos/ash/components/dbus:vm_launch_proto",
+    "//chromeos/ash/components/dbus:vm_wl_proto",
     "//chromeos/ash/components/dbus/anomaly_detector",
     "//chromeos/ash/components/dbus/anomaly_detector:proto",
     "//chromeos/ash/components/dbus/attestation",
@@ -4167,6 +4172,7 @@
     "dbus/vm/org.chromium.VmLaunchService.conf",
     "dbus/vm/org.chromium.VmPermissionService.conf",
     "dbus/vm/org.chromium.VmSKForwardingService.conf",
+    "dbus/vm/org.chromium.VmWlService.conf",
   ]
   output_conf_file = "$root_out_dir/dbus/chrome_dbus_services.conf"
   outputs = [ output_conf_file ]
@@ -5471,6 +5477,7 @@
     "policy/reporting/metrics_reporting/apps/app_events_observer_unittest.cc",
     "policy/reporting/metrics_reporting/apps/app_platform_metrics_retriever_unittest.cc",
     "policy/reporting/metrics_reporting/apps/app_usage_collector_unittest.cc",
+    "policy/reporting/metrics_reporting/apps/app_usage_telemetry_sampler_unittest.cc",
     "policy/reporting/metrics_reporting/audio/audio_events_observer_unittest.cc",
     "policy/reporting/metrics_reporting/cros_healthd_metric_sampler_unittest.cc",
     "policy/reporting/metrics_reporting/cros_reporting_settings_unittest.cc",
@@ -5656,6 +5663,8 @@
     "throttle_service_unittest.cc",
     "tpm_firmware_update_unittest.cc",
     "usb/cros_usb_detector_unittest.cc",
+    "video_conference/video_conference_manager_ash_unittest.cc",
+    "wallpaper_handlers/wallpaper_handlers_unittest.cc",
     "web_applications/face_ml/chrome_face_ml_user_provider_unittest.cc",
     "web_applications/help_app/help_app_discover_tab_notification_unittest.cc",
     "web_applications/help_app/help_app_notification_controller_unittest.cc",
diff --git a/chrome/browser/ash/app_list/search/system_info/system_info_card_provider.cc b/chrome/browser/ash/app_list/search/system_info/system_info_card_provider.cc
index 50378f7..8cca2543 100644
--- a/chrome/browser/ash/app_list/search/system_info/system_info_card_provider.cc
+++ b/chrome/browser/ash/app_list/search/system_info/system_info_card_provider.cc
@@ -321,7 +321,10 @@
 
   const absl::optional<power_manager::PowerSupplyProperties>& proto =
       chromeos::PowerManagerClient::Get()->GetLastStatus();
-  DCHECK(proto);
+  if (!proto) {
+    EmitBatteryDataError(BatteryDataError::kNoData);
+    return;
+  }
 
   PopulatePowerStatus(proto.value(), *new_battery_health.get());
 
diff --git a/chrome/browser/ash/app_list/search/system_info/system_info_card_provider_unittest.cc b/chrome/browser/ash/app_list/search/system_info/system_info_card_provider_unittest.cc
index bd6117f6..9cc67b7 100644
--- a/chrome/browser/ash/app_list/search/system_info/system_info_card_provider_unittest.cc
+++ b/chrome/browser/ash/app_list/search/system_info/system_info_card_provider_unittest.cc
@@ -16,7 +16,9 @@
 #include "base/path_service.h"
 #include "base/strings/strcat.h"
 #include "base/system/sys_info.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_running_on_chromeos.h"
+#include "chrome/browser/ash/app_list/search/system_info/system_info_util.h"
 #include "chrome/browser/ash/app_list/search/test/test_search_controller.h"
 #include "chrome/browser/ash/file_manager/fake_disk_mount_manager.h"
 #include "chrome/browser/ash/file_manager/path_util.h"
@@ -44,6 +46,16 @@
 
 namespace healthd_mojom = ash::cros_healthd::mojom;
 
+constexpr char kBatteryDataError[] =
+    "Apps.AppList.SystemInfoProvider.Error.Battery";
+
+constexpr char kProbeErrorBatteryInfo[] =
+    "Apps.AppList.SystemInfoProvider.CrosHealthdProbeError.BatteryInfo";
+constexpr char kProbeErrorCpuInfo[] =
+    "Apps.AppList.SystemInfoProvider.CrosHealthdProbeError.CpuInfo";
+constexpr char kProbeErrorMemoryInfo[] =
+    "Apps.AppList.SystemInfoProvider.CrosHealthdProbeError.MemoryInfo";
+
 void SetProbeTelemetryInfoResponse(healthd_mojom::BatteryInfoPtr battery_info,
                                    healthd_mojom::CpuInfoPtr cpu_info,
                                    healthd_mojom::MemoryInfoPtr memory_info) {
@@ -252,6 +264,50 @@
   ASSERT_EQ(expected_size, stat.st_size);
 }
 
+healthd_mojom::ProbeErrorPtr CreateProbeError(
+    healthd_mojom::ErrorType error_type) {
+  auto probe_error = healthd_mojom::ProbeError::New();
+  probe_error->type = error_type;
+  probe_error->msg = "probe error";
+  return probe_error;
+}
+
+void VerifyProbeErrorBucketCounts(const base::HistogramTester& tester,
+                                  const std::string& metric_name,
+                                  size_t expected_unknown_error,
+                                  size_t expected_parse_error,
+                                  size_t expected_service_unavailable,
+                                  size_t expected_system_utility_error,
+                                  size_t expected_file_read_error) {
+  tester.ExpectBucketCount(metric_name, healthd_mojom::ErrorType::kUnknown,
+                           expected_unknown_error);
+  tester.ExpectBucketCount(metric_name, healthd_mojom::ErrorType::kParseError,
+                           expected_parse_error);
+  tester.ExpectBucketCount(metric_name,
+                           healthd_mojom::ErrorType::kServiceUnavailable,
+                           expected_service_unavailable);
+  tester.ExpectBucketCount(metric_name,
+                           healthd_mojom::ErrorType::kSystemUtilityError,
+                           expected_system_utility_error);
+  tester.ExpectBucketCount(metric_name,
+                           healthd_mojom::ErrorType::kFileReadError,
+                           expected_file_read_error);
+}
+
+void VerifyBatteryDataErrorBucketCounts(
+    const base::HistogramTester& tester,
+    size_t expected_no_data_error,
+    size_t expected_not_a_number_error,
+    size_t expected_expectation_not_met_error) {
+  tester.ExpectBucketCount(kBatteryDataError, BatteryDataError::kNoData,
+                           expected_no_data_error);
+  tester.ExpectBucketCount(kBatteryDataError, BatteryDataError::kNotANumber,
+                           expected_not_a_number_error);
+  tester.ExpectBucketCount(kBatteryDataError,
+                           BatteryDataError::kExpectationNotMet,
+                           expected_expectation_not_met_error);
+}
+
 }  // namespace
 
 class SystemInfoCardProviderTest : public testing::Test {
@@ -323,7 +379,7 @@
   std::unique_ptr<SystemInfoCardProvider> provider_;
 };
 
-TEST_F(SystemInfoCardProviderTest, version) {
+TEST_F(SystemInfoCardProviderTest, Version) {
   StartSearch(u"version");
   Wait();
   std::u16string official =
@@ -361,7 +417,7 @@
   EXPECT_TRUE(details.GetTextTags().empty());
 }
 
-TEST_F(SystemInfoCardProviderTest, cpu) {
+TEST_F(SystemInfoCardProviderTest, Cpu) {
   int temp_1 = 40;
   int temp_2 = 50;
   int temp_3 = 15;
@@ -400,7 +456,29 @@
   EXPECT_TRUE(details.GetTextTags().empty());
 }
 
-TEST_F(SystemInfoCardProviderTest, memory) {
+TEST_F(SystemInfoCardProviderTest, CpuProbeError) {
+  auto info = healthd_mojom::TelemetryInfo::New();
+  base::HistogramTester histogram_tester;
+
+  auto cpu_result = healthd_mojom::CpuResult::NewError(
+      CreateProbeError(healthd_mojom::ErrorType::kFileReadError));
+  info->cpu_result = std::move(cpu_result);
+  ash::cros_healthd::FakeCrosHealthd::Get()
+      ->SetProbeTelemetryInfoResponseForTesting(info);
+
+  StartSearch(u"cpu");
+  Wait();
+
+  EXPECT_TRUE(results().empty());
+  VerifyProbeErrorBucketCounts(histogram_tester, kProbeErrorCpuInfo,
+                               /*expected_unknown_error=*/0,
+                               /*expected_parse_error=*/0,
+                               /*expected_service_unavailable=*/0,
+                               /*expected_system_utility_error=*/0,
+                               /*expected_file_read_error=*/1);
+}
+
+TEST_F(SystemInfoCardProviderTest, Memory) {
   const uint32_t total_memory_kib = 8000000;
   const uint32_t free_memory_kib = 2000000;
   const uint32_t available_memory_kib = 4000000;
@@ -425,18 +503,40 @@
 
   ASSERT_EQ(results()[0]->title_text_vector().size(), 1u);
   const auto& title = results()[0]->title_text_vector()[0];
-  ASSERT_EQ(title.GetType(), ash::SearchResultTextItemType::kString);
+  EXPECT_EQ(title.GetType(), ash::SearchResultTextItemType::kString);
   EXPECT_EQ(title.GetText(), u"");
   EXPECT_TRUE(title.GetTextTags().empty());
 
   ASSERT_EQ(results()[0]->details_text_vector().size(), 1u);
   const auto& details = results()[0]->details_text_vector()[0];
-  ASSERT_EQ(details.GetType(), ash::SearchResultTextItemType::kString);
+  EXPECT_EQ(details.GetType(), ash::SearchResultTextItemType::kString);
   EXPECT_EQ(details.GetText(), u"3.8 GB of 7.6 GB available");
   EXPECT_TRUE(details.GetTextTags().empty());
 }
 
-TEST_F(SystemInfoCardProviderTest, battery) {
+TEST_F(SystemInfoCardProviderTest, MemoryProbeError) {
+  auto info = healthd_mojom::TelemetryInfo::New();
+  base::HistogramTester histogram_tester;
+
+  auto memory_result = healthd_mojom::MemoryResult::NewError(
+      CreateProbeError(healthd_mojom::ErrorType::kSystemUtilityError));
+  info->memory_result = std::move(memory_result);
+  ash::cros_healthd::FakeCrosHealthd::Get()
+      ->SetProbeTelemetryInfoResponseForTesting(info);
+
+  StartSearch(u"memory");
+  Wait();
+
+  EXPECT_TRUE(results().empty());
+  VerifyProbeErrorBucketCounts(histogram_tester, kProbeErrorMemoryInfo,
+                               /*expected_unknown_error=*/0,
+                               /*expected_parse_error=*/0,
+                               /*expected_service_unavailable=*/0,
+                               /*expected_system_utility_error=*/1,
+                               /*expected_file_read_error=*/0);
+}
+
+TEST_F(SystemInfoCardProviderTest, Battery) {
   const double charge_full_now = 20;
   const double charge_full_design = 26;
   const int32_t cycle_count = 500;
@@ -503,7 +603,95 @@
   EXPECT_TRUE(updated_title.GetTextTags().empty());
 }
 
-TEST_F(SystemInfoCardProviderTest, storage) {
+TEST_F(SystemInfoCardProviderTest, BatteryProbeError) {
+  auto info = healthd_mojom::TelemetryInfo::New();
+  base::HistogramTester histogram_tester;
+
+  auto battery_result = healthd_mojom::BatteryResult::NewError(
+      CreateProbeError(healthd_mojom::ErrorType::kParseError));
+  info->battery_result = std::move(battery_result);
+  ash::cros_healthd::FakeCrosHealthd::Get()
+      ->SetProbeTelemetryInfoResponseForTesting(info);
+
+  const auto power_source =
+      power_manager::PowerSupplyProperties_ExternalPower_AC;
+  const auto battery_state =
+      power_manager::PowerSupplyProperties_BatteryState_CHARGING;
+  const bool is_calculating_battery_time = false;
+  const int64_t time_to_full_secs = 1000;
+  const int64_t time_to_empty_secs = 0;
+  const double battery_percent = 94.0;
+
+  SetPowerManagerProperties(power_source, battery_state,
+                            is_calculating_battery_time, time_to_full_secs,
+                            time_to_empty_secs, battery_percent);
+
+  StartSearch(u"battery");
+  Wait();
+
+  VerifyProbeErrorBucketCounts(histogram_tester, kProbeErrorBatteryInfo,
+                               /*expected_unknown_error=*/0,
+                               /*expected_parse_error=*/1,
+                               /*expected_service_unavailable=*/0,
+                               /*expected_system_utility_error=*/0,
+                               /*expected_file_read_error=*/0);
+  EXPECT_TRUE(results().empty());
+}
+
+TEST_F(SystemInfoCardProviderTest, BatteryProbeDataError) {
+  auto info = healthd_mojom::TelemetryInfo::New();
+  base::HistogramTester histogram_tester;
+
+  SetCrosHealthdBatteryHealthResponse(0, 0, 0);
+
+  const auto power_source =
+      power_manager::PowerSupplyProperties_ExternalPower_AC;
+  const auto battery_state =
+      power_manager::PowerSupplyProperties_BatteryState_CHARGING;
+  const bool is_calculating_battery_time = false;
+  const int64_t time_to_full_secs = 1000;
+  const int64_t time_to_empty_secs = 0;
+  const double battery_percent = 94.0;
+
+  SetPowerManagerProperties(power_source, battery_state,
+                            is_calculating_battery_time, time_to_full_secs,
+                            time_to_empty_secs, battery_percent);
+
+  StartSearch(u"battery");
+  Wait();
+
+  VerifyBatteryDataErrorBucketCounts(histogram_tester,
+                                     /*expected_no_data_error=*/0,
+                                     /*expected_not_a_number_error=*/0,
+                                     /*expected_expectation_not_met_error=*/1);
+  EXPECT_TRUE(results().empty());
+}
+
+TEST_F(SystemInfoCardProviderTest, BatteryPowerManagerError) {
+  auto info = healthd_mojom::TelemetryInfo::New();
+  base::HistogramTester histogram_tester;
+
+  const double charge_full_now = 20;
+  const double charge_full_design = 26;
+  const int32_t cycle_count = 500;
+
+  SetCrosHealthdBatteryHealthResponse(charge_full_now, charge_full_design,
+                                      cycle_count);
+
+  absl::nullopt_t props = absl::nullopt;
+  chromeos::FakePowerManagerClient::Get()->UpdatePowerProperties(props);
+
+  StartSearch(u"battery");
+  Wait();
+
+  VerifyBatteryDataErrorBucketCounts(histogram_tester,
+                                     /*expected_no_data_error=*/1,
+                                     /*expected_not_a_number_error=*/0,
+                                     /*expected_expectation_not_met_error=*/0);
+  EXPECT_TRUE(results().empty());
+}
+
+TEST_F(SystemInfoCardProviderTest, Storage) {
   base::ScopedAllowBlockingForTesting allow_blocking;
 
   // Get local filesystem storage statistics.
diff --git a/chrome/browser/ash/app_list/search/system_info/system_info_util.cc b/chrome/browser/ash/app_list/search/system_info/system_info_util.cc
index adb67c45..809a480 100644
--- a/chrome/browser/ash/app_list/search/system_info/system_info_util.cc
+++ b/chrome/browser/ash/app_list/search/system_info/system_info_util.cc
@@ -25,20 +25,6 @@
 
 constexpr int kMilliampsInAnAmp = 1000;
 
-// The enums below are used in histograms, do not remove/renumber entries. If
-// you're adding to any of these enums, update the corresponding enum listing in
-// tools/metrics/histograms/enums.xml: CrosDiagnosticsDataError.
-enum class DataError {
-  // Null or nullptr value.
-  kNoData = 0,
-  // For numeric values that are NaN.
-  kNotANumber = 1,
-  // Expectation about data not met. Ex. routing prefix is between zero and
-  // thirty-two.
-  kExpectationNotMet = 2,
-  kMaxValue = kExpectationNotMet,
-};
-
 const std::string GetMetricNameForSourceType(
     const base::StringPiece source_type) {
   if (source_type == "cpu info") {
@@ -70,11 +56,6 @@
   base::UmaHistogramEnumeration(metric_name, error_type);
 }
 
-void EmitBatteryDataError(DataError error) {
-  base::UmaHistogramEnumeration("Apps.AppList.SystemInfoProvider.Error.Battery",
-                                error);
-}
-
 template <typename TResult, typename TTag>
 
 bool CheckResponse(const TResult& result,
@@ -100,6 +81,11 @@
 
 }  // namespace
 
+void EmitBatteryDataError(BatteryDataError error) {
+  base::UmaHistogramEnumeration("Apps.AppList.SystemInfoProvider.Error.Battery",
+                                error);
+}
+
 healthd::MemoryInfo* GetMemoryInfo(const healthd::TelemetryInfo& info) {
   const healthd::MemoryResultPtr& memory_result = info.memory_result;
   if (!CheckResponse(memory_result, healthd::MemoryResult::Tag::kMemoryInfo,
@@ -117,7 +103,25 @@
     return nullptr;
   }
 
-  return battery_result->get_battery_info().get();
+  const healthd::BatteryInfo* battery_info =
+      battery_result->get_battery_info().get();
+  if (battery_info->charge_full == 0) {
+    LOG(ERROR) << "charge_full from battery_info should not be zero.";
+    EmitBatteryDataError(BatteryDataError::kExpectationNotMet);
+    return nullptr;
+  }
+
+  // Handle values in battery_info which could cause a SIGFPE. See b/227485637.
+  if (isnan(battery_info->charge_full) ||
+      isnan(battery_info->charge_full_design) ||
+      battery_info->charge_full_design == 0) {
+    LOG(ERROR) << "battery_info values could cause SIGFPE crash: { "
+               << "charge_full_design: " << battery_info->charge_full_design
+               << ", charge_full: " << battery_info->charge_full << " }";
+    return nullptr;
+  }
+
+  return battery_info;
 }
 
 healthd::CpuInfo* GetCpuInfo(const healthd::TelemetryInfo& info) {
@@ -195,27 +199,10 @@
       total_scaled_ghz / cpu_info.physical_cpus[0]->logical_cpus.size());
 }
 
-void PopulateBatteryHealth(
-    const ash::cros_healthd::mojom::BatteryInfo& battery_info,
-    BatteryHealth& battery_health) {
+void PopulateBatteryHealth(const healthd::BatteryInfo& battery_info,
+                           BatteryHealth& battery_health) {
   battery_health.SetCycleCount(battery_info.cycle_count);
 
-  if (battery_info.charge_full == 0) {
-    LOG(ERROR) << "charge_full from battery_info should not be zero.";
-    EmitBatteryDataError(DataError::kExpectationNotMet);
-  }
-
-  // Handle values in battery_info which could cause a SIGFPE. See b/227485637.
-  if (isnan(battery_info.charge_full) ||
-      isnan(battery_info.charge_full_design) ||
-      battery_info.charge_full_design == 0) {
-    LOG(ERROR) << "battery_info values could cause SIGFPE crash: { "
-               << "charge_full_design: " << battery_info.charge_full_design
-               << ", charge_full: " << battery_info.charge_full << " }";
-    battery_health.SetBatteryWearPercentage(0);
-    return;
-  }
-
   double charge_full_now_milliamp_hours =
       battery_info.charge_full * kMilliampsInAnAmp;
   double charge_full_design_milliamp_hours =
diff --git a/chrome/browser/ash/app_list/search/system_info/system_info_util.h b/chrome/browser/ash/app_list/search/system_info/system_info_util.h
index dfab86f..eba57e5 100644
--- a/chrome/browser/ash/app_list/search/system_info/system_info_util.h
+++ b/chrome/browser/ash/app_list/search/system_info/system_info_util.h
@@ -17,6 +17,22 @@
 
 namespace app_list {
 
+// The enums below are used in histograms, do not remove/renumber entries. If
+// you're adding to any of these enums, update the corresponding enum listing in
+// tools/metrics/histograms/enums.xml: CrosDiagnosticsDataError.
+enum class BatteryDataError {
+  // Null or nullptr value.
+  kNoData = 0,
+  // For numeric values that are NaN.
+  kNotANumber = 1,
+  // Expectation about data not met. Ex. routing prefix is between zero and
+  // thirty-two.
+  kExpectationNotMet = 2,
+  kMaxValue = kExpectationNotMet,
+};
+
+void EmitBatteryDataError(BatteryDataError error);
+
 // Extracts MemoryInfo from `info`. Logs and returns a nullptr if MemoryInfo
 // in not present.
 ash::cros_healthd::mojom::MemoryInfo* GetMemoryInfo(
diff --git a/chrome/browser/ash/arc/accessibility/arc_accessibility_helper_bridge_browsertest.cc b/chrome/browser/ash/arc/accessibility/arc_accessibility_helper_bridge_browsertest.cc
index de850c1..31c4518 100644
--- a/chrome/browser/ash/arc/accessibility/arc_accessibility_helper_bridge_browsertest.cc
+++ b/chrome/browser/ash/arc/accessibility/arc_accessibility_helper_bridge_browsertest.cc
@@ -195,17 +195,24 @@
 IN_PROC_BROWSER_TEST_F(ArcAccessibilityHelperBridgeBrowserTest,
                        RequestTreeSyncOnWindowIdChange) {
   auto shell_surface1 = MakeTestArcWindow("org.chromium.arc.1");
+  aura::Window* window1 = shell_surface1->GetWidget()->GetNativeWindow();
+  aura::Window child_window1 = aura::Window(nullptr);
+  child_window1.Init(ui::LAYER_NOT_DRAWN);
+  window1->AddChild(&child_window1);
+
   auto shell_surface2 = MakeTestArcWindow("org.chromium.arc.2");
+  aura::Window* window2 = shell_surface2->GetWidget()->GetNativeWindow();
+  aura::Window child_window2 = aura::Window(nullptr);
+  child_window2.Init(ui::LAYER_NOT_DRAWN);
+  window2->AddChild(&child_window2);
 
   wm::ActivationClient* activation_client =
       ash::Shell::Get()->activation_client();
-  activation_client->ActivateWindow(
-      shell_surface1->GetWidget()->GetNativeWindow());
+  activation_client->ActivateWindow(window1);
 
   AccessibilityManager::Get()->EnableSpokenFeedback(true);
 
-  exo::SetShellClientAccessibilityId(
-      shell_surface1->GetWidget()->GetNativeWindow(), 10);
+  exo::SetShellClientAccessibilityId(&child_window1, 10);
 
   EXPECT_TRUE(
       fake_accessibility_helper_instance_->last_requested_tree_window_key()
@@ -214,15 +221,13 @@
       10U, fake_accessibility_helper_instance_->last_requested_tree_window_key()
                ->get_window_id());
 
-  exo::SetShellClientAccessibilityId(
-      shell_surface2->GetWidget()->GetNativeWindow(), 20);
+  exo::SetShellClientAccessibilityId(&child_window2, 20);
 
   EXPECT_EQ(
       20U, fake_accessibility_helper_instance_->last_requested_tree_window_key()
                ->get_window_id());
 
-  exo::SetShellClientAccessibilityId(
-      shell_surface2->GetWidget()->GetNativeWindow(), 21);
+  exo::SetShellClientAccessibilityId(&child_window2, 21);
 
   EXPECT_EQ(
       21U, fake_accessibility_helper_instance_->last_requested_tree_window_key()
diff --git a/chrome/browser/ash/arc/accessibility/arc_accessibility_tree_tracker.cc b/chrome/browser/ash/arc/accessibility/arc_accessibility_tree_tracker.cc
index e0172ec8..91c1fa5 100644
--- a/chrome/browser/ash/arc/accessibility/arc_accessibility_tree_tracker.cc
+++ b/chrome/browser/ash/arc/accessibility/arc_accessibility_tree_tracker.cc
@@ -185,10 +185,7 @@
   void OnWindowPropertyChanged(aura::Window* window,
                                const void* key,
                                intptr_t old) override {
-    // TODO(b/270904414): remove kClientAccessibilityIdKey once sending a11y id
-    // is fully migrated to per-window level.
-    if (key != exo::kApplicationIdKey &&
-        key != ash::kClientAccessibilityIdKey) {
+    if (key != exo::kApplicationIdKey) {
       return;
     }
     owner_->UpdateTopWindowIds(window);
@@ -840,24 +837,17 @@
   if (!task_id.has_value())
     return;
 
-  if (task_id_to_window_.count(task_id.value()) == 0) {
-    task_id_to_window_.emplace(task_id.value(), window);
-
-    // Force re-evaluate children so that window_id and task_id are correctly
-    // mapped.
-    for (aura::Window* child : window->children()) {
-      TrackChildWindow(child);
-    }
-  }
-
-  // TODO(b/270904414): remove a11y window id check on top window once sending
-  // a11y id is fully migrated to per-window level.
-  const auto window_id = exo::GetShellClientAccessibilityId(window);
-  if (!window_id.has_value()) {
+  if (task_id_to_window_.count(task_id.value()) > 0) {
+    // We already know this task id.
     return;
   }
+  task_id_to_window_.emplace(task_id.value(), window);
 
-  UpdateWindowIdAndTaskId(window_id.value(), task_id.value());
+  // Force re-evaluate children so that window_id and task_id are correctly
+  // mapped.
+  for (aura::Window* child : window->children()) {
+    TrackChildWindow(child);
+  }
 }
 
 void ArcAccessibilityTreeTracker::UpdateChildWindowIds(aura::Window* window) {
@@ -865,6 +855,10 @@
   if (!window_id.has_value()) {
     return;
   }
+  if (window_id_to_task_id_.find(*window_id) != window_id_to_task_id_.end()) {
+    // We already know this window ID.
+    return;
+  }
 
   aura::Window* parent = FindArcWindow(window);
   auto task_id = GetWindowTaskId(parent);
@@ -872,21 +866,11 @@
     return;
   }
 
-  UpdateWindowIdAndTaskId(window_id.value(), task_id.value());
-}
-
-void ArcAccessibilityTreeTracker::UpdateWindowIdAndTaskId(int32_t window_id,
-                                                          int32_t task_id) {
-  if (window_id_to_task_id_.find(window_id) != window_id_to_task_id_.end()) {
-    // We already know this window ID.
-    return;
-  }
-
-  window_id_to_task_id_[window_id] = task_id;
+  window_id_to_task_id_[*window_id] = *task_id;
 
   // The window ID is new to us. Request the entire tree.
   arc::mojom::AccessibilityWindowKeyPtr window_key =
-      arc::mojom::AccessibilityWindowKey::NewWindowId(window_id);
+      arc::mojom::AccessibilityWindowKey::NewWindowId(*window_id);
   accessibility_helper_instance_.RequestSendAccessibilityTree(
       std::move(window_key));
 }
diff --git a/chrome/browser/ash/arc/accessibility/arc_accessibility_tree_tracker.h b/chrome/browser/ash/arc/accessibility/arc_accessibility_tree_tracker.h
index 48408d1..8d9b23c 100644
--- a/chrome/browser/ash/arc/accessibility/arc_accessibility_tree_tracker.h
+++ b/chrome/browser/ash/arc/accessibility/arc_accessibility_tree_tracker.h
@@ -147,9 +147,6 @@
   // window change.
   void UpdateChildWindowIds(aura::Window* window);
 
-  // Should be notified for mapping from |window_id| to |task_id|.
-  void UpdateWindowIdAndTaskId(int32_t window_id, int32_t task_id);
-
   // Updates properties set to the given aura::Window.
   void UpdateWindowProperties(aura::Window* window);
 
diff --git a/chrome/browser/ash/arc/accessibility/arc_accessibility_tree_tracker_unittest.cc b/chrome/browser/ash/arc/accessibility/arc_accessibility_tree_tracker_unittest.cc
index 265c4ab6..267299b 100644
--- a/chrome/browser/ash/arc/accessibility/arc_accessibility_tree_tracker_unittest.cc
+++ b/chrome/browser/ash/arc/accessibility/arc_accessibility_tree_tracker_unittest.cc
@@ -243,10 +243,14 @@
 
   // A ghost window is replaced with an actual ARC window.
   exo::SetShellApplicationId(test_window.get(), "org.chromium.arc.1");
-  exo::SetShellClientAccessibilityId(test_window.get(), 10);
   test_window->SetProperty(aura::client::kAppType,
                            static_cast<int>(ash::AppType::ARC_APP));
 
+  std::unique_ptr<aura::Window> child_window =
+      CreateWindow(ash::AppType::NON_APP);
+  exo::SetShellClientAccessibilityId(child_window.get(), 10);
+  test_window->AddChild(child_window.get());
+
   tree_tracker.OnAccessibilityEvent(event.Clone().get());
   ASSERT_EQ(1U, key_to_tree.size());
 }
diff --git a/chrome/browser/ash/chrome_browser_main_parts_ash.cc b/chrome/browser/ash/chrome_browser_main_parts_ash.cc
index e001074..1d025a5 100644
--- a/chrome/browser/ash/chrome_browser_main_parts_ash.cc
+++ b/chrome/browser/ash/chrome_browser_main_parts_ash.cc
@@ -89,6 +89,7 @@
 #include "chrome/browser/ash/dbus/vm/vm_launch_service_provider.h"
 #include "chrome/browser/ash/dbus/vm/vm_permission_service_provider.h"
 #include "chrome/browser/ash/dbus/vm/vm_sk_forwarding_service_provider.h"
+#include "chrome/browser/ash/dbus/vm/vm_wl_service_provider.h"
 #include "chrome/browser/ash/device_name/device_name_store.h"
 #include "chrome/browser/ash/diagnostics/diagnostics_browser_delegate_impl.h"
 #include "chrome/browser/ash/display/quirks_manager_delegate_impl.h"
@@ -266,6 +267,7 @@
 #include "services/audio/public/cpp/sounds/sounds_manager.h"
 #include "third_party/cros_system_api/dbus/service_constants.h"
 #include "third_party/cros_system_api/dbus/vm_launch/dbus-constants.h"
+#include "third_party/cros_system_api/dbus/vm_wl/dbus-constants.h"
 #include "ui/base/emoji/emoji_panel_helper.h"
 #include "ui/base/ime/ash/ime_keyboard.h"
 #include "ui/base/ime/ash/input_method_manager.h"
@@ -455,6 +457,12 @@
         CrosDBusService::CreateServiceProviderList(
             std::make_unique<VmPermissionServiceProvider>()));
 
+    vm_wl_service_ = CrosDBusService::Create(
+        system_bus, vm_tools::wl::kVmWlServiceName,
+        dbus::ObjectPath(vm_tools::wl::kVmWlServicePath),
+        CrosDBusService::CreateServiceProviderList(
+            std::make_unique<VmWlServiceProvider>()));
+
     drive_file_stream_service_ = CrosDBusService::Create(
         system_bus, drivefs::kDriveFileStreamServiceName,
         dbus::ObjectPath(drivefs::kDriveFileStreamServicePath),
@@ -572,6 +580,7 @@
     vm_launch_service_.reset();
     vm_sk_forwarding_service_.reset();
     vm_permission_service_.reset();
+    vm_wl_service_.reset();
     drive_file_stream_service_.reset();
     cryptohome_key_delegate_service_.reset();
     encrypted_reporting_service_.reset();
@@ -605,6 +614,7 @@
   std::unique_ptr<CrosDBusService> vm_launch_service_;
   std::unique_ptr<CrosDBusService> vm_sk_forwarding_service_;
   std::unique_ptr<CrosDBusService> vm_permission_service_;
+  std::unique_ptr<CrosDBusService> vm_wl_service_;
   std::unique_ptr<CrosDBusService> drive_file_stream_service_;
   std::unique_ptr<CrosDBusService> cryptohome_key_delegate_service_;
   std::unique_ptr<CrosDBusService> encrypted_reporting_service_;
diff --git a/chrome/browser/ash/crosapi/browser_data_migrator.cc b/chrome/browser/ash/crosapi/browser_data_migrator.cc
index 6b5afc7..5e1e1b3 100644
--- a/chrome/browser/ash/crosapi/browser_data_migrator.cc
+++ b/chrome/browser/ash/crosapi/browser_data_migrator.cc
@@ -218,20 +218,26 @@
   // Check if user exists i.e. not a guest session.
   if (!user)
     return false;
-  // Check if lacros is enabled. If not immediately return.
-  if (!crosapi::browser_util::IsLacrosEnabledForMigration(user,
-                                                          policy_init_state)) {
-    if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+  // Check if profile migration is enabled. If not immediately return.
+  if (!crosapi::browser_util::
+          IsProfileMigrationEnabledWithUserAndPolicyInitState(
+              user, policy_init_state)) {
+    if (crosapi::browser_util::IsLacrosEnabledForMigration(user,
+                                                           policy_init_state) ||
+        base::CommandLine::ForCurrentProcess()->HasSwitch(
             switches::kSafeMode)) {
-      // Skip clearing of flags if in safe mode to make sure
-      // that the migrator does not wipe Lacros user data dir due to unexpected
-      // Ash crashes. Specifically this avoids the following scenario: Ash
-      // experiences a crash loop due to some experimental flag -> experimental
-      // flags get dropped including ones to enable Lacros -> Lacros is
-      // disabled and migration completion flags gets cleared -> on next login
-      // migration is run and wipes existing user data.
-      LOG(WARNING) << "Lacros is disabled but safe mode is enabled so skipping "
-                      "clearing of prefs.";
+      // Skip clearing prefs if Lacros is enabled or Lacros is disabled due to
+      // safe mode. Profile migration can be disabled even if Lacros is enabled
+      // by enabling LacrosProfileMigrationForceOff flag. There's another case
+      // where Lacros is disabled due to "safe mode" being enabled after Ash
+      // crashes. By not clearing prefs in safe mode, we avoid the following
+      // scenario: Ash experiences a crash loop due to some experimental flag ->
+      // experimental flags get dropped including ones to enable Lacros ->
+      // Lacros is disabled and migration completion flags gets cleared -> on
+      // next login migration is run and wipes existing user data.
+      LOG(WARNING)
+          << "Profile migration is disabled but either Lacros is enabled or "
+             "safe mode is enabled so skipping clearing prefs.";
       return false;
     }
 
@@ -239,7 +245,7 @@
     // this log message.
     LOG(WARNING)
         << "Lacros is disabled. Call ClearMigrationAttemptCountForUser() so "
-           "that the migration can be attempted again after once lacros is "
+           "that the migration can be attempted again once migration is "
            "enabled again.";
 
     // If lacros is not enabled other than reaching the maximum retry count of
@@ -254,14 +260,6 @@
     return false;
   }
 
-  if (base::FeatureList::IsEnabled(
-          ash::features::kLacrosProfileMigrationForceOff)) {
-    // TODO(crbug.com/1277848): Once `BrowserDataMigrator` stabilises, remove
-    // this log message.
-    LOG(WARNING) << "Profile migration is disabled by a flag.";
-    return false;
-  }
-
   int attempts = GetMigrationAttemptCountForUser(local_state, user_id_hash);
   // TODO(crbug.com/1178702): Once BrowserDataMigrator stabilises, reduce the
   // log level to VLOG(1).
diff --git a/chrome/browser/ash/crosapi/browser_util.cc b/chrome/browser/ash/crosapi/browser_util.cc
index 58cecc41f..40302cd 100644
--- a/chrome/browser/ash/crosapi/browser_util.cc
+++ b/chrome/browser/ash/crosapi/browser_util.cc
@@ -427,11 +427,6 @@
 }
 
 bool IsProfileMigrationEnabled() {
-  if (base::FeatureList::IsEnabled(
-          ash::features::kLacrosProfileMigrationForceOff)) {
-    return false;
-  }
-
   const UserManager* user_manager = UserManager::Get();
   if (!user_manager) {
     return false;
@@ -442,7 +437,16 @@
     return false;
   }
 
-  return IsLacrosEnabledForMigration(user, PolicyInitState::kAfterInit);
+  return IsProfileMigrationEnabledWithUserAndPolicyInitState(
+      user, PolicyInitState::kAfterInit);
+}
+
+bool IsProfileMigrationEnabledWithUserAndPolicyInitState(
+    const user_manager::User* user,
+    PolicyInitState policy_init_state) {
+  return !base::FeatureList::IsEnabled(
+             ash::features::kLacrosProfileMigrationForceOff) &&
+         IsLacrosEnabledForMigration(user, policy_init_state);
 }
 
 bool IsProfileMigrationAvailable() {
diff --git a/chrome/browser/ash/crosapi/browser_util.h b/chrome/browser/ash/crosapi/browser_util.h
index 84fbb521..229c3a6 100644
--- a/chrome/browser/ash/crosapi/browser_util.h
+++ b/chrome/browser/ash/crosapi/browser_util.h
@@ -404,6 +404,10 @@
 // enabled, the completion of it is required to enable Lacros.
 bool IsProfileMigrationEnabled();
 
+bool IsProfileMigrationEnabledWithUserAndPolicyInitState(
+    const user_manager::User* user,
+    PolicyInitState policy_init_state);
+
 // Returns true if the profile migration is enabled, but not yet completed.
 bool IsProfileMigrationAvailable();
 
diff --git a/chrome/browser/ash/dbus/vm/org.chromium.VmWlService.conf b/chrome/browser/ash/dbus/vm/org.chromium.VmWlService.conf
new file mode 100644
index 0000000..0cee728
--- /dev/null
+++ b/chrome/browser/ash/dbus/vm/org.chromium.VmWlService.conf
@@ -0,0 +1,22 @@
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+  "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<!--
+  Copyright 2023 The Chromium Authors
+  Use of this source code is governed by a BSD-style license that can be
+  found in the LICENSE file.
+-->
+
+<busconfig>
+  <policy user="chronos">
+    <allow own="org.chromium.VmWlService"/>
+  </policy>
+
+  <policy user="crosvm">
+    <allow send_destination="org.chromium.VmWlService"
+           send_interface="org.chromium.VmWlService"
+           send_member="ListenOnSocket"/>
+    <allow send_destination="org.chromium.VmWlService"
+           send_interface="org.chromium.VmWlService"
+           send_member="CloseSocket"/>
+  </policy>
+</busconfig>
diff --git a/chrome/browser/ash/dbus/vm/vm_wl_service_provider.cc b/chrome/browser/ash/dbus/vm/vm_wl_service_provider.cc
new file mode 100644
index 0000000..fee7144b
--- /dev/null
+++ b/chrome/browser/ash/dbus/vm/vm_wl_service_provider.cc
@@ -0,0 +1,107 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ash/dbus/vm/vm_wl_service_provider.h"
+
+#include "base/files/scoped_file.h"
+#include "base/logging.h"
+#include "chrome/browser/ash/guest_os/public/guest_os_wayland_server.h"
+#include "chromeos/ash/components/dbus/vm_wl/wl.pb.h"
+#include "dbus/message.h"
+#include "third_party/cros_system_api/dbus/vm_wl/dbus-constants.h"
+
+namespace ash {
+
+namespace {
+
+void OnExported(const std::string& interface_name,
+                const std::string& method_name,
+                bool success) {
+  LOG_IF(ERROR, !success) << "Failed to export " << interface_name << "."
+                          << method_name;
+}
+
+void Respond(dbus::MethodCall* method_call,
+             dbus::ExportedObject::ResponseSender response_sender,
+             absl::optional<std::string> maybe_error) {
+  if (maybe_error) {
+    std::move(response_sender)
+        .Run(dbus::ErrorResponse::FromMethodCall(method_call, DBUS_ERROR_FAILED,
+                                                 maybe_error.value()));
+    return;
+  }
+  std::move(response_sender).Run(dbus::Response::FromMethodCall(method_call));
+}
+
+}  // namespace
+
+VmWlServiceProvider::VmWlServiceProvider() = default;
+
+VmWlServiceProvider::~VmWlServiceProvider() = default;
+
+void VmWlServiceProvider::Start(
+    scoped_refptr<dbus::ExportedObject> exported_object) {
+  exported_object->ExportMethod(
+      vm_tools::wl::kVmWlServiceInterface,
+      vm_tools::wl::kVmWlServiveListenOnSocketMethod,
+      base::BindRepeating(&VmWlServiceProvider::ListenOnSocket,
+                          weak_ptr_factory_.GetWeakPtr()),
+      base::BindOnce(&OnExported));
+
+  exported_object->ExportMethod(
+      vm_tools::wl::kVmWlServiceInterface,
+      vm_tools::wl::kVmWlServiceCloseSocketMethod,
+      base::BindRepeating(&VmWlServiceProvider::CloseSocket,
+                          weak_ptr_factory_.GetWeakPtr()),
+      base::BindOnce(&OnExported));
+}
+
+void VmWlServiceProvider::ListenOnSocket(
+    dbus::MethodCall* method_call,
+    dbus::ExportedObject::ResponseSender response_sender) {
+  dbus::MessageReader reader(method_call);
+
+  vm_tools::wl::ListenOnSocketRequest request;
+  if (!reader.PopArrayOfBytesAsProto(&request)) {
+    std::move(response_sender)
+        .Run(dbus::ErrorResponse::FromMethodCall(
+            method_call, DBUS_ERROR_INVALID_ARGS,
+            "Unable to parse ListenOnSocketRequest from message"));
+    return;
+  }
+
+  base::ScopedFD socket_fd;
+  if (!reader.PopFileDescriptor(&socket_fd)) {
+    std::move(response_sender)
+        .Run(dbus::ErrorResponse::FromMethodCall(
+            method_call, DBUS_ERROR_INVALID_ARGS,
+            "Unable to parse socket fd from message"));
+    return;
+  }
+
+  guest_os::GuestOsWaylandServer::ListenOnSocket(
+      request, std::move(socket_fd),
+      base::BindOnce(&Respond, method_call, std::move(response_sender)));
+}
+
+void VmWlServiceProvider::CloseSocket(
+    dbus::MethodCall* method_call,
+    dbus::ExportedObject::ResponseSender response_sender) {
+  dbus::MessageReader reader(method_call);
+
+  vm_tools::wl::CloseSocketRequest request;
+  if (!reader.PopArrayOfBytesAsProto(&request)) {
+    std::move(response_sender)
+        .Run(dbus::ErrorResponse::FromMethodCall(
+            method_call, DBUS_ERROR_INVALID_ARGS,
+            "Unable to parse CloseSocketRequest from message"));
+    return;
+  }
+
+  guest_os::GuestOsWaylandServer::CloseSocket(
+      request,
+      base::BindOnce(&Respond, method_call, std::move(response_sender)));
+}
+
+}  // namespace ash
diff --git a/chrome/browser/ash/dbus/vm/vm_wl_service_provider.h b/chrome/browser/ash/dbus/vm/vm_wl_service_provider.h
new file mode 100644
index 0000000..7c11a7f
--- /dev/null
+++ b/chrome/browser/ash/dbus/vm/vm_wl_service_provider.h
@@ -0,0 +1,38 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_ASH_DBUS_VM_VM_WL_SERVICE_PROVIDER_H_
+#define CHROME_BROWSER_ASH_DBUS_VM_VM_WL_SERVICE_PROVIDER_H_
+
+#include "chromeos/ash/components/dbus/services/cros_dbus_service.h"
+
+#include "dbus/exported_object.h"
+
+namespace ash {
+
+class VmWlServiceProvider : public CrosDBusService::ServiceProviderInterface {
+ public:
+  VmWlServiceProvider();
+  ~VmWlServiceProvider() override;
+
+  // Delete copy constructor/assign.
+  VmWlServiceProvider(const VmWlServiceProvider&) = delete;
+  VmWlServiceProvider& operator=(const VmWlServiceProvider&) = delete;
+
+  // CrosDBusService::ServiceProviderInterface overrides:
+  void Start(scoped_refptr<dbus::ExportedObject> exported_object) override;
+
+ private:
+  void ListenOnSocket(dbus::MethodCall* method_call,
+                      dbus::ExportedObject::ResponseSender response_sender);
+
+  void CloseSocket(dbus::MethodCall* method_call,
+                   dbus::ExportedObject::ResponseSender response_sender);
+
+  base::WeakPtrFactory<VmWlServiceProvider> weak_ptr_factory_{this};
+};
+
+}  // namespace ash
+
+#endif  // CHROME_BROWSER_ASH_DBUS_VM_VM_WL_SERVICE_PROVIDER_H_
diff --git a/chrome/browser/ash/extensions/file_manager/event_router.cc b/chrome/browser/ash/extensions/file_manager/event_router.cc
index 60a1af8b..19a34ef2 100644
--- a/chrome/browser/ash/extensions/file_manager/event_router.cc
+++ b/chrome/browser/ash/extensions/file_manager/event_router.cc
@@ -1196,9 +1196,9 @@
   // notifications for folders outside of those being watched by a file watcher.
   if (status.IsCompleted()) {
     std::set<std::pair<base::FilePath, url::Origin>> updated_paths;
-    if (status.destination_folder.is_valid()) {
-      updated_paths.emplace(status.destination_folder.virtual_path(),
-                            status.destination_folder.origin());
+    if (status.GetDestinationFolder().is_valid()) {
+      updated_paths.emplace(status.GetDestinationFolder().virtual_path(),
+                            status.GetDestinationFolder().origin());
     }
     for (const auto& source : status.sources) {
       updated_paths.emplace(source.url.virtual_path().DirName(),
@@ -1219,6 +1219,7 @@
   event_status.task_id = status.task_id;
   event_status.type = GetIOTaskType(status.type);
   event_status.state = GetIOTaskState(status.state);
+  event_status.destination_volume_id = status.GetDestinationVolumeId();
   event_status.show_notification = status.show_notification;
 
   // Speedometer can produce infinite result which can't be serialized to JSON
@@ -1227,9 +1228,9 @@
     event_status.remaining_seconds = status.remaining_seconds;
   }
 
-  if (status.destination_folder.is_valid()) {
+  if (status.GetDestinationFolder().is_valid()) {
     event_status.destination_name =
-        util::GetDisplayablePath(profile_, status.destination_folder)
+        util::GetDisplayablePath(profile_, status.GetDestinationFolder())
             .value_or(base::FilePath())
             .BaseName()
             .value();
diff --git a/chrome/browser/ash/extensions/file_manager/system_notification_manager.cc b/chrome/browser/ash/extensions/file_manager/system_notification_manager.cc
index 82e6b4b..8c23f96 100644
--- a/chrome/browser/ash/extensions/file_manager/system_notification_manager.cc
+++ b/chrome/browser/ash/extensions/file_manager/system_notification_manager.cc
@@ -86,7 +86,7 @@
   bool is_destination_drive =
       drive_integration_service &&
       drive_integration_service->GetMountPointPath().IsParent(
-          status.destination_folder.path());
+          status.GetDestinationFolder().path());
 
   switch (status.type) {
     case OperationType::kCopy:
diff --git a/chrome/browser/ash/extensions/file_manager/system_notification_manager_unittest.cc b/chrome/browser/ash/extensions/file_manager/system_notification_manager_unittest.cc
index f7857c4..4becb5b 100644
--- a/chrome/browser/ash/extensions/file_manager/system_notification_manager_unittest.cc
+++ b/chrome/browser/ash/extensions/file_manager/system_notification_manager_unittest.cc
@@ -1077,7 +1077,7 @@
   status.bytes_transferred = 0;
   status.sources.emplace_back(CreateTestFile("volume/src_file.txt"),
                               absl::nullopt);
-  status.destination_folder = CreateTestFile("volume/dest_dir/");
+  status.SetDestinationFolder(CreateTestFile("volume/dest_dir/"));
 
   // Send the copy begin/queued progress.
   auto* notification_manager = GetSystemNotificationManager();
@@ -1126,7 +1126,7 @@
   status.bytes_transferred = 0;
   status.sources.emplace_back(CreateTestFile("volume/src_file.zip"),
                               absl::nullopt);
-  status.destination_folder = CreateTestFile("volume/src_file/");
+  status.SetDestinationFolder(CreateTestFile("volume/src_file/"));
 
   // Send the copy begin/queued progress.
   auto* notification_manager = GetSystemNotificationManager();
@@ -1176,7 +1176,7 @@
   auto src = CreateTestFile("volume/src_file.txt");
   status.sources.emplace_back(src, absl::nullopt);
   auto dst = CreateTestFile("volume/dest_dir/");
-  status.destination_folder = dst;
+  status.SetDestinationFolder(dst);
 
   auto task = std::make_unique<file_manager::io_task::CopyOrMoveIOTask>(
       file_manager::io_task::OperationType::kCopy,
diff --git a/chrome/browser/ash/file_manager/copy_or_move_io_task.cc b/chrome/browser/ash/file_manager/copy_or_move_io_task.cc
index 8bde9a5..87d81469 100644
--- a/chrome/browser/ash/file_manager/copy_or_move_io_task.cc
+++ b/chrome/browser/ash/file_manager/copy_or_move_io_task.cc
@@ -36,7 +36,7 @@
   DCHECK(type == OperationType::kCopy || type == OperationType::kMove);
   progress_.state = State::kQueued;
   progress_.type = type;
-  progress_.destination_folder = std::move(destination_folder);
+  progress_.SetDestinationFolder(std::move(destination_folder), profile);
   progress_.bytes_transferred = 0;
   progress_.total_bytes = 0;
 
@@ -77,18 +77,18 @@
   if (scanning_feature_enabled) {
     scanning_settings =
         enterprise_connectors::FileTransferAnalysisDelegate::IsEnabledVec(
-            profile_, source_urls_, progress_.destination_folder);
+            profile_, source_urls_, progress_.GetDestinationFolder());
   }
 
   if (scanning_feature_enabled && !scanning_settings.empty()) {
     impl_ = std::make_unique<CopyOrMoveIOTaskScanningImpl>(
         progress_.type, progress_, std::move(destination_file_names_),
-        std::move(scanning_settings), progress_.destination_folder, profile_,
-        file_system_context_, progress_.show_notification);
+        std::move(scanning_settings), progress_.GetDestinationFolder(),
+        profile_, file_system_context_, progress_.show_notification);
   } else {
     impl_ = std::make_unique<CopyOrMoveIOTaskImpl>(
         progress_.type, progress_, std::move(destination_file_names_),
-        progress_.destination_folder, profile_, file_system_context_,
+        progress_.GetDestinationFolder(), profile_, file_system_context_,
         progress_.show_notification);
   }
 
diff --git a/chrome/browser/ash/file_manager/copy_or_move_io_task_impl.cc b/chrome/browser/ash/file_manager/copy_or_move_io_task_impl.cc
index 1d0c774..f7f457f5 100644
--- a/chrome/browser/ash/file_manager/copy_or_move_io_task_impl.cc
+++ b/chrome/browser/ash/file_manager/copy_or_move_io_task_impl.cc
@@ -227,7 +227,7 @@
   DCHECK(idx < progress_.sources.size());
 
   const base::FilePath& source = progress_.sources[idx].url.path();
-  const base::FilePath& destination = progress_.destination_folder.path();
+  const base::FilePath& destination = progress_.GetDestinationFolder().path();
 
   constexpr auto metadata_fields =
       storage::FileSystemOperation::GET_METADATA_FIELD_IS_DIRECTORY |
@@ -301,17 +301,18 @@
   // Got file size for all files at this point!
   speedometer_.SetTotalBytes(progress_.total_bytes);
 
-  if (util::IsNonNativeFileSystemType(progress_.destination_folder.type())) {
+  if (util::IsNonNativeFileSystemType(
+          progress_.GetDestinationFolder().type())) {
     // Destination is a virtual filesystem, so skip checking free space.
     GenerateDestinationURL(0);
   } else {
     // For Drive, check we have enough local disk first, then check quota.
-    base::FilePath path = progress_.destination_folder.path();
+    base::FilePath path = progress_.GetDestinationFolder().path();
     auto* drive_integration_service =
         drive::util::GetIntegrationServiceByProfile(profile_);
     if (drive_integration_service && drive_integration_service->IsMounted() &&
         drive_integration_service->GetMountPointPath().IsParent(
-            progress_.destination_folder.path())) {
+            progress_.GetDestinationFolder().path())) {
       path = drive_integration_service->GetDriveFsHost()->GetDataPath();
     }
     base::ThreadPool::PostTaskAndReplyWithResult(
@@ -329,8 +330,8 @@
   bool is_drive = drive_integration_service &&
                   drive_integration_service->IsMounted() &&
                   drive_integration_service->GetMountPointPath().IsParent(
-                      progress_.destination_folder.path());
-  if (progress_.destination_folder.filesystem_id() ==
+                      progress_.GetDestinationFolder().path());
+  if (progress_.GetDestinationFolder().filesystem_id() ==
           util::GetDownloadsMountPointName(profile_) ||
       is_drive) {
     free_space -= cryptohome::kMinFreeSpaceInBytes;
@@ -342,14 +343,14 @@
   if (progress_.type == OperationType::kMove) {
     for (size_t i = 0; i < source_sizes_.size(); i++) {
       if (!IsCrossFileSystem(profile_, progress_.sources[i].url,
-                             progress_.destination_folder)) {
+                             progress_.GetDestinationFolder())) {
         required_bytes -= source_sizes_[i];
       }
     }
   }
 
   if (required_bytes > free_space) {
-    progress_.outputs.emplace_back(progress_.destination_folder,
+    progress_.outputs.emplace_back(progress_.GetDestinationFolder(),
                                    base::File::FILE_ERROR_NO_SPACE);
     LOG(ERROR) << "Insufficient free space in destination";
     Complete(State::kError);
@@ -358,7 +359,7 @@
 
   if (is_drive) {
     bool is_shared_drive = drive_integration_service->IsSharedDrive(
-        progress_.destination_folder.path());
+        progress_.GetDestinationFolder().path());
     drive_integration_service->GetPooledQuotaUsage(
         base::BindOnce(base::BindOnce(
             &CopyOrMoveIOTaskImpl::GotDrivePooledQuota,
@@ -389,7 +390,7 @@
         !is_shared_drive && usage->total_user_bytes != -1 &&
         (usage->total_user_bytes - usage->used_user_bytes) < required_bytes;
     if (org_exceeded || user_exceeded) {
-      progress_.outputs.emplace_back(progress_.destination_folder,
+      progress_.outputs.emplace_back(progress_.GetDestinationFolder(),
                                      base::File::FILE_ERROR_NO_SPACE);
       LOG(ERROR) << "Insufficient drive quota";
       Complete(State::kError);
@@ -403,7 +404,7 @@
   if (is_shared_drive && drive_integration_service &&
       drive_integration_service->IsMounted()) {
     drive_integration_service->GetMetadata(
-        progress_.destination_folder.path(),
+        progress_.GetDestinationFolder().path(),
         base::BindOnce(&CopyOrMoveIOTaskImpl::GotSharedDriveMetadata,
                        weak_ptr_factory_.GetWeakPtr(), required_bytes));
     return;
@@ -426,7 +427,7 @@
     const auto& quota = metadata->shared_drive_quota;
     if ((quota->individual_quota_bytes_total -
          quota->quota_bytes_used_in_drive) < required_bytes) {
-      progress_.outputs.emplace_back(progress_.destination_folder,
+      progress_.outputs.emplace_back(progress_.GetDestinationFolder(),
                                      base::File::FILE_ERROR_NO_SPACE);
       LOG(ERROR) << "Insufficient shared drive quota";
       Complete(State::kError);
@@ -450,7 +451,8 @@
           : progress_.sources[idx].url.path().BaseName();
 
   util::GenerateUnusedFilename(
-      progress_.destination_folder, destination_file_name, file_system_context_,
+      progress_.GetDestinationFolder(), destination_file_name,
+      file_system_context_,
       base::BindOnce(&CopyOrMoveIOTaskImpl::CopyOrMoveFile,
                      weak_ptr_factory_.GetWeakPtr(), idx));
 }
@@ -462,7 +464,8 @@
   DCHECK(idx < progress_.sources.size());
 
   if (!destination_result.has_value()) {
-    progress_.outputs.emplace_back(progress_.destination_folder, absl::nullopt);
+    progress_.outputs.emplace_back(progress_.GetDestinationFolder(),
+                                   absl::nullopt);
     OnCopyOrMoveComplete(idx, destination_result.error());
     return;
   }
@@ -484,9 +487,9 @@
   // as the parent directory.
   auto basename = source_url.path().BaseName();
   auto replace_url = file_system_context_->CreateCrackedFileSystemURL(
-      progress_.destination_folder.storage_key(),
-      progress_.destination_folder.mount_type(),
-      progress_.destination_folder.virtual_path().Append(
+      progress_.GetDestinationFolder().storage_key(),
+      progress_.GetDestinationFolder().mount_type(),
+      progress_.GetDestinationFolder().virtual_path().Append(
           base::FilePath::FromUTF8Unsafe(basename.AsUTF8Unsafe())));
 
   // If the source url and replace url are the same, the copy/move operation
@@ -534,9 +537,9 @@
   progress_.pause_params.conflict_is_directory =
       progress_.sources[idx].is_directory;
   auto destination_folder = file_system_context_->CreateCrackedFileSystemURL(
-      progress_.destination_folder.storage_key(),
-      progress_.destination_folder.mount_type(),
-      progress_.destination_folder.virtual_path());
+      progress_.GetDestinationFolder().storage_key(),
+      progress_.GetDestinationFolder().mount_type(),
+      progress_.GetDestinationFolder().virtual_path());
   progress_.pause_params.conflict_target_url =
       destination_folder.ToGURL().spec();
   progress_callback_.Run(progress_);
diff --git a/chrome/browser/ash/file_manager/copy_or_move_io_task_scanning_impl.cc b/chrome/browser/ash/file_manager/copy_or_move_io_task_scanning_impl.cc
index 7c7a553..a0313b5 100644
--- a/chrome/browser/ash/file_manager/copy_or_move_io_task_scanning_impl.cc
+++ b/chrome/browser/ash/file_manager/copy_or_move_io_task_scanning_impl.cc
@@ -195,8 +195,9 @@
   file_transfer_analysis_delegates_[idx] =
       enterprise_connectors::FileTransferAnalysisDelegate::Create(
           safe_browsing::DeepScanAccessPoint::FILE_TRANSFER,
-          progress_.sources[idx].url, progress_.destination_folder, profile_,
-          file_system_context_.get(), std::move(settings_[idx].value()));
+          progress_.sources[idx].url, progress_.GetDestinationFolder(),
+          profile_, file_system_context_.get(),
+          std::move(settings_[idx].value()));
 
   file_transfer_analysis_delegates_[idx]->UploadData(
       base::BindOnce(&CopyOrMoveIOTaskScanningImpl::MaybeScanForDisallowedFiles,
diff --git a/chrome/browser/ash/file_manager/copy_or_move_io_task_unittest.cc b/chrome/browser/ash/file_manager/copy_or_move_io_task_unittest.cc
index a7b2636..c9d1423 100644
--- a/chrome/browser/ash/file_manager/copy_or_move_io_task_unittest.cc
+++ b/chrome/browser/ash/file_manager/copy_or_move_io_task_unittest.cc
@@ -52,6 +52,7 @@
 using ::testing::ElementsAreArray;
 using ::testing::Field;
 using ::testing::IsEmpty;
+using ::testing::Property;
 using ::testing::Return;
 
 namespace file_manager {
@@ -120,7 +121,7 @@
     progress_.sources.emplace_back(CreateFileSystemURL("foo.txt"),
                                    absl::nullopt);
     base::CreateDirectory(temp_dir_.GetPath().Append("dest_folder"));
-    progress_.destination_folder = CreateFileSystemURL("dest_folder/");
+    progress_.SetDestinationFolder(CreateFileSystemURL("dest_folder/"));
     CopyOrMoveIOTaskImpl task(GetParam(), progress_, {},
                               CreateFileSystemURL(""), &profile_,
                               file_system_context_);
@@ -136,7 +137,7 @@
     progress_.sources.emplace_back(CreateFileSystemURL("foo.txt"),
                                    absl::nullopt);
     base::CreateDirectory(temp_dir_.GetPath().Append("dest_folder"));
-    progress_.destination_folder = CreateFileSystemURL("dest_folder/");
+    progress_.SetDestinationFolder(CreateFileSystemURL("dest_folder/"));
     CopyOrMoveIOTaskImpl task(GetParam(), progress_, {},
                               CreateFileSystemURL(""), &profile_,
                               file_system_context_);
@@ -178,7 +179,7 @@
   auto base_matcher =
       AllOf(Field(&ProgressStatus::type, GetParam()),
             Field(&ProgressStatus::sources, EntryStatusUrls(source_urls)),
-            Field(&ProgressStatus::destination_folder, dest),
+            Property(&ProgressStatus::GetDestinationFolder, dest),
             Field(&ProgressStatus::total_bytes, 2 * kTestFileSize));
   base::MockRepeatingCallback<void(const ProgressStatus&)> progress_callback;
   base::MockOnceCallback<void(ProgressStatus)> complete_callback;
@@ -259,7 +260,7 @@
   auto base_matcher =
       AllOf(Field(&ProgressStatus::type, GetParam()),
             Field(&ProgressStatus::sources, EntryStatusUrls(source_urls)),
-            Field(&ProgressStatus::destination_folder, dest),
+            Property(&ProgressStatus::GetDestinationFolder, dest),
             Field(&ProgressStatus::total_bytes, 2 * kTestFileSize));
   base::MockOnceCallback<void(ProgressStatus)> complete_callback;
   EXPECT_CALL(
@@ -337,7 +338,7 @@
   EXPECT_CALL(
       complete_callback,
       Run(AllOf(Field(&ProgressStatus::type, GetParam()),
-                Field(&ProgressStatus::destination_folder, dest),
+                Property(&ProgressStatus::GetDestinationFolder, dest),
                 Field(&ProgressStatus::state, State::kError),
                 Field(&ProgressStatus::bytes_transferred, 0),
                 Field(&ProgressStatus::sources, EntryStatusUrls(source_urls)),
@@ -380,7 +381,7 @@
   EXPECT_CALL(
       complete_callback,
       Run(AllOf(Field(&ProgressStatus::type, GetParam()),
-                Field(&ProgressStatus::destination_folder, dest),
+                Property(&ProgressStatus::GetDestinationFolder, dest),
                 Field(&ProgressStatus::state, State::kError),
                 Field(&ProgressStatus::bytes_transferred, 2 * kTestFileSize),
                 Field(&ProgressStatus::total_bytes, 2 * kTestFileSize),
@@ -435,7 +436,7 @@
   EXPECT_CALL(
       complete_callback,
       Run(AllOf(Field(&ProgressStatus::type, GetParam()),
-                Field(&ProgressStatus::destination_folder, dest),
+                Property(&ProgressStatus::GetDestinationFolder, dest),
                 Field(&ProgressStatus::state, State::kSuccess),
                 Field(&ProgressStatus::bytes_transferred, 2 * kTestFileSize),
                 Field(&ProgressStatus::total_bytes, 2 * kTestFileSize),
@@ -770,7 +771,7 @@
         Field(&ProgressStatus::type, GetOperationType()),
         Field(&ProgressStatus::sources,
               EntryStatusUrls(GetSourceUrlsFromFileInfos(file_infos))),
-        Field(&ProgressStatus::destination_folder, dest),
+        Property(&ProgressStatus::GetDestinationFolder, dest),
         Field(&ProgressStatus::total_bytes, total_num_files * kTestFileSize));
   }
 
@@ -808,7 +809,7 @@
             Field(&ProgressStatus::type, GetOperationType()),
             Field(&ProgressStatus::sources,
                   EntryStatusUrls(GetSourceUrlsFromFileInfos(file_infos))),
-            Field(&ProgressStatus::destination_folder, dest),
+            Property(&ProgressStatus::GetDestinationFolder, dest),
             Field(&ProgressStatus::total_bytes, 0))))
         .Times(num_calls);
   }
diff --git a/chrome/browser/ash/file_manager/extract_io_task.cc b/chrome/browser/ash/file_manager/extract_io_task.cc
index 89c0ab9..3511d54f 100644
--- a/chrome/browser/ash/file_manager/extract_io_task.cc
+++ b/chrome/browser/ash/file_manager/extract_io_task.cc
@@ -50,7 +50,7 @@
       file_system_context_(std::move(file_system_context)) {
   progress_.type = OperationType::kExtract;
   progress_.state = State::kQueued;
-  progress_.destination_folder = parent_folder_;
+  progress_.SetDestinationFolder(parent_folder_, profile);
   progress_.bytes_transferred = 0;
   progress_.total_bytes = 0;
   // Store all the ZIP files in the selection so we have
@@ -227,16 +227,16 @@
 void ExtractIOTask::GotFreeDiskSpace(int64_t free_space) {
   auto* drive_integration_service =
       drive::util::GetIntegrationServiceByProfile(profile_);
-  if (progress_.destination_folder.filesystem_id() ==
+  if (progress_.GetDestinationFolder().filesystem_id() ==
           util::GetDownloadsMountPointName(profile_) ||
       (drive_integration_service &&
        drive_integration_service->GetMountPointPath().IsParent(
-           progress_.destination_folder.path()))) {
+           progress_.GetDestinationFolder().path()))) {
     free_space -= cryptohome::kMinFreeSpaceInBytes;
   }
 
   if (progress_.total_bytes > free_space) {
-    progress_.outputs.emplace_back(progress_.destination_folder,
+    progress_.outputs.emplace_back(progress_.GetDestinationFolder(),
                                    base::File::FILE_ERROR_NO_SPACE);
     progress_.state = State::kError;
     RecordUmaExtractStatus(ExtractStatus::kInsufficientDiskSpace);
diff --git a/chrome/browser/ash/file_manager/file_manager_jstest.cc b/chrome/browser/ash/file_manager/file_manager_jstest.cc
index 346f7a2..1e1ad0b 100644
--- a/chrome/browser/ash/file_manager/file_manager_jstest.cc
+++ b/chrome/browser/ash/file_manager/file_manager_jstest.cc
@@ -401,3 +401,7 @@
 IN_PROC_BROWSER_TEST_F(FileManagerJsTest, DirectoryTreeContainer) {
   RunTestURL("containers/directory_tree_container_unittest.js");
 }
+
+IN_PROC_BROWSER_TEST_F(FileManagerJsTest, EntryUtils) {
+  RunTestURL("common/js/entry_utils_unittest.js");
+}
diff --git a/chrome/browser/ash/file_manager/io_task.cc b/chrome/browser/ash/file_manager/io_task.cc
index da888e43..97f637d 100644
--- a/chrome/browser/ash/file_manager/io_task.cc
+++ b/chrome/browser/ash/file_manager/io_task.cc
@@ -10,6 +10,8 @@
 #include "base/functional/callback.h"
 #include "base/task/sequenced_task_runner.h"
 #include "chrome/browser/ash/file_manager/path_util.h"
+#include "chrome/browser/ash/file_manager/volume_manager.h"
+#include "chrome/browser/profiles/profile.h"
 #include "storage/browser/file_system/file_system_url.h"
 
 namespace file_manager::io_task {
@@ -54,6 +56,22 @@
       .value();
 }
 
+void ProgressStatus::SetDestinationFolder(storage::FileSystemURL folder,
+                                          Profile* profile) {
+  destination_folder_ = std::move(folder);
+  if (!profile) {
+    return;
+  }
+  if (VolumeManager* const volume_manager = VolumeManager::Get(profile);
+      volume_manager) {
+    base::WeakPtr<Volume> volume =
+        volume_manager->FindVolumeFromPath(destination_folder_.path());
+    if (volume) {
+      destination_volume_id_ = volume->volume_id();
+    }
+  }
+}
+
 DummyIOTask::DummyIOTask(std::vector<storage::FileSystemURL> source_urls,
                          storage::FileSystemURL destination_folder,
                          OperationType type,
@@ -61,7 +79,7 @@
     : IOTask(show_notifications) {
   progress_.state = State::kQueued;
   progress_.type = type;
-  progress_.destination_folder = std::move(destination_folder);
+  progress_.SetDestinationFolder(std::move(destination_folder));
   progress_.bytes_transferred = 0;
   progress_.total_bytes = 2;
 
diff --git a/chrome/browser/ash/file_manager/io_task.h b/chrome/browser/ash/file_manager/io_task.h
index 7ab0c26..b514117 100644
--- a/chrome/browser/ash/file_manager/io_task.h
+++ b/chrome/browser/ash/file_manager/io_task.h
@@ -112,7 +112,8 @@
 };
 
 // Represents the current progress of an I/O task.
-struct ProgressStatus {
+class ProgressStatus {
+ public:
   // Out-of-line constructors to appease the style linter.
   ProgressStatus();
   ProgressStatus(const ProgressStatus& other) = delete;
@@ -132,6 +133,14 @@
   // Returns a default method for obtaining the source name.
   std::string GetSourceName(Profile* profile) const;
 
+  // Setter for the destination folder and the destination volume id.
+  void SetDestinationFolder(const storage::FileSystemURL folder,
+                            Profile* profile = nullptr);
+  storage::FileSystemURL GetDestinationFolder() const {
+    return destination_folder_;
+  }
+  std::string GetDestinationVolumeId() const { return destination_volume_id_; }
+
   // Task state.
   State state;
 
@@ -148,10 +157,6 @@
   // |sources|.
   std::vector<EntryStatus> outputs;
 
-  // Optional destination folder for operations that transfer files to a
-  // directory (e.g. copy or move).
-  storage::FileSystemURL destination_folder;
-
   // I/O task state::PAUSED parameters.
   PauseParams pause_params;
 
@@ -169,6 +174,15 @@
 
   // Whether notifications should be shown on progress status.
   bool show_notification = true;
+
+ private:
+  // Optional destination folder for operations that transfer files to a
+  // directory (e.g. copy or move).
+  storage::FileSystemURL destination_folder_;
+
+  // Volume id of the destination directory for operations that transfer files
+  // to a directory (e.g. copy or move).
+  std::string destination_volume_id_;
 };
 
 // An IOTask represents an I/O operation over multiple files, and is responsible
diff --git a/chrome/browser/ash/file_manager/io_task_controller_unittest.cc b/chrome/browser/ash/file_manager/io_task_controller_unittest.cc
index 5c680ce4..9584e1f 100644
--- a/chrome/browser/ash/file_manager/io_task_controller_unittest.cc
+++ b/chrome/browser/ash/file_manager/io_task_controller_unittest.cc
@@ -19,6 +19,7 @@
 using testing::AllOf;
 using testing::Field;
 using testing::Invoke;
+using testing::Property;
 
 namespace file_manager {
 namespace io_task {
@@ -63,7 +64,7 @@
   auto base_matcher =
       AllOf(Field(&ProgressStatus::type, OperationType::kCopy),
             Field(&ProgressStatus::sources, EntryStatusUrls(source_urls)),
-            Field(&ProgressStatus::destination_folder, dest));
+            Property(&ProgressStatus::GetDestinationFolder, dest));
 
   // The controller should synchronously send out a progress status when queued.
   EXPECT_CALL(observer, OnIOTaskStatus(
@@ -115,7 +116,7 @@
   auto base_matcher =
       AllOf(Field(&ProgressStatus::type, OperationType::kMove),
             Field(&ProgressStatus::sources, EntryStatusUrls(source_urls)),
-            Field(&ProgressStatus::destination_folder, dest));
+            Property(&ProgressStatus::GetDestinationFolder, dest));
 
   // The controller should synchronously send out a progress status when queued.
   EXPECT_CALL(observer, OnIOTaskStatus(
diff --git a/chrome/browser/ash/file_manager/restore_to_destination_io_task.cc b/chrome/browser/ash/file_manager/restore_to_destination_io_task.cc
index 2381252a..3168a20 100644
--- a/chrome/browser/ash/file_manager/restore_to_destination_io_task.cc
+++ b/chrome/browser/ash/file_manager/restore_to_destination_io_task.cc
@@ -45,7 +45,7 @@
       base_path_(base_path) {
   progress_.state = State::kQueued;
   progress_.type = OperationType::kRestoreToDestination;
-  progress_.destination_folder = std::move(destination_folder);
+  progress_.SetDestinationFolder(std::move(destination_folder), profile);
   progress_.bytes_transferred = 0;
   progress_.total_bytes = 0;
 
@@ -135,9 +135,8 @@
     // parent task is tied to the life of the child task.
     move_io_task_ = std::make_unique<CopyOrMoveIOTask>(
         OperationType::kMove, std::move(source_urls_),
-        std::move(destination_file_names_),
-        std::move(progress_.destination_folder), profile_,
-        file_system_context_);
+        std::move(destination_file_names_), progress_.GetDestinationFolder(),
+        profile_, file_system_context_);
 
     // The existing callbacks need to be intercepted to ensure the IOTask
     // progress that is propagated is sent from the `RestoreToDestinationIOTask`
diff --git a/chrome/browser/ash/file_manager/zip_io_task.cc b/chrome/browser/ash/file_manager/zip_io_task.cc
index 1012df4..7732186 100644
--- a/chrome/browser/ash/file_manager/zip_io_task.cc
+++ b/chrome/browser/ash/file_manager/zip_io_task.cc
@@ -49,10 +49,11 @@
   for (const base::FilePath& relative_path : src_files) {
     const base::FilePath absolute_path = src_dir.Append(relative_path);
 
-    if (base::GetFileInfo(absolute_path, &info))
+    if (base::GetFileInfo(absolute_path, &info)) {
       total_bytes += info.is_directory
                          ? base::ComputeDirectorySize(absolute_path)
                          : info.size;
+    }
   }
   VLOG(1) << "<<< Total size is " << total_bytes << " bytes";
   return total_bytes;
@@ -71,7 +72,7 @@
       file_system_context_(file_system_context) {
   progress_.state = State::kQueued;
   progress_.type = OperationType::kZip;
-  progress_.destination_folder = std::move(parent_folder);
+  progress_.SetDestinationFolder(std::move(parent_folder), profile);
   progress_.bytes_transferred = 0;
   progress_.total_bytes = 0;
 
@@ -100,10 +101,10 @@
   progress_.state = State::kInProgress;
 
   // Convert the destination folder URL to absolute path.
-  source_dir_ = progress_.destination_folder.path();
-  if (!ash::FileSystemBackend::CanHandleURL(progress_.destination_folder) ||
+  source_dir_ = progress_.GetDestinationFolder().path();
+  if (!ash::FileSystemBackend::CanHandleURL(progress_.GetDestinationFolder()) ||
       source_dir_.empty()) {
-    progress_.outputs.emplace_back(progress_.destination_folder,
+    progress_.outputs.emplace_back(progress_.GetDestinationFolder(),
                                    base::File::FILE_ERROR_NOT_FOUND);
     Complete(State::kError);
     return;
@@ -188,7 +189,7 @@
     zip_name = source_relative_paths_[0].BaseName().ReplaceExtension("zip");
   }
   util::GenerateUnusedFilename(
-      progress_.destination_folder, zip_name, file_system_context_,
+      progress_.GetDestinationFolder(), zip_name, file_system_context_,
       base::BindOnce(&ZipIOTask::ZipItems, weak_ptr_factory_.GetWeakPtr()));
 }
 
@@ -196,7 +197,7 @@
 void ZipIOTask::ZipItems(
     base::FileErrorOr<storage::FileSystemURL> destination_result) {
   if (!destination_result.has_value()) {
-    progress_.outputs.emplace_back(progress_.destination_folder,
+    progress_.outputs.emplace_back(progress_.GetDestinationFolder(),
                                    destination_result.error());
     Complete(State::kError);
     return;
diff --git a/chrome/browser/ash/guest_os/public/guest_os_wayland_server.cc b/chrome/browser/ash/guest_os/public/guest_os_wayland_server.cc
index 1bf987b..240140b 100644
--- a/chrome/browser/ash/guest_os/public/guest_os_wayland_server.cc
+++ b/chrome/browser/ash/guest_os/public/guest_os_wayland_server.cc
@@ -114,8 +114,9 @@
 
 GuestOsWaylandServer::ServerDetails::~ServerDetails() {
   // In tests, this is used to avoid dealing with the real server controller.
-  if (server_path_.empty())
+  if (server_path_.empty()) {
     return;
+  }
   GuestOsSecurityDelegate::MaybeRemoveServer(security_delegate_, server_path_);
 }
 
@@ -136,6 +137,21 @@
       base::BindOnce(&OnWaylandServerStarted, std::move(response_callback)));
 }
 
+// static
+void GuestOsWaylandServer::ListenOnSocket(
+    const vm_tools::wl::ListenOnSocketRequest& request,
+    base::ScopedFD socket_fd,
+    base::OnceCallback<void(absl::optional<std::string>)> response_callback) {
+  std::move(response_callback).Run({"ListenOnSocket not implemented."});
+}
+
+// static
+void GuestOsWaylandServer::CloseSocket(
+    const vm_tools::wl::CloseSocketRequest& request,
+    base::OnceCallback<void(absl::optional<std::string>)> response_callback) {
+  std::move(response_callback).Run({"CloseSocket not implemented."});
+}
+
 GuestOsWaylandServer::GuestOsWaylandServer(Profile* profile)
     : profile_(profile) {
   delegate_holders_[vm_tools::launch::BOREALIS] =
diff --git a/chrome/browser/ash/guest_os/public/guest_os_wayland_server.h b/chrome/browser/ash/guest_os/public/guest_os_wayland_server.h
index c184e71..43580fb 100644
--- a/chrome/browser/ash/guest_os/public/guest_os_wayland_server.h
+++ b/chrome/browser/ash/guest_os/public/guest_os_wayland_server.h
@@ -9,10 +9,13 @@
 
 #include "base/containers/flat_map.h"
 #include "base/files/file_path.h"
+#include "base/files/scoped_file.h"
 #include "base/functional/callback_forward.h"
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/ash/borealis/infra/expected.h"
 #include "chromeos/ash/components/dbus/vm_launch/launch.pb.h"
+#include "chromeos/ash/components/dbus/vm_wl/wl.pb.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 class Profile;
 
@@ -70,12 +73,29 @@
   // Creates a wayland server as per the |request|, and responds with the
   // relevant details in the |response_callback|. This API is used by e.g.
   // dbus.
+  //
+  // TODO(b/270254359): deprecate this method.
   static void StartServer(
       const vm_tools::launch::StartWaylandServerRequest& request,
       base::OnceCallback<
           void(borealis::Expected<vm_tools::launch::StartWaylandServerResponse,
                                   std::string>)> response_callback);
 
+  // Use the given |socket_fd| as a wayland socket for the VM given by
+  // |request|. Invokes the |response_callback| with nullopt on success, or a
+  // string description of an error on failure.
+  static void ListenOnSocket(
+      const vm_tools::wl::ListenOnSocketRequest& request,
+      base::ScopedFD socket_fd,
+      base::OnceCallback<void(absl::optional<std::string>)> response_callback);
+
+  // Advise that the wayland server for the VM given in |request| is no-longer
+  // needed. Invokes the |response_callback| with nullopt on success, or a
+  // string description of an error on failure.
+  static void CloseSocket(
+      const vm_tools::wl::CloseSocketRequest& request,
+      base::OnceCallback<void(absl::optional<std::string>)> response_callback);
+
   explicit GuestOsWaylandServer(Profile* profile);
 
   ~GuestOsWaylandServer();
diff --git a/chrome/browser/ash/policy/reporting/metrics_reporting/apps/app_usage_telemetry_sampler.cc b/chrome/browser/ash/policy/reporting/metrics_reporting/apps/app_usage_telemetry_sampler.cc
new file mode 100644
index 0000000..e7cbdea
--- /dev/null
+++ b/chrome/browser/ash/policy/reporting/metrics_reporting/apps/app_usage_telemetry_sampler.cc
@@ -0,0 +1,119 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ash/policy/reporting/metrics_reporting/apps/app_usage_telemetry_sampler.h"
+
+#include <memory>
+
+#include "base/time/time.h"
+#include "chrome/browser/apps/app_service/metrics/app_platform_metrics.h"
+#include "chrome/browser/apps/app_service/metrics/app_platform_metrics_utils.h"
+#include "chrome/browser/ash/profiles/profile_helper.h"
+#include "chrome/browser/profiles/profile.h"
+#include "components/prefs/pref_service.h"
+#include "components/prefs/scoped_user_pref_update.h"
+#include "components/reporting/metrics/sampler.h"
+#include "components/reporting/proto/synced/metric_data.pb.h"
+#include "components/services/app_service/public/cpp/app_types.h"
+#include "components/user_manager/user.h"
+#include "components/user_manager/user_manager.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace reporting {
+namespace {
+
+// Returns the primary user profile. We use this every time we need to access
+// the profile so we can prevent dangling pointer references.
+Profile* GetPrimaryUserProfile() {
+  const ::user_manager::User* const primary_user =
+      ::user_manager::UserManager::Get()->GetPrimaryUser();
+  DCHECK(primary_user);
+  DCHECK(primary_user->is_profile_created());
+  auto* const profile =
+      ::ash::ProfileHelper::Get()->GetProfileByUser(primary_user);
+  DCHECK(profile);
+  return profile;
+}
+
+}  // namespace
+
+AppUsageTelemetrySampler::AppUsageTelemetrySampler() = default;
+
+AppUsageTelemetrySampler::~AppUsageTelemetrySampler() = default;
+
+void AppUsageTelemetrySampler::MaybeCollect(OptionalMetricCallback callback) {
+  if (!::content::BrowserThread::CurrentlyOn(::content::BrowserThread::UI)) {
+    ::content::GetUIThreadTaskRunner({})->PostTask(
+        FROM_HERE,
+        base::BindOnce(&AppUsageTelemetrySampler::MaybeCollect,
+                       weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+    return;
+  }
+  auto* const profile = GetPrimaryUserProfile();
+  MetricData metric_data;
+  auto* const app_usage_data = metric_data.mutable_telemetry_data()
+                                   ->mutable_app_telemetry()
+                                   ->mutable_app_usage_data();
+  const PrefService* const user_prefs = profile->GetPrefs();
+  if (!user_prefs->HasPrefPath(::apps::kAppUsageTime)) {
+    // No usage data in the pref store.
+    std::move(callback).Run(absl::nullopt);
+    return;
+  }
+
+  // Parse app instance usage from the pref store and populate `app_usage_data`.
+  for (auto usage_it : user_prefs->GetDict(::apps::kAppUsageTime)) {
+    ::apps::AppPlatformMetrics::UsageTime usage_time(usage_it.second);
+    if (usage_time.reporting_usage_time.is_zero()) {
+      // No reporting usage tracked by the `AppUsageCollector` since it was last
+      // enabled, so we skip. The `AppPlatformMetrics` component will
+      // subsequently delete this entry once it reports its UKM snapshot.
+      continue;
+    }
+
+    ::apps::AppType app_type = ::apps::GetAppType(profile, usage_time.app_id);
+    AppUsageData::AppUsage* const app_usage =
+        app_usage_data->mutable_app_usage()->Add();
+    app_usage->set_app_instance_id(usage_it.first);
+    app_usage->set_app_id(usage_time.app_id);
+    app_usage->set_app_type(
+        ::apps::ConvertAppTypeToProtoApplicationType(app_type));
+    app_usage->set_running_time_ms(
+        usage_time.reporting_usage_time.InMilliseconds());
+  }
+
+  if (app_usage_data->app_usage().empty()) {
+    // No app instance usage to report.
+    std::move(callback).Run(absl::nullopt);
+    return;
+  }
+
+  std::move(callback).Run(metric_data);
+  ResetAppUsageDataInPrefStore(app_usage_data);
+}
+
+void AppUsageTelemetrySampler::ResetAppUsageDataInPrefStore(
+    const AppUsageData* app_usage_data) {
+  DCHECK_CURRENTLY_ON(::content::BrowserThread::UI);
+  auto* const profile = GetPrimaryUserProfile();
+  ScopedDictPrefUpdate usage_dict_pref(profile->GetPrefs(),
+                                       ::apps::kAppUsageTime);
+  for (const auto& usage_info : app_usage_data->app_usage()) {
+    const std::string& instance_id = usage_info.app_instance_id();
+    DCHECK(usage_dict_pref->contains(instance_id))
+        << "Missing app usage data for instance: " << instance_id;
+
+    // Reduce usage time tracked in the pref store based on the data that was
+    // reported.
+    const auto running_time = base::Milliseconds(usage_info.running_time_ms());
+    ::apps::AppPlatformMetrics::UsageTime usage_time(
+        *usage_dict_pref->FindByDottedPath(instance_id));
+    usage_time.reporting_usage_time -= running_time;
+    usage_dict_pref->SetByDottedPath(instance_id, usage_time.ConvertToDict());
+  }
+}
+
+}  // namespace reporting
diff --git a/chrome/browser/ash/policy/reporting/metrics_reporting/apps/app_usage_telemetry_sampler.h b/chrome/browser/ash/policy/reporting/metrics_reporting/apps/app_usage_telemetry_sampler.h
new file mode 100644
index 0000000..001f9d8
--- /dev/null
+++ b/chrome/browser/ash/policy/reporting/metrics_reporting/apps/app_usage_telemetry_sampler.h
@@ -0,0 +1,43 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_ASH_POLICY_REPORTING_METRICS_REPORTING_APPS_APP_USAGE_TELEMETRY_SAMPLER_H_
+#define CHROME_BROWSER_ASH_POLICY_REPORTING_METRICS_REPORTING_APPS_APP_USAGE_TELEMETRY_SAMPLER_H_
+
+#include "base/memory/raw_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/profiles/profile.h"
+#include "components/reporting/metrics/sampler.h"
+#include "components/reporting/proto/synced/metric_data.pb.h"
+
+namespace reporting {
+
+// Sampler used to collect app usage telemetry from the primary user profile
+// pref store. This usage data is originally persisted in the pref store by the
+// `AppUsageCollector` as it observes and tracks data collection from the
+// `AppPlatformMetrics` component.
+class AppUsageTelemetrySampler : public Sampler {
+ public:
+  AppUsageTelemetrySampler();
+  AppUsageTelemetrySampler(const AppUsageTelemetrySampler& other) = delete;
+  AppUsageTelemetrySampler& operator=(const AppUsageTelemetrySampler& other) =
+      delete;
+  ~AppUsageTelemetrySampler() override;
+
+  // Collects apps usage telemetry data from the user pref store across several
+  // instances and triggers the specified callback with batched usage data.
+  // Sampler:
+  void MaybeCollect(OptionalMetricCallback callback) override;
+
+ private:
+  // Resets app usage entries in the pref store by discounting reported usage
+  // time. Triggered only after the data has been consumed to avoid data loss.
+  void ResetAppUsageDataInPrefStore(const AppUsageData* app_usage_data);
+
+  base::WeakPtrFactory<AppUsageTelemetrySampler> weak_ptr_factory_{this};
+};
+
+}  // namespace reporting
+
+#endif  // CHROME_BROWSER_ASH_POLICY_REPORTING_METRICS_REPORTING_APPS_APP_USAGE_TELEMETRY_SAMPLER_H_
diff --git a/chrome/browser/ash/policy/reporting/metrics_reporting/apps/app_usage_telemetry_sampler_unittest.cc b/chrome/browser/ash/policy/reporting/metrics_reporting/apps/app_usage_telemetry_sampler_unittest.cc
new file mode 100644
index 0000000..d716f79
--- /dev/null
+++ b/chrome/browser/ash/policy/reporting/metrics_reporting/apps/app_usage_telemetry_sampler_unittest.cc
@@ -0,0 +1,288 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ash/policy/reporting/metrics_reporting/apps/app_usage_telemetry_sampler.h"
+
+#include <memory>
+#include <string>
+#include <tuple>
+
+#include "base/json/values_util.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/raw_ptr.h"
+#include "base/time/time.h"
+#include "base/unguessable_token.h"
+#include "base/values.h"
+#include "chrome/browser/apps/app_service/metrics/app_platform_metrics.h"
+#include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
+#include "chrome/browser/ash/profiles/profile_helper.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/test/base/testing_profile.h"
+#include "components/prefs/pref_service.h"
+#include "components/prefs/scoped_user_pref_update.h"
+#include "components/reporting/proto/synced/metric_data.pb.h"
+#include "components/reporting/util/test_support_callbacks.h"
+#include "components/services/app_service/public/cpp/app_types.h"
+#include "components/services/app_service/public/protos/app_types.pb.h"
+#include "components/user_manager/scoped_user_manager.h"
+#include "components/user_manager/user_manager.h"
+#include "content/public/test/browser_task_environment.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+using ::testing::_;
+using ::testing::Eq;
+using ::testing::NotNull;
+using ::testing::Pointwise;
+using ::testing::StrEq;
+using ::testing::UnorderedPointwise;
+
+namespace reporting {
+namespace {
+
+constexpr char kTestUserEmail[] = "test@test.com";
+constexpr char kTestAppId[] = "TestApp";
+
+// Checks equality of the two protos in an std::tuple. Useful for matching two
+// two protos using ::testing::Pointwise or ::testing::UnorderedPointwise.
+MATCHER(EqualsProto, "") {
+  std::string serialized1, serialized2;
+  std::get<0>(arg).SerializeToString(&serialized1);
+  std::get<1>(arg).SerializeToString(&serialized2);
+  return serialized1 == serialized2;
+}
+
+class AppUsageTelemetrySamplerTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    // Set up user manager and test profile.
+    fake_user_manager_ = new ::ash::FakeChromeUserManager();
+    scoped_user_manager_ = std::make_unique<::user_manager::ScopedUserManager>(
+        base::WrapUnique(fake_user_manager_.get()));
+    AccountId account_id = AccountId::FromUserEmail(kTestUserEmail);
+    const ::user_manager::User* const user =
+        fake_user_manager_->AddUser(account_id);
+    fake_user_manager_->UserLoggedIn(account_id, user->username_hash(),
+                                     /*browser_restart=*/false,
+                                     /*is_child=*/false);
+    fake_user_manager_->SimulateUserProfileLoad(account_id);
+    profile_ = std::make_unique<TestingProfile>();
+    ::ash::ProfileHelper::Get()->SetUserToProfileMappingForTesting(
+        user, profile_.get());
+  }
+
+  // Simulates app usage for the specified app usage duration by aggregating
+  // relevant usage info in the pref store.
+  void CreateOrUpdateAppUsageForInstance(
+      const base::UnguessableToken& instance_id,
+      const base::TimeDelta& usage_duration) {
+    PrefService* const user_prefs = profile_->GetPrefs();
+    if (!user_prefs->HasPrefPath(::apps::kAppUsageTime)) {
+      // Create empty dictionary if none exists in the pref store.
+      user_prefs->SetDict(::apps::kAppUsageTime, base::Value::Dict());
+    }
+
+    ScopedDictPrefUpdate usage_dict_pref(profile_->GetPrefs(),
+                                         ::apps::kAppUsageTime);
+    const auto& instance_id_string = instance_id.ToString();
+    if (!usage_dict_pref->contains(instance_id_string)) {
+      // Create a new entry in the pref store with the specified running time.
+      ::apps::AppPlatformMetrics::UsageTime usage_time;
+      usage_time.app_id = kTestAppId;
+      usage_time.reporting_usage_time = usage_duration;
+      usage_dict_pref->SetByDottedPath(instance_id_string,
+                                       usage_time.ConvertToDict());
+      return;
+    }
+
+    // Aggregate and update just the running time otherwise.
+    ::apps::AppPlatformMetrics::UsageTime usage_time(
+        *usage_dict_pref->FindByDottedPath(instance_id_string));
+    usage_time.reporting_usage_time += usage_duration;
+    usage_dict_pref->SetByDottedPath(instance_id_string,
+                                     usage_time.ConvertToDict());
+  }
+
+  void VerifyAppUsageDataInPrefStoreForInstance(
+      const base::UnguessableToken& instance_id,
+      const base::TimeDelta& expected_usage_time) {
+    const auto& usage_dict_pref =
+        profile_->GetPrefs()->GetDict(::apps::kAppUsageTime);
+    const auto& instance_id_string = instance_id.ToString();
+    ASSERT_THAT(usage_dict_pref.Find(instance_id_string), NotNull());
+    EXPECT_THAT(*usage_dict_pref.Find(instance_id_string)
+                     ->FindStringKey(::apps::kUsageTimeAppIdKey),
+                StrEq(kTestAppId));
+    EXPECT_THAT(base::ValueToTimeDelta(
+                    usage_dict_pref.FindDict(instance_id_string)
+                        ->Find(::apps::kReportingUsageTimeDurationKey)),
+                Eq(expected_usage_time));
+  }
+
+  // Returns an `AppUsageData::AppUsage` proto message that tests can use to
+  // test match with the actual one.
+  const AppUsageData::AppUsage AppUsageProto(
+      const base::UnguessableToken& instance_id,
+      const base::TimeDelta& running_time) const {
+    AppUsageData::AppUsage app_usage;
+    app_usage.set_app_id(kTestAppId);
+    app_usage.set_app_type(::apps::ApplicationType::APPLICATION_TYPE_UNKNOWN);
+    app_usage.set_app_instance_id(instance_id.ToString());
+    app_usage.set_running_time_ms(running_time.InMilliseconds());
+    return app_usage;
+  }
+
+  content::BrowserTaskEnvironment task_environment_;
+
+  std::unique_ptr<TestingProfile> profile_;
+  AppUsageTelemetrySampler app_usage_telemetry_sampler_;
+
+ private:
+  raw_ptr<::ash::FakeChromeUserManager> fake_user_manager_;
+  std::unique_ptr<::user_manager::ScopedUserManager> scoped_user_manager_;
+};
+
+TEST_F(AppUsageTelemetrySamplerTest, CollectAppUsageDataForInstance) {
+  // Simulate app usage so we have data in the pref store to work with.
+  static constexpr base::TimeDelta kAppUsageDuration = base::Minutes(2);
+  const base::UnguessableToken& kInstanceId = base::UnguessableToken::Create();
+  CreateOrUpdateAppUsageForInstance(kInstanceId, kAppUsageDuration);
+  ASSERT_THAT(profile_->GetPrefs()->GetDict(::apps::kAppUsageTime).size(),
+              Eq(1UL));
+
+  // Attempt to collect this data and verify reported data.
+  test::TestEvent<absl::optional<MetricData>> test_event;
+  app_usage_telemetry_sampler_.MaybeCollect(test_event.cb());
+  const absl::optional<MetricData> metric_data_result = test_event.result();
+  ASSERT_TRUE(metric_data_result.has_value());
+  const MetricData& metric_data = metric_data_result.value();
+  ASSERT_TRUE(metric_data.has_telemetry_data());
+  ASSERT_TRUE(metric_data.telemetry_data().has_app_telemetry());
+  ASSERT_TRUE(
+      metric_data.telemetry_data().app_telemetry().has_app_usage_data());
+  EXPECT_THAT(
+      metric_data.telemetry_data().app_telemetry().app_usage_data().app_usage(),
+      Pointwise(EqualsProto(),
+                {AppUsageProto(kInstanceId, kAppUsageDuration)}));
+
+  // Also verify usage data is reset in the pref store.
+  VerifyAppUsageDataInPrefStoreForInstance(kInstanceId, base::TimeDelta());
+}
+
+TEST_F(AppUsageTelemetrySamplerTest, NoAppUsageData) {
+  test::TestEvent<absl::optional<MetricData>> test_event;
+  app_usage_telemetry_sampler_.MaybeCollect(test_event.cb());
+  const absl::optional<MetricData> metric_data_result = test_event.result();
+  ASSERT_FALSE(metric_data_result.has_value());
+}
+
+TEST_F(AppUsageTelemetrySamplerTest, CollectResetAppUsageData) {
+  // Simulate app usage so we have data in the pref store to work with.
+  static constexpr base::TimeDelta kAppUsageDuration = base::Minutes(2);
+  const base::UnguessableToken& kInstanceId = base::UnguessableToken::Create();
+  CreateOrUpdateAppUsageForInstance(kInstanceId, kAppUsageDuration);
+  ASSERT_THAT(profile_->GetPrefs()->GetDict(::apps::kAppUsageTime).size(),
+              Eq(1UL));
+  VerifyAppUsageDataInPrefStoreForInstance(kInstanceId, kAppUsageDuration);
+
+  // Attempt to collect this data and verify data is reset after it is reported.
+  {
+    test::TestEvent<absl::optional<MetricData>> test_event;
+    app_usage_telemetry_sampler_.MaybeCollect(test_event.cb());
+    const absl::optional<MetricData> metric_data_result = test_event.result();
+    ASSERT_TRUE(metric_data_result.has_value());
+    VerifyAppUsageDataInPrefStoreForInstance(kInstanceId, base::TimeDelta());
+  }
+
+  // Attempt to collect data after it was reset in the previous step and verify
+  // nothing is reported.
+  {
+    test::TestEvent<absl::optional<MetricData>> test_event;
+    app_usage_telemetry_sampler_.MaybeCollect(test_event.cb());
+    const absl::optional<MetricData> metric_data_result = test_event.result();
+    ASSERT_FALSE(metric_data_result.has_value());
+  }
+}
+
+TEST_F(AppUsageTelemetrySamplerTest, CollectSubsequentAppUsageData) {
+  // Simulate app usage so we have data in the pref store to work with.
+  static constexpr base::TimeDelta kAppUsageDuration = base::Minutes(2);
+  const base::UnguessableToken& kInstanceId = base::UnguessableToken::Create();
+  CreateOrUpdateAppUsageForInstance(kInstanceId, kAppUsageDuration);
+  ASSERT_THAT(profile_->GetPrefs()->GetDict(::apps::kAppUsageTime).size(),
+              Eq(1UL));
+  VerifyAppUsageDataInPrefStoreForInstance(kInstanceId, kAppUsageDuration);
+
+  // Attempt to collect this data and verify data is reset after it is reported.
+  {
+    test::TestEvent<absl::optional<MetricData>> test_event;
+    app_usage_telemetry_sampler_.MaybeCollect(test_event.cb());
+    const absl::optional<MetricData> metric_data_result = test_event.result();
+    ASSERT_TRUE(metric_data_result.has_value());
+    VerifyAppUsageDataInPrefStoreForInstance(kInstanceId, base::TimeDelta());
+  }
+
+  // Simulate additional usage after the previous collection.
+  CreateOrUpdateAppUsageForInstance(kInstanceId, kAppUsageDuration);
+
+  // Attempt to collect data and verify only data tracked from previous
+  // collection is reported.
+  {
+    test::TestEvent<absl::optional<MetricData>> test_event;
+    app_usage_telemetry_sampler_.MaybeCollect(test_event.cb());
+    const absl::optional<MetricData> metric_data_result = test_event.result();
+    ASSERT_TRUE(metric_data_result.has_value());
+    const MetricData& metric_data = metric_data_result.value();
+    ASSERT_TRUE(metric_data.has_telemetry_data());
+    ASSERT_TRUE(metric_data.telemetry_data().has_app_telemetry());
+    ASSERT_TRUE(
+        metric_data.telemetry_data().app_telemetry().has_app_usage_data());
+    EXPECT_THAT(metric_data.telemetry_data()
+                    .app_telemetry()
+                    .app_usage_data()
+                    .app_usage(),
+                Pointwise(EqualsProto(),
+                          {AppUsageProto(kInstanceId, kAppUsageDuration)}));
+    VerifyAppUsageDataInPrefStoreForInstance(kInstanceId, base::TimeDelta());
+  }
+}
+
+TEST_F(AppUsageTelemetrySamplerTest,
+       CollectAppUsageDataAcrossMultipleInstances) {
+  // Simulate app usage across instances so we have data in the pref store to
+  // work with.
+  static constexpr base::TimeDelta kAppUsageDuration = base::Minutes(2);
+  const base::UnguessableToken& kInstanceId1 = base::UnguessableToken::Create();
+  const base::UnguessableToken& kInstanceId2 = base::UnguessableToken::Create();
+  CreateOrUpdateAppUsageForInstance(kInstanceId1, kAppUsageDuration);
+  CreateOrUpdateAppUsageForInstance(kInstanceId2, kAppUsageDuration);
+  ASSERT_THAT(profile_->GetPrefs()->GetDict(::apps::kAppUsageTime).size(),
+              Eq(2UL));
+  VerifyAppUsageDataInPrefStoreForInstance(kInstanceId1, kAppUsageDuration);
+  VerifyAppUsageDataInPrefStoreForInstance(kInstanceId2, kAppUsageDuration);
+
+  // Attempt to collect usage data and verify data being reported.
+  test::TestEvent<absl::optional<MetricData>> test_event;
+  app_usage_telemetry_sampler_.MaybeCollect(test_event.cb());
+  const absl::optional<MetricData> metric_data_result = test_event.result();
+  ASSERT_TRUE(metric_data_result.has_value());
+  const MetricData& metric_data = metric_data_result.value();
+  ASSERT_TRUE(metric_data.has_telemetry_data());
+  ASSERT_TRUE(metric_data.telemetry_data().has_app_telemetry());
+  ASSERT_TRUE(
+      metric_data.telemetry_data().app_telemetry().has_app_usage_data());
+  EXPECT_THAT(
+      metric_data.telemetry_data().app_telemetry().app_usage_data().app_usage(),
+      UnorderedPointwise(EqualsProto(),
+                         {AppUsageProto(kInstanceId1, kAppUsageDuration),
+                          AppUsageProto(kInstanceId2, kAppUsageDuration)}));
+
+  // Verify data is reset in the pref store now that it has been reported.
+  VerifyAppUsageDataInPrefStoreForInstance(kInstanceId1, base::TimeDelta());
+  VerifyAppUsageDataInPrefStoreForInstance(kInstanceId2, base::TimeDelta());
+}
+
+}  // namespace
+}  // namespace reporting
diff --git a/chrome/browser/ash/video_conference/BUILD.gn b/chrome/browser/ash/video_conference/BUILD.gn
index c7530cbe..67bd7df 100644
--- a/chrome/browser/ash/video_conference/BUILD.gn
+++ b/chrome/browser/ash/video_conference/BUILD.gn
@@ -20,17 +20,3 @@
     "//components/prefs",
   ]
 }
-
-source_set("unit_tests") {
-  testonly = true
-  sources = [ "video_conference_manager_ash_unittest.cc" ]
-  deps = [
-    ":video_conference",
-    "//ash",
-    "//base",
-    "//base/test:test_support",
-    "//chromeos/crosapi/mojom",
-    "//testing/gmock",
-    "//testing/gtest",
-  ]
-}
diff --git a/chrome/browser/ash/video_conference/video_conference_app_service_client.cc b/chrome/browser/ash/video_conference/video_conference_app_service_client.cc
index 089e3c2..fab9236 100644
--- a/chrome/browser/ash/video_conference/video_conference_app_service_client.cc
+++ b/chrome/browser/ash/video_conference/video_conference_app_service_client.cc
@@ -205,6 +205,11 @@
     const apps::InstanceUpdate& update) {
   const AppIdString& app_id = update.AppId();
 
+  // We only care about the apps being tracked already.
+  if (!base::Contains(id_to_app_state_, app_id)) {
+    return;
+  }
+
   // An instance of app_id is about to be destructed.
   if (update.IsDestruction() &&
       instance_registry_->GetInstances(app_id).size() <= 1) {
@@ -218,8 +223,7 @@
   }
 
   if (update.StateChanged() &&
-      update.State() == apps::InstanceState::kVisible &&
-      base::Contains(id_to_app_state_, app_id)) {
+      (update.State() & apps::InstanceState::kActive) != 0) {
     id_to_app_state_[app_id].last_activity_time = update.LastUpdatedTime();
     return;
   }
diff --git a/chrome/browser/ash/video_conference/video_conference_app_service_client_browsertest.cc b/chrome/browser/ash/video_conference/video_conference_app_service_client_browsertest.cc
index af82148..42f77f26 100644
--- a/chrome/browser/ash/video_conference/video_conference_app_service_client_browsertest.cc
+++ b/chrome/browser/ash/video_conference/video_conference_app_service_client_browsertest.cc
@@ -112,7 +112,7 @@
     // Ideally, the following should be automatically triggered by showing the
     // window_; but that is not the case for now.
     auto instance = instance_->Clone();
-    instance->UpdateState(apps::InstanceState::kVisible, base::Time::Now());
+    instance->UpdateState(apps::InstanceState::kActive, base::Time::Now());
     instance_registry_->OnInstance(std::move(instance));
   }
 
diff --git a/chrome/browser/ash/video_conference/video_conference_manager_ash.cc b/chrome/browser/ash/video_conference/video_conference_manager_ash.cc
index 69e99a99..410da4f 100644
--- a/chrome/browser/ash/video_conference/video_conference_manager_ash.cc
+++ b/chrome/browser/ash/video_conference/video_conference_manager_ash.cc
@@ -65,6 +65,14 @@
               }
             }
 
+            // Sort all apps based on last activity time.
+            std::sort(
+                apps.begin(), apps.end(),
+                [](const crosapi::mojom::VideoConferenceMediaAppInfoPtr& app1,
+                   const crosapi::mojom::VideoConferenceMediaAppInfoPtr& app2) {
+                  return app1->last_activity_time > app2->last_activity_time;
+                });
+
             // Call bound |ui_callback| with aggregated app info structs.
             std::move(callback).Run(std::move(apps));
           },
diff --git a/chrome/browser/ash/video_conference/video_conference_manager_ash_unittest.cc b/chrome/browser/ash/video_conference/video_conference_manager_ash_unittest.cc
index d671ff5..036c349 100644
--- a/chrome/browser/ash/video_conference/video_conference_manager_ash_unittest.cc
+++ b/chrome/browser/ash/video_conference/video_conference_manager_ash_unittest.cc
@@ -103,13 +103,16 @@
 // Tests |VideoConferenceManagerAsh::GetMediaApps| returns correct aggregated
 // results from all VcClients.
 TEST_F(VideoConferenceManagerAshTest, VcManagerGetMediaApps) {
+  const auto now = base::Time::Now();
+  const auto duration = base::Seconds(1);
+
   std::unique_ptr<FakeVcManagerCppClient> client1 =
       std::make_unique<FakeVcManagerCppClient>(vc_manager());
   client1->apps_.push_back(crosapi::mojom::VideoConferenceMediaAppInfo::New(
       /*id=*/base::UnguessableToken::Create(),
-      /*last_activity_time=*/base::Time::Now(),
+      /*last_activity_time=*/now,
       /*is_capturing_camera=*/false, /*is_capturing_microphone=*/false,
-      /*is_capturing_screen=*/true, /*title=*/u"Test App",
+      /*is_capturing_screen=*/true, /*title=*/u"Test App0",
       /*url=*/absl::nullopt));
 
   vc_manager().RegisterCppClient(client1.get(), client1->id_);
@@ -119,6 +122,7 @@
   vc_manager().GetMediaApps(base::BindLambdaForTesting(
       [&](VideoConferenceManagerAsh::MediaApps apps) {
         EXPECT_EQ(apps.size(), 1u);
+        EXPECT_EQ(apps[0]->title, u"Test App0");
 
         auto status = GetAggregatedCaptureStatus(std::move(apps));
 
@@ -131,15 +135,17 @@
 
   client1->apps_.push_back(crosapi::mojom::VideoConferenceMediaAppInfo::New(
       /*id=*/base::UnguessableToken::Create(),
-      /*last_activity_time=*/base::Time::Now(),
+      /*last_activity_time=*/now + duration * 10,
       /*is_capturing_camera=*/true, /*is_capturing_microphone=*/false,
-      /*is_capturing_screen=*/true, /*title=*/u"Test App",
+      /*is_capturing_screen=*/true, /*title=*/u"Test App1",
       /*url=*/absl::nullopt));
 
   base::RunLoop run_loop2;
   vc_manager().GetMediaApps(base::BindLambdaForTesting(
       [&](VideoConferenceManagerAsh::MediaApps apps) {
         EXPECT_EQ(apps.size(), 2UL);
+        EXPECT_EQ(apps[0]->title, u"Test App1");
+        EXPECT_EQ(apps[1]->title, u"Test App0");
 
         auto status = GetAggregatedCaptureStatus(std::move(apps));
 
@@ -156,9 +162,9 @@
         std::make_unique<FakeVcManagerCppClient>(vc_manager());
     client2->apps_.push_back(crosapi::mojom::VideoConferenceMediaAppInfo::New(
         /*id=*/base::UnguessableToken::Create(),
-        /*last_activity_time=*/base::Time::Now(),
+        /*last_activity_time=*/now + duration * 2,
         /*is_capturing_camera=*/false, /*is_capturing_microphone=*/true,
-        /*is_capturing_screen=*/false, /*title=*/u"Test App",
+        /*is_capturing_screen=*/false, /*title=*/u"Test App2",
         /*url=*/absl::nullopt));
 
     vc_manager().RegisterCppClient(client2.get(), client2->id_);
@@ -167,6 +173,9 @@
     vc_manager().GetMediaApps(base::BindLambdaForTesting(
         [&](VideoConferenceManagerAsh::MediaApps apps) {
           EXPECT_EQ(apps.size(), 3UL);
+          EXPECT_EQ(apps[0]->title, u"Test App1");
+          EXPECT_EQ(apps[1]->title, u"Test App2");
+          EXPECT_EQ(apps[2]->title, u"Test App0");
 
           auto status = GetAggregatedCaptureStatus(std::move(apps));
 
diff --git a/chrome/browser/ash/wallpaper_handlers/test_wallpaper_fetcher_delegate.cc b/chrome/browser/ash/wallpaper_handlers/test_wallpaper_fetcher_delegate.cc
index 9e5c220..ba983b6 100644
--- a/chrome/browser/ash/wallpaper_handlers/test_wallpaper_fetcher_delegate.cc
+++ b/chrome/browser/ash/wallpaper_handlers/test_wallpaper_fetcher_delegate.cc
@@ -30,4 +30,32 @@
       collection_id);
 }
 
+std::unique_ptr<GooglePhotosAlbumsFetcher>
+TestWallpaperFetcherDelegate::CreateGooglePhotosAlbumsFetcher(
+    Profile* profile) const {
+  return std::make_unique<testing::NiceMock<MockGooglePhotosAlbumsFetcher>>(
+      profile);
+}
+
+std::unique_ptr<GooglePhotosSharedAlbumsFetcher>
+TestWallpaperFetcherDelegate::CreateGooglePhotosSharedAlbumsFetcher(
+    Profile* profile) const {
+  return std::make_unique<
+      testing::NiceMock<MockGooglePhotosSharedAlbumsFetcher>>(profile);
+}
+
+std::unique_ptr<GooglePhotosEnabledFetcher>
+TestWallpaperFetcherDelegate::CreateGooglePhotosEnabledFetcher(
+    Profile* profile) const {
+  return std::make_unique<testing::NiceMock<MockGooglePhotosEnabledFetcher>>(
+      profile);
+}
+
+std::unique_ptr<GooglePhotosPhotosFetcher>
+TestWallpaperFetcherDelegate::CreateGooglePhotosPhotosFetcher(
+    Profile* profile) const {
+  return std::make_unique<testing::NiceMock<MockGooglePhotosPhotosFetcher>>(
+      profile);
+}
+
 }  // namespace wallpaper_handlers
diff --git a/chrome/browser/ash/wallpaper_handlers/test_wallpaper_fetcher_delegate.h b/chrome/browser/ash/wallpaper_handlers/test_wallpaper_fetcher_delegate.h
index b544537..fad1e4c 100644
--- a/chrome/browser/ash/wallpaper_handlers/test_wallpaper_fetcher_delegate.h
+++ b/chrome/browser/ash/wallpaper_handlers/test_wallpaper_fetcher_delegate.h
@@ -30,6 +30,14 @@
   CreateBackdropCollectionInfoFetcher() const override;
   std::unique_ptr<BackdropImageInfoFetcher> CreateBackdropImageInfoFetcher(
       const std::string& collection_id) const override;
+  std::unique_ptr<GooglePhotosAlbumsFetcher> CreateGooglePhotosAlbumsFetcher(
+      Profile* profile) const override;
+  std::unique_ptr<GooglePhotosSharedAlbumsFetcher>
+  CreateGooglePhotosSharedAlbumsFetcher(Profile* profile) const override;
+  std::unique_ptr<GooglePhotosEnabledFetcher> CreateGooglePhotosEnabledFetcher(
+      Profile* profile) const override;
+  std::unique_ptr<GooglePhotosPhotosFetcher> CreateGooglePhotosPhotosFetcher(
+      Profile* profile) const override;
 };
 
 }  // namespace wallpaper_handlers
diff --git a/chrome/browser/ash/wallpaper_handlers/wallpaper_fetcher_delegate.cc b/chrome/browser/ash/wallpaper_handlers/wallpaper_fetcher_delegate.cc
index 3f20884..983e1ca 100644
--- a/chrome/browser/ash/wallpaper_handlers/wallpaper_fetcher_delegate.cc
+++ b/chrome/browser/ash/wallpaper_handlers/wallpaper_fetcher_delegate.cc
@@ -8,6 +8,7 @@
 #include <string>
 
 #include "chrome/browser/ash/wallpaper_handlers/wallpaper_handlers.h"
+#include "chrome/browser/profiles/profile.h"
 #include "third_party/abseil-cpp/absl/memory/memory.h"
 
 namespace wallpaper_handlers {
@@ -29,4 +30,32 @@
   return absl::WrapUnique(new BackdropImageInfoFetcher(collection_id));
 }
 
+std::unique_ptr<GooglePhotosAlbumsFetcher>
+WallpaperFetcherDelegateImpl::CreateGooglePhotosAlbumsFetcher(
+    Profile* profile) const {
+  // Use `WrapUnique` to access the protected constructor.
+  return absl::WrapUnique(new GooglePhotosAlbumsFetcher(profile));
+}
+
+std::unique_ptr<GooglePhotosSharedAlbumsFetcher>
+WallpaperFetcherDelegateImpl::CreateGooglePhotosSharedAlbumsFetcher(
+    Profile* profile) const {
+  // Use `WrapUnique` to access the protected constructor.
+  return absl::WrapUnique(new GooglePhotosSharedAlbumsFetcher(profile));
+}
+
+std::unique_ptr<GooglePhotosEnabledFetcher>
+WallpaperFetcherDelegateImpl::CreateGooglePhotosEnabledFetcher(
+    Profile* profile) const {
+  // Use `WrapUnique` to access the protected constructor.
+  return absl::WrapUnique(new GooglePhotosEnabledFetcher(profile));
+}
+
+std::unique_ptr<GooglePhotosPhotosFetcher>
+WallpaperFetcherDelegateImpl::CreateGooglePhotosPhotosFetcher(
+    Profile* profile) const {
+  // Use `WrapUnique` to access the protected constructor.
+  return absl::WrapUnique(new GooglePhotosPhotosFetcher(profile));
+}
+
 }  // namespace wallpaper_handlers
diff --git a/chrome/browser/ash/wallpaper_handlers/wallpaper_fetcher_delegate.h b/chrome/browser/ash/wallpaper_handlers/wallpaper_fetcher_delegate.h
index 0be12850..71ae383 100644
--- a/chrome/browser/ash/wallpaper_handlers/wallpaper_fetcher_delegate.h
+++ b/chrome/browser/ash/wallpaper_handlers/wallpaper_fetcher_delegate.h
@@ -8,10 +8,16 @@
 #include <memory>
 #include <string>
 
+#include "chrome/browser/profiles/profile.h"
+
 namespace wallpaper_handlers {
 
 class BackdropCollectionInfoFetcher;
 class BackdropImageInfoFetcher;
+class GooglePhotosAlbumsFetcher;
+class GooglePhotosSharedAlbumsFetcher;
+class GooglePhotosEnabledFetcher;
+class GooglePhotosPhotosFetcher;
 
 // Delegate class for creating backdrop fetchers. Abstract class to allow
 // mocking out in test.
@@ -24,6 +30,18 @@
 
   virtual std::unique_ptr<BackdropImageInfoFetcher>
   CreateBackdropImageInfoFetcher(const std::string& collection_id) const = 0;
+
+  virtual std::unique_ptr<GooglePhotosAlbumsFetcher>
+  CreateGooglePhotosAlbumsFetcher(Profile* profile) const = 0;
+
+  virtual std::unique_ptr<GooglePhotosSharedAlbumsFetcher>
+  CreateGooglePhotosSharedAlbumsFetcher(Profile* profile) const = 0;
+
+  virtual std::unique_ptr<GooglePhotosEnabledFetcher>
+  CreateGooglePhotosEnabledFetcher(Profile* profile) const = 0;
+
+  virtual std::unique_ptr<GooglePhotosPhotosFetcher>
+  CreateGooglePhotosPhotosFetcher(Profile* profile) const = 0;
 };
 
 class WallpaperFetcherDelegateImpl : public WallpaperFetcherDelegate {
@@ -42,6 +60,18 @@
 
   std::unique_ptr<BackdropImageInfoFetcher> CreateBackdropImageInfoFetcher(
       const std::string& collection_id) const override;
+
+  std::unique_ptr<GooglePhotosAlbumsFetcher> CreateGooglePhotosAlbumsFetcher(
+      Profile* profile) const override;
+
+  std::unique_ptr<GooglePhotosSharedAlbumsFetcher>
+  CreateGooglePhotosSharedAlbumsFetcher(Profile* profile) const override;
+
+  std::unique_ptr<GooglePhotosEnabledFetcher> CreateGooglePhotosEnabledFetcher(
+      Profile* profile) const override;
+
+  std::unique_ptr<GooglePhotosPhotosFetcher> CreateGooglePhotosPhotosFetcher(
+      Profile* profile) const override;
 };
 
 }  // namespace wallpaper_handlers
diff --git a/chrome/browser/ash/wallpaper_handlers/wallpaper_handlers.h b/chrome/browser/ash/wallpaper_handlers/wallpaper_handlers.h
index 0a6da7a..f53b9fc 100644
--- a/chrome/browser/ash/wallpaper_handlers/wallpaper_handlers.h
+++ b/chrome/browser/ash/wallpaper_handlers/wallpaper_handlers.h
@@ -13,6 +13,7 @@
 #include "base/functional/callback_forward.h"
 #include "base/scoped_observation.h"
 #include "base/values.h"
+#include "chrome/browser/ash/wallpaper_handlers/wallpaper_fetcher_delegate.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
 #include "url/gurl.h"
 
@@ -237,8 +238,6 @@
 class GooglePhotosAlbumsFetcher
     : public GooglePhotosFetcher<GooglePhotosAlbumsCbkArgs> {
  public:
-  explicit GooglePhotosAlbumsFetcher(Profile* profile);
-
   GooglePhotosAlbumsFetcher(const GooglePhotosAlbumsFetcher&) = delete;
   GooglePhotosAlbumsFetcher& operator=(const GooglePhotosAlbumsFetcher&) =
       delete;
@@ -250,6 +249,10 @@
       base::OnceCallback<void(GooglePhotosAlbumsCbkArgs)> callback);
 
  protected:
+  // Protected constructor forces creation via `WallpaperFetcherDelegate` to
+  // allow mocking in test code.
+  explicit GooglePhotosAlbumsFetcher(Profile* profile);
+
   // GooglePhotosFetcher:
   GooglePhotosAlbumsCbkArgs ParseResponse(
       const base::Value::Dict* response) override;
@@ -257,6 +260,10 @@
       const GooglePhotosAlbumsCbkArgs& result) override;
 
  private:
+  // Allow delegate to see the constructor.
+  friend class WallpaperFetcherDelegateImpl;
+  friend class GooglePhotosAlbumsFetcherTest;
+
   int albums_api_refresh_counter_ = 0;
 };
 
@@ -266,8 +273,6 @@
 class GooglePhotosSharedAlbumsFetcher
     : public GooglePhotosFetcher<GooglePhotosAlbumsCbkArgs> {
  public:
-  explicit GooglePhotosSharedAlbumsFetcher(Profile* profile);
-
   GooglePhotosSharedAlbumsFetcher(const GooglePhotosSharedAlbumsFetcher&) =
       delete;
   GooglePhotosSharedAlbumsFetcher& operator=(
@@ -280,6 +285,10 @@
       base::OnceCallback<void(GooglePhotosAlbumsCbkArgs)> callback);
 
  protected:
+  // Protected constructor forces creation via `WallpaperFetcherDelegate` to
+  // allow mocking in test code.
+  explicit GooglePhotosSharedAlbumsFetcher(Profile* profile);
+
   // GooglePhotosFetcher:
   GooglePhotosAlbumsCbkArgs ParseResponse(
       const base::Value::Dict* response) override;
@@ -287,6 +296,8 @@
       const GooglePhotosAlbumsCbkArgs& result) override;
 
  private:
+  friend class WallpaperFetcherDelegateImpl;
+
   int shared_albums_api_refresh_counter_ = 0;
 };
 
@@ -295,8 +306,6 @@
 class GooglePhotosEnabledFetcher
     : public GooglePhotosFetcher<GooglePhotosEnablementState> {
  public:
-  explicit GooglePhotosEnabledFetcher(Profile* profile);
-
   GooglePhotosEnabledFetcher(const GooglePhotosEnabledFetcher&) = delete;
   GooglePhotosEnabledFetcher& operator=(const GooglePhotosEnabledFetcher&) =
       delete;
@@ -307,11 +316,19 @@
       base::OnceCallback<void(GooglePhotosEnablementState)> callback);
 
  protected:
+  // Protected constructor forces creation via `WallpaperFetcherDelegate` to
+  // allow mocking in test code.
+  explicit GooglePhotosEnabledFetcher(Profile* profile);
+
   // GooglePhotosFetcher:
   GooglePhotosEnablementState ParseResponse(
       const base::Value::Dict* response) override;
   absl::optional<size_t> GetResultCount(
       const GooglePhotosEnablementState& result) override;
+
+ private:
+  friend class WallpaperFetcherDelegateImpl;
+  friend class GooglePhotosEnabledFetcherTest;
 };
 
 using GooglePhotosPhotosCbkArgs =
@@ -320,8 +337,6 @@
 class GooglePhotosPhotosFetcher
     : public GooglePhotosFetcher<GooglePhotosPhotosCbkArgs> {
  public:
-  explicit GooglePhotosPhotosFetcher(Profile* profile);
-
   GooglePhotosPhotosFetcher(const GooglePhotosPhotosFetcher&) = delete;
   GooglePhotosPhotosFetcher& operator=(const GooglePhotosPhotosFetcher&) =
       delete;
@@ -336,6 +351,10 @@
       base::OnceCallback<void(GooglePhotosPhotosCbkArgs)> callback);
 
  protected:
+  // Protected constructor forces creation via `WallpaperFetcherDelegate` to
+  // allow mocking in test code.
+  explicit GooglePhotosPhotosFetcher(Profile* profile);
+
   // GooglePhotosFetcher:
   absl::optional<base::Value> CreateErrorResponse(int error_code) override;
   GooglePhotosPhotosCbkArgs ParseResponse(
@@ -344,6 +363,9 @@
       const GooglePhotosPhotosCbkArgs& result) override;
 
  private:
+  friend class WallpaperFetcherDelegateImpl;
+  friend class GooglePhotosPhotosFetcherTest;
+
   int photos_api_refresh_counter_ = 0;
 };
 
diff --git a/chrome/browser/ash/wallpaper_handlers/wallpaper_handlers_unittest.cc b/chrome/browser/ash/wallpaper_handlers/wallpaper_handlers_unittest.cc
new file mode 100644
index 0000000..bf8c85d
--- /dev/null
+++ b/chrome/browser/ash/wallpaper_handlers/wallpaper_handlers_unittest.cc
@@ -0,0 +1,453 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ash/wallpaper_handlers/wallpaper_handlers.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "ash/webui/personalization_app/mojom/personalization_app.mojom.h"
+#include "base/json/json_reader.h"
+#include "base/memory/raw_ptr.h"
+#include "base/strings/string_piece.h"
+#include "base/values.h"
+#include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
+#include "chrome/browser/ash/settings/scoped_cros_settings_test_helper.h"
+#include "chrome/browser/ash/wallpaper_handlers/wallpaper_fetcher_delegate.h"
+#include "chrome/test/base/scoped_testing_local_state.h"
+#include "chrome/test/base/testing_browser_process.h"
+#include "chrome/test/base/testing_profile.h"
+#include "chrome/test/base/testing_profile_manager.h"
+#include "components/user_manager/scoped_user_manager.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/test/browser_task_environment.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/icu/source/i18n/unicode/timezone.h"
+
+namespace wallpaper_handlers {
+
+namespace {
+
+constexpr char kFakeTestEmail[] = "fakeemail@personalization";
+
+constexpr char kGooglePhotosResumeTokenOnlyResponse[] =
+    "{\"resumeToken\": \"token\"}";
+constexpr char kGooglePhotosResumeToken[] = "token";
+
+constexpr char kGooglePhotosPhotosFullResponse[] =
+    "{"
+    "   \"item\": [ {"
+    "      \"itemId\": {"
+    "         \"mediaKey\": \"photoId\""
+    "      },"
+    "      \"dedupKey\": \"dedupKey\","
+    "      \"filename\": \"photoName.png\","
+    "      \"creationTimestamp\": \"2021-12-31T07:07:07.000Z\","
+    "      \"photo\": {"
+    "         \"servingUrl\": \"https://www.google.com/\""
+    "      },"
+    "      \"locationFeature\": {"
+    "         \"name\": [ {"
+    "            \"text\": \"home\""
+    "         } ]"
+    "      }"
+    "   } ],"
+    "   \"resumeToken\": \"token\""
+    "}";
+
+constexpr char kGooglePhotosPhotosSingleItemResponse[] =
+    "{"
+    "   \"item\": {"
+    "      \"itemId\": {"
+    "         \"mediaKey\": \"photoId\""
+    "      },"
+    "      \"dedupKey\": \"dedupKey\","
+    "      \"filename\": \"photoName.png\","
+    "      \"creationTimestamp\": \"2021-12-31T07:07:07.000Z\","
+    "      \"photo\": {"
+    "         \"servingUrl\": \"https://www.google.com/\""
+    "      },"
+    "      \"locationFeature\": {"
+    "         \"name\": [ {"
+    "            \"text\": \"home\""
+    "         } ]"
+    "      }"
+    "   }"
+    "}";
+
+constexpr char kGooglePhotosAlbumsFullResponse[] =
+    "{"
+    "   \"collection\": [ {"
+    "      \"collectionId\": {"
+    "         \"mediaKey\": \"albumId\""
+    "      },"
+    "      \"coverItemServingUrl\": \"https://www.google.com/\","
+    "      \"name\": \"title\","
+    "      \"numPhotos\": \"1\","
+    "      \"latestModificationTimestamp\": \"2021-12-31T07:07:07.000Z\""
+    "   } ],"
+    "   \"resumeToken\": \"token\""
+    "}";
+
+// Parses `json` as a value dictionary. A test calling this function will fail
+// if `json` is not appropriately formatted.
+base::Value::Dict JsonToDict(base::StringPiece json) {
+  absl::optional<base::Value> parsed_json = base::JSONReader::Read(json);
+  EXPECT_TRUE(parsed_json.has_value() && parsed_json->is_dict());
+  return std::move(*parsed_json).TakeDict();
+}
+
+// Returns a non-null pointer to the photo in a hypothetical Google Photos
+// photos query response. A test calling this function will fail if the response
+// does not contain exactly one photo.
+base::Value::Dict* GetPhotoFromGooglePhotosPhotosResponse(
+    base::Value::Dict* response) {
+  EXPECT_TRUE(response);
+  auto* photos = response->FindList("item");
+  EXPECT_TRUE(photos && photos->size() == 1);
+  auto* photo = photos->front().GetIfDict();
+  EXPECT_TRUE(photo);
+  return photo;
+}
+
+// Returns a non-null pointer to the album in a hypothetical Google Photos
+// albums query response. A test calling this function will fail if the response
+// does not contain exactly one album.
+base::Value::Dict* GetAlbumFromGooglePhotosAlbumsResponse(
+    base::Value::Dict* response) {
+  EXPECT_TRUE(response);
+  auto* albums = response->FindList("collection");
+  EXPECT_TRUE(albums && albums->size() == 1);
+  auto* album = albums->front().GetIfDict();
+  EXPECT_TRUE(album);
+  return album;
+}
+
+using ash::personalization_app::mojom::FetchGooglePhotosAlbumsResponse;
+using ash::personalization_app::mojom::FetchGooglePhotosPhotosResponse;
+using ash::personalization_app::mojom::GooglePhotosAlbum;
+using ash::personalization_app::mojom::GooglePhotosAlbumPtr;
+using ash::personalization_app::mojom::GooglePhotosEnablementState;
+using ash::personalization_app::mojom::GooglePhotosPhoto;
+using ash::personalization_app::mojom::GooglePhotosPhotoPtr;
+
+}  // namespace
+
+class GooglePhotosFetcherTestBase : public testing::Test {
+ public:
+  GooglePhotosFetcherTestBase()
+      : scoped_user_manager_(std::make_unique<ash::FakeChromeUserManager>()),
+        profile_manager_(TestingBrowserProcess::GetGlobal()) {}
+
+  GooglePhotosFetcherTestBase(const GooglePhotosFetcherTestBase&) = delete;
+  GooglePhotosFetcherTestBase& operator=(const GooglePhotosFetcherTestBase&) =
+      delete;
+  ~GooglePhotosFetcherTestBase() override = default;
+
+  TestingProfile* profile() { return profile_; }
+
+ protected:
+  // testing::Test:
+  void SetUp() override {
+    ASSERT_TRUE(profile_manager_.SetUp());
+    profile_ = profile_manager_.CreateTestingProfile(kFakeTestEmail,
+                                                     /*is_main_profile=*/true);
+  }
+
+ private:
+  content::BrowserTaskEnvironment task_environment_;
+  user_manager::ScopedUserManager scoped_user_manager_;
+  TestingProfileManager profile_manager_;
+  raw_ptr<TestingProfile> profile_;
+};
+
+class GooglePhotosEnabledFetcherTest : public GooglePhotosFetcherTestBase {
+ public:
+  GooglePhotosEnablementState ParseResponse(base::Value::Dict* response) {
+    return google_photos_enabled_fetcher_->ParseResponse(response);
+  }
+
+  absl::optional<size_t> GetResultCount(
+      const GooglePhotosEnablementState& result) {
+    return google_photos_enabled_fetcher_->GetResultCount(result);
+  }
+
+ protected:
+  void SetUp() override {
+    GooglePhotosFetcherTestBase::SetUp();
+    google_photos_enabled_fetcher_ =
+        std::make_unique<WallpaperFetcherDelegateImpl>()
+            ->CreateGooglePhotosEnabledFetcher(profile());
+  }
+
+ private:
+  std::unique_ptr<GooglePhotosEnabledFetcher> google_photos_enabled_fetcher_;
+};
+
+TEST_F(GooglePhotosEnabledFetcherTest, ParseGooglePhotosEnabled) {
+  // Parse an absent response (simulating a fetching error).
+  auto result = GooglePhotosEnablementState::kError;
+  EXPECT_EQ(ParseResponse(nullptr), result);
+  EXPECT_EQ(GetResultCount(result), absl::nullopt);
+
+  // Parse a response without an enabled state.
+  base::Value::Dict response;
+  EXPECT_EQ(ParseResponse(&response), result);
+  EXPECT_EQ(GetResultCount(result), absl::nullopt);
+
+  // Parse a response with an unknown enabled state.
+  response.SetByDottedPath("status.userState", "UNKNOWN_STATUS_STATE");
+  EXPECT_EQ(ParseResponse(&response), result);
+  EXPECT_EQ(GetResultCount(result), absl::nullopt);
+
+  // Parse a response indicating that the user cannot access Google Photos data.
+  response.SetByDottedPath("status.userState", "USER_DASHER_DISABLED");
+  result = GooglePhotosEnablementState::kDisabled;
+  EXPECT_EQ(ParseResponse(&response), result);
+  EXPECT_EQ(GetResultCount(result), absl::make_optional<size_t>(1u));
+
+  // Parse a response indicating that the user can access Google Photos data.
+  response.SetByDottedPath("status.userState", "USER_PERMITTED");
+  result = GooglePhotosEnablementState::kEnabled;
+  EXPECT_EQ(ParseResponse(&response), result);
+  EXPECT_EQ(GetResultCount(result), absl::make_optional<size_t>(1u));
+}
+
+class GooglePhotosPhotosFetcherTest : public GooglePhotosFetcherTestBase {
+ public:
+  GooglePhotosPhotosCbkArgs ParseResponse(const base::Value::Dict* response) {
+    return google_photos_photos_fetcher_->ParseResponse(response);
+  }
+
+  absl::optional<size_t> GetResultCount(
+      const GooglePhotosPhotosCbkArgs& result) {
+    return google_photos_photos_fetcher_->GetResultCount(result);
+  }
+
+ protected:
+  void SetUp() override {
+    GooglePhotosFetcherTestBase::SetUp();
+    google_photos_photos_fetcher_ =
+        std::make_unique<WallpaperFetcherDelegateImpl>()
+            ->CreateGooglePhotosPhotosFetcher(profile());
+  }
+
+ private:
+  std::unique_ptr<GooglePhotosPhotosFetcher> google_photos_photos_fetcher_;
+};
+
+TEST_F(GooglePhotosPhotosFetcherTest, ParseGooglePhotosPhotosAbsentPhoto) {
+  // Parse an absent response (simulating a fetching error).
+  auto result = FetchGooglePhotosPhotosResponse::New();
+  EXPECT_EQ(ParseResponse(nullptr), result);
+  EXPECT_EQ(GetResultCount(result), absl::nullopt);
+
+  // Parse a response with no resume token or photos.
+  base::Value::Dict empty_response;
+  EXPECT_EQ(ParseResponse(&empty_response), result);
+  EXPECT_EQ(GetResultCount(result), absl::nullopt);
+
+  // Parse a response with a resume token and no photos.
+  auto response = JsonToDict(kGooglePhotosResumeTokenOnlyResponse);
+  result = FetchGooglePhotosPhotosResponse::New(absl::nullopt,
+                                                kGooglePhotosResumeToken);
+  EXPECT_EQ(ParseResponse(&response), result);
+  EXPECT_EQ(GetResultCount(result), absl::optional<size_t>());
+}
+
+TEST_F(GooglePhotosPhotosFetcherTest, ParsePhotosInvalidPhoto) {
+  auto result = FetchGooglePhotosPhotosResponse::New(
+      std::vector<GooglePhotosPhotoPtr>(), kGooglePhotosResumeToken);
+
+  // Parse one-photo responses where one of the photo's fields is missing.
+  for (const auto* const path : {"itemId.mediaKey", "filename",
+                                 "creationTimestamp", "photo.servingUrl"}) {
+    auto response = JsonToDict(kGooglePhotosPhotosFullResponse);
+    auto* photo = GetPhotoFromGooglePhotosPhotosResponse(&response);
+    photo->RemoveByDottedPath(path);
+    EXPECT_EQ(ParseResponse(&response), result);
+    EXPECT_EQ(GetResultCount(result), absl::make_optional<size_t>(0u));
+  }
+
+  // Parse one-photo responses where one of the photo's fields has an invalid
+  // value.
+  std::vector<std::pair<std::string, std::string>> invalid_field_test_cases = {
+      {"creationTimestamp", ""},
+      {"creationTimestamp", "Bad timestamp"},
+      {"creationTimestamp", "2021T07:07:07.000Z"},
+      {"creationTimestamp", "31T07:07:07.000Z"},
+      {"creationTimestamp", "12T07:07:07.000Z"},
+      {"creationTimestamp", "12-31T07:07:07.000Z"},
+      {"creationTimestamp", "2021-31T07:07:07.000Z"},
+      {"creationTimestamp", "2021-12T07:07:07.000Z"},
+      {"creationTimestamp", "-2021-12-31T07:07:07.000Z"}};
+  for (const auto& kv : invalid_field_test_cases) {
+    auto response = JsonToDict(kGooglePhotosPhotosFullResponse);
+    auto* photo = GetPhotoFromGooglePhotosPhotosResponse(&response);
+    photo->SetByDottedPath(kv.first, kv.second);
+    EXPECT_EQ(ParseResponse(&response), result);
+    EXPECT_EQ(GetResultCount(result), absl::make_optional<size_t>(0u));
+  }
+}
+
+TEST_F(GooglePhotosPhotosFetcherTest, ParsePhotosValidPhoto) {
+  // Ensure that photo timestamps resolve to the same date on all testing
+  // platforms.
+  icu::TimeZone::adoptDefault(icu::TimeZone::createTimeZone("UTC"));
+
+  // Parse a response with a valid photo and a resume token.
+  auto valid_photos_vector = std::vector<GooglePhotosPhotoPtr>();
+  valid_photos_vector.push_back(GooglePhotosPhoto::New(
+      "photoId", "dedupKey", "photoName", u"Friday, December 31, 2021",
+      GURL("https://www.google.com/"), "home"));
+  auto response = JsonToDict(kGooglePhotosPhotosFullResponse);
+  auto result = FetchGooglePhotosPhotosResponse::New(
+      mojo::Clone(valid_photos_vector), kGooglePhotosResumeToken);
+  EXPECT_EQ(ParseResponse(&response), result);
+  EXPECT_EQ(GetResultCount(result),
+            absl::make_optional<size_t>(valid_photos_vector.size()));
+
+  // Parse a response with a valid photo and no resume token.
+  response.Remove("resumeToken");
+  result = FetchGooglePhotosPhotosResponse::New(
+      mojo::Clone(valid_photos_vector), absl::nullopt);
+  EXPECT_EQ(ParseResponse(&response), result);
+  EXPECT_EQ(GetResultCount(result),
+            absl::make_optional<size_t>(valid_photos_vector.size()));
+
+  // Parse a response with a single valid photo not in a list.
+  response = JsonToDict(kGooglePhotosPhotosSingleItemResponse);
+  EXPECT_EQ(ParseResponse(&response), result);
+  EXPECT_EQ(GetResultCount(result),
+            absl::make_optional<size_t>(valid_photos_vector.size()));
+
+  // Parse a response with a valid photo and no dedup key.
+  auto valid_photos_vector_without_dedup_key =
+      std::vector<GooglePhotosPhotoPtr>();
+  valid_photos_vector_without_dedup_key.push_back(GooglePhotosPhoto::New(
+      "photoId", absl::nullopt, "photoName", u"Friday, December 31, 2021",
+      GURL("https://www.google.com/"), "home"));
+  response.RemoveByDottedPath("item.dedupKey");
+  result = FetchGooglePhotosPhotosResponse::New(
+      mojo::Clone(valid_photos_vector_without_dedup_key), absl::nullopt);
+  EXPECT_EQ(result, ParseResponse(&response));
+  EXPECT_EQ(GetResultCount(result),
+            absl::make_optional<size_t>(
+                valid_photos_vector_without_dedup_key.size()));
+
+  // Parse a response with a valid photo and no location.
+  auto valid_photos_vector_without_location =
+      std::vector<GooglePhotosPhotoPtr>();
+  valid_photos_vector_without_location.push_back(GooglePhotosPhoto::New(
+      "photoId", absl::nullopt, "photoName", u"Friday, December 31, 2021",
+      GURL("https://www.google.com/"), absl::nullopt));
+  auto* name_list = response.FindListByDottedPath("item.locationFeature.name");
+  EXPECT_FALSE(name_list->empty());
+  name_list->clear();
+  result = FetchGooglePhotosPhotosResponse::New(
+      mojo::Clone(valid_photos_vector_without_location), absl::nullopt);
+  EXPECT_EQ(result, ParseResponse(&response));
+  EXPECT_EQ(
+      GetResultCount(result),
+      absl::make_optional<size_t>(valid_photos_vector_without_location.size()));
+}
+
+class GooglePhotosAlbumsFetcherTest : public GooglePhotosFetcherTestBase {
+ public:
+  GooglePhotosAlbumsCbkArgs ParseResponse(const base::Value::Dict* response) {
+    return google_photos_albums_fetcher_->ParseResponse(response);
+  }
+
+  absl::optional<size_t> GetResultCount(
+      const GooglePhotosAlbumsCbkArgs& result) {
+    return google_photos_albums_fetcher_->GetResultCount(result);
+  }
+
+ protected:
+  void SetUp() override {
+    GooglePhotosFetcherTestBase::SetUp();
+    google_photos_albums_fetcher_ =
+        std::make_unique<WallpaperFetcherDelegateImpl>()
+            ->CreateGooglePhotosAlbumsFetcher(profile());
+  }
+
+ private:
+  std::unique_ptr<GooglePhotosAlbumsFetcher> google_photos_albums_fetcher_;
+};
+
+TEST_F(GooglePhotosAlbumsFetcherTest, ParseAlbumsAbsentAlbum) {
+  // Parse an absent response (simulating a fetching error).
+  auto result = FetchGooglePhotosAlbumsResponse::New();
+  EXPECT_EQ(ParseResponse(nullptr), result);
+  EXPECT_EQ(GetResultCount(result), absl::nullopt);
+
+  // Parse a response with no resume token or albums.
+  base::Value::Dict empty_response;
+  EXPECT_EQ(ParseResponse(&empty_response), result);
+
+  // Parse a response with a resume token and no albums.
+  auto response = JsonToDict(kGooglePhotosResumeTokenOnlyResponse);
+  result = FetchGooglePhotosAlbumsResponse::New(absl::nullopt,
+                                                kGooglePhotosResumeToken);
+  EXPECT_EQ(ParseResponse(&response), result);
+  EXPECT_EQ(GetResultCount(result), absl::nullopt);
+}
+
+TEST_F(GooglePhotosAlbumsFetcherTest, ParseAlbumsInvalidAlbum) {
+  auto result = FetchGooglePhotosAlbumsResponse::New(
+      std::vector<GooglePhotosAlbumPtr>(), kGooglePhotosResumeToken);
+
+  // Parse one-album responses where one of the album's fields is missing.
+  for (const auto* const path :
+       {"collectionId.mediaKey", "name", "numPhotos", "coverItemServingUrl"}) {
+    auto response = JsonToDict(kGooglePhotosAlbumsFullResponse);
+    auto* album = GetAlbumFromGooglePhotosAlbumsResponse(&response);
+    album->RemoveByDottedPath(path);
+    EXPECT_EQ(ParseResponse(&response), result);
+    EXPECT_EQ(GetResultCount(result), absl::make_optional<size_t>(0u));
+  }
+
+  // Parse one-album responses where one of the album's fields has an invalid
+  // value.
+  std::vector<std::pair<std::string, std::string>> invalid_field_test_cases = {
+      {"numPhotos", ""},
+      {"numPhotos", "NaN"},
+      {"numPhotos", "-1"},
+      {"numPhotos", "0"}};
+  for (const auto& kv : invalid_field_test_cases) {
+    auto response = JsonToDict(kGooglePhotosAlbumsFullResponse);
+    auto* album = GetAlbumFromGooglePhotosAlbumsResponse(&response);
+    album->SetByDottedPath(kv.first, kv.second);
+    EXPECT_EQ(ParseResponse(&response), result);
+    EXPECT_EQ(GetResultCount(result), absl::make_optional<size_t>(0u));
+  }
+}
+
+TEST_F(GooglePhotosAlbumsFetcherTest, ParseAlbumsValidAlbum) {
+  // Parse a response with a valid album and a resume token.
+  auto response = JsonToDict(kGooglePhotosAlbumsFullResponse);
+  auto valid_albums_vector = std::vector<GooglePhotosAlbumPtr>();
+  base::Time timestamp;
+  EXPECT_TRUE(
+      base::Time::FromUTCString("2021-12-31T07:07:07.000Z", &timestamp));
+  valid_albums_vector.push_back(GooglePhotosAlbum::New(
+      "albumId", "title", 1, GURL("https://www.google.com/"), timestamp,
+      /*is_shared=*/false));
+  auto result = FetchGooglePhotosAlbumsResponse::New(
+      mojo::Clone(valid_albums_vector), kGooglePhotosResumeToken);
+  EXPECT_EQ(ParseResponse(&response), result);
+  EXPECT_EQ(GetResultCount(result),
+            absl::make_optional<size_t>(valid_albums_vector.size()));
+
+  // Parse a response with a valid album and no resume token.
+  response.Remove("resumeToken");
+  result = FetchGooglePhotosAlbumsResponse::New(
+      mojo::Clone(valid_albums_vector), absl::nullopt);
+  EXPECT_EQ(ParseResponse(&response), result);
+  EXPECT_EQ(GetResultCount(result),
+            absl::make_optional<size_t>(valid_albums_vector.size()));
+}
+
+}  // namespace wallpaper_handlers
diff --git a/chrome/browser/ash/web_applications/personalization_app/personalization_app_wallpaper_provider_impl.cc b/chrome/browser/ash/web_applications/personalization_app/personalization_app_wallpaper_provider_impl.cc
index b325f44d..354f107 100644
--- a/chrome/browser/ash/web_applications/personalization_app/personalization_app_wallpaper_provider_impl.cc
+++ b/chrome/browser/ash/web_applications/personalization_app/personalization_app_wallpaper_provider_impl.cc
@@ -257,8 +257,7 @@
 
   if (!google_photos_albums_fetcher_) {
     google_photos_albums_fetcher_ =
-        std::make_unique<wallpaper_handlers::GooglePhotosAlbumsFetcher>(
-            profile_);
+        wallpaper_fetcher_delegate_->CreateGooglePhotosAlbumsFetcher(profile_);
   }
   google_photos_albums_fetcher_->AddRequestAndStartIfNecessary(
       resume_token, std::move(callback));
@@ -276,7 +275,7 @@
 
   if (!google_photos_shared_albums_fetcher_) {
     google_photos_shared_albums_fetcher_ =
-        std::make_unique<wallpaper_handlers::GooglePhotosSharedAlbumsFetcher>(
+        wallpaper_fetcher_delegate_->CreateGooglePhotosSharedAlbumsFetcher(
             profile_);
   }
   google_photos_shared_albums_fetcher_->AddRequestAndStartIfNecessary(
@@ -296,8 +295,7 @@
 
   if (!google_photos_enabled_fetcher_) {
     google_photos_enabled_fetcher_ =
-        std::make_unique<wallpaper_handlers::GooglePhotosEnabledFetcher>(
-            profile_);
+        wallpaper_fetcher_delegate_->CreateGooglePhotosEnabledFetcher(profile_);
   }
   // base::Unretained is safe to use because |this| outlives
   // |google_photos_enabled_fetcher_|.
@@ -323,8 +321,7 @@
 
   if (!google_photos_photos_fetcher_) {
     google_photos_photos_fetcher_ =
-        std::make_unique<wallpaper_handlers::GooglePhotosPhotosFetcher>(
-            profile_);
+        wallpaper_fetcher_delegate_->CreateGooglePhotosPhotosFetcher(profile_);
   }
   google_photos_photos_fetcher_->AddRequestAndStartIfNecessary(
       item_id, album_id, resume_token, /*shuffle=*/false,
diff --git a/chrome/browser/ash/web_applications/personalization_app/personalization_app_wallpaper_provider_impl_unittest.cc b/chrome/browser/ash/web_applications/personalization_app/personalization_app_wallpaper_provider_impl_unittest.cc
index 4fe9b5c..5cdfde2 100644
--- a/chrome/browser/ash/web_applications/personalization_app/personalization_app_wallpaper_provider_impl_unittest.cc
+++ b/chrome/browser/ash/web_applications/personalization_app/personalization_app_wallpaper_provider_impl_unittest.cc
@@ -9,7 +9,6 @@
 #include <utility>
 #include <vector>
 
-#include "ash/constants/ash_features.h"
 #include "ash/public/cpp/wallpaper/online_wallpaper_params.h"
 #include "ash/public/cpp/wallpaper/online_wallpaper_variant.h"
 #include "ash/public/cpp/wallpaper/wallpaper_controller_client.h"
@@ -18,15 +17,12 @@
 #include "ash/webui/personalization_app/mojom/personalization_app.mojom-forward.h"
 #include "ash/webui/personalization_app/mojom/personalization_app.mojom.h"
 #include "base/functional/callback_helpers.h"
-#include "base/json/json_reader.h"
 #include "base/memory/ref_counted_memory.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/notreached.h"
 #include "base/run_loop.h"
 #include "base/test/bind.h"
-#include "base/values.h"
 #include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
-#include "chrome/browser/ash/login/users/scoped_test_user_manager.h"
 #include "chrome/browser/ash/policy/external_data/handlers/device_wallpaper_image_external_data_handler.h"
 #include "chrome/browser/ash/settings/device_settings_cache.h"
 #include "chrome/browser/ash/settings/device_settings_service.h"
@@ -42,9 +38,7 @@
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chrome/test/base/testing_profile_manager.h"
-#include "chrome_device_policy.pb.h"
 #include "components/keyed_service/core/keyed_service.h"
-#include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/testing_pref_service.h"
 #include "components/user_manager/known_user.h"
 #include "components/user_manager/scoped_user_manager.h"
@@ -56,7 +50,6 @@
 #include "mojo/public/cpp/bindings/remote.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/icu/source/i18n/unicode/timezone.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/base/webui/web_ui_util.h"
@@ -70,60 +63,6 @@
 
 constexpr char kFakeTestEmail[] = "fakeemail@personalization";
 constexpr char kTestGaiaId[] = "1234567890";
-constexpr char kGooglePhotosAlbumsFullResponse[] =
-    "{"
-    "   \"collection\": [ {"
-    "      \"collectionId\": {"
-    "         \"mediaKey\": \"albumId\""
-    "      },"
-    "      \"coverItemServingUrl\": \"https://www.google.com/\","
-    "      \"name\": \"title\","
-    "      \"numPhotos\": \"1\","
-    "      \"latestModificationTimestamp\": \"2021-12-31T07:07:07.000Z\""
-    "   } ],"
-    "   \"resumeToken\": \"token\""
-    "}";
-constexpr char kGooglePhotosPhotosFullResponse[] =
-    "{"
-    "   \"item\": [ {"
-    "      \"itemId\": {"
-    "         \"mediaKey\": \"photoId\""
-    "      },"
-    "      \"dedupKey\": \"dedupKey\","
-    "      \"filename\": \"photoName.png\","
-    "      \"creationTimestamp\": \"2021-12-31T07:07:07.000Z\","
-    "      \"photo\": {"
-    "         \"servingUrl\": \"https://www.google.com/\""
-    "      },"
-    "      \"locationFeature\": {"
-    "         \"name\": [ {"
-    "            \"text\": \"home\""
-    "         } ]"
-    "      }"
-    "   } ],"
-    "   \"resumeToken\": \"token\""
-    "}";
-constexpr char kGooglePhotosPhotosSingleItemResponse[] =
-    "{"
-    "   \"item\": {"
-    "      \"itemId\": {"
-    "         \"mediaKey\": \"photoId\""
-    "      },"
-    "      \"dedupKey\": \"dedupKey\","
-    "      \"filename\": \"photoName.png\","
-    "      \"creationTimestamp\": \"2021-12-31T07:07:07.000Z\","
-    "      \"photo\": {"
-    "         \"servingUrl\": \"https://www.google.com/\""
-    "      },"
-    "      \"locationFeature\": {"
-    "         \"name\": [ {"
-    "            \"text\": \"home\""
-    "         } ]"
-    "      }"
-    "   }"
-    "}";
-constexpr char kGooglePhotosResumeTokenOnlyResponse[] =
-    "{\"resumeToken\": \"token\"}";
 
 TestingPrefServiceSimple* RegisterPrefs(TestingPrefServiceSimple* local_state) {
   ash::device_settings_cache::RegisterPrefs(local_state->registry());
@@ -153,40 +92,6 @@
   return gfx::ImageSkia::CreateFrom1xBitmap(bitmap);
 }
 
-// Parses `json` as a value dictionary. A test calling this function will fail
-// if `json` is not appropriately formatted.
-base::Value::Dict JsonToDict(base::StringPiece json) {
-  absl::optional<base::Value> parsed_json = base::JSONReader::Read(json);
-  EXPECT_TRUE(parsed_json.has_value() && parsed_json->is_dict());
-  return std::move(*parsed_json).TakeDict();
-}
-
-// Returns a non-null pointer to the album in a hypothetical Google Photos
-// albums query response. A test calling this function will fail if the response
-// does not contain exactly one album.
-base::Value::Dict* GetAlbumFromGooglePhotosAlbumsResponse(
-    base::Value::Dict* response) {
-  EXPECT_TRUE(response);
-  auto* albums = response->FindList("collection");
-  EXPECT_TRUE(albums && albums->size() == 1);
-  auto* album = albums->front().GetIfDict();
-  EXPECT_TRUE(album);
-  return album;
-}
-
-// Returns a non-null pointer to the photo in a hypothetical Google Photos
-// photos query response. A test calling this function will fail if the response
-// does not contain exactly one photo.
-base::Value::Dict* GetPhotoFromGooglePhotosPhotosResponse(
-    base::Value::Dict* response) {
-  EXPECT_TRUE(response);
-  auto* photos = response->FindList("item");
-  EXPECT_TRUE(photos && photos->size() == 1);
-  auto* photo = photos->front().GetIfDict();
-  EXPECT_TRUE(photo);
-  return photo;
-}
-
 std::unique_ptr<KeyedService> MakeMockPersonalizationAppManager(
     content::BrowserContext* context) {
   return std::make_unique<::testing::NiceMock<MockPersonalizationAppManager>>();
@@ -719,317 +624,6 @@
 }
 
 TEST_F(PersonalizationAppWallpaperProviderImplGooglePhotosTest,
-       ParseAlbumsAbsentAlbum) {
-  using ash::personalization_app::mojom::FetchGooglePhotosAlbumsResponse;
-  using ash::personalization_app::mojom::GooglePhotosAlbumPtr;
-
-  // Mock a fetcher to parse constructed responses.
-  auto* const google_photos_albums_fetcher = static_cast<
-      ::testing::NiceMock<wallpaper_handlers::MockGooglePhotosAlbumsFetcher>*>(
-      delegate()->SetGooglePhotosAlbumsFetcherForTest(
-          std::make_unique<::testing::NiceMock<
-              wallpaper_handlers::MockGooglePhotosAlbumsFetcher>>(profile())));
-
-  // Parse an absent response (simulating a fetching error).
-  auto result = FetchGooglePhotosAlbumsResponse::New();
-  EXPECT_EQ(google_photos_albums_fetcher->ParseResponse(nullptr), result);
-  EXPECT_EQ(google_photos_albums_fetcher->GetResultCount(result),
-            absl::nullopt);
-
-  // Parse a response with no resume token or albums.
-  base::Value::Dict empty_response;
-  EXPECT_EQ(google_photos_albums_fetcher->ParseResponse(&empty_response),
-            result);
-
-  // Parse a response with a resume token and no albums.
-  auto response = JsonToDict(kGooglePhotosResumeTokenOnlyResponse);
-  result = FetchGooglePhotosAlbumsResponse::New(absl::nullopt, kResumeToken);
-  EXPECT_EQ(google_photos_albums_fetcher->ParseResponse(&response), result);
-  EXPECT_EQ(google_photos_albums_fetcher->GetResultCount(result),
-            absl::nullopt);
-}
-
-TEST_F(PersonalizationAppWallpaperProviderImplGooglePhotosTest,
-       ParseAlbumsInvalidAlbum) {
-  using ash::personalization_app::mojom::FetchGooglePhotosAlbumsResponse;
-  using ash::personalization_app::mojom::GooglePhotosAlbumPtr;
-
-  // Mock a fetcher to parse constructed responses.
-  auto* const google_photos_albums_fetcher = static_cast<
-      ::testing::NiceMock<wallpaper_handlers::MockGooglePhotosAlbumsFetcher>*>(
-      delegate()->SetGooglePhotosAlbumsFetcherForTest(
-          std::make_unique<::testing::NiceMock<
-              wallpaper_handlers::MockGooglePhotosAlbumsFetcher>>(profile())));
-
-  auto result = FetchGooglePhotosAlbumsResponse::New(
-      std::vector<GooglePhotosAlbumPtr>(), kResumeToken);
-
-  // Parse one-album responses where one of the album's fields is missing.
-  for (const auto* const path :
-       {"collectionId.mediaKey", "name", "numPhotos", "coverItemServingUrl"}) {
-    auto response = JsonToDict(kGooglePhotosAlbumsFullResponse);
-    auto* album = GetAlbumFromGooglePhotosAlbumsResponse(&response);
-    album->RemoveByDottedPath(path);
-    EXPECT_EQ(google_photos_albums_fetcher->ParseResponse(&response), result);
-    EXPECT_EQ(google_photos_albums_fetcher->GetResultCount(result),
-              absl::make_optional<size_t>(0u));
-  }
-
-  // Parse one-album responses where one of the album's fields has an invalid
-  // value.
-  std::vector<std::pair<std::string, std::string>> invalid_field_test_cases = {
-      {"numPhotos", ""},
-      {"numPhotos", "NaN"},
-      {"numPhotos", "-1"},
-      {"numPhotos", "0"}};
-  EXPECT_CALL(*google_photos_albums_fetcher, ParseResponse).Times(4);
-  for (const auto& kv : invalid_field_test_cases) {
-    auto response = JsonToDict(kGooglePhotosAlbumsFullResponse);
-    auto* album = GetAlbumFromGooglePhotosAlbumsResponse(&response);
-    album->SetByDottedPath(kv.first, kv.second);
-    EXPECT_EQ(google_photos_albums_fetcher->ParseResponse(&response), result);
-    EXPECT_EQ(google_photos_albums_fetcher->GetResultCount(result),
-              absl::make_optional<size_t>(0u));
-  }
-}
-
-TEST_F(PersonalizationAppWallpaperProviderImplGooglePhotosTest,
-       ParseAlbumsValidAlbum) {
-  using ash::personalization_app::mojom::FetchGooglePhotosAlbumsResponse;
-  using ash::personalization_app::mojom::GooglePhotosAlbum;
-  using ash::personalization_app::mojom::GooglePhotosAlbumPtr;
-
-  // Mock a fetcher to parse constructed responses.
-  auto* const google_photos_albums_fetcher = static_cast<
-      ::testing::NiceMock<wallpaper_handlers::MockGooglePhotosAlbumsFetcher>*>(
-      delegate()->SetGooglePhotosAlbumsFetcherForTest(
-          std::make_unique<::testing::NiceMock<
-              wallpaper_handlers::MockGooglePhotosAlbumsFetcher>>(profile())));
-
-  // Parse a response with a valid album and a resume token.
-  auto response = JsonToDict(kGooglePhotosAlbumsFullResponse);
-  auto valid_albums_vector = std::vector<GooglePhotosAlbumPtr>();
-  base::Time timestamp;
-  EXPECT_TRUE(
-      base::Time::FromUTCString("2021-12-31T07:07:07.000Z", &timestamp));
-  valid_albums_vector.push_back(GooglePhotosAlbum::New(
-      "albumId", "title", 1, GURL("https://www.google.com/"), timestamp,
-      /*is_shared=*/false));
-  auto result = FetchGooglePhotosAlbumsResponse::New(
-      mojo::Clone(valid_albums_vector), kResumeToken);
-  EXPECT_EQ(google_photos_albums_fetcher->ParseResponse(&response), result);
-  EXPECT_EQ(google_photos_albums_fetcher->GetResultCount(result),
-            absl::make_optional<size_t>(valid_albums_vector.size()));
-
-  // Parse a response with a valid album and no resume token.
-  response.Remove("resumeToken");
-  result = FetchGooglePhotosAlbumsResponse::New(
-      mojo::Clone(valid_albums_vector), absl::nullopt);
-  EXPECT_EQ(google_photos_albums_fetcher->ParseResponse(&response), result);
-  EXPECT_EQ(google_photos_albums_fetcher->GetResultCount(result),
-            absl::make_optional<size_t>(valid_albums_vector.size()));
-}
-
-TEST_F(PersonalizationAppWallpaperProviderImplGooglePhotosTest, ParseEnabled) {
-  using ash::personalization_app::mojom::GooglePhotosEnablementState;
-
-  // Mock a fetcher to parse constructed responses.
-  auto* const google_photos_enabled_fetcher = static_cast<
-      ::testing::NiceMock<wallpaper_handlers::MockGooglePhotosEnabledFetcher>*>(
-      delegate()->SetGooglePhotosEnabledFetcherForTest(
-          std::make_unique<::testing::NiceMock<
-              wallpaper_handlers::MockGooglePhotosEnabledFetcher>>(profile())));
-
-  // Parse an absent response (simulating a fetching error).
-  auto result = GooglePhotosEnablementState::kError;
-  EXPECT_EQ(google_photos_enabled_fetcher->ParseResponse(nullptr), result);
-  EXPECT_EQ(google_photos_enabled_fetcher->GetResultCount(result),
-            absl::nullopt);
-
-  // Parse a response without an enabled state.
-  base::Value::Dict response;
-  EXPECT_EQ(google_photos_enabled_fetcher->ParseResponse(&response), result);
-  EXPECT_EQ(google_photos_enabled_fetcher->GetResultCount(result),
-            absl::nullopt);
-
-  // Parse a response with an unknown enabled state.
-  response.SetByDottedPath("status.userState", "UNKNOWN_STATUS_STATE");
-  EXPECT_EQ(google_photos_enabled_fetcher->ParseResponse(&response), result);
-  EXPECT_EQ(google_photos_enabled_fetcher->GetResultCount(result),
-            absl::nullopt);
-
-  // Parse a response indicating that the user cannot access Google Photos data.
-  response.SetByDottedPath("status.userState", "USER_DASHER_DISABLED");
-  result = GooglePhotosEnablementState::kDisabled;
-  EXPECT_EQ(google_photos_enabled_fetcher->ParseResponse(&response), result);
-  EXPECT_EQ(google_photos_enabled_fetcher->GetResultCount(result),
-            absl::make_optional<size_t>(1u));
-
-  // Parse a response indicating that the user can access Google Photos data.
-  response.SetByDottedPath("status.userState", "USER_PERMITTED");
-  result = GooglePhotosEnablementState::kEnabled;
-  EXPECT_EQ(google_photos_enabled_fetcher->ParseResponse(&response), result);
-  EXPECT_EQ(google_photos_enabled_fetcher->GetResultCount(result),
-            absl::make_optional<size_t>(1u));
-}
-
-TEST_F(PersonalizationAppWallpaperProviderImplGooglePhotosTest,
-       ParsePhotosAbsentPhoto) {
-  using ash::personalization_app::mojom::FetchGooglePhotosPhotosResponse;
-  using ash::personalization_app::mojom::GooglePhotosPhotoPtr;
-
-  // Mock a fetcher to parse constructed responses.
-  auto* const google_photos_photos_fetcher = static_cast<
-      ::testing::NiceMock<wallpaper_handlers::MockGooglePhotosPhotosFetcher>*>(
-      delegate()->SetGooglePhotosPhotosFetcherForTest(
-          std::make_unique<::testing::NiceMock<
-              wallpaper_handlers::MockGooglePhotosPhotosFetcher>>(profile())));
-
-  // Parse an absent response (simulating a fetching error).
-  auto result = FetchGooglePhotosPhotosResponse::New();
-  EXPECT_EQ(google_photos_photos_fetcher->ParseResponse(nullptr), result);
-  EXPECT_EQ(google_photos_photos_fetcher->GetResultCount(result),
-            absl::nullopt);
-
-  // Parse a response with no resume token or photos.
-  base::Value::Dict empty_response;
-  EXPECT_EQ(google_photos_photos_fetcher->ParseResponse(&empty_response),
-            result);
-  EXPECT_EQ(google_photos_photos_fetcher->GetResultCount(result),
-            absl::nullopt);
-
-  // Parse a response with a resume token and no photos.
-  auto response = JsonToDict(kGooglePhotosResumeTokenOnlyResponse);
-  result = FetchGooglePhotosPhotosResponse::New(absl::nullopt, kResumeToken);
-  EXPECT_EQ(google_photos_photos_fetcher->ParseResponse(&response), result);
-  EXPECT_EQ(google_photos_photos_fetcher->GetResultCount(result),
-            absl::optional<size_t>());
-}
-
-TEST_F(PersonalizationAppWallpaperProviderImplGooglePhotosTest,
-       ParsePhotosInvalidPhoto) {
-  using ash::personalization_app::mojom::FetchGooglePhotosPhotosResponse;
-  using ash::personalization_app::mojom::GooglePhotosPhotoPtr;
-
-  // Mock a fetcher to parse constructed responses.
-  auto* const google_photos_photos_fetcher = static_cast<
-      ::testing::NiceMock<wallpaper_handlers::MockGooglePhotosPhotosFetcher>*>(
-      delegate()->SetGooglePhotosPhotosFetcherForTest(
-          std::make_unique<::testing::NiceMock<
-              wallpaper_handlers::MockGooglePhotosPhotosFetcher>>(profile())));
-
-  auto result = FetchGooglePhotosPhotosResponse::New(
-      std::vector<GooglePhotosPhotoPtr>(), kResumeToken);
-
-  // Parse one-photo responses where one of the photo's fields is missing.
-  for (const auto* const path : {"itemId.mediaKey", "filename",
-                                 "creationTimestamp", "photo.servingUrl"}) {
-    auto response = JsonToDict(kGooglePhotosPhotosFullResponse);
-    auto* photo = GetPhotoFromGooglePhotosPhotosResponse(&response);
-    photo->RemoveByDottedPath(path);
-    EXPECT_EQ(google_photos_photos_fetcher->ParseResponse(&response), result);
-    EXPECT_EQ(google_photos_photos_fetcher->GetResultCount(result),
-              absl::make_optional<size_t>(0u));
-  }
-
-  // Parse one-photo responses where one of the photo's fields has an invalid
-  // value.
-  std::vector<std::pair<std::string, std::string>> invalid_field_test_cases = {
-      {"creationTimestamp", ""},
-      {"creationTimestamp", "Bad timestamp"},
-      {"creationTimestamp", "2021T07:07:07.000Z"},
-      {"creationTimestamp", "31T07:07:07.000Z"},
-      {"creationTimestamp", "12T07:07:07.000Z"},
-      {"creationTimestamp", "12-31T07:07:07.000Z"},
-      {"creationTimestamp", "2021-31T07:07:07.000Z"},
-      {"creationTimestamp", "2021-12T07:07:07.000Z"},
-      {"creationTimestamp", "-2021-12-31T07:07:07.000Z"}};
-  EXPECT_CALL(*google_photos_photos_fetcher, ParseResponse).Times(9);
-  for (const auto& kv : invalid_field_test_cases) {
-    auto response = JsonToDict(kGooglePhotosPhotosFullResponse);
-    auto* photo = GetPhotoFromGooglePhotosPhotosResponse(&response);
-    photo->SetByDottedPath(kv.first, kv.second);
-    EXPECT_EQ(google_photos_photos_fetcher->ParseResponse(&response), result);
-    EXPECT_EQ(google_photos_photos_fetcher->GetResultCount(result),
-              absl::make_optional<size_t>(0u));
-  }
-}
-
-TEST_F(PersonalizationAppWallpaperProviderImplGooglePhotosTest,
-       ParsePhotosValidPhoto) {
-  using ash::personalization_app::mojom::FetchGooglePhotosPhotosResponse;
-  using ash::personalization_app::mojom::GooglePhotosPhoto;
-  using ash::personalization_app::mojom::GooglePhotosPhotoPtr;
-
-  // Mock a fetcher to parse constructed responses.
-  auto* const google_photos_photos_fetcher = static_cast<
-      ::testing::NiceMock<wallpaper_handlers::MockGooglePhotosPhotosFetcher>*>(
-      delegate()->SetGooglePhotosPhotosFetcherForTest(
-          std::make_unique<::testing::NiceMock<
-              wallpaper_handlers::MockGooglePhotosPhotosFetcher>>(profile())));
-
-  // Ensure that photo timestamps resolve to the same date on all testing
-  // platforms.
-  icu::TimeZone::adoptDefault(icu::TimeZone::createTimeZone("UTC"));
-
-  // Parse a response with a valid photo and a resume token.
-  auto valid_photos_vector = std::vector<GooglePhotosPhotoPtr>();
-  valid_photos_vector.push_back(GooglePhotosPhoto::New(
-      "photoId", "dedupKey", "photoName", u"Friday, December 31, 2021",
-      GURL("https://www.google.com/"), "home"));
-  auto response = JsonToDict(kGooglePhotosPhotosFullResponse);
-  auto result = FetchGooglePhotosPhotosResponse::New(
-      mojo::Clone(valid_photos_vector), kResumeToken);
-  EXPECT_EQ(google_photos_photos_fetcher->ParseResponse(&response), result);
-  EXPECT_EQ(google_photos_photos_fetcher->GetResultCount(result),
-            absl::make_optional<size_t>(valid_photos_vector.size()));
-
-  // Parse a response with a valid photo and no resume token.
-  response.Remove("resumeToken");
-  result = FetchGooglePhotosPhotosResponse::New(
-      mojo::Clone(valid_photos_vector), absl::nullopt);
-  EXPECT_EQ(google_photos_photos_fetcher->ParseResponse(&response), result);
-  EXPECT_EQ(google_photos_photos_fetcher->GetResultCount(result),
-            absl::make_optional<size_t>(valid_photos_vector.size()));
-
-  // Parse a response with a single valid photo not in a list.
-  response = JsonToDict(kGooglePhotosPhotosSingleItemResponse);
-  EXPECT_EQ(google_photos_photos_fetcher->ParseResponse(&response), result);
-  EXPECT_EQ(google_photos_photos_fetcher->GetResultCount(result),
-            absl::make_optional<size_t>(valid_photos_vector.size()));
-
-  // Parse a response with a valid photo and no dedup key.
-  auto valid_photos_vector_without_dedup_key =
-      std::vector<GooglePhotosPhotoPtr>();
-  valid_photos_vector_without_dedup_key.push_back(GooglePhotosPhoto::New(
-      "photoId", absl::nullopt, "photoName", u"Friday, December 31, 2021",
-      GURL("https://www.google.com/"), "home"));
-  response.RemoveByDottedPath("item.dedupKey");
-  result = FetchGooglePhotosPhotosResponse::New(
-      mojo::Clone(valid_photos_vector_without_dedup_key), absl::nullopt);
-  EXPECT_EQ(result, google_photos_photos_fetcher->ParseResponse(&response));
-  EXPECT_EQ(google_photos_photos_fetcher->GetResultCount(result),
-            absl::make_optional<size_t>(
-                valid_photos_vector_without_dedup_key.size()));
-
-  // Parse a response with a valid photo and no location.
-  auto valid_photos_vector_without_location =
-      std::vector<GooglePhotosPhotoPtr>();
-  valid_photos_vector_without_location.push_back(GooglePhotosPhoto::New(
-      "photoId", absl::nullopt, "photoName", u"Friday, December 31, 2021",
-      GURL("https://www.google.com/"), absl::nullopt));
-  auto* name_list = response.FindListByDottedPath("item.locationFeature.name");
-  EXPECT_FALSE(name_list->empty());
-  name_list->clear();
-  result = FetchGooglePhotosPhotosResponse::New(
-      mojo::Clone(valid_photos_vector_without_location), absl::nullopt);
-  EXPECT_EQ(result, google_photos_photos_fetcher->ParseResponse(&response));
-  EXPECT_EQ(
-      google_photos_photos_fetcher->GetResultCount(result),
-      absl::make_optional<size_t>(valid_photos_vector_without_location.size()));
-}
-
-TEST_F(PersonalizationAppWallpaperProviderImplGooglePhotosTest,
        SelectGooglePhotosPhoto) {
   test_wallpaper_controller()->ClearCounts();
   const std::string photo_id = "OmnisVirLupus";
diff --git a/chrome/browser/bluetooth/chrome_bluetooth_delegate_impl_client.cc b/chrome/browser/bluetooth/chrome_bluetooth_delegate_impl_client.cc
index 3a9aa433..25f1c8c 100644
--- a/chrome/browser/bluetooth/chrome_bluetooth_delegate_impl_client.cc
+++ b/chrome/browser/bluetooth/chrome_bluetooth_delegate_impl_client.cc
@@ -68,9 +68,7 @@
 #else
   return std::make_unique<permissions::BluetoothScanningPromptDesktop>(
       frame, event_handler,
-      CreateExtensionAwareChooserTitle(frame,
-                                       IDS_BLUETOOTH_SCANNING_PROMPT_ORIGIN,
-                                       IDS_BLUETOOTH_SCANNING_PROMPT_ORIGIN),
+      CreateChooserTitle(frame, IDS_BLUETOOTH_SCANNING_PROMPT),
       base::BindOnce(chrome::ShowDeviceChooserDialog, frame));
 #endif
 }
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd
index f705411..57f8424a 100644
--- a/chrome/browser/browser_resources.grd
+++ b/chrome/browser/browser_resources.grd
@@ -214,8 +214,10 @@
         <include name="IDR_PARENT_ACCESS_BEFORE_JS" file="${root_gen_dir}\chrome\browser\resources\chromeos\parent_access\parent_access_before.js" use_base_dir="false" type="BINDATA" />
         <include name="IDR_PARENT_ACCESS_DISABLED_JS" file="${root_gen_dir}\chrome\browser\resources\chromeos\parent_access\parent_access_disabled.js" use_base_dir="false" type="BINDATA" />
         <include name="IDR_PARENT_ACCESS_WEBVIEW_MANAGER_JS" file="resources\chromeos\parent_access\webview_manager.js" type="BINDATA" />
+        <include name="IDR_PARENT_ACCESS_UTILS_JS" file="resources\chromeos\parent_access\utils.js" type="BINDATA" />
         <include name="IDR_LOCAL_WEB_APPROVALS_AFTER_JS" file="${root_gen_dir}\chrome\browser\resources\chromeos\parent_access\flows\local_web_approvals_after.js" use_base_dir="false" type="BINDATA" />
         <include name="IDR_EXTENSION_APPROVALS_DISABLED_JS" file="${root_gen_dir}\chrome\browser\resources\chromeos\parent_access\flows\extension_approvals_disabled.js" use_base_dir="false" type="BINDATA" />
+        <include name="IDR_EXTENSION_APPROVALS_BEFORE_JS" file="${root_gen_dir}\chrome\browser\resources\chromeos\parent_access\flows\extension_approvals_before.js" use_base_dir="false" type="BINDATA" />
         <include name="IDR_PARENT_ACCESS_CONTROLLER_JS" file="resources\chromeos\parent_access\parent_access_controller.js" type="BINDATA" />
         <include name="IDR_PARENT_ACCESS_UI_HANDLER_JS" file="resources\chromeos\parent_access\parent_access_ui_handler.js" type="BINDATA" />
         <include name="IDR_PARENT_ACCESS_SCREEN_JS" file="resources\chromeos\parent_access\parent_access_screen.js" type="BINDATA" />
diff --git a/chrome/browser/chooser_controller/title_util.cc b/chrome/browser/chooser_controller/title_util.cc
index 4c0a7bc..974b6db0 100644
--- a/chrome/browser/chooser_controller/title_util.cc
+++ b/chrome/browser/chooser_controller/title_util.cc
@@ -4,58 +4,40 @@
 
 #include "chrome/browser/chooser_controller/title_util.h"
 
+#include <string>
+
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/profiles/profile.h"
-#include "components/permissions/chooser_title_util.h"
+#include "chrome/browser/ui/url_identity.h"
 #include "content/public/browser/render_frame_host.h"
-#include "extensions/buildflags/buildflags.h"
 #include "ui/base/l10n/l10n_util.h"
-#include "url/origin.h"
+#include "url/gurl.h"
 
-#if BUILDFLAG(ENABLE_EXTENSIONS)
-#include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/browser_finder.h"
-#include "chrome/browser/ui/web_applications/app_browser_controller.h"
-#include "extensions/browser/extension_registry.h"
-#include "extensions/common/constants.h"
-#endif
+namespace {
 
-std::u16string CreateExtensionAwareChooserTitle(
-    content::RenderFrameHost* render_frame_host,
-    int title_string_id_origin,
-    int title_string_id_extension) {
-  if (!render_frame_host)
+constexpr UrlIdentity::TypeSet kUrlIdentityAllowedTypes = {
+    UrlIdentity::Type::kDefault, UrlIdentity::Type::kFile,
+    UrlIdentity::Type::kIsolatedWebApp, UrlIdentity::Type::kChromeExtension};
+constexpr UrlIdentity::FormatOptions kUrlIdentityOptions{
+    .default_options = {
+        UrlIdentity::DefaultFormatOptions::kOmitCryptographicScheme}};
+
+}  // namespace
+
+std::u16string CreateChooserTitle(content::RenderFrameHost* render_frame_host,
+                                  int title_string_id) {
+  if (!render_frame_host) {
     return u"";
+  }
   // Ensure the permission request is attributed to the main frame.
   render_frame_host = render_frame_host->GetMainFrame();
 
-#if BUILDFLAG(ENABLE_EXTENSIONS)
-  url::Origin origin = render_frame_host->GetLastCommittedOrigin();
+  const GURL& url = render_frame_host->GetLastCommittedURL();
   Profile* profile =
       Profile::FromBrowserContext(render_frame_host->GetBrowserContext());
 
-  if (origin.scheme() == extensions::kExtensionScheme) {
-    if (auto* extension_registry =
-            extensions::ExtensionRegistry::Get(profile)) {
-      if (const extensions::Extension* extension =
-              extension_registry->enabled_extensions().GetByID(origin.host())) {
-        return l10n_util::GetStringFUTF16(title_string_id_extension,
-                                          base::UTF8ToUTF16(extension->name()));
-      }
-    }
-  }
+  UrlIdentity identity = UrlIdentity::CreateFromUrl(
+      profile, url, kUrlIdentityAllowedTypes, kUrlIdentityOptions);
 
-  // Isolated Web Apps should show the app's name instead of the origin.
-  Browser* browser = chrome::FindBrowserWithWebContents(
-      content::WebContents::FromRenderFrameHost(render_frame_host));
-  if (browser && browser->app_controller() &&
-      browser->app_controller()->IsIsolatedWebApp()) {
-    return l10n_util::GetStringFUTF16(
-        title_string_id_extension,
-        browser->app_controller()->GetAppShortName());
-  }
-#endif
-
-  return permissions::CreateChooserTitle(render_frame_host,
-                                         title_string_id_origin);
+  return l10n_util::GetStringFUTF16(title_string_id, identity.name);
 }
diff --git a/chrome/browser/chooser_controller/title_util.h b/chrome/browser/chooser_controller/title_util.h
index 0367e582..e3f5f6a 100644
--- a/chrome/browser/chooser_controller/title_util.h
+++ b/chrome/browser/chooser_controller/title_util.h
@@ -11,12 +11,12 @@
 class RenderFrameHost;
 }
 
-// Creates a title for a chooser. For extensions the extension name is used if
-// possible. In all other cases the origin of the main frame for
+// Creates a title for a chooser.
+// For Isolated Web Apps the app name is used if possible.
+// For extensions the extension name is used if possible.
+// In all other cases the origin of the main frame for
 // `render_frame_host` is used.
-std::u16string CreateExtensionAwareChooserTitle(
-    content::RenderFrameHost* render_frame_host,
-    int title_string_id_origin,
-    int title_string_id_extension);
+std::u16string CreateChooserTitle(content::RenderFrameHost* render_frame_host,
+                                  int title_string_id);
 
 #endif  // CHROME_BROWSER_CHOOSER_CONTROLLER_TITLE_UTIL_H_
diff --git a/chrome/browser/chooser_controller/title_util_unittest.cc b/chrome/browser/chooser_controller/title_util_unittest.cc
index 4df4d50..8e0a987 100644
--- a/chrome/browser/chooser_controller/title_util_unittest.cc
+++ b/chrome/browser/chooser_controller/title_util_unittest.cc
@@ -8,36 +8,52 @@
 #include "chrome/grit/generated_resources.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "components/strings/grit/components_strings.h"
+#include "content/public/test/navigation_simulator.h"
+#include "url/gurl.h"
+#include "url/origin.h"
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/test_extension_system.h"
-#include "content/public/test/navigation_simulator.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/extension_builder.h"
 #include "extensions/common/value_builder.h"
-#include "url/gurl.h"
-#include "url/origin.h"
 #endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
+#if !BUILDFLAG(IS_ANDROID)
+#include "chrome/browser/ui/web_applications/test/isolated_web_app_test_utils.h"
+#include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
+#endif  // !BUILDFLAG(IS_ANDROID)
+
 namespace {
 
-constexpr int kNonExtensionTitleResourceId =
-    IDS_USB_DEVICE_CHOOSER_PROMPT_ORIGIN;
-constexpr int kExtensionTitleResourceId =
-    IDS_USB_DEVICE_CHOOSER_PROMPT_EXTENSION_NAME;
+constexpr int kTitleResourceId = IDS_USB_DEVICE_CHOOSER_PROMPT;
 
-using ExtensionsAwareChooserTitleTest = ChromeRenderViewHostTestHarness;
+using CreateChooserTitleTest = ChromeRenderViewHostTestHarness;
 
-TEST_F(ExtensionsAwareChooserTitleTest, NoFrame) {
-  EXPECT_EQ(u"", CreateExtensionAwareChooserTitle(nullptr,
-                                                  kNonExtensionTitleResourceId,
-                                                  kExtensionTitleResourceId));
+TEST_F(CreateChooserTitleTest, NoFrame) {
+  EXPECT_EQ(u"", CreateChooserTitle(nullptr, kTitleResourceId));
+}
+
+TEST_F(CreateChooserTitleTest, UrlFrameTree) {
+  NavigateAndCommit(GURL("https://main-frame.com"));
+  content::RenderFrameHost* subframe =
+      content::NavigationSimulator::NavigateAndCommitFromDocument(
+          GURL("https://sub-frame.com"),
+          content::RenderFrameHostTester::For(main_rfh())
+              ->AppendChild("subframe"));
+
+  EXPECT_EQ("main-frame.com", main_rfh()->GetLastCommittedOrigin().host());
+  EXPECT_EQ(u"main-frame.com wants to connect",
+            CreateChooserTitle(main_rfh(), kTitleResourceId));
+  EXPECT_EQ("sub-frame.com", subframe->GetLastCommittedOrigin().host());
+  EXPECT_EQ(u"main-frame.com wants to connect",
+            CreateChooserTitle(subframe, kTitleResourceId));
 }
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
-TEST_F(ExtensionsAwareChooserTitleTest, FrameTree) {
+TEST_F(CreateChooserTitleTest, ExtensionsFrameTree) {
   extensions::DictionaryBuilder manifest;
   manifest.Set("name", "Chooser Title Subframe Test")
       .Set("version", "0.1")
@@ -63,17 +79,39 @@
           content::RenderFrameHostTester::For(main_rfh())
               ->AppendChild("subframe"));
 
-  EXPECT_EQ(extension->id(), main_rfh()->GetLastCommittedOrigin().host());
-  EXPECT_EQ(
-      u"\"Chooser Title Subframe Test\" wants to connect",
-      CreateExtensionAwareChooserTitle(main_rfh(), kNonExtensionTitleResourceId,
-                                       kExtensionTitleResourceId));
-  EXPECT_NE(extension->id(), subframe->GetLastCommittedOrigin().host());
-  EXPECT_EQ(
-      u"\"Chooser Title Subframe Test\" wants to connect",
-      CreateExtensionAwareChooserTitle(subframe, kNonExtensionTitleResourceId,
-                                       kExtensionTitleResourceId));
+  ASSERT_EQ(extension->id(), main_rfh()->GetLastCommittedOrigin().host());
+  EXPECT_EQ(u"Chooser Title Subframe Test wants to connect",
+            CreateChooserTitle(main_rfh(), kTitleResourceId));
+  ASSERT_NE(extension->id(), subframe->GetLastCommittedOrigin().host());
+  EXPECT_EQ(u"Chooser Title Subframe Test wants to connect",
+            CreateChooserTitle(subframe, kTitleResourceId));
 }
 #endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
+#if !BUILDFLAG(IS_ANDROID)
+TEST_F(CreateChooserTitleTest, IsolatedWebAppFrameTree) {
+  const GURL app_url(
+      "isolated-app://"
+      "berugqztij5biqquuk3mfwpsaibuegaqcitgfchwuosuofdjabzqaaic");
+  const std::string app_name("Chooser Title FrameTree IWA Name");
+
+  web_app::test::AwaitStartWebAppProviderAndSubsystems(profile());
+  web_app::AddDummyIsolatedAppToRegistry(profile(), app_url, app_name);
+
+  NavigateAndCommit(app_url);
+  content::RenderFrameHost* subframe =
+      content::NavigationSimulator::NavigateAndCommitFromDocument(
+          GURL("data:text/html,"),
+          content::RenderFrameHostTester::For(main_rfh())
+              ->AppendChild("subframe"));
+
+  ASSERT_EQ(app_url, main_rfh()->GetLastCommittedOrigin().GetURL());
+  EXPECT_EQ(u"Chooser Title FrameTree IWA Name wants to connect",
+            CreateChooserTitle(main_rfh(), kTitleResourceId));
+  ASSERT_NE(app_url, subframe->GetLastCommittedOrigin().GetURL());
+  EXPECT_EQ(u"Chooser Title FrameTree IWA Name wants to connect",
+            CreateChooserTitle(subframe, kTitleResourceId));
+}
+#endif  // !BUILDFLAG(IS_ANDROID)
+
 }  // namespace
diff --git a/chrome/browser/chromeos/extensions/printing_metrics/printing_metrics_api_unittest.cc b/chrome/browser/chromeos/extensions/printing_metrics/printing_metrics_api_unittest.cc
index 6ed311d4..f2bca39 100644
--- a/chrome/browser/chromeos/extensions/printing_metrics/printing_metrics_api_unittest.cc
+++ b/chrome/browser/chromeos/extensions/printing_metrics/printing_metrics_api_unittest.cc
@@ -138,7 +138,8 @@
   ASSERT_TRUE(result->is_list());
   ASSERT_EQ(1u, result->GetList().size());
   std::unique_ptr<api::printing_metrics::PrintJobInfo> print_job_info =
-      api::printing_metrics::PrintJobInfo::FromValue(result->GetList()[0]);
+      api::printing_metrics::PrintJobInfo::FromValueDeprecated(
+          result->GetList()[0]);
 
   EXPECT_THAT(
       print_job_info,
@@ -167,11 +168,13 @@
   ASSERT_TRUE(result->is_list());
   ASSERT_EQ(2u, result->GetList().size());
   std::unique_ptr<api::printing_metrics::PrintJobInfo> print_job_info1 =
-      api::printing_metrics::PrintJobInfo::FromValue(result->GetList()[0]);
+      api::printing_metrics::PrintJobInfo::FromValueDeprecated(
+          result->GetList()[0]);
   EXPECT_TRUE(print_job_info1);
   EXPECT_EQ(kTitle1, print_job_info1->title);
   std::unique_ptr<api::printing_metrics::PrintJobInfo> print_job_info2 =
-      api::printing_metrics::PrintJobInfo::FromValue(result->GetList()[1]);
+      api::printing_metrics::PrintJobInfo::FromValueDeprecated(
+          result->GetList()[1]);
   EXPECT_TRUE(print_job_info2);
   EXPECT_EQ(kTitle2, print_job_info2->title);
 }
diff --git a/chrome/browser/creator/android/java/src/org/chromium/chrome/browser/creator/CreatorCoordinator.java b/chrome/browser/creator/android/java/src/org/chromium/chrome/browser/creator/CreatorCoordinator.java
index 84976cd..2bb3665c 100644
--- a/chrome/browser/creator/android/java/src/org/chromium/chrome/browser/creator/CreatorCoordinator.java
+++ b/chrome/browser/creator/android/java/src/org/chromium/chrome/browser/creator/CreatorCoordinator.java
@@ -309,6 +309,7 @@
             view.setId(R.id.creator_feed_stream_recycler_view);
             view.setClipToPadding(false);
             view.setBackgroundColor(SemanticColorUtils.getDefaultBgColor(mActivity));
+            view.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
         } else {
             view = null;
         }
diff --git a/chrome/browser/extensions/activity_log/activity_database.cc b/chrome/browser/extensions/activity_log/activity_database.cc
index f529fd8..0a60882 100644
--- a/chrome/browser/extensions/activity_log/activity_database.cc
+++ b/chrome/browser/extensions/activity_log/activity_database.cc
@@ -176,7 +176,7 @@
   valid_db_ = false;
   timer_.Stop();
   db_.reset_error_callback();
-  db_.RazeAndClose();
+  db_.RazeAndPoison();
   delegate_->OnDatabaseFailure();
   already_closed_ = true;
 }
diff --git a/chrome/browser/extensions/api/runtime/runtime_apitest.cc b/chrome/browser/extensions/api/runtime/runtime_apitest.cc
index 844e984..fbbaf15 100644
--- a/chrome/browser/extensions/api/runtime/runtime_apitest.cc
+++ b/chrome/browser/extensions/api/runtime/runtime_apitest.cc
@@ -6,6 +6,7 @@
 
 #include "base/json/json_reader.h"
 #include "base/run_loop.h"
+#include "base/strings/stringprintf.h"
 #include "base/test/bind.h"
 #include "base/test/simple_test_tick_clock.h"
 #include "base/test/values_test_util.h"
@@ -635,17 +636,62 @@
 
   // Runs `chrome.runtime.getContexts()` and returns the result as a
   // base::Value.
-  base::Value GetContexts() {
-    static constexpr char kScript[] =
+  base::Value GetContexts(const char* filter) {
+    static constexpr char kScriptTemplate[] =
         R"((async () => {
              chrome.test.sendScriptResult(
-                 await chrome.runtime.getContexts());
+                 await chrome.runtime.getContexts(%s));
            })();)";
+    std::string script = base::StringPrintf(kScriptTemplate, filter);
     return BackgroundScriptExecutor::ExecuteScript(
-        profile(), extension_->id(), kScript,
+        profile(), extension_->id(), script,
         BackgroundScriptExecutor::ResultCapture::kSendScriptResult);
   }
 
+  // Runs `chrome.runtime.getContexts()` and returns the result as a vector
+  // of strongly-typed `ExtensionContext`s. Expects the getContexts() call to
+  // return a valid value (i.e., not throw an error).
+  std::vector<api::runtime::ExtensionContext> GetContextStructs(
+      const char* filter) {
+    base::Value value = GetContexts(filter);
+    if (!value.is_list()) {
+      ADD_FAILURE() << "Invalid return value: " << value;
+      return {};
+    }
+
+    std::vector<api::runtime::ExtensionContext> result;
+    result.reserve(value.GetList().size());
+    for (const auto& entry : value.GetList()) {
+      if (!entry.is_dict()) {
+        ADD_FAILURE() << "Invalid return value: " << value;
+        return {};
+      }
+      auto context = api::runtime::ExtensionContext::FromValue(entry.GetDict());
+      if (!context) {
+        ADD_FAILURE() << "Invalid return value: " << value;
+        return {};
+      }
+
+      result.push_back(std::move(*context));
+    }
+
+    return result;
+  }
+
+  // Returns a matcher that expects an ExtensionContext to be a valid
+  // background context (without testing details of the entry).
+  auto GetBackgroundMatcher() {
+    return testing::AllOf(
+        testing::Field(&api::runtime::ExtensionContext::context_type,
+                       testing::Eq(api::runtime::CONTEXT_TYPE_BACKGROUND)),
+        testing::Field(&api::runtime::ExtensionContext::tab_id,
+                       testing::Eq(-1)),
+        testing::Field(&api::runtime::ExtensionContext::window_id,
+                       testing::Eq(-1)),
+        testing::Field(&api::runtime::ExtensionContext::frame_id,
+                       testing::Eq(-1)));
+  }
+
  private:
   const Extension* extension_ = nullptr;
   TestExtensionDir test_dir_;
@@ -656,7 +702,10 @@
 // Tests retrieving the background service worker context using
 // `chrome.runtime.getContexts()`.
 IN_PROC_BROWSER_TEST_F(RuntimeGetContextsApiTest, GetServiceWorkerContext) {
-  base::Value background_contexts = GetContexts();
+  // An empty dictionary filter should match all contexts (of which there is
+  // only one).
+  base::Value contexts = GetContexts("{}");
+
   // Note: fields of `documentId`, `documentUrl`, and `documentOrigin` are
   // undefined (service worker contexts don't have an associated document).
   // `tabId`, `frameId`, and `windowId` are -1 for consistency with other
@@ -670,10 +719,51 @@
             "frameId": -1,
             "incognito": false
          }])";
-  EXPECT_THAT(background_contexts, base::test::IsJson(kExpected));
+  EXPECT_THAT(contexts, base::test::IsJson(kExpected));
 
   // TODO(crbug/1426192): Add tests for retrieving a service worker context
   // when there isn't an active worker.
 }
 
+// Tests the filter matching behavior of `runtime.getContexts()`.
+IN_PROC_BROWSER_TEST_F(RuntimeGetContextsApiTest, FilterMatching) {
+  // Currently, there is only one context: the background service worker.
+
+  {
+    // Passing a filter to match background contexts should match the worker.
+    std::vector<api::runtime::ExtensionContext> contexts =
+        GetContextStructs(R"({"contextTypes": ["BACKGROUND"]})");
+    EXPECT_THAT(contexts, testing::ElementsAre(GetBackgroundMatcher()));
+  }
+  {
+    // Try passing a filter for a different context type. No contexts should
+    // match.
+    std::vector<api::runtime::ExtensionContext> contexts =
+        GetContextStructs(R"({"contextTypes": ["POPUP"]})");
+    EXPECT_THAT(contexts, testing::IsEmpty());
+  }
+  {
+    // Filter properties support an array of options; if the context matches an
+    // entry in the array, it matches the filter for that property. Thus,
+    // passing both "BACKGROUND" and "POPUP" should match the service worker
+    // context.
+    std::vector<api::runtime::ExtensionContext> contexts =
+        GetContextStructs(R"({"contextTypes": ["BACKGROUND", "POPUP"]})");
+    EXPECT_THAT(contexts, testing::ElementsAre(GetBackgroundMatcher()));
+  }
+  {
+    // All specified filter properties must match. Thus, if we look for a
+    // background context and also specify a tab ID, nothing should match
+    // (since the background context doesn't have an associated tab).
+    static constexpr char kFilter[] =
+        R"({
+             "contextTypes": ["BACKGROUND"],
+             "tabIds": [2]
+           })";
+    std::vector<api::runtime::ExtensionContext> contexts =
+        GetContextStructs(kFilter);
+    EXPECT_THAT(contexts, testing::IsEmpty());
+  }
+}
+
 }  // namespace extensions
diff --git a/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc b/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc
index f1628be..a2e2aca 100644
--- a/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc
+++ b/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc
@@ -26,6 +26,7 @@
 #include "chrome/browser/extensions/extension_apitest.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/renderer_context_menu/render_view_context_menu_test_util.h"
+#include "chrome/browser/ssl/https_upgrades_util.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_navigator_params.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
@@ -303,6 +304,11 @@
 
 IN_PROC_BROWSER_TEST_P(WebNavigationApiPrerenderTestWithContextType,
                        Prerendering) {
+  // TODO(crbug.com/1394910): Use https in the test and remove this allowlist
+  // entry.
+  ScopedAllowHttpForHostnamesForTesting scoped_allow_http(
+      {"a.test"}, browser()->profile()->GetPrefs());
+
   ASSERT_TRUE(StartEmbeddedTestServer());
   ASSERT_TRUE(RunExtensionTest("webnavigation/prerendering")) << message_;
 }
@@ -369,6 +375,11 @@
 
 IN_PROC_BROWSER_TEST_P(WebNavigationApiTestWithContextType,
                        ServerRedirectSingleProcess) {
+  // TODO(crbug.com/1394910): Use https in the test and remove these allowlist
+  // entries.
+  ScopedAllowHttpForHostnamesForTesting scoped_allow_http(
+      {"www.a.com", "www.b.com"}, browser()->profile()->GetPrefs());
+
   ASSERT_TRUE(StartEmbeddedTestServer());
 
   // Set max renderers to 1 to force running out of processes.
@@ -674,6 +685,11 @@
 }
 
 IN_PROC_BROWSER_TEST_P(WebNavigationApiTestWithContextType, Crash) {
+  // TODO(crbug.com/1394910): Use https in the test and remove this allowlist
+  // entry.
+  ScopedAllowHttpForHostnamesForTesting scoped_allow_http(
+      {"www.a.com"}, browser()->profile()->GetPrefs());
+
   content::ScopedAllowRendererCrashes scoped_allow_renderer_crashes;
   ASSERT_TRUE(StartEmbeddedTestServer());
 
diff --git a/chrome/browser/extensions/content_script_apitest.cc b/chrome/browser/extensions/content_script_apitest.cc
index 2966e44cd..6f62be03 100644
--- a/chrome/browser/extensions/content_script_apitest.cc
+++ b/chrome/browser/extensions/content_script_apitest.cc
@@ -23,6 +23,7 @@
 #include "chrome/browser/extensions/identifiability_metrics_test_util.h"
 #include "chrome/browser/search/search.h"
 #include "chrome/browser/ssl/https_upgrades_interceptor.h"
+#include "chrome/browser/ssl/https_upgrades_util.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_navigator.h"
 #include "chrome/browser/ui/search/ntp_test_utils.h"
@@ -145,6 +146,19 @@
         https_test_server_->port());
     HttpsUpgradesInterceptor::SetHttpPortForTesting(
         embedded_test_server()->port());
+
+    // Test extensions use these hostnames. Allow them to be loaded over
+    // HTTP so that HTTPS-Upgrades feature doesn't upgrade their URLs.
+    // TODO(crbug.com/1394910): Use https in these tests and remove these
+    // allowlist entries.
+    AllowHttpForHostnamesForTesting(
+        {"a.com", "b.com", "default.test", "bar.com", "path-test.example",
+         "example.com", "chromium.org", "example1.com"},
+        browser()->profile()->GetPrefs());
+  }
+
+  void TearDownOnMainThread() override {
+    ClearHttpAllowlistForHostnamesForTesting(browser()->profile()->GetPrefs());
   }
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
@@ -994,13 +1008,8 @@
 
   // The extension should inject on "normal" urls.
 
-  // Test on an HTTP URL. Disable HTTPS upgrades on example1.com for this test
-  // to work.
-  auto* prefs = browser()->profile()->GetPrefs();
-  base::Value::List allowlist;
-  allowlist.Append("example1.com");
-  prefs->SetList(prefs::kHttpAllowlist, std::move(allowlist));
-
+  // Test on an HTTP URL. HTTPS upgrades is disabled on example1.com so it
+  // loads over http instead of https. example2.com loads over https.
   GURL unprotected_url1 = embedded_test_server()->GetURL(
       "example1.com", "/extensions/test_file.html");
   ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), unprotected_url1));
diff --git a/chrome/browser/extensions/device_permissions_dialog_controller.cc b/chrome/browser/extensions/device_permissions_dialog_controller.cc
index e4d6799..5414e8c 100644
--- a/chrome/browser/extensions/device_permissions_dialog_controller.cc
+++ b/chrome/browser/extensions/device_permissions_dialog_controller.cc
@@ -13,11 +13,9 @@
 DevicePermissionsDialogController::DevicePermissionsDialogController(
     content::RenderFrameHost* owner,
     scoped_refptr<extensions::DevicePermissionsPrompt::Prompt> prompt)
-    : ChooserController(CreateExtensionAwareChooserTitle(
+    : ChooserController(CreateChooserTitle(
           owner,
           prompt->multiple() ? IDS_DEVICE_PERMISSIONS_PROMPT_MULTIPLE_SELECTION
-                             : IDS_DEVICE_PERMISSIONS_PROMPT_SINGLE_SELECTION,
-          prompt->multiple() ? IDS_DEVICE_PERMISSIONS_PROMPT_MULTIPLE_SELECTION
                              : IDS_DEVICE_PERMISSIONS_PROMPT_SINGLE_SELECTION)),
       prompt_(prompt) {
   prompt_->SetObserver(this);
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index ef165e6..bcc31002 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -3214,11 +3214,6 @@
     "expiry_milestone": 114
   },
   {
-    "name": "enable-tab-strip-improvements",
-    "owners": [ "skavuluru", "clank-app-team@google.com" ],
-    "expiry_milestone": 114
-  },
-  {
     "name": "enable-tab-strip-redesign",
     "owners": [ "zheliooo", "skavuluru", "nemco", "clank-app-team@google.com" ],
     "expiry_milestone": 120
@@ -5329,6 +5324,11 @@
     "expiry_milestone": 120
   },
   {
+    "name": "omnibox-gm3-steady-state-text-color",
+    "owners": [ "khalidpeer", "manukh", "chrome-omnibox-team@google.com" ],
+    "expiry_milestone": 120
+  },
+  {
     "name": "omnibox-gm3-steady-state-text-style",
     "owners": [ "khalidpeer", "manukh", "chrome-omnibox-team@google.com" ],
     "expiry_milestone": 120
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 37ab5f81..910c2eb 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -2112,6 +2112,11 @@
 const char kOmniboxGM3SteadyStateTextStyleDescription[] =
     "Updates Omnibox steady state text style to comply with GM3 guidelines.";
 
+const char kOmniboxGM3SteadyStateTextColorName[] =
+    "Omnibox Steady State Text Color";
+const char kOmniboxGM3SteadyStateTextColorDescription[] =
+    "Updates Omnibox steady state text color to comply with GM3 guidelines.";
+
 const char kOmniboxGroupingFrameworkZPSName[] =
     "Omnibox Grouping Framework for ZPS";
 const char kOmniboxGroupingFrameworkNonZPSName[] =
@@ -4142,11 +4147,6 @@
 const char kTabGroupsForTabletsName[] = "Tab groups on tablets";
 const char kTabGroupsForTabletsDescription[] = "Enable tab groups on tablets.";
 
-const char kTabStripImprovementsAndroidName[] =
-    "Tab strip improvements for Android.";
-const char kTabStripImprovementsAndroidDescription[] =
-    "Enables scrollable tab strip with tab group indicators.";
-
 const char kDiscoverFeedMultiColumnAndroidName[] =
     "Multi-column Discover feed Android.";
 const char kDiscoverFeedMultiColumnAndroidDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 332a2bb..f37a49e 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -1187,6 +1187,9 @@
 extern const char kOmniboxGM3SteadyStateTextStyleName[];
 extern const char kOmniboxGM3SteadyStateTextStyleDescription[];
 
+extern const char kOmniboxGM3SteadyStateTextColorName[];
+extern const char kOmniboxGM3SteadyStateTextColorDescription[];
+
 extern const char kOmniboxGroupingFrameworkNonZPSName[];
 extern const char kOmniboxGroupingFrameworkZPSName[];
 extern const char kOmniboxGroupingFrameworkDescription[];
@@ -1734,9 +1737,6 @@
 extern const char kTabSearchFuzzySearchName[];
 extern const char kTabSearchFuzzySearchDescription[];
 
-extern const char kTabStripImprovementsAndroidName[];
-extern const char kTabStripImprovementsAndroidDescription[];
-
 extern const char kDiscoverFeedMultiColumnAndroidName[];
 extern const char kDiscoverFeedMultiColumnAndroidDescription[];
 
diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc
index 893a017..8fbd6862 100644
--- a/chrome/browser/flags/android/chrome_feature_list.cc
+++ b/chrome/browser/flags/android/chrome_feature_list.cc
@@ -304,7 +304,6 @@
     &kTabGridLayoutAndroid,
     &kTabSelectionEditorV2,
     &kTabStateV1Optimizations,
-    &kTabStripImprovements,
     &kTabToGTSAnimation,
     &kTestDefaultDisabled,
     &kTestDefaultEnabled,
@@ -1002,10 +1001,6 @@
              "TabStateV1Optimizations",
              base::FEATURE_ENABLED_BY_DEFAULT);
 
-BASE_FEATURE(kTabStripImprovements,
-             "TabStripImprovements",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
 BASE_FEATURE(kDiscoverFeedMultiColumn,
              "DiscoverFeedMultiColumn",
              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 3e90bb1..6c7f7ef 100644
--- a/chrome/browser/flags/android/chrome_feature_list.h
+++ b/chrome/browser/flags/android/chrome_feature_list.h
@@ -168,7 +168,6 @@
 BASE_DECLARE_FEATURE(kTabGridLayoutAndroid);
 BASE_DECLARE_FEATURE(kTabSelectionEditorV2);
 BASE_DECLARE_FEATURE(kTabStateV1Optimizations);
-BASE_DECLARE_FEATURE(kTabStripImprovements);
 BASE_DECLARE_FEATURE(kDiscoverFeedMultiColumn);
 BASE_DECLARE_FEATURE(kTabStripRedesign);
 BASE_DECLARE_FEATURE(kTabToGTSAnimation);
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 94d0e79..74b00338 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
@@ -198,8 +198,6 @@
             "AutofillEnableNewCardArtAndNetworkImages";
     public static final String AUTOFILL_ENABLE_SUPPORT_FOR_HONORIFIC_PREFIXES =
             "AutofillEnableSupportForHonorificPrefixes";
-    public static final String AUTOFILL_ENABLE_SUPPORT_FOR_MORE_STRUCTURE_IN_ADDRESSES =
-            "AutofillEnableSupportForMoreStructureInAddresses";
     public static final String AUTOFILL_ENABLE_UPDATE_VIRTUAL_CARD_ENROLLMENT =
             "AutofillEnableUpdateVirtualCardEnrollment";
     public static final String AUTOFILL_ENABLE_VIRTUAL_CARD_METADATA =
@@ -215,8 +213,6 @@
     public static final String BACK_GESTURE_REFACTOR_ACTIVITY =
             "BackGestureRefactorActivityAndroid";
     public static final String BASELINE_GM3_SURFACE_COLORS = "BaselineGM3SurfaceColors";
-    public static final String BIOMETRIC_TOUCH_TO_FILL = "BiometricTouchToFill";
-    public static final String BOOKMARKS_IMPROVED_SAVE_FLOW = "BookmarksImprovedSaveFlow";
     public static final String BOOKMARKS_REFRESH = "BookmarksRefresh";
     public static final String CACHE_DEPRECATED_SYSTEM_LOCATION_SETTING =
             "CacheDeprecatedSystemLocationSetting";
@@ -225,7 +221,6 @@
     public static final String CCT_ALLOW_CROSS_UID_ACTIVITY_SWITCH_FROM_BELOW =
             "CCTAllowCrossUidActivitySwitchFromBelow";
     public static final String CCT_AUTO_TRANSLATE = "CCTAutoTranslate";
-    public static final String CCT_BACKGROUND_TAB = "CCTBackgroundTab";
     public static final String CCT_BOTTOM_BAR_SWIPE_UP_GESTURE = "CCTBottomBarSwipeUpGesture";
     public static final String CCT_BRAND_TRANSPARENCY = "CCTBrandTransparency";
     public static final String CCT_CLIENT_DATA_HEADER = "CCTClientDataHeader";
@@ -257,7 +252,6 @@
     public static final String CCT_RESOURCE_PREFETCH = "CCTResourcePrefetch";
     public static final String CCT_RETAINING_STATE_IN_MEMORY = "CCTRetainingStateInMemory";
     public static final String CCT_TOOLBAR_CUSTOMIZATIONS = "CCTToolbarCustomizations";
-    public static final String CHROME_NEW_DOWNLOAD_TAB = "ChromeNewDownloadTab";
     public static final String CHROME_SHARING_HUB = "ChromeSharingHub";
     public static final String CHROME_SHARING_HUB_LAUNCH_ADJACENT =
             "ChromeSharingHubLaunchAdjacent";
@@ -269,7 +263,6 @@
     public static final String COMMERCE_COUPONS = "CommerceCoupons";
     public static final String COMMERCE_MERCHANT_VIEWER = "CommerceMerchantViewer";
     public static final String COMMERCE_PRICE_TRACKING = "CommercePriceTracking";
-    public static final String CONTACTS_PICKER_SELECT_ALL = "ContactsPickerSelectAll";
     public static final String CONTEXTUAL_PAGE_ACTIONS = "ContextualPageActions";
     public static final String CONTEXTUAL_PAGE_ACTION_PRICE_TRACKING =
             "ContextualPageActionPriceTracking";
@@ -304,13 +297,10 @@
     public static final String DETAILED_LANGUAGE_SETTINGS = "DetailedLanguageSettings";
     public static final String DISCO_FEED_ENDPOINT = "DiscoFeedEndpoint";
     public static final String DNS_OVER_HTTPS = "DnsOverHttps";
-    public static final String DONT_AUTO_HIDE_BROWSER_CONTROLS = "DontAutoHideBrowserControls";
-    public static final String DOWNLOADS_FOREGROUND = "DownloadsForeground";
     public static final String DOWNLOAD_OFFLINE_CONTENT_PROVIDER =
             "UseDownloadOfflineContentProvider";
     public static final String EARLY_LIBRARY_LOAD = "EarlyLibraryLoad";
     public static final String ENABLE_IPH = "EnableIPH";
-    public static final String ENABLE_PASSWORDS_ACCOUNT_STORAGE = "EnablePasswordsAccountStorage";
     public static final String EXPERIMENTS_FOR_AGSA = "ExperimentsForAgsa";
     public static final String EXPLICIT_LANGUAGE_ASK = "ExplicitLanguageAsk";
     public static final String EXPLORE_SITES = "ExploreSites";
@@ -321,14 +311,10 @@
             "FeedImageMemoryCacheSizePercentage";
     public static final String FEED_LOADING_PLACEHOLDER = "FeedLoadingPlaceholder";
     public static final String FEED_MULTI_COLUMN = "DiscoverFeedMultiColumn";
-    public static final String FEED_NO_VIEW_CACHE = "FeedNoViewCache";
-    public static final String FEED_INTERACTIVE_REFRESH = "FeedInteractiveRefresh";
-    public static final String FEED_PERFORMANCE_STUDY = "FeedPerformanceStudy";
     public static final String FEED_POSITION_ANDROID = "FeedPositionAndroid";
     public static final String FEED_SHOW_SIGN_IN_COMMAND = "FeedShowSignInCommand";
     public static final String FEED_BOC_SIGN_IN_INTERSTITIAL = "FeedBoCSigninInterstitial";
     public static final String FILLING_PASSWORDS_FROM_ANY_ORIGIN = "FillingPasswordsFromAnyOrigin";
-    public static final String FIXED_UMA_SESSION_RESUME_ORDER = "FixedUmaSessionResumeOrder";
     public static final String FOCUS_OMNIBOX_IN_INCOGNITO_TAB_INTENTS =
             "FocusOmniboxInIncognitoTabIntents";
     public static final String FOLDABLE_JANK_FIX = "FoldableJankFix";
@@ -354,8 +340,6 @@
     public static final String INSTANCE_SWITCHER = "InstanceSwitcher";
     public static final String INSTANT_START = "InstantStart";
     public static final String INTEREST_FEED_CONTENT_SUGGESTIONS = "InterestFeedContentSuggestions";
-    public static final String INTEREST_FEED_NOTICE_CARD_AUTO_DISMISS =
-            "InterestFeedNoticeCardAutoDismiss";
     public static final String INTEREST_FEED_V2 = "InterestFeedV2";
     public static final String INTEREST_FEED_V2_AUTOPLAY = "InterestFeedV2Autoplay";
     public static final String INTEREST_FEED_V2_HEARTS = "InterestFeedV2Hearts";
@@ -366,17 +350,12 @@
     public static final String LOCAL_WEB_APPROVALS = "LocalWebApprovals";
     public static final String LOOKALIKE_NAVIGATION_URL_SUGGESTIONS_UI =
             "LookalikeUrlNavigationSuggestionsUI";
-    public static final String MARK_HTTP_AS = "MarkHttpAs";
     public static final String MESSAGES_FOR_ANDROID_ADS_BLOCKED = "MessagesForAndroidAdsBlocked";
     public static final String MESSAGES_FOR_ANDROID_INFRASTRUCTURE =
             "MessagesForAndroidInfrastructure";
     public static final String MESSAGES_FOR_ANDROID_PERMISSION_UPDATE =
             "MessagesForAndroidPermissionUpdate";
-    public static final String MESSAGES_FOR_ANDROID_STACKING_ANIMATION =
-            "MessagesForAndroidStackingAnimation";
-    public static final String MESSAGES_FOR_ANDROID_SYNC_ERROR = "MessagesForAndroidSyncError";
     public static final String METRICS_SETTINGS_ANDROID = "MetricsSettingsAndroid";
-    public static final String MODAL_PERMISSION_DIALOG_VIEW = "ModalPermissionDialogView";
     public static final String NOTIFICATION_PERMISSION_VARIANT = "NotificationPermissionVariant";
     public static final String NOTIFICATION_PERMISSION_BOTTOM_SHEET =
             "NotificationPermissionBottomSheet";
@@ -406,7 +385,6 @@
             "OptimizationGuidePushNotifications";
     public static final String OPTIMIZE_GEOLOCATION_HEADER_GENERATION =
             "OptimizeGeolocationHeaderGeneration";
-    public static final String OVERLAY_NEW_LAYOUT = "OverlayNewLayout";
     public static final String PAGE_ANNOTATIONS_SERVICE = "PageAnnotationsService";
     public static final String PAGE_INFO_ABOUT_THIS_SITE_EN = "PageInfoAboutThisSiteEn";
     public static final String PAGE_INFO_ABOUT_THIS_SITE_IMPROVED_BOTTOMSHEET =
@@ -416,15 +394,11 @@
     public static final String PAGE_INFO_ABOUT_THIS_SITE_NEW_ICON = "PageInfoAboutThisSiteNewIcon";
     public static final String PAGE_INFO_ABOUT_THIS_SITE_NON_EN = "PageInfoAboutThisSiteNonEn";
     public static final String PAINT_PREVIEW_DEMO = "PaintPreviewDemo";
-    public static final String PAINT_PREVIEW_SHOW_ON_STARTUP = "PaintPreviewShowOnStartup";
     public static final String PARTNER_HOMEPAGE_INITIAL_LOAD_IMPROVEMENT =
             "PartnerHomepageInitialLoadImprovement";
     public static final String PASSKEY_MANAGEMENT_USING_ACCOUNT_SETTINGS_ANDROID =
             "PasskeyManagementUsingAccountSettingsAndroid";
-    public static final String PASSWORD_DOMAIN_CAPABILITIES_FETCHING =
-            "PasswordDomainCapabilitiesFetching";
     public static final String PASSWORD_EDIT_DIALOG_WITH_DETAILS = "PasswordEditDialogWithDetails";
-    public static final String PERMISSION_DELEGATION = "PermissionDelegation";
     public static final String PORTALS = "Portals";
     public static final String PORTALS_CROSS_ORIGIN = "PortalsCrossOrigin";
     public static final String POST_TASK_FOCUS_TAB = "PostTaskFocusTab";
@@ -457,8 +431,6 @@
     public static final String RECOVER_FROM_NEVER_SAVE_ANDROID = "RecoverFromNeverSaveAndroid";
     public static final String REENGAGEMENT_NOTIFICATION = "ReengagementNotification";
     public static final String RELATED_SEARCHES = "RelatedSearches";
-    public static final String REPORT_PARENTAL_CONTROL_SITES_CHILD =
-            "ReportParentalControlSitesChild";
     public static final String REQUEST_DESKTOP_SITE_DEFAULTS = "RequestDesktopSiteDefaults";
     public static final String REQUEST_DESKTOP_SITE_DEFAULTS_CONTROL =
             "RequestDesktopSiteDefaultsControl";
@@ -486,10 +458,7 @@
     public static final String SEND_TAB_TO_SELF_SIGNIN_PROMO = "SendTabToSelfSigninPromo";
     public static final String SEND_TAB_TO_SELF_V2 = "SendTabToSelfV2";
     public static final String SHARED_HIGHLIGHTING_AMP = "SharedHighlightingAmp";
-    public static final String SHARE_CROW_BUTTON = "ShareCrowButton";
-    public static final String SHARE_CROW_BUTTON_LAUNCH_TAB = "ShareCrowLaunchTab";
     public static final String SHOPPING_LIST = "ShoppingList";
-    public static final String SHOW_EXTENDED_PRELOADING_SETTING = "ShowExtendedPreloadingSetting";
     public static final String SHOW_SCROLLABLE_MVT_ON_NTP_ANDROID = "ShowScrollableMVTOnNTPAndroid";
     public static final String SKIP_SERVICE_WORKER_FOR_INSTALL_PROMPT =
             "SkipServiceWorkerForInstallPromot";
@@ -520,7 +489,6 @@
     public static final String TAB_GROUPS_FOR_TABLETS = "TabGroupsForTablets";
     public static final String TAB_SELECTION_EDITOR_V2 = "TabSelectionEditorV2";
     public static final String TAB_STATE_V1_OPTIMIZATIONS = "TabStateV1Optimizations";
-    public static final String TAB_STRIP_IMPROVEMENTS = "TabStripImprovements";
     public static final String TAB_STRIP_REDESIGN = "TabStripRedesign";
     public static final String TAB_TO_GTS_ANIMATION = "TabToGTSAnimation";
     public static final String TANGIBLE_SYNC = "TangibleSync";
@@ -541,18 +509,13 @@
             "TrustedWebActivityQualityEnforcementForced";
     public static final String TRUSTED_WEB_ACTIVITY_QUALITY_ENFORCEMENT_WARNING =
             "TrustedWebActivityQualityEnforcementWarning";
-    public static final String UNIFIED_CREDENTIAL_MANAGER_DRY_RUN =
-            "UnifiedCredentialManagerDryRun";
     public static final String UNIFIED_PASSWORD_MANAGER_ANDROID = "UnifiedPasswordManagerAndroid";
     public static final String UNIFIED_PASSWORD_MANAGER_ANDROID_BRANDING =
             "UnifiedPasswordManagerAndroidBranding";
     public static final String UNIFIED_PASSWORD_MANAGER_ERROR_MESSAGES =
             "UnifiedPasswordManagerErrorMessages";
     public static final String UPCOMING_SHARING_FEATURES = "UpcomingSharingFeatures";
-    public static final String UPDATE_NOTIFICATION_IMMEDIATE_SHOW_OPTION =
-            "UpdateNotificationScheduleServiceImmediateShowOption";
     public static final String USE_CHIME_ANDROID_SDK = "UseChimeAndroidSdk";
-    public static final String USE_CLIENT_CONFIG_IPH = "UseClientConfigIPH";
     public static final String USE_LIBUNWINDSTACK_NATIVE_UNWINDER_ANDROID =
             "UseLibunwindstackNativeUnwinderAndroid";
     public static final String VIDEO_TUTORIALS = "VideoTutorials";
@@ -560,8 +523,6 @@
     public static final String VOICE_SEARCH_AUDIO_CAPTURE_POLICY = "VoiceSearchAudioCapturePolicy";
     public static final String WEBNOTES_STYLIZE = "WebNotesStylize";
     public static final String WEB_APK_ALLOW_ICON_UPDATA = "WebApkAllowIconUpdate";
-    public static final String WEB_APK_INSTALL_COMPLETE_NOTIFICATION =
-            "WebApkInstallCompleteNotification";
     public static final String WEB_APK_INSTALL_SERVICE = "WebApkInstallService";
     public static final String WEB_APK_TRAMPOLINE_ON_INITIAL_INTENT =
             "WebApkTrampolineOnInitialIntent";
@@ -682,8 +643,6 @@
             new CachedFlag(TAB_GROUPS_FOR_TABLETS, true);
     public static final CachedFlag sTabSelectionEditorV2 =
             new CachedFlag(TAB_SELECTION_EDITOR_V2, false);
-    public static final CachedFlag sTabStripImprovements =
-            new CachedFlag(TAB_STRIP_IMPROVEMENTS, true);
     public static final CachedFlag sTabStripRedesign = new CachedFlag(TAB_STRIP_REDESIGN, false);
     public static final CachedFlag sTabToGTSAnimation = new CachedFlag(TAB_TO_GTS_ANIMATION, true);
     public static final CachedFlag sTestDefaultDisabled =
diff --git a/chrome/browser/resources/chromeos/parent_access/BUILD.gn b/chrome/browser/resources/chromeos/parent_access/BUILD.gn
index f8e8645..52667156 100644
--- a/chrome/browser/resources/chromeos/parent_access/BUILD.gn
+++ b/chrome/browser/resources/chromeos/parent_access/BUILD.gn
@@ -26,7 +26,9 @@
     ":parent_access_screen",
     ":parent_access_ui",
     ":parent_access_ui_handler",
+    ":utils",
     ":webview_manager",
+    "flows:extension_approvals_before",
     "flows:extension_approvals_disabled",
     "flows:local_web_approvals_after",
   ]
@@ -108,6 +110,9 @@
 js_library("parent_access_screen") {
 }
 
+js_library("utils") {
+}
+
 html_to_js("web_components") {
   js_files = [
     "parent_access_after.js",
diff --git a/chrome/browser/resources/chromeos/parent_access/flows/BUILD.gn b/chrome/browser/resources/chromeos/parent_access/flows/BUILD.gn
index e65d9a5..82257676 100644
--- a/chrome/browser/resources/chromeos/parent_access/flows/BUILD.gn
+++ b/chrome/browser/resources/chromeos/parent_access/flows/BUILD.gn
@@ -5,6 +5,14 @@
 import("//third_party/closure_compiler/compile_js.gni")
 import("//tools/polymer/html_to_js.gni")
 
+js_library("extension_approvals_before") {
+  deps = [
+    "//ash/webui/common/resources:i18n_behavior",
+    "//chrome/browser/ui/webui/ash/parent_access:mojo_bindings_webui_js",
+    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+  ]
+}
+
 js_library("extension_approvals_disabled") {
   deps = [
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
@@ -21,6 +29,7 @@
 
 html_to_js("web_components") {
   js_files = [
+    "extension_approvals_before.js",
     "extension_approvals_disabled.js",
     "local_web_approvals_after.js",
   ]
diff --git a/chrome/browser/resources/chromeos/parent_access/flows/extension_approvals_before.html b/chrome/browser/resources/chromeos/parent_access/flows/extension_approvals_before.html
new file mode 100644
index 0000000..21eacb0
--- /dev/null
+++ b/chrome/browser/resources/chromeos/parent_access/flows/extension_approvals_before.html
@@ -0,0 +1,50 @@
+<style>
+  #before-screen-content {
+    color: var(--cros-color-primary);
+    display: flex;
+    flex-direction: column;
+  }
+
+  #title {
+    font-family: var(--cros-font-family-google-sans);
+    font-size: 24px;
+    font-weight: 500;
+    margin-bottom: 0;
+    margin-top: 16px;
+  }
+
+  #subtitle {
+    align-items: center;
+    color: var(--cros-color-secondary);
+    display: flex;
+    font: var(--cros-body-1-font);
+    margin-top: 16px;
+  }
+
+  #extension-title {
+    align-items: center;
+    display: flex;
+    margin-top: 24px;
+  }
+
+  #icon {
+    height: 32px;
+    margin-inline-end: 16px;
+    width: 32px;
+  }
+
+  #extension-name {
+    font-family: var(--cros-font-family-google-sans);
+    font-size: 16px;
+    font-weight: 500;
+  }
+</style>
+
+<div id="before-screen-content">
+  <h2 id="title">[[title]]</h2>
+  <div id="subtitle">[[subtitle]]</div>
+  <div id="extension-title">
+    <img id="icon" src="[[extensionIconSrc]]" alt=""></img>
+    <div id="extension-name">[[extensionName]]</div>
+  </div>
+</div>
diff --git a/chrome/browser/resources/chromeos/parent_access/flows/extension_approvals_before.js b/chrome/browser/resources/chromeos/parent_access/flows/extension_approvals_before.js
new file mode 100644
index 0000000..a57e536
--- /dev/null
+++ b/chrome/browser/resources/chromeos/parent_access/flows/extension_approvals_before.js
@@ -0,0 +1,100 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import {I18nBehavior, I18nBehaviorInterface} from 'chrome://resources/ash/common/i18n_behavior.js';
+import {html, mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {ExtensionApprovalsParams_ExtensionApprovalType} from '../parent_access_ui.mojom-webui.js';
+import {getParentAccessParams} from '../parent_access_ui_handler.js';
+import {decodeMojoString16, getBase64EncodedSrcForPng} from '../utils.js';
+
+/**
+ * @constructor
+ * @extends {PolymerElement}
+ * @implements {I18nBehaviorInterface}
+ */
+const ExtensionApprovalsBeforeBase =
+    mixinBehaviors([I18nBehavior], PolymerElement);
+
+export class ExtensionApprovalsBefore extends ExtensionApprovalsBeforeBase {
+  static get is() {
+    return 'extension-approvals-before';
+  }
+
+  static get template() {
+    return html`{__html_template__}`;
+  }
+
+  static get properties() {
+    return {
+      title: {type: String},
+      subtitle: {type: String},
+      extensionIconSrc: {type: String},
+      extensionName: {type: String},
+    };
+  }
+
+  constructor() {
+    super();
+    /**
+     * Title of the screen shown to the user.
+     * @protected {string}
+     */
+    this.title = '';
+    /**
+     * Subtitle of the screen shown to the user.
+     * @protected {string}
+     */
+    this.subtitle = '';
+    /**
+     * The extension icon, represented as a Base64 encoded
+     * string.
+     * @protected {string}
+     */
+    this.extensionIconSrc = '';
+    /**
+     * Display name of the extension.
+     * @protected {string}
+     */
+    this.extensionName = '';
+  }
+
+  /** @override */
+  ready() {
+    super.ready();
+    this.configureWithParams_();
+  }
+
+  /** @private */
+  async configureWithParams_() {
+    const response = await getParentAccessParams();
+    const params = response.params.flowTypeParams.extensionApprovalsParams;
+    if (params) {
+      this.renderDetails_(params);
+    } else {
+      console.error('Failed to fetch extension approvals params.');
+    }
+  }
+
+  /** @private */
+  renderDetails_(params) {
+    this.extensionIconSrc = getBase64EncodedSrcForPng(params.iconPngBytes);
+    this.extensionName = decodeMojoString16(params.extensionName);
+
+    switch (params.approvalType) {
+      case ExtensionApprovalsParams_ExtensionApprovalType.kAdd:
+        this.title = this.i18n('extensionApprovalsAddExtensionBeforeTitle');
+        this.subtitle =
+            this.i18n('extensionApprovalsAddExtensionBeforeSubtitle');
+        break;
+      case ExtensionApprovalsParams_ExtensionApprovalType.kEnable:
+        this.title = this.i18n('extensionApprovalsEnableExtensionBeforeTitle');
+        this.subtitle =
+            this.i18n('extensionApprovalsEnableExtensionBeforeSubtitle');
+        break;
+    }
+  }
+}
+
+customElements.define(ExtensionApprovalsBefore.is, ExtensionApprovalsBefore);
diff --git a/chrome/browser/resources/chromeos/parent_access/flows/local_web_approvals_after.js b/chrome/browser/resources/chromeos/parent_access/flows/local_web_approvals_after.js
index ac62af04..3cba011e 100644
--- a/chrome/browser/resources/chromeos/parent_access/flows/local_web_approvals_after.js
+++ b/chrome/browser/resources/chromeos/parent_access/flows/local_web_approvals_after.js
@@ -5,11 +5,11 @@
 import 'chrome://resources/cr_elements/cr_shared_vars.css.js';
 
 import {I18nBehavior, I18nBehaviorInterface} from 'chrome://resources/ash/common/i18n_behavior.js';
-import {String16} from 'chrome://resources/mojo/mojo/public/mojom/base/string16.mojom-webui.js';
 import {html, mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {WebApprovalsParams} from '../parent_access_ui.mojom-webui.js';
 import {getParentAccessParams} from '../parent_access_ui_handler.js';
+import {decodeMojoString16, getBase64EncodedSrcForPng} from '../utils.js';
 
 /**
  * @constructor
@@ -79,19 +79,9 @@
    * @private
    */
   renderDetails_(params) {
-    this.childName = this.decodeMojoString16_(params.childDisplayName);
+    this.childName = decodeMojoString16(params.childDisplayName);
     this.url = params.url.url;
-    // Convert the PNG bytes to a Base64 encoded string.
-    const favicon = btoa(String.fromCharCode(...params.faviconPngBytes));
-    this.favicon = 'data:image/png;base64,' + favicon;
-  }
-
-  /**
-   * @param {!String16} str
-   * @private
-   */
-  decodeMojoString16_(str) {
-    return str.data.map((ch) => String.fromCodePoint(ch)).join('');
+    this.favicon = getBase64EncodedSrcForPng(params.faviconPngBytes);
   }
 }
 
diff --git a/chrome/browser/resources/chromeos/parent_access/parent_access_before.html b/chrome/browser/resources/chromeos/parent_access/parent_access_before.html
index 3fed7535..493eac7 100644
--- a/chrome/browser/resources/chromeos/parent_access/parent_access_before.html
+++ b/chrome/browser/resources/chromeos/parent_access/parent_access_before.html
@@ -33,7 +33,7 @@
         media="(prefers-color-scheme: dark)">
     <img src="images/request_approval.svg" id="illustration">
   </picture>
-  <div id="befpre-screen-body" aria-live="polite"></div>
+  <div id="before-screen-body" aria-live="polite"></div>
   <div id="before-screen-buttons">
     <cr-button class="action-button" on-click="showParentAccessUI_">
       $i18n{askInPersonButtonText}
diff --git a/chrome/browser/resources/chromeos/parent_access/parent_access_before.js b/chrome/browser/resources/chromeos/parent_access/parent_access_before.js
index a27ffe8..1e5cfc8a 100644
--- a/chrome/browser/resources/chromeos/parent_access/parent_access_before.js
+++ b/chrome/browser/resources/chromeos/parent_access/parent_access_before.js
@@ -6,7 +6,10 @@
 
 import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
+import {ExtensionApprovalsBefore} from './flows/extension_approvals_before.js';
 import {ParentAccessScreenInterface} from './parent_access_screen.js';
+import {ParentAccessParams_FlowType} from './parent_access_ui.mojom-webui.js';
+import {getParentAccessParams} from './parent_access_ui_handler.js';
 
 /** @implements {ParentAccessScreenInterface} */
 class ParentAccessBefore extends PolymerElement {
@@ -30,7 +33,15 @@
 
   /** @override */
   async renderFlowSpecificContent() {
-    // TODO(b/262448127): Implement flow specific content.
+    const response = await getParentAccessParams();
+    switch (response.params.flowType) {
+      case ParentAccessParams_FlowType.kExtensionAccess:
+        this.shadowRoot.querySelector('#before-screen-body')
+            .appendChild(new ExtensionApprovalsBefore());
+        return;
+      default:
+        return;
+    }
   }
 
   /** @private */
diff --git a/chrome/browser/resources/chromeos/parent_access/utils.js b/chrome/browser/resources/chromeos/parent_access/utils.js
new file mode 100644
index 0000000..0e456a5
--- /dev/null
+++ b/chrome/browser/resources/chromeos/parent_access/utils.js
@@ -0,0 +1,22 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import {String16} from 'chrome://resources/mojo/mojo/public/mojom/base/string16.mojom-webui.js';
+
+/**
+ * @param {!String16} str
+ * @return {string}
+ */
+export function decodeMojoString16(str) {
+  return str.data.map((ch) => String.fromCodePoint(ch)).join('');
+}
+
+/**
+ * @param {!Array<number>} pngBytes
+ * @return {string}
+ */
+export function getBase64EncodedSrcForPng(pngBytes) {
+  const image = btoa(String.fromCharCode(...pngBytes));
+  return 'data:image/png;base64,' + image;
+}
diff --git a/chrome/browser/resources/settings/chromeos/os_settings_search_box/os_settings_search_box.html b/chrome/browser/resources/settings/chromeos/os_settings_search_box/os_settings_search_box.html
index f855eca..571c32a75 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings_search_box/os_settings_search_box.html
+++ b/chrome/browser/resources/settings/chromeos/os_settings_search_box/os_settings_search_box.html
@@ -20,7 +20,7 @@
   }
 
   :host-context(body.jelly-enabled):host {
-    --cr-toolbar-search-field-background: var(--cros-sys-input_field_dark);
+    --cr-toolbar-search-field-background: var(--cros-sys-input_field_on_base);
   }
 
   @media (prefers-color-scheme: dark) {
diff --git a/chrome/browser/ssl/https_only_mode_browsertest.cc b/chrome/browser/ssl/https_only_mode_browsertest.cc
index 3aabdc63..d2051a19 100644
--- a/chrome/browser/ssl/https_only_mode_browsertest.cc
+++ b/chrome/browser/ssl/https_only_mode_browsertest.cc
@@ -56,7 +56,8 @@
   void SetUp() override {
     feature_list_.InitWithFeatures(
         /*enabled_features=*/{features::kHttpsOnlyMode},
-        /*disabled_features=*/{features::kHttpsFirstModeV2});
+        /*disabled_features=*/{features::kHttpsFirstModeV2,
+                               features::kHttpsUpgrades});
     InProcessBrowserTest::SetUp();
   }
 
diff --git a/chrome/browser/ssl/https_only_mode_upgrade_interceptor.cc b/chrome/browser/ssl/https_only_mode_upgrade_interceptor.cc
index 12be8068..b9db43a 100644
--- a/chrome/browser/ssl/https_only_mode_upgrade_interceptor.cc
+++ b/chrome/browser/ssl/https_only_mode_upgrade_interceptor.cc
@@ -111,9 +111,8 @@
   }
 
   // Check if the hostname is in the enterprise policy HTTP allowlist.
-  PrefService* prefs = profile->GetPrefs();
-  if (IsHostnameInAllowlist(tentative_resource_request.url,
-                            prefs->GetList(prefs::kHttpAllowlist))) {
+  if (IsHostnameInHttpAllowlist(tentative_resource_request.url,
+                                profile->GetPrefs())) {
     std::move(callback).Run({});
     return;
   }
diff --git a/chrome/browser/ssl/https_upgrades_browsertest.cc b/chrome/browser/ssl/https_upgrades_browsertest.cc
index 4112035..0ca416f 100644
--- a/chrome/browser/ssl/https_upgrades_browsertest.cc
+++ b/chrome/browser/ssl/https_upgrades_browsertest.cc
@@ -80,6 +80,10 @@
           /*enabled_features=*/{features::kHttpsFirstModeV2,
                                 features::kHttpsUpgrades},
           /*disabled_features=*/{});
+    } else if (GetParam() == HttpsUpgradesTestType::kNeither) {
+      feature_list_.InitWithFeatures(
+          /*enabled_features=*/{features::kHttpsFirstModeV2},
+          /*disabled_features=*/{features::kHttpsUpgrades});
     } else {
       feature_list_.InitAndEnableFeature(features::kHttpsFirstModeV2);
     }
diff --git a/chrome/browser/ssl/https_upgrades_interceptor.cc b/chrome/browser/ssl/https_upgrades_interceptor.cc
index 3c3234d..685d3b40 100644
--- a/chrome/browser/ssl/https_upgrades_interceptor.cc
+++ b/chrome/browser/ssl/https_upgrades_interceptor.cc
@@ -314,8 +314,8 @@
   // Don't upgrade navigation if it is allowlisted.
   // First, check the enterprise policy HTTP allowlist.
   PrefService* prefs = profile->GetPrefs();
-  if (IsHostnameInAllowlist(tentative_resource_request.url,
-                            prefs->GetList(prefs::kHttpAllowlist))) {
+  if (IsHostnameInHttpAllowlist(tentative_resource_request.url,
+                                profile->GetPrefs())) {
     RecordNavigationRequestSecurityLevel(
         NavigationRequestSecurityLevel::kAllowlisted);
     std::move(callback).Run({});
diff --git a/chrome/browser/ssl/https_upgrades_util.cc b/chrome/browser/ssl/https_upgrades_util.cc
index b20c4eb9..dbd2d21a 100644
--- a/chrome/browser/ssl/https_upgrades_util.cc
+++ b/chrome/browser/ssl/https_upgrades_util.cc
@@ -5,11 +5,15 @@
 #include "chrome/browser/ssl/https_upgrades_util.h"
 
 #include "base/values.h"
+#include "chrome/common/pref_names.h"
 #include "components/content_settings/core/common/content_settings_pattern.h"
+#include "components/prefs/pref_service.h"
 #include "url/gurl.h"
 
-bool IsHostnameInAllowlist(const GURL& url,
-                           const base::Value::List& allowed_hosts) {
+bool IsHostnameInHttpAllowlist(const GURL& url, PrefService* prefs) {
+  const base::Value::List& allowed_hosts =
+      prefs->GetList(prefs::kHttpAllowlist);
+
   // Though this is not technically a Content Setting, ContentSettingsPattern
   // aligns better than URLMatcher with the rules from
   // https://chromeenterprise.google/policies/url-patterns/.
@@ -28,3 +32,31 @@
   }
   return false;
 }
+
+void AllowHttpForHostnamesForTesting(const std::vector<std::string>& hostnames,
+                                     PrefService* prefs) {
+  DCHECK(prefs->GetList(prefs::kHttpAllowlist).empty());
+
+  base::Value::List allowed_hosts;
+  for (const std::string& hostname : hostnames) {
+    allowed_hosts.Append(hostname);
+  }
+  prefs->SetList(prefs::kHttpAllowlist, std::move(allowed_hosts));
+}
+
+void ClearHttpAllowlistForHostnamesForTesting(PrefService* prefs) {
+  base::Value::List empty_list;
+  prefs->SetList(prefs::kHttpAllowlist, std::move(empty_list));
+}
+
+ScopedAllowHttpForHostnamesForTesting::ScopedAllowHttpForHostnamesForTesting(
+    const std::vector<std::string>& hostnames,
+    PrefService* prefs)
+    : prefs_(prefs) {
+  AllowHttpForHostnamesForTesting(hostnames, prefs);
+}
+
+ScopedAllowHttpForHostnamesForTesting::
+    ~ScopedAllowHttpForHostnamesForTesting() {
+  ClearHttpAllowlistForHostnamesForTesting(prefs_);
+}
diff --git a/chrome/browser/ssl/https_upgrades_util.h b/chrome/browser/ssl/https_upgrades_util.h
index 06db64c..2798513 100644
--- a/chrome/browser/ssl/https_upgrades_util.h
+++ b/chrome/browser/ssl/https_upgrades_util.h
@@ -5,9 +5,12 @@
 #ifndef CHROME_BROWSER_SSL_HTTPS_UPGRADES_UTIL_H_
 #define CHROME_BROWSER_SSL_HTTPS_UPGRADES_UTIL_H_
 
+#include "base/memory/stack_allocated.h"
 #include "base/values.h"
 #include "url/gurl.h"
 
+class PrefService;
+
 // Helper for applying the HttpAllowlist enterprise policy. Checks if the
 // hostname of `url` matches any of the hostnames or hostname patterns in the
 // `allowed_hosts` list. Does not allow blanket host wildcards (i.e., "*" which
@@ -15,7 +18,34 @@
 // "[*.]example.com"). Entries in `allowed_hosts` should follow the rules in
 // https://chromeenterprise.google/policies/url-patterns/ (or they'll be
 // ignored).
-bool IsHostnameInAllowlist(const GURL& url,
-                           const base::Value::List& allowed_hosts);
+bool IsHostnameInHttpAllowlist(const GURL& url, PrefService* prefs);
+
+// Adds `hostnames` to the HttpAllowlist enterprise policy for testing.
+// Expects the allowlist to be empty before updating it.
+void AllowHttpForHostnamesForTesting(const std::vector<std::string>& hostnames,
+                                     PrefService* prefs);
+
+// Clears HttpAllowlist enterprise policy for testing.
+void ClearHttpAllowlistForHostnamesForTesting(PrefService* prefs);
+
+// An instance of this class adds `hostnames` to the HttpAllowlist enterprise
+// policy for testing and clears the allowlist when it goes out of scope.
+class ScopedAllowHttpForHostnamesForTesting {
+  STACK_ALLOCATED();
+
+ public:
+  explicit ScopedAllowHttpForHostnamesForTesting(
+      const std::vector<std::string>& hostnames,
+      PrefService* prefs);
+  ScopedAllowHttpForHostnamesForTesting(
+      const ScopedAllowHttpForHostnamesForTesting&) = delete;
+  ScopedAllowHttpForHostnamesForTesting& operator=(
+      const ScopedAllowHttpForHostnamesForTesting&) = delete;
+
+  ~ScopedAllowHttpForHostnamesForTesting();
+
+ private:
+  PrefService* prefs_;
+};
 
 #endif  // CHROME_BROWSER_SSL_HTTPS_UPGRADES_UTIL_H_
diff --git a/chrome/browser/ssl/security_state_tab_helper_browsertest.cc b/chrome/browser/ssl/security_state_tab_helper_browsertest.cc
index 3db0dd3..425aca3 100644
--- a/chrome/browser/ssl/security_state_tab_helper_browsertest.cc
+++ b/chrome/browser/ssl/security_state_tab_helper_browsertest.cc
@@ -31,6 +31,7 @@
 #include "chrome/browser/profiles/profile_window.h"
 #include "chrome/browser/safe_browsing/chrome_password_protection_service.h"
 #include "chrome/browser/ssl/cert_verifier_browser_test.h"
+#include "chrome/browser/ssl/https_upgrades_util.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/browser_finder.h"
@@ -1363,6 +1364,10 @@
       SecurityStateTabHelper::FromWebContents(contents);
   ASSERT_TRUE(helper);
 
+  // Disable HTTPS upgrades on nonexistent.test for this test to work.
+  ScopedAllowHttpForHostnamesForTesting scoped_allow_http(
+      {"nonexistent.test"}, browser()->profile()->GetPrefs());
+
   // Navigate to a URL that results in an error page. Even though the displayed
   // URL is http://, there shouldn't be a Not Secure warning because the browser
   // hasn't really navigated to an http:// page.
diff --git a/chrome/browser/ssl/ssl_browsertest.cc b/chrome/browser/ssl/ssl_browsertest.cc
index 81a37efe..e4199ae 100644
--- a/chrome/browser/ssl/ssl_browsertest.cc
+++ b/chrome/browser/ssl/ssl_browsertest.cc
@@ -55,6 +55,7 @@
 #include "chrome/browser/ssl/cert_verifier_browser_test.h"
 #include "chrome/browser/ssl/certificate_reporting_test_utils.h"
 #include "chrome/browser/ssl/chrome_security_blocking_page_factory.h"
+#include "chrome/browser/ssl/https_upgrades_util.h"
 #include "chrome/browser/ssl/security_state_tab_helper.h"
 #include "chrome/browser/ssl/ssl_browsertest_util.h"
 #include "chrome/browser/ssl/ssl_error_controller_client.h"
@@ -109,6 +110,7 @@
 #include "components/security_interstitials/content/ssl_error_handler.h"
 #include "components/security_interstitials/content/stateful_ssl_host_state_delegate.h"
 #include "components/security_interstitials/core/controller_client.h"
+#include "components/security_interstitials/core/https_only_mode_metrics.h"
 #include "components/security_interstitials/core/metrics_helper.h"
 #include "components/security_interstitials/core/pref_names.h"
 #include "components/security_state/core/security_state.h"
@@ -6451,16 +6453,24 @@
   ASSERT_TRUE(embedded_test_server()->Start());
   ASSERT_TRUE(https_server_.Start());
 
+  // This test posts to does-not-exist.test. Disable HTTPS upgrades on this
+  // hostname for the test to work.
+  // TODO(crbug.com/1394910): Remove the allowlist entry.
+  ScopedAllowHttpForHostnamesForTesting scoped_allow_http(
+      {"does-not-exist.test"}, browser()->profile()->GetPrefs());
+
   std::string replacement_path = GetFilePathWithHostAndPortReplacement(
       "/ssl/page_displays_form_redirects_301_insecure.html",
       embedded_test_server()->host_port_pair());
 
   ASSERT_TRUE(ui_test_utils::NavigateToURL(
       browser(), https_server_.GetURL(replacement_path)));
+
   WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
   content::TestNavigationObserver nav_observer(tab, 1);
   ASSERT_TRUE(content::ExecuteScript(tab, "submitForm();"));
   nav_observer.Wait();
+
   security_interstitials::SecurityInterstitialTabHelper* helper =
       security_interstitials::SecurityInterstitialTabHelper::FromWebContents(
           tab);
@@ -6487,6 +6497,12 @@
   ASSERT_TRUE(embedded_test_server()->Start());
   ASSERT_TRUE(https_server_.Start());
 
+  // This test posts to does-not-exist.test. Disable HTTPS upgrades on this
+  // hostname for the test to work.
+  // TODO(crbug.com/1394910): Remove the allowlist entry.
+  ScopedAllowHttpForHostnamesForTesting scoped_allow_http(
+      {"does-not-exist.test"}, browser()->profile()->GetPrefs());
+
   std::string replacement_path = GetFilePathWithHostAndPortReplacement(
       "/ssl/page_displays_form_redirects_302_insecure.html",
       embedded_test_server()->host_port_pair());
@@ -6551,6 +6567,12 @@
       base::BindRepeating(&FormActionHTTPRedirectHandler, &https_server_));
   ASSERT_TRUE(https_server_.Start());
 
+  // This test redirects to example.org. Disable HTTPS upgrades on this
+  // hostname for the test to work.
+  // TODO(crbug.com/1394910): Remove the allowlist entry.
+  ScopedAllowHttpForHostnamesForTesting scoped_allow_http(
+      {"example.org"}, browser()->profile()->GetPrefs());
+
   std::string replacement_path = GetFilePathWithHostAndPortReplacement(
       "/ssl/page_displays_form_redirects_insecure_get.html",
       embedded_test_server()->host_port_pair());
diff --git a/chrome/browser/ui/ash/wallpaper_controller_client_impl.cc b/chrome/browser/ui/ash/wallpaper_controller_client_impl.cc
index 3d605f5..70fa500 100644
--- a/chrome/browser/ui/ash/wallpaper_controller_client_impl.cc
+++ b/chrome/browser/ui/ash/wallpaper_controller_client_impl.cc
@@ -574,7 +574,7 @@
     Profile* profile = ProfileHelper::Get()->GetProfileByAccountId(account_id);
     google_photos_photos_fetchers_.insert(
         {account_id,
-         std::make_unique<wallpaper_handlers::GooglePhotosPhotosFetcher>(
+         wallpaper_fetcher_delegate_->CreateGooglePhotosPhotosFetcher(
              profile)});
   }
   auto fetched_callback =
@@ -595,7 +595,7 @@
     Profile* profile = ProfileHelper::Get()->GetProfileByAccountId(account_id);
     google_photos_photos_fetchers_.insert(
         {account_id,
-         std::make_unique<wallpaper_handlers::GooglePhotosPhotosFetcher>(
+         wallpaper_fetcher_delegate_->CreateGooglePhotosPhotosFetcher(
              profile)});
   }
   auto fetched_callback = base::BindOnce(
diff --git a/chrome/browser/ui/bluetooth/chrome_bluetooth_chooser_controller.cc b/chrome/browser/ui/bluetooth/chrome_bluetooth_chooser_controller.cc
index 2a61470..757e05f 100644
--- a/chrome/browser/ui/bluetooth/chrome_bluetooth_chooser_controller.cc
+++ b/chrome/browser/ui/bluetooth/chrome_bluetooth_chooser_controller.cc
@@ -53,10 +53,7 @@
     : permissions::BluetoothChooserController(
           owner,
           event_handler,
-          CreateExtensionAwareChooserTitle(
-              owner,
-              IDS_BLUETOOTH_DEVICE_CHOOSER_PROMPT_ORIGIN,
-              IDS_BLUETOOTH_DEVICE_CHOOSER_PROMPT_EXTENSION_NAME)) {}
+          CreateChooserTitle(owner, IDS_BLUETOOTH_DEVICE_CHOOSER_PROMPT)) {}
 
 ChromeBluetoothChooserController::~ChromeBluetoothChooserController() = default;
 
diff --git a/chrome/browser/ui/color/material_omnibox_color_mixer.cc b/chrome/browser/ui/color/material_omnibox_color_mixer.cc
index f1b9e25..9235f70 100644
--- a/chrome/browser/ui/color/material_omnibox_color_mixer.cc
+++ b/chrome/browser/ui/color/material_omnibox_color_mixer.cc
@@ -32,5 +32,4 @@
   mixer[kColorToolbarBackgroundSubtleEmphasisHovered] =
       ui::GetResultingPaintColor(ui::kColorSysStateHoverBrightBlendProtection,
                                  kColorToolbarBackgroundSubtleEmphasis);
-  mixer[kColorOmniboxText] = {ui::kColorSysOnSurface};
 }
diff --git a/chrome/browser/ui/color/omnibox_color_mixer.cc b/chrome/browser/ui/color/omnibox_color_mixer.cc
index 8b23acf..e9adddc 100644
--- a/chrome/browser/ui/color/omnibox_color_mixer.cc
+++ b/chrome/browser/ui/color/omnibox_color_mixer.cc
@@ -4,9 +4,13 @@
 
 #include "chrome/browser/ui/color/omnibox_color_mixer.h"
 
+#include "base/feature_list.h"
+#include "base/strings/string_number_conversions.h"
 #include "chrome/browser/ui/color/chrome_color_id.h"
 #include "chrome/browser/ui/color/chrome_color_provider_utils.h"
+#include "components/omnibox/common/omnibox_features.h"
 #include "third_party/skia/include/core/SkColor.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/color/color_id.h"
 #include "ui/color/color_mixer.h"
 #include "ui/color/color_provider.h"
@@ -20,6 +24,75 @@
 // The contrast for omnibox colors in high contrast mode.
 constexpr float kOmniboxHighContrastRatio = 6.0f;
 
+// Apply updates to the Omnibox text color tokens per GM3 spec.
+void ApplyGM3OmniboxTextColor(ui::ColorMixer& mixer,
+                              const ui::ColorProviderManager::Key& key) {
+  const bool gm3_text_color_enabled =
+      features::IsChromeRefresh2023() ||
+      base::FeatureList::IsEnabled(omnibox::kOmniboxSteadyStateTextColor);
+
+  // Apply omnibox text color updates only to non-themed clients.
+  if (!gm3_text_color_enabled || key.custom_theme) {
+    return;
+  }
+
+  // Retrieve GM3 omnibox text color params (Dark Mode).
+  const std::string dark_text_color_param =
+      omnibox::kOmniboxTextColorDarkMode.Get();
+  const std::string dark_text_color_dimmed_param =
+      omnibox::kOmniboxTextColorDimmedDarkMode.Get();
+
+  // Retrieve GM3 omnibox text color params (Light Mode).
+  const std::string light_text_color_param =
+      omnibox::kOmniboxTextColorLightMode.Get();
+  const std::string light_text_color_dimmed_param =
+      omnibox::kOmniboxTextColorDimmedLightMode.Get();
+
+  const auto string_to_skcolor = [](const std::string& rgb_str,
+                                    SkColor* result) {
+    // Valid color strings are of the form 0xRRGGBB or 0xAARRGGBB.
+    const bool valid = result && (rgb_str.size() == 8 || rgb_str.size() == 10);
+    if (!valid) {
+      return false;
+    }
+
+    uint32_t parsed = 0;
+    const bool success = base::HexStringToUInt(rgb_str, &parsed);
+    if (success) {
+      *result = SkColorSetA(static_cast<SkColor>(parsed), SK_AlphaOPAQUE);
+    }
+    return success;
+  };
+
+  SkColor dark_text_color = 0;
+  SkColor dark_text_color_dimmed = 0;
+
+  SkColor light_text_color = 0;
+  SkColor light_text_color_dimmed = 0;
+
+  const bool success =
+      string_to_skcolor(dark_text_color_param, &dark_text_color) &&
+      string_to_skcolor(dark_text_color_dimmed_param,
+                        &dark_text_color_dimmed) &&
+      string_to_skcolor(light_text_color_param, &light_text_color) &&
+      string_to_skcolor(light_text_color_dimmed_param,
+                        &light_text_color_dimmed);
+
+  if (!success) {
+    return;
+  }
+
+  const auto selected_text_color = ui::SelectBasedOnDarkInput(
+      kColorToolbar, dark_text_color, light_text_color);
+
+  mixer[kColorOmniboxText] = {selected_text_color};
+
+  const auto selected_text_color_dimmed = ui::SelectBasedOnDarkInput(
+      kColorToolbar, dark_text_color_dimmed, light_text_color_dimmed);
+
+  mixer[kColorOmniboxTextDimmed] = {selected_text_color_dimmed};
+}
+
 }  // namespace
 
 void AddOmniboxColorMixer(ui::ColorProvider* provider,
@@ -217,4 +290,7 @@
       kColorToolbar, SkColorSetRGB(0, 74, 119), SkColorSetRGB(211, 227, 253));
   mixer[kColorOmniboxAnswerIconGM3Foreground] = ui::SelectBasedOnDarkInput(
       kColorToolbar, SkColorSetRGB(194, 231, 255), SkColorSetRGB(4, 30, 73));
+
+  // Override omnibox text color per GM3 spec.
+  ApplyGM3OmniboxTextColor(mixer, key);
 }
diff --git a/chrome/browser/ui/global_media_controls/cast_device_list_host.cc b/chrome/browser/ui/global_media_controls/cast_device_list_host.cc
index fe2f3715..643bf2a 100644
--- a/chrome/browser/ui/global_media_controls/cast_device_list_host.cc
+++ b/chrome/browser/ui/global_media_controls/cast_device_list_host.cc
@@ -134,9 +134,11 @@
   } else if (sink.state == media_router::UIMediaSinkState::CONNECTED) {
     // We record stopping casting here even if we are starting casting, because
     // the existing session is being stopped and replaced by a new session.
-    // TODO(crbug.com/1411139): Call RecordStopCastingMetrics() here.
+    // TODO(crbug.com/1411139): Call RecordStopCastingMetrics() here instead.
     if (sink.provider == media_router::mojom::MediaRouteProviderId::DIAL) {
       DCHECK(sink.route);
+      MediaItemUIMetrics::RecordStopCastMode(
+          media_router::MediaCastMode::PRESENTATION);
       cast_controller_->StopCasting(sink.route->media_route_id());
     } else {
       StartCasting(sink);
@@ -172,7 +174,8 @@
   if (cast_mode.value() == media_router::MediaCastMode::REMOTE_PLAYBACK) {
     media_remoting_callback_.Run();
   }
-  // TODO(crbug.com/1411139): Call RecordStartCastingMetrics() here.
+  // TODO(crbug.com/1411139): Call RecordStartCastingMetrics() here instead.
+  MediaItemUIMetrics::RecordStartCastMode(cast_mode.value());
 }
 
 void CastDeviceListHost::DestroyCastController() {
diff --git a/chrome/browser/ui/global_media_controls/media_item_ui_metrics.cc b/chrome/browser/ui/global_media_controls/media_item_ui_metrics.cc
index be2357d..f95cb0cd1 100644
--- a/chrome/browser/ui/global_media_controls/media_item_ui_metrics.cc
+++ b/chrome/browser/ui/global_media_controls/media_item_ui_metrics.cc
@@ -32,13 +32,23 @@
 
 }  // namespace
 
+void MediaItemUIMetrics::RecordStartCastMode(
+    media_router::MediaCastMode cast_mode) {
+  base::UmaHistogramEnumeration(kStartCastingModeHistogramName,
+                                GetGlobalMediaControlsCastMode(cast_mode));
+}
+void MediaItemUIMetrics::RecordStopCastMode(
+    media_router::MediaCastMode cast_mode) {
+  base::UmaHistogramEnumeration(kStopCastingModeHistogramName,
+                                GetGlobalMediaControlsCastMode(cast_mode));
+}
+
 void MediaItemUIMetrics::RecordStartCastingMetrics(
     media_router::SinkIconType sink_icon_type,
     media_router::MediaCastMode cast_mode,
     GlobalMediaControlsEntryPoint entry_point) {
   MediaRouterMetrics::RecordMediaSinkTypeForGlobalMediaControls(sink_icon_type);
-  base::UmaHistogramEnumeration(kStartCastingModeHistogramName,
-                                GetGlobalMediaControlsCastMode(cast_mode));
+  RecordStartCastMode(cast_mode);
 
   GlobalMediaControlsCastActionAndEntryPoint action;
   switch (entry_point) {
@@ -59,8 +69,7 @@
 void MediaItemUIMetrics::RecordStopCastingMetrics(
     media_router::MediaCastMode cast_mode,
     GlobalMediaControlsEntryPoint entry_point) {
-  base::UmaHistogramEnumeration(kStopCastingModeHistogramName,
-                                GetGlobalMediaControlsCastMode(cast_mode));
+  RecordStopCastMode(cast_mode);
 
   GlobalMediaControlsCastActionAndEntryPoint action;
   switch (entry_point) {
diff --git a/chrome/browser/ui/global_media_controls/media_item_ui_metrics.h b/chrome/browser/ui/global_media_controls/media_item_ui_metrics.h
index 126d0a3..4bd73ff 100644
--- a/chrome/browser/ui/global_media_controls/media_item_ui_metrics.h
+++ b/chrome/browser/ui/global_media_controls/media_item_ui_metrics.h
@@ -33,6 +33,8 @@
 
 class MediaItemUIMetrics {
  public:
+  static void RecordStartCastMode(media_router::MediaCastMode cast_mode);
+  static void RecordStopCastMode(media_router::MediaCastMode cast_mode);
   static void RecordStartCastingMetrics(
       media_router::SinkIconType sink_icon_type,
       media_router::MediaCastMode cast_mode,
diff --git a/chrome/browser/ui/hid/hid_chooser_controller.cc b/chrome/browser/ui/hid/hid_chooser_controller.cc
index 21bbc09..bb0e570 100644
--- a/chrome/browser/ui/hid/hid_chooser_controller.cc
+++ b/chrome/browser/ui/hid/hid_chooser_controller.cc
@@ -83,10 +83,8 @@
     std::vector<blink::mojom::HidDeviceFilterPtr> filters,
     std::vector<blink::mojom::HidDeviceFilterPtr> exclusion_filters,
     content::HidChooser::Callback callback)
-    : ChooserController(CreateExtensionAwareChooserTitle(
-          render_frame_host,
-          IDS_HID_CHOOSER_PROMPT_ORIGIN,
-          IDS_HID_CHOOSER_PROMPT_EXTENSION_NAME)),
+    : ChooserController(
+          CreateChooserTitle(render_frame_host, IDS_HID_CHOOSER_PROMPT)),
       filters_(std::move(filters)),
       exclusion_filters_(std::move(exclusion_filters)),
       callback_(std::move(callback)),
diff --git a/chrome/browser/ui/layout_constants.cc b/chrome/browser/ui/layout_constants.cc
index 68707652..e58fb77 100644
--- a/chrome/browser/ui/layout_constants.cc
+++ b/chrome/browser/ui/layout_constants.cc
@@ -46,6 +46,12 @@
       }
     case LOCATION_BAR_ICON_SIZE:
       return touch_ui ? 20 : 16;
+    case LOCATION_BAR_LEADING_ICON_SIZE:
+      return GetLayoutConstant(LOCATION_BAR_ICON_SIZE);
+    case LOCATION_BAR_TRAILING_ICON_SIZE:
+      return base::FeatureList::IsEnabled(features::kChromeRefresh2023)
+                 ? 20
+                 : GetLayoutConstant(LOCATION_BAR_ICON_SIZE);
     case TAB_AFTER_TITLE_PADDING:
       return touch_ui ? 8 : 4;
     case TAB_ALERT_INDICATOR_CAPTURE_ICON_WIDTH:
diff --git a/chrome/browser/ui/layout_constants.h b/chrome/browser/ui/layout_constants.h
index ac9506b..dc48748 100644
--- a/chrome/browser/ui/layout_constants.h
+++ b/chrome/browser/ui/layout_constants.h
@@ -44,8 +44,18 @@
   LOCATION_BAR_HEIGHT,
 
   // The size of the icons used inside the LocationBar.
+  // TODO(crbug.com/1399991): Deprecate this after the size of all location bar
+  // icons have moved to
+  // either `LOCATION_BAR_LEADING_ICON_SIZE` or
+  // `LOCATION_BAR_TRAILING_ICON_SIZE`
   LOCATION_BAR_ICON_SIZE,
 
+  // The size of the leading icons used inside the LocationBar.
+  LOCATION_BAR_LEADING_ICON_SIZE,
+
+  // The size of the trailing icons used inside the LocationBar.
+  LOCATION_BAR_TRAILING_ICON_SIZE,
+
   // The size of icons used in PageInfo bubbles.
   PAGE_INFO_ICON_SIZE,
 
diff --git a/chrome/browser/ui/serial/serial_chooser_controller.cc b/chrome/browser/ui/serial/serial_chooser_controller.cc
index 2e9fa4c2..0401bc32 100644
--- a/chrome/browser/ui/serial/serial_chooser_controller.cc
+++ b/chrome/browser/ui/serial/serial_chooser_controller.cc
@@ -27,10 +27,8 @@
     content::RenderFrameHost* render_frame_host,
     std::vector<blink::mojom::SerialPortFilterPtr> filters,
     content::SerialChooser::Callback callback)
-    : ChooserController(CreateExtensionAwareChooserTitle(
-          render_frame_host,
-          IDS_SERIAL_PORT_CHOOSER_PROMPT_ORIGIN,
-          IDS_SERIAL_PORT_CHOOSER_PROMPT_EXTENSION_NAME)),
+    : ChooserController(CreateChooserTitle(render_frame_host,
+                                           IDS_SERIAL_PORT_CHOOSER_PROMPT)),
       filters_(std::move(filters)),
       callback_(std::move(callback)),
       initiator_document_(render_frame_host->GetWeakDocumentPtr()) {
diff --git a/chrome/browser/ui/toolbar/chrome_location_bar_model_delegate.cc b/chrome/browser/ui/toolbar/chrome_location_bar_model_delegate.cc
index c63b469..ddf0644e 100644
--- a/chrome/browser/ui/toolbar/chrome_location_bar_model_delegate.cc
+++ b/chrome/browser/ui/toolbar/chrome_location_bar_model_delegate.cc
@@ -34,6 +34,7 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/url_constants.h"
 #include "extensions/common/constants.h"
+#include "ui/base/ui_base_features.h"
 
 #if !BUILDFLAG(IS_ANDROID)
 #include "components/omnibox/browser/vector_icons.h"  // nogncheck
@@ -184,7 +185,9 @@
   GetURL(&url);
 
   if (url.SchemeIs(content::kChromeUIScheme))
-    return &omnibox::kProductIcon;
+    return (features::IsChromeRefresh2023())
+               ? &omnibox::kProductChromeRefreshIcon
+               : &omnibox::kProductIcon;
 
   if (url.SchemeIs(extensions::kExtensionScheme))
     return &omnibox::kExtensionAppIcon;
diff --git a/chrome/browser/ui/views/extensions/extensions_menu_item_view.cc b/chrome/browser/ui/views/extensions/extensions_menu_item_view.cc
index 99c64538..f624a19 100644
--- a/chrome/browser/ui/views/extensions/extensions_menu_item_view.cc
+++ b/chrome/browser/ui/views/extensions/extensions_menu_item_view.cc
@@ -216,8 +216,9 @@
   const auto* const color_provider = GetColorProvider();
   const SkColor icon_color = color_provider->GetColor(kColorExtensionMenuIcon);
 
-  if (pin_button_)
+  if (pin_button_) {
     views::InkDrop::Get(pin_button_)->SetBaseColor(icon_color);
+  }
 
   SetButtonIconWithColor(
       context_menu_button_, kBrowserToolsIcon, icon_color,
@@ -241,26 +242,29 @@
 }
 
 void ExtensionMenuItemView::UpdatePinButton() {
-  if (!pin_button_)
+  if (!pin_button_) {
     return;
+  }
 
   bool is_force_pinned =
       model_ && model_->IsActionForcePinned(controller_->GetId());
   int pin_button_string_id = 0;
-  if (is_force_pinned)
+  if (is_force_pinned) {
     pin_button_string_id = IDS_EXTENSIONS_PINNED_BY_ADMIN;
-  else if (IsPinned())
+  } else if (IsPinned()) {
     pin_button_string_id = IDS_EXTENSIONS_UNPIN_FROM_TOOLBAR;
-  else
+  } else {
     pin_button_string_id = IDS_EXTENSIONS_PIN_TO_TOOLBAR;
+  }
   pin_button_->SetTooltipText(l10n_util::GetStringUTF16(pin_button_string_id));
   // Extension pinning is not available in Incognito as it leaves a trace of
   // user activity.
   pin_button_->SetEnabled(!is_force_pinned &&
                           !browser_->profile()->IsOffTheRecord());
 
-  if (!GetWidget())
+  if (!GetWidget()) {
     return;
+  }
   const auto* const color_provider = GetColorProvider();
   const SkColor icon_color = color_provider->GetColor(
       IsPinned() ? kColorExtensionMenuPinButtonIcon : kColorExtensionMenuIcon);
diff --git a/chrome/browser/ui/views/extensions/extensions_menu_site_permissions_page_view.cc b/chrome/browser/ui/views/extensions/extensions_menu_site_permissions_page_view.cc
index b4474bdd..302c2f9 100644
--- a/chrome/browser/ui/views/extensions/extensions_menu_site_permissions_page_view.cc
+++ b/chrome/browser/ui/views/extensions/extensions_menu_site_permissions_page_view.cc
@@ -41,10 +41,7 @@
 
 ExtensionsMenuSitePermissionsPageView::ExtensionsMenuSitePermissionsPageView(
     Browser* browser,
-    std::u16string extension_name,
-    ui::ImageModel extension_icon,
     extensions::ExtensionId extension_id,
-    bool is_show_requests_toggle_on,
     ExtensionsMenuNavigationHandler* navigation_handler)
     : browser_(browser), extension_id_(extension_id) {
   // TODO(crbug.com/1390952): Same stretch specification as
@@ -88,10 +85,11 @@
                       .SetCrossAxisAlignment(views::LayoutAlignment::kStretch)
                       .SetProperty(views::kFlexBehaviorKey,
                                    stretch_specification)
-                      .AddChildren(views::Builder<views::ImageView>().SetImage(
-                                       extension_icon),
-                                   views::Builder<views::Label>().SetText(
-                                       extension_name)),
+                      .AddChildren(
+                          views::Builder<views::ImageView>().CopyAddressTo(
+                              &extension_icon_),
+                          views::Builder<views::Label>().CopyAddressTo(
+                              &extension_name_)),
                   // Close button.
                   views::Builder<views::Button>(
                       views::BubbleFrameView::CreateCloseButton(
@@ -114,10 +112,6 @@
                                   IDS_EXTENSIONS_MENU_SITE_PERMISSIONS_PAGE_SHOW_REQUESTS_LABEL)),
                           views::Builder<views::ToggleButton>()
                               .CopyAddressTo(&show_requests_toggle_)
-                              .SetIsOn(is_show_requests_toggle_on)
-                              .SetAccessibleName(
-                                  GetShowRequestsToggleAccessibleName(
-                                      is_show_requests_toggle_on))
                               .SetCallback(base::BindRepeating(
                                   &ExtensionsMenuSitePermissionsPageView::
                                       OnShowRequestsTogglePressed,
@@ -130,7 +124,7 @@
                              extensions::ExtensionId extension_id) {
                             chrome::ShowExtensions(browser, extension_id);
                           },
-                          browser, extension_id),
+                          browser, extension_id_),
                       /*icon_view=*/nullptr,
                       l10n_util::GetStringUTF16(
                           IDS_EXTENSIONS_MENU_SITE_PERMISSIONS_PAGE_SETTINGS_BUTTON),
@@ -143,9 +137,21 @@
       .BuildChildren();
 }
 
+void ExtensionsMenuSitePermissionsPageView::Update(
+    const std::u16string& extension_name,
+    const ui::ImageModel& extension_icon,
+    bool is_show_requests_toggle_on) {
+  extension_icon_->SetImage(extension_icon);
+  extension_name_->SetText(extension_name);
+
+  UpdateShowRequestsToggle(is_show_requests_toggle_on);
+}
+
 void ExtensionsMenuSitePermissionsPageView::UpdateShowRequestsToggle(
     bool is_on) {
   show_requests_toggle_->SetIsOn(is_on);
+  show_requests_toggle_->SetAccessibleName(
+      GetShowRequestsToggleAccessibleName(is_on));
 }
 
 void ExtensionsMenuSitePermissionsPageView::OnShowRequestsTogglePressed() {
diff --git a/chrome/browser/ui/views/extensions/extensions_menu_site_permissions_page_view.h b/chrome/browser/ui/views/extensions/extensions_menu_site_permissions_page_view.h
index 09eee52..f341e89 100644
--- a/chrome/browser/ui/views/extensions/extensions_menu_site_permissions_page_view.h
+++ b/chrome/browser/ui/views/extensions/extensions_menu_site_permissions_page_view.h
@@ -13,6 +13,8 @@
 }  // namespace ui
 
 namespace views {
+class ImageView;
+class Label;
 class ToggleButton;
 }  // namespace views
 
@@ -25,10 +27,7 @@
 
   explicit ExtensionsMenuSitePermissionsPageView(
       Browser* browser,
-      std::u16string extension_name,
-      ui::ImageModel extension_icon,
       extensions::ExtensionId extension_id,
-      bool is_show_requests_toggle_on,
       ExtensionsMenuNavigationHandler* navigation_handler);
   ExtensionsMenuSitePermissionsPageView(
       const ExtensionsMenuSitePermissionsPageView&) = delete;
@@ -36,6 +35,11 @@
       const ExtensionsMenuSitePermissionsPageView&) = delete;
   ~ExtensionsMenuSitePermissionsPageView() override = default;
 
+  // Updates the page contents with the given parameters.
+  void Update(const std::u16string& extension_name,
+              const ui::ImageModel& extension_icon,
+              bool is_show_requests_toggle_on);
+
   // Updates `show_requests_toggle_` state to `is_on`.
   void UpdateShowRequestsToggle(bool is_on);
 
@@ -54,6 +58,8 @@
   const raw_ptr<Browser> browser_;
   extensions::ExtensionId extension_id_;
 
+  raw_ptr<views::ImageView> extension_icon_;
+  raw_ptr<views::Label> extension_name_;
   raw_ptr<views::ToggleButton> show_requests_toggle_;
 };
 
diff --git a/chrome/browser/ui/views/extensions/extensions_menu_site_permissions_page_view_unittest.cc b/chrome/browser/ui/views/extensions/extensions_menu_site_permissions_page_view_unittest.cc
index 21562d6a..b17d763 100644
--- a/chrome/browser/ui/views/extensions/extensions_menu_site_permissions_page_view_unittest.cc
+++ b/chrome/browser/ui/views/extensions/extensions_menu_site_permissions_page_view_unittest.cc
@@ -271,3 +271,66 @@
       site_permissions_page()->GetShowRequestsToggleForTesting()->GetIsOn());
   EXPECT_THAT(GetExtensionsShowingRequests(), testing::IsEmpty());
 }
+
+// Test that navigating to a new site where the user doesn't have runtime host
+// permissions controls (e.g restricted site) closes the site permissions page.
+TEST_F(ExtensionsSitePermissionsPageViewUnitTest,
+       PageNavigationWithMenuOpen_UserLosesRuntimeHostPermissionsControls) {
+  content::WebContentsTester* web_contents_tester =
+      AddWebContentsAndGetTester();
+
+  auto extension =
+      InstallExtensionWithHostPermissions("Extension", {"<all_urls>"});
+
+  const GURL url("http://www.non-restricted.com");
+  web_contents_tester->NavigateAndCommit(url);
+  WaitForAnimation();
+
+  ShowSitePermissionsPage(extension->id());
+  EXPECT_FALSE(IsMainPageOpened());
+  EXPECT_TRUE(IsSitePermissionsPageOpened(extension->id()));
+
+  // While the menu is open, navigate to an url where extension should not have
+  // a site permissions page.
+  const GURL restricted_url("chrome://extensions");
+  web_contents_tester->NavigateAndCommit(restricted_url);
+  WaitForAnimation();
+
+  // Menu should navigate back to main page since site permissions page should
+  // not be visible for the new url.
+  EXPECT_TRUE(IsMainPageOpened());
+  EXPECT_FALSE(IsSitePermissionsPageOpened(extension->id()));
+}
+
+// Test that navigating to a new site where the user still has runtime host
+// permissions controls updates the page contents.
+TEST_F(ExtensionsSitePermissionsPageViewUnitTest,
+       PageNavigationWithMenuOpen_UserMaintainsRuntimeHostPermissionsControls) {
+  content::WebContentsTester* web_contents_tester =
+      AddWebContentsAndGetTester();
+
+  auto extension =
+      InstallExtensionWithHostPermissions("Extension", {"<all_urls>"});
+
+  const GURL url_a("http://www.a.com");
+  web_contents_tester->NavigateAndCommit(url_a);
+  WaitForAnimation();
+
+  ShowSitePermissionsPage(extension->id());
+  EXPECT_FALSE(IsMainPageOpened());
+  EXPECT_TRUE(IsSitePermissionsPageOpened(extension->id()));
+
+  // While the menu is open, navigate to an url where extension also should have
+  // a site permissions page.
+  const GURL url_b("http://www.b.com");
+  web_contents_tester->NavigateAndCommit(url_b);
+  WaitForAnimation();
+
+  // Menu should stay open in site permissions page for `extension`.
+  EXPECT_FALSE(IsMainPageOpened());
+  EXPECT_TRUE(IsSitePermissionsPageOpened(extension->id()));
+}
+
+// TODO(crbug.com/1390952): Verify page content changes when extension is
+// updated. This will be easier to do once we have the site access radio
+// buttons, as we can change to the correct site access.
diff --git a/chrome/browser/ui/views/extensions/extensions_menu_view_controller.cc b/chrome/browser/ui/views/extensions/extensions_menu_view_controller.cc
index 848a9f6..ae9a78f 100644
--- a/chrome/browser/ui/views/extensions/extensions_menu_view_controller.cc
+++ b/chrome/browser/ui/views/extensions/extensions_menu_view_controller.cc
@@ -242,6 +242,10 @@
       *GetExtension(browser_, extension_id), *browser_->profile(),
       *toolbar_model_, *GetActiveWebContents()));
 
+  auto site_permissions_page =
+      std::make_unique<ExtensionsMenuSitePermissionsPageView>(
+          browser_, extension_id, this);
+
   const int icon_size = ChromeLayoutProvider::Get()->GetDistanceMetric(
       DISTANCE_EXTENSIONS_MENU_EXTENSION_ICON_SIZE);
   std::unique_ptr<ExtensionActionViewController> action_controller =
@@ -254,11 +258,9 @@
   bool is_show_requests_toggle_on =
       extensions::SitePermissionsHelper(browser_->profile())
           .ShowAccessRequestsInToolbar(extension_id);
+  site_permissions_page->Update(extension_name, extension_icon,
+                                is_show_requests_toggle_on);
 
-  auto site_permissions_page =
-      std::make_unique<ExtensionsMenuSitePermissionsPageView>(
-          browser_, extension_name, extension_icon, extension_id,
-          is_show_requests_toggle_on, this);
   SwitchToPage(std::move(site_permissions_page));
 }
 
@@ -289,29 +291,64 @@
     content::WebContents* web_contents) {
   DCHECK(current_page_);
 
-  ExtensionsMenuMainPageView* main_page = GetMainPage(current_page_);
-  if (main_page && web_contents) {
-    std::u16string current_site = GetCurrentHost(web_contents);
-    bool is_site_settings_toggle_visible =
-        IsSiteSettingsToggleVisible(*toolbar_model_, web_contents);
-    bool is_site_settings_toggle_on =
-        IsSiteSettingsToggleOn(browser_, web_contents);
-    main_page->Update(current_site, is_site_settings_toggle_visible,
-                      is_site_settings_toggle_on);
+  if (!web_contents) {
+    return;
+  }
 
-    std::vector<ExtensionMenuItemView*> menu_items = main_page->GetMenuItems();
-    for (auto* menu_item : menu_items) {
-      const extensions::Extension* extension =
-          extensions::ExtensionRegistry::Get(browser_->profile())
-              ->enabled_extensions()
-              .GetByID(menu_item->view_controller()->GetId());
-      CHECK(extension);
+  auto* site_permissions_page = GetSitePermissionsPage(current_page_);
+  if (site_permissions_page) {
+    extensions::ExtensionId extension_id =
+        site_permissions_page->extension_id();
 
-      ExtensionMenuItemView::SitePermissionsButtonState
-          site_permissions_button_state = GetSitePermissionsButtonState(
-              *extension, *browser_->profile(), *toolbar_model_, *web_contents);
-      menu_item->Update(site_permissions_button_state);
+    // Navigate back to the main page if extension should not have a site
+    // permissions page.
+    if (!CanUserCustomizeExtensionSiteAccess(
+            *GetExtension(browser_, extension_id), *browser_->profile(),
+            *toolbar_model_, *web_contents)) {
+      OpenMainPage();
+      return;
     }
+
+    // Otherwise, update the page content.
+    const int icon_size = ChromeLayoutProvider::Get()->GetDistanceMetric(
+        DISTANCE_EXTENSIONS_MENU_EXTENSION_ICON_SIZE);
+    std::unique_ptr<ExtensionActionViewController> action_controller =
+        ExtensionActionViewController::Create(extension_id, browser_,
+                                              extensions_container_);
+
+    std::u16string extension_name = action_controller->GetActionName();
+    ui::ImageModel extension_icon = action_controller->GetIcon(
+        GetActiveWebContents(), gfx::Size(icon_size, icon_size));
+    bool is_show_requests_toggle_on =
+        extensions::SitePermissionsHelper(browser_->profile())
+            .ShowAccessRequestsInToolbar(extension_id);
+
+    site_permissions_page->Update(extension_name, extension_icon,
+                                  is_show_requests_toggle_on);
+    return;
+  }
+
+  ExtensionsMenuMainPageView* main_page = GetMainPage(current_page_);
+  DCHECK(main_page);
+
+  std::u16string current_site = GetCurrentHost(web_contents);
+  bool is_site_settings_toggle_visible =
+      IsSiteSettingsToggleVisible(*toolbar_model_, web_contents);
+  bool is_site_settings_toggle_on =
+      IsSiteSettingsToggleOn(browser_, web_contents);
+  main_page->Update(current_site, is_site_settings_toggle_visible,
+                    is_site_settings_toggle_on);
+
+  std::vector<ExtensionMenuItemView*> menu_items = main_page->GetMenuItems();
+  for (auto* menu_item : menu_items) {
+    const extensions::Extension* extension =
+        GetExtension(browser_, menu_item->view_controller()->GetId());
+    CHECK(extension);
+
+    ExtensionMenuItemView::SitePermissionsButtonState
+        site_permissions_button_state = GetSitePermissionsButtonState(
+            *extension, *browser_->profile(), *toolbar_model_, *web_contents);
+    menu_item->Update(site_permissions_button_state);
   }
 }
 
diff --git a/chrome/browser/ui/views/location_bar/star_view.cc b/chrome/browser/ui/views/location_bar/star_view.cc
index 4b1f8b4..270f14c4 100644
--- a/chrome/browser/ui/views/location_bar/star_view.cc
+++ b/chrome/browser/ui/views/location_bar/star_view.cc
@@ -36,6 +36,7 @@
 #include "content/public/browser/web_contents.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/gfx/color_utils.h"
 #include "ui/gfx/paint_vector_icon.h"
 #include "ui/views/animation/ink_drop.h"
@@ -111,6 +112,11 @@
 }
 
 const gfx::VectorIcon& StarView::GetVectorIcon() const {
+  if (features::IsChromeRefresh2023()) {
+    return GetActive() ? omnibox::kStarActiveChromeRefreshIcon
+                       : omnibox::kStarChromeRefreshIcon;
+  }
+
   return GetActive() ? omnibox::kStarActiveIcon : omnibox::kStarIcon;
 }
 
diff --git a/chrome/browser/ui/views/omnibox/omnibox_view_views.cc b/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
index f195885..bb423ef4 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
@@ -90,6 +90,7 @@
 #include "ui/base/metadata/metadata_impl_macros.h"
 #include "ui/base/models/image_model.h"
 #include "ui/base/models/simple_menu_model.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/compositor/layer.h"
 #include "ui/events/event.h"
 #include "ui/gfx/canvas.h"
@@ -617,8 +618,12 @@
 void OmniboxViewViews::OnThemeChanged() {
   views::Textfield::OnThemeChanged();
 
-  set_placeholder_text_color(
-      GetColorProvider()->GetColor(kColorOmniboxTextDimmed));
+  bool gm3_text_color_enabled =
+      features::IsChromeRefresh2023() ||
+      base::FeatureList::IsEnabled(omnibox::kOmniboxSteadyStateTextColor);
+
+  set_placeholder_text_color(GetColorProvider()->GetColor(
+      gm3_text_color_enabled ? kColorOmniboxText : kColorOmniboxTextDimmed));
 
   EmphasizeURLComponents();
 }
diff --git a/chrome/browser/ui/views/page_action/page_action_icon_view.cc b/chrome/browser/ui/views/page_action/page_action_icon_view.cc
index 9abb277..11a3c5f3 100644
--- a/chrome/browser/ui/views/page_action/page_action_icon_view.cc
+++ b/chrome/browser/ui/views/page_action/page_action_icon_view.cc
@@ -37,7 +37,7 @@
 }
 
 int PageActionIconView::Delegate::GetPageActionIconSize() const {
-  return GetLayoutConstant(LOCATION_BAR_ICON_SIZE);
+  return GetLayoutConstant(LOCATION_BAR_TRAILING_ICON_SIZE);
 }
 
 gfx::Insets PageActionIconView::Delegate::GetPageActionIconInsets(
diff --git a/chrome/browser/ui/views/page_action/zoom_view.cc b/chrome/browser/ui/views/page_action/zoom_view.cc
index db33117..8624401 100644
--- a/chrome/browser/ui/views/page_action/zoom_view.cc
+++ b/chrome/browser/ui/views/page_action/zoom_view.cc
@@ -14,6 +14,7 @@
 #include "ui/accessibility/ax_node_data.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/events/event.h"
 #include "ui/gfx/geometry/size.h"
 
@@ -73,10 +74,20 @@
     current_zoom_percent_ = zoom_controller->GetZoomPercent();
 
     // The icon is hidden when the zoom level is default.
-    icon_ = zoom_controller && zoom_controller->GetZoomRelativeToDefault() ==
-                                   zoom::ZoomController::ZOOM_BELOW_DEFAULT_ZOOM
-                ? &kZoomMinusIcon
-                : &kZoomPlusIcon;
+
+    if (features::IsChromeRefresh2023()) {
+      icon_ =
+          zoom_controller && zoom_controller->GetZoomRelativeToDefault() ==
+                                 zoom::ZoomController::ZOOM_BELOW_DEFAULT_ZOOM
+              ? &kZoomMinusChromeRefreshIcon
+              : &kZoomPlusChromeRefreshIcon;
+    } else {
+      icon_ =
+          zoom_controller && zoom_controller->GetZoomRelativeToDefault() ==
+                                 zoom::ZoomController::ZOOM_BELOW_DEFAULT_ZOOM
+              ? &kZoomMinusIcon
+              : &kZoomPlusIcon;
+    }
     UpdateIconImage();
 
     // Visibility must be enabled before the bubble is shown to ensure the
diff --git a/chrome/browser/ui/views/side_panel/extensions/extension_side_panel_browsertest.cc b/chrome/browser/ui/views/side_panel/extensions/extension_side_panel_browsertest.cc
index 22b966d..1c00dab 100644
--- a/chrome/browser/ui/views/side_panel/extensions/extension_side_panel_browsertest.cc
+++ b/chrome/browser/ui/views/side_panel/extensions/extension_side_panel_browsertest.cc
@@ -6,6 +6,7 @@
 #include "base/test/scoped_feature_list.h"
 #include "chrome/browser/extensions/api/side_panel/side_panel_api.h"
 #include "chrome/browser/extensions/extension_browsertest.h"
+#include "chrome/browser/extensions/extension_tab_util.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
@@ -16,6 +17,8 @@
 #include "chrome/browser/ui/views/side_panel/side_panel_entry_observer.h"
 #include "chrome/browser/ui/views/side_panel/side_panel_registry.h"
 #include "chrome/browser/ui/views/side_panel/side_panel_registry_observer.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "components/sessions/content/session_tab_helper.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/test_utils.h"
@@ -117,21 +120,83 @@
   }
 
  protected:
+  int GetCurrentTabId() {
+    return ExtensionTabUtil::GetTabId(
+        browser()->tab_strip_model()->GetActiveWebContents());
+  }
+
   // Calls chrome.sidePanel.setOptions() for the given `extension`, `path` and
   // `enabled` and returns when the API call is complete.
   void RunSetOptions(const Extension& extension,
-                     const std::string& path,
+                     absl::optional<int> tab_id,
+                     absl::optional<std::string> path,
                      bool enabled) {
     auto function = base::MakeRefCounted<SidePanelSetOptionsFunction>();
     function->set_extension(&extension);
 
+    std::string tab_id_arg =
+        tab_id.has_value() ? base::StringPrintf(R"("tabId":%d,)", *tab_id) : "";
+    std::string path_arg =
+        path.has_value() ? base::StringPrintf(R"("path":"%s",)", path->c_str())
+                         : "";
     std::string args =
-        base::StringPrintf(R"([{"path":"%s","enabled":%s}])", path.c_str(),
-                           enabled ? "true" : "false");
+        base::StringPrintf(R"([{%s%s"enabled":%s}])", tab_id_arg.c_str(),
+                           path_arg.c_str(), enabled ? "true" : "false");
     EXPECT_TRUE(api_test_utils::RunFunction(function.get(), args, profile()))
         << function->GetError();
   }
 
+  // Disables the extension's side panel for the current tab.
+  void DisableForCurrentTab(const Extension& extension) {
+    ExtensionSidePanelRegistryWaiter waiter(global_registry(), extension.id());
+    RunSetOptions(extension, GetCurrentTabId(), /*path=*/absl::nullopt,
+                  /*enabled=*/false);
+    waiter.WaitForDeregistration();
+    EXPECT_FALSE(global_registry()->GetEntryForKey(GetKey(extension.id())));
+    EXPECT_FALSE(side_panel_coordinator()->IsSidePanelShowing());
+  }
+
+  // Shows a side panel entry and waits for the entry to be shown.
+  void ShowEntryAndWait(const SidePanelEntry::Key& key) {
+    TestSidePanelEntryWaiter extension_entry_waiter(
+        global_registry()->GetEntryForKey(key));
+    side_panel_coordinator()->Show(key);
+    extension_entry_waiter.WaitForEntryShown();
+    EXPECT_TRUE(side_panel_coordinator()->IsSidePanelShowing());
+  }
+
+  // Runs a script in the extension's side panel WebContents to retrieve the
+  // value of document.sidePanelTemp.
+  std::string GetGlobalVariableInExtensionSidePanel(
+      const ExtensionId& extension_id) {
+    auto* extension_coordinator =
+        extensions::ExtensionSidePanelManager::GetOrCreateForBrowser(browser())
+            ->GetExtensionCoordinatorForTesting(extension_id);
+
+    std::string result;
+    static constexpr char kScript[] =
+        "domAutomationController.send(document.sidePanelTemp);";
+
+    EXPECT_TRUE(content::ExecuteScriptAndExtractString(
+        extension_coordinator->GetHostWebContentsForTesting(), kScript,
+        &result));
+    return result;
+  }
+
+  // Runs a script in the extension's side panel WebContents to set the value of
+  // document.sidePanelTemp to `value`.
+  void SetGlobalVariableInExtensionSidePanel(const ExtensionId& extension_id,
+                                             const std::string& value) {
+    auto* extension_coordinator =
+        extensions::ExtensionSidePanelManager::GetOrCreateForBrowser(browser())
+            ->GetExtensionCoordinatorForTesting(extension_id);
+
+    std::string script =
+        base::StringPrintf(R"(document.sidePanelTemp = "%s";)", value.c_str());
+    ASSERT_TRUE(content::ExecuteScript(
+        extension_coordinator->GetHostWebContentsForTesting(), script.c_str()));
+  }
+
   SidePanelRegistry* global_registry() {
     return SidePanelCoordinator::GetGlobalSidePanelRegistry(browser());
   }
@@ -182,8 +247,7 @@
       test_data_dir_.AppendASCII("api_test/side_panel/simple_default"));
   ASSERT_TRUE(extension);
 
-  SidePanelEntry::Key extension_key =
-      SidePanelEntry::Key(SidePanelEntry::Id::kExtension, extension->id());
+  SidePanelEntry::Key extension_key = GetKey(extension->id());
   SidePanelEntry* extension_entry =
       global_registry()->GetEntryForKey(extension_key);
   ASSERT_TRUE(extension_entry);
@@ -224,8 +288,7 @@
   scoped_refptr<const extensions::Extension> extension = LoadExtension(
       test_data_dir_.AppendASCII("api_test/side_panel/simple_default"));
   ASSERT_TRUE(extension);
-  SidePanelEntry::Key extension_key =
-      SidePanelEntry::Key(SidePanelEntry::Id::kExtension, extension->id());
+  SidePanelEntry::Key extension_key = GetKey(extension->id());
 
   EXPECT_TRUE(global_registry()->GetEntryForKey(extension_key));
 
@@ -245,8 +308,7 @@
   scoped_refptr<const extensions::Extension> extension = LoadExtension(
       test_data_dir_.AppendASCII("api_test/side_panel/simple_default"));
   ASSERT_TRUE(extension);
-  SidePanelEntry::Key extension_key =
-      SidePanelEntry::Key(SidePanelEntry::Id::kExtension, extension->id());
+  SidePanelEntry::Key extension_key = GetKey(extension->id());
 
   EXPECT_TRUE(global_registry()->GetEntryForKey(extension_key));
   EXPECT_FALSE(side_panel_coordinator()->IsSidePanelShowing());
@@ -269,8 +331,7 @@
       extensions::ExtensionSidePanelManager::GetOrCreateForBrowser(browser())
           ->GetExtensionCoordinatorForTesting(extension->id());
 
-  SidePanelEntry::Key extension_key =
-      SidePanelEntry::Key(SidePanelEntry::Id::kExtension, extension->id());
+  SidePanelEntry::Key extension_key = GetKey(extension->id());
   SidePanelEntry* extension_entry =
       global_registry()->GetEntryForKey(extension_key);
 
@@ -305,15 +366,15 @@
   scoped_refptr<const extensions::Extension> extension = LoadExtension(
       test_data_dir_.AppendASCII("api_test/side_panel/setoptions_default_tab"));
   ASSERT_TRUE(extension);
-  SidePanelEntry::Key extension_key =
-      SidePanelEntry::Key(SidePanelEntry::Id::kExtension, extension->id());
+  SidePanelEntry::Key extension_key = GetKey(extension->id());
   EXPECT_FALSE(global_registry()->GetEntryForKey(extension_key));
 
   {
     // Call setOptions({enabled: true}) and wait for the extension's
     // SidePanelEntry to be registered.
     ExtensionSidePanelRegistryWaiter waiter(global_registry(), extension->id());
-    RunSetOptions(*extension, "panel_1.html", /*enabled=*/true);
+    RunSetOptions(*extension, /*tab_id=*/absl::nullopt, "panel_1.html",
+                  /*enabled=*/true);
     waiter.WaitForRegistration();
   }
 
@@ -323,7 +384,8 @@
     // Call setOptions({enabled: false}) and wait for the extension's
     // SidePanelEntry to be deregistered.
     ExtensionSidePanelRegistryWaiter waiter(global_registry(), extension->id());
-    RunSetOptions(*extension, "panel_1.html", /*enabled=*/false);
+    RunSetOptions(*extension, /*tab_id=*/absl::nullopt, /*path=*/absl::nullopt,
+                  /*enabled=*/false);
     waiter.WaitForDeregistration();
   }
 
@@ -333,7 +395,8 @@
     // Sanity check that re-enabling the side panel will register the entry
     // again and a view with the new side panel path can be shown.
     ExtensionSidePanelRegistryWaiter waiter(global_registry(), extension->id());
-    RunSetOptions(*extension, "panel_2.html", /*enabled=*/true);
+    RunSetOptions(*extension, /*tab_id=*/absl::nullopt, "panel_2.html",
+                  /*enabled=*/true);
     waiter.WaitForRegistration();
   }
 
@@ -349,7 +412,8 @@
     // Calling setOptions({enabled: false}) when the extension's SidePanelEntry
     // is shown should close the side panel.
     ExtensionSidePanelRegistryWaiter waiter(global_registry(), extension->id());
-    RunSetOptions(*extension, "panel_2.html", /*enabled=*/false);
+    RunSetOptions(*extension, /*tab_id=*/absl::nullopt, /*path=*/absl::nullopt,
+                  /*enabled=*/false);
     waiter.WaitForDeregistration();
   }
 
@@ -370,13 +434,13 @@
       extensions::ExtensionSidePanelManager::GetOrCreateForBrowser(browser())
           ->GetExtensionCoordinatorForTesting(extension->id());
 
-  SidePanelEntry::Key extension_key =
-      SidePanelEntry::Key(SidePanelEntry::Id::kExtension, extension->id());
+  SidePanelEntry::Key extension_key = GetKey(extension->id());
   EXPECT_TRUE(global_registry()->GetEntryForKey(extension_key));
 
   // Check that the extension's side panel view shows the most recently set
   // path.
-  RunSetOptions(*extension, "panel_1.html", /*enabled=*/true);
+  RunSetOptions(*extension, /*tab_id=*/absl::nullopt, "panel_1.html",
+                /*enabled=*/true);
   side_panel_coordinator()->Show(extension_key);
   ASSERT_TRUE(panel_1_listener.WaitUntilSatisfied());
   EXPECT_FALSE(default_path_listener.was_satisfied());
@@ -384,20 +448,15 @@
 
   // Check that changing the path while the view is active will cause the view
   // to navigate to the new path.
-  RunSetOptions(*extension, "default_path.html", /*enabled=*/true);
+  RunSetOptions(*extension, /*tab_id=*/absl::nullopt, "default_path.html",
+                /*enabled=*/true);
   ASSERT_TRUE(default_path_listener.WaitUntilSatisfied());
   EXPECT_TRUE(side_panel_coordinator()->IsSidePanelShowing());
 
   // Switch to the reading list in the side panel and check that the extension
   // view is cached (i.e. the view exists but is not shown, and its web contents
   // still exists).
-  {
-    TestSidePanelEntryWaiter reading_list_waiter(
-        global_registry()->GetEntryForKey(
-            SidePanelEntry::Key(SidePanelEntry::Id::kReadingList)));
-    side_panel_coordinator()->Show(SidePanelEntry::Id::kReadingList);
-    reading_list_waiter.WaitForEntryShown();
-  }
+  ShowEntryAndWait(SidePanelEntry::Key(SidePanelEntry::Id::kReadingList));
 
   EXPECT_TRUE(global_registry()->GetEntryForKey(extension_key)->CachedView());
 
@@ -408,7 +467,8 @@
   // Test calling setOptions with a different path when the extension's view is
   // cached. The cached view should then be invalidated and its web contents are
   // destroyed.
-  RunSetOptions(*extension, "panel_1.html", /*enabled=*/true);
+  RunSetOptions(*extension, /*tab_id=*/absl::nullopt, "panel_1.html",
+                /*enabled=*/true);
   destroyed_watcher.Wait();
 
   // When the extension's entry is shown again, the view with the updated path
@@ -426,8 +486,7 @@
   scoped_refptr<const extensions::Extension> extension = LoadExtension(
       test_data_dir_.AppendASCII("api_test/side_panel/simple_default"));
   ASSERT_TRUE(extension);
-  SidePanelEntry::Key extension_key =
-      SidePanelEntry::Key(SidePanelEntry::Id::kExtension, extension->id());
+  SidePanelEntry::Key extension_key = GetKey(extension->id());
   EXPECT_TRUE(global_registry()->GetEntryForKey(extension_key));
 
   {
@@ -492,8 +551,7 @@
   scoped_refptr<const extensions::Extension> extension = LoadExtension(
       test_data_dir_.AppendASCII("api_test/side_panel/simple_default"));
   ASSERT_TRUE(extension);
-  SidePanelEntry::Key extension_key =
-      SidePanelEntry::Key(SidePanelEntry::Id::kExtension, extension->id());
+  SidePanelEntry::Key extension_key = GetKey(extension->id());
   EXPECT_TRUE(global_registry()->GetEntryForKey(extension_key));
 
   {
@@ -525,6 +583,204 @@
   EXPECT_FALSE(side_panel_coordinator()->IsSidePanelShowing());
 }
 
+// Test that calling sidePanel.setOptions({enabled: false}) for a specific tab
+// will hide the extension's global side panel for that tab.
+IN_PROC_BROWSER_TEST_F(ExtensionSidePanelBrowserTest, HideGlobalPanelForTab) {
+  scoped_refptr<const extensions::Extension> extension = LoadExtension(
+      test_data_dir_.AppendASCII("api_test/side_panel/simple_default"));
+  ASSERT_TRUE(extension);
+
+  SidePanelEntry::Key extension_key = GetKey(extension->id());
+  EXPECT_TRUE(global_registry()->GetEntryForKey(extension_key));
+
+  // Show the extension's side panel and set a global variable to change the
+  // state of the side panel's page.
+  ExtensionTestMessageListener default_path_listener("default_path");
+  side_panel_coordinator()->Show(extension_key);
+  ASSERT_TRUE(default_path_listener.WaitUntilSatisfied());
+  EXPECT_TRUE(side_panel_coordinator()->IsSidePanelShowing());
+
+  SetGlobalVariableInExtensionSidePanel(extension->id(), "altered_state");
+  EXPECT_EQ("altered_state",
+            GetGlobalVariableInExtensionSidePanel(extension->id()));
+
+  // Disable the extension's side panel for the current tab.
+  DisableForCurrentTab(*extension);
+
+  // Calling sidePanel.setOptions({enabled: true}) for the current tab should
+  // re-register the entry.
+  {
+    ExtensionSidePanelRegistryWaiter waiter(global_registry(), extension->id());
+    RunSetOptions(*extension, GetCurrentTabId(), /*path=*/absl::nullopt,
+                  /*enabled=*/true);
+    waiter.WaitForRegistration();
+    EXPECT_TRUE(global_registry()->GetEntryForKey(extension_key));
+    EXPECT_FALSE(side_panel_coordinator()->IsSidePanelShowing());
+  }
+
+  // Show the side panel entry and check its state to verify that it's the same
+  // page as before.
+  ShowEntryAndWait(extension_key);
+  EXPECT_EQ("altered_state",
+            GetGlobalVariableInExtensionSidePanel(extension->id()));
+
+  // Disable the extension's side panel for the current tab again.
+  DisableForCurrentTab(*extension);
+
+  // Open a new tab and navigate to it. The extension's side panel should be
+  // available again since it's not disabled for the new tab.
+  {
+    ExtensionSidePanelRegistryWaiter waiter(global_registry(), extension->id());
+    ui_test_utils::NavigateToURLWithDisposition(
+        browser(), GURL("http://example.com"),
+        WindowOpenDisposition::NEW_FOREGROUND_TAB,
+        ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB);
+    ASSERT_EQ(2, browser()->tab_strip_model()->count());
+    ASSERT_TRUE(browser()->tab_strip_model()->IsTabSelected(1));
+
+    waiter.WaitForRegistration();
+    EXPECT_TRUE(global_registry()->GetEntryForKey(extension_key));
+    EXPECT_FALSE(side_panel_coordinator()->IsSidePanelShowing());
+  }
+
+  // Show the side panel entry and check its state to verify that it's the same
+  // page as before.
+  ShowEntryAndWait(extension_key);
+  EXPECT_EQ("altered_state",
+            GetGlobalVariableInExtensionSidePanel(extension->id()));
+
+  // Go back to the first tab where the side panel is disabled and verify the
+  // extension's side panel is no longer there.
+  {
+    ExtensionSidePanelRegistryWaiter waiter(global_registry(), extension->id());
+    browser()->tab_strip_model()->ActivateTabAt(0);
+    waiter.WaitForDeregistration();
+    EXPECT_FALSE(global_registry()->GetEntryForKey(extension_key));
+    EXPECT_FALSE(side_panel_coordinator()->IsSidePanelShowing());
+  }
+}
+
+// Test that the saved view state for the hidden global extension side panel is
+// invalidated if setOptions({enabled: false}) is called without a tab ID.
+IN_PROC_BROWSER_TEST_F(ExtensionSidePanelBrowserTest,
+                       DisableGlobalPanelWhileHidden) {
+  scoped_refptr<const extensions::Extension> extension = LoadExtension(
+      test_data_dir_.AppendASCII("api_test/side_panel/simple_default"));
+  ASSERT_TRUE(extension);
+  auto* extension_coordinator =
+      extensions::ExtensionSidePanelManager::GetOrCreateForBrowser(browser())
+          ->GetExtensionCoordinatorForTesting(extension->id());
+
+  SidePanelEntry::Key extension_key = GetKey(extension->id());
+  EXPECT_TRUE(global_registry()->GetEntryForKey(extension_key));
+
+  // Show the extension's side panel.
+  ExtensionTestMessageListener default_path_listener("default_path");
+  side_panel_coordinator()->Show(extension_key);
+  ASSERT_TRUE(default_path_listener.WaitUntilSatisfied());
+  EXPECT_TRUE(side_panel_coordinator()->IsSidePanelShowing());
+
+  // Disable the extension's side panel for the current tab.
+  {
+    ExtensionSidePanelRegistryWaiter waiter(global_registry(), extension->id());
+    RunSetOptions(*extension, GetCurrentTabId(), /*path=*/absl::nullopt,
+                  /*enabled=*/false);
+    waiter.WaitForDeregistration();
+    EXPECT_FALSE(global_registry()->GetEntryForKey(extension_key));
+    EXPECT_FALSE(side_panel_coordinator()->IsSidePanelShowing());
+  }
+
+  // There should be web contents from the saved view.
+  ASSERT_TRUE(extension_coordinator->GetHostWebContentsForTesting());
+  content::WebContentsDestroyedWatcher destroyed_watcher(
+      extension_coordinator->GetHostWebContentsForTesting());
+
+  // Calling setOptions({enabled: false}) for all tabs should destroy the
+  // contents.
+  RunSetOptions(*extension, /*tab_id=*/absl::nullopt, /*path=*/absl::nullopt,
+                /*enabled=*/false);
+  destroyed_watcher.Wait();
+
+  // Sanity check that calling setOptions({enabled: true}) for all tabs while on
+  // a tab where the panel is disabled should be a no-op.
+  RunSetOptions(*extension, /*tab_id=*/absl::nullopt, "default_path.html",
+                /*enabled=*/true);
+  EXPECT_FALSE(global_registry()->GetEntryForKey(extension_key));
+
+  // Open a new tab and navigate to it. The extension's side panel should be
+  // available again since it's not disabled for the new tab.
+  {
+    ExtensionSidePanelRegistryWaiter waiter(global_registry(), extension->id());
+    ui_test_utils::NavigateToURLWithDisposition(
+        browser(), GURL("http://example.com"),
+        WindowOpenDisposition::NEW_FOREGROUND_TAB,
+        ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB);
+    ASSERT_EQ(2, browser()->tab_strip_model()->count());
+    ASSERT_TRUE(browser()->tab_strip_model()->IsTabSelected(1));
+
+    waiter.WaitForRegistration();
+    EXPECT_TRUE(global_registry()->GetEntryForKey(extension_key));
+    EXPECT_FALSE(side_panel_coordinator()->IsSidePanelShowing());
+  }
+}
+
+// Test that when the extension's side panel is shown, switching from a tab
+// where the panel is enabled to one where it's disabled then back to the first
+// tab will re-register the entry but not show it. This behavior is a little
+// weird, but trying to have it reopen causes far more complexity than is
+// worthwhile.
+IN_PROC_BROWSER_TEST_F(ExtensionSidePanelBrowserTest, ReEnabledPanelNotShown) {
+  // Open a second tab and switch back to the first tab.
+  ui_test_utils::NavigateToURLWithDisposition(
+      browser(), GURL("http://example.com"),
+      WindowOpenDisposition::NEW_FOREGROUND_TAB,
+      ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB);
+  ASSERT_EQ(2, browser()->tab_strip_model()->count());
+  ASSERT_TRUE(browser()->tab_strip_model()->IsTabSelected(1));
+
+  int second_tab_id = GetCurrentTabId();
+  browser()->tab_strip_model()->ActivateTabAt(0);
+
+  scoped_refptr<const extensions::Extension> extension = LoadExtension(
+      test_data_dir_.AppendASCII("api_test/side_panel/simple_default"));
+  ASSERT_TRUE(extension);
+
+  SidePanelEntry::Key extension_key = GetKey(extension->id());
+  EXPECT_TRUE(global_registry()->GetEntryForKey(extension_key));
+
+  // Show the extension's side panel.
+  ExtensionTestMessageListener default_path_listener("default_path");
+  side_panel_coordinator()->Show(extension_key);
+  ASSERT_TRUE(default_path_listener.WaitUntilSatisfied());
+  EXPECT_TRUE(side_panel_coordinator()->IsSidePanelShowing());
+
+  // Disable the extension's side panel for the second tab, which shouldn't do
+  // anything here since we're on the first tab.
+  RunSetOptions(*extension, second_tab_id, /*path=*/absl::nullopt,
+                /*enabled=*/false);
+  EXPECT_TRUE(side_panel_coordinator()->IsSidePanelShowing());
+
+  // Switch to the second tab and verify that the extension's entry is no longer
+  // registered.
+  {
+    ExtensionSidePanelRegistryWaiter waiter(global_registry(), extension->id());
+    browser()->tab_strip_model()->ActivateTabAt(1);
+    waiter.WaitForDeregistration();
+    EXPECT_FALSE(global_registry()->GetEntryForKey(extension_key));
+    EXPECT_FALSE(side_panel_coordinator()->IsSidePanelShowing());
+  }
+
+  // Switch back to the first tab and verify that the extension's entry is
+  // registered again but is not showing.
+  {
+    ExtensionSidePanelRegistryWaiter waiter(global_registry(), extension->id());
+    browser()->tab_strip_model()->ActivateTabAt(0);
+    waiter.WaitForRegistration();
+    EXPECT_TRUE(global_registry()->GetEntryForKey(extension_key));
+    EXPECT_FALSE(side_panel_coordinator()->IsSidePanelShowing());
+  }
+}
+
 class ExtensionSidePanelDisabledBrowserTest : public ExtensionBrowserTest {
  public:
   ExtensionSidePanelDisabledBrowserTest() {
@@ -550,8 +806,7 @@
   scoped_refptr<const extensions::Extension> extension = LoadExtension(
       test_data_dir_.AppendASCII("api_test/side_panel/simple_default"));
   ASSERT_TRUE(extension);
-  SidePanelEntry::Key extension_key =
-      SidePanelEntry::Key(SidePanelEntry::Id::kExtension, extension->id());
+  SidePanelEntry::Key extension_key = GetKey(extension->id());
 
   EXPECT_FALSE(global_registry()->GetEntryForKey(extension_key));
 }
diff --git a/chrome/browser/ui/views/side_panel/extensions/extension_side_panel_coordinator.cc b/chrome/browser/ui/views/side_panel/extensions/extension_side_panel_coordinator.cc
index 588336d..1610666 100644
--- a/chrome/browser/ui/views/side_panel/extensions/extension_side_panel_coordinator.cc
+++ b/chrome/browser/ui/views/side_panel/extensions/extension_side_panel_coordinator.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/ui/views/side_panel/extensions/extension_side_panel_coordinator.h"
 
 #include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/extensions/extension_tab_util.h"
 #include "chrome/browser/extensions/extension_view_host_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
@@ -12,6 +13,7 @@
 #include "chrome/browser/ui/views/side_panel/side_panel_coordinator.h"
 #include "chrome/browser/ui/views/side_panel/side_panel_registry.h"
 #include "chrome/common/extensions/api/side_panel.h"
+#include "content/public/browser/browser_context.h"
 #include "content/public/browser/web_contents.h"
 #include "extensions/browser/extension_icon_placeholder.h"
 #include "extensions/common/constants.h"
@@ -23,6 +25,24 @@
 
 namespace extensions {
 
+namespace {
+
+int GetCurrentTabId(Browser* browser) {
+  return ExtensionTabUtil::GetTabId(
+      browser->tab_strip_model()->GetActiveWebContents());
+}
+
+bool HasGlobalSidePanel(content::BrowserContext* context,
+                        const Extension& extension) {
+  auto options = SidePanelService::Get(context)->GetOptions(
+      extension, /*tab_id=*/absl::nullopt);
+
+  return options.enabled.has_value() && *options.enabled &&
+         options.path.has_value();
+}
+
+}  // namespace
+
 ExtensionSidePanelCoordinator::ExtensionSidePanelCoordinator(
     Browser* browser,
     const Extension* extension,
@@ -40,6 +60,7 @@
   // `service` can be null for some tests.
   if (service) {
     scoped_service_observation_.Observe(service);
+    browser_->tab_strip_model()->AddObserver(this);
     LoadExtensionIcon();
     auto default_options =
         service->GetOptions(*extension, /*tab_id=*/absl::nullopt);
@@ -69,8 +90,26 @@
   return SidePanelEntry::Key(SidePanelEntry::Id::kExtension, extension_->id());
 }
 
+SidePanelEntry* ExtensionSidePanelCoordinator::GetEntry() const {
+  return global_registry_->GetEntryForKey(GetEntryKey());
+}
+
+bool ExtensionSidePanelCoordinator::IsDisabledForTab(int tab_id) const {
+  auto options = SidePanelService::Get(browser_->profile())
+                     ->GetOptions(*extension_, tab_id);
+  return options.enabled.has_value() && !(*options.enabled);
+}
+
 void ExtensionSidePanelCoordinator::DeregisterGlobalEntry() {
   global_registry_->Deregister(GetEntryKey());
+  global_entry_view_.reset();
+}
+
+void ExtensionSidePanelCoordinator::DeregisterGlobalEntryAndCacheView() {
+  if (GetEntry()) {
+    global_entry_view_ =
+        global_registry_->DeregisterAndReturnView(GetEntryKey());
+  }
 }
 
 void ExtensionSidePanelCoordinator::OnPanelOptionsChanged(
@@ -81,8 +120,31 @@
     return;
   }
 
-  // TODO(crbug.com/1378048): Handle tab specific side panel options.
+  bool should_enable_entry =
+      updated_options.enabled.has_value() && *updated_options.enabled;
+  bool should_disable_entry =
+      updated_options.enabled.has_value() && !(*updated_options.enabled);
+  SidePanelEntry* entry = GetEntry();
+
+  // TODO(crbug.com/1378048): Handle enabling tab specific side panel views if
+  // `updated_options.tab_id` is specified.
   if (updated_options.tab_id.has_value()) {
+    if (GetCurrentTabId(browser_) == *updated_options.tab_id) {
+      if (!entry && should_enable_entry &&
+          HasGlobalSidePanel(browser_->profile(), *extension_)) {
+        // We create an entry if:
+        //  - The side panel is being enabled/no longer being disabled for this
+        //    tab
+        //  - The extension has a global side panel specified
+        //  - There is currently no global entry registered
+        CreateAndRegisterEntry();
+      } else if (should_disable_entry) {
+        // if the side panel is being disabled for this tab and there exists an
+        // entry, deregister it and keep its view.
+        DeregisterGlobalEntryAndCacheView();
+      }
+    }
+
     return;
   }
 
@@ -90,23 +152,26 @@
   GURL previous_url = side_panel_url_;
   if (updated_options.path.has_value()) {
     side_panel_url_ = extension_->GetResourceURL(*updated_options.path);
+    if (previous_url != side_panel_url_) {
+      global_entry_view_.reset();
+    }
   }
 
   // Deregister the SidePanelEntry if `enabled` is false.
-  if (updated_options.enabled.has_value() && !(*updated_options.enabled)) {
+  if (should_disable_entry) {
     DeregisterGlobalEntry();
     return;
   }
 
-  // If there is no entry for this extension and `enabled` is true, create and
-  // register the entry.
-  SidePanelEntry::Key key = GetEntryKey();
-  auto* entry = global_registry_->GetEntryForKey(key);
-  if (!entry) {
+  bool should_create_entry = !entry && should_enable_entry &&
+                             !IsDisabledForTab(GetCurrentTabId(browser_));
+  if (should_create_entry) {
+    // Create a global entry if the extension has not disabled its side panel
+    // for the current tab.
     CreateAndRegisterEntry();
-  } else if (previous_url != side_panel_url_) {
-    if (global_registry_->active_entry().has_value() &&
-        (*global_registry_->active_entry())->key() == key) {
+  } else if (entry && previous_url != side_panel_url_) {
+    // Handle changes to the side panel's url if an entry exists.
+    if (global_registry_->active_entry() == entry) {
       // If this extension's entry is active, navigate the entry's view to the
       // updated URL.
       NavigateIfNecessary();
@@ -143,6 +208,34 @@
   }
 }
 
+void ExtensionSidePanelCoordinator::OnTabStripModelChanged(
+    TabStripModel* tab_strip_model,
+    const TabStripModelChange& change,
+    const TabStripSelectionChange& selection) {
+  // Registering/deregistering an entry should only happen if the active tab
+  // changes and the extension has specified a global side panel.
+  if (!selection.active_tab_changed() ||
+      !HasGlobalSidePanel(browser_->profile(), *extension_)) {
+    return;
+  }
+
+  bool disabled_for_old_tab =
+      IsDisabledForTab(ExtensionTabUtil::GetTabId(selection.old_contents));
+  bool disabled_for_new_tab =
+      IsDisabledForTab(ExtensionTabUtil::GetTabId(selection.new_contents));
+
+  if (!disabled_for_old_tab && disabled_for_new_tab) {
+    // If we switch to a tab where the extension's global side panel is
+    // disabled, deregister the entry but keep its view.
+    DeregisterGlobalEntryAndCacheView();
+  } else if (disabled_for_old_tab && !disabled_for_new_tab) {
+    // If we switch to a tab where the extension's global side panel is enabled,
+    // re-register the entry.
+    DCHECK(!GetEntry());
+    CreateAndRegisterEntry();
+  }
+}
+
 void ExtensionSidePanelCoordinator::CreateAndRegisterEntry() {
   // The extension icon should be initialized in the constructor, so this should
   // not be null.
@@ -160,6 +253,11 @@
 }
 
 std::unique_ptr<views::View> ExtensionSidePanelCoordinator::CreateView() {
+  if (global_entry_view_) {
+    DCHECK(host_);
+    return std::move(global_entry_view_);
+  }
+
   host_ =
       ExtensionViewHostFactory::CreateSidePanelHost(side_panel_url_, browser_);
 
diff --git a/chrome/browser/ui/views/side_panel/extensions/extension_side_panel_coordinator.h b/chrome/browser/ui/views/side_panel/extensions/extension_side_panel_coordinator.h
index dafd6c2..51319128 100644
--- a/chrome/browser/ui/views/side_panel/extensions/extension_side_panel_coordinator.h
+++ b/chrome/browser/ui/views/side_panel/extensions/extension_side_panel_coordinator.h
@@ -9,6 +9,7 @@
 #include "base/scoped_observation.h"
 #include "chrome/browser/extensions/api/side_panel/side_panel_service.h"
 #include "chrome/browser/extensions/extension_view_host.h"
+#include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
 #include "chrome/browser/ui/views/extensions/extension_view_views.h"
 #include "chrome/browser/ui/views/side_panel/side_panel_entry.h"
 #include "chrome/browser/ui/views/side_panel/side_panel_view_state_observer.h"
@@ -35,7 +36,8 @@
 // shown if this extension's SidePanelEntry is active.
 class ExtensionSidePanelCoordinator : public ExtensionViewViews::Observer,
                                       public IconImage::Observer,
-                                      public SidePanelService::Observer {
+                                      public SidePanelService::Observer,
+                                      public TabStripModelObserver {
  public:
   explicit ExtensionSidePanelCoordinator(Browser* browser,
                                          const Extension* extension,
@@ -57,10 +59,20 @@
  private:
   SidePanelEntry::Key GetEntryKey() const;
 
+  SidePanelEntry* GetEntry() const;
+
+  // Returns if this extension's side panel is explicitly disabled for the given
+  // `tab_id`.
+  bool IsDisabledForTab(int tab_id) const;
+
   // Deregisters this extension's SidePanelEntry from the global
   // SidePanelCoordinator.
   void DeregisterGlobalEntry();
 
+  // Deregisters this extension's SidePanelEntry from the global
+  // SidePanelCoordinator and caches the entry's view into `global_entry_view_`.
+  void DeregisterGlobalEntryAndCacheView();
+
   // SidePanelService::Observer:
   void OnPanelOptionsChanged(
       const ExtensionId& extension_id,
@@ -73,6 +85,12 @@
   // IconImage::Observer
   void OnExtensionIconImageChanged(IconImage* image) override;
 
+  // TabStripModelObserver:
+  void OnTabStripModelChanged(
+      TabStripModel* tab_strip_model,
+      const TabStripModelChange& change,
+      const TabStripSelectionChange& selection) override;
+
   // Creates and registers the SidePanelEntry for this extension, and observes
   // the entry. This is called if the extension has a default side panel path
   // when the browser view is created or when the extension is loaded.
@@ -113,6 +131,10 @@
   // The extension's own icon for its side panel entry.
   std::unique_ptr<IconImage> extension_icon_;
 
+  // Cached view for global entry if it was disabled for a specific tab and may
+  // be shown again on a different tab where it's enabled.
+  std::unique_ptr<views::View> global_entry_view_;
+
   base::ScopedObservation<ExtensionViewViews, ExtensionViewViews::Observer>
       scoped_view_observation_{this};
   base::ScopedObservation<SidePanelService, SidePanelService::Observer>
diff --git a/chrome/browser/ui/views/side_search/side_search_icon_view.cc b/chrome/browser/ui/views/side_search/side_search_icon_view.cc
index e3b2fdd..605dca17 100644
--- a/chrome/browser/ui/views/side_search/side_search_icon_view.cc
+++ b/chrome/browser/ui/views/side_search/side_search_icon_view.cc
@@ -24,6 +24,7 @@
 #include "components/vector_icons/vector_icons.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/gfx/paint_vector_icon.h"
 #include "ui/views/view_class_properties.h"
 
@@ -148,7 +149,9 @@
 
 const gfx::VectorIcon& SideSearchIconView::GetVectorIcon() const {
   // Default to the kSearchIcon if the DSE icon image is not available.
-  return vector_icons::kSearchIcon;
+  return features::IsChromeRefresh2023()
+             ? vector_icons::kSearchChromeRefreshIcon
+             : vector_icons::kSearchIcon;
 }
 
 ui::ImageModel SideSearchIconView::GetSizedIconImage(int size) const {
diff --git a/chrome/browser/ui/webui/ash/parent_access/parent_access_ui.cc b/chrome/browser/ui/webui/ash/parent_access/parent_access_ui.cc
index c3ad1ca..7176199a 100644
--- a/chrome/browser/ui/webui/ash/parent_access/parent_access_ui.cc
+++ b/chrome/browser/ui/webui/ash/parent_access/parent_access_ui.cc
@@ -83,6 +83,8 @@
                           IDR_LOCAL_WEB_APPROVALS_AFTER_JS);
   source->AddResourcePath("flows/extension_approvals_disabled.js",
                           IDR_EXTENSION_APPROVALS_DISABLED_JS);
+  source->AddResourcePath("flows/extension_approvals_before.js",
+                          IDR_EXTENSION_APPROVALS_BEFORE_JS);
   source->AddResourcePath("parent_access_before.js",
                           IDR_PARENT_ACCESS_BEFORE_JS);
   source->AddResourcePath("parent_access_disabled.js",
@@ -91,6 +93,7 @@
                           IDR_PARENT_ACCESS_UI_MOJOM_WEBUI_JS);
   source->AddResourcePath("webview_manager.js",
                           IDR_PARENT_ACCESS_WEBVIEW_MANAGER_JS);
+  source->AddResourcePath("utils.js", IDR_PARENT_ACCESS_UTILS_JS);
   source->AddResourcePath("parent_access_screen.js",
                           IDR_PARENT_ACCESS_SCREEN_JS);
   source->AddResourcePaths(
@@ -124,7 +127,15 @@
       {"extensionApprovalsDisabledTitle",
        IDS_PARENT_ACCESS_EXTENSION_APPROVALS_DISABLED_TITLE},
       {"extensionApprovalsDisabledSubtitle",
-       IDS_PARENT_ACCESS_EXTENSION_APPROVALS_DISABLED_SUBTITLE}};
+       IDS_PARENT_ACCESS_EXTENSION_APPROVALS_DISABLED_SUBTITLE},
+      {"extensionApprovalsAddExtensionBeforeTitle",
+       IDS_PARENT_ACCESS_EXTENSION_APPROVALS_ADD_EXTENSION_BEFORE_TITLE},
+      {"extensionApprovalsAddExtensionBeforeSubtitle",
+       IDS_PARENT_ACCESS_EXTENSION_APPROVALS_ADD_EXTENSION_BEFORE_SUBTITLE},
+      {"extensionApprovalsEnableExtensionBeforeTitle",
+       IDS_PARENT_ACCESS_EXTENSION_APPROVALS_ENABLE_EXTENSION_BEFORE_TITLE},
+      {"extensionApprovalsEnableExtensionBeforeSubtitle",
+       IDS_PARENT_ACCESS_EXTENSION_APPROVALS_ENABLE_EXTENSION_BEFORE_SUBTITLE}};
   source->AddLocalizedStrings(kLocalizedStrings);
 
   // Enables use of test_loader.html
diff --git a/chrome/browser/ui/webui/ash/parent_access/parent_access_ui.mojom b/chrome/browser/ui/webui/ash/parent_access/parent_access_ui.mojom
index 9e1aea7..40fb1c62 100644
--- a/chrome/browser/ui/webui/ash/parent_access/parent_access_ui.mojom
+++ b/chrome/browser/ui/webui/ash/parent_access/parent_access_ui.mojom
@@ -65,8 +65,29 @@
   array<uint8> favicon_png_bytes;
 };
 
+// Parameters for the local extension approvals V2 feature.
+struct ExtensionApprovalsParams {
+  enum ExtensionApprovalType {
+    kAdd, // Adding a new extension
+    kEnable, // Enabling an existing extension
+  };
+
+  // The type of approval being requested.
+  ExtensionApprovalType approval_type;
+
+  // Display name for the extension.
+  mojo_base.mojom.String16 extension_name;
+
+  // Extension icon, stored as an array of bytes.
+  array<uint8> icon_png_bytes;
+
+  // The child's name to be displayed to the parent.
+  mojo_base.mojom.String16 child_display_name;
+};
+
 union FlowTypeParams {
   WebApprovalsParams web_approvals_params;
+  ExtensionApprovalsParams extension_approvals_params;
 };
 
 // The result of the parent access request.
diff --git a/chrome/browser/usb/usb_chooser_controller.cc b/chrome/browser/usb/usb_chooser_controller.cc
index 0b1666b..eb4eb3c 100644
--- a/chrome/browser/usb/usb_chooser_controller.cc
+++ b/chrome/browser/usb/usb_chooser_controller.cc
@@ -90,10 +90,8 @@
     RenderFrameHost* render_frame_host,
     std::vector<device::mojom::UsbDeviceFilterPtr> device_filters,
     blink::mojom::WebUsbService::GetPermissionCallback callback)
-    : ChooserController(CreateExtensionAwareChooserTitle(
-          render_frame_host,
-          IDS_USB_DEVICE_CHOOSER_PROMPT_ORIGIN,
-          IDS_USB_DEVICE_CHOOSER_PROMPT_EXTENSION_NAME)),
+    : ChooserController(
+          CreateChooserTitle(render_frame_host, IDS_USB_DEVICE_CHOOSER_PROMPT)),
       filters_(std::move(device_filters)),
       callback_(std::move(callback)),
       requesting_frame_(render_frame_host) {
diff --git a/chrome/browser/webapps/web_app_offline_browsertest.cc b/chrome/browser/webapps/web_app_offline_browsertest.cc
index abdc1c7..784f5f2 100644
--- a/chrome/browser/webapps/web_app_offline_browsertest.cc
+++ b/chrome/browser/webapps/web_app_offline_browsertest.cc
@@ -28,12 +28,8 @@
 #include "base/win/windows_version.h"
 #endif
 
-#if BUILDFLAG(IS_CHROMEOS)
-#include "chromeos/constants/chromeos_features.h"
-#endif
-
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-#include "ash/constants/ash_features.h"
+#include "ash/public/cpp/style/dark_light_mode_controller.h"
 #endif
 
 using ::testing::ElementsAre;
@@ -496,14 +492,6 @@
  public:
   WebAppOfflineDarkModeTest() {
     std::vector<base::test::FeatureRef> disabled_features;
-#if BUILDFLAG(IS_CHROMEOS)
-    disabled_features.push_back(chromeos::features::kDarkLightMode);
-#endif
-
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-    disabled_features.push_back(ash::features::kNotificationsRefresh);
-#endif
-
     feature_list_.InitWithFeatures({features::kPWAsDefaultOfflinePage,
                                     blink::features::kWebAppEnableDarkMode},
                                    {disabled_features});
@@ -524,6 +512,15 @@
 #endif  // BUILDFLAG(IS_MAC)
   }
 
+  void SetUpOnMainThread() override {
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+    // Explicitly set dark mode in ChromeOS or we can't get light mode after
+    // sunset (due to dark mode auto-scheduling).
+    ash::DarkLightModeController::Get()->SetDarkModeEnabledForTest(
+        GetParam() == blink::mojom::PreferredColorScheme::kDark);
+#endif
+  }
+
  protected:
   void SetUpCommandLine(base::CommandLine* command_line) override {
     // ShellContentBrowserClient::OverrideWebkitPrefs() overrides the
diff --git a/chrome/browser/win/conflicts/msi_util.cc b/chrome/browser/win/conflicts/msi_util.cc
index 683a7ae..8b3e650 100644
--- a/chrome/browser/win/conflicts/msi_util.cc
+++ b/chrome/browser/win/conflicts/msi_util.cc
@@ -123,13 +123,17 @@
   // Internally, the Microsoft Installer uses a special formatting of the guids
   // to store the information in the registry.
   product_guid = product_guid.substr(1, 36);
-  if (!base::IsValidGUID(base::AsStringPiece16(product_guid)))
+  if (!base::GUID::ParseCaseInsensitive(base::AsStringPiece16(product_guid))
+           .is_valid()) {
     return false;
+  }
   std::wstring product_squid = InstallUtil::GuidToSquid(product_guid);
 
   component_guid = component_guid.substr(1, 36);
-  if (!base::IsValidGUID(base::AsStringPiece16(component_guid)))
+  if (!base::GUID::ParseCaseInsensitive(base::AsStringPiece16(component_guid))
+           .is_valid()) {
     return false;
+  }
   std::wstring component_squid = InstallUtil::GuidToSquid(component_guid);
 
   std::vector<std::wstring> sids = {
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index d6b77497..f5d7d14 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1679947177-fb316e82e3ba3c9b100a4d9e87e5e8c76b5abc41.profdata
+chrome-mac-arm-main-1679954386-05b1b656ba2d69b02ce785f9a11a387b8c09d45c.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 1da1b6f7..7ddc8955 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1679929060-34f29b821ee8e423dbbb3d45b09b0227d8306c7d.profdata
+chrome-win32-main-1679950782-683238c0416bcc31133aa08de790f1a9ba83313b.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 30c09f5..68c1c55 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1679929060-4c299167367e97d7cb321c6c0cf68fb0c992c785.profdata
+chrome-win64-main-1679939715-ef406d1f8b818e5b00d81bb8d0eae130ed5c3687.profdata
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc
index a6932bf..a4c99fd 100644
--- a/chrome/common/chrome_features.cc
+++ b/chrome/common/chrome_features.cc
@@ -319,10 +319,10 @@
 // deprecation.
 BASE_FEATURE(kKeepForceInstalledPreinstalledApps,
              "KeepForceInstalledPreinstalledApps",
-             base::FEATURE_ENABLED_BY_DEFAULT);
+             base::FEATURE_DISABLED_BY_DEFAULT);
 // Controls if the 'launch anyways' button is shown.
 const base::FeatureParam<bool> kChromeAppsDeprecationHideLaunchAnyways{
-    &kChromeAppsDeprecation, "HideLaunchAnyways", false};
+    &kChromeAppsDeprecation, "HideLaunchAnyways", true};
 #endif
 
 // Enables notification permission revocation for origins that may send
diff --git a/chrome/common/extensions/api/file_manager_private.idl b/chrome/common/extensions/api/file_manager_private.idl
index 8c71835..2e94920 100644
--- a/chrome/common/extensions/api/file_manager_private.idl
+++ b/chrome/common/extensions/api/file_manager_private.idl
@@ -1098,6 +1098,10 @@
 
   // The files affected by the IOTask. Currently only returned for TrashIOTask.
   [instanceOf=Entry] object[]? outputs;
+
+  // Volume id of the destination for operations that transfer files to a
+  // directory (e.g. copy or move).
+  DOMString destinationVolumeId;
 };
 
 dictionary DlpMetadata {
diff --git a/chrome/test/data/webui/chromeos/parent_access/parent_access_app_test.js b/chrome/test/data/webui/chromeos/parent_access/parent_access_app_test.js
index dd18e223..d83e60c4 100644
--- a/chrome/test/data/webui/chromeos/parent_access/parent_access_app_test.js
+++ b/chrome/test/data/webui/chromeos/parent_access/parent_access_app_test.js
@@ -83,10 +83,20 @@
         document.body.appendChild(parentAccessApp);
         await flushTasks();
 
-        // Verify online flow is showing and switch to the after screen.
+        // Verify before flow is showing and switch to the authentication
+        // screen.
         assertEquals(parentAccessApp.currentScreen_, Screens.BEFORE_FLOW);
-        parentAccessApp.dispatchEvent(
-            new CustomEvent('show-authentication-flow'));
+        // Verify the extension approvals before screen is showing.
+        const parentAccessBefore =
+            parentAccessApp.shadowRoot.querySelector('parent-access-before');
+        const extensionApprovalsBefore =
+            parentAccessBefore.shadowRoot.querySelector(
+                'extension-approvals-before');
+        assertNotEquals(null, extensionApprovalsBefore);
+
+        const askParentButton =
+            parentAccessBefore.shadowRoot.querySelector('.action-button');
+        askParentButton.click();
         await flushTasks();
 
         // Verify online flow is showing and switch to the after screen.
diff --git a/chrome/test/data/webui/chromeos/parent_access/parent_access_test_utils.js b/chrome/test/data/webui/chromeos/parent_access/parent_access_test_utils.js
index 175497c4..3d630e3c 100644
--- a/chrome/test/data/webui/chromeos/parent_access/parent_access_test_utils.js
+++ b/chrome/test/data/webui/chromeos/parent_access/parent_access_test_utils.js
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {ParentAccessParams, ParentAccessParams_FlowType, WebApprovalsParams} from 'chrome://parent-access/parent_access_ui.mojom-webui.js';
+import {ExtensionApprovalsParams, ExtensionApprovalsParams_ExtensionApprovalType, ParentAccessParams, ParentAccessParams_FlowType, WebApprovalsParams} from 'chrome://parent-access/parent_access_ui.mojom-webui.js';
 
 function strToMojoString16(str) {
   return {data: str.split('').map(ch => ch.charCodeAt(0))};
@@ -30,5 +30,12 @@
   const parentAccessParams = new ParentAccessParams();
   parentAccessParams.flowType = ParentAccessParams_FlowType.kExtensionAccess;
   parentAccessParams.isDisabled = isDisabled;
+  const extensionApprovalsParams = new ExtensionApprovalsParams();
+  extensionApprovalsParams.approvalType =
+      ExtensionApprovalsParams_ExtensionApprovalType.kAdd;
+  extensionApprovalsParams.extensionName = strToMojoString16('Extension name');
+  extensionApprovalsParams.iconPngBytes = [];
+  extensionApprovalsParams.childDisplayName = strToMojoString16('Child Name');
+  parentAccessParams.flowTypeParams = {extensionApprovalsParams};
   return parentAccessParams;
 }
diff --git a/chrome/test/data/webui/css/BUILD.gn b/chrome/test/data/webui/css/BUILD.gn
index 028513e0..c7210937 100644
--- a/chrome/test/data/webui/css/BUILD.gn
+++ b/chrome/test/data/webui/css/BUILD.gn
@@ -14,4 +14,7 @@
   if (is_chromeos_ash) {
     files += [ "color_provider_css_colors_test_chromeos.ts" ]
   }
+
+  ts_deps = [ "//ui/webui/resources/js:build_ts" ]
+  ts_tsconfig_base = "tsconfig_base.json"
 }
diff --git a/chrome/test/data/webui/css/css_browsertest.js b/chrome/test/data/webui/css/css_browsertest.js
index ec5454f..3d91cdfa 100644
--- a/chrome/test/data/webui/css/css_browsertest.js
+++ b/chrome/test/data/webui/css/css_browsertest.js
@@ -4,6 +4,7 @@
 
 GEN('#include "build/chromeos_buildflags.h"');
 GEN('#include "content/public/test/browser_test.h"');
+GEN('#include "ui/base/ui_base_features.h"');
 
 var CssTest = class extends testing.Test {
   /** @override */
@@ -30,7 +31,22 @@
 };
 
 TEST_F('TextDefaultsTest', 'All', function() {
-  mocha.run();
+  runMochaSuite('TextDefaults')
+});
+
+var TextDefaultsSystemFontTest = class extends TextDefaultsTest {
+  /** @override */
+  get featureList() {
+    return {
+      enabled: [
+        'features::kWebUiSystemFont',
+      ],
+    };
+  }
+}
+
+TEST_F('TextDefaultsSystemFontTest', 'All', function() {
+  runMochaSuite('TextDefaultsSystemFont')
 });
 
 var ColorProviderCSSColorsTest = class extends CssTest {
diff --git a/chrome/test/data/webui/css/text_defaults_test.ts b/chrome/test/data/webui/css/text_defaults_test.ts
index a2cb0dc..a6ebfc7 100644
--- a/chrome/test/data/webui/css/text_defaults_test.ts
+++ b/chrome/test/data/webui/css/text_defaults_test.ts
@@ -2,42 +2,126 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {assertNotEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {PromiseResolver} from 'chrome://resources/js/promise_resolver.js';
+import {assertTrue} from 'chrome://webui-test/chai_assert.js';
+
+function getExpectedFontFamily(expectingSystemFont: boolean): string {
+  if (!expectingSystemFont) {
+    return 'Roboto';
+  }
+
+  const fontFamily =
+      // <if expr="is_linux">
+      '"DejaVu Sans"';
+      // </if>
+      // <if expr="is_macosx">
+      'system-ui';
+      // </if>
+      // <if expr="is_win">
+      '"Segoe UI"';
+      // </if>
+      // <if expr="chromeos_ash">
+      'Roboto';
+      // </if>
+      // <if expr="chromeos_lacros">
+      // TODO(crbug.com/1427641): Change to 'Roboto' once bug is fixed.
+      'sans';
+      // </if>
+      // <if expr="is_fuchsia">
+      // TODO(dpapad): WebUI tests are compiled on Fuchsia but don't seem to run
+      // on any bot, so the value below does not matter, it just makes the code
+      // syntactically valid. Figure out whether the tests should be run, or
+      // excluded from compilation on Fuchsia.
+      'unknown';
+      // </if>
+
+  return fontFamily;
+}
+
+// Asserts that a CSS rule specifying font-family with the expected value
+// exists.
+function assertFontFamilyRule(
+    link: HTMLLinkElement, expectingSystemFont: boolean) {
+  assertTrue(!!link.sheet);
+  const styleRules =
+      Array.from(link.sheet.cssRules).filter(r => r instanceof CSSStyleRule) as
+      CSSStyleRule[];
+  assertTrue(styleRules.length > 0);
+
+  const fontFamily = styleRules[0]!.style.getPropertyValue('font-family');
+  const expectedFontFamily = getExpectedFontFamily(expectingSystemFont);
+  assertTrue(
+      fontFamily.startsWith(expectedFontFamily),
+      `Found: '${fontFamily.toString()}'`);
+}
+
+// Asserts that a 'div' inherits the expected font-family value.
+function assertFontFamilyApplied(expectingSystemFont: boolean) {
+  const div = document.createElement('div');
+  div.textContent = 'Dummy text';
+  document.body.appendChild(div);
+
+  const fontFamily = div.computedStyleMap().get('font-family');
+  assertTrue(!!fontFamily);
+  const expectedFontFamily = getExpectedFontFamily(expectingSystemFont);
+  assertTrue(
+      fontFamily.toString().startsWith(expectedFontFamily),
+      `Found: '${fontFamily.toString()}'`);
+}
+
+
+function testFontFamily(
+    cssFile: string, expectingSystemFont: boolean): Promise<void> {
+  const resolver = new PromiseResolver<void>();
+
+  const link = document.createElement('link');
+  link.rel = 'stylesheet';
+  link.href = cssFile;
+
+  link.onload = function() {
+    assertFontFamilyRule(link, expectingSystemFont);
+    assertFontFamilyApplied(expectingSystemFont);
+    resolver.resolve();
+  };
+  document.body.appendChild(link);
+
+  return resolver.promise;
+}
 
 suite('TextDefaults', function() {
   setup(function() {
     document.body.innerHTML = window.trustedTypes!.emptyHTML;
   });
 
-  function assertFontFamilyExists(link: HTMLLinkElement) {
-    assertTrue(!!link.sheet);
-    const styleRules =
-        Array.from(link.sheet.cssRules)
-            .filter(r => r instanceof CSSStyleRule) as CSSStyleRule[];
-    assertTrue(styleRules.length > 0);
-    const fontFamily = styleRules[0]!.style.getPropertyValue('font-family');
-    assertNotEquals('', fontFamily);
-  }
-
-  test('text_defaults.css', function(done) {
-    const link = document.createElement('link');
-    link.rel = 'stylesheet';
-    link.href = 'chrome://resources/css/text_defaults.css';
-    link.onload = function() {
-      assertFontFamilyExists(link);
-      done();
-    };
-    document.body.appendChild(link);
+  test('text_defaults.css', function() {
+    return testFontFamily(
+        'chrome://resources/css/text_defaults.css',
+        true /*expectingSystemFont*/);
   });
 
-  test('text_defaults_md.css', function(done) {
-    const link = document.createElement('link');
-    link.rel = 'stylesheet';
-    link.href = 'chrome://resources/css/text_defaults_md.css';
-    link.onload = function() {
-      assertFontFamilyExists(link);
-      done();
-    };
-    document.body.appendChild(link);
+  test('text_defaults_md.css', function() {
+    return testFontFamily(
+        'chrome://resources/css/text_defaults_md.css',
+        false /*expectingSystemFont*/);
+  });
+});
+
+// Test that text_defaults_md.css reverts back to the text_defaults.css
+// behavior when the WebUiSystemFont flag is enabled.
+suite('TextDefaultsSystemFont', function() {
+  setup(function() {
+    document.body.innerHTML = window.trustedTypes!.emptyHTML;
+  });
+
+  test('text_defaults.css', function() {
+    return testFontFamily(
+        'chrome://resources/css/text_defaults.css',
+        true /*expectingSystemFont*/);
+  });
+
+  test('text_defaults_md.css', function() {
+    return testFontFamily(
+        'chrome://resources/css/text_defaults_md.css',
+        true /*expectingSystemFont*/);
   });
 });
diff --git a/chrome/test/data/webui/css/tsconfig_base.json b/chrome/test/data/webui/css/tsconfig_base.json
new file mode 100644
index 0000000..5b74298
--- /dev/null
+++ b/chrome/test/data/webui/css/tsconfig_base.json
@@ -0,0 +1,8 @@
+{
+  "extends": "../../../../../chrome/test/data/webui/tsconfig_base.json",
+  "compilerOptions": {
+    "types": [
+      "chai", "mocha", "trusted-types", "w3c-css-typed-object-model-level-1"
+    ]
+  }
+}
diff --git a/chrome/test/data/webui/polymer_browser_test_base.js b/chrome/test/data/webui/polymer_browser_test_base.js
index e5f027f7..d19ae6d 100644
--- a/chrome/test/data/webui/polymer_browser_test_base.js
+++ b/chrome/test/data/webui/polymer_browser_test_base.js
@@ -32,32 +32,8 @@
 };
 
 /**
- * Imports the HTML file.
- * @param {string} src The URL to load.
- * @return {!Promise} A promise that is resolved/rejected on success/failure.
- */
-PolymerTest.importHtml = function(src) {
-  var link = document.createElement('link');
-  link.rel = 'import';
-  var promise = new Promise(function(resolve, reject) {
-    link.onload = resolve;
-    link.onerror = reject;
-  });
-  link.href = src;
-  document.head.appendChild(link);
-  return promise;
-};
-
-/**
- * Removes all content from the body. In a vulcanized build, this retains the
- * inlined tags so stylesheets and dom-modules are not discarded.
+ * Removes all content from the body.
  */
 PolymerTest.clearBody = function() {
-  // Save the div where vulcanize inlines content before clearing the page.
-  var vulcanizeDiv =
-      document.querySelector('body > div[hidden][by-polymer-bundler]');
-  document.body.innerHTML = '';
-  if (vulcanizeDiv) {
-    document.body.appendChild(vulcanizeDiv);
-  }
+  document.body.innerHTML = window.trustedTypes.emptyHTML;
 };
diff --git a/chrome/test/data/webui/settings/chromeos/BUILD.gn b/chrome/test/data/webui/settings/chromeos/BUILD.gn
index 95d05c5..05475e3 100644
--- a/chrome/test/data/webui/settings/chromeos/BUILD.gn
+++ b/chrome/test/data/webui/settings/chromeos/BUILD.gn
@@ -124,7 +124,6 @@
     "per_device_keyboard_remap_keys_test.js",
     "per_device_keyboard_test.js",
     "per_device_pointing_stick_subsection_test.js",
-    "per_device_touchpad_subsection_test.js",
     "personalization_page_with_personalization_hub_test.js",
     "privacy_hub_subpage_tests.js",
     "quick_unlock_authenticate_browsertest_chromeos.js",
@@ -206,6 +205,7 @@
     "device_page/per_device_mouse_subsection_test.ts",
     "device_page/per_device_mouse_test.ts",
     "device_page/per_device_pointing_stick_test.ts",
+    "device_page/per_device_touchpad_subsection_test.ts",
     "device_page/per_device_touchpad_test.ts",
 
     "internet_page/tether_connection_dialog_test.ts",
diff --git a/chrome/test/data/webui/settings/chromeos/device_page/per_device_touchpad_subsection_test.ts b/chrome/test/data/webui/settings/chromeos/device_page/per_device_touchpad_subsection_test.ts
new file mode 100644
index 0000000..f1a17fb
--- /dev/null
+++ b/chrome/test/data/webui/settings/chromeos/device_page/per_device_touchpad_subsection_test.ts
@@ -0,0 +1,325 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import {FakeInputDeviceSettingsProvider, fakeTouchpads, Router, routes, setInputDeviceSettingsProviderForTesting, SettingsPerDeviceTouchpadSubsectionElement, SettingsSliderElement, SettingsToggleButtonElement} from 'chrome://os-settings/chromeos/os_settings.js';
+import {CrToggleElement} from 'chrome://resources/cr_elements/cr_toggle/cr_toggle.js';
+import {assert} from 'chrome://resources/js/assert_ts.js';
+import {pressAndReleaseKeyOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
+import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {flushTasks, waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
+import {isVisible} from 'chrome://webui-test/test_util.js';
+
+const TOUCHPAD_SPEED_SETTING_ID = 405;
+
+suite('<settings-per-device-touchpad-subsection>', () => {
+  let subsection: SettingsPerDeviceTouchpadSubsectionElement;
+  let provider: FakeInputDeviceSettingsProvider;
+
+  setup(async () => {
+    provider = new FakeInputDeviceSettingsProvider();
+    provider.setFakeTouchpads(fakeTouchpads);
+    setInputDeviceSettingsProviderForTesting(provider);
+    subsection =
+        document.createElement('settings-per-device-touchpad-subsection');
+    assert(subsection);
+    subsection.set('touchpad', {...fakeTouchpads[0]});
+    subsection.set('allowScrollSettings_', true);
+    document.body.appendChild(subsection);
+    await flushTasks();
+  });
+
+  teardown(() => {
+    subsection.remove();
+  });
+
+  /**
+   * Test that API are updated when touchpad settings change.
+   */
+  test('Update API when touchpad settings change', async () => {
+    const enableTapToClickButton =
+        subsection.shadowRoot!.querySelector<SettingsToggleButtonElement>(
+            '#enableTapToClick');
+    assert(enableTapToClickButton);
+    enableTapToClickButton.click();
+    await flushTasks();
+    let updatedTouchpads = await provider.getConnectedTouchpadSettings();
+    assertEquals(
+        updatedTouchpads[0]!.settings.tapToClickEnabled,
+        enableTapToClickButton.pref!.value);
+
+    const enableTapDraggingButton =
+        subsection.shadowRoot!.querySelector<SettingsToggleButtonElement>(
+            '#enableTapDragging');
+    assert(enableTapDraggingButton);
+    enableTapDraggingButton.click();
+    await flushTasks();
+    updatedTouchpads = await provider.getConnectedTouchpadSettings();
+    assertEquals(
+        updatedTouchpads[0]!.settings.tapDraggingEnabled,
+        enableTapDraggingButton.pref!.value);
+
+    const touchpadAccelerationButton =
+        subsection.shadowRoot!.querySelector<SettingsToggleButtonElement>(
+            '#touchpadAcceleration');
+    assert(touchpadAccelerationButton);
+    touchpadAccelerationButton.click();
+    await flushTasks();
+    updatedTouchpads = await provider.getConnectedTouchpadSettings();
+    assertEquals(
+        updatedTouchpads[0]!.settings.accelerationEnabled,
+        touchpadAccelerationButton.pref!.value);
+
+    const touchpadScrollAccelerationButton =
+        subsection.shadowRoot!.querySelector<SettingsToggleButtonElement>(
+            '#touchpadScrollAcceleration');
+    assert(touchpadScrollAccelerationButton);
+    touchpadScrollAccelerationButton.click();
+    await flushTasks();
+    updatedTouchpads = await provider.getConnectedTouchpadSettings();
+    assertEquals(
+        updatedTouchpads[0]!.settings.scrollAcceleration,
+        touchpadScrollAccelerationButton.pref!.value);
+
+    const touchpadScrollSpeedSlider =
+        subsection.shadowRoot!.querySelector<SettingsSliderElement>(
+            '#touchpadScrollSpeedSlider');
+    assert(touchpadScrollSpeedSlider);
+    pressAndReleaseKeyOn(
+        touchpadScrollSpeedSlider.shadowRoot!.querySelector('cr-slider')!, 39,
+        [], 'ArrowRight');
+    await flushTasks();
+    updatedTouchpads = await provider.getConnectedTouchpadSettings();
+    assertEquals(
+        updatedTouchpads[0]!.settings.scrollSensitivity,
+        touchpadScrollSpeedSlider.pref!.value);
+
+    const touchpadSensitivitySlider =
+        subsection.shadowRoot!.querySelector<SettingsSliderElement>(
+            '#touchpadSensitivity');
+    assert(touchpadSensitivitySlider);
+    pressAndReleaseKeyOn(
+        touchpadSensitivitySlider.shadowRoot!.querySelector('cr-slider')!, 39,
+        [], 'ArrowRight');
+    await flushTasks();
+    updatedTouchpads = await provider.getConnectedTouchpadSettings();
+    assertEquals(
+        updatedTouchpads[0]!.settings.sensitivity,
+        touchpadSensitivitySlider.pref!.value);
+
+    const touchpadHapticClickSensitivitySlider =
+        subsection.shadowRoot!.querySelector<SettingsSliderElement>(
+            '#touchpadHapticClickSensitivity');
+    assert(touchpadHapticClickSensitivitySlider);
+    pressAndReleaseKeyOn(
+        touchpadHapticClickSensitivitySlider.shadowRoot!.querySelector(
+            'cr-slider')!,
+        39, [], 'ArrowRight');
+    await flushTasks();
+    updatedTouchpads = await provider.getConnectedTouchpadSettings();
+    assertEquals(
+        updatedTouchpads[0]!.settings.hapticSensitivity,
+        touchpadHapticClickSensitivitySlider.pref!.value);
+
+    const touchpadHapticFeedbackToggleButton =
+        subsection.shadowRoot!.querySelector<CrToggleElement>(
+            '#touchpadHapticFeedbackToggle');
+    touchpadHapticFeedbackToggleButton!.click();
+    await flushTasks();
+    updatedTouchpads = await provider.getConnectedTouchpadSettings();
+    assertEquals(
+        updatedTouchpads[0]!.settings.hapticEnabled,
+        touchpadHapticFeedbackToggleButton!.checked);
+
+    const touchpadReverseScrollToggleButton =
+        subsection.shadowRoot!.querySelector<CrToggleElement>(
+            '#enableReverseScrollingToggle');
+    touchpadReverseScrollToggleButton!.click();
+    await flushTasks();
+    updatedTouchpads = await provider.getConnectedTouchpadSettings();
+    assertEquals(
+        updatedTouchpads[0]!.settings.reverseScrolling,
+        touchpadReverseScrollToggleButton!.checked);
+  });
+
+  /**
+   * Test that touchpad settings data are from the touchpad provider.
+   */
+  test('Verify touchpad settings data', async () => {
+    let enableTapToClickButton =
+        subsection.shadowRoot!.querySelector<SettingsToggleButtonElement>(
+            '#enableTapToClick');
+    assertEquals(
+        fakeTouchpads[0]!.settings.tapToClickEnabled,
+        enableTapToClickButton!.pref!.value);
+    let enableTapDraggingButton =
+        subsection.shadowRoot!.querySelector<SettingsToggleButtonElement>(
+            '#enableTapDragging');
+    assertEquals(
+        fakeTouchpads[0]!.settings.tapDraggingEnabled,
+        enableTapDraggingButton!.pref!.value);
+    let touchpadAcceleration =
+        subsection.shadowRoot!.querySelector<SettingsToggleButtonElement>(
+            '#touchpadAcceleration');
+    assertEquals(
+        fakeTouchpads[0]!.settings.accelerationEnabled,
+        touchpadAcceleration!.pref!.value);
+    let touchpadScrollAccelerationButton =
+        subsection.shadowRoot!.querySelector<SettingsToggleButtonElement>(
+            '#touchpadScrollAcceleration');
+    assertTrue(isVisible(touchpadScrollAccelerationButton));
+    assertEquals(
+        fakeTouchpads[0]!.settings.scrollAcceleration,
+        touchpadScrollAccelerationButton!.pref!.value);
+    let touchpadScrollSpeedSlider =
+        subsection.shadowRoot!.querySelector<SettingsSliderElement>(
+            '#touchpadScrollSpeedSlider');
+    assertTrue(isVisible(touchpadScrollSpeedSlider));
+    assertEquals(
+        fakeTouchpads[0]!.settings.scrollSensitivity,
+        touchpadScrollSpeedSlider!.pref!.value);
+    let touchpadSensitivitySlider =
+        subsection.shadowRoot!.querySelector<SettingsSliderElement>(
+            '#touchpadSensitivity');
+    assertEquals(
+        fakeTouchpads[0]!.settings.sensitivity,
+        touchpadSensitivitySlider!.pref!.value);
+    let touchpadHapticClickSensitivitySlider =
+        subsection.shadowRoot!.querySelector<SettingsSliderElement>(
+            '#touchpadHapticClickSensitivity');
+    assertTrue(isVisible(touchpadHapticClickSensitivitySlider));
+    assertEquals(
+        fakeTouchpads[0]!.settings.hapticSensitivity,
+        touchpadHapticClickSensitivitySlider!.pref!.value);
+    let touchpadHapticFeedbackToggleButton =
+        subsection.shadowRoot!.querySelector<CrToggleElement>(
+            '#touchpadHapticFeedbackToggle');
+    assertTrue(isVisible(touchpadHapticFeedbackToggleButton));
+    assertEquals(
+        fakeTouchpads[0]!.settings.hapticEnabled,
+        touchpadHapticFeedbackToggleButton!.checked);
+    assertEquals(
+        fakeTouchpads[0]!.settings.reverseScrolling,
+        subsection.get('reverseScrollValue'));
+
+    subsection.set('touchpad', fakeTouchpads[1]);
+    subsection.set('allowScrollSettings_', false);
+
+    await flushTasks();
+    enableTapToClickButton =
+        subsection.shadowRoot!.querySelector('#enableTapToClick');
+    assertEquals(
+        fakeTouchpads[1]!.settings.tapToClickEnabled,
+        enableTapToClickButton!.pref!.value);
+    enableTapDraggingButton =
+        subsection.shadowRoot!.querySelector('#enableTapDragging');
+    assertEquals(
+        fakeTouchpads[1]!.settings.tapDraggingEnabled,
+        enableTapDraggingButton!.pref!.value);
+    touchpadAcceleration =
+        subsection.shadowRoot!.querySelector('#touchpadAcceleration');
+    assertEquals(
+        fakeTouchpads[1]!.settings.accelerationEnabled,
+        touchpadAcceleration!.pref!.value);
+    touchpadScrollAccelerationButton =
+        subsection.shadowRoot!.querySelector('#touchpadScrollAcceleration');
+    assertFalse(isVisible(touchpadScrollAccelerationButton));
+    touchpadScrollSpeedSlider =
+        subsection.shadowRoot!.querySelector('#touchpadScrollSpeedSlider');
+    assertFalse(isVisible(touchpadScrollSpeedSlider));
+    touchpadSensitivitySlider =
+        subsection.shadowRoot!.querySelector('#touchpadSensitivity');
+    assertEquals(
+        fakeTouchpads[1]!.settings.sensitivity,
+        touchpadSensitivitySlider!.pref!.value);
+    touchpadHapticClickSensitivitySlider =
+        subsection.shadowRoot!.querySelector('#touchpadHapticClickSensitivity');
+    assertFalse(isVisible(touchpadHapticClickSensitivitySlider));
+    touchpadHapticFeedbackToggleButton =
+        subsection.shadowRoot!.querySelector('#touchpadHapticFeedbackToggle');
+    assertFalse(isVisible(touchpadHapticFeedbackToggleButton));
+    assertEquals(
+        fakeTouchpads[1]!.settings.reverseScrolling,
+        subsection.get('reverseScrollValue'));
+  });
+
+  /**
+   * Test haptic settings are correctly show or hidden based on the touchpad is
+   * haptic or not.
+   */
+  test('Verify haptic settings visibility', async () => {
+    // Change the isHaptic state to true.
+    subsection.set('touchpad.isHaptic', true);
+    await flushTasks();
+    // Verify haptic click sensitivity slider is visible in the page.
+    let hapticClickSensitivitySlider =
+        subsection.shadowRoot!.querySelector<SettingsSliderElement>(
+            '#touchpadHapticClickSensitivity');
+    assertTrue(isVisible(hapticClickSensitivitySlider));
+
+    // Verify haptic feedback toggle button is visible in the page.
+    let hapticFeedbackToggleButton =
+        subsection.shadowRoot!.querySelector('#touchpadHapticFeedbackToggle');
+    assertTrue(isVisible(hapticFeedbackToggleButton));
+
+    // Change the isHaptic state to false.
+    subsection.set('touchpad.isHaptic', false);
+    await flushTasks();
+    // Verify haptic click sensitivity slider is not visible in the page.
+    hapticClickSensitivitySlider =
+        subsection.shadowRoot!.querySelector('#touchpadHapticClickSensitivity');
+    assertFalse(isVisible(hapticClickSensitivitySlider));
+
+    // Verify haptic feedback toggle button is not visible in the page.
+    hapticFeedbackToggleButton =
+        subsection.shadowRoot!.querySelector('#touchpadHapticFeedbackToggle');
+    assertFalse(isVisible(hapticFeedbackToggleButton));
+  });
+
+  /**
+   * Verify entering the page with search tags matched will auto focus the
+   * searched element.
+   */
+  test('Deep linking mixin focus on the first searched element', async () => {
+    const touchpadSensitivitySlider =
+        subsection.shadowRoot!.querySelector<SettingsSliderElement>(
+            '#touchpadSensitivity');
+    subsection.set('touchpadIndex', 0);
+    // Enter the page from auto repeat search tag.
+    const url = new URLSearchParams(
+        'search=touchpad+speed&settingId=' +
+        encodeURIComponent(TOUCHPAD_SPEED_SETTING_ID));
+
+    await Router.getInstance().navigateTo(
+        routes.PER_DEVICE_TOUCHPAD,
+        /* dynamicParams= */ url, /* removeSearch= */ true);
+
+    assert(touchpadSensitivitySlider);
+    await waitAfterNextRender(touchpadSensitivitySlider);
+    assertEquals(
+        touchpadSensitivitySlider, subsection.shadowRoot!.activeElement);
+  });
+
+  /**
+   * Verify entering the page with search tags matched wll not auto focus the
+   * searched element if it's not the first keyboard displayed.
+   */
+  test('Deep linkng mixin does not focus on second element', async () => {
+    const touchpadSensitivitySlider =
+        subsection.shadowRoot!.querySelector<SettingsSliderElement>(
+            '#touchpadSensitivity');
+    subsection.set('touchpadIndex', 1);
+    // Enter the page from auto repeat search tag.
+    const url = new URLSearchParams(
+        'search=touchpad+speed&settingId=' +
+        encodeURIComponent(TOUCHPAD_SPEED_SETTING_ID));
+
+    await Router.getInstance().navigateTo(
+        routes.PER_DEVICE_TOUCHPAD,
+        /* dynamicParams= */ url, /* removeSearch= */ true);
+    await flushTasks();
+
+    assert(touchpadSensitivitySlider);
+    assertEquals(null, subsection.shadowRoot!.activeElement);
+  });
+});
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js b/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js
index 3342b79..ba427dc 100644
--- a/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js
+++ b/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js
@@ -285,6 +285,11 @@
    {enabled: ['ash::features::kInputDeviceSettingsSplit']},
  ],
  [
+   'DevicePagePerDeviceTouchpadSubsection',
+   'device_page/per_device_touchpad_subsection_test.js',
+   {enabled: ['ash::features::kInputDeviceSettingsSplit']}
+ ],
+ [
    'DisplayAndMagnificationPage',
    'display_and_magnification_page_tests.js',
  ],
@@ -489,10 +494,6 @@
    {enabled: ['ash::features::kInputDeviceSettingsSplit']}
  ],
  [
-   'PerDeviceTouchpadSubsection', 'per_device_touchpad_subsection_test.js',
-   {enabled: ['ash::features::kInputDeviceSettingsSplit']}
- ],
- [
    'PersonalizationPageWithPersonalizationHub',
    'personalization_page_with_personalization_hub_test.js',
  ],
diff --git a/chrome/test/data/webui/settings/chromeos/per_device_touchpad_subsection_test.js b/chrome/test/data/webui/settings/chromeos/per_device_touchpad_subsection_test.js
deleted file mode 100644
index b6feef9..0000000
--- a/chrome/test/data/webui/settings/chromeos/per_device_touchpad_subsection_test.js
+++ /dev/null
@@ -1,334 +0,0 @@
-// Copyright 2023 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
-
-import {FakeInputDeviceSettingsProvider, fakeTouchpads, Router, routes, setInputDeviceSettingsProviderForTesting, SettingsPerDeviceTouchpadSubsectionElement} from 'chrome://os-settings/chromeos/os_settings.js';
-import {assert} from 'chrome://resources/ash/common/assert.js';
-import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {flushTasks, waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
-import {isVisible} from 'chrome://webui-test/test_util.js';
-
-const TOUCHPAD_SPEED_SETTING_ID = 405;
-
-suite('PerDeviceTouchpadSubsection', function() {
-  /**
-   * @type {?SettingsPerDeviceTouchpadSubsectionElement}
-   */
-  let subsection = null;
-  /**
-   * @type {?FakeInputDeviceSettingsProvider}
-   */
-  let provider = null;
-
-
-  setup(() => {
-    PolymerTest.clearBody();
-  });
-
-  teardown(() => {
-    subsection = null;
-    provider = null;
-  });
-
-  /**
-   * @return {!Promise}
-   */
-  function initializePerDeviceTouchpadSubsection() {
-    provider = new FakeInputDeviceSettingsProvider();
-    provider.setFakeTouchpads(fakeTouchpads);
-    setInputDeviceSettingsProviderForTesting(provider);
-    subsection =
-        document.createElement('settings-per-device-touchpad-subsection');
-    assertTrue(subsection != null);
-    subsection.touchpad = {...fakeTouchpads[0]};
-    subsection.allowScrollSettings_ = true;
-    document.body.appendChild(subsection);
-    return flushTasks();
-  }
-
-  /**
-   * @param {!Boolean} isHaptic
-   * @return {!Promise}
-   */
-  function changeIsHapticState(isHaptic) {
-    subsection.touchpad = {...subsection.touchpad, isHaptic: isHaptic};
-    return flushTasks();
-  }
-
-  /**
-   * @param {!Object} touchpad
-   * @param {!Boolean} allowScrollSettings
-   * @return {!Promise}
-   */
-  function changeTouchpadSubsectionState(touchpad, allowScrollSettings) {
-    subsection.touchpad = touchpad;
-    subsection.allowScrollSettings_ = allowScrollSettings;
-    return flushTasks();
-  }
-
-  async function getConnectedTouchpadSettings() {
-    const touchpads = await provider.getConnectedTouchpadSettings();
-    return touchpads;
-  }
-
-  // Test that API are updated when touchpad settings change.
-  test('Update API when touchpad settings change', async () => {
-    await initializePerDeviceTouchpadSubsection();
-    const enableTapToClickButton =
-        subsection.shadowRoot.querySelector('#enableTapToClick');
-    enableTapToClickButton.click();
-    await flushTasks();
-    let updatedTouchpads = await getConnectedTouchpadSettings();
-    assertEquals(
-        updatedTouchpads[0].settings.tapToClickEnabled,
-        enableTapToClickButton.pref.value);
-
-    const enableTapDraggingButton =
-        subsection.shadowRoot.querySelector('#enableTapDragging');
-    enableTapDraggingButton.click();
-    await flushTasks();
-    updatedTouchpads = await getConnectedTouchpadSettings();
-    assertEquals(
-        updatedTouchpads[0].settings.tapDraggingEnabled,
-        enableTapDraggingButton.pref.value);
-
-    const touchpadAccelerationButton =
-        subsection.shadowRoot.querySelector('#touchpadAcceleration');
-    touchpadAccelerationButton.click();
-    await flushTasks();
-    updatedTouchpads = await getConnectedTouchpadSettings();
-    assertEquals(
-        updatedTouchpads[0].settings.accelerationEnabled,
-        touchpadAccelerationButton.pref.value);
-
-    const touchpadScrollAccelerationButton =
-        subsection.shadowRoot.querySelector('#touchpadScrollAcceleration');
-    touchpadScrollAccelerationButton.click();
-    await flushTasks();
-    updatedTouchpads = await getConnectedTouchpadSettings();
-    assertEquals(
-        updatedTouchpads[0].settings.scrollAcceleration,
-        touchpadScrollAccelerationButton.pref.value);
-
-    const touchpadScrollSpeedSlider = assert(
-        subsection.shadowRoot.querySelector('#touchpadScrollSpeedSlider'));
-    MockInteractions.pressAndReleaseKeyOn(
-        touchpadScrollSpeedSlider.shadowRoot.querySelector('cr-slider'),
-        39 /* right */, [], 'ArrowRight');
-    await flushTasks();
-    updatedTouchpads = await getConnectedTouchpadSettings();
-    assertEquals(
-        updatedTouchpads[0].settings.scrollSensitivity,
-        touchpadScrollSpeedSlider.pref.value);
-
-    const touchpadSensitivitySlider =
-        assert(subsection.shadowRoot.querySelector('#touchpadSensitivity'));
-    MockInteractions.pressAndReleaseKeyOn(
-        touchpadSensitivitySlider.shadowRoot.querySelector('cr-slider'),
-        39 /* right */, [], 'ArrowRight');
-    await flushTasks();
-    updatedTouchpads = await getConnectedTouchpadSettings();
-    assertEquals(
-        updatedTouchpads[0].settings.sensitivity,
-        touchpadSensitivitySlider.pref.value);
-
-    const touchpadHapticClickSensitivitySlider = assert(
-        subsection.shadowRoot.querySelector('#touchpadHapticClickSensitivity'));
-    MockInteractions.pressAndReleaseKeyOn(
-        touchpadHapticClickSensitivitySlider.shadowRoot.querySelector(
-            'cr-slider'),
-        39 /* right */, [], 'ArrowRight');
-    await flushTasks();
-    updatedTouchpads = await getConnectedTouchpadSettings();
-    assertEquals(
-        updatedTouchpads[0].settings.hapticSensitivity,
-        touchpadHapticClickSensitivitySlider.pref.value);
-
-    const touchpadHapticFeedbackToggleButton =
-        subsection.shadowRoot.querySelector('#touchpadHapticFeedbackToggle');
-    touchpadHapticFeedbackToggleButton.click();
-    await flushTasks();
-    updatedTouchpads = await getConnectedTouchpadSettings();
-    assertEquals(
-        updatedTouchpads[0].settings.hapticEnabled,
-        touchpadHapticFeedbackToggleButton.checked);
-
-    const touchpadReverseScrollToggleButton =
-        subsection.shadowRoot.querySelector('#enableReverseScrollingToggle');
-    touchpadReverseScrollToggleButton.click();
-    await flushTasks();
-    updatedTouchpads = await getConnectedTouchpadSettings();
-    assertEquals(
-        updatedTouchpads[0].settings.reverseScrolling,
-        touchpadReverseScrollToggleButton.checked);
-  });
-
-  // Test that touchpad settings data are from the touchpad provider.
-  test('Verify touchpad settings data', async () => {
-    await initializePerDeviceTouchpadSubsection();
-    let enableTapToClickButton =
-        subsection.shadowRoot.querySelector('#enableTapToClick');
-    assertEquals(
-        fakeTouchpads[0].settings.tapToClickEnabled,
-        enableTapToClickButton.pref.value);
-    let enableTapDraggingButton =
-        subsection.shadowRoot.querySelector('#enableTapDragging');
-    assertEquals(
-        fakeTouchpads[0].settings.tapDraggingEnabled,
-        enableTapDraggingButton.pref.value);
-    let touchpadAcceleration =
-        subsection.shadowRoot.querySelector('#touchpadAcceleration');
-    assertEquals(
-        fakeTouchpads[0].settings.accelerationEnabled,
-        touchpadAcceleration.pref.value);
-    let touchpadScrollAccelerationButton =
-        subsection.shadowRoot.querySelector('#touchpadScrollAcceleration');
-    assertTrue(isVisible(touchpadScrollAccelerationButton));
-    assertEquals(
-        fakeTouchpads[0].settings.scrollAcceleration,
-        touchpadScrollAccelerationButton.pref.value);
-    let touchpadScrollSpeedSlider = assert(
-        subsection.shadowRoot.querySelector('#touchpadScrollSpeedSlider'));
-    assertTrue(isVisible(touchpadScrollSpeedSlider));
-    assertEquals(
-        fakeTouchpads[0].settings.scrollSensitivity,
-        touchpadScrollSpeedSlider.pref.value);
-    let touchpadSensitivitySlider =
-        assert(subsection.shadowRoot.querySelector('#touchpadSensitivity'));
-    assertEquals(
-        fakeTouchpads[0].settings.sensitivity,
-        touchpadSensitivitySlider.pref.value);
-    let touchpadHapticClickSensitivitySlider = assert(
-        subsection.shadowRoot.querySelector('#touchpadHapticClickSensitivity'));
-    assertTrue(isVisible(touchpadHapticClickSensitivitySlider));
-    assertEquals(
-        fakeTouchpads[0].settings.hapticSensitivity,
-        touchpadHapticClickSensitivitySlider.pref.value);
-    let touchpadHapticFeedbackToggleButton =
-        subsection.shadowRoot.querySelector('#touchpadHapticFeedbackToggle');
-    assertTrue(isVisible(touchpadHapticFeedbackToggleButton));
-    assertEquals(
-        fakeTouchpads[0].settings.hapticEnabled,
-        touchpadHapticFeedbackToggleButton.checked);
-    assertEquals(
-        fakeTouchpads[0].settings.reverseScrolling,
-        subsection.reverseScrollValue);
-
-    await changeTouchpadSubsectionState(fakeTouchpads[1], false);
-    enableTapToClickButton =
-        subsection.shadowRoot.querySelector('#enableTapToClick');
-    assertEquals(
-        fakeTouchpads[1].settings.tapToClickEnabled,
-        enableTapToClickButton.pref.value);
-    enableTapDraggingButton =
-        subsection.shadowRoot.querySelector('#enableTapDragging');
-    assertEquals(
-        fakeTouchpads[1].settings.tapDraggingEnabled,
-        enableTapDraggingButton.pref.value);
-    touchpadAcceleration =
-        subsection.shadowRoot.querySelector('#touchpadAcceleration');
-    assertEquals(
-        fakeTouchpads[1].settings.accelerationEnabled,
-        touchpadAcceleration.pref.value);
-    touchpadScrollAccelerationButton =
-        subsection.shadowRoot.querySelector('#touchpadScrollAcceleration');
-    assertFalse(isVisible(touchpadScrollAccelerationButton));
-    touchpadScrollSpeedSlider =
-        subsection.shadowRoot.querySelector('#touchpadScrollSpeedSlider');
-    assertFalse(isVisible(touchpadScrollSpeedSlider));
-    touchpadSensitivitySlider =
-        assert(subsection.shadowRoot.querySelector('#touchpadSensitivity'));
-    assertEquals(
-        fakeTouchpads[1].settings.sensitivity,
-        touchpadSensitivitySlider.pref.value);
-    touchpadHapticClickSensitivitySlider =
-        subsection.shadowRoot.querySelector('#touchpadHapticClickSensitivity');
-    assertFalse(isVisible(touchpadHapticClickSensitivitySlider));
-    touchpadHapticFeedbackToggleButton =
-        subsection.shadowRoot.querySelector('#touchpadHapticFeedbackToggle');
-    assertFalse(isVisible(touchpadHapticFeedbackToggleButton));
-    assertEquals(
-        fakeTouchpads[1].settings.reverseScrolling,
-        subsection.reverseScrollValue);
-  });
-
-  /**
-   * Test haptic settings are correctly show or hidden based on the touchpad is
-   * haptic or not.
-   */
-  test('Verify haptic settings visbility', async () => {
-    await initializePerDeviceTouchpadSubsection();
-    // Change the isHaptic state to true.
-    await changeIsHapticState(true);
-    // Verify haptic click sensitivity slider is visible in the page.
-    let hapticClickSensitivitySlider =
-        subsection.shadowRoot.querySelector('#touchpadHapticClickSensitivity');
-    assertTrue(isVisible(hapticClickSensitivitySlider));
-
-    // Verify haptic feedback toggle button is visible in the page.
-    let hapticFeedbackToggleButton =
-        subsection.shadowRoot.querySelector('#touchpadHapticFeedbackToggle');
-    assertTrue(isVisible(hapticFeedbackToggleButton));
-
-    // Change the isHaptic state to false.
-    await changeIsHapticState(false);
-    // Verify haptic click sensitivity slider is not visible in the page.
-    hapticClickSensitivitySlider =
-        subsection.shadowRoot.querySelector('#touchpadHapticClickSensitivity');
-    assertFalse(isVisible(hapticClickSensitivitySlider));
-
-    // Verify haptic feedback toggle button is not visible in the page.
-    hapticFeedbackToggleButton =
-        subsection.shadowRoot.querySelector('#touchpadHapticFeedbackToggle');
-    assertFalse(isVisible(hapticFeedbackToggleButton));
-  });
-
-  /**
-   * Verify entering the page with search tags matched will auto focus the
-   * searched element.
-   */
-  test('deep linking mixin focus on the first searched element', async () => {
-    await initializePerDeviceTouchpadSubsection();
-    const touchpadSensitivitySlider =
-        subsection.shadowRoot.querySelector('#touchpadSensitivity');
-    subsection.touchpadIndex = 0;
-    // Enter the page from auto repeat search tag.
-    const url = new URLSearchParams(
-        'search=touchpad+speed&settingId=' +
-        encodeURIComponent(TOUCHPAD_SPEED_SETTING_ID));
-
-    await Router.getInstance().navigateTo(
-        routes.PER_DEVICE_TOUCHPAD,
-        /* dynamicParams= */ url, /* removeSearch= */ true);
-
-    await waitAfterNextRender(touchpadSensitivitySlider);
-    assertTrue(!!touchpadSensitivitySlider);
-    assertEquals(
-        subsection.shadowRoot.activeElement, touchpadSensitivitySlider);
-  });
-
-  /**
-   * Verify entering the page with search tags matched wll not auto focus the
-   * searched element if it's not the first keyboard displayed.
-   */
-  test('deep linkng mixin does not focus on second element', async () => {
-    await initializePerDeviceTouchpadSubsection();
-    const touchpadSensitivitySlider =
-        subsection.shadowRoot.querySelector('#touchpadSensitivity');
-    subsection.touchpadIndex = 1;
-    // Enter the page from auto repeat search tag.
-    const url = new URLSearchParams(
-        'search=touchpad+speed&settingId=' +
-        encodeURIComponent(TOUCHPAD_SPEED_SETTING_ID));
-
-    await Router.getInstance().navigateTo(
-        routes.PER_DEVICE_TOUCHPAD,
-        /* dynamicParams= */ url, /* removeSearch= */ true);
-    await flushTasks();
-
-    assertTrue(!!touchpadSensitivitySlider);
-    assertFalse(!!subsection.shadowRoot.activeElement);
-  });
-});
diff --git a/chromeos/ash/components/dbus/BUILD.gn b/chromeos/ash/components/dbus/BUILD.gn
index 799d131..4119288 100644
--- a/chromeos/ash/components/dbus/BUILD.gn
+++ b/chromeos/ash/components/dbus/BUILD.gn
@@ -96,6 +96,16 @@
   ]
 }
 
+# TODO(b/273117306): The below protos generate to
+# gen/chromeos/ash/components/dbus/* which is the equivalent of
+# *this build file*. This forces us to redefine proto_out_dir every
+# time and jump through hoops for importing protos. To minimise
+# configuration a better approach uses the default gen location
+# gen/third_party/cros_system_api/dbus/* and avoids the below cfg.
+config("system_api_proto_include_cfg") {
+  include_dirs = [ "${root_gen_dir}/chromeos/ash/components/dbus" ]
+}
+
 proto_library("metrics_event_proto") {
   sources =
       [ "//third_party/cros_system_api/dbus/metrics_event/metrics_event.proto" ]
@@ -135,6 +145,14 @@
   proto_out_dir = "chromeos/ash/components/dbus/vm_sk_forwarding"
 }
 
+proto_library("vm_wl_proto") {
+  sources = [ "//third_party/cros_system_api/dbus/vm_wl/wl.proto" ]
+  proto_out_dir = "chromeos/ash/components/dbus/vm_wl"
+  proto_deps = [ ":vm_applications_apps_proto" ]
+  import_dirs = [ "//third_party/cros_system_api/dbus" ]
+  extra_configs = [ ":system_api_proto_include_cfg" ]
+}
+
 proto_library("vm_permission_service_proto") {
   sources = [ "//third_party/cros_system_api/dbus/vm_permission_service/vm_permission_service.proto" ]
 
diff --git a/chromeos/ash/components/dbus/featured/fake_featured_client.cc b/chromeos/ash/components/dbus/featured/fake_featured_client.cc
index 73e233ca..bc88d348 100644
--- a/chromeos/ash/components/dbus/featured/fake_featured_client.cc
+++ b/chromeos/ash/components/dbus/featured/fake_featured_client.cc
@@ -4,9 +4,8 @@
 
 #include "chromeos/ash/components/dbus/featured/fake_featured_client.h"
 
-#include <string>
-
 #include "base/check_op.h"
+#include "base/containers/queue.h"
 #include "base/functional/callback.h"
 #include "chromeos/ash/components/dbus/featured/featured.pb.h"
 #include "dbus/object_proxy.h"
@@ -35,14 +34,25 @@
   return g_instance;
 }
 
-void FakeFeaturedClient::SetCallbackSuccess(bool success) {
-  success_ = success;
-}
-
 void FakeFeaturedClient::HandleSeedFetched(
     const ::featured::SeedDetails& safe_seed,
     base::OnceCallback<void(bool success)> callback) {
-  std::move(callback).Run(success_);
+  handle_seed_fetched_attempts_++;
+
+  if (responses_.empty()) {
+    LOG(ERROR) << "Insufficient amount of responses added. Call AddResponse to "
+                  "add expected response to invoke the callback with";
+    std::move(callback).Run(false);
+    return;
+  }
+
+  bool success = responses_.front();
+  responses_.pop();
+  std::move(callback).Run(success);
+}
+
+void FakeFeaturedClient::AddResponse(bool success) {
+  responses_.push(success);
 }
 
 }  // namespace ash::featured
diff --git a/chromeos/ash/components/dbus/featured/fake_featured_client.h b/chromeos/ash/components/dbus/featured/fake_featured_client.h
index 2f204470..a81ceaf 100644
--- a/chromeos/ash/components/dbus/featured/fake_featured_client.h
+++ b/chromeos/ash/components/dbus/featured/fake_featured_client.h
@@ -6,7 +6,9 @@
 #define CHROMEOS_ASH_COMPONENTS_DBUS_FEATURED_FAKE_FEATURED_CLIENT_H_
 
 #include "base/component_export.h"
+#include "base/containers/queue.h"
 #include "base/functional/callback.h"
+#include "base/logging.h"
 #include "chromeos/ash/components/dbus/featured/featured.pb.h"
 #include "chromeos/ash/components/dbus/featured/featured_client.h"
 
@@ -17,25 +19,35 @@
     : public FeaturedClient {
  public:
   FakeFeaturedClient();
+
   FakeFeaturedClient(const FakeFeaturedClient&) = delete;
   FakeFeaturedClient& operator=(const FakeFeaturedClient&) = delete;
+
   ~FakeFeaturedClient() override;
 
   // Returns the global FakeFeaturedClient instance. Returns `nullptr` if
   // it is not initialized.
   static FakeFeaturedClient* Get();
 
-  // Sets the callback parameter for `HandleSeedFetched`.
-  void SetCallbackSuccess(bool success);
-
-  // |callback| is run with true by default. Call |SetCallbackSuccess|
-  // to change the callback parameter.
+  // |callback| is run with the first response in `responses_`. Call
+  // `AddResponse` to add a response. |callback| is invoked with false if not
+  // enough responses are provided.
   void HandleSeedFetched(
       const ::featured::SeedDetails& safe_seed,
       base::OnceCallback<void(bool success)> callback) override;
 
+  // Adds a response to call `HandleSeedFetched` with.
+  void AddResponse(bool success);
+
+  // Returns the number of times `HandleSeedFetched` was called. Used for
+  // testing.
+  int handle_seed_fetched_attempts() const {
+    return handle_seed_fetched_attempts_;
+  }
+
  private:
-  bool success_ = true;
+  base::queue<bool> responses_;
+  size_t handle_seed_fetched_attempts_ = 0;
 };
 
 }  // namespace ash::featured
diff --git a/chromeos/ash/components/dbus/featured/featured_client_unittest.cc b/chromeos/ash/components/dbus/featured/featured_client_unittest.cc
index 783e74b..497bb49 100644
--- a/chromeos/ash/components/dbus/featured/featured_client_unittest.cc
+++ b/chromeos/ash/components/dbus/featured/featured_client_unittest.cc
@@ -108,8 +108,8 @@
   EXPECT_EQ(FeaturedClient::Get(), nullptr);
 }
 
-// Check that HandleSeedFetched runs the callback with a false success value if
-// the server (platform) returns an error responses.
+// Check that `HandleSeedFetched` runs the callback with a false success value
+// if the server (platform) returns an error responses.
 TEST_F(FeaturedClientTest, HandleSeedFetched_Failure_ErrorResponse) {
   EXPECT_CALL(*proxy_,
               DoCallMethod(_, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, _))
@@ -144,8 +144,8 @@
   EXPECT_EQ(FeaturedClient::Get(), nullptr);
 }
 
-// Check that HandleSeedFetched runs the callback with a false success value if
-// the method call is unsuccessful (response is a nullptr).
+// Check that `HandleSeedFetched` runs the callback with a false success value
+// if the method call is unsuccessful (response is a nullptr).
 TEST_F(FeaturedClientTest, HandleSeedFetched_Failure_NullResponse) {
   EXPECT_CALL(*proxy_,
               DoCallMethod(_, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, _))
@@ -175,9 +175,9 @@
   EXPECT_EQ(FeaturedClient::Get(), nullptr);
 }
 
-// Check that Fake runs HandleSeedFetched callback with a true success value by
-// default.
-TEST_F(FeaturedClientTest, FakeHandleSeedFetched_Success) {
+// Check that Fake runs `HandleSeedFetched` callback with a false success value
+// by default (no expected responses added).
+TEST_F(FeaturedClientTest, FakeHandleSeedFetched_InvokeFalseByDefault) {
   FeaturedClient::InitializeFake();
   FakeFeaturedClient* client = FakeFeaturedClient::Get();
 
@@ -188,36 +188,38 @@
   bool ran_callback = false;
   client->HandleSeedFetched(
       safe_seed, base::BindLambdaForTesting([&ran_callback](bool success) {
-        EXPECT_TRUE(success);
-        ran_callback = true;
-      }));
-  // Ensures the callback was executed.
-  EXPECT_TRUE(ran_callback);
-
-  FeaturedClient::Shutdown();
-
-  EXPECT_EQ(FakeFeaturedClient::Get(), nullptr);
-}
-
-// Check that Fake runs HandleSeedFetched callback with a false success value
-// when set.
-TEST_F(FeaturedClientTest, FakeHandleSeedFetched_Failure) {
-  FeaturedClient::InitializeFake();
-  FakeFeaturedClient* client = FakeFeaturedClient::Get();
-
-  ASSERT_NE(client, nullptr);
-
-  ::featured::SeedDetails safe_seed;
-  client->SetCallbackSuccess(false);
-
-  bool ran_callback = false;
-  client->HandleSeedFetched(
-      safe_seed, base::BindLambdaForTesting([&ran_callback](bool success) {
         EXPECT_FALSE(success);
         ran_callback = true;
       }));
   // Ensures the callback was executed.
   EXPECT_TRUE(ran_callback);
+  EXPECT_EQ(client->handle_seed_fetched_attempts(), 1);
+
+  FeaturedClient::Shutdown();
+
+  EXPECT_EQ(FakeFeaturedClient::Get(), nullptr);
+}
+
+// Check that Fake runs `HandleSeedFetched` callback with value added by
+// `AddResponse`.
+TEST_F(FeaturedClientTest, FakeHandleSeedFetched_InvokeSuccessWhenSet) {
+  FeaturedClient::InitializeFake();
+  FakeFeaturedClient* client = FakeFeaturedClient::Get();
+
+  ASSERT_NE(client, nullptr);
+
+  ::featured::SeedDetails safe_seed;
+  client->AddResponse(true);
+
+  bool ran_callback = false;
+  client->HandleSeedFetched(
+      safe_seed, base::BindLambdaForTesting([&ran_callback](bool success) {
+        EXPECT_TRUE(success);
+        ran_callback = true;
+      }));
+  // Ensures the callback was executed.
+  EXPECT_TRUE(ran_callback);
+  EXPECT_EQ(client->handle_seed_fetched_attempts(), 1);
 
   FeaturedClient::Shutdown();
 
diff --git a/chromeos/ash/components/dbus/shill/README.md b/chromeos/ash/components/dbus/shill/README.md
index f9db152..bb0ddf98 100644
--- a/chromeos/ash/components/dbus/shill/README.md
+++ b/chromeos/ash/components/dbus/shill/README.md
@@ -4,68 +4,64 @@
 Shill via D-Bus. These clients provide a high-level API that enables interaction
 with the following Shill services:
 * [Shill manager service](#shill-manager-client)
-* Shill device service
-* [Shill IPconfig service](#shill-ipconfig-client)
-* Shill profile service
-* Shill service service
+* [Shill device service](#shill-device-client)
+* [Shill IPConfig service](#shill-ipconfig-client)
+* [Shill profile service](#shill-profile-client)
+* [Shill service service](#shill-service-client)
 
-## Shill clients initialization and shutdown
+All of the clients used to interact with these Shill services are instantiated
+as singletons, and all usage of their interfaces must happen on the same thread
+that was used to instantiate the client.
+
+For more information about Shill, the connection manager for ChromeOS, see the
+[official
+README.md](https://source.chromium.org/chromiumos/chromiumos/codesearch/+/main:src/platform2/shill/README.md).
+
+# Service clients
+
 ChromeOS interacts with Shill services through their corresponding Shill D-Bus
 clients. To initialize all Shill D-Bus clients, the
 [`shill_clients::Initialize()`](https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ash/dbus/ash_dbus_helper.cc;l=136;drc=1e745d6190686a85eea668b86350080be45b55f9)
 function is used. This function is called from the
 [`ash::InitializeDBus()`](https://source.chromium.org/chromium/chromium/src/+/main:chrome/app/chrome_main_delegate.cc;l=650;drc=705fc04a0d21cfe6709b8a56750704a19290ce96)
-function during the Ash/Chrome UI
-[early initialization](https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ash/dbus/ash_dbus_helper.cc;l=123;drc=33ab5f99dea200ad10ab79d898465e82f8a4ae77)
-stage as there are many other components that depend on Shill clients.
-The `shill_clients::Initialize()` function sets up a single global instance of
-each Shill D-Bus client which are accessible by public static functions provided
-by each client individually, e.g.: `ShillManagerClient::Get()`.\
-Additionally, it also provides a
+function during the Ash/Chrome UI [early
+initialization](https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ash/dbus/ash_dbus_helper.cc;l=123;drc=33ab5f99dea200ad10ab79d898465e82f8a4ae77)
+stage as there are many other components that depend on Shill clients. The
+`shill_clients::Initialize()` function sets up a single global instance of each
+Shill D-Bus client which are accessible by public static functions provided by
+each client individually, e.g. [`ShillManagerClient::Get()`](https://source.chromium.org/chromium/chromium/src/+/refs/heads/main:chromeos/ash/components/dbus/shill/shill_manager_client.h;l=160;drc=2450f2f5d0ce0da9b8cf493c533f9528ff17bab6). Similarly,
+the
 [`shill_clients::InitializeFakes()`](https://source.chromium.org/chromium/chromium/src/+/main:chromeos/ash/components/dbus/shill/shill_clients.cc;l=34;drc=e4714ce987b39d3207473e0cd5cc77fbbbf37fda)
-function which initializes the global singleton instance with fake implementation.
-This function is mostly used for unit tests or ChromeOS-Linux build.\
-Then, during Ash/Chrome UI shutdown, the
+function is also provided which initializes the global singleton instance with fake
+implementations. This function is mostly used for unit tests or ChromeOS-Linux
+build. Finally, during Ash/Chrome UI shutdown, the
 [`ash::ShutdownDBus()`](https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ash/dbus/ash_dbus_helper.cc;l=256;drc=1e745d6190686a85eea668b86350080be45b55f9)
 function is called, and the
 [`shill_clients::Shutdown()`](https://source.chromium.org/chromium/chromium/src/+/main:chromeos/ash/components/dbus/shill/shill_clients.cc;l=47;drc=e4714ce987b39d3207473e0cd5cc77fbbbf37fda)
 function is used to bring down all the Shill D-Bus clients.
 
-
-## Shill manager client
-The [ShillManagerClient](https://source.chromium.org/chromium/chromium/src/+/main:chromeos/ash/components/dbus/shill/shill_manager_client.h;drc=f10ad519eaa48f765938cc453b97f3333f1d1a9d)
-class provides an interface for interacting with the Shill manager service
+## Shill manager client {#shill-manager-client}
+The [`ShillManagerClient`](https://source.chromium.org/chromium/chromium/src/+/main:chromeos/ash/components/dbus/shill/shill_manager_client.h;drc=f10ad519eaa48f765938cc453b97f3333f1d1a9d)
+class provides the interface for interacting with the Shill manager service
 interface which is the top-level singleton exposed by Shill that provides
 global system properties. It enables Chrome to:
-* Get or set shill manager properties
+* Get or set Shill manager properties
 * Enable or disable technologies such as Wi-Fi/cellular networks
 * Scan and connect to the best service, like searching for nearby Wi-Fi networks
 * Configure networks, such as setting up a Wi-Fi network with a password
 * Add or remove passpoint credentials
 * Tethering related functionality such as enable or disable hotspot tethering
 
-For detailed documentation on the Shill Manager DBus API, please refer to
+For detailed documentation on the Shill Manager D-Bus API, please refer to
 [manager-api.txt](https://source.chromium.org/chromiumos/chromiumos/codesearch/+/main:src/platform2/shill/doc/manager-api.txt;drc=89f1629aac064713d70908436fa9834c4f443551).
 
-All Shill methods must be called from the origin thread that initializes the
-DBusThreadManager instance. Most methods that make Shill Manager calls pass
-two callbacks:
-* callback: invoked if the method call succeeds
-* error_callback: invoked if the method call fails or returns an error response
+## Shill device client {#shill-device-client}
 
-Additionally, ShillManagerClient provides a
-[test interface](https://source.chromium.org/chromium/chromium/src/+/main:chromeos/ash/components/dbus/shill/shill_manager_client.h;l=51;drc=f10ad519eaa48f765938cc453b97f3333f1d1a9d)
-that allows you to set up fake devices, services, and technologies for ChromeOS
-unit testing purposes. This interface is implemented in the
-[FakeShillManagerClient](https://source.chromium.org/chromium/chromium/src/+/main:chromeos/ash/components/dbus/shill/fake_shill_manager_client.h;drc=f10ad519eaa48f765938cc453b97f3333f1d1a9d)
-class, which simulates the behavior of ShillManagerClient and provides APIs for
-setting fake responses. For example, `SetInteractiveDelay` sets the fake
-interactive delay for the following shill response for testing purposes, while
-`SetSimulateConfigurationResult` sets the following `ConfigureService` call to
-succeed, fail or timeout.
+TODO: Discuss the Shill device client.
 
-## Shill IPConfig client
-The [ShillIPConfigClient](https://source.chromium.org/chromium/chromium/src/+/main:chromeos/ash/components/dbus/shill/shill_ipconfig_client.h;drc=ad947e92bd398452f42173e7a39ed7ab2e4ad094)
+## Shill IPConfig client {#shill-ipconfig-client}
+
+The [`ShillIPConfigClient`](https://source.chromium.org/chromium/chromium/src/+/main:chromeos/ash/components/dbus/shill/shill_ipconfig_client.h;drc=ad947e92bd398452f42173e7a39ed7ab2e4ad094)
 class provides an interface for interacting with the Shill IPConfig interface
 which is the top-level singleton exposed by Shill that provides Layer 3
 configuration. It enables Chrome to:
@@ -75,11 +71,82 @@
 For detailed documentation on the Shill IPConfig DBus API, please refer to
 [ipconfig-api.txt](https://source.chromium.org/chromiumos/chromiumos/codesearch/+/main:src/platform2/shill/doc/ipconfig-api.txt;drc=9a98a2fb4b28a8e3c32d7eafb39395ccbc730538).
 
-All Shill methods must be called from the origin thread that initializes the
-DBusThreadManager instance. Most methods that make Shill IPConfig calls pass
-a callback that is invoked when the method finishes.
+## Shill profile client {#shill-profile-client}
 
-Additionally, ShillIPConfigClient provides a
-[test interface](https://source.chromium.org/chromium/chromium/src/+/main:chromeos/ash/components/dbus/shill/shill_ipconfig_client.h;l=32-40;drc=ad947e92bd398452f42173e7a39ed7ab2e4ad094)
-that allows you to add fake IPConfig entries for ChromeOS unit testing
-purposes. This interface is implemented in the [FakeShillIPConfigClient](https://source.chromium.org/chromium/chromium/src/+/refs/heads/main:chromeos/ash/components/dbus/shill/fake_shill_ipconfig_client.h;drc=ad947e92bd398452f42173e7a39ed7ab2e4ad094).
\ No newline at end of file
+TODO: Discuss the Shill profile client.
+
+## Shill service client {#shill-service-client}
+The
+[`ShillServiceClient`](https://source.chromium.org/chromium/chromium/src/+/main:chromeos/ash/components/dbus/shill/shill_service_client.h;drc=af33e6b506bcb54e29efd850e2eb546f476ee63a)
+class provides the interface for interacting with the Shill service service.
+Services are how Shill represents network configurations and connections through
+an associated device within the Platform layer and can be thought of the source
+of truth for these network configurations
+and connections on ChromeOS devices. While Chrome, i.e. the Software layer,
+generally relies on the read-only
+[`NetworkState`](https://source.chromium.org/chromium/chromium/src/+/main:chromeos/ash/components/network/README.md;l=100-149;drc=4a50d13fc73268ef4a27cf67dc1eff40ea6f997a)
+class for network properties, the `NetworkState` class is effectively just a
+cache of a Shill service.
+
+The `ShillServiceClient` class provides a thorough interface for interacting
+with Shill services:
+* Get, set, and clear Shill service properties
+* Connect, disconnect, and remove Shill services
+* Listen for changes to Shill service properties
+* Retrieve the passphrase associated with the Shill service
+* Miscellaneous behavior e.g. complete cellular activation
+
+For detailed documentation on the Shill Service D-Bus API, please refer to
+[service-api.txt](https://source.chromium.org/chromiumos/chromiumos/codesearch/+/main:src/platform2/shill/doc/service-api.txt).
+
+# Testing interfaces
+
+Each of the clients discussed provides a testing interface that can be used to
+effectively configure Shill to be in a specific, custom state during testing.
+These testing interfaces are declared within the corresponding client class and
+can be used within tests in multiple ways. For example, the
+[`NetworkTestHelperBase`](https://source.chromium.org/chromium/chromium/src/+/main:chromeos/ash/components/network/network_test_helper_base.h;drc=e4714ce987b39d3207473e0cd5cc77fbbbf37fda)
+class can be used within tests which will initialize the entire network stack,
+including the test interfaces for each of the clients.
+
+## Shill manager client {#shill-manager-client-testing}
+
+The
+[`ShillManagerClient::TestInterface`](https://source.chromium.org/chromium/chromium/src/+/main:chromeos/ash/components/dbus/shill/shill_manager_client.h;l=51;drc=f10ad519eaa48f765938cc453b97f3333f1d1a9d)
+class supports a wide variety of functionality that is very useful when testing:
+* Creating fakes, e.g. devices, services, technologies and more
+* Configuring the delay before success/failure callbacks should be invoked
+* Configuring whether certain function calls should succeed, fail, or timeout
+
+The `ShillManagerClient::TestInterface` interface is implemented by
+[`FakeShillManagerClient`](https://source.chromium.org/chromium/chromium/src/+/main:chromeos/ash/components/dbus/shill/fake_shill_manager_client.h;drc=f10ad519eaa48f765938cc453b97f3333f1d1a9d).
+
+## Shill device client {#shill-device-client-testing}
+
+TODO: Discuss the Shill device client testing interface.
+
+## Shill IPConfig client {#shill-ipconfig-client-testing}
+
+The
+[`ShillIPConfigClient::TestInterface`](https://source.chromium.org/chromium/chromium/src/+/main:chromeos/ash/components/dbus/shill/shill_ipconfig_client.h;l=32-40;drc=ad947e92bd398452f42173e7a39ed7ab2e4ad094)
+class supports adding fake IPConfig entries during testing. This interface is
+implemented by
+[`FakeShillIPConfigClient`](https://source.chromium.org/chromium/chromium/src/+/refs/heads/main:chromeos/ash/components/dbus/shill/fake_shill_ipconfig_client.h;drc=ad947e92bd398452f42173e7a39ed7ab2e4ad094).
+
+## Shill profile client {#shill-profile-client-testing}
+
+TODO: Discuss the Shill profile client testing interface.
+
+## Shill service client {#shill-service-client-testing}
+
+The
+[`ShillServiceClient::TestInterface`](https://source.chromium.org/chromium/chromium/src/+/main:chromeos/ash/components/dbus/shill/shill_service_client.h;drc=af33e6b506bcb54e29efd850e2eb546f476ee63a)
+class provides a number of different APIs for interacting with services:
+* Configuring fake services
+* Getting, setting, and clearing service properties
+* Configuring whether certain function calls should succeed, fail, or timeout
+* Configuring the connect behavior of services, e.g. custom behavior on
+  connection
+
+The `ShillServiceClient::TestInterface` interface is implemented by
+[`FakeShillServiceClient`](https://source.chromium.org/chromium/chromium/src/+/main:chromeos/ash/components/dbus/shill/fake_shill_service_client.h;drc=af33e6b506bcb54e29efd850e2eb546f476ee63a).
diff --git a/chromeos/ash/components/nearby/common/DIR_METADATA b/chromeos/ash/components/nearby/common/DIR_METADATA
new file mode 100644
index 0000000..d6d7997
--- /dev/null
+++ b/chromeos/ash/components/nearby/common/DIR_METADATA
@@ -0,0 +1,4 @@
+buganizer {
+  component_id: 1131838
+}
+team_email: "chromeos-cross-device-eng@google.com "
diff --git a/chromeos/ash/components/nearby/common/OWNERS b/chromeos/ash/components/nearby/common/OWNERS
new file mode 100644
index 0000000..1064f36b
--- /dev/null
+++ b/chromeos/ash/components/nearby/common/OWNERS
@@ -0,0 +1,3 @@
+file://chrome/browser/nearby_sharing/OWNERS
+hansberry@chromium.org
+julietlevesque@google.com
diff --git a/components/exo/wayland/BUILD.gn b/components/exo/wayland/BUILD.gn
index fbcfad36..c1ad58c 100644
--- a/components/exo/wayland/BUILD.gn
+++ b/components/exo/wayland/BUILD.gn
@@ -132,6 +132,8 @@
     "zxdg_decoration_manager.h",
     "zxdg_output_manager.cc",
     "zxdg_output_manager.h",
+    "zxdg_shell.cc",
+    "zxdg_shell.h",
   ]
 
   defines = [ "EXO_IMPLEMENTATION" ]
diff --git a/components/exo/wayland/clients/client_base.cc b/components/exo/wayland/clients/client_base.cc
index 7a58b2cd..695c09e 100644
--- a/components/exo/wayland/clients/client_base.cc
+++ b/components/exo/wayland/clients/client_base.cc
@@ -619,29 +619,65 @@
       wl_shell_surface_set_toplevel(shell_surface.get());
     }
   } else {
-    xdg_surface_.reset(xdg_wm_base_get_xdg_surface(globals_.xdg_wm_base.get(),
-                                                   surface_.get()));
-    if (!xdg_surface_) {
-      LOG(ERROR) << "Can't get xdg surface";
-      return false;
+    if (!globals_.xdg_wm_base) {
+      // use zxdg
+      if (!globals_.xdg_shell_v6) {
+        LOG(ERROR) << "Can't find xdg_shell or zxdg_shell_v6 interface";
+        return false;
+      }
+
+      zxdg_surface_.reset(zxdg_shell_v6_get_xdg_surface(
+          globals_.xdg_shell_v6.get(), surface_.get()));
+      if (!zxdg_surface_) {
+        LOG(ERROR) << "Can't get zxdg surface";
+        return false;
+      }
+      static const zxdg_surface_v6_listener zxdg_surface_v6_listener = {
+          [](void* data, struct zxdg_surface_v6* zxdg_surface_v6,
+             uint32_t layout_mode) {
+            zxdg_surface_v6_ack_configure(zxdg_surface_v6, layout_mode);
+          },
+      };
+      zxdg_surface_v6_add_listener(zxdg_surface_.get(),
+                                   &zxdg_surface_v6_listener, this);
+      zxdg_toplevel_.reset(zxdg_surface_v6_get_toplevel(zxdg_surface_.get()));
+      if (!zxdg_toplevel_) {
+        LOG(ERROR) << "Can't get zxdg toplevel";
+        return false;
+      }
+      static const zxdg_toplevel_v6_listener zxdg_toplevel_v6_listener = {
+          [](void* data, struct zxdg_toplevel_v6* zxdg_toplevel_v6,
+             int32_t width, int32_t height, struct wl_array* states) {},
+          [](void* data, struct zxdg_toplevel_v6* zxdg_toplevel_v6) {}};
+      zxdg_toplevel_v6_add_listener(zxdg_toplevel_.get(),
+                                    &zxdg_toplevel_v6_listener, this);
+    } else {
+      // use xdg
+      xdg_surface_.reset(xdg_wm_base_get_xdg_surface(globals_.xdg_wm_base.get(),
+                                                     surface_.get()));
+      if (!xdg_surface_) {
+        LOG(ERROR) << "Can't get xdg surface";
+        return false;
+      }
+      static const xdg_surface_listener xdg_surface_listener = {
+          [](void* data, struct xdg_surface* xdg_surface,
+             uint32_t layout_mode) {
+            xdg_surface_ack_configure(xdg_surface, layout_mode);
+          },
+      };
+      xdg_surface_add_listener(xdg_surface_.get(), &xdg_surface_listener, this);
+      xdg_toplevel_.reset(xdg_surface_get_toplevel(xdg_surface_.get()));
+      if (!xdg_toplevel_) {
+        LOG(ERROR) << "Can't get xdg toplevel";
+        return false;
+      }
+      static const xdg_toplevel_listener xdg_toplevel_listener = {
+          [](void* data, struct xdg_toplevel* xdg_toplevel, int32_t width,
+             int32_t height, struct wl_array* states) {},
+          [](void* data, struct xdg_toplevel* xdg_toplevel) {}};
+      xdg_toplevel_add_listener(xdg_toplevel_.get(), &xdg_toplevel_listener,
+                                this);
     }
-    static const xdg_surface_listener xdg_surface_listener = {
-        [](void* data, struct xdg_surface* xdg_surface, uint32_t layout_mode) {
-          xdg_surface_ack_configure(xdg_surface, layout_mode);
-        },
-    };
-    xdg_surface_add_listener(xdg_surface_.get(), &xdg_surface_listener, this);
-    xdg_toplevel_.reset(xdg_surface_get_toplevel(xdg_surface_.get()));
-    if (!xdg_toplevel_) {
-      LOG(ERROR) << "Can't get xdg toplevel";
-      return false;
-    }
-    static const xdg_toplevel_listener xdg_toplevel_listener = {
-        [](void* data, struct xdg_toplevel* xdg_toplevel, int32_t width,
-           int32_t height, struct wl_array* states) {},
-        [](void* data, struct xdg_toplevel* xdg_toplevel) {}};
-    xdg_toplevel_add_listener(xdg_toplevel_.get(), &xdg_toplevel_listener,
-                              this);
 
     if (fullscreen_) {
       LOG(ERROR) << "full screen not supported yet.";
diff --git a/components/exo/wayland/clients/client_base.h b/components/exo/wayland/clients/client_base.h
index a2f92a0a..fab7537d 100644
--- a/components/exo/wayland/clients/client_base.h
+++ b/components/exo/wayland/clients/client_base.h
@@ -203,6 +203,8 @@
   std::unique_ptr<wl_shell_surface> shell_surface_;
   std::unique_ptr<xdg_surface> xdg_surface_;
   std::unique_ptr<xdg_toplevel> xdg_toplevel_;
+  std::unique_ptr<zxdg_surface_v6> zxdg_surface_;
+  std::unique_ptr<zxdg_toplevel_v6> zxdg_toplevel_;
   std::unique_ptr<wl_pointer> wl_pointer_;
   std::unique_ptr<zcr_pointer_stylus_v2> zcr_pointer_stylus_;
   Globals globals_;
diff --git a/components/exo/wayland/clients/client_helper.cc b/components/exo/wayland/clients/client_helper.cc
index 3659802e..1dffd0e 100644
--- a/components/exo/wayland/clients/client_helper.cc
+++ b/components/exo/wayland/clients/client_helper.cc
@@ -83,6 +83,7 @@
 DEFAULT_DELETER(wp_content_type_manager_v1, wp_content_type_manager_v1_destroy)
 DEFAULT_DELETER(wp_content_type_v1, wp_content_type_v1_destroy)
 DEFAULT_DELETER(wp_viewporter, wp_viewporter_destroy)
+DEFAULT_DELETER(zxdg_shell_v6, zxdg_shell_v6_destroy)
 DEFAULT_DELETER(xdg_wm_base, xdg_wm_base_destroy)
 DEFAULT_DELETER(zwp_text_input_manager_v1, zwp_text_input_manager_v1_destroy)
 DEFAULT_DELETER(zcr_secure_output_v1, zcr_secure_output_v1_destroy)
@@ -111,6 +112,8 @@
 DEFAULT_DELETER(zcr_extended_drag_v1, zcr_extended_drag_v1_destroy)
 DEFAULT_DELETER(xdg_surface, xdg_surface_destroy)
 DEFAULT_DELETER(xdg_toplevel, xdg_toplevel_destroy)
+DEFAULT_DELETER(zxdg_surface_v6, zxdg_surface_v6_destroy)
+DEFAULT_DELETER(zxdg_toplevel_v6, zxdg_toplevel_v6_destroy)
 DEFAULT_DELETER(zxdg_output_manager_v1, zxdg_output_manager_v1_destroy)
 DEFAULT_DELETER(weston_test, weston_test_destroy)
 DEFAULT_DELETER(zwp_idle_inhibit_manager_v1,
diff --git a/components/exo/wayland/clients/client_helper.h b/components/exo/wayland/clients/client_helper.h
index cc2371a..c8c719f 100644
--- a/components/exo/wayland/clients/client_helper.h
+++ b/components/exo/wayland/clients/client_helper.h
@@ -43,6 +43,7 @@
 #include <xdg-decoration-unstable-v1-client-protocol.h>
 #include <xdg-output-unstable-v1-client-protocol.h>
 #include <xdg-shell-client-protocol.h>
+#include <xdg-shell-unstable-v6-client-protocol.h>
 #include "base/scoped_generic.h"
 
 #if defined(USE_GBM)
@@ -105,6 +106,7 @@
 DEFAULT_DELETER_FDECL(wp_content_type_manager_v1)
 DEFAULT_DELETER_FDECL(wp_content_type_v1)
 DEFAULT_DELETER_FDECL(wp_viewporter)
+DEFAULT_DELETER_FDECL(zxdg_shell_v6)
 DEFAULT_DELETER_FDECL(xdg_wm_base)
 DEFAULT_DELETER_FDECL(zwp_text_input_manager_v1)
 DEFAULT_DELETER_FDECL(zcr_secure_output_v1)
@@ -129,6 +131,8 @@
 DEFAULT_DELETER_FDECL(zcr_extended_drag_v1)
 DEFAULT_DELETER_FDECL(xdg_surface)
 DEFAULT_DELETER_FDECL(xdg_toplevel)
+DEFAULT_DELETER_FDECL(zxdg_surface_v6)
+DEFAULT_DELETER_FDECL(zxdg_toplevel_v6)
 DEFAULT_DELETER_FDECL(zxdg_output_manager_v1)
 DEFAULT_DELETER_FDECL(weston_test)
 DEFAULT_DELETER_FDECL(zwp_idle_inhibit_manager_v1)
diff --git a/components/exo/wayland/clients/globals.cc b/components/exo/wayland/clients/globals.cc
index 8f965114..aab2033c 100644
--- a/components/exo/wayland/clients/globals.cc
+++ b/components/exo/wayland/clients/globals.cc
@@ -53,6 +53,7 @@
   BIND(wl_output, output)
   BIND(zwp_linux_explicit_synchronization_v1, linux_explicit_synchronization)
   BIND(zcr_vsync_feedback_v1, vsync_feedback)
+  BIND(zxdg_shell_v6, xdg_shell_v6)
   BIND(xdg_wm_base, xdg_wm_base)
   BIND(zcr_stylus_v2, stylus)
   BIND(zcr_remote_shell_v1, cr_remote_shell_v1)
diff --git a/components/exo/wayland/clients/globals.h b/components/exo/wayland/clients/globals.h
index 654d1a2..31c9d41 100644
--- a/components/exo/wayland/clients/globals.h
+++ b/components/exo/wayland/clients/globals.h
@@ -59,6 +59,7 @@
   Object<wl_touch> touch;
   Object<zaura_shell> aura_shell;
   Object<zaura_output> aura_output;
+  Object<zxdg_shell_v6> xdg_shell_v6;
   Object<xdg_wm_base> xdg_wm_base;
   Object<zwp_fullscreen_shell_v1> fullscreen_shell;
   Object<zwp_input_timestamps_manager_v1> input_timestamps_manager;
diff --git a/components/exo/wayland/clients/security_delegate_binding_test.cc b/components/exo/wayland/clients/security_delegate_binding_test.cc
index f5be747..3cd36cd7 100644
--- a/components/exo/wayland/clients/security_delegate_binding_test.cc
+++ b/components/exo/wayland/clients/security_delegate_binding_test.cc
@@ -7,6 +7,7 @@
 #include <wayland-client.h>
 #include <wayland-server.h>
 #include <xdg-shell-client-protocol.h>
+#include <xdg-shell-unstable-v6-client-protocol.h>
 
 #include "components/exo/client_controlled_shell_surface.h"
 #include "components/exo/shell_surface.h"
@@ -109,6 +110,43 @@
             nullptr);
 }
 
+TEST_F(SecurityDelegateBindingTest, ZxdgSurfaceV6HasSecurityDelegate) {
+  class ClientData : public test::TestClient::CustomData {
+   public:
+    std::unique_ptr<wl_surface> surface;
+    std::unique_ptr<zxdg_surface_v6> zxdg_surface;
+  };
+
+  test::ResourceKey zxdg_surface_key;
+
+  PostToClientAndWait([&](test::TestClient* client) {
+    auto data = std::make_unique<ClientData>();
+
+    data->surface.reset(wl_compositor_create_surface(client->compositor()));
+    data->zxdg_surface.reset(zxdg_shell_v6_get_xdg_surface(
+        client->xdg_shell_v6(), data->surface.get()));
+
+    zxdg_surface_key =
+        test::client_util::GetResourceKey(data->zxdg_surface.get());
+
+    client->set_data(std::move(data));
+  });
+
+  EXPECT_EQ(test::server_util::GetUserDataForResource<WaylandXdgSurface>(
+                server_.get(), zxdg_surface_key)
+                ->shell_surface->GetSecurityDelegate(),
+            server_security_delegate_);
+
+  PostToClientAndWait([](test::TestClient* client) {
+    // Destroy the client objects.
+    client->set_data(nullptr);
+  });
+
+  EXPECT_EQ(test::server_util::GetUserDataForResource<WaylandXdgSurface>(
+                server_.get(), zxdg_surface_key),
+            nullptr);
+}
+
 TEST_F(SecurityDelegateBindingTest, ZcrRemoteSurfaceV1HasSecurityDelegate) {
   class ClientData : public test::TestClient::CustomData {
    public:
diff --git a/components/exo/wayland/clients/test/client_version_test.cc b/components/exo/wayland/clients/test/client_version_test.cc
index 441f7cfd7..e8c3f52 100644
--- a/components/exo/wayland/clients/test/client_version_test.cc
+++ b/components/exo/wayland/clients/test/client_version_test.cc
@@ -35,6 +35,7 @@
 #include <weston-test-server-protocol.h>
 #include <xdg-decoration-unstable-v1-server-protocol.h>
 #include <xdg-shell-server-protocol.h>
+#include <xdg-shell-unstable-v6-server-protocol.h>
 
 #include <memory>
 #include <string>
@@ -80,6 +81,7 @@
   std::unique_ptr<wl_data_device_manager> wl_data_device_manager;
   std::unique_ptr<wp_content_type_manager_v1> wp_content_type_manager_v1;
   std::unique_ptr<wp_viewporter> wp_viewporter;
+  std::unique_ptr<zxdg_shell_v6> zxdg_shell_v6;
   std::unique_ptr<xdg_wm_base> xdg_wm_base;
   std::unique_ptr<zwp_text_input_manager_v1> zwp_text_input_manager_v1;
   std::unique_ptr<zcr_secure_output_v1> zcr_secure_output_v1;
@@ -174,6 +176,7 @@
           REGISTRY_CALLBACK(wp_content_type_manager_v1,
                             wp_content_type_manager_v1),
           REGISTRY_CALLBACK(wp_viewporter, wp_viewporter),
+          REGISTRY_CALLBACK(zxdg_shell_v6, zxdg_shell_v6),
           REGISTRY_CALLBACK(xdg_wm_base, xdg_wm_base),
           REGISTRY_CALLBACK(zwp_text_input_manager_v1,
                             zwp_text_input_manager_v1),
diff --git a/components/exo/wayland/compatibility_test/BUILD.gn b/components/exo/wayland/compatibility_test/BUILD.gn
index ac1441f..61498f51 100644
--- a/components/exo/wayland/compatibility_test/BUILD.gn
+++ b/components/exo/wayland/compatibility_test/BUILD.gn
@@ -20,6 +20,7 @@
   "//third_party/wayland-protocols/src/unstable/pointer-gestures/pointer-gestures-unstable-v1.xml",
   "//third_party/wayland-protocols/src/unstable/relative-pointer/relative-pointer-unstable-v1.xml",
   "//third_party/wayland-protocols/src/unstable/text-input/text-input-unstable-v1.xml",
+  "//third_party/wayland-protocols/src/unstable/xdg-shell/xdg-shell-unstable-v6.xml",
   "//third_party/wayland-protocols/unstable/alpha-compositing/alpha-compositing-unstable-v1.xml",
   "//third_party/wayland-protocols/unstable/cursor-shapes/cursor-shapes-unstable-v1.xml",
   "//third_party/wayland-protocols/unstable/gaming-input/gaming-input-unstable-v2.xml",
diff --git a/components/exo/wayland/fuzzer/BUILD.gn b/components/exo/wayland/fuzzer/BUILD.gn
index b485480..68e8641 100644
--- a/components/exo/wayland/fuzzer/BUILD.gn
+++ b/components/exo/wayland/fuzzer/BUILD.gn
@@ -23,6 +23,7 @@
   "//third_party/wayland-protocols/src/unstable/pointer-gestures/pointer-gestures-unstable-v1.xml",
   "//third_party/wayland-protocols/src/unstable/relative-pointer/relative-pointer-unstable-v1.xml",
   "//third_party/wayland-protocols/src/unstable/text-input/text-input-unstable-v1.xml",
+  "//third_party/wayland-protocols/src/unstable/xdg-shell/xdg-shell-unstable-v6.xml",
   "//third_party/wayland-protocols/unstable/alpha-compositing/alpha-compositing-unstable-v1.xml",
   "//third_party/wayland-protocols/unstable/cursor-shapes/cursor-shapes-unstable-v1.xml",
   "//third_party/wayland-protocols/unstable/gaming-input/gaming-input-unstable-v2.xml",
diff --git a/components/exo/wayland/fuzzer/wayland_sequencer.py b/components/exo/wayland/fuzzer/wayland_sequencer.py
index 87f5256..c2e46f87 100644
--- a/components/exo/wayland/fuzzer/wayland_sequencer.py
+++ b/components/exo/wayland/fuzzer/wayland_sequencer.py
@@ -181,7 +181,25 @@
   c.RecordInterfaceCreated('wl_data_offer')
 
   e = SequenceBuilder('empty', dep_graph)
-  return [c, e]
+
+  p = SequenceBuilder('popup_configuration', dep_graph)
+  p_positioner = p.BuildInterface('zxdg_positioner_v6')
+  p_parent = p.BuildInterface('zxdg_toplevel_v6')
+  p.AppendCall('zxdg_surface_v6', 'set_window_geometry',
+               [('receiver', p_parent), ('x', 0), ('y', 0), ('width', 10),
+                ('height', 10)])
+  p.AppendRoundTrip()
+  p.AppendCall('wl_surface', 'commit', [('receiver', p_parent)])
+  p.AppendRoundTrip()
+  p.AppendCall('zxdg_surface_v6', 'ack_configure', [('receiver', p_parent),
+                                                    ('serial', 1)])
+  p_child = p.BuildInterface('zxdg_surface_v6')
+  p.AppendCall('zxdg_surface_v6', 'get_popup', [('receiver', p_child),
+                                                ('parent', p_parent),
+                                                ('positioner', p_positioner)])
+  p.AppendRoundTrip()
+
+  return [c, e, p]
 
 
 def SequenceToTemplate(parsed_arguments, builder):
diff --git a/components/exo/wayland/server.cc b/components/exo/wayland/server.cc
index dbcd6857..554eecc 100644
--- a/components/exo/wayland/server.cc
+++ b/components/exo/wayland/server.cc
@@ -41,6 +41,7 @@
 #include <xdg-decoration-unstable-v1-server-protocol.h>
 #include <xdg-output-unstable-v1-server-protocol.h>
 #include <xdg-shell-server-protocol.h>
+#include <xdg-shell-unstable-v6-server-protocol.h>
 
 #include <linux-dmabuf-unstable-v1-server-protocol.h>
 #include <memory>
@@ -106,6 +107,7 @@
 #include "components/exo/wayland/zwp_text_input_manager.h"
 #include "components/exo/wayland/zxdg_decoration_manager.h"
 #include "components/exo/wayland/zxdg_output_manager.h"
+#include "components/exo/wayland/zxdg_shell.h"
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
 #include "ui/ozone/public/ozone_platform.h"
@@ -430,6 +432,11 @@
                    zcr_text_input_extension_data_.get(),
                    bind_text_input_extension);
 
+  zxdg_shell_data_ =
+      std::make_unique<WaylandZxdgShell>(display_, serial_tracker_.get());
+  wl_global_create(wl_display_.get(), &zxdg_shell_v6_interface, 1,
+                   zxdg_shell_data_.get(), bind_zxdg_shell_v6);
+
   xdg_shell_data_ =
       std::make_unique<WaylandXdgShell>(display_, serial_tracker_.get());
   wl_global_create(wl_display_.get(), &xdg_wm_base_interface, 3,
diff --git a/components/exo/wayland/server.h b/components/exo/wayland/server.h
index 65d4e58..82b2a78 100644
--- a/components/exo/wayland/server.h
+++ b/components/exo/wayland/server.h
@@ -35,6 +35,7 @@
 struct WaylandTextInputExtension;
 struct WaylandTextInputManager;
 struct WaylandXdgShell;
+struct WaylandZxdgShell;
 struct WaylandRemoteShellData;
 class WaylandDmabufFeedbackManager;
 class WestonTest;
@@ -142,6 +143,7 @@
   std::unique_ptr<WaylandKeyboardExtension> zcr_keyboard_extension_data_;
   std::unique_ptr<WaylandTextInputManager> zwp_text_manager_data_;
   std::unique_ptr<WaylandTextInputExtension> zcr_text_input_extension_data_;
+  std::unique_ptr<WaylandZxdgShell> zxdg_shell_data_;
   std::unique_ptr<WaylandXdgShell> xdg_shell_data_;
   std::unique_ptr<WaylandRemoteShellData> remote_shell_data_;
   std::unique_ptr<WestonTest> weston_test_holder_;
diff --git a/components/exo/wayland/test/test_client.h b/components/exo/wayland/test/test_client.h
index 199549f2..3cedf4a 100644
--- a/components/exo/wayland/test/test_client.h
+++ b/components/exo/wayland/test/test_client.h
@@ -62,6 +62,7 @@
   wl_touch* touch() { return globals().touch.get(); }
   zaura_shell* aura_shell() { return globals().aura_shell.get(); }
   zaura_output* aura_output() { return globals().aura_output.get(); }
+  zxdg_shell_v6* xdg_shell_v6() { return globals().xdg_shell_v6.get(); }
   xdg_wm_base* xdg_wm_base() { return globals().xdg_wm_base.get(); }
   zwp_fullscreen_shell_v1* fullscreen_shell() {
     return globals().fullscreen_shell.get();
diff --git a/components/exo/wayland/wayland_positioner.cc b/components/exo/wayland/wayland_positioner.cc
index 5b282f0..874a896 100644
--- a/components/exo/wayland/wayland_positioner.cc
+++ b/components/exo/wayland/wayland_positioner.cc
@@ -6,12 +6,37 @@
 
 #include <ostream>
 
+#include <xdg-shell-unstable-v6-server-protocol.h>
+
 namespace exo::wayland {
 
 namespace {
 
 std::pair<WaylandPositioner::Direction, WaylandPositioner::Direction>
-DecomposeAnchor(uint32_t anchor) {
+DecomposeUnstableAnchor(uint32_t anchor) {
+  WaylandPositioner::Direction x, y;
+
+  if (anchor & ZXDG_POSITIONER_V6_ANCHOR_LEFT) {
+    x = WaylandPositioner::Direction::kNegative;
+  } else if (anchor & ZXDG_POSITIONER_V6_ANCHOR_RIGHT) {
+    x = WaylandPositioner::Direction::kPositive;
+  } else {
+    x = WaylandPositioner::Direction::kNeutral;
+  }
+
+  if (anchor & ZXDG_POSITIONER_V6_ANCHOR_TOP) {
+    y = WaylandPositioner::Direction::kNegative;
+  } else if (anchor & ZXDG_POSITIONER_V6_ANCHOR_BOTTOM) {
+    y = WaylandPositioner::Direction::kPositive;
+  } else {
+    y = WaylandPositioner::Direction::kNeutral;
+  }
+
+  return std::make_pair(x, y);
+}
+
+std::pair<WaylandPositioner::Direction, WaylandPositioner::Direction>
+DecomposeStableAnchor(uint32_t anchor) {
   switch (anchor) {
     default:
     case XDG_POSITIONER_ANCHOR_NONE:
@@ -45,7 +70,30 @@
 }
 
 std::pair<WaylandPositioner::Direction, WaylandPositioner::Direction>
-DecomposeGravity(uint32_t gravity) {
+DecomposeUnstableGravity(uint32_t gravity) {
+  WaylandPositioner::Direction x, y;
+
+  if (gravity & ZXDG_POSITIONER_V6_GRAVITY_LEFT) {
+    x = WaylandPositioner::Direction::kNegative;
+  } else if (gravity & ZXDG_POSITIONER_V6_GRAVITY_RIGHT) {
+    x = WaylandPositioner::Direction::kPositive;
+  } else {
+    x = WaylandPositioner::Direction::kNeutral;
+  }
+
+  if (gravity & ZXDG_POSITIONER_V6_GRAVITY_TOP) {
+    y = WaylandPositioner::Direction::kNegative;
+  } else if (gravity & ZXDG_POSITIONER_V6_GRAVITY_BOTTOM) {
+    y = WaylandPositioner::Direction::kPositive;
+  } else {
+    y = WaylandPositioner::Direction::kNeutral;
+  }
+
+  return std::make_pair(x, y);
+}
+
+std::pair<WaylandPositioner::Direction, WaylandPositioner::Direction>
+DecomposeStableGravity(uint32_t gravity) {
   switch (gravity) {
     default:
     case XDG_POSITIONER_GRAVITY_NONE:
@@ -289,7 +337,11 @@
 void WaylandPositioner::SetAnchor(uint32_t anchor) {
   std::pair<WaylandPositioner::Direction, WaylandPositioner::Direction>
       decompose;
-  decompose = DecomposeAnchor(anchor);
+  if (version_ == UNSTABLE) {
+    decompose = DecomposeUnstableAnchor(anchor);
+  } else {
+    decompose = DecomposeStableAnchor(anchor);
+  }
   anchor_x_ = decompose.first;
   anchor_y_ = decompose.second;
 }
@@ -297,7 +349,11 @@
 void WaylandPositioner::SetGravity(uint32_t gravity) {
   std::pair<WaylandPositioner::Direction, WaylandPositioner::Direction>
       decompose;
-  decompose = DecomposeGravity(gravity);
+  if (version_ == UNSTABLE) {
+    decompose = DecomposeUnstableGravity(gravity);
+  } else {
+    decompose = DecomposeStableGravity(gravity);
+  }
   gravity_x_ = decompose.first;
   gravity_y_ = decompose.second;
 }
diff --git a/components/exo/wayland/wayland_positioner.h b/components/exo/wayland/wayland_positioner.h
index 8f53d16d..7ff5143a 100644
--- a/components/exo/wayland/wayland_positioner.h
+++ b/components/exo/wayland/wayland_positioner.h
@@ -26,7 +26,12 @@
   // Represents the 1-dimensional projection of the gravity/anchor values.
   enum Direction { kNegative = -1, kNeutral = 0, kPositive = 1 };
 
-  WaylandPositioner() = default;
+  // Controls whether anchor and gravity are set using the unstable bitfields or
+  // the stable enums.
+  enum Version { UNSTABLE, STABLE };
+
+  WaylandPositioner(Version v) : version_(v) {}
+
   WaylandPositioner(const WaylandPositioner&) = delete;
   WaylandPositioner& operator=(const WaylandPositioner&) = delete;
 
@@ -48,6 +53,8 @@
   void SetOffset(gfx::Vector2d offset) { offset_ = std::move(offset); }
 
  private:
+  Version version_;
+
   gfx::Size size_;
 
   gfx::Rect anchor_rect_;
@@ -59,13 +66,13 @@
   Direction gravity_y_ = kNeutral;
 
   // A bitmask that defines the subset of modifications to the position/size
-  // that are allowed, see xdg_positioner.constraint_adjustment() for more
+  // that are allowed, see zxdg_positioner.constraint_adjustment() for more
   // details.
   uint32_t adjustment_ = XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_NONE;
 
   // Defines an absolute translation (i.e. unaffected by flipping, scaling or
   // resizing) for the placement of the window relative to the |anchor_rect_|.
-  // See xdg_positioner.set_offset() for more details.
+  // See zxdg_positioner.set_offset() for more details.
   gfx::Vector2d offset_;
 };
 
diff --git a/components/exo/wayland/wayland_positioner_unittest.cc b/components/exo/wayland/wayland_positioner_unittest.cc
index e44e474a..fcb283b 100644
--- a/components/exo/wayland/wayland_positioner_unittest.cc
+++ b/components/exo/wayland/wayland_positioner_unittest.cc
@@ -7,6 +7,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gfx/geometry/rect.h"
 #include "xdg-shell-server-protocol.h"
+#include "xdg-shell-unstable-v6-server-protocol.h"
 
 namespace exo {
 namespace wayland {
@@ -20,7 +21,9 @@
     WaylandPositioner positioner;
     gfx::Rect work_area = {0, 0, 5, 5};
 
-    explicit TestCaseBuilder() { positioner.SetAnchorRect({2, 2, 1, 1}); }
+    explicit TestCaseBuilder(WaylandPositioner::Version v) : positioner(v) {
+      positioner.SetAnchorRect({2, 2, 1, 1});
+    }
 
     TestCaseBuilder& SetFlipState(bool x, bool y) {
       return *this;
@@ -68,31 +71,218 @@
   };
 };
 
-TEST_F(WaylandPositionerTest, UnconstrainedCases) {
+// Tests for the unstable protocol.
+
+TEST_F(WaylandPositionerTest, UnconstrainedCasesUnstable) {
   // No gravity or anchor.
-  EXPECT_EQ(TestCaseBuilder().SetSize(1, 1).SolveToRect(),
+  EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::UNSTABLE)
+                .SetSize(1, 1)
+                .SolveToRect(),
             gfx::Rect(2, 2, 1, 1));
 
   // Anchor without gravity.
-  EXPECT_EQ(TestCaseBuilder()
+  EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::UNSTABLE)
+                .SetSize(2, 1)
+                .SetAnchor(ZXDG_POSITIONER_V6_ANCHOR_RIGHT)
+                .SolveToRect(),
+            gfx::Rect(2, 2, 2, 1));
+  EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::UNSTABLE)
+                .SetSize(2, 1)
+                .SetAnchor(ZXDG_POSITIONER_V6_ANCHOR_LEFT)
+                .SolveToRect(),
+            gfx::Rect(1, 2, 2, 1));
+
+  // Gravity without anchor.
+  EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::UNSTABLE)
+                .SetSize(1, 2)
+                .SetAnchorRect(2, 2, 0, 0)
+                .SetGravity(ZXDG_POSITIONER_V6_GRAVITY_TOP)
+                .SolveToRect(),
+            gfx::Rect(2, 0, 1, 2));
+  EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::UNSTABLE)
+                .SetSize(1, 2)
+                .SetAnchorRect(2, 2, 0, 0)
+                .SetGravity(ZXDG_POSITIONER_V6_GRAVITY_BOTTOM)
+                .SolveToRect(),
+            gfx::Rect(2, 2, 1, 2));
+
+  // Gravity + anchor in the same direction.
+  EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::UNSTABLE)
+                .SetSize(2, 2)
+                .SetGravity(ZXDG_POSITIONER_V6_GRAVITY_BOTTOM |
+                            ZXDG_POSITIONER_V6_GRAVITY_LEFT)
+                .SetAnchor(ZXDG_POSITIONER_V6_ANCHOR_BOTTOM |
+                           ZXDG_POSITIONER_V6_ANCHOR_LEFT)
+                .SolveToRect(),
+            gfx::Rect(0, 3, 2, 2));
+
+  // Gravity + anchor in opposing directions.
+  EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::UNSTABLE)
+                .SetSize(2, 2)
+                .SetGravity(ZXDG_POSITIONER_V6_GRAVITY_BOTTOM |
+                            ZXDG_POSITIONER_V6_GRAVITY_LEFT)
+                .SetAnchor(ZXDG_POSITIONER_V6_ANCHOR_TOP |
+                           ZXDG_POSITIONER_V6_ANCHOR_RIGHT)
+                .SolveToRect(),
+            gfx::Rect(1, 2, 2, 2));
+}
+
+TEST_F(WaylandPositionerTest, FlipSlideResizePriorityUnstable) {
+  TestCaseBuilder builder{WaylandPositioner::Version::UNSTABLE};
+  builder.SetAnchorRect(4, 4, 0, 0)
+      .SetSize(2, 2)
+      .SetGravity(ZXDG_POSITIONER_V6_GRAVITY_BOTTOM |
+                  ZXDG_POSITIONER_V6_GRAVITY_RIGHT)
+      .SetAnchor(ZXDG_POSITIONER_V6_ANCHOR_BOTTOM |
+                 ZXDG_POSITIONER_V6_ANCHOR_RIGHT);
+  // Flip is enabled, so the result will be at 2,2 (i.e. flipping a 2-wide
+  // square around 4,4).
+  EXPECT_EQ(
+      builder.SetAdjustment(~ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_NONE)
+          .SolveToRect(),
+      gfx::Rect(2, 2, 2, 2));
+  // If we cant flip on an axis, that axis will slide to 3 instead.
+  EXPECT_EQ(
+      builder.SetAdjustment(~ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_FLIP_X)
+          .SolveToRect(),
+      gfx::Rect(3, 2, 2, 2));
+  EXPECT_EQ(
+      builder.SetAdjustment(~ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_FLIP_Y)
+          .SolveToRect(),
+      gfx::Rect(2, 3, 2, 2));
+  // If we cant flip or slide, we resize.
+  EXPECT_EQ(
+      builder
+          .SetAdjustment(ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_RESIZE_X |
+                         ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_RESIZE_Y)
+          .SolveToRect(),
+      gfx::Rect(4, 4, 1, 1));
+}
+
+TEST_F(WaylandPositionerTest, TriesToMaximizeAreaUnstable) {
+  // The size is too large to fit where the anchor is.
+  WaylandPositioner::Result result =
+      TestCaseBuilder(WaylandPositioner::Version::UNSTABLE)
+          .SetAnchorRect(2, 4, 0, 0)
+          .SetSize(4, 10)
+          .SetGravity(ZXDG_POSITIONER_V6_GRAVITY_BOTTOM |
+                      ZXDG_POSITIONER_V6_GRAVITY_RIGHT)
+          .SetAnchor(ZXDG_POSITIONER_V6_ANCHOR_BOTTOM |
+                     ZXDG_POSITIONER_V6_ANCHOR_RIGHT)
+          .SetAdjustment(~ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_NONE)
+          .Solve();
+  // We can slide to 1 on x, but we must resize on y (after sliding to 0).
+  EXPECT_EQ(result.origin, gfx::Point(1, 0));
+  // The x size will be preserved but y shrinks to the work area.
+  EXPECT_EQ(result.size, gfx::Size(4, 5));
+}
+
+TEST_F(WaylandPositionerTest, PropagatesAnInitialFlipUnstable) {
+  WaylandPositioner::Result result =
+      TestCaseBuilder(WaylandPositioner::Version::UNSTABLE)
+          .SetAnchorRect(3, 1, 0, 0)
+          .SetSize(2, 2)
+          .SetGravity(ZXDG_POSITIONER_V6_GRAVITY_BOTTOM |
+                      ZXDG_POSITIONER_V6_GRAVITY_RIGHT)
+          .SetAnchor(ZXDG_POSITIONER_V6_ANCHOR_BOTTOM |
+                     ZXDG_POSITIONER_V6_ANCHOR_RIGHT)
+          .SetAdjustment(~ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_NONE)
+          .SetFlipState(true, true)
+          .Solve();
+  // With a propagated flip state:
+  //  - X and Y remain flipped to be positioned by the client.
+  EXPECT_EQ(result.origin, gfx::Point(3, 1));
+  EXPECT_EQ(result.size, gfx::Size(2, 2));
+}
+
+// This is a common case for dropdown menus. In ChromeOS we do not let them
+// slide if they might occlude the anchor rectangle. For this case, x axis does
+// slide but the y axis resized instead.
+TEST_F(WaylandPositionerTest, PreventsSlidingThatOccludesAnchorRectUnstable) {
+  EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::UNSTABLE)
+                .SetSize(3, 3)
+                .SetGravity(ZXDG_POSITIONER_V6_GRAVITY_BOTTOM |
+                            ZXDG_POSITIONER_V6_GRAVITY_RIGHT)
+                .SetAnchor(ZXDG_POSITIONER_V6_ANCHOR_BOTTOM |
+                           ZXDG_POSITIONER_V6_ANCHOR_LEFT)
+                .SetAdjustment(~ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_NONE)
+                .SolveToRect(),
+            gfx::Rect(2, 3, 3, 2));
+
+  // Here we ensure that the 4x4 popup does slide, which is allowed because
+  // the anchor rect is already occluded.
+  EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::UNSTABLE)
+                .SetSize(4, 4)
+                .SetGravity(ZXDG_POSITIONER_V6_GRAVITY_BOTTOM |
+                            ZXDG_POSITIONER_V6_GRAVITY_RIGHT)
+                .SetAnchor(ZXDG_POSITIONER_V6_ANCHOR_TOP |
+                           ZXDG_POSITIONER_V6_ANCHOR_LEFT)
+                .SetAdjustment(~ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_NONE)
+                .SolveToRect(),
+            gfx::Rect(1, 1, 4, 4));
+}
+
+// Allowing sliding which will occlude the anchor if there are no other
+// positioning options which do not result in a constrained view available.
+TEST_F(WaylandPositionerTest,
+       AllowsSlidingThatOccludesWhenThereAreNoOtherOptionsUnstable) {
+  EXPECT_EQ(
+      TestCaseBuilder(WaylandPositioner::Version::UNSTABLE)
+          .SetSize(4, 4)
+          .SetGravity(XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT)
+          .SetAnchor(XDG_POSITIONER_ANCHOR_BOTTOM_LEFT)
+          // Disable resizing in both axes which will force sliding.
+          .SetAdjustment(~(ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_RESIZE_X |
+                           ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_RESIZE_Y))
+          .SolveToRect(),
+      gfx::Rect(1, 1, 4, 4));
+}
+
+TEST_F(WaylandPositionerTest,
+       AllowsAdditionalAdjustmentsIfNoSolutionCanBeFoundUnstable) {
+  EXPECT_EQ(
+      TestCaseBuilder(WaylandPositioner::Version::UNSTABLE)
+          .SetWorkArea(gfx::Rect(5, 5))
+          .SetSize(10, 10)
+          .SetAnchorRect(0, 0, 0, 0)
+          .SetGravity(ZXDG_POSITIONER_V6_GRAVITY_BOTTOM |
+                      ZXDG_POSITIONER_V6_GRAVITY_RIGHT)
+          // No solution should forcibly allow resize
+          .SetAdjustment(ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_SLIDE_X |
+                         ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_SLIDE_Y)
+          .SolveToRect(),
+      gfx::Rect(0, 0, 5, 5));
+}
+
+// Tests for the stable protocol.
+
+TEST_F(WaylandPositionerTest, UnconstrainedCases) {
+  // No gravity or anchor.
+  EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::STABLE)
+                .SetSize(1, 1)
+                .SolveToRect(),
+            gfx::Rect(2, 2, 1, 1));
+
+  // Anchor without gravity.
+  EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::STABLE)
                 .SetSize(2, 1)
                 .SetAnchor(XDG_POSITIONER_ANCHOR_RIGHT)
                 .SolveToRect(),
             gfx::Rect(2, 2, 2, 1));
-  EXPECT_EQ(TestCaseBuilder()
+  EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::STABLE)
                 .SetSize(2, 1)
                 .SetAnchor(XDG_POSITIONER_ANCHOR_LEFT)
                 .SolveToRect(),
             gfx::Rect(1, 2, 2, 1));
 
   // Gravity without anchor.
-  EXPECT_EQ(TestCaseBuilder()
+  EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::STABLE)
                 .SetSize(1, 2)
                 .SetAnchorRect(2, 2, 0, 0)
                 .SetGravity(XDG_POSITIONER_GRAVITY_TOP)
                 .SolveToRect(),
             gfx::Rect(2, 0, 1, 2));
-  EXPECT_EQ(TestCaseBuilder()
+  EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::STABLE)
                 .SetSize(1, 2)
                 .SetAnchorRect(2, 2, 0, 0)
                 .SetGravity(XDG_POSITIONER_GRAVITY_BOTTOM)
@@ -100,7 +290,7 @@
             gfx::Rect(2, 2, 1, 2));
 
   // Gravity + anchor in the same direction.
-  EXPECT_EQ(TestCaseBuilder()
+  EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::STABLE)
                 .SetSize(2, 2)
                 .SetGravity(XDG_POSITIONER_GRAVITY_BOTTOM_LEFT)
                 .SetAnchor(XDG_POSITIONER_ANCHOR_BOTTOM_LEFT)
@@ -108,7 +298,7 @@
             gfx::Rect(0, 3, 2, 2));
 
   // Gravity + anchor in opposing directions.
-  EXPECT_EQ(TestCaseBuilder()
+  EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::STABLE)
                 .SetSize(2, 2)
                 .SetGravity(XDG_POSITIONER_GRAVITY_BOTTOM_LEFT)
                 .SetAnchor(XDG_POSITIONER_ANCHOR_TOP_RIGHT)
@@ -117,7 +307,7 @@
 }
 
 TEST_F(WaylandPositionerTest, FlipSlideResizePriority) {
-  TestCaseBuilder builder;
+  TestCaseBuilder builder{WaylandPositioner::Version::STABLE};
   builder.SetAnchorRect(4, 4, 0, 0)
       .SetSize(2, 2)
       .SetGravity(XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT)
@@ -145,7 +335,7 @@
 TEST_F(WaylandPositionerTest, TriesToMaximizeArea) {
   // The size is too large to fit where the anchor is.
   WaylandPositioner::Result result =
-      TestCaseBuilder()
+      TestCaseBuilder(WaylandPositioner::Version::STABLE)
           .SetAnchorRect(2, 4, 0, 0)
           .SetSize(4, 10)
           .SetGravity(XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT)
@@ -160,7 +350,7 @@
 
 TEST_F(WaylandPositionerTest, PropagatesAnInitialFlip) {
   WaylandPositioner::Result result =
-      TestCaseBuilder()
+      TestCaseBuilder(WaylandPositioner::Version::STABLE)
           .SetAnchorRect(3, 1, 0, 0)
           .SetSize(2, 2)
           .SetGravity(XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT)
@@ -178,7 +368,7 @@
 // slide if they might occlude the anchor rectangle. For this case, x axis does
 // slide but the y axis resized instead.
 TEST_F(WaylandPositionerTest, PreventsSlidingThatOccludesAnchorRect) {
-  EXPECT_EQ(TestCaseBuilder()
+  EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::STABLE)
                 .SetSize(3, 3)
                 .SetGravity(XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT)
                 .SetAnchor(XDG_POSITIONER_ANCHOR_BOTTOM_LEFT)
@@ -188,7 +378,7 @@
 
   // Here we ensure that the 4x4 popup does slide, which is allowed because
   // the anchor rect is already occluded.
-  EXPECT_EQ(TestCaseBuilder()
+  EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::STABLE)
                 .SetSize(4, 4)
                 .SetGravity(XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT)
                 .SetAnchor(XDG_POSITIONER_ANCHOR_TOP_LEFT)
@@ -201,7 +391,7 @@
 // positioning options which do not result in a constrained view available.
 TEST_F(WaylandPositionerTest,
        AllowsSlidingThatOccludesWhenThereAreNoOtherOptions) {
-  EXPECT_EQ(TestCaseBuilder()
+  EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::STABLE)
                 .SetSize(4, 4)
                 .SetGravity(XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT)
                 .SetAnchor(XDG_POSITIONER_ANCHOR_BOTTOM_LEFT)
@@ -215,7 +405,7 @@
 // Make sure that the size should never be an empty even if the constraints
 // resulted in empty size.
 TEST_F(WaylandPositionerTest, ResizableShouldNotBeEmpty) {
-  EXPECT_EQ(TestCaseBuilder()
+  EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::STABLE)
                 .SetSize(3, 3)
                 .SetGravity(XDG_POSITIONER_GRAVITY_BOTTOM)
                 .SetAnchor(XDG_POSITIONER_ANCHOR_BOTTOM)
@@ -223,7 +413,7 @@
                 .SetAnchorRect(1, -10, 4, 4)
                 .SolveToRect(),
             gfx::Rect(2, 0, 3, 1));
-  EXPECT_EQ(TestCaseBuilder()
+  EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::STABLE)
                 .SetSize(3, 3)
                 .SetGravity(XDG_POSITIONER_GRAVITY_RIGHT)
                 .SetAnchor(XDG_POSITIONER_ANCHOR_RIGHT)
@@ -235,7 +425,7 @@
 
 TEST_F(WaylandPositionerTest,
        AllowsAdditionalAdjustmentsIfNoSolutionCanBeFound) {
-  EXPECT_EQ(TestCaseBuilder()
+  EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::STABLE)
                 .SetWorkArea(gfx::Rect(5, 5))
                 .SetSize(10, 10)
                 .SetAnchorRect(0, 0, 0, 0)
diff --git a/components/exo/wayland/xdg_shell.cc b/components/exo/wayland/xdg_shell.cc
index 560e959..0fc7cf8d 100644
--- a/components/exo/wayland/xdg_shell.cc
+++ b/components/exo/wayland/xdg_shell.cc
@@ -730,8 +730,9 @@
   wl_resource* positioner_resource = wl_resource_create(
       client, &xdg_positioner_interface, wl_resource_get_version(resource), id);
 
-  SetImplementation(positioner_resource, &xdg_positioner_implementation,
-                    std::make_unique<WaylandPositioner>());
+  SetImplementation(
+      positioner_resource, &xdg_positioner_implementation,
+      std::make_unique<WaylandPositioner>(WaylandPositioner::Version::STABLE));
 }
 
 void xdg_wm_base_get_xdg_surface(wl_client* client,
diff --git a/components/exo/wayland/zxdg_shell.cc b/components/exo/wayland/zxdg_shell.cc
new file mode 100644
index 0000000..80ad3db
--- /dev/null
+++ b/components/exo/wayland/zxdg_shell.cc
@@ -0,0 +1,690 @@
+// Copyright 2018 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/exo/wayland/zxdg_shell.h"
+
+#include <wayland-server-core.h>
+#include <wayland-server-protocol-core.h>
+#include <xdg-shell-unstable-v6-server-protocol.h>
+
+#include "ash/public/cpp/shell_window_ids.h"
+#include "ash/public/cpp/window_properties.h"
+#include "base/functional/bind.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chromeos/ui/base/window_state_type.h"
+#include "components/exo/display.h"
+#include "components/exo/shell_surface_util.h"
+#include "components/exo/wayland/serial_tracker.h"
+#include "components/exo/wayland/server_util.h"
+#include "components/exo/wayland/wayland_positioner.h"
+#include "components/exo/wayland/xdg_shell.h"
+#include "components/exo/xdg_shell_surface.h"
+#include "ui/aura/window_observer.h"
+#include "ui/base/hit_test.h"
+#include "ui/display/screen.h"
+#include "ui/views/widget/widget.h"
+#include "ui/wm/core/coordinate_conversion.h"
+
+namespace exo {
+namespace wayland {
+namespace {
+
+////////////////////////////////////////////////////////////////////////////////
+// xdg_positioner_interface:
+
+void xdg_positioner_v6_destroy(wl_client* client, wl_resource* resource) {
+  wl_resource_destroy(resource);
+}
+
+void xdg_positioner_v6_set_size(wl_client* client,
+                                wl_resource* resource,
+                                int32_t width,
+                                int32_t height) {
+  if (width < 1 || height < 1) {
+    wl_resource_post_error(resource, ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT,
+                           "width and height must be positive and non-zero");
+    return;
+  }
+
+  GetUserDataAs<WaylandPositioner>(resource)->SetSize(gfx::Size(width, height));
+}
+
+void xdg_positioner_v6_set_anchor_rect(wl_client* client,
+                                       wl_resource* resource,
+                                       int32_t x,
+                                       int32_t y,
+                                       int32_t width,
+                                       int32_t height) {
+  if (width < 1 || height < 1) {
+    wl_resource_post_error(resource, ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT,
+                           "width and height must be positive and non-zero");
+    return;
+  }
+
+  GetUserDataAs<WaylandPositioner>(resource)->SetAnchorRect(
+      gfx::Rect(x, y, width, height));
+}
+
+void xdg_positioner_v6_set_anchor(wl_client* client,
+                                  wl_resource* resource,
+                                  uint32_t anchor) {
+  if (((anchor & ZXDG_POSITIONER_V6_ANCHOR_LEFT) &&
+       (anchor & ZXDG_POSITIONER_V6_ANCHOR_RIGHT)) ||
+      ((anchor & ZXDG_POSITIONER_V6_ANCHOR_TOP) &&
+       (anchor & ZXDG_POSITIONER_V6_ANCHOR_BOTTOM))) {
+    wl_resource_post_error(resource, ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT,
+                           "same-axis values are not allowed");
+    return;
+  }
+
+  GetUserDataAs<WaylandPositioner>(resource)->SetAnchor(anchor);
+}
+
+void xdg_positioner_v6_set_gravity(wl_client* client,
+                                   wl_resource* resource,
+                                   uint32_t gravity) {
+  if (((gravity & ZXDG_POSITIONER_V6_GRAVITY_LEFT) &&
+       (gravity & ZXDG_POSITIONER_V6_GRAVITY_RIGHT)) ||
+      ((gravity & ZXDG_POSITIONER_V6_GRAVITY_TOP) &&
+       (gravity & ZXDG_POSITIONER_V6_GRAVITY_BOTTOM))) {
+    wl_resource_post_error(resource, ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT,
+                           "same-axis values are not allowed");
+    return;
+  }
+
+  GetUserDataAs<WaylandPositioner>(resource)->SetGravity(gravity);
+}
+
+void xdg_positioner_v6_set_constraint_adjustment(wl_client* client,
+                                                 wl_resource* resource,
+                                                 uint32_t adjustment) {
+  GetUserDataAs<WaylandPositioner>(resource)->SetAdjustment(adjustment);
+}
+
+void xdg_positioner_v6_set_offset(wl_client* client,
+                                  wl_resource* resource,
+                                  int32_t x,
+                                  int32_t y) {
+  GetUserDataAs<WaylandPositioner>(resource)->SetOffset(gfx::Vector2d(x, y));
+}
+
+const struct zxdg_positioner_v6_interface xdg_positioner_v6_implementation = {
+    xdg_positioner_v6_destroy,
+    xdg_positioner_v6_set_size,
+    xdg_positioner_v6_set_anchor_rect,
+    xdg_positioner_v6_set_anchor,
+    xdg_positioner_v6_set_gravity,
+    xdg_positioner_v6_set_constraint_adjustment,
+    xdg_positioner_v6_set_offset};
+
+////////////////////////////////////////////////////////////////////////////////
+// xdg_toplevel_interface:
+
+int XdgToplevelV6ResizeComponent(uint32_t edges) {
+  switch (edges) {
+    case ZXDG_TOPLEVEL_V6_RESIZE_EDGE_TOP:
+      return HTTOP;
+    case ZXDG_TOPLEVEL_V6_RESIZE_EDGE_BOTTOM:
+      return HTBOTTOM;
+    case ZXDG_TOPLEVEL_V6_RESIZE_EDGE_LEFT:
+      return HTLEFT;
+    case ZXDG_TOPLEVEL_V6_RESIZE_EDGE_TOP_LEFT:
+      return HTTOPLEFT;
+    case ZXDG_TOPLEVEL_V6_RESIZE_EDGE_BOTTOM_LEFT:
+      return HTBOTTOMLEFT;
+    case ZXDG_TOPLEVEL_V6_RESIZE_EDGE_RIGHT:
+      return HTRIGHT;
+    case ZXDG_TOPLEVEL_V6_RESIZE_EDGE_TOP_RIGHT:
+      return HTTOPRIGHT;
+    case ZXDG_TOPLEVEL_V6_RESIZE_EDGE_BOTTOM_RIGHT:
+      return HTBOTTOMRIGHT;
+    default:
+      return HTBOTTOMRIGHT;
+  }
+}
+
+using XdgSurfaceConfigureCallback =
+    base::RepeatingCallback<void(const gfx::Size& size,
+                                 chromeos::WindowStateType state_type,
+                                 bool resizing,
+                                 bool activated)>;
+
+uint32_t HandleXdgSurfaceV6ConfigureCallback(
+    wl_resource* resource,
+    SerialTracker* serial_tracker,
+    const XdgSurfaceConfigureCallback& callback,
+    const gfx::Rect& bounds,
+    chromeos::WindowStateType state_type,
+    bool resizing,
+    bool activated,
+    const gfx::Vector2d& origin_offset,
+    float raster_scale) {
+  uint32_t serial =
+      serial_tracker->GetNextSerial(SerialTracker::EventType::OTHER_EVENT);
+  callback.Run(bounds.size(), state_type, resizing, activated);
+  zxdg_surface_v6_send_configure(resource, serial);
+  wl_client_flush(wl_resource_get_client(resource));
+  return serial;
+}
+
+// Wrapper around shell surface that allows us to handle the case where the
+// xdg surface resource is destroyed before the toplevel resource.
+class WaylandToplevel : public aura::WindowObserver {
+ public:
+  WaylandToplevel(wl_resource* resource, wl_resource* surface_resource)
+      : resource_(resource),
+        shell_surface_data_(
+            GetUserDataAs<WaylandXdgSurface>(surface_resource)) {
+    shell_surface_data_->shell_surface->host_window()->AddObserver(this);
+    shell_surface_data_->shell_surface->set_close_callback(base::BindRepeating(
+        &WaylandToplevel::OnClose, weak_ptr_factory_.GetWeakPtr()));
+    shell_surface_data_->shell_surface->set_configure_callback(
+        base::BindRepeating(
+            &HandleXdgSurfaceV6ConfigureCallback, surface_resource,
+            shell_surface_data_->serial_tracker,
+            base::BindRepeating(&WaylandToplevel::OnConfigure,
+                                weak_ptr_factory_.GetWeakPtr())));
+  }
+
+  WaylandToplevel(const WaylandToplevel&) = delete;
+  WaylandToplevel& operator=(const WaylandToplevel&) = delete;
+
+  ~WaylandToplevel() override {
+    if (shell_surface_data_)
+      shell_surface_data_->shell_surface->host_window()->RemoveObserver(this);
+  }
+
+  // Overridden from aura::WindowObserver:
+  void OnWindowDestroying(aura::Window* window) override {
+    shell_surface_data_ = nullptr;
+  }
+
+  void SetParent(WaylandToplevel* parent) {
+    if (!shell_surface_data_)
+      return;
+
+    if (!parent) {
+      shell_surface_data_->shell_surface->SetParent(nullptr);
+      return;
+    }
+
+    if (this == parent) {
+      // Some apps e.g. crbug/1210235 try to be their own parent. Ignore them.
+      auto* app_id = GetShellApplicationId(
+          shell_surface_data_->shell_surface->host_window());
+      LOG(WARNING)
+          << "Client attempts to add itself as a transient parent: app_id="
+          << app_id;
+      return;
+    }
+
+    // This is a no-op if parent is not mapped.
+    if (parent->shell_surface_data_ &&
+        parent->shell_surface_data_->shell_surface->GetWidget())
+      shell_surface_data_->shell_surface->SetParent(
+          parent->shell_surface_data_->shell_surface.get());
+  }
+
+  void SetTitle(const std::u16string& title) {
+    if (shell_surface_data_)
+      shell_surface_data_->shell_surface->SetTitle(title);
+  }
+
+  void SetApplicationId(const char* application_id) {
+    if (shell_surface_data_)
+      shell_surface_data_->shell_surface->SetApplicationId(application_id);
+  }
+
+  void Move() {
+    if (shell_surface_data_)
+      shell_surface_data_->shell_surface->StartMove();
+  }
+
+  void Resize(int component) {
+    if (!shell_surface_data_)
+      return;
+
+    if (component != HTNOWHERE)
+      shell_surface_data_->shell_surface->StartResize(component);
+  }
+
+  void SetMaximumSize(const gfx::Size& size) {
+    if (shell_surface_data_)
+      shell_surface_data_->shell_surface->SetMaximumSize(size);
+  }
+
+  void SetMinimumSize(const gfx::Size& size) {
+    if (shell_surface_data_)
+      shell_surface_data_->shell_surface->SetMinimumSize(size);
+  }
+
+  void Maximize() {
+    if (shell_surface_data_)
+      shell_surface_data_->shell_surface->Maximize();
+  }
+
+  void Restore() {
+    if (shell_surface_data_)
+      shell_surface_data_->shell_surface->Restore();
+  }
+
+  void SetFullscreen(bool fullscreen) {
+    if (shell_surface_data_)
+      shell_surface_data_->shell_surface->SetFullscreen(fullscreen);
+  }
+
+  void Minimize() {
+    if (shell_surface_data_)
+      shell_surface_data_->shell_surface->Minimize();
+  }
+
+ private:
+  void OnClose() {
+    zxdg_toplevel_v6_send_close(resource_);
+    wl_client_flush(wl_resource_get_client(resource_));
+  }
+
+  static void AddState(wl_array* states, zxdg_toplevel_v6_state state) {
+    zxdg_toplevel_v6_state* value = static_cast<zxdg_toplevel_v6_state*>(
+        wl_array_add(states, sizeof(zxdg_toplevel_v6_state)));
+    DCHECK(value);
+    *value = state;
+  }
+
+  void OnConfigure(const gfx::Size& size,
+                   chromeos::WindowStateType state_type,
+                   bool resizing,
+                   bool activated) {
+    wl_array states;
+    wl_array_init(&states);
+    if (state_type == chromeos::WindowStateType::kMaximized)
+      AddState(&states, ZXDG_TOPLEVEL_V6_STATE_MAXIMIZED);
+    if (state_type == chromeos::WindowStateType::kFullscreen)
+      AddState(&states, ZXDG_TOPLEVEL_V6_STATE_FULLSCREEN);
+    if (resizing)
+      AddState(&states, ZXDG_TOPLEVEL_V6_STATE_RESIZING);
+    if (activated)
+      AddState(&states, ZXDG_TOPLEVEL_V6_STATE_ACTIVATED);
+    zxdg_toplevel_v6_send_configure(resource_, size.width(), size.height(),
+                                    &states);
+    wl_array_release(&states);
+  }
+
+  wl_resource* const resource_;
+  WaylandXdgSurface* shell_surface_data_;
+  base::WeakPtrFactory<WaylandToplevel> weak_ptr_factory_{this};
+};
+
+void xdg_toplevel_v6_destroy(wl_client* client, wl_resource* resource) {
+  wl_resource_destroy(resource);
+}
+
+void xdg_toplevel_v6_set_parent(wl_client* client,
+                                wl_resource* resource,
+                                wl_resource* parent) {
+  WaylandToplevel* parent_surface = nullptr;
+  if (parent)
+    parent_surface = GetUserDataAs<WaylandToplevel>(parent);
+
+  GetUserDataAs<WaylandToplevel>(resource)->SetParent(parent_surface);
+}
+
+void xdg_toplevel_v6_set_title(wl_client* client,
+                               wl_resource* resource,
+                               const char* title) {
+  GetUserDataAs<WaylandToplevel>(resource)->SetTitle(
+      std::u16string(base::UTF8ToUTF16(title)));
+}
+
+void xdg_toplevel_v6_set_app_id(wl_client* client,
+                                wl_resource* resource,
+                                const char* app_id) {
+  GetUserDataAs<WaylandToplevel>(resource)->SetApplicationId(app_id);
+}
+
+void xdg_toplevel_v6_show_window_menu(wl_client* client,
+                                      wl_resource* resource,
+                                      wl_resource* seat,
+                                      uint32_t serial,
+                                      int32_t x,
+                                      int32_t y) {
+  NOTIMPLEMENTED();
+}
+
+void xdg_toplevel_v6_move(wl_client* client,
+                          wl_resource* resource,
+                          wl_resource* seat,
+                          uint32_t serial) {
+  GetUserDataAs<WaylandToplevel>(resource)->Move();
+}
+
+void xdg_toplevel_v6_resize(wl_client* client,
+                            wl_resource* resource,
+                            wl_resource* seat,
+                            uint32_t serial,
+                            uint32_t edges) {
+  GetUserDataAs<WaylandToplevel>(resource)->Resize(
+      XdgToplevelV6ResizeComponent(edges));
+}
+
+void xdg_toplevel_v6_set_max_size(wl_client* client,
+                                  wl_resource* resource,
+                                  int32_t width,
+                                  int32_t height) {
+  GetUserDataAs<WaylandToplevel>(resource)->SetMaximumSize(
+      gfx::Size(width, height));
+}
+
+void xdg_toplevel_v6_set_min_size(wl_client* client,
+                                  wl_resource* resource,
+                                  int32_t width,
+                                  int32_t height) {
+  GetUserDataAs<WaylandToplevel>(resource)->SetMinimumSize(
+      gfx::Size(width, height));
+}
+
+void xdg_toplevel_v6_set_maximized(wl_client* client, wl_resource* resource) {
+  GetUserDataAs<WaylandToplevel>(resource)->Maximize();
+}
+
+void xdg_toplevel_v6_unset_maximized(wl_client* client, wl_resource* resource) {
+  GetUserDataAs<WaylandToplevel>(resource)->Restore();
+}
+
+void xdg_toplevel_v6_set_fullscreen(wl_client* client,
+                                    wl_resource* resource,
+                                    wl_resource* output) {
+  GetUserDataAs<WaylandToplevel>(resource)->SetFullscreen(true);
+}
+
+void xdg_toplevel_v6_unset_fullscreen(wl_client* client,
+                                      wl_resource* resource) {
+  GetUserDataAs<WaylandToplevel>(resource)->SetFullscreen(false);
+}
+
+void xdg_toplevel_v6_set_minimized(wl_client* client, wl_resource* resource) {
+  GetUserDataAs<WaylandToplevel>(resource)->Minimize();
+}
+
+const struct zxdg_toplevel_v6_interface xdg_toplevel_v6_implementation = {
+    xdg_toplevel_v6_destroy,          xdg_toplevel_v6_set_parent,
+    xdg_toplevel_v6_set_title,        xdg_toplevel_v6_set_app_id,
+    xdg_toplevel_v6_show_window_menu, xdg_toplevel_v6_move,
+    xdg_toplevel_v6_resize,           xdg_toplevel_v6_set_max_size,
+    xdg_toplevel_v6_set_min_size,     xdg_toplevel_v6_set_maximized,
+    xdg_toplevel_v6_unset_maximized,  xdg_toplevel_v6_set_fullscreen,
+    xdg_toplevel_v6_unset_fullscreen, xdg_toplevel_v6_set_minimized};
+
+////////////////////////////////////////////////////////////////////////////////
+// xdg_popup_interface:
+
+// Wrapper around shell surface that allows us to handle the case where the
+// xdg surface resource is destroyed before the popup resource.
+class WaylandPopup : aura::WindowObserver {
+ public:
+  WaylandPopup(wl_resource* resource, wl_resource* surface_resource)
+      : resource_(resource),
+        shell_surface_data_(
+            GetUserDataAs<WaylandXdgSurface>(surface_resource)) {
+    shell_surface_data_->shell_surface->host_window()->AddObserver(this);
+    shell_surface_data_->shell_surface->set_close_callback(base::BindRepeating(
+        &WaylandPopup::OnClose, weak_ptr_factory_.GetWeakPtr()));
+    shell_surface_data_->shell_surface->set_configure_callback(
+        base::BindRepeating(
+            &HandleXdgSurfaceV6ConfigureCallback, surface_resource,
+            shell_surface_data_->serial_tracker,
+            base::BindRepeating(&WaylandPopup::OnConfigure,
+                                weak_ptr_factory_.GetWeakPtr())));
+  }
+
+  WaylandPopup(const WaylandPopup&) = delete;
+  WaylandPopup& operator=(const WaylandPopup&) = delete;
+
+  ~WaylandPopup() override {
+    if (shell_surface_data_)
+      shell_surface_data_->shell_surface->host_window()->RemoveObserver(this);
+  }
+
+  void Grab() {
+    if (!shell_surface_data_) {
+      wl_resource_post_error(resource_, ZXDG_POPUP_V6_ERROR_INVALID_GRAB,
+                             "the surface has already been destroyed");
+      return;
+    }
+    if (shell_surface_data_->shell_surface->GetWidget()) {
+      wl_resource_post_error(resource_, ZXDG_POPUP_V6_ERROR_INVALID_GRAB,
+                             "grab must be called before construction");
+      return;
+    }
+    shell_surface_data_->shell_surface->Grab();
+  }
+
+  // Overridden from aura::WindowObserver:
+  void OnWindowDestroying(aura::Window* window) override {
+    shell_surface_data_ = nullptr;
+  }
+
+ private:
+  void OnClose() {
+    zxdg_popup_v6_send_popup_done(resource_);
+    wl_client_flush(wl_resource_get_client(resource_));
+  }
+
+  void OnConfigure(const gfx::Size& size,
+                   chromeos::WindowStateType state_type,
+                   bool resizing,
+                   bool activated) {
+    // Nothing to do here as popups don't have additional configure state.
+  }
+
+  wl_resource* const resource_;
+  WaylandXdgSurface* shell_surface_data_;
+  base::WeakPtrFactory<WaylandPopup> weak_ptr_factory_{this};
+};
+
+void xdg_popup_v6_destroy(wl_client* client, wl_resource* resource) {
+  wl_resource_destroy(resource);
+}
+
+void xdg_popup_v6_grab(wl_client* client,
+                       wl_resource* resource,
+                       wl_resource* seat,
+                       uint32_t serial) {
+  GetUserDataAs<WaylandPopup>(resource)->Grab();
+}
+
+const struct zxdg_popup_v6_interface xdg_popup_v6_implementation = {
+    xdg_popup_v6_destroy, xdg_popup_v6_grab};
+
+////////////////////////////////////////////////////////////////////////////////
+// xdg_surface_interface:
+
+void xdg_surface_v6_destroy(wl_client* client, wl_resource* resource) {
+  wl_resource_destroy(resource);
+}
+
+void xdg_surface_v6_get_toplevel(wl_client* client,
+                                 wl_resource* resource,
+                                 uint32_t id) {
+  auto* shell_surface_data = GetUserDataAs<WaylandXdgSurface>(resource);
+  if (shell_surface_data->shell_surface->GetEnabled()) {
+    wl_resource_post_error(resource, ZXDG_SURFACE_V6_ERROR_ALREADY_CONSTRUCTED,
+                           "surface has already been constructed");
+    return;
+  }
+
+  shell_surface_data->shell_surface->SetCanMinimize(true);
+  shell_surface_data->shell_surface->SetEnabled(true);
+
+  wl_resource* xdg_toplevel_resource =
+      wl_resource_create(client, &zxdg_toplevel_v6_interface, 1, id);
+
+  SetImplementation(
+      xdg_toplevel_resource, &xdg_toplevel_v6_implementation,
+      std::make_unique<WaylandToplevel>(xdg_toplevel_resource, resource));
+}
+
+void xdg_surface_v6_get_popup(wl_client* client,
+                              wl_resource* resource,
+                              uint32_t id,
+                              wl_resource* parent_resource,
+                              wl_resource* positioner_resource) {
+  auto* shell_surface_data = GetUserDataAs<WaylandXdgSurface>(resource);
+  if (shell_surface_data->shell_surface->GetEnabled()) {
+    wl_resource_post_error(resource, ZXDG_SURFACE_V6_ERROR_ALREADY_CONSTRUCTED,
+                           "surface has already been constructed");
+    return;
+  }
+
+  auto* parent_data = GetUserDataAs<WaylandXdgSurface>(parent_resource);
+  if (!parent_data->shell_surface->GetWidget()) {
+    wl_resource_post_error(resource, ZXDG_SURFACE_V6_ERROR_NOT_CONSTRUCTED,
+                           "popup parent not constructed");
+    return;
+  }
+
+  if (shell_surface_data->shell_surface->GetWidget()) {
+    wl_resource_post_error(resource, ZXDG_SURFACE_V6_ERROR_ALREADY_CONSTRUCTED,
+                           "get_popup is called after constructed");
+    return;
+  }
+
+  display::Display display =
+      display::Screen::GetScreen()->GetDisplayNearestWindow(
+          parent_data->shell_surface->GetWidget()->GetNativeWindow());
+  gfx::Rect work_area = display.work_area();
+  wm::ConvertRectFromScreen(
+      parent_data->shell_surface->GetWidget()->GetNativeWindow(), &work_area);
+
+  // Try layout using parent's flip state.
+  WaylandPositioner* positioner =
+      GetUserDataAs<WaylandPositioner>(positioner_resource);
+  WaylandPositioner::Result position = positioner->CalculateBounds(work_area);
+
+  // |position| is relative to the parent's contents view origin, and |origin|
+  // is in screen coordinates.
+  gfx::Point origin = position.origin;
+  views::View::ConvertPointToScreen(parent_data->shell_surface->GetWidget()
+                                        ->widget_delegate()
+                                        ->GetContentsView(),
+                                    &origin);
+  shell_surface_data->shell_surface->SetOrigin(origin);
+  shell_surface_data->shell_surface->SetSize(position.size);
+  shell_surface_data->shell_surface->DisableMovement();
+  shell_surface_data->shell_surface->SetActivatable(false);
+  shell_surface_data->shell_surface->SetCanMinimize(false);
+  shell_surface_data->shell_surface->SetParent(
+      parent_data->shell_surface.get());
+  shell_surface_data->shell_surface->SetPopup();
+  shell_surface_data->shell_surface->SetEnabled(true);
+
+  wl_resource* xdg_popup_resource =
+      wl_resource_create(client, &zxdg_popup_v6_interface, 1, id);
+
+  SetImplementation(
+      xdg_popup_resource, &xdg_popup_v6_implementation,
+      std::make_unique<WaylandPopup>(xdg_popup_resource, resource));
+
+  // We send the configure event here as this event needs x,y coordinates
+  // relative to the parent window.
+  zxdg_popup_v6_send_configure(xdg_popup_resource, position.origin.x(),
+                               position.origin.y(), position.size.width(),
+                               position.size.height());
+}
+
+void xdg_surface_v6_set_window_geometry(wl_client* client,
+                                        wl_resource* resource,
+                                        int32_t x,
+                                        int32_t y,
+                                        int32_t width,
+                                        int32_t height) {
+  GetUserDataAs<WaylandXdgSurface>(resource)->shell_surface->SetGeometry(
+      gfx::Rect(x, y, width, height));
+}
+
+void xdg_surface_v6_ack_configure(wl_client* client,
+                                  wl_resource* resource,
+                                  uint32_t serial) {
+  GetUserDataAs<WaylandXdgSurface>(resource)
+      ->shell_surface->AcknowledgeConfigure(serial);
+}
+
+const struct zxdg_surface_v6_interface xdg_surface_v6_implementation = {
+    xdg_surface_v6_destroy, xdg_surface_v6_get_toplevel,
+    xdg_surface_v6_get_popup, xdg_surface_v6_set_window_geometry,
+    xdg_surface_v6_ack_configure};
+
+////////////////////////////////////////////////////////////////////////////////
+// xdg_shell_interface:
+
+void xdg_shell_v6_destroy(wl_client* client, wl_resource* resource) {
+  // Nothing to do here.
+}
+
+void xdg_shell_v6_create_positioner(wl_client* client,
+                                    wl_resource* resource,
+                                    uint32_t id) {
+  wl_resource* positioner_resource =
+      wl_resource_create(client, &zxdg_positioner_v6_interface, 1, id);
+
+  SetImplementation(positioner_resource, &xdg_positioner_v6_implementation,
+                    std::make_unique<WaylandPositioner>(
+                        WaylandPositioner::Version::UNSTABLE));
+}
+
+void xdg_shell_v6_get_xdg_surface(wl_client* client,
+                                  wl_resource* resource,
+                                  uint32_t id,
+                                  wl_resource* surface) {
+  auto* data = GetUserDataAs<WaylandZxdgShell>(resource);
+  std::unique_ptr<XdgShellSurface> shell_surface =
+      data->display->CreateXdgShellSurface(GetUserDataAs<Surface>(surface));
+  if (!shell_surface) {
+    wl_resource_post_error(resource, ZXDG_SHELL_V6_ERROR_ROLE,
+                           "surface has already been assigned a role");
+    return;
+  }
+
+  // Xdg shell v6 surfaces are initially disabled and needs to be explicitly
+  // mapped before they are enabled and can become visible.
+  shell_surface->SetEnabled(false);
+
+  shell_surface->SetSecurityDelegate(GetSecurityDelegate(client));
+
+  std::unique_ptr<WaylandXdgSurface> wayland_shell_surface =
+      std::make_unique<WaylandXdgSurface>(std::move(shell_surface),
+                                          data->serial_tracker);
+
+  wl_resource* xdg_surface_resource =
+      wl_resource_create(client, &zxdg_surface_v6_interface, 1, id);
+
+  SetImplementation(xdg_surface_resource, &xdg_surface_v6_implementation,
+                    std::move(wayland_shell_surface));
+}
+
+void xdg_shell_v6_pong(wl_client* client,
+                       wl_resource* resource,
+                       uint32_t serial) {
+  NOTIMPLEMENTED();
+}
+
+const struct zxdg_shell_v6_interface xdg_shell_v6_implementation = {
+    xdg_shell_v6_destroy, xdg_shell_v6_create_positioner,
+    xdg_shell_v6_get_xdg_surface, xdg_shell_v6_pong};
+
+}  // namespace
+
+void bind_zxdg_shell_v6(wl_client* client,
+                        void* data,
+                        uint32_t version,
+                        uint32_t id) {
+  wl_resource* resource =
+      wl_resource_create(client, &zxdg_shell_v6_interface, 1, id);
+
+  wl_resource_set_implementation(resource, &xdg_shell_v6_implementation, data,
+                                 nullptr);
+}
+
+}  // namespace wayland
+}  // namespace exo
diff --git a/components/exo/wayland/zxdg_shell.h b/components/exo/wayland/zxdg_shell.h
new file mode 100644
index 0000000..4eadfdd
--- /dev/null
+++ b/components/exo/wayland/zxdg_shell.h
@@ -0,0 +1,40 @@
+// Copyright 2018 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_EXO_WAYLAND_ZXDG_SHELL_H_
+#define COMPONENTS_EXO_WAYLAND_ZXDG_SHELL_H_
+
+#include <stdint.h>
+
+struct wl_client;
+
+namespace exo {
+class Display;
+
+namespace wayland {
+class SerialTracker;
+
+struct WaylandZxdgShell {
+  WaylandZxdgShell(Display* display, SerialTracker* serial_tracker)
+      : display(display), serial_tracker(serial_tracker) {}
+
+  WaylandZxdgShell(const WaylandZxdgShell&) = delete;
+  WaylandZxdgShell& operator=(const WaylandZxdgShell&) = delete;
+
+  // Owned by WaylandServerController, which always outlives zxdg_shell.
+  Display* const display;
+
+  // Owned by Server, which always outlives zxdg_shell.
+  SerialTracker* const serial_tracker;
+};
+
+void bind_zxdg_shell_v6(wl_client* client,
+                        void* data,
+                        uint32_t version,
+                        uint32_t id);
+
+}  // namespace wayland
+}  // namespace exo
+
+#endif  // COMPONENTS_EXO_WAYLAND_ZXDG_SHELL_H_
diff --git a/components/history_clusters/core/config.cc b/components/history_clusters/core/config.cc
index 4401db2..51b8d1f 100644
--- a/components/history_clusters/core/config.cc
+++ b/components/history_clusters/core/config.cc
@@ -137,14 +137,6 @@
   {
     omnibox_action = base::FeatureList::IsEnabled(internal::kOmniboxAction);
 
-    omnibox_action_on_urls = base::GetFieldTrialParamByFeatureAsBool(
-        internal::kOmniboxAction, "omnibox_action_on_urls",
-        omnibox_action_on_urls);
-
-    omnibox_action_on_noisy_urls = base::GetFieldTrialParamByFeatureAsBool(
-        internal::kOmniboxAction, "omnibox_action_on_noisy_urls",
-        omnibox_action_on_noisy_urls);
-
     omnibox_action_with_pedals = base::GetFieldTrialParamByFeatureAsBool(
         internal::kOmniboxAction, "omnibox_action_with_pedals",
         omnibox_action_with_pedals);
diff --git a/components/history_clusters/core/config.h b/components/history_clusters/core/config.h
index f05060d..c34abf5 100644
--- a/components/history_clusters/core/config.h
+++ b/components/history_clusters/core/config.h
@@ -144,17 +144,6 @@
   // for this to take effect.
   bool omnibox_action = false;
 
-  // If enabled, allows the Omnibox Action chip to also appear on URLs. This
-  // does nothing if `omnibox_action` is disabled. Note, that if you turn this
-  // flag to true, you almost certainly will want to set
-  // `omnibox_action_on_navigation_intents` to true as well, as otherwise your
-  // desired action chips on URLs will almost certainly all be suppressed.
-  bool omnibox_action_on_urls = false;
-
-  // If enabled, allows the Omnibox Action chip to appear on URLs from noisy
-  // visits. This does nothing if `omnibox_action_on_urls` is disabled.
-  bool omnibox_action_on_noisy_urls = true;
-
   // If enabled, allows the Omnibox Action chip to appear when the suggestions
   // contain pedals. Does nothing if `omnibox_action` is disabled.
   bool omnibox_action_with_pedals = false;
diff --git a/components/history_clusters/core/history_clusters_service.cc b/components/history_clusters/core/history_clusters_service.cc
index d15761f..3bda01d4 100644
--- a/components/history_clusters/core/history_clusters_service.cc
+++ b/components/history_clusters/core/history_clusters_service.cc
@@ -388,31 +388,10 @@
   return absl::nullopt;
 }
 
-bool HistoryClustersService::DoesURLMatchAnyCluster(
-    const std::string& url_keyword) {
-  if (!IsJourneysEnabled())
-    return false;
-
-  // We don't want any omnibox jank for low-end devices.
-  if (base::SysInfo::IsLowEndDevice())
-    return false;
-
-  StartKeywordCacheRefresh();
-  if (GetConfig().persist_on_query)
-    UpdateClusters();
-
-  return short_url_keywords_cache_.find(url_keyword) !=
-             short_url_keywords_cache_.end() ||
-         all_url_keywords_cache_.find(url_keyword) !=
-             all_url_keywords_cache_.end();
-}
-
 void HistoryClustersService::ClearKeywordCache() {
   all_keywords_cache_timestamp_ = base::Time();
   short_keyword_cache_timestamp_ = base::Time();
   all_keywords_cache_.clear();
-  all_url_keywords_cache_.clear();
-  short_keyword_cache_.clear();
   short_keyword_cache_.clear();
   cache_keyword_query_task_.reset();
   WriteShortCacheToPrefs();
@@ -424,13 +403,11 @@
   NotifyDebugMessage("Timestamp: " +
                      GetDebugTime(short_keyword_cache_timestamp_));
   NotifyDebugMessage(GetDebugJSONForKeywordMap(short_keyword_cache_));
-  NotifyDebugMessage(GetDebugJSONForUrlKeywordSet(short_url_keywords_cache_));
 
   NotifyDebugMessage("-- Printing All-Time Keyword Bag --");
   NotifyDebugMessage("Timestamp: " +
                      GetDebugTime(all_keywords_cache_timestamp_));
   NotifyDebugMessage(GetDebugJSONForKeywordMap(all_keywords_cache_));
-  NotifyDebugMessage(GetDebugJSONForUrlKeywordSet(all_url_keywords_cache_));
 
   NotifyDebugMessage("-- Printing Keyword Bags Done --");
 }
@@ -469,9 +446,7 @@
         base::BindOnce(&HistoryClustersService::PopulateClusterKeywordCache,
                        weak_ptr_factory_.GetWeakPtr(), base::ElapsedTimer(),
                        /*begin_time=*/base::Time(),
-                       std::make_unique<KeywordMap>(),
-                       std::make_unique<URLKeywordSet>(), &all_keywords_cache_,
-                       &all_url_keywords_cache_));
+                       std::make_unique<KeywordMap>(), &all_keywords_cache_));
   } else if ((base::Time::Now() - all_keywords_cache_timestamp_).InSeconds() >
                  10 &&
              (base::Time::Now() - short_keyword_cache_timestamp_).InSeconds() >
@@ -488,9 +463,7 @@
         base::BindOnce(&HistoryClustersService::PopulateClusterKeywordCache,
                        weak_ptr_factory_.GetWeakPtr(), base::ElapsedTimer(),
                        all_keywords_cache_timestamp_,
-                       std::make_unique<KeywordMap>(),
-                       std::make_unique<URLKeywordSet>(), &short_keyword_cache_,
-                       &short_url_keywords_cache_));
+                       std::make_unique<KeywordMap>(), &short_keyword_cache_));
   }
 }
 
@@ -498,9 +471,7 @@
     base::ElapsedTimer total_latency_timer,
     base::Time begin_time,
     std::unique_ptr<KeywordMap> keyword_accumulator,
-    std::unique_ptr<URLKeywordSet> url_keyword_accumulator,
     KeywordMap* cache,
-    URLKeywordSet* url_cache,
     std::vector<history::Cluster> clusters,
     QueryClustersContinuationParams continuation_params) {
   base::ElapsedThreadTimer populate_keywords_thread_timer;
@@ -544,26 +515,6 @@
         }
       }
     }
-
-    // Push a simplified form of the URL for each visit into the cache.
-    if (url_keyword_accumulator->size() < max_keyword_phrases) {
-      for (const auto& visit : cluster.visits) {
-        if (visit.engagement_score >
-                GetConfig().noisy_cluster_visits_engagement_threshold &&
-            !GetConfig().omnibox_action_on_noisy_urls) {
-          // Do not add a noisy visit to the URL keyword accumulator if not
-          // enabled via flag. Note that this is at the visit-level rather than
-          // at the cluster-level, which is handled by the NoisyClusterFinalizer
-          // in the ClusteringBackend.
-          continue;
-        }
-        url_keyword_accumulator->insert(
-            (!visit.annotated_visit.content_annotations.search_normalized_url
-                  .is_empty())
-                ? visit.normalized_url.spec()
-                : ComputeURLKeywordForLookup(visit.normalized_url));
-      }
-    }
   }
 
   // Make a continuation request to get the next page of clusters and their
@@ -572,8 +523,7 @@
   constexpr char kKeywordCacheThreadTimeUmaName[] =
       "History.Clusters.KeywordCache.ThreadTime";
   if (!continuation_params.exhausted_all_visits &&
-      (keyword_accumulator->size() < max_keyword_phrases ||
-       url_keyword_accumulator->size() < max_keyword_phrases)) {
+      keyword_accumulator->size() < max_keyword_phrases) {
     const ClusteringRequestSource clustering_request_source =
         cache == &all_keywords_cache_
             ? ClusteringRequestSource::kAllKeywordCacheRefresh
@@ -586,8 +536,7 @@
                        weak_ptr_factory_.GetWeakPtr(),
                        std::move(total_latency_timer), begin_time,
                        // Pass on the accumulator sets to the next callback.
-                       std::move(keyword_accumulator),
-                       std::move(url_keyword_accumulator), cache, url_cache));
+                       std::move(keyword_accumulator), cache));
     // Log this even if we go back for more clusters.
     base::UmaHistogramTimes(kKeywordCacheThreadTimeUmaName,
                             populate_keywords_thread_timer.Elapsed());
@@ -598,12 +547,9 @@
   // via the constructor for efficiency (as recommended by the flat_set docs).
   // De-duplication is handled by the flat_set itself.
   *cache = std::move(*keyword_accumulator);
-  *url_cache = std::move(*url_keyword_accumulator);
   if (ShouldNotifyDebugMessage()) {
     NotifyDebugMessage("Cache construction complete; keyword cache:");
     NotifyDebugMessage(GetDebugJSONForKeywordMap(*cache));
-    NotifyDebugMessage("Url cache:");
-    NotifyDebugMessage(GetDebugJSONForUrlKeywordSet(*url_cache));
   }
 
   // Record keyword phrase & keyword counts for the appropriate cache.
diff --git a/components/history_clusters/core/history_clusters_service.h b/components/history_clusters/core/history_clusters_service.h
index 06af0fe2..970fe8c 100644
--- a/components/history_clusters/core/history_clusters_service.h
+++ b/components/history_clusters/core/history_clusters_service.h
@@ -8,7 +8,6 @@
 #include <map>
 #include <memory>
 #include <string>
-#include <unordered_set>
 #include <vector>
 
 #include "base/functional/callback.h"
@@ -85,7 +84,6 @@
   // percentile, and we do synchronous lookups as the user types in the omnibox.
   using KeywordMap =
       std::unordered_map<std::u16string, history::ClusterKeywordData>;
-  using URLKeywordSet = std::unordered_set<std::string>;
 
   // `url_loader_factory` is allowed to be nullptr, like in unit tests.
   HistoryClustersService(
@@ -186,12 +184,6 @@
   absl::optional<history::ClusterKeywordData> DoesQueryMatchAnyCluster(
       const std::string& query);
 
-  // Returns true if `url_keyword` matches a URL in a significant cluster. This
-  // may kick off a cache refresh while still immediately returning false.
-  // `url_keyword` is derived from a given URL by ComputeURLKeywordForLookup().
-  // SRP URLs canonicalized by TemplateURLService should be passed in directly.
-  bool DoesURLMatchAnyCluster(const std::string& url_keyword);
-
   // Clears `all_keywords_cache_` and cancels any pending tasks to populate it.
   void ClearKeywordCache();
 
@@ -219,9 +211,7 @@
       base::ElapsedTimer total_latency_timer,
       base::Time begin_time,
       std::unique_ptr<KeywordMap> keyword_accumulator,
-      std::unique_ptr<URLKeywordSet> url_keyword_accumulator,
       KeywordMap* cache,
-      URLKeywordSet* url_cache,
       std::vector<history::Cluster> clusters,
       QueryClustersContinuationParams continuation_params);
 
@@ -255,7 +245,6 @@
   // the cache was generated so we can periodically re-generate.
   // TODO(tommycli): Make a smarter mechanism for regenerating the cache.
   KeywordMap all_keywords_cache_;
-  URLKeywordSet all_url_keywords_cache_;
   base::Time all_keywords_cache_timestamp_;
 
   // Like above, but will represent the clusters newer than
@@ -269,7 +258,6 @@
   // TODO(manukh) This is a "band aid" fix to missing keywords for recent
   //  visits.
   KeywordMap short_keyword_cache_;
-  URLKeywordSet short_url_keywords_cache_;
   base::Time short_keyword_cache_timestamp_;
 
   // Tracks the current keyword task. Will be `nullptr` or
diff --git a/components/history_clusters/core/history_clusters_service_test_api.h b/components/history_clusters/core/history_clusters_service_test_api.h
index c685734a..860b217 100644
--- a/components/history_clusters/core/history_clusters_service_test_api.h
+++ b/components/history_clusters/core/history_clusters_service_test_api.h
@@ -66,10 +66,6 @@
     history_clusters_service_->all_keywords_cache_ = cache;
   }
 
-  void SetAllUrlKeywordsCache(HistoryClustersService::URLKeywordSet cache) {
-    history_clusters_service_->all_url_keywords_cache_ = cache;
-  }
-
   HistoryClustersService* const history_clusters_service_;
   history::HistoryService* const history_service_;
 };
diff --git a/components/history_clusters/core/history_clusters_service_unittest.cc b/components/history_clusters/core/history_clusters_service_unittest.cc
index d9b1448..a66335d 100644
--- a/components/history_clusters/core/history_clusters_service_unittest.cc
+++ b/components/history_clusters/core/history_clusters_service_unittest.cc
@@ -1390,280 +1390,6 @@
       ExpectSyncedVisits());
 }
 
-TEST_P(HistoryClustersServiceTest, DoesURLMatchAnyClusterWithNoisyURLs) {
-  Config config;
-  config.omnibox_action_on_urls = true;
-  config.omnibox_action_on_noisy_urls = true;
-  SetConfigForTesting(config);
-
-  AddHardcodedTestDataToHistoryService();
-
-  // Verify that initially, the test URL doesn't match anything, but this
-  // query should have kicked off a cache population request. This is the URL
-  // for visit 5.
-  EXPECT_FALSE(history_clusters_service_->DoesURLMatchAnyCluster(
-      ComputeURLKeywordForLookup(GURL("https://second-1-day-old-visit.com/"))));
-
-  std::vector<history::Cluster> clusters;
-  // This cluster should contribute to keywords.
-  clusters.push_back(history::Cluster(
-      0,
-      {
-          GetHardcodedClusterVisit(5),
-          GetHardcodedClusterVisit(5),
-          GetHardcodedClusterVisit(
-              /*visit_id=*/2, /*score=*/0.0, /*engagement_score=*/20.0),
-      },
-      {{u"apples", history::ClusterKeywordData()},
-       {u"oranges", history::ClusterKeywordData()},
-       {u"z", history::ClusterKeywordData()},
-       {u"apples bananas", history::ClusterKeywordData()}},
-      /*should_show_on_prominent_ui_surfaces=*/true));
-  // This cluster should NOT contribute to keywords because
-  // `should_show_on_prominent_ui_surfaces` is false.
-  clusters.push_back(
-      history::Cluster(0,
-                       {
-                           GetHardcodedClusterVisit(5),
-                           GetHardcodedClusterVisit(2),
-                       },
-                       {{u"sensitive", history::ClusterKeywordData()}},
-                       /*should_show_on_prominent_ui_surfaces=*/false));
-  // This cluster should NOT contribute to keywords because it only has 1
-  // visible visit.
-  clusters.push_back(
-      history::Cluster(0,
-                       {
-                           GetHardcodedClusterVisit(2),
-                           GetHardcodedClusterVisit(2, /*score=*/0),
-                       },
-                       {{u"singlevisit", history::ClusterKeywordData()}},
-                       /*should_show_on_prominent_ui_surfaces=*/true));
-
-  // Hardcoded test visits span 3 days (1-day-old, 2-days-old, and 60-day-old).
-  FlushKeywordRequests(clusters, 3);
-
-  // Now the exact query should match the populated cache.
-  EXPECT_TRUE(history_clusters_service_->DoesURLMatchAnyCluster(
-      ComputeURLKeywordForLookup(GURL("https://second-1-day-old-visit.com/"))));
-
-  // Github should be shown since we are including visits from noisy URLs.
-  EXPECT_TRUE(history_clusters_service_->DoesURLMatchAnyCluster(
-      ComputeURLKeywordForLookup(GURL("https://github.com/"))));
-
-  // Deleting a history entry should clear the keyword cache.
-  history_service_->DeleteURLs({GURL{"https://google.com/"}});
-  history::BlockUntilHistoryProcessesPendingRequests(history_service_.get());
-  EXPECT_FALSE(history_clusters_service_->DoesURLMatchAnyCluster(
-      ComputeURLKeywordForLookup(GURL("https://second-1-day-old-visit.com/"))));
-
-  // Visits now span 2 days (1-day-old and 60-day-old) since we deleted the only
-  // 2-day-old visit.
-  FlushKeywordRequests(clusters, 2);
-
-  // The keyword cache should be repopulated.
-  EXPECT_TRUE(history_clusters_service_->DoesURLMatchAnyCluster(
-      ComputeURLKeywordForLookup(GURL("https://second-1-day-old-visit.com/"))));
-}
-
-TEST_P(HistoryClustersServiceTest, DoesURLMatchAnyClusterNoNoisyURLs) {
-  Config config;
-  config.omnibox_action_on_urls = true;
-  config.omnibox_action_on_noisy_urls = false;
-  SetConfigForTesting(config);
-
-  AddHardcodedTestDataToHistoryService();
-
-  // Verify that initially, the test URL doesn't match anything, but this
-  // query should have kicked off a cache population request. This is the URL
-  // for visit 5.
-  EXPECT_FALSE(history_clusters_service_->DoesURLMatchAnyCluster(
-      ComputeURLKeywordForLookup(GURL("https://second-1-day-old-visit.com/"))));
-
-  std::vector<history::Cluster> clusters;
-  // This cluster should contribute to keywords.
-  clusters.push_back(history::Cluster(
-      0,
-      {
-          GetHardcodedClusterVisit(5),
-          GetHardcodedClusterVisit(5),
-          GetHardcodedClusterVisit(
-              /*visit_id=*/2, /*score=*/0.0, /*engagement_score=*/20.0),
-      },
-      {{u"apples", history::ClusterKeywordData()},
-       {u"oranges", history::ClusterKeywordData()},
-       {u"z", history::ClusterKeywordData()},
-       {u"apples bananas", history::ClusterKeywordData()}},
-      /*should_show_on_prominent_ui_surfaces=*/true));
-  // This cluster should NOT contribute to keywords because
-  // `should_show_on_prominent_ui_surfaces` is false.
-  clusters.push_back(
-      history::Cluster(0,
-                       {
-                           GetHardcodedClusterVisit(5),
-                           GetHardcodedClusterVisit(2),
-                       },
-                       {{u"sensitive", history::ClusterKeywordData()}},
-                       /*should_show_on_prominent_ui_surfaces=*/false));
-  // This cluster should NOT contribute to keywords because it only has 1
-  // visible visit.
-  clusters.push_back(
-      history::Cluster(0,
-                       {
-                           GetHardcodedClusterVisit(2),
-                           GetHardcodedClusterVisit(2, /*score=*/0),
-                       },
-                       {{u"singlevisit", history::ClusterKeywordData()}},
-                       /*should_show_on_prominent_ui_surfaces=*/true));
-
-  // Hardcoded test visits span 3 days (1-day-old, 2-days-old, and 60-day-old).
-  FlushKeywordRequests(clusters, 3);
-
-  // Now the exact query should match the populated cache.
-  EXPECT_TRUE(history_clusters_service_->DoesURLMatchAnyCluster(
-      ComputeURLKeywordForLookup(GURL("https://second-1-day-old-visit.com/"))));
-
-  // Github should never be shown (highly-engaged for cluster 1, sensitive for
-  // cluster 2, single visit cluster for cluster 3).
-  EXPECT_FALSE(history_clusters_service_->DoesURLMatchAnyCluster(
-      ComputeURLKeywordForLookup(GURL("https://github.com/"))));
-
-  // Deleting a history entry should clear the keyword cache.
-  history_service_->DeleteURLs({GURL{"https://google.com/"}});
-  history::BlockUntilHistoryProcessesPendingRequests(history_service_.get());
-  EXPECT_FALSE(history_clusters_service_->DoesURLMatchAnyCluster(
-      ComputeURLKeywordForLookup(GURL("https://second-1-day-old-visit.com/"))));
-
-  // Visits now span 2 days (1-day-old and 60-day-old) since we deleted the only
-  // 2-day-old visit.
-  FlushKeywordRequests(clusters, 2);
-
-  // The keyword cache should be repopulated.
-  EXPECT_TRUE(history_clusters_service_->DoesURLMatchAnyCluster(
-      ComputeURLKeywordForLookup(GURL("https://second-1-day-old-visit.com/"))));
-}
-
-class HistoryClustersServicePrefPersistenceTest
-    : public HistoryClustersServiceTestBase {
- public:
-  HistoryClustersServicePrefPersistenceTest() {
-    scoped_feature_list_.InitWithFeatures(
-        /*enabled_features=*/{internal::kJourneys,
-                              internal::kJourneysPersistCachesToPrefs},
-        /*disabled_features=*/{});
-    Config config;
-    config.persist_clusters_in_history_db = true;
-    SetConfigForTesting(config);
-  }
-};
-
-TEST_F(HistoryClustersServicePrefPersistenceTest, LoadCachesFromPrefs) {
-  AddHardcodedTestDataToHistoryService();
-  EXPECT_FALSE(history_clusters_service_->DoesQueryMatchAnyCluster("apples"));
-
-  std::vector<history::Cluster> clusters;
-  clusters.push_back(history::Cluster(
-      0,
-      {
-          GetHardcodedClusterVisit(5),
-          GetHardcodedClusterVisit(2),
-      },
-      {{u"apples",
-        history::ClusterKeywordData(history::ClusterKeywordData::kEntity, 5.0f,
-                                    {"fuji", "honeycrisp"})},
-       {u"oranges", history::ClusterKeywordData(
-                        history::ClusterKeywordData::kSearchTerms, 100.0f, {})},
-       {u"z", history::ClusterKeywordData()},
-       {u"apples bananas", history::ClusterKeywordData()}},
-      /*should_show_on_prominent_ui_surfaces=*/true));
-
-  FlushKeywordRequests(clusters, 3);
-
-  const auto keyword_data =
-      history_clusters_service_->DoesQueryMatchAnyCluster("apples");
-  EXPECT_TRUE(keyword_data);
-
-  // Empty the cache artificially to simulate a process restart.
-  history_clusters_service_test_api_->SetAllKeywordsCache({});
-  EXPECT_FALSE(history_clusters_service_->DoesQueryMatchAnyCluster("apples"));
-
-  LoadCachesFromPrefs();
-
-  const auto apples_keyword_data =
-      history_clusters_service_->DoesQueryMatchAnyCluster("apples");
-  EXPECT_TRUE(apples_keyword_data);
-  EXPECT_EQ(apples_keyword_data,
-            history::ClusterKeywordData(history::ClusterKeywordData::kEntity,
-                                        5.0f, {"fuji", "honeycrisp"}));
-  const auto oranges_keyword_data =
-      history_clusters_service_->DoesQueryMatchAnyCluster("oranges");
-  EXPECT_TRUE(oranges_keyword_data);
-  EXPECT_EQ(oranges_keyword_data,
-            history::ClusterKeywordData(history::ClusterKeywordData(
-                history::ClusterKeywordData::kSearchTerms, 100.0f, {})));
-  EXPECT_TRUE(
-      history_clusters_service_->DoesQueryMatchAnyCluster("apples bananas"));
-}
-
-TEST_F(HistoryClustersServicePrefPersistenceTest,
-       LoadSecondaryCachesFromPrefs) {
-  AddHardcodedTestDataToHistoryService();
-  auto minutes_ago = [](int minutes) {
-    return base::Time::Now() - base::Minutes(minutes);
-  };
-
-  // Set up the cache timestamps.
-  history_clusters_service_test_api_->SetAllKeywordsCacheTimestamp(
-      minutes_ago(60));
-  history_clusters_service_test_api_->SetShortKeywordCacheTimestamp(
-      minutes_ago(15));
-
-  // Set up the visit timestamps.
-  // Visits newer than both cache timestamps should be reclustered.
-  auto visit = GetHardcodedTestVisits()[0];
-  visit.visit_row.visit_time = minutes_ago(5);
-  AddCompleteVisit(visit);
-  visit = GetHardcodedTestVisits()[1];
-  visit.visit_row.visit_time = minutes_ago(10);
-  AddCompleteVisit(visit);
-
-  // Kick off cluster request and verify the correct visits are sent.
-  EXPECT_FALSE(history_clusters_service_->DoesQueryMatchAnyCluster("peach"));
-  test_clustering_backend_->WaitForGetClustersCall();
-
-  // Send the cluster response and verify the keyword was cached.
-  std::vector<history::Cluster> clusters2;
-  clusters2.push_back(history::Cluster(
-      0,
-      {
-          GetHardcodedClusterVisit(1),
-          GetHardcodedClusterVisit(2),
-      },
-      {{u"peach",
-        history::ClusterKeywordData(history::ClusterKeywordData::kEntity, 13.0f,
-                                    {"georgia"})},
-       {u"", history::ClusterKeywordData()}},
-      /*should_show_on_prominent_ui_surfaces=*/true));
-  test_clustering_backend_->FulfillCallback(clusters2);
-  history::BlockUntilHistoryProcessesPendingRequests(history_service_.get());
-  EXPECT_TRUE(history_clusters_service_->DoesQueryMatchAnyCluster("peach"));
-
-  // Verify the keyword is in the short cache specifically.
-  history_clusters_service_test_api_->SetAllKeywordsCache({});
-  EXPECT_TRUE(history_clusters_service_->DoesQueryMatchAnyCluster("peach"));
-
-  // Empty the cache artificially to simulate a process restart.
-  history_clusters_service_test_api_->SetShortKeywordCache({});
-  EXPECT_FALSE(history_clusters_service_->DoesQueryMatchAnyCluster("peach"));
-
-  LoadCachesFromPrefs();
-  const auto peach_keyword_data =
-      history_clusters_service_->DoesQueryMatchAnyCluster("peach");
-  EXPECT_EQ(peach_keyword_data,
-            history::ClusterKeywordData(history::ClusterKeywordData(
-                history::ClusterKeywordData::kEntity, 13.0f, {"georgia"})));
-}
-
 class HistoryClustersServiceJourneysDisabledTest
     : public HistoryClustersServiceTestBase {
  public:
diff --git a/components/history_clusters/core/history_clusters_util.cc b/components/history_clusters/core/history_clusters_util.cc
index 539b9fbc..561835e7 100644
--- a/components/history_clusters/core/history_clusters_util.cc
+++ b/components/history_clusters/core/history_clusters_util.cc
@@ -14,7 +14,6 @@
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "components/history/core/browser/history_types.h"
-#include "components/history/core/browser/visitsegment_database.h"
 #include "components/history_clusters/core/config.h"
 #include "components/history_clusters/core/features.h"
 #include "components/history_clusters/core/history_clusters_types.h"
@@ -167,11 +166,6 @@
   return url_for_deduping;
 }
 
-std::string ComputeURLKeywordForLookup(const GURL& url) {
-  return history::VisitSegmentDatabase::ComputeSegmentName(
-      ComputeURLForDeduping(url));
-}
-
 std::u16string ComputeURLForDisplay(const GURL& url, bool trim_after_host) {
   // Use URL formatting options similar to the omnibox popup. The url_formatter
   // component does IDN hostname conversion as well.
diff --git a/components/history_clusters/core/history_clusters_util.h b/components/history_clusters/core/history_clusters_util.h
index d14fbe3f..c0d8071 100644
--- a/components/history_clusters/core/history_clusters_util.h
+++ b/components/history_clusters/core/history_clusters_util.h
@@ -23,15 +23,6 @@
 // should be separately canonicalized by TemplateURLService and not sent here.
 GURL ComputeURLForDeduping(const GURL& url);
 
-// Generates a keyword from the URL used for looking up relevant clusters to a
-// given URL. Does everything that ComputeURLForDeduping() does and additionally
-// applies history::VisitSegmentDatabase::ComputeSegmentName() to the resulting
-// URL to maximize coverage.
-//
-// Note, this is NOT meant to be applied to Search Result Page URLs. Those
-// should be separately canonicalized by TemplateURLService and not sent here.
-std::string ComputeURLKeywordForLookup(const GURL& url);
-
 // Returns a string suitable for display in the Journeys UI from the normalized
 // visit URL. Displays the host and the path. Set `trim_after_host` to true to
 // also remove the path, query, and ref.
diff --git a/components/history_clusters/core/history_clusters_util_unittest.cc b/components/history_clusters/core/history_clusters_util_unittest.cc
index 1ebdb3c..3ac30fc5 100644
--- a/components/history_clusters/core/history_clusters_util_unittest.cc
+++ b/components/history_clusters/core/history_clusters_util_unittest.cc
@@ -55,47 +55,6 @@
   }
 }
 
-TEST(HistoryClustersUtilTest, ComputeURLKeywordForLookup) {
-  {
-    Config config;
-    config.use_host_for_visit_deduping = false;
-    SetConfigForTesting(config);
-
-    EXPECT_EQ(ComputeURLKeywordForLookup(GURL("http://www.google.com/")),
-              "http://google.com/")
-        << "Strip off WWW.";
-    EXPECT_EQ(ComputeURLKeywordForLookup(GURL("https://google.com/")),
-              "http://google.com/")
-        << "Normalizes scheme to http.";
-    EXPECT_EQ(ComputeURLKeywordForLookup(
-                  GURL("http://google.com/path?foo=bar#reftag")),
-              "http://google.com/path")
-        << "Strips ref and query, leaves path.";
-    EXPECT_EQ(ComputeURLKeywordForLookup(
-                  GURL("https://www.google.com/path?foo=bar#reftag")),
-              "http://google.com/path")
-        << "Does all of the above at once.";
-    EXPECT_EQ(ComputeURLKeywordForLookup(GURL("http://google.com/path")),
-              "http://google.com/path")
-        << "Sanity check when no replacements needed.";
-  }
-
-  {
-    Config config;
-    config.use_host_for_visit_deduping = true;
-    SetConfigForTesting(config);
-
-    EXPECT_EQ(ComputeURLKeywordForLookup(GURL("https://google.com/path/")),
-              "http://google.com/")
-        << "Strips path.";
-
-    EXPECT_EQ(ComputeURLKeywordForLookup(
-                  GURL("https://www.google.com/path?foo=bar#reftag")),
-              "http://google.com/")
-        << "Does everything at once.";
-  }
-}
-
 TEST(HistoryClustersUtilTest, FilterClustersMatchingQuery) {
   std::vector<history::Cluster> all_clusters;
   all_clusters.push_back(
diff --git a/components/omnibox/browser/BUILD.gn b/components/omnibox/browser/BUILD.gn
index 47c00e4..10fec13 100644
--- a/components/omnibox/browser/BUILD.gn
+++ b/components/omnibox/browser/BUILD.gn
@@ -51,6 +51,7 @@
     "extension_app.icon",
     "find_in_page.icon",
     "http.icon",
+    "http_chrome_refresh.icon",
     "https_valid_in_chip.icon",
     "incognito.icon",
     "install_desktop.icon",
@@ -62,9 +63,12 @@
     "price_tracking_disabled.icon",
     "price_tracking_enabled_filled.icon",
     "product.icon",
+    "product_chrome_refresh.icon",
     "share.icon",
     "star.icon",
     "star_active.icon",
+    "star_active_chrome_refresh.icon",
+    "star_chrome_refresh.icon",
     "switch.icon",
     "tab.icon",
     "trending_up.icon",
diff --git a/components/omnibox/browser/actions/history_clusters_action.cc b/components/omnibox/browser/actions/history_clusters_action.cc
index 5523153..7fc36a57 100644
--- a/components/omnibox/browser/actions/history_clusters_action.cc
+++ b/components/omnibox/browser/actions/history_clusters_action.cc
@@ -258,17 +258,6 @@
             query, std::move(matched_keyword_data.value()),
             /*takes_over_match=*/false));
       }
-    } else if (GetConfig().omnibox_action_on_urls) {
-      // We do the URL stripping here, because we need it to both execute the
-      // query, as well as to feed it into the action chip so the chip navigates
-      // to the right place (with the query pre-populated).
-      std::string url_keyword =
-          history_clusters::ComputeURLKeywordForLookup(match.destination_url);
-      if (service->DoesURLMatchAnyCluster(url_keyword)) {
-        match.actions.push_back(base::MakeRefCounted<HistoryClustersAction>(
-            url_keyword, history::ClusterKeywordData(),
-            /*takes_over_match=*/false));
-      }
     }
 
     // Only ever attach one action (to the highest match), to not overwhelm
diff --git a/components/omnibox/browser/actions/history_clusters_action_unittest.cc b/components/omnibox/browser/actions/history_clusters_action_unittest.cc
index aea6f37c..164c5b5b 100644
--- a/components/omnibox/browser/actions/history_clusters_action_unittest.cc
+++ b/components/omnibox/browser/actions/history_clusters_action_unittest.cc
@@ -70,14 +70,7 @@
     search_actions_config_.is_journeys_enabled_no_locale_check = true;
     search_actions_config_.omnibox_action = true;
     search_actions_config_.omnibox_action_on_navigation_intents = false;
-    search_actions_config_.omnibox_action_on_urls = false;
     SetConfigForTesting(search_actions_config_);
-
-    url_actions_config_.is_journeys_enabled_no_locale_check = true;
-    url_actions_config_.omnibox_action = true;
-    url_actions_config_.omnibox_action_on_navigation_intents = false;
-    url_actions_config_.omnibox_action_on_urls = true;
-    SetConfigForTesting(url_actions_config_);
   }
 
   // `history_clusters_service_` needs to be initialized repeatedly since it
@@ -98,8 +91,6 @@
             history_clusters_service_.get(), history_service_.get());
     history_clusters_service_test_api_->SetAllKeywordsCache(
         {{u"keyword", history::ClusterKeywordData()}});
-    history_clusters_service_test_api_->SetAllUrlKeywordsCache(
-        {"http://keyword/"});
   }
 
   void TestAttachHistoryClustersActions(std::vector<MatchData> matches_data,
@@ -141,14 +132,12 @@
 
   // Commonly used configs & prefs used or derived from in the tests.
   Config search_actions_config_;
-  Config url_actions_config_;
   TestingPrefServiceSimple prefs_enabled_;
 };
 
 TEST_F(HistoryClustersActionTest, AttachHistoryClustersActions) {
   {
     SCOPED_TRACE("Shouldn't add action if history cluster service is nullptr.");
-    SetUpWithConfig(search_actions_config_);
     TestAttachHistoryClustersActions({{}}, nullptr, &prefs_enabled_);
   }
 
@@ -217,19 +206,7 @@
   }
 
   {
-    SCOPED_TRACE(
-        "Should add action if a navigation suggestion matches and "
-        "`omnibox_action_on_urls` is enabled.");
-    SetUpWithConfig(url_actions_config_);
-    TestAttachHistoryClustersActions(
-        {{.type = AutocompleteMatchType::Type::HISTORY_TITLE,
-          .expect_history_clusters_action = true}});
-  }
-
-  {
-    SCOPED_TRACE(
-        "Should not add action if a navigation suggestion matches and "
-        "`omnibox_action_on_urls` is disabled.");
+    SCOPED_TRACE("Should not add action if a navigation suggestion matches.");
     SetUpWithConfig(search_actions_config_);
     TestAttachHistoryClustersActions(
         {{.type = AutocompleteMatchType::Type::HISTORY_TITLE}});
@@ -238,8 +215,8 @@
   {
     SCOPED_TRACE(
         "Should add action if both a search and navigation suggestions "
-        "match and `omnibox_action_on_urls` is disabled. The search suggestion "
-        "should have an action, even if it is ranked & scored lower.");
+        "match. The search suggestion should have an action, even if it is "
+        "ranked & scored lower.");
     SetUpWithConfig(search_actions_config_);
     TestAttachHistoryClustersActions(
         {{.type = AutocompleteMatchType::Type::HISTORY_TITLE},
@@ -273,16 +250,6 @@
 
   {
     SCOPED_TRACE(
-        "Should add action to a top-scoring navigation suggestion, if it is "
-        "not high-scoring.");
-    SetUpWithConfig(url_actions_config_);
-    TestAttachHistoryClustersActions(
-        {{.type = AutocompleteMatchType::Type::HISTORY_TITLE,
-          .expect_history_clusters_action = true}});
-  }
-
-  {
-    SCOPED_TRACE(
         "Should not add action if a search suggestion matches and the top "
         "scoring suggestion is a high score navigation suggestion, even if it "
         "doesn't match.");
@@ -307,20 +274,6 @@
           .type = AutocompleteMatchType::Type::HISTORY_TITLE},
          {.relevance = 1350, .expect_history_clusters_action = true}});
   }
-
-  {
-    SCOPED_TRACE(
-        "Should add action to a top scoring, high score navigation "
-        "suggestion if `omnibox_action_on_navigation_intents` is enabled.");
-    Config config = url_actions_config_;
-    config.omnibox_action_on_navigation_intents = true;
-    SetUpWithConfig(config);
-    TestAttachHistoryClustersActions({
-        {.relevance = 1350,
-         .type = AutocompleteMatchType::Type::HISTORY_TITLE,
-         .expect_history_clusters_action = true},
-    });
-  }
 }
 
 }  // namespace history_clusters
diff --git a/components/omnibox/browser/autocomplete_grouper_sections.cc b/components/omnibox/browser/autocomplete_grouper_sections.cc
index b633b74..0885780b 100644
--- a/components/omnibox/browser/autocomplete_grouper_sections.cc
+++ b/components/omnibox/browser/autocomplete_grouper_sections.cc
@@ -109,6 +109,7 @@
     : ZpsSection(
           15 + max_related_queries + max_trending_queries,
           {
+              {1, omnibox::GROUP_MOBILE_CLIPBOARD},
               {15, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST},
               {max_related_queries, omnibox::GROUP_PREVIOUS_SEARCH_RELATED},
               {max_trending_queries, omnibox::GROUP_TRENDS},
diff --git a/components/omnibox/browser/autocomplete_grouper_sections_unittest.cc b/components/omnibox/browser/autocomplete_grouper_sections_unittest.cc
index 4289bf6b..53a5d8c 100644
--- a/components/omnibox/browser/autocomplete_grouper_sections_unittest.cc
+++ b/components/omnibox/browser/autocomplete_grouper_sections_unittest.cc
@@ -926,7 +926,7 @@
 }
 
 // Tests the groups, limits, and rules for the Android NTP ZPS+Inspire Me.
-TEST(AutocompleteGrouperSectionsTest, AndroidNTPZpsSection) {
+TEST(AutocompleteGrouperSectionsTest, AndroidNTPZpsSection_withInspireMe) {
   auto test = [](ACMatches matches, std::vector<int> expected_relevances) {
     constexpr int MAX_PREVIOUS_SEARCH_RELATED = 3;
     constexpr int MAX_TRENDING_QUERIES = 5;
@@ -948,10 +948,6 @@
     // Verify that the Clipboard suggestion is retained on top.
     test(
         {
-            // Auxiliary matches not valid for NTP ZPS.
-            CreateMatch(200, omnibox::GROUP_MOBILE_CLIPBOARD),
-            CreateMatch(199, omnibox::GROUP_MOBILE_SEARCH_READY_OMNIBOX),
-            CreateMatch(198, omnibox::GROUP_MOBILE_MOST_VISITED),
             // PSUGGEST to show on the NTP ZPS.
             CreateMatch(100, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
             CreateMatch(99, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
@@ -974,6 +970,35 @@
         {100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87, 86});
   }
   {
+    SCOPED_TRACE("Clipboard suggestion is always shown when available.");
+    test(
+        {
+            CreateMatch(3, omnibox::GROUP_MOBILE_CLIPBOARD),
+            // Auxiliary matches not valid for NTP ZPS.
+            CreateMatch(2, omnibox::GROUP_MOBILE_SEARCH_READY_OMNIBOX),
+            CreateMatch(1, omnibox::GROUP_MOBILE_MOST_VISITED),
+            // PSUGGEST to show on the NTP ZPS.
+            CreateMatch(100, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
+            CreateMatch(99, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
+            CreateMatch(98, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
+            CreateMatch(97, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
+            CreateMatch(96, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
+            CreateMatch(95, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
+            CreateMatch(94, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
+            CreateMatch(93, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
+            CreateMatch(92, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
+            CreateMatch(91, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
+            CreateMatch(90, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
+            CreateMatch(89, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
+            CreateMatch(88, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
+            CreateMatch(87, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
+            CreateMatch(86, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
+            CreateMatch(85, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
+            CreateMatch(84, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
+        },
+        {3, 100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87, 86});
+  }
+  {
     SCOPED_TRACE("No Trending Queries Backfill");
     // Verify that Trending queries don't backfill unoccupied Related queries
     // slots.
@@ -1040,3 +1065,92 @@
         {19, 20});
   }
 }
+
+TEST(AutocompleteGrouperSectionsTest, AndroidNTPZpsSection_noInspireMe) {
+  auto test = [](ACMatches matches, std::vector<int> expected_relevances) {
+    PSections sections;
+    omnibox::GroupConfigMap group_configs;
+    sections.push_back(
+        std::make_unique<AndroidNTPZpsSection>(0, 0, group_configs));
+    auto out_matches = Section::GroupMatches(std::move(sections), matches);
+    VerifyMatches(out_matches, expected_relevances);
+  };
+
+  {
+    SCOPED_TRACE("Given no matches, should return no matches.");
+    test({}, {});
+  }
+  {
+    SCOPED_TRACE("Given no InspireMe matches, should return no matches.");
+    // Verify that the Clipboard suggestion is retained on top.
+    test(
+        {
+            // PSUGGEST to show on the NTP ZPS.
+            CreateMatch(100, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
+            CreateMatch(99, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
+            CreateMatch(98, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
+            CreateMatch(97, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
+            CreateMatch(96, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
+            CreateMatch(95, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
+            CreateMatch(94, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
+            CreateMatch(93, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
+            CreateMatch(92, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
+            CreateMatch(91, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
+            CreateMatch(90, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
+            CreateMatch(89, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
+            CreateMatch(88, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
+            CreateMatch(87, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
+            CreateMatch(86, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
+            CreateMatch(85, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
+            CreateMatch(84, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
+        },
+        {100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87, 86});
+  }
+  {
+    SCOPED_TRACE("Clipboard suggestion is always shown when available.");
+    test(
+        {
+            CreateMatch(3, omnibox::GROUP_MOBILE_CLIPBOARD),
+            // Auxiliary matches not valid for NTP ZPS.
+            CreateMatch(2, omnibox::GROUP_MOBILE_SEARCH_READY_OMNIBOX),
+            CreateMatch(1, omnibox::GROUP_MOBILE_MOST_VISITED),
+            // PSUGGEST to show on the NTP ZPS.
+            CreateMatch(100, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
+            CreateMatch(99, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
+            CreateMatch(98, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
+            CreateMatch(97, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
+            CreateMatch(96, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
+            CreateMatch(95, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
+            CreateMatch(94, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
+            CreateMatch(93, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
+            CreateMatch(92, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
+            CreateMatch(91, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
+            CreateMatch(90, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
+            CreateMatch(89, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
+            CreateMatch(88, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
+            CreateMatch(87, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
+            CreateMatch(86, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
+            CreateMatch(85, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
+            CreateMatch(84, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
+        },
+        {3, 100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87});
+  }
+  {
+    SCOPED_TRACE("No Trending or Related searches");
+    test(
+        {
+            CreateMatch(20, omnibox::GROUP_PREVIOUS_SEARCH_RELATED),
+            CreateMatch(19, omnibox::GROUP_PREVIOUS_SEARCH_RELATED),
+            CreateMatch(18, omnibox::GROUP_PREVIOUS_SEARCH_RELATED),
+            CreateMatch(17, omnibox::GROUP_PREVIOUS_SEARCH_RELATED),
+            CreateMatch(16, omnibox::GROUP_PREVIOUS_SEARCH_RELATED),
+            CreateMatch(15, omnibox::GROUP_TRENDS),
+            CreateMatch(14, omnibox::GROUP_TRENDS),
+            CreateMatch(13, omnibox::GROUP_TRENDS),
+            CreateMatch(12, omnibox::GROUP_TRENDS),
+            CreateMatch(11, omnibox::GROUP_TRENDS),
+            CreateMatch(10, omnibox::GROUP_TRENDS),
+        },
+        {});
+  }
+}
diff --git a/components/omnibox/browser/location_bar_model_util.cc b/components/omnibox/browser/location_bar_model_util.cc
index c3f5ab4a..165ac0c 100644
--- a/components/omnibox/browser/location_bar_model_util.cc
+++ b/components/omnibox/browser/location_bar_model_util.cc
@@ -9,6 +9,7 @@
 #include "build/build_config.h"
 #include "components/omnibox/browser/buildflags.h"
 #include "components/omnibox/common/omnibox_features.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/gfx/vector_icon_types.h"
 
 #if (!BUILDFLAG(IS_ANDROID) || BUILDFLAG(ENABLE_VR)) && !BUILDFLAG(IS_IOS)
@@ -24,23 +25,30 @@
 #if (!BUILDFLAG(IS_ANDROID) || BUILDFLAG(ENABLE_VR)) && !BUILDFLAG(IS_IOS)
   switch (security_level) {
     case security_state::NONE:
-      return omnibox::kHttpIcon;
+      return features::IsChromeRefresh2023() ? omnibox::kHttpChromeRefreshIcon
+                                             : omnibox::kHttpIcon;
     case security_state::SECURE: {
       return use_updated_connection_security_indicators
                  ? vector_icons::kHttpsValidArrowIcon
                  : vector_icons::kHttpsValidIcon;
     }
     case security_state::SECURE_WITH_POLICY_INSTALLED_CERT:
-      return vector_icons::kBusinessIcon;
+      return features::IsChromeRefresh2023()
+                 ? vector_icons::kBusinessChromeRefreshIcon
+                 : vector_icons::kBusinessIcon;
     case security_state::WARNING:
     case security_state::DANGEROUS:
-      return vector_icons::kNotSecureWarningIcon;
+      return features::IsChromeRefresh2023()
+                 ? vector_icons::kNotSecureWarningChromeRefreshIcon
+                 : vector_icons::kNotSecureWarningIcon;
     case security_state::SECURITY_LEVEL_COUNT:
       NOTREACHED();
-      return omnibox::kHttpIcon;
+      return features::IsChromeRefresh2023() ? omnibox::kHttpChromeRefreshIcon
+                                             : omnibox::kHttpIcon;
   }
   NOTREACHED();
-  return omnibox::kHttpIcon;
+  return features::IsChromeRefresh2023() ? omnibox::kHttpChromeRefreshIcon
+                                         : omnibox::kHttpIcon;
 #else
   NOTREACHED();
   static const gfx::VectorIcon dummy = {};
diff --git a/components/omnibox/browser/vector_icons/http_chrome_refresh.icon b/components/omnibox/browser/vector_icons/http_chrome_refresh.icon
new file mode 100644
index 0000000..5a01699
--- /dev/null
+++ b/components/omnibox/browser/vector_icons/http_chrome_refresh.icon
@@ -0,0 +1,127 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+CANVAS_DIMENSIONS, 32,
+MOVE_TO, 14.8f, 22.67f,
+H_LINE_TO, 17.27f,
+V_LINE_TO, 14.67f,
+H_LINE_TO, 14.8f,
+V_LINE_TO, 22.67f,
+CLOSE,
+MOVE_TO, 16, 12.07f,
+CUBIC_TO, 16.36f, 12.07f, 16.66f, 11.96f, 16.9f, 11.73f,
+CUBIC_TO, 17.14f, 11.49f, 17.27f, 11.19f, 17.27f, 10.83f,
+CUBIC_TO, 17.27f, 10.48f, 17.14f, 10.18f, 16.9f, 9.93f,
+CUBIC_TO, 16.66f, 9.69f, 16.36f, 9.57f, 16, 9.57f,
+CUBIC_TO, 15.64f, 9.57f, 15.34f, 9.69f, 15.1f, 9.93f,
+CUBIC_TO, 14.86f, 10.18f, 14.73f, 10.48f, 14.73f, 10.83f,
+CUBIC_TO, 14.73f, 11.19f, 14.86f, 11.49f, 15.1f, 11.73f,
+CUBIC_TO, 15.34f, 11.96f, 15.64f, 12.07f, 16, 12.07f,
+CLOSE,
+MOVE_TO, 16, 29.33f,
+CUBIC_TO, 14.16f, 29.33f, 12.42f, 28.99f, 10.8f, 28.3f,
+CUBIC_TO, 9.18f, 27.59f, 7.77f, 26.63f, 6.57f, 25.43f,
+CUBIC_TO, 5.37f, 24.23f, 4.41f, 22.82f, 3.7f, 21.2f,
+CUBIC_TO, 3.01f, 19.58f, 2.67f, 17.84f, 2.67f, 16,
+CUBIC_TO, 2.67f, 14.16f, 3.01f, 12.42f, 3.7f, 10.8f,
+CUBIC_TO, 4.41f, 9.18f, 5.37f, 7.77f, 6.57f, 6.57f,
+CUBIC_TO, 7.77f, 5.37f, 9.18f, 4.42f, 10.8f, 3.73f,
+CUBIC_TO, 12.42f, 3.02f, 14.16f, 2.67f, 16, 2.67f,
+CUBIC_TO, 17.84f, 2.67f, 19.58f, 3.02f, 21.2f, 3.73f,
+CUBIC_TO, 22.82f, 4.42f, 24.23f, 5.37f, 25.43f, 6.57f,
+CUBIC_TO, 26.63f, 7.77f, 27.58f, 9.18f, 28.27f, 10.8f,
+CUBIC_TO, 28.98f, 12.42f, 29.33f, 14.16f, 29.33f, 16,
+CUBIC_TO, 29.33f, 17.84f, 28.98f, 19.58f, 28.27f, 21.2f,
+CUBIC_TO, 27.58f, 22.82f, 26.63f, 24.23f, 25.43f, 25.43f,
+CUBIC_TO, 24.23f, 26.63f, 22.82f, 27.59f, 21.2f, 28.3f,
+CUBIC_TO, 19.58f, 28.99f, 17.84f, 29.33f, 16, 29.33f,
+CLOSE,
+MOVE_TO, 16, 26.9f,
+CUBIC_TO, 19.04f, 26.9f, 21.62f, 25.84f, 23.73f, 23.73f,
+CUBIC_TO, 25.84f, 21.6f, 26.9f, 19.02f, 26.9f, 16,
+CUBIC_TO, 26.9f, 12.96f, 25.84f, 10.38f, 23.73f, 8.27f,
+CUBIC_TO, 21.62f, 6.16f, 19.04f, 5.1f, 16, 5.1f,
+CUBIC_TO, 12.98f, 5.1f, 10.4f, 6.16f, 8.27f, 8.27f,
+CUBIC_TO, 6.16f, 10.38f, 5.1f, 12.96f, 5.1f, 16,
+CUBIC_TO, 5.1f, 19.02f, 6.16f, 21.6f, 8.27f, 23.73f,
+CUBIC_TO, 10.4f, 25.84f, 12.98f, 26.9f, 16, 26.9f,
+CLOSE,
+NEW_PATH
+
+CANVAS_DIMENSIONS, 20,
+MOVE_TO, 9.25f, 14,
+H_LINE_TO, 10.75f,
+V_LINE_TO, 9,
+H_LINE_TO, 9.25f,
+V_LINE_TO, 14,
+CLOSE,
+MOVE_TO, 10, 7.5f,
+CUBIC_TO, 10.21f, 7.5f, 10.38f, 7.43f, 10.52f, 7.29f,
+CUBIC_TO, 10.67f, 7.14f, 10.75f, 6.96f, 10.75f, 6.75f,
+CUBIC_TO, 10.75f, 6.54f, 10.67f, 6.37f, 10.52f, 6.23f,
+CUBIC_TO, 10.38f, 6.08f, 10.21f, 6, 10, 6,
+CUBIC_TO, 9.79f, 6, 9.61f, 6.08f, 9.46f, 6.23f,
+CUBIC_TO, 9.32f, 6.37f, 9.25f, 6.54f, 9.25f, 6.75f,
+CUBIC_TO, 9.25f, 6.96f, 9.32f, 7.14f, 9.46f, 7.29f,
+CUBIC_TO, 9.61f, 7.43f, 9.79f, 7.5f, 10, 7.5f,
+CLOSE,
+MOVE_TO, 10, 18,
+CUBIC_TO, 8.9f, 18, 7.87f, 17.79f, 6.9f, 17.38f,
+CUBIC_TO, 5.92f, 16.96f, 5.07f, 16.39f, 4.33f, 15.67f,
+CUBIC_TO, 3.61f, 14.93f, 3.04f, 14.08f, 2.63f, 13.1f,
+CUBIC_TO, 2.21f, 12.13f, 2, 11.1f, 2, 10,
+CUBIC_TO, 2, 8.89f, 2.21f, 7.85f, 2.63f, 6.9f,
+CUBIC_TO, 3.04f, 5.92f, 3.61f, 5.08f, 4.33f, 4.35f,
+CUBIC_TO, 5.07f, 3.62f, 5.92f, 3.04f, 6.9f, 2.63f,
+CUBIC_TO, 7.87f, 2.21f, 8.9f, 2, 10, 2,
+CUBIC_TO, 11.11f, 2, 12.15f, 2.21f, 13.1f, 2.63f,
+CUBIC_TO, 14.08f, 3.04f, 14.92f, 3.62f, 15.65f, 4.35f,
+CUBIC_TO, 16.38f, 5.08f, 16.96f, 5.92f, 17.38f, 6.9f,
+CUBIC_TO, 17.79f, 7.85f, 18, 8.89f, 18, 10,
+CUBIC_TO, 18, 11.1f, 17.79f, 12.13f, 17.38f, 13.1f,
+CUBIC_TO, 16.96f, 14.08f, 16.38f, 14.93f, 15.65f, 15.67f,
+CUBIC_TO, 14.92f, 16.39f, 14.08f, 16.96f, 13.1f, 17.38f,
+CUBIC_TO, 12.15f, 17.79f, 11.11f, 18, 10, 18,
+CLOSE,
+MOVE_TO, 10, 16.5f,
+CUBIC_TO, 11.81f, 16.5f, 13.34f, 15.87f, 14.6f, 14.6f,
+CUBIC_TO, 15.87f, 13.34f, 16.5f, 11.81f, 16.5f, 10,
+CUBIC_TO, 16.5f, 8.19f, 15.87f, 6.66f, 14.6f, 5.4f,
+CUBIC_TO, 13.34f, 4.13f, 11.81f, 3.5f, 10, 3.5f,
+CUBIC_TO, 8.19f, 3.5f, 6.66f, 4.13f, 5.4f, 5.4f,
+CUBIC_TO, 4.13f, 6.66f, 3.5f, 8.19f, 3.5f, 10,
+CUBIC_TO, 3.5f, 11.81f, 4.13f, 13.34f, 5.4f, 14.6f,
+CUBIC_TO, 6.66f, 15.87f, 8.19f, 16.5f, 10, 16.5f,
+CLOSE,
+NEW_PATH
+
+
+CANVAS_DIMENSIONS, 16,
+MOVE_TO, 8, 1,
+CUBIC_TO, 4.13f, 1, 1, 4.13f, 1, 8,
+CUBIC_TO, 1, 11.87f, 4.13f, 15, 8, 15,
+CUBIC_TO, 11.87f, 15, 15, 11.87f, 15, 8,
+CUBIC_TO, 15, 4.13f, 11.87f, 1, 8, 1,
+CLOSE,
+MOVE_TO, 8, 13.5f,
+CUBIC_TO, 4.97f, 13.5f, 2.5f, 11.03f, 2.5f, 8,
+CUBIC_TO, 2.5f, 4.97f, 4.97f, 2.5f, 8, 2.5f,
+CUBIC_TO, 11.03f, 2.5f, 13.5f, 4.97f, 13.5f, 8,
+CUBIC_TO, 13.5f, 11.03f, 11.03f, 13.5f, 8, 13.5f,
+CLOSE,
+NEW_PATH,
+MOVE_TO, 8.75f, 7.5f,
+H_LINE_TO, 7.25f,
+V_LINE_TO, 11.5f,
+H_LINE_TO, 8.75f,
+V_LINE_TO, 7.5f,
+CLOSE,
+NEW_PATH,
+MOVE_TO, 8, 6,
+CUBIC_TO, 8.41f, 6, 8.75f, 5.66f, 8.75f, 5.25f,
+CUBIC_TO, 8.75f, 4.84f, 8.41f, 4.5f, 8, 4.5f,
+CUBIC_TO, 7.59f, 4.5f, 7.25f, 4.84f, 7.25f, 5.25f,
+CUBIC_TO, 7.25f, 5.66f, 7.59f, 6, 8, 6,
+CLOSE,
+NEW_PATH
diff --git a/components/omnibox/browser/vector_icons/product_chrome_refresh.icon b/components/omnibox/browser/vector_icons/product_chrome_refresh.icon
new file mode 100644
index 0000000..6f6ada3
--- /dev/null
+++ b/components/omnibox/browser/vector_icons/product_chrome_refresh.icon
@@ -0,0 +1,101 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+CANVAS_DIMENSIONS, 32,
+MOVE_TO, 10.67f, 16.1f,
+CUBIC_TO, 10.67f, 17.57f, 11.19f, 18.82f, 12.23f, 19.87f,
+CUBIC_TO, 13.28f, 20.91f, 14.53f, 21.43f, 16, 21.43f,
+CUBIC_TO, 17.47f, 21.43f, 18.72f, 20.91f, 19.77f, 19.87f,
+CUBIC_TO, 20.81f, 18.82f, 21.33f, 17.57f, 21.33f, 16.1f,
+CUBIC_TO, 21.33f, 14.63f, 20.81f, 13.38f, 19.77f, 12.33f,
+CUBIC_TO, 18.72f, 11.29f, 17.47f, 10.77f, 16, 10.77f,
+CUBIC_TO, 14.53f, 10.77f, 13.28f, 11.29f, 12.23f, 12.33f,
+CUBIC_TO, 11.19f, 13.38f, 10.67f, 14.63f, 10.67f, 16.1f,
+CLOSE,
+MOVE_TO, 16, 24.1f,
+CUBIC_TO, 16.29f, 24.1f, 16.57f, 24.09f, 16.83f, 24.07f,
+CUBIC_TO, 17.1f, 24.04f, 17.37f, 24, 17.63f, 23.93f,
+LINE_TO, 14.5f, 29.33f,
+CUBIC_TO, 11.14f, 28.96f, 8.33f, 27.52f, 6.07f, 25.03f,
+CUBIC_TO, 3.8f, 22.52f, 2.67f, 19.54f, 2.67f, 16.1f,
+CUBIC_TO, 2.67f, 15.17f, 2.76f, 14.27f, 2.93f, 13.4f,
+CUBIC_TO, 3.11f, 12.51f, 3.38f, 11.67f, 3.73f, 10.87f,
+LINE_TO, 9.07f, 20.1f,
+CUBIC_TO, 9.76f, 21.3f, 10.71f, 22.27f, 11.93f, 23,
+CUBIC_TO, 13.16f, 23.73f, 14.51f, 24.1f, 16, 24.1f,
+CLOSE,
+MOVE_TO, 16, 8.1f,
+CUBIC_TO, 14.22f, 8.1f, 12.64f, 8.62f, 11.27f, 9.67f,
+CUBIC_TO, 9.89f, 10.69f, 8.93f, 12, 8.4f, 13.6f,
+LINE_TO, 5.27f, 8.2f,
+CUBIC_TO, 6.49f, 6.56f, 8.02f, 5.24f, 9.87f, 4.27f,
+CUBIC_TO, 11.73f, 3.27f, 13.78f, 2.77f, 16, 2.77f,
+CUBIC_TO, 18.2f, 2.77f, 20.22f, 3.26f, 22.07f, 4.23f,
+CUBIC_TO, 23.91f, 5.19f, 25.44f, 6.48f, 26.67f, 8.1f,
+H_LINE_TO, 16,
+CLOSE,
+MOVE_TO, 28.2f, 10.77f,
+CUBIC_TO, 28.58f, 11.59f, 28.86f, 12.44f, 29.03f, 13.33f,
+CUBIC_TO, 29.23f, 14.22f, 29.33f, 15.14f, 29.33f, 16.1f,
+CUBIC_TO, 29.33f, 19.54f, 28.2f, 22.51f, 25.93f, 25,
+CUBIC_TO, 23.69f, 27.49f, 20.91f, 28.93f, 17.6f, 29.33f,
+LINE_TO, 22.93f, 20.1f,
+CUBIC_TO, 23.27f, 19.52f, 23.52f, 18.9f, 23.7f, 18.23f,
+CUBIC_TO, 23.9f, 17.54f, 24, 16.83f, 24, 16.1f,
+CUBIC_TO, 24, 15.06f, 23.81f, 14.09f, 23.43f, 13.2f,
+CUBIC_TO, 23.08f, 12.29f, 22.58f, 11.48f, 21.93f, 10.77f,
+H_LINE_TO, 28.2f,
+CLOSE,
+NEW_PATH
+
+
+
+CANVAS_DIMENSIONS, 16,
+MOVE_TO, 5.25f, 8,
+CUBIC_TO, 5.25f, 8.76f, 5.52f, 9.4f, 6.06f, 9.94f,
+CUBIC_TO, 6.6f, 10.48f, 7.25f, 10.75f, 8, 10.75f,
+CUBIC_TO, 8.75f, 10.75f, 9.4f, 10.48f, 9.94f, 9.94f,
+CUBIC_TO, 10.48f, 9.4f, 10.75f, 8.75f, 10.75f, 8,
+CUBIC_TO, 10.75f, 7.25f, 10.48f, 6.6f, 9.94f, 6.06f,
+CUBIC_TO, 9.4f, 5.52f, 8.75f, 5.25f, 8, 5.25f,
+CUBIC_TO, 7.25f, 5.25f, 6.6f, 5.52f, 6.06f, 6.06f,
+CUBIC_TO, 5.52f, 6.6f, 5.25f, 7.25f, 5.25f, 8,
+CLOSE,
+MOVE_TO, 8, 12.25f,
+CUBIC_TO, 8.15f, 12.25f, 8.3f, 12.25f, 8.44f, 12.23f,
+CUBIC_TO, 8.58f, 12.22f, 8.72f, 12.19f, 8.86f, 12.16f,
+LINE_TO, 7.22f, 15,
+CUBIC_TO, 5.46f, 14.8f, 3.98f, 14.05f, 2.79f, 12.74f,
+CUBIC_TO, 1.6f, 11.42f, 1, 9.86f, 1, 8.05f,
+CUBIC_TO, 1, 7.56f, 1.05f, 7.09f, 1.14f, 6.63f,
+CUBIC_TO, 1.23f, 6.16f, 1.37f, 5.72f, 1.56f, 5.3f,
+LINE_TO, 4.36f, 10.15f,
+CUBIC_TO, 4.72f, 10.78f, 5.22f, 11.29f, 5.86f, 11.67f,
+CUBIC_TO, 6.5f, 12.05f, 7.21f, 12.25f, 8, 12.25f,
+CLOSE,
+MOVE_TO, 8, 3.85f,
+CUBIC_TO, 7.07f, 3.85f, 6.24f, 4.12f, 5.52f, 4.67f,
+CUBIC_TO, 4.8f, 5.21f, 4.29f, 5.9f, 4.01f, 6.74f,
+LINE_TO, 2.36f, 3.9f,
+CUBIC_TO, 3, 3.04f, 3.81f, 2.35f, 4.78f, 1.83f,
+CUBIC_TO, 5.76f, 1.3f, 6.83f, 1.04f, 8, 1.04f,
+CUBIC_TO, 9.17f, 1.04f, 10.22f, 1.3f, 11.19f, 1.81f,
+CUBIC_TO, 12.16f, 2.31f, 12.96f, 2.99f, 13.61f, 3.84f,
+H_LINE_TO, 8.01f,
+LINE_TO, 8, 3.85f,
+CLOSE,
+MOVE_TO, 14.41f, 5.25f,
+CUBIC_TO, 14.61f, 5.68f, 14.75f, 6.13f, 14.85f, 6.6f,
+CUBIC_TO, 14.95f, 7.07f, 15.01f, 7.55f, 15.01f, 8.05f,
+CUBIC_TO, 15.01f, 9.86f, 14.42f, 11.42f, 13.22f, 12.72f,
+CUBIC_TO, 12.04f, 14.03f, 10.58f, 14.78f, 8.85f, 14.99f,
+LINE_TO, 11.65f, 10.14f,
+CUBIC_TO, 11.83f, 9.84f, 11.96f, 9.51f, 12.05f, 9.16f,
+CUBIC_TO, 12.15f, 8.8f, 12.21f, 8.42f, 12.21f, 8.04f,
+CUBIC_TO, 12.21f, 7.49f, 12.11f, 6.98f, 11.91f, 6.52f,
+CUBIC_TO, 11.72f, 6.04f, 11.46f, 5.62f, 11.12f, 5.24f,
+H_LINE_TO, 14.41f,
+V_LINE_TO, 5.25f,
+CLOSE,
+NEW_PATH
diff --git a/components/omnibox/browser/vector_icons/star_active_chrome_refresh.icon b/components/omnibox/browser/vector_icons/star_active_chrome_refresh.icon
new file mode 100644
index 0000000..6944b0be
--- /dev/null
+++ b/components/omnibox/browser/vector_icons/star_active_chrome_refresh.icon
@@ -0,0 +1,33 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+CANVAS_DIMENSIONS, 20,
+MOVE_TO, 5.06f, 18,
+LINE_TO, 6.38f, 12.46f,
+LINE_TO, 2, 8.73f,
+LINE_TO, 7.75f, 8.23f,
+LINE_TO, 10, 3,
+LINE_TO, 12.25f, 8.25f,
+LINE_TO, 18, 8.73f,
+LINE_TO, 13.63f, 12.46f,
+LINE_TO, 14.94f, 18,
+LINE_TO, 10, 15.06f,
+LINE_TO, 5.06f, 18,
+CLOSE,
+NEW_PATH
+
+CANVAS_DIMENSIONS, 16,
+MOVE_TO, 8, 11.45f,
+LINE_TO, 12.33f, 14,
+LINE_TO, 11.18f, 9.19f,
+LINE_TO, 15, 5.95f,
+LINE_TO, 9.97f, 5.54f,
+LINE_TO, 8, 1,
+LINE_TO, 6.03f, 5.54f,
+LINE_TO, 1, 5.95f,
+LINE_TO, 4.82f, 9.19f,
+LINE_TO, 3.67f, 14,
+LINE_TO, 8, 11.45f,
+CLOSE,
+NEW_PATH
diff --git a/components/omnibox/browser/vector_icons/star_chrome_refresh.icon b/components/omnibox/browser/vector_icons/star_chrome_refresh.icon
new file mode 100644
index 0000000..de9e941
--- /dev/null
+++ b/components/omnibox/browser/vector_icons/star_chrome_refresh.icon
@@ -0,0 +1,60 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+CANVAS_DIMENSIONS, 20,
+MOVE_TO, 7.33f, 14.9f,
+LINE_TO, 10, 13.31f,
+LINE_TO, 12.69f, 14.9f,
+LINE_TO, 11.98f, 11.9f,
+LINE_TO, 14.29f, 9.92f,
+LINE_TO, 11.23f, 9.65f,
+LINE_TO, 10, 6.79f,
+LINE_TO, 8.77f, 9.65f,
+LINE_TO, 5.71f, 9.92f,
+LINE_TO, 8.04f, 11.9f,
+LINE_TO, 7.33f, 14.9f,
+CLOSE,
+MOVE_TO, 5.06f, 18,
+LINE_TO, 6.38f, 12.46f,
+LINE_TO, 2, 8.73f,
+LINE_TO, 7.75f, 8.23f,
+LINE_TO, 10, 3,
+LINE_TO, 12.25f, 8.25f,
+LINE_TO, 18, 8.73f,
+LINE_TO, 13.63f, 12.46f,
+LINE_TO, 14.94f, 18,
+LINE_TO, 10, 15.06f,
+LINE_TO, 5.06f, 18,
+CLOSE,
+NEW_PATH
+
+
+CANVAS_DIMENSIONS, 16,
+MOVE_TO, 15, 5.95f,
+LINE_TO, 9.97f, 5.53f,
+LINE_TO, 8, 1,
+LINE_TO, 6.03f, 5.54f,
+LINE_TO, 1, 5.96f,
+LINE_TO, 4.82f, 9.2f,
+LINE_TO, 3.67f, 14.01f,
+LINE_TO, 8, 11.46f,
+LINE_TO, 12.33f, 14.01f,
+LINE_TO, 11.18f, 9.2f,
+LINE_TO, 15, 5.96f,
+V_LINE_TO, 5.95f,
+CLOSE,
+MOVE_TO, 10.05f, 10.91f,
+LINE_TO, 8, 9.7f,
+LINE_TO, 5.95f, 10.91f,
+LINE_TO, 6.49f, 8.64f,
+LINE_TO, 4.73f, 7.15f,
+LINE_TO, 7.05f, 6.96f,
+LINE_TO, 8, 4.77f,
+LINE_TO, 8.95f, 6.96f,
+LINE_TO, 11.27f, 7.15f,
+LINE_TO, 9.51f, 8.64f,
+LINE_TO, 10.05f, 10.91f,
+V_LINE_TO, 10.91f,
+CLOSE,
+NEW_PATH
diff --git a/components/omnibox/common/omnibox_features.cc b/components/omnibox/common/omnibox_features.cc
index 089c33e..f70d7c7 100644
--- a/components/omnibox/common/omnibox_features.cc
+++ b/components/omnibox/common/omnibox_features.cc
@@ -481,6 +481,76 @@
              "OmniboxSteadyStateTextStyle",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+// If enabled, Omnibox "steady state" text color is updated to match GM3
+// guidelines.
+BASE_FEATURE(kOmniboxSteadyStateTextColor,
+             "OmniboxSteadyStateTextColor",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
+// Specifies the GM3 omnibox text color in Dark Mode.
+//
+// In order to control the value of this param via Finch, the
+// kOmniboxSteadyStateTextColor feature flag must be enabled.
+//
+// Enabling only the kChromeRefresh2023 flag, while leaving the
+// kOmniboxSteadyStateTextColor flag disabled, will result in the param being
+// locked to its default value and ignoring any overrides provided via Finch.
+//
+// If neither kChromeRefresh2023 nor kOmniboxSteadyStateTextColor are enabled,
+// then this feature param will have zero effect on Chrome UI.
+const base::FeatureParam<std::string> kOmniboxTextColorDarkMode(
+    &omnibox::kOmniboxSteadyStateTextColor,
+    "OmniboxTextColorDarkMode",
+    "0xE3E3E3");
+
+// Specifies the GM3 omnibox text color in Dark Mode (dimmed).
+//
+// In order to control the value of this param via Finch, the
+// kOmniboxSteadyStateTextColor feature flag must be enabled.
+//
+// Enabling only the kChromeRefresh2023 flag, while leaving the
+// kOmniboxSteadyStateTextColor flag disabled, will result in the param being
+// locked to its default value and ignoring any overrides provided via Finch.
+//
+// If neither kChromeRefresh2023 nor kOmniboxSteadyStateTextColor are enabled,
+// then this feature param will have zero effect on Chrome UI.
+const base::FeatureParam<std::string> kOmniboxTextColorDimmedDarkMode(
+    &omnibox::kOmniboxSteadyStateTextColor,
+    "OmniboxTextColorDimmedDarkMode",
+    "0xC7C7C7");
+
+// Specifies the GM3 omnibox text color in Light Mode.
+//
+// In order to control the value of this param via Finch, the
+// kOmniboxSteadyStateTextColor feature flag must be enabled.
+//
+// Enabling only the kChromeRefresh2023 flag, while leaving the
+// kOmniboxSteadyStateTextColor flag disabled, will result in the param being
+// locked to its default value and ignoring any overrides provided via Finch.
+//
+// If neither kChromeRefresh2023 nor kOmniboxSteadyStateTextColor are enabled,
+// then this feature param will have zero effect on Chrome UI.
+const base::FeatureParam<std::string> kOmniboxTextColorLightMode(
+    &omnibox::kOmniboxSteadyStateTextColor,
+    "OmniboxTextColorLightMode",
+    "0x1F1F1F");
+
+// Specifies the GM3 omnibox text color in Light Mode (dimmed).
+//
+// In order to control the value of this param via Finch, the
+// kOmniboxSteadyStateTextColor feature flag must be enabled.
+//
+// Enabling only the kChromeRefresh2023 flag, while leaving the
+// kOmniboxSteadyStateTextColor flag disabled, will result in the param being
+// locked to its default value and ignoring any overrides provided via Finch.
+//
+// If neither kChromeRefresh2023 nor kOmniboxSteadyStateTextColor are enabled,
+// then this feature param will have zero effect on Chrome UI.
+const base::FeatureParam<std::string> kOmniboxTextColorDimmedLightMode(
+    &omnibox::kOmniboxSteadyStateTextColor,
+    "OmniboxTextColorDimmedLightMode",
+    "0x474747");
+
 // If enabled, switching tabs will not restore the omnibox state.
 // TODO(manukh): Should also blur the omnibox on tab switch.
 BASE_FEATURE(kDiscardTemporaryInputOnTabSwitch,
diff --git a/components/omnibox/common/omnibox_features.h b/components/omnibox/common/omnibox_features.h
index 0797b0c..f2fb1cd7 100644
--- a/components/omnibox/common/omnibox_features.h
+++ b/components/omnibox/common/omnibox_features.h
@@ -117,6 +117,15 @@
 
 BASE_DECLARE_FEATURE(kOmniboxSteadyStateHeight);
 BASE_DECLARE_FEATURE(kOmniboxSteadyStateTextStyle);
+
+BASE_DECLARE_FEATURE(kOmniboxSteadyStateTextColor);
+// These feature params are located here, as opposed to omnibox_field_trial.h,
+// in order to permit inclusion into (non-Omnibox) color mixer code.
+extern const base::FeatureParam<std::string> kOmniboxTextColorDarkMode;
+extern const base::FeatureParam<std::string> kOmniboxTextColorDimmedDarkMode;
+extern const base::FeatureParam<std::string> kOmniboxTextColorLightMode;
+extern const base::FeatureParam<std::string> kOmniboxTextColorDimmedLightMode;
+
 BASE_DECLARE_FEATURE(kDiscardTemporaryInputOnTabSwitch);
 BASE_DECLARE_FEATURE(kRedoCurrentMatch);
 BASE_DECLARE_FEATURE(kRevertModelBeforeClosingPopup);
diff --git a/components/permissions/BUILD.gn b/components/permissions/BUILD.gn
index 46a85b0b..36017e7 100644
--- a/components/permissions/BUILD.gn
+++ b/components/permissions/BUILD.gn
@@ -25,8 +25,6 @@
     "bluetooth_delegate_impl.h",
     "chooser_controller.cc",
     "chooser_controller.h",
-    "chooser_title_util.cc",
-    "chooser_title_util.h",
     "contexts/accessibility_permission_context.cc",
     "contexts/accessibility_permission_context.h",
     "contexts/bluetooth_chooser_context.cc",
@@ -256,7 +254,6 @@
 source_set("unit_tests") {
   testonly = true
   sources = [
-    "chooser_title_util_unittest.cc",
     "contexts/camera_pan_tilt_zoom_permission_context_unittest.cc",
     "contexts/geolocation_permission_context_unittest.cc",
     "contexts/midi_permission_context_unittest.cc",
diff --git a/components/permissions/chooser_title_util.cc b/components/permissions/chooser_title_util.cc
deleted file mode 100644
index baa5170..0000000
--- a/components/permissions/chooser_title_util.cc
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2021 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/permissions/chooser_title_util.h"
-
-#include "components/url_formatter/elide_url.h"
-#include "content/public/browser/render_frame_host.h"
-#include "ui/base/l10n/l10n_util.h"
-#include "url/origin.h"
-
-namespace permissions {
-
-std::u16string CreateChooserTitle(content::RenderFrameHost* render_frame_host,
-                                  int title_string_id_origin) {
-  if (!render_frame_host)
-    return u"";
-  return l10n_util::GetStringFUTF16(
-      title_string_id_origin,
-      url_formatter::FormatOriginForSecurityDisplay(
-          render_frame_host->GetMainFrame()->GetLastCommittedOrigin(),
-          url_formatter::SchemeDisplay::OMIT_CRYPTOGRAPHIC));
-}
-
-}  // namespace permissions
diff --git a/components/permissions/chooser_title_util.h b/components/permissions/chooser_title_util.h
deleted file mode 100644
index 77d9a91b..0000000
--- a/components/permissions/chooser_title_util.h
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2021 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_PERMISSIONS_CHOOSER_TITLE_UTIL_H_
-#define COMPONENTS_PERMISSIONS_CHOOSER_TITLE_UTIL_H_
-
-#include <string>
-
-namespace content {
-class RenderFrameHost;
-}
-
-namespace permissions {
-
-// Creates a title for a chooser using the origin of the main frame
-// containing `render_frame_host`. Returns the empty string if
-// `render_frame_host` is null.
-std::u16string CreateChooserTitle(content::RenderFrameHost* render_frame_host,
-                                  int title_string_id_origin);
-
-}  // namespace permissions
-
-#endif  // COMPONENTS_PERMISSIONS_CHOOSER_TITLE_UTIL_H_
diff --git a/components/permissions/chooser_title_util_unittest.cc b/components/permissions/chooser_title_util_unittest.cc
deleted file mode 100644
index 443a4cd..0000000
--- a/components/permissions/chooser_title_util_unittest.cc
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2022 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/permissions/chooser_title_util.h"
-
-#include "components/strings/grit/components_strings.h"
-#include "content/public/test/navigation_simulator.h"
-#include "content/public/test/test_renderer_host.h"
-#include "url/gurl.h"
-#include "url/origin.h"
-
-namespace permissions {
-namespace {
-
-constexpr int kTitleResourceId = IDS_USB_DEVICE_CHOOSER_PROMPT_ORIGIN;
-
-using ChooserTitleTest = content::RenderViewHostTestHarness;
-
-TEST_F(ChooserTitleTest, NoFrame) {
-  EXPECT_EQ(u"", CreateChooserTitle(nullptr, kTitleResourceId));
-}
-
-TEST_F(ChooserTitleTest, FrameTree) {
-  NavigateAndCommit(GURL("https://main-frame.com"));
-  content::RenderFrameHost* subframe =
-      content::NavigationSimulator::NavigateAndCommitFromDocument(
-          GURL("https://sub-frame.com"),
-          content::RenderFrameHostTester::For(main_rfh())
-              ->AppendChild("subframe"));
-
-  EXPECT_EQ("main-frame.com", main_rfh()->GetLastCommittedOrigin().host());
-  EXPECT_EQ(u"main-frame.com wants to connect",
-            CreateChooserTitle(main_rfh(), kTitleResourceId));
-  EXPECT_EQ("sub-frame.com", subframe->GetLastCommittedOrigin().host());
-  EXPECT_EQ(u"main-frame.com wants to connect",
-            CreateChooserTitle(subframe, kTitleResourceId));
-}
-
-}  // namespace
-}  // namespace permissions
diff --git a/components/permissions/fake_bluetooth_chooser_controller.cc b/components/permissions/fake_bluetooth_chooser_controller.cc
index 321de55f..dfa71dc 100644
--- a/components/permissions/fake_bluetooth_chooser_controller.cc
+++ b/components/permissions/fake_bluetooth_chooser_controller.cc
@@ -15,7 +15,7 @@
 FakeBluetoothChooserController::FakeBluetoothChooserController(
     std::vector<FakeDevice> devices)
     : ChooserController(
-          l10n_util::GetStringFUTF16(IDS_BLUETOOTH_DEVICE_CHOOSER_PROMPT_ORIGIN,
+          l10n_util::GetStringFUTF16(IDS_BLUETOOTH_DEVICE_CHOOSER_PROMPT,
                                      u"example.com")),
       devices_(std::move(devices)) {}
 
diff --git a/components/permissions/fake_usb_chooser_controller.cc b/components/permissions/fake_usb_chooser_controller.cc
index 69de8b1..63e718c 100644
--- a/components/permissions/fake_usb_chooser_controller.cc
+++ b/components/permissions/fake_usb_chooser_controller.cc
@@ -12,7 +12,7 @@
 FakeUsbChooserController::FakeUsbChooserController(int device_count)
     : ChooserController(u""), device_count_(device_count) {
   set_title_for_testing(l10n_util::GetStringFUTF16(
-      IDS_USB_DEVICE_CHOOSER_PROMPT_ORIGIN, u"example.com"));
+      IDS_USB_DEVICE_CHOOSER_PROMPT, u"example.com"));
 }
 
 std::u16string FakeUsbChooserController::GetNoOptionsText() const {
diff --git a/components/permissions_strings.grdp b/components/permissions_strings.grdp
index dd35fc8d..05b347e 100644
--- a/components/permissions_strings.grdp
+++ b/components/permissions_strings.grdp
@@ -161,7 +161,7 @@
   </message>
   <!-- Device Chooser -->
   <if expr="not is_android">
-    <message name="IDS_BLUETOOTH_DEVICE_CHOOSER_PROMPT_ORIGIN" desc="The label that is used to introduce Bluetooth chooser details to the user in a popup when it is from a website.">
+    <message name="IDS_BLUETOOTH_DEVICE_CHOOSER_PROMPT" desc="The label that is used to introduce Bluetooth chooser details to the user in a popup.">
       <ph name="Origin">$1<ex>www.google.com</ex></ph> wants to pair
     </message>
     <message name="IDS_BLUETOOTH_DEVICE_CHOOSER_NO_DEVICES_FOUND_PROMPT" desc="The label shown to the user to inform them that no Bluetooth devices were found matching the requirements that the application provided.">
@@ -183,7 +183,7 @@
 
   <!-- Bluetooth Scanning Prompt -->
   <if expr="not is_android">
-    <message name="IDS_BLUETOOTH_SCANNING_PROMPT_ORIGIN" desc="The label that is used to introduce Bluetooth scanning prompt details to the user when it is from a website.">
+    <message name="IDS_BLUETOOTH_SCANNING_PROMPT" desc="The label that is used to introduce Bluetooth scanning prompt details to the user.">
       <ph name="Origin">$1<ex>www.google.com</ex></ph> wants to scan for nearby Bluetooth devices. The following devices have been found:
     </message>
     <message name="IDS_BLUETOOTH_SCANNING_DEVICE_UNKNOWN" desc="Text to identify Bluetooth devices of unknown or unsupported class.">
@@ -206,7 +206,7 @@
   <message name="IDS_DEVICE_CHOOSER_NO_DEVICES_FOUND_PROMPT" desc="The label shown to the user to inform them that no USB devices were found matching the requirements that the application provided.">
     No compatible devices found.
   </message>
-  <message name="IDS_USB_DEVICE_CHOOSER_PROMPT_ORIGIN" desc="The label that is used to introduce USB chooser details to the user in a popup when it is from a website.">
+  <message name="IDS_USB_DEVICE_CHOOSER_PROMPT" desc="The label that is used to introduce USB chooser details to the user in a popup.">
     <ph name="Origin">$1<ex>www.google.com</ex></ph> wants to connect
   </message>
   <message name="IDS_USB_DEVICE_CHOOSER_CONNECT_BUTTON_TEXT" desc="Label on the button that closes the USB chooser popup and connects the selected device.">
diff --git a/components/permissions_strings_grdp/IDS_BLUETOOTH_DEVICE_CHOOSER_PROMPT_ORIGIN.png.sha1 b/components/permissions_strings_grdp/IDS_BLUETOOTH_DEVICE_CHOOSER_PROMPT.png.sha1
similarity index 100%
rename from components/permissions_strings_grdp/IDS_BLUETOOTH_DEVICE_CHOOSER_PROMPT_ORIGIN.png.sha1
rename to components/permissions_strings_grdp/IDS_BLUETOOTH_DEVICE_CHOOSER_PROMPT.png.sha1
diff --git a/components/permissions_strings_grdp/IDS_BLUETOOTH_SCANNING_PROMPT.png.sha1 b/components/permissions_strings_grdp/IDS_BLUETOOTH_SCANNING_PROMPT.png.sha1
new file mode 100644
index 0000000..97c8251d
--- /dev/null
+++ b/components/permissions_strings_grdp/IDS_BLUETOOTH_SCANNING_PROMPT.png.sha1
@@ -0,0 +1 @@
+02c71330856e0abf140c701afa2bd3be2fe08b80
\ No newline at end of file
diff --git a/components/permissions_strings_grdp/IDS_USB_DEVICE_CHOOSER_PROMPT_ORIGIN.png.sha1 b/components/permissions_strings_grdp/IDS_USB_DEVICE_CHOOSER_PROMPT.png.sha1
similarity index 100%
rename from components/permissions_strings_grdp/IDS_USB_DEVICE_CHOOSER_PROMPT_ORIGIN.png.sha1
rename to components/permissions_strings_grdp/IDS_USB_DEVICE_CHOOSER_PROMPT.png.sha1
diff --git a/components/saved_tab_groups/saved_tab_group_model_unittest.cc b/components/saved_tab_groups/saved_tab_group_model_unittest.cc
index 3b9fce33..6b3e229 100644
--- a/components/saved_tab_groups/saved_tab_group_model_unittest.cc
+++ b/components/saved_tab_groups/saved_tab_group_model_unittest.cc
@@ -26,20 +26,6 @@
 
 namespace {
 
-base::GUID GenerateNextGUID() {
-  static uint64_t guid_increment;
-  if (!guid_increment) {
-    guid_increment = 0;
-  }
-
-  uint64_t kBytes[] = {0, guid_increment};
-  base::GUID guid =
-      base::GUID::ParseCaseInsensitive(base::RandomDataToGUIDString(kBytes));
-
-  guid_increment++;
-  return guid;
-}
-
 void CompareSavedTabGroupTabs(const std::vector<SavedTabGroupTab>& v1,
                               const std::vector<SavedTabGroupTab>& v2) {
   ASSERT_EQ(v1.size(), v2.size());
@@ -88,7 +74,7 @@
 }
 
 SavedTabGroup CreateTestSavedTabGroup() {
-  base::GUID id = GenerateNextGUID();
+  base::GUID id = base::GUID::GenerateRandomV4();
   const std::u16string title = u"Test Test";
   const tab_groups::TabGroupColorId& color = tab_groups::TabGroupColorId::kBlue;
 
@@ -183,9 +169,9 @@
 class SavedTabGroupModelTest : public ::testing::Test {
  protected:
   SavedTabGroupModelTest()
-      : id_1_(GenerateNextGUID()),
-        id_2_(GenerateNextGUID()),
-        id_3_(GenerateNextGUID()) {}
+      : id_1_(base::GUID::GenerateRandomV4()),
+        id_2_(base::GUID::GenerateRandomV4()),
+        id_3_(base::GUID::GenerateRandomV4()) {}
 
   ~SavedTabGroupModelTest() override { RemoveTestData(); }
 
@@ -263,7 +249,8 @@
   EXPECT_TRUE(saved_tab_group_model_->Contains(id_1_));
   EXPECT_TRUE(saved_tab_group_model_->Contains(id_2_));
   EXPECT_TRUE(saved_tab_group_model_->Contains(id_3_));
-  EXPECT_FALSE(saved_tab_group_model_->Contains(GenerateNextGUID()));
+  EXPECT_FALSE(
+      saved_tab_group_model_->Contains(base::GUID::GenerateRandomV4()));
 }
 
 // Tests that the SavedTabGroupModel::GetIndexOf preserves the order the
@@ -300,7 +287,7 @@
 // Tests that SavedTabGroupModel::Add adds an extra element into the model and
 // keeps the data.
 TEST_F(SavedTabGroupModelTest, AddNewElement) {
-  base::GUID id_4 = GenerateNextGUID();
+  base::GUID id_4 = base::GUID::GenerateRandomV4();
   const std::u16string title_4 = u"Test Test";
   const tab_groups::TabGroupColorId& color_4 =
       tab_groups::TabGroupColorId::kBlue;
@@ -976,13 +963,13 @@
 TEST_F(SavedTabGroupModelObserverTest, MoveElement) {
   SavedTabGroup stg_1(std::u16string(u"stg_1"),
                       tab_groups::TabGroupColorId::kGrey, {},
-                      GenerateNextGUID());
+                      base::GUID::GenerateRandomV4());
   SavedTabGroup stg_2(std::u16string(u"stg_2"),
                       tab_groups::TabGroupColorId::kGrey, {},
-                      GenerateNextGUID());
+                      base::GUID::GenerateRandomV4());
   SavedTabGroup stg_3(std::u16string(u"stg_3"),
                       tab_groups::TabGroupColorId::kGrey, {},
-                      GenerateNextGUID());
+                      base::GUID::GenerateRandomV4());
 
   saved_tab_group_model_->Add(stg_1);
   saved_tab_group_model_->Add(stg_2);
@@ -1002,7 +989,7 @@
   SavedTabGroup matching_group = CreateTestSavedTabGroup();
   base::GUID matching_group_guid = matching_group.saved_guid();
 
-  base::GUID matching_tab_guid = GenerateNextGUID();
+  base::GUID matching_tab_guid = base::GUID::GenerateRandomV4();
   base::Token matching_local_tab_id = base::Token::CreateRandom();
 
   SavedTabGroupTab tab(GURL(url::kAboutBlankURL), std::u16string(u"title"),
@@ -1023,8 +1010,8 @@
       saved_tab_group_model_->GetGroupContainingTab(matching_local_tab_id));
 
   // Expect GetGroupContainingTab to return null when there is no match.
-  EXPECT_EQ(nullptr,
-            saved_tab_group_model_->GetGroupContainingTab(GenerateNextGUID()));
+  EXPECT_EQ(nullptr, saved_tab_group_model_->GetGroupContainingTab(
+                         base::GUID::GenerateRandomV4()));
   EXPECT_EQ(nullptr,
             saved_tab_group_model_->GetGroupContainingTab(base::Token()));
 }
diff --git a/components/supervised_user/core/common/features.h b/components/supervised_user/core/common/features.h
index 2469c3d2..1c86faf5 100644
--- a/components/supervised_user/core/common/features.h
+++ b/components/supervised_user/core/common/features.h
@@ -23,6 +23,8 @@
 BASE_DECLARE_FEATURE(kFilterWebsitesForSupervisedUsersOnDesktopAndIOS);
 BASE_DECLARE_FEATURE(kEnableExtensionsPermissionsForSupervisedUsersOnDesktop);
 
+BASE_DECLARE_FEATURE(kLocalExtensionApprovalsV2);
+
 // Returns whether refreshed version of the website filter interstitial is
 // enabled.
 bool IsWebFilterInterstitialRefreshEnabled();
diff --git a/components/variations/service/BUILD.gn b/components/variations/service/BUILD.gn
index 6d822ac..a5ff8a82 100644
--- a/components/variations/service/BUILD.gn
+++ b/components/variations/service/BUILD.gn
@@ -4,6 +4,7 @@
 
 import("//build/buildflag_header.gni")
 import("//build/config/chrome_build.gni")
+import("//build/config/chromeos/ui_mode.gni")
 
 declare_args() {
   # Set to true make a build that disables activation of field trial tests
@@ -70,6 +71,13 @@
     "//services/network/public/cpp",
     "//ui/base",
   ]
+
+  if (is_chromeos_ash) {
+    deps += [
+      "//chromeos/ash/components/dbus/featured",
+      "//components/variations/cros:proto",
+    ]
+  }
 }
 
 source_set("unit_tests") {
@@ -105,4 +113,11 @@
     "//services/network/public/cpp",
     "//testing/gtest",
   ]
+
+  if (is_chromeos_ash) {
+    deps += [
+      "//chromeos/ash/components/dbus/featured",
+      "//components/variations/cros:proto",
+    ]
+  }
 }
diff --git a/components/variations/service/DEPS b/components/variations/service/DEPS
index 0c3020c..45260d32 100644
--- a/components/variations/service/DEPS
+++ b/components/variations/service/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
+  "+chromeos/ash/components/dbus/featured",
   "+components/encrypted_messages",
   "+components/language/core/browser",
   "+components/metrics",
diff --git a/components/variations/service/safe_seed_manager.cc b/components/variations/service/safe_seed_manager.cc
index 3b0fcf7f9..07f54571 100644
--- a/components/variations/service/safe_seed_manager.cc
+++ b/components/variations/service/safe_seed_manager.cc
@@ -9,6 +9,7 @@
 #include "base/cxx17_backports.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
+#include "build/chromeos_buildflags.h"
 #include "components/prefs/pref_registry.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
@@ -17,6 +18,12 @@
 #include "components/variations/variations_seed_store.h"
 #include "components/variations/variations_switches.h"
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+#include "base/functional/callback.h"
+#include "chromeos/ash/components/dbus/featured/featured_client.h"
+#include "components/variations/cros/featured.pb.h"
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+
 namespace variations {
 
 // Consecutive seed fetch failures are, unfortunately, a bit more common. As of
@@ -49,6 +56,12 @@
 constexpr int kFetchFailureStreakSafeSeedThreshold = 25;
 constexpr int kFetchFailureStreakNullSeedThreshold = 50;
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+// Number of attempts to send the safe seed from Chrome to CrOS platforms before
+// giving up.
+constexpr int kSendPlatformSafeSeedMaxAttempts = 2;
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+
 SafeSeedManager::SafeSeedManager(PrefService* local_state)
     : local_state_(local_state) {
   int num_failed_fetches =
@@ -129,6 +142,13 @@
                               active_seed_state_->seed_milestone,
                               *active_seed_state_->client_filterable_state,
                               active_seed_state_->seed_fetch_time);
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+    // `SendSafeSeedToPlatform` will send the safe seed at most twice.
+    // This is a best effort attempt and it is possible that the safe seed for
+    // platform and Chrome are different if sending the safe seed fails twice.
+    send_seed_to_platform_attempts_ = 0;
+    SendSafeSeedToPlatform(GetSafeSeedStateForPlatform());
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
     // The active seed state is only needed for the first time this code path is
     // reached, so free up its memory once the data is no longer needed.
@@ -157,4 +177,48 @@
 
 SafeSeedManager::ActiveSeedState::~ActiveSeedState() = default;
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+featured::SeedDetails SafeSeedManager::GetSafeSeedStateForPlatform() {
+  featured::SeedDetails safe_seed;
+  safe_seed.set_compressed_data(active_seed_state_->seed_data);
+  safe_seed.set_locale(active_seed_state_->client_filterable_state->locale);
+  safe_seed.set_milestone(active_seed_state_->seed_milestone);
+  safe_seed.set_permanent_consistency_country(
+      active_seed_state_->client_filterable_state
+          ->permanent_consistency_country);
+  safe_seed.set_session_consistency_country(
+      active_seed_state_->client_filterable_state->session_consistency_country);
+  safe_seed.set_signature(active_seed_state_->base64_seed_signature);
+  safe_seed.set_date(active_seed_state_->client_filterable_state->reference_date
+                         .ToDeltaSinceWindowsEpoch()
+                         .InMilliseconds());
+  safe_seed.set_fetch_time(
+      active_seed_state_->seed_fetch_time.ToDeltaSinceWindowsEpoch()
+          .InMilliseconds());
+
+  return safe_seed;
+}
+
+void SafeSeedManager::MaybeRetrySendSafeSeed(
+    const featured::SeedDetails& safe_seed,
+    bool success) {
+  // Do not retry after two failed attempts.
+  if (!success &&
+      send_seed_to_platform_attempts_ < kSendPlatformSafeSeedMaxAttempts) {
+    SendSafeSeedToPlatform(safe_seed);
+  }
+}
+
+void SafeSeedManager::SendSafeSeedToPlatform(
+    const featured::SeedDetails& safe_seed) {
+  send_seed_to_platform_attempts_++;
+  ash::featured::FeaturedClient* client = ash::featured::FeaturedClient::Get();
+  if (client) {
+    client->HandleSeedFetched(
+        safe_seed, base::BindOnce(&SafeSeedManager::MaybeRetrySendSafeSeed,
+                                  weak_ptr_factory_.GetWeakPtr(), safe_seed));
+  }
+}
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+
 }  // namespace variations
diff --git a/components/variations/service/safe_seed_manager.h b/components/variations/service/safe_seed_manager.h
index f2d0a5b..0e626ae 100644
--- a/components/variations/service/safe_seed_manager.h
+++ b/components/variations/service/safe_seed_manager.h
@@ -8,8 +8,15 @@
 #include <memory>
 #include <string>
 
+#include "base/gtest_prod_util.h"
 #include "base/memory/raw_ptr.h"
+#include "base/memory/weak_ptr.h"
 #include "base/time/time.h"
+#include "build/chromeos_buildflags.h"
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+#include "components/variations/cros/featured.pb.h"
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 class PrefRegistrySimple;
 class PrefService;
@@ -86,6 +93,8 @@
   void RecordSuccessfulFetch(VariationsSeedStore* seed_store);
 
  private:
+  FRIEND_TEST_ALL_PREFIXES(SafeSeedManagerTest, GetSafeSeedStateForPlatform);
+
   // The combined server and client state needed to save an active seed as a
   // safe seed. Not set when running in safe mode.
   struct ActiveSeedState {
@@ -121,6 +130,28 @@
   // The pref service used to persist the variations seed. Weak reference; must
   // outlive |this| instance.
   raw_ptr<PrefService> local_state_;
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  // Gets the combined server and client state used for early boot variations
+  // platform disaster recovery.
+  featured::SeedDetails GetSafeSeedStateForPlatform();
+
+  // Retries sending the safe seed to platform. Does not retry after two failed
+  // attempts.
+  void MaybeRetrySendSafeSeed(const featured::SeedDetails& safe_seed,
+                              bool success);
+
+  // Sends the safe seed to the platform.
+  void SendSafeSeedToPlatform(const featured::SeedDetails& safe_seed);
+
+  // A counter that keeps track of how many times the current safe seed is sent
+  // to platform.
+  size_t send_seed_to_platform_attempts_ = 0;
+
+  // Note: This should remain the last member so it'll be destroyed and
+  // invalidate its weak pointers before any other members are destroyed.
+  base::WeakPtrFactory<SafeSeedManager> weak_ptr_factory_{this};
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 };
 
 }  // namespace variations
diff --git a/components/variations/service/safe_seed_manager_unittest.cc b/components/variations/service/safe_seed_manager_unittest.cc
index 3ab567e..a95404bb 100644
--- a/components/variations/service/safe_seed_manager_unittest.cc
+++ b/components/variations/service/safe_seed_manager_unittest.cc
@@ -11,6 +11,7 @@
 #include "base/command_line.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/time/time.h"
+#include "build/chromeos_buildflags.h"
 #include "components/metrics/clean_exit_beacon.h"
 #include "components/prefs/testing_pref_service.h"
 #include "components/variations/client_filterable_state.h"
@@ -20,7 +21,13 @@
 #include "components/variations/variations_test_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+#include "chromeos/ash/components/dbus/featured/fake_featured_client.h"
+#include "components/variations/cros/featured.pb.h"
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+
 namespace variations {
+
 namespace {
 
 const char kTestSeed[] = "compressed, base-64 encoded serialized seed data";
@@ -172,6 +179,83 @@
   EXPECT_EQ(base::Time(), seed_store.fetch_time());
 }
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+TEST_F(SafeSeedManagerTest, GetSafeSeedStateForPlatform) {
+  SafeSeedManager safe_seed_manager(&prefs_);
+  FakeSeedStore seed_store(&prefs_);
+  SetDefaultActiveState(&safe_seed_manager, &prefs_);
+
+  featured::SeedDetails platform_seed =
+      safe_seed_manager.GetSafeSeedStateForPlatform();
+
+  EXPECT_EQ(kTestSeed, platform_seed.compressed_data());
+  EXPECT_EQ(kTestSignature, platform_seed.signature());
+  EXPECT_EQ(kTestSeedMilestone, platform_seed.milestone());
+  EXPECT_EQ(kTestLocale, platform_seed.locale());
+  EXPECT_EQ(kTestPermanentConsistencyCountry,
+            platform_seed.permanent_consistency_country());
+  EXPECT_EQ(kTestSessionConsistencyCountry,
+            platform_seed.session_consistency_country());
+  EXPECT_EQ(base::Time::UnixEpoch().ToDeltaSinceWindowsEpoch().InMilliseconds(),
+            platform_seed.date());
+  EXPECT_EQ(GetTestFetchTime().ToDeltaSinceWindowsEpoch().InMilliseconds(),
+            platform_seed.fetch_time());
+}
+
+TEST_F(SafeSeedManagerTest, SendSafeSeedToPlatform_SucceedFirstAttempt) {
+  SafeSeedManager safe_seed_manager(&prefs_);
+  FakeSeedStore seed_store(&prefs_);
+  SetDefaultActiveState(&safe_seed_manager, &prefs_);
+
+  ash::featured::FeaturedClient::InitializeFake();
+  ash::featured::FakeFeaturedClient* client =
+      ash::featured::FakeFeaturedClient::Get();
+  client->AddResponse(true);
+
+  safe_seed_manager.RecordSuccessfulFetch(&seed_store);
+  ExpectDefaultActiveState(seed_store);
+  EXPECT_EQ(client->handle_seed_fetched_attempts(), 1);
+
+  ash::featured::FeaturedClient::Shutdown();
+}
+
+TEST_F(SafeSeedManagerTest, SendSafeSeedToPlatform_FailFirstAttempt) {
+  SafeSeedManager safe_seed_manager(&prefs_);
+  FakeSeedStore seed_store(&prefs_);
+  SetDefaultActiveState(&safe_seed_manager, &prefs_);
+
+  ash::featured::FeaturedClient::InitializeFake();
+  ash::featured::FakeFeaturedClient* client =
+      ash::featured::FakeFeaturedClient::Get();
+  client->AddResponse(false);
+  client->AddResponse(true);
+
+  safe_seed_manager.RecordSuccessfulFetch(&seed_store);
+  ExpectDefaultActiveState(seed_store);
+  EXPECT_EQ(client->handle_seed_fetched_attempts(), 2);
+
+  ash::featured::FeaturedClient::Shutdown();
+}
+
+TEST_F(SafeSeedManagerTest, SendSafeSeedToPlatform_FailTwoAttempts) {
+  SafeSeedManager safe_seed_manager(&prefs_);
+  FakeSeedStore seed_store(&prefs_);
+  SetDefaultActiveState(&safe_seed_manager, &prefs_);
+
+  ash::featured::FeaturedClient::InitializeFake();
+  ash::featured::FakeFeaturedClient* client =
+      ash::featured::FakeFeaturedClient::Get();
+  client->AddResponse(false);
+  client->AddResponse(false);
+
+  safe_seed_manager.RecordSuccessfulFetch(&seed_store);
+  ExpectDefaultActiveState(seed_store);
+  EXPECT_EQ(client->handle_seed_fetched_attempts(), 2);
+
+  ash::featured::FeaturedClient::Shutdown();
+}
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+
 TEST_F(SafeSeedManagerTest, FetchFailureMetrics_DefaultPrefs) {
   base::HistogramTester histogram_tester;
   SafeSeedManager safe_seed_manager(&prefs_);
diff --git a/components/vector_icons/BUILD.gn b/components/vector_icons/BUILD.gn
index 959e1f8b..f61e17c 100644
--- a/components/vector_icons/BUILD.gn
+++ b/components/vector_icons/BUILD.gn
@@ -22,6 +22,7 @@
     "bluetooth_connected.icon",
     "bluetooth_scanning.icon",
     "business.icon",
+    "business_chrome_refresh.icon",
     "call.icon",
     "call_end.icon",
     "cancel.icon",
@@ -92,6 +93,7 @@
     "midi.icon",
     "midi_off.icon",
     "not_secure_warning.icon",
+    "not_secure_warning_chrome_refresh.icon",
     "notification_warning.icon",
     "notifications.icon",
     "notifications_off.icon",
@@ -110,6 +112,7 @@
     "save_original_file.icon",
     "screen_share.icon",
     "search.icon",
+    "search_chrome_refresh.icon",
     "select_window.icon",
     "sensors.icon",
     "serial_port.icon",
diff --git a/components/vector_icons/business_chrome_refresh.icon b/components/vector_icons/business_chrome_refresh.icon
new file mode 100644
index 0000000..2588f08
--- /dev/null
+++ b/components/vector_icons/business_chrome_refresh.icon
@@ -0,0 +1,268 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+
+CANVAS_DIMENSIONS, 24,
+MOVE_TO, 2, 21,
+V_LINE_TO, 3,
+H_LINE_TO, 12,
+V_LINE_TO, 7,
+H_LINE_TO, 22,
+V_LINE_TO, 21,
+H_LINE_TO, 2,
+CLOSE,
+MOVE_TO, 4, 19,
+H_LINE_TO, 6,
+V_LINE_TO, 17,
+H_LINE_TO, 4,
+V_LINE_TO, 19,
+CLOSE,
+MOVE_TO, 4, 15,
+H_LINE_TO, 6,
+V_LINE_TO, 13,
+H_LINE_TO, 4,
+V_LINE_TO, 15,
+CLOSE,
+MOVE_TO, 4, 11,
+H_LINE_TO, 6,
+V_LINE_TO, 9,
+H_LINE_TO, 4,
+V_LINE_TO, 11,
+CLOSE,
+MOVE_TO, 4, 7,
+H_LINE_TO, 6,
+V_LINE_TO, 5,
+H_LINE_TO, 4,
+V_LINE_TO, 7,
+CLOSE,
+MOVE_TO, 8, 19,
+H_LINE_TO, 10,
+V_LINE_TO, 17,
+H_LINE_TO, 8,
+V_LINE_TO, 19,
+CLOSE,
+MOVE_TO, 8, 15,
+H_LINE_TO, 10,
+V_LINE_TO, 13,
+H_LINE_TO, 8,
+V_LINE_TO, 15,
+CLOSE,
+MOVE_TO, 8, 11,
+H_LINE_TO, 10,
+V_LINE_TO, 9,
+H_LINE_TO, 8,
+V_LINE_TO, 11,
+CLOSE,
+MOVE_TO, 8, 7,
+H_LINE_TO, 10,
+V_LINE_TO, 5,
+H_LINE_TO, 8,
+V_LINE_TO, 7,
+CLOSE,
+MOVE_TO, 12, 19,
+H_LINE_TO, 20,
+V_LINE_TO, 9,
+H_LINE_TO, 12,
+V_LINE_TO, 11,
+H_LINE_TO, 14,
+V_LINE_TO, 13,
+H_LINE_TO, 12,
+V_LINE_TO, 15,
+H_LINE_TO, 14,
+V_LINE_TO, 17,
+H_LINE_TO, 12,
+V_LINE_TO, 19,
+CLOSE,
+MOVE_TO, 16, 13,
+V_LINE_TO, 11,
+H_LINE_TO, 18,
+V_LINE_TO, 13,
+H_LINE_TO, 16,
+CLOSE,
+MOVE_TO, 16, 17,
+V_LINE_TO, 15,
+H_LINE_TO, 18,
+V_LINE_TO, 17,
+H_LINE_TO, 16,
+CLOSE
+
+
+
+
+CANVAS_DIMENSIONS, 20,
+MOVE_TO, 2, 17,
+V_LINE_TO, 3,
+H_LINE_TO, 10,
+V_LINE_TO, 6,
+H_LINE_TO, 18,
+V_LINE_TO, 17,
+H_LINE_TO, 2,
+CLOSE,
+MOVE_TO, 3.5f, 15.5f,
+H_LINE_TO, 5,
+V_LINE_TO, 14,
+H_LINE_TO, 3.5f,
+V_LINE_TO, 15.5f,
+CLOSE,
+MOVE_TO, 3.5f, 12.33f,
+H_LINE_TO, 5,
+V_LINE_TO, 10.83f,
+H_LINE_TO, 3.5f,
+V_LINE_TO, 12.33f,
+CLOSE,
+MOVE_TO, 3.5f, 9.17f,
+H_LINE_TO, 5,
+V_LINE_TO, 7.67f,
+H_LINE_TO, 3.5f,
+V_LINE_TO, 9.17f,
+CLOSE,
+MOVE_TO, 3.5f, 6,
+H_LINE_TO, 5,
+V_LINE_TO, 4.5f,
+H_LINE_TO, 3.5f,
+V_LINE_TO, 6,
+CLOSE,
+MOVE_TO, 7, 15.5f,
+H_LINE_TO, 8.5f,
+V_LINE_TO, 14,
+H_LINE_TO, 7,
+V_LINE_TO, 15.5f,
+CLOSE,
+MOVE_TO, 7, 12.33f,
+H_LINE_TO, 8.5f,
+V_LINE_TO, 10.83f,
+H_LINE_TO, 7,
+V_LINE_TO, 12.33f,
+CLOSE,
+MOVE_TO, 7, 9.17f,
+H_LINE_TO, 8.5f,
+V_LINE_TO, 7.67f,
+H_LINE_TO, 7,
+V_LINE_TO, 9.17f,
+CLOSE,
+MOVE_TO, 7, 6,
+H_LINE_TO, 8.5f,
+V_LINE_TO, 4.5f,
+H_LINE_TO, 7,
+V_LINE_TO, 6,
+CLOSE,
+MOVE_TO, 10, 15.5f,
+H_LINE_TO, 16.5f,
+V_LINE_TO, 7.5f,
+H_LINE_TO, 10,
+V_LINE_TO, 9.17f,
+H_LINE_TO, 11.5f,
+V_LINE_TO, 10.67f,
+H_LINE_TO, 10,
+V_LINE_TO, 12.33f,
+H_LINE_TO, 11.5f,
+V_LINE_TO, 13.83f,
+H_LINE_TO, 10,
+V_LINE_TO, 15.5f,
+CLOSE,
+MOVE_TO, 13.5f, 10.67f,
+V_LINE_TO, 9.17f,
+H_LINE_TO, 15,
+V_LINE_TO, 10.67f,
+H_LINE_TO, 13.5f,
+CLOSE,
+MOVE_TO, 13.5f, 13.83f,
+V_LINE_TO, 12.33f,
+H_LINE_TO, 15,
+V_LINE_TO, 13.83f,
+H_LINE_TO, 13.5f,
+CLOSE,
+NEW_PATH
+
+
+
+
+
+CANVAS_DIMENSIONS, 16,
+MOVE_TO, 12, 7,
+H_LINE_TO, 11,
+V_LINE_TO, 8,
+H_LINE_TO, 12,
+V_LINE_TO, 7,
+CLOSE,
+NEW_PATH,
+MOVE_TO, 12, 10,
+H_LINE_TO, 11,
+V_LINE_TO, 11,
+H_LINE_TO, 12,
+V_LINE_TO, 10,
+CLOSE,
+NEW_PATH,
+MOVE_TO, 8, 4,
+V_LINE_TO, 2,
+H_LINE_TO, 1,
+V_LINE_TO, 14,
+H_LINE_TO, 15,
+V_LINE_TO, 4,
+H_LINE_TO, 8,
+CLOSE,
+MOVE_TO, 4, 12,
+H_LINE_TO, 3,
+V_LINE_TO, 11,
+H_LINE_TO, 4,
+V_LINE_TO, 12,
+CLOSE,
+MOVE_TO, 4, 9.67f,
+H_LINE_TO, 3,
+V_LINE_TO, 8.67f,
+H_LINE_TO, 4,
+V_LINE_TO, 9.67f,
+CLOSE,
+MOVE_TO, 4, 7.34f,
+H_LINE_TO, 3,
+V_LINE_TO, 6.34f,
+H_LINE_TO, 4,
+V_LINE_TO, 7.34f,
+CLOSE,
+MOVE_TO, 4, 5.01f,
+H_LINE_TO, 3,
+V_LINE_TO, 4.01f,
+H_LINE_TO, 4,
+V_LINE_TO, 5.01f,
+CLOSE,
+MOVE_TO, 6, 12.01f,
+H_LINE_TO, 5,
+V_LINE_TO, 11.01f,
+H_LINE_TO, 6,
+V_LINE_TO, 12.01f,
+CLOSE,
+MOVE_TO, 6, 9.68f,
+H_LINE_TO, 5,
+V_LINE_TO, 8.68f,
+H_LINE_TO, 6,
+V_LINE_TO, 9.68f,
+CLOSE,
+MOVE_TO, 6, 7.35f,
+H_LINE_TO, 5,
+V_LINE_TO, 6.35f,
+H_LINE_TO, 6,
+V_LINE_TO, 7.35f,
+CLOSE,
+MOVE_TO, 6, 5.02f,
+H_LINE_TO, 5,
+V_LINE_TO, 4.02f,
+H_LINE_TO, 6,
+V_LINE_TO, 5.02f,
+CLOSE,
+MOVE_TO, 13.5f, 12.52f,
+H_LINE_TO, 8,
+V_LINE_TO, 11.02f,
+H_LINE_TO, 9,
+V_LINE_TO, 10.02f,
+H_LINE_TO, 8,
+V_LINE_TO, 8.02f,
+H_LINE_TO, 9,
+V_LINE_TO, 7.02f,
+H_LINE_TO, 8,
+V_LINE_TO, 5.52f,
+H_LINE_TO, 13.5f,
+V_LINE_TO, 12.52f,
+CLOSE,
+NEW_PATH
+
diff --git a/components/vector_icons/not_secure_warning_chrome_refresh.icon b/components/vector_icons/not_secure_warning_chrome_refresh.icon
new file mode 100644
index 0000000..875406a
--- /dev/null
+++ b/components/vector_icons/not_secure_warning_chrome_refresh.icon
@@ -0,0 +1,59 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+CANVAS_DIMENSIONS, 32,
+MOVE_TO, 1.33f, 28,
+LINE_TO, 16, 2.67f,
+LINE_TO, 30.67f, 28,
+H_LINE_TO, 1.33f,
+CLOSE,
+MOVE_TO, 5.57f, 25.57f,
+H_LINE_TO, 26.43f,
+LINE_TO, 16, 7.57f,
+LINE_TO, 5.57f, 25.57f,
+CLOSE,
+MOVE_TO, 16.03f, 24.03f,
+CUBIC_TO, 16.39f, 24.03f, 16.68f, 23.92f, 16.9f, 23.7f,
+CUBIC_TO, 17.14f, 23.46f, 17.27f, 23.16f, 17.27f, 22.8f,
+CUBIC_TO, 17.27f, 22.47f, 17.14f, 22.19f, 16.9f, 21.97f,
+CUBIC_TO, 16.68f, 21.72f, 16.39f, 21.6f, 16.03f, 21.6f,
+CUBIC_TO, 15.7f, 21.6f, 15.41f, 21.72f, 15.17f, 21.97f,
+CUBIC_TO, 14.94f, 22.19f, 14.83f, 22.47f, 14.83f, 22.8f,
+CUBIC_TO, 14.83f, 23.16f, 14.94f, 23.46f, 15.17f, 23.7f,
+CUBIC_TO, 15.41f, 23.92f, 15.7f, 24.03f, 16.03f, 24.03f,
+CLOSE,
+MOVE_TO, 14.83f, 20.13f,
+H_LINE_TO, 17.27f,
+V_LINE_TO, 13.2f,
+H_LINE_TO, 14.83f,
+V_LINE_TO, 20.13f,
+CLOSE,
+NEW_PATH
+
+CANVAS_DIMENSIONS, 16,
+MOVE_TO, 8, 2,
+LINE_TO, 1, 14,
+H_LINE_TO, 15,
+LINE_TO, 8, 2,
+CLOSE,
+MOVE_TO, 8, 4.98f,
+LINE_TO, 12.39f, 12.5f,
+H_LINE_TO, 3.61f,
+LINE_TO, 8, 4.98f,
+CLOSE,
+NEW_PATH,
+MOVE_TO, 8.75f, 7,
+H_LINE_TO, 7.25f,
+V_LINE_TO, 10,
+H_LINE_TO, 8.75f,
+V_LINE_TO, 7,
+CLOSE,
+NEW_PATH,
+MOVE_TO, 8, 12,
+CUBIC_TO, 8.41f, 12, 8.75f, 11.66f, 8.75f, 11.25f,
+CUBIC_TO, 8.75f, 10.84f, 8.41f, 10.5f, 8, 10.5f,
+CUBIC_TO, 7.59f, 10.5f, 7.25f, 10.84f, 7.25f, 11.25f,
+CUBIC_TO, 7.25f, 11.66f, 7.59f, 12, 8, 12,
+CLOSE,
+NEW_PATH
diff --git a/components/vector_icons/search_chrome_refresh.icon b/components/vector_icons/search_chrome_refresh.icon
new file mode 100644
index 0000000..508b567
--- /dev/null
+++ b/components/vector_icons/search_chrome_refresh.icon
@@ -0,0 +1,59 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+CANVAS_DIMENSIONS, 32,
+MOVE_TO, 26.27f, 28,
+LINE_TO, 17.73f, 19.47f,
+CUBIC_TO, 17.07f, 20.02f, 16.3f, 20.46f, 15.43f, 20.77f,
+CUBIC_TO, 14.57f, 21.08f, 13.63f, 21.23f, 12.63f, 21.23f,
+CUBIC_TO, 10.23f, 21.23f, 8.19f, 20.4f, 6.5f, 18.73f,
+CUBIC_TO, 4.83f, 17.04f, 4, 15, 4, 12.6f,
+CUBIC_TO, 4, 10.2f, 4.83f, 8.17f, 6.5f, 6.5f,
+CUBIC_TO, 8.19f, 4.83f, 10.23f, 4, 12.63f, 4,
+CUBIC_TO, 15.03f, 4, 17.07f, 4.83f, 18.73f, 6.5f,
+CUBIC_TO, 20.4f, 8.17f, 21.23f, 10.2f, 21.23f, 12.6f,
+CUBIC_TO, 21.23f, 13.58f, 21.08f, 14.51f, 20.77f, 15.4f,
+CUBIC_TO, 20.46f, 16.27f, 20.02f, 17.04f, 19.47f, 17.73f,
+LINE_TO, 28, 26.27f,
+LINE_TO, 26.27f, 28,
+CLOSE,
+MOVE_TO, 12.63f, 18.8f,
+CUBIC_TO, 14.34f, 18.8f, 15.8f, 18.2f, 17, 17,
+CUBIC_TO, 18.2f, 15.78f, 18.8f, 14.31f, 18.8f, 12.6f,
+CUBIC_TO, 18.8f, 10.89f, 18.2f, 9.43f, 17, 8.23f,
+CUBIC_TO, 15.8f, 7.03f, 14.34f, 6.43f, 12.63f, 6.43f,
+CUBIC_TO, 10.92f, 6.43f, 9.46f, 7.03f, 8.23f, 8.23f,
+CUBIC_TO, 7.03f, 9.43f, 6.43f, 10.89f, 6.43f, 12.6f,
+CUBIC_TO, 6.43f, 14.31f, 7.03f, 15.78f, 8.23f, 17,
+CUBIC_TO, 9.46f, 18.2f, 10.92f, 18.8f, 12.63f, 18.8f,
+CLOSE,
+NEW_PATH
+
+CANVAS_DIMENSIONS, 20,
+MOVE_TO, 15.94f, 17,
+LINE_TO, 10.96f, 12.02f,
+CUBIC_TO, 10.54f, 12.33f, 10.08f, 12.57f, 9.58f, 12.75f,
+CUBIC_TO, 9.08f, 12.92f, 8.56f, 13, 8, 13,
+CUBIC_TO, 6.61f, 13, 5.43f, 12.51f, 4.46f, 11.54f,
+CUBIC_TO, 3.49f, 10.57f, 3, 9.39f, 3, 8,
+CUBIC_TO, 3, 6.61f, 3.49f, 5.43f, 4.46f, 4.46f,
+CUBIC_TO, 5.43f, 3.49f, 6.61f, 3, 8, 3,
+CUBIC_TO, 9.39f, 3, 10.57f, 3.49f, 11.54f, 4.46f,
+CUBIC_TO, 12.51f, 5.43f, 13, 6.61f, 13, 8,
+CUBIC_TO, 13, 8.56f, 12.91f, 9.08f, 12.73f, 9.58f,
+CUBIC_TO, 12.56f, 10.08f, 12.33f, 10.54f, 12.02f, 10.96f,
+LINE_TO, 17, 15.94f,
+LINE_TO, 15.94f, 17,
+CLOSE,
+MOVE_TO, 8, 11.5f,
+CUBIC_TO, 8.97f, 11.5f, 9.8f, 11.16f, 10.48f, 10.48f,
+CUBIC_TO, 11.16f, 9.8f, 11.5f, 8.97f, 11.5f, 8,
+CUBIC_TO, 11.5f, 7.03f, 11.16f, 6.2f, 10.48f, 5.52f,
+CUBIC_TO, 9.8f, 4.84f, 8.97f, 4.5f, 8, 4.5f,
+CUBIC_TO, 7.03f, 4.5f, 6.2f, 4.84f, 5.52f, 5.52f,
+CUBIC_TO, 4.84f, 6.2f, 4.5f, 7.03f, 4.5f, 8,
+CUBIC_TO, 4.5f, 8.97f, 4.84f, 9.8f, 5.52f, 10.48f,
+CUBIC_TO, 6.2f, 11.16f, 7.03f, 11.5f, 8, 11.5f,
+CLOSE,
+NEW_PATH
diff --git a/components/webdata/common/web_database_backend.cc b/components/webdata/common/web_database_backend.cc
index 2ebcbab..4b9e566 100644
--- a/components/webdata/common/web_database_backend.cc
+++ b/components/webdata/common/web_database_backend.cc
@@ -118,7 +118,7 @@
     diagnostics_ = db_->GetDiagnosticInfo(error, statement);
     diagnostics_ += sql::GetCorruptFileDiagnosticsInfo(db_path_);
 
-    db_->GetSQLConnection()->RazeAndClose();
+    db_->GetSQLConnection()->RazeAndPoison();
   }
 }
 
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index f6a8eda..b5644ad 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -2463,10 +2463,10 @@
     ]
     deps += [
       "//third_party/abseil-cpp:absl",
-      "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.accessibility.semantics:fuchsia.accessibility.semantics_hlcpp",
+      "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.accessibility.semantics:fuchsia.accessibility.semantics_cpp",
+      "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.accessibility.semantics:fuchsia.accessibility.semantics_cpp_hlcpp_conversion",
       "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.mediacodec:fuchsia.mediacodec_hlcpp",
       "//third_party/fuchsia-sdk/sdk/pkg/inspect",
-      "//third_party/fuchsia-sdk/sdk/pkg/scenic_cpp",
       "//third_party/fuchsia-sdk/sdk/pkg/sys_inspect_cpp",
       "//third_party/fuchsia-sdk/sdk/pkg/zx",
       "//ui/accessibility",
diff --git a/content/browser/accessibility/accessibility_tree_formatter_fuchsia.cc b/content/browser/accessibility/accessibility_tree_formatter_fuchsia.cc
index eeb47a6..383b020 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_fuchsia.cc
+++ b/content/browser/accessibility/accessibility_tree_formatter_fuchsia.cc
@@ -12,10 +12,10 @@
 namespace content {
 namespace {
 
-using FuchsiaAction = fuchsia::accessibility::semantics::Action;
-using FuchsiaCheckedState = fuchsia::accessibility::semantics::CheckedState;
-using FuchsiaRole = fuchsia::accessibility::semantics::Role;
-using FuchsiaToggledState = fuchsia::accessibility::semantics::ToggledState;
+using FuchsiaAction = fuchsia_accessibility_semantics::Action;
+using FuchsiaCheckedState = fuchsia_accessibility_semantics::CheckedState;
+using FuchsiaRole = fuchsia_accessibility_semantics::Role;
+using FuchsiaToggledState = fuchsia_accessibility_semantics::ToggledState;
 
 constexpr const char* const kBoolAttributes[] = {
     "hidden", "focusable", "has_input_focus", "is_keyboard_key", "selected",
@@ -40,53 +40,53 @@
 
 std::string FuchsiaRoleToString(const FuchsiaRole role) {
   switch (role) {
-    case FuchsiaRole::BUTTON:
+    case FuchsiaRole::kButton:
       return "BUTTON";
-    case FuchsiaRole::CELL:
+    case FuchsiaRole::kCell:
       return "CELL";
-    case FuchsiaRole::CHECK_BOX:
+    case FuchsiaRole::kCheckBox:
       return "CHECK_BOX";
-    case FuchsiaRole::COLUMN_HEADER:
+    case FuchsiaRole::kColumnHeader:
       return "COLUMN_HEADER";
-    case FuchsiaRole::GRID:
+    case FuchsiaRole::kGrid:
       return "GRID";
-    case FuchsiaRole::HEADER:
+    case FuchsiaRole::kHeader:
       return "HEADER";
-    case FuchsiaRole::IMAGE:
+    case FuchsiaRole::kImage:
       return "IMAGE";
-    case FuchsiaRole::LINK:
+    case FuchsiaRole::kLink:
       return "LINK";
-    case FuchsiaRole::LIST:
+    case FuchsiaRole::kList:
       return "LIST";
-    case FuchsiaRole::LIST_ELEMENT:
+    case FuchsiaRole::kListElement:
       return "LIST_ELEMENT";
-    case FuchsiaRole::LIST_ELEMENT_MARKER:
+    case FuchsiaRole::kListElementMarker:
       return "LIST_ELEMENT_MARKER";
-    case FuchsiaRole::PARAGRAPH:
+    case FuchsiaRole::kParagraph:
       return "PARAGRAPH";
-    case FuchsiaRole::RADIO_BUTTON:
+    case FuchsiaRole::kRadioButton:
       return "RADIO_BUTTON";
-    case FuchsiaRole::ROW_GROUP:
+    case FuchsiaRole::kRowGroup:
       return "ROW_GROUP";
-    case FuchsiaRole::ROW_HEADER:
+    case FuchsiaRole::kRowHeader:
       return "ROW_HEADER";
-    case FuchsiaRole::SEARCH_BOX:
+    case FuchsiaRole::kSearchBox:
       return "SEARCH_BOX";
-    case FuchsiaRole::SLIDER:
+    case FuchsiaRole::kSlider:
       return "SLIDER";
-    case FuchsiaRole::STATIC_TEXT:
+    case FuchsiaRole::kStaticText:
       return "STATIC_TEXT";
-    case FuchsiaRole::TABLE:
+    case FuchsiaRole::kTable:
       return "TABLE";
-    case FuchsiaRole::TABLE_ROW:
+    case FuchsiaRole::kTableRow:
       return "TABLE_ROW";
-    case FuchsiaRole::TEXT_FIELD:
+    case FuchsiaRole::kTextField:
       return "TEXT_FIELD";
-    case FuchsiaRole::TEXT_FIELD_WITH_COMBO_BOX:
+    case FuchsiaRole::kTextFieldWithComboBox:
       return "TEXT_FIELD_WITH_COMBO_BOX";
-    case FuchsiaRole::TOGGLE_SWITCH:
+    case FuchsiaRole::kToggleSwitch:
       return "TOGGLE_SWITCH";
-    case FuchsiaRole::UNKNOWN:
+    case FuchsiaRole::kUnknown:
       return "UNKNOWN";
     default:
       NOTREACHED();
@@ -96,19 +96,19 @@
 
 std::string FuchsiaActionToString(FuchsiaAction action) {
   switch (action) {
-    case FuchsiaAction::DEFAULT:
+    case FuchsiaAction::kDefault:
       return "DEFAULT";
-    case FuchsiaAction::DECREMENT:
+    case FuchsiaAction::kDecrement:
       return "DECREMENT";
-    case FuchsiaAction::INCREMENT:
+    case FuchsiaAction::kIncrement:
       return "INCREMENT";
-    case FuchsiaAction::SECONDARY:
+    case FuchsiaAction::kSecondary:
       return "SECONDARY";
-    case FuchsiaAction::SET_FOCUS:
+    case FuchsiaAction::kSetFocus:
       return "SET_FOCUS";
-    case FuchsiaAction::SET_VALUE:
+    case FuchsiaAction::kSetValue:
       return "SET_VALUE";
-    case FuchsiaAction::SHOW_ON_SCREEN:
+    case FuchsiaAction::kShowOnScreen:
       return "SHOW_ON_SCREEN";
     default:
       NOTREACHED();
@@ -130,13 +130,13 @@
 
 std::string CheckedStateToString(const FuchsiaCheckedState checked_state) {
   switch (checked_state) {
-    case FuchsiaCheckedState::NONE:
+    case FuchsiaCheckedState::kNone:
       return "NONE";
-    case FuchsiaCheckedState::CHECKED:
+    case FuchsiaCheckedState::kChecked:
       return "CHECKED";
-    case FuchsiaCheckedState::UNCHECKED:
+    case FuchsiaCheckedState::kUnchecked:
       return "UNCHECKED";
-    case FuchsiaCheckedState::MIXED:
+    case FuchsiaCheckedState::kMixed:
       return "MIXED";
     default:
       NOTREACHED();
@@ -146,11 +146,11 @@
 
 std::string ToggledStateToString(const FuchsiaToggledState toggled_state) {
   switch (toggled_state) {
-    case FuchsiaToggledState::ON:
+    case FuchsiaToggledState::kOn:
       return "ON";
-    case FuchsiaToggledState::OFF:
+    case FuchsiaToggledState::kOff:
       return "OFF";
-    case FuchsiaToggledState::INDETERMINATE:
+    case FuchsiaToggledState::kIndeterminate:
       return "INDETERMINATE";
     default:
       NOTREACHED();
@@ -159,29 +159,30 @@
 }
 
 std::string ViewportOffsetToString(
-    const fuchsia::ui::gfx::vec2& viewport_offset) {
-  return base::StringPrintf("(%.1f, %.1f)", viewport_offset.x,
-                            viewport_offset.y);
+    const fuchsia_ui_gfx::Vec2& viewport_offset) {
+  return base::StringPrintf("(%.1f, %.1f)", viewport_offset.x(),
+                            viewport_offset.y());
 }
 
-std::string Vec3ToString(const fuchsia::ui::gfx::vec3& vec) {
-  return base::StringPrintf("(%.1f, %.1f, %.1f)", vec.x, vec.y, vec.z);
+std::string Vec3ToString(const fuchsia_ui_gfx::Vec3& vec) {
+  return base::StringPrintf("(%.1f, %.1f, %.1f)", vec.x(), vec.y(), vec.z());
 }
 
-std::string Mat4ToString(const fuchsia::ui::gfx::mat4& mat) {
+std::string Mat4ToString(const fuchsia_ui_gfx::Mat4& mat) {
   std::string retval = "{ ";
   for (int i = 0; i < 4; i++) {
-    retval.append(base::StringPrintf(
-        "col%d: (%.1f,%.1f,%.1f,%.1f), ", i, mat.matrix[i * 4],
-        mat.matrix[i * 4 + 1], mat.matrix[i * 4 + 2], mat.matrix[i * 4 + 3]));
+    retval.append(
+        base::StringPrintf("col%d: (%.1f,%.1f,%.1f,%.1f), ", i,
+                           mat.matrix()[i * 4], mat.matrix()[i * 4 + 1],
+                           mat.matrix()[i * 4 + 2], mat.matrix()[i * 4 + 3]));
   }
   return retval.append(" }");
 }
 
-std::string LocationToString(const fuchsia::ui::gfx::BoundingBox& location) {
+std::string LocationToString(const fuchsia_ui_gfx::BoundingBox& location) {
   return base::StringPrintf("{ min: %s, max: %s }",
-                            Vec3ToString(location.min).c_str(),
-                            Vec3ToString(location.max).c_str());
+                            Vec3ToString(location.min()).c_str(),
+                            Vec3ToString(location.max()).c_str());
 }
 
 }  // namespace
@@ -223,10 +224,10 @@
 
   base::Value::List children;
 
-  fuchsia::accessibility::semantics::Node fuchsia_node =
+  fuchsia_accessibility_semantics::Node fuchsia_node =
       static_cast<const BrowserAccessibilityFuchsia&>(node).ToFuchsiaNodeData();
 
-  for (uint32_t child_id : fuchsia_node.child_ids()) {
+  for (uint32_t child_id : fuchsia_node.child_ids().value()) {
     ui::AXPlatformNodeFuchsia* child_node =
         static_cast<ui::AXPlatformNodeFuchsia*>(
             ui::AXPlatformNodeBase::GetFromUniqueId(child_id));
@@ -259,135 +260,159 @@
 
   CHECK(browser_accessibility_fuchsia);
 
-  const fuchsia::accessibility::semantics::Node& fuchsia_node =
+  const fuchsia_accessibility_semantics::Node& fuchsia_node =
       browser_accessibility_fuchsia->ToFuchsiaNodeData();
 
   // Add fuchsia node attributes.
-  dict->Set("role", FuchsiaRoleToString(fuchsia_node.role()));
+  dict->Set("role", FuchsiaRoleToString(fuchsia_node.role().value()));
 
-  dict->Set("actions", FuchsiaActionsToString(fuchsia_node.actions()));
+  dict->Set("actions", FuchsiaActionsToString(fuchsia_node.actions().value()));
 
-  if (fuchsia_node.has_attributes()) {
-    const fuchsia::accessibility::semantics::Attributes& attributes =
-        fuchsia_node.attributes();
+  if (fuchsia_node.attributes()) {
+    const fuchsia_accessibility_semantics::Attributes& attributes =
+        fuchsia_node.attributes().value();
 
-    if (attributes.has_label() && !attributes.label().empty())
-      dict->Set("label", attributes.label());
-
-    if (attributes.has_secondary_label() &&
-        !attributes.secondary_label().empty()) {
-      dict->Set("secondary_label", attributes.secondary_label());
+    if (attributes.label() && !attributes.label()->empty()) {
+      dict->Set("label", attributes.label().value());
     }
 
-    if (attributes.has_range()) {
-      const auto& range_attributes = attributes.range();
-
-      if (range_attributes.has_min_value())
-        dict->Set("min_value", range_attributes.min_value());
-
-      if (range_attributes.has_max_value())
-        dict->Set("max_value", range_attributes.max_value());
-
-      if (range_attributes.has_step_delta())
-        dict->Set("step_delta", range_attributes.step_delta());
+    if (attributes.secondary_label() &&
+        !attributes.secondary_label()->empty()) {
+      dict->Set("secondary_label", attributes.secondary_label().value());
     }
 
-    if (attributes.has_table_attributes()) {
-      const auto& table_attributes = attributes.table_attributes();
+    if (attributes.range()) {
+      const auto& range_attributes = attributes.range().value();
 
-      if (table_attributes.has_number_of_rows())
+      if (range_attributes.min_value()) {
+        dict->Set("min_value", range_attributes.min_value().value());
+      }
+
+      if (range_attributes.max_value()) {
+        dict->Set("max_value", range_attributes.max_value().value());
+      }
+
+      if (range_attributes.step_delta()) {
+        dict->Set("step_delta", range_attributes.step_delta().value());
+      }
+    }
+
+    if (attributes.table_attributes()) {
+      const auto& table_attributes = attributes.table_attributes().value();
+
+      if (table_attributes.number_of_rows()) {
         dict->Set("number_of_rows",
-                  static_cast<int>(table_attributes.number_of_rows()));
+                  static_cast<int>(table_attributes.number_of_rows().value()));
+      }
 
-      if (table_attributes.has_number_of_columns()) {
-        dict->Set("number_of_columns",
-                  static_cast<int>(table_attributes.number_of_columns()));
+      if (table_attributes.number_of_columns()) {
+        dict->Set(
+            "number_of_columns",
+            static_cast<int>(table_attributes.number_of_columns().value()));
       }
     }
 
-    if (attributes.has_table_row_attributes()) {
-      const auto& table_row_attributes = attributes.table_row_attributes();
+    if (attributes.table_row_attributes()) {
+      const auto& table_row_attributes =
+          attributes.table_row_attributes().value();
 
-      if (table_row_attributes.has_row_index())
+      if (table_row_attributes.row_index()) {
         dict->Set("row_index",
-                  static_cast<int>(table_row_attributes.row_index()));
+                  static_cast<int>(table_row_attributes.row_index().value()));
+      }
     }
 
-    if (attributes.has_table_cell_attributes()) {
-      const auto& table_cell_attributes = attributes.table_cell_attributes();
+    if (attributes.table_cell_attributes()) {
+      const auto& table_cell_attributes =
+          attributes.table_cell_attributes().value();
 
-      if (table_cell_attributes.has_row_index())
+      if (table_cell_attributes.row_index()) {
         dict->Set("cell_row_index",
-                  static_cast<int>(table_cell_attributes.row_index()));
-
-      if (table_cell_attributes.has_column_index()) {
-        dict->Set("cell_column_index",
-                  static_cast<int>(table_cell_attributes.column_index()));
+                  static_cast<int>(table_cell_attributes.row_index().value()));
       }
 
-      if (table_cell_attributes.has_row_span())
+      if (table_cell_attributes.column_index()) {
+        dict->Set(
+            "cell_column_index",
+            static_cast<int>(table_cell_attributes.column_index().value()));
+      }
+
+      if (table_cell_attributes.row_span()) {
         dict->Set("cell_row_span",
-                  static_cast<int>(table_cell_attributes.row_span()));
+                  static_cast<int>(table_cell_attributes.row_span().value()));
+      }
 
-      if (table_cell_attributes.has_column_span()) {
-        dict->Set("cell_column_span",
-                  static_cast<int>(table_cell_attributes.column_span()));
+      if (table_cell_attributes.column_span()) {
+        dict->Set(
+            "cell_column_span",
+            static_cast<int>(table_cell_attributes.column_span().value()));
       }
     }
 
-    if (attributes.has_list_attributes()) {
+    if (attributes.list_attributes() && attributes.list_attributes()->size()) {
       dict->Set("list_size",
-                static_cast<int>(attributes.list_attributes().size()));
+                static_cast<int>(attributes.list_attributes()->size().value()));
     }
 
-    if (attributes.has_list_element_attributes()) {
+    if (attributes.list_element_attributes() &&
+        attributes.list_element_attributes()->index()) {
       dict->Set("list_element_index",
-                static_cast<int>(attributes.list_element_attributes().index()));
+                static_cast<int>(
+                    attributes.list_element_attributes()->index().value()));
     }
 
-    if (attributes.has_is_keyboard_key())
-      dict->Set("is_keyboard_key", attributes.is_keyboard_key());
+    if (attributes.is_keyboard_key()) {
+      dict->Set("is_keyboard_key", attributes.is_keyboard_key().value());
+    }
   }
 
-  if (fuchsia_node.has_states()) {
-    const fuchsia::accessibility::semantics::States& states =
-        fuchsia_node.states();
+  if (fuchsia_node.states()) {
+    const fuchsia_accessibility_semantics::States& states =
+        fuchsia_node.states().value();
 
-    if (states.has_selected())
-      dict->Set("selected", states.selected());
-
-    if (states.has_checked_state()) {
-      dict->Set("checked_state", CheckedStateToString(states.checked_state()));
+    if (states.selected()) {
+      dict->Set("selected", states.selected().value());
     }
 
-    if (states.has_hidden())
-      dict->Set("hidden", states.hidden());
+    if (states.checked_state()) {
+      dict->Set("checked_state",
+                CheckedStateToString(states.checked_state().value()));
+    }
 
-    if (states.has_value() && !states.value().empty())
-      dict->Set("value", states.value());
+    if (states.hidden()) {
+      dict->Set("hidden", states.hidden().value());
+    }
 
-    if (states.has_viewport_offset()) {
+    if (states.value() && !states.value()->empty()) {
+      dict->Set("value", states.value().value());
+    }
+
+    if (states.viewport_offset()) {
       dict->Set("viewport_offset",
-                ViewportOffsetToString(states.viewport_offset()));
+                ViewportOffsetToString(states.viewport_offset().value()));
     }
 
-    if (states.has_toggled_state()) {
-      dict->Set("toggled_state", ToggledStateToString(states.toggled_state()));
+    if (states.toggled_state()) {
+      dict->Set("toggled_state",
+                ToggledStateToString(states.toggled_state().value()));
     }
 
-    if (states.has_focusable())
-      dict->Set("focusable", states.focusable());
+    if (states.focusable()) {
+      dict->Set("focusable", states.focusable().value());
+    }
 
-    if (states.has_has_input_focus())
-      dict->Set("has_input_focus", states.has_input_focus());
+    if (states.has_input_focus()) {
+      dict->Set("has_input_focus", states.has_input_focus().value());
+    }
   }
 
-  if (fuchsia_node.has_location())
-    dict->Set("location", LocationToString(fuchsia_node.location()));
+  if (fuchsia_node.location()) {
+    dict->Set("location", LocationToString(fuchsia_node.location().value()));
+  }
 
-  if (fuchsia_node.has_transform()) {
+  if (fuchsia_node.transform()) {
     dict->Set("transform",
-              Mat4ToString(fuchsia_node.node_to_container_transform()));
+              Mat4ToString(fuchsia_node.node_to_container_transform().value()));
   }
 }
 
diff --git a/content/browser/accessibility/browser_accessibility_fuchsia.cc b/content/browser/accessibility/browser_accessibility_fuchsia.cc
index a0f6ba15..c2f033c1 100644
--- a/content/browser/accessibility/browser_accessibility_fuchsia.cc
+++ b/content/browser/accessibility/browser_accessibility_fuchsia.cc
@@ -4,7 +4,7 @@
 
 #include "content/browser/accessibility/browser_accessibility_fuchsia.h"
 
-#include <lib/ui/scenic/cpp/commands.h>
+#include <fidl/fuchsia.accessibility.semantics/cpp/hlcpp_conversion.h>
 
 #include "base/fuchsia/fuchsia_logging.h"
 #include "content/browser/accessibility/browser_accessibility_manager_fuchsia.h"
@@ -15,7 +15,7 @@
 namespace content {
 
 using AXRole = ax::mojom::Role;
-using FuchsiaRole = fuchsia::accessibility::semantics::Role;
+using FuchsiaRole = fuchsia_accessibility_semantics::Role;
 
 BrowserAccessibilityFuchsia::BrowserAccessibilityFuchsia(
     BrowserAccessibilityManager* manager,
@@ -50,21 +50,19 @@
   return static_cast<uint32_t>(GetUniqueId());
 }
 
-fuchsia::accessibility::semantics::Node
+fuchsia_accessibility_semantics::Node
 BrowserAccessibilityFuchsia::ToFuchsiaNodeData() const {
-  fuchsia::accessibility::semantics::Node fuchsia_node_data;
-
-  fuchsia_node_data.set_node_id(GetFuchsiaNodeID());
-  fuchsia_node_data.set_role(GetFuchsiaRole());
-  fuchsia_node_data.set_states(GetFuchsiaStates());
-  fuchsia_node_data.set_attributes(GetFuchsiaAttributes());
-  fuchsia_node_data.set_actions(GetFuchsiaActions());
-  fuchsia_node_data.set_location(GetFuchsiaLocation());
-  fuchsia_node_data.set_node_to_container_transform(GetFuchsiaTransform());
-  fuchsia_node_data.set_container_id(GetOffsetContainerOrRootNodeID());
-  fuchsia_node_data.set_child_ids(GetFuchsiaChildIDs());
-
-  return fuchsia_node_data;
+  return {{
+      .node_id = GetFuchsiaNodeID(),
+      .role = GetFuchsiaRole(),
+      .states = GetFuchsiaStates(),
+      .attributes = GetFuchsiaAttributes(),
+      .actions = GetFuchsiaActions(),
+      .child_ids = GetFuchsiaChildIDs(),
+      .location = GetFuchsiaLocation(),
+      .container_id = GetOffsetContainerOrRootNodeID(),
+      .node_to_container_transform = GetFuchsiaTransform(),
+  }};
 }
 
 void BrowserAccessibilityFuchsia::OnDataChanged() {
@@ -107,104 +105,103 @@
   return child_ids;
 }
 
-std::vector<fuchsia::accessibility::semantics::Action>
+std::vector<fuchsia_accessibility_semantics::Action>
 BrowserAccessibilityFuchsia::GetFuchsiaActions() const {
-  std::vector<fuchsia::accessibility::semantics::Action> actions;
+  std::vector<fuchsia_accessibility_semantics::Action> actions;
 
   if (HasAction(ax::mojom::Action::kDoDefault) ||
       GetData().GetDefaultActionVerb() != ax::mojom::DefaultActionVerb::kNone) {
-    actions.push_back(fuchsia::accessibility::semantics::Action::DEFAULT);
+    actions.push_back(fuchsia_accessibility_semantics::Action::kDefault);
   }
 
   if (HasAction(ax::mojom::Action::kFocus))
-    actions.push_back(fuchsia::accessibility::semantics::Action::SET_FOCUS);
+    actions.push_back(fuchsia_accessibility_semantics::Action::kSetFocus);
 
   if (HasAction(ax::mojom::Action::kSetValue))
-    actions.push_back(fuchsia::accessibility::semantics::Action::SET_VALUE);
+    actions.push_back(fuchsia_accessibility_semantics::Action::kSetValue);
 
   if (HasAction(ax::mojom::Action::kScrollToMakeVisible)) {
-    actions.push_back(
-        fuchsia::accessibility::semantics::Action::SHOW_ON_SCREEN);
+    actions.push_back(fuchsia_accessibility_semantics::Action::kShowOnScreen);
   }
 
   return actions;
 }
 
-fuchsia::accessibility::semantics::Role
+fuchsia_accessibility_semantics::Role
 BrowserAccessibilityFuchsia::GetFuchsiaRole() const {
   auto role = GetRole();
 
   switch (role) {
     case AXRole::kButton:
-      return FuchsiaRole::BUTTON;
+      return FuchsiaRole::kButton;
     case AXRole::kCell:
-      return FuchsiaRole::CELL;
+      return FuchsiaRole::kCell;
     case AXRole::kCheckBox:
-      return FuchsiaRole::CHECK_BOX;
+      return FuchsiaRole::kCheckBox;
     case AXRole::kColumnHeader:
-      return FuchsiaRole::COLUMN_HEADER;
+      return FuchsiaRole::kColumnHeader;
     case AXRole::kGrid:
-      return FuchsiaRole::GRID;
+      return FuchsiaRole::kGrid;
     case AXRole::kHeader:
-      return FuchsiaRole::HEADER;
+      return FuchsiaRole::kHeader;
     case AXRole::kImage:
-      return FuchsiaRole::IMAGE;
+      return FuchsiaRole::kImage;
     case AXRole::kLink:
-      return FuchsiaRole::LINK;
+      return FuchsiaRole::kLink;
     case AXRole::kList:
-      return FuchsiaRole::LIST;
+      return FuchsiaRole::kList;
     case AXRole::kListItem:
-      return FuchsiaRole::LIST_ELEMENT;
+      return FuchsiaRole::kListElement;
     case AXRole::kListMarker:
-      return FuchsiaRole::LIST_ELEMENT_MARKER;
+      return FuchsiaRole::kListElementMarker;
     case AXRole::kParagraph:
-      return FuchsiaRole::PARAGRAPH;
+      return FuchsiaRole::kParagraph;
     case AXRole::kRadioButton:
-      return FuchsiaRole::RADIO_BUTTON;
+      return FuchsiaRole::kRadioButton;
     case AXRole::kRowGroup:
-      return FuchsiaRole::ROW_GROUP;
+      return FuchsiaRole::kRowGroup;
     case AXRole::kSearchBox:
-      return FuchsiaRole::SEARCH_BOX;
+      return FuchsiaRole::kSearchBox;
     case AXRole::kSlider:
-      return FuchsiaRole::SLIDER;
+      return FuchsiaRole::kSlider;
     case AXRole::kStaticText:
-      return FuchsiaRole::STATIC_TEXT;
+      return FuchsiaRole::kStaticText;
     case AXRole::kTable:
-      return FuchsiaRole::TABLE;
+      return FuchsiaRole::kTable;
     case AXRole::kRow:
-      return FuchsiaRole::TABLE_ROW;
+      return FuchsiaRole::kTableRow;
     case AXRole::kTextField:
-      return FuchsiaRole::TEXT_FIELD;
+      return FuchsiaRole::kTextField;
     case AXRole::kTextFieldWithComboBox:
-      return FuchsiaRole::TEXT_FIELD_WITH_COMBO_BOX;
+      return FuchsiaRole::kTextFieldWithComboBox;
     default:
-      return FuchsiaRole::UNKNOWN;
+      return FuchsiaRole::kUnknown;
   }
 }
 
-fuchsia::accessibility::semantics::States
+fuchsia_accessibility_semantics::States
 BrowserAccessibilityFuchsia::GetFuchsiaStates() const {
-  fuchsia::accessibility::semantics::States states;
+  fuchsia_accessibility_semantics::States states;
 
   // Convert checked state.
   if (HasIntAttribute(ax::mojom::IntAttribute::kCheckedState)) {
     ax::mojom::CheckedState ax_state = GetData().GetCheckedState();
     switch (ax_state) {
       case ax::mojom::CheckedState::kNone:
-        states.set_checked_state(
-            fuchsia::accessibility::semantics::CheckedState::NONE);
+        states.checked_state(
+            fuchsia_accessibility_semantics::CheckedState::kNone);
         break;
       case ax::mojom::CheckedState::kTrue:
-        states.set_checked_state(
-            fuchsia::accessibility::semantics::CheckedState::CHECKED);
+        states.checked_state(
+            fuchsia_accessibility_semantics::CheckedState::kChecked);
         break;
       case ax::mojom::CheckedState::kFalse:
-        states.set_checked_state(
-            fuchsia::accessibility::semantics::CheckedState::UNCHECKED);
+        states.checked_state(
+            fuchsia_accessibility_semantics::CheckedState::kUnchecked);
         break;
       case ax::mojom::CheckedState::kMixed:
-        states.set_checked_state(
-            fuchsia::accessibility::semantics::CheckedState::MIXED);
+        states.checked_state(
+            fuchsia_accessibility_semantics::CheckedState::kMixed);
         break;
     }
   }
@@ -213,23 +210,23 @@
   // Indicates whether a node has been selected.
   if (GetData().IsSelectable() &&
       HasBoolAttribute(ax::mojom::BoolAttribute::kSelected)) {
-    states.set_selected(GetBoolAttribute(ax::mojom::BoolAttribute::kSelected));
+    states.selected(GetBoolAttribute(ax::mojom::BoolAttribute::kSelected));
   }
 
   // Indicates if the node is hidden.
-  states.set_hidden(IsInvisibleOrIgnored());
+  states.hidden(IsInvisibleOrIgnored());
 
   // The user entered value of the node, if applicable.
   if (HasStringAttribute(ax::mojom::StringAttribute::kValue)) {
     const std::string& value =
         GetStringAttribute(ax::mojom::StringAttribute::kValue);
-    states.set_value(
-        value.substr(0, fuchsia::accessibility::semantics::MAX_LABEL_SIZE));
+    states.value(
+        value.substr(0, fuchsia_accessibility_semantics::kMaxLabelSize));
   }
 
   // The value a range element currently has.
   if (HasFloatAttribute(ax::mojom::FloatAttribute::kValueForRange)) {
-    states.set_range_value(
+    states.range_value(
         GetFloatAttribute(ax::mojom::FloatAttribute::kValueForRange));
   }
 
@@ -239,133 +236,136 @@
   const float y_scroll_offset =
       GetIntAttribute(ax::mojom::IntAttribute::kScrollY);
   if (x_scroll_offset || y_scroll_offset)
-    states.set_viewport_offset({x_scroll_offset, y_scroll_offset});
+    states.viewport_offset({{x_scroll_offset, y_scroll_offset}});
 
   if (IsFocusable())
-    states.set_focusable(true);
+    states.focusable(true);
 
-  states.set_has_input_focus(IsFocused());
+  states.has_input_focus(IsFocused());
 
   return states;
 }
 
-fuchsia::accessibility::semantics::Attributes
+fuchsia_accessibility_semantics::Attributes
 BrowserAccessibilityFuchsia::GetFuchsiaAttributes() const {
-  fuchsia::accessibility::semantics::Attributes attributes;
+  fuchsia_accessibility_semantics::Attributes attributes;
   if (HasStringAttribute(ax::mojom::StringAttribute::kName)) {
     const std::string& name =
         GetStringAttribute(ax::mojom::StringAttribute::kName);
-    attributes.set_label(
-        name.substr(0, fuchsia::accessibility::semantics::MAX_LABEL_SIZE));
+    attributes.label(
+        name.substr(0, fuchsia_accessibility_semantics::kMaxLabelSize));
   }
 
   if (HasStringAttribute(ax::mojom::StringAttribute::kDescription)) {
     const std::string& description =
         GetStringAttribute(ax::mojom::StringAttribute::kDescription);
-    attributes.set_secondary_label(description.substr(
-        0, fuchsia::accessibility::semantics::MAX_LABEL_SIZE));
+    attributes.secondary_label(
+        description.substr(0, fuchsia_accessibility_semantics::kMaxLabelSize));
   }
 
   if (GetData().IsRangeValueSupported()) {
-    fuchsia::accessibility::semantics::RangeAttributes range_attributes;
+    fuchsia_accessibility_semantics::RangeAttributes range_attributes;
     if (HasFloatAttribute(ax::mojom::FloatAttribute::kMinValueForRange)) {
-      range_attributes.set_min_value(
+      range_attributes.min_value(
           GetFloatAttribute(ax::mojom::FloatAttribute::kMinValueForRange));
     }
     if (HasFloatAttribute(ax::mojom::FloatAttribute::kMaxValueForRange)) {
-      range_attributes.set_max_value(
+      range_attributes.max_value(
           GetFloatAttribute(ax::mojom::FloatAttribute::kMaxValueForRange));
     }
     if (HasFloatAttribute(ax::mojom::FloatAttribute::kStepValueForRange)) {
-      range_attributes.set_step_delta(
+      range_attributes.step_delta(
           GetFloatAttribute(ax::mojom::FloatAttribute::kStepValueForRange));
     }
-    attributes.set_range(std::move(range_attributes));
+    attributes.range(std::move(range_attributes));
   }
 
   if (IsTable()) {
-    fuchsia::accessibility::semantics::TableAttributes table_attributes;
+    fuchsia_accessibility_semantics::TableAttributes table_attributes;
     auto col_count = GetTableColCount();
     if (col_count)
-      table_attributes.set_number_of_columns(*col_count);
+      table_attributes.number_of_columns(*col_count);
 
     auto row_count = GetTableRowCount();
     if (row_count)
-      table_attributes.set_number_of_rows(*row_count);
+      table_attributes.number_of_rows(*row_count);
 
     if (!table_attributes.IsEmpty())
-      attributes.set_table_attributes(std::move(table_attributes));
+      attributes.table_attributes(std::move(table_attributes));
   }
 
   if (IsTableRow()) {
-    fuchsia::accessibility::semantics::TableRowAttributes table_row_attributes;
+    fuchsia_accessibility_semantics::TableRowAttributes table_row_attributes;
     auto row_index = GetTableRowRowIndex();
     if (row_index) {
-      table_row_attributes.set_row_index(*row_index);
-      attributes.set_table_row_attributes(std::move(table_row_attributes));
+      table_row_attributes.row_index(*row_index);
+      attributes.table_row_attributes(std::move(table_row_attributes));
     }
   }
 
   if (IsTableCellOrHeader()) {
-    fuchsia::accessibility::semantics::TableCellAttributes
-        table_cell_attributes;
+    fuchsia_accessibility_semantics::TableCellAttributes table_cell_attributes;
 
     auto col_index = GetTableCellColIndex();
     if (col_index)
-      table_cell_attributes.set_column_index(*col_index);
+      table_cell_attributes.column_index(*col_index);
 
     auto row_index = GetTableCellRowIndex();
     if (row_index)
-      table_cell_attributes.set_row_index(*row_index);
+      table_cell_attributes.row_index(*row_index);
 
     auto col_span = GetTableCellColSpan();
     if (col_span)
-      table_cell_attributes.set_column_span(*col_span);
+      table_cell_attributes.column_span(*col_span);
 
     auto row_span = GetTableCellRowSpan();
     if (row_span)
-      table_cell_attributes.set_row_span(*row_span);
+      table_cell_attributes.row_span(*row_span);
 
     if (!table_cell_attributes.IsEmpty())
-      attributes.set_table_cell_attributes(std::move(table_cell_attributes));
+      attributes.table_cell_attributes(std::move(table_cell_attributes));
   }
 
   if (IsList()) {
     absl::optional<int> size = GetSetSize();
     if (size) {
-      fuchsia::accessibility::semantics::SetAttributes list_attributes;
-      list_attributes.set_size(*size);
-      attributes.set_list_attributes(std::move(list_attributes));
+      fuchsia_accessibility_semantics::SetAttributes list_attributes;
+      list_attributes.size(*size);
+      attributes.list_attributes(std::move(list_attributes));
     }
   }
 
   if (IsListElement()) {
     absl::optional<int> index = GetPosInSet();
     if (index) {
-      fuchsia::accessibility::semantics::SetAttributes list_element_attributes;
-      list_element_attributes.set_index(*index);
-      attributes.set_list_element_attributes(
-          std::move(list_element_attributes));
+      fuchsia_accessibility_semantics::SetAttributes list_element_attributes;
+      list_element_attributes.index(*index);
+      attributes.list_element_attributes(std::move(list_element_attributes));
     }
   }
 
   return attributes;
 }
 
-fuchsia::ui::gfx::BoundingBox BrowserAccessibilityFuchsia::GetFuchsiaLocation()
+fuchsia_ui_gfx::BoundingBox BrowserAccessibilityFuchsia::GetFuchsiaLocation()
     const {
   const gfx::RectF& bounds = GetLocation();
 
-  fuchsia::ui::gfx::BoundingBox box;
-  // Since the origin is at the top left, min should represent the top left and
-  // max should be the bottom right.
-  box.min = scenic::NewVector3({bounds.x(), bounds.y(), 0.0f});
-  box.max = scenic::NewVector3({bounds.right(), bounds.bottom(), 0.0f});
-  return box;
+  return {{
+      .min = {{
+          .x = bounds.x(),
+          .y = bounds.y(),
+          .z = 0.0f,
+      }},
+      .max = {{
+          .x = bounds.right(),
+          .y = bounds.bottom(),
+          .z = 0.0f,
+      }},
+  }};
 }
 
-fuchsia::ui::gfx::mat4 BrowserAccessibilityFuchsia::GetFuchsiaTransform()
-    const {
+fuchsia_ui_gfx::Mat4 BrowserAccessibilityFuchsia::GetFuchsiaTransform() const {
   // Get AXNode's explicit transform.
   gfx::Transform transform;
   if (GetData().relative_bounds.transform)
@@ -374,9 +374,7 @@
   // Convert to fuchsia's transform type.
   std::array<float, 16> mat = {};
   transform.GetColMajorF(mat.data());
-  fuchsia::ui::gfx::Matrix4Value fuchsia_transform =
-      scenic::NewMatrix4Value(mat);
-  return fuchsia_transform.value;
+  return {{.matrix = mat}};
 }
 
 uint32_t BrowserAccessibilityFuchsia::GetOffsetContainerOrRootNodeID() const {
@@ -405,7 +403,8 @@
   if (!GetAccessibilityBridge())
     return;
 
-  GetAccessibilityBridge()->UpdateNode(ToFuchsiaNodeData());
+  GetAccessibilityBridge()->UpdateNode(
+      fidl::NaturalToHLCPP(ToFuchsiaNodeData()));
 }
 
 void BrowserAccessibilityFuchsia::DeleteNode() {
diff --git a/content/browser/accessibility/browser_accessibility_fuchsia.h b/content/browser/accessibility/browser_accessibility_fuchsia.h
index 347ff00..b069f72 100644
--- a/content/browser/accessibility/browser_accessibility_fuchsia.h
+++ b/content/browser/accessibility/browser_accessibility_fuchsia.h
@@ -5,7 +5,7 @@
 #ifndef CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_FUCHSIA_H_
 #define CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_FUCHSIA_H_
 
-#include <fuchsia/accessibility/semantics/cpp/fidl.h>
+#include <fidl/fuchsia.accessibility.semantics/cpp/fidl.h>
 
 #include <vector>
 
@@ -36,7 +36,7 @@
 
   // Returns the fuchsia representation of the AXNode to which this
   // BrowserAccessibility object refers.
-  fuchsia::accessibility::semantics::Node ToFuchsiaNodeData() const;
+  fuchsia_accessibility_semantics::Node ToFuchsiaNodeData() const;
 
   // Returns the fuchsia ID of this node's offset container if the offset
   // container ID is valid. Otherwise, returns the ID of this tree's root node.
@@ -58,13 +58,13 @@
 
   void UpdateNode();
   void DeleteNode();
-  std::vector<fuchsia::accessibility::semantics::Action> GetFuchsiaActions()
+  std::vector<fuchsia_accessibility_semantics::Action> GetFuchsiaActions()
       const;
-  fuchsia::accessibility::semantics::Role GetFuchsiaRole() const;
-  fuchsia::accessibility::semantics::States GetFuchsiaStates() const;
-  fuchsia::accessibility::semantics::Attributes GetFuchsiaAttributes() const;
-  fuchsia::ui::gfx::BoundingBox GetFuchsiaLocation() const;
-  fuchsia::ui::gfx::mat4 GetFuchsiaTransform() const;
+  fuchsia_accessibility_semantics::Role GetFuchsiaRole() const;
+  fuchsia_accessibility_semantics::States GetFuchsiaStates() const;
+  fuchsia_accessibility_semantics::Attributes GetFuchsiaAttributes() const;
+  fuchsia_ui_gfx::BoundingBox GetFuchsiaLocation() const;
+  fuchsia_ui_gfx::Mat4 GetFuchsiaTransform() const;
   std::vector<uint32_t> GetFuchsiaChildIDs() const;
 
   // Returns true if this AXNode has role AXRole::kList.
diff --git a/content/browser/accessibility/browser_accessibility_fuchsia_unittest.cc b/content/browser/accessibility/browser_accessibility_fuchsia_unittest.cc
index 29e7667f3..eb1bffa 100644
--- a/content/browser/accessibility/browser_accessibility_fuchsia_unittest.cc
+++ b/content/browser/accessibility/browser_accessibility_fuchsia_unittest.cc
@@ -4,7 +4,7 @@
 
 #include "content/browser/accessibility/browser_accessibility_fuchsia.h"
 
-#include <fuchsia/accessibility/semantics/cpp/fidl.h>
+#include <fidl/fuchsia.accessibility.semantics/cpp/fidl.h>
 #include <lib/ui/scenic/cpp/commands.h>
 
 #include <map>
@@ -20,7 +20,7 @@
 namespace {
 
 using AXRole = ax::mojom::Role;
-using fuchsia::accessibility::semantics::Role;
+using fuchsia_accessibility_semantics::Role;
 
 constexpr int32_t kRootId = 182;
 constexpr int32_t kRowNodeId1 = 2;
@@ -141,28 +141,28 @@
 }
 
 TEST_F(BrowserAccessibilityFuchsiaTest, ToFuchsiaNodeDataTranslatesRoles) {
-  std::map<ax::mojom::Role, fuchsia::accessibility::semantics::Role>
+  std::map<ax::mojom::Role, fuchsia_accessibility_semantics::Role>
       role_mapping = {
-          {AXRole::kButton, Role::BUTTON},
-          {AXRole::kCheckBox, Role::CHECK_BOX},
-          {AXRole::kHeader, Role::HEADER},
-          {AXRole::kImage, Role::IMAGE},
-          {AXRole::kLink, Role::LINK},
-          {AXRole::kList, Role::LIST},
-          {AXRole::kListItem, Role::LIST_ELEMENT},
-          {AXRole::kListMarker, Role::LIST_ELEMENT_MARKER},
-          {AXRole::kRadioButton, Role::RADIO_BUTTON},
-          {AXRole::kSlider, Role::SLIDER},
-          {AXRole::kTextField, Role::TEXT_FIELD},
-          {AXRole::kSearchBox, Role::SEARCH_BOX},
-          {AXRole::kTextFieldWithComboBox, Role::TEXT_FIELD_WITH_COMBO_BOX},
-          {AXRole::kTable, Role::TABLE},
-          {AXRole::kGrid, Role::GRID},
-          {AXRole::kRow, Role::TABLE_ROW},
-          {AXRole::kCell, Role::CELL},
-          {AXRole::kColumnHeader, Role::COLUMN_HEADER},
-          {AXRole::kRowGroup, Role::ROW_GROUP},
-          {AXRole::kParagraph, Role::PARAGRAPH}};
+          {AXRole::kButton, Role::kButton},
+          {AXRole::kCheckBox, Role::kCheckBox},
+          {AXRole::kHeader, Role::kHeader},
+          {AXRole::kImage, Role::kImage},
+          {AXRole::kLink, Role::kLink},
+          {AXRole::kList, Role::kList},
+          {AXRole::kListItem, Role::kListElement},
+          {AXRole::kListMarker, Role::kListElementMarker},
+          {AXRole::kRadioButton, Role::kRadioButton},
+          {AXRole::kSlider, Role::kSlider},
+          {AXRole::kTextField, Role::kTextField},
+          {AXRole::kSearchBox, Role::kSearchBox},
+          {AXRole::kTextFieldWithComboBox, Role::kTextFieldWithComboBox},
+          {AXRole::kTable, Role::kTable},
+          {AXRole::kGrid, Role::kGrid},
+          {AXRole::kRow, Role::kTableRow},
+          {AXRole::kCell, Role::kCell},
+          {AXRole::kColumnHeader, Role::kColumnHeader},
+          {AXRole::kRowGroup, Role::kRowGroup},
+          {AXRole::kParagraph, Role::kParagraph}};
 
   for (const auto& role_pair : role_mapping) {
     ui::AXNodeData node;
@@ -186,16 +186,16 @@
 
 TEST_F(BrowserAccessibilityFuchsiaTest,
        ToFuchsiaNodeDataTranslatesNodeActions) {
-  std::map<ax::mojom::Action, fuchsia::accessibility::semantics::Action>
+  std::map<ax::mojom::Action, fuchsia_accessibility_semantics::Action>
       action_mapping = {
           {ax::mojom::Action::kDoDefault,
-           fuchsia::accessibility::semantics::Action::DEFAULT},
+           fuchsia_accessibility_semantics::Action::kDefault},
           {ax::mojom::Action::kFocus,
-           fuchsia::accessibility::semantics::Action::SET_FOCUS},
+           fuchsia_accessibility_semantics::Action::kSetFocus},
           {ax::mojom::Action::kSetValue,
-           fuchsia::accessibility::semantics::Action::SET_VALUE},
+           fuchsia_accessibility_semantics::Action::kSetValue},
           {ax::mojom::Action::kScrollToMakeVisible,
-           fuchsia::accessibility::semantics::Action::SHOW_ON_SCREEN}};
+           fuchsia_accessibility_semantics::Action::kShowOnScreen}};
 
   for (const auto& action_pair : action_mapping) {
     ui::AXNodeData node;
@@ -213,7 +213,7 @@
     ASSERT_TRUE(browser_accessibility_fuchsia);
     auto fuchsia_node_data = browser_accessibility_fuchsia->ToFuchsiaNodeData();
 
-    const auto& actions = fuchsia_node_data.actions();
+    const auto& actions = fuchsia_node_data.actions().value();
     ASSERT_EQ(actions.size(), 1u);
     EXPECT_EQ(actions[0], action_pair.second);
   }
@@ -240,10 +240,11 @@
   ASSERT_TRUE(browser_accessibility_fuchsia);
   auto fuchsia_node_data = browser_accessibility_fuchsia->ToFuchsiaNodeData();
 
-  ASSERT_TRUE(fuchsia_node_data.has_attributes());
-  ASSERT_TRUE(fuchsia_node_data.attributes().has_label());
-  EXPECT_EQ(fuchsia_node_data.attributes().label(), kLabel);
-  EXPECT_EQ(fuchsia_node_data.attributes().secondary_label(), kSecondaryLabel);
+  ASSERT_TRUE(fuchsia_node_data.attributes().has_value());
+  ASSERT_TRUE(fuchsia_node_data.attributes()->label().has_value());
+  EXPECT_EQ(fuchsia_node_data.attributes()->label().value(), kLabel);
+  EXPECT_EQ(fuchsia_node_data.attributes()->secondary_label().value(),
+            kSecondaryLabel);
 }
 
 TEST_F(BrowserAccessibilityFuchsiaTest,
@@ -272,15 +273,16 @@
   ASSERT_TRUE(browser_accessibility_fuchsia);
   auto fuchsia_node_data = browser_accessibility_fuchsia->ToFuchsiaNodeData();
 
-  ASSERT_TRUE(fuchsia_node_data.has_attributes());
-  ASSERT_TRUE(fuchsia_node_data.attributes().has_range());
-  const auto& range_attributes = fuchsia_node_data.attributes().range();
+  ASSERT_TRUE(fuchsia_node_data.attributes().has_value());
+  ASSERT_TRUE(fuchsia_node_data.attributes()->range().has_value());
+  const auto& range_attributes =
+      fuchsia_node_data.attributes()->range().value();
   EXPECT_EQ(range_attributes.min_value(), kMin);
   EXPECT_EQ(range_attributes.max_value(), kMax);
   EXPECT_EQ(range_attributes.step_delta(), kStep);
-  EXPECT_TRUE(fuchsia_node_data.has_states());
-  EXPECT_TRUE(fuchsia_node_data.states().has_range_value());
-  EXPECT_EQ(fuchsia_node_data.states().range_value(), kValue);
+  EXPECT_TRUE(fuchsia_node_data.states());
+  EXPECT_TRUE(fuchsia_node_data.states()->range_value().has_value());
+  EXPECT_EQ(fuchsia_node_data.states()->range_value().value(), kValue);
 }
 
 TEST_F(BrowserAccessibilityFuchsiaTest,
@@ -298,13 +300,14 @@
     auto fuchsia_node_data = browser_accessibility_fuchsia->ToFuchsiaNodeData();
 
     EXPECT_EQ(fuchsia_node_data.role(),
-              fuchsia::accessibility::semantics::Role::TABLE);
-    ASSERT_TRUE(fuchsia_node_data.has_attributes());
-    ASSERT_TRUE(fuchsia_node_data.attributes().has_table_attributes());
+              fuchsia_accessibility_semantics::Role::kTable);
+    ASSERT_TRUE(fuchsia_node_data.attributes().has_value());
+    ASSERT_TRUE(fuchsia_node_data.attributes()->table_attributes().has_value());
     EXPECT_EQ(
-        fuchsia_node_data.attributes().table_attributes().number_of_rows(), 2u);
+        fuchsia_node_data.attributes()->table_attributes()->number_of_rows(),
+        2u);
     EXPECT_EQ(
-        fuchsia_node_data.attributes().table_attributes().number_of_columns(),
+        fuchsia_node_data.attributes()->table_attributes()->number_of_columns(),
         2u);
   }
 
@@ -317,11 +320,13 @@
     auto fuchsia_node_data = browser_accessibility_fuchsia->ToFuchsiaNodeData();
 
     EXPECT_EQ(fuchsia_node_data.role(),
-              fuchsia::accessibility::semantics::Role::TABLE_ROW);
-    ASSERT_TRUE(fuchsia_node_data.has_attributes());
-    ASSERT_TRUE(fuchsia_node_data.attributes().has_table_row_attributes());
-    EXPECT_EQ(fuchsia_node_data.attributes().table_row_attributes().row_index(),
-              1u);
+              fuchsia_accessibility_semantics::Role::kTableRow);
+    ASSERT_TRUE(fuchsia_node_data.attributes().has_value());
+    ASSERT_TRUE(
+        fuchsia_node_data.attributes()->table_row_attributes().has_value());
+    EXPECT_EQ(
+        fuchsia_node_data.attributes()->table_row_attributes()->row_index(),
+        1u);
   }
 
   // Verify table cell node translation.
@@ -333,13 +338,15 @@
     auto fuchsia_node_data = browser_accessibility_fuchsia->ToFuchsiaNodeData();
 
     EXPECT_EQ(fuchsia_node_data.role(),
-              fuchsia::accessibility::semantics::Role::CELL);
-    ASSERT_TRUE(fuchsia_node_data.has_attributes());
-    ASSERT_TRUE(fuchsia_node_data.attributes().has_table_cell_attributes());
+              fuchsia_accessibility_semantics::Role::kCell);
+    ASSERT_TRUE(fuchsia_node_data.attributes().has_value());
+    ASSERT_TRUE(
+        fuchsia_node_data.attributes()->table_cell_attributes().has_value());
     EXPECT_EQ(
-        fuchsia_node_data.attributes().table_cell_attributes().row_index(), 1u);
+        fuchsia_node_data.attributes()->table_cell_attributes()->row_index(),
+        1u);
     EXPECT_EQ(
-        fuchsia_node_data.attributes().table_cell_attributes().column_index(),
+        fuchsia_node_data.attributes()->table_cell_attributes()->column_index(),
         1u);
   }
 }
@@ -357,11 +364,12 @@
     ASSERT_TRUE(browser_accessibility_fuchsia);
     auto fuchsia_node_data = browser_accessibility_fuchsia->ToFuchsiaNodeData();
     EXPECT_EQ(fuchsia_node_data.role(),
-              fuchsia::accessibility::semantics::Role::LIST);
-    ASSERT_TRUE(fuchsia_node_data.has_attributes());
-    ASSERT_TRUE(fuchsia_node_data.attributes().has_list_attributes());
-    ASSERT_FALSE(fuchsia_node_data.attributes().has_list_element_attributes());
-    EXPECT_EQ(fuchsia_node_data.attributes().list_attributes().size(), 2u);
+              fuchsia_accessibility_semantics::Role::kList);
+    ASSERT_TRUE(fuchsia_node_data.attributes().has_value());
+    ASSERT_TRUE(fuchsia_node_data.attributes()->list_attributes().has_value());
+    ASSERT_FALSE(
+        fuchsia_node_data.attributes()->list_element_attributes().has_value());
+    EXPECT_EQ(fuchsia_node_data.attributes()->list_attributes()->size(), 2u);
   }
 
   // Verify that the list elements were translated.
@@ -371,28 +379,29 @@
     ASSERT_TRUE(browser_accessibility_fuchsia);
     auto fuchsia_node_data = browser_accessibility_fuchsia->ToFuchsiaNodeData();
     EXPECT_EQ(fuchsia_node_data.role(),
-              fuchsia::accessibility::semantics::Role::LIST_ELEMENT);
-    ASSERT_TRUE(fuchsia_node_data.has_attributes());
-    ASSERT_FALSE(fuchsia_node_data.attributes().has_list_attributes());
-    ASSERT_TRUE(fuchsia_node_data.attributes().has_list_element_attributes());
-    EXPECT_EQ(fuchsia_node_data.attributes().list_element_attributes().index(),
-              2u);
+              fuchsia_accessibility_semantics::Role::kListElement);
+    ASSERT_TRUE(fuchsia_node_data.attributes().has_value());
+    ASSERT_FALSE(fuchsia_node_data.attributes()->list_attributes().has_value());
+    ASSERT_TRUE(
+        fuchsia_node_data.attributes()->list_element_attributes().has_value());
+    EXPECT_EQ(
+        fuchsia_node_data.attributes()->list_element_attributes()->index(), 2u);
   }
 }
 
 TEST_F(BrowserAccessibilityFuchsiaTest,
        ToFuchsiaNodeDataTranslatesCheckedState) {
   std::map<ax::mojom::CheckedState,
-           fuchsia::accessibility::semantics::CheckedState>
+           fuchsia_accessibility_semantics::CheckedState>
       state_mapping = {
           {ax::mojom::CheckedState::kNone,
-           fuchsia::accessibility::semantics::CheckedState::NONE},
+           fuchsia_accessibility_semantics::CheckedState::kNone},
           {ax::mojom::CheckedState::kTrue,
-           fuchsia::accessibility::semantics::CheckedState::CHECKED},
+           fuchsia_accessibility_semantics::CheckedState::kChecked},
           {ax::mojom::CheckedState::kFalse,
-           fuchsia::accessibility::semantics::CheckedState::UNCHECKED},
+           fuchsia_accessibility_semantics::CheckedState::kUnchecked},
           {ax::mojom::CheckedState::kMixed,
-           fuchsia::accessibility::semantics::CheckedState::MIXED}};
+           fuchsia_accessibility_semantics::CheckedState::kMixed}};
 
   for (const auto state_pair : state_mapping) {
     ui::AXNodeData node;
@@ -411,9 +420,10 @@
     ASSERT_TRUE(browser_accessibility_fuchsia);
     auto fuchsia_node_data = browser_accessibility_fuchsia->ToFuchsiaNodeData();
 
-    ASSERT_TRUE(fuchsia_node_data.has_states());
-    ASSERT_TRUE(fuchsia_node_data.states().has_checked_state());
-    EXPECT_EQ(fuchsia_node_data.states().checked_state(), state_pair.second);
+    ASSERT_TRUE(fuchsia_node_data.states().has_value());
+    ASSERT_TRUE(fuchsia_node_data.states()->checked_state().has_value());
+    EXPECT_EQ(fuchsia_node_data.states()->checked_state().value(),
+              state_pair.second);
   }
 }
 
@@ -434,9 +444,9 @@
   ASSERT_TRUE(browser_accessibility_fuchsia);
   auto fuchsia_node_data = browser_accessibility_fuchsia->ToFuchsiaNodeData();
 
-  ASSERT_TRUE(fuchsia_node_data.has_states());
-  EXPECT_TRUE(fuchsia_node_data.states().has_selected());
-  EXPECT_TRUE(fuchsia_node_data.states().selected());
+  ASSERT_TRUE(fuchsia_node_data.states().has_value());
+  EXPECT_TRUE(fuchsia_node_data.states()->selected().has_value());
+  EXPECT_TRUE(fuchsia_node_data.states()->selected().value());
 }
 
 TEST_F(BrowserAccessibilityFuchsiaTest,
@@ -456,9 +466,9 @@
   ASSERT_TRUE(browser_accessibility_fuchsia);
   auto fuchsia_node_data = browser_accessibility_fuchsia->ToFuchsiaNodeData();
 
-  ASSERT_TRUE(fuchsia_node_data.has_states());
-  ASSERT_TRUE(fuchsia_node_data.states().has_hidden());
-  EXPECT_TRUE(fuchsia_node_data.states().hidden());
+  ASSERT_TRUE(fuchsia_node_data.states().has_value());
+  ASSERT_TRUE(fuchsia_node_data.states()->hidden().has_value());
+  EXPECT_TRUE(fuchsia_node_data.states()->hidden().value());
 }
 
 TEST_F(BrowserAccessibilityFuchsiaTest,
@@ -478,16 +488,16 @@
   ASSERT_TRUE(browser_accessibility_fuchsia);
   auto fuchsia_node_data = browser_accessibility_fuchsia->ToFuchsiaNodeData();
 
-  ASSERT_TRUE(fuchsia_node_data.has_states());
-  ASSERT_TRUE(fuchsia_node_data.states().has_hidden());
-  EXPECT_TRUE(fuchsia_node_data.states().hidden());
+  ASSERT_TRUE(fuchsia_node_data.states().has_value());
+  ASSERT_TRUE(fuchsia_node_data.states()->hidden().has_value());
+  EXPECT_TRUE(fuchsia_node_data.states()->hidden().value());
 }
 
 TEST_F(BrowserAccessibilityFuchsiaTest, ToFuchsiaNodeDataTranslatesValue) {
   const auto full_value =
-      std::string(fuchsia::accessibility::semantics::MAX_LABEL_SIZE + 1, 'a');
+      std::string(fuchsia_accessibility_semantics::kMaxLabelSize + 1, 'a');
   const auto truncated_value =
-      std::string(fuchsia::accessibility::semantics::MAX_LABEL_SIZE, 'a');
+      std::string(fuchsia_accessibility_semantics::kMaxLabelSize, 'a');
 
   ui::AXNodeData node;
   node.id = kRootId;
@@ -504,9 +514,8 @@
   ASSERT_TRUE(browser_accessibility_fuchsia);
   auto fuchsia_node_data = browser_accessibility_fuchsia->ToFuchsiaNodeData();
 
-  ASSERT_TRUE(fuchsia_node_data.has_states());
   ASSERT_TRUE(fuchsia_node_data.states().has_value());
-  EXPECT_EQ(fuchsia_node_data.states().value(), truncated_value);
+  EXPECT_EQ(fuchsia_node_data.states()->value(), truncated_value);
 }
 
 TEST_F(BrowserAccessibilityFuchsiaTest,
@@ -530,11 +539,12 @@
   ASSERT_TRUE(browser_accessibility_fuchsia);
   auto fuchsia_node_data = browser_accessibility_fuchsia->ToFuchsiaNodeData();
 
-  ASSERT_TRUE(fuchsia_node_data.has_states());
-  ASSERT_TRUE(fuchsia_node_data.states().has_viewport_offset());
-  const auto& viewport_offset = fuchsia_node_data.states().viewport_offset();
-  EXPECT_EQ(viewport_offset.x, kScrollX);
-  EXPECT_EQ(viewport_offset.y, kScrollY);
+  ASSERT_TRUE(fuchsia_node_data.states().has_value());
+  ASSERT_TRUE(fuchsia_node_data.states()->viewport_offset().has_value());
+  const auto& viewport_offset =
+      fuchsia_node_data.states()->viewport_offset().value();
+  EXPECT_EQ(viewport_offset.x(), kScrollX);
+  EXPECT_EQ(viewport_offset.y(), kScrollY);
 }
 
 TEST_F(BrowserAccessibilityFuchsiaTest,
@@ -570,21 +580,21 @@
   ASSERT_TRUE(browser_accessibility_fuchsia);
   auto fuchsia_node_data = browser_accessibility_fuchsia->ToFuchsiaNodeData();
 
-  ASSERT_TRUE(fuchsia_node_data.has_node_to_container_transform());
+  ASSERT_TRUE(fuchsia_node_data.node_to_container_transform().has_value());
   const auto& transform =
-      fuchsia_node_data.node_to_container_transform().matrix;
+      fuchsia_node_data.node_to_container_transform()->matrix();
   EXPECT_EQ(transform[0], x_scale);
   EXPECT_EQ(transform[5], y_scale);
   EXPECT_EQ(transform[10], z_scale);
   EXPECT_EQ(transform[12], x_translation);
   EXPECT_EQ(transform[13], y_translation);
   EXPECT_EQ(transform[14], z_translation);
-  ASSERT_TRUE(fuchsia_node_data.has_location());
-  const auto& location = fuchsia_node_data.location();
-  EXPECT_EQ(location.min.x, x_min);
-  EXPECT_EQ(location.min.y, y_min);
-  EXPECT_EQ(location.max.x, x_max);
-  EXPECT_EQ(location.max.y, y_max);
+  ASSERT_TRUE(fuchsia_node_data.location().has_value());
+  const auto& location = fuchsia_node_data.location().value();
+  EXPECT_EQ(location.min().x(), x_min);
+  EXPECT_EQ(location.min().y(), y_min);
+  EXPECT_EQ(location.max().x(), x_max);
+  EXPECT_EQ(location.max().y(), y_max);
 }
 
 TEST_F(BrowserAccessibilityFuchsiaTest,
@@ -611,16 +621,17 @@
       ToBrowserAccessibilityFuchsia(manager->GetFromID(2));
   ASSERT_TRUE(child);
   auto child_node_data = child->ToFuchsiaNodeData();
-  ASSERT_TRUE(child_node_data.has_container_id());
-  EXPECT_EQ(child_node_data.container_id(), root->GetFuchsiaNodeID());
+  ASSERT_TRUE(child_node_data.container_id().has_value());
+  EXPECT_EQ(child_node_data.container_id().value(), root->GetFuchsiaNodeID());
 
   // Verify that node 3's offset container was translated correctly.
   BrowserAccessibilityFuchsia* grandchild =
       ToBrowserAccessibilityFuchsia(manager->GetFromID(3));
   ASSERT_TRUE(grandchild);
   auto grandchild_node_data = grandchild->ToFuchsiaNodeData();
-  ASSERT_TRUE(grandchild_node_data.has_container_id());
-  EXPECT_EQ(grandchild_node_data.container_id(), child->GetFuchsiaNodeID());
+  ASSERT_TRUE(grandchild_node_data.container_id().has_value());
+  EXPECT_EQ(grandchild_node_data.container_id().value(),
+            child->GetFuchsiaNodeID());
 }
 
 TEST_F(BrowserAccessibilityFuchsiaTest,
@@ -641,10 +652,10 @@
       ToBrowserAccessibilityFuchsia(manager->GetFromID(2));
   ASSERT_TRUE(child);
   auto child_node_data = child->ToFuchsiaNodeData();
-  ASSERT_TRUE(child_node_data.has_container_id());
+  ASSERT_TRUE(child_node_data.container_id().has_value());
   // Offset container ID should default to 0 if the specified node doesn't
   // exist.
-  EXPECT_EQ(child_node_data.container_id(), 0u);
+  EXPECT_EQ(child_node_data.container_id().value(), 0u);
 }
 
 TEST_F(BrowserAccessibilityFuchsiaTest,
@@ -677,9 +688,11 @@
   ASSERT_TRUE(child_1);
   ASSERT_TRUE(child_2);
   EXPECT_EQ(fuchsia_node_data.node_id(), root->GetFuchsiaNodeID());
-  ASSERT_EQ(fuchsia_node_data.child_ids().size(), 2u);
-  EXPECT_EQ(fuchsia_node_data.child_ids()[0], child_1->GetFuchsiaNodeID());
-  EXPECT_EQ(fuchsia_node_data.child_ids()[1], child_2->GetFuchsiaNodeID());
+  ASSERT_EQ(fuchsia_node_data.child_ids()->size(), 2u);
+  EXPECT_EQ(fuchsia_node_data.child_ids().value()[0],
+            child_1->GetFuchsiaNodeID());
+  EXPECT_EQ(fuchsia_node_data.child_ids().value()[1],
+            child_2->GetFuchsiaNodeID());
 }
 
 TEST_F(BrowserAccessibilityFuchsiaTest, ChildTree) {
@@ -717,7 +730,7 @@
 
   {
     ASSERT_TRUE(browser_accessibility_fuchsia);
-    fuchsia::accessibility::semantics::Node fuchsia_node_data =
+    fuchsia_accessibility_semantics::Node fuchsia_node_data =
         browser_accessibility_fuchsia->ToFuchsiaNodeData();
 
     // Get the root of the child tree to verify that it's present in the parent
@@ -725,8 +738,9 @@
     BrowserAccessibilityFuchsia* child_root = ToBrowserAccessibilityFuchsia(
         child_manager->GetBrowserAccessibilityRoot());
 
-    ASSERT_EQ(fuchsia_node_data.child_ids().size(), 1u);
-    EXPECT_EQ(fuchsia_node_data.child_ids()[0], child_root->GetFuchsiaNodeID());
+    ASSERT_EQ(fuchsia_node_data.child_ids()->size(), 1u);
+    EXPECT_EQ(fuchsia_node_data.child_ids().value()[0],
+              child_root->GetFuchsiaNodeID());
   }
 
   // Destroy the child tree, and ensure that the parent fuchsia node's child IDs
@@ -735,10 +749,10 @@
 
   {
     ASSERT_TRUE(browser_accessibility_fuchsia);
-    fuchsia::accessibility::semantics::Node fuchsia_node_data =
+    fuchsia_accessibility_semantics::Node fuchsia_node_data =
         browser_accessibility_fuchsia->ToFuchsiaNodeData();
 
-    EXPECT_TRUE(fuchsia_node_data.child_ids().empty());
+    EXPECT_TRUE(fuchsia_node_data.child_ids()->empty());
   }
 }
 
@@ -762,7 +776,7 @@
   ASSERT_TRUE(browser_accessibility_fuchsia);
   auto fuchsia_node_data = browser_accessibility_fuchsia->ToFuchsiaNodeData();
 
-  EXPECT_TRUE(fuchsia_node_data.child_ids().empty());
+  EXPECT_TRUE(fuchsia_node_data.child_ids()->empty());
 }
 
 TEST_F(BrowserAccessibilityFuchsiaTest, GetFuchsiaNodeIDNonRootTree) {
diff --git a/content/browser/attribution_reporting/attribution_beacon_id.h b/content/browser/attribution_reporting/attribution_beacon_id.h
index ad1aa7a5..f41cd77 100644
--- a/content/browser/attribution_reporting/attribution_beacon_id.h
+++ b/content/browser/attribution_reporting/attribution_beacon_id.h
@@ -8,14 +8,10 @@
 #include <stdint.h>
 
 #include "base/types/strong_alias.h"
-#include "third_party/abseil-cpp/absl/types/variant.h"
 
 namespace content {
 
-using EventBeaconId = base::StrongAlias<struct EventBeaconTag, int64_t>;
-using NavigationBeaconId =
-    base::StrongAlias<struct NavigationBeaconTag, int64_t>;
-using BeaconId = absl::variant<EventBeaconId, NavigationBeaconId>;
+using BeaconId = base::StrongAlias<struct BeaconTag, int64_t>;
 
 }  // namespace content
 
diff --git a/content/browser/attribution_reporting/attribution_data_host_manager.h b/content/browser/attribution_reporting/attribution_data_host_manager.h
index e2f4cae..75f1b36 100644
--- a/content/browser/attribution_reporting/attribution_data_host_manager.h
+++ b/content/browser/attribution_reporting/attribution_data_host_manager.h
@@ -5,10 +5,13 @@
 #ifndef CONTENT_BROWSER_ATTRIBUTION_REPORTING_ATTRIBUTION_DATA_HOST_MANAGER_H_
 #define CONTENT_BROWSER_ATTRIBUTION_REPORTING_ATTRIBUTION_DATA_HOST_MANAGER_H_
 
+#include <stdint.h>
+
 #include "base/memory/weak_ptr.h"
 #include "components/attribution_reporting/registration_type.mojom-forward.h"
 #include "content/browser/attribution_reporting/attribution_beacon_id.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/blink/public/common/tokens/tokens.h"
 #include "third_party/blink/public/mojom/conversions/attribution_data_host.mojom-forward.h"
 #include "third_party/blink/public/mojom/conversions/attribution_reporting.mojom-forward.h"
@@ -94,8 +97,11 @@
   // RFHI was destroyed, therefore we need to store the information for later
   // use. Passes the topmost ancestor of the initiator render frame for
   // obtaining the page access report.
+  // `navigation_id` is the id of the navigation for automatic beacons and
+  // `absl::nullopt` for event beacons.
   virtual void NotifyFencedFrameReportingBeaconStarted(
       BeaconId beacon_id,
+      absl::optional<int64_t> navigation_id,
       attribution_reporting::SuitableOrigin source_origin,
       bool is_within_fenced_frame,
       AttributionInputEvent input_event,
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 d785a21..db77edfe1 100644
--- a/content/browser/attribution_reporting/attribution_data_host_manager_impl.cc
+++ b/content/browser/attribution_reporting/attribution_data_host_manager_impl.cc
@@ -37,6 +37,7 @@
 #include "components/attribution_reporting/trigger_registration.h"
 #include "content/browser/attribution_reporting/attribution_beacon_id.h"
 #include "content/browser/attribution_reporting/attribution_constants.h"
+#include "content/browser/attribution_reporting/attribution_features.h"
 #include "content/browser/attribution_reporting/attribution_input_event.h"
 #include "content/browser/attribution_reporting/attribution_manager.h"
 #include "content/browser/attribution_reporting/attribution_trigger.h"
@@ -235,7 +236,12 @@
     AttributionNavigationType nav_type;
   };
 
-  using Data = absl::variant<NavigationRedirect, BeaconId>;
+  struct Beacon {
+    BeaconId id;
+    absl::optional<int64_t> navigation_id;
+  };
+
+  using Data = absl::variant<NavigationRedirect, Beacon>;
 
   SourceRegistrations(SuitableOrigin source_origin,
                       bool is_within_fenced_frame,
@@ -302,8 +308,8 @@
             [](const NavigationRedirect& redirect) {
               return SourceRegistrationsId(redirect.attribution_src_token);
             },
-            [](const BeaconId& beacon_id) {
-              return SourceRegistrationsId(beacon_id);
+            [](const Beacon& beacon) {
+              return SourceRegistrationsId(beacon.id);
             },
         },
         data_);
@@ -795,13 +801,18 @@
 
 void AttributionDataHostManagerImpl::NotifyFencedFrameReportingBeaconStarted(
     BeaconId beacon_id,
+    absl::optional<int64_t> navigation_id,
     SuitableOrigin source_origin,
     bool is_within_fenced_frame,
     AttributionInputEvent input_event,
     GlobalRenderFrameHostId render_frame_id) {
-  auto [it, inserted] = registrations_.emplace(
-      std::move(source_origin), is_within_fenced_frame, std::move(input_event),
-      render_frame_id, beacon_id);
+  auto [it, inserted] =
+      registrations_.emplace(std::move(source_origin), is_within_fenced_frame,
+                             std::move(input_event), render_frame_id,
+                             SourceRegistrations::Beacon{
+                                 .id = beacon_id,
+                                 .navigation_id = navigation_id,
+                             });
   DCHECK(inserted);
 
   // Treat ongoing beacon registrations as a data host for the purpose of
@@ -819,8 +830,10 @@
     bool is_final_response) {
   auto it = registrations_.find(beacon_id);
 
-  // The registration may no longer be tracked in the event the navigation
-  // failed.
+  if (base::FeatureList::IsEnabled(kAttributionFencedFrameReportingBeacon)) {
+    DCHECK(it != registrations_.end());
+  }
+
   if (it == registrations_.end()) {
     return;
   }
@@ -855,12 +868,7 @@
     SourceRegistrationsId id,
     base::FunctionRef<void(const SourceRegistrations&)> handle_result) {
   auto it = registrations_.find(id);
-
-  // The registration may no longer be tracked in the event the navigation
-  // failed.
-  if (it == registrations_.end()) {
-    return;
-  }
+  DCHECK(it != registrations_.end());
 
   it->DecrementPendingSourceData();
   handle_result(*it);
@@ -874,8 +882,9 @@
     data_decoder::DataDecoder::ValueOrError result) {
   OnSourceParsed(id, [&](const SourceRegistrations& registrations) {
     auto source_type = SourceType::kNavigation;
-    if (const auto* beacon_id = absl::get_if<BeaconId>(&registrations.data());
-        beacon_id && absl::holds_alternative<EventBeaconId>(*beacon_id)) {
+    if (const auto* beacon =
+            absl::get_if<SourceRegistrations::Beacon>(&registrations.data());
+        beacon && !beacon->navigation_id.has_value()) {
       source_type = SourceType::kEvent;
     }
 
diff --git a/content/browser/attribution_reporting/attribution_data_host_manager_impl.h b/content/browser/attribution_reporting/attribution_data_host_manager_impl.h
index 2b22f40..c9b577a3 100644
--- a/content/browser/attribution_reporting/attribution_data_host_manager_impl.h
+++ b/content/browser/attribution_reporting/attribution_data_host_manager_impl.h
@@ -104,6 +104,7 @@
       const blink::AttributionSrcToken& attribution_src_token) override;
   void NotifyFencedFrameReportingBeaconStarted(
       BeaconId beacon_id,
+      absl::optional<int64_t> navigation_id,
       attribution_reporting::SuitableOrigin source_origin,
       bool is_within_fenced_frame,
       AttributionInputEvent input_event,
diff --git a/content/browser/attribution_reporting/attribution_data_host_manager_impl_unittest.cc b/content/browser/attribution_reporting/attribution_data_host_manager_impl_unittest.cc
index f39ac41..c316919 100644
--- a/content/browser/attribution_reporting/attribution_data_host_manager_impl_unittest.cc
+++ b/content/browser/attribution_reporting/attribution_data_host_manager_impl_unittest.cc
@@ -100,6 +100,9 @@
 
 const GlobalRenderFrameHostId kFrameId = {0, 1};
 
+constexpr BeaconId kBeaconId(123);
+constexpr int64_t kNavigationId(456);
+
 struct ExpectedTriggerQueueEventCounts {
   base::HistogramBase::Count skipped_queue = 0;
   base::HistogramBase::Count dropped = 0;
@@ -1667,24 +1670,23 @@
   auto reporting_origin = url::Origin::Create(GURL("https://report.test"));
   auto source_origin = *SuitableOrigin::Deserialize("https://source.test");
 
-  NavigationBeaconId navigation_id(123);
   data_host_manager_.NotifyFencedFrameReportingBeaconStarted(
-      navigation_id, std::move(source_origin), /*is_within_fenced_frame=*/false,
-      AttributionInputEvent(), kFrameId);
+      kBeaconId, kNavigationId, std::move(source_origin),
+      /*is_within_fenced_frame=*/false, AttributionInputEvent(), kFrameId);
 
   auto headers = base::MakeRefCounted<net::HttpResponseHeaders>("");
   headers->SetHeader(kAttributionReportingRegisterSourceHeader,
                      kRegisterSourceJson);
 
   data_host_manager_.NotifyFencedFrameReportingBeaconData(
-      navigation_id, reporting_origin, headers.get(),
+      kBeaconId, reporting_origin, headers.get(),
       /*is_final_response=*/false);
 
   // Wait for parsing to finish.
   task_environment_.FastForwardBy(base::TimeDelta());
 
   data_host_manager_.NotifyFencedFrameReportingBeaconData(
-      navigation_id, std::move(reporting_origin), headers.get(),
+      kBeaconId, std::move(reporting_origin), headers.get(),
       /*is_final_response=*/true);
 
   data_host_manager_.NotifyNavigationFailure(blink::AttributionSrcToken());
@@ -1700,17 +1702,16 @@
   auto reporting_origin = url::Origin::Create(GURL("https://report.test"));
   auto source_origin = *SuitableOrigin::Deserialize("https://source.test");
 
-  NavigationBeaconId navigation_id(123);
   data_host_manager_.NotifyFencedFrameReportingBeaconStarted(
-      navigation_id, std::move(source_origin), /*is_within_fenced_frame=*/false,
-      AttributionInputEvent(), kFrameId);
+      kBeaconId, kNavigationId, std::move(source_origin),
+      /*is_within_fenced_frame=*/false, AttributionInputEvent(), kFrameId);
 
   auto headers = base::MakeRefCounted<net::HttpResponseHeaders>("");
   headers->SetHeader(kAttributionReportingRegisterSourceHeader,
                      kRegisterSourceJson);
 
   data_host_manager_.NotifyFencedFrameReportingBeaconData(
-      navigation_id, reporting_origin, headers.get(),
+      kBeaconId, reporting_origin, headers.get(),
       /*is_final_response=*/false);
 
   // Wait for parsing to finish.
@@ -1719,7 +1720,7 @@
   data_host_manager_.NotifyNavigationFailure(blink::AttributionSrcToken());
 
   data_host_manager_.NotifyFencedFrameReportingBeaconData(
-      navigation_id, std::move(reporting_origin), headers.get(),
+      kBeaconId, std::move(reporting_origin), headers.get(),
       /*is_final_response=*/true);
 
   // Wait for parsing to finish.
@@ -1737,9 +1738,8 @@
   auto reporting_origin = url::Origin::Create(GURL("https://report.test"));
   auto source_origin = *SuitableOrigin::Deserialize("https://source.test");
 
-  NavigationBeaconId navigation_id(123);
   data_host_manager_.NotifyFencedFrameReportingBeaconStarted(
-      navigation_id, source_origin, /*is_within_fenced_frame=*/false,
+      kBeaconId, kNavigationId, source_origin, /*is_within_fenced_frame=*/false,
       AttributionInputEvent(), kFrameId);
 
   auto headers = base::MakeRefCounted<net::HttpResponseHeaders>("");
@@ -1747,14 +1747,14 @@
                      kRegisterSourceJson);
 
   data_host_manager_.NotifyFencedFrameReportingBeaconData(
-      navigation_id, reporting_origin, headers.get(),
+      kBeaconId, reporting_origin, headers.get(),
       /*is_final_response=*/false);
 
   // Wait for parsing to finish.
   task_environment_.FastForwardBy(base::TimeDelta());
 
   data_host_manager_.NotifyFencedFrameReportingBeaconData(
-      navigation_id, std::move(reporting_origin), headers.get(),
+      kBeaconId, std::move(reporting_origin), headers.get(),
       /*is_final_response=*/true);
 
   // This is irrelevant to beacon source registrations.
@@ -1788,9 +1788,8 @@
                   kFrameId))
       .Times(2);
 
-  NavigationBeaconId navigation_id(123);
   data_host_manager_.NotifyFencedFrameReportingBeaconStarted(
-      navigation_id, source_origin, /*is_within_fenced_frame=*/false,
+      kBeaconId, kNavigationId, source_origin, /*is_within_fenced_frame=*/false,
       AttributionInputEvent(), kFrameId);
 
   auto headers = base::MakeRefCounted<net::HttpResponseHeaders>("");
@@ -1798,14 +1797,14 @@
                      R"("https://r.test/x")");
 
   data_host_manager_.NotifyFencedFrameReportingBeaconData(
-      navigation_id, reporting_origin, headers.get(),
+      kBeaconId, reporting_origin, headers.get(),
       /*is_final_response=*/false);
 
   // Wait for parsing to finish.
   task_environment_.FastForwardBy(base::TimeDelta());
 
   data_host_manager_.NotifyFencedFrameReportingBeaconData(
-      navigation_id, std::move(reporting_origin), headers.get(),
+      kBeaconId, std::move(reporting_origin), headers.get(),
       /*is_final_response=*/true);
 
   // This is irrelevant to beacon source registrations.
@@ -1833,9 +1832,8 @@
                                  SourceRegistrationError::kInvalidJson))
       .Times(2);
 
-  NavigationBeaconId navigation_id(123);
   data_host_manager_.NotifyFencedFrameReportingBeaconStarted(
-      navigation_id, source_origin, /*is_within_fenced_frame=*/false,
+      kBeaconId, kNavigationId, source_origin, /*is_within_fenced_frame=*/false,
       AttributionInputEvent(), kFrameId);
 
   auto headers = base::MakeRefCounted<net::HttpResponseHeaders>("");
@@ -1843,14 +1841,14 @@
                      "!!!invalid json");
 
   data_host_manager_.NotifyFencedFrameReportingBeaconData(
-      navigation_id, reporting_origin, headers.get(),
+      kBeaconId, reporting_origin, headers.get(),
       /*is_final_response=*/false);
 
   // Wait for parsing to finish.
   task_environment_.FastForwardBy(base::TimeDelta());
 
   data_host_manager_.NotifyFencedFrameReportingBeaconData(
-      navigation_id, reporting_origin, headers.get(),
+      kBeaconId, reporting_origin, headers.get(),
       /*is_final_response=*/true);
 
   // This is irrelevant to beacon source registrations.
@@ -1872,9 +1870,8 @@
   auto reporting_origin = url::Origin::Create(GURL("https://report.test"));
   auto source_origin = *SuitableOrigin::Deserialize("https://source.test");
 
-  NavigationBeaconId navigation_id(123);
   data_host_manager_.NotifyFencedFrameReportingBeaconStarted(
-      navigation_id, source_origin, /*is_within_fenced_frame=*/false,
+      kBeaconId, kNavigationId, source_origin, /*is_within_fenced_frame=*/false,
       AttributionInputEvent(), kFrameId);
 
   auto headers = base::MakeRefCounted<net::HttpResponseHeaders>("");
@@ -1882,7 +1879,7 @@
                      kRegisterSourceJson);
 
   data_host_manager_.NotifyFencedFrameReportingBeaconData(
-      navigation_id, reporting_origin, headers.get(),
+      kBeaconId, reporting_origin, headers.get(),
       /*is_final_response=*/false);
 
   // Wait for parsing to finish.
@@ -1895,7 +1892,7 @@
       /*is_within_fenced_frame=*/false, kFrameId);
 
   data_host_manager_.NotifyFencedFrameReportingBeaconData(
-      navigation_id, std::move(reporting_origin), headers.get(),
+      kBeaconId, std::move(reporting_origin), headers.get(),
       /*is_final_response=*/true);
 
   // Wait for parsing to finish.
@@ -1908,9 +1905,8 @@
 
   auto source_origin = *SuitableOrigin::Deserialize("https://source.test");
 
-  NavigationBeaconId navigation_id(123);
   data_host_manager_.NotifyFencedFrameReportingBeaconStarted(
-      navigation_id, source_origin,
+      kBeaconId, kNavigationId, source_origin,
       /*is_within_fenced_frame=*/false, AttributionInputEvent(), kFrameId);
 
   auto headers = base::MakeRefCounted<net::HttpResponseHeaders>("");
@@ -1918,7 +1914,7 @@
                      kRegisterSourceJson);
 
   data_host_manager_.NotifyFencedFrameReportingBeaconData(
-      navigation_id,
+      kBeaconId,
       /*reporting_origin=*/url::Origin::Create(GURL("http://insecure.test")),
       headers.get(),
       /*is_final_response=*/true);
@@ -1946,9 +1942,8 @@
     EXPECT_CALL(mock_manager_, HandleTrigger);
   }
 
-  NavigationBeaconId navigation_id(123);
   data_host_manager_.NotifyFencedFrameReportingBeaconStarted(
-      navigation_id,
+      kBeaconId, kNavigationId,
       /*source_origin=*/*SuitableOrigin::Deserialize("https://report.test"),
       /*is_within_fenced_frame=*/false, AttributionInputEvent(), kFrameId);
 
@@ -1997,21 +1992,20 @@
   auto reporting_origin = url::Origin::Create(GURL("https://report.test"));
   auto source_origin = *SuitableOrigin::Deserialize("https://source.test");
 
-  NavigationBeaconId navigation_id(123);
   data_host_manager_.NotifyFencedFrameReportingBeaconStarted(
-      navigation_id, std::move(source_origin), /*is_within_fenced_frame=*/false,
-      AttributionInputEvent(), kFrameId);
+      kBeaconId, kNavigationId, std::move(source_origin),
+      /*is_within_fenced_frame=*/false, AttributionInputEvent(), kFrameId);
 
   auto headers = base::MakeRefCounted<net::HttpResponseHeaders>("");
   headers->SetHeader(kAttributionReportingRegisterSourceHeader,
                      kRegisterSourceJson);
 
   data_host_manager_.NotifyFencedFrameReportingBeaconData(
-      navigation_id, reporting_origin, headers.get(),
+      kBeaconId, reporting_origin, headers.get(),
       /*is_final_response=*/false);
 
   data_host_manager_.NotifyFencedFrameReportingBeaconData(
-      navigation_id, reporting_origin, headers.get(),
+      kBeaconId, reporting_origin, headers.get(),
       /*is_final_response=*/true);
 
   // Wait for parsing.
@@ -2043,13 +2037,12 @@
   auto reporting_origin = url::Origin::Create(GURL("https://report.test"));
   auto source_origin = *SuitableOrigin::Deserialize("https://source.test");
 
-  NavigationBeaconId navigation_id(123);
   data_host_manager_.NotifyFencedFrameReportingBeaconStarted(
-      navigation_id, std::move(source_origin), /*is_within_fenced_frame=*/false,
-      AttributionInputEvent(), kFrameId);
+      kBeaconId, kNavigationId, std::move(source_origin),
+      /*is_within_fenced_frame=*/false, AttributionInputEvent(), kFrameId);
 
   data_host_manager_.NotifyFencedFrameReportingBeaconData(
-      navigation_id, /*reporting_origin=*/url::Origin(), /*headers=*/nullptr,
+      kBeaconId, /*reporting_origin=*/url::Origin(), /*headers=*/nullptr,
       /*is_final_response=*/true);
 
   mojo::Remote<blink::mojom::AttributionDataHost> trigger_data_host_remote;
@@ -2073,9 +2066,8 @@
                                  SourceIsWithinFencedFrameIs(true)),
                            kFrameId));
 
-  EventBeaconId event_id(123);
   data_host_manager_.NotifyFencedFrameReportingBeaconStarted(
-      event_id,
+      kBeaconId, /*navigation_id=*/absl::nullopt,
       /*source_origin=*/*SuitableOrigin::Deserialize("https://source.test"),
       /*is_within_fenced_frame=*/true,
       /*input_event=*/AttributionInputEvent(), kFrameId);
@@ -2085,7 +2077,7 @@
                      kRegisterSourceJson);
 
   data_host_manager_.NotifyFencedFrameReportingBeaconData(
-      event_id,
+      kBeaconId,
       /*reporting_origin=*/url::Origin::Create(GURL("https://report.test")),
       headers.get(),
       /*is_final_response=*/true);
diff --git a/content/browser/attribution_reporting/attribution_host.cc b/content/browser/attribution_reporting/attribution_host.cc
index c406e21..9c0d16cd 100644
--- a/content/browser/attribution_reporting/attribution_host.cc
+++ b/content/browser/attribution_reporting/attribution_host.cc
@@ -4,6 +4,8 @@
 
 #include "content/browser/attribution_reporting/attribution_host.h"
 
+#include <stdint.h>
+
 #include <utility>
 
 #include "base/check.h"
@@ -33,6 +35,7 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/content_client.h"
 #include "mojo/public/cpp/bindings/message.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/abseil-cpp/absl/types/variant.h"
 #include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/common/navigation/impression.h"
@@ -418,6 +421,7 @@
 
 void AttributionHost::NotifyFencedFrameReportingBeaconStarted(
     BeaconId beacon_id,
+    absl::optional<int64_t> navigation_id,
     RenderFrameHostImpl* initiator_frame_host) {
   if (!base::FeatureList::IsEnabled(kAttributionFencedFrameReportingBeacon)) {
     return;
@@ -454,14 +458,14 @@
   }
 
   AttributionInputEvent input_event;
-  if (absl::holds_alternative<NavigationBeaconId>(beacon_id)) {
+  if (navigation_id.has_value()) {
     input_event = AttributionHost::FromWebContents(
                       WebContents::FromRenderFrameHost(initiator_frame_host))
                       ->GetMostRecentNavigationInputEvent();
   }
 
   data_host_manager->NotifyFencedFrameReportingBeaconStarted(
-      beacon_id, std::move(*initiator_root_frame_origin),
+      beacon_id, navigation_id, std::move(*initiator_root_frame_origin),
       initiator_frame_host->IsNestedWithinFencedFrame(), input_event,
       initiator_root_frame->GetGlobalId());
 }
diff --git a/content/browser/attribution_reporting/attribution_host.h b/content/browser/attribution_reporting/attribution_host.h
index b4dfb60..ba4e72a 100644
--- a/content/browser/attribution_reporting/attribution_host.h
+++ b/content/browser/attribution_reporting/attribution_host.h
@@ -68,8 +68,13 @@
   // navigation beacon.
   // This function should only be invoked if Attribution Reporting API is
   // enabled on the page.
+  // `navigation_id` will be set if this beacon is being sent as the result of a
+  // top navigation initiated by a fenced frame. This is used to track
+  // attributions that occur on a navigated page after the current page has been
+  // unloaded. Otherwise `absl::nullopt`.
   void NotifyFencedFrameReportingBeaconStarted(
       BeaconId beacon_id,
+      absl::optional<int64_t> navigation_id,
       RenderFrameHostImpl* initiator_frame_host);
 
  private:
diff --git a/content/browser/attribution_reporting/attribution_host_unittest.cc b/content/browser/attribution_reporting/attribution_host_unittest.cc
index bc114848..33eaa38 100644
--- a/content/browser/attribution_reporting/attribution_host_unittest.cc
+++ b/content/browser/attribution_reporting/attribution_host_unittest.cc
@@ -4,6 +4,8 @@
 
 #include "content/browser/attribution_reporting/attribution_host.h"
 
+#include <stdint.h>
+
 #include <memory>
 #include <vector>
 
@@ -34,6 +36,7 @@
 #include "mojo/public/cpp/test_support/test_utils.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/common/permissions_policy/origin_with_possible_wildcards.h"
 #include "third_party/blink/public/common/permissions_policy/permissions_policy_declaration.h"
@@ -63,13 +66,15 @@
 using ::testing::_;
 using ::testing::Optional;
 using ::testing::Return;
-using ::testing::VariantWith;
 
 using ::attribution_reporting::mojom::RegistrationType;
 using ::blink::mojom::AttributionNavigationType;
 
 const char kConversionUrl[] = "https://b.com";
 
+constexpr BeaconId kBeaconId(123);
+constexpr int64_t kNavigationId(456);
+
 class AttributionHostTest : public RenderViewHostTestHarness {
  public:
   AttributionHostTest() = default;
@@ -515,7 +520,8 @@
       GURL("https://fencedframe.example"), fenced_frame);
 
   conversion_host()->NotifyFencedFrameReportingBeaconStarted(
-      NavigationBeaconId(123), static_cast<RenderFrameHostImpl*>(fenced_frame));
+      kBeaconId, kNavigationId,
+      static_cast<RenderFrameHostImpl*>(fenced_frame));
 }
 
 TEST_F(AttributionHostTest, NotifyFencedFrameReportingBeaconStarted) {
@@ -533,15 +539,13 @@
       {"https:/secure.com", true},
   };
 
-  NavigationBeaconId navigation_id(123);
-
   for (const auto& test_case : kTestCases) {
     contents()->NavigateAndCommit(GURL(test_case.source_origin));
     if (test_case.expected_valid) {
       EXPECT_CALL(
           *mock_data_host_manager(),
           NotifyFencedFrameReportingBeaconStarted(
-              VariantWith<NavigationBeaconId>(navigation_id),
+              kBeaconId, Optional(kNavigationId),
               *SuitableOrigin::Deserialize(test_case.source_origin),
               /*is_within_fenced_frame=*/true, _, main_rfh()->GetGlobalId()));
     } else {
@@ -559,7 +563,8 @@
         GURL("https://fencedframe.example"), fenced_frame);
 
     conversion_host()->NotifyFencedFrameReportingBeaconStarted(
-        navigation_id, static_cast<RenderFrameHostImpl*>(fenced_frame));
+        kBeaconId, kNavigationId,
+        static_cast<RenderFrameHostImpl*>(fenced_frame));
   }
 }
 
@@ -606,7 +611,8 @@
     fenced_frame = simulator->GetFinalRenderFrameHost();
 
     conversion_host()->NotifyFencedFrameReportingBeaconStarted(
-        EventBeaconId(123), static_cast<RenderFrameHostImpl*>(fenced_frame));
+        kBeaconId, /*navigation_id=*/absl::nullopt,
+        static_cast<RenderFrameHostImpl*>(fenced_frame));
   }
 }
 
diff --git a/content/browser/attribution_reporting/test/mock_attribution_data_host_manager.h b/content/browser/attribution_reporting/test/mock_attribution_data_host_manager.h
index db3ee55..64bdc09a 100644
--- a/content/browser/attribution_reporting/test/mock_attribution_data_host_manager.h
+++ b/content/browser/attribution_reporting/test/mock_attribution_data_host_manager.h
@@ -5,6 +5,8 @@
 #ifndef CONTENT_BROWSER_ATTRIBUTION_REPORTING_TEST_MOCK_ATTRIBUTION_DATA_HOST_MANAGER_H_
 #define CONTENT_BROWSER_ATTRIBUTION_REPORTING_TEST_MOCK_ATTRIBUTION_DATA_HOST_MANAGER_H_
 
+#include <stdint.h>
+
 #include "components/attribution_reporting/registration_type.mojom-forward.h"
 #include "components/attribution_reporting/suitable_origin.h"
 #include "content/browser/attribution_reporting/attribution_beacon_id.h"
@@ -13,6 +15,7 @@
 #include "content/public/browser/global_routing_id.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "testing/gmock/include/gmock/gmock.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/blink/public/common/tokens/tokens.h"
 #include "third_party/blink/public/mojom/conversions/attribution_data_host.mojom-forward.h"
 #include "third_party/blink/public/mojom/conversions/attribution_reporting.mojom-forward.h"
@@ -75,6 +78,7 @@
   MOCK_METHOD(void,
               NotifyFencedFrameReportingBeaconStarted,
               (BeaconId beacon_id,
+               absl::optional<int64_t> navigation_id,
                attribution_reporting::SuitableOrigin source_origin,
                bool is_within_fenced_frame,
                AttributionInputEvent input_event,
diff --git a/content/browser/fenced_frame/fenced_frame_browsertest.cc b/content/browser/fenced_frame/fenced_frame_browsertest.cc
index ebbd36a..3becac58 100644
--- a/content/browser/fenced_frame/fenced_frame_browsertest.cc
+++ b/content/browser/fenced_frame/fenced_frame_browsertest.cc
@@ -16,6 +16,7 @@
 #include "base/time/time.h"
 #include "components/network_session_configurator/common/network_switches.h"
 #include "components/ukm/test_ukm_recorder.h"
+#include "content/browser/attribution_reporting/attribution_features.h"
 #include "content/browser/attribution_reporting/attribution_manager.h"
 #include "content/browser/back_forward_cache_browsertest.h"
 #include "content/browser/fenced_frame/fenced_frame.h"
@@ -4549,7 +4550,8 @@
   // supporting iframes.
   FencedFrameReportEventBrowserTest() {
     scoped_feature_list_.InitWithFeaturesAndParameters(
-        {{blink::features::kAllowURNsInIframes, {}}},
+        {{blink::features::kAllowURNsInIframes, {}},
+         {kAttributionFencedFrameReportingBeacon, {}}},
         {/* disabled_features */});
   }
 
@@ -6027,8 +6029,10 @@
       public testing::WithParamInterface<const char*> {
  public:
   FencedFrameAutomaticBeaconBrowserTest() {
-    scoped_feature_list_.InitAndEnableFeature(
-        blink::features::kAllowURNsInIframes);
+    scoped_feature_list_.InitWithFeatures(
+        /*enabled_features=*/{blink::features::kAllowURNsInIframes,
+                              kAttributionFencedFrameReportingBeacon},
+        /*disabled_features=*/{});
   }
 
   // An object representing the configuration of the test. First a frame is
diff --git a/content/browser/fenced_frame/fenced_frame_reporter.cc b/content/browser/fenced_frame/fenced_frame_reporter.cc
index e0534d6..2d5aa16 100644
--- a/content/browser/fenced_frame/fenced_frame_reporter.cc
+++ b/content/browser/fenced_frame/fenced_frame_reporter.cc
@@ -136,11 +136,13 @@
     const std::string& type,
     const std::string& data,
     const url::Origin& request_initiator,
-    BeaconId beacon_id)
+    BeaconId beacon_id,
+    bool is_automatic_beacon)
     : type(type),
       data(data),
       request_initiator(request_initiator),
-      beacon_id(beacon_id) {}
+      beacon_id(beacon_id),
+      is_automatic_beacon(is_automatic_beacon) {}
 
 FencedFrameReporter::PendingEvent::PendingEvent(const PendingEvent&) = default;
 
@@ -248,7 +250,9 @@
     std::string ignored_error_message;
     SendReportInternal(it->second, pending_event.type, pending_event.data,
                        reporting_destination, pending_event.request_initiator,
-                       pending_event.beacon_id, ignored_error_message);
+                       pending_event.beacon_id,
+                       pending_event.is_automatic_beacon,
+                       ignored_error_message);
   }
 }
 
@@ -291,37 +295,31 @@
 
   static base::AtomicSequenceNumber unique_id_counter;
 
-  // The id will be a `NavigationBeaconId` only if this report is being sent as
-  // the result of a top navigation initiated by a fenced frame. This is used to
-  // track attributions that occur on a navigated page after the current page
-  // has been unloaded.
-  BeaconId beacon_id;
-  if (navigation_id.has_value()) {
-    beacon_id = NavigationBeaconId(navigation_id.value());
-  } else {
-    beacon_id = EventBeaconId(unique_id_counter.GetNext());
-  }
+  BeaconId beacon_id(unique_id_counter.GetNext());
 
   auto* attribution_host = AttributionHost::FromWebContents(
       WebContents::FromRenderFrameHost(request_initiator_frame));
   if (attribution_host) {
     attribution_host->NotifyFencedFrameReportingBeaconStarted(
-        beacon_id, request_initiator_frame);
+        beacon_id, navigation_id, request_initiator_frame);
   }
 
   const url::Origin& request_initiator =
       request_initiator_frame->GetLastCommittedOrigin();
 
+  const bool is_automatic_beacon = navigation_id.has_value();
+
   // If the reporting URL map is pending, queue the event.
   if (it->second.reporting_url_map == absl::nullopt) {
     it->second.pending_events.emplace_back(event_type, event_data,
-                                           request_initiator, beacon_id);
+                                           request_initiator, beacon_id,
+                                           is_automatic_beacon);
     return true;
   }
 
   return SendReportInternal(it->second, event_type, event_data,
                             reporting_destination, request_initiator, beacon_id,
-                            error_message);
+                            is_automatic_beacon, error_message);
 }
 
 bool FencedFrameReporter::SendReportInternal(
@@ -331,6 +329,7 @@
     blink::FencedFrame::ReportingDestination reporting_destination,
     const url::Origin& request_initiator,
     BeaconId beacon_id,
+    bool is_automatic_beacon,
     std::string& error_message) {
   // The URL map should not be pending at this point.
   DCHECK(reporting_destination_info.reporting_url_map);
@@ -371,10 +370,9 @@
       net::IsolationInfo::CreateTransient();
 
   if (attribution_manager_) {
-    request->headers.SetHeader("Attribution-Reporting-Eligible",
-                               absl::holds_alternative<EventBeaconId>(beacon_id)
-                                   ? "event-source"
-                                   : "navigation-source");
+    request->headers.SetHeader(
+        "Attribution-Reporting-Eligible",
+        is_automatic_beacon ? "navigation-source" : "event-source");
 
     if (base::FeatureList::IsEnabled(
             blink::features::kAttributionReportingCrossAppWeb)) {
diff --git a/content/browser/fenced_frame/fenced_frame_reporter.h b/content/browser/fenced_frame/fenced_frame_reporter.h
index 2fe8b94..d28269a 100644
--- a/content/browser/fenced_frame/fenced_frame_reporter.h
+++ b/content/browser/fenced_frame/fenced_frame_reporter.h
@@ -194,7 +194,8 @@
     PendingEvent(const std::string& type,
                  const std::string& data,
                  const url::Origin& request_initiator,
-                 BeaconId beacon_id);
+                 BeaconId beacon_id,
+                 bool is_automatic_beacon);
 
     PendingEvent(const PendingEvent&);
     PendingEvent(PendingEvent&&);
@@ -208,6 +209,7 @@
     std::string data;
     url::Origin request_initiator;
     BeaconId beacon_id;
+    bool is_automatic_beacon;
   };
 
   // The per-blink::FencedFrame::ReportingDestination reporting information.
@@ -240,6 +242,7 @@
       blink::FencedFrame::ReportingDestination reporting_destination,
       const url::Origin& request_initiator,
       BeaconId beacon_id,
+      bool is_automatic_beacon,
       std::string& error_message);
 
   // Helper to send private aggregation requests in
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 33d73d5..07e327ec 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -2949,7 +2949,10 @@
   }
 
   if (is_fuchsia) {
-    deps += [ "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.accessibility.semantics:fuchsia.accessibility.semantics_hlcpp" ]
+    deps += [
+      "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.accessibility.semantics:fuchsia.accessibility.semantics_cpp",
+      "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.accessibility.semantics:fuchsia.accessibility.semantics_hlcpp",
+    ]
   }
 
   if (enable_printing) {
diff --git a/extensions/browser/api/runtime/runtime_api.cc b/extensions/browser/api/runtime/runtime_api.cc
index d112f7e..f299478 100644
--- a/extensions/browser/api/runtime/runtime_api.cc
+++ b/extensions/browser/api/runtime/runtime_api.cc
@@ -153,6 +153,51 @@
   return url_string;
 }
 
+// Returns true if the given `context` matches the `filter`.
+bool ExtensionContextMatchesFilter(
+    const api::runtime::ExtensionContext& context,
+    const api::runtime::ContextFilter& filter) {
+  if (filter.context_types &&
+      !base::Contains(*filter.context_types, context.context_type)) {
+    return false;
+  }
+  if (filter.context_ids &&
+      !base::Contains(*filter.context_ids, context.context_id)) {
+    return false;
+  }
+  if (filter.tab_ids && !base::Contains(*filter.tab_ids, context.tab_id)) {
+    return false;
+  }
+  if (filter.window_ids &&
+      !base::Contains(*filter.window_ids, context.window_id)) {
+    return false;
+  }
+  if (filter.document_ids &&
+      (!context.document_id ||
+       !base::Contains(*filter.document_ids, *context.document_id))) {
+    return false;
+  }
+  if (filter.frame_ids &&
+      !base::Contains(*filter.frame_ids, context.frame_id)) {
+    return false;
+  }
+  if (filter.document_urls &&
+      (!context.document_url ||
+       !base::Contains(*filter.document_urls, *context.document_url))) {
+    return false;
+  }
+  if (filter.document_origins &&
+      (!context.document_origin ||
+       !base::Contains(*filter.document_origins, *context.document_origin))) {
+    return false;
+  }
+  if (filter.incognito && *filter.incognito != context.incognito) {
+    return false;
+  }
+
+  return true;
+}
+
 }  // namespace
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -751,12 +796,29 @@
 ExtensionFunction::ResponseAction RuntimeGetContextsFunction::Run() {
   EXTENSION_FUNCTION_VALIDATE(extension());
 
+  auto params = api::runtime::GetContexts::Params::Create(args());
+  EXTENSION_FUNCTION_VALIDATE(params);
+  const api::runtime::ContextFilter& filter = params->filter;
+
   std::vector<api::runtime::ExtensionContext> result;
-  if (absl::optional<api::runtime::ExtensionContext> worker =
-          GetWorkerContext()) {
-    result.push_back(std::move(*worker));
+
+  // Minor optimization: only construct the context if there's a chance it will
+  // match the filter.
+  if (!filter.context_types ||
+      base::Contains(*filter.context_types,
+                     api::runtime::CONTEXT_TYPE_BACKGROUND)) {
+    if (absl::optional<api::runtime::ExtensionContext> worker =
+            GetWorkerContext()) {
+      result.push_back(std::move(*worker));
+    }
   }
 
+  // Erase any contexts that don't match the specified filter.
+  base::EraseIf(result,
+                [&filter](const api::runtime::ExtensionContext& context) {
+                  return !ExtensionContextMatchesFilter(context, filter);
+                });
+
   return RespondNow(
       ArgumentList(api::runtime::GetContexts::Results::Create(result)));
 }
diff --git a/extensions/common/api/runtime.json b/extensions/common/api/runtime.json
index 13f0b63..e3b350d 100644
--- a/extensions/common/api/runtime.json
+++ b/extensions/common/api/runtime.json
@@ -188,6 +188,57 @@
             "description": "Whether the context is associated with an incognito profile."
           }
         }
+      },
+      {
+        "id": "ContextFilter",
+        "type": "object",
+        "description": "A filter to match against certain extension contexts. Matching contexts must match all specified filters; any filter that is not specified matches all available contexts. Thus, a filter of `{}` will match all available contexts.",
+        "properties": {
+          "contextTypes": {
+            "type": "array",
+            "items": { "$ref": "ContextType" },
+            "optional": true
+          },
+          "contextIds": {
+            "type": "array",
+            "items": { "type": "string" },
+            "optional": true
+          },
+          "tabIds": {
+            "type": "array",
+            "items": { "type": "integer" },
+            "optional": true
+          },
+          "windowIds": {
+            "type": "array",
+            "items": { "type": "integer" },
+            "optional": true
+          },
+          "documentIds": {
+            "type": "array",
+            "items": { "type": "string" },
+            "optional": true
+          },
+          "frameIds": {
+            "type": "array",
+            "items": { "type": "integer" },
+            "optional": true
+          },
+          "documentUrls": {
+            "type": "array",
+            "items": { "type": "string" },
+            "optional": true
+          },
+          "documentOrigins": {
+            "type": "array",
+            "items": { "type": "string" },
+            "optional": true
+          },
+          "incognito": {
+            "type": "boolean",
+            "optional": true
+          }
+        }
       }
     ],
     "properties": {
@@ -491,7 +542,11 @@
         "type": "function",
         "description": "Fetches information about active contexts associated with this extension",
         "parameters": [
-            // TODO(crbug/1426192): Add a filter.
+          {
+            "$ref": "ContextFilter",
+            "name": "filter",
+            "description": "A filter to find matching contexts. A context matches if it matches all specified fields in the filter. Any unspecified field in the filter matches all contexts."
+          }
         ],
         "returns_async": {
           "name": "callback",
diff --git a/gpu/BUILD.gn b/gpu/BUILD.gn
index 671a9e7..8cd9016 100644
--- a/gpu/BUILD.gn
+++ b/gpu/BUILD.gn
@@ -305,6 +305,8 @@
     "command_buffer/service/shared_image/shared_image_factory_unittest.cc",
     "command_buffer/service/shared_image/shared_image_manager_unittest.cc",
     "command_buffer/service/shared_image/shared_image_representation_unittest.cc",
+    "command_buffer/service/shared_image/shared_image_test_base.cc",
+    "command_buffer/service/shared_image/shared_image_test_base.h",
     "command_buffer/service/shared_image/test_utils.cc",
     "command_buffer/service/shared_image/test_utils.h",
     "command_buffer/service/shared_image/wrapped_sk_image_backing_factory_unittest.cc",
@@ -399,6 +401,7 @@
     "//gpu/ipc:gl_in_process_context",
     "//gpu/ipc/host",
     "//gpu/ipc/service",
+    "//gpu/vulkan:buildflags",
     "//gpu/webgpu:common",
     "//mojo/core/embedder",
     "//testing/gmock",
@@ -417,6 +420,10 @@
     deps += [ "//ui/ozone" ]
   }
 
+  if (enable_vulkan) {
+    deps += [ "//gpu/vulkan/init:init" ]
+  }
+
   libs = []
 
   if (is_android) {
@@ -440,7 +447,6 @@
     # Simply loading the Vulkan driver leaks crbug.com/1134681
     # CFI error in third_party/vulkan_memory_allocator crbug.com/1139916
     if (enable_vulkan && !is_lsan && !is_cfi) {
-      deps += [ "//gpu/vulkan/init:init" ]
       sources += [ "command_buffer/service/shared_image/external_vk_image_backing_factory_unittest.cc" ]
     }
   }
diff --git a/gpu/command_buffer/service/shared_image/DEPS b/gpu/command_buffer/service/shared_image/DEPS
index 3dab9e7e..3220bd1 100644
--- a/gpu/command_buffer/service/shared_image/DEPS
+++ b/gpu/command_buffer/service/shared_image/DEPS
@@ -3,11 +3,14 @@
 ]
 
 specific_include_rules = {
+  "dcomp_image_backing_factory_unittest\.cc": [
+    "+ui/platform_window",
+  ],
   "external_vk_image_backing_factory_unittest\.cc": [
     "+components/viz/common/gpu/vulkan_in_process_context_provider.h",
   ],
-  "dcomp_image_backing_factory_unittest\.cc": [
-    "+ui/platform_window",
+  "shared_image_test_base\.cc": [
+    "+components/viz/common/gpu/vulkan_in_process_context_provider.h",
   ],
   ".*_unittest\.cc": [
     "+cc/test",
diff --git a/gpu/command_buffer/service/shared_image/ahardwarebuffer_image_backing_factory_unittest.cc b/gpu/command_buffer/service/shared_image/ahardwarebuffer_image_backing_factory_unittest.cc
index 90fb10c4..8644ade 100644
--- a/gpu/command_buffer/service/shared_image/ahardwarebuffer_image_backing_factory_unittest.cc
+++ b/gpu/command_buffer/service/shared_image/ahardwarebuffer_image_backing_factory_unittest.cc
@@ -15,9 +15,9 @@
 #include "gpu/command_buffer/service/shared_image/shared_image_factory.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_manager.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_representation.h"
+#include "gpu/command_buffer/service/shared_image/shared_image_test_base.h"
 #include "gpu/command_buffer/service/shared_image/test_utils.h"
 #include "gpu/command_buffer/service/texture_manager.h"
-#include "gpu/config/gpu_driver_bug_workarounds.h"
 #include "gpu/config/gpu_feature_info.h"
 #include "gpu/config/gpu_preferences.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -29,69 +29,31 @@
 #include "third_party/skia/include/gpu/GrDirectContext.h"
 #include "ui/gfx/color_space.h"
 #include "ui/gl/gl_bindings.h"
-#include "ui/gl/gl_context.h"
 #include "ui/gl/gl_gl_api_implementation.h"
-#include "ui/gl/gl_surface.h"
-#include "ui/gl/gl_utils.h"
-#include "ui/gl/init/gl_factory.h"
 
 namespace gpu {
 namespace {
 
-class AHardwareBufferImageBackingFactoryTest : public testing::Test {
+class AHardwareBufferImageBackingFactoryTest : public SharedImageTestBase {
  public:
   void SetUp() override {
     // AHardwareBuffer is only supported on ANDROID O+. Hence these tests
     // should not be run on android versions less that O.
-    if (!base::AndroidHardwareBufferCompat::IsSupportAvailable())
-      return;
-    surface_ = gl::init::CreateOffscreenGLSurface(gl::GetDefaultDisplayEGL(),
-                                                  gfx::Size());
-    ASSERT_TRUE(surface_);
-    context_ = gl::init::CreateGLContext(nullptr, surface_.get(),
-                                         gl::GLContextAttribs());
-    ASSERT_TRUE(context_);
-    bool result = context_->MakeCurrent(surface_.get());
-    ASSERT_TRUE(result);
+    if (!base::AndroidHardwareBufferCompat::IsSupportAvailable()) {
+      GTEST_SKIP() << "AHardwareBuffer not supported";
+    }
 
-    GpuDriverBugWorkarounds workarounds;
-    auto gpu_preferences = GpuPreferences();
-
-    scoped_refptr<gl::GLShareGroup> share_group = new gl::GLShareGroup();
-    context_state_ = base::MakeRefCounted<SharedContextState>(
-        std::move(share_group), surface_, context_,
-        /*use_virtualized_gl_contexts=*/false, base::DoNothing());
-    context_state_->InitializeGrContext(gpu_preferences, workarounds, nullptr);
-    auto feature_info =
-        base::MakeRefCounted<gles2::FeatureInfo>(workarounds, GpuFeatureInfo());
-    context_state_->InitializeGL(gpu_preferences, std::move(feature_info));
+    ASSERT_NO_FATAL_FAILURE(InitializeContext(GrContextType::kGL));
 
     backing_factory_ = std::make_unique<AHardwareBufferImageBackingFactory>(
-        context_state_->feature_info(), gpu_preferences);
-
-    memory_type_tracker_ = std::make_unique<MemoryTypeTracker>(nullptr);
-    shared_image_representation_factory_ =
-        std::make_unique<SharedImageRepresentationFactory>(
-            &shared_image_manager_, nullptr);
+        context_state_->feature_info(), gpu_preferences_);
   }
-
-  GrDirectContext* gr_context() { return context_state_->gr_context(); }
-
- protected:
-  scoped_refptr<gl::GLSurface> surface_;
-  scoped_refptr<gl::GLContext> context_;
-  scoped_refptr<SharedContextState> context_state_;
-  std::unique_ptr<AHardwareBufferImageBackingFactory> backing_factory_;
-  SharedImageManager shared_image_manager_;
-  std::unique_ptr<MemoryTypeTracker> memory_type_tracker_;
-  std::unique_ptr<SharedImageRepresentationFactory>
-      shared_image_representation_factory_;
 };
 
 class GlLegacySharedImage {
  public:
   GlLegacySharedImage(
-      AHardwareBufferImageBackingFactory* backing_factory,
+      SharedImageBackingFactory* backing_factory,
       bool is_thread_safe,
       SharedImageManager* shared_image_manager,
       MemoryTypeTracker* memory_type_tracker,
@@ -110,15 +72,12 @@
 
 // Basic test to check creation and deletion of AHB backed shared image.
 TEST_F(AHardwareBufferImageBackingFactoryTest, Basic) {
-  if (!base::AndroidHardwareBufferCompat::IsSupportAvailable())
-    return;
-
   GlLegacySharedImage gl_legacy_shared_image{
       backing_factory_.get(), /*is_thread_safe=*/false, &shared_image_manager_,
-      memory_type_tracker_.get(), shared_image_representation_factory_.get()};
+      &memory_type_tracker_, &shared_image_representation_factory_};
 
   // Validate a SkiaImageRepresentation.
-  auto skia_representation = shared_image_representation_factory_->ProduceSkia(
+  auto skia_representation = shared_image_representation_factory_.ProduceSkia(
       gl_legacy_shared_image.mailbox(), context_state_.get());
   EXPECT_TRUE(skia_representation);
   std::vector<GrBackendSemaphore> begin_semaphores;
@@ -157,9 +116,6 @@
 // We write to a GL texture using gl representation and then read from skia
 // representation.
 TEST_F(AHardwareBufferImageBackingFactoryTest, GLSkiaGL) {
-  if (!base::AndroidHardwareBufferCompat::IsSupportAvailable())
-    return;
-
   // Create a backing using mailbox.
   auto mailbox = Mailbox::GenerateForSharedImage();
   auto format = viz::SinglePlaneFormat::kRGBA_8888;
@@ -176,12 +132,11 @@
 
   GLenum expected_target = GL_TEXTURE_2D;
   std::unique_ptr<SharedImageRepresentationFactoryRef> factory_ref =
-      shared_image_manager_.Register(std::move(backing),
-                                     memory_type_tracker_.get());
+      shared_image_manager_.Register(std::move(backing), &memory_type_tracker_);
 
   // Create a GLTextureImageRepresentation.
   auto gl_representation =
-      shared_image_representation_factory_->ProduceGLTexture(mailbox);
+      shared_image_representation_factory_.ProduceGLTexture(mailbox);
   EXPECT_TRUE(gl_representation);
   EXPECT_EQ(expected_target, gl_representation->GetTexture()->target());
 
@@ -206,7 +161,7 @@
   gl_representation.reset();
 
   auto dst_pixels = ReadPixels(mailbox, size, context_state_.get(),
-                               shared_image_representation_factory_.get());
+                               &shared_image_representation_factory_);
 
   // Compare the pixel values.
   EXPECT_EQ(dst_pixels[0], 0);
@@ -218,9 +173,6 @@
 }
 
 TEST_F(AHardwareBufferImageBackingFactoryTest, InitialData) {
-  if (!base::AndroidHardwareBufferCompat::IsSupportAvailable())
-    return;
-
   auto mailbox = Mailbox::GenerateForSharedImage();
   auto format = viz::SinglePlaneFormat::kRGBA_8888;
   gfx::Size size(4, 4);
@@ -241,11 +193,10 @@
   EXPECT_TRUE(backing);
 
   std::unique_ptr<SharedImageRepresentationFactoryRef> factory_ref =
-      shared_image_manager_.Register(std::move(backing),
-                                     memory_type_tracker_.get());
+      shared_image_manager_.Register(std::move(backing), &memory_type_tracker_);
 
   auto dst_pixels = ReadPixels(mailbox, size, context_state_.get(),
-                               shared_image_representation_factory_.get());
+                               &shared_image_representation_factory_);
 
   // Compare the pixel values.
   DCHECK(dst_pixels.size() == initial_data.size());
@@ -256,9 +207,6 @@
 
 // Test to check invalid format support.
 TEST_F(AHardwareBufferImageBackingFactoryTest, InvalidFormat) {
-  if (!base::AndroidHardwareBufferCompat::IsSupportAvailable())
-    return;
-
   auto mailbox = Mailbox::GenerateForSharedImage();
   auto format = viz::SharedImageFormat::SinglePlane(
       viz::ResourceFormat::YUV_420_BIPLANAR);
@@ -276,9 +224,6 @@
 
 // Test to check invalid size support.
 TEST_F(AHardwareBufferImageBackingFactoryTest, InvalidSize) {
-  if (!base::AndroidHardwareBufferCompat::IsSupportAvailable())
-    return;
-
   auto mailbox = Mailbox::GenerateForSharedImage();
   auto format = viz::SinglePlaneFormat::kRGBA_8888;
   gfx::Size size(0, 0);
@@ -300,9 +245,6 @@
 }
 
 TEST_F(AHardwareBufferImageBackingFactoryTest, EstimatedSize) {
-  if (!base::AndroidHardwareBufferCompat::IsSupportAvailable())
-    return;
-
   auto mailbox = Mailbox::GenerateForSharedImage();
   auto format = viz::SinglePlaneFormat::kRGBA_8888;
   gfx::Size size(256, 256);
@@ -320,9 +262,8 @@
   EXPECT_GT(backing_estimated_size, 0u);
 
   std::unique_ptr<SharedImageRepresentationFactoryRef> shared_image =
-      shared_image_manager_.Register(std::move(backing),
-                                     memory_type_tracker_.get());
-  EXPECT_EQ(backing_estimated_size, memory_type_tracker_->GetMemRepresented());
+      shared_image_manager_.Register(std::move(backing), &memory_type_tracker_);
+  EXPECT_EQ(backing_estimated_size, memory_type_tracker_.GetMemRepresented());
 
   shared_image.reset();
 }
@@ -330,14 +271,11 @@
 // TODO(crbug/994720): Failing on Android builders.
 // Test to check that only one context can write at a time
 TEST_F(AHardwareBufferImageBackingFactoryTest, DISABLED_OnlyOneWriter) {
-  if (!base::AndroidHardwareBufferCompat::IsSupportAvailable())
-    return;
-
   GlLegacySharedImage gl_legacy_shared_image{
       backing_factory_.get(), /*is_thread_safe=*/true, &shared_image_manager_,
-      memory_type_tracker_.get(), shared_image_representation_factory_.get()};
+      &memory_type_tracker_, &shared_image_representation_factory_};
 
-  auto skia_representation = shared_image_representation_factory_->ProduceSkia(
+  auto skia_representation = shared_image_representation_factory_.ProduceSkia(
       gl_legacy_shared_image.mailbox(), context_state_.get());
 
   std::vector<GrBackendSemaphore> begin_semaphores;
@@ -351,7 +289,7 @@
   EXPECT_EQ(0u, begin_semaphores.size());
   EXPECT_EQ(0u, end_semaphores.size());
 
-  auto skia_representation2 = shared_image_representation_factory_->ProduceSkia(
+  auto skia_representation2 = shared_image_representation_factory_.ProduceSkia(
       gl_legacy_shared_image.mailbox(), context_state_.get());
   std::vector<GrBackendSemaphore> begin_semaphores2;
   std::vector<GrBackendSemaphore> end_semaphores2;
@@ -371,16 +309,13 @@
 
 // Test to check that multiple readers are allowed
 TEST_F(AHardwareBufferImageBackingFactoryTest, CanHaveMultipleReaders) {
-  if (!base::AndroidHardwareBufferCompat::IsSupportAvailable())
-    return;
-
   GlLegacySharedImage gl_legacy_shared_image{
       backing_factory_.get(), /*is_thread_safe=*/true, &shared_image_manager_,
-      memory_type_tracker_.get(), shared_image_representation_factory_.get()};
+      &memory_type_tracker_, &shared_image_representation_factory_};
 
-  auto skia_representation = shared_image_representation_factory_->ProduceSkia(
+  auto skia_representation = shared_image_representation_factory_.ProduceSkia(
       gl_legacy_shared_image.mailbox(), context_state_.get());
-  auto skia_representation2 = shared_image_representation_factory_->ProduceSkia(
+  auto skia_representation2 = shared_image_representation_factory_.ProduceSkia(
       gl_legacy_shared_image.mailbox(), context_state_.get());
 
   std::vector<GrBackendSemaphore> begin_semaphores;
@@ -408,14 +343,11 @@
 
 // Test to check that a context cannot write while another context is reading
 TEST_F(AHardwareBufferImageBackingFactoryTest, CannotWriteWhileReading) {
-  if (!base::AndroidHardwareBufferCompat::IsSupportAvailable())
-    return;
-
   GlLegacySharedImage gl_legacy_shared_image{
       backing_factory_.get(), /*is_thread_safe=*/true, &shared_image_manager_,
-      memory_type_tracker_.get(), shared_image_representation_factory_.get()};
+      &memory_type_tracker_, &shared_image_representation_factory_};
 
-  auto skia_representation = shared_image_representation_factory_->ProduceSkia(
+  auto skia_representation = shared_image_representation_factory_.ProduceSkia(
       gl_legacy_shared_image.mailbox(), context_state_.get());
 
   std::vector<GrBackendSemaphore> begin_semaphores;
@@ -428,7 +360,7 @@
   EXPECT_EQ(0u, begin_semaphores.size());
   EXPECT_EQ(0u, end_semaphores.size());
 
-  auto skia_representation2 = shared_image_representation_factory_->ProduceSkia(
+  auto skia_representation2 = shared_image_representation_factory_.ProduceSkia(
       gl_legacy_shared_image.mailbox(), context_state_.get());
 
   std::vector<GrBackendSemaphore> begin_semaphores2;
@@ -450,14 +382,11 @@
 
 // Test to check that a context cannot read while another context is writing
 TEST_F(AHardwareBufferImageBackingFactoryTest, CannotReadWhileWriting) {
-  if (!base::AndroidHardwareBufferCompat::IsSupportAvailable())
-    return;
-
   GlLegacySharedImage gl_legacy_shared_image{
       backing_factory_.get(), /*is_thread_safe=*/true, &shared_image_manager_,
-      memory_type_tracker_.get(), shared_image_representation_factory_.get()};
+      &memory_type_tracker_, &shared_image_representation_factory_};
 
-  auto skia_representation = shared_image_representation_factory_->ProduceSkia(
+  auto skia_representation = shared_image_representation_factory_.ProduceSkia(
       gl_legacy_shared_image.mailbox(), context_state_.get());
   std::vector<GrBackendSemaphore> begin_semaphores;
   std::vector<GrBackendSemaphore> end_semaphores;
@@ -470,7 +399,7 @@
   EXPECT_EQ(0u, begin_semaphores.size());
   EXPECT_EQ(0u, end_semaphores.size());
 
-  auto skia_representation2 = shared_image_representation_factory_->ProduceSkia(
+  auto skia_representation2 = shared_image_representation_factory_.ProduceSkia(
       gl_legacy_shared_image.mailbox(), context_state_.get());
   std::vector<GrBackendSemaphore> begin_semaphores2;
   std::vector<GrBackendSemaphore> end_semaphores2;
@@ -487,7 +416,7 @@
 }
 
 GlLegacySharedImage::GlLegacySharedImage(
-    AHardwareBufferImageBackingFactory* backing_factory,
+    SharedImageBackingFactory* backing_factory,
     bool is_thread_safe,
     SharedImageManager* shared_image_manager,
     MemoryTypeTracker* memory_type_tracker,
@@ -538,14 +467,11 @@
 }
 
 TEST_F(AHardwareBufferImageBackingFactoryTest, Overlay) {
-  if (!base::AndroidHardwareBufferCompat::IsSupportAvailable())
-    return;
-
   GlLegacySharedImage gl_legacy_shared_image{
       backing_factory_.get(), /*is_thread_safe=*/false, &shared_image_manager_,
-      memory_type_tracker_.get(), shared_image_representation_factory_.get()};
+      &memory_type_tracker_, &shared_image_representation_factory_};
 
-  auto skia_representation = shared_image_representation_factory_->ProduceSkia(
+  auto skia_representation = shared_image_representation_factory_.ProduceSkia(
       gl_legacy_shared_image.mailbox(), context_state_.get());
 
   std::vector<GrBackendSemaphore> begin_semaphores;
@@ -559,7 +485,7 @@
   scoped_write_access.reset();
 
   auto overlay_representation =
-      shared_image_representation_factory_->ProduceOverlay(
+      shared_image_representation_factory_.ProduceOverlay(
           gl_legacy_shared_image.mailbox());
   EXPECT_TRUE(overlay_representation);
 
diff --git a/gpu/command_buffer/service/shared_image/external_vk_image_backing_factory_unittest.cc b/gpu/command_buffer/service/shared_image/external_vk_image_backing_factory_unittest.cc
index e2d97d4..a41cbbd 100644
--- a/gpu/command_buffer/service/shared_image/external_vk_image_backing_factory_unittest.cc
+++ b/gpu/command_buffer/service/shared_image/external_vk_image_backing_factory_unittest.cc
@@ -19,9 +19,9 @@
 #include "gpu/command_buffer/service/shared_image/shared_image_factory.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_manager.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_representation.h"
+#include "gpu/command_buffer/service/shared_image/shared_image_test_base.h"
 #include "gpu/command_buffer/service/skia_utils.h"
 #include "gpu/config/gpu_test_config.h"
-#include "gpu/vulkan/init/vulkan_factory.h"
 #include "gpu/vulkan/vulkan_implementation.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/skia/include/core/SkCanvas.h"
@@ -32,10 +32,6 @@
 #include "third_party/skia/include/gpu/GrBackendSemaphore.h"
 #include "third_party/skia/include/gpu/ganesh/SkImageGanesh.h"
 #include "ui/gl/buildflags.h"
-#include "ui/gl/gl_context.h"
-#include "ui/gl/gl_surface.h"
-#include "ui/gl/gl_utils.h"
-#include "ui/gl/init/gl_factory.h"
 
 #if BUILDFLAG(USE_DAWN)
 #include <dawn/dawn_proc.h>
@@ -46,77 +42,17 @@
 namespace gpu {
 namespace {
 
-class ExternalVkImageBackingFactoryTest : public testing::Test {
+class ExternalVkImageBackingFactoryTest : public SharedImageTestBase {
  protected:
-  bool use_passthrough() const {
-    return gles2::UsePassthroughCommandDecoder(
-               base::CommandLine::ForCurrentProcess()) &&
-           gles2::PassthroughCommandDecoderSupported();
-  }
-
-  GrDirectContext* gr_context() { return context_state_->gr_context(); }
-
   void SetUp() override {
 #if BUILDFLAG(IS_CHROMEOS)
     GTEST_SKIP() << "Chrome OS Vulkan initialization fails";
 #else
-    // Set up the Vulkan implementation and context provider.
-    vulkan_implementation_ = gpu::CreateVulkanImplementation();
-    ASSERT_TRUE(vulkan_implementation_);
-
-    auto initialize_vulkan = vulkan_implementation_->InitializeVulkanInstance();
-    ASSERT_TRUE(initialize_vulkan);
-
-    vulkan_context_provider_ = viz::VulkanInProcessContextProvider::Create(
-        vulkan_implementation_.get());
-    ASSERT_TRUE(vulkan_context_provider_);
-
-    // Set up a GL context. We don't actually need it, but we can't make
-    // a SharedContextState without one.
-    gl_surface_ = gl::init::CreateOffscreenGLSurface(gl::GetDefaultDisplayEGL(),
-                                                     gfx::Size());
-    ASSERT_TRUE(gl_surface_);
-    gl_context_ = gl::init::CreateGLContext(nullptr, gl_surface_.get(),
-                                            gl::GLContextAttribs());
-    ASSERT_TRUE(gl_context_);
-    bool make_current_result = gl_context_->MakeCurrent(gl_surface_.get());
-    ASSERT_TRUE(make_current_result);
-
-    scoped_refptr<gl::GLShareGroup> share_group = new gl::GLShareGroup();
-    context_state_ = base::MakeRefCounted<SharedContextState>(
-        std::move(share_group), gl_surface_, gl_context_,
-        /*use_virtualized_gl_contexts=*/false, base::DoNothing(),
-        GrContextType::kVulkan, vulkan_context_provider_.get());
-
-    context_state_->InitializeGL(
-        GpuPreferences(), base::MakeRefCounted<gles2::FeatureInfo>(
-                              GpuDriverBugWorkarounds(), GpuFeatureInfo()));
-
-    GpuPreferences gpu_preferences = {};
-    GpuDriverBugWorkarounds workarounds = {};
-    context_state_->InitializeGrContext(gpu_preferences, workarounds, nullptr);
-
-    memory_type_tracker_ = std::make_unique<MemoryTypeTracker>(nullptr);
-    shared_image_representation_factory_ =
-        std::make_unique<SharedImageRepresentationFactory>(
-            &shared_image_manager_, nullptr);
+    ASSERT_NO_FATAL_FAILURE(InitializeContext(GrContextType::kVulkan));
     backing_factory_ =
         std::make_unique<ExternalVkImageBackingFactory>(context_state_);
 #endif
   }
-
-  std::unique_ptr<VulkanImplementation> vulkan_implementation_;
-  scoped_refptr<viz::VulkanInProcessContextProvider> vulkan_context_provider_;
-
-  scoped_refptr<gl::GLSurface> gl_surface_;
-  scoped_refptr<gl::GLContext> gl_context_;
-  scoped_refptr<SharedContextState> context_state_;
-
-  SharedImageManager shared_image_manager_;
-  std::unique_ptr<MemoryTypeTracker> memory_type_tracker_;
-  std::unique_ptr<SharedImageRepresentationFactory>
-      shared_image_representation_factory_;
-  std::unique_ptr<ExternalVkImageBackingFactory> backing_factory_;
 };
 
 #if BUILDFLAG(USE_DAWN)
@@ -184,14 +120,12 @@
   ASSERT_NE(backing, nullptr);
 
   std::unique_ptr<SharedImageRepresentationFactoryRef> factory_ref =
-      shared_image_manager_.Register(std::move(backing),
-                                     memory_type_tracker_.get());
+      shared_image_manager_.Register(std::move(backing), &memory_type_tracker_);
 
   {
     // Create a Dawn representation to clear the texture contents to a green.
-    auto dawn_representation =
-        shared_image_representation_factory_->ProduceDawn(
-            mailbox, dawn_device_.Get(), WGPUBackendType_Vulkan, {});
+    auto dawn_representation = shared_image_representation_factory_.ProduceDawn(
+        mailbox, dawn_device_.Get(), WGPUBackendType_Vulkan, {});
     ASSERT_TRUE(dawn_representation);
 
     auto dawn_scoped_access = dawn_representation->BeginScopedAccess(
@@ -224,9 +158,8 @@
   EXPECT_TRUE(factory_ref->IsCleared());
 
   {
-    auto skia_representation =
-        shared_image_representation_factory_->ProduceSkia(mailbox,
-                                                          context_state_.get());
+    auto skia_representation = shared_image_representation_factory_.ProduceSkia(
+        mailbox, context_state_.get());
 
     std::vector<GrBackendSemaphore> begin_semaphores;
     std::vector<GrBackendSemaphore> end_semaphores;
@@ -302,14 +235,12 @@
   ASSERT_NE(backing, nullptr);
 
   std::unique_ptr<SharedImageRepresentationFactoryRef> factory_ref =
-      shared_image_manager_.Register(std::move(backing),
-                                     memory_type_tracker_.get());
+      shared_image_manager_.Register(std::move(backing), &memory_type_tracker_);
 
   {
     // Create a SkiaImageRepresentation
-    auto skia_representation =
-        shared_image_representation_factory_->ProduceSkia(mailbox,
-                                                          context_state_.get());
+    auto skia_representation = shared_image_representation_factory_.ProduceSkia(
+        mailbox, context_state_.get());
 
     // Begin access for writing
     std::vector<GrBackendSemaphore> begin_semaphores;
@@ -347,9 +278,8 @@
 
   {
     // Create a Dawn representation
-    auto dawn_representation =
-        shared_image_representation_factory_->ProduceDawn(
-            mailbox, dawn_device_.Get(), WGPUBackendType_Vulkan, {});
+    auto dawn_representation = shared_image_representation_factory_.ProduceDawn(
+        mailbox, dawn_device_.Get(), WGPUBackendType_Vulkan, {});
     ASSERT_TRUE(dawn_representation);
 
     // Begin access to copy the data out. Skia should have initialized the
@@ -445,7 +375,7 @@
   SkAlphaType alpha_type = kPremul_SkAlphaType;
   uint32_t usage = SHARED_IMAGE_USAGE_DISPLAY_READ | SHARED_IMAGE_USAGE_GLES2;
 
-  bool supported = backing_factory_->IsSupported(
+  bool supported = backing_factory_->CanCreateSharedImage(
       usage, format, size, /*thread_safe=*/false, gfx::EMPTY_BUFFER,
       GrContextType::kVulkan, {});
   ASSERT_TRUE(supported);
@@ -457,11 +387,10 @@
   ASSERT_TRUE(backing);
 
   std::unique_ptr<SharedImageRepresentationFactoryRef> shared_image =
-      shared_image_manager_.Register(std::move(backing),
-                                     memory_type_tracker_.get());
+      shared_image_manager_.Register(std::move(backing), &memory_type_tracker_);
   EXPECT_TRUE(shared_image);
 
-  auto skia_representation = shared_image_representation_factory_->ProduceSkia(
+  auto skia_representation = shared_image_representation_factory_.ProduceSkia(
       mailbox, context_state_.get());
   ASSERT_TRUE(skia_representation);
 
@@ -532,7 +461,7 @@
   // Verify GL access works.
   if (use_passthrough()) {
     auto gl_representation =
-        shared_image_representation_factory_->ProduceGLTexturePassthrough(
+        shared_image_representation_factory_.ProduceGLTexturePassthrough(
             mailbox);
     ASSERT_TRUE(gl_representation);
     auto scoped_access = gl_representation->BeginScopedAccess(
@@ -545,7 +474,7 @@
     EXPECT_NE(texture->service_id(), 0u);
   } else {
     auto gl_representation =
-        shared_image_representation_factory_->ProduceGLTexture(mailbox);
+        shared_image_representation_factory_.ProduceGLTexture(mailbox);
     ASSERT_TRUE(gl_representation);
     auto scoped_access = gl_representation->BeginScopedAccess(
         GL_SHARED_IMAGE_ACCESS_MODE_READ_CHROMIUM,
diff --git a/gpu/command_buffer/service/shared_image/gl_texture_image_backing_factory_unittest.cc b/gpu/command_buffer/service/shared_image/gl_texture_image_backing_factory_unittest.cc
index d31be8e0..5ba4a05 100644
--- a/gpu/command_buffer/service/shared_image/gl_texture_image_backing_factory_unittest.cc
+++ b/gpu/command_buffer/service/shared_image/gl_texture_image_backing_factory_unittest.cc
@@ -23,6 +23,7 @@
 #include "gpu/command_buffer/service/shared_image/shared_image_format_utils.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_manager.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_representation.h"
+#include "gpu/command_buffer/service/shared_image/shared_image_test_base.h"
 #include "gpu/command_buffer/service/texture_manager.h"
 #include "gpu/config/gpu_driver_bug_workarounds.h"
 #include "gpu/config/gpu_feature_info.h"
@@ -36,9 +37,6 @@
 #include "third_party/skia/include/gpu/GrBackendSurface.h"
 #include "ui/gfx/color_space.h"
 #include "ui/gfx/gpu_memory_buffer.h"
-#include "ui/gl/gl_surface.h"
-#include "ui/gl/gl_utils.h"
-#include "ui/gl/init/gl_factory.h"
 #include "ui/gl/progress_reporter.h"
 
 using testing::AtLeast;
@@ -73,43 +71,11 @@
   return bitmaps;
 }
 
-std::vector<SkPixmap> GetSkPixmaps(const std::vector<SkBitmap>& bitmaps) {
-  std::vector<SkPixmap> pixmaps;
-  for (auto& bitmap : bitmaps) {
-    pixmaps.push_back(bitmap.pixmap());
-  }
-  return pixmaps;
-}
-
 bool IsGLSupported(viz::SharedImageFormat format) {
   return format.is_single_plane() && !format.IsLegacyMultiplanar() &&
          format != viz::SinglePlaneFormat::kBGR_565;
 }
 
-void CreateSharedContext(const GpuDriverBugWorkarounds& workarounds,
-                         scoped_refptr<gl::GLSurface>& surface,
-                         scoped_refptr<gl::GLContext>& context,
-                         scoped_refptr<SharedContextState>& context_state,
-                         scoped_refptr<gles2::FeatureInfo>& feature_info) {
-  surface =
-      gl::init::CreateOffscreenGLSurface(gl::GetDefaultDisplay(), gfx::Size());
-  ASSERT_TRUE(surface);
-  context =
-      gl::init::CreateGLContext(nullptr, surface.get(), gl::GLContextAttribs());
-  ASSERT_TRUE(context);
-  bool result = context->MakeCurrent(surface.get());
-  ASSERT_TRUE(result);
-
-  scoped_refptr<gl::GLShareGroup> share_group = new gl::GLShareGroup();
-  feature_info =
-      base::MakeRefCounted<gles2::FeatureInfo>(workarounds, GpuFeatureInfo());
-  context_state = base::MakeRefCounted<SharedContextState>(
-      std::move(share_group), surface, context,
-      /*use_virtualized_gl_contexts=*/false, base::DoNothing());
-  context_state->InitializeGrContext(GpuPreferences(), workarounds, nullptr);
-  context_state->InitializeGL(GpuPreferences(), feature_info);
-}
-
 class MockProgressReporter : public gl::ProgressReporter {
  public:
   MockProgressReporter() = default;
@@ -119,21 +85,14 @@
   MOCK_METHOD0(ReportProgress, void());
 };
 
-class GLTextureImageBackingFactoryTestBase : public testing::Test {
+class GLTextureImageBackingFactoryTestBase : public SharedImageTestBase {
  public:
-  explicit GLTextureImageBackingFactoryTestBase(bool is_thread_safe)
-      : shared_image_manager_(
-            std::make_unique<SharedImageManager>(is_thread_safe)) {}
-  ~GLTextureImageBackingFactoryTestBase() override {
-    // |context_state_| must be destroyed on its own context.
-    context_state_->MakeCurrent(surface_.get(), /*needs_gl=*/true);
-  }
+  GLTextureImageBackingFactoryTestBase() = default;
+  ~GLTextureImageBackingFactoryTestBase() override = default;
 
-  void SetUpBase(const GpuDriverBugWorkarounds& workarounds,
-                 bool for_cpu_upload_usage) {
-    scoped_refptr<gles2::FeatureInfo> feature_info;
-    CreateSharedContext(workarounds, surface_, context_, context_state_,
-                        feature_info);
+  void SetUpBase(bool for_cpu_upload_usage) {
+    ASSERT_NO_FATAL_FAILURE(InitializeContext(GrContextType::kGL));
+    auto* feature_info = context_state_->feature_info();
 
     // Check if platform should support various formats.
     supports_r_rg_ =
@@ -153,22 +112,9 @@
     supports_ar30_ = feature_info->feature_flags().chromium_image_ar30;
     supports_ab30_ = feature_info->feature_flags().chromium_image_ab30;
 
-    GpuPreferences preferences;
-    preferences.use_passthrough_cmd_decoder = use_passthrough();
     backing_factory_ = std::make_unique<GLTextureImageBackingFactory>(
-        preferences, workarounds, context_state_->feature_info(),
+        gpu_preferences_, gpu_workarounds_, context_state_->feature_info(),
         &progress_reporter_, for_cpu_upload_usage);
-
-    memory_type_tracker_ = std::make_unique<MemoryTypeTracker>(nullptr);
-    shared_image_representation_factory_ =
-        std::make_unique<SharedImageRepresentationFactory>(
-            shared_image_manager_.get(), nullptr);
-  }
-
-  bool use_passthrough() const {
-    return gles2::UsePassthroughCommandDecoder(
-               base::CommandLine::ForCurrentProcess()) &&
-           gles2::PassthroughCommandDecoderSupported();
   }
 
   bool IsFormatSupport(viz::SharedImageFormat format) const {
@@ -197,14 +143,6 @@
 
  protected:
   ::testing::NiceMock<MockProgressReporter> progress_reporter_;
-  scoped_refptr<gl::GLSurface> surface_;
-  scoped_refptr<gl::GLContext> context_;
-  scoped_refptr<SharedContextState> context_state_;
-  std::unique_ptr<GLTextureImageBackingFactory> backing_factory_;
-  std::unique_ptr<SharedImageManager> shared_image_manager_;
-  std::unique_ptr<MemoryTypeTracker> memory_type_tracker_;
-  std::unique_ptr<SharedImageRepresentationFactory>
-      shared_image_representation_factory_;
   bool supports_r_rg_ = false;
   bool supports_rg16_ = false;
   bool supports_rgba_f16_ = false;
@@ -217,12 +155,7 @@
 class GLTextureImageBackingFactoryTest
     : public GLTextureImageBackingFactoryTestBase {
  public:
-  GLTextureImageBackingFactoryTest()
-      : GLTextureImageBackingFactoryTestBase(false) {}
-  void SetUp() override {
-    GpuDriverBugWorkarounds workarounds;
-    SetUpBase(workarounds, /*for_cpu_upload_usage=*/false);
-  }
+  void SetUp() override { SetUpBase(/*for_cpu_upload_usage=*/false); }
 };
 
 // SharedImageFormat parameterized tests.
@@ -244,12 +177,7 @@
     : public GLTextureImageBackingFactoryTestBase,
       public testing::WithParamInterface<viz::SharedImageFormat> {
  public:
-  GLTextureImageBackingFactoryWithUploadTest()
-      : GLTextureImageBackingFactoryTestBase(false) {}
-  void SetUp() override {
-    GpuDriverBugWorkarounds workarounds;
-    SetUpBase(workarounds, /*for_cpu_upload_usage=*/true);
-  }
+  void SetUp() override { SetUpBase(/*for_cpu_upload_usage=*/true); }
   viz::SharedImageFormat get_format() { return GetParam(); }
 };
 
@@ -293,9 +221,8 @@
   EXPECT_GT(backing_estimated_size, 0u);
 
   std::unique_ptr<SharedImageRepresentationFactoryRef> shared_image =
-      shared_image_manager_->Register(std::move(backing),
-                                      memory_type_tracker_.get());
-  EXPECT_EQ(backing_estimated_size, memory_type_tracker_->GetMemRepresented());
+      shared_image_manager_.Register(std::move(backing), &memory_type_tracker_);
+  EXPECT_EQ(backing_estimated_size, memory_type_tracker_.GetMemRepresented());
 
   shared_image.reset();
 }
@@ -408,13 +335,12 @@
 
   // First, validate via a GLTextureImageRepresentation.
   std::unique_ptr<SharedImageRepresentationFactoryRef> shared_image =
-      shared_image_manager_->Register(std::move(backing),
-                                      memory_type_tracker_.get());
+      shared_image_manager_.Register(std::move(backing), &memory_type_tracker_);
   EXPECT_TRUE(shared_image);
   GLenum expected_target = GL_TEXTURE_2D;
   if (!use_passthrough()) {
     auto gl_representation =
-        shared_image_representation_factory_->ProduceGLTexture(mailbox);
+        shared_image_representation_factory_.ProduceGLTexture(mailbox);
     EXPECT_TRUE(gl_representation);
     auto* texture = gl_representation->GetTexture(/*plane_index=*/0);
     EXPECT_TRUE(texture->service_id());
@@ -429,7 +355,7 @@
   // Next, validate a GLTexturePassthroughImageRepresentation.
   if (use_passthrough()) {
     auto gl_representation =
-        shared_image_representation_factory_->ProduceGLTexturePassthrough(
+        shared_image_representation_factory_.ProduceGLTexturePassthrough(
             mailbox);
     EXPECT_TRUE(gl_representation);
     auto texture = gl_representation->GetTexturePassthrough(/*plane_index=*/0);
@@ -443,7 +369,7 @@
   }
 
   // Finally, validate a SkiaImageRepresentation.
-  auto skia_representation = shared_image_representation_factory_->ProduceSkia(
+  auto skia_representation = shared_image_representation_factory_.ProduceSkia(
       mailbox, context_state_.get());
   EXPECT_TRUE(skia_representation);
   std::vector<GrBackendSemaphore> begin_semaphores;
@@ -535,13 +461,12 @@
 
   // Validate via a GLTextureImageRepresentation(Passthrough).
   std::unique_ptr<SharedImageRepresentationFactoryRef> shared_image =
-      shared_image_manager_->Register(std::move(backing),
-                                      memory_type_tracker_.get());
+      shared_image_manager_.Register(std::move(backing), &memory_type_tracker_);
   EXPECT_TRUE(shared_image);
   GLenum expected_target = GL_TEXTURE_2D;
   if (!use_passthrough()) {
     auto gl_representation =
-        shared_image_representation_factory_->ProduceGLTexture(mailbox);
+        shared_image_representation_factory_.ProduceGLTexture(mailbox);
     EXPECT_TRUE(gl_representation);
     EXPECT_TRUE(gl_representation->GetTexture()->service_id());
     EXPECT_EQ(expected_target, gl_representation->GetTexture()->target());
@@ -552,7 +477,7 @@
     gl_representation.reset();
   } else {
     auto gl_representation =
-        shared_image_representation_factory_->ProduceGLTexturePassthrough(
+        shared_image_representation_factory_.ProduceGLTexturePassthrough(
             mailbox);
     EXPECT_TRUE(gl_representation);
     EXPECT_TRUE(gl_representation->GetTexturePassthrough()->service_id());
diff --git a/gpu/command_buffer/service/shared_image/iosurface_image_backing_factory_unittest.cc b/gpu/command_buffer/service/shared_image/iosurface_image_backing_factory_unittest.cc
index 2073a52..b8fda39d 100644
--- a/gpu/command_buffer/service/shared_image/iosurface_image_backing_factory_unittest.cc
+++ b/gpu/command_buffer/service/shared_image/iosurface_image_backing_factory_unittest.cc
@@ -16,6 +16,7 @@
 #include "gpu/command_buffer/service/shared_image/shared_image_format_utils.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_manager.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_representation.h"
+#include "gpu/command_buffer/service/shared_image/shared_image_test_base.h"
 #include "gpu/config/gpu_driver_bug_workarounds.h"
 #include "gpu/config/gpu_feature_info.h"
 #include "gpu/config/gpu_preferences.h"
@@ -30,10 +31,7 @@
 #include "third_party/skia/include/gpu/GrBackendSurface.h"
 #include "third_party/skia/include/gpu/ganesh/SkImageGanesh.h"
 #include "ui/gl/buildflags.h"
-#include "ui/gl/gl_context.h"
-#include "ui/gl/gl_surface.h"
-#include "ui/gl/gl_utils.h"
-#include "ui/gl/init/gl_factory.h"
+#include "ui/gl/gl_bindings.h"
 #include "ui/gl/progress_reporter.h"
 
 #if BUILDFLAG(USE_DAWN)
@@ -54,62 +52,27 @@
 
 }  // namespace
 
-class IOSurfaceImageBackingFactoryTest : public testing::Test {
+class IOSurfaceImageBackingFactoryTest : public SharedImageTestBase {
  public:
   void SetUp() override {
-    surface_ = gl::init::CreateOffscreenGLSurface(gl::GetDefaultDisplayEGL(),
-                                                  gfx::Size());
-    ASSERT_TRUE(surface_);
-    context_ = gl::init::CreateGLContext(nullptr, surface_.get(),
-                                         gl::GLContextAttribs());
-    ASSERT_TRUE(context_);
-    bool result = context_->MakeCurrent(surface_.get());
-    ASSERT_TRUE(result);
-
-    GpuPreferences preferences;
-    preferences.use_passthrough_cmd_decoder = true;
-    preferences.texture_target_exception_list.push_back(
+    ASSERT_TRUE(gpu_preferences_.use_passthrough_cmd_decoder);
+    gpu_preferences_.texture_target_exception_list.push_back(
         gfx::BufferUsageAndFormat(gfx::BufferUsage::SCANOUT,
                                   gfx::BufferFormat::RGBA_8888));
 
-    GpuDriverBugWorkarounds workarounds;
-    scoped_refptr<gl::GLShareGroup> share_group = new gl::GLShareGroup();
-    context_state_ = base::MakeRefCounted<SharedContextState>(
-        std::move(share_group), surface_, context_,
-        /*use_virtualized_gl_contexts=*/false, base::DoNothing());
-    context_state_->InitializeGrContext(preferences, workarounds, nullptr);
-    auto feature_info =
-        base::MakeRefCounted<gles2::FeatureInfo>(workarounds, GpuFeatureInfo());
-    context_state_->InitializeGL(preferences, std::move(feature_info));
+    ASSERT_NO_FATAL_FAILURE(InitializeContext(GrContextType::kGL));
 
     backing_factory_ = std::make_unique<IOSurfaceImageBackingFactory>(
-        preferences, workarounds, context_state_->feature_info(),
+        gpu_preferences_, gpu_workarounds_, context_state_->feature_info(),
         /*progress_reporter=*/nullptr);
-
-    memory_type_tracker_ = std::make_unique<MemoryTypeTracker>(nullptr);
-    shared_image_representation_factory_ =
-        std::make_unique<SharedImageRepresentationFactory>(
-            &shared_image_manager_, nullptr);
   }
 
-  GrDirectContext* gr_context() { return context_state_->gr_context(); }
-
  protected:
-  scoped_refptr<gl::GLSurface> surface_;
-  scoped_refptr<gl::GLContext> context_;
-  scoped_refptr<SharedContextState> context_state_;
-  std::unique_ptr<IOSurfaceImageBackingFactory> backing_factory_;
-  SharedImageManager shared_image_manager_;
-  std::unique_ptr<MemoryTypeTracker> memory_type_tracker_;
-  std::unique_ptr<SharedImageRepresentationFactory>
-      shared_image_representation_factory_;
-
   void CheckSkiaPixels(const Mailbox& mailbox,
                        const gfx::Size& size,
                        const std::vector<uint8_t> expected_color) {
-    auto skia_representation =
-        shared_image_representation_factory_->ProduceSkia(mailbox,
-                                                          context_state_);
+    auto skia_representation = shared_image_representation_factory_.ProduceSkia(
+        mailbox, context_state_);
     ASSERT_NE(skia_representation, nullptr);
 
     std::unique_ptr<SkiaImageRepresentation::ScopedReadAccess>
@@ -172,13 +135,12 @@
 
   GLenum expected_target = gpu::GetPlatformSpecificTextureTarget();
   std::unique_ptr<SharedImageRepresentationFactoryRef> factory_ref =
-      shared_image_manager_.Register(std::move(backing),
-                                     memory_type_tracker_.get());
+      shared_image_manager_.Register(std::move(backing), &memory_type_tracker_);
 
   // Create a GLTextureImageRepresentation.
   {
     auto gl_representation =
-        shared_image_representation_factory_->ProduceGLTexturePassthrough(
+        shared_image_representation_factory_.ProduceGLTexturePassthrough(
             mailbox);
     EXPECT_TRUE(gl_representation);
     EXPECT_EQ(expected_target,
@@ -251,11 +213,10 @@
   EXPECT_TRUE(backing);
 
   std::unique_ptr<SharedImageRepresentationFactoryRef> factory_ref =
-      shared_image_manager_.Register(std::move(backing),
-                                     memory_type_tracker_.get());
+      shared_image_manager_.Register(std::move(backing), &memory_type_tracker_);
 
   // Create a DawnImageRepresentation.
-  auto dawn_representation = shared_image_representation_factory_->ProduceDawn(
+  auto dawn_representation = shared_image_representation_factory_.ProduceDawn(
       mailbox, device.Get(), WGPUBackendType_Metal, {});
   EXPECT_TRUE(dawn_representation);
 
@@ -319,13 +280,12 @@
 
   GLenum expected_target = GL_TEXTURE_RECTANGLE;
   std::unique_ptr<SharedImageRepresentationFactoryRef> factory_ref =
-      shared_image_manager_.Register(std::move(backing),
-                                     memory_type_tracker_.get());
+      shared_image_manager_.Register(std::move(backing), &memory_type_tracker_);
 
   {
     // Create a GLTextureImageRepresentation.
     auto gl_representation =
-        shared_image_representation_factory_->ProduceGLTexturePassthrough(
+        shared_image_representation_factory_.ProduceGLTexturePassthrough(
             mailbox);
     EXPECT_TRUE(gl_representation);
     EXPECT_EQ(expected_target,
@@ -380,9 +340,8 @@
   DawnProcTable procs = dawn::native::GetProcs();
   dawnProcSetProcs(&procs);
   {
-    auto dawn_representation =
-        shared_image_representation_factory_->ProduceDawn(
-            mailbox, device.Get(), WGPUBackendType_Metal, {});
+    auto dawn_representation = shared_image_representation_factory_.ProduceDawn(
+        mailbox, device.Get(), WGPUBackendType_Metal, {});
     ASSERT_TRUE(dawn_representation);
 
     auto dawn_scoped_access = dawn_representation->BeginScopedAccess(
@@ -444,8 +403,7 @@
   ASSERT_NE(backing, nullptr);
 
   std::unique_ptr<SharedImageRepresentationFactoryRef> factory_ref =
-      shared_image_manager_.Register(std::move(backing),
-                                     memory_type_tracker_.get());
+      shared_image_manager_.Register(std::move(backing), &memory_type_tracker_);
 
   // Create dawn device
   dawn::native::Instance instance;
@@ -470,9 +428,8 @@
   DawnProcTable procs = dawn::native::GetProcs();
   dawnProcSetProcs(&procs);
   {
-    auto dawn_representation =
-        shared_image_representation_factory_->ProduceDawn(
-            mailbox, device.Get(), WGPUBackendType_Metal, {});
+    auto dawn_representation = shared_image_representation_factory_.ProduceDawn(
+        mailbox, device.Get(), WGPUBackendType_Metal, {});
     ASSERT_TRUE(dawn_representation);
 
     auto dawn_scoped_access = dawn_representation->BeginScopedAccess(
@@ -509,8 +466,8 @@
   EXPECT_FALSE(factory_ref->IsCleared());
 
   // Produce skia representation
-  auto skia_representation = shared_image_representation_factory_->ProduceSkia(
-      mailbox, context_state_);
+  auto skia_representation =
+      shared_image_representation_factory_.ProduceSkia(mailbox, context_state_);
   ASSERT_NE(skia_representation, nullptr);
 
   // Expect BeginScopedReadAccess to fail because sharedImage is uninitialized
@@ -538,11 +495,10 @@
   ASSERT_NE(backing, nullptr);
 
   std::unique_ptr<SharedImageRepresentationFactoryRef> factory_ref =
-      shared_image_manager_.Register(std::move(backing),
-                                     memory_type_tracker_.get());
+      shared_image_manager_.Register(std::move(backing), &memory_type_tracker_);
 
-  auto skia_representation = shared_image_representation_factory_->ProduceSkia(
-      mailbox, context_state_);
+  auto skia_representation =
+      shared_image_representation_factory_.ProduceSkia(mailbox, context_state_);
   ASSERT_NE(skia_representation, nullptr);
   EXPECT_FALSE(skia_representation->IsCleared());
 
@@ -553,30 +509,6 @@
   EXPECT_EQ(scoped_read_access, nullptr);
 }
 
-void CreateSharedContext(const GpuDriverBugWorkarounds& workarounds,
-                         scoped_refptr<gl::GLSurface>& surface,
-                         scoped_refptr<gl::GLContext>& context,
-                         scoped_refptr<SharedContextState>& context_state,
-                         scoped_refptr<gles2::FeatureInfo>& feature_info) {
-  surface =
-      gl::init::CreateOffscreenGLSurface(gl::GetDefaultDisplay(), gfx::Size());
-  ASSERT_TRUE(surface);
-  context =
-      gl::init::CreateGLContext(nullptr, surface.get(), gl::GLContextAttribs());
-  ASSERT_TRUE(context);
-  bool result = context->MakeCurrent(surface.get());
-  ASSERT_TRUE(result);
-
-  scoped_refptr<gl::GLShareGroup> share_group = new gl::GLShareGroup();
-  feature_info =
-      base::MakeRefCounted<gles2::FeatureInfo>(workarounds, GpuFeatureInfo());
-  context_state = base::MakeRefCounted<SharedContextState>(
-      std::move(share_group), surface, context,
-      /*use_virtualized_gl_contexts=*/false, base::DoNothing());
-  context_state->InitializeGrContext(GpuPreferences(), workarounds, nullptr);
-  context_state->InitializeGL(GpuPreferences(), feature_info);
-}
-
 class MockProgressReporter : public gl::ProgressReporter {
  public:
   MockProgressReporter() = default;
@@ -587,21 +519,18 @@
 };
 
 class IOSurfaceImageBackingFactoryWithFormatTestBase
-    : public testing::TestWithParam<viz::SharedImageFormat> {
+    : public SharedImageTestBase,
+      public testing::WithParamInterface<viz::SharedImageFormat> {
  public:
-  explicit IOSurfaceImageBackingFactoryWithFormatTestBase()
-      : shared_image_manager_(
-            std::make_unique<SharedImageManager>(/*thread_safe=*/false)) {}
-  ~IOSurfaceImageBackingFactoryWithFormatTestBase() override {
-    // |context_state_| must be destroyed on its own context.
-    context_state_->MakeCurrent(surface_.get(), /*needs_gl=*/true);
-  }
+  IOSurfaceImageBackingFactoryWithFormatTestBase() = default;
+  ~IOSurfaceImageBackingFactoryWithFormatTestBase() override = default;
 
   void SetUp() override {
-    GpuDriverBugWorkarounds workarounds;
-    scoped_refptr<gles2::FeatureInfo> feature_info;
-    CreateSharedContext(workarounds, surface_, context_, context_state_,
-                        feature_info);
+    ASSERT_TRUE(gpu_preferences_.use_passthrough_cmd_decoder);
+
+    ASSERT_NO_FATAL_FAILURE(InitializeContext(GrContextType::kGL));
+    auto* feature_info = context_state_->feature_info();
+
     supports_etc1_ =
         feature_info->validators()->compressed_texture_format.IsValid(
             GL_ETC1_RGB8_OES);
@@ -612,30 +541,15 @@
     supports_ycbcr_p010_ =
         feature_info->feature_flags().chromium_image_ycbcr_p010;
 
-    GpuPreferences preferences;
-    preferences.use_passthrough_cmd_decoder = true;
     backing_factory_ = std::make_unique<IOSurfaceImageBackingFactory>(
-        preferences, workarounds, context_state_->feature_info(),
+        gpu_preferences_, gpu_workarounds_, context_state_->feature_info(),
         &progress_reporter_);
-
-    memory_type_tracker_ = std::make_unique<MemoryTypeTracker>(nullptr);
-    shared_image_representation_factory_ =
-        std::make_unique<SharedImageRepresentationFactory>(
-            shared_image_manager_.get(), nullptr);
   }
 
   viz::SharedImageFormat get_format() { return GetParam(); }
 
  protected:
   ::testing::NiceMock<MockProgressReporter> progress_reporter_;
-  scoped_refptr<gl::GLSurface> surface_;
-  scoped_refptr<gl::GLContext> context_;
-  scoped_refptr<SharedContextState> context_state_;
-  std::unique_ptr<IOSurfaceImageBackingFactory> backing_factory_;
-  std::unique_ptr<SharedImageManager> shared_image_manager_;
-  std::unique_ptr<MemoryTypeTracker> memory_type_tracker_;
-  std::unique_ptr<SharedImageRepresentationFactory>
-      shared_image_representation_factory_;
   bool supports_etc1_ = false;
   bool supports_ar30_ = false;
   bool supports_ab30_ = false;
@@ -691,12 +605,11 @@
 
   // First, validate a GLTexturePassthroughImageRepresentation.
   std::unique_ptr<SharedImageRepresentationFactoryRef> shared_image =
-      shared_image_manager_->Register(std::move(backing),
-                                      memory_type_tracker_.get());
+      shared_image_manager_.Register(std::move(backing), &memory_type_tracker_);
   EXPECT_TRUE(shared_image);
   {
     auto gl_representation =
-        shared_image_representation_factory_->ProduceGLTexturePassthrough(
+        shared_image_representation_factory_.ProduceGLTexturePassthrough(
             mailbox);
     EXPECT_TRUE(gl_representation);
     EXPECT_TRUE(gl_representation->GetTexturePassthrough()->service_id());
@@ -713,7 +626,7 @@
   }
 
   // Finally, validate a SkiaImageRepresentation.
-  auto skia_representation = shared_image_representation_factory_->ProduceSkia(
+  auto skia_representation = shared_image_representation_factory_.ProduceSkia(
       mailbox, context_state_.get());
   EXPECT_TRUE(skia_representation);
   std::vector<GrBackendSemaphore> begin_semaphores;
@@ -777,14 +690,13 @@
 
   // Validate via a GLTextureImageRepresentation(Passthrough).
   std::unique_ptr<SharedImageRepresentationFactoryRef> shared_image =
-      shared_image_manager_->Register(std::move(backing),
-                                      memory_type_tracker_.get());
+      shared_image_manager_.Register(std::move(backing), &memory_type_tracker_);
   EXPECT_TRUE(shared_image);
   GLenum expected_target = gpu::GetPlatformSpecificTextureTarget();
 
   {
     auto gl_representation =
-        shared_image_representation_factory_->ProduceGLTexturePassthrough(
+        shared_image_representation_factory_.ProduceGLTexturePassthrough(
             mailbox);
     EXPECT_TRUE(gl_representation);
     EXPECT_TRUE(gl_representation->GetTexturePassthrough()->service_id());
@@ -822,12 +734,11 @@
 
   // Validate via a GLTextureImageRepresentation(Passthrough).
   std::unique_ptr<SharedImageRepresentationFactoryRef> shared_image =
-      shared_image_manager_->Register(std::move(backing),
-                                      memory_type_tracker_.get());
+      shared_image_manager_.Register(std::move(backing), &memory_type_tracker_);
   EXPECT_TRUE(shared_image);
   {
     auto gl_representation =
-        shared_image_representation_factory_->ProduceGLTexturePassthrough(
+        shared_image_representation_factory_.ProduceGLTexturePassthrough(
             mailbox);
     EXPECT_TRUE(gl_representation);
     EXPECT_TRUE(gl_representation->GetTexturePassthrough()->service_id());
@@ -923,9 +834,8 @@
   EXPECT_GT(backing_estimated_size, 0u);
 
   std::unique_ptr<SharedImageRepresentationFactoryRef> shared_image =
-      shared_image_manager_->Register(std::move(backing),
-                                      memory_type_tracker_.get());
-  EXPECT_EQ(backing_estimated_size, memory_type_tracker_->GetMemRepresented());
+      shared_image_manager_.Register(std::move(backing), &memory_type_tracker_);
+  EXPECT_EQ(backing_estimated_size, memory_type_tracker_.GetMemRepresented());
 
   shared_image.reset();
 }
@@ -1035,12 +945,11 @@
 
   // First, validate a GLTexturePassthroughImageRepresentation.
   std::unique_ptr<SharedImageRepresentationFactoryRef> shared_image =
-      shared_image_manager_->Register(std::move(backing),
-                                      memory_type_tracker_.get());
+      shared_image_manager_.Register(std::move(backing), &memory_type_tracker_);
   EXPECT_TRUE(shared_image);
   {
     auto gl_representation =
-        shared_image_representation_factory_->ProduceGLTexturePassthrough(
+        shared_image_representation_factory_.ProduceGLTexturePassthrough(
             mailbox);
     EXPECT_TRUE(gl_representation);
     for (int plane = 0; plane < format.NumberOfPlanes(); plane++) {
@@ -1055,7 +964,7 @@
   }
 
   // Finally, validate a SkiaImageRepresentation.
-  auto skia_representation = shared_image_representation_factory_->ProduceSkia(
+  auto skia_representation = shared_image_representation_factory_.ProduceSkia(
       mailbox, context_state_.get());
   EXPECT_TRUE(skia_representation);
   std::vector<GrBackendSemaphore> begin_semaphores;
diff --git a/gpu/command_buffer/service/shared_image/shared_image_test_base.cc b/gpu/command_buffer/service/shared_image/shared_image_test_base.cc
new file mode 100644
index 0000000..4554f3a5
--- /dev/null
+++ b/gpu/command_buffer/service/shared_image/shared_image_test_base.cc
@@ -0,0 +1,107 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "gpu/command_buffer/service/shared_image/shared_image_test_base.h"
+
+#include "base/command_line.h"
+#include "gpu/command_buffer/service/service_utils.h"
+#include "ui/gl/gl_context.h"
+#include "ui/gl/gl_surface.h"
+#include "ui/gl/gl_utils.h"
+#include "ui/gl/init/gl_factory.h"
+
+#if BUILDFLAG(ENABLE_VULKAN)
+#include "components/viz/common/gpu/vulkan_in_process_context_provider.h"
+#include "gpu/vulkan/init/vulkan_factory.h"
+#include "gpu/vulkan/vulkan_implementation.h"
+#endif
+
+namespace gpu {
+
+// static
+SkBitmap SharedImageTestBase::MakeRedBitmap(SkColorType color_type,
+                                            const gfx::Size& size) {
+  SkBitmap bitmap;
+  bitmap.allocPixels(SkImageInfo::Make(size.width(), size.height(), color_type,
+                                       kOpaque_SkAlphaType));
+
+  bitmap.eraseColor(SK_ColorRED);
+  return bitmap;
+}
+
+// static
+std::vector<SkPixmap> SharedImageTestBase::GetSkPixmaps(
+    const std::vector<SkBitmap>& bitmaps) {
+  std::vector<SkPixmap> pixmaps;
+  for (auto& bitmap : bitmaps) {
+    pixmaps.push_back(bitmap.pixmap());
+  }
+  return pixmaps;
+}
+
+SharedImageTestBase::SharedImageTestBase() {
+  gpu_preferences_.use_passthrough_cmd_decoder =
+      gles2::UsePassthroughCommandDecoder(
+          base::CommandLine::ForCurrentProcess()) &&
+      gles2::PassthroughCommandDecoderSupported();
+}
+
+SharedImageTestBase::~SharedImageTestBase() {
+  if (context_state_) {
+    // |context_state_| must be destroyed while current.
+    context_state_->MakeCurrent(gl_surface_.get(), /*needs_gl=*/true);
+  }
+}
+
+bool SharedImageTestBase::use_passthrough() const {
+  return gpu_preferences_.use_passthrough_cmd_decoder;
+}
+
+GrDirectContext* SharedImageTestBase::gr_context() {
+  return context_state_->gr_context();
+}
+
+void SharedImageTestBase::InitializeContext(GrContextType context_type) {
+  if (context_type == GrContextType::kVulkan) {
+#if BUILDFLAG(ENABLE_VULKAN)
+    vulkan_implementation_ = gpu::CreateVulkanImplementation();
+    ASSERT_TRUE(vulkan_implementation_);
+    ASSERT_TRUE(vulkan_implementation_->InitializeVulkanInstance());
+    vulkan_context_provider_ = viz::VulkanInProcessContextProvider::Create(
+        vulkan_implementation_.get());
+    ASSERT_TRUE(vulkan_context_provider_);
+#else
+    FAIL() << "Vulkan not available";
+#endif
+  }
+
+  // Set up a GL context. Even if the GrContext is Vulkan it's still needed.
+  gl_surface_ = gl::init::CreateOffscreenGLSurface(gl::GetDefaultDisplayEGL(),
+                                                   gfx::Size());
+  ASSERT_TRUE(gl_surface_);
+  gl_context_ = gl::init::CreateGLContext(nullptr, gl_surface_.get(),
+                                          gl::GLContextAttribs());
+  ASSERT_TRUE(gl_context_);
+  bool make_current_result = gl_context_->MakeCurrent(gl_surface_.get());
+  ASSERT_TRUE(make_current_result);
+
+  context_state_ = base::MakeRefCounted<SharedContextState>(
+      base::MakeRefCounted<gl::GLShareGroup>(), gl_surface_, gl_context_,
+      /*use_virtualized_gl_contexts=*/false, base::DoNothing(), context_type,
+#if BUILDFLAG(ENABLE_VULKAN)
+      vulkan_context_provider_.get()
+#endif
+  );
+
+  bool initialize_gl = context_state_->InitializeGL(
+      gpu_preferences_, base::MakeRefCounted<gles2::FeatureInfo>(
+                            gpu_workarounds_, GpuFeatureInfo()));
+  ASSERT_TRUE(initialize_gl);
+
+  bool initialize_gr = context_state_->InitializeGrContext(
+      gpu_preferences_, gpu_workarounds_, nullptr);
+  ASSERT_TRUE(initialize_gr);
+}
+
+}  // namespace gpu
diff --git a/gpu/command_buffer/service/shared_image/shared_image_test_base.h b/gpu/command_buffer/service/shared_image/shared_image_test_base.h
new file mode 100644
index 0000000..bcc51531
--- /dev/null
+++ b/gpu/command_buffer/service/shared_image/shared_image_test_base.h
@@ -0,0 +1,73 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef GPU_COMMAND_BUFFER_SERVICE_SHARED_IMAGE_SHARED_IMAGE_TEST_BASE_H_
+#define GPU_COMMAND_BUFFER_SERVICE_SHARED_IMAGE_SHARED_IMAGE_TEST_BASE_H_
+
+#include "gpu/command_buffer/service/shared_context_state.h"
+#include "gpu/command_buffer/service/shared_image/shared_image_backing_factory.h"
+#include "gpu/command_buffer/service/shared_image/shared_image_factory.h"
+#include "gpu/command_buffer/service/shared_image/shared_image_manager.h"
+#include "gpu/config/gpu_driver_bug_workarounds.h"
+#include "gpu/config/gpu_preferences.h"
+#include "gpu/vulkan/buildflags.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+
+namespace viz {
+class VulkanInProcessContextProvider;
+}
+
+namespace gpu {
+
+class VulkanImplementation;
+
+// Test base that initializes SharedContextState and other classes needed to
+// create SharedImageBackings + SharedImageBackingFactories.
+class SharedImageTestBase : public testing::Test {
+ protected:
+  // Allocate a bitmap with red pixels. RED_8 will be filled with 0xFF repeating
+  // and RG_88 will be filled with OxFF00 repeating.
+  static SkBitmap MakeRedBitmap(SkColorType color_type, const gfx::Size& size);
+
+  // Returns SkPixmap from each SkBitmap.
+  static std::vector<SkPixmap> GetSkPixmaps(
+      const std::vector<SkBitmap>& bitmaps);
+
+  SharedImageTestBase();
+  ~SharedImageTestBase() override;
+
+  bool use_passthrough() const;
+  GrDirectContext* gr_context();
+
+  // Initializes `context_state_` for `context_type`. Expected to be called as
+  // part of test SetUp(). Note this function can fail with an assertion error
+  // so caller should wrap call in ASSERT_NO_FATAL_FAILURE() to ensure SetUp()
+  // exits on error.
+  void InitializeContext(GrContextType context_type);
+
+  GpuPreferences gpu_preferences_;
+  GpuDriverBugWorkarounds gpu_workarounds_;
+
+#if BUILDFLAG(ENABLE_VULKAN)
+  std::unique_ptr<VulkanImplementation> vulkan_implementation_;
+  scoped_refptr<viz::VulkanInProcessContextProvider> vulkan_context_provider_;
+#endif
+
+  scoped_refptr<gl::GLSurface> gl_surface_;
+  scoped_refptr<gl::GLContext> gl_context_;
+  scoped_refptr<SharedContextState> context_state_;
+
+  MemoryTypeTracker memory_type_tracker_{nullptr};
+  SharedImageManager shared_image_manager_{/*thread_safe=*/false};
+  SharedImageRepresentationFactory shared_image_representation_factory_{
+      &shared_image_manager_, nullptr};
+
+  // To be initialized by the test implementation.
+  std::unique_ptr<SharedImageBackingFactory> backing_factory_;
+};
+
+}  // namespace gpu
+
+#endif  // GPU_COMMAND_BUFFER_SERVICE_SHARED_IMAGE_SHARED_IMAGE_TEST_BASE_H_
diff --git a/gpu/command_buffer/service/shared_image/wrapped_sk_image_backing_factory_unittest.cc b/gpu/command_buffer/service/shared_image/wrapped_sk_image_backing_factory_unittest.cc
index bbeba3e..dadac79 100644
--- a/gpu/command_buffer/service/shared_image/wrapped_sk_image_backing_factory_unittest.cc
+++ b/gpu/command_buffer/service/shared_image/wrapped_sk_image_backing_factory_unittest.cc
@@ -17,7 +17,7 @@
 #include "gpu/command_buffer/service/shared_image/shared_image_factory.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_manager.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_representation.h"
-#include "gpu/config/gpu_driver_bug_workarounds.h"
+#include "gpu/command_buffer/service/shared_image/shared_image_test_base.h"
 #include "gpu/config/gpu_feature_info.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/skia/include/core/SkAlphaType.h"
@@ -28,11 +28,6 @@
 #include "third_party/skia/include/core/SkPromiseImageTexture.h"
 #include "third_party/skia/include/gpu/GrBackendSemaphore.h"
 #include "third_party/skia/include/gpu/ganesh/SkImageGanesh.h"
-#include "ui/gl/gl_context.h"
-#include "ui/gl/gl_share_group.h"
-#include "ui/gl/gl_surface.h"
-#include "ui/gl/gl_utils.h"
-#include "ui/gl/init/gl_factory.h"
 
 namespace gpu {
 namespace {
@@ -44,85 +39,28 @@
                             SHARED_IMAGE_USAGE_RASTER |
                             SHARED_IMAGE_USAGE_CPU_UPLOAD;
 
-// Allocate a bitmap with red pixels. RED_8 will be filled with 0xFF repeating
-// and RG_88 will be filled with OxFF00 repeating.
-SkBitmap MakeRedBitmap(SkColorType color_type, const gfx::Size& size) {
-  SkBitmap bitmap;
-  bitmap.allocPixels(SkImageInfo::Make(size.width(), size.height(), color_type,
-                                       kOpaque_SkAlphaType));
-
-  bitmap.eraseColor(SK_ColorRED);
-  return bitmap;
-}
-
-std::vector<SkPixmap> GetSkPixmaps(const std::vector<SkBitmap>& bitmaps) {
-  std::vector<SkPixmap> pixmaps;
-  for (auto& bitmap : bitmaps) {
-    pixmaps.push_back(bitmap.pixmap());
-  }
-  return pixmaps;
-}
-
 class WrappedSkImageBackingFactoryTest
-    : public testing::TestWithParam<viz::SharedImageFormat> {
+    : public SharedImageTestBase,
+      public testing::WithParamInterface<viz::SharedImageFormat> {
  public:
   WrappedSkImageBackingFactoryTest() = default;
-  ~WrappedSkImageBackingFactoryTest() override {
-    if (context_state_) {
-      // |context_state_| must be destroyed while current.
-      context_state_->MakeCurrent(surface_.get(), /*needs_gl=*/true);
-    }
-  }
+  ~WrappedSkImageBackingFactoryTest() override = default;
 
   viz::SharedImageFormat GetFormat() { return GetParam(); }
 
   void SetUp() override {
-    GpuPreferences preferences;
-
     // We don't use WrappedSkImage with ALPHA8 if it's GL context.
     // Note, that `gr_context_type` is not wired right now and is always GL.
     if (GetFormat() == viz::SinglePlaneFormat::kALPHA_8 &&
-        preferences.gr_context_type == GrContextType::kGL) {
+        gpu_preferences_.gr_context_type == GrContextType::kGL) {
       GTEST_SKIP();
     }
 
-    surface_ = gl::init::CreateOffscreenGLSurface(gl::GetDefaultDisplay(),
-                                                  gfx::Size());
-    ASSERT_TRUE(surface_);
-
-    auto context = gl::init::CreateGLContext(nullptr, surface_.get(),
-                                             gl::GLContextAttribs());
-    ASSERT_TRUE(context);
-    bool result = context->MakeCurrent(surface_.get());
-    ASSERT_TRUE(result);
-
-    context_state_ = base::MakeRefCounted<SharedContextState>(
-        base::MakeRefCounted<gl::GLShareGroup>(), surface_, std::move(context),
-        /*use_virtualized_gl_contexts=*/false, base::DoNothing());
-
-    GpuDriverBugWorkarounds workarounds;
-    scoped_refptr<gles2::FeatureInfo> feature_info =
-        base::MakeRefCounted<gles2::FeatureInfo>(workarounds, GpuFeatureInfo());
-    ASSERT_TRUE(
-        context_state_->InitializeGrContext(preferences, workarounds, nullptr));
-    ASSERT_TRUE(context_state_->InitializeGL(preferences, feature_info));
+    ASSERT_NO_FATAL_FAILURE(InitializeContext(GrContextType::kGL));
 
     backing_factory_ =
         std::make_unique<WrappedSkImageBackingFactory>(context_state_);
-
-    shared_image_representation_factory_ =
-        std::make_unique<SharedImageRepresentationFactory>(
-            &shared_image_manager_, nullptr);
   }
-
- protected:
-  MemoryTypeTracker memory_type_tracker_{nullptr};
-  SharedImageManager shared_image_manager_{/*thread_safe=*/false};
-  scoped_refptr<gl::GLSurface> surface_;
-  scoped_refptr<SharedContextState> context_state_;
-  std::unique_ptr<WrappedSkImageBackingFactory> backing_factory_;
-  std::unique_ptr<SharedImageRepresentationFactory>
-      shared_image_representation_factory_;
 };
 
 // Verify creation and Skia access works as expected.
@@ -145,7 +83,7 @@
       shared_image_manager_.Register(std::move(backing), &memory_type_tracker_);
 
   // Validate SkiaImageRepresentation works.
-  auto skia_representation = shared_image_representation_factory_->ProduceSkia(
+  auto skia_representation = shared_image_representation_factory_.ProduceSkia(
       mailbox, context_state_.get());
   EXPECT_TRUE(skia_representation);
 
@@ -213,7 +151,7 @@
       shared_image_manager_.Register(std::move(backing), &memory_type_tracker_);
 
   // Validate SkiaImageRepresentation works.
-  auto skia_representation = shared_image_representation_factory_->ProduceSkia(
+  auto skia_representation = shared_image_representation_factory_.ProduceSkia(
       mailbox, context_state_.get());
   EXPECT_TRUE(skia_representation);
   std::vector<GrBackendSemaphore> begin_semaphores;
diff --git a/headless/lib/browser/headless_browser_context_impl.cc b/headless/lib/browser/headless_browser_context_impl.cc
index 55fb6b58c..9b7dd4c 100644
--- a/headless/lib/browser/headless_browser_context_impl.cc
+++ b/headless/lib/browser/headless_browser_context_impl.cc
@@ -156,50 +156,6 @@
   return result;
 }
 
-void HeadlessBrowserContextImpl::SetDevToolsFrameToken(
-    int render_process_id,
-    int render_frame_routing_id,
-    const base::UnguessableToken& devtools_frame_token,
-    int frame_tree_node_id) {
-  base::AutoLock lock(devtools_frame_token_map_lock_);
-  devtools_frame_token_map_[content::GlobalRenderFrameHostId(
-      render_process_id, render_frame_routing_id)] = devtools_frame_token;
-  frame_tree_node_id_to_devtools_frame_token_map_[frame_tree_node_id] =
-      devtools_frame_token;
-}
-
-void HeadlessBrowserContextImpl::RemoveDevToolsFrameToken(
-    int render_process_id,
-    int render_frame_routing_id,
-    int frame_tree_node_id) {
-  base::AutoLock lock(devtools_frame_token_map_lock_);
-  devtools_frame_token_map_.erase(content::GlobalRenderFrameHostId(
-      render_process_id, render_frame_routing_id));
-  frame_tree_node_id_to_devtools_frame_token_map_.erase(frame_tree_node_id);
-}
-
-const base::UnguessableToken* HeadlessBrowserContextImpl::GetDevToolsFrameToken(
-    int render_process_id,
-    int render_frame_id) const {
-  base::AutoLock lock(devtools_frame_token_map_lock_);
-  const auto& find_it = devtools_frame_token_map_.find(
-      content::GlobalRenderFrameHostId(render_process_id, render_frame_id));
-  if (find_it == devtools_frame_token_map_.end())
-    return nullptr;
-  return &find_it->second;
-}
-
-const base::UnguessableToken*
-HeadlessBrowserContextImpl::GetDevToolsFrameTokenForFrameTreeNodeId(
-    int frame_tree_node_id) const {
-  base::AutoLock lock(devtools_frame_token_map_lock_);
-  const auto& find_it =
-      frame_tree_node_id_to_devtools_frame_token_map_.find(frame_tree_node_id);
-  if (find_it == frame_tree_node_id_to_devtools_frame_token_map_.end())
-    return nullptr;
-  return &find_it->second;
-}
-
 void HeadlessBrowserContextImpl::Close() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   browser_->DestroyBrowserContext(this);
diff --git a/headless/lib/browser/headless_browser_context_impl.h b/headless/lib/browser/headless_browser_context_impl.h
index 070e29b..bcd75e8 100644
--- a/headless/lib/browser/headless_browser_context_impl.h
+++ b/headless/lib/browser/headless_browser_context_impl.h
@@ -54,15 +54,6 @@
   void Close() override;
   const std::string& Id() override;
 
-  void SetDevToolsFrameToken(int render_process_id,
-                             int render_frame_routing_id,
-                             const base::UnguessableToken& devtools_frame_token,
-                             int frame_tree_node_id);
-
-  void RemoveDevToolsFrameToken(int render_process_id,
-                                int render_frame_routing_id,
-                                int frame_tree_node_id);
-
   // BrowserContext implementation:
   std::unique_ptr<content::ZoomLevelDelegate> CreateZoomLevelDelegate(
       const base::FilePath& partition_path) override;
@@ -100,17 +91,6 @@
   HeadlessBrowserImpl* browser() const;
   const HeadlessBrowserContextOptions* options() const;
 
-  // Returns the DevTools frame token for the corresponding RenderFrameHost or
-  // null if can't be found. Can be called on any thread.
-  const base::UnguessableToken* GetDevToolsFrameToken(
-      int render_process_id,
-      int render_frame_id) const;
-
-  // Returns the DevTools frame token for the corresponding frame tree node id
-  // or null if can't be found. Can be called on any thread.
-  const base::UnguessableToken* GetDevToolsFrameTokenForFrameTreeNodeId(
-      int frame_tree_node_id) const;
-
   void ConfigureNetworkContextParams(
       bool in_memory,
       const base::FilePath& relative_partition_path,
@@ -134,16 +114,6 @@
   std::unordered_map<std::string, std::unique_ptr<HeadlessWebContents>>
       web_contents_map_;
 
-  // Guards |devtools_frame_token_map_| from being concurrently written on the
-  // UI thread and read on the IO thread.
-  // TODO(alexclarke): Remove if we can add DevTools frame token ID to
-  // ResourceRequestInfo. See https://crbug.com/715541
-  mutable base::Lock devtools_frame_token_map_lock_;
-  base::flat_map<content::GlobalRenderFrameHostId, base::UnguessableToken>
-      devtools_frame_token_map_;
-  base::flat_map<int, base::UnguessableToken>
-      frame_tree_node_id_to_devtools_frame_token_map_;
-
   std::unique_ptr<content::PermissionControllerDelegate>
       permission_controller_delegate_;
 
diff --git a/headless/lib/browser/headless_web_contents_impl.cc b/headless/lib/browser/headless_web_contents_impl.cc
index 2bb690e0..95136ad 100644
--- a/headless/lib/browser/headless_web_contents_impl.cc
+++ b/headless/lib/browser/headless_web_contents_impl.cc
@@ -350,8 +350,6 @@
 }
 
 HeadlessWebContentsImpl::~HeadlessWebContentsImpl() {
-  for (auto& observer : observers_)
-    observer.HeadlessWebContentsDestroyed();
   agent_host_->RemoveObserver(this);
   if (render_process_host_)
     render_process_host_->RemoveObserver(this);
@@ -361,23 +359,6 @@
       FROM_HERE, std::move(window_tree_host_));
 }
 
-void HeadlessWebContentsImpl::RenderFrameCreated(
-    content::RenderFrameHost* render_frame_host) {
-  browser_context_->SetDevToolsFrameToken(
-      render_frame_host->GetProcess()->GetID(),
-      render_frame_host->GetRoutingID(),
-      render_frame_host->GetDevToolsFrameToken(),
-      render_frame_host->GetFrameTreeNodeId());
-}
-
-void HeadlessWebContentsImpl::RenderFrameDeleted(
-    content::RenderFrameHost* render_frame_host) {
-  browser_context_->RemoveDevToolsFrameToken(
-      render_frame_host->GetProcess()->GetID(),
-      render_frame_host->GetRoutingID(),
-      render_frame_host->GetFrameTreeNodeId());
-}
-
 void HeadlessWebContentsImpl::RenderViewReady() {
   DCHECK(web_contents()->GetPrimaryMainFrame()->IsRenderFrameLive());
 
@@ -390,27 +371,6 @@
   devtools_target_ready_notification_sent_ = true;
 }
 
-int HeadlessWebContentsImpl::GetMainFrameRenderProcessId() const {
-  if (!web_contents() || !web_contents()->GetPrimaryMainFrame())
-    return -1;
-  return web_contents()->GetPrimaryMainFrame()->GetProcess()->GetID();
-}
-
-int HeadlessWebContentsImpl::GetMainFrameTreeNodeId() const {
-  if (!web_contents() || !web_contents()->GetPrimaryMainFrame())
-    return -1;
-  return web_contents()->GetPrimaryMainFrame()->GetFrameTreeNodeId();
-}
-
-std::string HeadlessWebContentsImpl::GetMainFrameDevToolsId() const {
-  if (!web_contents() || !web_contents()->GetPrimaryMainFrame())
-    return "";
-  return web_contents()
-      ->GetPrimaryMainFrame()
-      ->GetDevToolsFrameToken()
-      .ToString();
-}
-
 bool HeadlessWebContentsImpl::OpenURL(const GURL& url) {
   if (!url.is_valid())
     return false;
@@ -440,23 +400,10 @@
   observers_.RemoveObserver(observer);
 }
 
-void HeadlessWebContentsImpl::DevToolsAgentHostAttached(
-    content::DevToolsAgentHost* agent_host) {
-  for (auto& observer : observers_)
-    observer.DevToolsClientAttached();
-}
-
-void HeadlessWebContentsImpl::DevToolsAgentHostDetached(
-    content::DevToolsAgentHost* agent_host) {
-  for (auto& observer : observers_)
-    observer.DevToolsClientDetached();
-}
-
 void HeadlessWebContentsImpl::RenderProcessExited(
     content::RenderProcessHost* host,
     const content::ChildProcessTerminationInfo& info) {
   DCHECK_EQ(render_process_host_, host);
-  render_process_exited_ = true;
   for (auto& observer : observers_)
     observer.RenderProcessExited(info.status, info.exit_code);
 }
diff --git a/headless/lib/browser/headless_web_contents_impl.h b/headless/lib/browser/headless_web_contents_impl.h
index b80147fd..e14cff8 100644
--- a/headless/lib/browser/headless_web_contents_impl.h
+++ b/headless/lib/browser/headless_web_contents_impl.h
@@ -6,7 +6,6 @@
 #define HEADLESS_LIB_BROWSER_HEADLESS_WEB_CONTENTS_IMPL_H_
 
 #include <memory>
-#include <set>
 #include <string>
 
 #include "base/memory/raw_ptr.h"
@@ -65,22 +64,12 @@
   void AddObserver(Observer* observer) override;
   void RemoveObserver(Observer* observer) override;
   HeadlessDevToolsTarget* GetDevToolsTarget() override;
-  int GetMainFrameRenderProcessId() const override;
-  int GetMainFrameTreeNodeId() const override;
-  std::string GetMainFrameDevToolsId() const override;
-  std::unique_ptr<HeadlessDevToolsChannel> CreateDevToolsChannel() override;
 
   // HeadlessDevToolsTarget implementation:
   void AttachClient(HeadlessDevToolsClient* client) override;
   void DetachClient(HeadlessDevToolsClient* client) override;
   bool IsAttached() override;
 
-  // content::DevToolsAgentHostObserver implementation:
-  void DevToolsAgentHostAttached(
-      content::DevToolsAgentHost* agent_host) override;
-  void DevToolsAgentHostDetached(
-      content::DevToolsAgentHost* agent_host) override;
-
   // content::RenderProcessHostObserver implementation:
   void RenderProcessExited(
       content::RenderProcessHost* host,
@@ -88,10 +77,9 @@
   void RenderProcessHostDestroyed(content::RenderProcessHost* host) override;
 
   // content::WebContentsObserver implementation:
-  void RenderFrameCreated(content::RenderFrameHost* render_frame_host) override;
-  void RenderFrameDeleted(content::RenderFrameHost* render_frame_host) override;
   void RenderViewReady() override;
 
+  std::unique_ptr<HeadlessDevToolsChannel> CreateDevToolsChannel();
   content::WebContents* web_contents() const;
   bool OpenURL(const GURL& url);
 
@@ -159,7 +147,6 @@
   std::unique_ptr<content::WebContents> web_contents_;
   scoped_refptr<content::DevToolsAgentHost> agent_host_;
   bool devtools_target_ready_notification_sent_ = false;
-  bool render_process_exited_ = false;
   bool use_tab_target_ = false;
 
   base::ObserverList<HeadlessWebContents::Observer>::Unchecked observers_;
diff --git a/headless/public/headless_web_contents.h b/headless/public/headless_web_contents.h
index 9661329d..a8d3209 100644
--- a/headless/public/headless_web_contents.h
+++ b/headless/public/headless_web_contents.h
@@ -5,13 +5,8 @@
 #ifndef HEADLESS_PUBLIC_HEADLESS_WEB_CONTENTS_H_
 #define HEADLESS_PUBLIC_HEADLESS_WEB_CONTENTS_H_
 
-#include <string>
-#include <utility>
-
-#include "base/functional/callback.h"
 #include "base/memory/raw_ptr.h"
 #include "base/process/kill.h"
-#include "headless/public/headless_devtools_channel.h"
 #include "headless/public/headless_export.h"
 #include "ui/gfx/geometry/size.h"
 #include "url/gurl.h"
@@ -44,15 +39,6 @@
     //
     // TODO(altimin): Support this event for pages that aren't created by us.
     virtual void DevToolsTargetReady() {}
-
-    // Indicates that a DevTools client attached to this HeadlessWebContents
-    // instance.
-    virtual void DevToolsClientAttached() {}
-
-    // Indicates that a DevTools client detached from this HeadlessWebContents
-    // instance.
-    virtual void DevToolsClientDetached() {}
-
     // This method is invoked when the process of the observed RenderProcessHost
     // exits (either normally or with a crash). To determine if the process
     // closed normally or crashed, examine the |status| parameter.
@@ -63,9 +49,6 @@
     virtual void RenderProcessExited(base::TerminationStatus status,
                                      int exit_code) {}
 
-    // Invoked when HeadlessWebContents is being destroyed.
-    virtual void HeadlessWebContentsDestroyed() {}
-
    protected:
     Observer() {}
     virtual ~Observer() {}
@@ -81,23 +64,9 @@
   // signaled.
   virtual HeadlessDevToolsTarget* GetDevToolsTarget() = 0;
 
-  // Creates a DevTools channel corresponding to this tab. Note that this method
-  // won't return a valid value until Observer::DevToolsTargetReady has been
-  // signaled.
-  virtual std::unique_ptr<HeadlessDevToolsChannel> CreateDevToolsChannel() = 0;
-
   // Close this page. |HeadlessWebContents| object will be destroyed.
   virtual void Close() = 0;
 
-  // Returns the main frame's process id or -1 if there's no main frame.
-  virtual int GetMainFrameRenderProcessId() const = 0;
-
-  // Returns the main frame's node id or -1 if there's no main frame.
-  virtual int GetMainFrameTreeNodeId() const = 0;
-
-  // Returns the main frame's devtools id or "" if there's no main frame.
-  virtual std::string GetMainFrameDevToolsId() const = 0;
-
  protected:
   HeadlessWebContents() {}
 };
diff --git a/headless/test/headless_devtools_client_browsertest.cc b/headless/test/headless_devtools_client_browsertest.cc
index 0fb6d87..b1c76379 100644
--- a/headless/test/headless_devtools_client_browsertest.cc
+++ b/headless/test/headless_devtools_client_browsertest.cc
@@ -539,29 +539,6 @@
 
 HEADLESS_DEVTOOLED_TEST_F(DevToolsNetworkOfflineEmulationTest);
 
-class DevToolsAttachAndDetachNotifications
-    : public HeadlessDevTooledBrowserTest {
- public:
-  void DevToolsClientAttached() override { dev_tools_client_attached_ = true; }
-
-  void RunDevTooledTest() override {
-    EXPECT_TRUE(dev_tools_client_attached_);
-    FinishAsynchronousTest();
-  }
-
-  void DevToolsClientDetached() override { dev_tools_client_detached_ = true; }
-
-  void TearDownOnMainThread() override {
-    EXPECT_TRUE(dev_tools_client_detached_);
-  }
-
- private:
-  bool dev_tools_client_attached_ = false;
-  bool dev_tools_client_detached_ = false;
-};
-
-HEADLESS_DEVTOOLED_TEST_F(DevToolsAttachAndDetachNotifications);
-
 class DomTreeExtractionBrowserTest : public HeadlessDevTooledBrowserTest {
  public:
   void RunDevTooledTest() override {
diff --git a/ios/chrome/browser/autofill/form_structure_browsertest.mm b/ios/chrome/browser/autofill/form_structure_browsertest.mm
index b8d3ae6..1d79e83 100644
--- a/ios/chrome/browser/autofill/form_structure_browsertest.mm
+++ b/ios/chrome/browser/autofill/form_structure_browsertest.mm
@@ -45,6 +45,7 @@
 #import "ios/chrome/browser/web/chrome_web_client.h"
 #import "ios/web/public/js_messaging/web_frame.h"
 #import "ios/web/public/js_messaging/web_frames_manager.h"
+#import "ios/web/public/test/js_test_util.h"
 #import "ios/web/public/test/scoped_testing_web_client.h"
 #import "ios/web/public/test/task_observer_util.h"
 #import "ios/web/public/test/web_state_test_util.h"
@@ -278,11 +279,13 @@
     return false;
   }
 
+  autofill::FormUtilJavaScriptFeature* feature =
+      autofill::FormUtilJavaScriptFeature::GetInstance();
+
   __block web::WebFrame* main_frame = nullptr;
   success = WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^bool {
     web::WebFramesManager* frames_manager =
-        autofill::FormUtilJavaScriptFeature::GetInstance()->GetWebFramesManager(
-            web_state());
+        feature->GetWebFramesManager(web_state());
     main_frame = frames_manager->GetMainWebFrame();
     return main_frame != nullptr;
   });
@@ -292,14 +295,13 @@
   DCHECK(main_frame);
 
   uint32_t next_available_id = 1;
-  autofill::FormUtilJavaScriptFeature::GetInstance()
-      ->SetUpForUniqueIDsWithInitialState(main_frame, next_available_id);
+  feature->SetUpForUniqueIDsWithInitialState(main_frame, next_available_id);
 
   // Wait for `SetUpForUniqueIDsWithInitialState` to complete.
   return WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^bool {
-    return [web::test::ExecuteJavaScript(@"document[__gCrWeb.fill.ID_SYMBOL]",
-                                         web_state()) intValue] ==
-           static_cast<int>(next_available_id);
+    return [web::test::ExecuteJavaScriptForFeature(
+               web_state(), @"document[__gCrWeb.fill.ID_SYMBOL]", feature)
+               intValue] == static_cast<int>(next_available_id);
   });
 }
 
diff --git a/media/capture/video/chromeos/camera_device_delegate_unittest.cc b/media/capture/video/chromeos/camera_device_delegate_unittest.cc
index 645ac06e..8fe631f2 100644
--- a/media/capture/video/chromeos/camera_device_delegate_unittest.cc
+++ b/media/capture/video/chromeos/camera_device_delegate_unittest.cc
@@ -116,6 +116,11 @@
   MOCK_METHOD2(DoConfigureStreamsAndGetAllocatedBuffers,
                void(cros::mojom::Camera3StreamConfigurationPtr& config,
                     ConfigureStreamsAndGetAllocatedBuffersCallback& callback));
+
+  void SignalStreamFlush(const std::vector<uint64_t>& stream_ids) override {
+    DoSignalStreamFlush(stream_ids);
+  }
+  MOCK_METHOD1(DoSignalStreamFlush, void(std::vector<uint64_t> stream_ids));
 };
 
 constexpr int32_t kJpegMaxBufferSize = 1024;
diff --git a/media/capture/video/chromeos/mojom/camera3.mojom b/media/capture/video/chromeos/mojom/camera3.mojom
index 40b9c4a9..1a81d203 100644
--- a/media/capture/video/chromeos/mojom/camera3.mojom
+++ b/media/capture/video/chromeos/mojom/camera3.mojom
@@ -163,6 +163,35 @@
   Camera3NotifyMsgMessage message;
 };
 
+enum Camera3BufferRequestStatus {
+  CAMERA3_BUF_REQ_OK = 0,
+  CAMERA3_BUF_REQ_FAILED_PARTIAL = 1,
+  CAMERA3_BUF_REQ_FAILED_CONFIGURING = 2,
+  CAMERA3_BUF_REQ_FAILED_ILLEGAL_ARGUMENTS = 3,
+  CAMERA3_BUF_REQ_FAILED_UNKNOWN = 4,
+  CAMERA3_BUF_REQ_NUM_STATUS
+};
+
+enum Camera3StreamBufferReqStatus {
+  CAMERA3_PS_BUF_REQ_OK = 0,
+  CAMERA3_PS_BUF_REQ_NO_BUFFER_AVAILABLE = 1,
+  CAMERA3_PS_BUF_REQ_MAX_BUFFER_EXCEEDED = 2,
+  CAMERA3_PS_BUF_REQ_STREAM_DISCONNECTED = 3,
+  CAMERA3_PS_BUF_REQ_UNKNOWN_ERROR = 4,
+  CAMERA3_PS_BUF_REQ_NUM_STATUS
+};
+
+struct Camera3BufferRequest {
+  uint64 stream_id;
+  uint32 num_buffers_requested;
+};
+
+struct Camera3StreamBufferRet {
+  uint64 stream_id;
+  Camera3StreamBufferReqStatus status;
+  array<Camera3StreamBuffer>? output_buffers;
+};
+
 enum Camera3RequestTemplate {
   CAMERA3_TEMPLATE_PREVIEW = 1,
   CAMERA3_TEMPLATE_STILL_CAPTURE = 2,
@@ -204,7 +233,7 @@
 // in Android camera HAL v3.  For the work flow of the functions in
 // Camera3CallbackOps, see the comments about Camera3DeviceOps above.
 //
-// Next method ID: 2
+// Next method ID: 4
 interface Camera3CallbackOps {
   // ProcessCaptureResult() is called by the camera HAL to send result metadata
   // and filled buffer to the client.
@@ -213,6 +242,18 @@
   // Notify() is called by the camera HAL to notify the client of the start of
   // each capture, and of errors encountered.
   Notify@1(Camera3NotifyMsg msg);
+
+  // RequestStreamBuffers() is called by the camera HAL to request output
+  // buffers from the client.
+  [MinVersion=5]
+  RequestStreamBuffers@2(array<Camera3BufferRequest> buffer_reqs) =>
+      (Camera3BufferRequestStatus result,
+       array<Camera3StreamBufferRet>? returned_buf_reqs);
+
+  // ReturnStreamBuffers() is called by the camera HAL to return output buffers
+  // to the client.
+  [MinVersion=5]
+  ReturnStreamBuffers@3(array<Camera3StreamBuffer> buffers);
 };
 
 // Camera3DeviceOps is mostly a translation of the camera3_device_ops_t API from
@@ -269,7 +310,7 @@
 //
 //    7. Close() closes the camera device.
 //
-// Next method ID: 9
+// Next method ID: 10
 interface Camera3DeviceOps {
   // Initialize() is called once after the camera device is opened to register
   // the Camera3CallbackOps handle.
@@ -332,4 +373,11 @@
   ConfigureStreamsAndGetAllocatedBuffers@8(Camera3StreamConfiguration config) =>
       (int32 result, Camera3StreamConfiguration? updated_config,
        map<uint64, array<Camera3StreamBuffer>> allocated_buffers);
+
+  // SignalStreamFlush() can be called when the client is about to call
+  // ConfigureStreams(). The camera HAL must finish in-flight requests normally
+  // and return all buffers belonging to the designated streams through
+  // ProcessCaptureResult() or ReturnStreamBuffers().
+  [MinVersion=5]
+  SignalStreamFlush@9(array<uint64> stream_ids);
 };
diff --git a/media/capture/video/chromeos/request_manager.cc b/media/capture/video/chromeos/request_manager.cc
index 28e340f..d5576d8 100644
--- a/media/capture/video/chromeos/request_manager.cc
+++ b/media/capture/video/chromeos/request_manager.cc
@@ -885,6 +885,17 @@
   }
 }
 
+void RequestManager::RequestStreamBuffers(
+    std::vector<cros::mojom::Camera3BufferRequestPtr> buffer_reqs,
+    RequestStreamBuffersCallback callback) {
+  // TODO(b/226688669): Implement RequestManager::RequestStreamBuffers.
+}
+
+void RequestManager::ReturnStreamBuffers(
+    std::vector<cros::mojom::Camera3StreamBufferPtr> buffers) {
+  // TODO(b/226688669): Implement RequestManager::ReturnStreamBuffers.
+}
+
 void RequestManager::SubmitCaptureResult(
     uint32_t frame_number,
     StreamType stream_type,
diff --git a/media/capture/video/chromeos/request_manager.h b/media/capture/video/chromeos/request_manager.h
index 34c0ec02..b5269c1 100644
--- a/media/capture/video/chromeos/request_manager.h
+++ b/media/capture/video/chromeos/request_manager.h
@@ -263,6 +263,16 @@
                          StreamType stream_type,
                          cros::mojom::Camera3ErrorMsgCode error_code);
 
+  // RequestStreamBuffers receives output buffer requests and a callback to
+  // receive results.
+  void RequestStreamBuffers(
+      std::vector<cros::mojom::Camera3BufferRequestPtr> buffer_reqs,
+      RequestStreamBuffersCallback callback) override;
+
+  // ReturnStreamBuffers receives returned output buffers.
+  void ReturnStreamBuffers(
+      std::vector<cros::mojom::Camera3StreamBufferPtr> buffers) override;
+
   // Submits the captured buffer of frame |frame_number_| for the given
   // |stream_type| to Chrome if all the required metadata and the captured
   // buffer are received.  After the buffer is submitted the function then
diff --git a/media/gpu/vaapi/vaapi_video_encode_accelerator.cc b/media/gpu/vaapi/vaapi_video_encode_accelerator.cc
index a987f6e2..33035b5 100644
--- a/media/gpu/vaapi/vaapi_video_encode_accelerator.cc
+++ b/media/gpu/vaapi/vaapi_video_encode_accelerator.cc
@@ -104,8 +104,6 @@
 
 VaapiVideoEncodeAccelerator::VaapiVideoEncodeAccelerator()
     : can_use_encoder_(num_instances_.Increment() < kMaxNumOfInstances),
-      output_buffer_byte_size_(0),
-      state_(kUninitialized),
       child_task_runner_(base::SequencedTaskRunner::GetCurrentDefault()),
       // TODO(akahuang): Change to use SequencedTaskRunner to see if the
       // performance is affected.
@@ -394,7 +392,8 @@
   const size_t max_ref_frames = encoder_->GetMaxNumOfRefFrames();
   num_frames_in_flight_ = std::max(kMinNumFramesInFlight, max_ref_frames);
   DVLOGF(1) << "Frames in flight: " << num_frames_in_flight_;
-
+  max_pending_results_size_ =
+      num_frames_in_flight_ * std::max<size_t>(1, config.spatial_layers.size());
   if (!vaapi_wrapper_->CreateContext(encoder_->GetCodedSize())) {
     NOTIFY_ERROR(kPlatformFailureError, "Failed creating VAContext");
     return;
@@ -437,17 +436,6 @@
   DVLOGF(4) << "va_surface_id: " << va_surface_id;
 
   va_surfaces->push_back(std::move(va_surface));
-
-  // At least one surface must available in |available_encode_surfaces_|
-  // to succeed in EncodePendingInputs(). Checks here to avoid redundant
-  // EncodePendingInputs() call.
-  for (const auto& surfaces : available_encode_surfaces_) {
-    if (surfaces.second.empty())
-      return;
-  }
-
-  if (!input_queue_.empty())
-    EncodePendingInputs();
 }
 
 void VaapiVideoEncodeAccelerator::TryToReturnBitstreamBuffers() {
@@ -735,10 +723,9 @@
   const VASurfaceID id = scoped_va_surface->id();
   const gfx::Size& size = scoped_va_surface->size();
   const unsigned int format = scoped_va_surface->format();
-  VASurface::ReleaseCB release_cb =
-      base::BindPostTaskToCurrentDefault(base::BindOnce(
-          &VaapiVideoEncodeAccelerator::RecycleVASurface, encoder_weak_this_,
-          &surfaces, std::move(scoped_va_surface)));
+  VASurface::ReleaseCB release_cb = base::BindOnce(
+      &VaapiVideoEncodeAccelerator::RecycleVASurface, encoder_weak_this_,
+      &surfaces, std::move(scoped_va_surface));
 
   return base::MakeRefCounted<VASurface>(id, size, format,
                                          std::move(release_cb));
@@ -857,7 +844,15 @@
 
   TRACE_EVENT1("media,gpu", "VAVEA::EncodePendingInputs",
                "pending input frames", input_queue_.size());
-  while (state_ == kEncoding && !input_queue_.empty()) {
+  // Encode all the frames in |input_queue_|. So that we avoid a number of
+  // encoded chunks are stuck at |pending_encode_results_|, we breaks if the
+  // queue size is more than |max_pending_encode_results_size|. Since the
+  // pending frames to be encoded are held in |input_queue_|, a client that
+  // recycles the VideoFrames will not input any more frames until an available
+  // bitstream buffer is given and a pending frame is released thanks to
+  // the resumed encode.
+  while (state_ == kEncoding && !input_queue_.empty() &&
+         pending_encode_results_.size() < max_pending_results_size_) {
     const InputFrameRef& input_frame = input_queue_.front();
     if (!input_frame.frame) {
       // If this is a flush (null) frame, don't create/submit a new encode
@@ -963,6 +958,11 @@
 
   available_bitstream_buffers_.push(std::move(buffer));
   TryToReturnBitstreamBuffers();
+  // If there is a pending frame, it is pended because of the bitstream buffer
+  // shortage. Try to encode it.
+  if (!input_queue_.empty()) {
+    EncodePendingInputs();
+  }
 }
 
 void VaapiVideoEncodeAccelerator::RequestEncodingParametersChange(
@@ -1064,8 +1064,6 @@
   if (vaapi_wrapper_)
     vaapi_wrapper_->DestroyContext();
 
-  available_encode_surfaces_.clear();
-
   if (vpp_vaapi_wrapper_)
     vpp_vaapi_wrapper_->DestroyContext();
 
@@ -1082,6 +1080,11 @@
 
   encoder_.reset();
 
+  // Clear |available_encode_surfaces_| after |encoder_| is destroyed because
+  // the reconstructed surface in the reference frame pool owned by |encoder_|
+  // are back to |available_encode_surfaces_|.
+  available_encode_surfaces_.clear();
+
   delete this;
 }
 
diff --git a/media/gpu/vaapi/vaapi_video_encode_accelerator.h b/media/gpu/vaapi/vaapi_video_encode_accelerator.h
index 496933d7..c4b8cfca 100644
--- a/media/gpu/vaapi/vaapi_video_encode_accelerator.h
+++ b/media/gpu/vaapi/vaapi_video_encode_accelerator.h
@@ -245,7 +245,9 @@
   gfx::Rect visible_rect_;
 
   // Size in bytes required for output bitstream buffers.
-  size_t output_buffer_byte_size_;
+  size_t output_buffer_byte_size_ = 0;
+  // Size of the max size of |pending_encode_results_|.
+  size_t max_pending_results_size_ = 0;
 
   // This flag signals when the client is sending NV12 + DmaBuf-backed
   // VideoFrames to encode, which allows for skipping a copy-adaptation on
@@ -253,13 +255,13 @@
   bool native_input_mode_ = false;
 
   // The number of frames that needs to be held on encoding.
-  size_t num_frames_in_flight_;
+  size_t num_frames_in_flight_ = 0;
 
   // All of the members below must be accessed on the encoder_task_runner_,
   // while it is running.
 
   // Encoder state. Encode tasks will only run in kEncoding state.
-  State state_;
+  State state_ = State::kUninitialized;
 
   // Encoder instance managing video codec state and preparing encode jobs.
   // Should only be used on |encoder_task_runner_|.
diff --git a/mojo/core/broker_host.cc b/mojo/core/broker_host.cc
index a0371495..7a5d4f94 100644
--- a/mojo/core/broker_host.cc
+++ b/mojo/core/broker_host.cc
@@ -32,11 +32,8 @@
       client_process_(std::move(client_process))
 #endif
 {
-  CHECK(connection_params.endpoint().is_valid() ||
-        connection_params.server_endpoint().is_valid());
-
   base::CurrentThread::Get()->AddDestructionObserver(this);
-
+  CHECK(connection_params.endpoint().is_valid());
   channel_ = Channel::Create(this, std::move(connection_params),
 #if BUILDFLAG(IS_WIN)
                              client_process_
diff --git a/mojo/core/channel.cc b/mojo/core/channel.cc
index 71d2ab3..4b7bbc1 100644
--- a/mojo/core/channel.cc
+++ b/mojo/core/channel.cc
@@ -893,23 +893,13 @@
 // static
 scoped_refptr<Channel> Channel::CreateForIpczDriver(
     Delegate* delegate,
-    Endpoint endpoint,
+    PlatformChannelEndpoint endpoint,
     scoped_refptr<base::SingleThreadTaskRunner> io_task_runner) {
 #if BUILDFLAG(IS_NACL)
   return nullptr;
 #else
-  ConnectionParams params =
-      absl::visit(base::Overloaded{
-                      [](PlatformChannelEndpoint& endpoint) {
-                        return ConnectionParams(std::move(endpoint));
-                      },
-                      [](PlatformChannelServerEndpoint& endpoint) {
-                        return ConnectionParams(std::move(endpoint));
-                      },
-                  },
-                  endpoint);
-  return Create(delegate, std::move(params), HandlePolicy::kAcceptHandles,
-                std::move(io_task_runner));
+  return Create(delegate, ConnectionParams{std::move(endpoint)},
+                HandlePolicy::kAcceptHandles, std::move(io_task_runner));
 #endif
 }
 
diff --git a/mojo/core/channel.h b/mojo/core/channel.h
index 4cc0e92..40e03ebe 100644
--- a/mojo/core/channel.h
+++ b/mojo/core/channel.h
@@ -22,9 +22,7 @@
 #include "mojo/core/connection_params.h"
 #include "mojo/core/platform_handle_in_transit.h"
 #include "mojo/public/cpp/platform/platform_channel_endpoint.h"
-#include "mojo/public/cpp/platform/platform_channel_server_endpoint.h"
 #include "mojo/public/cpp/platform/platform_handle.h"
-#include "third_party/abseil-cpp/absl/types/variant.h"
 
 namespace mojo::core {
 
@@ -318,11 +316,9 @@
   // header, and the Channel is no longer responsible for encoding or decoding
   // any metadata about transmitted PlatformHandles, since the ipcz driver takes
   // care of that.
-  using Endpoint =
-      absl::variant<PlatformChannelEndpoint, PlatformChannelServerEndpoint>;
   static scoped_refptr<Channel> CreateForIpczDriver(
       Delegate* delegate,
-      Endpoint endpoint,
+      PlatformChannelEndpoint endpoint,
       scoped_refptr<base::SingleThreadTaskRunner> io_task_runner);
 
   Channel(const Channel&) = delete;
diff --git a/mojo/core/channel_mac.cc b/mojo/core/channel_mac.cc
index 24405df2..229ff07 100644
--- a/mojo/core/channel_mac.cc
+++ b/mojo/core/channel_mac.cc
@@ -55,14 +55,8 @@
         self_(this),
         io_task_runner_(io_task_runner),
         watch_controller_(FROM_HERE) {
-    PlatformHandle channel_handle;
-    if (connection_params.server_endpoint().is_valid()) {
-      channel_handle =
-          connection_params.TakeServerEndpoint().TakePlatformHandle();
-    } else {
-      channel_handle = connection_params.TakeEndpoint().TakePlatformHandle();
-    }
-
+    PlatformHandle channel_handle =
+        connection_params.TakeEndpoint().TakePlatformHandle();
     if (channel_handle.is_mach_send()) {
       send_port_ = channel_handle.TakeMachSendRight();
     } else if (channel_handle.is_mach_receive()) {
diff --git a/mojo/core/channel_posix.cc b/mojo/core/channel_posix.cc
index f57c9b3..42b55c2 100644
--- a/mojo/core/channel_posix.cc
+++ b/mojo/core/channel_posix.cc
@@ -122,12 +122,8 @@
     : Channel(delegate, handle_policy),
       self_(this),
       io_task_runner_(io_task_runner) {
-  if (connection_params.server_endpoint().is_valid())
-    server_ = connection_params.TakeServerEndpoint();
-  else
-    socket_ = connection_params.TakeEndpoint().TakePlatformHandle().TakeFD();
-
-  CHECK(server_.is_valid() || socket_.is_valid());
+  socket_ = connection_params.TakeEndpoint().TakePlatformHandle().TakeFD();
+  CHECK(socket_.is_valid());
 }
 
 ChannelPosix::~ChannelPosix() {
@@ -225,19 +221,13 @@
   read_watcher_ =
       std::make_unique<base::MessagePumpForIO::FdWatchController>(FROM_HERE);
   base::CurrentThread::Get()->AddDestructionObserver(this);
-  if (server_.is_valid()) {
-    base::CurrentIOThread::Get()->WatchFileDescriptor(
-        server_.platform_handle().GetFD().get(), false /* persistent */,
-        base::MessagePumpForIO::WATCH_READ, read_watcher_.get(), this);
-  } else {
-    write_watcher_ =
-        std::make_unique<base::MessagePumpForIO::FdWatchController>(FROM_HERE);
-    base::CurrentIOThread::Get()->WatchFileDescriptor(
-        socket_.get(), true /* persistent */,
-        base::MessagePumpForIO::WATCH_READ, read_watcher_.get(), this);
-    base::AutoLock lock(write_lock_);
-    FlushOutgoingMessagesNoLock();
-  }
+  write_watcher_ =
+      std::make_unique<base::MessagePumpForIO::FdWatchController>(FROM_HERE);
+  base::CurrentIOThread::Get()->WatchFileDescriptor(
+      socket_.get(), true /* persistent */, base::MessagePumpForIO::WATCH_READ,
+      read_watcher_.get(), this);
+  base::AutoLock lock(write_lock_);
+  FlushOutgoingMessagesNoLock();
 }
 
 void ChannelPosix::WaitForWriteOnIOThread() {
@@ -268,10 +258,8 @@
   write_watcher_.reset();
   if (leak_handle_) {
     std::ignore = socket_.release();
-    server_.TakePlatformHandle().release();
   } else {
     socket_.reset();
-    std::ignore = server_.TakePlatformHandle();
   }
 #if BUILDFLAG(IS_IOS)
   fds_to_close_.clear();
@@ -288,24 +276,6 @@
 }
 
 void ChannelPosix::OnFileCanReadWithoutBlocking(int fd) {
-  if (server_.is_valid()) {
-    CHECK_EQ(fd, server_.platform_handle().GetFD().get());
-#if !BUILDFLAG(IS_NACL)
-    read_watcher_.reset();
-    base::CurrentThread::Get()->RemoveDestructionObserver(this);
-
-    AcceptSocketConnection(server_.platform_handle().GetFD().get(), &socket_);
-    std::ignore = server_.TakePlatformHandle();
-    if (!socket_.is_valid()) {
-      OnError(Error::kConnectionFailed);
-      return;
-    }
-    StartOnIOThread();
-#else
-    NOTREACHED();
-#endif
-    return;
-  }
   CHECK_EQ(fd, socket_.get());
 
   bool validation_error = false;
@@ -370,10 +340,6 @@
 // cannot be written, it's queued and a wait is initiated to write the message
 // ASAP on the I/O thread.
 bool ChannelPosix::WriteNoLock(MessageView message_view) {
-  if (server_.is_valid()) {
-    outgoing_messages_.emplace_front(std::move(message_view));
-    return true;
-  }
   size_t bytes_written = 0;
   std::vector<PlatformHandleInTransit> handles = message_view.TakeHandles();
   size_t num_handles = handles.size();
diff --git a/mojo/core/channel_posix.h b/mojo/core/channel_posix.h
index 695be6d..94b55c9 100644
--- a/mojo/core/channel_posix.h
+++ b/mojo/core/channel_posix.h
@@ -98,12 +98,7 @@
   bool CloseHandles(const int* fds, size_t num_fds);
 #endif  // BUILDFLAG(IS_IOS)
 
-  // We may be initialized with a server socket, in which case this will be
-  // valid until it accepts an incoming connection.
-  PlatformChannelServerEndpoint server_;
-
-  // The socket over which to communicate. May be passed in at construction time
-  // or accepted over |server_|.
+  // The socket over which to communicate.
   base::ScopedFD socket_;
 
   // These watchers must only be accessed on the IO thread.
diff --git a/mojo/core/channel_win.cc b/mojo/core/channel_win.cc
index f75273f..9f751fa9 100644
--- a/mojo/core/channel_win.cc
+++ b/mojo/core/channel_win.cc
@@ -67,16 +67,8 @@
         is_untrusted_process_(connection_params.is_untrusted_process()),
         self_(this),
         io_task_runner_(io_task_runner) {
-    if (connection_params.server_endpoint().is_valid()) {
-      handle_ = connection_params.TakeServerEndpoint()
-                    .TakePlatformHandle()
-                    .TakeHandle();
-      needs_connection_ = true;
-    } else {
-      handle_ =
-          connection_params.TakeEndpoint().TakePlatformHandle().TakeHandle();
-    }
-
+    handle_ =
+        connection_params.TakeEndpoint().TakePlatformHandle().TakeHandle();
     CHECK(handle_.IsValid());
   }
 
@@ -186,29 +178,6 @@
     base::CurrentThread::Get()->AddDestructionObserver(this);
     base::CurrentIOThread::Get()->RegisterIOHandler(handle_.Get(), this);
 
-    if (needs_connection_) {
-      BOOL ok = ::ConnectNamedPipe(handle_.Get(), &connect_context_.overlapped);
-      if (ok) {
-        PLOG(ERROR) << "Unexpected success while waiting for pipe connection";
-        OnError(Error::kConnectionFailed);
-        return;
-      }
-
-      const DWORD err = GetLastError();
-      switch (err) {
-        case ERROR_PIPE_CONNECTED:
-          break;
-        case ERROR_IO_PENDING:
-          is_connect_pending_ = true;
-          AddRef();
-          return;
-        case ERROR_NO_DATA:
-        default:
-          OnError(Error::kConnectionFailed);
-          return;
-      }
-    }
-
     // Now that we have registered our IOHandler, we can start writing.
     {
       base::AutoLock lock(write_lock_);
@@ -261,16 +230,6 @@
       } else {
         OnError(Error::kDisconnected);
       }
-    } else if (context == &connect_context_) {
-      DCHECK(is_connect_pending_);
-      is_connect_pending_ = false;
-      ReadMore(0);
-
-      base::AutoLock lock(write_lock_);
-      if (delay_writes_) {
-        delay_writes_ = false;
-        WriteNextNoLock();
-      }
     } else if (context == &read_context_) {
       OnReadDone(static_cast<size_t>(bytes_transfered));
     } else {
@@ -381,8 +340,9 @@
       // If we can't write because the pipe is disconnected then continue
       // reading to fetch any in-flight messages, relying on end-of-stream to
       // signal the actual disconnection.
-      if (is_read_pending_ || is_connect_pending_)
+      if (is_read_pending_) {
         return;
+      }
     }
 
     OnError(error);
@@ -396,14 +356,9 @@
   // The pipe handle this Channel uses for communication.
   base::win::ScopedHandle handle_;
 
-  // Indicates whether |handle_| must wait for a connection.
-  bool needs_connection_ = false;
-
   const scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
 
-  base::MessagePumpForIO::IOContext connect_context_;
   base::MessagePumpForIO::IOContext read_context_;
-  bool is_connect_pending_ = false;
   bool is_read_pending_ = false;
 
   // Protects all fields potentially accessed on multiple threads via Write().
diff --git a/mojo/core/connection_params.cc b/mojo/core/connection_params.cc
index dac76f1d..b05b211 100644
--- a/mojo/core/connection_params.cc
+++ b/mojo/core/connection_params.cc
@@ -15,10 +15,6 @@
 ConnectionParams::ConnectionParams(PlatformChannelEndpoint endpoint)
     : endpoint_(std::move(endpoint)) {}
 
-ConnectionParams::ConnectionParams(
-    PlatformChannelServerEndpoint server_endpoint)
-    : server_endpoint_(std::move(server_endpoint)) {}
-
 ConnectionParams::ConnectionParams(ConnectionParams&&) = default;
 
 ConnectionParams::~ConnectionParams() = default;
diff --git a/mojo/core/connection_params.h b/mojo/core/connection_params.h
index 052f38b..bf8dce5 100644
--- a/mojo/core/connection_params.h
+++ b/mojo/core/connection_params.h
@@ -8,7 +8,6 @@
 #include "build/build_config.h"
 #include "mojo/core/system_impl_export.h"
 #include "mojo/public/cpp/platform/platform_channel_endpoint.h"
-#include "mojo/public/cpp/platform/platform_channel_server_endpoint.h"
 
 namespace mojo {
 namespace core {
@@ -18,7 +17,6 @@
  public:
   ConnectionParams();
   explicit ConnectionParams(PlatformChannelEndpoint endpoint);
-  explicit ConnectionParams(PlatformChannelServerEndpoint server_endpoint);
   ConnectionParams(ConnectionParams&&);
 
   ConnectionParams(const ConnectionParams&) = delete;
@@ -29,16 +27,9 @@
   ConnectionParams& operator=(ConnectionParams&&);
 
   const PlatformChannelEndpoint& endpoint() const { return endpoint_; }
-  const PlatformChannelServerEndpoint& server_endpoint() const {
-    return server_endpoint_;
-  }
 
   PlatformChannelEndpoint TakeEndpoint() { return std::move(endpoint_); }
 
-  PlatformChannelServerEndpoint TakeServerEndpoint() {
-    return std::move(server_endpoint_);
-  }
-
   void set_is_async(bool is_async) { is_async_ = is_async; }
   bool is_async() const { return is_async_; }
 
@@ -55,7 +46,6 @@
   bool is_untrusted_process_ = false;
   bool leak_endpoint_ = false;
   PlatformChannelEndpoint endpoint_;
-  PlatformChannelServerEndpoint server_endpoint_;
 };
 
 }  // namespace core
diff --git a/mojo/core/core.cc b/mojo/core/core.cc
index 415c175..40027e0 100644
--- a/mojo/core/core.cc
+++ b/mojo/core/core.cc
@@ -1301,8 +1301,6 @@
     return MOJO_RESULT_INVALID_ARGUMENT;
   if (transport_endpoint->type != MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL &&
       transport_endpoint->type !=
-          MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL_SERVER &&
-      transport_endpoint->type !=
           MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL_ASYNC) {
     return MOJO_RESULT_UNIMPLEMENTED;
   }
@@ -1318,18 +1316,8 @@
   if (!endpoint.is_valid())
     return MOJO_RESULT_INVALID_ARGUMENT;
 
-  ConnectionParams connection_params;
-#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_POSIX)
-  if (transport_endpoint->type ==
-      MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL_SERVER) {
-    connection_params =
-        ConnectionParams(PlatformChannelServerEndpoint(std::move(endpoint)));
-  }
-#endif
-  if (!connection_params.server_endpoint().is_valid()) {
-    connection_params =
-        ConnectionParams(PlatformChannelEndpoint(std::move(endpoint)));
-  }
+  ConnectionParams connection_params(
+      PlatformChannelEndpoint(std::move(endpoint)));
 
   // At this point everything else has been validated, so we can take ownership
   // of the dispatcher.
@@ -1342,7 +1330,6 @@
       // Release ownership of the endpoint platform handle, per the API
       // contract. The caller retains ownership on failure.
       connection_params.TakeEndpoint().TakePlatformHandle().release();
-      connection_params.TakeServerEndpoint().TakePlatformHandle().release();
       return result;
     }
     DCHECK_EQ(removed_dispatcher.get(), invitation_dispatcher);
@@ -1400,8 +1387,6 @@
     return MOJO_RESULT_INVALID_ARGUMENT;
   if (transport_endpoint->type != MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL &&
       transport_endpoint->type !=
-          MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL_SERVER &&
-      transport_endpoint->type !=
           MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL_ASYNC) {
     return MOJO_RESULT_UNIMPLEMENTED;
   }
@@ -1421,18 +1406,8 @@
     return MOJO_RESULT_INVALID_ARGUMENT;
   }
 
-  ConnectionParams connection_params;
-#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_POSIX)
-  if (transport_endpoint->type ==
-      MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL_SERVER) {
-    connection_params =
-        ConnectionParams(PlatformChannelServerEndpoint(std::move(endpoint)));
-  }
-#endif
-  if (!connection_params.server_endpoint().is_valid()) {
-    connection_params =
-        ConnectionParams(PlatformChannelEndpoint(std::move(endpoint)));
-  }
+  ConnectionParams connection_params(
+      PlatformChannelEndpoint(std::move(endpoint)));
   if (options &&
       options->flags & MOJO_ACCEPT_INVITATION_FLAG_LEAK_TRANSPORT_ENDPOINT) {
     connection_params.set_leak_endpoint(true);
diff --git a/mojo/core/embedder/BUILD.gn b/mojo/core/embedder/BUILD.gn
index fce6597c..df6f3e93 100644
--- a/mojo/core/embedder/BUILD.gn
+++ b/mojo/core/embedder/BUILD.gn
@@ -21,13 +21,13 @@
   public_deps = [
     "//base",
     "//mojo:buildflags",
+    "//mojo/core:embedder_internal",
     "//mojo/public/cpp/platform",
     "//third_party/ipcz/src:ipcz_chromium",
   ]
 
   deps = [
     ":features",
-    "//mojo/core:embedder_internal",
     "//mojo/public/c/system",
   ]
 }
diff --git a/mojo/core/invitation_unittest.cc b/mojo/core/invitation_unittest.cc
index 346e290..f8c11dd 100644
--- a/mojo/core/invitation_unittest.cc
+++ b/mojo/core/invitation_unittest.cc
@@ -45,11 +45,6 @@
 namespace core {
 namespace {
 
-enum class TransportType {
-  kChannel,
-  kChannelServer,
-};
-
 const char kSecondaryChannelHandleSwitch[] = "test-secondary-channel-handle";
 
 class InvitationTest : public test::MojoTestBase {
@@ -66,7 +61,6 @@
       const std::string& test_client_name,
       MojoHandle* primordial_pipes,
       size_t num_primordial_pipes,
-      TransportType transport_type,
       MojoSendInvitationFlags send_flags,
       MojoProcessErrorHandler error_handler = nullptr,
       uintptr_t error_handler_context = 0,
@@ -78,7 +72,6 @@
       base::ProcessHandle process,
       MojoHandle* primordial_pipes,
       size_t num_primordial_pipes,
-      TransportType transport_type,
       MojoSendInvitationFlags flags,
       MojoProcessErrorHandler error_handler,
       uintptr_t error_handler_context,
@@ -106,8 +99,9 @@
 #error "Platform not yet supported."
 #endif
 
-  if (switch_name.empty())
+  if (switch_name.empty()) {
     switch_name = PlatformChannel::kHandleSwitch;
+  }
   command_line->AppendSwitchASCII(std::string(switch_name), value);
 }
 
@@ -305,7 +299,6 @@
     const std::string& test_client_name,
     MojoHandle* primordial_pipes,
     size_t num_primordial_pipes,
-    TransportType transport_type,
     MojoSendInvitationFlags send_flags,
     MojoProcessErrorHandler error_handler,
     uintptr_t error_handler_context,
@@ -323,31 +316,10 @@
   launch_options.start_hidden = true;
 #endif
 
-#if !BUILDFLAG(IS_FUCHSIA)
-  absl::optional<NamedPlatformChannel> named_channel;
-#endif
-  absl::optional<PlatformChannel> channel;
+  PlatformChannel channel;
   PlatformHandle local_endpoint_handle;
-  if (transport_type == TransportType::kChannel) {
-    channel.emplace();
-    PrepareToPassRemoteEndpoint(&channel.value(), &launch_options,
-                                &command_line);
-    local_endpoint_handle = channel->TakeLocalEndpoint().TakePlatformHandle();
-  } else {
-#if !BUILDFLAG(IS_FUCHSIA)
-    NamedPlatformChannel::Options named_channel_options;
-#if !BUILDFLAG(IS_WIN)
-    CHECK(base::PathService::Get(base::DIR_TEMP,
-                                 &named_channel_options.socket_dir));
-#endif
-    named_channel.emplace(named_channel_options);
-    named_channel->PassServerNameOnCommandLine(&command_line);
-    local_endpoint_handle =
-        named_channel->TakeServerEndpoint().TakePlatformHandle();
-#else   //  !BUILDFLAG(IS_FUCHSIA)
-    NOTREACHED() << "Named pipe support does not exist for Mojo on Fuchsia.";
-#endif  //  !BUILDFLAG(IS_FUCHSIA)
-  }
+  PrepareToPassRemoteEndpoint(&channel, &launch_options, &command_line);
+  local_endpoint_handle = channel.TakeLocalEndpoint().TakePlatformHandle();
 
   std::string enable_features;
   std::string disable_features;
@@ -362,13 +334,12 @@
 
   base::Process child_process = base::SpawnMultiProcessTestChild(
       test_client_name, command_line, launch_options);
-  if (channel)
-    channel->RemoteProcessLaunchAttempted();
+  channel.RemoteProcessLaunchAttempted();
 
   SendInvitationToClient(std::move(local_endpoint_handle),
                          child_process.Handle(), primordial_pipes,
-                         num_primordial_pipes, transport_type, send_flags,
-                         error_handler, error_handler_context, "");
+                         num_primordial_pipes, send_flags, error_handler,
+                         error_handler_context, "");
 
   return child_process;
 }
@@ -379,7 +350,6 @@
     base::ProcessHandle process,
     MojoHandle* primordial_pipes,
     size_t num_primordial_pipes,
-    TransportType transport_type,
     MojoSendInvitationFlags flags,
     MojoProcessErrorHandler error_handler,
     uintptr_t error_handler_context,
@@ -407,10 +377,7 @@
 
   MojoInvitationTransportEndpoint transport_endpoint;
   transport_endpoint.struct_size = sizeof(transport_endpoint);
-  if (transport_type == TransportType::kChannel)
-    transport_endpoint.type = MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL;
-  else
-    transport_endpoint.type = MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL_SERVER;
+  transport_endpoint.type = MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL;
   transport_endpoint.num_platform_handles = 1;
   transport_endpoint.platform_handles = &handle;
 
@@ -436,17 +403,12 @@
                                      base::StringPiece switch_name = {}) {
     const auto& command_line = *base::CommandLine::ForCurrentProcess();
     PlatformChannelEndpoint channel_endpoint;
-#if !BUILDFLAG(IS_FUCHSIA)
-    channel_endpoint = NamedPlatformChannel::ConnectToServer(command_line);
-#endif
-    if (!channel_endpoint.is_valid()) {
-      if (switch_name.empty()) {
-        channel_endpoint =
-            PlatformChannel::RecoverPassedEndpointFromCommandLine(command_line);
-      } else {
-        channel_endpoint = PlatformChannel::RecoverPassedEndpointFromString(
-            command_line.GetSwitchValueASCII(switch_name));
-      }
+    if (switch_name.empty()) {
+      channel_endpoint =
+          PlatformChannel::RecoverPassedEndpointFromCommandLine(command_line);
+    } else {
+      channel_endpoint = PlatformChannel::RecoverPassedEndpointFromString(
+          command_line.GetSwitchValueASCII(switch_name));
     }
     MojoPlatformHandle endpoint_handle;
     PlatformHandle::ToMojoPlatformHandle(channel_endpoint.TakePlatformHandle(),
@@ -487,9 +449,9 @@
 
 TEST_F(InvitationTest, SendInvitation) {
   MojoHandle primordial_pipe;
-  base::Process child_process = LaunchChildTestClient(
-      "SendInvitationClient", &primordial_pipe, 1, TransportType::kChannel,
-      MOJO_SEND_INVITATION_FLAG_NONE);
+  base::Process child_process =
+      LaunchChildTestClient("SendInvitationClient", &primordial_pipe, 1,
+                            MOJO_SEND_INVITATION_FLAG_NONE);
 
   WriteMessage(primordial_pipe, kTestMessage1);
   EXPECT_EQ(MOJO_RESULT_OK,
@@ -523,9 +485,9 @@
 
 TEST_F(InvitationTest, SendInvitationMultiplePipes) {
   MojoHandle pipes[2];
-  base::Process child_process = LaunchChildTestClient(
-      "SendInvitationMultiplePipesClient", pipes, 2, TransportType::kChannel,
-      MOJO_SEND_INVITATION_FLAG_NONE);
+  base::Process child_process =
+      LaunchChildTestClient("SendInvitationMultiplePipesClient", pipes, 2,
+                            MOJO_SEND_INVITATION_FLAG_NONE);
 
   WriteMessage(pipes[0], kTestMessage1);
   WriteMessage(pipes[1], kTestMessage2);
@@ -570,51 +532,6 @@
   EXPECT_EQ(MOJO_RESULT_OK, MojoClose(pipes[1]));
 }
 
-// Fuchsia has no named pipe support.
-#if !BUILDFLAG(IS_FUCHSIA)
-// TODO(crbug.com/1426421): Flaky on Linux TSAN.
-#if BUILDFLAG(IS_LINUX) && defined(THREAD_SANITIZER)
-#define MAYBE_SendInvitationWithServer DISABLED_SendInvitationWithServer
-#else
-#define MAYBE_SendInvitationWithServer SendInvitationWithServer
-#endif
-TEST_F(InvitationTest, MAYBE_SendInvitationWithServer) {
-  MojoHandle primordial_pipe;
-  base::Process child_process = LaunchChildTestClient(
-      "SendInvitationWithServerClient", &primordial_pipe, 1,
-      TransportType::kChannelServer, MOJO_SEND_INVITATION_FLAG_NONE);
-
-  WriteMessage(primordial_pipe, kTestMessage1);
-  EXPECT_EQ(MOJO_RESULT_OK,
-            WaitForSignals(primordial_pipe, MOJO_HANDLE_SIGNAL_READABLE));
-  EXPECT_EQ(kTestMessage3, ReadMessage(primordial_pipe));
-  EXPECT_EQ(MOJO_RESULT_OK, MojoClose(primordial_pipe));
-
-  int wait_result = -1;
-  base::WaitForMultiprocessTestChildExit(
-      child_process, TestTimeouts::action_timeout(), &wait_result);
-  child_process.Close();
-  EXPECT_EQ(0, wait_result);
-}
-
-DEFINE_TEST_CLIENT(SendInvitationWithServerClient) {
-  MojoHandle primordial_pipe;
-  MojoHandle invitation = AcceptInvitation(MOJO_ACCEPT_INVITATION_FLAG_NONE);
-  const uint32_t pipe_name = 0;
-  ASSERT_EQ(MOJO_RESULT_OK,
-            MojoExtractMessagePipeFromInvitation(invitation, &pipe_name, 4,
-                                                 nullptr, &primordial_pipe));
-  ASSERT_EQ(MOJO_RESULT_OK, MojoClose(invitation));
-
-  WaitForSignals(primordial_pipe, MOJO_HANDLE_SIGNAL_READABLE);
-  ASSERT_EQ(kTestMessage1, ReadMessage(primordial_pipe));
-  WriteMessage(primordial_pipe, kTestMessage3);
-  WaitForSignals(primordial_pipe, MOJO_HANDLE_SIGNAL_PEER_CLOSED);
-
-  ASSERT_EQ(MOJO_RESULT_OK, MojoClose(primordial_pipe));
-}
-#endif  // !BUILDFLAG(IS_FUCHSIA)
-
 const char kErrorMessage[] = "ur bad :(";
 const char kDisconnectMessage[] = "go away plz";
 
@@ -677,9 +594,8 @@
   RemoteProcessState process_state;
   MojoHandle pipe;
   base::Process child_process = LaunchChildTestClient(
-      "ProcessErrorsClient", &pipe, 1, TransportType::kChannel,
-      MOJO_SEND_INVITATION_FLAG_NONE, &TestProcessErrorHandler,
-      reinterpret_cast<uintptr_t>(&process_state));
+      "ProcessErrorsClient", &pipe, 1, MOJO_SEND_INVITATION_FLAG_NONE,
+      &TestProcessErrorHandler, reinterpret_cast<uintptr_t>(&process_state));
 
   MojoMessageHandle message;
   WaitForSignals(pipe, MOJO_HANDLE_SIGNAL_READABLE);
@@ -750,9 +666,8 @@
 
   MojoHandle pipe;
   base::Process child_process = LaunchChildTestClient(
-      "ReinvitationClient", &pipe, 1, TransportType::kChannel,
-      MOJO_SEND_INVITATION_FLAG_NONE, nullptr, 0, &command_line,
-      &launch_options);
+      "ReinvitationClient", &pipe, 1, MOJO_SEND_INVITATION_FLAG_NONE, nullptr,
+      0, &command_line, &launch_options);
   secondary_channel.RemoteProcessLaunchAttempted();
 
   // Synchronize end-to-end communication first to ensure the process connection
@@ -819,9 +734,9 @@
 
 TEST_F(InvitationTest, SendIsolatedInvitation) {
   MojoHandle primordial_pipe;
-  base::Process child_process = LaunchChildTestClient(
-      "SendIsolatedInvitationClient", &primordial_pipe, 1,
-      TransportType::kChannel, MOJO_SEND_INVITATION_FLAG_ISOLATED);
+  base::Process child_process =
+      LaunchChildTestClient("SendIsolatedInvitationClient", &primordial_pipe, 1,
+                            MOJO_SEND_INVITATION_FLAG_ISOLATED);
 
   WriteMessage(primordial_pipe, kTestMessage1);
   EXPECT_EQ(MOJO_RESULT_OK,
@@ -874,8 +789,7 @@
   MojoHandle primordial_pipe;
   base::Process child_process = LaunchChildTestClient(
       "SendMultipleIsolatedInvitationsClient", &primordial_pipe, 1,
-      TransportType::kChannel, MOJO_SEND_INVITATION_FLAG_ISOLATED, nullptr, 0,
-      &command_line, &options);
+      MOJO_SEND_INVITATION_FLAG_ISOLATED, nullptr, 0, &command_line, &options);
   secondary_transport.RemoteProcessLaunchAttempted();
 
   WriteMessage(primordial_pipe, kTestMessage1);
@@ -888,8 +802,8 @@
   MojoHandle new_pipe;
   SendInvitationToClient(
       secondary_transport.TakeLocalEndpoint().TakePlatformHandle(),
-      child_process.Handle(), &new_pipe, 1, TransportType::kChannel,
-      MOJO_SEND_INVITATION_FLAG_ISOLATED, nullptr, 0, "");
+      child_process.Handle(), &new_pipe, 1, MOJO_SEND_INVITATION_FLAG_ISOLATED,
+      nullptr, 0, "");
   WaitForSignals(primordial_pipe, MOJO_HANDLE_SIGNAL_PEER_CLOSED);
   EXPECT_EQ(MOJO_RESULT_OK, MojoClose(primordial_pipe));
 
@@ -952,17 +866,17 @@
   PlatformChannel channel2;
   MojoHandle pipe0, pipe1;
   const char kConnectionName[] = "there can be only one!";
-  SendInvitationToClient(
-      channel1.TakeLocalEndpoint().TakePlatformHandle(),
-      base::kNullProcessHandle, &pipe0, 1, TransportType::kChannel,
-      MOJO_SEND_INVITATION_FLAG_ISOLATED, nullptr, 0, kConnectionName);
+  SendInvitationToClient(channel1.TakeLocalEndpoint().TakePlatformHandle(),
+                         base::kNullProcessHandle, &pipe0, 1,
+                         MOJO_SEND_INVITATION_FLAG_ISOLATED, nullptr, 0,
+                         kConnectionName);
 
   // Send another invitation with the same connection name. |pipe0| should be
   // disconnected as the first invitation's connection is torn down.
-  SendInvitationToClient(
-      channel2.TakeLocalEndpoint().TakePlatformHandle(),
-      base::kNullProcessHandle, &pipe1, 1, TransportType::kChannel,
-      MOJO_SEND_INVITATION_FLAG_ISOLATED, nullptr, 0, kConnectionName);
+  SendInvitationToClient(channel2.TakeLocalEndpoint().TakePlatformHandle(),
+                         base::kNullProcessHandle, &pipe1, 1,
+                         MOJO_SEND_INVITATION_FLAG_ISOLATED, nullptr, 0,
+                         kConnectionName);
 
   WaitForSignals(pipe0, MOJO_HANDLE_SIGNAL_PEER_CLOSED);
   EXPECT_EQ(MOJO_RESULT_OK, MojoClose(pipe0));
@@ -979,11 +893,9 @@
   MojoHandle pipe0, pipe1;
   SendInvitationToClient(channel.TakeLocalEndpoint().TakePlatformHandle(),
                          base::kNullProcessHandle, &pipe0, 1,
-                         TransportType::kChannel,
                          MOJO_SEND_INVITATION_FLAG_ISOLATED, nullptr, 0, "");
   SendInvitationToClient(channel.TakeRemoteEndpoint().TakePlatformHandle(),
                          base::kNullProcessHandle, &pipe1, 1,
-                         TransportType::kChannel,
                          MOJO_SEND_INVITATION_FLAG_ISOLATED, nullptr, 0, "");
 
   WriteMessage(pipe0, kTestMessage1);
@@ -994,9 +906,9 @@
 
 TEST_F(InvitationTest, BrokenInvitationTransportBreaksAttachedPipe) {
   MojoHandle primordial_pipe;
-  base::Process child_process = LaunchChildTestClient(
-      "BrokenTransportClient", &primordial_pipe, 1, TransportType::kChannel,
-      MOJO_SEND_INVITATION_FLAG_NONE);
+  base::Process child_process =
+      LaunchChildTestClient("BrokenTransportClient", &primordial_pipe, 1,
+                            MOJO_SEND_INVITATION_FLAG_NONE);
 
   EXPECT_EQ(MOJO_RESULT_OK,
             WaitForSignals(primordial_pipe, MOJO_HANDLE_SIGNAL_PEER_CLOSED));
@@ -1011,9 +923,9 @@
 
 TEST_F(InvitationTest, BrokenIsolatedInvitationTransportBreaksAttachedPipe) {
   MojoHandle primordial_pipe;
-  base::Process child_process = LaunchChildTestClient(
-      "BrokenTransportClient", &primordial_pipe, 1, TransportType::kChannel,
-      MOJO_SEND_INVITATION_FLAG_ISOLATED);
+  base::Process child_process =
+      LaunchChildTestClient("BrokenTransportClient", &primordial_pipe, 1,
+                            MOJO_SEND_INVITATION_FLAG_ISOLATED);
 
   EXPECT_EQ(MOJO_RESULT_OK,
             WaitForSignals(primordial_pipe, MOJO_HANDLE_SIGNAL_PEER_CLOSED));
diff --git a/mojo/core/ipcz_driver/invitation.cc b/mojo/core/ipcz_driver/invitation.cc
index ef55e9a..80c4cf0 100644
--- a/mojo/core/ipcz_driver/invitation.cc
+++ b/mojo/core/ipcz_driver/invitation.cc
@@ -89,15 +89,9 @@
     return IPCZ_INVALID_DRIVER_HANDLE;
   }
 
-  Channel::Endpoint channel_endpoint;
-  if (endpoint.type == MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL_SERVER) {
-    channel_endpoint = PlatformChannelServerEndpoint(std::move(handle));
-  } else {
-    channel_endpoint = PlatformChannelEndpoint(std::move(handle));
-  }
   auto transport = base::MakeRefCounted<Transport>(
-      endpoint_types, std::move(channel_endpoint), std::move(remote_process),
-      is_remote_process_untrusted);
+      endpoint_types, PlatformChannelEndpoint(std::move(handle)),
+      std::move(remote_process), is_remote_process_untrusted);
   transport->SetErrorHandler(error_handler, error_handler_context);
   transport->set_leak_channel_on_shutdown(options.leak_channel_on_shutdown);
   transport->set_is_peer_trusted(options.is_peer_trusted);
diff --git a/mojo/core/ipcz_driver/transport.cc b/mojo/core/ipcz_driver/transport.cc
index fa572f7..e797e92b 100644
--- a/mojo/core/ipcz_driver/transport.cc
+++ b/mojo/core/ipcz_driver/transport.cc
@@ -25,7 +25,6 @@
 #include "mojo/public/cpp/platform/platform_channel.h"
 #include "mojo/public/cpp/platform/platform_channel_endpoint.h"
 #include "mojo/public/cpp/platform/platform_handle.h"
-#include "third_party/abseil-cpp/absl/types/variant.h"
 #include "third_party/ipcz/include/ipcz/ipcz.h"
 
 #if BUILDFLAG(IS_WIN)
@@ -201,7 +200,7 @@
 }  // namespace
 
 Transport::Transport(EndpointTypes endpoint_types,
-                     Channel::Endpoint endpoint,
+                     PlatformChannelEndpoint endpoint,
                      base::Process remote_process,
                      bool is_remote_process_untrusted)
     : endpoint_types_(endpoint_types),
@@ -214,7 +213,7 @@
 
 // static
 scoped_refptr<Transport> Transport::Create(EndpointTypes endpoint_types,
-                                           Channel::Endpoint endpoint,
+                                           PlatformChannelEndpoint endpoint,
                                            base::Process remote_process,
                                            bool is_remote_process_untrusted) {
   return base::MakeRefCounted<Transport>(endpoint_types, std::move(endpoint),
@@ -279,7 +278,7 @@
   std::vector<PendingTransmission> pending_transmissions;
   {
     base::AutoLock lock(lock_);
-    if (channel_ || !IsEndpointValid()) {
+    if (channel_ || !inactive_endpoint_.is_valid()) {
       return false;
     }
 
@@ -351,7 +350,7 @@
   scoped_refptr<Channel> channel;
   {
     base::AutoLock lock(lock_);
-    if (IsEndpointValid()) {
+    if (inactive_endpoint_.is_valid()) {
       PendingTransmission transmission;
       transmission.bytes = std::vector<uint8_t>(data.begin(), data.end());
       transmission.handles = std::move(platform_handles);
@@ -589,10 +588,8 @@
   DCHECK_EQ(handles.size(), 1u);
 #endif
 
-  DCHECK(IsEndpointValid());
-  DCHECK(absl::holds_alternative<PlatformChannelEndpoint>(inactive_endpoint_));
-  handles[0] = absl::get<PlatformChannelEndpoint>(inactive_endpoint_)
-                   .TakePlatformHandle();
+  CHECK(inactive_endpoint_.is_valid());
+  handles[0] = inactive_endpoint_.TakePlatformHandle();
   return true;
 }
 
@@ -670,18 +667,6 @@
   self = std::move(self_reference_for_channel_);
 }
 
-bool Transport::IsEndpointValid() const {
-  return absl::visit(base::Overloaded{
-                         [](const PlatformChannelEndpoint& endpoint) {
-                           return endpoint.is_valid();
-                         },
-                         [](const PlatformChannelServerEndpoint& endpoint) {
-                           return endpoint.is_valid();
-                         },
-                     },
-                     inactive_endpoint_);
-}
-
 bool Transport::CanTransmitHandles() const {
 #if BUILDFLAG(IS_WIN)
   // On Windows, we can transmit handles only if at least one endpoint is a
diff --git a/mojo/core/ipcz_driver/transport.h b/mojo/core/ipcz_driver/transport.h
index 1472037..9642910 100644
--- a/mojo/core/ipcz_driver/transport.h
+++ b/mojo/core/ipcz_driver/transport.h
@@ -41,7 +41,7 @@
     EndpointType destination;
   };
   Transport(EndpointTypes endpoint_types,
-            Channel::Endpoint endpoint,
+            PlatformChannelEndpoint endpoint,
             base::Process remote_process,
             bool is_remote_process_untrusted = false);
 
@@ -49,7 +49,7 @@
   // than MakeRefCounted<T>.
   static scoped_refptr<Transport> Create(
       EndpointTypes endpoint_types,
-      Channel::Endpoint endpoint,
+      PlatformChannelEndpoint endpoint,
       base::Process remote_process = base::Process(),
       bool is_remote_process_untrusted = false);
 
@@ -93,7 +93,7 @@
   // invalidating the transport. May only be called on a Transport which has not
   // yet been activated, and only when the channel endpoint is not a server.
   PlatformChannelEndpoint TakeEndpoint() {
-    return std::move(absl::get<PlatformChannelEndpoint>(inactive_endpoint_));
+    return std::move(inactive_endpoint_);
   }
 
   // Handles reports of bad activity from ipcz, resulting from parcel rejection
@@ -168,7 +168,6 @@
 
   ~Transport() override;
 
-  bool IsEndpointValid() const;
   bool CanTransmitHandles() const;
 
   // Indicates whether this transport should serialize its remote process handle
@@ -209,7 +208,7 @@
   // start its underlying Channel instance once activated. Not guarded by a lock
   // since it must not accessed beyond activation, where thread safety becomes a
   // factor.
-  Channel::Endpoint inactive_endpoint_;
+  PlatformChannelEndpoint inactive_endpoint_;
 
   base::Lock lock_;
   scoped_refptr<Channel> channel_ GUARDED_BY(lock_);
diff --git a/mojo/core/node_controller.cc b/mojo/core/node_controller.cc
index b4998b0..5bb1939 100644
--- a/mojo/core/node_controller.cc
+++ b/mojo/core/node_controller.cc
@@ -162,21 +162,6 @@
       target_process.IsValid() ? target_process.Duplicate() : base::Process(),
       std::move(connection_params), process_error_callback);
 
-#if BUILDFLAG(IS_WIN)
-  // On Windows, if target_process is invalid it means it's elevated or running
-  // in another session so a named pipe should be used instead.
-  if (!target_process.IsValid()) {
-    handle_policy = Channel::HandlePolicy::kRejectHandles;
-    NamedPlatformChannel::Options options;
-    NamedPlatformChannel named_channel(options);
-    node_connection_params =
-        ConnectionParams(named_channel.TakeServerEndpoint());
-    node_connection_params.set_is_untrusted_process(is_untrusted_process);
-    broker_host->SendNamedChannel(named_channel.GetServerName());
-    return node_connection_params;
-  }
-#endif
-
   // Sync connections usurp the passed endpoint and use it for the sync broker
   // channel. A new channel is created here for the NodeChannel and sent over
   // a sync broker message to the client.
@@ -431,6 +416,48 @@
     // |BIND_SYNC_BROKER| message from the invited client.
     node_connection_params = std::move(connection_params);
   } else {
+#if defined(IS_WIN)
+    // On Windows, if `target_process` is invalid we can't duplicate a pipe
+    // handle to the remote client. In that case we instead open a new named
+    // pipe and send the client its name via the broker. Once connected, the new
+    // named pipe will be used for the client Channel.
+    if (!target_process.IsValid()) {
+      NamedPlatformChannel::Options options;
+      NamedPlatformChannel named_channel(options);
+
+      const bool is_untrusted_process =
+          connection_params.is_untrusted_process();
+      BrokerHost* broker_host =
+          new BrokerHost(base::Process(), std::move(connection_params),
+                         process_error_callback);
+      broker_host->SendNamedChannel(named_channel.GetServerName());
+
+      PlatformChannelServer::WaitForConnection(
+          named_channel.TakeServerEndpoint(),
+          base::BindOnce(
+              [](base::Process target_process,
+                 const ports::NodeName& temporary_node_name,
+                 const ProcessErrorCallback& process_error_callback,
+                 bool is_untrusted_process,
+                 scoped_refptr<NodeController> node_controller,
+                 PlatformChannelEndpoint endpoint) {
+                if (!endpoint.is_valid()) {
+                  return;
+                }
+
+                ConnectionParams params(std::move(endpoint));
+                params.set_is_untrusted_process(is_untrusted_process);
+                node_controller->FinishSendBrokerClientInvitationOnIOThread(
+                    std::move(target_process), std::move(params),
+                    temporary_node_name, Channel::HandlePolicy::kRejectHandles,
+                    process_error_callback);
+              },
+              std::move(target_process), temporary_node_name,
+              process_error_callback, is_untrusted_process, this));
+      return;
+    }
+#endif
+
     absl::optional<ConnectionParams> params = CreateSyncNodeConnectionParams(
         target_process, std::move(connection_params), process_error_callback,
         handle_policy);
@@ -444,14 +471,26 @@
     node_connection_params = std::move(*params);
   }
 
-  scoped_refptr<NodeChannel> channel = NodeChannel::Create(
-      this, std::move(node_connection_params), handle_policy, io_task_runner_,
-      process_error_callback);
+  FinishSendBrokerClientInvitationOnIOThread(
+      std::move(target_process), std::move(node_connection_params),
+      temporary_node_name, handle_policy, process_error_callback);
 #else   // !BUILDFLAG(IS_APPLE) && !BUILDFLAG(IS_NACL) && !BUILDFLAG(IS_FUCHSIA)
-  scoped_refptr<NodeChannel> channel = NodeChannel::Create(
-      this, std::move(connection_params), Channel::HandlePolicy::kAcceptHandles,
-      io_task_runner_, process_error_callback);
+  FinishSendBrokerClientInvitationOnIOThread(
+      std::move(target_process), std::move(connection_params),
+      temporary_node_name, Channel::HandlePolicy::kAcceptHandles,
+      process_error_callback);
 #endif  // !BUILDFLAG(IS_APPLE) && !BUILDFLAG(IS_NACL) && !BUILDFLAG(IS_FUCHSIA)
+}
+
+void NodeController::FinishSendBrokerClientInvitationOnIOThread(
+    base::Process target_process,
+    ConnectionParams connection_params,
+    ports::NodeName temporary_node_name,
+    Channel::HandlePolicy handle_policy,
+    const ProcessErrorCallback& process_error_callback) {
+  scoped_refptr<NodeChannel> channel =
+      NodeChannel::Create(this, std::move(connection_params), handle_policy,
+                          io_task_runner_, process_error_callback);
 
   // We set up the invitee channel with a temporary name so it can be identified
   // as a pending invitee if it writes any messages to the channel. We may start
diff --git a/mojo/core/node_controller.h b/mojo/core/node_controller.h
index 80651533..70a914c 100644
--- a/mojo/core/node_controller.h
+++ b/mojo/core/node_controller.h
@@ -188,8 +188,15 @@
   void SendBrokerClientInvitationOnIOThread(
       base::Process target_process,
       ConnectionParams connection_params,
-      ports::NodeName token,
+      ports::NodeName temporary_node_name,
       const ProcessErrorCallback& process_error_callback);
+  void FinishSendBrokerClientInvitationOnIOThread(
+      base::Process target_process,
+      ConnectionParams connection_params,
+      ports::NodeName temporary_node_name,
+      Channel::HandlePolicy handle_policy,
+      const ProcessErrorCallback& process_error_callback);
+
   void AcceptBrokerClientInvitationOnIOThread(
       ConnectionParams connection_params,
       absl::optional<PlatformHandle> broker_host_handle);
diff --git a/mojo/public/cpp/platform/BUILD.gn b/mojo/public/cpp/platform/BUILD.gn
index 5c07c918..95326ae7 100644
--- a/mojo/public/cpp/platform/BUILD.gn
+++ b/mojo/public/cpp/platform/BUILD.gn
@@ -28,9 +28,21 @@
     sources += [ "socket_utils_posix.cc" ]
   }
 
+  if (is_win || is_mac || (is_posix && !is_nacl)) {
+    public += [ "platform_channel_server.h" ]
+    sources += [ "platform_channel_server.cc" ]
+  }
+
   if (is_win) {
     public += [ "platform_handle_security_util_win.h" ]
-    sources += [ "platform_handle_security_util_win.cc" ]
+    sources += [
+      "platform_channel_server_win.cc",
+      "platform_handle_security_util_win.cc",
+    ]
+  } else if (is_mac) {
+    sources += [ "platform_channel_server_mac.cc" ]
+  } else if (is_posix && !is_nacl) {
+    sources += [ "platform_channel_server_posix.cc" ]
   }
 
   public_deps = [
diff --git a/mojo/public/cpp/platform/named_platform_channel.h b/mojo/public/cpp/platform/named_platform_channel.h
index c8e19615..455a9fa 100644
--- a/mojo/public/cpp/platform/named_platform_channel.h
+++ b/mojo/public/cpp/platform/named_platform_channel.h
@@ -87,8 +87,8 @@
   // named pipe server; on POSIX it's a bound, listening domain socket. In each
   // case it should accept a single new connection.
   //
-  // Use the handle to send or receive an invitation, with the endpoint type as
-  // |MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL_SERVER|.
+  // Use with PlatformChannelServer to wait for a new connection, yielding a
+  // PlatformChannelEndpoint that is usable with the Mojo invitations API.
   [[nodiscard]] PlatformChannelServerEndpoint TakeServerEndpoint() {
     return std::move(server_endpoint_);
   }
diff --git a/mojo/public/cpp/platform/platform_channel_server.cc b/mojo/public/cpp/platform/platform_channel_server.cc
new file mode 100644
index 0000000..f9cb895
--- /dev/null
+++ b/mojo/public/cpp/platform/platform_channel_server.cc
@@ -0,0 +1,52 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/public/cpp/platform/platform_channel_server.h"
+
+#include <memory>
+#include <utility>
+
+namespace mojo {
+
+PlatformChannelServer::PlatformChannelServer() = default;
+
+PlatformChannelServer::~PlatformChannelServer() = default;
+
+// static
+void PlatformChannelServer::WaitForConnection(
+    PlatformChannelServerEndpoint server_endpoint,
+    ConnectionCallback callback) {
+  auto server = std::make_unique<PlatformChannelServer>();
+  auto* server_ptr = server.get();
+  auto wrapped_callback = base::BindOnce(
+      [](std::unique_ptr<PlatformChannelServer> server,
+         PlatformChannelServer::ConnectionCallback callback,
+         PlatformChannelEndpoint endpoint) {
+        std::move(callback).Run(std::move(endpoint));
+      },
+      std::move(server), std::move(callback));
+  if (!server_ptr->TryListen(server_endpoint, wrapped_callback)) {
+    base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
+        FROM_HERE,
+        base::BindOnce(std::move(wrapped_callback), PlatformChannelEndpoint()));
+  }
+}
+
+bool PlatformChannelServer::TryListen(
+    PlatformChannelServerEndpoint& server_endpoint,
+    ConnectionCallback& callback) {
+  auto listener = Listener::Create();
+  if (!listener->Start(server_endpoint, callback)) {
+    return false;
+  }
+
+  listener_ = std::move(listener);
+  return true;
+}
+
+void PlatformChannelServer::Stop() {
+  listener_.reset();
+}
+
+}  // namespace mojo
diff --git a/mojo/public/cpp/platform/platform_channel_server.h b/mojo/public/cpp/platform/platform_channel_server.h
new file mode 100644
index 0000000..3d9db8a4
--- /dev/null
+++ b/mojo/public/cpp/platform/platform_channel_server.h
@@ -0,0 +1,89 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_PUBLIC_CPP_PLATFORM_PLATFORM_CHANNEL_SERVER_H_
+#define MOJO_PUBLIC_CPP_PLATFORM_PLATFORM_CHANNEL_SERVER_H_
+
+#include <memory>
+
+#include "base/component_export.h"
+#include "base/functional/callback.h"
+#include "base/task/single_thread_task_runner.h"
+#include "mojo/public/cpp/platform/platform_channel_endpoint.h"
+#include "mojo/public/cpp/platform/platform_channel_server_endpoint.h"
+
+namespace mojo {
+
+// PlatformChannelServer takes ownership of a PlatformChannelServerEndpoint
+// and listens for a single incoming client connection.
+//
+// This class is not thread-safe and must be used on a thread which runs an I/O
+// MessagePump.
+class COMPONENT_EXPORT(MOJO_CPP_PLATFORM) PlatformChannelServer {
+ public:
+  using ConnectionCallback = base::OnceCallback<void(PlatformChannelEndpoint)>;
+
+  // Implemented for each supported platform.
+  class COMPONENT_EXPORT(MOJO_CPP_PLATFORM) Listener {
+   public:
+    virtual ~Listener() = default;
+
+    // Implemented for each supported platform.
+    static std::unique_ptr<Listener> Create();
+
+    // Attempts to start listening on `server_endpoint`. Returns true on success
+    // or false on failure. Same semantics as Listen() below, which calls this.
+    virtual bool Start(PlatformChannelServerEndpoint& server_endpoint,
+                       ConnectionCallback& callback) = 0;
+  };
+
+  PlatformChannelServer();
+
+  // Destruction implicitly stops the listener if started, ensuring the
+  // ConnectionCallback will not be called beyoned the lifetime of this object.
+  ~PlatformChannelServer();
+
+  // Spins up a PlatformChannelServer on the current (I/O) task runner to listen
+  // on `server_endpoint` for an incoming connection. `callback` is always
+  // called eventually, as long as the calling task runner is still running when
+  // either the server accepts a connection or is disconnected. If disconnected,
+  // `callback` receives an invalid endpoint.
+  static void WaitForConnection(PlatformChannelServerEndpoint server_endpoint,
+                                ConnectionCallback callback);
+
+  // Listens on `server_endpoint` for a single connection, invoking `callback`
+  // once it arrives. Must not be called on a server that is already listening.
+  //
+  // If the server endpoint is disconnected before a connection is received,
+  // the callback will be invoked with an invalid endpoint.
+  //
+  // If the server is stopped before a connection is received, `callback` will
+  // not be called.
+  //
+  // If the server could not listen on the given endpoint, this returns false
+  // and `callback` is never called. Otherwise it returns true.
+  //
+  // This takes ownership of (i.e. moves) `server_endpoint` and `callback` if
+  // and only if it returns true.
+  bool TryListen(PlatformChannelServerEndpoint& server_endpoint,
+                 ConnectionCallback& callback);
+
+  // Same as above, but takes a callback by value for convenience when the
+  // doesn't care about retaining the arguments in the failure case.
+  bool Listen(PlatformChannelServerEndpoint server_endpoint,
+              ConnectionCallback callback) {
+    return TryListen(server_endpoint, callback);
+  }
+
+  // Stops listening for new connections immediately. The callback given to
+  // Listen() can no longer be invoked once this is called.
+  void Stop();
+
+ private:
+  std::unique_ptr<Listener> listener_;
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_PLATFORM_PLATFORM_CHANNEL_SERVER_H_
diff --git a/mojo/public/cpp/platform/platform_channel_server_endpoint.h b/mojo/public/cpp/platform/platform_channel_server_endpoint.h
index c741b87c..6229a076 100644
--- a/mojo/public/cpp/platform/platform_channel_server_endpoint.h
+++ b/mojo/public/cpp/platform/platform_channel_server_endpoint.h
@@ -11,9 +11,10 @@
 namespace mojo {
 
 // A PlatformHandle with a little extra type information to convey that it's
-// a channel server endpoint, i.e. a handle that can be used to send invitations
-// as |MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL_SERVER| to a remote
-// PlatformChannelEndpoint.
+// a channel server endpoint, i.e. a handle that should be used with
+// PlatformChannelServer to wait for a new connection and ultimately provide
+// a connected PlatformChannelEndpoint suitable for use with the Mojo
+// invitations API.
 class COMPONENT_EXPORT(MOJO_CPP_PLATFORM) PlatformChannelServerEndpoint {
  public:
   PlatformChannelServerEndpoint();
diff --git a/mojo/public/cpp/platform/platform_channel_server_mac.cc b/mojo/public/cpp/platform/platform_channel_server_mac.cc
new file mode 100644
index 0000000..3eb32fa
--- /dev/null
+++ b/mojo/public/cpp/platform/platform_channel_server_mac.cc
@@ -0,0 +1,66 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/public/cpp/platform/platform_channel_server.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/check.h"
+#include "base/memory/weak_ptr.h"
+#include "base/task/sequenced_task_runner.h"
+#include "mojo/public/cpp/platform/platform_channel_endpoint.h"
+#include "mojo/public/cpp/platform/platform_handle.h"
+
+namespace mojo {
+
+namespace {
+
+// NOTE: On macOS, PlatformChannelServerEndpoint is not special, as they need to
+// perform the same connection handshake as any other PlatformChannelEndpoint.
+// PlatformChannelServer acts as a simple passthrough implementation for
+// compatibility with application logic on other platforms.
+class ListenerImpl : public PlatformChannelServer::Listener {
+ public:
+  ListenerImpl() = default;
+  ~ListenerImpl() override = default;
+
+  // PlatformChannelServer::Listener:
+  bool Start(PlatformChannelServerEndpoint& server_endpoint,
+             PlatformChannelServer::ConnectionCallback& callback) override {
+    if (!server_endpoint.is_valid() ||
+        !server_endpoint.platform_handle().is_mach_receive()) {
+      return false;
+    }
+
+    // Invoke the callback asynchronously to guard against re-entrancy issues.
+    // This simply repackages the server endpoint as a PlatformChannelEndpoint,
+    // since they're functionally equivalent on macOS. Note that we post the
+    // task as a WeakPtr-bound method to ensure that it doesn't run if the
+    // Listener is destroyed first.
+    PlatformChannelEndpoint endpoint{server_endpoint.TakePlatformHandle()};
+    base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
+        FROM_HERE, base::BindOnce(&ListenerImpl::RunCallback,
+                                  weak_ptr_factory_.GetWeakPtr(),
+                                  std::move(callback), std::move(endpoint)));
+    return true;
+  }
+
+ private:
+  void RunCallback(PlatformChannelServer::ConnectionCallback callback,
+                   PlatformChannelEndpoint endpoint) {
+    std::move(callback).Run(std::move(endpoint));
+  }
+
+  base::WeakPtrFactory<ListenerImpl> weak_ptr_factory_{this};
+};
+
+}  // namespace
+
+std::unique_ptr<PlatformChannelServer::Listener>
+PlatformChannelServer::Listener::Create() {
+  return std::make_unique<ListenerImpl>();
+}
+
+}  // namespace mojo
diff --git a/mojo/public/cpp/platform/platform_channel_server_posix.cc b/mojo/public/cpp/platform/platform_channel_server_posix.cc
new file mode 100644
index 0000000..914c93e
--- /dev/null
+++ b/mojo/public/cpp/platform/platform_channel_server_posix.cc
@@ -0,0 +1,90 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/public/cpp/platform/platform_channel_server.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/check.h"
+#include "base/files/scoped_file.h"
+#include "base/message_loop/message_pump_for_io.h"
+#include "base/notreached.h"
+#include "base/task/current_thread.h"
+#include "mojo/public/cpp/platform/platform_channel_endpoint.h"
+#include "mojo/public/cpp/platform/platform_handle.h"
+#include "mojo/public/cpp/platform/socket_utils_posix.h"
+
+namespace mojo {
+
+namespace {
+
+class ListenerImpl : public PlatformChannelServer::Listener,
+                     public base::MessagePumpForIO::FdWatcher {
+ public:
+  ListenerImpl() : watch_controller_(FROM_HERE) {}
+  ~ListenerImpl() override = default;
+
+  // PlatformChannelServer::Listener:
+  bool Start(PlatformChannelServerEndpoint& server_endpoint,
+             PlatformChannelServer::ConnectionCallback& callback) override {
+    if (!server_endpoint.is_valid()) {
+      return false;
+    }
+
+    base::ScopedFD server = server_endpoint.TakePlatformHandle().TakeFD();
+    if (!base::CurrentIOThread::Get()->WatchFileDescriptor(
+            server.get(), /*persistent=*/true,
+            base::MessagePumpForIO::WATCH_READ, &watch_controller_, this)) {
+      return false;
+    }
+
+    server_ = std::move(server);
+    callback_ = std::move(callback);
+    return true;
+  }
+
+  // base::MessagePumpForIO::FdWatcher:
+  void OnFileCanReadWithoutBlocking(int fd) override {
+    base::ScopedFD socket;
+    CHECK_EQ(fd, server_.get());
+    if (!AcceptSocketConnection(fd, &socket)) {
+      // Unrecoverable error, e.g. socket disconnection. Fail.
+      Stop();
+      std::move(callback_).Run({});
+      return;
+    }
+
+    if (!socket.is_valid()) {
+      // Transient failure; a second connection attempt might succeed.
+      return;
+    }
+
+    Stop();
+    std::move(callback_).Run(
+        PlatformChannelEndpoint{PlatformHandle{std::move(socket)}});
+  }
+
+  void OnFileCanWriteWithoutBlocking(int fd) override { NOTREACHED(); }
+
+ private:
+  void Stop() {
+    CHECK(server_.is_valid());
+    watch_controller_.StopWatchingFileDescriptor();
+    server_.reset();
+  }
+
+  base::ScopedFD server_;
+  PlatformChannelServer::ConnectionCallback callback_;
+  base::MessagePumpForIO::FdWatchController watch_controller_;
+};
+
+}  // namespace
+
+std::unique_ptr<PlatformChannelServer::Listener>
+PlatformChannelServer::Listener::Create() {
+  return std::make_unique<ListenerImpl>();
+}
+
+}  // namespace mojo
diff --git a/mojo/public/cpp/platform/platform_channel_server_win.cc b/mojo/public/cpp/platform/platform_channel_server_win.cc
new file mode 100644
index 0000000..08b31902f
--- /dev/null
+++ b/mojo/public/cpp/platform/platform_channel_server_win.cc
@@ -0,0 +1,134 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/public/cpp/platform/platform_channel_server.h"
+
+#include <windows.h>
+
+#include <cstring>
+#include <memory>
+#include <utility>
+
+#include "base/check.h"
+#include "base/memory/weak_ptr.h"
+#include "base/task/current_thread.h"
+#include "base/task/sequenced_task_runner.h"
+#include "base/win/object_watcher.h"
+#include "base/win/scoped_handle.h"
+#include "mojo/public/cpp/platform/platform_channel_endpoint.h"
+#include "mojo/public/cpp/platform/platform_handle.h"
+
+namespace mojo {
+
+namespace {
+
+class ListenerImpl : public PlatformChannelServer::Listener,
+                     public base::win::ObjectWatcher::Delegate {
+ public:
+  ListenerImpl() {
+    memset(&connect_overlapped_, 0, sizeof(connect_overlapped_));
+  }
+
+  ~ListenerImpl() override {
+    if (server_.is_valid()) {
+      ::CancelIo(server_.get());
+    }
+  }
+
+  // PlatformChannelServer::Listener:
+  bool Start(PlatformChannelServerEndpoint& server_endpoint,
+             PlatformChannelServer::ConnectionCallback& callback) override {
+    connect_event_.Set(::CreateEvent(NULL, TRUE, FALSE, NULL));
+    if (!connect_event_.is_valid()) {
+      return false;
+    }
+
+    connect_overlapped_.hEvent = connect_event_.get();
+    if (!event_watcher_.StartWatchingOnce(connect_event_.get(), this)) {
+      return false;
+    }
+
+    base::win::ScopedHandle server =
+        server_endpoint.TakePlatformHandle().TakeHandle();
+    BOOL ok = ::ConnectNamedPipe(server.get(), &connect_overlapped_);
+    if (ok) {
+      // This call should always fail with ERROR_IO_PENDING or
+      // ERROR_PIPE_CONNECTED.
+      return false;
+    }
+
+    const DWORD error = ::GetLastError();
+    switch (error) {
+      case ERROR_PIPE_CONNECTED:
+        // Already connected. Invoke the callback asynchronously to avoid any
+        // potential re-entrancy issues in the caller. The task is posted with
+        // a WeakPtr-bound method to ensure that it doesn't run if the server
+        // is stopped first.
+        event_watcher_.StopWatching();
+        connect_event_.Close();
+        base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
+            FROM_HERE, base::BindOnce(&ListenerImpl::RunCallback,
+                                      weak_ptr_factory_.GetWeakPtr(),
+                                      std::move(callback), std::move(server)));
+        break;
+
+      case ERROR_IO_PENDING:
+        // Will continue in OnIOCompleted whenever the event is signaled.
+        break;
+
+      default:
+        // Fail.
+        connect_event_.Close();
+        return false;
+    }
+
+    server_ = std::move(server);
+    callback_ = std::move(callback);
+    return true;
+  }
+
+  // base::win::ObjectWatcher:
+  void OnObjectSignaled(HANDLE object) override {
+    // Event signaled. Check the status of the pipe. The only success case is
+    // when ConnectNamedPipe() returns ERROR_PIPE_CONNECTED here.
+    CHECK_EQ(object, connect_event_.get());
+    BOOL ok = ::ConnectNamedPipe(server_.get(), &connect_overlapped_);
+    if (ok || ::GetLastError() != ERROR_PIPE_CONNECTED) {
+      std::move(callback_).Run({});
+      return;
+    }
+
+    // Success. Pass ownership of the connected pipe to the user-provided
+    // callback.
+    connect_event_.Close();
+    std::move(callback_).Run(
+        PlatformChannelEndpoint{PlatformHandle{std::move(server_)}});
+  }
+
+ private:
+  void RunCallback(PlatformChannelServer::ConnectionCallback callback,
+                   base::win::ScopedHandle server) {
+    std::move(callback).Run(
+        PlatformChannelEndpoint{PlatformHandle{std::move(server)}});
+  }
+
+  base::win::ScopedHandle server_;
+
+  OVERLAPPED connect_overlapped_;
+  base::win::ScopedHandle connect_event_;
+  base::win::ObjectWatcher event_watcher_;
+
+  PlatformChannelServer::ConnectionCallback callback_;
+
+  base::WeakPtrFactory<ListenerImpl> weak_ptr_factory_{this};
+};
+
+}  // namespace
+
+std::unique_ptr<PlatformChannelServer::Listener>
+PlatformChannelServer::Listener::Create() {
+  return std::make_unique<ListenerImpl>();
+}
+
+}  // namespace mojo
diff --git a/mojo/public/cpp/platform/tests/BUILD.gn b/mojo/public/cpp/platform/tests/BUILD.gn
index 8a7e7c9..66de64e 100644
--- a/mojo/public/cpp/platform/tests/BUILD.gn
+++ b/mojo/public/cpp/platform/tests/BUILD.gn
@@ -14,4 +14,12 @@
     "//mojo/public/cpp/system",
     "//testing/gtest",
   ]
+
+  if (is_win || is_mac || (is_posix && !is_nacl)) {
+    sources += [ "platform_channel_server_unittest.cc" ]
+    deps += [
+      "//base/test:test_support",
+      "//mojo/core/embedder",
+    ]
+  }
 }
diff --git a/mojo/public/cpp/platform/tests/DEPS b/mojo/public/cpp/platform/tests/DEPS
index ef8ad28..d11274b53 100644
--- a/mojo/public/cpp/platform/tests/DEPS
+++ b/mojo/public/cpp/platform/tests/DEPS
@@ -1,3 +1,4 @@
 include_rules = [
+  "+mojo/core",
   "+mojo/public",
 ]
diff --git a/mojo/public/cpp/platform/tests/platform_channel_server_unittest.cc b/mojo/public/cpp/platform/tests/platform_channel_server_unittest.cc
new file mode 100644
index 0000000..48c1bc3
--- /dev/null
+++ b/mojo/public/cpp/platform/tests/platform_channel_server_unittest.cc
@@ -0,0 +1,252 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/public/cpp/platform/platform_channel_server.h"
+
+#include <tuple>
+#include <utility>
+
+#include "base/containers/span.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/functional/callback.h"
+#include "base/run_loop.h"
+#include "base/task/single_thread_task_runner.h"
+#include "base/test/bind.h"
+#include "base/test/task_environment.h"
+#include "build/build_config.h"
+#include "mojo/core/channel.h"
+#include "mojo/public/cpp/platform/named_platform_channel.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace mojo {
+namespace {
+
+class RunOnDestruction {
+ public:
+  template <typename Fn>
+  explicit RunOnDestruction(Fn fn)
+      : callback_(base::BindLambdaForTesting(fn)) {}
+  RunOnDestruction(RunOnDestruction&&) = default;
+  ~RunOnDestruction() {
+    if (callback_) {
+      std::move(callback_).Run();
+    }
+  }
+
+ private:
+  base::OnceClosure callback_;
+};
+
+class TestChannel : public core::Channel::Delegate {
+ public:
+  explicit TestChannel(PlatformChannelEndpoint endpoint)
+      : io_task_runner_(base::SingleThreadTaskRunner::GetCurrentDefault()),
+        channel_(
+            core::Channel::Create(this,
+                                  core::ConnectionParams{std::move(endpoint)},
+                                  core::Channel::HandlePolicy::kRejectHandles,
+                                  io_task_runner_)) {
+    channel_->Start();
+  }
+
+  ~TestChannel() override {
+    // We pump the IO task queue after ShutDown() to ensure completion, as
+    // Channel implementions post a cleanup task there.
+    base::RunLoop shutdown_flush;
+    channel_->ShutDown();
+    io_task_runner_->PostTask(FROM_HERE, shutdown_flush.QuitClosure());
+    shutdown_flush.Run();
+  }
+
+  void SendMessage(const std::string& message) {
+    auto data = base::make_span(
+        reinterpret_cast<const uint8_t*>(message.data()), message.size());
+    channel_->Write(core::Channel::Message::CreateIpczMessage(data, {}));
+  }
+
+  std::string WaitForSingleMessage() {
+    wait_for_message_.Run();
+    CHECK(received_message_);
+    return *received_message_;
+  }
+
+  // core::Channel::Delegate:
+  bool IsIpczTransport() const override {
+    // We use Channel in ipcz mode because it's simpler. Doesn't matter if
+    // MojoIpcz is actually enabled.
+    return true;
+  }
+
+  void OnChannelMessage(const void* payload,
+                        size_t payload_size,
+                        std::vector<PlatformHandle> handles) override {
+    received_message_ =
+        std::string(static_cast<const char*>(payload), payload_size);
+    std::move(quit_).Run();
+  }
+
+  void OnChannelError(core::Channel::Error error) override {}
+
+ private:
+  const scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
+  const scoped_refptr<core::Channel> channel_;
+  base::RunLoop wait_for_message_;
+  base::OnceClosure quit_{wait_for_message_.QuitClosure()};
+  absl::optional<std::string> received_message_;
+};
+
+class PlatformChannelServerTest : public testing::Test {
+ public:
+  PlatformChannelServerTest() { CHECK(temp_dir_.CreateUniqueTempDir()); }
+
+  ~PlatformChannelServerTest() override = default;
+
+  using NamedChannelDetails = std::tuple<PlatformChannelServerEndpoint,
+                                         NamedPlatformChannel::ServerName>;
+  NamedChannelDetails CreateRandomChannel() {
+    NamedPlatformChannel::Options options;
+#if BUILDFLAG(IS_POSIX)
+    options.socket_dir = temp_dir_.GetPath();
+#endif
+    NamedPlatformChannel channel(options);
+    return {channel.TakeServerEndpoint(), channel.GetServerName()};
+  }
+
+  PlatformChannelServer& server() { return server_; }
+
+  void VerifyEndToEndConnection(PlatformChannelEndpoint a,
+                                PlatformChannelEndpoint b) {
+    TestChannel channel_a(std::move(a));
+    TestChannel channel_b(std::move(b));
+
+    const std::string kMessage1 = "Hello, world?";
+    const std::string kMessage2 = "Oh, hi world.";
+    channel_a.SendMessage(kMessage1);
+    channel_b.SendMessage(kMessage2);
+    EXPECT_EQ(kMessage2, channel_a.WaitForSingleMessage());
+    EXPECT_EQ(kMessage1, channel_b.WaitForSingleMessage());
+  }
+
+ private:
+  base::ScopedTempDir temp_dir_;
+  base::test::TaskEnvironment task_environment_{
+      base::test::TaskEnvironment::MainThreadType::IO};
+  PlatformChannelServer server_;
+};
+
+TEST_F(PlatformChannelServerTest, ConnectAfterListen) {
+  // Basic test that a client can connect after Listen() and the server will
+  // invoke its user-provided callback with a new functioning endpoint.
+  auto [server_endpoint, name] = CreateRandomChannel();
+
+  base::RunLoop loop;
+  PlatformChannelEndpoint endpoint_a;
+  EXPECT_TRUE(server().Listen(
+      std::move(server_endpoint),
+      base::BindLambdaForTesting([&](PlatformChannelEndpoint endpoint) {
+        endpoint_a = std::move(endpoint);
+        loop.Quit();
+      })));
+  auto endpoint_b = NamedPlatformChannel::ConnectToServer(name);
+  EXPECT_TRUE(endpoint_b.is_valid());
+  loop.Run();
+  VerifyEndToEndConnection(std::move(endpoint_a), std::move(endpoint_b));
+}
+
+TEST_F(PlatformChannelServerTest, ConnectBeforeListen) {
+  // Basic test that a client can connect *before* Listen() and the server will
+  // still invoke its user-provided callback with a new functioning endpoint.
+  auto [server_endpoint, name] = CreateRandomChannel();
+
+  base::RunLoop loop;
+  auto endpoint_a = NamedPlatformChannel::ConnectToServer(name);
+  EXPECT_TRUE(endpoint_a.is_valid());
+  PlatformChannelEndpoint endpoint_b;
+  EXPECT_TRUE(server().Listen(
+      std::move(server_endpoint),
+      base::BindLambdaForTesting([&](PlatformChannelEndpoint endpoint) {
+        endpoint_b = std::move(endpoint);
+        loop.Quit();
+      })));
+  loop.Run();
+  VerifyEndToEndConnection(std::move(endpoint_a), std::move(endpoint_b));
+}
+
+TEST_F(PlatformChannelServerTest, WaitForConnection) {
+  // Tests the static WaitForConnection() helper.
+  auto [server_endpoint, name] = CreateRandomChannel();
+
+  base::RunLoop loop;
+  auto endpoint_a = NamedPlatformChannel::ConnectToServer(name);
+  PlatformChannelEndpoint endpoint_b;
+  PlatformChannelServer::WaitForConnection(
+      std::move(server_endpoint),
+      base::BindLambdaForTesting([&](PlatformChannelEndpoint endpoint) {
+        endpoint_b = std::move(endpoint);
+        loop.Quit();
+      }));
+  loop.Run();
+  VerifyEndToEndConnection(std::move(endpoint_a), std::move(endpoint_b));
+}
+
+TEST_F(PlatformChannelServerTest, NoCallbackAfterListenConnectStop) {
+  // Tests that the ConnectionCallback is never invoked after Stop(), even if
+  // we Listen() and the client connects immediately before the Stop() call.
+  auto [server_endpoint, name] = CreateRandomChannel();
+  bool callback_invoked = false;
+  bool callback_destroyed = false;
+  base::RunLoop loop;
+  EXPECT_TRUE(server().Listen(
+      std::move(server_endpoint),
+      base::BindOnce(
+          // This callback should never run and should be destroyed when we
+          // Stop() below.
+          [](RunOnDestruction, bool* callback_invoked,
+             PlatformChannelEndpoint endpoint) { *callback_invoked = true; },
+          // When the above callback is destroyed, this one will run.
+          RunOnDestruction([&] {
+            callback_destroyed = true;
+            loop.Quit();
+          }),
+          &callback_invoked)));
+  auto endpoint = NamedPlatformChannel::ConnectToServer(name);
+  server().Stop();
+  loop.Run();
+  EXPECT_TRUE(callback_destroyed);
+  EXPECT_FALSE(callback_invoked);
+  EXPECT_TRUE(endpoint.is_valid());
+}
+
+TEST_F(PlatformChannelServerTest, NoCallbackAfterConnectListenStop) {
+  // Tests that the ConnectionCallback is never invoked after Stop(), even if
+  // the client connects before a Listen() which immediately precedes the Stop()
+  // call.
+  auto [server_endpoint, name] = CreateRandomChannel();
+  bool callback_invoked = false;
+  bool callback_destroyed = false;
+  base::RunLoop loop;
+  auto endpoint = NamedPlatformChannel::ConnectToServer(name);
+  EXPECT_TRUE(endpoint.is_valid());
+  EXPECT_TRUE(server().Listen(
+      std::move(server_endpoint),
+      base::BindOnce(
+          // This callback should never run and should be destroyed when we
+          // Stop() below.
+          [](RunOnDestruction, bool* callback_invoked,
+             PlatformChannelEndpoint endpoint) { *callback_invoked = true; },
+          // When the above callback is destroyed, this one will run.
+          RunOnDestruction([&] {
+            callback_destroyed = true;
+            loop.Quit();
+          }),
+          &callback_invoked)));
+  server().Stop();
+  loop.Run();
+  EXPECT_TRUE(callback_destroyed);
+  EXPECT_FALSE(callback_invoked);
+}
+
+}  // namespace
+}  // namespace mojo
diff --git a/mojo/public/cpp/system/invitation.cc b/mojo/public/cpp/system/invitation.cc
index 7245722..42e0b46 100644
--- a/mojo/public/cpp/system/invitation.cc
+++ b/mojo/public/cpp/system/invitation.cc
@@ -4,14 +4,22 @@
 
 #include "mojo/public/cpp/system/invitation.h"
 
+#include <memory>
 #include <tuple>
+#include <utility>
 
 #include "base/numerics/safe_conversions.h"
 #include "build/build_config.h"
+#include "mojo/core/embedder/embedder.h"
 #include "mojo/public/c/system/invitation.h"
 #include "mojo/public/c/system/platform_handle.h"
+#include "mojo/public/cpp/platform/platform_channel_server.h"
 #include "mojo/public/cpp/system/platform_handle.h"
 
+#if BUILDFLAG(IS_WIN)
+#include <windows.h>
+#endif
+
 namespace mojo {
 
 namespace {
@@ -102,6 +110,38 @@
     std::ignore = invitation.release();
 }
 
+void WaitForServerConnection(
+    PlatformChannelServerEndpoint server_endpoint,
+    PlatformChannelServer::ConnectionCallback callback) {
+  core::GetIOTaskRunner()->PostTask(
+      FROM_HERE,
+      base::BindOnce(&PlatformChannelServer::WaitForConnection,
+                     std::move(server_endpoint), std::move(callback)));
+}
+
+base::Process CloneProcessFromHandle(base::ProcessHandle handle) {
+  if (handle == base::kNullProcessHandle) {
+    return base::Process{};
+  }
+
+#if BUILDFLAG(IS_WIN)
+  // We can't use the hack below on Windows, because handle verification will
+  // explode when a new Process instance tries to own the already-owned
+  // `handle`.
+  HANDLE new_handle;
+  BOOL ok =
+      ::DuplicateHandle(::GetCurrentProcess(), handle, ::GetCurrentProcess(),
+                        &new_handle, 0, FALSE, DUPLICATE_SAME_ACCESS);
+  CHECK(ok);
+  return base::Process(new_handle);
+#else
+  base::Process temporary_owner(handle);
+  base::Process clone = temporary_owner.Duplicate();
+  std::ignore = temporary_owner.Release();
+  return clone;
+#endif
+}
+
 }  // namespace
 
 OutgoingInvitation::OutgoingInvitation() {
@@ -169,10 +209,20 @@
                               base::ProcessHandle target_process,
                               PlatformChannelServerEndpoint server_endpoint,
                               const ProcessErrorCallback& error_callback) {
-  SendInvitation(std::move(invitation.handle_), target_process,
-                 server_endpoint.TakePlatformHandle(),
-                 MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL_SERVER,
-                 invitation.extra_flags_, error_callback, "");
+  WaitForServerConnection(
+      std::move(server_endpoint),
+      base::BindOnce(
+          [](OutgoingInvitation invitation, base::Process target_process,
+             const ProcessErrorCallback& error_callback,
+             PlatformChannelEndpoint endpoint) {
+            SendInvitation(std::move(invitation.handle_),
+                           target_process.Handle(),
+                           endpoint.TakePlatformHandle(),
+                           MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL,
+                           invitation.extra_flags_, error_callback, "");
+          },
+          std::move(invitation), CloneProcessFromHandle(target_process),
+          error_callback));
 }
 
 // static
@@ -191,7 +241,7 @@
     PlatformChannelEndpoint channel_endpoint,
     base::StringPiece connection_name,
     base::ProcessHandle target_process) {
-  mojo::OutgoingInvitation invitation;
+  OutgoingInvitation invitation;
   ScopedMessagePipeHandle pipe =
       invitation.AttachMessagePipe(kIsolatedPipeName);
   SendInvitation(std::move(invitation.handle_), target_process,
@@ -207,14 +257,24 @@
     PlatformChannelServerEndpoint server_endpoint,
     base::StringPiece connection_name,
     base::ProcessHandle target_process) {
-  mojo::OutgoingInvitation invitation;
+  OutgoingInvitation invitation;
   ScopedMessagePipeHandle pipe =
       invitation.AttachMessagePipe(kIsolatedPipeName);
-  SendInvitation(std::move(invitation.handle_), target_process,
-                 server_endpoint.TakePlatformHandle(),
-                 MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL_SERVER,
-                 MOJO_SEND_INVITATION_FLAG_ISOLATED | invitation.extra_flags_,
-                 ProcessErrorCallback(), connection_name);
+  WaitForServerConnection(
+      std::move(server_endpoint),
+      base::BindOnce(
+          [](OutgoingInvitation invitation, base::Process target_process,
+             const std::string& connection_name,
+             PlatformChannelEndpoint endpoint) {
+            SendInvitation(
+                std::move(invitation.handle_), target_process.Handle(),
+                endpoint.TakePlatformHandle(),
+                MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL,
+                MOJO_SEND_INVITATION_FLAG_ISOLATED | invitation.extra_flags_,
+                ProcessErrorCallback(), connection_name);
+          },
+          std::move(invitation), CloneProcessFromHandle(target_process),
+          std::string(connection_name)));
   return pipe;
 }
 
diff --git a/net/cert/cert_verifier.cc b/net/cert/cert_verifier.cc
index 95450af..eb7873b 100644
--- a/net/cert/cert_verifier.cc
+++ b/net/cert/cert_verifier.cc
@@ -21,6 +21,42 @@
 
 namespace net {
 
+namespace {
+
+class DefaultCertVerifyProcFactory : public net::CertVerifyProcFactory {
+ public:
+  scoped_refptr<net::CertVerifyProc> CreateCertVerifyProc(
+      scoped_refptr<net::CertNetFetcher> cert_net_fetcher,
+      const net::ChromeRootStoreData* root_store_data) override {
+    scoped_refptr<net::CertVerifyProc> verify_proc;
+#if BUILDFLAG(CHROME_ROOT_STORE_OPTIONAL)
+    if (!verify_proc &&
+        base::FeatureList::IsEnabled(features::kChromeRootStoreUsed)) {
+      verify_proc = CertVerifyProc::CreateBuiltinWithChromeRootStore(
+          std::move(cert_net_fetcher), root_store_data);
+    }
+#endif
+    if (!verify_proc) {
+#if BUILDFLAG(CHROME_ROOT_STORE_ONLY)
+      verify_proc = CertVerifyProc::CreateBuiltinWithChromeRootStore(
+          std::move(cert_net_fetcher), root_store_data);
+#elif BUILDFLAG(IS_FUCHSIA) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
+      verify_proc =
+          CertVerifyProc::CreateBuiltinVerifyProc(std::move(cert_net_fetcher));
+#else
+      verify_proc =
+          CertVerifyProc::CreateSystemVerifyProc(std::move(cert_net_fetcher));
+#endif
+    }
+    return verify_proc;
+  }
+
+ private:
+  ~DefaultCertVerifyProcFactory() override = default;
+};
+
+}  // namespace
+
 CertVerifier::Config::Config() = default;
 CertVerifier::Config::Config(const Config&) = default;
 CertVerifier::Config::Config(Config&&) = default;
@@ -79,28 +115,10 @@
 // static
 std::unique_ptr<CertVerifier> CertVerifier::CreateDefaultWithoutCaching(
     scoped_refptr<CertNetFetcher> cert_net_fetcher) {
-  scoped_refptr<CertVerifyProc> verify_proc;
-#if BUILDFLAG(CHROME_ROOT_STORE_OPTIONAL)
-  if (!verify_proc &&
-      base::FeatureList::IsEnabled(features::kChromeRootStoreUsed)) {
-    verify_proc = CertVerifyProc::CreateBuiltinWithChromeRootStore(
-        std::move(cert_net_fetcher));
-  }
-#endif
-  if (!verify_proc) {
-#if BUILDFLAG(CHROME_ROOT_STORE_ONLY)
-    verify_proc = CertVerifyProc::CreateBuiltinWithChromeRootStore(
-        std::move(cert_net_fetcher));
-#elif BUILDFLAG(IS_FUCHSIA) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
-    verify_proc =
-        CertVerifyProc::CreateBuiltinVerifyProc(std::move(cert_net_fetcher));
-#else
-    verify_proc =
-        CertVerifyProc::CreateSystemVerifyProc(std::move(cert_net_fetcher));
-#endif
-  }
-
-  return std::make_unique<MultiThreadedCertVerifier>(std::move(verify_proc));
+  auto proc_factory = base::MakeRefCounted<DefaultCertVerifyProcFactory>();
+  return std::make_unique<MultiThreadedCertVerifier>(
+      proc_factory->CreateCertVerifyProc(std::move(cert_net_fetcher), nullptr),
+      proc_factory);
 }
 
 // static
diff --git a/net/cert/cert_verify_proc.cc b/net/cert/cert_verify_proc.cc
index 3854eb4..bcb5ad2 100644
--- a/net/cert/cert_verify_proc.cc
+++ b/net/cert/cert_verify_proc.cc
@@ -441,11 +441,14 @@
 #if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
 // static
 scoped_refptr<CertVerifyProc> CertVerifyProc::CreateBuiltinWithChromeRootStore(
-    scoped_refptr<CertNetFetcher> cert_net_fetcher) {
+    scoped_refptr<CertNetFetcher> cert_net_fetcher,
+    const ChromeRootStoreData* root_store_data) {
+  std::unique_ptr<TrustStoreChrome> chrome_root =
+      root_store_data ? std::make_unique<TrustStoreChrome>(*root_store_data)
+                      : std::make_unique<TrustStoreChrome>();
   return CreateCertVerifyProcBuiltin(
       std::move(cert_net_fetcher),
-      CreateSslSystemTrustStoreChromeRoot(
-          std::make_unique<net::TrustStoreChrome>()));
+      CreateSslSystemTrustStoreChromeRoot(std::move(chrome_root)));
 }
 #endif
 
diff --git a/net/cert/cert_verify_proc.h b/net/cert/cert_verify_proc.h
index 68c0847..833b9e09 100644
--- a/net/cert/cert_verify_proc.h
+++ b/net/cert/cert_verify_proc.h
@@ -84,9 +84,11 @@
 
 #if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
   // Creates and returns a CertVerifyProcBuiltin using the Chrome Root Store
-  // SystemTrustStore.
+  // SystemTrustStore and the given |root_store_data|, which may be nullptr to
+  // use the default.
   static scoped_refptr<CertVerifyProc> CreateBuiltinWithChromeRootStore(
-      scoped_refptr<CertNetFetcher> cert_net_fetcher);
+      scoped_refptr<CertNetFetcher> cert_net_fetcher,
+      const ChromeRootStoreData* root_store_data);
 #endif
 
   CertVerifyProc(const CertVerifyProc&) = delete;
diff --git a/net/cert/multi_threaded_cert_verifier.cc b/net/cert/multi_threaded_cert_verifier.cc
index 49d24604..ad375318 100644
--- a/net/cert/multi_threaded_cert_verifier.cc
+++ b/net/cert/multi_threaded_cert_verifier.cc
@@ -194,14 +194,12 @@
 }
 
 MultiThreadedCertVerifier::MultiThreadedCertVerifier(
-    scoped_refptr<CertVerifyProc> verify_proc)
-    : MultiThreadedCertVerifier(std::move(verify_proc), nullptr) {}
-
-MultiThreadedCertVerifier::MultiThreadedCertVerifier(
     scoped_refptr<CertVerifyProc> verify_proc,
     scoped_refptr<CertVerifyProcFactory> verify_proc_factory)
     : verify_proc_(std::move(verify_proc)),
       verify_proc_factory_(std::move(verify_proc_factory)) {
+  CHECK(verify_proc_);
+  CHECK(verify_proc_factory_);
   // Guarantee there is always a CRLSet (this can be overridden via SetConfig).
   config_.crl_set = CRLSet::BuiltinCRLSet();
 }
@@ -244,11 +242,8 @@
     scoped_refptr<CertNetFetcher> cert_net_fetcher,
     const ChromeRootStoreData* root_store_data) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  // TODO(hchao): investigate to see if we can make this a DCHECK.
-  if (verify_proc_factory_) {
-    verify_proc_ = verify_proc_factory_->CreateCertVerifyProc(
-        std::move(cert_net_fetcher), root_store_data);
-  }
+  verify_proc_ = verify_proc_factory_->CreateCertVerifyProc(
+      std::move(cert_net_fetcher), root_store_data);
 }
 
 void MultiThreadedCertVerifier::SetConfig(const CertVerifier::Config& config) {
diff --git a/net/cert/multi_threaded_cert_verifier.h b/net/cert/multi_threaded_cert_verifier.h
index 1300c3cd..642f4ad 100644
--- a/net/cert/multi_threaded_cert_verifier.h
+++ b/net/cert/multi_threaded_cert_verifier.h
@@ -34,7 +34,6 @@
 class NET_EXPORT_PRIVATE MultiThreadedCertVerifier
     : public CertVerifierWithUpdatableProc {
  public:
-  explicit MultiThreadedCertVerifier(scoped_refptr<CertVerifyProc> verify_proc);
   explicit MultiThreadedCertVerifier(
       scoped_refptr<CertVerifyProc> verify_proc,
       scoped_refptr<CertVerifyProcFactory> verify_proc_factory);
diff --git a/net/cert/test_root_certs_unittest.cc b/net/cert/test_root_certs_unittest.cc
index abb998d..b66f1f506 100644
--- a/net/cert/test_root_certs_unittest.cc
+++ b/net/cert/test_root_certs_unittest.cc
@@ -37,12 +37,12 @@
 #if BUILDFLAG(CHROME_ROOT_STORE_OPTIONAL)
   if (base::FeatureList::IsEnabled(features::kChromeRootStoreUsed)) {
     return CertVerifyProc::CreateBuiltinWithChromeRootStore(
-        /*cert_net_fetcher=*/nullptr);
+        /*cert_net_fetcher=*/nullptr, /*root_store_data=*/nullptr);
   }
 #endif
 #if BUILDFLAG(CHROME_ROOT_STORE_ONLY)
   return CertVerifyProc::CreateBuiltinWithChromeRootStore(
-      /*cert_net_fetcher=*/nullptr);
+      /*cert_net_fetcher=*/nullptr, /*root_store_data=*/nullptr);
 #elif BUILDFLAG(IS_FUCHSIA) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
   return CertVerifyProc::CreateBuiltinVerifyProc(/*cert_net_fetcher=*/nullptr);
 #else
diff --git a/net/cert/trial_comparison_cert_verifier_unittest.cc b/net/cert/trial_comparison_cert_verifier_unittest.cc
index 06ee8ed..f20816d 100644
--- a/net/cert/trial_comparison_cert_verifier_unittest.cc
+++ b/net/cert/trial_comparison_cert_verifier_unittest.cc
@@ -252,6 +252,11 @@
   scoped_refptr<CertVerifyProc> verify_proc_;
 };
 
+scoped_refptr<CertVerifyProcFactory> SwapWithNotCalledProcFactory() {
+  return base::MakeRefCounted<SwapWithNewProcFactory>(
+      base::MakeRefCounted<NotCalledCertVerifyProc>());
+}
+
 struct TrialReportInfo {
   TrialReportInfo(const std::string& hostname,
                   const scoped_refptr<X509Certificate>& unverified_cert,
@@ -347,8 +352,10 @@
   auto verify_proc = base::MakeRefCounted<FakeCertVerifyProc>(OK, dummy_result);
   std::vector<TrialReportInfo> reports;
   TrialComparisonCertVerifier verifier(
-      verify_proc, nullptr, base::MakeRefCounted<NotCalledCertVerifyProc>(),
-      nullptr, base::BindRepeating(&RecordTrialReport, &reports));
+      verify_proc, SwapWithNotCalledProcFactory(),
+      base::MakeRefCounted<NotCalledCertVerifyProc>(),
+      SwapWithNotCalledProcFactory(),
+      base::BindRepeating(&RecordTrialReport, &reports));
   CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", /*flags=*/0,
                                      /*ocsp_response=*/std::string(),
                                      /*sct_list=*/std::string());
@@ -408,7 +415,8 @@
 
   std::vector<TrialReportInfo> reports;
   TrialComparisonCertVerifier verifier(
-      verify_proc1, nullptr, verify_proc2, nullptr,
+      verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2,
+      SwapWithNotCalledProcFactory(),
       base::BindRepeating(&RecordTrialReport, &reports));
 
   CertVerifier::RequestParams params(leaf, "t0.test", /*flags=*/0,
@@ -491,7 +499,8 @@
 
   std::vector<TrialReportInfo> reports;
   TrialComparisonCertVerifier verifier(
-      verify_proc1, nullptr, verify_proc2, nullptr,
+      verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2,
+      SwapWithNotCalledProcFactory(),
       base::BindRepeating(&RecordTrialReport, &reports));
   verifier.set_trial_allowed(true);
 
@@ -552,8 +561,10 @@
 
   std::vector<TrialReportInfo> reports;
   TrialComparisonCertVerifier verifier(
-      verify_proc1, nullptr, base::MakeRefCounted<NotCalledCertVerifyProc>(),
-      nullptr, base::BindRepeating(&RecordTrialReport, &reports));
+      verify_proc1, SwapWithNotCalledProcFactory(),
+      base::MakeRefCounted<NotCalledCertVerifyProc>(),
+      SwapWithNotCalledProcFactory(),
+      base::BindRepeating(&RecordTrialReport, &reports));
   verifier.set_trial_allowed(true);
 
   CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", /*flags=*/0,
@@ -588,6 +599,81 @@
   EXPECT_TRUE(reports.empty());
 }
 
+TEST_F(TrialComparisonCertVerifierTest, ConfigChangedBeforeVerification) {
+  // Primary verifier returns an error status.
+  CertVerifyResult primary_result;
+  primary_result.verified_cert = cert_chain_1_;
+  primary_result.cert_status = CERT_STATUS_DATE_INVALID;
+  scoped_refptr<FakeCertVerifyProc> verify_proc1 =
+      base::MakeRefCounted<FakeCertVerifyProc>(ERR_CERT_DATE_INVALID,
+                                               primary_result);
+
+  CertVerifyResult secondary_result;
+  secondary_result.verified_cert = cert_chain_1_;
+  scoped_refptr<FakeCertVerifyProc> verify_proc2 =
+      base::MakeRefCounted<FakeCertVerifyProc>(OK, secondary_result);
+
+  std::vector<TrialReportInfo> reports;
+  // Both verifiers are initially NotCalledCertVerifyProc, but should swap to
+  // verify_proc1 and verify_proc2 when UpdateChromeRootStoreData is called.
+  TrialComparisonCertVerifier verifier(
+      base::MakeRefCounted<NotCalledCertVerifyProc>(),
+      base::MakeRefCounted<SwapWithNewProcFactory>(verify_proc1),
+      base::MakeRefCounted<NotCalledCertVerifyProc>(),
+      base::MakeRefCounted<SwapWithNewProcFactory>(verify_proc2),
+      base::BindRepeating(&RecordTrialReport, &reports));
+  verifier.set_trial_allowed(true);
+
+  // Change the verifier Chrome Root Store data before verification, so the
+  // Verify should call the verifiers that were swapped in by the factories
+  // instead of the initial ones.
+  verifier.UpdateChromeRootStoreData(nullptr, nullptr);
+
+  CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", /*flags=*/0,
+                                     /*ocsp_response=*/std::string(),
+                                     /*sct_list=*/std::string());
+  CertVerifyResult result;
+  TestCompletionCallback callback;
+  std::unique_ptr<CertVerifier::Request> request;
+  int error = verifier.Verify(params, &result, callback.callback(), &request,
+                              NetLogWithSource());
+  ASSERT_THAT(error, IsError(ERR_IO_PENDING));
+  EXPECT_TRUE(request);
+
+  error = callback.WaitForResult();
+  EXPECT_THAT(error, IsError(ERR_CERT_DATE_INVALID));
+
+  verify_proc2->WaitForVerifyCall();
+  RunUntilIdle();
+
+  // Expect a report.
+  ASSERT_EQ(1U, reports.size());
+  const TrialReportInfo& report = reports[0];
+
+  EXPECT_EQ(CERT_STATUS_DATE_INVALID, report.primary_result.cert_status);
+  EXPECT_EQ(0U, report.trial_result.cert_status);
+
+  EXPECT_TRUE(report.primary_result.verified_cert->EqualsIncludingChain(
+      cert_chain_1_.get()));
+  EXPECT_TRUE(report.trial_result.verified_cert->EqualsIncludingChain(
+      cert_chain_1_.get()));
+  EXPECT_TRUE(report.unverified_cert->EqualsIncludingChain(leaf_cert_1_.get()));
+
+  EXPECT_FALSE(report.enable_rev_checking);
+  EXPECT_FALSE(report.require_rev_checking_local_anchors);
+  EXPECT_FALSE(report.enable_sha1_local_anchors);
+  EXPECT_FALSE(report.disable_symantec_enforcement);
+
+  EXPECT_EQ(1, verify_proc1->num_verifications());
+  EXPECT_EQ(1, verify_proc2->num_verifications());
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialPrimary", 1);
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialSecondary",
+                               1);
+  histograms_.ExpectUniqueSample(
+      "Net.CertVerifier_TrialComparisonResult",
+      TrialComparisonResult::kPrimaryErrorSecondaryValid, 1);
+}
+
 TEST_F(TrialComparisonCertVerifierTest,
        CertVerifyProcChangedDuringPrimaryVerification) {
   CertVerifyResult primary_result;
@@ -595,13 +681,12 @@
   scoped_refptr<FakeCertVerifyProc> verify_proc1 =
       base::MakeRefCounted<FakeCertVerifyProc>(OK, primary_result);
 
-  scoped_refptr<SwapWithNewProcFactory> swap_proc =
-      base::MakeRefCounted<SwapWithNewProcFactory>(
-          base::MakeRefCounted<NotCalledCertVerifyProc>());
   std::vector<TrialReportInfo> reports;
   TrialComparisonCertVerifier verifier(
-      verify_proc1, swap_proc, base::MakeRefCounted<NotCalledCertVerifyProc>(),
-      nullptr, base::BindRepeating(&RecordTrialReport, &reports));
+      verify_proc1, SwapWithNotCalledProcFactory(),
+      base::MakeRefCounted<NotCalledCertVerifyProc>(),
+      SwapWithNotCalledProcFactory(),
+      base::BindRepeating(&RecordTrialReport, &reports));
   verifier.set_trial_allowed(true);
 
   CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", /*flags=*/0,
@@ -651,7 +736,8 @@
 
   std::vector<TrialReportInfo> reports;
   TrialComparisonCertVerifier verifier(
-      verify_proc1, nullptr, verify_proc2, nullptr,
+      verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2,
+      SwapWithNotCalledProcFactory(),
       base::BindRepeating(&RecordTrialReport, &reports));
   verifier.set_trial_allowed(true);
 
@@ -710,12 +796,10 @@
       base::MakeRefCounted<FakeCertVerifyProc>(ERR_CERT_DATE_INVALID,
                                                secondary_result);
 
-  scoped_refptr<SwapWithNewProcFactory> swap_proc =
-      base::MakeRefCounted<SwapWithNewProcFactory>(
-          base::MakeRefCounted<NotCalledCertVerifyProc>());
   std::vector<TrialReportInfo> reports;
   TrialComparisonCertVerifier verifier(
-      verify_proc1, nullptr, verify_proc2, swap_proc,
+      verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2,
+      SwapWithNotCalledProcFactory(),
       base::BindRepeating(&RecordTrialReport, &reports));
   verifier.set_trial_allowed(true);
 
@@ -767,7 +851,8 @@
 
   std::vector<TrialReportInfo> reports;
   TrialComparisonCertVerifier verifier(
-      verify_proc1, nullptr, verify_proc2, nullptr,
+      verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2,
+      SwapWithNotCalledProcFactory(),
       base::BindRepeating(&RecordTrialReport, &reports));
   verifier.set_trial_allowed(true);
 
@@ -816,7 +901,8 @@
 
   std::vector<TrialReportInfo> reports;
   TrialComparisonCertVerifier verifier(
-      verify_proc1, nullptr, verify_proc2, nullptr,
+      verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2,
+      SwapWithNotCalledProcFactory(),
       base::BindRepeating(&RecordTrialReport, &reports));
   verifier.set_trial_allowed(true);
 
@@ -881,7 +967,8 @@
 
   std::vector<TrialReportInfo> reports;
   TrialComparisonCertVerifier verifier(
-      verify_proc1, nullptr, verify_proc2, nullptr,
+      verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2,
+      SwapWithNotCalledProcFactory(),
       base::BindRepeating(&RecordTrialReport, &reports));
   verifier.set_trial_allowed(true);
 
@@ -945,7 +1032,8 @@
 
   std::vector<TrialReportInfo> reports;
   TrialComparisonCertVerifier verifier(
-      verify_proc1, nullptr, verify_proc2, nullptr,
+      verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2,
+      SwapWithNotCalledProcFactory(),
       base::BindRepeating(&RecordTrialReport, &reports));
   verifier.set_trial_allowed(true);
 
@@ -1005,7 +1093,8 @@
 
   std::vector<TrialReportInfo> reports;
   TrialComparisonCertVerifier verifier(
-      verify_proc1, nullptr, verify_proc2, nullptr,
+      verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2,
+      SwapWithNotCalledProcFactory(),
       base::BindRepeating(&RecordTrialReport, &reports));
   verifier.set_trial_allowed(true);
 
@@ -1068,7 +1157,8 @@
 
   std::vector<TrialReportInfo> reports;
   TrialComparisonCertVerifier verifier(
-      verify_proc1, nullptr, verify_proc2, nullptr,
+      verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2,
+      SwapWithNotCalledProcFactory(),
       base::BindRepeating(&RecordTrialReport, &reports));
   verifier.set_trial_allowed(true);
 
@@ -1135,7 +1225,8 @@
 
   std::vector<TrialReportInfo> reports;
   TrialComparisonCertVerifier verifier(
-      verify_proc1, nullptr, verify_proc2, nullptr,
+      verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2,
+      SwapWithNotCalledProcFactory(),
       base::BindRepeating(&RecordTrialReport, &reports));
   verifier.set_trial_allowed(true);
 
@@ -1231,7 +1322,8 @@
 
   std::vector<TrialReportInfo> reports;
   TrialComparisonCertVerifier verifier(
-      verify_proc1, nullptr, verify_proc2, nullptr,
+      verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2,
+      SwapWithNotCalledProcFactory(),
       base::BindRepeating(&RecordTrialReport, &reports));
   verifier.set_trial_allowed(true);
 
@@ -1284,7 +1376,8 @@
 
   std::vector<TrialReportInfo> reports;
   TrialComparisonCertVerifier verifier(
-      verify_proc1, nullptr, verify_proc2, nullptr,
+      verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2,
+      SwapWithNotCalledProcFactory(),
       base::BindRepeating(&RecordTrialReport, &reports));
   verifier.set_trial_allowed(true);
 
@@ -1356,7 +1449,8 @@
 
   std::vector<TrialReportInfo> reports;
   TrialComparisonCertVerifier verifier(
-      verify_proc1, nullptr, verify_proc2, nullptr,
+      verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2,
+      SwapWithNotCalledProcFactory(),
       base::BindRepeating(&RecordTrialReport, &reports));
   verifier.set_trial_allowed(true);
 
@@ -1415,8 +1509,10 @@
 
   std::vector<TrialReportInfo> reports;
   auto verifier = std::make_unique<TrialComparisonCertVerifier>(
-      verify_proc1, nullptr, base::MakeRefCounted<NotCalledCertVerifyProc>(),
-      nullptr, base::BindRepeating(&RecordTrialReport, &reports));
+      verify_proc1, SwapWithNotCalledProcFactory(),
+      base::MakeRefCounted<NotCalledCertVerifyProc>(),
+      SwapWithNotCalledProcFactory(),
+      base::BindRepeating(&RecordTrialReport, &reports));
   verifier->set_trial_allowed(true);
 
   CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", /*flags=*/0,
@@ -1462,8 +1558,10 @@
 
   std::vector<TrialReportInfo> reports;
   auto verifier = std::make_unique<TrialComparisonCertVerifier>(
-      verify_proc1, nullptr, base::MakeRefCounted<NotCalledCertVerifyProc>(),
-      nullptr, base::BindRepeating(&RecordTrialReport, &reports));
+      verify_proc1, SwapWithNotCalledProcFactory(),
+      base::MakeRefCounted<NotCalledCertVerifyProc>(),
+      SwapWithNotCalledProcFactory(),
+      base::BindRepeating(&RecordTrialReport, &reports));
   verifier->set_trial_allowed(true);
 
   CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", /*flags=*/0,
@@ -1525,7 +1623,8 @@
   bool was_report_callback_called = false;
   std::unique_ptr<TrialComparisonCertVerifier> verifier;
   verifier = std::make_unique<TrialComparisonCertVerifier>(
-      verify_proc1, nullptr, verify_proc2, nullptr,
+      verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2,
+      SwapWithNotCalledProcFactory(),
       base::BindLambdaForTesting(
           [&verifier, &was_report_callback_called](
               const std::string& hostname,
@@ -1590,7 +1689,8 @@
 
   std::vector<TrialReportInfo> reports;
   auto verifier = std::make_unique<TrialComparisonCertVerifier>(
-      verify_proc1, nullptr, verify_proc2, nullptr,
+      verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2,
+      SwapWithNotCalledProcFactory(),
       base::BindRepeating(&RecordTrialReport, &reports));
   verifier->set_trial_allowed(true);
 
@@ -1665,7 +1765,8 @@
 
   std::vector<TrialReportInfo> reports;
   TrialComparisonCertVerifier verifier(
-      verify_proc1, nullptr, verify_proc2, nullptr,
+      verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2,
+      SwapWithNotCalledProcFactory(),
       base::BindRepeating(&RecordTrialReport, &reports));
   verifier.set_trial_allowed(true);
 
@@ -1736,7 +1837,8 @@
 
   std::vector<TrialReportInfo> reports;
   TrialComparisonCertVerifier verifier(
-      verify_proc1, nullptr, verify_proc2, nullptr,
+      verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2,
+      SwapWithNotCalledProcFactory(),
       base::BindRepeating(&RecordTrialReport, &reports));
   verifier.set_trial_allowed(true);
 
@@ -1808,7 +1910,8 @@
 
   std::vector<TrialReportInfo> reports;
   TrialComparisonCertVerifier verifier(
-      verify_proc1, nullptr, verify_proc2, nullptr,
+      verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2,
+      SwapWithNotCalledProcFactory(),
       base::BindRepeating(&RecordTrialReport, &reports));
   verifier.set_trial_allowed(true);
 
@@ -1873,7 +1976,8 @@
 
   std::vector<TrialReportInfo> reports;
   TrialComparisonCertVerifier verifier(
-      verify_proc1, nullptr, verify_proc2, nullptr,
+      verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2,
+      SwapWithNotCalledProcFactory(),
       base::BindRepeating(&RecordTrialReport, &reports));
   verifier.set_trial_allowed(true);
 
@@ -1942,7 +2046,8 @@
 
   std::vector<TrialReportInfo> reports;
   TrialComparisonCertVerifier verifier(
-      verify_proc1, nullptr, verify_proc2, nullptr,
+      verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2,
+      SwapWithNotCalledProcFactory(),
       base::BindRepeating(&RecordTrialReport, &reports));
   verifier.set_trial_allowed(true);
 
@@ -1994,7 +2099,8 @@
 
   std::vector<TrialReportInfo> reports;
   TrialComparisonCertVerifier verifier(
-      verify_proc1, nullptr, verify_proc2, nullptr,
+      verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2,
+      SwapWithNotCalledProcFactory(),
       base::BindRepeating(&RecordTrialReport, &reports));
   verifier.set_trial_allowed(true);
 
@@ -2047,7 +2153,8 @@
 
   std::vector<TrialReportInfo> reports;
   TrialComparisonCertVerifier verifier(
-      verify_proc1, nullptr, verify_proc2, nullptr,
+      verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2,
+      SwapWithNotCalledProcFactory(),
       base::BindRepeating(&RecordTrialReport, &reports));
   verifier.set_trial_allowed(true);
 
@@ -2101,7 +2208,8 @@
 
   std::vector<TrialReportInfo> reports;
   TrialComparisonCertVerifier verifier(
-      verify_proc1, nullptr, verify_proc2, nullptr,
+      verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2,
+      SwapWithNotCalledProcFactory(),
       base::BindRepeating(&RecordTrialReport, &reports));
   verifier.set_trial_allowed(true);
 
@@ -2156,7 +2264,8 @@
 
   std::vector<TrialReportInfo> reports;
   TrialComparisonCertVerifier verifier(
-      verify_proc1, nullptr, verify_proc2, nullptr,
+      verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2,
+      SwapWithNotCalledProcFactory(),
       base::BindRepeating(&RecordTrialReport, &reports));
   verifier.set_trial_allowed(true);
 
@@ -2211,7 +2320,8 @@
 
   std::vector<TrialReportInfo> reports;
   TrialComparisonCertVerifier verifier(
-      verify_proc1, nullptr, verify_proc2, nullptr,
+      verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2,
+      SwapWithNotCalledProcFactory(),
       base::BindRepeating(&RecordTrialReport, &reports));
   verifier.set_trial_allowed(true);
 
@@ -2269,7 +2379,8 @@
 
   std::vector<TrialReportInfo> reports;
   TrialComparisonCertVerifier verifier(
-      verify_proc1, nullptr, verify_proc2, nullptr,
+      verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2,
+      SwapWithNotCalledProcFactory(),
       base::BindRepeating(&RecordTrialReport, &reports));
   verifier.set_trial_allowed(true);
 
@@ -2319,7 +2430,8 @@
 
   std::vector<TrialReportInfo> reports;
   TrialComparisonCertVerifier verifier(
-      verify_proc1, nullptr, verify_proc2, nullptr,
+      verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2,
+      SwapWithNotCalledProcFactory(),
       base::BindRepeating(&RecordTrialReport, &reports));
   verifier.set_trial_allowed(true);
 
@@ -2371,7 +2483,8 @@
 
   std::vector<TrialReportInfo> reports;
   TrialComparisonCertVerifier verifier(
-      verify_proc1, nullptr, verify_proc2, nullptr,
+      verify_proc1, SwapWithNotCalledProcFactory(), verify_proc2,
+      SwapWithNotCalledProcFactory(),
       base::BindRepeating(&RecordTrialReport, &reports));
   verifier.set_trial_allowed(true);
 
diff --git a/remoting/host/chromoting_host_services_client.cc b/remoting/host/chromoting_host_services_client.cc
index 04337d5..9fcf5b53 100644
--- a/remoting/host/chromoting_host_services_client.cc
+++ b/remoting/host/chromoting_host_services_client.cc
@@ -37,6 +37,19 @@
     return {};
   }
 #if BUILDFLAG(IS_WIN)
+  DWORD peer_session_id;
+  if (!GetNamedPipeServerSessionId(endpoint.platform_handle().GetHandle().get(),
+                                   &peer_session_id)) {
+    PLOG(ERROR) << "GetNamedPipeServerSessionId failed";
+    return {};
+  }
+  // '0' (default) corresponds to the session the network process runs in.
+  if (peer_session_id != 0) {
+    LOG(ERROR)
+        << "Cannot establish connection with IPC server running in session: "
+        << peer_session_id;
+    return {};
+  }
   // TODO(crbug.com/1378803): Make Windows hosts work with non-isolated
   // connections.
   auto message_pipe = connection.Connect(std::move(endpoint));
diff --git a/services/cert_verifier/trial_comparison_cert_verifier_mojo_unittest.cc b/services/cert_verifier/trial_comparison_cert_verifier_mojo_unittest.cc
index 849f3210..bf944f0 100644
--- a/services/cert_verifier/trial_comparison_cert_verifier_mojo_unittest.cc
+++ b/services/cert_verifier/trial_comparison_cert_verifier_mojo_unittest.cc
@@ -5,6 +5,7 @@
 #include "services/cert_verifier/trial_comparison_cert_verifier_mojo.h"
 
 #include "base/containers/span.h"
+#include "base/memory/scoped_refptr.h"
 #include "base/test/task_environment.h"
 #include "build/build_config.h"
 #include "net/cert/cert_verify_proc.h"
@@ -28,6 +29,8 @@
 #include "net/cert/internal/trust_store_nss.h"
 #endif
 
+namespace {
+
 struct ReceivedReport {
   std::string hostname;
   scoped_refptr<net::X509Certificate> unverified_cert;
@@ -94,6 +97,43 @@
   base::RunLoop run_loop_;
 };
 
+class NotCalledCertVerifyProc : public net::CertVerifyProc {
+ public:
+  bool SupportsAdditionalTrustAnchors() const override { return false; }
+
+ protected:
+  ~NotCalledCertVerifyProc() override = default;
+
+ private:
+  int VerifyInternal(net::X509Certificate* cert,
+                     const std::string& hostname,
+                     const std::string& ocsp_response,
+                     const std::string& sct_list,
+                     int flags,
+                     net::CRLSet* crl_set,
+                     const net::CertificateList& additional_trust_anchors,
+                     net::CertVerifyResult* verify_result,
+                     const net::NetLogWithSource& net_log) override {
+    ADD_FAILURE() << "NotCalledCertVerifyProc was called!";
+    return net::ERR_UNEXPECTED;
+  }
+};
+
+class NotCalledProcFactory : public net::CertVerifyProcFactory {
+ public:
+  scoped_refptr<net::CertVerifyProc> CreateCertVerifyProc(
+      scoped_refptr<net::CertNetFetcher> cert_net_fetcher,
+      const net::ChromeRootStoreData* root_store_data) override {
+    ADD_FAILURE() << "NotCalledProcFactory was called!";
+    return nullptr;
+  }
+
+ protected:
+  ~NotCalledProcFactory() override = default;
+};
+
+}  // namespace
+
 TEST(TrialComparisonCertVerifierMojoTest, SendReportDebugInfo) {
   base::test::TaskEnvironment scoped_task_environment;
 
@@ -158,8 +198,11 @@
   FakeReportClient report_client(
       report_client_remote.InitWithNewPipeAndPassReceiver());
   cert_verifier::TrialComparisonCertVerifierMojo tccvm(
-      true, {}, std::move(report_client_remote), nullptr, nullptr, nullptr,
-      nullptr);
+      true, {}, std::move(report_client_remote),
+      base::MakeRefCounted<NotCalledCertVerifyProc>(),
+      base::MakeRefCounted<NotCalledProcFactory>(),
+      base::MakeRefCounted<NotCalledCertVerifyProc>(),
+      base::MakeRefCounted<NotCalledProcFactory>());
 
   tccvm.OnSendTrialReport("example.com", unverified_cert, false, false, false,
                           false, "stapled ocsp", "sct list", primary_result,
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json
index 3ddb4da..22b3cc3 100644
--- a/testing/buildbot/chromium.chromiumos.json
+++ b/testing/buildbot/chromium.chromiumos.json
@@ -5886,9 +5886,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5677.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5679.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 114.0.5677.0",
+        "description": "Run with ash-chrome version 114.0.5679.0",
         "isolate_profile_data": true,
         "merge": {
           "args": [],
@@ -5900,8 +5900,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v114.0.5677.0",
-              "revision": "version:114.0.5677.0"
+              "location": "lacros_version_skew_tests_v114.0.5679.0",
+              "revision": "version:114.0.5679.0"
             }
           ],
           "dimension_sets": [
@@ -6057,9 +6057,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5677.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5679.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 114.0.5677.0",
+        "description": "Run with ash-chrome version 114.0.5679.0",
         "isolate_profile_data": true,
         "merge": {
           "args": [],
@@ -6071,8 +6071,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v114.0.5677.0",
-              "revision": "version:114.0.5677.0"
+              "location": "lacros_version_skew_tests_v114.0.5679.0",
+              "revision": "version:114.0.5679.0"
             }
           ],
           "dimension_sets": [
@@ -6209,9 +6209,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5677.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5679.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 114.0.5677.0",
+        "description": "Run with ash-chrome version 114.0.5679.0",
         "isolate_profile_data": true,
         "merge": {
           "args": [],
@@ -6223,8 +6223,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v114.0.5677.0",
-              "revision": "version:114.0.5677.0"
+              "location": "lacros_version_skew_tests_v114.0.5679.0",
+              "revision": "version:114.0.5679.0"
             }
           ],
           "dimension_sets": [
diff --git a/testing/buildbot/chromium.coverage.json b/testing/buildbot/chromium.coverage.json
index 80d20332..8a5749f 100644
--- a/testing/buildbot/chromium.coverage.json
+++ b/testing/buildbot/chromium.coverage.json
@@ -25738,9 +25738,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5677.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5679.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 114.0.5677.0",
+        "description": "Run with ash-chrome version 114.0.5679.0",
         "isolate_profile_data": true,
         "merge": {
           "args": [],
@@ -25752,8 +25752,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v114.0.5677.0",
-              "revision": "version:114.0.5677.0"
+              "location": "lacros_version_skew_tests_v114.0.5679.0",
+              "revision": "version:114.0.5679.0"
             }
           ],
           "dimension_sets": [
@@ -25909,9 +25909,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5677.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5679.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 114.0.5677.0",
+        "description": "Run with ash-chrome version 114.0.5679.0",
         "isolate_profile_data": true,
         "merge": {
           "args": [],
@@ -25923,8 +25923,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v114.0.5677.0",
-              "revision": "version:114.0.5677.0"
+              "location": "lacros_version_skew_tests_v114.0.5679.0",
+              "revision": "version:114.0.5679.0"
             }
           ],
           "dimension_sets": [
@@ -26061,9 +26061,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5677.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5679.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 114.0.5677.0",
+        "description": "Run with ash-chrome version 114.0.5679.0",
         "isolate_profile_data": true,
         "merge": {
           "args": [],
@@ -26075,8 +26075,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v114.0.5677.0",
-              "revision": "version:114.0.5677.0"
+              "location": "lacros_version_skew_tests_v114.0.5679.0",
+              "revision": "version:114.0.5679.0"
             }
           ],
           "dimension_sets": [
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 946050d..87df94e9 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -46385,9 +46385,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5677.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5679.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 114.0.5677.0",
+        "description": "Run with ash-chrome version 114.0.5679.0",
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -46398,8 +46398,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v114.0.5677.0",
-              "revision": "version:114.0.5677.0"
+              "location": "lacros_version_skew_tests_v114.0.5679.0",
+              "revision": "version:114.0.5679.0"
             }
           ],
           "dimension_sets": [
@@ -46556,9 +46556,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5677.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5679.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 114.0.5677.0",
+        "description": "Run with ash-chrome version 114.0.5679.0",
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -46569,8 +46569,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v114.0.5677.0",
-              "revision": "version:114.0.5677.0"
+              "location": "lacros_version_skew_tests_v114.0.5679.0",
+              "revision": "version:114.0.5679.0"
             }
           ],
           "dimension_sets": [
@@ -46708,9 +46708,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5677.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5679.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 114.0.5677.0",
+        "description": "Run with ash-chrome version 114.0.5679.0",
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -46721,8 +46721,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v114.0.5677.0",
-              "revision": "version:114.0.5677.0"
+              "location": "lacros_version_skew_tests_v114.0.5679.0",
+              "revision": "version:114.0.5679.0"
             }
           ],
           "dimension_sets": [
@@ -48228,9 +48228,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5677.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5679.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 114.0.5677.0",
+        "description": "Run with ash-chrome version 114.0.5679.0",
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -48241,8 +48241,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v114.0.5677.0",
-              "revision": "version:114.0.5677.0"
+              "location": "lacros_version_skew_tests_v114.0.5679.0",
+              "revision": "version:114.0.5679.0"
             }
           ],
           "dimension_sets": [
@@ -48399,9 +48399,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5677.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5679.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 114.0.5677.0",
+        "description": "Run with ash-chrome version 114.0.5679.0",
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -48412,8 +48412,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v114.0.5677.0",
-              "revision": "version:114.0.5677.0"
+              "location": "lacros_version_skew_tests_v114.0.5679.0",
+              "revision": "version:114.0.5679.0"
             }
           ],
           "dimension_sets": [
@@ -48551,9 +48551,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5677.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5679.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 114.0.5677.0",
+        "description": "Run with ash-chrome version 114.0.5679.0",
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -48564,8 +48564,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v114.0.5677.0",
-              "revision": "version:114.0.5677.0"
+              "location": "lacros_version_skew_tests_v114.0.5679.0",
+              "revision": "version:114.0.5679.0"
             }
           ],
           "dimension_sets": [
@@ -49319,9 +49319,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5677.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5679.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 114.0.5677.0",
+        "description": "Run with ash-chrome version 114.0.5679.0",
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -49332,8 +49332,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v114.0.5677.0",
-              "revision": "version:114.0.5677.0"
+              "location": "lacros_version_skew_tests_v114.0.5679.0",
+              "revision": "version:114.0.5679.0"
             }
           ],
           "dimension_sets": [
diff --git a/testing/buildbot/chromium.memory.json b/testing/buildbot/chromium.memory.json
index 7463e1d..5264360 100644
--- a/testing/buildbot/chromium.memory.json
+++ b/testing/buildbot/chromium.memory.json
@@ -18414,12 +18414,12 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5677.0/test_ash_chrome",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5679.0/test_ash_chrome",
           "--test-launcher-print-test-stdio=always",
           "--combine-ash-logs-on-bots",
           "--asan-symbolize-output"
         ],
-        "description": "Run with ash-chrome version 114.0.5677.0",
+        "description": "Run with ash-chrome version 114.0.5679.0",
         "isolate_profile_data": true,
         "merge": {
           "args": [],
@@ -18431,8 +18431,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v114.0.5677.0",
-              "revision": "version:114.0.5677.0"
+              "location": "lacros_version_skew_tests_v114.0.5679.0",
+              "revision": "version:114.0.5679.0"
             }
           ],
           "dimension_sets": [
@@ -18605,12 +18605,12 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5677.0/test_ash_chrome",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5679.0/test_ash_chrome",
           "--test-launcher-print-test-stdio=always",
           "--combine-ash-logs-on-bots",
           "--asan-symbolize-output"
         ],
-        "description": "Run with ash-chrome version 114.0.5677.0",
+        "description": "Run with ash-chrome version 114.0.5679.0",
         "isolate_profile_data": true,
         "merge": {
           "args": [],
@@ -18622,8 +18622,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v114.0.5677.0",
-              "revision": "version:114.0.5677.0"
+              "location": "lacros_version_skew_tests_v114.0.5679.0",
+              "revision": "version:114.0.5679.0"
             }
           ],
           "dimension_sets": [
@@ -18772,12 +18772,12 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5677.0/test_ash_chrome",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5679.0/test_ash_chrome",
           "--test-launcher-print-test-stdio=always",
           "--combine-ash-logs-on-bots",
           "--asan-symbolize-output"
         ],
-        "description": "Run with ash-chrome version 114.0.5677.0",
+        "description": "Run with ash-chrome version 114.0.5679.0",
         "isolate_profile_data": true,
         "merge": {
           "args": [],
@@ -18789,8 +18789,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v114.0.5677.0",
-              "revision": "version:114.0.5677.0"
+              "location": "lacros_version_skew_tests_v114.0.5679.0",
+              "revision": "version:114.0.5679.0"
             }
           ],
           "dimension_sets": [
diff --git a/testing/buildbot/variants.pyl b/testing/buildbot/variants.pyl
index 5bc11ac..b638ad3 100644
--- a/testing/buildbot/variants.pyl
+++ b/testing/buildbot/variants.pyl
@@ -22,16 +22,16 @@
   },
   'LACROS_VERSION_SKEW_CANARY': {
     'args': [
-      '--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5677.0/test_ash_chrome',
+      '--ash-chrome-path-override=../../lacros_version_skew_tests_v114.0.5679.0/test_ash_chrome',
     ],
-    'description': 'Run with ash-chrome version 114.0.5677.0',
+    'description': 'Run with ash-chrome version 114.0.5679.0',
     'identifier': 'Lacros version skew testing ash canary',
     'swarming': {
       'cipd_packages': [
         {
           'cipd_package': 'chromium/testing/linux-ash-chromium/x86_64/ash.zip',
-          'location': 'lacros_version_skew_tests_v114.0.5677.0',
-          'revision': 'version:114.0.5677.0',
+          'location': 'lacros_version_skew_tests_v114.0.5679.0',
+          'revision': 'version:114.0.5679.0',
         },
       ],
     },
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 7ad0648..0d4e7420 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -6081,6 +6081,26 @@
             ]
         }
     ],
+    "HttpsUpgrades": [
+        {
+            "platforms": [
+                "android",
+                "windows",
+                "chromeos",
+                "chromeos_lacros",
+                "mac",
+                "linux"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "HttpsUpgrades"
+                    ]
+                }
+            ]
+        }
+    ],
     "IOSAddToHomeScreen": [
         {
             "platforms": [
diff --git a/third_party/blink/renderer/core/css/css_math_expression_node.cc b/third_party/blink/renderer/core/css/css_math_expression_node.cc
index 24afe1d..cce7d83 100644
--- a/third_party/blink/renderer/core/css/css_math_expression_node.cc
+++ b/third_party/blink/renderer/core/css/css_math_expression_node.cc
@@ -38,6 +38,7 @@
 #include "third_party/blink/renderer/core/css/css_primitive_value.h"
 #include "third_party/blink/renderer/core/css/css_primitive_value_mappings.h"
 #include "third_party/blink/renderer/core/css/css_value_clamping_utils.h"
+#include "third_party/blink/renderer/core/css/parser/css_parser_context.h"
 #include "third_party/blink/renderer/core/css/properties/css_parsing_utils.h"
 #include "third_party/blink/renderer/core/css/resolver/style_resolver.h"
 #include "third_party/blink/renderer/core/css_value_keywords.h"
diff --git a/third_party/blink/renderer/core/css/css_math_expression_node.h b/third_party/blink/renderer/core/css/css_math_expression_node.h
index 5359ff2..606f7ae 100644
--- a/third_party/blink/renderer/core/css/css_math_expression_node.h
+++ b/third_party/blink/renderer/core/css/css_math_expression_node.h
@@ -39,7 +39,6 @@
 #include "third_party/blink/renderer/core/css/css_math_operator.h"
 #include "third_party/blink/renderer/core/css/css_primitive_value.h"
 #include "third_party/blink/renderer/core/css/css_value.h"
-#include "third_party/blink/renderer/core/css/parser/css_parser_context.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser_token_range.h"
 #include "third_party/blink/renderer/platform/geometry/calculation_value.h"
 #include "third_party/blink/renderer/platform/wtf/forward.h"
@@ -50,6 +49,7 @@
 
 class CalculationExpressionNode;
 class CSSNumericLiteralValue;
+class CSSParserContext;
 
 // The order of this enum should not change since its elements are used as
 // indices in the addSubtractResult matrix.
diff --git a/third_party/blink/renderer/core/css/css_properties.json5 b/third_party/blink/renderer/core/css/css_properties.json5
index bd9ff4b2..33f1c93 100644
--- a/third_party/blink/renderer/core/css/css_properties.json5
+++ b/third_party/blink/renderer/core/css/css_properties.json5
@@ -3608,7 +3608,6 @@
       field_template: "primitive",
       default_value: "0",
       type_name: "int",
-      computed_style_custom_functions: ["setter"],
       typedom_types: ["Number"],
     },
     {
diff --git a/third_party/blink/renderer/core/css/cssom/css_numeric_value.cc b/third_party/blink/renderer/core/css/cssom/css_numeric_value.cc
index 5797db6..999c366c 100644
--- a/third_party/blink/renderer/core/css/cssom/css_numeric_value.cc
+++ b/third_party/blink/renderer/core/css/cssom/css_numeric_value.cc
@@ -20,6 +20,7 @@
 #include "third_party/blink/renderer/core/css/cssom/css_math_product.h"
 #include "third_party/blink/renderer/core/css/cssom/css_math_sum.h"
 #include "third_party/blink/renderer/core/css/cssom/css_unit_value.h"
+#include "third_party/blink/renderer/core/css/parser/css_parser_context.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser_token_stream.h"
 #include "third_party/blink/renderer/core/css/parser/css_tokenizer.h"
 #include "third_party/blink/renderer/core/css_value_keywords.h"
diff --git a/third_party/blink/renderer/core/css/style_engine.cc b/third_party/blink/renderer/core/css/style_engine.cc
index 8b07b57..92f07f5 100644
--- a/third_party/blink/renderer/core/css/style_engine.cc
+++ b/third_party/blink/renderer/core/css/style_engine.cc
@@ -34,6 +34,7 @@
 #include "base/hash/hash.h"
 #include "base/ranges/algorithm.h"
 #include "third_party/blink/public/mojom/frame/color_scheme.mojom-blink.h"
+#include "third_party/blink/public/mojom/timing/resource_timing.mojom-blink.h"
 #include "third_party/blink/public/platform/web_theme_engine.h"
 #include "third_party/blink/renderer/core/css/cascade_layer_map.h"
 #include "third_party/blink/renderer/core/css/check_pseudo_has_cache_scope.h"
diff --git a/third_party/blink/renderer/core/html/html_link_element.h b/third_party/blink/renderer/core/html/html_link_element.h
index e81a7b6..041552c 100644
--- a/third_party/blink/renderer/core/html/html_link_element.h
+++ b/third_party/blink/renderer/core/html/html_link_element.h
@@ -27,6 +27,7 @@
 #include <memory>
 
 #include "base/task/single_thread_task_runner.h"
+#include "services/network/public/mojom/referrer_policy.mojom-shared.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/css/css_style_sheet.h"
 #include "third_party/blink/renderer/core/dom/create_element_flags.h"
diff --git a/third_party/blink/renderer/core/html/parser/preload_request.h b/third_party/blink/renderer/core/html/parser/preload_request.h
index 4486012..dad33a8 100644
--- a/third_party/blink/renderer/core/html/parser/preload_request.h
+++ b/third_party/blink/renderer/core/html/parser/preload_request.h
@@ -8,7 +8,7 @@
 #include <memory>
 
 #include "base/memory/ptr_util.h"
-#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink-forward.h"
+#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
 #include "third_party/blink/public/mojom/script/script_type.mojom-blink-forward.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/script/script.h"
diff --git a/third_party/blink/renderer/core/layout/build.gni b/third_party/blink/renderer/core/layout/build.gni
index 127763c..a480c21 100644
--- a/third_party/blink/renderer/core/layout/build.gni
+++ b/third_party/blink/renderer/core/layout/build.gni
@@ -650,8 +650,6 @@
   "ng/table/ng_table_row_layout_algorithm.h",
   "ng/table/ng_table_section_layout_algorithm.cc",
   "ng/table/ng_table_section_layout_algorithm.h",
-  "order_iterator.cc",
-  "order_iterator.h",
   "overflow_model.h",
   "pointer_events_hit_rules.cc",
   "pointer_events_hit_rules.h",
diff --git a/third_party/blink/renderer/core/layout/flexible_box_algorithm.h b/third_party/blink/renderer/core/layout/flexible_box_algorithm.h
index 50434562..5612453b 100644
--- a/third_party/blink/renderer/core/layout/flexible_box_algorithm.h
+++ b/third_party/blink/renderer/core/layout/flexible_box_algorithm.h
@@ -37,7 +37,6 @@
 #include "third_party/blink/renderer/core/layout/ng/ng_baseline_utils.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_block_node.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h"
-#include "third_party/blink/renderer/core/layout/order_iterator.h"
 #include "third_party/blink/renderer/core/style/computed_style.h"
 #include "third_party/blink/renderer/platform/geometry/layout_point.h"
 #include "third_party/blink/renderer/platform/geometry/layout_unit.h"
diff --git a/third_party/blink/renderer/core/layout/order_iterator.cc b/third_party/blink/renderer/core/layout/order_iterator.cc
deleted file mode 100644
index 550126c..0000000
--- a/third_party/blink/renderer/core/layout/order_iterator.cc
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (C) 2011 Google Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "third_party/blink/renderer/core/layout/order_iterator.h"
-
-#include "third_party/blink/renderer/core/layout/layout_box.h"
-
-namespace blink {
-
-OrderIterator::OrderIterator(const LayoutBox* container_box)
-    : container_box_(container_box) {}
-
-LayoutBox* OrderIterator::First() {
-  Reset();
-  return Next();
-}
-
-LayoutBox* OrderIterator::Next() {
-  do {
-    if (!current_child_) {
-      if (order_values_iterator_ == order_values_.end())
-        return nullptr;
-
-      if (!is_reset_) {
-        ++order_values_iterator_;
-        if (order_values_iterator_ == order_values_.end())
-          return nullptr;
-      } else {
-        is_reset_ = false;
-      }
-
-      current_child_ = container_box_->FirstChildBox();
-    } else {
-      current_child_ = current_child_->NextSiblingBox();
-    }
-  } while (!current_child_ ||
-           ResolvedOrder(*current_child_) != *order_values_iterator_);
-
-  return current_child_;
-}
-
-void OrderIterator::Reset() {
-  current_child_.Clear();
-  order_values_iterator_ = order_values_.begin();
-  is_reset_ = true;
-}
-
-int OrderIterator::ResolvedOrder(const LayoutBox& child) const {
-  if (container_box_->StyleRef().IsDeprecatedWebkitBox())
-    return child.StyleRef().BoxOrdinalGroup();
-  return child.StyleRef().Order();
-}
-
-OrderIteratorPopulator::~OrderIteratorPopulator() {
-  iterator_.Reset();
-}
-
-void OrderIteratorPopulator::CollectChild(const LayoutBox* child) {
-  iterator_.order_values_.insert(iterator_.ResolvedOrder(*child));
-}
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/order_iterator.h b/third_party/blink/renderer/core/layout/order_iterator.h
deleted file mode 100644
index b06142c..0000000
--- a/third_party/blink/renderer/core/layout/order_iterator.h
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright (C) 2011 Google Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_ORDER_ITERATOR_H_
-#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_ORDER_ITERATOR_H_
-
-#include "third_party/blink/renderer/platform/allow_discouraged_type.h"
-#include "third_party/blink/renderer/platform/heap/member.h"
-#include "third_party/blink/renderer/platform/heap/visitor.h"
-#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
-
-#include <set>
-
-namespace blink {
-
-class LayoutBox;
-
-class OrderIterator {
-  DISALLOW_NEW();
-
- public:
-  friend class OrderIteratorPopulator;
-
-  explicit OrderIterator(const LayoutBox*);
-  OrderIterator(const OrderIterator&) = delete;
-  OrderIterator& operator=(const OrderIterator&) = delete;
-
-  LayoutBox* CurrentChild() { return current_child_; }
-  const LayoutBox* CurrentChild() const { return current_child_; }
-  LayoutBox* First();
-  const LayoutBox* First() const {
-    return const_cast<OrderIterator*>(this)->First();
-  }
-  LayoutBox* Next();
-  const LayoutBox* Next() const {
-    return const_cast<OrderIterator*>(this)->Next();
-  }
-
-  void Trace(Visitor* visitor) const {
-    visitor->Trace(container_box_);
-    visitor->Trace(current_child_);
-  }
-
- private:
-  void Reset();
-
-  // Returns the order to use for |child|.
-  int ResolvedOrder(const LayoutBox& child) const;
-
-  Member<const LayoutBox> container_box_;
-
-  Member<LayoutBox> current_child_;
-
-  using OrderValues ALLOW_DISCOURAGED_TYPE("TODO(crbug.com/1404327") =
-      std::set<int>;
-  OrderValues order_values_;
-  OrderValues::const_iterator order_values_iterator_;
-  // Set by |Reset()|, triggers iteration to start from the beginning.
-  bool is_reset_ = false;
-};
-
-class OrderIteratorPopulator {
-  STACK_ALLOCATED();
-
- public:
-  explicit OrderIteratorPopulator(OrderIterator& iterator)
-      : iterator_(iterator) {
-    iterator_.order_values_.clear();
-  }
-
-  ~OrderIteratorPopulator();
-
-  void CollectChild(const LayoutBox*);
-
- private:
-  OrderIterator& iterator_;
-};
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_ORDER_ITERATOR_H_
diff --git a/third_party/blink/renderer/core/loader/link_loader.cc b/third_party/blink/renderer/core/loader/link_loader.cc
index 075cd10..fdf4137 100644
--- a/third_party/blink/renderer/core/loader/link_loader.cc
+++ b/third_party/blink/renderer/core/loader/link_loader.cc
@@ -31,6 +31,7 @@
 
 #include "third_party/blink/renderer/core/loader/link_loader.h"
 
+#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-shared.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
diff --git a/third_party/blink/renderer/core/loader/resource/resource_loader_code_cache_test.cc b/third_party/blink/renderer/core/loader/resource/resource_loader_code_cache_test.cc
index 4e8752e..d741ea6 100644
--- a/third_party/blink/renderer/core/loader/resource/resource_loader_code_cache_test.cc
+++ b/third_party/blink/renderer/core/loader/resource/resource_loader_code_cache_test.cc
@@ -4,6 +4,7 @@
 
 #include "base/task/single_thread_task_runner.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
 #include "third_party/blink/renderer/core/loader/resource/script_resource.h"
 #include "third_party/blink/renderer/platform/exported/wrapped_resource_response.h"
 #include "third_party/blink/renderer/platform/loader/fetch/cached_metadata.h"
diff --git a/third_party/blink/renderer/core/loader/threadable_loader.cc b/third_party/blink/renderer/core/loader/threadable_loader.cc
index f19bece0..4f6eea1 100644
--- a/third_party/blink/renderer/core/loader/threadable_loader.cc
+++ b/third_party/blink/renderer/core/loader/threadable_loader.cc
@@ -38,6 +38,7 @@
 #include "services/network/public/cpp/cors/cors_error_status.h"
 #include "services/network/public/mojom/cors.mojom-blink.h"
 #include "services/network/public/mojom/fetch_api.mojom-blink.h"
+#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
 #include "third_party/blink/public/platform/task_type.h"
 #include "third_party/blink/renderer/core/frame/frame_console.h"
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
diff --git a/third_party/blink/renderer/core/paint/block_painter.cc b/third_party/blink/renderer/core/paint/block_painter.cc
index 7663e73..1eab9805 100644
--- a/third_party/blink/renderer/core/paint/block_painter.cc
+++ b/third_party/blink/renderer/core/paint/block_painter.cc
@@ -166,16 +166,6 @@
   ObjectPainter(child).PaintAllPhasesAtomically(float_paint_info);
 }
 
-void BlockPainter::PaintChildrenAtomically(const OrderIterator& order_iterator,
-                                           const PaintInfo& paint_info) {
-  if (paint_info.DescendantPaintingBlocked())
-    return;
-  for (const LayoutBox* child = order_iterator.First(); child;
-       child = order_iterator.Next()) {
-    PaintAllChildPhasesAtomically(*child, paint_info);
-  }
-}
-
 void BlockPainter::PaintAllChildPhasesAtomically(const LayoutBox& child,
                                                  const PaintInfo& paint_info) {
   if (paint_info.DescendantPaintingBlocked())
diff --git a/third_party/blink/renderer/core/paint/block_painter.h b/third_party/blink/renderer/core/paint/block_painter.h
index 42acbfd..a524e50 100644
--- a/third_party/blink/renderer/core/paint/block_painter.h
+++ b/third_party/blink/renderer/core/paint/block_painter.h
@@ -7,7 +7,6 @@
 
 #include "base/gtest_prod_util.h"
 #include "third_party/blink/renderer/core/core_export.h"
-#include "third_party/blink/renderer/core/layout/order_iterator.h"
 #include "third_party/blink/renderer/platform/geometry/layout_rect.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
 
@@ -35,7 +34,6 @@
 
   // See ObjectPainter::PaintAllPhasesAtomically().
   void PaintAllChildPhasesAtomically(const LayoutBox&, const PaintInfo&);
-  void PaintChildrenAtomically(const OrderIterator&, const PaintInfo&);
   static void PaintInlineBox(const InlineBox&, const PaintInfo&);
 
  private:
diff --git a/third_party/blink/renderer/core/speculation_rules/speculation_rules_header.cc b/third_party/blink/renderer/core/speculation_rules/speculation_rules_header.cc
index 041f3a2..e48cad4 100644
--- a/third_party/blink/renderer/core/speculation_rules/speculation_rules_header.cc
+++ b/third_party/blink/renderer/core/speculation_rules/speculation_rules_header.cc
@@ -11,6 +11,7 @@
 #include "third_party/blink/public/common/origin_trials/trial_token.h"
 #include "third_party/blink/public/common/origin_trials/trial_token_result.h"
 #include "third_party/blink/public/mojom/devtools/console_message.mojom-shared.h"
+#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-shared.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/execution_context/security_context.h"
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
diff --git a/third_party/blink/renderer/core/style/computed_style.h b/third_party/blink/renderer/core/style/computed_style.h
index 31ee4653..a476a12 100644
--- a/third_party/blink/renderer/core/style/computed_style.h
+++ b/third_party/blink/renderer/core/style/computed_style.h
@@ -3179,14 +3179,6 @@
     SetOpacityInternal(v);
   }
 
-  // order
-  void SetOrder(int o) {
-    // TODO(ikilpatrick): Remove this setter once OrderIterator is removed.
-    // We restrict the smallest value to int min + 2 because we use int min and
-    // int min + 1 as special values in a hash set.
-    SetOrderInternal(max(std::numeric_limits<int>::min() + 2, o));
-  }
-
   // orphans
   void SetOrphans(int16_t o) { SetOrphansInternal(ClampTo<int16_t>(o, 1)); }
 
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_command_encoder.idl b/third_party/blink/renderer/modules/webgpu/gpu_command_encoder.idl
index c17f860..314dcd1 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_command_encoder.idl
+++ b/third_party/blink/renderer/modules/webgpu/gpu_command_encoder.idl
@@ -44,7 +44,7 @@
         GPUBuffer destination,
         GPUSize64 destinationOffset);
 
-   [RaisesException] void writeTimestamp(GPUQuerySet querySet, GPUSize32 queryIndex);
+   [NoAllocDirectCall, RaisesException] void writeTimestamp(GPUQuerySet querySet, GPUSize32 queryIndex);
 
     [NoAllocDirectCall] void clearBuffer(
         GPUBuffer buffer,
diff --git a/third_party/blink/renderer/platform/graphics/paint/paint_controller.cc b/third_party/blink/renderer/platform/graphics/paint/paint_controller.cc
index 5c30a6a..1b2c9e4 100644
--- a/third_party/blink/renderer/platform/graphics/paint/paint_controller.cc
+++ b/third_party/blink/renderer/platform/graphics/paint/paint_controller.cc
@@ -742,6 +742,17 @@
   num_indexed_items_ = 0;
   num_sequential_matches_ = 0;
   num_out_of_order_matches_ = 0;
+
+  if (VLOG_IS_ON(1)) {
+    VLOG(1) << "PaintController::CommitNewDisplayItems() completed";
+    if (VLOG_IS_ON(3)) {
+      ShowDebugDataWithPaintRecords();
+    } else if (VLOG_IS_ON(2)) {
+      ShowDebugData();
+    } else if (VLOG_IS_ON(1)) {
+      ShowCompactDebugData();
+    }
+  }
 #endif
 }
 
@@ -781,18 +792,6 @@
 
   for (auto& chunk : current_paint_artifact_->PaintChunks())
     chunk.client_is_just_created = false;
-
-#if DCHECK_IS_ON()
-  if (VLOG_IS_ON(1)) {
-    VLOG(1) << "PaintController::FinishCycle() completed";
-    if (VLOG_IS_ON(3))
-      ShowDebugDataWithPaintRecords();
-    else if (VLOG_IS_ON(2))
-      ShowDebugData();
-    else if (VLOG_IS_ON(1))
-      ShowCompactDebugData();
-  }
-#endif
 }
 
 size_t PaintController::ApproximateUnsharedMemoryUsage() const {
diff --git a/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder.cc b/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder.cc
index 989a6277..5d7ae49 100644
--- a/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder.cc
+++ b/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder.cc
@@ -297,7 +297,7 @@
 }
 
 cc::YUVSubsampling AVIFImageDecoder::GetYUVSubsampling() const {
-  switch (decoder_->image->yuvFormat) {
+  switch (avif_yuv_format_) {
     case AVIF_PIXEL_FORMAT_YUV420:
       return cc::YUVSubsampling::k420;
     case AVIF_PIXEL_FORMAT_YUV422:
@@ -307,10 +307,16 @@
     case AVIF_PIXEL_FORMAT_YUV400:
       return cc::YUVSubsampling::kUnknown;
     case AVIF_PIXEL_FORMAT_NONE:
-    case AVIF_PIXEL_FORMAT_COUNT:
-      NOTREACHED();
+      // avif_yuv_format_ is initialized to AVIF_PIXEL_FORMAT_NONE in the
+      // constructor. If we have called SetSize() successfully at the end
+      // of UpdateDemuxer(), avif_yuv_format_ cannot possibly be
+      // AVIF_PIXEL_FORMAT_NONE.
+      CHECK(!IsDecodedSizeAvailable());
       return cc::YUVSubsampling::kUnknown;
+    case AVIF_PIXEL_FORMAT_COUNT:
+      break;
   }
+  NOTREACHED_NORETURN() << "Invalid YUV format: " << avif_yuv_format_;
 }
 
 gfx::Size AVIFImageDecoder::DecodedYUVSize(cc::YUVIndex index) const {
@@ -849,6 +855,15 @@
       ImageIsHighBitDepth() &&
       high_bit_depth_decoding_option_ == kHighBitDepthToHalfFloat;
 
+  // Verify that AVIF_PIXEL_FORMAT_{YUV444,YUV422,YUV420,YUV400} are
+  // consecutive.
+  static_assert(AVIF_PIXEL_FORMAT_YUV422 == AVIF_PIXEL_FORMAT_YUV444 + 1);
+  static_assert(AVIF_PIXEL_FORMAT_YUV420 == AVIF_PIXEL_FORMAT_YUV422 + 1);
+  static_assert(AVIF_PIXEL_FORMAT_YUV400 == AVIF_PIXEL_FORMAT_YUV420 + 1);
+  // Assert that container->yuvFormat is one of the four YUV formats in AV1.
+  CHECK(container->yuvFormat >= AVIF_PIXEL_FORMAT_YUV444 &&
+        container->yuvFormat <= AVIF_PIXEL_FORMAT_YUV400)
+      << "Invalid YUV format: " << container->yuvFormat;
   avif_yuv_format_ = container->yuvFormat;
   avifPixelFormatInfo format_info;
   avifGetPixelFormatInfo(container->yuvFormat, &format_info);
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource.cc b/third_party/blink/renderer/platform/loader/fetch/resource.cc
index 8207141..973a077 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource.cc
@@ -34,6 +34,7 @@
 #include "base/time/default_clock.h"
 #include "build/build_config.h"
 #include "services/network/public/mojom/fetch_api.mojom-blink.h"
+#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-shared.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/web_security_origin.h"
 #include "third_party/blink/renderer/platform/heap/collection_support/heap_vector.h"
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h
index a20e403..ca741d72 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h
@@ -40,7 +40,7 @@
 #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink-forward.h"
 #include "third_party/blink/public/mojom/loader/resource_cache.mojom-blink.h"
 #include "third_party/blink/public/mojom/service_worker/controller_service_worker_mode.mojom-blink-forward.h"
-#include "third_party/blink/public/mojom/timing/resource_timing.mojom-blink-forward.h"
+#include "third_party/blink/public/mojom/timing/resource_timing.mojom-blink.h"
 #include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_map.h"
 #include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_set.h"
 #include "third_party/blink/renderer/platform/heap/collection_support/heap_vector.h"
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc b/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc
index fcfcc1d3..fc7c1e0 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc
@@ -53,6 +53,7 @@
 #include "third_party/blink/public/common/thread_safe_browser_interface_broker_proxy.h"
 #include "third_party/blink/public/mojom/blob/blob_registry.mojom-blink.h"
 #include "third_party/blink/public/mojom/devtools/console_message.mojom-blink.h"
+#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
 #include "third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom-shared.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/web_code_cache_loader.h"
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_response.cc b/third_party/blink/renderer/platform/loader/fetch/resource_response.cc
index c14e253b..79aaa06 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_response.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_response.cc
@@ -35,6 +35,7 @@
 #include "net/ssl/ssl_info.h"
 #include "services/network/public/cpp/cors/cors.h"
 #include "services/network/public/mojom/fetch_api.mojom-blink.h"
+#include "third_party/blink/public/mojom/timing/resource_timing.mojom-blink.h"
 #include "third_party/blink/public/platform/web_url_response.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_load_timing.h"
 #include "third_party/blink/renderer/platform/network/http_names.h"
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_response.h b/third_party/blink/renderer/platform/loader/fetch/resource_response.h
index 9c50fb8..7ee6413b 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_response.h
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_response.h
@@ -35,12 +35,11 @@
 #include "net/base/ip_endpoint.h"
 #include "net/ssl/ssl_info.h"
 #include "services/network/public/cpp/trigger_attestation.h"
-#include "services/network/public/mojom/alternate_protocol_usage.mojom-shared.h"
-#include "services/network/public/mojom/cross_origin_embedder_policy.mojom-shared.h"
+#include "services/network/public/mojom/cross_origin_embedder_policy.mojom-forward.h"
 #include "services/network/public/mojom/fetch_api.mojom-shared.h"
 #include "services/network/public/mojom/ip_address_space.mojom-shared.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
-#include "third_party/blink/public/mojom/timing/resource_timing.mojom-blink.h"
+#include "third_party/blink/public/mojom/timing/resource_timing.mojom-blink-forward.h"
 #include "third_party/blink/public/platform/web_url_response.h"
 #include "third_party/blink/renderer/platform/network/http_header_map.h"
 #include "third_party/blink/renderer/platform/network/http_parsers.h"
diff --git a/third_party/blink/renderer/platform/loader/fetch/url_loader/cached_metadata_handler.cc b/third_party/blink/renderer/platform/loader/fetch/url_loader/cached_metadata_handler.cc
index 2ab1349a..1ce7df0 100644
--- a/third_party/blink/renderer/platform/loader/fetch/url_loader/cached_metadata_handler.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/url_loader/cached_metadata_handler.cc
@@ -5,6 +5,7 @@
 #include "third_party/blink/renderer/platform/loader/fetch/url_loader/cached_metadata_handler.h"
 
 #include "base/time/time.h"
+#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
 #include "third_party/blink/public/mojom/loader/code_cache.mojom-blink.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/web_security_origin.h"
diff --git a/third_party/blink/renderer/platform/loader/fetch/url_loader/worker_main_script_loader.cc b/third_party/blink/renderer/platform/loader/fetch/url_loader/worker_main_script_loader.cc
index fdb8149..e39be3dd 100644
--- a/third_party/blink/renderer/platform/loader/fetch/url_loader/worker_main_script_loader.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/url_loader/worker_main_script_loader.cc
@@ -10,6 +10,7 @@
 #include "services/network/public/mojom/url_response_head.mojom.h"
 #include "third_party/blink/public/common/loader/referrer_utils.h"
 #include "third_party/blink/public/mojom/loader/code_cache.mojom-blink.h"
+#include "third_party/blink/public/mojom/timing/resource_timing.mojom-blink.h"
 #include "third_party/blink/public/platform/resource_load_info_notifier_wrapper.h"
 #include "third_party/blink/public/platform/url_conversion.h"
 #include "third_party/blink/public/platform/web_url.h"
diff --git a/third_party/blink/renderer/platform/loader/testing/mock_fetch_context.h b/third_party/blink/renderer/platform/loader/testing/mock_fetch_context.h
index cc39a2d..11aec65 100644
--- a/third_party/blink/renderer/platform/loader/testing/mock_fetch_context.h
+++ b/third_party/blink/renderer/platform/loader/testing/mock_fetch_context.h
@@ -8,7 +8,7 @@
 #include "base/task/single_thread_task_runner.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink-forward.h"
-#include "third_party/blink/public/mojom/timing/resource_timing.mojom-blink-forward.h"
+#include "third_party/blink/public/mojom/timing/resource_timing.mojom-blink.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/resource_load_info_notifier_wrapper.h"
 #include "third_party/blink/public/platform/weak_wrapper_resource_load_info_notifier.h"
diff --git a/third_party/blink/tools/blinkpy/common/net/results_fetcher_test.py b/third_party/blink/tools/blinkpy/common/net/results_fetcher_test.py
index adb0290..f07824b 100644
--- a/third_party/blink/tools/blinkpy/common/net/results_fetcher_test.py
+++ b/third_party/blink/tools/blinkpy/common/net/results_fetcher_test.py
@@ -176,6 +176,70 @@
                                               'foo(bar)'),
             'https://storage.googleapis.com/foo_bar_')
 
+    def test_gather_results(self):
+        self.fetcher.web.append_prpc_response({
+            'testResults': [{
+                'name':
+                ('invocations/task-chromium-swarm.appspot.com-6139bb/'
+                 'tests/ninja:%2F%2F:blink_web_tests%2Fshould-pass.html/'
+                 'results/033e-aaaa'),
+                'testId':
+                'ninja://:blink_web_tests/should-pass.html',
+                'status':
+                'FAIL',
+            }, {
+                'name':
+                ('invocations/task-chromium-swarm.appspot.com-6139bb/'
+                 'tests/ninja:%2F%2F:blink_web_tests%2Fshould-pass.html/'
+                 'results/033e-bbbb'),
+                'testId':
+                'ninja://:blink_web_tests/should-pass.html',
+                'status':
+                'PASS',
+                'expected':
+                True,
+            }, {
+                'name': ('invocations/task-chromium-swarm.appspot.com-6139bb/'
+                         'tests/ninja:%2F%2F:blink_wpt_tests%2F'
+                         'external%2Fwpt%2Ftimeout.html/'
+                         'results/033e-cccc'),
+                'testId':
+                'ninja://:blink_wpt_tests/external/wpt/timeout.html',
+                'status':
+                'ABORT',
+                'expected':
+                True,
+            }],
+        })
+        self.fetcher.web.append_prpc_response({
+            'artifacts': [{
+                'name':
+                ('invocations/task-chromium-swarm.appspot.com-6139bb/'
+                 'tests/ninja:%2F%2F:blink_web_tests%2Fshould-pass.html/'
+                 'results/033e-aaaa/'
+                 'artifacts/actual_text'),
+                'artifactId':
+                'actual_text',
+                'fetchUrl':
+                'https://results.usercontent.cr.dev/actual_text',
+            }],
+        })
+        results = self.fetcher.gather_results(Build('linux-rel', 9000, '1234'),
+                                              'blink_web_tests (with patch)')
+
+        result = results.result_for_test('should-pass.html')
+        self.assertEqual(result.actual_results(), ['FAIL', 'PASS'])
+        self.assertFalse(result.did_run_as_expected())
+        self.assertEqual(result.result_dict()['artifacts'], {
+            'actual_text': [
+                'https://results.usercontent.cr.dev/actual_text',
+            ],
+        })
+
+        result = results.result_for_test('external/wpt/timeout.html')
+        self.assertEqual(result.actual_results(), ['TIMEOUT'])
+        self.assertTrue(result.did_run_as_expected())
+
     def test_fetch_wpt_report_urls(self):
         self.fetcher.web.append_prpc_response({
             'artifacts': [{
diff --git a/third_party/blink/tools/blinkpy/tool/commands/rebaseline_cl_unittest.py b/third_party/blink/tools/blinkpy/tool/commands/rebaseline_cl_unittest.py
index 8f037c5..de54841 100644
--- a/third_party/blink/tools/blinkpy/tool/commands/rebaseline_cl_unittest.py
+++ b/third_party/blink/tools/blinkpy/tool/commands/rebaseline_cl_unittest.py
@@ -155,77 +155,6 @@
                 },
             },
         }
-        # TODO(crbug.com/1213998): Fix the example web test result format.
-        raw_test_results = [{
-            "name":
-            "invocations/task-chromium-swarm.appspot.com-2/tests/ninja:%2F%2F:blink_web_tests%2Ftwo%2Fimage-fail.html",
-            "testId": "ninja://:blink_web_tests/two/image-fail.html",
-            "resultId": "2",
-            "variant": {
-                "def": {
-                    "builder": "",
-                    "os": "",
-                    "test_suite": "blink_web_tests"
-                }
-            },
-            "status": "FAIL"
-        }, {
-            "name":
-            "invocations/task-chromium-swarm.appspot.com-1/tests/ninja:%2F%2F:blink_wpt_tests%2Fone%2Fmissing.html",
-            "testId": "ninja://:blink_wpt_tests/one/missing.html",
-            "resultId": "1",
-            "variant": {
-                "def": {
-                    "builder": "",
-                    "os": "",
-                    "test_suite": "blink_web_tests"
-                }
-            },
-            "status": "FAIL"
-        }, {
-            "name":
-            "invocations/task-chromium-swarm.appspot.com-2/tests/ninja:%2F%2F:blink_web_tests%2Fone%2Fcrash.html",
-            "testId": "ninja://:blink_web_tests/one/crash.html",
-            "resultId": "3",
-            "variant": {
-                "def": {
-                    "builder": "",
-                    "os": "",
-                    "test_suite": "blink_web_tests"
-                }
-            },
-            "status": "CRASH"
-        }]
-        raw_artifacts = [{
-            "name":
-            "invocations/task-chromium-swarm.appspot.com-1/tests/ninja:%2F%2F:blink_wpt_tests%2Fone%2Fmissing.html/results/1",
-            "artifactId": "actual_image",
-            "fetchUrl":
-            "https://results.usercontent.cr.dev/invocations/task-chromium-swarm.appspot.com-1/tests/ninja:%2F%2F:blink_wpt_tests%2Fone%2Fmissing.html/results/artifacts/actual_image?token=1",
-            "contentType": "image/png",
-        }, {
-            "name":
-            "invocations/task-chromium-swarm.appspot.com-2/tests/ninja:%2F%2F:blink_web_tests%2Ftwo%2Fimage-fail.html/results/2",
-            "artifactId": "actual_image",
-            "fetchUrl":
-            "https://results.usercontent.cr.dev/invocations/task-chromium-swarm.appspot.com-2/tests/ninja:%2F%2F:blink_web_tests%2Ftwo%2Fimage-fail.html/results/artifacts/actual_image?token=2",
-            "contentType": "image/png",
-        }, {
-            "name":
-            "invocations/task-chromium-swarm.appspot.com-2/tests/ninja:%2F%2F:blink_web_tests%2Fone%2Fcrash.html/results/3",
-            "artifactId": "actual_text",
-            "fetchUrl":
-            "https://results.usercontent.cr.dev/invocations/task-chromium-swarm.appspot.com-2/tests/ninja:%2F%2F:blink_web_tests%2Fone%2Fcrash.html/results/artifacts/actual_text?token=3",
-            "contentType": "text",
-        }]
-        # TODO(crbug.com/1376646): Need to test the ResultDB flag-specific path.
-        # Ideally, we would run all the same tests on both the ResultDB-enabled
-        # and ResultDB-disabled paths, only changing what `WebTestResults` are
-        # returned.
-        self.web_test_resultdb = self.tool.results_fetcher.make_results_from_raw_rdb(
-            raw_test_results,
-            raw_artifacts,
-            step_name='blink_web_tests (with patch)')
 
         for build in self.builds:
             self.tool.results_fetcher.set_results(
@@ -327,21 +256,6 @@
         self.assertEqual(self.tool.filesystem.files, files_before)
         self.assertEqual(self.tool.git().added_paths, set())
 
-    def test_execute_basic_resultDB(self):
-        # By default, with no arguments or options, rebaseline-cl rebaselines
-        # all of the tests that unexpectedly failed.
-        for build in self.builds:
-            self.tool.results_fetcher.set_results(build,
-                                                  self.web_test_resultdb)
-        exit_code = self.command.execute(self.command_options(resultDB=True),
-                                         [], self.tool)
-        self.assertEqual(exit_code, 0)
-        self.assertLog([
-            'INFO: All builds finished.\n',
-            'INFO: Rebaselining one/missing.html\n',
-            'INFO: Rebaselining two/image-fail.html\n',
-        ])
-
     def test_execute_with_test_name_file(self):
         fs = self.mac_port.host.filesystem
         test_name_file = fs.mktemp()
@@ -369,34 +283,6 @@
             'INFO: Rebaselining two/image-fail.html\n',
         ])
 
-    def test_execute_with_test_name_file_resultDB(self):
-        fs = self.mac_port.host.filesystem
-        test_name_file = fs.mktemp()
-        fs.write_text_file(
-            test_name_file,
-            textwrap.dedent('''
-            one/missing.html
-              two/missing.html
-            # one/slow-fail.html
-            #
-
-            ones/does-not-exist.html
-                two/image-fail.html   '''))
-        for build in self.builds:
-            self.tool.results_fetcher.set_results(build,
-                                                  self.web_test_resultdb)
-        exit_code = self.command.execute(
-            self.command_options(test_name_file=test_name_file, resultDB=True),
-            [], self.tool)
-        self.assertEqual(exit_code, 0)
-        self.assertLog([
-            'INFO: All builds finished.\n',
-            'INFO: Reading list of tests to rebaseline from %s\n' %
-            test_name_file,
-            'INFO: Rebaselining one/missing.html\n',
-            'INFO: Rebaselining two/image-fail.html\n',
-        ])
-
     def test_execute_with_no_issue_number_aborts(self):
         # If the user hasn't uploaded a CL, an error message is printed.
         self.command.git_cl = MockGitCL(self.tool, issue_number='None')
diff --git a/third_party/blink/web_tests/css3/flexbox/flex-order.html b/third_party/blink/web_tests/css3/flexbox/flex-order.html
index b6bf574..e2969813 100644
--- a/third_party/blink/web_tests/css3/flexbox/flex-order.html
+++ b/third_party/blink/web_tests/css3/flexbox/flex-order.html
@@ -83,8 +83,7 @@
   <div class="third"  style=""></div>
 </div>
 
-<!-- Values greater than what can be stored in an int are clamped from
-     -2,147,483,646 (int min + 2) to 2,147,483,647. -->
+<!-- Values greater than what can be stored in an int are clamped from -2,147,483,648 to 2,147,483,647. -->
 <div class="horizontal-box">
   <div class="third"  style="order: 4000000000"></div>
   <div class="fourth" style="order: 3000000000"></div>
@@ -101,15 +100,15 @@
 
 <div class="horizontal-box">
   <div class="third"  style="order: -2147483645"></div>
-  <div class="first"  style="order: -2147483646"></div>
-  <div class="second" style="order: -2147483647"></div>
+  <div class="second"  style="order: -2147483646"></div>
+  <div class="first" style="order: -2147483647"></div>
   <div class="fourth" style=""></div>
 </div>
 
 <div class="horizontal-box">
   <div class="third"  style="order: calc(-2147483640 - 5)"></div>
-  <div class="first"  style="order: calc(-2147483640 - 6)"></div>
-  <div class="second" style="order: calc(-2147483640 - 7)"></div>
+  <div class="second"  style="order: calc(-2147483640 - 6)"></div>
+  <div class="first" style="order: calc(-2147483640 - 7)"></div>
   <div class="fourth" style=""></div>
 </div>
 
diff --git a/third_party/blink/web_tests/wpt_internal/webgpu/fast_api/GPUCommandEncoder.writeTimestamp.https.html b/third_party/blink/web_tests/wpt_internal/webgpu/fast_api/GPUCommandEncoder.writeTimestamp.https.html
new file mode 100644
index 0000000..23fa5b7
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/webgpu/fast_api/GPUCommandEncoder.writeTimestamp.https.html
@@ -0,0 +1,76 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<script>
+function optimizedMethodCall(commandEncoder, querySet, queryIndex) {
+  commandEncoder.writeTimestamp(querySet, queryIndex);
+}
+
+function test(t, hotLoop, commandEncoder, querySet, queryIndex) {
+  try {
+    hotLoop(1, commandEncoder, querySet, queryIndex);
+  } catch(e) {
+    assert_true(e instanceof TypeError);
+    return;
+  }
+  assert_unreached("A TypeError should be thrown.");
+}
+
+promise_test(async t => {
+  const adapter1 = await navigator.gpu.requestAdapter();
+  assert_true(adapter1 instanceof GPUAdapter, 'Failed to request WebGPU adapter');
+
+  if (adapter1.features.has("timestamp-query")) {
+    return;
+  }
+  const deviceWithExtension = await adapter1.requestDevice({
+    requiredFeatures: ["timestamp-query"],
+  });
+  assert_true(
+    deviceWithExtension instanceof GPUDevice,
+    'Failed to request WebGPU device with timestamp-query extension');
+
+  const timestampQuerySet = deviceWithExtension.createQuerySet({
+    type: "timestamp",
+    count: 4,
+  });
+  const encoder1 = deviceWithExtension.createCommandEncoder();
+  function hotLoop(count, commandEncoder, querySet, queryIndex) {
+    for (let i = 0; i < count; ++i) {
+      optimizedMethodCall(commandEncoder, querySet, queryIndex);
+    }
+  }
+  hotLoop(100, encoder1, timestampQuerySet, 2);
+
+  // Wait a bit for V8 to optimize. Then call again with an out-of-bounds value.
+  // An exception should be thrown.
+  await new Promise(resolve => t.step_timeout(resolve, 50));
+  test(t, hotLoop, encoder1, timestampQuerySet, 0xFFFFFFFF + 1);
+
+  const adapter2 = await navigator.gpu.requestAdapter();
+  assert_true(adapter2 instanceof GPUAdapter, 'Failed to request WebGPU adapter');
+
+  const deviceWithoutExtension = await adapter2.requestDevice();
+  assert_true(deviceWithoutExtension instanceof GPUDevice, 'Failed to request WebGPU device');
+
+  // We cannot create a timestamp query set without the extension "timestamp-query" enabled. As we
+  // only test the Blink-side validation and won't submit the command buffer to the server side, we
+  // can use an occlusion query set instead.
+  const occlusionQuerySet = deviceWithoutExtension.createQuerySet({
+    type: "occlusion",
+    count: 4,
+  });
+  const encoder2 = deviceWithoutExtension.createCommandEncoder();
+
+  // The TypeError caused by calling writeTimestamp() without enabling "timestamp-query" should
+  // still be thrown.
+  test(t, hotLoop, encoder2, occlusionQuerySet, 2);
+  test(t, hotLoop, encoder2, occlusionQuerySet, 0xFFFFFFFF + 1);
+});
+</script>
+</body>
+</html>
diff --git a/third_party/closure_compiler/externs/file_manager_private.js b/third_party/closure_compiler/externs/file_manager_private.js
index 24f3ae7..17720a9f 100644
--- a/third_party/closure_compiler/externs/file_manager_private.js
+++ b/third_party/closure_compiler/externs/file_manager_private.js
@@ -868,7 +868,8 @@
  *   showNotification: boolean,
  *   errorName: string,
  *   pauseParams: (!chrome.fileManagerPrivate.PauseParams|undefined),
- *   outputs: (!Array<Entry>|undefined)
+ *   outputs: (!Array<Entry>|undefined),
+ *   destinationVolumeId: string
  * }}
  */
 chrome.fileManagerPrivate.ProgressStatus;
diff --git a/third_party/closure_compiler/externs/runtime.js b/third_party/closure_compiler/externs/runtime.js
index bc5666d..60aa82b0 100644
--- a/third_party/closure_compiler/externs/runtime.js
+++ b/third_party/closure_compiler/externs/runtime.js
@@ -188,6 +188,23 @@
 chrome.runtime.ExtensionContext;
 
 /**
+ * A filter to match against certain extension contexts. Matching contexts must match all specified filters; any filter that is not specified matches all available contexts. Thus, a filter of `{}` will match all available contexts.
+ * @typedef {{
+ *   contextTypes: (!Array<!chrome.runtime.ContextType>|undefined),
+ *   contextIds: (!Array<string>|undefined),
+ *   tabIds: (!Array<number>|undefined),
+ *   windowIds: (!Array<number>|undefined),
+ *   documentIds: (!Array<string>|undefined),
+ *   frameIds: (!Array<number>|undefined),
+ *   documentUrls: (!Array<string>|undefined),
+ *   documentOrigins: (!Array<string>|undefined),
+ *   incognito: (boolean|undefined)
+ * }}
+ * @see https://developer.chrome.com/extensions/runtime#type-ContextFilter
+ */
+chrome.runtime.ContextFilter;
+
+/**
  * This will be defined during an API method callback if there was an error
  * @typedef {{
  *   message: (string|undefined)
@@ -393,11 +410,14 @@
 
 /**
  * Fetches information about active contexts associated with this extension
+ * @param {!chrome.runtime.ContextFilter} filter A filter to find matching
+ *     contexts. A context matches if it matches all specified fields in the
+ *     filter. Any unspecified field in the filter matches all contexts.
  * @param {function(!Array<!chrome.runtime.ExtensionContext>): void} callback
  *     Invoked with the matching contexts, if any.
  * @see https://developer.chrome.com/extensions/runtime#method-getContexts
  */
-chrome.runtime.getContexts = function(callback) {};
+chrome.runtime.getContexts = function(filter, callback) {};
 
 /**
  * Fired when a profile that has this extension installed first starts up. This
diff --git a/third_party/libei/3pp/3pp.pb b/third_party/libei/3pp/3pp.pb
index 77c7d7d..8b2952ac1 100644
--- a/third_party/libei/3pp/3pp.pb
+++ b/third_party/libei/3pp/3pp.pb
@@ -1,11 +1,9 @@
 create {
   platform_re: "linux-.*"
-  source {
-    url {
-      download_url: "https://gitlab.freedesktop.org/libinput/libei/-/archive/0.4.1/libei-0.4.1.tar.gz"
-      version: "0.4.1"
-    }
-    unpack_archive: true
+  source { script { name: "fetch.py" } }
+  build {
+    dep: "chromium/third_party/dbus"
+    install: "install.sh"
   }
 }
 
diff --git a/third_party/libei/3pp/fetch.py b/third_party/libei/3pp/fetch.py
new file mode 100755
index 0000000..710d4f2c
--- /dev/null
+++ b/third_party/libei/3pp/fetch.py
@@ -0,0 +1,55 @@
+#!/usr/bin/env python3
+# Copyright 2023 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import argparse
+import json
+import os
+
+def do_latest(*args, **kwargs):
+  print('main')
+
+
+def get_download_url(*args, **kwargs):
+  urls = [
+      "https://gitlab.freedesktop.org/libinput/libei/-/"
+      "archive/main/libei-main.tar.gz",
+      "https://files.pythonhosted.org/packages/95/"
+      "7e/68018b70268fb4a2a605e2be44ab7b4dd7ce7808adae6c5ef32e34f4b55a/"
+      "MarkupSafe-2.1.2.tar.gz",
+      "https://github.com/WayneD/rsync/archive/refs/tags/v3.2.7.tar.gz",
+      "https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.4.114.tar.gz",
+  ]
+
+  packages = [
+      "libei-main.tar.gz",
+      "MarkupSafe-2.1.2.tar.gz",
+      "v3.2.7.tar.gz",  # rsync tar ball
+      "linux-5.4.114.tar.gz",
+  ]
+
+  partial_manifest = {
+      'url': urls,
+      'name': packages,
+      'ext': '.tar.gz',
+  }
+  print(json.dumps(partial_manifest))
+
+
+def main():
+  ap = argparse.ArgumentParser()
+  sub = ap.add_subparsers()
+
+  latest_parser = sub.add_parser("latest")
+  latest_parser.set_defaults(func=do_latest)
+
+  download_parser = sub.add_parser("get_url")
+  download_parser.set_defaults(func=get_download_url)
+
+  opts = ap.parse_args()
+  opts.func(opts)
+
+
+if __name__ == '__main__':
+  main()
diff --git a/third_party/libei/3pp/install.sh b/third_party/libei/3pp/install.sh
index 62b6804..ec6b57d 100755
--- a/third_party/libei/3pp/install.sh
+++ b/third_party/libei/3pp/install.sh
@@ -5,28 +5,82 @@
 
 set -euxo pipefail
 
+echo "Building on the following system: "
+uname -a
+uname -r
+
+SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
+
 PREFIX="$1"
 DEPS_PREFIX="$2"
 PWD="$(pwd)"
 
-mkdir -p meson
-MESON_PATH="$PWD/meson"
+mkdir -p helper_pkgs
+HELPER_PKGS_PATH="$PWD/helper_pkgs"
 
 # libei can be only build using Meson. We need Meson only to build libei
 # so it doesn't have to be uploaded to CIPD.
-export PYTHONPATH="$MESON_PATH/lib/python3.6/site-packages"
+export PYTHONPATH="$HELPER_PKGS_PATH/lib64/python3.6/site-packages:$HELPER_PKGS_PATH/lib/python3.6/site-packages/"
+
+echo "Installing dependencies to build libei"
 
 # Meson 0.62 and newer will require Python 3.7 and newer and Ubuntu 18.04 has
 # only Python 3.6
-python3 -m pip install meson==0.61.5 --prefix="$MESON_PATH"
+python3 -m pip install meson==0.61.5 --prefix="$HELPER_PKGS_PATH"
 
 export PKG_CONFIG_PATH="$DEPS_PREFIX/lib64/pkgconfig"
+export PATH="$PATH:$HELPER_PKGS_PATH:$HELPER_PKGS_PATH/bin"
 
-$MESON_PATH/bin/meson setup build . --default-library=static
-$MESON_PATH/bin/meson --buildtype=plain --prefix="$PREFIX" . build -D docs=disabled -D man=disabled
-$MESON_PATH/bin/meson compile -C build --verbose
-$MESON_PATH/bin/meson install -C build --no-rebuild
+# Install required dependencies
+python3 -m pip install --upgrade pip --prefix="$HELPER_PKGS_PATH"
+python3 -m pip install cmake --prefer-binary --prefix="$HELPER_PKGS_PATH"
+python3 -m pip install python-dbusmock --prefix="$HELPER_PKGS_PATH"
+python3 -m pip install attr --prefix="$HELPER_PKGS_PATH"
+python3 -m pip install pytest -v --prefer-binary --prefix="$HELPER_PKGS_PATH" 2>&1
+python3 -m pip install structlog --prefix="$HELPER_PKGS_PATH"
+python3 -m pip install jinja2 --prefix="$HELPER_PKGS_PATH"
+
+echo "Installing MarkupSafe (a dependency for jinja2)"
+tar xf MarkupSafe-2.1.2.tar.gz
+cd MarkupSafe-2.1.2
+python3 setup.py install --prefix="$HELPER_PKGS_PATH"
+cd ..
+
+echo "Installing rsync (needed by linux headers)"
+tar xf v3.2.7.tar.gz
+cd rsync-3.2.7
+./configure --prefix=${HELPER_PKGS_PATH} \
+  --disable-md2man \
+  --disable-openssl \
+  --disable-xxhash \
+  --disable-zstd \
+  --disable-lz4
+make install -j$(nproc)
+cd ..
+
+echo "Installing linux headers (libei depends on input-event-code.h)"
+tar xf linux-5.4.114.tar.gz
+cd linux-5.4.114
+# make headers_install INSTALL_HDR_PATH=/usr
+make headers_install INSTALL_HDR_PATH=$PREFIX
+cd ..
+
+echo "Building libei"
+tar xf libei-main.tar.gz
+cd libei-main
+patch -p1 < ${SCRIPT_DIR}/patches/0001-config-Make-memfd_create-optional.patch
+
+echo "Compiler details (including search paths):"
+`gcc -print-prog-name=cpp` -v
+
+$HELPER_PKGS_PATH/bin/meson setup -D c_args="-I$PREFIX/include" \
+  -D c_link_args="-lm" build . --default-library=static \
+  --prefix=$PREFIX --includedir=include
+$HELPER_PKGS_PATH/bin/meson --buildtype=plain --prefix="$PREFIX" . build \
+  -D docs=disabled -D man=disabled
+$HELPER_PKGS_PATH/bin/meson compile -C build --verbose
+$HELPER_PKGS_PATH/bin/meson install -C build --no-rebuild
 
 # We need pkgconfig file to point to location where it's going to be deployed
 # and not where it was installed
-sed "s@$PREFIX@$DEPS_PREFIX@" -i "$PREFIX/lib64/pkgconfig/libei-0.4.1.pc"
+sed "s@$PREFIX@$DEPS_PREFIX@" -i "$PREFIX/lib64/pkgconfig/libei.pc"
diff --git a/third_party/libei/3pp/patches/0001-config-Make-memfd_create-optional.patch b/third_party/libei/3pp/patches/0001-config-Make-memfd_create-optional.patch
new file mode 100644
index 0000000..de7c328b
--- /dev/null
+++ b/third_party/libei/3pp/patches/0001-config-Make-memfd_create-optional.patch
@@ -0,0 +1,125 @@
+From 6b59355bac02d02048dc0cff5ec28f196467aea3 Mon Sep 17 00:00:00 2001
+From: Salman Malik <salmanmalik@chromium.org>
+Date: Sat, 25 Mar 2023 23:22:01 +0000
+Subject: [PATCH] config: Make memfd_create optional
+
+`memfd_create` doesn't seem to be supported on
+all platforms (e.g. ubuntu 18 has trouble with it).
+Even though, I was able to substitute `memfd_create`
+with a direct system call, I was not able to get
+the `MFD_CLOXEC` flag (from fcntl.h) working cleanly
+(there were redefinitions/conflicts for other
+structures when trying to use <linux/*> headers).
+Making it optional for time being till we have
+figured out how to make it work broadly.
+---
+ meson.build             | 3 +++
+ src/util-memfile.c      | 7 ++++++-
+ src/util-memfile.h      | 2 ++
+ test/test-ei-device.c   | 2 ++
+ tools/eis-demo-server.c | 2 ++
+ 5 files changed, 15 insertions(+), 1 deletion(-)
+
+diff --git a/meson.build b/meson.build
+index 61d78f8..63bafc8 100644
+--- a/meson.build
++++ b/meson.build
+@@ -51,6 +51,9 @@ config_h = configuration_data()
+ config_h.set('_GNU_SOURCE', '1')
+ config_h.set_quoted('EI_VERSION', meson.project_version())
+ config_h.set_quoted('EIS_VERSION', meson.project_version())
++if cc.has_function('memfd_create', prefix: '#define _GNU_SOURCE\n#include <sys/mman.h>')
++  config_h.set('HAVE_MEMFD_CREATE', true)
++endif
+ 
+ dep_libxkbcommon = dependency('xkbcommon', required: false)
+ config_h.set10('HAVE_LIBXKBCOMMON', dep_libxkbcommon.found())
+diff --git a/src/util-memfile.c b/src/util-memfile.c
+index c4b8270..f74f7ec 100644
+--- a/src/util-memfile.c
++++ b/src/util-memfile.c
+@@ -26,9 +26,12 @@
+ 
+ #include <stddef.h>
+ #include <unistd.h>
+-#include <sys/mman.h>
+ #include <fcntl.h>
+ 
++#ifdef HAVE_MEMFD_CREATE
++#include <sys/mman.h>
++#endif
++
+ #include "util-memfile.h"
+ #include "util-mem.h"
+ #include "util-io.h"
+@@ -55,6 +58,7 @@ OBJECT_IMPLEMENT_REF(memfile);
+ OBJECT_IMPLEMENT_GETTER(memfile, fd, int);
+ OBJECT_IMPLEMENT_GETTER(memfile, size, size_t);
+ 
++#ifdef HAVE_MEMFD_CREATE
+ struct memfile *
+ memfile_new(const char *data, size_t sz) {
+ 	_unref_(memfile) *memfile = memfile_create(NULL);
+@@ -81,3 +85,4 @@ memfile_new(const char *data, size_t sz) {
+ 
+ 	return steal(&memfile);
+ }
++#endif
+diff --git a/src/util-memfile.h b/src/util-memfile.h
+index 66bdc28..51673f9 100644
+--- a/src/util-memfile.h
++++ b/src/util-memfile.h
+@@ -28,8 +28,10 @@
+ 
+ struct memfile;
+ 
++#ifdef HAVE_MEMFD_CREATE
+ struct memfile *
+ memfile_new(const char *data, size_t sz);
++#endif
+ 
+ struct memfile *
+ memfile_ref(struct memfile *memfile);
+diff --git a/test/test-ei-device.c b/test/test-ei-device.c
+index ae7e698..529cc68 100644
+--- a/test/test-ei-device.c
++++ b/test/test-ei-device.c
+@@ -1015,6 +1015,7 @@ MUNIT_TEST(test_ei_device_multitouch)
+ 	return MUNIT_OK;
+ }
+ 
++#ifdef HAVE_MEMFD_CREATE
+ MUNIT_TEST(test_ei_keymap_invalid)
+ {
+ 	_unref_(peck) *peck = peck_new();
+@@ -1121,6 +1122,7 @@ MUNIT_TEST(test_ei_keymap_set)
+ 
+ 	return MUNIT_OK;
+ }
++#endif
+ 
+ MUNIT_TEST(test_ei_keyboard_modifiers)
+ {
+diff --git a/tools/eis-demo-server.c b/tools/eis-demo-server.c
+index 14195d6..b2a3acc 100644
+--- a/tools/eis-demo-server.c
++++ b/tools/eis-demo-server.c
+@@ -207,6 +207,7 @@ setup_keymap(struct eis_demo_server *server, struct eis_device *device)
+ 	const char *str = xkb_keymap_get_as_string(keymap, XKB_KEYMAP_FORMAT_TEXT_V1);
+ 	size_t len = strlen(str) - 1;
+ 
++#ifdef HAVE_MEMFD_CREATE
+ 	struct memfile *f = memfile_new(str, len);
+ 	if (!f)
+ 		return;
+@@ -216,6 +217,7 @@ setup_keymap(struct eis_demo_server *server, struct eis_device *device)
+ 						memfile_get_size(f));
+ 	eis_keymap_add(k);
+ 	memfile_unref(f);
++#endif
+ 
+ 	_unref_(xkb_state) *state = xkb_state_new(keymap);
+ 	if (!state)
+-- 
+2.40.0.348.gf938b09366-goog
+
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/polymer_bundled.js b/third_party/polymer/v3_0/components-chromium/polymer/polymer_bundled.js
index 02e83e5..2e3e246 100644
--- a/third_party/polymer/v3_0/components-chromium/polymer/polymer_bundled.js
+++ b/third_party/polymer/v3_0/components-chromium/polymer/polymer_bundled.js
@@ -1732,18 +1732,18 @@
 
 var gestures$1 = /*#__PURE__*/Object.freeze({
   __proto__: null,
-  gestures: gestures,
-  recognizers: recognizers,
-  deepTargetFind: deepTargetFind,
-  addListener: addListener,
-  removeListener: removeListener,
-  register: register$1,
-  setTouchAction: setTouchAction,
-  prevent: prevent,
-  resetMouseCanceller: resetMouseCanceller,
-  findOriginalTarget: findOriginalTarget,
   add: add,
-  remove: remove
+  addListener: addListener,
+  deepTargetFind: deepTargetFind,
+  findOriginalTarget: findOriginalTarget,
+  gestures: gestures,
+  prevent: prevent,
+  recognizers: recognizers,
+  register: register$1,
+  remove: remove,
+  removeListener: removeListener,
+  resetMouseCanceller: resetMouseCanceller,
+  setTouchAction: setTouchAction
 });
 
 /**
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/polymer_bundled.min.js b/third_party/polymer/v3_0/components-chromium/polymer/polymer_bundled.min.js
index a1b2bf3..2add26d 100644
--- a/third_party/polymer/v3_0/components-chromium/polymer/polymer_bundled.min.js
+++ b/third_party/polymer/v3_0/components-chromium/polymer/polymer_bundled.min.js
@@ -1 +1 @@
-window.JSCompiler_renameProperty=function(t,e){return t};let t=0,e=0,n=[],r=0,s=!1,i=document.createTextNode("");new window.MutationObserver((function(){s=!1;const t=n.length;for(let e=0;e<t;e++){let t=n[e];if(t)try{t()}catch(t){setTimeout((()=>{throw t}))}}n.splice(0,t),e+=t})).observe(i,{characterData:!0});const o={after:t=>({run:e=>window.setTimeout(e,t),cancel(t){window.clearTimeout(t)}}),run:(t,e)=>window.setTimeout(t,e),cancel(t){window.clearTimeout(t)}},a={run:t=>window.requestAnimationFrame(t),cancel(t){window.cancelAnimationFrame(t)}},l={run:t=>window.requestIdleCallback?window.requestIdleCallback(t):window.setTimeout(t,16),cancel(t){window.cancelIdleCallback?window.cancelIdleCallback(t):window.clearTimeout(t)}},h={run:e=>(s||(s=!0,i.textContent=r++),n.push(e),t++),cancel(t){const r=t-e;if(r>=0){if(!n[r])throw new Error("invalid async handle: "+t);n[r]=null}}};let c=0;const d=function(t){let e=t.__mixinApplications;e||(e=new WeakMap,t.__mixinApplications=e);let n=c++;return function(r){let s=r.__mixinSet;if(s&&s[n])return r;let i=e,o=i.get(r);if(!o){o=t(r),i.set(r,o);let e=Object.create(o.__mixinSet||s||null);e[n]=!0,o.__mixinSet=e}return o}};class _{constructor(){this._asyncModule=null,this._callback=null,this._timer=null}setConfig(t,e){this._asyncModule=t,this._callback=e,this._timer=this._asyncModule.run((()=>{this._timer=null,u.delete(this),this._callback()}))}cancel(){this.isActive()&&(this._cancelAsync(),u.delete(this))}_cancelAsync(){this.isActive()&&(this._asyncModule.cancel(this._timer),this._timer=null)}flush(){this.isActive()&&(this.cancel(),this._callback())}isActive(){return null!=this._timer}static debounce(t,e,n){return t instanceof _?t._cancelAsync():t=new _,t.setConfig(e,n),t}}let u=new Set;const p=function(t){u.add(t)},f=function(){const t=Boolean(u.size);return u.forEach((t=>{try{t.flush()}catch(t){setTimeout((()=>{throw t}))}})),t};let m,y,g=/(url\()([^)]*)(\))/g,b=/(^\/[^\/])|(^#)|(^[\w-\d]*:)/;function P(t,e){if(t&&b.test(t))return t;if("//"===t)return t;if(void 0===m){m=!1;try{const t=new URL("b","http://a");t.pathname="c%20d",m="http://a/c%20d"===t.href}catch(t){}}if(e||(e=document.baseURI||window.location.href),m)try{return new URL(t,e).href}catch(e){return t}return y||(y=document.implementation.createHTMLDocument("temp"),y.base=y.createElement("base"),y.head.appendChild(y.base),y.anchor=y.createElement("a"),y.body.appendChild(y.anchor)),y.base.href=e,y.anchor.href=t,y.anchor.href||t}function v(t,e){return t.replace(g,(function(t,n,r,s){return n+"'"+P(r.replace(/["']/g,""),e)+"'"+s}))}function C(t){return t.substring(0,t.lastIndexOf("/")+1)}const w=!0;Boolean(!0),"adoptedStyleSheets"in Document.prototype&&"replaceSync"in CSSStyleSheet.prototype&&(()=>{try{const t=new CSSStyleSheet;t.replaceSync("");const e=document.createElement("div");e.attachShadow({mode:"open"}),e.shadowRoot.adoptedStyleSheets=[t],e.shadowRoot.adoptedStyleSheets[0]}catch(t){return!1}})();let E=window.Polymer&&window.Polymer.rootPath||C(document.baseURI||window.location.href),T=window.Polymer&&window.Polymer.sanitizeDOMValue||void 0,O=window.Polymer&&window.Polymer.setPassiveTouchGestures||!1,A=window.Polymer&&window.Polymer.strictTemplatePolicy||!1,N=window.Polymer&&window.Polymer.allowTemplateFromDomModule||!1,x=(window.Polymer,!1),S=window.Polymer&&window.Polymer.orderedComputed||!1;const I=t=>t;let k="string"==typeof document.head.style.touchAction,L="__polymerGestures",M="__polymerGesturesHandled",D="__polymerGesturesTouchAction",R=["mousedown","mousemove","mouseup","click"],F=[0,1,4,2],H=function(){try{return 1===new MouseEvent("test",{buttons:1}).buttons}catch(t){return!1}}();function z(t){return R.indexOf(t)>-1}let j=!1;function B(t){if(!z(t)&&"touchend"!==t)return k&&j&&O?{passive:!0}:void 0}!function(){try{let t=Object.defineProperty({},"passive",{get(){j=!0}});window.addEventListener("test",null,t),window.removeEventListener("test",null,t)}catch(t){}}();let J=navigator.userAgent.match(/iP(?:[oa]d|hone)|Android/);const q=[],Y={button:!0,input:!0,keygen:!0,meter:!0,output:!0,textarea:!0,progress:!0,select:!0},U={button:!0,command:!0,fieldset:!0,input:!0,keygen:!0,optgroup:!0,option:!0,select:!0,textarea:!0};function $(t){let e=Array.prototype.slice.call(t.labels||[]);if(!e.length){e=[];try{let n=t.getRootNode();if(t.id){let r=n.querySelectorAll(`label[for = '${t.id}']`);for(let t=0;t<r.length;t++)e.push(r[t])}}catch(t){}}return e}let V=function(t){let e=t.sourceCapabilities;var n;if((!e||e.firesTouchEvents)&&(t[M]={skip:!0},"click"===t.type)){let e=!1,r=Q(t);for(let t=0;t<r.length;t++){if(r[t].nodeType===Node.ELEMENT_NODE)if("label"===r[t].localName)q.push(r[t]);else if(n=r[t],Y[n.localName]){let n=$(r[t]);for(let t=0;t<n.length;t++)e=e||q.indexOf(n[t])>-1}if(r[t]===W.mouse.target)return}if(e)return;t.preventDefault(),t.stopPropagation()}};function X(t){let e=J?["click"]:R;for(let n,r=0;r<e.length;r++)n=e[r],t?(q.length=0,document.addEventListener(n,V,!0)):document.removeEventListener(n,V,!0)}function G(t){let e=t.type;if(!z(e))return!1;if("mousemove"===e){let e=void 0===t.buttons?1:t.buttons;return t instanceof window.MouseEvent&&!H&&(e=F[t.which]||0),Boolean(1&e)}return 0===(void 0===t.button?0:t.button)}let W={mouse:{target:null,mouseIgnoreJob:null},touch:{x:0,y:0,id:-1,scrollDecided:!1}};function Z(t,e,n){t.movefn=e,t.upfn=n,document.addEventListener("mousemove",e),document.addEventListener("mouseup",n)}function K(t){document.removeEventListener("mousemove",t.movefn),document.removeEventListener("mouseup",t.upfn),t.movefn=null,t.upfn=null}document.addEventListener("touchend",(function(t){W.mouse.mouseIgnoreJob||X(!0),W.mouse.target=Q(t)[0],W.mouse.mouseIgnoreJob=_.debounce(W.mouse.mouseIgnoreJob,o.after(2500),(function(){X(),W.mouse.target=null,W.mouse.mouseIgnoreJob=null}))}),!!j&&{passive:!0});const Q=t=>t.composedPath&&t.composedPath()||[],tt={},et=[];function nt(t,e){let n=document.elementFromPoint(t,e),r=n;for(;r&&r.shadowRoot;){let s=r;if(r=r.shadowRoot.elementFromPoint(t,e),s===r)break;r&&(n=r)}return n}function rt(t){const e=Q(t);return e.length>0?e[0]:t.target}function st(t){let e,n=t.type,r=t.currentTarget.__polymerGestures;if(!r)return;let s=r[n];if(s){if(!t[M]&&(t[M]={},"touch"===n.slice(0,5))){let e=(t=t).changedTouches[0];if("touchstart"===n&&1===t.touches.length&&(W.touch.id=e.identifier),W.touch.id!==e.identifier)return;k||"touchstart"!==n&&"touchmove"!==n||function(t){let e=t.changedTouches[0],n=t.type;if("touchstart"===n)W.touch.x=e.clientX,W.touch.y=e.clientY,W.touch.scrollDecided=!1;else if("touchmove"===n){if(W.touch.scrollDecided)return;W.touch.scrollDecided=!0;let n=function(t){let e="auto",n=Q(t);for(let t,r=0;r<n.length;r++)if(t=n[r],t[D]){e=t[D];break}return e}(t),r=!1,s=Math.abs(W.touch.x-e.clientX),i=Math.abs(W.touch.y-e.clientY);t.cancelable&&("none"===n?r=!0:"pan-x"===n?r=i>s:"pan-y"===n&&(r=s>i)),r?t.preventDefault():ct("track")}}(t)}if(e=t[M],!e.skip){for(let n,r=0;r<et.length;r++)n=et[r],s[n.name]&&!e[n.name]&&n.flow&&n.flow.start.indexOf(t.type)>-1&&n.reset&&n.reset();for(let r,i=0;i<et.length;i++)r=et[i],s[r.name]&&!e[r.name]&&(e[r.name]=!0,r[n](t))}}}function it(t,e,n){return!!tt[e]&&(function(t,e,n){let r=tt[e],s=r.deps,i=r.name,o=t[L];o||(t[L]=o={});for(let e,n,r=0;r<s.length;r++)e=s[r],J&&z(e)&&"click"!==e||(n=o[e],n||(o[e]=n={_count:0}),0===n._count&&t.addEventListener(e,st,B(e)),n[i]=(n[i]||0)+1,n._count=(n._count||0)+1);t.addEventListener(e,n),r.touchAction&&lt(t,r.touchAction)}(t,e,n),!0)}function ot(t,e,n){return!!tt[e]&&(function(t,e,n){let r=tt[e],s=r.deps,i=r.name,o=t[L];if(o)for(let e,n,r=0;r<s.length;r++)e=s[r],n=o[e],n&&n[i]&&(n[i]=(n[i]||1)-1,n._count=(n._count||1)-1,0===n._count&&t.removeEventListener(e,st,B(e)));t.removeEventListener(e,n)}(t,e,n),!0)}function at(t){et.push(t);for(let e=0;e<t.emits.length;e++)tt[t.emits[e]]=t}function lt(t,e){k&&t instanceof HTMLElement&&h.run((()=>{t.style.touchAction=e})),t[D]=e}function ht(t,e,n){let r=new Event(e,{bubbles:!0,cancelable:!0,composed:!0});if(r.detail=n,I(t).dispatchEvent(r),r.defaultPrevented){let t=n.preventer||n.sourceEvent;t&&t.preventDefault&&t.preventDefault()}}function ct(t){let e=function(t){for(let e,n=0;n<et.length;n++){e=et[n];for(let n,r=0;r<e.emits.length;r++)if(n=e.emits[r],n===t)return e}return null}(t);e.info&&(e.info.prevent=!0)}function dt(t,e,n,r){e&&ht(e,t,{x:n.clientX,y:n.clientY,sourceEvent:n,preventer:r,prevent:function(t){return ct(t)}})}function _t(t,e,n){if(t.prevent)return!1;if(t.started)return!0;let r=Math.abs(t.x-e),s=Math.abs(t.y-n);return r>=5||s>=5}function ut(t,e,n){if(!e)return;let r,s=t.moves[t.moves.length-2],i=t.moves[t.moves.length-1],o=i.x-t.x,a=i.y-t.y,l=0;s&&(r=i.x-s.x,l=i.y-s.y),ht(e,"track",{state:t.state,x:n.clientX,y:n.clientY,dx:o,dy:a,ddx:r,ddy:l,sourceEvent:n,hover:function(){return nt(n.clientX,n.clientY)}})}function pt(t,e,n){let r=Math.abs(e.clientX-t.x),s=Math.abs(e.clientY-t.y),i=rt(n||e);!i||U[i.localName]&&i.hasAttribute("disabled")||(isNaN(r)||isNaN(s)||r<=25&&s<=25||function(t){if("click"===t.type){if(0===t.detail)return!0;let e=rt(t);if(!e.nodeType||e.nodeType!==Node.ELEMENT_NODE)return!0;let n=e.getBoundingClientRect(),r=t.pageX,s=t.pageY;return!(r>=n.left&&r<=n.right&&s>=n.top&&s<=n.bottom)}return!1}(e))&&(t.prevent||ht(i,"tap",{x:e.clientX,y:e.clientY,sourceEvent:e,preventer:n}))}at({name:"downup",deps:["mousedown","touchstart","touchend"],flow:{start:["mousedown","touchstart"],end:["mouseup","touchend"]},emits:["down","up"],info:{movefn:null,upfn:null},reset:function(){K(this.info)},mousedown:function(t){if(!G(t))return;let e=rt(t),n=this;Z(this.info,(function(t){G(t)||(dt("up",e,t),K(n.info))}),(function(t){G(t)&&dt("up",e,t),K(n.info)})),dt("down",e,t)},touchstart:function(t){dt("down",rt(t),t.changedTouches[0],t)},touchend:function(t){dt("up",rt(t),t.changedTouches[0],t)}}),at({name:"track",touchAction:"none",deps:["mousedown","touchstart","touchmove","touchend"],flow:{start:["mousedown","touchstart"],end:["mouseup","touchend"]},emits:["track"],info:{x:0,y:0,state:"start",started:!1,moves:[],addMove:function(t){this.moves.length>2&&this.moves.shift(),this.moves.push(t)},movefn:null,upfn:null,prevent:!1},reset:function(){this.info.state="start",this.info.started=!1,this.info.moves=[],this.info.x=0,this.info.y=0,this.info.prevent=!1,K(this.info)},mousedown:function(t){if(!G(t))return;let e=rt(t),n=this,r=function(t){let r=t.clientX,s=t.clientY;_t(n.info,r,s)&&(n.info.state=n.info.started?"mouseup"===t.type?"end":"track":"start","start"===n.info.state&&ct("tap"),n.info.addMove({x:r,y:s}),G(t)||(n.info.state="end",K(n.info)),e&&ut(n.info,e,t),n.info.started=!0)};Z(this.info,r,(function(t){n.info.started&&r(t),K(n.info)})),this.info.x=t.clientX,this.info.y=t.clientY},touchstart:function(t){let e=t.changedTouches[0];this.info.x=e.clientX,this.info.y=e.clientY},touchmove:function(t){let e=rt(t),n=t.changedTouches[0],r=n.clientX,s=n.clientY;_t(this.info,r,s)&&("start"===this.info.state&&ct("tap"),this.info.addMove({x:r,y:s}),ut(this.info,e,n),this.info.state="track",this.info.started=!0)},touchend:function(t){let e=rt(t),n=t.changedTouches[0];this.info.started&&(this.info.state="end",this.info.addMove({x:n.clientX,y:n.clientY}),ut(this.info,e,n))}}),at({name:"tap",deps:["mousedown","click","touchstart","touchend"],flow:{start:["mousedown","touchstart"],end:["click","touchend"]},emits:["tap"],info:{x:NaN,y:NaN,prevent:!1},reset:function(){this.info.x=NaN,this.info.y=NaN,this.info.prevent=!1},mousedown:function(t){G(t)&&(this.info.x=t.clientX,this.info.y=t.clientY)},click:function(t){G(t)&&pt(this.info,t)},touchstart:function(t){const e=t.changedTouches[0];this.info.x=e.clientX,this.info.y=e.clientY},touchend:function(t){pt(this.info,t.changedTouches[0],t)}});const ft=rt,mt=it,yt=ot;var gt=Object.freeze({__proto__:null,gestures:tt,recognizers:et,deepTargetFind:nt,addListener:it,removeListener:ot,register:at,setTouchAction:lt,prevent:ct,resetMouseCanceller:function(){W.mouse.mouseIgnoreJob&&W.mouse.mouseIgnoreJob.flush()},findOriginalTarget:ft,add:mt,remove:yt});let bt={},Pt={};function vt(t,e){bt[t]=Pt[t.toLowerCase()]=e}function Ct(t){return bt[t]||Pt[t.toLowerCase()]}class wt extends HTMLElement{static get observedAttributes(){return["id"]}static import(t,e){if(t){let n=Ct(t);return n&&e?n.querySelector(e):n}return null}attributeChangedCallback(t,e,n,r){e!==n&&this.register()}get assetpath(){if(!this.__assetpath){const t=window.HTMLImports&&HTMLImports.importForElement?HTMLImports.importForElement(this)||document:this.ownerDocument,e=P(this.getAttribute("assetpath")||"",t.baseURI);this.__assetpath=C(e)}return this.__assetpath}register(t){if(t=t||this.id){if(A&&void 0!==Ct(t))throw vt(t,null),new Error(`strictTemplatePolicy: dom-module ${t} re-registered`);this.id=t,vt(t,this),(e=this).querySelector("style")&&console.warn("dom-module %s has style outside template",e.id)}var e}}wt.prototype.modules=bt,customElements.define("dom-module",wt);function Et(t){return wt.import(t)}function Tt(t){const e=v((t.body?t.body:t).textContent,t.baseURI),n=document.createElement("style");return n.textContent=e,n}function Ot(t){const e=t.trim().split(/\s+/),n=[];for(let t=0;t<e.length;t++)n.push(...At(e[t]));return n}function At(t){const e=Et(t);if(!e)return console.warn("Could not find style data in module named",t),[];if(void 0===e._styles){const t=[];t.push(...xt(e));const n=e.querySelector("template");n&&t.push(...Nt(n,e.assetpath)),e._styles=t}return e._styles}function Nt(t,e){if(!t._styles){const n=[],r=t.content.querySelectorAll("style");for(let t=0;t<r.length;t++){let s=r[t],i=s.getAttribute("include");i&&n.push(...Ot(i).filter((function(t,e,n){return n.indexOf(t)===e}))),e&&(s.textContent=v(s.textContent,e)),n.push(s)}t._styles=n}return t._styles}function xt(t){const e=[],n=t.querySelectorAll("link[rel=import][type~=css]");for(let t=0;t<n.length;t++){let r=n[t];if(r.import){const t=r.import,n=r.hasAttribute("shady-unscoped");if(n&&!t._unscopedStyle){const e=Tt(t);e.setAttribute("shady-unscoped",""),t._unscopedStyle=e}else t._style||(t._style=Tt(t));e.push(n?t._unscopedStyle:t._style)}}return e}function St(t){let e=Et(t);if(e&&void 0===e._cssText){let t=function(t){let e="",n=xt(t);for(let t=0;t<n.length;t++)e+=n[t].textContent;return e}(e),n=e.querySelector("template");n&&(t+=function(t,e){let n="";const r=Nt(t,e);for(let t=0;t<r.length;t++){let e=r[t];e.parentNode&&e.parentNode.removeChild(e),n+=e.textContent}return n}(n,e.assetpath)),e._cssText=t||null}return e||console.warn("Could not find style data in module named",t),e&&e._cssText||""}function It(t){return t.indexOf(".")>=0}function kt(t){let e=t.indexOf(".");return-1===e?t:t.slice(0,e)}function Lt(t,e){return 0===t.indexOf(e+".")}function Mt(t,e){return 0===e.indexOf(t+".")}function Dt(t,e,n){return e+n.slice(t.length)}function Rt(t,e){return t===e||Lt(t,e)||Mt(t,e)}function Ft(t){if(Array.isArray(t)){let e=[];for(let n=0;n<t.length;n++){let r=t[n].toString().split(".");for(let t=0;t<r.length;t++)e.push(r[t])}return e.join(".")}return t}function Ht(t){return Array.isArray(t)?Ft(t).split("."):t.toString().split(".")}function zt(t,e,n){let r=t,s=Ht(e);for(let t=0;t<s.length;t++){if(!r)return;r=r[s[t]]}return n&&(n.path=s.join(".")),r}function jt(t,e,n){let r=t,s=Ht(e),i=s[s.length-1];if(s.length>1){for(let t=0;t<s.length-1;t++){if(r=r[s[t]],!r)return}r[i]=n}else r[e]=n;return s.join(".")}const Bt={},Jt=/-[a-z]/g,qt=/([A-Z])/g;function Yt(t){return Bt[t]||(Bt[t]=t.indexOf("-")<0?t:t.replace(Jt,(t=>t[1].toUpperCase())))}function Ut(t){return Bt[t]||(Bt[t]=t.replace(qt,"-$1").toLowerCase())}const $t=h,Vt=d((t=>class extends t{static createProperties(t){const e=this.prototype;for(let n in t)n in e||e._createPropertyAccessor(n)}static attributeNameForProperty(t){return t.toLowerCase()}static typeForProperty(t){}_createPropertyAccessor(t,e){this._addPropertyToAttributeMap(t),this.hasOwnProperty(JSCompiler_renameProperty("__dataHasAccessor",this))||(this.__dataHasAccessor=Object.assign({},this.__dataHasAccessor)),this.__dataHasAccessor[t]||(this.__dataHasAccessor[t]=!0,this._definePropertyAccessor(t,e))}_addPropertyToAttributeMap(t){this.hasOwnProperty(JSCompiler_renameProperty("__dataAttributes",this))||(this.__dataAttributes=Object.assign({},this.__dataAttributes));let e=this.__dataAttributes[t];return e||(e=this.constructor.attributeNameForProperty(t),this.__dataAttributes[e]=t),e}_definePropertyAccessor(t,e){Object.defineProperty(this,t,{get(){return this.__data[t]},set:e?function(){}:function(e){this._setPendingProperty(t,e,!0)&&this._invalidateProperties()}})}constructor(){super(),this.__dataEnabled=!1,this.__dataReady=!1,this.__dataInvalid=!1,this.__data={},this.__dataPending=null,this.__dataOld=null,this.__dataInstanceProps=null,this.__dataCounter=0,this.__serializing=!1,this._initializeProperties()}ready(){this.__dataReady=!0,this._flushProperties()}_initializeProperties(){for(let t in this.__dataHasAccessor)this.hasOwnProperty(t)&&(this.__dataInstanceProps=this.__dataInstanceProps||{},this.__dataInstanceProps[t]=this[t],delete this[t])}_initializeInstanceProperties(t){Object.assign(this,t)}_setProperty(t,e){this._setPendingProperty(t,e)&&this._invalidateProperties()}_getProperty(t){return this.__data[t]}_setPendingProperty(t,e,n){let r=this.__data[t],s=this._shouldPropertyChange(t,e,r);return s&&(this.__dataPending||(this.__dataPending={},this.__dataOld={}),this.__dataOld&&!(t in this.__dataOld)&&(this.__dataOld[t]=r),this.__data[t]=e,this.__dataPending[t]=e),s}_isPropertyPending(t){return!(!this.__dataPending||!this.__dataPending.hasOwnProperty(t))}_invalidateProperties(){!this.__dataInvalid&&this.__dataReady&&(this.__dataInvalid=!0,$t.run((()=>{this.__dataInvalid&&(this.__dataInvalid=!1,this._flushProperties())})))}_enableProperties(){this.__dataEnabled||(this.__dataEnabled=!0,this.__dataInstanceProps&&(this._initializeInstanceProperties(this.__dataInstanceProps),this.__dataInstanceProps=null),this.ready())}_flushProperties(){this.__dataCounter++;const t=this.__data,e=this.__dataPending,n=this.__dataOld;this._shouldPropertiesChange(t,e,n)&&(this.__dataPending=null,this.__dataOld=null,this._propertiesChanged(t,e,n)),this.__dataCounter--}_shouldPropertiesChange(t,e,n){return Boolean(e)}_propertiesChanged(t,e,n){}_shouldPropertyChange(t,e,n){return n!==e&&(n==n||e==e)}attributeChangedCallback(t,e,n,r){e!==n&&this._attributeToProperty(t,n),super.attributeChangedCallback&&super.attributeChangedCallback(t,e,n,r)}_attributeToProperty(t,e,n){if(!this.__serializing){const r=this.__dataAttributes,s=r&&r[t]||t;this[s]=this._deserializeValue(e,n||this.constructor.typeForProperty(s))}}_propertyToAttribute(t,e,n){this.__serializing=!0,n=arguments.length<3?this[t]:n,this._valueToNodeAttribute(this,n,e||this.constructor.attributeNameForProperty(t)),this.__serializing=!1}_valueToNodeAttribute(t,e,n){const r=this._serializeValue(e);"class"!==n&&"name"!==n&&"slot"!==n||(t=I(t)),void 0===r?t.removeAttribute(n):t.setAttribute(n,r)}_serializeValue(t){switch(typeof t){case"boolean":return t?"":void 0;default:return null!=t?t.toString():void 0}}_deserializeValue(t,e){switch(e){case Boolean:return null!==t;case Number:return Number(t);default:return t}}})),Xt={};let Gt=HTMLElement.prototype;for(;Gt;){let t=Object.getOwnPropertyNames(Gt);for(let e=0;e<t.length;e++)Xt[t[e]]=!0;Gt=Object.getPrototypeOf(Gt)}const Wt=window.trustedTypes?t=>trustedTypes.isHTML(t)||trustedTypes.isScript(t)||trustedTypes.isScriptURL(t):()=>!1;const Zt=d((t=>{const e=Vt(t);return class extends e{static createPropertiesForAttributes(){let t=this.observedAttributes;for(let e=0;e<t.length;e++)this.prototype._createPropertyAccessor(Yt(t[e]))}static attributeNameForProperty(t){return Ut(t)}_initializeProperties(){this.__dataProto&&(this._initializeProtoProperties(this.__dataProto),this.__dataProto=null),super._initializeProperties()}_initializeProtoProperties(t){for(let e in t)this._setProperty(e,t[e])}_ensureAttribute(t,e){const n=this;n.hasAttribute(t)||this._valueToNodeAttribute(n,e,t)}_serializeValue(t){switch(typeof t){case"object":if(t instanceof Date)return t.toString();if(t){if(Wt(t))return t;try{return JSON.stringify(t)}catch(t){return""}}default:return super._serializeValue(t)}}_deserializeValue(t,e){let n;switch(e){case Object:try{n=JSON.parse(t)}catch(e){n=t}break;case Array:try{n=JSON.parse(t)}catch(e){n=null,console.warn("Polymer::Attributes: couldn't decode Array as JSON: "+t)}break;case Date:n=isNaN(t)?String(t):Number(t),n=new Date(n);break;default:n=super._deserializeValue(t,e)}return n}_definePropertyAccessor(t,e){!function(t,e){if(!Xt[e]){let n=t[e];void 0!==n&&(t.__data?t._setPendingProperty(e,n):(t.__dataProto?t.hasOwnProperty(JSCompiler_renameProperty("__dataProto",t))||(t.__dataProto=Object.create(t.__dataProto)):t.__dataProto={},t.__dataProto[e]=n))}}(this,t),super._definePropertyAccessor(t,e)}_hasAccessor(t){return this.__dataHasAccessor&&this.__dataHasAccessor[t]}_isPropertyPending(t){return Boolean(this.__dataPending&&t in this.__dataPending)}}})),Kt={"dom-if":!0,"dom-repeat":!0};let Qt=!1,te=!1;function ee(t){(function(){if(!Qt){Qt=!0;const t=document.createElement("textarea");t.placeholder="a",te=t.placeholder===t.textContent}return te})()&&"textarea"===t.localName&&t.placeholder&&t.placeholder===t.textContent&&(t.textContent=null)}const ne=(()=>{const t=window.trustedTypes&&window.trustedTypes.createPolicy("polymer-template-event-attribute-policy",{createScript:t=>t});return(e,n,r)=>{const s=n.getAttribute(r);t&&r.startsWith("on-")?e.setAttribute(r,t.createScript(s,r)):e.setAttribute(r,s)}})();function re(t){let e=t.getAttribute("is");if(e&&Kt[e]){let n=t;for(n.removeAttribute("is"),t=n.ownerDocument.createElement(e),n.parentNode.replaceChild(t,n),t.appendChild(n);n.attributes.length;){const{name:e}=n.attributes[0];ne(t,n,e),n.removeAttribute(e)}}return t}function se(t,e){let n=e.parentInfo&&se(t,e.parentInfo);if(!n)return t;for(let t=n.firstChild,r=0;t;t=t.nextSibling)if(e.parentIndex===r++)return t}function ie(t,e,n,r){r.id&&(e[r.id]=n)}function oe(t,e,n){if(n.events&&n.events.length)for(let r,s=0,i=n.events;s<i.length&&(r=i[s]);s++)t._addMethodEventListenerToNode(e,r.name,r.value,t)}function ae(t,e,n,r){n.templateInfo&&(e._templateInfo=n.templateInfo,e._parentTemplateInfo=r)}const le=d((t=>class extends t{static _parseTemplate(t,e){if(!t._templateInfo){let n=t._templateInfo={};n.nodeInfoList=[],n.nestedTemplate=Boolean(e),n.stripWhiteSpace=!0,this._parseTemplateContent(t,n,{parent:null})}return t._templateInfo}static _parseTemplateContent(t,e,n){return this._parseTemplateNode(t.content,e,n)}static _parseTemplateNode(t,e,n){let r=!1,s=t;return"template"!=s.localName||s.hasAttribute("preserve-content")?"slot"===s.localName&&(e.hasInsertionPoint=!0):r=this._parseTemplateNestedTemplate(s,e,n)||r,ee(s),s.firstChild&&this._parseTemplateChildNodes(s,e,n),s.hasAttributes&&s.hasAttributes()&&(r=this._parseTemplateNodeAttributes(s,e,n)||r),r||n.noted}static _parseTemplateChildNodes(t,e,n){if("script"!==t.localName&&"style"!==t.localName)for(let r,s=t.firstChild,i=0;s;s=r){if("template"==s.localName&&(s=re(s)),r=s.nextSibling,s.nodeType===Node.TEXT_NODE){let n=r;for(;n&&n.nodeType===Node.TEXT_NODE;)s.textContent+=n.textContent,r=n.nextSibling,t.removeChild(n),n=r;if(e.stripWhiteSpace&&!s.textContent.trim()){t.removeChild(s);continue}}let o={parentIndex:i,parentInfo:n};this._parseTemplateNode(s,e,o)&&(o.infoIndex=e.nodeInfoList.push(o)-1),s.parentNode&&i++}}static _parseTemplateNestedTemplate(t,e,n){let r=t,s=this._parseTemplate(r,e);return(s.content=r.content.ownerDocument.createDocumentFragment()).appendChild(r.content),n.templateInfo=s,!0}static _parseTemplateNodeAttributes(t,e,n){let r=!1,s=Array.from(t.attributes);for(let i,o=s.length-1;i=s[o];o--)r=this._parseTemplateNodeAttribute(t,e,n,i.name,i.value)||r;return r}static _parseTemplateNodeAttribute(t,e,n,r,s){return"on-"===r.slice(0,3)?(t.removeAttribute(r),n.events=n.events||[],n.events.push({name:r.slice(3),value:s}),!0):"id"===r&&(n.id=s,!0)}static _contentForTemplate(t){let e=t._templateInfo;return e&&e.content||t.content}_stampTemplate(t,e){t&&!t.content&&window.HTMLTemplateElement&&HTMLTemplateElement.decorate&&HTMLTemplateElement.decorate(t);let n=(e=e||this.constructor._parseTemplate(t)).nodeInfoList,r=e.content||t.content,s=document.importNode(r,!0);s.__noInsertionPoint=!e.hasInsertionPoint;let i=s.nodeList=new Array(n.length);s.$={};for(let t,r=0,o=n.length;r<o&&(t=n[r]);r++){let n=i[r]=se(s,t);ie(0,s.$,n,t),ae(0,n,t,e),oe(this,n,t)}return s=s,s}_addMethodEventListenerToNode(t,e,n,r){let s=function(t,e,n){return t=t._methodHost||t,function(e){t[n]?t[n](e,e.detail):console.warn("listener method `"+n+"` not defined")}}(r=r||t,0,n);return this._addEventListenerToNode(t,e,s),s}_addEventListenerToNode(t,e,n){t.addEventListener(e,n)}_removeEventListenerFromNode(t,e,n){t.removeEventListener(e,n)}}));let he=0;const ce=[],de={COMPUTE:"__computeEffects",REFLECT:"__reflectEffects",NOTIFY:"__notifyEffects",PROPAGATE:"__propagateEffects",OBSERVE:"__observeEffects",READ_ONLY:"__readOnly"},_e=/[A-Z]/;function ue(t,e,n){let r=t[e];if(r){if(!t.hasOwnProperty(e)&&(r=t[e]=Object.create(t[e]),n))for(let t in r){let e=r[t],n=r[t]=Array(e.length);for(let t=0;t<e.length;t++)n[t]=e[t]}}else r=t[e]={};return r}function pe(t,e,n,r,s,i){if(e){let o=!1;const a=he++;for(let l in n){let h=e[s?kt(l):l];if(h)for(let e,c=0,d=h.length;c<d&&(e=h[c]);c++)e.info&&e.info.lastRun===a||s&&!me(l,e.trigger)||(e.info&&(e.info.lastRun=a),e.fn(t,l,n,r,e.info,s,i),o=!0)}return o}return!1}function fe(t,e,n,r,s,i,o,a){let l=!1,h=e[o?kt(r):r];if(h)for(let e,c=0,d=h.length;c<d&&(e=h[c]);c++)e.info&&e.info.lastRun===n||o&&!me(r,e.trigger)||(e.info&&(e.info.lastRun=n),e.fn(t,r,s,i,e.info,o,a),l=!0);return l}function me(t,e){if(e){let n=e.name;return n==t||!(!e.structured||!Lt(n,t))||!(!e.wildcard||!Mt(n,t))}return!0}function ye(t,e,n,r,s){let i="string"==typeof s.method?t[s.method]:s.method,o=s.property;i?i.call(t,t.__data[o],r[o]):s.dynamicFn||console.warn("observer method `"+s.method+"` not defined")}function ge(t,e,n){let r=kt(e);if(r!==e){return be(t,Ut(r)+"-changed",n[e],e),!0}return!1}function be(t,e,n,r){let s={value:n,queueProperty:!0};r&&(s.path=r),I(t).dispatchEvent(new CustomEvent(e,{detail:s}))}function Pe(t,e,n,r,s,i){let o=(i?kt(e):e)!=e?e:null,a=o?zt(t,o):t.__data[e];o&&void 0===a&&(a=n[e]),be(t,s.eventName,a,o)}function ve(t,e,n,r,s){let i=t.__data[e];T&&(i=T(i,s.attrName,"attribute",t)),t._propertyToAttribute(e,s.attrName,i)}function Ce(t,e,n,r){let s=t[de.COMPUTE];if(s)if(S){he++;const i=function(t){let e=t.constructor.__orderedComputedDeps;if(!e){e=new Map;const n=t[de.COMPUTE];let r,{counts:s,ready:i,total:o}=function(t){const e=t.__computeInfo,n={},r=t[de.COMPUTE],s=[];let i=0;for(let t in e){const r=e[t];i+=n[t]=r.args.filter((t=>!t.literal)).length+(r.dynamicFn?1:0)}for(let t in r)e[t]||s.push(t);return{counts:n,ready:s,total:i}}(t);for(;r=i.shift();){e.set(r,e.size);const t=n[r];t&&t.forEach((t=>{const e=t.info.methodInfo;--o,0==--s[e]&&i.push(e)}))}if(0!==o){const e=t;console.warn(`Computed graph for ${e.localName} incomplete; circular?`)}t.constructor.__orderedComputedDeps=e}return e}(t),o=[];for(let t in e)Ee(t,s,o,i,r);let a;for(;a=o.shift();)Te(t,"",e,n,a)&&Ee(a.methodInfo,s,o,i,r);Object.assign(n,t.__dataOld),Object.assign(e,t.__dataPending),t.__dataPending=null}else{let i=e;for(;pe(t,s,i,n,r);)Object.assign(n,t.__dataOld),Object.assign(e,t.__dataPending),i=t.__dataPending,t.__dataPending=null}}const we=(t,e,n)=>{let r=0,s=e.length-1,i=-1;for(;r<=s;){const o=r+s>>1,a=n.get(e[o].methodInfo)-n.get(t.methodInfo);if(a<0)r=o+1;else{if(!(a>0)){i=o;break}s=o-1}}i<0&&(i=s+1),e.splice(i,0,t)},Ee=(t,e,n,r,s)=>{const i=e[s?kt(t):t];if(i)for(let e=0;e<i.length;e++){const o=i[e];o.info.lastRun===he||s&&!me(t,o.trigger)||(o.info.lastRun=he,we(o.info,n,r))}};function Te(t,e,n,r,s){let i=ke(t,e,n,r,s);if(i===ce)return!1;let o=s.methodInfo;return t.__dataHasAccessor&&t.__dataHasAccessor[o]?t._setPendingProperty(o,i,!0):(t[o]=i,!1)}function Oe(t,e,n,r,s,i,o){n.bindings=n.bindings||[];let a={kind:r,target:s,parts:i,literal:o,isCompound:1!==i.length};if(n.bindings.push(a),function(t){return Boolean(t.target)&&"attribute"!=t.kind&&"text"!=t.kind&&!t.isCompound&&"{"===t.parts[0].mode}(a)){let{event:t,negate:e}=a.parts[0];a.listenerEvent=t||Ut(s)+"-changed",a.listenerNegate=e}let l=e.nodeInfoList.length;for(let n=0;n<a.parts.length;n++){let r=a.parts[n];r.compoundIndex=n,Ae(t,e,a,r,l)}}function Ae(t,e,n,r,s){if(!r.literal)if("attribute"===n.kind&&"-"===n.target[0])console.warn("Cannot set attribute "+n.target+' because "-" is not a valid attribute starting character');else{let i=r.dependencies,o={index:s,binding:n,part:r,evaluator:t};for(let n=0;n<i.length;n++){let r=i[n];"string"==typeof r&&(r=Fe(r),r.wildcard=!0),t._addTemplatePropertyEffect(e,r.rootProperty,{fn:Ne,info:o,trigger:r})}}}function Ne(t,e,n,r,s,i,o){let a=o[s.index],l=s.binding,h=s.part;if(i&&h.source&&e.length>h.source.length&&"property"==l.kind&&!l.isCompound&&a.__isPropertyEffectsClient&&a.__dataHasAccessor&&a.__dataHasAccessor[l.target]){let r=n[e];e=Dt(h.source,l.target,e),a._setPendingPropertyOrPath(e,r,!1,!0)&&t._enqueueClient(a)}else{let o=s.evaluator._evaluateBinding(t,h,e,n,r,i);o!==ce&&function(t,e,n,r,s){s=function(t,e,n,r){if(n.isCompound){let s=t.__dataCompoundStorage[n.target];s[r.compoundIndex]=e,e=s.join("")}"attribute"!==n.kind&&("textContent"!==n.target&&("value"!==n.target||"input"!==t.localName&&"textarea"!==t.localName)||(e=null==e?"":e));return e}(e,s,n,r),T&&(s=T(s,n.target,n.kind,e));if("attribute"==n.kind)t._valueToNodeAttribute(e,s,n.target);else{let r=n.target;e.__isPropertyEffectsClient&&e.__dataHasAccessor&&e.__dataHasAccessor[r]?e[de.READ_ONLY]&&e[de.READ_ONLY][r]||e._setPendingProperty(r,s)&&t._enqueueClient(e):t._setUnmanagedPropertyToNode(e,r,s)}}(t,a,l,h,o)}}function xe(t,e){if(e.isCompound){let n=t.__dataCompoundStorage||(t.__dataCompoundStorage={}),r=e.parts,s=new Array(r.length);for(let t=0;t<r.length;t++)s[t]=r[t].literal;let i=e.target;n[i]=s,e.literal&&"property"==e.kind&&("className"===i&&(t=I(t)),t[i]=e.literal)}}function Se(t,e,n){if(n.listenerEvent){let r=n.parts[0];t.addEventListener(n.listenerEvent,(function(t){!function(t,e,n,r,s){let i,o=t.detail,a=o&&o.path;a?(r=Dt(n,r,a),i=o&&o.value):i=t.currentTarget[n],i=s?!i:i,e[de.READ_ONLY]&&e[de.READ_ONLY][r]||!e._setPendingPropertyOrPath(r,i,!0,Boolean(a))||o&&o.queueProperty||e._invalidateProperties()}(t,e,n.target,r.source,r.negate)}))}}function Ie(t,e,n,r,s,i){i=e.static||i&&("object"!=typeof i||i[e.methodName]);let o={methodName:e.methodName,args:e.args,methodInfo:s,dynamicFn:i};for(let s,i=0;i<e.args.length&&(s=e.args[i]);i++)s.literal||t._addPropertyEffect(s.rootProperty,n,{fn:r,info:o,trigger:s});return i&&t._addPropertyEffect(e.methodName,n,{fn:r,info:o}),o}function ke(t,e,n,r,s){let i=t._methodHost||t,o=i[s.methodName];if(o){let r=t._marshalArgs(s.args,e,n);return r===ce?ce:o.apply(i,r)}s.dynamicFn||console.warn("method `"+s.methodName+"` not defined")}const Le=[],Me=new RegExp("(\\[\\[|{{)\\s*(?:(!)\\s*)?((?:[a-zA-Z_$][\\w.:$\\-*]*)\\s*(?:\\(\\s*(?:(?:(?:((?:[a-zA-Z_$][\\w.:$\\-*]*)|(?:[-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?)|(?:(?:'(?:[^'\\\\]|\\\\.)*')|(?:\"(?:[^\"\\\\]|\\\\.)*\")))\\s*)(?:,\\s*(?:((?:[a-zA-Z_$][\\w.:$\\-*]*)|(?:[-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?)|(?:(?:'(?:[^'\\\\]|\\\\.)*')|(?:\"(?:[^\"\\\\]|\\\\.)*\")))\\s*))*)?)\\)\\s*)?)(?:]]|}})","g");function De(t){let e="";for(let n=0;n<t.length;n++){e+=t[n].literal||""}return e}function Re(t){let e=t.match(/([^\s]+?)\(([\s\S]*)\)/);if(e){let t={methodName:e[1],static:!0,args:Le};if(e[2].trim()){return function(t,e){return e.args=t.map((function(t){let n=Fe(t);return n.literal||(e.static=!1),n}),this),e}(e[2].replace(/\\,/g,"&comma;").split(","),t)}return t}return null}function Fe(t){let e=t.trim().replace(/&comma;/g,",").replace(/\\(.)/g,"$1"),n={name:e,value:"",literal:!1},r=e[0];switch("-"===r&&(r=e[1]),r>="0"&&r<="9"&&(r="#"),r){case"'":case'"':n.value=e.slice(1,-1),n.literal=!0;break;case"#":n.value=Number(e),n.literal=!0}return n.literal||(n.rootProperty=kt(e),n.structured=It(e),n.structured&&(n.wildcard=".*"==e.slice(-2),n.wildcard&&(n.name=e.slice(0,-2)))),n}function He(t,e,n){let r=zt(t,n);return void 0===r&&(r=e[n]),r}function ze(t,e,n,r){const s={indexSplices:r};t.notifyPath(n+".splices",s),t.notifyPath(n+".length",e.length)}function je(t,e,n,r,s,i){ze(t,e,n,[{index:r,addedCount:s,removed:i,object:e,type:"splice"}])}const Be=d((t=>{const e=le(Zt(t));return class extends e{constructor(){super(),this.__isPropertyEffectsClient=!0,this.__dataClientsReady,this.__dataPendingClients,this.__dataToNotify,this.__dataLinkedPaths,this.__dataHasPaths,this.__dataCompoundStorage,this.__dataHost,this.__dataTemp,this.__dataClientsInitialized,this.__data,this.__dataPending,this.__dataOld,this.__computeEffects,this.__computeInfo,this.__reflectEffects,this.__notifyEffects,this.__propagateEffects,this.__observeEffects,this.__readOnly,this.__templateInfo,this._overrideLegacyUndefined}get PROPERTY_EFFECT_TYPES(){return de}_initializeProperties(){super._initializeProperties(),this._registerHost(),this.__dataClientsReady=!1,this.__dataPendingClients=null,this.__dataToNotify=null,this.__dataLinkedPaths=null,this.__dataHasPaths=!1,this.__dataCompoundStorage=this.__dataCompoundStorage||null,this.__dataHost=this.__dataHost||null,this.__dataTemp={},this.__dataClientsInitialized=!1}_registerHost(){if(Je.length){let t=Je[Je.length-1];t._enqueueClient(this),this.__dataHost=t}}_initializeProtoProperties(t){this.__data=Object.create(t),this.__dataPending=Object.create(t),this.__dataOld={}}_initializeInstanceProperties(t){let e=this[de.READ_ONLY];for(let n in t)e&&e[n]||(this.__dataPending=this.__dataPending||{},this.__dataOld=this.__dataOld||{},this.__data[n]=this.__dataPending[n]=t[n])}_addPropertyEffect(t,e,n){this._createPropertyAccessor(t,e==de.READ_ONLY);let r=ue(this,e,!0)[t];r||(r=this[e][t]=[]),r.push(n)}_removePropertyEffect(t,e,n){let r=ue(this,e,!0)[t],s=r.indexOf(n);s>=0&&r.splice(s,1)}_hasPropertyEffect(t,e){let n=this[e];return Boolean(n&&n[t])}_hasReadOnlyEffect(t){return this._hasPropertyEffect(t,de.READ_ONLY)}_hasNotifyEffect(t){return this._hasPropertyEffect(t,de.NOTIFY)}_hasReflectEffect(t){return this._hasPropertyEffect(t,de.REFLECT)}_hasComputedEffect(t){return this._hasPropertyEffect(t,de.COMPUTE)}_setPendingPropertyOrPath(t,e,n,r){if(r||kt(Array.isArray(t)?t[0]:t)!==t){if(!r){let n=zt(this,t);if(!(t=jt(this,t,e))||!super._shouldPropertyChange(t,e,n))return!1}if(this.__dataHasPaths=!0,this._setPendingProperty(t,e,n))return function(t,e,n){let r=t.__dataLinkedPaths;if(r){let s;for(let i in r){let o=r[i];Mt(i,e)?(s=Dt(i,o,e),t._setPendingPropertyOrPath(s,n,!0,!0)):Mt(o,e)&&(s=Dt(o,i,e),t._setPendingPropertyOrPath(s,n,!0,!0))}}}(this,t,e),!0}else{if(this.__dataHasAccessor&&this.__dataHasAccessor[t])return this._setPendingProperty(t,e,n);this[t]=e}return!1}_setUnmanagedPropertyToNode(t,e,n){n===t[e]&&"object"!=typeof n||("className"===e&&(t=I(t)),t[e]=n)}_setPendingProperty(t,e,n){let r=this.__dataHasPaths&&It(t),s=r?this.__dataTemp:this.__data;return!!this._shouldPropertyChange(t,e,s[t])&&(this.__dataPending||(this.__dataPending={},this.__dataOld={}),t in this.__dataOld||(this.__dataOld[t]=this.__data[t]),r?this.__dataTemp[t]=e:this.__data[t]=e,this.__dataPending[t]=e,(r||this[de.NOTIFY]&&this[de.NOTIFY][t])&&(this.__dataToNotify=this.__dataToNotify||{},this.__dataToNotify[t]=n),!0)}_setProperty(t,e){this._setPendingProperty(t,e,!0)&&this._invalidateProperties()}_invalidateProperties(){this.__dataReady&&this._flushProperties()}_enqueueClient(t){this.__dataPendingClients=this.__dataPendingClients||[],t!==this&&this.__dataPendingClients.push(t)}_flushClients(){this.__dataClientsReady?this.__enableOrFlushClients():(this.__dataClientsReady=!0,this._readyClients(),this.__dataReady=!0)}__enableOrFlushClients(){let t=this.__dataPendingClients;if(t){this.__dataPendingClients=null;for(let e=0;e<t.length;e++){let n=t[e];n.__dataEnabled?n.__dataPending&&n._flushProperties():n._enableProperties()}}}_readyClients(){this.__enableOrFlushClients()}setProperties(t,e){for(let n in t)!e&&this[de.READ_ONLY]&&this[de.READ_ONLY][n]||this._setPendingPropertyOrPath(n,t[n],!0);this._invalidateProperties()}ready(){this._flushProperties(),this.__dataClientsReady||this._flushClients(),this.__dataPending&&this._flushProperties()}_propertiesChanged(t,e,n){let r,s=this.__dataHasPaths;this.__dataHasPaths=!1,Ce(this,e,n,s),r=this.__dataToNotify,this.__dataToNotify=null,this._propagatePropertyChanges(e,n,s),this._flushClients(),pe(this,this[de.REFLECT],e,n,s),pe(this,this[de.OBSERVE],e,n,s),r&&function(t,e,n,r,s){let i,o,a=t[de.NOTIFY],l=he++;for(let o in e)e[o]&&(a&&fe(t,a,l,o,n,r,s)||s&&ge(t,o,n))&&(i=!0);i&&(o=t.__dataHost)&&o._invalidateProperties&&o._invalidateProperties()}(this,r,e,n,s),1==this.__dataCounter&&(this.__dataTemp={})}_propagatePropertyChanges(t,e,n){this[de.PROPAGATE]&&pe(this,this[de.PROPAGATE],t,e,n),this.__templateInfo&&this._runEffectsForTemplate(this.__templateInfo,t,e,n)}_runEffectsForTemplate(t,e,n,r){const s=(e,r)=>{pe(this,t.propertyEffects,e,n,r,t.nodeList);for(let s=t.firstChild;s;s=s.nextSibling)this._runEffectsForTemplate(s,e,n,r)};t.runEffects?t.runEffects(s,e,r):s(e,r)}linkPaths(t,e){t=Ft(t),e=Ft(e),this.__dataLinkedPaths=this.__dataLinkedPaths||{},this.__dataLinkedPaths[t]=e}unlinkPaths(t){t=Ft(t),this.__dataLinkedPaths&&delete this.__dataLinkedPaths[t]}notifySplices(t,e){let n={path:""};ze(this,zt(this,t,n),n.path,e)}get(t,e){return zt(e||this,t)}set(t,e,n){n?jt(n,t,e):this[de.READ_ONLY]&&this[de.READ_ONLY][t]||this._setPendingPropertyOrPath(t,e,!0)&&this._invalidateProperties()}push(t,...e){let n={path:""},r=zt(this,t,n),s=r.length,i=r.push(...e);return e.length&&je(this,r,n.path,s,e.length,[]),i}pop(t){let e={path:""},n=zt(this,t,e),r=Boolean(n.length),s=n.pop();return r&&je(this,n,e.path,n.length,0,[s]),s}splice(t,e,n,...r){let s,i={path:""},o=zt(this,t,i);return e<0?e=o.length-Math.floor(-e):e&&(e=Math.floor(e)),s=2===arguments.length?o.splice(e):o.splice(e,n,...r),(r.length||s.length)&&je(this,o,i.path,e,r.length,s),s}shift(t){let e={path:""},n=zt(this,t,e),r=Boolean(n.length),s=n.shift();return r&&je(this,n,e.path,0,0,[s]),s}unshift(t,...e){let n={path:""},r=zt(this,t,n),s=r.unshift(...e);return e.length&&je(this,r,n.path,0,e.length,[]),s}notifyPath(t,e){let n;if(1==arguments.length){let r={path:""};e=zt(this,t,r),n=r.path}else n=Array.isArray(t)?Ft(t):t;this._setPendingPropertyOrPath(n,e,!0,!0)&&this._invalidateProperties()}_createReadOnlyProperty(t,e){var n;this._addPropertyEffect(t,de.READ_ONLY),e&&(this["_set"+(n=t,n[0].toUpperCase()+n.substring(1))]=function(e){this._setProperty(t,e)})}_createPropertyObserver(t,e,n){let r={property:t,method:e,dynamicFn:Boolean(n)};this._addPropertyEffect(t,de.OBSERVE,{fn:ye,info:r,trigger:{name:t}}),n&&this._addPropertyEffect(e,de.OBSERVE,{fn:ye,info:r,trigger:{name:e}})}_createMethodObserver(t,e){let n=Re(t);if(!n)throw new Error("Malformed observer expression '"+t+"'");Ie(this,n,de.OBSERVE,ke,null,e)}_createNotifyingProperty(t){this._addPropertyEffect(t,de.NOTIFY,{fn:Pe,info:{eventName:Ut(t)+"-changed",property:t}})}_createReflectedProperty(t){let e=this.constructor.attributeNameForProperty(t);"-"===e[0]?console.warn("Property "+t+" cannot be reflected to attribute "+e+' because "-" is not a valid starting attribute name. Use a lowercase first letter for the property instead.'):this._addPropertyEffect(t,de.REFLECT,{fn:ve,info:{attrName:e}})}_createComputedProperty(t,e,n){let r=Re(e);if(!r)throw new Error("Malformed computed expression '"+e+"'");const s=Ie(this,r,de.COMPUTE,Te,t,n);ue(this,"__computeInfo")[t]=s}_marshalArgs(t,e,n){const r=this.__data,s=[];for(let i=0,o=t.length;i<o;i++){let{name:o,structured:a,wildcard:l,value:h,literal:c}=t[i];if(!c)if(l){const t=Mt(o,e),s=He(r,n,t?e:o);h={path:t?e:o,value:s,base:t?zt(r,o):s}}else h=a?He(r,n,o):r[o];s[i]=h}return s}static addPropertyEffect(t,e,n){this.prototype._addPropertyEffect(t,e,n)}static createPropertyObserver(t,e,n){this.prototype._createPropertyObserver(t,e,n)}static createMethodObserver(t,e){this.prototype._createMethodObserver(t,e)}static createNotifyingProperty(t){this.prototype._createNotifyingProperty(t)}static createReadOnlyProperty(t,e){this.prototype._createReadOnlyProperty(t,e)}static createReflectedProperty(t){this.prototype._createReflectedProperty(t)}static createComputedProperty(t,e,n){this.prototype._createComputedProperty(t,e,n)}static bindTemplate(t){return this.prototype._bindTemplate(t)}_bindTemplate(t,e){let n=this.constructor._parseTemplate(t),r=this.__preBoundTemplateInfo==n;if(!r)for(let t in n.propertyEffects)this._createPropertyAccessor(t);if(e)if(n=Object.create(n),n.wasPreBound=r,this.__templateInfo){const e=t._parentTemplateInfo||this.__templateInfo,r=e.lastChild;n.parent=e,e.lastChild=n,n.previousSibling=r,r?r.nextSibling=n:e.firstChild=n}else this.__templateInfo=n;else this.__preBoundTemplateInfo=n;return n}static _addTemplatePropertyEffect(t,e,n){(t.hostProps=t.hostProps||{})[e]=!0;let r=t.propertyEffects=t.propertyEffects||{};(r[e]=r[e]||[]).push(n)}_stampTemplate(t,e){e=e||this._bindTemplate(t,!0),Je.push(this);let n=super._stampTemplate(t,e);if(Je.pop(),e.nodeList=n.nodeList,!e.wasPreBound){let t=e.childNodes=[];for(let e=n.firstChild;e;e=e.nextSibling)t.push(e)}return n.templateInfo=e,function(t,e){let{nodeList:n,nodeInfoList:r}=e;if(r.length)for(let e=0;e<r.length;e++){let s=r[e],i=n[e],o=s.bindings;if(o)for(let e=0;e<o.length;e++){let n=o[e];xe(i,n),Se(i,t,n)}i.__dataHost=t}}(this,e),this.__dataClientsReady&&(this._runEffectsForTemplate(e,this.__data,null,!1),this._flushClients()),n}_removeBoundDom(t){const e=t.templateInfo,{previousSibling:n,nextSibling:r,parent:s}=e;n?n.nextSibling=r:s&&(s.firstChild=r),r?r.previousSibling=n:s&&(s.lastChild=n),e.nextSibling=e.previousSibling=null;let i=e.childNodes;for(let t=0;t<i.length;t++){let e=i[t];I(I(e).parentNode).removeChild(e)}}static _parseTemplateNode(t,n,r){let s=e._parseTemplateNode.call(this,t,n,r);if(t.nodeType===Node.TEXT_NODE){let e=this._parseBindings(t.textContent,n);e&&(t.textContent=De(e)||" ",Oe(this,n,r,"text","textContent",e),s=!0)}return s}static _parseTemplateNodeAttribute(t,n,r,s,i){let o=this._parseBindings(i,n);if(o){let e=s,i="property";_e.test(s)?i="attribute":"$"==s[s.length-1]&&(s=s.slice(0,-1),i="attribute");let a=De(o);return a&&"attribute"==i&&("class"==s&&t.hasAttribute("class")&&(a+=" "+t.getAttribute(s)),t.setAttribute(s,a)),"attribute"==i&&"disable-upgrade$"==e&&t.setAttribute(s,""),"input"===t.localName&&"value"===e&&t.setAttribute(e,""),t.removeAttribute(e),"property"===i&&(s=Yt(s)),Oe(this,n,r,i,s,o,a),!0}return e._parseTemplateNodeAttribute.call(this,t,n,r,s,i)}static _parseTemplateNestedTemplate(t,n,r){let s=e._parseTemplateNestedTemplate.call(this,t,n,r);const i=t.parentNode,o=r.templateInfo;i.localName,i.localName;let a=o.hostProps;{let t="{";for(let e in a){Oe(this,n,r,"property","_host_"+e,[{mode:t,source:e,dependencies:[e],hostProp:!0}])}}return s}static _parseBindings(t,e){let n,r=[],s=0;for(;null!==(n=Me.exec(t));){n.index>s&&r.push({literal:t.slice(s,n.index)});let i=n[1][0],o=Boolean(n[2]),a=n[3].trim(),l=!1,h="",c=-1;"{"==i&&(c=a.indexOf("::"))>0&&(h=a.substring(c+2),a=a.substring(0,c),l=!0);let d=Re(a),_=[];if(d){let{args:t,methodName:n}=d;for(let e=0;e<t.length;e++){let n=t[e];n.literal||_.push(n)}let r=e.dynamicFns;(r&&r[n]||d.static)&&(_.push(n),d.dynamicFn=!0)}else _.push(a);r.push({source:a,mode:i,negate:o,customEvent:l,signature:d,dependencies:_,event:h}),s=Me.lastIndex}if(s&&s<t.length){let e=t.substring(s);e&&r.push({literal:e})}return r.length?r:null}static _evaluateBinding(t,e,n,r,s,i){let o;return o=e.signature?ke(t,n,r,0,e.signature):n!=e.source?zt(t,e.source):i&&It(n)?zt(t,n):t.__data[n],e.negate&&(o=!o),o}}})),Je=[];const qe=d((t=>{const e=Vt(t);function n(t){const e=Object.getPrototypeOf(t);return e.prototype instanceof s?e:null}function r(t){if(!t.hasOwnProperty(JSCompiler_renameProperty("__ownProperties",t))){let e=null;if(t.hasOwnProperty(JSCompiler_renameProperty("properties",t))){const n=t.properties;n&&(e=function(t){const e={};for(let n in t){const r=t[n];e[n]="function"==typeof r?{type:r}:r}return e}(n))}t.__ownProperties=e}return t.__ownProperties}class s extends e{static get observedAttributes(){if(!this.hasOwnProperty(JSCompiler_renameProperty("__observedAttributes",this))){this.prototype;const t=this._properties;this.__observedAttributes=t?Object.keys(t).map((t=>this.prototype._addPropertyToAttributeMap(t))):[]}return this.__observedAttributes}static finalize(){if(!this.hasOwnProperty(JSCompiler_renameProperty("__finalized",this))){const t=n(this);t&&t.finalize(),this.__finalized=!0,this._finalizeClass()}}static _finalizeClass(){const t=r(this);t&&this.createProperties(t)}static get _properties(){if(!this.hasOwnProperty(JSCompiler_renameProperty("__properties",this))){const t=n(this);this.__properties=Object.assign({},t&&t._properties,r(this))}return this.__properties}static typeForProperty(t){const e=this._properties[t];return e&&e.type}_initializeProperties(){this.constructor.finalize(),super._initializeProperties()}connectedCallback(){super.connectedCallback&&super.connectedCallback(),this._enableProperties()}disconnectedCallback(){super.disconnectedCallback&&super.disconnectedCallback()}}return s})),Ye=d((t=>{const e=qe(Be(t));function n(t,e,n,r){n.computed&&(n.readOnly=!0),n.computed&&(t._hasReadOnlyEffect(e)?console.warn(`Cannot redefine computed property '${e}'.`):t._createComputedProperty(e,n.computed,r)),n.readOnly&&!t._hasReadOnlyEffect(e)?t._createReadOnlyProperty(e,!n.computed):!1===n.readOnly&&t._hasReadOnlyEffect(e)&&console.warn(`Cannot make readOnly property '${e}' non-readOnly.`),n.reflectToAttribute&&!t._hasReflectEffect(e)?t._createReflectedProperty(e):!1===n.reflectToAttribute&&t._hasReflectEffect(e)&&console.warn(`Cannot make reflected property '${e}' non-reflected.`),n.notify&&!t._hasNotifyEffect(e)?t._createNotifyingProperty(e):!1===n.notify&&t._hasNotifyEffect(e)&&console.warn(`Cannot make notify property '${e}' non-notify.`),n.observer&&t._createPropertyObserver(e,n.observer,r[n.observer]),t._addPropertyToAttributeMap(e)}function r(t,e,n,r){{const s=e.content.querySelectorAll("style"),i=Nt(e),o=function(t){let e=Et(t);return e?xt(e):[]}(n),a=e.content.firstElementChild;for(let n=0;n<o.length;n++){let s=o[n];s.textContent=t._processStyleText(s.textContent,r),e.content.insertBefore(s,a)}let l=0;for(let e=0;e<i.length;e++){let n=i[e],o=s[l];o!==n?(n=n.cloneNode(!0),o.parentNode.insertBefore(n,o)):l++,n.textContent=t._processStyleText(n.textContent,r)}}}return class extends e{static get polymerElementVersion(){return"3.5.0"}static _finalizeClass(){e._finalizeClass.call(this);const t=((n=this).hasOwnProperty(JSCompiler_renameProperty("__ownObservers",n))||(n.__ownObservers=n.hasOwnProperty(JSCompiler_renameProperty("observers",n))?n.observers:null),n.__ownObservers);var n;t&&this.createObservers(t,this._properties),this._prepareTemplate()}static _prepareTemplate(){let t=this.template;t&&("string"==typeof t?(console.error("template getter must return HTMLTemplateElement"),t=null):x||(t=t.cloneNode(!0))),this.prototype._template=t}static createProperties(t){for(let e in t)n(this.prototype,e,t[e],t)}static createObservers(t,e){const n=this.prototype;for(let r=0;r<t.length;r++)n._createMethodObserver(t[r],e)}static get template(){if(!this.hasOwnProperty(JSCompiler_renameProperty("_template",this))){let t=this.prototype.hasOwnProperty(JSCompiler_renameProperty("_template",this.prototype))?this.prototype._template:void 0;"function"==typeof t&&(t=t()),this._template=void 0!==t?t:this.hasOwnProperty(JSCompiler_renameProperty("is",this))&&function(t){let e=null;if(t&&(!A||N)&&(e=wt.import(t,"template"),A&&!e))throw new Error("strictTemplatePolicy: expecting dom-module or null template for "+t);return e}(this.is)||Object.getPrototypeOf(this.prototype).constructor.template}return this._template}static set template(t){this._template=t}static get importPath(){if(!this.hasOwnProperty(JSCompiler_renameProperty("_importPath",this))){const t=this.importMeta;if(t)this._importPath=C(t.url);else{const t=wt.import(this.is);this._importPath=t&&t.assetpath||Object.getPrototypeOf(this.prototype).constructor.importPath}}return this._importPath}constructor(){super(),this._template,this._importPath,this.rootPath,this.importPath,this.root,this.$}_initializeProperties(){this.constructor.finalize(),this.constructor._finalizeTemplate(this.localName),super._initializeProperties(),this.rootPath=E,this.importPath=this.constructor.importPath;let t=function(t){if(!t.hasOwnProperty(JSCompiler_renameProperty("__propertyDefaults",t))){t.__propertyDefaults=null;let e=t._properties;for(let n in e){let r=e[n];"value"in r&&(t.__propertyDefaults=t.__propertyDefaults||{},t.__propertyDefaults[n]=r)}}return t.__propertyDefaults}(this.constructor);if(t)for(let e in t){let n=t[e];if(this._canApplyPropertyDefault(e)){let t="function"==typeof n.value?n.value.call(this):n.value;this._hasAccessor(e)?this._setPendingProperty(e,t,!0):this[e]=t}}}_canApplyPropertyDefault(t){return!this.hasOwnProperty(t)}static _processStyleText(t,e){return v(t,e)}static _finalizeTemplate(t){const e=this.prototype._template;if(e&&!e.__polymerFinalized){e.__polymerFinalized=!0;const n=this.importPath;r(this,e,t,n?P(n):""),this.prototype._bindTemplate(e)}}connectedCallback(){super.connectedCallback()}ready(){this._template&&(this.root=this._stampTemplate(this._template),this.$=this.root.$),super.ready()}_readyClients(){this._template&&(this.root=this._attachDom(this.root)),super._readyClients()}_attachDom(t){const e=I(this);if(e.attachShadow)return t?(e.shadowRoot||(e.attachShadow({mode:"open",shadyUpgradeFragment:t}),this.constructor._styleSheet&&(e.shadowRoot.adoptedStyleSheets=[this.constructor._styleSheet])),e.shadowRoot.appendChild(t),e.shadowRoot):null;throw new Error("ShadowDOM not available. PolymerElement can create dom as children instead of in ShadowDOM by setting `this.root = this;` before `ready`.")}updateStyles(t){for(const[e,n]of Object.entries(t))this.style.setProperty(e,n)}resolveUrl(t,e){return!e&&this.importPath&&(e=P(this.importPath)),P(t,e)}static _parseTemplateContent(t,n,r){return n.dynamicFns=n.dynamicFns||this._properties,e._parseTemplateContent.call(this,t,n,r)}static _addTemplatePropertyEffect(t,n,r){return e._addTemplatePropertyEffect.call(this,t,n,r)}}})),Ue=window.trustedTypes&&trustedTypes.createPolicy("polymer-html-literal",{createHTML:t=>t});class $e{constructor(t,e){Ge(t,e);const n=e.reduce(((e,n,r)=>e+Ve(n)+t[r+1]),t[0]);this.value=n.toString()}toString(){return this.value}}function Ve(t){if(t instanceof $e)return t.value;throw new Error("non-literal value passed to Polymer's htmlLiteral function: "+t)}const Xe=function(t,...e){Ge(t,e);const n=document.createElement("template");let r=e.reduce(((e,n,r)=>e+function(t){if(t instanceof HTMLTemplateElement)return t.innerHTML;if(t instanceof $e)return Ve(t);throw new Error("non-template value passed to Polymer's html function: "+t)}(n)+t[r+1]),t[0]);return Ue&&(r=Ue.createHTML(r)),n.innerHTML=r,n},Ge=(t,e)=>{if(!Array.isArray(t)||!Array.isArray(t.raw)||e.length!==t.length-1)throw new TypeError("Invalid call to the html template tag")},We=Ye(HTMLElement),Ze=function(){let t,e;do{t=!1,e=f()}while(e)};function Ke(t,e,n,r,s){let i;s&&(i="object"==typeof n&&null!==n,i&&(r=t.__dataTemp[e]));let o=r!==n&&(r==r||n==n);return i&&o&&(t.__dataTemp[e]=n),o}const Qe=d((t=>class extends t{_shouldPropertyChange(t,e,n){return Ke(this,t,e,n,!0)}})),tn=d((t=>class extends t{static get properties(){return{mutableData:Boolean}}_shouldPropertyChange(t,e,n){return Ke(this,t,e,n,this.mutableData)}}));Qe._mutablePropertyChange=Ke;let en=null;function nn(){return en}nn.prototype=Object.create(HTMLTemplateElement.prototype,{constructor:{value:nn,writable:!0}});const rn=Be(nn),sn=Qe(rn);const on=Be(class{});class an extends on{constructor(t){super(),this._configureProperties(t),this.root=this._stampTemplate(this.__dataHost);let e=[];this.children=e;for(let t=this.root.firstChild;t;t=t.nextSibling)e.push(t),t.__templatizeInstance=this;this.__templatizeOwner&&this.__templatizeOwner.__hideTemplateChildren__&&this._showHideChildren(!0);let n=this.__templatizeOptions;(t&&n.instanceProps||!n.instanceProps)&&this._enableProperties()}_configureProperties(t){if(this.__templatizeOptions.forwardHostProp)for(let t in this.__hostProps)this._setPendingProperty(t,this.__dataHost["_host_"+t]);for(let e in t)this._setPendingProperty(e,t[e])}forwardHostProp(t,e){this._setPendingPropertyOrPath(t,e,!1,!0)&&this.__dataHost._enqueueClient(this)}_addEventListenerToNode(t,e,n){if(this._methodHost&&this.__templatizeOptions.parentModel)this._methodHost._addEventListenerToNode(t,e,(t=>{t.model=this,n(t)}));else{let r=this.__dataHost.__dataHost;r&&r._addEventListenerToNode(t,e,n)}}_showHideChildren(t){!function(t,e){for(let n=0;n<e.length;n++){let r=e[n];if(Boolean(t)!=Boolean(r.__hideTemplateChildren__))if(r.nodeType===Node.TEXT_NODE)t?(r.__polymerTextContent__=r.textContent,r.textContent=""):r.textContent=r.__polymerTextContent__;else if("slot"===r.localName)if(t)r.__polymerReplaced__=document.createComment("hidden-slot"),I(I(r).parentNode).replaceChild(r.__polymerReplaced__,r);else{const t=r.__polymerReplaced__;t&&I(I(t).parentNode).replaceChild(r,t)}else r.style&&(t?(r.__polymerDisplay__=r.style.display,r.style.display="none"):r.style.display=r.__polymerDisplay__);r.__hideTemplateChildren__=t,r._showHideChildren&&r._showHideChildren(t)}}(t,this.children)}_setUnmanagedPropertyToNode(t,e,n){t.__hideTemplateChildren__&&t.nodeType==Node.TEXT_NODE&&"textContent"==e?t.__polymerTextContent__=n:super._setUnmanagedPropertyToNode(t,e,n)}get parentModel(){let t=this.__parentModel;if(!t){let e;t=this;do{t=t.__dataHost.__dataHost}while((e=t.__templatizeOptions)&&!e.parentModel);this.__parentModel=t}return t}dispatchEvent(t){return!0}}an.prototype.__dataHost,an.prototype.__templatizeOptions,an.prototype._methodHost,an.prototype.__templatizeOwner,an.prototype.__hostProps;const ln=Qe(an);function hn(t){let e=t.__dataHost;return e&&e._methodHost||e}function cn(t,e,n){let r=n.mutableData?ln:an;pn.mixin&&(r=pn.mixin(r));let s=class extends r{};return s.prototype.__templatizeOptions=n,s.prototype._bindTemplate(t),function(t,e,n,r){let s=n.hostProps||{};for(let e in r.instanceProps){delete s[e];let n=r.notifyInstanceProp;n&&t.prototype._addPropertyEffect(e,t.prototype.PROPERTY_EFFECT_TYPES.NOTIFY,{fn:un(e,n)})}if(r.forwardHostProp&&e.__dataHost)for(let e in s)n.hasHostProps||(n.hasHostProps=!0),t.prototype._addPropertyEffect(e,t.prototype.PROPERTY_EFFECT_TYPES.NOTIFY,{fn:function(t,e,n){t.__dataHost._setPendingPropertyOrPath("_host_"+e,n[e],!0,!0)}})}(s,t,e,n),s}function dn(t,e,n,r){let s=n.forwardHostProp;if(s&&e.hasHostProps){const r="template"==t.localName;let a=e.templatizeTemplateClass;if(!a){if(r){let t=n.mutableData?sn:rn;class r extends t{}a=e.templatizeTemplateClass=r}else{const n=t.constructor;class r extends n{}a=e.templatizeTemplateClass=r}let i=e.hostProps;for(let t in i)a.prototype._addPropertyEffect("_host_"+t,a.prototype.PROPERTY_EFFECT_TYPES.PROPAGATE,{fn:_n(t,s)}),a.prototype._createNotifyingProperty("_host_"+t)}if(t.__dataProto&&Object.assign(t.__data,t.__dataProto),r)o=a,en=i=t,Object.setPrototypeOf(i,o.prototype),new o,en=null,t.__dataTemp={},t.__dataPending=null,t.__dataOld=null,t._enableProperties();else{Object.setPrototypeOf(t,a.prototype);const n=e.hostProps;for(let e in n)if(e="_host_"+e,e in t){const n=t[e];delete t[e],t.__data[e]=n}}}var i,o}function _n(t,e){return function(t,n,r){e.call(t.__templatizeOwner,n.substring("_host_".length),r[n])}}function un(t,e){return function(t,n,r){e.call(t.__templatizeOwner,t,n,r[n])}}function pn(t,e,n){if(A&&!hn(t))throw new Error("strictTemplatePolicy: template owner not trusted");if(n=n||{},t.__templatizeOwner)throw new Error("A <template> can only be templatized once");t.__templatizeOwner=e;let r=(e?e.constructor:an)._parseTemplate(t),s=r.templatizeInstanceClass;s||(s=cn(t,r,n),r.templatizeInstanceClass=s);const i=hn(t);dn(t,r,n);let o=class extends s{};return o.prototype._methodHost=i,o.prototype.__dataHost=t,o.prototype.__templatizeOwner=e,o.prototype.__hostProps=r.hostProps,o=o,o}function fn(t,e){let n;for(;e;)if(n=e.__dataHost?e:e.__templatizeInstance){if(n.__dataHost==t)return n;e=n.__dataHost}else e=I(e).parentNode;return null}class mn extends We{static get is(){return"dom-if"}static get template(){return null}static get properties(){return{if:{type:Boolean,observer:"__debounceRender"},restamp:{type:Boolean,observer:"__debounceRender"},notifyDomChange:{type:Boolean}}}constructor(){super(),this.__renderDebouncer=null,this._lastIf=!1,this.__hideTemplateChildren__=!1,this.__template,this._templateInfo}__debounceRender(){this.__renderDebouncer=_.debounce(this.__renderDebouncer,h,(()=>this.__render())),p(this.__renderDebouncer)}disconnectedCallback(){super.disconnectedCallback();const t=I(this).parentNode;t&&(t.nodeType!=Node.DOCUMENT_FRAGMENT_NODE||I(t).host)||this.__teardownInstance()}connectedCallback(){super.connectedCallback(),this.style.display="none",this.if&&this.__debounceRender()}__ensureTemplate(){if(!this.__template){const t=this;let e=t._templateInfo?t:I(t).querySelector("template");if(!e){let t=new MutationObserver((()=>{if(!I(this).querySelector("template"))throw new Error("dom-if requires a <template> child");t.disconnect(),this.__render()}));return t.observe(this,{childList:!0}),!1}this.__template=e}return!0}__ensureInstance(){let t=I(this).parentNode;if(this.__hasInstance()){let e=this.__getInstanceNodes();if(e&&e.length){if(I(this).previousSibling!==e[e.length-1])for(let n,r=0;r<e.length&&(n=e[r]);r++)I(t).insertBefore(n,this)}}else{if(!t)return!1;if(!this.__ensureTemplate())return!1;this.__createAndInsertInstance(t)}return!0}render(){Ze()}__render(){if(this.if){if(!this.__ensureInstance())return}else this.restamp&&this.__teardownInstance();this._showHideChildren(),this.if!=this._lastIf&&(this.dispatchEvent(new CustomEvent("dom-change",{bubbles:!0,composed:!0})),this._lastIf=this.if)}__hasInstance(){}__getInstanceNodes(){}__createAndInsertInstance(t){}__teardownInstance(){}_showHideChildren(){}}const yn=class extends mn{constructor(){super(),this.__ctor=null,this.__instance=null,this.__invalidProps=null}__hasInstance(){return Boolean(this.__instance)}__getInstanceNodes(){return this.__instance.children}__createAndInsertInstance(t){this.__ctor||(this.__ctor=pn(this.__template,this,{mutableData:!0,forwardHostProp:function(t,e){this.__instance&&(this.if?this.__instance.forwardHostProp(t,e):(this.__invalidProps=this.__invalidProps||Object.create(null),this.__invalidProps[kt(t)]=!0))}})),this.__instance=new this.__ctor,I(t).insertBefore(this.__instance.root,this)}__teardownInstance(){if(this.__instance){let t=this.__instance.children;if(t&&t.length){let e=I(t[0]).parentNode;if(e){e=I(e);for(let n,r=0;r<t.length&&(n=t[r]);r++)e.removeChild(n)}}this.__invalidProps=null,this.__instance=null}}__syncHostProperties(){let t=this.__invalidProps;if(t){this.__invalidProps=null;for(let e in t)this.__instance._setPendingProperty(e,this.__dataHost[e]);this.__instance._flushProperties()}}_showHideChildren(){const t=this.__hideTemplateChildren__||!this.if;this.__instance&&Boolean(this.__instance.__hidden)!==t&&(this.__instance.__hidden=t,this.__instance._showHideChildren(t)),t||this.__syncHostProperties()}};customElements.define(yn.is,yn);const gn=tn(We);class bn extends gn{static get is(){return"dom-repeat"}static get template(){return null}static get properties(){return{items:{type:Array},as:{type:String,value:"item"},indexAs:{type:String,value:"index"},itemsIndexAs:{type:String,value:"itemsIndex"},sort:{type:Function,observer:"__sortChanged"},filter:{type:Function,observer:"__filterChanged"},observe:{type:String,observer:"__observeChanged"},delay:Number,renderedItemCount:{type:Number,notify:!0,readOnly:!0},initialCount:{type:Number},targetFramerate:{type:Number,value:20},_targetFrameTime:{type:Number,computed:"__computeFrameTime(targetFramerate)"},notifyDomChange:{type:Boolean},reuseChunkedInstances:{type:Boolean}}}static get observers(){return["__itemsChanged(items.*)"]}constructor(){super(),this.__instances=[],this.__renderDebouncer=null,this.__itemsIdxToInstIdx={},this.__chunkCount=null,this.__renderStartTime=null,this.__itemsArrayChanged=!1,this.__shouldMeasureChunk=!1,this.__shouldContinueChunking=!1,this.__chunkingId=0,this.__sortFn=null,this.__filterFn=null,this.__observePaths=null,this.__ctor=null,this.__isDetached=!0,this.template=null,this._templateInfo}disconnectedCallback(){super.disconnectedCallback(),this.__isDetached=!0;for(let t=0;t<this.__instances.length;t++)this.__detachInstance(t);this.__chunkingId&&cancelAnimationFrame(this.__chunkingId)}connectedCallback(){if(super.connectedCallback(),this.style.display="none",this.__isDetached){this.__isDetached=!1;let t=I(I(this).parentNode);for(let e=0;e<this.__instances.length;e++)this.__attachInstance(e,t);this.__chunkingId&&this.__render()}}__ensureTemplatized(){if(!this.__ctor){const t=this;let e=this.template=t._templateInfo?t:this.querySelector("template");if(!e){let t=new MutationObserver((()=>{if(!this.querySelector("template"))throw new Error("dom-repeat requires a <template> child");t.disconnect(),this.__render()}));return t.observe(this,{childList:!0}),!1}let n={};n[this.as]=!0,n[this.indexAs]=!0,n[this.itemsIndexAs]=!0,this.__ctor=pn(e,this,{mutableData:this.mutableData,parentModel:!0,instanceProps:n,forwardHostProp:function(t,e){let n=this.__instances;for(let r,s=0;s<n.length&&(r=n[s]);s++)r.forwardHostProp(t,e)},notifyInstanceProp:function(t,e,n){if(Rt(this.as,e)){let r=t[this.itemsIndexAs];e==this.as&&(this.items[r]=n);let s=Dt(this.as,`${JSCompiler_renameProperty("items",this)}.${r}`,e);this.notifyPath(s,n)}}})}return!0}__getMethodHost(){return this.__dataHost._methodHost||this.__dataHost}__functionFromPropertyValue(t){if("string"==typeof t){let e=t,n=this.__getMethodHost();return function(){return n[e].apply(n,arguments)}}return t}__sortChanged(t){this.__sortFn=this.__functionFromPropertyValue(t),this.items&&this.__debounceRender(this.__render)}__filterChanged(t){this.__filterFn=this.__functionFromPropertyValue(t),this.items&&this.__debounceRender(this.__render)}__computeFrameTime(t){return Math.ceil(1e3/t)}__observeChanged(){this.__observePaths=this.observe&&this.observe.replace(".*",".").split(" ")}__handleObservedPaths(t){if(this.__sortFn||this.__filterFn)if(t){if(this.__observePaths){let e=this.__observePaths;for(let n=0;n<e.length;n++)0===t.indexOf(e[n])&&this.__debounceRender(this.__render,this.delay)}}else this.__debounceRender(this.__render,this.delay)}__itemsChanged(t){this.items&&!Array.isArray(this.items)&&console.warn("dom-repeat expected array for `items`, found",this.items),this.__handleItemPath(t.path,t.value)||("items"===t.path&&(this.__itemsArrayChanged=!0),this.__debounceRender(this.__render))}__debounceRender(t,e=0){this.__renderDebouncer=_.debounce(this.__renderDebouncer,e>0?o.after(e):h,t.bind(this)),p(this.__renderDebouncer)}render(){this.__debounceRender(this.__render),Ze()}__render(){if(!this.__ensureTemplatized())return;let t=this.items||[];const e=this.__sortAndFilterItems(t),n=this.__calculateLimit(e.length);this.__updateInstances(t,n,e),this.initialCount&&(this.__shouldMeasureChunk||this.__shouldContinueChunking)&&(cancelAnimationFrame(this.__chunkingId),this.__chunkingId=requestAnimationFrame((()=>{this.__chunkingId=null,this.__continueChunking()}))),this._setRenderedItemCount(this.__instances.length),this.dispatchEvent(new CustomEvent("dom-change",{bubbles:!0,composed:!0}))}__sortAndFilterItems(t){let e=new Array(t.length);for(let n=0;n<t.length;n++)e[n]=n;return this.__filterFn&&(e=e.filter(((e,n,r)=>this.__filterFn(t[e],n,r)))),this.__sortFn&&e.sort(((e,n)=>this.__sortFn(t[e],t[n]))),e}__calculateLimit(t){let e=t;const n=this.__instances.length;if(this.initialCount){let r;!this.__chunkCount||this.__itemsArrayChanged&&!this.reuseChunkedInstances?(e=Math.min(t,this.initialCount),r=Math.max(e-n,0),this.__chunkCount=r||1):(r=Math.min(Math.max(t-n,0),this.__chunkCount),e=Math.min(n+r,t)),this.__shouldMeasureChunk=r===this.__chunkCount,this.__shouldContinueChunking=e<t,this.__renderStartTime=performance.now()}return this.__itemsArrayChanged=!1,e}__continueChunking(){if(this.__shouldMeasureChunk){const t=performance.now()-this.__renderStartTime,e=this._targetFrameTime/t;this.__chunkCount=Math.round(this.__chunkCount*e)||1}this.__shouldContinueChunking&&this.__debounceRender(this.__render)}__updateInstances(t,e,n){const r=this.__itemsIdxToInstIdx={};let s;for(s=0;s<e;s++){let e=this.__instances[s],i=n[s],o=t[i];r[i]=s,e?(e._setPendingProperty(this.as,o),e._setPendingProperty(this.indexAs,s),e._setPendingProperty(this.itemsIndexAs,i),e._flushProperties()):this.__insertInstance(o,s,i)}for(let t=this.__instances.length-1;t>=s;t--)this.__detachAndRemoveInstance(t)}__detachInstance(t){let e=this.__instances[t];const n=I(e.root);for(let t=0;t<e.children.length;t++){let r=e.children[t];n.appendChild(r)}return e}__attachInstance(t,e){let n=this.__instances[t];e.insertBefore(n.root,this)}__detachAndRemoveInstance(t){this.__detachInstance(t),this.__instances.splice(t,1)}__stampInstance(t,e,n){let r={};return r[this.as]=t,r[this.indexAs]=e,r[this.itemsIndexAs]=n,new this.__ctor(r)}__insertInstance(t,e,n){const r=this.__stampInstance(t,e,n);let s=this.__instances[e+1],i=s?s.children[0]:this;return I(I(this).parentNode).insertBefore(r.root,i),this.__instances[e]=r,r}_showHideChildren(t){for(let e=0;e<this.__instances.length;e++)this.__instances[e]._showHideChildren(t)}__handleItemPath(t,e){let n=t.slice(6),r=n.indexOf("."),s=r<0?n:n.substring(0,r);if(s==parseInt(s,10)){let t=r<0?"":n.substring(r+1);this.__handleObservedPaths(t);let i=this.__itemsIdxToInstIdx[s],o=this.__instances[i];if(o){let n=this.as+(t?"."+t:"");o._setPendingPropertyOrPath(n,e,!1,!0),o._flushProperties()}return!0}}itemForElement(t){let e=this.modelForElement(t);return e&&e[this.as]}indexForElement(t){let e=this.modelForElement(t);return e&&e[this.indexAs]}modelForElement(t){return fn(this.template,t)}}customElements.define(bn.is,bn);const Pn=d((t=>class extends t{_addEventListenerToNode(t,e,n){it(t,e,n)||super._addEventListenerToNode(t,e,n)}_removeEventListenerFromNode(t,e,n){ot(t,e,n)||super._removeEventListenerFromNode(t,e,n)}}));let vn=!1,Cn=[],wn=[];function En(){vn=!0,requestAnimationFrame((function(){vn=!1,function(t){for(;t.length;)Tn(t.shift())}(Cn),setTimeout((function(){!function(t){for(let e=0,n=t.length;e<n;e++)Tn(t.shift())}(wn)}))}))}function Tn(t){const e=t[0],n=t[1],r=t[2];try{n.apply(e,r)}catch(t){setTimeout((()=>{throw t}))}}function On(t,e,n){vn||En(),Cn.push([t,e,n])}function An(t,e,n){vn||En(),wn.push([t,e,n])}function Nn(){document.body.removeAttribute("unresolved")}function xn(t,e,n){return{index:t,removed:e,addedCount:n}}"interactive"===document.readyState||"complete"===document.readyState?Nn():window.addEventListener("DOMContentLoaded",Nn);function Sn(t,e,n,r,s,i){let o,a=0,l=0,h=Math.min(n-e,i-s);if(0==e&&0==s&&(a=function(t,e,n){for(let r=0;r<n;r++)if(!kn(t[r],e[r]))return r;return n}(t,r,h)),n==t.length&&i==r.length&&(l=function(t,e,n){let r=t.length,s=e.length,i=0;for(;i<n&&kn(t[--r],e[--s]);)i++;return i}(t,r,h-a)),s+=a,i-=l,(n-=l)-(e+=a)==0&&i-s==0)return[];if(e==n){for(o=xn(e,[],0);s<i;)o.removed.push(r[s++]);return[o]}if(s==i)return[xn(e,[],n-e)];let c=function(t){let e=t.length-1,n=t[0].length-1,r=t[e][n],s=[];for(;e>0||n>0;){if(0==e){s.push(2),n--;continue}if(0==n){s.push(3),e--;continue}let i,o=t[e-1][n-1],a=t[e-1][n],l=t[e][n-1];i=a<l?a<o?a:o:l<o?l:o,i==o?(o==r?s.push(0):(s.push(1),r=o),e--,n--):i==a?(s.push(3),e--,r=a):(s.push(2),n--,r=l)}return s.reverse(),s}(function(t,e,n,r,s,i){let o=i-s+1,a=n-e+1,l=new Array(o);for(let t=0;t<o;t++)l[t]=new Array(a),l[t][0]=t;for(let t=0;t<a;t++)l[0][t]=t;for(let n=1;n<o;n++)for(let i=1;i<a;i++)if(kn(t[e+i-1],r[s+n-1]))l[n][i]=l[n-1][i-1];else{let t=l[n-1][i]+1,e=l[n][i-1]+1;l[n][i]=t<e?t:e}return l}(t,e,n,r,s,i));o=void 0;let d=[],_=e,u=s;for(let t=0;t<c.length;t++)switch(c[t]){case 0:o&&(d.push(o),o=void 0),_++,u++;break;case 1:o||(o=xn(_,[],0)),o.addedCount++,_++,o.removed.push(r[u]),u++;break;case 2:o||(o=xn(_,[],0)),o.addedCount++,_++;break;case 3:o||(o=xn(_,[],0)),o.removed.push(r[u]),u++}return o&&d.push(o),d}function In(t,e){return Sn(t,0,t.length,e,0,e.length)}function kn(t,e){return t===e}function Ln(t){return"slot"===t.localName}let Mn=class{static getFlattenedNodes(t){const e=I(t);return Ln(t)?(t=t,e.assignedNodes({flatten:!0})):Array.from(e.childNodes).map((t=>Ln(t)?I(t=t).assignedNodes({flatten:!0}):[t])).reduce(((t,e)=>t.concat(e)),[])}constructor(t,e){this._shadyChildrenObserver=null,this._nativeChildrenObserver=null,this._connected=!1,this._target=t,this.callback=e,this._effectiveNodes=[],this._observer=null,this._scheduled=!1,this._boundSchedule=()=>{this._schedule()},this.connect(),this._schedule()}connect(){Ln(this._target)?this._listenSlots([this._target]):I(this._target).children&&(this._listenSlots(I(this._target).children),this._nativeChildrenObserver=new MutationObserver((t=>{this._processMutations(t)})),this._nativeChildrenObserver.observe(this._target,{childList:!0})),this._connected=!0}disconnect(){Ln(this._target)?this._unlistenSlots([this._target]):I(this._target).children&&(this._unlistenSlots(I(this._target).children),this._nativeChildrenObserver&&(this._nativeChildrenObserver.disconnect(),this._nativeChildrenObserver=null)),this._connected=!1}_schedule(){this._scheduled||(this._scheduled=!0,h.run((()=>this.flush())))}_processMutations(t){this._processSlotMutations(t),this.flush()}_processSlotMutations(t){if(t)for(let e=0;e<t.length;e++){let n=t[e];n.addedNodes&&this._listenSlots(n.addedNodes),n.removedNodes&&this._unlistenSlots(n.removedNodes)}}flush(){if(!this._connected)return!1;this._nativeChildrenObserver?this._processSlotMutations(this._nativeChildrenObserver.takeRecords()):this._shadyChildrenObserver&&this._processSlotMutations(this._shadyChildrenObserver.takeRecords()),this._scheduled=!1;let t={target:this._target,addedNodes:[],removedNodes:[]},e=this.constructor.getFlattenedNodes(this._target),n=In(e,this._effectiveNodes);for(let e,r=0;r<n.length&&(e=n[r]);r++)for(let n,r=0;r<e.removed.length&&(n=e.removed[r]);r++)t.removedNodes.push(n);for(let r,s=0;s<n.length&&(r=n[s]);s++)for(let n=r.index;n<r.index+r.addedCount;n++)t.addedNodes.push(e[n]);this._effectiveNodes=e;let r=!1;return(t.addedNodes.length||t.removedNodes.length)&&(r=!0,this.callback.call(this._target,t)),r}_listenSlots(t){for(let e=0;e<t.length;e++){let n=t[e];Ln(n)&&n.addEventListener("slotchange",this._boundSchedule)}}_unlistenSlots(t){for(let e=0;e<t.length;e++){let n=t[e];Ln(n)&&n.removeEventListener("slotchange",this._boundSchedule)}}};const Dn=Element.prototype,Rn=Dn.matches||Dn.matchesSelector||Dn.mozMatchesSelector||Dn.msMatchesSelector||Dn.oMatchesSelector||Dn.webkitMatchesSelector,Fn=function(t,e){return Rn.call(t,e)};class Hn{constructor(t){this.node=t}observeNodes(t){return new Mn(this.node,t)}unobserveNodes(t){t.disconnect()}notifyObserver(){}deepContains(t){if(I(this.node).contains(t))return!0;let e=t,n=t.ownerDocument;for(;e&&e!==n&&e!==this.node;)e=I(e).parentNode||I(e).host;return e===this.node}getOwnerRoot(){return I(this.node).getRootNode()}getDistributedNodes(){return"slot"===this.node.localName?I(this.node).assignedNodes({flatten:!0}):[]}getDestinationInsertionPoints(){let t=[],e=I(this.node).assignedSlot;for(;e;)t.push(e),e=I(e).assignedSlot;return t}importNode(t,e){let n=this.node instanceof Document?this.node:this.node.ownerDocument;return I(n).importNode(t,e)}getEffectiveChildNodes(){return Mn.getFlattenedNodes(this.node)}queryDistributedElements(t){let e=this.getEffectiveChildNodes(),n=[];for(let r,s=0,i=e.length;s<i&&(r=e[s]);s++)r.nodeType===Node.ELEMENT_NODE&&Fn(r,t)&&n.push(r);return n}get activeElement(){let t=this.node;return void 0!==t._activeElement?t._activeElement:t.activeElement}}function zn(t,e){for(let n=0;n<e.length;n++){let r=e[n];Object.defineProperty(t,r,{get:function(){return this.node[r]},configurable:!0})}}class jn{constructor(t){this.event=t}get rootTarget(){return this.path[0]}get localTarget(){return this.event.target}get path(){return this.event.composedPath()}}Hn.prototype.cloneNode,Hn.prototype.appendChild,Hn.prototype.insertBefore,Hn.prototype.removeChild,Hn.prototype.replaceChild,Hn.prototype.setAttribute,Hn.prototype.removeAttribute,Hn.prototype.querySelector,Hn.prototype.querySelectorAll,Hn.prototype.parentNode,Hn.prototype.firstChild,Hn.prototype.lastChild,Hn.prototype.nextSibling,Hn.prototype.previousSibling,Hn.prototype.firstElementChild,Hn.prototype.lastElementChild,Hn.prototype.nextElementSibling,Hn.prototype.previousElementSibling,Hn.prototype.childNodes,Hn.prototype.children,Hn.prototype.classList,Hn.prototype.textContent,Hn.prototype.innerHTML;let Bn=Hn;!function(t,e){for(let n=0;n<e.length;n++){let r=e[n];t[r]=function(){return this.node[r].apply(this.node,arguments)}}}(Hn.prototype,["cloneNode","appendChild","insertBefore","removeChild","replaceChild","setAttribute","removeAttribute","querySelector","querySelectorAll","attachShadow"]),zn(Hn.prototype,["parentNode","firstChild","lastChild","nextSibling","previousSibling","firstElementChild","lastElementChild","nextElementSibling","previousElementSibling","childNodes","children","classList","shadowRoot"]),function(t,e){for(let n=0;n<e.length;n++){let r=e[n];Object.defineProperty(t,r,{get:function(){return this.node[r]},set:function(t){this.node[r]=t},configurable:!0})}}(Hn.prototype,["textContent","innerHTML","className"]);const Jn=function(t){if((t=t||document)instanceof Bn)return t;if(t instanceof jn)return t;let e=t.__domApi;return e||(e=t instanceof Event?new jn(t):new Bn(t),t.__domApi=e),e};const qn=t=>{for(;t;){const e=Object.getOwnPropertyDescriptor(t,"observedAttributes");if(e)return e.get;t=Object.getPrototypeOf(t.prototype).constructor}return()=>[]};d((t=>{const e=Ye(t);let n=qn(e);return class extends e{constructor(){super(),this.__isUpgradeDisabled}static get observedAttributes(){return n.call(this).concat("disable-upgrade")}_initializeProperties(){this.hasAttribute("disable-upgrade")?this.__isUpgradeDisabled=!0:super._initializeProperties()}_enableProperties(){this.__isUpgradeDisabled||super._enableProperties()}_canApplyPropertyDefault(t){return super._canApplyPropertyDefault(t)&&!(this.__isUpgradeDisabled&&this._isPropertyPending(t))}attributeChangedCallback(t,e,n,r){"disable-upgrade"==t?this.__isUpgradeDisabled&&null==n&&(super._initializeProperties(),this.__isUpgradeDisabled=!1,I(this).isConnected&&super.connectedCallback()):super.attributeChangedCallback(t,e,n,r)}connectedCallback(){this.__isUpgradeDisabled||super.connectedCallback()}disconnectedCallback(){this.__isUpgradeDisabled||super.disconnectedCallback()}}}));const Yn=d((t=>{const e=Pn(Ye(t)),n=qn(e),r={x:"pan-x",y:"pan-y",none:"none",all:"auto"};class s extends e{constructor(){super(),this.isAttached,this.__boundListeners,this._debouncers,this.__isUpgradeDisabled,this.__needsAttributesAtConnected,this._legacyForceObservedAttributes}static get importMeta(){return this.prototype.importMeta}created(){}__attributeReaction(t,e,n){(this.__dataAttributes&&this.__dataAttributes[t]||"disable-upgrade"===t)&&this.attributeChangedCallback(t,e,n,null)}setAttribute(t,e){super.setAttribute(t,e)}removeAttribute(t){super.removeAttribute(t)}static get observedAttributes(){return n.call(this).concat("disable-upgrade")}_enableProperties(){this.__isUpgradeDisabled||super._enableProperties()}_canApplyPropertyDefault(t){return super._canApplyPropertyDefault(t)&&!(this.__isUpgradeDisabled&&this._isPropertyPending(t))}connectedCallback(){this.__needsAttributesAtConnected&&this._takeAttributes(),this.__isUpgradeDisabled||(super.connectedCallback(),this.isAttached=!0,this.attached())}attached(){}disconnectedCallback(){this.__isUpgradeDisabled||(super.disconnectedCallback(),this.isAttached=!1,this.detached())}detached(){}attributeChangedCallback(t,e,n,r){e!==n&&("disable-upgrade"==t?this.__isUpgradeDisabled&&null==n&&(this._initializeProperties(),this.__isUpgradeDisabled=!1,I(this).isConnected&&this.connectedCallback()):(super.attributeChangedCallback(t,e,n,r),this.attributeChanged(t,e,n)))}attributeChanged(t,e,n){}_initializeProperties(){if(x&&this.hasAttribute("disable-upgrade"))this.__isUpgradeDisabled=!0;else{let t=Object.getPrototypeOf(this);t.hasOwnProperty(JSCompiler_renameProperty("__hasRegisterFinished",t))||(this._registered(),t.__hasRegisterFinished=!0),super._initializeProperties(),this.root=this,this.created(),this._applyListeners()}}_takeAttributes(){const t=this.attributes;for(let e=0,n=t.length;e<n;e++){const n=t[e];this.__attributeReaction(n.name,null,n.value)}}_registered(){}ready(){this._ensureAttributes(),super.ready()}_ensureAttributes(){}_applyListeners(){}serialize(t){return this._serializeValue(t)}deserialize(t,e){return this._deserializeValue(t,e)}reflectPropertyToAttribute(t,e,n){this._propertyToAttribute(t,e,n)}serializeValueToAttribute(t,e,n){this._valueToNodeAttribute(n||this,t,e)}extend(t,e){if(!t||!e)return t||e;let n=Object.getOwnPropertyNames(e);for(let r,s=0;s<n.length&&(r=n[s]);s++){let n=Object.getOwnPropertyDescriptor(e,r);n&&Object.defineProperty(t,r,n)}return t}mixin(t,e){for(let n in e)t[n]=e[n];return t}chainObject(t,e){return t&&e&&t!==e&&(t.__proto__=e),t}instanceTemplate(t){let e=this.constructor._contentForTemplate(t);return document.importNode(e,!0)}fire(t,e,n){n=n||{},e=null==e?{}:e;let r=new Event(t,{bubbles:void 0===n.bubbles||n.bubbles,cancelable:Boolean(n.cancelable),composed:void 0===n.composed||n.composed});r.detail=e;let s=n.node||this;return I(s).dispatchEvent(r),r}listen(t,e,n){t=t||this;let r=this.__boundListeners||(this.__boundListeners=new WeakMap),s=r.get(t);s||(s={},r.set(t,s));let i=e+n;s[i]||(s[i]=this._addMethodEventListenerToNode(t,e,n,this))}unlisten(t,e,n){t=t||this;let r=this.__boundListeners&&this.__boundListeners.get(t),s=e+n,i=r&&r[s];i&&(this._removeEventListenerFromNode(t,e,i),r[s]=null)}setScrollDirection(t,e){lt(e||this,r[t]||"auto")}$$(t){return this.root.querySelector(t)}get domHost(){let t=I(this).getRootNode();return t instanceof DocumentFragment?t.host:t}distributeContent(){Jn(this)}getEffectiveChildNodes(){return Jn(this).getEffectiveChildNodes()}queryDistributedElements(t){return Jn(this).queryDistributedElements(t)}getEffectiveChildren(){return this.getEffectiveChildNodes().filter((function(t){return t.nodeType===Node.ELEMENT_NODE}))}getEffectiveTextContent(){let t=this.getEffectiveChildNodes(),e=[];for(let n,r=0;n=t[r];r++)n.nodeType!==Node.COMMENT_NODE&&e.push(n.textContent);return e.join("")}queryEffectiveChildren(t){let e=this.queryDistributedElements(t);return e&&e[0]}queryAllEffectiveChildren(t){return this.queryDistributedElements(t)}getContentChildNodes(t){let e=this.root.querySelector(t||"slot");return e?Jn(e).getDistributedNodes():[]}getContentChildren(t){return this.getContentChildNodes(t).filter((function(t){return t.nodeType===Node.ELEMENT_NODE}))}isLightDescendant(t){const e=this;return e!==t&&I(e).contains(t)&&I(e).getRootNode()===I(t).getRootNode()}isLocalDescendant(t){return this.root===I(t).getRootNode()}scopeSubtree(t,e=!1){return null}getComputedStyleValue(t){return false.getComputedStyleValue(this,t)}debounce(t,e,n){return this._debouncers=this._debouncers||{},this._debouncers[t]=_.debounce(this._debouncers[t],n>0?o.after(n):h,e.bind(this))}isDebouncerActive(t){this._debouncers=this._debouncers||{};let e=this._debouncers[t];return!(!e||!e.isActive())}flushDebouncer(t){this._debouncers=this._debouncers||{};let e=this._debouncers[t];e&&e.flush()}cancelDebouncer(t){this._debouncers=this._debouncers||{};let e=this._debouncers[t];e&&e.cancel()}async(t,e){return e>0?o.run(t.bind(this),e):~h.run(t.bind(this))}cancelAsync(t){t<0?h.cancel(~t):o.cancel(t)}create(t,e){let n=document.createElement(t);if(e)if(n.setProperties)n.setProperties(e);else for(let t in e)n[t]=e[t];return n}elementMatches(t,e){return Fn(e||this,t)}toggleAttribute(t,e){let n=this;return 3===arguments.length&&(n=arguments[2]),1==arguments.length&&(e=!n.hasAttribute(t)),e?(I(n).setAttribute(t,""),!0):(I(n).removeAttribute(t),!1)}toggleClass(t,e,n){n=n||this,1==arguments.length&&(e=!n.classList.contains(t)),e?n.classList.add(t):n.classList.remove(t)}transform(t,e){(e=e||this).style.webkitTransform=t,e.style.transform=t}translate3d(t,e,n,r){r=r||this,this.transform("translate3d("+t+","+e+","+n+")",r)}arrayDelete(t,e){let n;if(Array.isArray(t)){if(n=t.indexOf(e),n>=0)return t.splice(n,1)}else{if(n=zt(this,t).indexOf(e),n>=0)return this.splice(t,n,1)}return null}_logger(t,e){switch(Array.isArray(e)&&1===e.length&&Array.isArray(e[0])&&(e=e[0]),t){case"log":case"warn":case"error":console[t](...e)}}_log(...t){this._logger("log",t)}_warn(...t){this._logger("warn",t)}_error(...t){this._logger("error",t)}_logf(t,...e){return["[%s::%s]",this.is,t,...e]}}return s.prototype.is="",s})),Un={attached:!0,detached:!0,ready:!0,created:!0,beforeRegister:!0,registered:!0,attributeChanged:!0,listeners:!0,hostAttributes:!0},$n={attached:!0,detached:!0,ready:!0,created:!0,beforeRegister:!0,registered:!0,attributeChanged:!0,behaviors:!0,_noAccessors:!0},Vn=Object.assign({listeners:!0,hostAttributes:!0,properties:!0,observers:!0},$n);function Xn(t,e){return Qn({},Yn(e),t)}function Gn(t,e,n,r){!function(t,e,n){const r=t._noAccessors,s=Object.getOwnPropertyNames(t);for(let i=0;i<s.length;i++){let o=s[i];if(!(o in n))if(r)e[o]=t[o];else{let n=Object.getOwnPropertyDescriptor(t,o);n&&(n.configurable=!0,Object.defineProperty(e,o,n))}}}(e,t,r);for(let t in Un)e[t]&&(n[t]=n[t]||[],n[t].push(e[t]))}function Wn(t,e,n){e=e||[];for(let r=t.length-1;r>=0;r--){let s=t[r];s?Array.isArray(s)?Wn(s,e):e.indexOf(s)<0&&(!n||n.indexOf(s)<0)&&e.unshift(s):console.warn("behavior is null, check for missing or 404 import")}return e}function Zn(t,e){for(const n in e){const r=t[n],s=e[n];t[n]=!("value"in s)&&r&&"value"in r?Object.assign({value:r.value},s):s}}const Kn=Yn(HTMLElement);function Qn(t,e,n){let r;const s={};class i extends e{static _finalizeClass(){if(this.hasOwnProperty(JSCompiler_renameProperty("generatedFrom",this))){if(r)for(let t,e=0;e<r.length;e++)t=r[e],t.properties&&this.createProperties(t.properties),t.observers&&this.createObservers(t.observers,t.properties);t.properties&&this.createProperties(t.properties),t.observers&&this.createObservers(t.observers,t.properties),this._prepareTemplate()}else e._finalizeClass.call(this)}static get properties(){const e={};if(r)for(let t=0;t<r.length;t++)Zn(e,r[t].properties);return Zn(e,t.properties),e}static get observers(){let e=[];if(r)for(let t,n=0;n<r.length;n++)t=r[n],t.observers&&(e=e.concat(t.observers));return t.observers&&(e=e.concat(t.observers)),e}created(){super.created();const t=s.created;if(t)for(let e=0;e<t.length;e++)t[e].call(this)}_registered(){const t=i.prototype;if(!t.hasOwnProperty(JSCompiler_renameProperty("__hasRegisterFinished",t))){t.__hasRegisterFinished=!0,super._registered(),x&&o(t);const e=Object.getPrototypeOf(this);let n=s.beforeRegister;if(n)for(let t=0;t<n.length;t++)n[t].call(e);if(n=s.registered,n)for(let t=0;t<n.length;t++)n[t].call(e)}}_applyListeners(){super._applyListeners();const t=s.listeners;if(t)for(let e=0;e<t.length;e++){const n=t[e];if(n)for(let t in n)this._addMethodEventListenerToNode(this,t,n[t])}}_ensureAttributes(){const t=s.hostAttributes;if(t)for(let e=t.length-1;e>=0;e--){const n=t[e];for(let t in n)this._ensureAttribute(t,n[t])}super._ensureAttributes()}ready(){super.ready();let t=s.ready;if(t)for(let e=0;e<t.length;e++)t[e].call(this)}attached(){super.attached();let t=s.attached;if(t)for(let e=0;e<t.length;e++)t[e].call(this)}detached(){super.detached();let t=s.detached;if(t)for(let e=0;e<t.length;e++)t[e].call(this)}attributeChanged(t,e,n){super.attributeChanged();let r=s.attributeChanged;if(r)for(let s=0;s<r.length;s++)r[s].call(this,t,e,n)}}if(n){Array.isArray(n)||(n=[n]);let t=e.prototype.behaviors;r=Wn(n,null,t),i.prototype.behaviors=t?t.concat(n):r}const o=e=>{r&&function(t,e,n){for(let r=0;r<e.length;r++)Gn(t,e[r],n,Vn)}(e,r,s),Gn(e,t,s,$n)};return x||o(i.prototype),i.generatedFrom=t,i}let tr;tr=Qe._mutablePropertyChange;const er={properties:{mutableData:Boolean},_shouldPropertyChange(t,e,n){return tr(this,t,e,n,this.mutableData)}},nr=function(t){let e;return e="function"==typeof t?t:nr.Class(t),t._legacyForceObservedAttributes&&(e.prototype._legacyForceObservedAttributes=t._legacyForceObservedAttributes),customElements.define(e.is,e),e};nr.Class=function(t,e){t||console.warn("Polymer.Class requires `info` argument");let n=e?e(Kn):Kn;return n=Qn(t,n,t.behaviors),n.is=n.prototype.is=t.is,n};const rr={templatize(t,e){this._templatizerTemplate=t,this.ctor=pn(t,this,{mutableData:Boolean(e),parentModel:this._parentModel,instanceProps:this._instanceProps,forwardHostProp:this._forwardHostPropV2,notifyInstanceProp:this._notifyInstancePropV2})},stamp(t){return new this.ctor(t)},modelForElement(t){return fn(this._templatizerTemplate,t)}},sr=Pn(tn(Be(HTMLElement)));customElements.define("dom-bind",class extends sr{static get observedAttributes(){return["mutable-data"]}constructor(){if(super(),A)throw new Error("strictTemplatePolicy: dom-bind not allowed");this.root=null,this.$=null,this.__children=null}attributeChangedCallback(t,e,n,r){this.mutableData=!0}connectedCallback(){this.style.display="none",this.render()}disconnectedCallback(){this.__removeChildren()}__insertChildren(){I(I(this).parentNode).insertBefore(this.root,this)}__removeChildren(){if(this.__children)for(let t=0;t<this.__children.length;t++)this.root.appendChild(this.__children[t])}render(){let t;if(!this.__children){if(t=t||this.querySelector("template"),!t){let e=new MutationObserver((()=>{if(t=this.querySelector("template"),!t)throw new Error("dom-bind requires a <template> child");e.disconnect(),this.render()}));return void e.observe(this,{childList:!0})}this.root=this._stampTemplate(t),this.$=this.root.$,this.__children=[];for(let t=this.root.firstChild;t;t=t.nextSibling)this.__children[this.__children.length]=t;this._enableProperties()}this.__insertChildren(),this.dispatchEvent(new CustomEvent("dom-change",{bubbles:!0,composed:!0}))}});let ir=d((t=>{let e=Ye(t);return class extends e{static get properties(){return{items:{type:Array},multi:{type:Boolean,value:!1},selected:{type:Object,notify:!0},selectedItem:{type:Object,notify:!0},toggle:{type:Boolean,value:!1}}}static get observers(){return["__updateSelection(multi, items.*)"]}constructor(){super(),this.__lastItems=null,this.__lastMulti=null,this.__selectedMap=null}__updateSelection(t,e){let n=e.path;if(n==JSCompiler_renameProperty("items",this)){let n=e.base||[],r=this.__lastItems;if(t!==this.__lastMulti&&this.clearSelection(),r){let t=In(n,r);this.__applySplices(t)}this.__lastItems=n,this.__lastMulti=t}else if(e.path==JSCompiler_renameProperty("items",this)+".splices")this.__applySplices(e.value.indexSplices);else{let t=n.slice((JSCompiler_renameProperty("items",this)+".").length),e=parseInt(t,10);t.indexOf(".")<0&&t==e&&this.__deselectChangedIdx(e)}}__applySplices(t){let e=this.__selectedMap;for(let n=0;n<t.length;n++){let r=t[n];e.forEach(((t,n)=>{t<r.index||(t>=r.index+r.removed.length?e.set(n,t+r.addedCount-r.removed.length):e.set(n,-1))}));for(let t=0;t<r.addedCount;t++){let n=r.index+t;e.has(this.items[n])&&e.set(this.items[n],n)}}this.__updateLinks();let n=0;e.forEach(((t,r)=>{t<0?(this.multi?this.splice(JSCompiler_renameProperty("selected",this),n,1):this.selected=this.selectedItem=null,e.delete(r)):n++}))}__updateLinks(){if(this.__dataLinkedPaths={},this.multi){let t=0;this.__selectedMap.forEach((e=>{e>=0&&this.linkPaths(`${JSCompiler_renameProperty("items",this)}.${e}`,`${JSCompiler_renameProperty("selected",this)}.${t++}`)}))}else this.__selectedMap.forEach((t=>{this.linkPaths(JSCompiler_renameProperty("selected",this),`${JSCompiler_renameProperty("items",this)}.${t}`),this.linkPaths(JSCompiler_renameProperty("selectedItem",this),`${JSCompiler_renameProperty("items",this)}.${t}`)}))}clearSelection(){this.__dataLinkedPaths={},this.__selectedMap=new Map,this.selected=this.multi?[]:null,this.selectedItem=null}isSelected(t){return this.__selectedMap.has(t)}isIndexSelected(t){return this.isSelected(this.items[t])}__deselectChangedIdx(t){let e=this.__selectedIndexForItemIndex(t);if(e>=0){let t=0;this.__selectedMap.forEach(((n,r)=>{e==t++&&this.deselect(r)}))}}__selectedIndexForItemIndex(t){let e=this.__dataLinkedPaths[`${JSCompiler_renameProperty("items",this)}.${t}`];if(e)return parseInt(e.slice((JSCompiler_renameProperty("selected",this)+".").length),10)}deselect(t){let e=this.__selectedMap.get(t);if(e>=0){let n;this.__selectedMap.delete(t),this.multi&&(n=this.__selectedIndexForItemIndex(e)),this.__updateLinks(),this.multi?this.splice(JSCompiler_renameProperty("selected",this),n,1):this.selected=this.selectedItem=null}}deselectIndex(t){this.deselect(this.items[t])}select(t){this.selectIndex(this.items.indexOf(t))}selectIndex(t){let e=this.items[t];this.isSelected(e)?this.toggle&&this.deselectIndex(t):(this.multi||this.__selectedMap.clear(),this.__selectedMap.set(e,t),this.__updateLinks(),this.multi?this.push(JSCompiler_renameProperty("selected",this),e):this.selected=this.selectedItem=e)}}}))(We);class or extends ir{static get is(){return"array-selector"}static get template(){return null}}customElements.define(or.is,or);class ar extends HTMLElement{constructor(){super(),this._style=null}getStyle(){if(this._style)return this._style;const t=this.querySelector("style");if(!t)return null;this._style=t;const e=t.getAttribute("include");return e&&(t.removeAttribute("include"),t.textContent=function(t){let e=t.trim().split(/\s+/),n="";for(let t=0;t<e.length;t++)n+=St(e[t]);return n}(e)+t.textContent),this.ownerDocument!==window.document&&window.document.head.appendChild(this),this._style}}window.customElements.define("custom-style",ar);const lr=Yn(HTMLElement).prototype;export{lr as Base,_ as Debouncer,yn as DomIf,bn as DomRepeat,Mn as FlattenedNodesObserver,er as OptionalMutableDataBehavior,nr as Polymer,We as PolymerElement,an as TemplateInstanceBase,rr as Templatizer,An as afterNextRender,a as animationFrame,On as beforeNextRender,In as calculateSplices,Yt as dashToCamelCase,d as dedupingMixin,Jn as dom,p as enqueueDebouncer,Ze as flush,gt as gestures,zt as get,Xe as html,l as idlePeriod,Rt as matches,h as microTask,Xn as mixinBehaviors,pn as templatize,o as timeOut,Dt as translate,w as useShadow};
\ No newline at end of file
+window.JSCompiler_renameProperty=function(t,e){return t};let t=0,e=0,n=[],r=0,s=!1,i=document.createTextNode("");new window.MutationObserver((function(){s=!1;const t=n.length;for(let e=0;e<t;e++){let t=n[e];if(t)try{t()}catch(t){setTimeout((()=>{throw t}))}}n.splice(0,t),e+=t})).observe(i,{characterData:!0});const o={after:t=>({run:e=>window.setTimeout(e,t),cancel(t){window.clearTimeout(t)}}),run:(t,e)=>window.setTimeout(t,e),cancel(t){window.clearTimeout(t)}},a={run:t=>window.requestAnimationFrame(t),cancel(t){window.cancelAnimationFrame(t)}},l={run:t=>window.requestIdleCallback?window.requestIdleCallback(t):window.setTimeout(t,16),cancel(t){window.cancelIdleCallback?window.cancelIdleCallback(t):window.clearTimeout(t)}},h={run:e=>(s||(s=!0,i.textContent=r++),n.push(e),t++),cancel(t){const r=t-e;if(r>=0){if(!n[r])throw new Error("invalid async handle: "+t);n[r]=null}}};let c=0;const d=function(t){let e=t.__mixinApplications;e||(e=new WeakMap,t.__mixinApplications=e);let n=c++;return function(r){let s=r.__mixinSet;if(s&&s[n])return r;let i=e,o=i.get(r);if(!o){o=t(r),i.set(r,o);let e=Object.create(o.__mixinSet||s||null);e[n]=!0,o.__mixinSet=e}return o}};class _{constructor(){this._asyncModule=null,this._callback=null,this._timer=null}setConfig(t,e){this._asyncModule=t,this._callback=e,this._timer=this._asyncModule.run((()=>{this._timer=null,u.delete(this),this._callback()}))}cancel(){this.isActive()&&(this._cancelAsync(),u.delete(this))}_cancelAsync(){this.isActive()&&(this._asyncModule.cancel(this._timer),this._timer=null)}flush(){this.isActive()&&(this.cancel(),this._callback())}isActive(){return null!=this._timer}static debounce(t,e,n){return t instanceof _?t._cancelAsync():t=new _,t.setConfig(e,n),t}}let u=new Set;const p=function(t){u.add(t)},f=function(){const t=Boolean(u.size);return u.forEach((t=>{try{t.flush()}catch(t){setTimeout((()=>{throw t}))}})),t};let m,y,g=/(url\()([^)]*)(\))/g,b=/(^\/[^\/])|(^#)|(^[\w-\d]*:)/;function P(t,e){if(t&&b.test(t))return t;if("//"===t)return t;if(void 0===m){m=!1;try{const t=new URL("b","http://a");t.pathname="c%20d",m="http://a/c%20d"===t.href}catch(t){}}if(e||(e=document.baseURI||window.location.href),m)try{return new URL(t,e).href}catch(e){return t}return y||(y=document.implementation.createHTMLDocument("temp"),y.base=y.createElement("base"),y.head.appendChild(y.base),y.anchor=y.createElement("a"),y.body.appendChild(y.anchor)),y.base.href=e,y.anchor.href=t,y.anchor.href||t}function v(t,e){return t.replace(g,(function(t,n,r,s){return n+"'"+P(r.replace(/["']/g,""),e)+"'"+s}))}function C(t){return t.substring(0,t.lastIndexOf("/")+1)}const w=!0;Boolean(!0),"adoptedStyleSheets"in Document.prototype&&"replaceSync"in CSSStyleSheet.prototype&&(()=>{try{const t=new CSSStyleSheet;t.replaceSync("");const e=document.createElement("div");e.attachShadow({mode:"open"}),e.shadowRoot.adoptedStyleSheets=[t],e.shadowRoot.adoptedStyleSheets[0]}catch(t){return!1}})();let E=window.Polymer&&window.Polymer.rootPath||C(document.baseURI||window.location.href),T=window.Polymer&&window.Polymer.sanitizeDOMValue||void 0,O=window.Polymer&&window.Polymer.setPassiveTouchGestures||!1,A=window.Polymer&&window.Polymer.strictTemplatePolicy||!1,N=window.Polymer&&window.Polymer.allowTemplateFromDomModule||!1,x=(window.Polymer,!1),S=window.Polymer&&window.Polymer.orderedComputed||!1;const I=t=>t;let k="string"==typeof document.head.style.touchAction,L="__polymerGestures",M="__polymerGesturesHandled",D="__polymerGesturesTouchAction",R=["mousedown","mousemove","mouseup","click"],F=[0,1,4,2],H=function(){try{return 1===new MouseEvent("test",{buttons:1}).buttons}catch(t){return!1}}();function z(t){return R.indexOf(t)>-1}let j=!1;function B(t){if(!z(t)&&"touchend"!==t)return k&&j&&O?{passive:!0}:void 0}!function(){try{let t=Object.defineProperty({},"passive",{get(){j=!0}});window.addEventListener("test",null,t),window.removeEventListener("test",null,t)}catch(t){}}();let J=navigator.userAgent.match(/iP(?:[oa]d|hone)|Android/);const q=[],Y={button:!0,input:!0,keygen:!0,meter:!0,output:!0,textarea:!0,progress:!0,select:!0},$={button:!0,command:!0,fieldset:!0,input:!0,keygen:!0,optgroup:!0,option:!0,select:!0,textarea:!0};function U(t){let e=Array.prototype.slice.call(t.labels||[]);if(!e.length){e=[];try{let n=t.getRootNode();if(t.id){let r=n.querySelectorAll(`label[for = '${t.id}']`);for(let t=0;t<r.length;t++)e.push(r[t])}}catch(t){}}return e}let V=function(t){let e=t.sourceCapabilities;var n;if((!e||e.firesTouchEvents)&&(t[M]={skip:!0},"click"===t.type)){let e=!1,r=Q(t);for(let t=0;t<r.length;t++){if(r[t].nodeType===Node.ELEMENT_NODE)if("label"===r[t].localName)q.push(r[t]);else if(n=r[t],Y[n.localName]){let n=U(r[t]);for(let t=0;t<n.length;t++)e=e||q.indexOf(n[t])>-1}if(r[t]===W.mouse.target)return}if(e)return;t.preventDefault(),t.stopPropagation()}};function X(t){let e=J?["click"]:R;for(let n,r=0;r<e.length;r++)n=e[r],t?(q.length=0,document.addEventListener(n,V,!0)):document.removeEventListener(n,V,!0)}function G(t){let e=t.type;if(!z(e))return!1;if("mousemove"===e){let e=void 0===t.buttons?1:t.buttons;return t instanceof window.MouseEvent&&!H&&(e=F[t.which]||0),Boolean(1&e)}return 0===(void 0===t.button?0:t.button)}let W={mouse:{target:null,mouseIgnoreJob:null},touch:{x:0,y:0,id:-1,scrollDecided:!1}};function Z(t,e,n){t.movefn=e,t.upfn=n,document.addEventListener("mousemove",e),document.addEventListener("mouseup",n)}function K(t){document.removeEventListener("mousemove",t.movefn),document.removeEventListener("mouseup",t.upfn),t.movefn=null,t.upfn=null}document.addEventListener("touchend",(function(t){W.mouse.mouseIgnoreJob||X(!0),W.mouse.target=Q(t)[0],W.mouse.mouseIgnoreJob=_.debounce(W.mouse.mouseIgnoreJob,o.after(2500),(function(){X(),W.mouse.target=null,W.mouse.mouseIgnoreJob=null}))}),!!j&&{passive:!0});const Q=t=>t.composedPath&&t.composedPath()||[],tt={},et=[];function nt(t,e){let n=document.elementFromPoint(t,e),r=n;for(;r&&r.shadowRoot;){let s=r;if(r=r.shadowRoot.elementFromPoint(t,e),s===r)break;r&&(n=r)}return n}function rt(t){const e=Q(t);return e.length>0?e[0]:t.target}function st(t){let e,n=t.type,r=t.currentTarget[L];if(!r)return;let s=r[n];if(s){if(!t[M]&&(t[M]={},"touch"===n.slice(0,5))){let e=t.changedTouches[0];if("touchstart"===n&&1===t.touches.length&&(W.touch.id=e.identifier),W.touch.id!==e.identifier)return;k||"touchstart"!==n&&"touchmove"!==n||function(t){let e=t.changedTouches[0],n=t.type;if("touchstart"===n)W.touch.x=e.clientX,W.touch.y=e.clientY,W.touch.scrollDecided=!1;else if("touchmove"===n){if(W.touch.scrollDecided)return;W.touch.scrollDecided=!0;let n=function(t){let e="auto",n=Q(t);for(let t,r=0;r<n.length;r++)if(t=n[r],t[D]){e=t[D];break}return e}(t),r=!1,s=Math.abs(W.touch.x-e.clientX),i=Math.abs(W.touch.y-e.clientY);t.cancelable&&("none"===n?r=!0:"pan-x"===n?r=i>s:"pan-y"===n&&(r=s>i)),r?t.preventDefault():ct("track")}}(t)}if(e=t[M],!e.skip){for(let n,r=0;r<et.length;r++)n=et[r],s[n.name]&&!e[n.name]&&n.flow&&n.flow.start.indexOf(t.type)>-1&&n.reset&&n.reset();for(let r,i=0;i<et.length;i++)r=et[i],s[r.name]&&!e[r.name]&&(e[r.name]=!0,r[n](t))}}}function it(t,e,n){return!!tt[e]&&(function(t,e,n){let r=tt[e],s=r.deps,i=r.name,o=t[L];o||(t[L]=o={});for(let e,n,r=0;r<s.length;r++)e=s[r],J&&z(e)&&"click"!==e||(n=o[e],n||(o[e]=n={_count:0}),0===n._count&&t.addEventListener(e,st,B(e)),n[i]=(n[i]||0)+1,n._count=(n._count||0)+1);t.addEventListener(e,n),r.touchAction&&lt(t,r.touchAction)}(t,e,n),!0)}function ot(t,e,n){return!!tt[e]&&(function(t,e,n){let r=tt[e],s=r.deps,i=r.name,o=t[L];if(o)for(let e,n,r=0;r<s.length;r++)e=s[r],n=o[e],n&&n[i]&&(n[i]=(n[i]||1)-1,n._count=(n._count||1)-1,0===n._count&&t.removeEventListener(e,st,B(e)));t.removeEventListener(e,n)}(t,e,n),!0)}function at(t){et.push(t);for(let e=0;e<t.emits.length;e++)tt[t.emits[e]]=t}function lt(t,e){k&&t instanceof HTMLElement&&h.run((()=>{t.style.touchAction=e})),t[D]=e}function ht(t,e,n){let r=new Event(e,{bubbles:!0,cancelable:!0,composed:!0});if(r.detail=n,I(t).dispatchEvent(r),r.defaultPrevented){let t=n.preventer||n.sourceEvent;t&&t.preventDefault&&t.preventDefault()}}function ct(t){let e=function(t){for(let e,n=0;n<et.length;n++){e=et[n];for(let n,r=0;r<e.emits.length;r++)if(n=e.emits[r],n===t)return e}return null}(t);e.info&&(e.info.prevent=!0)}function dt(t,e,n,r){e&&ht(e,t,{x:n.clientX,y:n.clientY,sourceEvent:n,preventer:r,prevent:function(t){return ct(t)}})}function _t(t,e,n){if(t.prevent)return!1;if(t.started)return!0;let r=Math.abs(t.x-e),s=Math.abs(t.y-n);return r>=5||s>=5}function ut(t,e,n){if(!e)return;let r,s=t.moves[t.moves.length-2],i=t.moves[t.moves.length-1],o=i.x-t.x,a=i.y-t.y,l=0;s&&(r=i.x-s.x,l=i.y-s.y),ht(e,"track",{state:t.state,x:n.clientX,y:n.clientY,dx:o,dy:a,ddx:r,ddy:l,sourceEvent:n,hover:function(){return nt(n.clientX,n.clientY)}})}function pt(t,e,n){let r=Math.abs(e.clientX-t.x),s=Math.abs(e.clientY-t.y),i=rt(n||e);!i||$[i.localName]&&i.hasAttribute("disabled")||(isNaN(r)||isNaN(s)||r<=25&&s<=25||function(t){if("click"===t.type){if(0===t.detail)return!0;let e=rt(t);if(!e.nodeType||e.nodeType!==Node.ELEMENT_NODE)return!0;let n=e.getBoundingClientRect(),r=t.pageX,s=t.pageY;return!(r>=n.left&&r<=n.right&&s>=n.top&&s<=n.bottom)}return!1}(e))&&(t.prevent||ht(i,"tap",{x:e.clientX,y:e.clientY,sourceEvent:e,preventer:n}))}at({name:"downup",deps:["mousedown","touchstart","touchend"],flow:{start:["mousedown","touchstart"],end:["mouseup","touchend"]},emits:["down","up"],info:{movefn:null,upfn:null},reset:function(){K(this.info)},mousedown:function(t){if(!G(t))return;let e=rt(t),n=this;Z(this.info,(function(t){G(t)||(dt("up",e,t),K(n.info))}),(function(t){G(t)&&dt("up",e,t),K(n.info)})),dt("down",e,t)},touchstart:function(t){dt("down",rt(t),t.changedTouches[0],t)},touchend:function(t){dt("up",rt(t),t.changedTouches[0],t)}}),at({name:"track",touchAction:"none",deps:["mousedown","touchstart","touchmove","touchend"],flow:{start:["mousedown","touchstart"],end:["mouseup","touchend"]},emits:["track"],info:{x:0,y:0,state:"start",started:!1,moves:[],addMove:function(t){this.moves.length>2&&this.moves.shift(),this.moves.push(t)},movefn:null,upfn:null,prevent:!1},reset:function(){this.info.state="start",this.info.started=!1,this.info.moves=[],this.info.x=0,this.info.y=0,this.info.prevent=!1,K(this.info)},mousedown:function(t){if(!G(t))return;let e=rt(t),n=this,r=function(t){let r=t.clientX,s=t.clientY;_t(n.info,r,s)&&(n.info.state=n.info.started?"mouseup"===t.type?"end":"track":"start","start"===n.info.state&&ct("tap"),n.info.addMove({x:r,y:s}),G(t)||(n.info.state="end",K(n.info)),e&&ut(n.info,e,t),n.info.started=!0)};Z(this.info,r,(function(t){n.info.started&&r(t),K(n.info)})),this.info.x=t.clientX,this.info.y=t.clientY},touchstart:function(t){let e=t.changedTouches[0];this.info.x=e.clientX,this.info.y=e.clientY},touchmove:function(t){let e=rt(t),n=t.changedTouches[0],r=n.clientX,s=n.clientY;_t(this.info,r,s)&&("start"===this.info.state&&ct("tap"),this.info.addMove({x:r,y:s}),ut(this.info,e,n),this.info.state="track",this.info.started=!0)},touchend:function(t){let e=rt(t),n=t.changedTouches[0];this.info.started&&(this.info.state="end",this.info.addMove({x:n.clientX,y:n.clientY}),ut(this.info,e,n))}}),at({name:"tap",deps:["mousedown","click","touchstart","touchend"],flow:{start:["mousedown","touchstart"],end:["click","touchend"]},emits:["tap"],info:{x:NaN,y:NaN,prevent:!1},reset:function(){this.info.x=NaN,this.info.y=NaN,this.info.prevent=!1},mousedown:function(t){G(t)&&(this.info.x=t.clientX,this.info.y=t.clientY)},click:function(t){G(t)&&pt(this.info,t)},touchstart:function(t){const e=t.changedTouches[0];this.info.x=e.clientX,this.info.y=e.clientY},touchend:function(t){pt(this.info,t.changedTouches[0],t)}});const ft=rt,mt=it,yt=ot;var gt=Object.freeze({__proto__:null,add:mt,addListener:it,deepTargetFind:nt,findOriginalTarget:ft,gestures:tt,prevent:ct,recognizers:et,register:at,remove:yt,removeListener:ot,resetMouseCanceller:function(){W.mouse.mouseIgnoreJob&&W.mouse.mouseIgnoreJob.flush()},setTouchAction:lt});let bt={},Pt={};function vt(t,e){bt[t]=Pt[t.toLowerCase()]=e}function Ct(t){return bt[t]||Pt[t.toLowerCase()]}class wt extends HTMLElement{static get observedAttributes(){return["id"]}static import(t,e){if(t){let n=Ct(t);return n&&e?n.querySelector(e):n}return null}attributeChangedCallback(t,e,n,r){e!==n&&this.register()}get assetpath(){if(!this.__assetpath){const t=this.ownerDocument,e=P(this.getAttribute("assetpath")||"",t.baseURI);this.__assetpath=C(e)}return this.__assetpath}register(t){if(t=t||this.id){if(A&&void 0!==Ct(t))throw vt(t,null),new Error(`strictTemplatePolicy: dom-module ${t} re-registered`);this.id=t,vt(t,this),(e=this).querySelector("style")&&console.warn("dom-module %s has style outside template",e.id)}var e}}wt.prototype.modules=bt,customElements.define("dom-module",wt);const Et="shady-unscoped";function Tt(t){return wt.import(t)}function Ot(t){const e=v((t.body?t.body:t).textContent,t.baseURI),n=document.createElement("style");return n.textContent=e,n}function At(t){const e=t.trim().split(/\s+/),n=[];for(let t=0;t<e.length;t++)n.push(...Nt(e[t]));return n}function Nt(t){const e=Tt(t);if(!e)return console.warn("Could not find style data in module named",t),[];if(void 0===e._styles){const t=[];t.push(...St(e));const n=e.querySelector("template");n&&t.push(...xt(n,e.assetpath)),e._styles=t}return e._styles}function xt(t,e){if(!t._styles){const n=[],r=t.content.querySelectorAll("style");for(let t=0;t<r.length;t++){let s=r[t],i=s.getAttribute("include");i&&n.push(...At(i).filter((function(t,e,n){return n.indexOf(t)===e}))),e&&(s.textContent=v(s.textContent,e)),n.push(s)}t._styles=n}return t._styles}function St(t){const e=[],n=t.querySelectorAll("link[rel=import][type~=css]");for(let t=0;t<n.length;t++){let r=n[t];if(r.import){const t=r.import,n=r.hasAttribute(Et);if(n&&!t._unscopedStyle){const e=Ot(t);e.setAttribute(Et,""),t._unscopedStyle=e}else t._style||(t._style=Ot(t));e.push(n?t._unscopedStyle:t._style)}}return e}function It(t){let e=Tt(t);if(e&&void 0===e._cssText){let t=function(t){let e="",n=St(t);for(let t=0;t<n.length;t++)e+=n[t].textContent;return e}(e),n=e.querySelector("template");n&&(t+=function(t,e){let n="";const r=xt(t,e);for(let t=0;t<r.length;t++){let e=r[t];e.parentNode&&e.parentNode.removeChild(e),n+=e.textContent}return n}(n,e.assetpath)),e._cssText=t||null}return e||console.warn("Could not find style data in module named",t),e&&e._cssText||""}function kt(t){return t.indexOf(".")>=0}function Lt(t){let e=t.indexOf(".");return-1===e?t:t.slice(0,e)}function Mt(t,e){return 0===t.indexOf(e+".")}function Dt(t,e){return 0===e.indexOf(t+".")}function Rt(t,e,n){return e+n.slice(t.length)}function Ft(t,e){return t===e||Mt(t,e)||Dt(t,e)}function Ht(t){if(Array.isArray(t)){let e=[];for(let n=0;n<t.length;n++){let r=t[n].toString().split(".");for(let t=0;t<r.length;t++)e.push(r[t])}return e.join(".")}return t}function zt(t){return Array.isArray(t)?Ht(t).split("."):t.toString().split(".")}function jt(t,e,n){let r=t,s=zt(e);for(let t=0;t<s.length;t++){if(!r)return;r=r[s[t]]}return n&&(n.path=s.join(".")),r}function Bt(t,e,n){let r=t,s=zt(e),i=s[s.length-1];if(s.length>1){for(let t=0;t<s.length-1;t++){if(r=r[s[t]],!r)return}r[i]=n}else r[e]=n;return s.join(".")}const Jt={},qt=/-[a-z]/g,Yt=/([A-Z])/g;function $t(t){return Jt[t]||(Jt[t]=t.indexOf("-")<0?t:t.replace(qt,(t=>t[1].toUpperCase())))}function Ut(t){return Jt[t]||(Jt[t]=t.replace(Yt,"-$1").toLowerCase())}const Vt=h,Xt=d((t=>class extends t{static createProperties(t){const e=this.prototype;for(let n in t)n in e||e._createPropertyAccessor(n)}static attributeNameForProperty(t){return t.toLowerCase()}static typeForProperty(t){}_createPropertyAccessor(t,e){this._addPropertyToAttributeMap(t),this.hasOwnProperty(JSCompiler_renameProperty("__dataHasAccessor",this))||(this.__dataHasAccessor=Object.assign({},this.__dataHasAccessor)),this.__dataHasAccessor[t]||(this.__dataHasAccessor[t]=!0,this._definePropertyAccessor(t,e))}_addPropertyToAttributeMap(t){this.hasOwnProperty(JSCompiler_renameProperty("__dataAttributes",this))||(this.__dataAttributes=Object.assign({},this.__dataAttributes));let e=this.__dataAttributes[t];return e||(e=this.constructor.attributeNameForProperty(t),this.__dataAttributes[e]=t),e}_definePropertyAccessor(t,e){Object.defineProperty(this,t,{get(){return this.__data[t]},set:e?function(){}:function(e){this._setPendingProperty(t,e,!0)&&this._invalidateProperties()}})}constructor(){super(),this.__dataEnabled=!1,this.__dataReady=!1,this.__dataInvalid=!1,this.__data={},this.__dataPending=null,this.__dataOld=null,this.__dataInstanceProps=null,this.__dataCounter=0,this.__serializing=!1,this._initializeProperties()}ready(){this.__dataReady=!0,this._flushProperties()}_initializeProperties(){for(let t in this.__dataHasAccessor)this.hasOwnProperty(t)&&(this.__dataInstanceProps=this.__dataInstanceProps||{},this.__dataInstanceProps[t]=this[t],delete this[t])}_initializeInstanceProperties(t){Object.assign(this,t)}_setProperty(t,e){this._setPendingProperty(t,e)&&this._invalidateProperties()}_getProperty(t){return this.__data[t]}_setPendingProperty(t,e,n){let r=this.__data[t],s=this._shouldPropertyChange(t,e,r);return s&&(this.__dataPending||(this.__dataPending={},this.__dataOld={}),this.__dataOld&&!(t in this.__dataOld)&&(this.__dataOld[t]=r),this.__data[t]=e,this.__dataPending[t]=e),s}_isPropertyPending(t){return!(!this.__dataPending||!this.__dataPending.hasOwnProperty(t))}_invalidateProperties(){!this.__dataInvalid&&this.__dataReady&&(this.__dataInvalid=!0,Vt.run((()=>{this.__dataInvalid&&(this.__dataInvalid=!1,this._flushProperties())})))}_enableProperties(){this.__dataEnabled||(this.__dataEnabled=!0,this.__dataInstanceProps&&(this._initializeInstanceProperties(this.__dataInstanceProps),this.__dataInstanceProps=null),this.ready())}_flushProperties(){this.__dataCounter++;const t=this.__data,e=this.__dataPending,n=this.__dataOld;this._shouldPropertiesChange(t,e,n)&&(this.__dataPending=null,this.__dataOld=null,this._propertiesChanged(t,e,n)),this.__dataCounter--}_shouldPropertiesChange(t,e,n){return Boolean(e)}_propertiesChanged(t,e,n){}_shouldPropertyChange(t,e,n){return n!==e&&(n==n||e==e)}attributeChangedCallback(t,e,n,r){e!==n&&this._attributeToProperty(t,n),super.attributeChangedCallback&&super.attributeChangedCallback(t,e,n,r)}_attributeToProperty(t,e,n){if(!this.__serializing){const r=this.__dataAttributes,s=r&&r[t]||t;this[s]=this._deserializeValue(e,n||this.constructor.typeForProperty(s))}}_propertyToAttribute(t,e,n){this.__serializing=!0,n=arguments.length<3?this[t]:n,this._valueToNodeAttribute(this,n,e||this.constructor.attributeNameForProperty(t)),this.__serializing=!1}_valueToNodeAttribute(t,e,n){const r=this._serializeValue(e);"class"!==n&&"name"!==n&&"slot"!==n||(t=I(t)),void 0===r?t.removeAttribute(n):t.setAttribute(n,r)}_serializeValue(t){return"boolean"==typeof t?t?"":void 0:null!=t?t.toString():void 0}_deserializeValue(t,e){switch(e){case Boolean:return null!==t;case Number:return Number(t);default:return t}}})),Gt={};let Wt=HTMLElement.prototype;for(;Wt;){let t=Object.getOwnPropertyNames(Wt);for(let e=0;e<t.length;e++)Gt[t[e]]=!0;Wt=Object.getPrototypeOf(Wt)}const Zt=window.trustedTypes?t=>trustedTypes.isHTML(t)||trustedTypes.isScript(t)||trustedTypes.isScriptURL(t):()=>!1;const Kt=d((t=>{const e=Xt(t);return class extends e{static createPropertiesForAttributes(){let t=this.observedAttributes;for(let e=0;e<t.length;e++)this.prototype._createPropertyAccessor($t(t[e]))}static attributeNameForProperty(t){return Ut(t)}_initializeProperties(){this.__dataProto&&(this._initializeProtoProperties(this.__dataProto),this.__dataProto=null),super._initializeProperties()}_initializeProtoProperties(t){for(let e in t)this._setProperty(e,t[e])}_ensureAttribute(t,e){const n=this;n.hasAttribute(t)||this._valueToNodeAttribute(n,e,t)}_serializeValue(t){if("object"==typeof t){if(t instanceof Date)return t.toString();if(t){if(Zt(t))return t;try{return JSON.stringify(t)}catch(t){return""}}}return super._serializeValue(t)}_deserializeValue(t,e){let n;switch(e){case Object:try{n=JSON.parse(t)}catch(e){n=t}break;case Array:try{n=JSON.parse(t)}catch(e){n=null,console.warn(`Polymer::Attributes: couldn't decode Array as JSON: ${t}`)}break;case Date:n=isNaN(t)?String(t):Number(t),n=new Date(n);break;default:n=super._deserializeValue(t,e)}return n}_definePropertyAccessor(t,e){!function(t,e){if(!Gt[e]){let n=t[e];void 0!==n&&(t.__data?t._setPendingProperty(e,n):(t.__dataProto?t.hasOwnProperty(JSCompiler_renameProperty("__dataProto",t))||(t.__dataProto=Object.create(t.__dataProto)):t.__dataProto={},t.__dataProto[e]=n))}}(this,t),super._definePropertyAccessor(t,e)}_hasAccessor(t){return this.__dataHasAccessor&&this.__dataHasAccessor[t]}_isPropertyPending(t){return Boolean(this.__dataPending&&t in this.__dataPending)}}})),Qt={"dom-if":!0,"dom-repeat":!0};let te=!1,ee=!1;function ne(t){(function(){if(!te){te=!0;const t=document.createElement("textarea");t.placeholder="a",ee=t.placeholder===t.textContent}return ee})()&&"textarea"===t.localName&&t.placeholder&&t.placeholder===t.textContent&&(t.textContent=null)}const re=(()=>{const t=window.trustedTypes&&window.trustedTypes.createPolicy("polymer-template-event-attribute-policy",{createScript:t=>t});return(e,n,r)=>{const s=n.getAttribute(r);t&&r.startsWith("on-")?e.setAttribute(r,t.createScript(s,r)):e.setAttribute(r,s)}})();function se(t){let e=t.getAttribute("is");if(e&&Qt[e]){let n=t;for(n.removeAttribute("is"),t=n.ownerDocument.createElement(e),n.parentNode.replaceChild(t,n),t.appendChild(n);n.attributes.length;){const{name:e}=n.attributes[0];re(t,n,e),n.removeAttribute(e)}}return t}function ie(t,e){let n=e.parentInfo&&ie(t,e.parentInfo);if(!n)return t;for(let t=n.firstChild,r=0;t;t=t.nextSibling)if(e.parentIndex===r++)return t}function oe(t,e,n,r){r.id&&(e[r.id]=n)}function ae(t,e,n){if(n.events&&n.events.length)for(let r,s=0,i=n.events;s<i.length&&(r=i[s]);s++)t._addMethodEventListenerToNode(e,r.name,r.value,t)}function le(t,e,n,r){n.templateInfo&&(e._templateInfo=n.templateInfo,e._parentTemplateInfo=r)}const he=d((t=>class extends t{static _parseTemplate(t,e){if(!t._templateInfo){let n=t._templateInfo={};n.nodeInfoList=[],n.nestedTemplate=Boolean(e),n.stripWhiteSpace=!0,this._parseTemplateContent(t,n,{parent:null})}return t._templateInfo}static _parseTemplateContent(t,e,n){return this._parseTemplateNode(t.content,e,n)}static _parseTemplateNode(t,e,n){let r=!1,s=t;return"template"!=s.localName||s.hasAttribute("preserve-content")?"slot"===s.localName&&(e.hasInsertionPoint=!0):r=this._parseTemplateNestedTemplate(s,e,n)||r,ne(s),s.firstChild&&this._parseTemplateChildNodes(s,e,n),s.hasAttributes&&s.hasAttributes()&&(r=this._parseTemplateNodeAttributes(s,e,n)||r),r||n.noted}static _parseTemplateChildNodes(t,e,n){if("script"!==t.localName&&"style"!==t.localName)for(let r,s=t.firstChild,i=0;s;s=r){if("template"==s.localName&&(s=se(s)),r=s.nextSibling,s.nodeType===Node.TEXT_NODE){let n=r;for(;n&&n.nodeType===Node.TEXT_NODE;)s.textContent+=n.textContent,r=n.nextSibling,t.removeChild(n),n=r;if(e.stripWhiteSpace&&!s.textContent.trim()){t.removeChild(s);continue}}let o={parentIndex:i,parentInfo:n};this._parseTemplateNode(s,e,o)&&(o.infoIndex=e.nodeInfoList.push(o)-1),s.parentNode&&i++}}static _parseTemplateNestedTemplate(t,e,n){let r=t,s=this._parseTemplate(r,e);return(s.content=r.content.ownerDocument.createDocumentFragment()).appendChild(r.content),n.templateInfo=s,!0}static _parseTemplateNodeAttributes(t,e,n){let r=!1,s=Array.from(t.attributes);for(let i,o=s.length-1;i=s[o];o--)r=this._parseTemplateNodeAttribute(t,e,n,i.name,i.value)||r;return r}static _parseTemplateNodeAttribute(t,e,n,r,s){return"on-"===r.slice(0,3)?(t.removeAttribute(r),n.events=n.events||[],n.events.push({name:r.slice(3),value:s}),!0):"id"===r&&(n.id=s,!0)}static _contentForTemplate(t){let e=t._templateInfo;return e&&e.content||t.content}_stampTemplate(t,e){t&&!t.content&&window.HTMLTemplateElement&&HTMLTemplateElement.decorate&&HTMLTemplateElement.decorate(t);let n=(e=e||this.constructor._parseTemplate(t)).nodeInfoList,r=e.content||t.content,s=document.importNode(r,!0);s.__noInsertionPoint=!e.hasInsertionPoint;let i=s.nodeList=new Array(n.length);s.$={};for(let t,r=0,o=n.length;r<o&&(t=n[r]);r++){let n=i[r]=ie(s,t);oe(0,s.$,n,t),le(0,n,t,e),ae(this,n,t)}return s}_addMethodEventListenerToNode(t,e,n,r){let s=function(t,e,n){return t=t._methodHost||t,function(e){t[n]?t[n](e,e.detail):console.warn("listener method `"+n+"` not defined")}}(r=r||t,0,n);return this._addEventListenerToNode(t,e,s),s}_addEventListenerToNode(t,e,n){t.addEventListener(e,n)}_removeEventListenerFromNode(t,e,n){t.removeEventListener(e,n)}}));let ce=0;const de=[],_e={COMPUTE:"__computeEffects",REFLECT:"__reflectEffects",NOTIFY:"__notifyEffects",PROPAGATE:"__propagateEffects",OBSERVE:"__observeEffects",READ_ONLY:"__readOnly"},ue="__computeInfo",pe=/[A-Z]/;function fe(t,e,n){let r=t[e];if(r){if(!t.hasOwnProperty(e)&&(r=t[e]=Object.create(t[e]),n))for(let t in r){let e=r[t],n=r[t]=Array(e.length);for(let t=0;t<e.length;t++)n[t]=e[t]}}else r=t[e]={};return r}function me(t,e,n,r,s,i){if(e){let o=!1;const a=ce++;for(let l in n){let h=e[s?Lt(l):l];if(h)for(let e,c=0,d=h.length;c<d&&(e=h[c]);c++)e.info&&e.info.lastRun===a||s&&!ge(l,e.trigger)||(e.info&&(e.info.lastRun=a),e.fn(t,l,n,r,e.info,s,i),o=!0)}return o}return!1}function ye(t,e,n,r,s,i,o,a){let l=!1,h=e[o?Lt(r):r];if(h)for(let e,c=0,d=h.length;c<d&&(e=h[c]);c++)e.info&&e.info.lastRun===n||o&&!ge(r,e.trigger)||(e.info&&(e.info.lastRun=n),e.fn(t,r,s,i,e.info,o,a),l=!0);return l}function ge(t,e){if(e){let n=e.name;return n==t||!(!e.structured||!Mt(n,t))||!(!e.wildcard||!Dt(n,t))}return!0}function be(t,e,n,r,s){let i="string"==typeof s.method?t[s.method]:s.method,o=s.property;i?i.call(t,t.__data[o],r[o]):s.dynamicFn||console.warn("observer method `"+s.method+"` not defined")}function Pe(t,e,n){let r=Lt(e);if(r!==e){return ve(t,Ut(r)+"-changed",n[e],e),!0}return!1}function ve(t,e,n,r){let s={value:n,queueProperty:!0};r&&(s.path=r),I(t).dispatchEvent(new CustomEvent(e,{detail:s}))}function Ce(t,e,n,r,s,i){let o=(i?Lt(e):e)!=e?e:null,a=o?jt(t,o):t.__data[e];o&&void 0===a&&(a=n[e]),ve(t,s.eventName,a,o)}function we(t,e,n,r,s){let i=t.__data[e];T&&(i=T(i,s.attrName,"attribute",t)),t._propertyToAttribute(e,s.attrName,i)}function Ee(t,e,n,r){let s=t[_e.COMPUTE];if(s)if(S){ce++;const i=function(t){let e=t.constructor.__orderedComputedDeps;if(!e){e=new Map;const n=t[_e.COMPUTE];let r,{counts:s,ready:i,total:o}=function(t){const e=t[ue],n={},r=t[_e.COMPUTE],s=[];let i=0;for(let t in e){const r=e[t];i+=n[t]=r.args.filter((t=>!t.literal)).length+(r.dynamicFn?1:0)}for(let t in r)e[t]||s.push(t);return{counts:n,ready:s,total:i}}(t);for(;r=i.shift();){e.set(r,e.size);const t=n[r];t&&t.forEach((t=>{const e=t.info.methodInfo;--o,0==--s[e]&&i.push(e)}))}if(0!==o){const e=t;console.warn(`Computed graph for ${e.localName} incomplete; circular?`)}t.constructor.__orderedComputedDeps=e}return e}(t),o=[];for(let t in e)Oe(t,s,o,i,r);let a;for(;a=o.shift();)Ae(t,"",e,n,a)&&Oe(a.methodInfo,s,o,i,r);Object.assign(n,t.__dataOld),Object.assign(e,t.__dataPending),t.__dataPending=null}else{let i=e;for(;me(t,s,i,n,r);)Object.assign(n,t.__dataOld),Object.assign(e,t.__dataPending),i=t.__dataPending,t.__dataPending=null}}const Te=(t,e,n)=>{let r=0,s=e.length-1,i=-1;for(;r<=s;){const o=r+s>>1,a=n.get(e[o].methodInfo)-n.get(t.methodInfo);if(a<0)r=o+1;else{if(!(a>0)){i=o;break}s=o-1}}i<0&&(i=s+1),e.splice(i,0,t)},Oe=(t,e,n,r,s)=>{const i=e[s?Lt(t):t];if(i)for(let e=0;e<i.length;e++){const o=i[e];o.info.lastRun===ce||s&&!ge(t,o.trigger)||(o.info.lastRun=ce,Te(o.info,n,r))}};function Ae(t,e,n,r,s){let i=Me(t,e,n,r,s);if(i===de)return!1;let o=s.methodInfo;return t.__dataHasAccessor&&t.__dataHasAccessor[o]?t._setPendingProperty(o,i,!0):(t[o]=i,!1)}function Ne(t,e,n,r,s,i,o){n.bindings=n.bindings||[];let a={kind:r,target:s,parts:i,literal:o,isCompound:1!==i.length};if(n.bindings.push(a),function(t){return Boolean(t.target)&&"attribute"!=t.kind&&"text"!=t.kind&&!t.isCompound&&"{"===t.parts[0].mode}(a)){let{event:t,negate:e}=a.parts[0];a.listenerEvent=t||Ut(s)+"-changed",a.listenerNegate=e}let l=e.nodeInfoList.length;for(let n=0;n<a.parts.length;n++){let r=a.parts[n];r.compoundIndex=n,xe(t,e,a,r,l)}}function xe(t,e,n,r,s){if(!r.literal)if("attribute"===n.kind&&"-"===n.target[0])console.warn("Cannot set attribute "+n.target+' because "-" is not a valid attribute starting character');else{let i=r.dependencies,o={index:s,binding:n,part:r,evaluator:t};for(let n=0;n<i.length;n++){let r=i[n];"string"==typeof r&&(r=Be(r),r.wildcard=!0),t._addTemplatePropertyEffect(e,r.rootProperty,{fn:Se,info:o,trigger:r})}}}function Se(t,e,n,r,s,i,o){let a=o[s.index],l=s.binding,h=s.part;if(i&&h.source&&e.length>h.source.length&&"property"==l.kind&&!l.isCompound&&a.__isPropertyEffectsClient&&a.__dataHasAccessor&&a.__dataHasAccessor[l.target]){let r=n[e];e=Rt(h.source,l.target,e),a._setPendingPropertyOrPath(e,r,!1,!0)&&t._enqueueClient(a)}else{let o=s.evaluator._evaluateBinding(t,h,e,n,r,i);o!==de&&function(t,e,n,r,s){s=function(t,e,n,r){if(n.isCompound){let s=t.__dataCompoundStorage[n.target];s[r.compoundIndex]=e,e=s.join("")}"attribute"!==n.kind&&("textContent"!==n.target&&("value"!==n.target||"input"!==t.localName&&"textarea"!==t.localName)||(e=null==e?"":e));return e}(e,s,n,r),T&&(s=T(s,n.target,n.kind,e));if("attribute"==n.kind)t._valueToNodeAttribute(e,s,n.target);else{let r=n.target;e.__isPropertyEffectsClient&&e.__dataHasAccessor&&e.__dataHasAccessor[r]?e[_e.READ_ONLY]&&e[_e.READ_ONLY][r]||e._setPendingProperty(r,s)&&t._enqueueClient(e):t._setUnmanagedPropertyToNode(e,r,s)}}(t,a,l,h,o)}}function Ie(t,e){if(e.isCompound){let n=t.__dataCompoundStorage||(t.__dataCompoundStorage={}),r=e.parts,s=new Array(r.length);for(let t=0;t<r.length;t++)s[t]=r[t].literal;let i=e.target;n[i]=s,e.literal&&"property"==e.kind&&("className"===i&&(t=I(t)),t[i]=e.literal)}}function ke(t,e,n){if(n.listenerEvent){let r=n.parts[0];t.addEventListener(n.listenerEvent,(function(t){!function(t,e,n,r,s){let i,o=t.detail,a=o&&o.path;a?(r=Rt(n,r,a),i=o&&o.value):i=t.currentTarget[n],i=s?!i:i,e[_e.READ_ONLY]&&e[_e.READ_ONLY][r]||!e._setPendingPropertyOrPath(r,i,!0,Boolean(a))||o&&o.queueProperty||e._invalidateProperties()}(t,e,n.target,r.source,r.negate)}))}}function Le(t,e,n,r,s,i){i=e.static||i&&("object"!=typeof i||i[e.methodName]);let o={methodName:e.methodName,args:e.args,methodInfo:s,dynamicFn:i};for(let s,i=0;i<e.args.length&&(s=e.args[i]);i++)s.literal||t._addPropertyEffect(s.rootProperty,n,{fn:r,info:o,trigger:s});return i&&t._addPropertyEffect(e.methodName,n,{fn:r,info:o}),o}function Me(t,e,n,r,s){let i=t._methodHost||t,o=i[s.methodName];if(o){let r=t._marshalArgs(s.args,e,n);return r===de?de:o.apply(i,r)}s.dynamicFn||console.warn("method `"+s.methodName+"` not defined")}const De=[],Re="(?:[a-zA-Z_$][\\w.:$\\-*]*)",Fe="(?:("+Re+"|(?:[-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?)|(?:(?:'(?:[^'\\\\]|\\\\.)*')|(?:\"(?:[^\"\\\\]|\\\\.)*\")))\\s*)",He=new RegExp("(\\[\\[|{{)\\s*(?:(!)\\s*)?"+("("+Re+"\\s*"+("(?:\\(\\s*(?:"+("(?:"+Fe+"(?:,\\s*"+Fe+")*)")+"?)\\)\\s*)")+"?)")+"(?:]]|}})","g");function ze(t){let e="";for(let n=0;n<t.length;n++){e+=t[n].literal||""}return e}function je(t){let e=t.match(/([^\s]+?)\(([\s\S]*)\)/);if(e){let t={methodName:e[1],static:!0,args:De};if(e[2].trim()){return function(t,e){return e.args=t.map((function(t){let n=Be(t);return n.literal||(e.static=!1),n}),this),e}(e[2].replace(/\\,/g,"&comma;").split(","),t)}return t}return null}function Be(t){let e=t.trim().replace(/&comma;/g,",").replace(/\\(.)/g,"$1"),n={name:e,value:"",literal:!1},r=e[0];switch("-"===r&&(r=e[1]),r>="0"&&r<="9"&&(r="#"),r){case"'":case'"':n.value=e.slice(1,-1),n.literal=!0;break;case"#":n.value=Number(e),n.literal=!0}return n.literal||(n.rootProperty=Lt(e),n.structured=kt(e),n.structured&&(n.wildcard=".*"==e.slice(-2),n.wildcard&&(n.name=e.slice(0,-2)))),n}function Je(t,e,n){let r=jt(t,n);return void 0===r&&(r=e[n]),r}function qe(t,e,n,r){const s={indexSplices:r};t.notifyPath(n+".splices",s),t.notifyPath(n+".length",e.length)}function Ye(t,e,n,r,s,i){qe(t,e,n,[{index:r,addedCount:s,removed:i,object:e,type:"splice"}])}const $e=d((t=>{const e=he(Kt(t));return class extends e{constructor(){super(),this.__isPropertyEffectsClient=!0,this.__dataClientsReady,this.__dataPendingClients,this.__dataToNotify,this.__dataLinkedPaths,this.__dataHasPaths,this.__dataCompoundStorage,this.__dataHost,this.__dataTemp,this.__dataClientsInitialized,this.__data,this.__dataPending,this.__dataOld,this.__computeEffects,this.__computeInfo,this.__reflectEffects,this.__notifyEffects,this.__propagateEffects,this.__observeEffects,this.__readOnly,this.__templateInfo,this._overrideLegacyUndefined}get PROPERTY_EFFECT_TYPES(){return _e}_initializeProperties(){super._initializeProperties(),this._registerHost(),this.__dataClientsReady=!1,this.__dataPendingClients=null,this.__dataToNotify=null,this.__dataLinkedPaths=null,this.__dataHasPaths=!1,this.__dataCompoundStorage=this.__dataCompoundStorage||null,this.__dataHost=this.__dataHost||null,this.__dataTemp={},this.__dataClientsInitialized=!1}_registerHost(){if(Ue.length){let t=Ue[Ue.length-1];t._enqueueClient(this),this.__dataHost=t}}_initializeProtoProperties(t){this.__data=Object.create(t),this.__dataPending=Object.create(t),this.__dataOld={}}_initializeInstanceProperties(t){let e=this[_e.READ_ONLY];for(let n in t)e&&e[n]||(this.__dataPending=this.__dataPending||{},this.__dataOld=this.__dataOld||{},this.__data[n]=this.__dataPending[n]=t[n])}_addPropertyEffect(t,e,n){this._createPropertyAccessor(t,e==_e.READ_ONLY);let r=fe(this,e,!0)[t];r||(r=this[e][t]=[]),r.push(n)}_removePropertyEffect(t,e,n){let r=fe(this,e,!0)[t],s=r.indexOf(n);s>=0&&r.splice(s,1)}_hasPropertyEffect(t,e){let n=this[e];return Boolean(n&&n[t])}_hasReadOnlyEffect(t){return this._hasPropertyEffect(t,_e.READ_ONLY)}_hasNotifyEffect(t){return this._hasPropertyEffect(t,_e.NOTIFY)}_hasReflectEffect(t){return this._hasPropertyEffect(t,_e.REFLECT)}_hasComputedEffect(t){return this._hasPropertyEffect(t,_e.COMPUTE)}_setPendingPropertyOrPath(t,e,n,r){if(r||Lt(Array.isArray(t)?t[0]:t)!==t){if(!r){let n=jt(this,t);if(!(t=Bt(this,t,e))||!super._shouldPropertyChange(t,e,n))return!1}if(this.__dataHasPaths=!0,this._setPendingProperty(t,e,n))return function(t,e,n){let r=t.__dataLinkedPaths;if(r){let s;for(let i in r){let o=r[i];Dt(i,e)?(s=Rt(i,o,e),t._setPendingPropertyOrPath(s,n,!0,!0)):Dt(o,e)&&(s=Rt(o,i,e),t._setPendingPropertyOrPath(s,n,!0,!0))}}}(this,t,e),!0}else{if(this.__dataHasAccessor&&this.__dataHasAccessor[t])return this._setPendingProperty(t,e,n);this[t]=e}return!1}_setUnmanagedPropertyToNode(t,e,n){n===t[e]&&"object"!=typeof n||("className"===e&&(t=I(t)),t[e]=n)}_setPendingProperty(t,e,n){let r=this.__dataHasPaths&&kt(t),s=r?this.__dataTemp:this.__data;return!!this._shouldPropertyChange(t,e,s[t])&&(this.__dataPending||(this.__dataPending={},this.__dataOld={}),t in this.__dataOld||(this.__dataOld[t]=this.__data[t]),r?this.__dataTemp[t]=e:this.__data[t]=e,this.__dataPending[t]=e,(r||this[_e.NOTIFY]&&this[_e.NOTIFY][t])&&(this.__dataToNotify=this.__dataToNotify||{},this.__dataToNotify[t]=n),!0)}_setProperty(t,e){this._setPendingProperty(t,e,!0)&&this._invalidateProperties()}_invalidateProperties(){this.__dataReady&&this._flushProperties()}_enqueueClient(t){this.__dataPendingClients=this.__dataPendingClients||[],t!==this&&this.__dataPendingClients.push(t)}_flushClients(){this.__dataClientsReady?this.__enableOrFlushClients():(this.__dataClientsReady=!0,this._readyClients(),this.__dataReady=!0)}__enableOrFlushClients(){let t=this.__dataPendingClients;if(t){this.__dataPendingClients=null;for(let e=0;e<t.length;e++){let n=t[e];n.__dataEnabled?n.__dataPending&&n._flushProperties():n._enableProperties()}}}_readyClients(){this.__enableOrFlushClients()}setProperties(t,e){for(let n in t)!e&&this[_e.READ_ONLY]&&this[_e.READ_ONLY][n]||this._setPendingPropertyOrPath(n,t[n],!0);this._invalidateProperties()}ready(){this._flushProperties(),this.__dataClientsReady||this._flushClients(),this.__dataPending&&this._flushProperties()}_propertiesChanged(t,e,n){let r,s=this.__dataHasPaths;this.__dataHasPaths=!1,Ee(this,e,n,s),r=this.__dataToNotify,this.__dataToNotify=null,this._propagatePropertyChanges(e,n,s),this._flushClients(),me(this,this[_e.REFLECT],e,n,s),me(this,this[_e.OBSERVE],e,n,s),r&&function(t,e,n,r,s){let i,o,a=t[_e.NOTIFY],l=ce++;for(let o in e)e[o]&&(a&&ye(t,a,l,o,n,r,s)||s&&Pe(t,o,n))&&(i=!0);i&&(o=t.__dataHost)&&o._invalidateProperties&&o._invalidateProperties()}(this,r,e,n,s),1==this.__dataCounter&&(this.__dataTemp={})}_propagatePropertyChanges(t,e,n){this[_e.PROPAGATE]&&me(this,this[_e.PROPAGATE],t,e,n),this.__templateInfo&&this._runEffectsForTemplate(this.__templateInfo,t,e,n)}_runEffectsForTemplate(t,e,n,r){const s=(e,r)=>{me(this,t.propertyEffects,e,n,r,t.nodeList);for(let s=t.firstChild;s;s=s.nextSibling)this._runEffectsForTemplate(s,e,n,r)};t.runEffects?t.runEffects(s,e,r):s(e,r)}linkPaths(t,e){t=Ht(t),e=Ht(e),this.__dataLinkedPaths=this.__dataLinkedPaths||{},this.__dataLinkedPaths[t]=e}unlinkPaths(t){t=Ht(t),this.__dataLinkedPaths&&delete this.__dataLinkedPaths[t]}notifySplices(t,e){let n={path:""};qe(this,jt(this,t,n),n.path,e)}get(t,e){return jt(e||this,t)}set(t,e,n){n?Bt(n,t,e):this[_e.READ_ONLY]&&this[_e.READ_ONLY][t]||this._setPendingPropertyOrPath(t,e,!0)&&this._invalidateProperties()}push(t,...e){let n={path:""},r=jt(this,t,n),s=r.length,i=r.push(...e);return e.length&&Ye(this,r,n.path,s,e.length,[]),i}pop(t){let e={path:""},n=jt(this,t,e),r=Boolean(n.length),s=n.pop();return r&&Ye(this,n,e.path,n.length,0,[s]),s}splice(t,e,n,...r){let s,i={path:""},o=jt(this,t,i);return e<0?e=o.length-Math.floor(-e):e&&(e=Math.floor(e)),s=2===arguments.length?o.splice(e):o.splice(e,n,...r),(r.length||s.length)&&Ye(this,o,i.path,e,r.length,s),s}shift(t){let e={path:""},n=jt(this,t,e),r=Boolean(n.length),s=n.shift();return r&&Ye(this,n,e.path,0,0,[s]),s}unshift(t,...e){let n={path:""},r=jt(this,t,n),s=r.unshift(...e);return e.length&&Ye(this,r,n.path,0,e.length,[]),s}notifyPath(t,e){let n;if(1==arguments.length){let r={path:""};e=jt(this,t,r),n=r.path}else n=Array.isArray(t)?Ht(t):t;this._setPendingPropertyOrPath(n,e,!0,!0)&&this._invalidateProperties()}_createReadOnlyProperty(t,e){var n;this._addPropertyEffect(t,_e.READ_ONLY),e&&(this["_set"+(n=t,n[0].toUpperCase()+n.substring(1))]=function(e){this._setProperty(t,e)})}_createPropertyObserver(t,e,n){let r={property:t,method:e,dynamicFn:Boolean(n)};this._addPropertyEffect(t,_e.OBSERVE,{fn:be,info:r,trigger:{name:t}}),n&&this._addPropertyEffect(e,_e.OBSERVE,{fn:be,info:r,trigger:{name:e}})}_createMethodObserver(t,e){let n=je(t);if(!n)throw new Error("Malformed observer expression '"+t+"'");Le(this,n,_e.OBSERVE,Me,null,e)}_createNotifyingProperty(t){this._addPropertyEffect(t,_e.NOTIFY,{fn:Ce,info:{eventName:Ut(t)+"-changed",property:t}})}_createReflectedProperty(t){let e=this.constructor.attributeNameForProperty(t);"-"===e[0]?console.warn("Property "+t+" cannot be reflected to attribute "+e+' because "-" is not a valid starting attribute name. Use a lowercase first letter for the property instead.'):this._addPropertyEffect(t,_e.REFLECT,{fn:we,info:{attrName:e}})}_createComputedProperty(t,e,n){let r=je(e);if(!r)throw new Error("Malformed computed expression '"+e+"'");const s=Le(this,r,_e.COMPUTE,Ae,t,n);fe(this,ue)[t]=s}_marshalArgs(t,e,n){const r=this.__data,s=[];for(let i=0,o=t.length;i<o;i++){let{name:o,structured:a,wildcard:l,value:h,literal:c}=t[i];if(!c)if(l){const t=Dt(o,e),s=Je(r,n,t?e:o);h={path:t?e:o,value:s,base:t?jt(r,o):s}}else h=a?Je(r,n,o):r[o];s[i]=h}return s}static addPropertyEffect(t,e,n){this.prototype._addPropertyEffect(t,e,n)}static createPropertyObserver(t,e,n){this.prototype._createPropertyObserver(t,e,n)}static createMethodObserver(t,e){this.prototype._createMethodObserver(t,e)}static createNotifyingProperty(t){this.prototype._createNotifyingProperty(t)}static createReadOnlyProperty(t,e){this.prototype._createReadOnlyProperty(t,e)}static createReflectedProperty(t){this.prototype._createReflectedProperty(t)}static createComputedProperty(t,e,n){this.prototype._createComputedProperty(t,e,n)}static bindTemplate(t){return this.prototype._bindTemplate(t)}_bindTemplate(t,e){let n=this.constructor._parseTemplate(t),r=this.__preBoundTemplateInfo==n;if(!r)for(let t in n.propertyEffects)this._createPropertyAccessor(t);if(e)if(n=Object.create(n),n.wasPreBound=r,this.__templateInfo){const e=t._parentTemplateInfo||this.__templateInfo,r=e.lastChild;n.parent=e,e.lastChild=n,n.previousSibling=r,r?r.nextSibling=n:e.firstChild=n}else this.__templateInfo=n;else this.__preBoundTemplateInfo=n;return n}static _addTemplatePropertyEffect(t,e,n){(t.hostProps=t.hostProps||{})[e]=!0;let r=t.propertyEffects=t.propertyEffects||{};(r[e]=r[e]||[]).push(n)}_stampTemplate(t,e){e=e||this._bindTemplate(t,!0),Ue.push(this);let n=super._stampTemplate(t,e);if(Ue.pop(),e.nodeList=n.nodeList,!e.wasPreBound){let t=e.childNodes=[];for(let e=n.firstChild;e;e=e.nextSibling)t.push(e)}return n.templateInfo=e,function(t,e){let{nodeList:n,nodeInfoList:r}=e;if(r.length)for(let e=0;e<r.length;e++){let s=r[e],i=n[e],o=s.bindings;if(o)for(let e=0;e<o.length;e++){let n=o[e];Ie(i,n),ke(i,t,n)}i.__dataHost=t}}(this,e),this.__dataClientsReady&&(this._runEffectsForTemplate(e,this.__data,null,!1),this._flushClients()),n}_removeBoundDom(t){const e=t.templateInfo,{previousSibling:n,nextSibling:r,parent:s}=e;n?n.nextSibling=r:s&&(s.firstChild=r),r?r.previousSibling=n:s&&(s.lastChild=n),e.nextSibling=e.previousSibling=null;let i=e.childNodes;for(let t=0;t<i.length;t++){let e=i[t];I(I(e).parentNode).removeChild(e)}}static _parseTemplateNode(t,n,r){let s=e._parseTemplateNode.call(this,t,n,r);if(t.nodeType===Node.TEXT_NODE){let e=this._parseBindings(t.textContent,n);e&&(t.textContent=ze(e)||" ",Ne(this,n,r,"text","textContent",e),s=!0)}return s}static _parseTemplateNodeAttribute(t,n,r,s,i){let o=this._parseBindings(i,n);if(o){let e=s,i="property";pe.test(s)?i="attribute":"$"==s[s.length-1]&&(s=s.slice(0,-1),i="attribute");let a=ze(o);return a&&"attribute"==i&&("class"==s&&t.hasAttribute("class")&&(a+=" "+t.getAttribute(s)),t.setAttribute(s,a)),"attribute"==i&&"disable-upgrade$"==e&&t.setAttribute(s,""),"input"===t.localName&&"value"===e&&t.setAttribute(e,""),t.removeAttribute(e),"property"===i&&(s=$t(s)),Ne(this,n,r,i,s,o,a),!0}return e._parseTemplateNodeAttribute.call(this,t,n,r,s,i)}static _parseTemplateNestedTemplate(t,n,r){let s=e._parseTemplateNestedTemplate.call(this,t,n,r);const i=t.parentNode,o=r.templateInfo;i.localName,i.localName;let a=o.hostProps;{let t="{";for(let e in a){Ne(this,n,r,"property","_host_"+e,[{mode:t,source:e,dependencies:[e],hostProp:!0}])}}return s}static _parseBindings(t,e){let n,r=[],s=0;for(;null!==(n=He.exec(t));){n.index>s&&r.push({literal:t.slice(s,n.index)});let i=n[1][0],o=Boolean(n[2]),a=n[3].trim(),l=!1,h="",c=-1;"{"==i&&(c=a.indexOf("::"))>0&&(h=a.substring(c+2),a=a.substring(0,c),l=!0);let d=je(a),_=[];if(d){let{args:t,methodName:n}=d;for(let e=0;e<t.length;e++){let n=t[e];n.literal||_.push(n)}let r=e.dynamicFns;(r&&r[n]||d.static)&&(_.push(n),d.dynamicFn=!0)}else _.push(a);r.push({source:a,mode:i,negate:o,customEvent:l,signature:d,dependencies:_,event:h}),s=He.lastIndex}if(s&&s<t.length){let e=t.substring(s);e&&r.push({literal:e})}return r.length?r:null}static _evaluateBinding(t,e,n,r,s,i){let o;return o=e.signature?Me(t,n,r,0,e.signature):n!=e.source?jt(t,e.source):i&&kt(n)?jt(t,n):t.__data[n],e.negate&&(o=!o),o}}})),Ue=[];const Ve=d((t=>{const e=Xt(t);function n(t){const e=Object.getPrototypeOf(t);return e.prototype instanceof s?e:null}function r(t){if(!t.hasOwnProperty(JSCompiler_renameProperty("__ownProperties",t))){let e=null;if(t.hasOwnProperty(JSCompiler_renameProperty("properties",t))){const n=t.properties;n&&(e=function(t){const e={};for(let n in t){const r=t[n];e[n]="function"==typeof r?{type:r}:r}return e}(n))}t.__ownProperties=e}return t.__ownProperties}class s extends e{static get observedAttributes(){if(!this.hasOwnProperty(JSCompiler_renameProperty("__observedAttributes",this))){this.prototype;const t=this._properties;this.__observedAttributes=t?Object.keys(t).map((t=>this.prototype._addPropertyToAttributeMap(t))):[]}return this.__observedAttributes}static finalize(){if(!this.hasOwnProperty(JSCompiler_renameProperty("__finalized",this))){const t=n(this);t&&t.finalize(),this.__finalized=!0,this._finalizeClass()}}static _finalizeClass(){const t=r(this);t&&this.createProperties(t)}static get _properties(){if(!this.hasOwnProperty(JSCompiler_renameProperty("__properties",this))){const t=n(this);this.__properties=Object.assign({},t&&t._properties,r(this))}return this.__properties}static typeForProperty(t){const e=this._properties[t];return e&&e.type}_initializeProperties(){this.constructor.finalize(),super._initializeProperties()}connectedCallback(){super.connectedCallback&&super.connectedCallback(),this._enableProperties()}disconnectedCallback(){super.disconnectedCallback&&super.disconnectedCallback()}}return s})),Xe=d((t=>{const e=Ve($e(t));function n(t,e,n,r){n.computed&&(n.readOnly=!0),n.computed&&(t._hasReadOnlyEffect(e)?console.warn(`Cannot redefine computed property '${e}'.`):t._createComputedProperty(e,n.computed,r)),n.readOnly&&!t._hasReadOnlyEffect(e)?t._createReadOnlyProperty(e,!n.computed):!1===n.readOnly&&t._hasReadOnlyEffect(e)&&console.warn(`Cannot make readOnly property '${e}' non-readOnly.`),n.reflectToAttribute&&!t._hasReflectEffect(e)?t._createReflectedProperty(e):!1===n.reflectToAttribute&&t._hasReflectEffect(e)&&console.warn(`Cannot make reflected property '${e}' non-reflected.`),n.notify&&!t._hasNotifyEffect(e)?t._createNotifyingProperty(e):!1===n.notify&&t._hasNotifyEffect(e)&&console.warn(`Cannot make notify property '${e}' non-notify.`),n.observer&&t._createPropertyObserver(e,n.observer,r[n.observer]),t._addPropertyToAttributeMap(e)}function r(t,e,n,r){{const s=e.content.querySelectorAll("style"),i=xt(e),o=function(t){let e=Tt(t);return e?St(e):[]}(n),a=e.content.firstElementChild;for(let n=0;n<o.length;n++){let s=o[n];s.textContent=t._processStyleText(s.textContent,r),e.content.insertBefore(s,a)}let l=0;for(let e=0;e<i.length;e++){let n=i[e],o=s[l];o!==n?(n=n.cloneNode(!0),o.parentNode.insertBefore(n,o)):l++,n.textContent=t._processStyleText(n.textContent,r)}}}return class extends e{static get polymerElementVersion(){return"3.5.0"}static _finalizeClass(){e._finalizeClass.call(this);const t=((n=this).hasOwnProperty(JSCompiler_renameProperty("__ownObservers",n))||(n.__ownObservers=n.hasOwnProperty(JSCompiler_renameProperty("observers",n))?n.observers:null),n.__ownObservers);var n;t&&this.createObservers(t,this._properties),this._prepareTemplate()}static _prepareTemplate(){let t=this.template;t&&("string"==typeof t?(console.error("template getter must return HTMLTemplateElement"),t=null):x||(t=t.cloneNode(!0))),this.prototype._template=t}static createProperties(t){for(let e in t)n(this.prototype,e,t[e],t)}static createObservers(t,e){const n=this.prototype;for(let r=0;r<t.length;r++)n._createMethodObserver(t[r],e)}static get template(){if(!this.hasOwnProperty(JSCompiler_renameProperty("_template",this))){let t=this.prototype.hasOwnProperty(JSCompiler_renameProperty("_template",this.prototype))?this.prototype._template:void 0;"function"==typeof t&&(t=t()),this._template=void 0!==t?t:this.hasOwnProperty(JSCompiler_renameProperty("is",this))&&function(t){let e=null;if(t&&(!A||N)&&(e=wt.import(t,"template"),A&&!e))throw new Error(`strictTemplatePolicy: expecting dom-module or null template for ${t}`);return e}(this.is)||Object.getPrototypeOf(this.prototype).constructor.template}return this._template}static set template(t){this._template=t}static get importPath(){if(!this.hasOwnProperty(JSCompiler_renameProperty("_importPath",this))){const t=this.importMeta;if(t)this._importPath=C(t.url);else{const t=wt.import(this.is);this._importPath=t&&t.assetpath||Object.getPrototypeOf(this.prototype).constructor.importPath}}return this._importPath}constructor(){super(),this._template,this._importPath,this.rootPath,this.importPath,this.root,this.$}_initializeProperties(){this.constructor.finalize(),this.constructor._finalizeTemplate(this.localName),super._initializeProperties(),this.rootPath=E,this.importPath=this.constructor.importPath;let t=function(t){if(!t.hasOwnProperty(JSCompiler_renameProperty("__propertyDefaults",t))){t.__propertyDefaults=null;let e=t._properties;for(let n in e){let r=e[n];"value"in r&&(t.__propertyDefaults=t.__propertyDefaults||{},t.__propertyDefaults[n]=r)}}return t.__propertyDefaults}(this.constructor);if(t)for(let e in t){let n=t[e];if(this._canApplyPropertyDefault(e)){let t="function"==typeof n.value?n.value.call(this):n.value;this._hasAccessor(e)?this._setPendingProperty(e,t,!0):this[e]=t}}}_canApplyPropertyDefault(t){return!this.hasOwnProperty(t)}static _processStyleText(t,e){return v(t,e)}static _finalizeTemplate(t){const e=this.prototype._template;if(e&&!e.__polymerFinalized){e.__polymerFinalized=!0;const n=this.importPath;r(this,e,t,n?P(n):""),this.prototype._bindTemplate(e)}}connectedCallback(){super.connectedCallback()}ready(){this._template&&(this.root=this._stampTemplate(this._template),this.$=this.root.$),super.ready()}_readyClients(){this._template&&(this.root=this._attachDom(this.root)),super._readyClients()}_attachDom(t){const e=I(this);if(e.attachShadow)return t?(e.shadowRoot||(e.attachShadow({mode:"open",shadyUpgradeFragment:t}),this.constructor._styleSheet&&(e.shadowRoot.adoptedStyleSheets=[this.constructor._styleSheet])),e.shadowRoot.appendChild(t),e.shadowRoot):null;throw new Error("ShadowDOM not available. PolymerElement can create dom as children instead of in ShadowDOM by setting `this.root = this;` before `ready`.")}updateStyles(t){for(const[e,n]of Object.entries(t))this.style.setProperty(e,n)}resolveUrl(t,e){return!e&&this.importPath&&(e=P(this.importPath)),P(t,e)}static _parseTemplateContent(t,n,r){return n.dynamicFns=n.dynamicFns||this._properties,e._parseTemplateContent.call(this,t,n,r)}static _addTemplatePropertyEffect(t,n,r){return e._addTemplatePropertyEffect.call(this,t,n,r)}}})),Ge=window.trustedTypes&&trustedTypes.createPolicy("polymer-html-literal",{createHTML:t=>t});class We{constructor(t,e){Qe(t,e);const n=e.reduce(((e,n,r)=>e+Ze(n)+t[r+1]),t[0]);this.value=n.toString()}toString(){return this.value}}function Ze(t){if(t instanceof We)return t.value;throw new Error(`non-literal value passed to Polymer's htmlLiteral function: ${t}`)}const Ke=function(t,...e){Qe(t,e);const n=document.createElement("template");let r=e.reduce(((e,n,r)=>e+function(t){if(t instanceof HTMLTemplateElement)return t.innerHTML;if(t instanceof We)return Ze(t);throw new Error(`non-template value passed to Polymer's html function: ${t}`)}(n)+t[r+1]),t[0]);return Ge&&(r=Ge.createHTML(r)),n.innerHTML=r,n},Qe=(t,e)=>{if(!Array.isArray(t)||!Array.isArray(t.raw)||e.length!==t.length-1)throw new TypeError("Invalid call to the html template tag")},tn=Xe(HTMLElement),en=function(){let t,e;do{t=!1,e=f()}while(e)};function nn(t,e,n,r,s){let i;s&&(i="object"==typeof n&&null!==n,i&&(r=t.__dataTemp[e]));let o=r!==n&&(r==r||n==n);return i&&o&&(t.__dataTemp[e]=n),o}const rn=d((t=>class extends t{_shouldPropertyChange(t,e,n){return nn(this,t,e,n,!0)}})),sn=d((t=>class extends t{static get properties(){return{mutableData:Boolean}}_shouldPropertyChange(t,e,n){return nn(this,t,e,n,this.mutableData)}}));rn._mutablePropertyChange=nn;let on=null;function an(){return on}an.prototype=Object.create(HTMLTemplateElement.prototype,{constructor:{value:an,writable:!0}});const ln=$e(an),hn=rn(ln);const cn=$e(class{});class dn extends cn{constructor(t){super(),this._configureProperties(t),this.root=this._stampTemplate(this.__dataHost);let e=[];this.children=e;for(let t=this.root.firstChild;t;t=t.nextSibling)e.push(t),t.__templatizeInstance=this;this.__templatizeOwner&&this.__templatizeOwner.__hideTemplateChildren__&&this._showHideChildren(!0);let n=this.__templatizeOptions;(t&&n.instanceProps||!n.instanceProps)&&this._enableProperties()}_configureProperties(t){if(this.__templatizeOptions.forwardHostProp)for(let t in this.__hostProps)this._setPendingProperty(t,this.__dataHost["_host_"+t]);for(let e in t)this._setPendingProperty(e,t[e])}forwardHostProp(t,e){this._setPendingPropertyOrPath(t,e,!1,!0)&&this.__dataHost._enqueueClient(this)}_addEventListenerToNode(t,e,n){if(this._methodHost&&this.__templatizeOptions.parentModel)this._methodHost._addEventListenerToNode(t,e,(t=>{t.model=this,n(t)}));else{let r=this.__dataHost.__dataHost;r&&r._addEventListenerToNode(t,e,n)}}_showHideChildren(t){!function(t,e){for(let n=0;n<e.length;n++){let r=e[n];if(Boolean(t)!=Boolean(r.__hideTemplateChildren__))if(r.nodeType===Node.TEXT_NODE)t?(r.__polymerTextContent__=r.textContent,r.textContent=""):r.textContent=r.__polymerTextContent__;else if("slot"===r.localName)if(t)r.__polymerReplaced__=document.createComment("hidden-slot"),I(I(r).parentNode).replaceChild(r.__polymerReplaced__,r);else{const t=r.__polymerReplaced__;t&&I(I(t).parentNode).replaceChild(r,t)}else r.style&&(t?(r.__polymerDisplay__=r.style.display,r.style.display="none"):r.style.display=r.__polymerDisplay__);r.__hideTemplateChildren__=t,r._showHideChildren&&r._showHideChildren(t)}}(t,this.children)}_setUnmanagedPropertyToNode(t,e,n){t.__hideTemplateChildren__&&t.nodeType==Node.TEXT_NODE&&"textContent"==e?t.__polymerTextContent__=n:super._setUnmanagedPropertyToNode(t,e,n)}get parentModel(){let t=this.__parentModel;if(!t){let e;t=this;do{t=t.__dataHost.__dataHost}while((e=t.__templatizeOptions)&&!e.parentModel);this.__parentModel=t}return t}dispatchEvent(t){return!0}}dn.prototype.__dataHost,dn.prototype.__templatizeOptions,dn.prototype._methodHost,dn.prototype.__templatizeOwner,dn.prototype.__hostProps;const _n=rn(dn);function un(t){let e=t.__dataHost;return e&&e._methodHost||e}function pn(t,e,n){let r=n.mutableData?_n:dn;gn.mixin&&(r=gn.mixin(r));let s=class extends r{};return s.prototype.__templatizeOptions=n,s.prototype._bindTemplate(t),function(t,e,n,r){let s=n.hostProps||{};for(let e in r.instanceProps){delete s[e];let n=r.notifyInstanceProp;n&&t.prototype._addPropertyEffect(e,t.prototype.PROPERTY_EFFECT_TYPES.NOTIFY,{fn:yn(e,n)})}if(r.forwardHostProp&&e.__dataHost)for(let e in s)n.hasHostProps||(n.hasHostProps=!0),t.prototype._addPropertyEffect(e,t.prototype.PROPERTY_EFFECT_TYPES.NOTIFY,{fn:function(t,e,n){t.__dataHost._setPendingPropertyOrPath("_host_"+e,n[e],!0,!0)}})}(s,t,e,n),s}function fn(t,e,n,r){let s=n.forwardHostProp;if(s&&e.hasHostProps){const r="template"==t.localName;let a=e.templatizeTemplateClass;if(!a){if(r){let t=n.mutableData?hn:ln;class r extends t{}a=e.templatizeTemplateClass=r}else{const n=t.constructor;class r extends n{}a=e.templatizeTemplateClass=r}let i=e.hostProps;for(let t in i)a.prototype._addPropertyEffect("_host_"+t,a.prototype.PROPERTY_EFFECT_TYPES.PROPAGATE,{fn:mn(t,s)}),a.prototype._createNotifyingProperty("_host_"+t)}if(t.__dataProto&&Object.assign(t.__data,t.__dataProto),r)o=a,on=i=t,Object.setPrototypeOf(i,o.prototype),new o,on=null,t.__dataTemp={},t.__dataPending=null,t.__dataOld=null,t._enableProperties();else{Object.setPrototypeOf(t,a.prototype);const n=e.hostProps;for(let e in n)if(e="_host_"+e,e in t){const n=t[e];delete t[e],t.__data[e]=n}}}var i,o}function mn(t,e){return function(t,n,r){e.call(t.__templatizeOwner,n.substring("_host_".length),r[n])}}function yn(t,e){return function(t,n,r){e.call(t.__templatizeOwner,t,n,r[n])}}function gn(t,e,n){if(A&&!un(t))throw new Error("strictTemplatePolicy: template owner not trusted");if(n=n||{},t.__templatizeOwner)throw new Error("A <template> can only be templatized once");t.__templatizeOwner=e;let r=(e?e.constructor:dn)._parseTemplate(t),s=r.templatizeInstanceClass;s||(s=pn(t,r,n),r.templatizeInstanceClass=s);const i=un(t);fn(t,r,n);let o=class extends s{};return o.prototype._methodHost=i,o.prototype.__dataHost=t,o.prototype.__templatizeOwner=e,o.prototype.__hostProps=r.hostProps,o}function bn(t,e){let n;for(;e;)if(n=e.__dataHost?e:e.__templatizeInstance){if(n.__dataHost==t)return n;e=n.__dataHost}else e=I(e).parentNode;return null}class Pn extends tn{static get is(){return"dom-if"}static get template(){return null}static get properties(){return{if:{type:Boolean,observer:"__debounceRender"},restamp:{type:Boolean,observer:"__debounceRender"},notifyDomChange:{type:Boolean}}}constructor(){super(),this.__renderDebouncer=null,this._lastIf=!1,this.__hideTemplateChildren__=!1,this.__template,this._templateInfo}__debounceRender(){this.__renderDebouncer=_.debounce(this.__renderDebouncer,h,(()=>this.__render())),p(this.__renderDebouncer)}disconnectedCallback(){super.disconnectedCallback();const t=I(this).parentNode;t&&(t.nodeType!=Node.DOCUMENT_FRAGMENT_NODE||I(t).host)||this.__teardownInstance()}connectedCallback(){super.connectedCallback(),this.style.display="none",this.if&&this.__debounceRender()}__ensureTemplate(){if(!this.__template){const t=this;let e=t._templateInfo?t:I(t).querySelector("template");if(!e){let t=new MutationObserver((()=>{if(!I(this).querySelector("template"))throw new Error("dom-if requires a <template> child");t.disconnect(),this.__render()}));return t.observe(this,{childList:!0}),!1}this.__template=e}return!0}__ensureInstance(){let t=I(this).parentNode;if(this.__hasInstance()){let e=this.__getInstanceNodes();if(e&&e.length){if(I(this).previousSibling!==e[e.length-1])for(let n,r=0;r<e.length&&(n=e[r]);r++)I(t).insertBefore(n,this)}}else{if(!t)return!1;if(!this.__ensureTemplate())return!1;this.__createAndInsertInstance(t)}return!0}render(){en()}__render(){if(this.if){if(!this.__ensureInstance())return}else this.restamp&&this.__teardownInstance();this._showHideChildren(),this.if!=this._lastIf&&(this.dispatchEvent(new CustomEvent("dom-change",{bubbles:!0,composed:!0})),this._lastIf=this.if)}__hasInstance(){}__getInstanceNodes(){}__createAndInsertInstance(t){}__teardownInstance(){}_showHideChildren(){}}const vn=class extends Pn{constructor(){super(),this.__ctor=null,this.__instance=null,this.__invalidProps=null}__hasInstance(){return Boolean(this.__instance)}__getInstanceNodes(){return this.__instance.children}__createAndInsertInstance(t){this.__ctor||(this.__ctor=gn(this.__template,this,{mutableData:!0,forwardHostProp:function(t,e){this.__instance&&(this.if?this.__instance.forwardHostProp(t,e):(this.__invalidProps=this.__invalidProps||Object.create(null),this.__invalidProps[Lt(t)]=!0))}})),this.__instance=new this.__ctor,I(t).insertBefore(this.__instance.root,this)}__teardownInstance(){if(this.__instance){let t=this.__instance.children;if(t&&t.length){let e=I(t[0]).parentNode;if(e){e=I(e);for(let n,r=0;r<t.length&&(n=t[r]);r++)e.removeChild(n)}}this.__invalidProps=null,this.__instance=null}}__syncHostProperties(){let t=this.__invalidProps;if(t){this.__invalidProps=null;for(let e in t)this.__instance._setPendingProperty(e,this.__dataHost[e]);this.__instance._flushProperties()}}_showHideChildren(){const t=this.__hideTemplateChildren__||!this.if;this.__instance&&Boolean(this.__instance.__hidden)!==t&&(this.__instance.__hidden=t,this.__instance._showHideChildren(t)),t||this.__syncHostProperties()}};customElements.define(vn.is,vn);const Cn=sn(tn);class wn extends Cn{static get is(){return"dom-repeat"}static get template(){return null}static get properties(){return{items:{type:Array},as:{type:String,value:"item"},indexAs:{type:String,value:"index"},itemsIndexAs:{type:String,value:"itemsIndex"},sort:{type:Function,observer:"__sortChanged"},filter:{type:Function,observer:"__filterChanged"},observe:{type:String,observer:"__observeChanged"},delay:Number,renderedItemCount:{type:Number,notify:!0,readOnly:!0},initialCount:{type:Number},targetFramerate:{type:Number,value:20},_targetFrameTime:{type:Number,computed:"__computeFrameTime(targetFramerate)"},notifyDomChange:{type:Boolean},reuseChunkedInstances:{type:Boolean}}}static get observers(){return["__itemsChanged(items.*)"]}constructor(){super(),this.__instances=[],this.__renderDebouncer=null,this.__itemsIdxToInstIdx={},this.__chunkCount=null,this.__renderStartTime=null,this.__itemsArrayChanged=!1,this.__shouldMeasureChunk=!1,this.__shouldContinueChunking=!1,this.__chunkingId=0,this.__sortFn=null,this.__filterFn=null,this.__observePaths=null,this.__ctor=null,this.__isDetached=!0,this.template=null,this._templateInfo}disconnectedCallback(){super.disconnectedCallback(),this.__isDetached=!0;for(let t=0;t<this.__instances.length;t++)this.__detachInstance(t);this.__chunkingId&&cancelAnimationFrame(this.__chunkingId)}connectedCallback(){if(super.connectedCallback(),this.style.display="none",this.__isDetached){this.__isDetached=!1;let t=I(I(this).parentNode);for(let e=0;e<this.__instances.length;e++)this.__attachInstance(e,t);this.__chunkingId&&this.__render()}}__ensureTemplatized(){if(!this.__ctor){const t=this;let e=this.template=t._templateInfo?t:this.querySelector("template");if(!e){let t=new MutationObserver((()=>{if(!this.querySelector("template"))throw new Error("dom-repeat requires a <template> child");t.disconnect(),this.__render()}));return t.observe(this,{childList:!0}),!1}let n={};n[this.as]=!0,n[this.indexAs]=!0,n[this.itemsIndexAs]=!0,this.__ctor=gn(e,this,{mutableData:this.mutableData,parentModel:!0,instanceProps:n,forwardHostProp:function(t,e){let n=this.__instances;for(let r,s=0;s<n.length&&(r=n[s]);s++)r.forwardHostProp(t,e)},notifyInstanceProp:function(t,e,n){if(Ft(this.as,e)){let r=t[this.itemsIndexAs];e==this.as&&(this.items[r]=n);let s=Rt(this.as,`${JSCompiler_renameProperty("items",this)}.${r}`,e);this.notifyPath(s,n)}}})}return!0}__getMethodHost(){return this.__dataHost._methodHost||this.__dataHost}__functionFromPropertyValue(t){if("string"==typeof t){let e=t,n=this.__getMethodHost();return function(){return n[e].apply(n,arguments)}}return t}__sortChanged(t){this.__sortFn=this.__functionFromPropertyValue(t),this.items&&this.__debounceRender(this.__render)}__filterChanged(t){this.__filterFn=this.__functionFromPropertyValue(t),this.items&&this.__debounceRender(this.__render)}__computeFrameTime(t){return Math.ceil(1e3/t)}__observeChanged(){this.__observePaths=this.observe&&this.observe.replace(".*",".").split(" ")}__handleObservedPaths(t){if(this.__sortFn||this.__filterFn)if(t){if(this.__observePaths){let e=this.__observePaths;for(let n=0;n<e.length;n++)0===t.indexOf(e[n])&&this.__debounceRender(this.__render,this.delay)}}else this.__debounceRender(this.__render,this.delay)}__itemsChanged(t){this.items&&!Array.isArray(this.items)&&console.warn("dom-repeat expected array for `items`, found",this.items),this.__handleItemPath(t.path,t.value)||("items"===t.path&&(this.__itemsArrayChanged=!0),this.__debounceRender(this.__render))}__debounceRender(t,e=0){this.__renderDebouncer=_.debounce(this.__renderDebouncer,e>0?o.after(e):h,t.bind(this)),p(this.__renderDebouncer)}render(){this.__debounceRender(this.__render),en()}__render(){if(!this.__ensureTemplatized())return;let t=this.items||[];const e=this.__sortAndFilterItems(t),n=this.__calculateLimit(e.length);this.__updateInstances(t,n,e),this.initialCount&&(this.__shouldMeasureChunk||this.__shouldContinueChunking)&&(cancelAnimationFrame(this.__chunkingId),this.__chunkingId=requestAnimationFrame((()=>{this.__chunkingId=null,this.__continueChunking()}))),this._setRenderedItemCount(this.__instances.length),this.dispatchEvent(new CustomEvent("dom-change",{bubbles:!0,composed:!0}))}__sortAndFilterItems(t){let e=new Array(t.length);for(let n=0;n<t.length;n++)e[n]=n;return this.__filterFn&&(e=e.filter(((e,n,r)=>this.__filterFn(t[e],n,r)))),this.__sortFn&&e.sort(((e,n)=>this.__sortFn(t[e],t[n]))),e}__calculateLimit(t){let e=t;const n=this.__instances.length;if(this.initialCount){let r;!this.__chunkCount||this.__itemsArrayChanged&&!this.reuseChunkedInstances?(e=Math.min(t,this.initialCount),r=Math.max(e-n,0),this.__chunkCount=r||1):(r=Math.min(Math.max(t-n,0),this.__chunkCount),e=Math.min(n+r,t)),this.__shouldMeasureChunk=r===this.__chunkCount,this.__shouldContinueChunking=e<t,this.__renderStartTime=performance.now()}return this.__itemsArrayChanged=!1,e}__continueChunking(){if(this.__shouldMeasureChunk){const t=performance.now()-this.__renderStartTime,e=this._targetFrameTime/t;this.__chunkCount=Math.round(this.__chunkCount*e)||1}this.__shouldContinueChunking&&this.__debounceRender(this.__render)}__updateInstances(t,e,n){const r=this.__itemsIdxToInstIdx={};let s;for(s=0;s<e;s++){let e=this.__instances[s],i=n[s],o=t[i];r[i]=s,e?(e._setPendingProperty(this.as,o),e._setPendingProperty(this.indexAs,s),e._setPendingProperty(this.itemsIndexAs,i),e._flushProperties()):this.__insertInstance(o,s,i)}for(let t=this.__instances.length-1;t>=s;t--)this.__detachAndRemoveInstance(t)}__detachInstance(t){let e=this.__instances[t];const n=I(e.root);for(let t=0;t<e.children.length;t++){let r=e.children[t];n.appendChild(r)}return e}__attachInstance(t,e){let n=this.__instances[t];e.insertBefore(n.root,this)}__detachAndRemoveInstance(t){this.__detachInstance(t),this.__instances.splice(t,1)}__stampInstance(t,e,n){let r={};return r[this.as]=t,r[this.indexAs]=e,r[this.itemsIndexAs]=n,new this.__ctor(r)}__insertInstance(t,e,n){const r=this.__stampInstance(t,e,n);let s=this.__instances[e+1],i=s?s.children[0]:this;return I(I(this).parentNode).insertBefore(r.root,i),this.__instances[e]=r,r}_showHideChildren(t){for(let e=0;e<this.__instances.length;e++)this.__instances[e]._showHideChildren(t)}__handleItemPath(t,e){let n=t.slice(6),r=n.indexOf("."),s=r<0?n:n.substring(0,r);if(s==parseInt(s,10)){let t=r<0?"":n.substring(r+1);this.__handleObservedPaths(t);let i=this.__itemsIdxToInstIdx[s],o=this.__instances[i];if(o){let n=this.as+(t?"."+t:"");o._setPendingPropertyOrPath(n,e,!1,!0),o._flushProperties()}return!0}}itemForElement(t){let e=this.modelForElement(t);return e&&e[this.as]}indexForElement(t){let e=this.modelForElement(t);return e&&e[this.indexAs]}modelForElement(t){return bn(this.template,t)}}customElements.define(wn.is,wn);const En=d((t=>class extends t{_addEventListenerToNode(t,e,n){it(t,e,n)||super._addEventListenerToNode(t,e,n)}_removeEventListenerFromNode(t,e,n){ot(t,e,n)||super._removeEventListenerFromNode(t,e,n)}}));let Tn=!1,On=[],An=[];function Nn(){Tn=!0,requestAnimationFrame((function(){Tn=!1,function(t){for(;t.length;)xn(t.shift())}(On),setTimeout((function(){!function(t){for(let e=0,n=t.length;e<n;e++)xn(t.shift())}(An)}))}))}function xn(t){const e=t[0],n=t[1],r=t[2];try{n.apply(e,r)}catch(t){setTimeout((()=>{throw t}))}}function Sn(t,e,n){Tn||Nn(),On.push([t,e,n])}function In(t,e,n){Tn||Nn(),An.push([t,e,n])}function kn(){document.body.removeAttribute("unresolved")}function Ln(t,e,n){return{index:t,removed:e,addedCount:n}}"interactive"===document.readyState||"complete"===document.readyState?kn():window.addEventListener("DOMContentLoaded",kn);function Mn(t,e,n,r,s,i){let o,a=0,l=0,h=Math.min(n-e,i-s);if(0==e&&0==s&&(a=function(t,e,n){for(let r=0;r<n;r++)if(!Rn(t[r],e[r]))return r;return n}(t,r,h)),n==t.length&&i==r.length&&(l=function(t,e,n){let r=t.length,s=e.length,i=0;for(;i<n&&Rn(t[--r],e[--s]);)i++;return i}(t,r,h-a)),s+=a,i-=l,(n-=l)-(e+=a)==0&&i-s==0)return[];if(e==n){for(o=Ln(e,[],0);s<i;)o.removed.push(r[s++]);return[o]}if(s==i)return[Ln(e,[],n-e)];let c=function(t){let e=t.length-1,n=t[0].length-1,r=t[e][n],s=[];for(;e>0||n>0;){if(0==e){s.push(2),n--;continue}if(0==n){s.push(3),e--;continue}let i,o=t[e-1][n-1],a=t[e-1][n],l=t[e][n-1];i=a<l?a<o?a:o:l<o?l:o,i==o?(o==r?s.push(0):(s.push(1),r=o),e--,n--):i==a?(s.push(3),e--,r=a):(s.push(2),n--,r=l)}return s.reverse(),s}(function(t,e,n,r,s,i){let o=i-s+1,a=n-e+1,l=new Array(o);for(let t=0;t<o;t++)l[t]=new Array(a),l[t][0]=t;for(let t=0;t<a;t++)l[0][t]=t;for(let n=1;n<o;n++)for(let i=1;i<a;i++)if(Rn(t[e+i-1],r[s+n-1]))l[n][i]=l[n-1][i-1];else{let t=l[n-1][i]+1,e=l[n][i-1]+1;l[n][i]=t<e?t:e}return l}(t,e,n,r,s,i));o=void 0;let d=[],_=e,u=s;for(let t=0;t<c.length;t++)switch(c[t]){case 0:o&&(d.push(o),o=void 0),_++,u++;break;case 1:o||(o=Ln(_,[],0)),o.addedCount++,_++,o.removed.push(r[u]),u++;break;case 2:o||(o=Ln(_,[],0)),o.addedCount++,_++;break;case 3:o||(o=Ln(_,[],0)),o.removed.push(r[u]),u++}return o&&d.push(o),d}function Dn(t,e){return Mn(t,0,t.length,e,0,e.length)}function Rn(t,e){return t===e}function Fn(t){return"slot"===t.localName}let Hn=class{static getFlattenedNodes(t){const e=I(t);return Fn(t)?e.assignedNodes({flatten:!0}):Array.from(e.childNodes).map((t=>Fn(t)?I(t).assignedNodes({flatten:!0}):[t])).reduce(((t,e)=>t.concat(e)),[])}constructor(t,e){this._shadyChildrenObserver=null,this._nativeChildrenObserver=null,this._connected=!1,this._target=t,this.callback=e,this._effectiveNodes=[],this._observer=null,this._scheduled=!1,this._boundSchedule=()=>{this._schedule()},this.connect(),this._schedule()}connect(){Fn(this._target)?this._listenSlots([this._target]):I(this._target).children&&(this._listenSlots(I(this._target).children),this._nativeChildrenObserver=new MutationObserver((t=>{this._processMutations(t)})),this._nativeChildrenObserver.observe(this._target,{childList:!0})),this._connected=!0}disconnect(){Fn(this._target)?this._unlistenSlots([this._target]):I(this._target).children&&(this._unlistenSlots(I(this._target).children),this._nativeChildrenObserver&&(this._nativeChildrenObserver.disconnect(),this._nativeChildrenObserver=null)),this._connected=!1}_schedule(){this._scheduled||(this._scheduled=!0,h.run((()=>this.flush())))}_processMutations(t){this._processSlotMutations(t),this.flush()}_processSlotMutations(t){if(t)for(let e=0;e<t.length;e++){let n=t[e];n.addedNodes&&this._listenSlots(n.addedNodes),n.removedNodes&&this._unlistenSlots(n.removedNodes)}}flush(){if(!this._connected)return!1;this._nativeChildrenObserver?this._processSlotMutations(this._nativeChildrenObserver.takeRecords()):this._shadyChildrenObserver&&this._processSlotMutations(this._shadyChildrenObserver.takeRecords()),this._scheduled=!1;let t={target:this._target,addedNodes:[],removedNodes:[]},e=this.constructor.getFlattenedNodes(this._target),n=Dn(e,this._effectiveNodes);for(let e,r=0;r<n.length&&(e=n[r]);r++)for(let n,r=0;r<e.removed.length&&(n=e.removed[r]);r++)t.removedNodes.push(n);for(let r,s=0;s<n.length&&(r=n[s]);s++)for(let n=r.index;n<r.index+r.addedCount;n++)t.addedNodes.push(e[n]);this._effectiveNodes=e;let r=!1;return(t.addedNodes.length||t.removedNodes.length)&&(r=!0,this.callback.call(this._target,t)),r}_listenSlots(t){for(let e=0;e<t.length;e++){let n=t[e];Fn(n)&&n.addEventListener("slotchange",this._boundSchedule)}}_unlistenSlots(t){for(let e=0;e<t.length;e++){let n=t[e];Fn(n)&&n.removeEventListener("slotchange",this._boundSchedule)}}};const zn=Element.prototype,jn=zn.matches||zn.matchesSelector||zn.mozMatchesSelector||zn.msMatchesSelector||zn.oMatchesSelector||zn.webkitMatchesSelector,Bn=function(t,e){return jn.call(t,e)};class Jn{constructor(t){this.node=t}observeNodes(t){return new Hn(this.node,t)}unobserveNodes(t){t.disconnect()}notifyObserver(){}deepContains(t){if(I(this.node).contains(t))return!0;let e=t,n=t.ownerDocument;for(;e&&e!==n&&e!==this.node;)e=I(e).parentNode||I(e).host;return e===this.node}getOwnerRoot(){return I(this.node).getRootNode()}getDistributedNodes(){return"slot"===this.node.localName?I(this.node).assignedNodes({flatten:!0}):[]}getDestinationInsertionPoints(){let t=[],e=I(this.node).assignedSlot;for(;e;)t.push(e),e=I(e).assignedSlot;return t}importNode(t,e){let n=this.node instanceof Document?this.node:this.node.ownerDocument;return I(n).importNode(t,e)}getEffectiveChildNodes(){return Hn.getFlattenedNodes(this.node)}queryDistributedElements(t){let e=this.getEffectiveChildNodes(),n=[];for(let r,s=0,i=e.length;s<i&&(r=e[s]);s++)r.nodeType===Node.ELEMENT_NODE&&Bn(r,t)&&n.push(r);return n}get activeElement(){let t=this.node;return void 0!==t._activeElement?t._activeElement:t.activeElement}}function qn(t,e){for(let n=0;n<e.length;n++){let r=e[n];Object.defineProperty(t,r,{get:function(){return this.node[r]},configurable:!0})}}class Yn{constructor(t){this.event=t}get rootTarget(){return this.path[0]}get localTarget(){return this.event.target}get path(){return this.event.composedPath()}}Jn.prototype.cloneNode,Jn.prototype.appendChild,Jn.prototype.insertBefore,Jn.prototype.removeChild,Jn.prototype.replaceChild,Jn.prototype.setAttribute,Jn.prototype.removeAttribute,Jn.prototype.querySelector,Jn.prototype.querySelectorAll,Jn.prototype.parentNode,Jn.prototype.firstChild,Jn.prototype.lastChild,Jn.prototype.nextSibling,Jn.prototype.previousSibling,Jn.prototype.firstElementChild,Jn.prototype.lastElementChild,Jn.prototype.nextElementSibling,Jn.prototype.previousElementSibling,Jn.prototype.childNodes,Jn.prototype.children,Jn.prototype.classList,Jn.prototype.textContent,Jn.prototype.innerHTML;let $n=Jn;!function(t,e){for(let n=0;n<e.length;n++){let r=e[n];t[r]=function(){return this.node[r].apply(this.node,arguments)}}}(Jn.prototype,["cloneNode","appendChild","insertBefore","removeChild","replaceChild","setAttribute","removeAttribute","querySelector","querySelectorAll","attachShadow"]),qn(Jn.prototype,["parentNode","firstChild","lastChild","nextSibling","previousSibling","firstElementChild","lastElementChild","nextElementSibling","previousElementSibling","childNodes","children","classList","shadowRoot"]),function(t,e){for(let n=0;n<e.length;n++){let r=e[n];Object.defineProperty(t,r,{get:function(){return this.node[r]},set:function(t){this.node[r]=t},configurable:!0})}}(Jn.prototype,["textContent","innerHTML","className"]);const Un=function(t){if((t=t||document)instanceof $n)return t;if(t instanceof Yn)return t;let e=t.__domApi;return e||(e=t instanceof Event?new Yn(t):new $n(t),t.__domApi=e),e};const Vn="disable-upgrade",Xn=t=>{for(;t;){const e=Object.getOwnPropertyDescriptor(t,"observedAttributes");if(e)return e.get;t=Object.getPrototypeOf(t.prototype).constructor}return()=>[]};d((t=>{const e=Xe(t);let n=Xn(e);return class extends e{constructor(){super(),this.__isUpgradeDisabled}static get observedAttributes(){return n.call(this).concat(Vn)}_initializeProperties(){this.hasAttribute(Vn)?this.__isUpgradeDisabled=!0:super._initializeProperties()}_enableProperties(){this.__isUpgradeDisabled||super._enableProperties()}_canApplyPropertyDefault(t){return super._canApplyPropertyDefault(t)&&!(this.__isUpgradeDisabled&&this._isPropertyPending(t))}attributeChangedCallback(t,e,n,r){t==Vn?this.__isUpgradeDisabled&&null==n&&(super._initializeProperties(),this.__isUpgradeDisabled=!1,I(this).isConnected&&super.connectedCallback()):super.attributeChangedCallback(t,e,n,r)}connectedCallback(){this.__isUpgradeDisabled||super.connectedCallback()}disconnectedCallback(){this.__isUpgradeDisabled||super.disconnectedCallback()}}}));const Gn="disable-upgrade";const Wn=d((t=>{const e=En(Xe(t)),n=Xn(e),r={x:"pan-x",y:"pan-y",none:"none",all:"auto"};class s extends e{constructor(){super(),this.isAttached,this.__boundListeners,this._debouncers,this.__isUpgradeDisabled,this.__needsAttributesAtConnected,this._legacyForceObservedAttributes}static get importMeta(){return this.prototype.importMeta}created(){}__attributeReaction(t,e,n){(this.__dataAttributes&&this.__dataAttributes[t]||t===Gn)&&this.attributeChangedCallback(t,e,n,null)}setAttribute(t,e){super.setAttribute(t,e)}removeAttribute(t){super.removeAttribute(t)}static get observedAttributes(){return n.call(this).concat(Gn)}_enableProperties(){this.__isUpgradeDisabled||super._enableProperties()}_canApplyPropertyDefault(t){return super._canApplyPropertyDefault(t)&&!(this.__isUpgradeDisabled&&this._isPropertyPending(t))}connectedCallback(){this.__needsAttributesAtConnected&&this._takeAttributes(),this.__isUpgradeDisabled||(super.connectedCallback(),this.isAttached=!0,this.attached())}attached(){}disconnectedCallback(){this.__isUpgradeDisabled||(super.disconnectedCallback(),this.isAttached=!1,this.detached())}detached(){}attributeChangedCallback(t,e,n,r){e!==n&&(t==Gn?this.__isUpgradeDisabled&&null==n&&(this._initializeProperties(),this.__isUpgradeDisabled=!1,I(this).isConnected&&this.connectedCallback()):(super.attributeChangedCallback(t,e,n,r),this.attributeChanged(t,e,n)))}attributeChanged(t,e,n){}_initializeProperties(){if(x&&this.hasAttribute(Gn))this.__isUpgradeDisabled=!0;else{let t=Object.getPrototypeOf(this);t.hasOwnProperty(JSCompiler_renameProperty("__hasRegisterFinished",t))||(this._registered(),t.__hasRegisterFinished=!0),super._initializeProperties(),this.root=this,this.created(),this._applyListeners()}}_takeAttributes(){const t=this.attributes;for(let e=0,n=t.length;e<n;e++){const n=t[e];this.__attributeReaction(n.name,null,n.value)}}_registered(){}ready(){this._ensureAttributes(),super.ready()}_ensureAttributes(){}_applyListeners(){}serialize(t){return this._serializeValue(t)}deserialize(t,e){return this._deserializeValue(t,e)}reflectPropertyToAttribute(t,e,n){this._propertyToAttribute(t,e,n)}serializeValueToAttribute(t,e,n){this._valueToNodeAttribute(n||this,t,e)}extend(t,e){if(!t||!e)return t||e;let n=Object.getOwnPropertyNames(e);for(let r,s=0;s<n.length&&(r=n[s]);s++){let n=Object.getOwnPropertyDescriptor(e,r);n&&Object.defineProperty(t,r,n)}return t}mixin(t,e){for(let n in e)t[n]=e[n];return t}chainObject(t,e){return t&&e&&t!==e&&(t.__proto__=e),t}instanceTemplate(t){let e=this.constructor._contentForTemplate(t);return document.importNode(e,!0)}fire(t,e,n){n=n||{},e=null==e?{}:e;let r=new Event(t,{bubbles:void 0===n.bubbles||n.bubbles,cancelable:Boolean(n.cancelable),composed:void 0===n.composed||n.composed});r.detail=e;let s=n.node||this;return I(s).dispatchEvent(r),r}listen(t,e,n){t=t||this;let r=this.__boundListeners||(this.__boundListeners=new WeakMap),s=r.get(t);s||(s={},r.set(t,s));let i=e+n;s[i]||(s[i]=this._addMethodEventListenerToNode(t,e,n,this))}unlisten(t,e,n){t=t||this;let r=this.__boundListeners&&this.__boundListeners.get(t),s=e+n,i=r&&r[s];i&&(this._removeEventListenerFromNode(t,e,i),r[s]=null)}setScrollDirection(t,e){lt(e||this,r[t]||"auto")}$$(t){return this.root.querySelector(t)}get domHost(){let t=I(this).getRootNode();return t instanceof DocumentFragment?t.host:t}distributeContent(){Un(this)}getEffectiveChildNodes(){return Un(this).getEffectiveChildNodes()}queryDistributedElements(t){return Un(this).queryDistributedElements(t)}getEffectiveChildren(){return this.getEffectiveChildNodes().filter((function(t){return t.nodeType===Node.ELEMENT_NODE}))}getEffectiveTextContent(){let t=this.getEffectiveChildNodes(),e=[];for(let n,r=0;n=t[r];r++)n.nodeType!==Node.COMMENT_NODE&&e.push(n.textContent);return e.join("")}queryEffectiveChildren(t){let e=this.queryDistributedElements(t);return e&&e[0]}queryAllEffectiveChildren(t){return this.queryDistributedElements(t)}getContentChildNodes(t){let e=this.root.querySelector(t||"slot");return e?Un(e).getDistributedNodes():[]}getContentChildren(t){return this.getContentChildNodes(t).filter((function(t){return t.nodeType===Node.ELEMENT_NODE}))}isLightDescendant(t){const e=this;return e!==t&&I(e).contains(t)&&I(e).getRootNode()===I(t).getRootNode()}isLocalDescendant(t){return this.root===I(t).getRootNode()}scopeSubtree(t,e=!1){return null}getComputedStyleValue(t){return false.getComputedStyleValue(this,t)}debounce(t,e,n){return this._debouncers=this._debouncers||{},this._debouncers[t]=_.debounce(this._debouncers[t],n>0?o.after(n):h,e.bind(this))}isDebouncerActive(t){this._debouncers=this._debouncers||{};let e=this._debouncers[t];return!(!e||!e.isActive())}flushDebouncer(t){this._debouncers=this._debouncers||{};let e=this._debouncers[t];e&&e.flush()}cancelDebouncer(t){this._debouncers=this._debouncers||{};let e=this._debouncers[t];e&&e.cancel()}async(t,e){return e>0?o.run(t.bind(this),e):~h.run(t.bind(this))}cancelAsync(t){t<0?h.cancel(~t):o.cancel(t)}create(t,e){let n=document.createElement(t);if(e)if(n.setProperties)n.setProperties(e);else for(let t in e)n[t]=e[t];return n}elementMatches(t,e){return Bn(e||this,t)}toggleAttribute(t,e){let n=this;return 3===arguments.length&&(n=arguments[2]),1==arguments.length&&(e=!n.hasAttribute(t)),e?(I(n).setAttribute(t,""),!0):(I(n).removeAttribute(t),!1)}toggleClass(t,e,n){n=n||this,1==arguments.length&&(e=!n.classList.contains(t)),e?n.classList.add(t):n.classList.remove(t)}transform(t,e){(e=e||this).style.webkitTransform=t,e.style.transform=t}translate3d(t,e,n,r){r=r||this,this.transform("translate3d("+t+","+e+","+n+")",r)}arrayDelete(t,e){let n;if(Array.isArray(t)){if(n=t.indexOf(e),n>=0)return t.splice(n,1)}else{if(n=jt(this,t).indexOf(e),n>=0)return this.splice(t,n,1)}return null}_logger(t,e){switch(Array.isArray(e)&&1===e.length&&Array.isArray(e[0])&&(e=e[0]),t){case"log":case"warn":case"error":console[t](...e)}}_log(...t){this._logger("log",t)}_warn(...t){this._logger("warn",t)}_error(...t){this._logger("error",t)}_logf(t,...e){return["[%s::%s]",this.is,t,...e]}}return s.prototype.is="",s})),Zn={attached:!0,detached:!0,ready:!0,created:!0,beforeRegister:!0,registered:!0,attributeChanged:!0,listeners:!0,hostAttributes:!0},Kn={attached:!0,detached:!0,ready:!0,created:!0,beforeRegister:!0,registered:!0,attributeChanged:!0,behaviors:!0,_noAccessors:!0},Qn=Object.assign({listeners:!0,hostAttributes:!0,properties:!0,observers:!0},Kn);function tr(t,e){return ir({},Wn(e),t)}function er(t,e,n,r){!function(t,e,n){const r=t._noAccessors,s=Object.getOwnPropertyNames(t);for(let i=0;i<s.length;i++){let o=s[i];if(!(o in n))if(r)e[o]=t[o];else{let n=Object.getOwnPropertyDescriptor(t,o);n&&(n.configurable=!0,Object.defineProperty(e,o,n))}}}(e,t,r);for(let t in Zn)e[t]&&(n[t]=n[t]||[],n[t].push(e[t]))}function nr(t,e,n){e=e||[];for(let r=t.length-1;r>=0;r--){let s=t[r];s?Array.isArray(s)?nr(s,e):e.indexOf(s)<0&&(!n||n.indexOf(s)<0)&&e.unshift(s):console.warn("behavior is null, check for missing or 404 import")}return e}function rr(t,e){for(const n in e){const r=t[n],s=e[n];t[n]=!("value"in s)&&r&&"value"in r?Object.assign({value:r.value},s):s}}const sr=Wn(HTMLElement);function ir(t,e,n){let r;const s={};class i extends e{static _finalizeClass(){if(this.hasOwnProperty(JSCompiler_renameProperty("generatedFrom",this))){if(r)for(let t,e=0;e<r.length;e++)t=r[e],t.properties&&this.createProperties(t.properties),t.observers&&this.createObservers(t.observers,t.properties);t.properties&&this.createProperties(t.properties),t.observers&&this.createObservers(t.observers,t.properties),this._prepareTemplate()}else e._finalizeClass.call(this)}static get properties(){const e={};if(r)for(let t=0;t<r.length;t++)rr(e,r[t].properties);return rr(e,t.properties),e}static get observers(){let e=[];if(r)for(let t,n=0;n<r.length;n++)t=r[n],t.observers&&(e=e.concat(t.observers));return t.observers&&(e=e.concat(t.observers)),e}created(){super.created();const t=s.created;if(t)for(let e=0;e<t.length;e++)t[e].call(this)}_registered(){const t=i.prototype;if(!t.hasOwnProperty(JSCompiler_renameProperty("__hasRegisterFinished",t))){t.__hasRegisterFinished=!0,super._registered(),x&&o(t);const e=Object.getPrototypeOf(this);let n=s.beforeRegister;if(n)for(let t=0;t<n.length;t++)n[t].call(e);if(n=s.registered,n)for(let t=0;t<n.length;t++)n[t].call(e)}}_applyListeners(){super._applyListeners();const t=s.listeners;if(t)for(let e=0;e<t.length;e++){const n=t[e];if(n)for(let t in n)this._addMethodEventListenerToNode(this,t,n[t])}}_ensureAttributes(){const t=s.hostAttributes;if(t)for(let e=t.length-1;e>=0;e--){const n=t[e];for(let t in n)this._ensureAttribute(t,n[t])}super._ensureAttributes()}ready(){super.ready();let t=s.ready;if(t)for(let e=0;e<t.length;e++)t[e].call(this)}attached(){super.attached();let t=s.attached;if(t)for(let e=0;e<t.length;e++)t[e].call(this)}detached(){super.detached();let t=s.detached;if(t)for(let e=0;e<t.length;e++)t[e].call(this)}attributeChanged(t,e,n){super.attributeChanged();let r=s.attributeChanged;if(r)for(let s=0;s<r.length;s++)r[s].call(this,t,e,n)}}if(n){Array.isArray(n)||(n=[n]);let t=e.prototype.behaviors;r=nr(n,null,t),i.prototype.behaviors=t?t.concat(n):r}const o=e=>{r&&function(t,e,n){for(let r=0;r<e.length;r++)er(t,e[r],n,Qn)}(e,r,s),er(e,t,s,Kn)};return x||o(i.prototype),i.generatedFrom=t,i}let or;or=rn._mutablePropertyChange;const ar={properties:{mutableData:Boolean},_shouldPropertyChange(t,e,n){return or(this,t,e,n,this.mutableData)}},lr=function(t){let e;return e="function"==typeof t?t:lr.Class(t),t._legacyForceObservedAttributes&&(e.prototype._legacyForceObservedAttributes=t._legacyForceObservedAttributes),customElements.define(e.is,e),e};lr.Class=function(t,e){t||console.warn("Polymer.Class requires `info` argument");let n=e?e(sr):sr;return n=ir(t,n,t.behaviors),n.is=n.prototype.is=t.is,n};const hr={templatize(t,e){this._templatizerTemplate=t,this.ctor=gn(t,this,{mutableData:Boolean(e),parentModel:this._parentModel,instanceProps:this._instanceProps,forwardHostProp:this._forwardHostPropV2,notifyInstanceProp:this._notifyInstancePropV2})},stamp(t){return new this.ctor(t)},modelForElement(t){return bn(this._templatizerTemplate,t)}},cr=En(sn($e(HTMLElement)));customElements.define("dom-bind",class extends cr{static get observedAttributes(){return["mutable-data"]}constructor(){if(super(),A)throw new Error("strictTemplatePolicy: dom-bind not allowed");this.root=null,this.$=null,this.__children=null}attributeChangedCallback(t,e,n,r){this.mutableData=!0}connectedCallback(){this.style.display="none",this.render()}disconnectedCallback(){this.__removeChildren()}__insertChildren(){I(I(this).parentNode).insertBefore(this.root,this)}__removeChildren(){if(this.__children)for(let t=0;t<this.__children.length;t++)this.root.appendChild(this.__children[t])}render(){let t;if(!this.__children){if(t=t||this.querySelector("template"),!t){let e=new MutationObserver((()=>{if(t=this.querySelector("template"),!t)throw new Error("dom-bind requires a <template> child");e.disconnect(),this.render()}));return void e.observe(this,{childList:!0})}this.root=this._stampTemplate(t),this.$=this.root.$,this.__children=[];for(let t=this.root.firstChild;t;t=t.nextSibling)this.__children[this.__children.length]=t;this._enableProperties()}this.__insertChildren(),this.dispatchEvent(new CustomEvent("dom-change",{bubbles:!0,composed:!0}))}});let dr=d((t=>{let e=Xe(t);return class extends e{static get properties(){return{items:{type:Array},multi:{type:Boolean,value:!1},selected:{type:Object,notify:!0},selectedItem:{type:Object,notify:!0},toggle:{type:Boolean,value:!1}}}static get observers(){return["__updateSelection(multi, items.*)"]}constructor(){super(),this.__lastItems=null,this.__lastMulti=null,this.__selectedMap=null}__updateSelection(t,e){let n=e.path;if(n==JSCompiler_renameProperty("items",this)){let n=e.base||[],r=this.__lastItems;if(t!==this.__lastMulti&&this.clearSelection(),r){let t=Dn(n,r);this.__applySplices(t)}this.__lastItems=n,this.__lastMulti=t}else if(e.path==`${JSCompiler_renameProperty("items",this)}.splices`)this.__applySplices(e.value.indexSplices);else{let t=n.slice(`${JSCompiler_renameProperty("items",this)}.`.length),e=parseInt(t,10);t.indexOf(".")<0&&t==e&&this.__deselectChangedIdx(e)}}__applySplices(t){let e=this.__selectedMap;for(let n=0;n<t.length;n++){let r=t[n];e.forEach(((t,n)=>{t<r.index||(t>=r.index+r.removed.length?e.set(n,t+r.addedCount-r.removed.length):e.set(n,-1))}));for(let t=0;t<r.addedCount;t++){let n=r.index+t;e.has(this.items[n])&&e.set(this.items[n],n)}}this.__updateLinks();let n=0;e.forEach(((t,r)=>{t<0?(this.multi?this.splice(JSCompiler_renameProperty("selected",this),n,1):this.selected=this.selectedItem=null,e.delete(r)):n++}))}__updateLinks(){if(this.__dataLinkedPaths={},this.multi){let t=0;this.__selectedMap.forEach((e=>{e>=0&&this.linkPaths(`${JSCompiler_renameProperty("items",this)}.${e}`,`${JSCompiler_renameProperty("selected",this)}.${t++}`)}))}else this.__selectedMap.forEach((t=>{this.linkPaths(JSCompiler_renameProperty("selected",this),`${JSCompiler_renameProperty("items",this)}.${t}`),this.linkPaths(JSCompiler_renameProperty("selectedItem",this),`${JSCompiler_renameProperty("items",this)}.${t}`)}))}clearSelection(){this.__dataLinkedPaths={},this.__selectedMap=new Map,this.selected=this.multi?[]:null,this.selectedItem=null}isSelected(t){return this.__selectedMap.has(t)}isIndexSelected(t){return this.isSelected(this.items[t])}__deselectChangedIdx(t){let e=this.__selectedIndexForItemIndex(t);if(e>=0){let t=0;this.__selectedMap.forEach(((n,r)=>{e==t++&&this.deselect(r)}))}}__selectedIndexForItemIndex(t){let e=this.__dataLinkedPaths[`${JSCompiler_renameProperty("items",this)}.${t}`];if(e)return parseInt(e.slice(`${JSCompiler_renameProperty("selected",this)}.`.length),10)}deselect(t){let e=this.__selectedMap.get(t);if(e>=0){let n;this.__selectedMap.delete(t),this.multi&&(n=this.__selectedIndexForItemIndex(e)),this.__updateLinks(),this.multi?this.splice(JSCompiler_renameProperty("selected",this),n,1):this.selected=this.selectedItem=null}}deselectIndex(t){this.deselect(this.items[t])}select(t){this.selectIndex(this.items.indexOf(t))}selectIndex(t){let e=this.items[t];this.isSelected(e)?this.toggle&&this.deselectIndex(t):(this.multi||this.__selectedMap.clear(),this.__selectedMap.set(e,t),this.__updateLinks(),this.multi?this.push(JSCompiler_renameProperty("selected",this),e):this.selected=this.selectedItem=e)}}}))(tn);class _r extends dr{static get is(){return"array-selector"}static get template(){return null}}customElements.define(_r.is,_r);const ur="include";class pr extends HTMLElement{constructor(){super(),this._style=null}getStyle(){if(this._style)return this._style;const t=this.querySelector("style");if(!t)return null;this._style=t;const e=t.getAttribute(ur);return e&&(t.removeAttribute(ur),t.textContent=function(t){let e=t.trim().split(/\s+/),n="";for(let t=0;t<e.length;t++)n+=It(e[t]);return n}(e)+t.textContent),this.ownerDocument!==window.document&&window.document.head.appendChild(this),this._style}}window.customElements.define("custom-style",pr);const fr=Wn(HTMLElement).prototype;export{fr as Base,_ as Debouncer,vn as DomIf,wn as DomRepeat,Hn as FlattenedNodesObserver,ar as OptionalMutableDataBehavior,lr as Polymer,tn as PolymerElement,dn as TemplateInstanceBase,hr as Templatizer,In as afterNextRender,a as animationFrame,Sn as beforeNextRender,Dn as calculateSplices,$t as dashToCamelCase,d as dedupingMixin,Un as dom,p as enqueueDebouncer,en as flush,gt as gestures,jt as get,Ke as html,l as idlePeriod,Ft as matches,h as microTask,tr as mixinBehaviors,gn as templatize,o as timeOut,Rt as translate,w as useShadow};
\ No newline at end of file
diff --git a/third_party/polymer/v3_0/minify_polymer.py b/third_party/polymer/v3_0/minify_polymer.py
index 007f3c9..ba46a1e5 100644
--- a/third_party/polymer/v3_0/minify_polymer.py
+++ b/third_party/polymer/v3_0/minify_polymer.py
@@ -59,8 +59,9 @@
         #'--comments', '/Copyright|license|LICENSE/',
 
         # Declare global variables as false, to prune out unnecessary code.
-        '--define', 'window.ShadyDOM=false',
+        '--define', 'window.HTMLImports=false',
         '--define', 'window.ShadyCSS=false',
+        '--define', 'window.ShadyDOM=false',
         '--define', 'window.Polymer.legacyOptimizations=false',
 
         '--output', minified_js])
diff --git a/tools/cast3p/cast_core.version b/tools/cast3p/cast_core.version
index 1111ead..2edf240 100644
--- a/tools/cast3p/cast_core.version
+++ b/tools/cast3p/cast_core.version
@@ -1 +1 @@
-cast_20230321_1054_RC00
+cast_20230306_0601_RC04
diff --git a/tools/cast3p/runtime.version b/tools/cast3p/runtime.version
index 2ea448da..230e423 100644
--- a/tools/cast3p/runtime.version
+++ b/tools/cast3p/runtime.version
@@ -1 +1 @@
-350948
+352020
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 23f689a..0a683665 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -62622,6 +62622,7 @@
   <int value="520982116" label="BuiltInModuleAll:enabled"/>
   <int value="521992655" label="LauncherPlayStoreSearch:disabled"/>
   <int value="522013077" label="LinkCapturingAutoDisplayIntentPicker:disabled"/>
+  <int value="524557829" label="OmniboxSteadyStateTextColor:enabled"/>
   <int value="525286392" label="PageInfoAboutThisSiteNonMsbb:disabled"/>
   <int value="527140412"
       label="AndroidAutofillViewStructureWithFormHierarchyLayer:disabled"/>
@@ -62882,7 +62883,6 @@
   <int value="670404276" label="ImeUsEnglishModelUpdate:disabled"/>
   <int value="671427343"
       label="DnsOverHttpsWithIdentifiersReuseOldPolicy:enabled"/>
-  <int value="671800756" label="TabStripImprovements:enabled"/>
   <int value="672067370" label="InstallableAmbientBadgeMessage:enabled"/>
   <int value="672255299" label="WebAuthFlowInBrowserTab:disabled"/>
   <int value="672599279" label="WebUiSystemFont:disabled"/>
@@ -65501,6 +65501,7 @@
   <int value="2107406401" label="UseDnsHttpsSvcb:disabled"/>
   <int value="2107520493" label="PolicyMergeMultiSource:enabled"/>
   <int value="2107942975" label="ApnRevamp:disabled"/>
+  <int value="2109483186" label="OmniboxSteadyStateTextColor:disabled"/>
   <int value="2110109003" label="WebAssemblyRelaxedSimd:disabled"/>
   <int value="2110832901" label="OmniboxPedalsTranslationConsole:enabled"/>
   <int value="2112312086" label="EditContext:disabled"/>
diff --git a/tools/metrics/histograms/metadata/language/histograms.xml b/tools/metrics/histograms/metadata/language/histograms.xml
index 2dbd7a25..49bba81 100644
--- a/tools/metrics/histograms/metadata/language/histograms.xml
+++ b/tools/metrics/histograms/metadata/language/histograms.xml
@@ -79,7 +79,7 @@
 </histogram>
 
 <histogram name="LanguageSettings.AppLanguagePrompt.Action"
-    enum="LanguageSettingsAppLanguagePromptAction" expires_after="2023-08-08">
+    enum="LanguageSettingsAppLanguagePromptAction" expires_after="2023-09-22">
   <owner>perrier@chromium.org</owner>
   <owner>chrome-language@google.com</owner>
   <summary>
@@ -90,24 +90,8 @@
   </summary>
 </histogram>
 
-<histogram name="LanguageSettings.AppLanguagePrompt.HasTopULPMatch"
-    enum="BooleanYesNo" expires_after="2023-01-15">
-  <obsolete>
-    Replaced by LanguageSettings.AppLanguagePrompt.HasTopULPMatchOrEmpty
-  </obsolete>
-  <owner>perrier@chromium.org</owner>
-  <owner>chrome-language@google.com</owner>
-  <summary>
-    Whether or not the Chrome UI language matches the top ULP language when the
-    AppLanguagePrompt should be shown. Logged when checking if the prompt should
-    be shown and it has not already been shown.
-
-    Only the base languages are compared so pt-PT is considered equal to pt-BR.
-  </summary>
-</histogram>
-
 <histogram name="LanguageSettings.AppLanguagePrompt.IsOnline"
-    enum="BooleanYesNo" expires_after="2023-08-20">
+    enum="BooleanYesNo" expires_after="2023-09-22">
   <owner>perrier@chromium.org</owner>
   <owner>chrome-language@google.com</owner>
   <summary>
@@ -121,7 +105,7 @@
 </histogram>
 
 <histogram name="LanguageSettings.AppLanguagePrompt.IsTopLanguageSelected"
-    enum="BooleanYesNo" expires_after="2023-08-08">
+    enum="BooleanYesNo" expires_after="2023-09-22">
   <owner>perrier@chromium.org</owner>
   <owner>chrome-language@google.com</owner>
   <summary>
@@ -135,7 +119,7 @@
 </histogram>
 
 <histogram name="LanguageSettings.AppLanguagePrompt.Language"
-    enum="LocaleCodeISO639" expires_after="2023-08-31">
+    enum="LocaleCodeISO639" expires_after="2023-09-22">
   <owner>perrier@chromium.org</owner>
   <owner>chrome-language@google.com</owner>
   <summary>
@@ -149,30 +133,8 @@
   </summary>
 </histogram>
 
-<histogram name="LanguageSettings.AppLanguagePrompt.OpenDuration.{ActionType}"
-    units="ms" expires_after="2022-06-30">
-  <obsolete>
-    Deleted May 2022
-  </obsolete>
-  <owner>perrier@chromium.org</owner>
-  <owner>chrome-language@google.com</owner>
-  <summary>
-    The length of time between showing the app lanugage prompt and the user
-    pressing {ActionType}.
-
-    An action must be taken if the prompt is shown, unless the application is
-    closed while the prompt is displayed. Reported when the prompt is dismissed.
-  </summary>
-  <token key="ActionType">
-    <variant name="Back" summary="system back"/>
-    <variant name="Cancel" summary="the cancel button"/>
-    <variant name="Change" summary="OK changing the app language"/>
-    <variant name="Same" summary="OK for the current app language"/>
-  </token>
-</histogram>
-
 <histogram name="LanguageSettings.AppLanguagePrompt.OtherLanguagesShown"
-    enum="BooleanYesNo" expires_after="2023-08-31">
+    enum="BooleanYesNo" expires_after="2023-09-22">
   <owner>perrier@chromium.org</owner>
   <owner>chrome-language@google.com</owner>
   <summary>
@@ -184,7 +146,7 @@
 </histogram>
 
 <histogram name="LanguageSettings.AppLanguagePrompt.TopLanguageCount"
-    units="count" expires_after="2023-03-19">
+    units="count" expires_after="2023-09-22">
   <owner>perrier@chromium.org</owner>
   <owner>chrome-language@google.com</owner>
   <summary>
@@ -196,7 +158,7 @@
 </histogram>
 
 <histogram name="LanguageSettings.AppLanguagePrompt.TopULPMatchStatus"
-    enum="ULPTopLanguageMatch" expires_after="2023-08-06">
+    enum="ULPTopLanguageMatch" expires_after="2023-09-22">
   <owner>perrier@chromium.org</owner>
   <owner>chrome-language@google.com</owner>
   <summary>
@@ -273,20 +235,6 @@
   </summary>
 </histogram>
 
-<histogram name="LanguageUsage.ApplicationLanguage" enum="LanguageName"
-    expires_after="2023-03-19">
-  <obsolete>
-    Removed in M109. Use UMA.SystemProfile.ApplicationLocale which is a
-    synthetic histogram that supports country codes.
-  </obsolete>
-  <owner>perrier@chromium.org</owner>
-  <owner>chrome-language@google.com</owner>
-  <summary>
-    Application languages used for UI recorded once per profile initialization.
-    Incognito profiles are ignored.
-  </summary>
-</histogram>
-
 <histogram name="LanguageUsage.MostFrequentPageLanguages" enum="LanguageName"
     expires_after="2023-08-08">
   <owner>perrier@chromium.org</owner>
@@ -408,7 +356,7 @@
 
 <histogram
     name="LanguageUsage.ULP.Initiation.AcceptLanguagesPageLanguageOverlap.Base"
-    units="%" expires_after="2023-03-19">
+    units="%" expires_after="2023-09-22">
   <owner>perrier@google.com</owner>
   <owner>chrome-language@google.com</owner>
   <summary>
@@ -422,25 +370,8 @@
   </summary>
 </histogram>
 
-<histogram name="LanguageUsage.ULP.Initiation.AcceptLanguagesULPOverlap"
-    units="%" expires_after="2022-07-22">
-  <obsolete>
-    Replaced by LanguageUsage.ULP.Initiation.AcceptLanguagesULPOverlap.Base
-  </obsolete>
-  <owner>perrier@google.com</owner>
-  <owner>chrome-language@google.com</owner>
-  <summary>
-    The ratio of ULP languages in the user's Accept Languages over the total
-    number of languages in their LanguageProfile. This answers the question of
-    &quot;How many of the predicted ULP languages have already been added by the
-    user?&quot;.
-
-    Recorded once at startup when ULP is initialized.
-  </summary>
-</histogram>
-
 <histogram name="LanguageUsage.ULP.Initiation.AcceptLanguagesULPOverlap.Base"
-    units="%" expires_after="2023-08-08">
+    units="%" expires_after="2023-09-22">
   <owner>perrier@google.com</owner>
   <owner>chrome-language@google.com</owner>
   <summary>
@@ -455,7 +386,7 @@
 </histogram>
 
 <histogram name="LanguageUsage.ULP.Initiation.ChromeUILanguageInULP"
-    enum="ULPLanguageStatus" expires_after="2023-08-08">
+    enum="ULPLanguageStatus" expires_after="2023-09-22">
   <owner>perrier@google.com</owner>
   <owner>chrome-language@google.com</owner>
   <summary>
@@ -468,7 +399,7 @@
 </histogram>
 
 <histogram name="LanguageUsage.ULP.Initiation.Count" units="count"
-    expires_after="2023-08-08">
+    expires_after="2023-09-22">
   <owner>perrier@google.com</owner>
   <owner>chrome-language@google.com</owner>
   <summary>
@@ -479,7 +410,7 @@
 </histogram>
 
 <histogram name="LanguageUsage.ULP.Initiation.NeverLanguagesMissingFromULP"
-    enum="LocaleCodeISO639" expires_after="2023-08-08">
+    enum="LocaleCodeISO639" expires_after="2023-09-22">
   <owner>perrier@google.com</owner>
   <owner>chrome-language@google.com</owner>
   <summary>
@@ -492,7 +423,7 @@
 
 <histogram
     name="LanguageUsage.ULP.Initiation.NeverLanguagesMissingFromULP.Count"
-    units="count" expires_after="2023-08-08">
+    units="count" expires_after="2023-09-22">
   <owner>perrier@google.com</owner>
   <owner>chrome-language@google.com</owner>
   <summary>
@@ -503,7 +434,7 @@
 </histogram>
 
 <histogram name="LanguageUsage.ULP.Initiation.PageLanguagesMissingFromULP"
-    enum="LocaleCodeISO639" expires_after="2023-03-19">
+    enum="LocaleCodeISO639" expires_after="2023-09-22">
   <owner>perrier@google.com</owner>
   <owner>chrome-language@google.com</owner>
   <summary>
@@ -515,7 +446,7 @@
 
 <histogram
     name="LanguageUsage.ULP.Initiation.PageLanguagesMissingFromULP.Count"
-    units="count" expires_after="2023-03-19">
+    units="count" expires_after="2023-09-22">
   <owner>perrier@google.com</owner>
   <owner>chrome-language@google.com</owner>
   <summary>
@@ -526,7 +457,7 @@
 </histogram>
 
 <histogram name="LanguageUsage.ULP.Initiation.Status{AccountType}"
-    enum="ULPInitiationStatus" expires_after="2023-08-31">
+    enum="ULPInitiationStatus" expires_after="2023-09-22">
   <owner>perrier@google.com</owner>
   <owner>chrome-language@google.com</owner>
   <summary>
@@ -542,7 +473,7 @@
 </histogram>
 
 <histogram name="LanguageUsage.ULP.Initiation.TopAcceptLanguageInULP"
-    enum="ULPLanguageStatus" expires_after="2023-08-08">
+    enum="ULPLanguageStatus" expires_after="2023-09-22">
   <owner>perrier@google.com</owner>
   <owner>chrome-language@google.com</owner>
   <summary>
@@ -555,7 +486,7 @@
 </histogram>
 
 <histogram name="LanguageUsage.ULP.Initiation.TranslateTargetInULP"
-    enum="ULPLanguageStatus" expires_after="2023-08-08">
+    enum="ULPLanguageStatus" expires_after="2023-09-22">
   <owner>perrier@google.com</owner>
   <owner>chrome-language@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/sb_client/histograms.xml b/tools/metrics/histograms/metadata/sb_client/histograms.xml
index 42e11612..6accb18 100644
--- a/tools/metrics/histograms/metadata/sb_client/histograms.xml
+++ b/tools/metrics/histograms/metadata/sb_client/histograms.xml
@@ -358,7 +358,7 @@
 </histogram>
 
 <histogram name="SBClientDownload.MalwareDeepScanResult.{trigger}"
-    enum="SBClientDownloadCheckResult" expires_after="2023-04-28">
+    enum="SBClientDownloadCheckResult" expires_after="2024-04-28">
   <owner>drubery@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/variations/histograms.xml b/tools/metrics/histograms/metadata/variations/histograms.xml
index e74fe8a..f43c33af 100644
--- a/tools/metrics/histograms/metadata/variations/histograms.xml
+++ b/tools/metrics/histograms/metadata/variations/histograms.xml
@@ -452,7 +452,7 @@
 </histogram>
 
 <histogram name="Variations.SafeModeCachedFlags.Streak.Crashes" units="crashes"
-    expires_after="2023-09-03">
+    expires_after="2024-03-25">
   <owner>hnakashima@chromium.org</owner>
   <owner>src/chrome/browser/flags/OWNERS</owner>
   <summary>
@@ -465,7 +465,7 @@
 </histogram>
 
 <histogram name="Variations.SafeModeCachedFlags.{Event}"
-    enum="VariationsSafeModeCachedFlagsBehavior" expires_after="2023-05-01">
+    enum="VariationsSafeModeCachedFlagsBehavior" expires_after="2024-03-25">
   <owner>hnakashima@chromium.org</owner>
   <owner>src/chrome/browser/flags/OWNERS</owner>
   <summary>
diff --git a/tools/style_variable_generator/base_generator.py b/tools/style_variable_generator/base_generator.py
index 6dcf344..b0352580 100644
--- a/tools/style_variable_generator/base_generator.py
+++ b/tools/style_variable_generator/base_generator.py
@@ -45,19 +45,19 @@
         # If specified, only generates the given mode.
         self.generate_single_mode = None
 
-        # If true, resolves all blend() colors to the RGBA values at
-        # compile time.
-        self.resolve_blended_colors = True
-
         # A dictionary of options used to alter generator function. See
         # ./README.md for each generators list of options.
         self.generator_options = {}
 
 
+    # If true, resolves all blend() colors to the RGBA values at
+    # compile time.
+    def ShouldResolveBlendedColors(self):
+        return True
+
     def GetInputFiles(self):
         return sorted(self.in_file_to_context.keys())
 
-
     def AddJSONFilesToModel(self, paths):
         '''Adds one or more JSON files to the model.
         '''
@@ -69,7 +69,7 @@
                 raise ValueError(f'Could not add {path}') from err
 
         self.model.PostProcess(
-            resolve_blended_colors=self.resolve_blended_colors)
+            resolve_blended_colors=self.ShouldResolveBlendedColors())
 
     def AddJSONToModel(self, json_string, in_file=None):
         '''Adds a |json_string| with variable definitions to the model.
diff --git a/tools/style_variable_generator/color_mappings_generator.py b/tools/style_variable_generator/color_mappings_generator.py
index 9bd4468..f15db0d 100644
--- a/tools/style_variable_generator/color_mappings_generator.py
+++ b/tools/style_variable_generator/color_mappings_generator.py
@@ -18,10 +18,6 @@
     def GetName():
         return 'ColorMappings'
 
-    def __init__(self):
-        super().__init__()
-        self.resolve_blended_colors = False
-
     def GetParameters(self):
         return {
             'color_mappings': self._CreateMappings(),
@@ -55,6 +51,9 @@
 
         return globals
 
+    def ShouldResolveBlendedColors(self):
+        return False
+
     def _CreateMappings(self):
         mappings = collections.defaultdict(list)
         for name, mode_values in self.model.colors.items():
diff --git a/tools/style_variable_generator/css_generator.py b/tools/style_variable_generator/css_generator.py
index 49dc90f..55cf962 100644
--- a/tools/style_variable_generator/css_generator.py
+++ b/tools/style_variable_generator/css_generator.py
@@ -44,7 +44,6 @@
     def GetFilters(self):
         return {
             'to_css_var_name': self.ToCSSVarName,
-            'css_color': self._CSSColor,
             'css_opacity': self._CSSOpacity,
             'css_color_rgb': self.CSSColorRGB,
             'process_simple_ref': self.ProcessSimpleRef,
@@ -54,6 +53,8 @@
         return {
             'css_color_var':
             self.CSSColorVar,
+            'needs_rgb_variant':
+            self.NeedsRGBVariant,
             'in_files':
             self.GetInputFiles(),
             'dark_mode_selector':
@@ -64,6 +65,9 @@
             Modes,
         }
 
+    def ShouldResolveBlendedColors(self):
+        return False
+
     def AddGeneratedVars(self, var_names, variable):
         def AddVarNames(name, variations):
             for v in variations:
@@ -129,25 +133,6 @@
 
         return ('%f' % opacity.a).rstrip('0').rstrip('.')
 
-    def _CSSColor(self, c):
-        '''Returns the CSS color representation of |c|'''
-        assert (isinstance(c, Color))
-        if c.var:
-            return 'var(%s)' % self.ToCSSVarName(c.var)
-
-        if c.rgb_var:
-            if c.opacity.a != 1:
-                return 'rgba(var(%s-rgb), %g)' % (self.ToCSSVarName(
-                    c.RGBVarToVar()), self._CSSOpacity(c.opacity))
-            else:
-                return 'rgb(var(%s-rgb))' % self.ToCSSVarName(c.RGBVarToVar())
-
-        elif c.a != 1:
-            return 'rgba(%d, %d, %d, %g)' % (c.r, c.g, c.b,
-                                             self._CSSOpacity(c.opacity))
-        else:
-            return 'rgb(%d, %d, %d)' % (c.r, c.g, c.b)
-
     def CSSColorRGB(self, c):
         '''Returns the CSS rgb representation of |c|'''
         if c.var:
@@ -158,12 +143,35 @@
 
         return '%d, %d, %d' % (c.r, c.g, c.b)
 
-    def CSSColorVar(self, name, color):
+    def CSSBlendInputColor(self, c):
+        '''Resolves a color for use in a color-mix call.'''
+        return 'rgb(%s)' % self.CSSColorRGB(c)
+
+    def ExtractOpacity(self, c, mode):
+        if c.var:
+            return self.ExtractOpacity(self.model.colors.Resolve(c.var, mode),
+                                       mode)
+        if c.opacity:
+            return self.model.opacities.ResolveOpacity(c.opacity, mode).a * 100
+
+        # If we don't have opacity information assume we want to blend 100%.
+        return 100
+
+    def CSSColorVar(self, name, color, mode):
         '''Returns the CSS color representation given a color name and color'''
+        if color.blended_colors:
+            return 'color-mix(in srgb, %s %s%%, %s)' % (
+                self.CSSBlendInputColor(color.blended_colors[0]),
+                self.ExtractOpacity(color.blended_colors[0], mode),
+                self.CSSBlendInputColor(color.blended_colors[1]))
         if color.var:
             return 'var(%s)' % self.ToCSSVarName(color.var)
+
         if color.opacity and color.opacity.a != 1:
             return 'rgba(var(%s-rgb), %s)' % (self.ToCSSVarName(name),
                                               self._CSSOpacity(color.opacity))
-        else:
-            return 'rgb(var(%s-rgb))' % self.ToCSSVarName(name)
+
+        return 'rgb(var(%s-rgb))' % self.ToCSSVarName(name)
+
+    def NeedsRGBVariant(self, color):
+        return not color.blended_colors
diff --git a/tools/style_variable_generator/templates/css_generator.tmpl b/tools/style_variable_generator/templates/css_generator.tmpl
index 003e30977..46cce07 100644
--- a/tools/style_variable_generator/templates/css_generator.tmpl
+++ b/tools/style_variable_generator/templates/css_generator.tmpl
@@ -14,8 +14,10 @@
 }
 {% macro render_variables_for_mode(mode) -%}
 {%- for model_name, color in colors[mode].items() %}
+  {%- if needs_rgb_variant(color) %}
   {{model_name | to_css_var_name}}-rgb: {{color | css_color_rgb}};
-  {{model_name | to_css_var_name}}: {{css_color_var(model_name, color)}};
+  {%- endif %}
+  {{model_name | to_css_var_name}}: {{css_color_var(model_name, color, mode)}};
 {% endfor %}
 
 {%- for name, value in opacities[mode].items() %}
diff --git a/tools/style_variable_generator/templates/ts_generator.tmpl b/tools/style_variable_generator/templates/ts_generator.tmpl
index 69747171..ac773a8 100644
--- a/tools/style_variable_generator/templates/ts_generator.tmpl
+++ b/tools/style_variable_generator/templates/ts_generator.tmpl
@@ -11,8 +11,10 @@
 {%- endif %}
 {% macro render_variables_as_css(mode) -%}
 {%- for model_name, color in colors[mode].items() %}
+  {%- if needs_rgb_variant(color) %}
   {{model_name | to_css_var_name}}-rgb: {{color | css_color_rgb}};
-  {{model_name | to_css_var_name}}: {{css_color_var(model_name, color)}};
+  {%- endif %}
+  {{model_name | to_css_var_name}}: {{css_color_var(model_name, color, mode)}};
 {% endfor %}
 
 {%- for name, value in opacities[mode].items() %}
diff --git a/tools/style_variable_generator/tests/blend_colors_test.json5 b/tools/style_variable_generator/tests/blend_colors_test.json5
index d1c136a..08564ad1 100644
--- a/tools/style_variable_generator/tests/blend_colors_test.json5
+++ b/tools/style_variable_generator/tests/blend_colors_test.json5
@@ -11,7 +11,7 @@
     },
     highlight_color_hover: {
       light: "rgba($black.rgb, 0.2)",
-      dark: "rgba($white.rgb, 0.2)",
+      dark: "rgba($white.rgb, 0.4)",
     },
     foo_color: {
       light: "blend($highlight_color_hover, $bg_color)",
diff --git a/tools/style_variable_generator/tests/goldens/blend_colors_test_expected.css b/tools/style_variable_generator/tests/goldens/blend_colors_test_expected.css
index f8eda87..5c4d1644 100644
--- a/tools/style_variable_generator/tests/goldens/blend_colors_test_expected.css
+++ b/tools/style_variable_generator/tests/goldens/blend_colors_test_expected.css
@@ -20,11 +20,9 @@
   --cros-highlight-color-hover-rgb: 0, 0, 0;
   --cros-highlight-color-hover: rgba(var(--cros-highlight-color-hover-rgb), 0.2);
 
-  --cros-foo-color-rgb: 204, 204, 204;
-  --cros-foo-color: rgb(var(--cros-foo-color-rgb));
+  --cros-foo-color: color-mix(in srgb, rgb(var(--cros-highlight-color-hover-rgb)) 20.0%, rgb(var(--cros-bg-color-rgb)));
 
-  --cros-bar-color-rgb: 204, 204, 204;
-  --cros-bar-color: rgb(var(--cros-bar-color-rgb));
+  --cros-bar-color: color-mix(in srgb, rgb(var(--cros-highlight-color-hover-rgb)) 20.0%, rgb(var(--cros-bg-color-rgb)));
 
   --cros-disabled-opacity: 0.38;
 }
@@ -35,12 +33,10 @@
   --cros-bg-color: var(--google-grey-900);
 
   --cros-highlight-color-hover-rgb: 255, 255, 255;
-  --cros-highlight-color-hover: rgba(var(--cros-highlight-color-hover-rgb), 0.2);
+  --cros-highlight-color-hover: rgba(var(--cros-highlight-color-hover-rgb), 0.4);
 
-  --cros-foo-color-rgb: 77, 77, 80;
-  --cros-foo-color: rgb(var(--cros-foo-color-rgb));
+  --cros-foo-color: color-mix(in srgb, rgb(var(--cros-highlight-color-hover-rgb)) 40.0%, rgb(var(--cros-bg-color-rgb)));
 
-  --cros-bar-color-rgb: 77, 77, 80;
-  --cros-bar-color: rgb(var(--cros-bar-color-rgb));
+  --cros-bar-color: color-mix(in srgb, rgb(var(--cros-highlight-color-hover-rgb)) 40.0%, rgb(var(--cros-bg-color-rgb)));
 }
 }
diff --git a/tools/style_variable_generator/tests/goldens/colors_test_custom_dark_toggle_expected.css b/tools/style_variable_generator/tests/goldens/colors_test_custom_dark_toggle_expected.css
index 14aea06..8204029 100644
--- a/tools/style_variable_generator/tests/goldens/colors_test_custom_dark_toggle_expected.css
+++ b/tools/style_variable_generator/tests/goldens/colors_test_custom_dark_toggle_expected.css
@@ -35,8 +35,7 @@
   --cros-toggle-color-rgb: var(--cros-text-color-primary-rgb);
   --cros-toggle-color: rgba(var(--cros-toggle-color-rgb), var(--cros-disabled-opacity));
 
-  --cros-bg-color-elevation-1-rgb: 41, 42, 45;
-  --cros-bg-color-elevation-1: rgb(var(--cros-bg-color-elevation-1-rgb));
+  --cros-bg-color-elevation-1: color-mix(in srgb, rgb(255, 255, 255) 4.0%, rgb(var(--google-grey-900-rgb)));
 
   --cros-reference-opacity: 1;
 }
diff --git a/tools/style_variable_generator/tests/goldens/colors_test_dark_only_expected.css b/tools/style_variable_generator/tests/goldens/colors_test_dark_only_expected.css
index e3bc9656..3777c38 100644
--- a/tools/style_variable_generator/tests/goldens/colors_test_dark_only_expected.css
+++ b/tools/style_variable_generator/tests/goldens/colors_test_dark_only_expected.css
@@ -20,8 +20,7 @@
   --cros-toggle-color-rgb: var(--cros-text-color-primary-rgb);
   --cros-toggle-color: rgba(var(--cros-toggle-color-rgb), var(--cros-disabled-opacity));
 
-  --cros-bg-color-elevation-1-rgb: 41, 42, 45;
-  --cros-bg-color-elevation-1: rgb(var(--cros-bg-color-elevation-1-rgb));
+  --cros-bg-color-elevation-1: color-mix(in srgb, rgb(255, 255, 255) 4.0%, rgb(var(--google-grey-900-rgb)));
 
   --cros-disabled-opacity: 0.38;
 
diff --git a/tools/style_variable_generator/tests/goldens/colors_test_expected.css b/tools/style_variable_generator/tests/goldens/colors_test_expected.css
index ee4fe3d..faad3cd 100644
--- a/tools/style_variable_generator/tests/goldens/colors_test_expected.css
+++ b/tools/style_variable_generator/tests/goldens/colors_test_expected.css
@@ -36,8 +36,7 @@
   --cros-toggle-color-rgb: var(--cros-text-color-primary-rgb);
   --cros-toggle-color: rgba(var(--cros-toggle-color-rgb), var(--cros-disabled-opacity));
 
-  --cros-bg-color-elevation-1-rgb: 41, 42, 45;
-  --cros-bg-color-elevation-1: rgb(var(--cros-bg-color-elevation-1-rgb));
+  --cros-bg-color-elevation-1: color-mix(in srgb, rgb(255, 255, 255) 4.0%, rgb(var(--google-grey-900-rgb)));
 
   --cros-reference-opacity: 1;
 }
diff --git a/tools/style_variable_generator/tests/goldens/colors_test_include_style_sheet_expected.ts b/tools/style_variable_generator/tests/goldens/colors_test_include_style_sheet_expected.ts
index ff60c6cb..2ea858466 100644
--- a/tools/style_variable_generator/tests/goldens/colors_test_include_style_sheet_expected.ts
+++ b/tools/style_variable_generator/tests/goldens/colors_test_include_style_sheet_expected.ts
@@ -47,8 +47,7 @@
   --cros-toggle-color-rgb: var(--cros-text-color-primary-rgb);
   --cros-toggle-color: rgba(var(--cros-toggle-color-rgb), var(--cros-disabled-opacity));
 
-  --cros-bg-color-elevation-1-rgb: 41, 42, 45;
-  --cros-bg-color-elevation-1: rgb(var(--cros-bg-color-elevation-1-rgb));
+  --cros-bg-color-elevation-1: color-mix(in srgb, rgb(255, 255, 255) 4.0%, rgb(var(--google-grey-900-rgb)));
 
   --cros-reference-opacity: 1;
 ` : '';
diff --git a/tools/style_variable_generator/tests/goldens/colors_test_typography_and_untyped_css_expected.ts b/tools/style_variable_generator/tests/goldens/colors_test_typography_and_untyped_css_expected.ts
index cb6f504..987b229 100644
--- a/tools/style_variable_generator/tests/goldens/colors_test_typography_and_untyped_css_expected.ts
+++ b/tools/style_variable_generator/tests/goldens/colors_test_typography_and_untyped_css_expected.ts
@@ -49,8 +49,7 @@
   --cros-toggle-color-rgb: var(--cros-text-color-primary-rgb);
   --cros-toggle-color: rgba(var(--cros-toggle-color-rgb), var(--cros-disabled-opacity));
 
-  --cros-bg-color-elevation-1-rgb: 41, 42, 45;
-  --cros-bg-color-elevation-1: rgb(var(--cros-bg-color-elevation-1-rgb));
+  --cros-bg-color-elevation-1: color-mix(in srgb, rgb(255, 255, 255) 4.0%, rgb(var(--google-grey-900-rgb)));
 
   --cros-reference-opacity: 1;
 ` : '';
diff --git a/tools/style_variable_generator/tests/goldens/colors_test_typography_expected.ts b/tools/style_variable_generator/tests/goldens/colors_test_typography_expected.ts
index 0c75811..c7202de 100644
--- a/tools/style_variable_generator/tests/goldens/colors_test_typography_expected.ts
+++ b/tools/style_variable_generator/tests/goldens/colors_test_typography_expected.ts
@@ -48,8 +48,7 @@
   --cros-toggle-color-rgb: var(--cros-text-color-primary-rgb);
   --cros-toggle-color: rgba(var(--cros-toggle-color-rgb), var(--cros-disabled-opacity));
 
-  --cros-bg-color-elevation-1-rgb: 41, 42, 45;
-  --cros-bg-color-elevation-1: rgb(var(--cros-bg-color-elevation-1-rgb));
+  --cros-bg-color-elevation-1: color-mix(in srgb, rgb(255, 255, 255) 4.0%, rgb(var(--google-grey-900-rgb)));
 
   --cros-reference-opacity: 1;
 ` : '';
diff --git a/tools/style_variable_generator/tests/goldens/colors_test_untyped_css_expected.ts b/tools/style_variable_generator/tests/goldens/colors_test_untyped_css_expected.ts
index 1cb38dc..5f1667a 100644
--- a/tools/style_variable_generator/tests/goldens/colors_test_untyped_css_expected.ts
+++ b/tools/style_variable_generator/tests/goldens/colors_test_untyped_css_expected.ts
@@ -48,8 +48,7 @@
   --cros-toggle-color-rgb: var(--cros-text-color-primary-rgb);
   --cros-toggle-color: rgba(var(--cros-toggle-color-rgb), var(--cros-disabled-opacity));
 
-  --cros-bg-color-elevation-1-rgb: 41, 42, 45;
-  --cros-bg-color-elevation-1: rgb(var(--cros-bg-color-elevation-1-rgb));
+  --cros-bg-color-elevation-1: color-mix(in srgb, rgb(255, 255, 255) 4.0%, rgb(var(--google-grey-900-rgb)));
 
   --cros-reference-opacity: 1;
 ` : '';
diff --git a/tools/style_variable_generator/tests/goldens/colors_tokens_test_expected.css b/tools/style_variable_generator/tests/goldens/colors_tokens_test_expected.css
index 89b2e8b..2826742 100644
--- a/tools/style_variable_generator/tests/goldens/colors_tokens_test_expected.css
+++ b/tools/style_variable_generator/tests/goldens/colors_tokens_test_expected.css
@@ -42,8 +42,7 @@
   --cros-sys-primary_container-rgb: var(--cros-ref-primary50-rgb);
   --cros-sys-primary_container: rgba(var(--cros-sys-primary_container-rgb), var(--cros-sys-disabled_opacity));
 
-  --cros-sys-on_primary_container-rgb: 41, 42, 45;
-  --cros-sys-on_primary_container: rgb(var(--cros-sys-on_primary_container-rgb));
+  --cros-sys-on_primary_container: color-mix(in srgb, rgb(255, 255, 255) 4.0%, rgb(var(--cros-ref-primary50-rgb)));
 
   --cros-sys-reference_opacity: 1;
 }
diff --git a/tools/style_variable_generator/tests/goldens/suppress_sources_comment_test_expected.css b/tools/style_variable_generator/tests/goldens/suppress_sources_comment_test_expected.css
index 9ff3308..768d284 100644
--- a/tools/style_variable_generator/tests/goldens/suppress_sources_comment_test_expected.css
+++ b/tools/style_variable_generator/tests/goldens/suppress_sources_comment_test_expected.css
@@ -31,8 +31,7 @@
   --cros-toggle-color-rgb: var(--cros-text-color-primary-rgb);
   --cros-toggle-color: rgba(var(--cros-toggle-color-rgb), var(--cros-disabled-opacity));
 
-  --cros-bg-color-elevation-1-rgb: 41, 42, 45;
-  --cros-bg-color-elevation-1: rgb(var(--cros-bg-color-elevation-1-rgb));
+  --cros-bg-color-elevation-1: color-mix(in srgb, rgb(255, 255, 255) 4.0%, rgb(var(--google-grey-900-rgb)));
 
   --cros-reference-opacity: 1;
 }
diff --git a/tools/style_variable_generator/views_generator.py b/tools/style_variable_generator/views_generator.py
index cf35c993..25a09d4 100644
--- a/tools/style_variable_generator/views_generator.py
+++ b/tools/style_variable_generator/views_generator.py
@@ -57,6 +57,11 @@
 
         return globals
 
+    def ShouldResolveBlendedColors(self):
+        # CSSGenerator sets this to false, we set it back to true since
+        # views does not do runtime blends.
+        return True
+
     def _CreateColorList(self):
         color_list = []
         for name, mode_values in self.model.colors.items():
diff --git a/ui/base/webui/web_ui_util.cc b/ui/base/webui/web_ui_util.cc
index 265824de..48aae8c 100644
--- a/ui/base/webui/web_ui_util.cc
+++ b/ui/base/webui/web_ui_util.cc
@@ -19,6 +19,7 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/base/template_expressions.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/base/window_open_disposition.h"
 #include "ui/base/window_open_disposition_utils.h"
 #include "ui/gfx/codec/png_codec.h"
@@ -37,10 +38,19 @@
 // Generous cap to guard against out-of-memory issues.
 constexpr float kMaxScaleFactor = 1000.0f;
 
+std::string GetFontFamilyMd() {
+  if (base::FeatureList::IsEnabled(features::kWebUiSystemFont)) {
+    return GetFontFamily();
+  }
+
+  return "Roboto, " + GetFontFamily();
+}
+
 std::string GetWebUiCssTextDefaults(const std::string& css_template) {
   ui::TemplateReplacements placeholders;
   placeholders["textdirection"] = GetTextDirection();
   placeholders["fontfamily"] = GetFontFamily();
+  placeholders["fontfamilyMd"] = GetFontFamilyMd();
   placeholders["fontsize"] = GetFontSize();
   return ui::ReplaceTemplateExpressions(css_template, placeholders);
 }
@@ -174,6 +184,7 @@
 void SetLoadTimeDataDefaults(const std::string& app_locale,
                              base::Value::Dict* localized_strings) {
   localized_strings->Set("fontfamily", GetFontFamily());
+  localized_strings->Set("fontfamilyMd", GetFontFamilyMd());
   localized_strings->Set("fontsize", GetFontSize());
   localized_strings->Set("language", l10n_util::GetLanguage(app_locale));
   localized_strings->Set("textdirection", GetTextDirection());
@@ -182,6 +193,7 @@
 void SetLoadTimeDataDefaults(const std::string& app_locale,
                              ui::TemplateReplacements* replacements) {
   (*replacements)["fontfamily"] = GetFontFamily();
+  (*replacements)["fontfamilyMd"] = GetFontFamilyMd();
   (*replacements)["fontsize"] = GetFontSize();
   (*replacements)["language"] = l10n_util::GetLanguage(app_locale);
   (*replacements)["textdirection"] = GetTextDirection();
diff --git a/ui/chromeos/styles/cros_sys_colors.json5 b/ui/chromeos/styles/cros_sys_colors.json5
index 12eb84c6..40791cf 100644
--- a/ui/chromeos/styles/cros_sys_colors.json5
+++ b/ui/chromeos/styles/cros_sys_colors.json5
@@ -132,7 +132,7 @@
       light: '$cros.ref.neutral99',
       dark:  '$cros.ref.neutral10',
     },
-    'app-base-elevated': {
+    'base-elevated': {
       light: '$cros.ref.neutralvariant100',
       /* In dark mode we layer primary80 @ 11% ontop of neutral80 @ 2% ontop of neutral 10. */
       dark: 'blend(rgba($cros.ref.primary80.rgb, .11), blend(rgba($cros.ref.neutral80.rgb, .02), $cros.ref.neutral10))',
@@ -198,11 +198,11 @@
       dark: 'rgba($cros.ref.neutralvariant0.rgb, 0.14)',
     },
 
-    'input-field-light': {
+    'input-field-on-shaded': {
       light: '$cros.ref.neutral99',
       dark: 'rgba($cros.ref.neutral50.rgb, 0.4)'
     },
-    'input-field-dark': {
+    'input-field-on-base': {
       light: '$cros.ref.neutral95',
       dark: 'rgba($cros.ref.neutral0.rgb, 0.6)'
     },
diff --git a/ui/file_manager/file_manager/BUILD.gn b/ui/file_manager/file_manager/BUILD.gn
index 434bd25..d2596f1c 100644
--- a/ui/file_manager/file_manager/BUILD.gn
+++ b/ui/file_manager/file_manager/BUILD.gn
@@ -39,6 +39,7 @@
 
     # CSS:
     "foreground/css/combobutton.css",
+    "foreground/css/combobutton_gm3.css",
     "foreground/css/common.css",
     "foreground/css/file_manager.css",
     "foreground/css/file_manager_gm3.css",
diff --git a/ui/file_manager/file_manager/common/js/entry_utils.ts b/ui/file_manager/file_manager/common/js/entry_utils.ts
index 0e6ced4..b590657 100644
--- a/ui/file_manager/file_manager/common/js/entry_utils.ts
+++ b/ui/file_manager/file_manager/common/js/entry_utils.ts
@@ -113,6 +113,40 @@
       rootType === VolumeManagerCommon.RootType.DRIVE_OFFLINE;
 }
 
+/**
+ * Returns true if fileData's entry is inside any part of Drive 'My Drive'.
+ */
+export function isEntryInsideMyDrive(fileData: FileData): boolean {
+  const {rootType} = fileData;
+  return !!rootType && rootType === VolumeManagerCommon.RootType.DRIVE;
+}
+
+/**
+ * Returns true if fileData's entry is inside any part of Drive 'Computers'.
+ */
+export function isEntryInsideComputers(fileData: FileData): boolean {
+  const {rootType} = fileData;
+  return !!rootType &&
+      (rootType === VolumeManagerCommon.RootType.COMPUTERS_GRAND_ROOT ||
+       rootType === VolumeManagerCommon.RootType.COMPUTER);
+}
+
+/**
+ * Returns true if fileData's entry is inside any part of Drive.
+ */
+export function isEntryInsideDrive(fileData: FileData): boolean {
+  const {rootType} = fileData;
+  return !!rootType &&
+      (rootType === VolumeManagerCommon.RootType.DRIVE ||
+       rootType === VolumeManagerCommon.RootType.SHARED_DRIVES_GRAND_ROOT ||
+       rootType === VolumeManagerCommon.RootType.SHARED_DRIVE ||
+       rootType === VolumeManagerCommon.RootType.COMPUTERS_GRAND_ROOT ||
+       rootType === VolumeManagerCommon.RootType.COMPUTER ||
+       rootType === VolumeManagerCommon.RootType.DRIVE_OFFLINE ||
+       rootType === VolumeManagerCommon.RootType.DRIVE_SHARED_WITH_ME ||
+       rootType === VolumeManagerCommon.RootType.DRIVE_FAKE_ROOT);
+}
+
 /** Sort the entries based on the filter and the names. */
 export function sortEntries(
     parentEntry: Entry|FilesAppEntry,
diff --git a/ui/file_manager/file_manager/common/js/entry_utils_unittest.ts b/ui/file_manager/file_manager/common/js/entry_utils_unittest.ts
new file mode 100644
index 0000000..474ac56
--- /dev/null
+++ b/ui/file_manager/file_manager/common/js/entry_utils_unittest.ts
@@ -0,0 +1,71 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import {assertFalse, assertTrue} from 'chrome://webui-test/chromeos/chai_assert.js';
+
+import {setUpFileManagerOnWindow} from '../../state/for_tests.js';
+import {convertEntryToFileData} from '../../state/reducers/all_entries.js';
+
+import {isEntryInsideDrive, isEntryInsideMyDrive} from './entry_utils.js';
+import {EntryList} from './files_app_entry_types.js';
+import {MockFileSystem} from './mock_entry.js';
+import {VolumeManagerCommon} from './volume_manager_types.js';
+
+export function setUp() {
+  setUpFileManagerOnWindow();
+}
+
+/**
+ * Test private methods: isEntryInsideDrive_() and isEntryInsideMyDrive_(),
+ * which should return true when inside My Drive and any of its sub-directories;
+ * Should return false for everything else, including within Team Drive.
+ */
+export function testInsideMyDriveAndInsideDrive() {
+  // Add sub folders for MyFiles and Drive.
+  const {volumeManager} = window.fileManager;
+  const myFilesFs =
+      volumeManager
+          .getCurrentProfileVolumeInfo(
+              VolumeManagerCommon.VolumeType.DOWNLOADS)!.fileSystem as
+      MockFileSystem;
+  const driveFs = volumeManager
+                      .getCurrentProfileVolumeInfo(
+                          VolumeManagerCommon.VolumeType.DRIVE)!.fileSystem as
+      MockFileSystem;
+  myFilesFs.populate(['/folder1/']);
+  driveFs.populate(['/root/folder1']);
+  const driveRootEntryList =
+      new EntryList('Drive root', VolumeManagerCommon.RootType.DRIVE_FAKE_ROOT);
+
+  // Convert entry into FileData.
+  const driveRootFileData = convertEntryToFileData(driveRootEntryList);
+  const myDrivesFileData = convertEntryToFileData(driveFs.entries['/root']);
+  const teamDrivesFileData =
+      convertEntryToFileData(driveFs.entries['/team_drives']);
+  const computersFileData =
+      convertEntryToFileData(driveFs.entries['/Computers']);
+  const myFilesFileData = convertEntryToFileData(myFilesFs.entries['/']);
+  const myFilesFolder1FileData =
+      convertEntryToFileData(myFilesFs.entries['/folder1']);
+  const myDrivesFolder1FileData =
+      convertEntryToFileData(driveFs.entries['/root/folder1']);
+
+  // insideMyDrive
+  assertFalse(isEntryInsideMyDrive(driveRootFileData), 'Drive root');
+  assertTrue(isEntryInsideMyDrive(myDrivesFileData), 'My Drives root');
+  assertFalse(isEntryInsideMyDrive(teamDrivesFileData), 'Team Drives root');
+  assertFalse(isEntryInsideMyDrive(computersFileData), 'Computers root');
+  assertFalse(isEntryInsideMyDrive(myFilesFileData), 'MyFiles root');
+  assertFalse(isEntryInsideMyDrive(myFilesFolder1FileData), 'MyFiles folder1');
+  assertTrue(
+      isEntryInsideMyDrive(myDrivesFolder1FileData), 'My Drives folder1');
+  // insideDrive
+  assertTrue(isEntryInsideDrive(driveRootFileData), 'Drive root');
+  assertTrue(isEntryInsideDrive(myDrivesFileData), 'My Drives root');
+  assertTrue(isEntryInsideDrive(teamDrivesFileData), 'Team Drives root');
+  assertTrue(isEntryInsideDrive(computersFileData), 'Computers root');
+  assertFalse(isEntryInsideDrive(myFilesFileData), 'MyFiles root');
+  assertFalse(isEntryInsideMyDrive(myFilesFolder1FileData), 'MyFiles folder1');
+  assertTrue(isEntryInsideDrive(myDrivesFolder1FileData), 'My Drives folder1');
+}
diff --git a/ui/file_manager/file_manager/containers/directory_tree_container.ts b/ui/file_manager/file_manager/containers/directory_tree_container.ts
index 852d85f..1dc8e8b 100644
--- a/ui/file_manager/file_manager/containers/directory_tree_container.ts
+++ b/ui/file_manager/file_manager/containers/directory_tree_container.ts
@@ -6,12 +6,11 @@
 import {CrButtonElement} from 'chrome://resources/cr_elements/cr_button/cr_button.js';
 
 import {maybeShowTooltip} from '../common/js/dom_utils.js';
-import {isGrandRootEntryInDrives, isMyFilesEntry, isVolumeEntry} from '../common/js/entry_utils.js';
+import {isEntryInsideComputers, isEntryInsideDrive, isEntryInsideMyDrive, isGrandRootEntryInDrives, isMyFilesEntry, isVolumeEntry} from '../common/js/entry_utils.js';
 import {EntryList, VolumeEntry} from '../common/js/files_app_entry_types.js';
 import {metrics} from '../common/js/metrics.js';
 import {strf} from '../common/js/util.js';
 import {VolumeManagerCommon} from '../common/js/volume_manager_types.js';
-import {FilesAppEntry} from '../externs/files_app_entry_interfaces.js';
 import {FileData, NavigationKey, NavigationRoot, NavigationType, PropStatus, State} from '../externs/ts/state.js';
 import {VolumeManager} from '../externs/volume_manager.js';
 import {constants} from '../foreground/js/constants.js';
@@ -21,6 +20,7 @@
 import {changeDirectory} from '../state/actions/current_directory.js';
 import {refreshNavigationRoots, updateNavigationEntry} from '../state/actions/navigation.js';
 import {readSubDirectories} from '../state/actions_producers/all_entries.js';
+import {convertEntryToFileData} from '../state/reducers/all_entries.js';
 import {driveRootEntryListKey} from '../state/reducers/volumes.js';
 import {getEntry, getFileData, getStore, Store} from '../state/store.js';
 import {TreeSelectedChangedEvent, XfTree} from '../widgets/xf_tree.js';
@@ -275,16 +275,13 @@
 
     // Handle navigation items backed up by a file entry.
     const fileData = newData as FileData;
-    const entry = fileData.entry;
 
     element.label = fileData.label;
     if (navigationRoot) {
       element.separator = navigationRoot.separator;
     }
     this.setItemIcon_(element, fileData, navigationRoot);
-    element.disabled = ('disabled' in entry && entry.disabled) ?
-        entry.disabled as boolean :
-        false;
+    element.disabled = fileData.disabled;
 
     // Add eject button for ejectable item.
     if (fileData.isEjectable) {
@@ -292,9 +289,9 @@
     }
 
     // Fetch metadata if the entry supports Drive specific share icon.
-    if (this.shouldSupportDriveSpecificIcons_(fileData.entry)) {
+    if (this.shouldSupportDriveSpecificIcons_(fileData)) {
       this.metadataModel_.get(
-          [fileData.entry], DRIVE_ENTRY_METADATA_PROPERTY_NAMES);
+          [fileData.entry as Entry], DRIVE_ENTRY_METADATA_PROPERTY_NAMES);
     }
 
     if (!navigationRoot?.type ||
@@ -361,8 +358,7 @@
       element.icon = fileData.icon;
     }
     // For drive item, update icon based on the metadata.
-    if (this.shouldSupportDriveSpecificIcons_(fileData.entry) &&
-        fileData.metadata) {
+    if (this.shouldSupportDriveSpecificIcons_(fileData) && fileData.metadata) {
       const {shared, isMachineRoot, isExternalMedia} = fileData.metadata;
       if (shared) {
         element.icon = constants.ICON_TYPES.SHARED_FOLDER;
@@ -525,19 +521,18 @@
   }
 
   /**
-   * Returns true if the entry supports the "shared" feature, as in, displays
-   * a shared icon. It's only supported inside "My Drive" or "Computers", even
-   * Shared Drive does not support it, the "My Drive" and "Computers" itself
-   * don't support it either, only their children.
+   * Returns true if fileData's entry supports the "shared" feature, as in,
+   * displays a shared icon. It's only supported inside "My Drive" or
+   * "Computers", even Shared Drive does not support it, the "My Drive" and
+   * "Computers" itself don't support it either, only their children.
    *
-   * Note: if the return value is true, the input entry is guaranteed to be
+   * Note: if the return value is true, fileData's entry is guaranteed to be
    * native Entry type.
    */
-  private shouldSupportDriveSpecificIcons_(entry: Entry|
-                                           FilesAppEntry): entry is Entry {
-    return (this.isEntryInsideMyDrive_(entry) && !isVolumeEntry(entry)) ||
-        (this.isEntryInsideComputers_(entry) &&
-         !isGrandRootEntryInDrives(entry));
+  private shouldSupportDriveSpecificIcons_(fileData: FileData): boolean {
+    return (isEntryInsideMyDrive(fileData) && !isVolumeEntry(fileData.entry)) ||
+        (isEntryInsideComputers(fileData) &&
+         !isGrandRootEntryInDrives(fileData.entry));
   }
 
   /**
@@ -583,7 +578,7 @@
     }));
 
     // UMA: expand time.
-    const rootType = this.getRootType_(fileData.entry) ?? 'unknown';
+    const rootType = fileData.rootType ?? 'unknown';
     const metricName = `DirectoryTree.Expand.${rootType}`;
     this.recordUmaForItemExpandedOrCollapsed_(fileData);
 
@@ -726,7 +721,7 @@
 
   /** Record UMA for item expanded or collapsed. */
   private recordUmaForItemExpandedOrCollapsed_(fileData: FileData) {
-    const rootType = this.getRootType_(fileData.entry) ?? 'unknown';
+    const rootType = fileData.rootType ?? 'unknown';
     const level = fileData.isRootEntry ? 'TopLevel' : 'NonTopLevel';
     const metricName = `Location.OnEntryExpandedOrCollapsed.${level}`;
     metrics.recordEnum(
@@ -735,7 +730,7 @@
 
   /** Record UMA for tree item selected. */
   private recordUmaForItemSelected_(fileData: FileData) {
-    const rootType = this.getRootType_(fileData.entry) ?? 'unknown';
+    const rootType = fileData.rootType ?? 'unknown';
     const level = fileData.isRootEntry ? 'TopLevel' : 'NonTopLevel';
     const metricName = `Location.OnEntrySelected.${level}`;
     metrics.recordEnum(
@@ -842,13 +837,18 @@
    * is deleted.
    */
   private updateTreeByEntry_(entry: DirectoryEntry) {
+    // TODO(b/271485133): Remove `getDirectory` call here and prevent
+    // convertEntryToFileData() below.
     entry.getDirectory(
         entry.fullPath, {create: false},
         () => {
+          // Can't rely on store data to get entry's rootType, if the entry is
+          // grand root entry's first sub folder, the grand root entry might not
+          // be the in the store yet.
+          const fileData = convertEntryToFileData(entry);
           // If entry exists.
           // e.g. /a/b is deleted while watching /a.
-          if (this.isEntryInsideDrive_(entry) &&
-              isGrandRootEntryInDrives(entry)) {
+          if (isEntryInsideDrive(fileData) && isGrandRootEntryInDrives(entry)) {
             // For grand root related changes, we need to re-read child
             // entries from the fake drive root level, because the grand root
             // might be show/hide based on if they have children or not.
@@ -913,53 +913,4 @@
 
     return false;
   }
-
-  /**
-   * Gets the RootType of the Volume this entry belongs to.
-   */
-  private getRootType_(entry: Entry|FilesAppEntry|
-                       null): VolumeManagerCommon.RootType|null {
-    let rootType = null;
-
-    if (entry) {
-      const locationInfo = this.volumeManager_.getLocationInfo(entry);
-      rootType = locationInfo ? locationInfo.rootType : null;
-    }
-
-    return rootType;
-  }
-
-  /**
-   * Returns true if the entry is inside any part of Drive 'My Drive'.
-   */
-  private isEntryInsideMyDrive_(entry: Entry|FilesAppEntry|null): boolean {
-    const rootType = this.getRootType_(entry);
-    return !!rootType && rootType === VolumeManagerCommon.RootType.DRIVE;
-  }
-
-  /**
-   * Returns true if the entry is inside any part of Drive 'Computers'.
-   */
-  private isEntryInsideComputers_(entry: Entry|FilesAppEntry|null): boolean {
-    const rootType = this.getRootType_(entry);
-    return !!rootType &&
-        (rootType === VolumeManagerCommon.RootType.COMPUTERS_GRAND_ROOT ||
-         rootType === VolumeManagerCommon.RootType.COMPUTER);
-  }
-
-  /**
-   * Returns true if the entry is inside any part of Drive.
-   */
-  private isEntryInsideDrive_(entry: Entry|FilesAppEntry|null): boolean {
-    const rootType = this.getRootType_(entry);
-    return !!rootType &&
-        (rootType === VolumeManagerCommon.RootType.DRIVE ||
-         rootType === VolumeManagerCommon.RootType.SHARED_DRIVES_GRAND_ROOT ||
-         rootType === VolumeManagerCommon.RootType.SHARED_DRIVE ||
-         rootType === VolumeManagerCommon.RootType.COMPUTERS_GRAND_ROOT ||
-         rootType === VolumeManagerCommon.RootType.COMPUTER ||
-         rootType === VolumeManagerCommon.RootType.DRIVE_OFFLINE ||
-         rootType === VolumeManagerCommon.RootType.DRIVE_SHARED_WITH_ME ||
-         rootType === VolumeManagerCommon.RootType.DRIVE_FAKE_ROOT);
-  }
 }
diff --git a/ui/file_manager/file_manager/containers/directory_tree_container_unittest.ts b/ui/file_manager/file_manager/containers/directory_tree_container_unittest.ts
index e45e4f9..d6789732 100644
--- a/ui/file_manager/file_manager/containers/directory_tree_container_unittest.ts
+++ b/ui/file_manager/file_manager/containers/directory_tree_container_unittest.ts
@@ -21,7 +21,7 @@
 import {createFakeVolumeMetadata, setUpFileManagerOnWindow, setupStore} from '../state/for_tests.js';
 import {convertEntryToFileData} from '../state/reducers/all_entries.js';
 import {convertVolumeInfoAndMetadataToVolume, driveRootEntryListKey} from '../state/reducers/volumes.js';
-import {getEmptyState, getEntry, getStore} from '../state/store.js';
+import {getEmptyState, getEntry, getFileData, getStore} from '../state/store.js';
 import {XfTree} from '../widgets/xf_tree.js';
 
 import {DirectoryTreeContainer} from './directory_tree_container.js';
@@ -716,14 +716,11 @@
   // Add sub folders for them.
   myFilesFs.populate(['/folder1/']);
   driveFs.populate(['/root/folder1/']);
-  const driveRootEntryList =
-      getEntry(initialState, driveRootEntryListKey) as EntryList;
+  const driveRootEntryFileData =
+      getFileData(initialState, driveRootEntryListKey)!;
   // Disable the drive root entry.
-  driveRootEntryList.disabled = true;
-  const {volumeManager} = window.fileManager;
-  volumeManager.isDisabled = (volumeType) => {
-    return volumeType === VolumeManagerCommon.VolumeType.DRIVE;
-  };
+  driveRootEntryFileData.disabled = true;
+  (driveRootEntryFileData.entry as EntryList).disabled = true;
   // Store initialization will notify DirectoryTree.
   setupStore(initialState);
 
@@ -949,67 +946,6 @@
 }
 
 /**
- * Test private methods: isEntryInsideDrive_() and isEntryInsideMyDrive_(),
- * which should return true when inside My Drive and any of its sub-directories;
- * Should return false for everything else, including within Team Drive.
- */
-export async function testInsideMyDriveAndInsideDrive(done: () => void) {
-  const initialState = getEmptyState();
-  const directoryTree = directoryTreeContainer.tree;
-
-  // Add MyFiles and Drive to the store.
-  const {myFilesFs, driveFs} = await addMyFilesAndDriveToStore(initialState);
-  // Add sub folders for them.
-  myFilesFs.populate(['/folder1/']);
-  driveFs.populate(['/root/folder1']);
-  // Store initialization will notify DirectoryTree.
-  const store = setupStore(initialState);
-
-  // At top level, MyFiles and Drive should be listed.
-  await waitUntil(() => directoryTree.items.length === 2);
-  const myFilesItem = directoryTree.items[0]!;
-  const driveItem = directoryTree.items[1]!;
-
-  await waitUntil(() => {
-    // Under the drive item, there exist 3 entries. In MyFiles should
-    // exist 1 entry folder1.
-    return driveItem.items.length === 3 && myFilesItem.items.length === 1;
-  });
-
-  // Use [] to access private methods to bypass Typescript check.
-  const isEntryInsideDrive = directoryTreeContainer['isEntryInsideDrive_'].bind(
-      directoryTreeContainer);
-  const isEntryInsideMyDrive =
-      directoryTreeContainer['isEntryInsideMyDrive_'].bind(
-          directoryTreeContainer);
-
-  const driveRootEntryList =
-      getEntry(store.getState(), driveRootEntryListKey) as EntryList;
-  // insideMyDrive
-  assertFalse(isEntryInsideMyDrive(driveRootEntryList), 'Drive root');
-  assertTrue(isEntryInsideMyDrive(driveFs.entries['/root']), 'My Drive root');
-  assertFalse(
-      isEntryInsideMyDrive(driveFs.entries['/team_drives']),
-      'Team Drives root');
-  assertFalse(
-      isEntryInsideMyDrive(driveFs.entries['/Computers']), 'Offline root');
-  assertFalse(isEntryInsideMyDrive(myFilesFs.entries['/']), 'MyFiles root');
-  assertFalse(
-      isEntryInsideMyDrive(myFilesFs.entries['/folder1']), 'MyFiles folder1');
-  // insideDrive
-  assertTrue(isEntryInsideDrive(driveRootEntryList), 'Drive root');
-  assertTrue(isEntryInsideDrive(driveFs.entries['/root']), 'My Drive root');
-  assertTrue(
-      isEntryInsideDrive(driveFs.entries['/team_drives']), 'Team Drives root');
-  assertTrue(isEntryInsideDrive(driveFs.entries['/Computers']), 'Offline root');
-  assertFalse(isEntryInsideDrive(myFilesFs.entries['/']), 'MyFiles root');
-  assertFalse(
-      isEntryInsideDrive(myFilesFs.entries['/folder1']), 'MyFiles folder1');
-
-  done();
-}
-
-/**
  * Test adding FSPs.
  * Sub directories should be fetched for FSPs, but not for the Smb FSP.
  */
diff --git a/ui/file_manager/file_manager/externs/ts/state.js b/ui/file_manager/file_manager/externs/ts/state.js
index f29f45b5..2916086 100644
--- a/ui/file_manager/file_manager/externs/ts/state.js
+++ b/ui/file_manager/file_manager/externs/ts/state.js
@@ -45,6 +45,7 @@
  *   icon: (!string|!chrome.fileManagerPrivate.IconSet),
  *   label: string,
  *   volumeType: (VolumeManagerCommon.VolumeType|null),
+ *   rootType: (VolumeManagerCommon.RootType|null),
  *   metadata: !MetadataItem,
  *   isDirectory: boolean,
  *   type: !EntryType,
@@ -53,6 +54,7 @@
  *   shouldDelayLoadingChildren: !boolean,
  *   children: (!Array<!FileKey>),
  *   expanded: !boolean,
+ *   disabled: !boolean,
  * }}
  */
 export let FileData;
diff --git a/ui/file_manager/file_manager/foreground/css/combobutton_gm3.css b/ui/file_manager/file_manager/foreground/css/combobutton_gm3.css
new file mode 100644
index 0000000..7987319
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/css/combobutton_gm3.css
@@ -0,0 +1,61 @@
+/* Copyright 2023 The Chromium Authors
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file. */
+
+.dialog-header button.combobutton {
+  align-items: stretch;
+  background: transparent;
+  border-radius: 3px;
+  cursor: pointer;
+  display: flex;
+  flex: none;
+  font-weight: 500;
+  outline: none;
+  padding: 8px;
+  position: relative;
+  user-select: none;
+}
+
+.dialog-header .combobutton > .button {
+  align-items: center;
+  display: flex;
+}
+
+.dialog-header .combobutton > .button > .action {
+  background-position: left center;
+  background-repeat: no-repeat;
+  background-size: 16px 16px;
+  padding-top: 1px;
+  z-index: 1;
+}
+
+html[dir='rtl'] .dialog-header .combobutton > .button > .action {
+  background-position: right center;
+}
+
+.dialog-header .combobutton > .button > .trigger {
+  align-items: center;
+  display: inline-flex;
+  height: 20px;
+  justify-content: center;
+  margin-inline-start: 8px;
+  width: 20px;
+}
+
+/* This pseudo element expands clickable area of the .trigger */
+.dialog-header .combobutton > .button > .trigger::before {
+  content: '';
+  display: block;
+  height: 48px;
+  position: absolute;
+  right: 12px; /* Same value as padding-inline-end of the button. */
+  width: 20px;
+}
+
+.dialog-header .combobutton:not([multiple]) > .button > .trigger {
+  display: none;
+}
+
+.dialog-header .combobutton[hidden] {
+  display: none;
+}
diff --git a/ui/file_manager/file_manager/foreground/css/file_manager_gm3.css b/ui/file_manager/file_manager/foreground/css/file_manager_gm3.css
index 0471ac9f..ae1790d 100644
--- a/ui/file_manager/file_manager/foreground/css/file_manager_gm3.css
+++ b/ui/file_manager/file_manager/foreground/css/file_manager_gm3.css
@@ -341,7 +341,7 @@
 body.check-select .dialog-header {
   border-bottom: 1px solid var(--cros-separator-color);
   border-top: 1px solid transparent;
-  color: var(--cros-text-color-prominent);
+  color: var(--cros-sys-on_primary_container);
 }
 
 /* Display a border during check-select mode if we're in a dialog. (This
@@ -351,31 +351,28 @@
 }
 
 .dialog-header > .spacer {
-  flex: auto;
+  flex: 1 0 48px;
 }
 
 .dialog-header cr-button {
-  --active-bg: var(--cros-ripple-color);
-  --hover-bg-color: var(--cros-ripple-color);
-  --ink-color: var(--cros-ripple-color);
+  --hover-bg-color: var(--cros-sys-hover_on_subtle);
+  --ink-color: var(--cros-sys-ripple_neutral_on_subtle);
   --ripple-opacity: 100%;
   --text-color: currentColor;
-  border: 2px solid transparent;
+  border: none;
   border-radius: 18px;
   box-sizing: border-box;
-  color: var(--cros-text-color-primary);
+  color: var(--cros-sys-on_surface);
   cursor: pointer;
   height: 36px;
-  margin: 0 6px;
-  min-width: 36px;
-  padding: 0;
+  min-width: 48px;
   position: relative;
   text-transform: uppercase;
   z-index: 1;
 }
 
 html.pointer-active .dialog-header cr-button:not(:active):hover {
-  background-color: unset;
+  --hover-bg-color: none;
   cursor: default;
 }
 
@@ -387,33 +384,16 @@
 }
 
 .dialog-header cr-button[menu-shown] {
- background: var(--cros-icon-button-pressed-color);
+  background-color: var(--cros-sys-primary_container);
+  color: var(--cros-sys-on_primary_container);
 }
 
-.dialog-header cr-button:not([menu-shown]):not(:active):hover {
-  background-color: var(--cros-ripple-color);
-}
-
-html.pointer-active .dialog-header cr-button:not([menu-shown]):not(:active):hover {
-  background-color: unset;
-  cursor: default;
-}
-
-.dialog-header cr-button:active {
-  background: var(--cros-icon-button-pressed-color);
+.dialog-header cr-button:not([aria-haspopup]):active {
+  background-color: var(--cros-sys-pressed_on_subtle);
 }
 
 html.focus-outline-visible .dialog-header cr-button:not(:active):focus {
-  border: 2px solid var(--cros-focus-ring-color);
-}
-
-body.check-select button,
-body.check-select button:hover {
-  color: var(--cros-text-color-primary);
-}
-
-body.check-select .dialog-header button paper-ripple {
-  color: var(--cros-text-color-primary);
+  outline: 2px solid var(--cros-sys-focus_ring);
 }
 
 /** Avoid highlighting if element doesn't have focus by tab (tabindex=-1)
@@ -425,7 +405,7 @@
 
 body.check-select .dialog-header
     .menu-button:focus:not([tabindex='-1']):not(:active) {
-  background-color: var(--cros-ripple-color);
+  background-color: var(--cros-sys-ripple_neutral_on_subtle);
 }
 
 .dialog-header iron-icon,
@@ -434,33 +414,25 @@
   width: 16px;
 }
 
-.dialog-header cr-button {
-  line-height: 32px;
-  padding: 0 8px;
+.dialog-header cr-button.icon-button {
+  border-radius: 12px;
+  height: 40px;
+  margin: 4px;
+  min-width: 40px;
+  padding: 0;
+  width: 40px;
 }
 
-.dialog-header button.icon-button {
-  -webkit-app-region: no-drag;
-  background-color: transparent;
-  background-image: none;
-  background-position: center;
-  background-repeat: no-repeat;
-  box-shadow: none;
-  outline: none;
-  position: relative;
+.dialog-header cr-button.icon-button[aria-haspopup] {
+  --ink-color: var(--cros-sys-ripple_primary);
 }
 
 .dialog-header cr-button.icon-button > .icon {
   background-position: center;
   background-repeat: no-repeat;
-  height: 48px;
-  left: 0;
-  margin-inline-end: -8px;
-  margin-inline-start: -8px;
-  margin-top: -8px;
+  height: 20px;
   position: absolute;
-  top: 0;
-  width: 48px;
+  width: 20px;
 }
 
 .dialog-header cr-button.icon-button > xf-icon {
@@ -468,9 +440,15 @@
 }
 
 .dialog-header #tasks {
-  border-radius: 4px;
-  height: 32px;
-  padding: 0 12px;
+  color: var(--cros-sys-primary);
+  margin: 0 8px;
+  padding-bottom: 0;
+  padding-inline: 16px;
+  padding-top: 2px;
+}
+
+.dialog-header #tasks[multiple] {
+  padding-inline-end: 12px;
 }
 
 .dialog-header cr-button > .icon,
@@ -488,23 +466,27 @@
 
 .dialog-header.files-ng #search-wrapper {
   align-items: center;
-  border-radius: 4px;
-  height: 40px;
+  border-radius: 8px;
+  height: 48px;
   transition: background-color 200ms ease;
 }
 
 .dialog-header.files-ng #search-wrapper.has-cursor,
 .dialog-header.files-ng #search-wrapper.has-text,
 .dialog-header.files-ng #search-wrapper.hide-pending {
-  background-color: var(--cros-textfield-background-color);
-  margin-inline-end: 16px;
+  background-color: var(--cros-sys-input_field_on_base);
 }
 
 .dialog-header #search-box cr-input {
   --cr-input-background-color: transparent;
+  --cr-input-border-bottom: transparent;
   --cr-input-border-radius: 0;
+  --cr-input-color: var(--cros-sys-on_surface);
   --cr-input-error-display: none;
-  --cr-input-padding-end: 20px;
+  --cr-input-focus-color: transparent;
+  --cr-input-min-height: 20px;
+  --cr-input-placeholder-color: var(--cros-sys-secondary);
+  --cr-input-padding-end: 0;
   --cr-input-padding-start: 0;
   display: inline-block;
   transition: width 200ms ease;
@@ -585,7 +567,7 @@
 }
 
 body.files-ng #files-selected-label {
-  margin-inline-start: 8px;
+  margin-inline: 8px 12px;
 }
 
 body.check-select #files-selected-label {
@@ -593,7 +575,7 @@
 }
 
 #cancel-selection-button {
-  --ink-color: var(--cros-ripple-color);
+  --ink-color: var(--cros-sys-ripple_neutral_on_subtle);
   border: none;
   box-shadow: none;
   color: currentColor;
@@ -601,15 +583,16 @@
   text-transform: none;
 }
 
-/* TODO(adanilo) document the calc() reason. */
 body.files-ng #cancel-selection-button {
-  border: 2px solid transparent;
-  margin-inline-start: calc(10px + 1px);
-  width: 36px;
+  height: 32px;
+  margin-inline-start: 12px;
+  min-width: 32px;
+  padding: 6px;
+  width: 32px;
 }
 
 html.focus-outline-visible body.files-ng #cancel-selection-button:focus:not([tabindex='-1']):not(:active) {
-  border: 2px solid var(--cros-icon-color-prominent);
+  outline: 2px solid var(--cros-sys-focus_ring);
 }
 
 body.files-ng #cancel-selection-button > span#cancel-selection-label {
@@ -620,7 +603,7 @@
   -webkit-mask-image: url(../images/files/ui/list_check.svg);
   -webkit-mask-position: center;
   -webkit-mask-repeat: no-repeat;
-  background-color: var(--cros-icon-color-prominent);
+  background-color: currentColor;
   flex: none;
   height: 20px;
   width: 20px;
@@ -659,15 +642,9 @@
   font-size: 14px;
 }
 
-.dialog-header.files-ng #search-box cr-input {
-  --cr-input-border-bottom: transparent;
-  --cr-input-color: var(--cros-text-color-primary);
-  --cr-input-focus-color: transparent;
-  --cr-input-placeholder-color: var(--cros-text-color-secondary);
-}
-
 .dialog-header.files-ng #search-box cr-input::part(input) {
-  caret-color: var(--cr-input-color);
+  caret-color: var(--cros-sys-primary);
+  margin-inline-end: 16px;
 }
 
 body.check-select #search-box {
@@ -699,8 +676,10 @@
 
 .dialog-header.files-ng #search-box .clear {
   background: none;
-  height: 36px;
-  width: 36px;
+  height: 32px;
+  margin-inline-end: 4px;
+  padding: 0;
+  width: 32px;
 }
 
 html:not(.pointer-active) .dialog-header.files-ng #search-box .clear:hover {
@@ -732,22 +711,21 @@
 }
 
 .dialog-header.files-ng #pinned-toggle-label {
-  color: var(--cros-text-color-primary);
-  margin-inline-end: 16px;
-  margin-inline-start: 12px;
+  color: var(--cros-sys-on_surface);
 }
 
 .dialog-header.files-ng cr-toggle {
-  --cr-toggle-checked-bar-color: var(--cros-switch-track-color-active);
+  --cr-toggle-checked-bar-color: var(--cros-sys-primary);
   /* bar color above already defines the opacity, so use 100% here */
   --cr-toggle-checked-bar-opacity: 100%;
-  --cr-toggle-checked-button-color: var(--cros-switch-knob-color-active);
+  --cr-toggle-checked-button-color: var(--cros-sys-on_primary);
   --cr-toggle-checked-ripple-color: var(--cros-focus-aura-color);
-  --cr-toggle-unchecked-bar-color: var(--cros-switch-track-color-inactive);
-  --cr-toggle-unchecked-button-color: var(--cros-switch-knob-color-inactive);
+  --cr-toggle-unchecked-bar-color: var(--cros-sys-secondary);
+  --cr-toggle-unchecked-button-color: var(--cros-sys-on_secondary);
   --cr-toggle-unchecked-ripple-color: var(--cros-ripple-color);
   --cr-toggle-box-shadow: var(--cros-elevation-1-shadow);
   --cr-toggle-ripple-diameter: 32px;
+  margin-inline: 6px;
 }
 
 /* only show the ripple ring for tab navigation */
@@ -1011,7 +989,7 @@
   flex-direction: row;
   overflow: hidden;
   padding-bottom: 4px;
-  padding-inline: 8px 48px;
+  padding-inline-start: 8px;
   padding-top: 4px;
 }
 
@@ -1022,14 +1000,13 @@
 /* The toolbar indicator that means the current directory is read only. */
 #read-only-indicator {
   align-items: center;
-  background-color: var(--cros-highlight-color-hover);
-  border-radius: 16px;
+  background-color: var(--cros-sys-disabled_container);
+  border: 1px solid var(--cros-sys-separator);
+  border-radius: 8px;
   display: flex;
   flex: none;
   height: 32px;
-  margin-inline-start: 12px;
-  padding: 0 16px;
-  padding-inline: 16px 12px;
+  padding-inline: 8px 12px;
 }
 
 body.check-select #read-only-indicator {
diff --git a/ui/file_manager/file_manager/foreground/elements/files_tooltip.html b/ui/file_manager/file_manager/foreground/elements/files_tooltip.html
index a34a75c2..2496a17e 100644
--- a/ui/file_manager/file_manager/foreground/elements/files_tooltip.html
+++ b/ui/file_manager/file_manager/foreground/elements/files_tooltip.html
@@ -39,6 +39,11 @@
     padding: 12px 16px;
   }
 
+  :host-context([theme='refresh23']):host(.card-tooltip) {
+    background-color: var(--cros-sys-base_elevated);
+    color: var(--cros-sys-on_surface);
+  }
+
   :host(.link-tooltip) {
     align-items: flex-start;
     flex-direction: column;
diff --git a/ui/file_manager/file_manager/state/reducers/all_entries.ts b/ui/file_manager/file_manager/state/reducers/all_entries.ts
index 23cc7ea..242a1fff 100644
--- a/ui/file_manager/file_manager/state/reducers/all_entries.ts
+++ b/ui/file_manager/file_manager/state/reducers/all_entries.ts
@@ -222,16 +222,19 @@
   // getEntryLabel() can accept locationInfo=null, but TS doesn't recognize the
   // type definition in closure, hence the ! here.
   const label = util.getEntryLabel(locationInfo!, entry);
-  const volumeType = volumeInfo?.volumeType || null;
+  // For FakeEntry, we need to read from entry.volumeType because it doesn't
+  // have volumeInfo in the volume manager.
+  const volumeType = 'volumeType' in entry && entry.volumeType ?
+      entry.volumeType as VolumeManagerCommon.VolumeType :
+      (volumeInfo?.volumeType || null);
   const icon = getEntryIcon(entry, locationInfo, volumeType);
 
   /**
    * Update disabled attribute if entry supports disabled attribute and has a
    * non-null volumeType.
    */
-  if ('disabled' in entry && 'volumeType' in entry && entry.volumeType) {
-    entry.disabled = volumeManager.isDisabled(
-        entry.volumeType as VolumeManagerCommon.VolumeType);
+  if ('disabled' in entry && volumeType) {
+    entry.disabled = volumeManager.isDisabled(volumeType);
   }
 
   const metadata = metadataModel ?
@@ -245,8 +248,10 @@
     isDirectory: entry.isDirectory,
     label,
     volumeType,
+    rootType: locationInfo?.rootType ?? null,
     metadata,
     expanded: false,
+    disabled: 'disabled' in entry ? entry.disabled as boolean : false,
     isRootEntry: !!locationInfo?.isRootEntry,
     // `isEjectable/shouldDelayLoadingChildren` is determined by its
     // corresponding volume, will be updated when volume is added.
diff --git a/ui/file_manager/file_manager/state/reducers/all_entries_unittest.ts b/ui/file_manager/file_manager/state/reducers/all_entries_unittest.ts
index 794ef553..eeb8a60 100644
--- a/ui/file_manager/file_manager/state/reducers/all_entries_unittest.ts
+++ b/ui/file_manager/file_manager/state/reducers/all_entries_unittest.ts
@@ -2,15 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {assertEquals, assertFalse, assertNotEquals, assertTrue} from 'chrome://webui-test/chromeos/chai_assert.js';
+import {assertDeepEquals, assertEquals, assertFalse, assertNotEquals, assertTrue} from 'chrome://webui-test/chromeos/chai_assert.js';
 
 import {MockVolumeManager} from '../../background/js/mock_volume_manager.js';
-import {EntryList, FakeEntryImpl, VolumeEntry} from '../../common/js/files_app_entry_types.js';
+import {EntryList, FakeEntryImpl, GuestOsPlaceholder, VolumeEntry} from '../../common/js/files_app_entry_types.js';
 import {MockFileSystem} from '../../common/js/mock_entry.js';
 import {waitUntil} from '../../common/js/test_error_reporting.js';
 import {VolumeManagerCommon} from '../../common/js/volume_manager_types.js';
 import {EntryType, FileData, State} from '../../externs/ts/state.js';
 import {VolumeInfo} from '../../externs/volume_info.js';
+import {constants} from '../../foreground/js/constants.js';
 import {MetadataItem} from '../../foreground/js/metadata/metadata_item.js';
 import {MockMetadataModel} from '../../foreground/js/metadata/mock_metadata.js';
 import {ActionType} from '../actions.js';
@@ -342,3 +343,140 @@
 
   done();
 }
+
+/** Tests converting VolumeEntry into FileData. */
+export async function testConvertVolumeEntryToFileData(done: () => void) {
+  const {volumeManager} = window.fileManager;
+  const downloadsVolumeInfo = volumeManager.getCurrentProfileVolumeInfo(
+      VolumeManagerCommon.VolumeType.DOWNLOADS)!;
+  const downloadsEntry = new VolumeEntry(downloadsVolumeInfo);
+  const got = convertEntryToFileData(downloadsEntry);
+  const want: FileData = {
+    entry: downloadsEntry,
+    icon: constants.ICON_TYPES.MY_FILES,
+    type: EntryType.VOLUME_ROOT,
+    isDirectory: true,
+    label: 'Downloads',
+    volumeType: VolumeManagerCommon.VolumeType.DOWNLOADS,
+    rootType: VolumeManagerCommon.RootType.DOWNLOADS,
+    metadata: {} as MetadataItem,
+    expanded: false,
+    disabled: false,
+    isRootEntry: true,
+    isEjectable: false,
+    shouldDelayLoadingChildren: false,
+    children: [],
+  };
+  assertDeepEquals(want, got);
+
+  // Now disabled the volume in the volume manager.
+  volumeManager.isDisabled = (volumeType) =>
+      volumeType === VolumeManagerCommon.VolumeType.DOWNLOADS;
+  const fileData = convertEntryToFileData(downloadsEntry);
+  assertEquals(true, fileData.disabled);
+
+  done();
+}
+
+/** Tests converting EntryList into FileData. */
+export async function testConvertEntryListToFileData(done: () => void) {
+  const myFilesEntryList =
+      new EntryList('My files', VolumeManagerCommon.RootType.MY_FILES);
+  const got = convertEntryToFileData(myFilesEntryList);
+  const want: FileData = {
+    entry: myFilesEntryList,
+    icon: constants.ICON_TYPES.MY_FILES,
+    type: EntryType.ENTRY_LIST,
+    isDirectory: true,
+    label: 'My files',
+    volumeType: VolumeManagerCommon.VolumeType.DOWNLOADS,
+    rootType: VolumeManagerCommon.VolumeType.MY_FILES,
+    metadata: {} as MetadataItem,
+    expanded: false,
+    disabled: false,
+    isRootEntry: true,
+    isEjectable: false,
+    shouldDelayLoadingChildren: false,
+    children: [],
+  };
+  assertDeepEquals(want, got);
+
+  done();
+}
+
+/** Tests converting FakeEntry into FileData. */
+export async function testConvertFakeEntryToFileData(done: () => void) {
+  const androidFakeEntry = new GuestOsPlaceholder(
+      'Android files', 0, chrome.fileManagerPrivate.VmType.ARCVM);
+  const got = convertEntryToFileData(androidFakeEntry);
+  const want: FileData = {
+    entry: androidFakeEntry,
+    icon: constants.ICON_TYPES.ANDROID_FILES,
+    type: EntryType.PLACEHOLDER,
+    isDirectory: true,
+    label: 'Android files',
+    volumeType: VolumeManagerCommon.VolumeType.ANDROID_FILES,
+    rootType: VolumeManagerCommon.RootType.GUEST_OS,
+    metadata: {} as MetadataItem,
+    expanded: false,
+    disabled: false,
+    isRootEntry: true,
+    isEjectable: false,
+    shouldDelayLoadingChildren: false,
+    children: [],
+  };
+  assertDeepEquals(want, got);
+
+  done();
+}
+
+/** Tests converting native file entry into FileData. */
+export async function testConvertNativeFileEntryToFileData(done: () => void) {
+  const fileEntry = fileSystem.entries['/dir-2/file-1.txt'];
+  const got = convertEntryToFileData(fileEntry);
+  const want: FileData = {
+    entry: fileEntry,
+    icon: 'text',
+    type: EntryType.FS_API,
+    isDirectory: false,
+    label: 'file-1.txt',
+    volumeType: VolumeManagerCommon.VolumeType.DOWNLOADS,
+    rootType: VolumeManagerCommon.RootType.DOWNLOADS,
+    metadata: {} as MetadataItem,
+    expanded: false,
+    disabled: false,
+    isRootEntry: false,
+    isEjectable: false,
+    shouldDelayLoadingChildren: false,
+    children: [],
+  };
+  assertDeepEquals(want, got);
+
+  done();
+}
+
+/** Tests converting native directory entry into FileData. */
+export async function testConvertNativeDirectoryEntryToFileData(
+    done: () => void) {
+  const directoryEntry = fileSystem.entries['/dir-1'];
+  const got = convertEntryToFileData(directoryEntry);
+  const want: FileData = {
+    entry: directoryEntry,
+    icon: constants.ICON_TYPES.FOLDER,
+    type: EntryType.FS_API,
+    isDirectory: true,
+    label: 'dir-1',
+    volumeType: VolumeManagerCommon.VolumeType.DOWNLOADS,
+    rootType: VolumeManagerCommon.RootType.DOWNLOADS,
+    metadata: {} as MetadataItem,
+    expanded: false,
+    disabled: false,
+    isRootEntry: false,
+    isEjectable: false,
+    shouldDelayLoadingChildren: false,
+    children: [],
+  };
+  assertDeepEquals(want, got);
+
+  done();
+}
diff --git a/ui/file_manager/file_manager/widgets/xf_breadcrumb.ts b/ui/file_manager/file_manager/widgets/xf_breadcrumb.ts
index c918703..ca1145f9 100644
--- a/ui/file_manager/file_manager/widgets/xf_breadcrumb.ts
+++ b/ui/file_manager/file_manager/widgets/xf_breadcrumb.ts
@@ -589,7 +589,7 @@
     }
 
     cr-action-menu {
-      --cr-menu-background-color: var(--cros-sys-app_base_elevated);
+      --cr-menu-background-color: var(--cros-sys-base_elevated);
       --cr-menu-background-sheen: none;
       /* TODO(wenbojie): use elevation variable when it's ready.
       --cros-sys-elevation3 */
diff --git a/ui/file_manager/file_manager/widgets/xf_select.ts b/ui/file_manager/file_manager/widgets/xf_select.ts
index 39eed9e..cb26cbf2 100644
--- a/ui/file_manager/file_manager/widgets/xf_select.ts
+++ b/ui/file_manager/file_manager/widgets/xf_select.ts
@@ -11,7 +11,7 @@
 import {AnchorAlignment, CrActionMenuElement} from 'chrome://resources/cr_elements/cr_action_menu/cr_action_menu.js';
 import {CrButtonElement} from 'chrome://resources/cr_elements/cr_button/cr_button.js';
 
-import {css, CSSResultGroup, customElement, html, property, query, state, XfBase} from './xf_base.js';
+import {css, CSSResultGroup, customElement, html, property, query, XfBase} from './xf_base.js';
 
 /**
  * The data structure used to set the new options on the select element.
@@ -102,11 +102,6 @@
   @query('cr-action-menu') private $optionsMenu_?: CrActionMenuElement;
 
   /**
-   * Keeps track of whether we are showing the options menu.
-   */
-  @state() private optionsVisible_: boolean = false;
-
-  /**
    * The currently selected option.
    */
   private selectedOption_: XfSelectedValue = {
@@ -138,7 +133,7 @@
    * collapsed.
    */
   get expanded(): boolean {
-    return this.optionsVisible_;
+    return this.$optionsMenu_ ? this.$optionsMenu_.open : false;
   }
 
   /**
@@ -157,7 +152,7 @@
     return html`
       <cr-button id="dropdown-toggle"
               aria-haspopup="menu"
-              aria-expanded=${this.optionsVisible_}
+              aria-expanded=${this.expanded}
               @click=${this.onToggleOptions_}>
         ${iconPart}${labelPart}<span id="dropdown-icon"></span>
       </cr-button>`;
@@ -248,7 +243,7 @@
    * dropdown options.
    */
   private onToggleOptions_(): void {
-    if (this.optionsVisible_) {
+    if (this.expanded) {
       this.closeOptions_();
     } else {
       this.openOptions_();
@@ -259,12 +254,11 @@
    * Opens the dropdown options, providing they were closed.
    */
   private openOptions_() {
-    if (!this.optionsVisible_) {
+    if (!this.expanded) {
       const element: HTMLElement = this.$toggleDropdownButton_!;
       const top = element.offsetTop + element.offsetHeight + 8;
       this.$optionsMenu_!.showAt(
           element, {top: top, anchorAlignmentX: AnchorAlignment.AFTER_START});
-      this.optionsVisible_ = true;
     }
   }
 
@@ -272,9 +266,8 @@
    * Closes the dropdown options, providing they were open.
    */
   private closeOptions_() {
-    if (this.optionsVisible_) {
+    if (this.expanded) {
       this.$optionsMenu_!.close();
-      this.optionsVisible_ = false;
     }
   }
 
diff --git a/ui/file_manager/file_names.gni b/ui/file_manager/file_names.gni
index bc72c979..1ce64f31 100644
--- a/ui/file_manager/file_names.gni
+++ b/ui/file_manager/file_names.gni
@@ -322,6 +322,9 @@
 ]
 
 ts_test_files = [
+  # Common.
+  "file_manager/common/js/entry_utils_unittest.ts",
+
   # Containers
   "file_manager/containers/breadcrumb_container_unittest.ts",
   "file_manager/containers/directory_tree_container_unittest.ts",
diff --git a/ui/gfx/android/android_surface_control_compat.cc b/ui/gfx/android/android_surface_control_compat.cc
index 243541a..b94d128 100644
--- a/ui/gfx/android/android_surface_control_compat.cc
+++ b/ui/gfx/android/android_surface_control_compat.cc
@@ -10,13 +10,17 @@
 
 #include "base/android/build_info.h"
 #include "base/atomic_sequence_num.h"
+#include "base/containers/flat_set.h"
 #include "base/debug/crash_logging.h"
+#include "base/debug/dump_without_crashing.h"
 #include "base/functional/bind.h"
 #include "base/hash/md5_constexpr.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/raw_ptr.h"
+#include "base/no_destructor.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/synchronization/lock.h"
 #include "base/system/sys_info.h"
 #include "base/task/bind_post_task.h"
 #include "base/task/single_thread_task_runner.h"
@@ -481,6 +485,16 @@
   return kMask ^ transaction_id;
 }
 
+base::Lock& GetGlobalLock() {
+  static base::NoDestructor<base::Lock> lock;
+  return *lock;
+}
+
+base::flat_set<int>& GetGlobalPendingCompleteCallbackIds() {
+  static base::NoDestructor<base::flat_set<int>> set;
+  return *set;
+}
+
 // Note that the framework API states that this callback can be dispatched on
 // any thread (in practice it should be a binder thread).
 void OnTransactionCompletedOnAnyThread(void* context,
@@ -493,6 +507,17 @@
       "toplevel.flow", "gfx::SurfaceControlTransaction completed",
       GetTraceIdForTransaction(ack_ctx->id), TRACE_EVENT_FLAG_FLOW_IN);
 
+  bool dump = false;
+  {
+    base::AutoLock lock(GetGlobalLock());
+    size_t num_removed =
+        GetGlobalPendingCompleteCallbackIds().erase(ack_ctx->id);
+    dump = !num_removed;
+  }
+  if (dump) {
+    base::debug::DumpWithoutCrashing(base::Location::Current(), base::Days(1));
+  }
+
   std::move(ack_ctx->callback).Run(std::move(transaction_stats));
   delete ack_ctx;
 }
@@ -883,6 +908,16 @@
     ack_ctx->callback = std::move(on_complete_cb_);
     ack_ctx->id = id_;
 
+    bool dump = false;
+    {
+      base::AutoLock lock(GetGlobalLock());
+      auto result = GetGlobalPendingCompleteCallbackIds().insert(id_);
+      dump = !result.second;
+    }
+    if (dump) {
+      base::debug::DumpWithoutCrashing(base::Location::Current(),
+                                       base::Days(1));
+    }
     SurfaceControlMethods::Get().ASurfaceTransaction_setOnCompleteFn(
         transaction_, ack_ctx, &OnTransactionCompletedOnAnyThread);
     need_to_apply_ = true;
diff --git a/ui/views/controls/button/button.cc b/ui/views/controls/button/button.cc
index 61c61384..5513499 100644
--- a/ui/views/controls/button/button.cc
+++ b/ui/views/controls/button/button.cc
@@ -540,8 +540,6 @@
   }
   if (GetEnabled())
     node_data->SetDefaultActionVerb(ax::mojom::DefaultActionVerb::kPress);
-
-  button_controller_->UpdateAccessibleNodeData(node_data);
 }
 
 void Button::VisibilityChanged(View* starting_from, bool visible) {
diff --git a/ui/views/controls/button/button_controller.cc b/ui/views/controls/button/button_controller.cc
index b27d698..404033706 100644
--- a/ui/views/controls/button/button_controller.cc
+++ b/ui/views/controls/button/button_controller.cc
@@ -137,8 +137,6 @@
   }
 }
 
-void ButtonController::UpdateAccessibleNodeData(ui::AXNodeData* node_data) {}
-
 bool ButtonController::IsTriggerableEvent(const ui::Event& event) {
   return event.type() == ui::ET_GESTURE_TAP_DOWN ||
          event.type() == ui::ET_GESTURE_TAP ||
diff --git a/ui/views/controls/button/button_controller.h b/ui/views/controls/button/button_controller.h
index 941586c6..3f3cb40 100644
--- a/ui/views/controls/button/button_controller.h
+++ b/ui/views/controls/button/button_controller.h
@@ -50,9 +50,6 @@
   virtual bool OnKeyReleased(const ui::KeyEvent& event);
   virtual void OnGestureEvent(ui::GestureEvent* event);
 
-  // Updates |node_data| for a button based on the functionality.
-  virtual void UpdateAccessibleNodeData(ui::AXNodeData* node_data);
-
   // Methods that parallel respective methods in Button:
   virtual bool IsTriggerableEvent(const ui::Event& event);
 
diff --git a/ui/views/controls/button/checkbox.cc b/ui/views/controls/button/checkbox.cc
index fb0b27ab..da305daa1 100644
--- a/ui/views/controls/button/checkbox.cc
+++ b/ui/views/controls/button/checkbox.cc
@@ -91,6 +91,8 @@
   // Avoid the default ink-drop mask to allow the ripple effect to extend beyond
   // the checkbox view (otherwise it gets clipped which looks weird).
   views::InstallEmptyHighlightPathGenerator(this);
+
+  SetAccessibilityProperties(ax::mojom::Role::kCheckBox);
 }
 
 Checkbox::~Checkbox() = default;
@@ -146,7 +148,6 @@
 
 void Checkbox::GetAccessibleNodeData(ui::AXNodeData* node_data) {
   LabelButton::GetAccessibleNodeData(node_data);
-  node_data->role = ax::mojom::Role::kCheckBox;
   const ax::mojom::CheckedState checked_state =
       GetChecked() ? ax::mojom::CheckedState::kTrue
                    : ax::mojom::CheckedState::kFalse;
diff --git a/ui/views/controls/button/checkbox_unittest.cc b/ui/views/controls/button/checkbox_unittest.cc
index 69ac9fd9..f041472 100644
--- a/ui/views/controls/button/checkbox_unittest.cc
+++ b/ui/views/controls/button/checkbox_unittest.cc
@@ -61,10 +61,24 @@
 
   ui::AXNodeData ax_data;
   checkbox()->GetAccessibleNodeData(&ax_data);
-
   EXPECT_EQ(ax_data.GetString16Attribute(ax::mojom::StringAttribute::kName),
             label_text);
+  EXPECT_EQ(checkbox()->GetAccessibleName(), label_text);
   EXPECT_EQ(ax_data.role, ax::mojom::Role::kCheckBox);
+  EXPECT_EQ(checkbox()->GetAccessibleRole(), ax::mojom::Role::kCheckBox);
+  EXPECT_EQ(ax_data.GetCheckedState(), ax::mojom::CheckedState::kFalse);
+
+  ax_data = ui::AXNodeData();
+  checkbox()->SetChecked(true);
+  checkbox()->GetAccessibleNodeData(&ax_data);
+  EXPECT_EQ(ax_data.GetCheckedState(), ax::mojom::CheckedState::kTrue);
+
+  ax_data = ui::AXNodeData();
+  checkbox()->SetAccessibleRole(ax::mojom::Role::kMenuItemCheckBox);
+  checkbox()->GetAccessibleNodeData(&ax_data);
+  EXPECT_EQ(ax_data.role, ax::mojom::Role::kMenuItemCheckBox);
+  EXPECT_EQ(checkbox()->GetAccessibleRole(),
+            ax::mojom::Role::kMenuItemCheckBox);
 }
 
 }  // namespace views
diff --git a/ui/views/controls/button/image_button.cc b/ui/views/controls/button/image_button.cc
index 32de82bb..c0255128 100644
--- a/ui/views/controls/button/image_button.cc
+++ b/ui/views/controls/button/image_button.cc
@@ -6,6 +6,7 @@
 
 #include <utility>
 
+#include "base/functional/bind.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/trace_event/trace_event.h"
 #include "ui/accessibility/ax_enums.mojom.h"
@@ -15,7 +16,10 @@
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/image/image_skia_operations.h"
 #include "ui/gfx/scoped_canvas.h"
+#include "ui/views/animation/ink_drop.h"
 #include "ui/views/background.h"
+#include "ui/views/controls/highlight_path_generator.h"
+#include "ui/views/layout/layout_provider.h"
 #include "ui/views/painter.h"
 #include "ui/views/widget/widget.h"
 
@@ -149,6 +153,67 @@
   SchedulePaint();
 }
 
+// static
+std::unique_ptr<ImageButton> ImageButton::CreateIconButton(
+    PressedCallback callback,
+    const gfx::VectorIcon& icon,
+    const std::u16string& accessible_name,
+    MaterialIconStyle icon_style) {
+  const int kSmallIconSize = 14;
+  const int kLargeIconSize = 20;
+  int icon_size = (icon_style == MaterialIconStyle::kLarge) ? kLargeIconSize
+                                                            : kSmallIconSize;
+  // Icon images have padding between the image and image border. To account
+  // for that padding, add a general padding value. This value might be
+  // incorrect depending on the icon image.
+  icon_size +=
+      LayoutProvider::Get()->GetDistanceMetric(DISTANCE_VECTOR_ICON_PADDING);
+
+  std::unique_ptr<ImageButton> icon_button =
+      std::make_unique<ImageButton>(callback);
+  icon_button->SetImageModel(
+      ButtonState::STATE_NORMAL,
+      ui::ImageModel::FromVectorIcon(icon, ui::kColorIcon, icon_size));
+  icon_button->SetImageModel(
+      ButtonState::STATE_HOVERED,
+      ui::ImageModel::FromVectorIcon(icon, ui::kColorIcon, icon_size));
+  icon_button->SetImageModel(
+      ButtonState::STATE_PRESSED,
+      ui::ImageModel::FromVectorIcon(icon, ui::kColorIcon, icon_size));
+  icon_button->SetImageModel(
+      ButtonState::STATE_DISABLED,
+      ui::ImageModel::FromVectorIcon(icon, ui::kColorIconDisabled, icon_size));
+
+  const gfx::Insets target_insets =
+      LayoutProvider::Get()->GetInsetsMetric(InsetsMetric::INSETS_ICON_BUTTON);
+  icon_button->SetBorder(views::CreateEmptyBorder(target_insets));
+
+  const int kSmallIconButtonSize = 24;
+  const int kLargeIconButtonSize = 32;
+  int button_size = (icon_style == MaterialIconStyle::kLarge)
+                        ? kLargeIconButtonSize
+                        : kSmallIconButtonSize;
+  const int highlight_radius = LayoutProvider::Get()->GetCornerRadiusMetric(
+      views::Emphasis::kMaximum, gfx::Size(button_size, button_size));
+  views::InstallRoundRectHighlightPathGenerator(
+      icon_button.get(), gfx::Insets(), highlight_radius);
+
+  InkDrop::Get(icon_button.get())->SetMode(views::InkDropHost::InkDropMode::ON);
+  icon_button->SetHasInkDropActionOnClick(true);
+  icon_button->SetShowInkDropWhenHotTracked(true);
+  InkDrop::Get(icon_button.get())
+      ->SetBaseColorCallback(base::BindRepeating(
+          [](ImageButton* host) {
+            return host->GetColorProvider()->GetColor(
+                ui::kColorSysOnSurfaceSubtle);
+          },
+          icon_button.get()));
+
+  icon_button->SetAccessibleName(accessible_name);
+
+  return icon_button;
+}
+
 void ImageButton::PaintButtonContents(gfx::Canvas* canvas) {
   // TODO(estade|tdanderson|bruthig): The ink drop layer should be positioned
   // behind the button's image which means the image needs to be painted to its
@@ -227,6 +292,7 @@
 
 ToggleImageButton::ToggleImageButton(PressedCallback callback)
     : ImageButton(std::move(callback)) {
+  SetAccessibilityProperties(ax::mojom::Role::kToggleButton);
 }
 
 ToggleImageButton::~ToggleImageButton() = default;
@@ -277,6 +343,11 @@
 void ToggleImageButton::SetToggledTooltipText(const std::u16string& tooltip) {
   if (tooltip == toggled_tooltip_text_)
     return;
+
+  if (toggled_accessible_name_.empty() && !tooltip.empty()) {
+    SetAccessibleName(tooltip);
+  }
+
   toggled_tooltip_text_ = tooltip;
   OnPropertyChanged(&toggled_tooltip_text_, kPropertyEffectsNone);
 }
@@ -288,7 +359,14 @@
 void ToggleImageButton::SetToggledAccessibleName(const std::u16string& name) {
   if (name == toggled_accessible_name_)
     return;
+
   toggled_accessible_name_ = name;
+  if (!toggled_accessible_name_.empty()) {
+    SetAccessibleName(toggled_accessible_name_);
+  } else if (!toggled_tooltip_text_.empty()) {
+    SetAccessibleName(toggled_tooltip_text_);
+  }
+
   OnPropertyChanged(&toggled_accessible_name_, kPropertyEffectsNone);
 }
 
@@ -336,11 +414,6 @@
   if (!toggled_)
     return;
 
-  if (!toggled_accessible_name_.empty())
-    node_data->SetName(toggled_accessible_name_);
-  else if (!toggled_tooltip_text_.empty())
-    node_data->SetName(toggled_tooltip_text_);
-
   // Use the visual pressed image as a cue for making this control into an
   // accessible toggle button.
   if ((toggled_ && !images_[ButtonState::STATE_NORMAL].IsEmpty()) ||
diff --git a/ui/views/controls/button/image_button.h b/ui/views/controls/button/image_button.h
index a3aea3f..8da5ac8d 100644
--- a/ui/views/controls/button/image_button.h
+++ b/ui/views/controls/button/image_button.h
@@ -75,6 +75,16 @@
   views::PaintInfo::ScaleType GetPaintScaleType() const override;
   void OnThemeChanged() override;
 
+  enum class MaterialIconStyle { kSmall, kLarge };
+
+  // Static method to create a Icon button with Google Material style
+  // guidelines.
+  static std::unique_ptr<ImageButton> CreateIconButton(
+      PressedCallback callback,
+      const gfx::VectorIcon& icon,
+      const std::u16string& accessible_name,
+      MaterialIconStyle icon_style = MaterialIconStyle::kLarge);
+
  protected:
   // Overridden from Button:
   void PaintButtonContents(gfx::Canvas* canvas) override;
@@ -166,10 +176,17 @@
   Background* GetToggledBackground() const { return toggled_background_.get(); }
 
   // Get/Set the tooltip text displayed when the button is toggled.
+  // TODO(accessibility): This seems like it provides a fallback name.
+  // Should callers who want this to be the name use `SetAccessibleName`?
+  // If it should be a description, then `SetAccessibleDescription`?
+  // Note that if something lacks an accessible description but has a tooltip,
+  // the tooltip text will be used. Does the tooltip text match this text?
   std::u16string GetToggledTooltipText() const;
   void SetToggledTooltipText(const std::u16string& tooltip);
 
   // Get/Set the accessible text used when the button is toggled.
+  // TODO(accessibility): Can we just use the `AccessibleName` getter/setter
+  // from View?
   std::u16string GetToggledAccessibleName() const;
   void SetToggledAccessibleName(const std::u16string& name);
 
diff --git a/ui/views/controls/button/image_button_unittest.cc b/ui/views/controls/button/image_button_unittest.cc
index 5c6567bd..f7611802 100644
--- a/ui/views/controls/button/image_button_unittest.cc
+++ b/ui/views/controls/button/image_button_unittest.cc
@@ -197,4 +197,34 @@
   EXPECT_EQ(1, parent.pref_size_changed_calls());
 }
 
+TEST_F(ImageButtonTest, ImageButtonAccessibleProperties) {
+  ImageButton button;
+  ui::AXNodeData data;
+  button.GetAccessibleNodeData(&data);
+  EXPECT_EQ(data.role, ax::mojom::Role::kButton);
+  EXPECT_EQ(button.GetAccessibleRole(), ax::mojom::Role::kButton);
+
+  button.SetAccessibleRole(ax::mojom::Role::kPopUpButton);
+
+  data = ui::AXNodeData();
+  button.GetAccessibleNodeData(&data);
+  EXPECT_EQ(data.role, ax::mojom::Role::kPopUpButton);
+  EXPECT_EQ(button.GetAccessibleRole(), ax::mojom::Role::kPopUpButton);
+}
+
+TEST_F(ImageButtonTest, ToggleImageButtonAccessibleProperties) {
+  ToggleImageButton button;
+  ui::AXNodeData data;
+  button.GetAccessibleNodeData(&data);
+  EXPECT_EQ(data.role, ax::mojom::Role::kToggleButton);
+  EXPECT_EQ(button.GetAccessibleRole(), ax::mojom::Role::kToggleButton);
+
+  button.SetAccessibleRole(ax::mojom::Role::kPopUpButton);
+
+  data = ui::AXNodeData();
+  button.GetAccessibleNodeData(&data);
+  EXPECT_EQ(data.role, ax::mojom::Role::kPopUpButton);
+  EXPECT_EQ(button.GetAccessibleRole(), ax::mojom::Role::kPopUpButton);
+}
+
 }  // namespace views
diff --git a/ui/views/controls/button/menu_button.cc b/ui/views/controls/button/menu_button.cc
index b1d3b76..3203d471 100644
--- a/ui/views/controls/button/menu_button.cc
+++ b/ui/views/controls/button/menu_button.cc
@@ -7,6 +7,7 @@
 #include <memory>
 #include <utility>
 
+#include "ui/accessibility/ax_enums.mojom.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
 #include "ui/events/event.h"
 #include "ui/views/controls/button/button_controller_delegate.h"
@@ -28,6 +29,7 @@
   SetButtonController(std::move(menu_button_controller));
 
   SetFocusBehavior(FocusBehavior::ACCESSIBLE_ONLY);
+  SetAccessibilityProperties(ax::mojom::Role::kPopUpButton);
 }
 
 MenuButton::~MenuButton() = default;
@@ -40,6 +42,14 @@
   menu_button_controller_->SetCallback(std::move(callback));
 }
 
+void MenuButton::GetAccessibleNodeData(ui::AXNodeData* node_data) {
+  LabelButton::GetAccessibleNodeData(node_data);
+  node_data->SetHasPopup(ax::mojom::HasPopup::kMenu);
+  if (GetEnabled()) {
+    node_data->SetDefaultActionVerb(ax::mojom::DefaultActionVerb::kOpen);
+  }
+}
+
 void MenuButton::NotifyClick(const ui::Event& event) {
   // Run pressed callback via MenuButtonController, instead of directly.
   button_controller()->Activate(&event);
diff --git a/ui/views/controls/button/menu_button.h b/ui/views/controls/button/menu_button.h
index db8131d..53ba2b8 100644
--- a/ui/views/controls/button/menu_button.h
+++ b/ui/views/controls/button/menu_button.h
@@ -41,6 +41,7 @@
 
   // Button:
   void SetCallback(PressedCallback callback) override;
+  void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
 
  protected:
   // Button:
diff --git a/ui/views/controls/button/menu_button_controller.cc b/ui/views/controls/button/menu_button_controller.cc
index 4eb3e629..4a2dd57 100644
--- a/ui/views/controls/button/menu_button_controller.cc
+++ b/ui/views/controls/button/menu_button_controller.cc
@@ -8,7 +8,6 @@
 
 #include "base/functional/bind.h"
 #include "ui/accessibility/ax_enums.mojom.h"
-#include "ui/accessibility/ax_node_data.h"
 #include "ui/base/dragdrop/drag_drop_types.h"
 #include "ui/base/interaction/element_identifier.h"
 #include "ui/events/event_constants.h"
@@ -177,13 +176,6 @@
   return false;
 }
 
-void MenuButtonController::UpdateAccessibleNodeData(ui::AXNodeData* node_data) {
-  node_data->role = ax::mojom::Role::kPopUpButton;
-  node_data->SetHasPopup(ax::mojom::HasPopup::kMenu);
-  if (button()->GetEnabled())
-    node_data->SetDefaultActionVerb(ax::mojom::DefaultActionVerb::kOpen);
-}
-
 bool MenuButtonController::IsTriggerableEvent(const ui::Event& event) {
   return ButtonController::IsTriggerableEvent(event) &&
          IsTriggerableEventType(event) && is_intentional_menu_trigger_;
diff --git a/ui/views/controls/button/menu_button_controller.h b/ui/views/controls/button/menu_button_controller.h
index 7f23dbe7..358e420 100644
--- a/ui/views/controls/button/menu_button_controller.h
+++ b/ui/views/controls/button/menu_button_controller.h
@@ -59,7 +59,6 @@
   bool OnKeyPressed(const ui::KeyEvent& event) override;
   bool OnKeyReleased(const ui::KeyEvent& event) override;
   void OnGestureEvent(ui::GestureEvent* event) override;
-  void UpdateAccessibleNodeData(ui::AXNodeData* node_data) override;
   bool IsTriggerableEvent(const ui::Event& event) override;
 
   // Calls TakeLock with is_sibling_menu_show as false and a nullptr to the
diff --git a/ui/views/controls/button/menu_button_unittest.cc b/ui/views/controls/button/menu_button_unittest.cc
index 2c4ff9e2..b8c6ace 100644
--- a/ui/views/controls/button/menu_button_unittest.cc
+++ b/ui/views/controls/button/menu_button_unittest.cc
@@ -610,4 +610,23 @@
   button()->OnGestureEvent(&gesture_event);
 }
 
+TEST_F(MenuButtonTest, AccessibleProperties) {
+  ConfigureMenuButton(std::make_unique<TestMenuButton>());
+  ui::AXNodeData data;
+  button()->GetAccessibleNodeData(&data);
+  EXPECT_EQ(button()->GetAccessibleRole(), ax::mojom::Role::kPopUpButton);
+  EXPECT_EQ(data.role, ax::mojom::Role::kPopUpButton);
+  EXPECT_EQ(data.GetHasPopup(), ax::mojom::HasPopup::kMenu);
+  EXPECT_EQ(data.GetDefaultActionVerb(), ax::mojom::DefaultActionVerb::kOpen);
+
+  button()->SetAccessibleRole(ax::mojom::Role::kButton);
+
+  data = ui::AXNodeData();
+  button()->GetAccessibleNodeData(&data);
+  EXPECT_EQ(button()->GetAccessibleRole(), ax::mojom::Role::kButton);
+  EXPECT_EQ(data.role, ax::mojom::Role::kButton);
+  EXPECT_EQ(data.GetHasPopup(), ax::mojom::HasPopup::kMenu);
+  EXPECT_EQ(data.GetDefaultActionVerb(), ax::mojom::DefaultActionVerb::kOpen);
+}
+
 }  // namespace views
diff --git a/ui/views/controls/button/radio_button.cc b/ui/views/controls/button/radio_button.cc
index 4b61c2d1..18665f0 100644
--- a/ui/views/controls/button/radio_button.cc
+++ b/ui/views/controls/button/radio_button.cc
@@ -9,7 +9,6 @@
 #include "base/ranges/algorithm.h"
 #include "ui/accessibility/ax_action_data.h"
 #include "ui/accessibility/ax_enums.mojom.h"
-#include "ui/accessibility/ax_node_data.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/events/event_utils.h"
@@ -33,15 +32,11 @@
 RadioButton::RadioButton(const std::u16string& label, int group_id)
     : Checkbox(label) {
   SetGroup(group_id);
+  SetAccessibilityProperties(ax::mojom::Role::kRadioButton);
 }
 
 RadioButton::~RadioButton() = default;
 
-void RadioButton::GetAccessibleNodeData(ui::AXNodeData* node_data) {
-  Checkbox::GetAccessibleNodeData(node_data);
-  node_data->role = ax::mojom::Role::kRadioButton;
-}
-
 View* RadioButton::GetSelectedViewForGroup(int group) {
   Views views;
   GetViewsInGroupFromParent(group, &views);
diff --git a/ui/views/controls/button/radio_button.h b/ui/views/controls/button/radio_button.h
index d4114df..7a4a53e 100644
--- a/ui/views/controls/button/radio_button.h
+++ b/ui/views/controls/button/radio_button.h
@@ -28,7 +28,6 @@
   ~RadioButton() override;
 
   // Overridden from View:
-  void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
   View* GetSelectedViewForGroup(int group) override;
   bool HandleAccessibleAction(const ui::AXActionData& action_data) override;
   bool IsGroupFocusTraversable() const override;
diff --git a/ui/views/controls/button/radio_button_unittest.cc b/ui/views/controls/button/radio_button_unittest.cc
index 20a45fb..f9cf8ad5 100644
--- a/ui/views/controls/button/radio_button_unittest.cc
+++ b/ui/views/controls/button/radio_button_unittest.cc
@@ -131,4 +131,28 @@
   EXPECT_EQ(button1, focus_manager->GetFocusedView());
 }
 
+TEST_F(RadioButtonTest, AccessibilityTest) {
+  RadioButton* button = new RadioButton(u"Item 1", kGroup);
+  ui::AXNodeData data;
+  button->GetAccessibleNodeData(&data);
+
+  EXPECT_EQ(data.GetString16Attribute(ax::mojom::StringAttribute::kName),
+            u"Item 1");
+  EXPECT_EQ(button->GetAccessibleName(), u"Item 1");
+  EXPECT_EQ(data.role, ax::mojom::Role::kRadioButton);
+  EXPECT_EQ(button->GetAccessibleRole(), ax::mojom::Role::kRadioButton);
+  EXPECT_EQ(data.GetCheckedState(), ax::mojom::CheckedState::kFalse);
+
+  data = ui::AXNodeData();
+  button->SetChecked(true);
+  button->GetAccessibleNodeData(&data);
+  EXPECT_EQ(data.GetCheckedState(), ax::mojom::CheckedState::kTrue);
+
+  data = ui::AXNodeData();
+  button->SetAccessibleRole(ax::mojom::Role::kMenuItemRadio);
+  button->GetAccessibleNodeData(&data);
+  EXPECT_EQ(data.role, ax::mojom::Role::kMenuItemRadio);
+  EXPECT_EQ(button->GetAccessibleRole(), ax::mojom::Role::kMenuItemRadio);
+}
+
 }  // namespace views
diff --git a/ui/views/controls/button/toggle_button.cc b/ui/views/controls/button/toggle_button.cc
index d1056314..ca0a0e9 100644
--- a/ui/views/controls/button/toggle_button.cc
+++ b/ui/views/controls/button/toggle_button.cc
@@ -292,6 +292,8 @@
   SetInstallFocusRingOnFocus(true);
   FocusRing::Get(this)->SetPathGenerator(
       std::make_unique<FocusRingHighlightPathGenerator>());
+
+  SetAccessibilityProperties(ax::mojom::Role::kSwitch);
 }
 
 ToggleButton::~ToggleButton() {
@@ -522,8 +524,6 @@
 
 void ToggleButton::GetAccessibleNodeData(ui::AXNodeData* node_data) {
   Button::GetAccessibleNodeData(node_data);
-
-  node_data->role = ax::mojom::Role::kSwitch;
   node_data->SetCheckedState(GetIsOn() ? ax::mojom::CheckedState::kTrue
                                        : ax::mojom::CheckedState::kFalse);
 }
diff --git a/ui/views/controls/button/toggle_button.h b/ui/views/controls/button/toggle_button.h
index 04f4d6e..1d719fe 100644
--- a/ui/views/controls/button/toggle_button.h
+++ b/ui/views/controls/button/toggle_button.h
@@ -81,6 +81,7 @@
   friend class TestToggleButton;
   class FocusRingHighlightPathGenerator;
   class ThumbView;
+  FRIEND_TEST_ALL_PREFIXES(ToggleButtonTest, AccessibilityTest);
 
   // Updates position of the thumb.
   void UpdateThumb();
diff --git a/ui/views/controls/button/toggle_button_unittest.cc b/ui/views/controls/button/toggle_button_unittest.cc
index 59357c8..74d34aa 100644
--- a/ui/views/controls/button/toggle_button_unittest.cc
+++ b/ui/views/controls/button/toggle_button_unittest.cc
@@ -152,4 +152,28 @@
   EXPECT_FALSE(button()->GetIsOn());
 }
 
+TEST_F(ToggleButtonTest, AccessibilityTest) {
+  button()->SetAccessibleName(u"Name");
+  ui::AXNodeData data;
+  button()->GetAccessibleNodeData(&data);
+
+  EXPECT_EQ(data.GetString16Attribute(ax::mojom::StringAttribute::kName),
+            u"Name");
+  EXPECT_EQ(button()->GetAccessibleName(), u"Name");
+  EXPECT_EQ(data.role, ax::mojom::Role::kSwitch);
+  EXPECT_EQ(button()->GetAccessibleRole(), ax::mojom::Role::kSwitch);
+  EXPECT_EQ(data.GetCheckedState(), ax::mojom::CheckedState::kFalse);
+
+  data = ui::AXNodeData();
+  button()->SetIsOn(true);
+  button()->GetAccessibleNodeData(&data);
+  EXPECT_EQ(data.GetCheckedState(), ax::mojom::CheckedState::kTrue);
+
+  data = ui::AXNodeData();
+  button()->SetAccessibleRole(ax::mojom::Role::kCheckBox);
+  button()->GetAccessibleNodeData(&data);
+  EXPECT_EQ(data.role, ax::mojom::Role::kCheckBox);
+  EXPECT_EQ(button()->GetAccessibleRole(), ax::mojom::Role::kCheckBox);
+}
+
 }  // namespace views
diff --git a/ui/views/examples/button_example.cc b/ui/views/examples/button_example.cc
index 06158bd1..c1676b6 100644
--- a/ui/views/examples/button_example.cc
+++ b/ui/views/examples/button_example.cc
@@ -250,6 +250,11 @@
                           base::Unretained(this)),
       u"Fab Prototype"));
 
+  view->AddChildView(ImageButton::CreateIconButton(
+      base::BindRepeating(&ButtonExample::ImageButtonPressed,
+                          base::Unretained(this)),
+      views::kLaunchIcon, u"Icon button"));
+
   image_button_->SetImage(ImageButton::STATE_NORMAL,
                           rb.GetImageNamed(IDR_CLOSE).ToImageSkia());
   image_button_->SetImage(ImageButton::STATE_HOVERED,
diff --git a/ui/views/layout/layout_provider.cc b/ui/views/layout/layout_provider.cc
index 8437744..d6885dc 100644
--- a/ui/views/layout/layout_provider.cc
+++ b/ui/views/layout/layout_provider.cc
@@ -69,6 +69,8 @@
       return gfx::Insets(4);
     case InsetsMetric::INSETS_LABEL_BUTTON:
       return gfx::Insets::VH(5, 6);
+    case InsetsMetric::INSETS_ICON_BUTTON:
+      return gfx::Insets(2);
   }
   NOTREACHED_NORETURN();
 }
@@ -121,6 +123,8 @@
       return features::IsChromeRefresh2023() ? 10 : 8;
     case DISTANCE_UNRELATED_CONTROL_VERTICAL:
       return 16;
+    case DISTANCE_VECTOR_ICON_PADDING:
+      return 4;
     case VIEWS_DISTANCE_END:
     case VIEWS_DISTANCE_MAX:
       NOTREACHED_NORETURN();
diff --git a/ui/views/layout/layout_provider.h b/ui/views/layout/layout_provider.h
index b432a88..a15febe2 100644
--- a/ui/views/layout/layout_provider.h
+++ b/ui/views/layout/layout_provider.h
@@ -40,6 +40,8 @@
   INSETS_VECTOR_IMAGE_BUTTON,
   // Padding used in a label button.
   INSETS_LABEL_BUTTON,
+  // Padding used in icon buttons.
+  INSETS_ICON_BUTTON,
 
   // Embedders must start Insets enum values from this value.
   VIEWS_INSETS_END,
@@ -105,6 +107,8 @@
   DISTANCE_TEXTFIELD_HORIZONTAL_TEXT_PADDING,
   // Vertical spacing between controls that are logically unrelated.
   DISTANCE_UNRELATED_CONTROL_VERTICAL,
+  // Padding in vector icons. This is a general number for more vector icons.
+  DISTANCE_VECTOR_ICON_PADDING,
 
   // Embedders must start DistanceMetric enum values from here.
   VIEWS_DISTANCE_END,
diff --git a/ui/webui/resources/cr_elements/chromeos/cros_color_overrides.css b/ui/webui/resources/cr_elements/chromeos/cros_color_overrides.css
index 6fa6ef5..07bf428 100644
--- a/ui/webui/resources/cr_elements/chromeos/cros_color_overrides.css
+++ b/ui/webui/resources/cr_elements/chromeos/cros_color_overrides.css
@@ -167,7 +167,7 @@
 
 /* Dialog */
 :host-context(body.jelly-enabled) cr-dialog::part(dialog) {
-  --cr-dialog-background-color: var(--cros-sys-app_base_elevated);
+  --cr-dialog-background-color: var(--cros-sys-base_elevated);
   background-image: none;
 
   /* TODO(b/266837484) Replace with cros.sys.app-elevation3 when available */
@@ -185,7 +185,7 @@
 :host-context(body.jelly-enabled) cr-input,
 :host-context(body.jelly-enabled) cr-search-field::part(searchInput),
 :host-context(body.jelly-enabled) cr-textarea {
-  --cr-input-background-color: var(--cros-sys-input_field_dark);
+  --cr-input-background-color: var(--cros-sys-input_field_on_base);
   --cr-input-error-color: var(--cros-sys-error);
   --cr-input-focus-color: var(--cros-sys-primary);
   --cr-input-placeholder-color: var(--cros-sys-secondary);
@@ -193,9 +193,9 @@
 
 /* md-select */
 :host-context(body.jelly-enabled) .md-select {
-  --md-select-bg-color: var(--cros-sys-input_field_dark);
+  --md-select-bg-color: var(--cros-sys-input_field_on_base);
   --md-select-focus-shadow-color: var(--cros-sys-primary);
-  --md-select-option-bg-color: var(--cros-sys-app_base_elevated);
+  --md-select-option-bg-color: var(--cros-sys-base_elevated);
   --md-select-text-color: var(--cros-sys-on_surface);
 }
 
diff --git a/ui/webui/resources/css/text_defaults_md.css b/ui/webui/resources/css/text_defaults_md.css
index 5bb3cfe0..13015eb 100644
--- a/ui/webui/resources/css/text_defaults_md.css
+++ b/ui/webui/resources/css/text_defaults_md.css
@@ -20,10 +20,10 @@
 </if>
 
 body {
-  font-family: Roboto, $i18nRaw{fontfamily};
+  font-family: $i18nRaw{fontfamilyMd};
   font-size: 81.25%;
 }
 
 button {
-  font-family: Roboto, $i18nRaw{fontfamily};
+  font-family: $i18nRaw{fontfamilyMd};
 }
diff --git a/weblayer/browser/android/javatests/BUILD.gn b/weblayer/browser/android/javatests/BUILD.gn
index 6246a7f5..33d0088e 100644
--- a/weblayer/browser/android/javatests/BUILD.gn
+++ b/weblayer/browser/android/javatests/BUILD.gn
@@ -32,14 +32,14 @@
     "//net/android:net_java_test_support",
     "//third_party/android_deps:com_google_guava_listenablefuture_java",
     "//third_party/android_deps:guava_android_java",
-    "//third_party/android_support_test_runner:rules_java",
-    "//third_party/android_support_test_runner:runner_java",
     "//third_party/androidx:androidx_activity_activity_java",
     "//third_party/androidx:androidx_annotation_annotation_java",
     "//third_party/androidx:androidx_appcompat_appcompat_java",
     "//third_party/androidx:androidx_core_core_java",
     "//third_party/androidx:androidx_fragment_fragment_java",
     "//third_party/androidx:androidx_test_core_java",
+    "//third_party/androidx:androidx_test_monitor_java",
+    "//third_party/androidx:androidx_test_rules_java",
     "//third_party/androidx:androidx_test_runner_java",
     "//third_party/blink/public/common:common_java",
     "//third_party/hamcrest:hamcrest_core_java",
@@ -64,10 +64,11 @@
     "//content/public/test/android:content_java_test_support",
     "//net/android:net_java_test_support",
     "//third_party/android_deps:com_google_guava_listenablefuture_java",
-    "//third_party/android_support_test_runner:rules_java",
-    "//third_party/android_support_test_runner:runner_java",
     "//third_party/androidx:androidx_annotation_annotation_java",
     "//third_party/androidx:androidx_fragment_fragment_java",
+    "//third_party/androidx:androidx_test_monitor_java",
+    "//third_party/androidx:androidx_test_rules_java",
+    "//third_party/androidx:androidx_test_runner_java",
     "//third_party/junit:junit",
     "//ui/android:ui_java_test_support",
     "//weblayer/public/java:webengine_java",
diff --git a/weblayer/browser/android/javatests/src/org/chromium/webengine/test/FullscreenCallbackTest.java b/weblayer/browser/android/javatests/src/org/chromium/webengine/test/FullscreenCallbackTest.java
index 13f0f16f..9405254 100644
--- a/weblayer/browser/android/javatests/src/org/chromium/webengine/test/FullscreenCallbackTest.java
+++ b/weblayer/browser/android/javatests/src/org/chromium/webengine/test/FullscreenCallbackTest.java
@@ -6,8 +6,7 @@
 
 import static org.chromium.content_public.browser.test.util.TestThreadUtils.runOnUiThreadBlocking;
 
-import android.support.test.InstrumentationRegistry;
-
+import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 
 import org.junit.After;
diff --git a/weblayer/browser/android/javatests/src/org/chromium/webengine/test/InstrumentationActivityTestRule.java b/weblayer/browser/android/javatests/src/org/chromium/webengine/test/InstrumentationActivityTestRule.java
index c9973b3..fe2ccd7 100644
--- a/weblayer/browser/android/javatests/src/org/chromium/webengine/test/InstrumentationActivityTestRule.java
+++ b/weblayer/browser/android/javatests/src/org/chromium/webengine/test/InstrumentationActivityTestRule.java
@@ -9,10 +9,10 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.support.test.InstrumentationRegistry;
 import android.view.View;
 
 import androidx.annotation.Nullable;
+import androidx.test.InstrumentationRegistry;
 
 import org.junit.Assert;
 
diff --git a/weblayer/browser/android/javatests/src/org/chromium/webengine/test/WebEngineActivityTestRule.java b/weblayer/browser/android/javatests/src/org/chromium/webengine/test/WebEngineActivityTestRule.java
index 264af1b..c98dcb6 100644
--- a/weblayer/browser/android/javatests/src/org/chromium/webengine/test/WebEngineActivityTestRule.java
+++ b/weblayer/browser/android/javatests/src/org/chromium/webengine/test/WebEngineActivityTestRule.java
@@ -6,9 +6,10 @@
 
 import android.app.Activity;
 import android.content.Context;
-import android.support.test.InstrumentationRegistry;
 import android.text.TextUtils;
 
+import androidx.test.InstrumentationRegistry;
+
 import org.junit.runner.Description;
 import org.junit.runners.model.Statement;
 
diff --git a/weblayer/public/javatests/BUILD.gn b/weblayer/public/javatests/BUILD.gn
index c1b1bde..184207d1 100644
--- a/weblayer/public/javatests/BUILD.gn
+++ b/weblayer/public/javatests/BUILD.gn
@@ -13,7 +13,7 @@
   ]
   deps = [
     "//base:base_java_test_support",
-    "//third_party/android_support_test_runner:runner_java",
+    "//third_party/androidx:androidx_test_monitor_java",
     "//third_party/androidx:androidx_test_runner_java",
     "//third_party/junit:junit",
     "//weblayer/public/java",
diff --git a/weblayer/public/javatests/org/chromium/weblayer/WebViewCompatibilityHelperTest.java b/weblayer/public/javatests/org/chromium/weblayer/WebViewCompatibilityHelperTest.java
index 963fde3..f7186ec09 100644
--- a/weblayer/public/javatests/org/chromium/weblayer/WebViewCompatibilityHelperTest.java
+++ b/weblayer/public/javatests/org/chromium/weblayer/WebViewCompatibilityHelperTest.java
@@ -5,8 +5,8 @@
 package org.chromium.weblayer;
 
 import android.content.Context;
-import android.support.test.InstrumentationRegistry;
 
+import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 
 import org.junit.Assert;