diff --git a/DEPS b/DEPS
index 8eb2a6bd..d3ba6421 100644
--- a/DEPS
+++ b/DEPS
@@ -206,11 +206,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': 'fd74ac6498c418497e971a4441ba6b94c1a8bb4a',
+  'skia_revision': '7db7139f40321161d3db4f2c4c7b478855d1e237',
   # 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': 'f3b5599f3d1002a512135925a9b7c6f86f509803',
+  'v8_revision': 'b0723ad5a4612732df247bf98099f4e61fc32681',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -218,11 +218,11 @@
   # 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': 'b4fb7cc9ec8f4c1751f84dcf5098b1ec5212a9ec',
+  'angle_revision': '119d867c232d0abd85c9b7e37bced413466653fa',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
-  'swiftshader_revision': '04bd4d8e4ece08cc4efbb4754665f46ec42eff79',
+  'swiftshader_revision': 'a23231ea7d9174adf545b803a57835e1a2ff8ed0',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
@@ -277,7 +277,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': 'd8da6adce186a280ce4f69772793b6cf02b50c4b',
+  'devtools_frontend_revision': 'ae3dc89611ac4e73e790716441a96d1ccba29238',
   # 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.
@@ -329,11 +329,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': 'b1938273e4050931cadf697a946a0bcb49821ce1',
+  'dawn_revision': '2a8ada79514632d2b9a60bd570cc3fcbf02b18e8',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'quiche_revision': '7df418b6dc0ad8fe543e00e2c34c274b70bf771c',
+  'quiche_revision': 'ed30357616a74eb59b1f99d7bfad9a28f699a6b3',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ios_webkit
   # and whatever else without interference from each other.
@@ -1497,7 +1497,7 @@
   },
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '711de82cd1ce75d9e7c0a1230fa080a5eaf6b07d',
+    Var('webrtc_git') + '/src.git' + '@' + 'a667f878b15c30cb18d39716b676fc151d809d5a',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1569,7 +1569,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@985f6835c9007b21de4931b40f701b017d38bc1d',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@71478928d7c28b0b60d6b0892ed64b1c82039a12',
     'condition': 'checkout_src_internal',
   },
 
@@ -1577,7 +1577,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/help_app/app',
-        'version': '92sajF7l5YU_FHOF0S5XbbEL1anK6DQMIxOzfUm2nZQC',
+        'version': 'rrmrHRTSDAizvCLGZJ2cK4G6dB81VsAGljn1FJVQwlAC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -1588,7 +1588,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/media_app/app',
-        'version': 'tzLm8HyLE4vYMwHlQ9sxWnDlV3EFRrx_A_t8MIGN9YcC',
+        'version': '9Q3G72W_FP-DamN_GtRTUZe7jm4X8tsVnaUJ3Gp3fuMC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
diff --git a/android_webview/browser/gfx/surfaces_instance.cc b/android_webview/browser/gfx/surfaces_instance.cc
index 5d495a0..935bd47 100644
--- a/android_webview/browser/gfx/surfaces_instance.cc
+++ b/android_webview/browser/gfx/surfaces_instance.cc
@@ -235,7 +235,7 @@
                       gfx::Transform());
   viz::SharedQuadState* quad_state =
       render_pass->CreateAndAppendSharedQuadState();
-  quad_state->SetAll(gfx::Transform(), rect, rect, gfx::RRectF(), rect,
+  quad_state->SetAll(gfx::Transform(), rect, rect, gfx::MaskFilterInfo(), rect,
                      is_clipped, are_contents_opaque, 1.f,
                      SkBlendMode::kSrcOver, 0);
   viz::SolidColorDrawQuad* solid_quad =
diff --git a/android_webview/browser/metrics/aw_metrics_service_client.cc b/android_webview/browser/metrics/aw_metrics_service_client.cc
index 63a8a07..d16ef6a 100644
--- a/android_webview/browser/metrics/aw_metrics_service_client.cc
+++ b/android_webview/browser/metrics/aw_metrics_service_client.cc
@@ -9,6 +9,7 @@
 
 #include "android_webview/browser/metrics/aw_stability_metrics_provider.h"
 #include "android_webview/browser_jni_headers/AwMetricsServiceClient_jni.h"
+#include "base/android/callback_android.h"
 #include "base/android/jni_android.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/persistent_histogram_allocator.h"
@@ -161,4 +162,14 @@
       base::TimeDelta::FromMilliseconds(upload_interval_ms));
 }
 
+// static
+void JNI_AwMetricsServiceClient_SetOnFinalMetricsCollectedListenerForTesting(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jobject>& listener) {
+  AwMetricsServiceClient::GetInstance()
+      ->SetOnFinalMetricsCollectedListenerForTesting(base::BindRepeating(
+          base::android::RunRunnableAndroid,
+          base::android::ScopedJavaGlobalRef<jobject>(listener)));
+}
+
 }  // namespace android_webview
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 5d9da63..7249bc2 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
@@ -96,5 +96,7 @@
                     "Enables display cutout (notch) support in WebView for Android P and above."),
             Flag.commandLine(AwSwitches.WEBVIEW_FORCE_LITTLE_CORES,
                     "Forces WebView to do rendering work in little cores"),
+            Flag.baseFeature(BlinkFeatures.WEBVIEW_ACCELERATE_SMALL_CANVASES,
+                    "Accelerate all canvases in webview."),
     };
 }
diff --git a/android_webview/java/src/org/chromium/android_webview/metrics/AwMetricsServiceClient.java b/android_webview/java/src/org/chromium/android_webview/metrics/AwMetricsServiceClient.java
index 0f49c219..9c6cbb4 100644
--- a/android_webview/java/src/org/chromium/android_webview/metrics/AwMetricsServiceClient.java
+++ b/android_webview/java/src/org/chromium/android_webview/metrics/AwMetricsServiceClient.java
@@ -72,10 +72,19 @@
         AwMetricsServiceClientJni.get().setUploadIntervalForTesting(uploadIntervalMs);
     }
 
+    /**
+     * Sets a callback to run each time after final metrics have been collected.
+     */
+    @VisibleForTesting
+    public static void setOnFinalMetricsCollectedListenerForTesting(Runnable listener) {
+        AwMetricsServiceClientJni.get().setOnFinalMetricsCollectedListenerForTesting(listener);
+    }
+
     @NativeMethods
     interface Natives {
         void setHaveMetricsConsent(boolean userConsent, boolean appConsent);
         void setFastStartupForTesting(boolean fastStartupForTesting);
         void setUploadIntervalForTesting(long uploadIntervalMs);
+        void setOnFinalMetricsCollectedListenerForTesting(Runnable listener);
     }
 }
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 d730abd1f..c26d421 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
@@ -4,6 +4,10 @@
 
 package org.chromium.android_webview.test;
 
+import static org.chromium.android_webview.test.OnlyRunIn.ProcessMode.MULTI_PROCESS;
+
+import android.support.test.InstrumentationRegistry;
+
 import androidx.test.filters.MediumTest;
 
 import org.junit.Assert;
@@ -19,12 +23,14 @@
 import org.chromium.base.Callback;
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.base.test.util.CallbackHelper;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.Feature;
 import org.chromium.components.metrics.ChromeUserMetricsExtensionProtos.ChromeUserMetricsExtension;
 import org.chromium.components.metrics.MetricsSwitches;
 import org.chromium.components.metrics.SystemProfileProtos.SystemProfileProto;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
+import org.chromium.net.test.EmbeddedTestServer;
 
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.LinkedBlockingQueue;
@@ -305,4 +311,56 @@
         // onPageStarted & onPageFinished, in which case onPageFinished would *also* wake up the
         // metrics service, and we might potentially have a third metrics log in the queue.
     }
+
+    @Test
+    @MediumTest
+    @Feature({"AndroidWebView"})
+    @OnlyRunIn(MULTI_PROCESS) // This test is specific to the OOP-renderer
+    public void testRendererHistograms() throws Throwable {
+        EmbeddedTestServer embeddedTestServer = EmbeddedTestServer.createAndStartServer(
+                InstrumentationRegistry.getInstrumentation().getContext());
+        try {
+            // Discard initial log since the renderer process hasn't been created yet.
+            mPlatformServiceBridge.waitForNextMetricsLog();
+
+            final CallbackHelper helper = new CallbackHelper();
+            int finalMetricsCollectedCount = helper.getCallCount();
+
+            // Load a page and wait for final metrics collection.
+            TestThreadUtils.runOnUiThreadBlocking(() -> {
+                AwMetricsServiceClient.setOnFinalMetricsCollectedListenerForTesting(
+                        () -> { helper.notifyCalled(); });
+            });
+
+            // Load a page to ensure the renderer process is created.
+            mRule.loadUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(),
+                    embeddedTestServer.getURL("/simple_page.html"));
+            helper.waitForCallback(finalMetricsCollectedCount, 1);
+
+            // At this point we know one of two things must be true:
+            //
+            // 1. The renderer process completed startup (logging the expected histogram) before
+            //    subprocess histograms were collected. In this case, we know the desired histogram
+            //    has been copied into the browser process.
+            // 2. Subprocess histograms were collected before the renderer process completed
+            //    startup. While we don't know if our histogram was copied over, we do know the
+            //    page load has finished and this woke up the metrics service, so MetricsService
+            //    will collect subprocess metrics again.
+            //
+            // Load a page and wait for another final log collection. We know this log collection
+            // must be triggered by either the second page load start (scenario 1) or the first page
+            // load finish (scenario 2), either of which ensures the renderer startup histogram must
+            // have been copied into the browser process.
+
+            mRule.loadUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(),
+                    embeddedTestServer.getURL("/simple_page.html"));
+            helper.waitForCallback(finalMetricsCollectedCount, 2);
+
+            Assert.assertEquals(1,
+                    RecordHistogram.getHistogramTotalCountForTesting(
+                            "Android.SeccompStatus.RendererSandbox"));
+        } finally {
+            embeddedTestServer.stopAndDestroyServer();
+        }
+    }
 }
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 6f633218..7c856d9 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -946,6 +946,8 @@
     "system/gesture_education/gesture_education_notification_controller.h",
     "system/holding_space/holding_space_color_provider_impl.cc",
     "system/holding_space/holding_space_color_provider_impl.h",
+    "system/holding_space/holding_space_drag_util.cc",
+    "system/holding_space/holding_space_drag_util.h",
     "system/holding_space/holding_space_item_chip_view.cc",
     "system/holding_space/holding_space_item_chip_view.h",
     "system/holding_space/holding_space_item_chips_container.cc",
@@ -2354,6 +2356,7 @@
     "//chromeos/services/multidevice_setup/public/mojom",
     "//chromeos/services/network_config/public/mojom",
     "//chromeos/system",
+    "//chromeos/ui/frame:test_support",
     "//components/account_id",
     "//components/arc:notification_test_support",
     "//components/media_message_center",
diff --git a/ash/app_list/views/privacy_info_view.cc b/ash/app_list/views/privacy_info_view.cc
index 94bb8d0a..d23e543 100644
--- a/ash/app_list/views/privacy_info_view.cc
+++ b/ash/app_list/views/privacy_info_view.cc
@@ -161,10 +161,8 @@
 void PrivacyInfoView::SelectInitialResultAction(bool reverse_tab_order) {
   if (!reverse_tab_order) {
     selected_action_ = Action::kTextLink;
-    text_view_->NotifyAccessibilityEvent(ax::mojom::Event::kSelection, true);
   } else {
     selected_action_ = Action::kCloseButton;
-    close_button_->NotifyAccessibilityEvent(ax::mojom::Event::kSelection, true);
   }
 
   // Update visual indicators for focus.
@@ -179,12 +177,10 @@
   if (!reverse_tab_order && selected_action_ == Action::kTextLink) {
     // Move selection forward from the text view to the close button.
     selected_action_ = Action::kCloseButton;
-    close_button_->NotifyAccessibilityEvent(ax::mojom::Event::kSelection, true);
     action_changed = true;
   } else if (reverse_tab_order && selected_action_ == Action::kCloseButton) {
     // Move selection backward from the close button to the text view.
     selected_action_ = Action::kTextLink;
-    text_view_->NotifyAccessibilityEvent(ax::mojom::Event::kSelection, true);
     action_changed = true;
   } else {
     selected_action_ = Action::kNone;
@@ -196,17 +192,14 @@
   return action_changed;
 }
 
-void PrivacyInfoView::NotifyA11yResultSelected() {
+views::View* PrivacyInfoView::GetSelectedView() {
   switch (selected_action_) {
     case Action::kTextLink:
-      text_view_->NotifyAccessibilityEvent(ax::mojom::Event::kSelection, true);
-      break;
+      return text_view_;
     case Action::kCloseButton:
-      close_button_->NotifyAccessibilityEvent(ax::mojom::Event::kSelection,
-                                              true);
-      break;
+      return close_button_;
     case Action::kNone:
-      break;
+      return this;
   }
 }
 
diff --git a/ash/app_list/views/privacy_info_view.h b/ash/app_list/views/privacy_info_view.h
index b534631b..3316a1f 100644
--- a/ash/app_list/views/privacy_info_view.h
+++ b/ash/app_list/views/privacy_info_view.h
@@ -38,7 +38,7 @@
   // SearchResultBaseView:
   void SelectInitialResultAction(bool reverse_tab_order) override;
   bool SelectNextResultAction(bool reverse_tab_order) override;
-  void NotifyA11yResultSelected() override;
+  views::View* GetSelectedView() override;
 
   virtual void LinkClicked() = 0;
   virtual void CloseButtonPressed() = 0;
diff --git a/ash/app_list/views/search_box_view.cc b/ash/app_list/views/search_box_view.cc
index dafc50e8..d171fde 100644
--- a/ash/app_list/views/search_box_view.cc
+++ b/ash/app_list/views/search_box_view.cc
@@ -280,6 +280,17 @@
   }
 }
 
+void SearchBoxView::OnSearchBoxActiveChanged(bool active) {
+  if (active) {
+    search_box()->SetAccessibleName(base::string16());
+  } else {
+    search_box()->SetAccessibleName(l10n_util::GetStringUTF16(
+        is_tablet_mode_
+            ? IDS_APP_LIST_SEARCH_BOX_ACCESSIBILITY_NAME_TABLET
+            : IDS_APP_LIST_SEARCH_BOX_ACCESSIBILITY_NAME_CLAMSHELL));
+  }
+}
+
 void SearchBoxView::OnKeyEvent(ui::KeyEvent* event) {
   app_list_view_->RedirectKeyEventToSearchBox(event);
 
@@ -492,6 +503,13 @@
   ResetHighlightRange();
 }
 
+void SearchBoxView::OnBeforeUserAction(views::Textfield* sender) {
+  if (a11y_selection_on_search_result_) {
+    a11y_selection_on_search_result_ = false;
+    search_box()->NotifyAccessibilityEvent(ax::mojom::Event::kSelection, true);
+  }
+}
+
 void SearchBoxView::ContentsChanged(views::Textfield* sender,
                                     const base::string16& new_contents) {
   if (IsTrimmedQueryEmpty(current_query_) && !IsSearchBoxTrimmedQueryEmpty()) {
@@ -565,6 +583,7 @@
   contents_view_->search_results_page_view()
       ->result_selection_controller()
       ->ClearSelection();
+  a11y_selection_on_search_result_ = false;
   ClearSearch();
   SetSearchBoxActive(false, ui::ET_UNKNOWN);
 }
@@ -691,6 +710,9 @@
 
       DCHECK(close_button()->GetVisible());
       close_button()->RequestFocus();
+      close_button()->NotifyAccessibilityEvent(ax::mojom::Event::kSelection,
+                                               true);
+      a11y_selection_on_search_result_ = false;
       break;
     case ResultSelectionController::MoveResult::kResultChanged:
       UpdateSearchBoxTextForSelectedResult(
diff --git a/ash/app_list/views/search_box_view.h b/ash/app_list/views/search_box_view.h
index 7d9e76d..3a96dc3c 100644
--- a/ash/app_list/views/search_box_view.h
+++ b/ash/app_list/views/search_box_view.h
@@ -60,6 +60,7 @@
   void SetupCloseButton() override;
   void SetupBackButton() override;
   void RecordSearchBoxActivationHistogram(ui::EventType event_type) override;
+  void OnSearchBoxActiveChanged(bool active) override;
 
   // Overridden from views::View:
   void OnKeyEvent(ui::KeyEvent* event) override;
@@ -110,6 +111,10 @@
   }
   ContentsView* contents_view() { return contents_view_; }
 
+  void set_a11y_selection_on_search_result(bool value) {
+    a11y_selection_on_search_result_ = value;
+  }
+
   void set_highlight_range_for_test(const gfx::Range& range) {
     highlight_range_ = range;
   }
@@ -133,6 +138,7 @@
   void SetAutocompleteText(const base::string16& autocomplete_text);
 
   // Overridden from views::TextfieldController:
+  void OnBeforeUserAction(views::Textfield* sender) override;
   void ContentsChanged(views::Textfield* sender,
                        const base::string16& new_contents) override;
   bool HandleKeyEvent(views::Textfield* sender,
@@ -175,10 +181,13 @@
   // True if app list search autocomplete is enabled.
   const bool is_app_list_search_autocomplete_enabled_;
 
-
   // Whether tablet mode is active.
   bool is_tablet_mode_ = false;
 
+  // Set by SearchResultPageView when the accessibility selection moves to a
+  // search result view.
+  bool a11y_selection_on_search_result_ = false;
+
   base::WeakPtrFactory<SearchBoxView> weak_ptr_factory_{this};
 
   DISALLOW_COPY_AND_ASSIGN(SearchBoxView);
diff --git a/ash/app_list/views/search_result_actions_view.cc b/ash/app_list/views/search_result_actions_view.cc
index 6ea5f74..a6f6232a 100644
--- a/ash/app_list/views/search_result_actions_view.cc
+++ b/ash/app_list/views/search_result_actions_view.cc
@@ -254,16 +254,16 @@
   return true;
 }
 
-void SearchResultActionsView::NotifyA11yResultSelected() {
+views::View* SearchResultActionsView::GetSelectedView() {
   DCHECK(HasSelectedAction());
 
   int selected_action = GetSelectedAction();
   for (views::View* child : children()) {
-    if (static_cast<views::Button*>(child)->tag() == selected_action) {
-      child->NotifyAccessibilityEvent(ax::mojom::Event::kSelection, true);
-      return;
-    }
+    if (static_cast<views::Button*>(child)->tag() == selected_action)
+      return child;
   }
+
+  return nullptr;
 }
 
 void SearchResultActionsView::ClearSelectedAction() {
diff --git a/ash/app_list/views/search_result_actions_view.h b/ash/app_list/views/search_result_actions_view.h
index 05d4e8a9..521f8b74 100644
--- a/ash/app_list/views/search_result_actions_view.h
+++ b/ash/app_list/views/search_result_actions_view.h
@@ -56,8 +56,8 @@
   // getting cleared).
   bool SelectNextAction(bool reverse_tab_order);
 
-  // Sends kSelection a11y notification for the selected action button.
-  void NotifyA11yResultSelected();
+  // Returns the selected action button.
+  views::View* GetSelectedView();
 
   // Clears selected action state.
   void ClearSelectedAction();
diff --git a/ash/app_list/views/search_result_base_view.cc b/ash/app_list/views/search_result_base_view.cc
index 905c5b8..af790ab 100644
--- a/ash/app_list/views/search_result_base_view.cc
+++ b/ash/app_list/views/search_result_base_view.cc
@@ -62,12 +62,10 @@
   return true;
 }
 
-void SearchResultBaseView::NotifyA11yResultSelected() {
-  if (actions_view_ && actions_view_->HasSelectedAction()) {
-    actions_view_->NotifyA11yResultSelected();
-    return;
-  }
-  NotifyAccessibilityEvent(ax::mojom::Event::kSelection, true);
+views::View* SearchResultBaseView::GetSelectedView() {
+  if (actions_view_ && actions_view_->HasSelectedAction())
+    return actions_view_->GetSelectedView();
+  return this;
 }
 
 void SearchResultBaseView::SetResult(SearchResult* result) {
@@ -112,10 +110,8 @@
 }
 
 void SearchResultBaseView::SelectInitialResultAction(bool reverse_tab_order) {
-  if (actions_view_ && actions_view_->SelectInitialAction(reverse_tab_order))
-    return;
-
-  NotifyAccessibilityEvent(ax::mojom::Event::kSelection, true);
+  if (actions_view_)
+    actions_view_->SelectInitialAction(reverse_tab_order);
 }
 
 void SearchResultBaseView::ClearSelectedResultAction() {
diff --git a/ash/app_list/views/search_result_base_view.h b/ash/app_list/views/search_result_base_view.h
index c2e106f..eb31696 100644
--- a/ash/app_list/views/search_result_base_view.h
+++ b/ash/app_list/views/search_result_base_view.h
@@ -45,11 +45,10 @@
   // Returns whether the selected result action was changed.
   virtual bool SelectNextResultAction(bool reverse_tab_order);
 
-  // If the search result is currently selected, sends the appropriate
-  // kSelection view accessibility event. For example, if a result action is
-  // selected, the notification will be sent for the selected action button
-  // view.
-  virtual void NotifyA11yResultSelected();
+  // Returns the view that is currently selected - for example, if the result
+  // supports action views and an action view is currently selected, this
+  // should return the action view, otherwise it should return `this`.
+  virtual views::View* GetSelectedView();
 
   SearchResult* result() const { return result_; }
   void SetResult(SearchResult* result);
diff --git a/ash/app_list/views/search_result_list_view.cc b/ash/app_list/views/search_result_list_view.cc
index 9517006..c53f29a 100644
--- a/ash/app_list/views/search_result_list_view.cc
+++ b/ash/app_list/views/search_result_list_view.cc
@@ -249,9 +249,6 @@
       view_delegate_->InvokeSearchResultAction(view->result()->id(),
                                                action_index, event_flags);
     } else if (action == OmniBoxZeroStateAction::kAppendSuggestion) {
-      // Make sure ChromeVox will focus on the search box.
-      main_view_->search_box_view()->search_box()->NotifyAccessibilityEvent(
-          ax::mojom::Event::kSelection, true);
       main_view_->search_box_view()->UpdateQuery(view->result()->title());
     }
   }
diff --git a/ash/app_list/views/search_result_page_view.cc b/ash/app_list/views/search_result_page_view.cc
index ce9f125..2b7cb08 100644
--- a/ash/app_list/views/search_result_page_view.cc
+++ b/ash/app_list/views/search_result_page_view.cc
@@ -75,7 +75,7 @@
 // The amount of time by which notifications to accessibility framework about
 // result page changes are delayed.
 constexpr base::TimeDelta kNotifyA11yDelay =
-    base::TimeDelta::FromMilliseconds(500);
+    base::TimeDelta::FromMilliseconds(1500);
 
 // A container view that ensures the card background and the shadow are painted
 // in the correct order.
@@ -341,6 +341,7 @@
   ignore_result_changes_for_a11y_ = ignore;
 
   GetViewAccessibility().OverrideIsLeaf(ignore);
+  GetViewAccessibility().OverrideIsIgnored(ignore);
   NotifyAccessibilityEvent(ax::mojom::Event::kTreeChanged, true);
 }
 
@@ -370,8 +371,20 @@
     return;
   }
 
+  SearchBoxView* search_box = AppListPage::contents_view()->GetSearchBoxView();
+  // Ignore result selection change if the focus moved away from the search boc
+  // textfield, for example to the close button.
+  if (!search_box->search_box()->HasFocus())
+    return;
+
+  views::View* selected_view =
+      result_selection_controller_->selected_result()->GetSelectedView();
+  if (!selected_view)
+    return;
+
+  selected_view->NotifyAccessibilityEvent(ax::mojom::Event::kSelection, true);
   NotifyAccessibilityEvent(ax::mojom::Event::kSelectedChildrenChanged, true);
-  result_selection_controller_->selected_result()->NotifyA11yResultSelected();
+  search_box->set_a11y_selection_on_search_result(true);
 }
 
 void SearchResultPageView::OnSearchResultContainerResultsChanging() {
diff --git a/ash/app_list/views/search_result_view.cc b/ash/app_list/views/search_result_view.cc
index 7d1f99f..98fe943f 100644
--- a/ash/app_list/views/search_result_view.cc
+++ b/ash/app_list/views/search_result_view.cc
@@ -341,7 +341,6 @@
       if (actions_view()->IsValidActionIndex(
               OmniBoxZeroStateAction::kRemoveSuggestion)) {
         ScrollRectToVisible(GetLocalBounds());
-        NotifyAccessibilityEvent(ax::mojom::Event::kSelection, true);
         SetSelected(true, base::nullopt);
         confirm_remove_by_long_press_ = true;
         OnSearchResultActionActivated(OmniBoxZeroStateAction::kRemoveSuggestion,
diff --git a/ash/capture_mode/capture_label_view.cc b/ash/capture_mode/capture_label_view.cc
index 1b4bc356..4e4b3964 100644
--- a/ash/capture_mode/capture_label_view.cc
+++ b/ash/capture_mode/capture_label_view.cc
@@ -149,7 +149,7 @@
 }
 
 bool CaptureLabelView::ShouldHandleEvent() {
-  return label_button_->GetVisible();
+  return label_button_->GetVisible() && !IsInCountDownAnimation();
 }
 
 void CaptureLabelView::StartCountDown(
@@ -166,6 +166,10 @@
                           &CaptureLabelView::CountDown);
 }
 
+bool CaptureLabelView::IsInCountDownAnimation() const {
+  return !!countdown_finished_callback_;
+}
+
 void CaptureLabelView::Layout() {
   label_button_->SetBoundsRect(GetLocalBounds());
 
diff --git a/ash/capture_mode/capture_label_view.h b/ash/capture_mode/capture_label_view.h
index 9898ab4..5e1e3f0b 100644
--- a/ash/capture_mode/capture_label_view.h
+++ b/ash/capture_mode/capture_label_view.h
@@ -46,6 +46,9 @@
   // Called when starting 3-seconds count down before recording video.
   void StartCountDown(base::OnceClosure countdown_finished_callback);
 
+  // Returns true if count down animation is in progress.
+  bool IsInCountDownAnimation() const;
+
   // views::View:
   void Layout() override;
   gfx::Size CalculatePreferredSize() const override;
diff --git a/ash/capture_mode/capture_mode_session.cc b/ash/capture_mode/capture_mode_session.cc
index 8f08ed0..712230a 100644
--- a/ash/capture_mode/capture_mode_session.cc
+++ b/ash/capture_mode/capture_mode_session.cc
@@ -25,6 +25,7 @@
 #include "ui/compositor/layer.h"
 #include "ui/compositor/layer_type.h"
 #include "ui/compositor/paint_recorder.h"
+#include "ui/compositor/scoped_layer_animation_settings.h"
 #include "ui/display/screen.h"
 #include "ui/display/types/display_constants.h"
 #include "ui/events/types/event_type.h"
@@ -37,6 +38,7 @@
 #include "ui/gfx/scoped_canvas.h"
 #include "ui/gfx/shadow_value.h"
 #include "ui/gfx/skia_paint_util.h"
+#include "ui/gfx/transform_util.h"
 #include "ui/views/background.h"
 #include "ui/views/controls/button/label_button.h"
 #include "ui/views/controls/label.h"
@@ -99,6 +101,23 @@
 // padding, it will be placed below or above the capture region.
 constexpr int kCaptureRegionMinimumPaddingDp = 16;
 
+// Animation parameters needed when countdown starts.
+// The animation duration that the label fades out and scales down before count
+// down starts.
+constexpr base::TimeDelta kCaptureLabelAnimationDuration =
+    base::TimeDelta::FromMilliseconds(267);
+// The animation duration that the capture bar fades out before count down
+// starts.
+constexpr base::TimeDelta kCaptureBarFadeOutDuration =
+    base::TimeDelta::FromMilliseconds(167);
+// The animation duration that the fullscreen shield fades out before count down
+// starts.
+constexpr base::TimeDelta kCaptureShieldFadeOutDuration =
+    base::TimeDelta::FromMilliseconds(333);
+// If there is no text message was showing when count down starts, the label
+// widget will shrink down from 120% -> 100% and fade in.
+constexpr float kLabelScaleUpOnCountdown = 1.2;
+
 // Mouse cursor warping is disabled when the capture source is a custom region.
 // Sets the mouse warp status to |enable| and return the original value.
 bool SetMouseWarpEnabled(bool enable) {
@@ -236,7 +255,27 @@
   CaptureLabelView* label_view =
       static_cast<CaptureLabelView*>(capture_label_widget_->GetContentsView());
   label_view->StartCountDown(std::move(countdown_finished_callback));
-  UpdateCaptureLabelWidgetBounds();
+  UpdateCaptureLabelWidgetBounds(/*animate=*/true);
+
+  // Fade out toolbar.
+  ui::Layer* toolbar_layer = capture_mode_bar_widget_.GetLayer();
+  ui::ScopedLayerAnimationSettings toolbar_settings(
+      toolbar_layer->GetAnimator());
+  toolbar_settings.SetTransitionDuration(kCaptureBarFadeOutDuration);
+  toolbar_settings.SetTweenType(gfx::Tween::FAST_OUT_SLOW_IN);
+  toolbar_settings.SetPreemptionStrategy(
+      ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
+  toolbar_layer->SetOpacity(0.f);
+
+  // Fade out the shield if it's recording fullscreen.
+  if (controller_->source() == CaptureModeSource::kFullscreen) {
+    ui::ScopedLayerAnimationSettings shield_settings(layer()->GetAnimator());
+    shield_settings.SetTransitionDuration(kCaptureShieldFadeOutDuration);
+    shield_settings.SetTweenType(gfx::Tween::FAST_OUT_SLOW_IN);
+    shield_settings.SetPreemptionStrategy(
+        ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
+    layer()->SetOpacity(0.f);
+  }
 }
 
 void CaptureModeSession::OnPaintLayer(const ui::PaintContext& context) {
@@ -762,12 +801,63 @@
   CaptureLabelView* label_view =
       static_cast<CaptureLabelView*>(capture_label_widget_->GetContentsView());
   label_view->UpdateIconAndText();
-  UpdateCaptureLabelWidgetBounds();
+  UpdateCaptureLabelWidgetBounds(/*animate=*/false);
 }
 
-void CaptureModeSession::UpdateCaptureLabelWidgetBounds() {
+void CaptureModeSession::UpdateCaptureLabelWidgetBounds(bool animate) {
   DCHECK(capture_label_widget_);
 
+  const gfx::Rect bounds = CalculateCaptureLabelWidgetBounds();
+  const gfx::Rect old_bounds =
+      capture_label_widget_->GetNativeWindow()->GetBoundsInScreen();
+  if (old_bounds == bounds)
+    return;
+
+  if (!animate) {
+    capture_label_widget_->SetBounds(bounds);
+    return;
+  }
+
+  ui::Layer* layer = capture_label_widget_->GetLayer();
+  if (!old_bounds.IsEmpty()) {
+    // This happens if there is a label or a label button showing when count
+    // down starts. In this case we'll do a bounds change animation.
+    ui::ScopedLayerAnimationSettings settings(layer->GetAnimator());
+    settings.SetTweenType(gfx::Tween::LINEAR_OUT_SLOW_IN);
+    settings.SetPreemptionStrategy(
+        ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
+    settings.SetTransitionDuration(kCaptureLabelAnimationDuration);
+    capture_label_widget_->SetBounds(bounds);
+  } else {
+    // This happens when no text message was showing when count down starts, in
+    // this case we'll do a fade in + shrinking down animation.
+    capture_label_widget_->SetBounds(bounds);
+    const gfx::Point center_point = bounds.CenterPoint();
+    layer->SetTransform(
+        gfx::GetScaleTransform(gfx::Point(center_point.x() - bounds.x(),
+                                          center_point.y() - bounds.y()),
+                               kLabelScaleUpOnCountdown));
+    layer->SetOpacity(0.f);
+
+    // Fade in.
+    ui::ScopedLayerAnimationSettings settings(layer->GetAnimator());
+    settings.SetTransitionDuration(kCaptureLabelAnimationDuration);
+    settings.SetTweenType(gfx::Tween::LINEAR);
+    settings.SetPreemptionStrategy(
+        ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
+    layer->SetOpacity(1.f);
+
+    // Scale down from 120% -> 100%.
+    settings.SetTweenType(gfx::Tween::LINEAR_OUT_SLOW_IN);
+    layer->SetTransform(gfx::Transform());
+  }
+}
+
+gfx::Rect CaptureModeSession::CalculateCaptureLabelWidgetBounds() {
+  DCHECK(capture_label_widget_);
+  CaptureLabelView* label_view =
+      static_cast<CaptureLabelView*>(capture_label_widget_->GetContentsView());
+
   // For fullscreen and window capture mode, the capture label is placed in the
   // middle of the screen. For region capture mode, if it's in select phase, the
   // capture label is also placed in the middle of the screen, and if it's in
@@ -776,49 +866,57 @@
   // below the capture region.
   gfx::Rect bounds(current_root_->bounds());
   const gfx::Rect capture_region = controller_->user_capture_region();
-  const gfx::Size preferred_size =
-      capture_label_widget_->GetContentsView()->GetPreferredSize();
+  const gfx::Size preferred_size = label_view->GetPreferredSize();
   if (controller_->source() == CaptureModeSource::kRegion &&
       !is_selecting_region_ && !capture_region.IsEmpty()) {
-    bounds = capture_region;
-
-    // The capture region must be at least the size of |preferred_size| plus
-    // some padding for the capture label to be centered inside it.
-    gfx::Size capture_region_min_size = preferred_size;
-    capture_region_min_size.Enlarge(kCaptureRegionMinimumPaddingDp,
-                                    kCaptureRegionMinimumPaddingDp);
-    if (bounds.width() > capture_region_min_size.width() &&
-        bounds.height() > capture_region_min_size.height()) {
+    if (label_view->IsInCountDownAnimation()) {
+      // If countdown starts, calculate the bounds based on the old capture
+      // label's position, otherwise, since the countdown label bounds is
+      // smaller than the label bounds and may fit into the capture region even
+      // if the old capture label doesn't fit thus was place outside of the
+      // capture region, it's possible that we see the countdown label animates
+      // to inside of the capture region from outside of the capture region.
+      bounds = capture_label_widget_->GetNativeWindow()->bounds();
       bounds.ClampToCenteredSize(preferred_size);
     } else {
-      // The capture region is too small for the capture label to be inside it.
-      // Align |bounds| so that its horizontal centerpoint aligns with the
-      // capture regions centerpoint.
-      bounds.set_size(preferred_size);
-      bounds.set_x(capture_region.CenterPoint().x() -
-                   preferred_size.width() / 2);
-
-      // Try to put the capture label slightly below the capture region. If it
-      // does not fully fit in the root window bounds, place the capture label
-      // slightly above.
-      const int under_region_label_y =
-          capture_region.bottom() + kCaptureButtonDistanceFromRegionDp;
-      if (under_region_label_y + preferred_size.height() <
-          current_root_->bounds().bottom()) {
-        bounds.set_y(under_region_label_y);
+      bounds = capture_region;
+      // The capture region must be at least the size of |preferred_size| plus
+      // some padding for the capture label to be centered inside it.
+      gfx::Size capture_region_min_size = preferred_size;
+      capture_region_min_size.Enlarge(kCaptureRegionMinimumPaddingDp,
+                                      kCaptureRegionMinimumPaddingDp);
+      if (bounds.width() > capture_region_min_size.width() &&
+          bounds.height() > capture_region_min_size.height()) {
+        bounds.ClampToCenteredSize(preferred_size);
       } else {
-        bounds.set_y(capture_region.y() - kCaptureButtonDistanceFromRegionDp -
-                     preferred_size.height());
+        // The capture region is too small for the capture label to be inside
+        // it. Align |bounds| so that its horizontal centerpoint aligns with the
+        // capture regions centerpoint.
+        bounds.set_size(preferred_size);
+        bounds.set_x(capture_region.CenterPoint().x() -
+                     preferred_size.width() / 2);
+
+        // Try to put the capture label slightly below the capture region. If it
+        // does not fully fit in the root window bounds, place the capture label
+        // slightly above.
+        const int under_region_label_y =
+            capture_region.bottom() + kCaptureButtonDistanceFromRegionDp;
+        if (under_region_label_y + preferred_size.height() <
+            current_root_->bounds().bottom()) {
+          bounds.set_y(under_region_label_y);
+        } else {
+          bounds.set_y(capture_region.y() - kCaptureButtonDistanceFromRegionDp -
+                       preferred_size.height());
+        }
       }
     }
   } else {
     bounds.ClampToCenteredSize(preferred_size);
   }
-
   // User capture region bounds are in root window coordinates so convert them
   // here.
   wm::ConvertRectToScreen(current_root_, &bounds);
-  capture_label_widget_->SetBounds(bounds);
+  return bounds;
 }
 
 bool CaptureModeSession::ShouldCaptureLabelHandleEvent(
@@ -860,7 +958,7 @@
   // bounds, moving it onto the correct display, but will early return if the
   // region is already empty.
   if (controller_->user_capture_region().IsEmpty())
-    UpdateCaptureLabelWidgetBounds();
+    UpdateCaptureLabelWidgetBounds(/*animate=*/false);
 
   // Start with a new region when we switch displays.
   is_selecting_region_ = true;
diff --git a/ash/capture_mode/capture_mode_session.h b/ash/capture_mode/capture_mode_session.h
index 51884b7..38cc6db 100644
--- a/ash/capture_mode/capture_mode_session.h
+++ b/ash/capture_mode/capture_mode_session.h
@@ -159,9 +159,13 @@
   // anchor points if |position| is an edge.
   std::vector<gfx::Point> GetAnchorPointsForPosition(FineTunePosition position);
 
-  // Updates the capture label widget.
+  // Updates the capture label widget's icon/text and bounds.
   void UpdateCaptureLabelWidget();
-  void UpdateCaptureLabelWidgetBounds();
+  // Updates the capture label widget's bounds. If |animate| is true, do bounds
+  // animation.
+  void UpdateCaptureLabelWidgetBounds(bool animate);
+  // Calculates the targeted capture label widget bounds in screen coordinates.
+  gfx::Rect CalculateCaptureLabelWidgetBounds();
 
   // Returns true if the capture label should handle the event. |event_target|
   // is the window which is receiving the event. The capture label should handle
diff --git a/ash/fast_ink/fast_ink_host.cc b/ash/fast_ink/fast_ink_host.cc
index 93e2eba0..ba041e1 100644
--- a/ash/fast_ink/fast_ink_host.cc
+++ b/ash/fast_ink/fast_ink_host.cc
@@ -408,7 +408,7 @@
       buffer_to_target_transform,
       /*quad_layer_rect=*/output_rect,
       /*visible_quad_layer_rect=*/output_rect,
-      /*rounded_corner_bounds=*/gfx::RRectF(),
+      /*mask_filter_info=*/gfx::MaskFilterInfo(),
       /*clip_rect=*/gfx::Rect(),
       /*is_clipped=*/false, /*are_contents_opaque=*/false, /*opacity=*/1.f,
       /*blend_mode=*/SkBlendMode::kSrcOver, /*sorting_context_id=*/0);
diff --git a/ash/fast_ink/view_tree_host_root_view.cc b/ash/fast_ink/view_tree_host_root_view.cc
index dd05502..9bb359c8 100644
--- a/ash/fast_ink/view_tree_host_root_view.cc
+++ b/ash/fast_ink/view_tree_host_root_view.cc
@@ -452,7 +452,7 @@
       buffer_to_target_transform,
       /*quad_layer_rect=*/output_rect,
       /*visible_quad_layer_rect=*/output_rect,
-      /*rounded_corner_bounds=*/gfx::RRectF(),
+      /*mask_filter_info=*/gfx::MaskFilterInfo(),
       /*clip_rect=*/gfx::Rect(),
       /*is_clipped=*/false, /*are_contents_opaque=*/false, /*opacity=*/1.f,
       /*blend_mode=*/SkBlendMode::kSrcOver, /*sorting_context_id=*/0);
diff --git a/ash/frame/header_view.h b/ash/frame/header_view.h
index b15321f..1ac31d4 100644
--- a/ash/frame/header_view.h
+++ b/ash/frame/header_view.h
@@ -9,11 +9,11 @@
 
 #include "ash/ash_export.h"
 #include "ash/public/cpp/frame_header.h"
-#include "ash/public/cpp/immersive/immersive_fullscreen_controller_delegate.h"
 #include "ash/public/cpp/tablet_mode_observer.h"
 #include "base/callback.h"
 #include "base/macros.h"
 #include "base/scoped_observer.h"
+#include "chromeos/ui/frame/immersive/immersive_fullscreen_controller_delegate.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_observer.h"
@@ -42,10 +42,11 @@
 
 // View which paints the frame header (title, caption buttons...). It slides off
 // and on screen in immersive fullscreen.
-class ASH_EXPORT HeaderView : public views::View,
-                              public ImmersiveFullscreenControllerDelegate,
-                              public TabletModeObserver,
-                              public aura::WindowObserver {
+class ASH_EXPORT HeaderView
+    : public views::View,
+      public chromeos::ImmersiveFullscreenControllerDelegate,
+      public TabletModeObserver,
+      public aura::WindowObserver {
  public:
   // |target_widget| is the widget that the caption buttons act on.
   // |target_widget| is not necessarily the same as the widget the header is
diff --git a/ash/frame/non_client_frame_view_ash.cc b/ash/frame/non_client_frame_view_ash.cc
index 553fc8f..e4608d9 100644
--- a/ash/frame/non_client_frame_view_ash.cc
+++ b/ash/frame/non_client_frame_view_ash.cc
@@ -12,7 +12,6 @@
 #include "ash/public/cpp/ash_constants.h"
 #include "ash/public/cpp/default_frame_header.h"
 #include "ash/public/cpp/frame_utils.h"
-#include "ash/public/cpp/immersive/immersive_fullscreen_controller.h"
 #include "ash/public/cpp/tablet_mode_observer.h"
 #include "ash/public/cpp/window_properties.h"
 #include "ash/shell.h"
@@ -23,7 +22,9 @@
 #include "ash/wm/window_state_observer.h"
 #include "ash/wm/window_util.h"
 #include "base/bind.h"
+#include "chromeos/ui/base/window_properties.h"
 #include "chromeos/ui/frame/caption_buttons/frame_caption_button_container_view.h"
+#include "chromeos/ui/frame/immersive/immersive_fullscreen_controller.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_observer.h"
@@ -39,6 +40,8 @@
 
 namespace ash {
 
+using ::chromeos::ImmersiveFullscreenController;
+using ::chromeos::kImmersiveImpliedByFullscreen;
 using ::chromeos::WindowStateType;
 
 DEFINE_UI_CLASS_PROPERTY_KEY(NonClientFrameViewAsh*,
diff --git a/ash/frame/non_client_frame_view_ash.h b/ash/frame/non_client_frame_view_ash.h
index 4f549f5..9275144 100644
--- a/ash/frame/non_client_frame_view_ash.h
+++ b/ash/frame/non_client_frame_view_ash.h
@@ -19,6 +19,7 @@
 
 namespace chromeos {
 class FrameCaptionButtonContainerView;
+class ImmersiveFullscreenController;
 }
 
 namespace views {
@@ -27,7 +28,6 @@
 
 namespace ash {
 
-class ImmersiveFullscreenController;
 class NonClientFrameViewAshImmersiveHelper;
 
 // A NonClientFrameView used for packaged apps, dialogs and other non-browser
@@ -59,7 +59,7 @@
   // NonClientFrameViewAsh does not take ownership of
   // |immersive_fullscreen_controller|.
   void InitImmersiveFullscreenControllerForView(
-      ImmersiveFullscreenController* immersive_fullscreen_controller);
+      chromeos::ImmersiveFullscreenController* immersive_fullscreen_controller);
 
   // Sets the active and inactive frame colors. Note the inactive frame color
   // will have some transparency added when the frame is drawn.
diff --git a/ash/frame/non_client_frame_view_ash_unittest.cc b/ash/frame/non_client_frame_view_ash_unittest.cc
index d529f590..31b59f9d 100644
--- a/ash/frame/non_client_frame_view_ash_unittest.cc
+++ b/ash/frame/non_client_frame_view_ash_unittest.cc
@@ -11,8 +11,6 @@
 #include "ash/frame/wide_frame_view.h"
 #include "ash/public/cpp/ash_switches.h"
 #include "ash/public/cpp/default_frame_header.h"
-#include "ash/public/cpp/immersive/immersive_fullscreen_controller.h"
-#include "ash/public/cpp/immersive/immersive_fullscreen_controller_test_api.h"
 #include "ash/public/cpp/window_properties.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/shell.h"
@@ -27,6 +25,8 @@
 #include "base/command_line.h"
 #include "base/containers/flat_set.h"
 #include "chromeos/ui/frame/caption_buttons/frame_caption_button_container_view.h"
+#include "chromeos/ui/frame/immersive/immersive_fullscreen_controller.h"
+#include "chromeos/ui/frame/immersive/immersive_fullscreen_controller_test_api.h"
 #include "chromeos/ui/vector_icons/vector_icons.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/window.h"
@@ -50,6 +50,9 @@
 namespace ash {
 
 using ::chromeos::FrameCaptionButtonContainerView;
+using ::chromeos::ImmersiveFullscreenController;
+using ::chromeos::ImmersiveFullscreenControllerDelegate;
+using ::chromeos::ImmersiveFullscreenControllerTestApi;
 
 // A views::WidgetDelegate which uses a NonClientFrameViewAsh.
 class NonClientFrameViewAshTestWidgetDelegate
diff --git a/ash/frame/wide_frame_view.cc b/ash/frame/wide_frame_view.cc
index 9399c90..503b3ae 100644
--- a/ash/frame/wide_frame_view.cc
+++ b/ash/frame/wide_frame_view.cc
@@ -7,7 +7,6 @@
 #include "ash/frame/header_view.h"
 #include "ash/frame/non_client_frame_view_ash.h"
 #include "ash/public/cpp/default_frame_header.h"
-#include "ash/public/cpp/immersive/immersive_fullscreen_controller.h"
 #include "ash/public/cpp/window_properties.h"
 #include "ash/shell.h"
 #include "ash/wm/overview/overview_controller.h"
@@ -15,6 +14,7 @@
 #include "ash/wm/wm_event.h"
 #include "base/metrics/user_metrics.h"
 #include "chromeos/ui/frame/caption_buttons/frame_caption_button_container_view.h"
+#include "chromeos/ui/frame/immersive/immersive_fullscreen_controller.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_targeter.h"
 #include "ui/display/display.h"
@@ -25,6 +25,8 @@
 namespace ash {
 namespace {
 
+using ::chromeos::ImmersiveFullscreenController;
+
 class WideFrameTargeter : public aura::WindowTargeter {
  public:
   explicit WideFrameTargeter(HeaderView* header_view)
diff --git a/ash/frame/wide_frame_view.h b/ash/frame/wide_frame_view.h
index a843bbc6..5657d0c 100644
--- a/ash/frame/wide_frame_view.h
+++ b/ash/frame/wide_frame_view.h
@@ -6,20 +6,23 @@
 #define ASH_FRAME_WIDE_FRAME_VIEW_H_
 
 #include "ash/ash_export.h"
-#include "ash/public/cpp/immersive/immersive_fullscreen_controller_delegate.h"
 #include "ash/wm/overview/overview_observer.h"
 #include "chromeos/ui/frame/caption_buttons/caption_button_model.h"
+#include "chromeos/ui/frame/immersive/immersive_fullscreen_controller_delegate.h"
 #include "ui/aura/window_observer.h"
 #include "ui/display/display_observer.h"
 #include "ui/views/widget/widget_delegate.h"
 
+namespace chromeos {
+class ImmersiveFullscreenController;
+}
+
 namespace views {
 class Widget;
 }
 
 namespace ash {
 class HeaderView;
-class ImmersiveFullscreenController;
 
 // WideFrameView is used for the case where the widget's maximzed/fullscreen
 // doesn't cover the entire workarea/display area but the caption frame should
@@ -31,17 +34,18 @@
 // the target widget because ImmersiveFullscreenController is not owned by
 // NonClientFrameViewAsh. Investigate if we integrate this into
 // NonClientFrameViewAsh.
-class ASH_EXPORT WideFrameView : public views::WidgetDelegateView,
-                                 public aura::WindowObserver,
-                                 public display::DisplayObserver,
-                                 public ImmersiveFullscreenControllerDelegate {
+class ASH_EXPORT WideFrameView
+    : public views::WidgetDelegateView,
+      public aura::WindowObserver,
+      public display::DisplayObserver,
+      public chromeos::ImmersiveFullscreenControllerDelegate {
  public:
   explicit WideFrameView(views::Widget* target);
   ~WideFrameView() override;
 
   // Initialize |immersive_fullscreen_controller| so that the controller reveals
   // and |hides_header_| in immersive mode.
-  void Init(ImmersiveFullscreenController* controller);
+  void Init(chromeos::ImmersiveFullscreenController* controller);
 
   // Set the caption model for caption buttions on this frame.
   void SetCaptionButtonModel(
diff --git a/ash/public/cpp/BUILD.gn b/ash/public/cpp/BUILD.gn
index fdf9769..8b23b50d 100644
--- a/ash/public/cpp/BUILD.gn
+++ b/ash/public/cpp/BUILD.gn
@@ -154,15 +154,6 @@
     "ime_controller_client.h",
     "ime_info.cc",
     "ime_info.h",
-    "immersive/immersive_context.cc",
-    "immersive/immersive_context.h",
-    "immersive/immersive_focus_watcher.cc",
-    "immersive/immersive_focus_watcher.h",
-    "immersive/immersive_fullscreen_controller.cc",
-    "immersive/immersive_fullscreen_controller.h",
-    "immersive/immersive_fullscreen_controller_delegate.h",
-    "immersive/immersive_revealed_lock.cc",
-    "immersive/immersive_revealed_lock.h",
     "in_session_auth_dialog_client.h",
     "in_session_auth_dialog_controller.cc",
     "in_session_auth_dialog_controller.h",
@@ -403,8 +394,6 @@
 source_set("test_support") {
   testonly = true
   sources = [
-    "immersive/immersive_fullscreen_controller_test_api.cc",
-    "immersive/immersive_fullscreen_controller_test_api.h",
     "test/test_ambient_client.cc",
     "test/test_ambient_client.h",
     "test/test_image_downloader.cc",
diff --git a/ash/public/cpp/window_properties.cc b/ash/public/cpp/window_properties.cc
index f1b6092d..2c3531a 100644
--- a/ash/public/cpp/window_properties.cc
+++ b/ash/public/cpp/window_properties.cc
@@ -4,12 +4,12 @@
 
 #include "ash/public/cpp/window_properties.h"
 
-#include "ash/public/cpp/immersive/immersive_fullscreen_controller.h"
 #include "ash/public/cpp/shelf_types.h"
 #include "ash/public/cpp/window_backdrop.h"
 #include "ash/public/cpp/window_pin_type.h"
 #include "chromeos/ui/base/chromeos_ui_constants.h"
 #include "chromeos/ui/base/window_state_type.h"
+#include "chromeos/ui/frame/immersive/immersive_fullscreen_controller.h"
 #include "third_party/skia/include/core/SkRegion.h"
 #include "ui/aura/window.h"
 #include "ui/wm/core/window_properties.h"
@@ -34,15 +34,6 @@
 DEFINE_UI_CLASS_PROPERTY_KEY(bool, kHideInOverviewKey, false)
 DEFINE_UI_CLASS_PROPERTY_KEY(bool, kHideInShelfKey, false)
 DEFINE_UI_CLASS_PROPERTY_KEY(bool, kHideShelfWhenFullscreenKey, true)
-DEFINE_UI_CLASS_PROPERTY_KEY(bool, kImmersiveImpliedByFullscreen, true)
-DEFINE_UI_CLASS_PROPERTY_KEY(bool, kImmersiveIsActive, false)
-DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(gfx::Rect,
-                                   kImmersiveTopContainerBoundsInScreen,
-                                   nullptr)
-DEFINE_UI_CLASS_PROPERTY_KEY(
-    int,
-    kImmersiveWindowType,
-    ImmersiveFullscreenController::WindowType::WINDOW_TYPE_OTHER)
 DEFINE_UI_CLASS_PROPERTY_KEY(bool, kIsDeferredTabDraggingTargetWindowKey, false)
 DEFINE_UI_CLASS_PROPERTY_KEY(bool, kIsDraggingTabsKey, false)
 DEFINE_UI_CLASS_PROPERTY_KEY(bool, kHideInDeskMiniViewKey, false)
diff --git a/ash/public/cpp/window_properties.h b/ash/public/cpp/window_properties.h
index 6febe80..319edcb9 100644
--- a/ash/public/cpp/window_properties.h
+++ b/ash/public/cpp/window_properties.h
@@ -85,25 +85,6 @@
 ASH_PUBLIC_EXPORT extern const aura::WindowProperty<bool>* const
     kHideShelfWhenFullscreenKey;
 
-// Whether entering fullscreen means that a window should automatically enter
-// immersive mode. This is false for some client windows, such as Chrome Apps.
-ASH_PUBLIC_EXPORT extern const aura::WindowProperty<bool>* const
-    kImmersiveImpliedByFullscreen;
-
-// Whether immersive is currently active (in ImmersiveFullscreenController
-// parlance, "enabled").
-ASH_PUBLIC_EXPORT extern const aura::WindowProperty<bool>* const
-    kImmersiveIsActive;
-
-// The bounds of the top container in screen coordinates.
-ASH_PUBLIC_EXPORT extern const aura::WindowProperty<gfx::Rect*>* const
-    kImmersiveTopContainerBoundsInScreen;
-
-// The type of window for logging immersive metrics. Type:
-// ImmersiveFullscreenController::WindowType.
-ASH_PUBLIC_EXPORT extern const aura::WindowProperty<int>* const
-    kImmersiveWindowType;
-
 // If true, the window is the target window for the tab-dragged window. The key
 // is used by overview to show a highlight indication to indicate which overview
 // window the dragged tabs will merge into when the user releases the pointer.
diff --git a/ash/search_box/search_box_view_base.cc b/ash/search_box/search_box_view_base.cc
index ac2e4fe..241f86ba 100644
--- a/ash/search_box/search_box_view_base.cc
+++ b/ash/search_box/search_box_view_base.cc
@@ -349,6 +349,7 @@
   if (event_type != ui::ET_KEY_PRESSED && event_type != ui::ET_KEY_RELEASED)
     UpdateKeyboardVisibility();
   UpdateButtonsVisisbility();
+  OnSearchBoxActiveChanged(active);
 
   NotifyActiveChanged();
 
@@ -438,6 +439,8 @@
   NotifyQueryChanged();
 }
 
+void SearchBoxViewBase::OnSearchBoxActiveChanged(bool active) {}
+
 void SearchBoxViewBase::NotifyQueryChanged() {
   DCHECK(delegate_);
   delegate_->QueryChanged(this);
diff --git a/ash/search_box/search_box_view_base.h b/ash/search_box/search_box_view_base.h
index ac963e2..ccd9302 100644
--- a/ash/search_box/search_box_view_base.h
+++ b/ash/search_box/search_box_view_base.h
@@ -118,6 +118,9 @@
 
   virtual void ClearSearch();
 
+  // Called when the search box active state changes.
+  virtual void OnSearchBoxActiveChanged(bool active);
+
  protected:
   // Fires query change notification.
   void NotifyQueryChanged();
diff --git a/ash/shelf/shelf_layout_manager_unittest.cc b/ash/shelf/shelf_layout_manager_unittest.cc
index 88faeb2d..3f6fc60 100644
--- a/ash/shelf/shelf_layout_manager_unittest.cc
+++ b/ash/shelf/shelf_layout_manager_unittest.cc
@@ -26,7 +26,6 @@
 #include "ash/public/cpp/ash_features.h"
 #include "ash/public/cpp/ash_pref_names.h"
 #include "ash/public/cpp/ash_switches.h"
-#include "ash/public/cpp/immersive/immersive_fullscreen_controller_test_api.h"
 #include "ash/public/cpp/keyboard/keyboard_controller.h"
 #include "ash/public/cpp/keyboard/keyboard_controller_observer.h"
 #include "ash/public/cpp/shelf_config.h"
diff --git a/ash/shelf/test/shelf_layout_manager_test_base.cc b/ash/shelf/test/shelf_layout_manager_test_base.cc
index 46eba01..1abbf42 100644
--- a/ash/shelf/test/shelf_layout_manager_test_base.cc
+++ b/ash/shelf/test/shelf_layout_manager_test_base.cc
@@ -16,6 +16,7 @@
 #include "ash/wm/window_state.h"
 #include "ash/wm/workspace_controller.h"
 #include "chromeos/constants/chromeos_switches.h"
+#include "chromeos/ui/base/window_properties.h"
 #include "components/prefs/pref_service.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/client/window_parenting_client.h"
@@ -27,6 +28,9 @@
 
 namespace ash {
 namespace {
+
+using ::chromeos::kImmersiveIsActive;
+
 ShelfWidget* GetShelfWidget() {
   return AshTestBase::GetPrimaryShelf()->shelf_widget();
 }
diff --git a/ash/shell.h b/ash/shell.h
index f47bc16d..32417ba 100644
--- a/ash/shell.h
+++ b/ash/shell.h
@@ -33,6 +33,7 @@
 }  // namespace aura
 
 namespace chromeos {
+class ImmersiveContext;
 class SnapController;
 }  // namespace chromeos
 
@@ -126,7 +127,6 @@
 class HoldingSpaceController;
 class HomeScreenController;
 class ImeControllerImpl;
-class ImmersiveContext;
 class InSessionAuthDialogControllerImpl;
 class KeyAccessibilityEnabler;
 class KeyboardBrightnessControlDelegate;
@@ -693,7 +693,7 @@
   std::unique_ptr<HoldingSpaceController> holding_space_controller_;
   std::unique_ptr<HomeScreenController> home_screen_controller_;
   std::unique_ptr<ImeControllerImpl> ime_controller_;
-  std::unique_ptr<ImmersiveContext> immersive_context_;
+  std::unique_ptr<chromeos::ImmersiveContext> immersive_context_;
   std::unique_ptr<InSessionAuthDialogControllerImpl>
       in_session_auth_dialog_controller_;
   std::unique_ptr<KeyboardBrightnessControlDelegate>
diff --git a/ash/system/holding_space/holding_space_drag_util.cc b/ash/system/holding_space/holding_space_drag_util.cc
new file mode 100644
index 0000000..54682ea
--- /dev/null
+++ b/ash/system/holding_space/holding_space_drag_util.cc
@@ -0,0 +1,275 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/system/holding_space/holding_space_drag_util.h"
+
+#include <memory>
+
+#include "ash/public/cpp/holding_space/holding_space_color_provider.h"
+#include "ash/public/cpp/holding_space/holding_space_image.h"
+#include "ash/public/cpp/holding_space/holding_space_item.h"
+#include "ash/public/cpp/rounded_image_view.h"
+#include "ash/system/holding_space/holding_space_item_view.h"
+#include "ash/system/tray/tray_popup_item_style.h"
+#include "base/containers/adapters.h"
+#include "ui/compositor/canvas_painter.h"
+#include "ui/gfx/canvas.h"
+#include "ui/gfx/image/image_skia.h"
+#include "ui/gfx/shadow_util.h"
+#include "ui/gfx/skia_paint_util.h"
+#include "ui/views/border.h"
+#include "ui/views/controls/label.h"
+#include "ui/views/drag_utils.h"
+#include "ui/views/layout/box_layout.h"
+#include "ui/views/layout/layout_manager_base.h"
+#include "ui/views/view.h"
+#include "ui/views/widget/widget.h"
+
+namespace ash {
+namespace holding_space_util {
+
+namespace {
+
+// Appearance.
+constexpr int kDragImageItemViewCornerRadius = 8;
+constexpr int kDragImageItemViewElevation = 2;
+constexpr int kDragImageItemChipViewIconSize = 24;
+constexpr gfx::Insets kDragImageItemChipViewInsets(0, 13);
+constexpr gfx::Size kDragImageItemChipViewPreferredSize(160, 40);
+constexpr int kDragImageItemChipViewSpacing = 13;
+constexpr int kDragImageViewChildOffset = 8;
+
+// Helpers ---------------------------------------------------------------------
+
+#if DCHECK_IS_ON()
+// Asserts that there are no `ui::Layer`s in the specified `view` hierarchy.
+void AssertNoLayers(const views::View* view) {
+  DCHECK(!view->layer());
+  for (const views::View* child : view->children())
+    AssertNoLayers(child);
+}
+#endif  // DCHECK_IS_ON()
+
+// Returns an empty border with space to accommodate the specified `shadow`.
+std::unique_ptr<views::Border> CreateEmptyBorderForShadow(
+    const gfx::ShadowDetails& shadow) {
+  return views::CreateEmptyBorder(-gfx::ShadowValue::GetMargin(shadow.values));
+}
+
+// Returns the holding space items associated with the specified `views`.
+std::vector<const HoldingSpaceItem*> GetHoldingSpaceItems(
+    const std::vector<const HoldingSpaceItemView*> views) {
+  std::vector<const HoldingSpaceItem*> items;
+  for (const HoldingSpaceItemView* view : views)
+    items.push_back(view->item());
+  return items;
+}
+
+// DragImageLayoutManager ------------------------------------------------------
+
+// A `views::LayoutManager` which lays out its children atop each other with a
+// specified `child_offset`. Note that children are painted in reverse order.
+class DragImageLayoutManager : public views::LayoutManagerBase {
+ public:
+  explicit DragImageLayoutManager(int child_offset)
+      : child_offset_(child_offset) {}
+
+  DragImageLayoutManager(const DragImageLayoutManager&) = delete;
+  DragImageLayoutManager& operator=(const DragImageLayoutManager&) = delete;
+  ~DragImageLayoutManager() override = default;
+
+ private:
+  // views::LayoutManagerBase:
+  views::ProposedLayout CalculateProposedLayout(
+      const views::SizeBounds& size_bounds) const override {
+    views::ProposedLayout proposed_layout;
+
+    int left = 0, top = 0;
+    for (views::View* child_view : host_view()->children()) {
+      const gfx::Size child_preferred_size = child_view->GetPreferredSize();
+
+      // Child layout.
+      views::ChildLayout child_layout;
+      child_layout.available_size = views::SizeBounds(child_preferred_size);
+      child_layout.bounds = gfx::Rect({left, top}, child_preferred_size);
+      child_layout.child_view = child_view;
+      child_layout.visible = true;
+      proposed_layout.child_layouts.push_back(std::move(child_layout));
+
+      // Host size.
+      if (proposed_layout.host_size.IsEmpty()) {
+        proposed_layout.host_size = child_preferred_size;
+      } else {
+        int host_width = left + child_preferred_size.width();
+        int host_height = top + child_preferred_size.height();
+        proposed_layout.host_size.SetToMax(gfx::Size(host_width, host_height));
+      }
+
+      left += child_offset_;
+      top += child_offset_;
+    }
+
+    return proposed_layout;
+  }
+
+  std::vector<views::View*> GetChildViewsInPaintOrder(
+      const views::View* host) const override {
+    // Paint `children` in reverse order so that earlier views paint at a higher
+    // z-index than later views, like a deck of cards with the first `child`
+    // stacked on top.
+    std::vector<views::View*> children;
+    for (views::View* child : base::Reversed(host->children()))
+      children.push_back(child);
+    return children;
+  }
+
+  const int child_offset_;
+};
+
+// DragImageItemView -----------------------------------------------------------
+
+// An abstract `views::View` which represents a single holding space item in the
+// drag image for a collection of holding space item views. The main purpose of
+// this view is to implement the shadow which is intentionally done without use
+// of `ui::Layer`s to accommodate painting to an `SkBitmap`.
+class DragImageItemView : public views::View {
+ public:
+  DragImageItemView(const DragImageItemView&) = delete;
+  DragImageItemView& operator=(const DragImageItemView&) = delete;
+  ~DragImageItemView() override = default;
+
+ protected:
+  DragImageItemView() {
+    // Add an empty border to accommodate the shadow so that the view's content
+    // will be laid out within the appropriate shadow margins.
+    SetBorder(CreateEmptyBorderForShadow(GetShadowDetails()));
+  }
+
+ private:
+  // views::View:
+  void OnPaintBackground(gfx::Canvas* canvas) override {
+    cc::PaintFlags flags;
+    flags.setAntiAlias(true);
+    flags.setColor(SK_ColorWHITE);
+    flags.setLooper(gfx::CreateShadowDrawLooper(GetShadowDetails().values));
+    canvas->DrawRoundRect(GetContentsBounds(), kDragImageItemViewCornerRadius,
+                          flags);
+  }
+
+  const gfx::ShadowDetails& GetShadowDetails() const {
+    return gfx::ShadowDetails::Get(kDragImageItemViewElevation,
+                                   kDragImageItemViewCornerRadius);
+  }
+};
+
+// DragImageItemChipView -------------------------------------------------------
+
+// A `DragImageItemView` which represents a single holding space `item` as a
+// chip in the drag image for a collection of holding space item views.
+class DragImageItemChipView : public DragImageItemView {
+ public:
+  explicit DragImageItemChipView(const HoldingSpaceItem* item) {
+    InitLayout(item);
+  }
+
+ private:
+  void InitLayout(const HoldingSpaceItem* item) {
+    // NOTE: We enlarge `preferred_size` to accommodate the view's shadow.
+    gfx::Size preferred_size(kDragImageItemChipViewPreferredSize);
+    preferred_size.Enlarge(GetInsets().width(), GetInsets().height());
+    SetPreferredSize(preferred_size);
+
+    // Layout.
+    views::BoxLayout* layout =
+        SetLayoutManager(std::make_unique<views::BoxLayout>(
+            views::BoxLayout::Orientation::kHorizontal,
+            kDragImageItemChipViewInsets, kDragImageItemChipViewSpacing));
+    layout->set_cross_axis_alignment(
+        views::BoxLayout::CrossAxisAlignment::kCenter);
+    layout->set_main_axis_alignment(
+        views::BoxLayout::MainAxisAlignment::kCenter);
+
+    // Icon.
+    auto* icon = AddChildView(std::make_unique<RoundedImageView>(
+        /*radius=*/kDragImageItemChipViewIconSize / 2,
+        RoundedImageView::Alignment::kCenter));
+    icon->SetPreferredSize(gfx::Size(kDragImageItemChipViewIconSize,
+                                     kDragImageItemChipViewIconSize));
+    icon->SetImage(item->image().image_skia(), icon->GetPreferredSize());
+
+    // Label.
+    auto* label = AddChildView(std::make_unique<views::Label>(item->text()));
+    label->SetElideBehavior(gfx::ElideBehavior::ELIDE_MIDDLE);
+    label->SetHorizontalAlignment(gfx::HorizontalAlignment::ALIGN_LEFT);
+    layout->SetFlexForView(label, 1);
+
+    TrayPopupItemStyle(TrayPopupItemStyle::FontStyle::DETAILED_VIEW_LABEL,
+                       /*use_unified_theme=*/false)
+        .SetupLabel(label);
+  }
+};
+
+// DragImageView ---------------------------------------------------------------
+
+// A `views::View` for use as a drag image for a collection of holding space
+// item `views`. This view expects to be painted to an `SkBitmap`.
+class DragImageView : public views::View {
+ public:
+  explicit DragImageView(const std::vector<const HoldingSpaceItem*>& items) {
+    InitLayout(items);
+  }
+
+  DragImageView(const DragImageView&) = delete;
+  DragImageView& operator=(const DragImageView&) = delete;
+  ~DragImageView() override = default;
+
+  // Paints this view to a `gfx::ImageSkia`.
+  gfx::ImageSkia PaintToImageSkia(float scale, bool is_pixel_canvas) {
+#if DCHECK_IS_ON()
+    // NOTE: This method will *not* paint `ui::Layer`s, so it is expected that
+    // all views in this view hierarchy *not* paint to layers.
+    AssertNoLayers(this);
+#endif  // DCHECK_IS_ON()
+    SkBitmap bitmap;
+    Paint(views::PaintInfo::CreateRootPaintInfo(
+        ui::CanvasPainter(&bitmap, size(), scale,
+                          /*clear_color=*/SK_ColorTRANSPARENT, is_pixel_canvas)
+            .context(),
+        size()));
+    return gfx::ImageSkia(gfx::ImageSkiaRep(bitmap, scale));
+  }
+
+ private:
+  void InitLayout(const std::vector<const HoldingSpaceItem*>& items) {
+    SetLayoutManager(
+        std::make_unique<DragImageLayoutManager>(kDragImageViewChildOffset));
+
+    // TODO(crbug.com/1139113): Support theming.
+    // TODO(crbug.com/1139113): Paint screenshot items differently.
+    // TODO(crbug.com/1139113): Limit number of items and add overflow badge.
+    for (const HoldingSpaceItem* item : items)
+      AddChildView(std::make_unique<DragImageItemChipView>(item));
+  }
+};
+
+}  // namespace
+
+// Utilities -------------------------------------------------------------------
+
+gfx::ImageSkia CreateDragImage(
+    const std::vector<const HoldingSpaceItemView*>& views) {
+  if (views.empty())
+    return gfx::ImageSkia();
+
+  const views::Widget* widget = views[0]->GetWidget();
+  const float scale = views::ScaleFactorForDragFromWidget(widget);
+  const bool is_pixel_canvas = widget->GetCompositor()->is_pixel_canvas();
+
+  DragImageView drag_image_view(GetHoldingSpaceItems(views));
+  drag_image_view.SetSize(drag_image_view.GetPreferredSize());
+  return drag_image_view.PaintToImageSkia(scale, is_pixel_canvas);
+}
+
+}  // namespace holding_space_util
+}  // namespace ash
diff --git a/ash/system/holding_space/holding_space_drag_util.h b/ash/system/holding_space/holding_space_drag_util.h
new file mode 100644
index 0000000..84fc0a33
--- /dev/null
+++ b/ash/system/holding_space/holding_space_drag_util.h
@@ -0,0 +1,31 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_SYSTEM_HOLDING_SPACE_HOLDING_SPACE_DRAG_UTIL_H_
+#define ASH_SYSTEM_HOLDING_SPACE_HOLDING_SPACE_DRAG_UTIL_H_
+
+#include <vector>
+
+namespace gfx {
+class ImageSkia;
+}  // namespace gfx
+
+namespace ash {
+
+class HoldingSpaceItemView;
+
+namespace holding_space_util {
+
+// Returns a drag image for the specified holding space item `views`. The drag
+// image consists of a stacked representation of the dragged items with the
+// first item being stacked on top. Note that the drag image will paint at most
+// two items with an overflow badge to represent the presence of additional drag
+// items if necessary.
+gfx::ImageSkia CreateDragImage(
+    const std::vector<const HoldingSpaceItemView*>& views);
+
+}  // namespace holding_space_util
+}  // namespace ash
+
+#endif  // ASH_SYSTEM_HOLDING_SPACE_HOLDING_SPACE_DRAG_UTIL_H_
diff --git a/ash/system/holding_space/holding_space_item_view_delegate.cc b/ash/system/holding_space/holding_space_item_view_delegate.cc
index 1ac1c4ae..8322c6f 100644
--- a/ash/system/holding_space/holding_space_item_view_delegate.cc
+++ b/ash/system/holding_space/holding_space_item_view_delegate.cc
@@ -12,12 +12,15 @@
 #include "ash/public/cpp/holding_space/holding_space_model.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/strings/grit/ash_strings.h"
+#include "ash/system/holding_space/holding_space_drag_util.h"
 #include "ash/system/holding_space/holding_space_item_view.h"
 #include "base/bind.h"
+#include "base/i18n/rtl.h"
 #include "net/base/mime_util.h"
 #include "ui/accessibility/ax_action_data.h"
 #include "ui/accessibility/ax_enums.mojom.h"
 #include "ui/base/dragdrop/drag_drop_types.h"
+#include "ui/base/dragdrop/os_exchange_data_provider.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/models/simple_menu_model.h"
 #include "ui/views/controls/menu/menu_runner.h"
@@ -234,10 +237,18 @@
   holding_space_metrics::RecordItemAction(
       GetItems(selection), holding_space_metrics::ItemAction::kDrag);
 
+  // Drag image.
+  gfx::ImageSkia drag_image = holding_space_util::CreateDragImage(selection);
+  data->provider().SetDragImage(std::move(drag_image),
+                                /*image_offset=*/base::i18n::IsRTL()
+                                    ? gfx::Vector2d(drag_image.width(), 0)
+                                    : gfx::Vector2d());
+
+  // Payload.
   std::vector<ui::FileInfo> filenames;
   for (const HoldingSpaceItemView* view : selection) {
-    filenames.push_back(ui::FileInfo(view->item()->file_path(),
-                                     view->item()->file_path().BaseName()));
+    const base::FilePath& file_path = view->item()->file_path();
+    filenames.push_back(ui::FileInfo(file_path, file_path.BaseName()));
   }
   data->SetFilenames(filenames);
 }
diff --git a/ash/wm/immersive_context_ash.cc b/ash/wm/immersive_context_ash.cc
index b003b45..4c6d054 100644
--- a/ash/wm/immersive_context_ash.cc
+++ b/ash/wm/immersive_context_ash.cc
@@ -4,16 +4,18 @@
 
 #include "ash/wm/immersive_context_ash.h"
 
-#include "ash/public/cpp/immersive/immersive_fullscreen_controller.h"
 #include "ash/shelf/shelf.h"
 #include "ash/shell.h"
 #include "ash/wm/window_state.h"
 #include "ash/wm/window_util.h"
+#include "chromeos/ui/frame/immersive/immersive_fullscreen_controller.h"
 #include "ui/display/screen.h"
 #include "ui/views/widget/widget.h"
 
 namespace ash {
 
+using ::chromeos::ImmersiveFullscreenController;
+
 ImmersiveContextAsh::ImmersiveContextAsh() = default;
 
 ImmersiveContextAsh::~ImmersiveContextAsh() = default;
diff --git a/ash/wm/immersive_context_ash.h b/ash/wm/immersive_context_ash.h
index 3684ccf..e2578f0 100644
--- a/ash/wm/immersive_context_ash.h
+++ b/ash/wm/immersive_context_ash.h
@@ -6,19 +6,20 @@
 #define ASH_WM_IMMERSIVE_CONTEXT_ASH_H_
 
 #include "ash/ash_export.h"
-#include "ash/public/cpp/immersive/immersive_context.h"
 #include "base/macros.h"
+#include "chromeos/ui/frame/immersive/immersive_context.h"
 
 namespace ash {
 
-class ASH_EXPORT ImmersiveContextAsh : public ImmersiveContext {
+class ASH_EXPORT ImmersiveContextAsh : public chromeos::ImmersiveContext {
  public:
   ImmersiveContextAsh();
   ~ImmersiveContextAsh() override;
 
   // ImmersiveContext:
-  void OnEnteringOrExitingImmersive(ImmersiveFullscreenController* controller,
-                                    bool entering) override;
+  void OnEnteringOrExitingImmersive(
+      chromeos::ImmersiveFullscreenController* controller,
+      bool entering) override;
   gfx::Rect GetDisplayBoundsInScreen(views::Widget* widget) override;
   bool DoesAnyWindowHaveCapture() override;
 
diff --git a/ash/wm/immersive_fullscreen_controller_unittest.cc b/ash/wm/immersive_fullscreen_controller_unittest.cc
index fd32b4a..c1d55d3 100644
--- a/ash/wm/immersive_fullscreen_controller_unittest.cc
+++ b/ash/wm/immersive_fullscreen_controller_unittest.cc
@@ -2,13 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ash/public/cpp/immersive/immersive_fullscreen_controller.h"
+#include "chromeos/ui/frame/immersive/immersive_fullscreen_controller.h"
 
 #include "ash/frame/header_view.h"
 #include "ash/frame/non_client_frame_view_ash.h"
 #include "ash/public/cpp/ash_features.h"
-#include "ash/public/cpp/immersive/immersive_fullscreen_controller_delegate.h"
-#include "ash/public/cpp/immersive/immersive_fullscreen_controller_test_api.h"
 #include "ash/public/cpp/shelf_config.h"
 #include "ash/public/cpp/shelf_types.h"
 #include "ash/root_window_controller.h"
@@ -20,6 +18,8 @@
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "ash/wm/window_state.h"
 #include "base/test/scoped_feature_list.h"
+#include "chromeos/ui/frame/immersive/immersive_fullscreen_controller_delegate.h"
+#include "chromeos/ui/frame/immersive/immersive_fullscreen_controller_test_api.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/client/cursor_client.h"
 #include "ui/aura/env.h"
@@ -42,6 +42,11 @@
 
 namespace {
 
+using ::chromeos::ImmersiveFullscreenController;
+using ::chromeos::ImmersiveFullscreenControllerDelegate;
+using ::chromeos::ImmersiveFullscreenControllerTestApi;
+using ::chromeos::ImmersiveRevealedLock;
+
 class TestBubbleDialogDelegate : public views::BubbleDialogDelegateView {
  public:
   explicit TestBubbleDialogDelegate(views::View* anchor)
diff --git a/ash/wm/splitview/split_view_controller_unittest.cc b/ash/wm/splitview/split_view_controller_unittest.cc
index 5ddea92..8134bf84 100644
--- a/ash/wm/splitview/split_view_controller_unittest.cc
+++ b/ash/wm/splitview/split_view_controller_unittest.cc
@@ -4819,7 +4819,7 @@
   const WMEvent fullscreen_event(WM_EVENT_TOGGLE_FULLSCREEN);
   window_state->OnWMEvent(&fullscreen_event);
   window_state->SetHideShelfWhenFullscreen(false);
-  window()->SetProperty(kImmersiveIsActive, true);
+  window()->SetProperty(chromeos::kImmersiveIsActive, true);
   shelf_layout_manager->UpdateVisibilityState();
   EXPECT_TRUE(window_state->IsFullscreen());
   EXPECT_FALSE(shelf_layout_manager->IsVisible());
diff --git a/ash/wm/tablet_mode/tablet_mode_toggle_fullscreen_event_handler_unittest.cc b/ash/wm/tablet_mode/tablet_mode_toggle_fullscreen_event_handler_unittest.cc
index c369eb7..6817eb4 100644
--- a/ash/wm/tablet_mode/tablet_mode_toggle_fullscreen_event_handler_unittest.cc
+++ b/ash/wm/tablet_mode/tablet_mode_toggle_fullscreen_event_handler_unittest.cc
@@ -4,11 +4,11 @@
 
 #include "ash/wm/tablet_mode/tablet_mode_toggle_fullscreen_event_handler.h"
 
-#include "ash/public/cpp/window_properties.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller_test_api.h"
 #include "ash/wm/window_state.h"
 #include "ash/wm/wm_event.h"
+#include "chromeos/ui/base/window_properties.h"
 #include "ui/aura/window.h"
 
 namespace ash {
@@ -44,7 +44,7 @@
   void ToggleFullscreen(aura::Window* window, bool immersive) {
     WMEvent toggle_fullscreen(WM_EVENT_TOGGLE_FULLSCREEN);
     WindowState::Get(window)->OnWMEvent(&toggle_fullscreen);
-    window->SetProperty(kImmersiveIsActive, immersive);
+    window->SetProperty(chromeos::kImmersiveIsActive, immersive);
   }
 
   bool IsFullscreen(aura::Window* window) const {
diff --git a/ash/wm/window_state.cc b/ash/wm/window_state.cc
index d094ae1..6da6761 100644
--- a/ash/wm/window_state.cc
+++ b/ash/wm/window_state.cc
@@ -52,6 +52,7 @@
 namespace ash {
 namespace {
 
+using ::chromeos::kImmersiveIsActive;
 using ::chromeos::WindowStateType;
 
 bool IsTabletModeEnabled() {
diff --git a/base/allocator/partition_allocator/page_allocator_internals_posix.h b/base/allocator/partition_allocator/page_allocator_internals_posix.h
index d8a21bc..52518de 100644
--- a/base/allocator/partition_allocator/page_allocator_internals_posix.h
+++ b/base/allocator/partition_allocator/page_allocator_internals_posix.h
@@ -11,6 +11,7 @@
 #include "base/allocator/partition_allocator/partition_alloc_check.h"
 #include "base/check_op.h"
 #include "base/notreached.h"
+#include "base/posix/eintr_wrapper.h"
 #include "build/build_config.h"
 
 #if defined(OS_APPLE)
@@ -135,8 +136,7 @@
   }
 #endif
 
-  void* ret =
-      mmap(hint, length, access_flag, map_flags, fd, 0);
+  void* ret = mmap(hint, length, access_flag, map_flags, fd, 0);
   if (ret == MAP_FAILED) {
     s_allocPageErrorCode = errno;
     ret = nullptr;
@@ -159,14 +159,33 @@
     void* address,
     size_t length,
     PageAccessibilityConfiguration accessibility) {
-  return 0 == mprotect(address, length, GetAccessFlags(accessibility));
+  return 0 ==
+         HANDLE_EINTR(mprotect(address, length, GetAccessFlags(accessibility)));
 }
 
 void SetSystemPagesAccessInternal(
     void* address,
     size_t length,
     PageAccessibilityConfiguration accessibility) {
-  PCHECK(!mprotect(address, length, GetAccessFlags(accessibility)));
+  if (!HANDLE_EINTR(mprotect(address, length, GetAccessFlags(accessibility))))
+    return;
+
+  // mprotect() failed, let's get more data on errno in crash reports. Values
+  // taken from man mprotect(2) on Linux:
+  switch (errno) {
+    case EACCES:
+      PCHECK(false);
+      break;
+    case EINVAL:
+      PCHECK(false);
+      break;
+    case ENOMEM:
+      PCHECK(false);
+      break;
+    default:
+      PCHECK(false);
+      break;
+  }
 }
 
 void FreePagesInternal(void* address, size_t length) {
diff --git a/base/win/registry.cc b/base/win/registry.cc
index c317daf7..3d5f1e2 100644
--- a/base/win/registry.cc
+++ b/base/win/registry.cc
@@ -8,16 +8,19 @@
 
 #include <algorithm>
 #include <memory>
-
 #include <string>
 #include <utility>
+#include <vector>
 
+#include "base/callback.h"
 #include "base/check_op.h"
 #include "base/notreached.h"
 #include "base/stl_util.h"
 #include "base/strings/string_util.h"
 #include "base/strings/string_util_win.h"
 #include "base/threading/thread_restrictions.h"
+#include "base/win/object_watcher.h"
+#include "base/win/scoped_handle.h"
 #include "base/win/shlwapi.h"
 #include "base/win/windows_version.h"
 
@@ -51,9 +54,10 @@
 
   bool StartWatching(HKEY key, ChangeCallback callback);
 
-  // Implementation of ObjectWatcher::Delegate.
+  // ObjectWatcher::Delegate:
   void OnObjectSignaled(HANDLE object) override {
-    DCHECK(watch_event_.IsValid() && watch_event_.Get() == object);
+    DCHECK(watch_event_.IsValid());
+    DCHECK_EQ(watch_event_.Get(), object);
     std::move(callback_).Run();
   }
 
@@ -74,12 +78,13 @@
   if (!watch_event_.IsValid())
     return false;
 
-  DWORD filter = REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_ATTRIBUTES |
-                 REG_NOTIFY_CHANGE_LAST_SET | REG_NOTIFY_CHANGE_SECURITY;
+  const DWORD filter = REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_ATTRIBUTES |
+                       REG_NOTIFY_CHANGE_LAST_SET | REG_NOTIFY_CHANGE_SECURITY;
 
   // Watch the registry key for a change of value.
   LONG result =
-      RegNotifyChangeKeyValue(key, TRUE, filter, watch_event_.Get(), TRUE);
+      RegNotifyChangeKeyValue(key, /*bWatchSubtree=*/TRUE, filter,
+                              watch_event_.Get(), /*fAsynchronous=*/TRUE);
   if (result != ERROR_SUCCESS) {
     watch_event_.Close();
     return false;
diff --git a/base/win/registry.h b/base/win/registry.h
index 784b58b..e5769d42 100644
--- a/base/win/registry.h
+++ b/base/win/registry.h
@@ -12,10 +12,8 @@
 #include <vector>
 
 #include "base/base_export.h"
-#include "base/callback.h"
+#include "base/callback_forward.h"
 #include "base/macros.h"
-#include "base/win/object_watcher.h"
-#include "base/win/scoped_handle.h"
 #include "base/win/windows_types.h"
 
 namespace base {
diff --git a/build/android/pylib/utils/test_filter.py b/build/android/pylib/utils/test_filter.py
index 430b4c5..6db6243 100644
--- a/build/android/pylib/utils/test_filter.py
+++ b/build/android/pylib/utils/test_filter.py
@@ -50,9 +50,9 @@
       '--gtest-filter-file',
       # New argument.
       '--test-launcher-filter-file',
-      dest='test_filter_file', type=os.path.realpath,
+      dest='test_filter_file',
       help='Path to file that contains googletest-style filter strings. '
-           'See also //testing/buildbot/filters/README.md.')
+      'See also //testing/buildbot/filters/README.md.')
 
   filter_group = parser.add_mutually_exclusive_group()
   filter_group.add_argument(
@@ -126,14 +126,16 @@
         '', args.test_filter.replace('#', '.'))
 
   if args.test_filter_file:
-    with open(args.test_filter_file, 'r') as f:
-      positive_file_patterns, negative_file_patterns = ParseFilterFile(f)
-      if positive_file_patterns and HasPositivePatterns(test_filter):
-        raise ConflictingPositiveFiltersException(
-            'Cannot specify positive pattern in both filter file and ' +
-            'filter command line argument')
-      test_filter = AppendPatternsToFilter(test_filter,
-          positive_patterns=positive_file_patterns,
-          negative_patterns=negative_file_patterns)
+    for test_filter_file in args.test_filter_file.split(';'):
+      with open(test_filter_file, 'r') as f:
+        positive_file_patterns, negative_file_patterns = ParseFilterFile(f)
+        if positive_file_patterns and HasPositivePatterns(test_filter):
+          raise ConflictingPositiveFiltersException(
+              'Cannot specify positive pattern in both filter file and ' +
+              'filter command line argument')
+        test_filter = AppendPatternsToFilter(
+            test_filter,
+            positive_patterns=positive_file_patterns,
+            negative_patterns=negative_file_patterns)
 
   return test_filter
diff --git a/build/android/stacktrace/crashpad_stackwalker.py b/build/android/stacktrace/crashpad_stackwalker.py
index 6be71ca..beeb6f4 100755
--- a/build/android/stacktrace/crashpad_stackwalker.py
+++ b/build/android/stacktrace/crashpad_stackwalker.py
@@ -119,8 +119,7 @@
       description='Fetches Crashpad dumps from a given device, '
       'walks and symbolizes the stacks.')
   parser.add_argument('--device', required=True, help='Device serial number')
-  parser.add_argument(
-      '--adb-path', required=True, help='Path to the "adb" command')
+  parser.add_argument('--adb-path', help='Path to the "adb" command')
   parser.add_argument(
       '--build-path',
       required=True,
@@ -137,7 +136,8 @@
     logging.error('Missing minidump_stackwalk executable')
     return 1
 
-  devil_chromium.Initialize(adb_path=args.adb_path)
+  devil_chromium.Initialize(output_directory=args.build_path,
+                            adb_path=args.adb_path)
   device = device_utils.DeviceUtils(args.device)
 
   device_crashpad_path = posixpath.join(args.chrome_cache_path, 'Crashpad',
diff --git a/build/android/tombstones.py b/build/android/tombstones.py
index 821c31b0..33fd681 100755
--- a/build/android/tombstones.py
+++ b/build/android/tombstones.py
@@ -251,16 +251,15 @@
                       help='Path to the adb binary.')
   args = parser.parse_args()
 
-  devil_chromium.Initialize(adb_path=args.adb_path)
+  if args.output_directory:
+    constants.SetOutputDirectory(args.output_directory)
+
+  devil_chromium.Initialize(output_directory=constants.GetOutDirectory(),
+                            adb_path=args.adb_path)
 
   denylist = (device_denylist.Denylist(args.denylist_file)
               if args.denylist_file else None)
 
-  if args.output_directory:
-    constants.SetOutputDirectory(args.output_directory)
-  # Do an up-front test that the output directory is known.
-  constants.CheckOutputDirectory()
-
   if args.device:
     devices = [device_utils.DeviceUtils(args.device)]
   else:
diff --git a/build/build_config.h b/build/build_config.h
index 2c3e81ee..c37c7429 100644
--- a/build/build_config.h
+++ b/build/build_config.h
@@ -61,7 +61,11 @@
 #define OS_MAC 1
 #endif  // defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
 #elif defined(__linux__)
+#if !defined(OS_CHROMEOS)
+// Do not define OS_LINUX on Chrome OS build.
+// The OS_CHROMEOS macro is defined in GN.
 #define OS_LINUX 1
+#endif  // !defined(OS_CHROMEOS)
 // Include a system header to pull in features.h for glibc/uclibc macros.
 #include <unistd.h>
 #if defined(__GLIBC__) && !defined(__UCLIBC__)
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index 6fa8428..3bd02b7e 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-0.20201016.0.1
+0.20201016.2.1
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index 6fa8428..3bd02b7e 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-0.20201016.0.1
+0.20201016.2.1
diff --git a/build/toolchain/win/midl.py b/build/toolchain/win/midl.py
index 5483173..4fa0383 100644
--- a/build/toolchain/win/midl.py
+++ b/build/toolchain/win/midl.py
@@ -57,7 +57,8 @@
     # First: Type string (0x8), followed by 0x3e characters.
     assert contents[custom_off:custom_off + 6] == b'\x08\x00\x3e\x00\x00\x00'
     assert re.match(
-        br'Created by MIDL version 8\.\d\d\.\d{4} at ... Jan 1. ..:..:.. 2038\n',
+        br'Created by MIDL version 8\.\d\d\.\d{4} '
+        br'at ... Jan 1. ..:..:.. 2038\n',
         contents[custom_off + 6:custom_off + 6 + 0x3e])
     # Second: Type uint32 (0x13) storing 0x7fffffff (followed by WW / 0x57 pad)
     assert contents[custom_off+6+0x3e:custom_off+6+0x3e+8] == \
@@ -121,32 +122,27 @@
   open(iid_file, 'wb').write(contents)
 
 
-def overwrite_cls_guid_tlb(tlb_file, dynamic_guid):
-  # See ZapTimestamp() for a short overview of the .tlb format.  The 1st
-  # section contains type descriptions, and the first type should be our
-  # coclass.  It points to the type's GUID in section 6, the GUID section.
+def get_tlb_contents(tlb_file):
+  # See ZapTimestamp() for a short overview of the .tlb format.
   contents = open(tlb_file, 'rb').read()
   assert contents[0:8] == b'MSFT\x02\x00\x01\x00'
   ntypes, = struct.unpack_from('<I', contents, 0x20)
   type_off, type_len = struct.unpack_from('<II', contents, 0x54 + 4*ntypes)
 
-  # contents is a bytestring in Python 3, but a normal string in Py2.
-  if sys.version_info.major == 2:
-    coclass = ord(contents[type_off])
-  else:
-    coclass = contents[type_off]
-  assert coclass == 0x25, "expected coclass"
-
-  guidind = struct.unpack_from('<I', contents, type_off + 0x2c)[0]
   guid_off, guid_len = struct.unpack_from(
       '<II', contents, 0x54 + 4*ntypes + 5*16)
-  assert guidind + 14 <= guid_len
+  assert guid_len % 24 == 0
+
   contents = array.array('B', contents)
-  struct.pack_into('<IHH8s', contents, guid_off + guidind,
-                   *(dynamic_guid.fields[0:3] + (dynamic_guid.bytes[8:],)))
-  # The GUID is correct now, but there's also a GUID hashtable in section 5.
-  # Need to recreate that too.  Since the hash table uses chaining, it's
-  # easiest to recompute it from scratch rather than trying to patch it up.
+
+  return contents, ntypes, type_off, guid_off, guid_len
+
+
+def recreate_guid_hashtable(contents, ntypes, guid_off, guid_len):
+  # This function is called after changing guids in section 6 (the "guid"
+  # section). This function recreates the GUID hashtable in section 5. Since the
+  # hash table uses chaining, it's easiest to recompute it from scratch rather
+  # than trying to patch it up.
   hashtab = [0xffffffff] * (0x80 // 4)
   for guidind in range(guid_off, guid_off + guid_len, 24):
     guidbytes, typeoff, nextguid = struct.unpack_from(
@@ -161,18 +157,140 @@
       '<II', contents, 0x54 + 4*ntypes + 4*16)
   for i, hashval in enumerate(hashtab):
     struct.pack_into('<I', contents, hash_off + 4*i, hashval)
+
+
+def overwrite_cls_guid_tlb(tlb_file, dynamic_guid):
+  contents, ntypes, type_off, guid_off, guid_len = get_tlb_contents(tlb_file)
+
+  # The first type should be a coclass.  It points to the type's GUID in
+  # section 6, the GUID section.
+  coclass, = struct.unpack_from('<B', contents, type_off)
+  assert coclass == 0x25, "expected coclass"
+
+  guidind = struct.unpack_from('<I', contents, type_off + 0x2c)[0]
+  assert guidind + 14 <= guid_len
+  struct.pack_into('<IHH8s', contents, guid_off + guidind,
+                   *(dynamic_guid.fields[0:3] + (dynamic_guid.bytes[8:], )))
+
+  recreate_guid_hashtable(contents, ntypes, guid_off, guid_len)
   open(tlb_file, 'wb').write(contents)
 
 
+# Handle a single clsid substitution where |dynamic_guid| is of the form
+# "D0E1CACC-C63C-4192-94AB-BF8EAD0E3B83".
 def overwrite_cls_guid(h_file, iid_file, tlb_file, dynamic_guid):
-  # Fix up GUID in .h, _i.c, and .tlb.  This currently assumes that there's
-  # only one coclass in the idl file, and that that's the type with the
-  # dynamic type.
+  # Fix up GUID in .h, _i.c, and .tlb.  This function assumes that there is only
+  # one coclass in the idl file, and that that's the type with the dynamic type.
   overwrite_cls_guid_h(h_file, dynamic_guid)
   overwrite_cls_guid_iid(iid_file, dynamic_guid)
   overwrite_cls_guid_tlb(tlb_file, dynamic_guid)
 
 
+def overwrite_guids_h(h_file, dynamic_guids):
+  contents = open(h_file, 'rb').read()
+  for key in dynamic_guids:
+    contents = re.sub(key, dynamic_guids[key], contents, flags=re.IGNORECASE)
+  open(h_file, 'wb').write(contents)
+
+
+def get_uuid_format(guid, prefix, suffix):
+  formatted_uuid = '%s0x%s,0x%s,0x%s,' % (prefix, guid[0:8], guid[9:13],
+                                          guid[14:18])
+  formatted_uuid += '%s0x%s,0x%s' % (prefix, guid[19:21], guid[21:23])
+  for i in range(24, len(guid), 2):
+    formatted_uuid += ',0x' + guid[i:i + 2]
+  formatted_uuid += '%s%s' % (suffix, suffix)
+  return formatted_uuid
+
+
+def get_uuid_format_iid_file(guid):
+  # Convert from "D0E1CACC-C63C-4192-94AB-BF8EAD0E3B83" to
+  # 0xD0E1CACC,0xC63C,0x4192,0x94,0xAB,0xBF,0x8E,0xAD,0x0E,0x3B,0x83.
+  return get_uuid_format(guid, '', '')
+
+
+def overwrite_guids_iid(iid_file, dynamic_guids):
+  contents = open(iid_file, 'rb').read()
+  for key in dynamic_guids:
+    contents = re.sub(get_uuid_format_iid_file(key),
+                      get_uuid_format_iid_file(dynamic_guids[key]),
+                      contents,
+                      flags=re.IGNORECASE)
+  open(iid_file, 'wb').write(contents)
+
+
+def get_uuid_format_proxy_file(guid):
+  # Convert from "D0E1CACC-C63C-4192-94AB-BF8EAD0E3B83" to
+  # {0xD0E1CACC,0xC63C,0x4192,{0x94,0xAB,0xBF,0x8E,0xAD,0x0E,0x3B,0x83}}.
+  return get_uuid_format(guid, '{', '}')
+
+
+def overwrite_guids_proxy(proxy_file, dynamic_guids):
+  contents = open(proxy_file, 'rb').read()
+  for key in dynamic_guids:
+    contents = re.sub(get_uuid_format_proxy_file(key),
+                      get_uuid_format_proxy_file(dynamic_guids[key]),
+                      contents,
+                      flags=re.IGNORECASE)
+  open(proxy_file, 'wb').write(contents)
+
+
+def getguid(contents, offset):
+  # Returns a guid string of the form "D0E1CACC-C63C-4192-94AB-BF8EAD0E3B83".
+  g0, g1, g2, g3 = struct.unpack_from('<IHH8s', contents, offset)
+  g3 = ''.join(['%02X' % ord(g) for g in g3])
+  return '%08X-%04X-%04X-%s-%s' % (g0, g1, g2, g3[0:4], g3[4:])
+
+
+def setguid(contents, offset, guid):
+  guid = uuid.UUID(guid)
+  struct.pack_into('<IHH8s', contents, offset,
+                   *(guid.fields[0:3] + (guid.bytes[8:], )))
+
+
+def overwrite_guids_tlb(tlb_file, dynamic_guids):
+  contents, ntypes, type_off, guid_off, guid_len = get_tlb_contents(tlb_file)
+
+  for i in range(0, guid_len, 24):
+    current_guid = getguid(contents, guid_off + i)
+    for key in dynamic_guids:
+      if key.lower() == current_guid.lower():
+        setguid(contents, guid_off + i, dynamic_guids[key])
+
+  recreate_guid_hashtable(contents, ntypes, guid_off, guid_len)
+  open(tlb_file, 'wb').write(contents)
+
+
+# Handle multiple guid substitutions, where |dynamic_guid| is of the form
+# "158428a4-6014-4978-83ba-9fad0dabe791=3d852661-c795-4d20-9b95-5561e9a1d2d9,"
+# "63B8FFB1-5314-48C9-9C57-93EC8BC6184B=D0E1CACC-C63C-4192-94AB-BF8EAD0E3B83".
+#
+# Before specifying |dynamic_guid| in the build, the IDL file is first compiled
+# with "158428a4-6014-4978-83ba-9fad0dabe791" and
+# "63B8FFB1-5314-48C9-9C57-93EC8BC6184B". These are the "replaceable" guids,
+# i.e., guids that can be replaced in future builds. The resulting MIDL outputs
+# are copied over to src\third_party\win_build_output\.
+#
+# Then, in the future, any changes to these guids can be accomplished by
+# providing a |dynamic_guid| of the format above in the build file. These
+# "dynamic" guid changes by themselves will not require the MIDL compiler and
+# therefore will not require copying output over to
+# src\third_party\win_build_output\.
+#
+# The pre-generated src\third_party\win_build_output\ files are used for
+# cross-compiling on other platforms, since the MIDL compiler is Windows-only.
+def overwrite_guids(h_file, iid_file, proxy_file, tlb_file, dynamic_guids):
+  n = len(dynamic_guids)
+  dynamic_guids = dynamic_guids.split(',')
+  dynamic_guids = dict(s.split('=') for s in dynamic_guids)
+
+  # Fix up GUIDs in .h, _i.c, _p.c, and .tlb.
+  overwrite_guids_h(h_file, dynamic_guids)
+  overwrite_guids_iid(iid_file, dynamic_guids)
+  overwrite_guids_proxy(proxy_file, dynamic_guids)
+  overwrite_guids_tlb(tlb_file, dynamic_guids)
+
+
 def main(arch, gendir, outdir, dynamic_guid, tlb, h, dlldata, iid, proxy, clang,
          idl, *flags):
   # Copy checked-in outputs to final location.
@@ -183,10 +301,13 @@
   source = os.path.normpath(source)
   distutils.dir_util.copy_tree(source, outdir, preserve_times=False)
   if dynamic_guid != 'none':
-    overwrite_cls_guid(os.path.join(outdir, h),
-                       os.path.join(outdir, iid),
-                       os.path.join(outdir, tlb),
-                       uuid.UUID(dynamic_guid))
+    if '=' not in dynamic_guid:
+      overwrite_cls_guid(os.path.join(outdir, h), os.path.join(outdir, iid),
+                         os.path.join(outdir, tlb), uuid.UUID(dynamic_guid))
+    else:
+      overwrite_guids(os.path.join(outdir, h), os.path.join(outdir, iid),
+                      os.path.join(outdir, proxy), os.path.join(outdir, tlb),
+                      dynamic_guid)
 
   # On non-Windows, that's all we can do.
   if sys.platform != 'win32':
diff --git a/cc/base/math_util.cc b/cc/base/math_util.cc
index d9b43f4..486974d 100644
--- a/cc/base/math_util.cc
+++ b/cc/base/math_util.cc
@@ -804,6 +804,22 @@
   res->EndArray();
 }
 
+void MathUtil::AddCornerRadiiToTracedValue(
+    const char* name,
+    const gfx::RRectF& rect,
+    base::trace_event::TracedValue* res) {
+  res->BeginArray(name);
+  res->AppendDouble(rect.GetCornerRadii(gfx::RRectF::Corner::kUpperLeft).x());
+  res->AppendDouble(rect.GetCornerRadii(gfx::RRectF::Corner::kUpperLeft).y());
+  res->AppendDouble(rect.GetCornerRadii(gfx::RRectF::Corner::kUpperRight).x());
+  res->AppendDouble(rect.GetCornerRadii(gfx::RRectF::Corner::kUpperRight).y());
+  res->AppendDouble(rect.GetCornerRadii(gfx::RRectF::Corner::kLowerRight).x());
+  res->AppendDouble(rect.GetCornerRadii(gfx::RRectF::Corner::kLowerRight).y());
+  res->AppendDouble(rect.GetCornerRadii(gfx::RRectF::Corner::kLowerLeft).x());
+  res->AppendDouble(rect.GetCornerRadii(gfx::RRectF::Corner::kLowerLeft).y());
+  res->EndArray();
+}
+
 double MathUtil::AsDoubleSafely(double value) {
   return std::min(value, std::numeric_limits<double>::max());
 }
diff --git a/cc/base/math_util.h b/cc/base/math_util.h
index 1368c9c1..8cb8c386 100644
--- a/cc/base/math_util.h
+++ b/cc/base/math_util.h
@@ -16,6 +16,7 @@
 #include "ui/gfx/geometry/box_f.h"
 #include "ui/gfx/geometry/point3_f.h"
 #include "ui/gfx/geometry/point_f.h"
+#include "ui/gfx/geometry/rounded_corners_f.h"
 #include "ui/gfx/geometry/scroll_offset.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/transform.h"
@@ -303,6 +304,9 @@
   static void AddToTracedValue(const char* name,
                                const gfx::RRectF& rect,
                                base::trace_event::TracedValue* res);
+  static void AddCornerRadiiToTracedValue(const char* name,
+                                          const gfx::RRectF& rect,
+                                          base::trace_event::TracedValue* res);
 
   // Returns a base::Value representation of the floating point value.
   // If the value is inf, returns max double/float representation.
diff --git a/cc/layers/draw_properties.h b/cc/layers/draw_properties.h
index d5a0a7d8..96d7ffd 100644
--- a/cc/layers/draw_properties.h
+++ b/cc/layers/draw_properties.h
@@ -11,7 +11,7 @@
 
 #include "cc/trees/occlusion.h"
 #include "ui/gfx/geometry/rect.h"
-#include "ui/gfx/rrect_f.h"
+#include "ui/gfx/mask_filter_info.h"
 #include "ui/gfx/transform.h"
 
 namespace cc {
@@ -45,10 +45,6 @@
   // True if the layer needs to be clipped by clip_rect.
   bool is_clipped = false;
 
-  // If set, it makes the layer's rounded corner not trigger a render surface if
-  // possible.
-  bool is_fast_rounded_corner = false;
-
   // This rect is a bounding box around what part of the layer is visible, in
   // the layer's coordinate space.
   gfx::Rect visible_layer_rect;
@@ -61,9 +57,9 @@
   // value is used to avoid unnecessarily changing GL scissor state.
   gfx::Rect clip_rect;
 
-  // Contains a rounded corner rect to clip this layer when drawing. This rrect
-  // is in the target space of the layer.
-  gfx::RRectF rounded_corner_bounds;
+  // Contains a mask information applied to the layer. The coordinates is in the
+  // target space of the layer.
+  gfx::MaskFilterInfo mask_filter_info;
 };
 
 }  // namespace cc
diff --git a/cc/layers/layer.cc b/cc/layers/layer.cc
index 9c40eed..5f3fefa8 100644
--- a/cc/layers/layer.cc
+++ b/cc/layers/layer.cc
@@ -549,8 +549,8 @@
         effect_tree_index() != EffectTree::kInvalidNodeId) {
       if (EffectNode* node =
               property_trees->effect_tree.Node(effect_tree_index())) {
-        node->rounded_corner_bounds =
-            gfx::RRectF(effective_clip_rect, corner_radii());
+        node->mask_filter_info = gfx::MaskFilterInfo(
+            effective_clip_rect, corner_radii(), is_fast_rounded_corner());
         node->effect_changed = true;
         property_trees->effect_tree.set_needs_update(true);
       }
@@ -656,8 +656,8 @@
   EffectNode* node = nullptr;
   if (property_trees && effect_tree_index() != EffectTree::kInvalidNodeId &&
       (node = property_trees->effect_tree.Node(effect_tree_index()))) {
-    node->rounded_corner_bounds =
-        gfx::RRectF(EffectiveClipRect(), corner_radii);
+    node->mask_filter_info = gfx::MaskFilterInfo(
+        EffectiveClipRect(), corner_radii, is_fast_rounded_corner());
     node->effect_changed = true;
     property_trees->effect_tree.set_needs_update(true);
   } else {
diff --git a/cc/layers/layer_impl.cc b/cc/layers/layer_impl.cc
index b29796f9..af70440b 100644
--- a/cc/layers/layer_impl.cc
+++ b/cc/layers/layer_impl.cc
@@ -142,13 +142,12 @@
   EffectNode* effect_node = GetEffectTree().Node(effect_tree_index_);
   state->SetAll(draw_properties_.target_space_transform, gfx::Rect(bounds()),
                 draw_properties_.visible_layer_rect,
-                draw_properties_.rounded_corner_bounds,
-                draw_properties_.clip_rect, draw_properties_.is_clipped,
-                contents_opaque, draw_properties_.opacity,
+                draw_properties_.mask_filter_info, draw_properties_.clip_rect,
+                draw_properties_.is_clipped, contents_opaque,
+                draw_properties_.opacity,
                 effect_node->HasRenderSurface() ? SkBlendMode::kSrcOver
                                                 : effect_node->blend_mode,
                 GetSortingContextId());
-  state->is_fast_rounded_corner = draw_properties_.is_fast_rounded_corner;
 }
 
 void LayerImpl::PopulateScaledSharedQuadState(viz::SharedQuadState* state,
@@ -178,13 +177,12 @@
 
   EffectNode* effect_node = GetEffectTree().Node(effect_tree_index_);
   state->SetAll(scaled_draw_transform, content_rect, visible_content_rect,
-                draw_properties().rounded_corner_bounds,
-                draw_properties().clip_rect, draw_properties().is_clipped,
-                contents_opaque, draw_properties().opacity,
+                draw_properties().mask_filter_info, draw_properties().clip_rect,
+                draw_properties().is_clipped, contents_opaque,
+                draw_properties().opacity,
                 effect_node->HasRenderSurface() ? SkBlendMode::kSrcOver
                                                 : effect_node->blend_mode,
                 GetSortingContextId());
-  state->is_fast_rounded_corner = draw_properties().is_fast_rounded_corner;
 }
 
 bool LayerImpl::WillDraw(DrawMode draw_mode,
diff --git a/cc/layers/layer_unittest.cc b/cc/layers/layer_unittest.cc
index b483fc7..2b7d4d69 100644
--- a/cc/layers/layer_unittest.cc
+++ b/cc/layers/layer_unittest.cc
@@ -1811,15 +1811,15 @@
       layer_5->effect_tree_index());
 
   EXPECT_EQ(gfx::RRectF(gfx::RectF(kClipRect), kRoundedCorners),
-            node_1->rounded_corner_bounds);
+            node_1->mask_filter_info.rounded_corner_bounds());
   EXPECT_EQ(gfx::RRectF(gfx::RectF(kClipRect), kRoundedCorners),
-            node_2->rounded_corner_bounds);
+            node_2->mask_filter_info.rounded_corner_bounds());
   EXPECT_EQ(gfx::RRectF(gfx::RectF(kClipRect), kRoundedCorners),
-            node_3->rounded_corner_bounds);
+            node_3->mask_filter_info.rounded_corner_bounds());
   EXPECT_EQ(gfx::RRectF(gfx::RectF(kClipRect), kRoundedCorners),
-            node_4->rounded_corner_bounds);
+            node_4->mask_filter_info.rounded_corner_bounds());
   EXPECT_EQ(gfx::RRectF(gfx::RectF(gfx::Rect(kLayerSize)), kRoundedCorners),
-            node_5->rounded_corner_bounds);
+            node_5->mask_filter_info.rounded_corner_bounds());
 
   // Setting clip to layer bounds.
   layer_1->SetMasksToBounds(true);
@@ -1850,18 +1850,18 @@
   EXPECT_EQ(gfx::RRectF(gfx::RectF(gfx::IntersectRects(gfx::Rect(kLayerSize),
                                                        kClipRect)),
                         kUpdatedRoundedCorners),
-            node_1->rounded_corner_bounds);
+            node_1->mask_filter_info.rounded_corner_bounds());
   EXPECT_EQ(gfx::RRectF(gfx::RectF(gfx::IntersectRects(gfx::Rect(kLayerSize),
                                                        kClipRect)),
                         kUpdatedRoundedCorners),
-            node_2->rounded_corner_bounds);
+            node_2->mask_filter_info.rounded_corner_bounds());
   EXPECT_EQ(gfx::RRectF(gfx::RectF(kClipRect), kUpdatedRoundedCorners),
-            node_3->rounded_corner_bounds);
+            node_3->mask_filter_info.rounded_corner_bounds());
   EXPECT_EQ(gfx::RRectF(gfx::RectF(kUpdatedClipRect), kRoundedCorners),
-            node_4->rounded_corner_bounds);
+            node_4->mask_filter_info.rounded_corner_bounds());
   EXPECT_EQ(
       gfx::RRectF(gfx::RectF(gfx::Rect(kLayerSize)), kUpdatedRoundedCorners),
-      node_5->rounded_corner_bounds);
+      node_5->mask_filter_info.rounded_corner_bounds());
 }
 
 }  // namespace
diff --git a/cc/layers/render_surface_impl.cc b/cc/layers/render_surface_impl.cc
index ac3f7c28..a1eb58c 100644
--- a/cc/layers/render_surface_impl.cc
+++ b/cc/layers/render_surface_impl.cc
@@ -399,7 +399,7 @@
   viz::SharedQuadState* shared_quad_state =
       render_pass->CreateAndAppendSharedQuadState();
   shared_quad_state->SetAll(
-      draw_transform(), content_rect(), content_rect(), rounded_corner_bounds(),
+      draw_transform(), content_rect(), content_rect(), mask_filter_info(),
       draw_properties_.clip_rect, draw_properties_.is_clipped, contents_opaque,
       draw_properties_.draw_opacity, BlendMode(), sorting_context_id);
 
diff --git a/cc/layers/render_surface_impl.h b/cc/layers/render_surface_impl.h
index 115b49a..0dc3766 100644
--- a/cc/layers/render_surface_impl.h
+++ b/cc/layers/render_surface_impl.h
@@ -20,6 +20,7 @@
 #include "components/viz/common/quads/shared_quad_state.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/rect_f.h"
+#include "ui/gfx/mask_filter_info.h"
 #include "ui/gfx/transform.h"
 
 namespace cc {
@@ -54,11 +55,11 @@
   }
   float draw_opacity() const { return draw_properties_.draw_opacity; }
 
-  void SetRoundedCornerRRect(const gfx::RRectF& rounded_corner_bounds) {
-    draw_properties_.rounded_corner_bounds = rounded_corner_bounds;
+  void SetMaskFilterInfo(const gfx::MaskFilterInfo& mask_filter_info) {
+    draw_properties_.mask_filter_info = mask_filter_info;
   }
-  const gfx::RRectF& rounded_corner_bounds() const {
-    return draw_properties_.rounded_corner_bounds;
+  const gfx::MaskFilterInfo& mask_filter_info() const {
+    return draw_properties_.mask_filter_info;
   }
 
   SkBlendMode BlendMode() const;
@@ -241,10 +242,10 @@
     // True if the surface needs to be clipped by clip_rect.
     bool is_clipped : 1;
 
-    // Contains a rounded corner rect to clip this render surface by when
-    // drawing. This rrect is in the target space of the render surface.  The
-    // root render surface will never have this set.
-    gfx::RRectF rounded_corner_bounds;
+    // Contains a mask information applied to the layer. The coordinates is in
+    // the target space of the render surface. The root render surface will
+    // never have this set.
+    gfx::MaskFilterInfo mask_filter_info;
   };
 
   DrawProperties draw_properties_;
diff --git a/cc/layers/video_layer_impl.cc b/cc/layers/video_layer_impl.cc
index 3767b58a..824dd4a 100644
--- a/cc/layers/video_layer_impl.cc
+++ b/cc/layers/video_layer_impl.cc
@@ -163,10 +163,10 @@
   if (visible_quad_rect.IsEmpty())
     return;
 
-  updater_->AppendQuads(
-      render_pass, frame_, transform, quad_rect, visible_quad_rect,
-      draw_properties().rounded_corner_bounds, clip_rect(), is_clipped(),
-      contents_opaque(), draw_opacity(), GetSortingContextId());
+  updater_->AppendQuads(render_pass, frame_, transform, quad_rect,
+                        visible_quad_rect, draw_properties().mask_filter_info,
+                        clip_rect(), is_clipped(), contents_opaque(),
+                        draw_opacity(), GetSortingContextId());
 }
 
 void VideoLayerImpl::DidDraw(viz::ClientResourceProvider* resource_provider) {
diff --git a/cc/test/render_pass_test_utils.cc b/cc/test/render_pass_test_utils.cc
index 3809da1..3232fdc 100644
--- a/cc/test/render_pass_test_utils.cc
+++ b/cc/test/render_pass_test_utils.cc
@@ -112,8 +112,8 @@
                                         const gfx::Rect& rect,
                                         SkColor color) {
   viz::SharedQuadState* shared_state = pass->CreateAndAppendSharedQuadState();
-  shared_state->SetAll(gfx::Transform(), rect, rect, gfx::RRectF(), rect, true,
-                       false, 1, SkBlendMode::kSrcOver, 0);
+  shared_state->SetAll(gfx::Transform(), rect, rect, gfx::MaskFilterInfo(),
+                       rect, true, false, 1, SkBlendMode::kSrcOver, 0);
   auto* quad = pass->CreateAndAppendDrawQuad<viz::SolidColorDrawQuad>();
   quad->SetNew(shared_state, rect, rect, color, false);
   return quad;
@@ -124,8 +124,8 @@
                                             SkColor color,
                                             const gfx::Transform& transform) {
   viz::SharedQuadState* shared_state = pass->CreateAndAppendSharedQuadState();
-  shared_state->SetAll(transform, rect, rect, gfx::RRectF(), rect, false, false,
-                       1,
+  shared_state->SetAll(transform, rect, rect, gfx::MaskFilterInfo(), rect,
+                       false, false, 1,
 
                        SkBlendMode::kSrcOver, 0);
   auto* quad = pass->CreateAndAppendDrawQuad<viz::SolidColorDrawQuad>();
@@ -140,7 +140,7 @@
   viz::SharedQuadState* shared_state =
       to_pass->CreateAndAppendSharedQuadState();
   shared_state->SetAll(gfx::Transform(), output_rect, output_rect,
-                       gfx::RRectF(), output_rect, false, false, 1,
+                       gfx::MaskFilterInfo(), output_rect, false, false, 1,
                        SkBlendMode::kSrcOver, 0);
   auto* quad = to_pass->template CreateAndAppendDrawQuad<QuadType>();
   quad->SetNew(shared_state, output_rect, output_rect, contributing_pass->id, 0,
@@ -170,8 +170,9 @@
   gfx::Rect output_rect = contributing_pass->output_rect;
   viz::SharedQuadState* shared_state =
       to_pass->CreateAndAppendSharedQuadState();
-  shared_state->SetAll(transform, output_rect, output_rect, gfx::RRectF(),
-                       output_rect, false, false, 1, blend_mode, 0);
+  shared_state->SetAll(transform, output_rect, output_rect,
+                       gfx::MaskFilterInfo(), output_rect, false, false, 1,
+                       blend_mode, 0);
   auto* quad =
       to_pass->CreateAndAppendDrawQuad<viz::AggregatedRenderPassDrawQuad>();
   gfx::Size arbitrary_nonzero_size(1, 1);
@@ -217,8 +218,8 @@
 
   viz::SharedQuadState* shared_state =
       to_pass->CreateAndAppendSharedQuadState();
-  shared_state->SetAll(gfx::Transform(), rect, rect, gfx::RRectF(), rect, false,
-                       false, 1, SkBlendMode::kSrcOver, 0);
+  shared_state->SetAll(gfx::Transform(), rect, rect, gfx::MaskFilterInfo(),
+                       rect, false, false, 1, SkBlendMode::kSrcOver, 0);
 
   auto* debug_border_quad =
       to_pass->CreateAndAppendDrawQuad<viz::DebugBorderDrawQuad>();
@@ -278,8 +279,8 @@
 
   viz::SharedQuadState* shared_state2 =
       to_pass->CreateAndAppendSharedQuadState();
-  shared_state->SetAll(gfx::Transform(), rect, rect, gfx::RRectF(), rect, false,
-                       false, 1, SkBlendMode::kSrcOver, 0);
+  shared_state->SetAll(gfx::Transform(), rect, rect, gfx::MaskFilterInfo(),
+                       rect, false, false, 1, SkBlendMode::kSrcOver, 0);
 
   auto* tile_quad = to_pass->CreateAndAppendDrawQuad<viz::TileDrawQuad>();
   tile_quad->SetNew(shared_state2, rect, visible_rect, needs_blending,
@@ -394,8 +395,8 @@
 
   viz::SharedQuadState* shared_state =
       to_pass->CreateAndAppendSharedQuadState();
-  shared_state->SetAll(gfx::Transform(), rect, rect, gfx::RRectF(), rect, false,
-                       false, 1, SkBlendMode::kSrcOver, 0);
+  shared_state->SetAll(gfx::Transform(), rect, rect, gfx::MaskFilterInfo(),
+                       rect, false, false, 1, SkBlendMode::kSrcOver, 0);
 
   viz::DebugBorderDrawQuad* debug_border_quad =
       to_pass->CreateAndAppendDrawQuad<viz::DebugBorderDrawQuad>();
@@ -455,8 +456,8 @@
 
   viz::SharedQuadState* shared_state2 =
       to_pass->CreateAndAppendSharedQuadState();
-  shared_state2->SetAll(gfx::Transform(), rect, rect, gfx::RRectF(), rect,
-                        false, false, 1, SkBlendMode::kSrcOver, 0);
+  shared_state2->SetAll(gfx::Transform(), rect, rect, gfx::MaskFilterInfo(),
+                        rect, false, false, 1, SkBlendMode::kSrcOver, 0);
 
   viz::TileDrawQuad* tile_quad =
       to_pass->CreateAndAppendDrawQuad<viz::TileDrawQuad>();
diff --git a/cc/test/render_pass_test_utils.h b/cc/test/render_pass_test_utils.h
index 54a8d59..0c4b5001 100644
--- a/cc/test/render_pass_test_utils.h
+++ b/cc/test/render_pass_test_utils.h
@@ -72,8 +72,8 @@
                                         const gfx::Rect& rect,
                                         SkColor color) {
   viz::SharedQuadState* shared_state = pass->CreateAndAppendSharedQuadState();
-  shared_state->SetAll(gfx::Transform(), rect, rect, gfx::RRectF(), rect, false,
-                       false, 1, SkBlendMode::kSrcOver, 0);
+  shared_state->SetAll(gfx::Transform(), rect, rect, gfx::MaskFilterInfo(),
+                       rect, false, false, 1, SkBlendMode::kSrcOver, 0);
   auto* quad =
       pass->template CreateAndAppendDrawQuad<viz::SolidColorDrawQuad>();
   quad->SetNew(shared_state, rect, rect, color, false);
diff --git a/cc/trees/draw_properties_unittest.cc b/cc/trees/draw_properties_unittest.cc
index cd040c96..d461d6a 100644
--- a/cc/trees/draw_properties_unittest.cc
+++ b/cc/trees/draw_properties_unittest.cc
@@ -8122,12 +8122,12 @@
   UpdateMainDrawProperties();
   CommitAndActivate();
 
-  EXPECT_FALSE(
-      GetRenderSurfaceImpl(child_1)->rounded_corner_bounds().IsEmpty());
-  EXPECT_FALSE(
-      GetRenderSurfaceImpl(child_2)->rounded_corner_bounds().IsEmpty());
-  EXPECT_FALSE(
-      GetRenderSurfaceImpl(child_3)->rounded_corner_bounds().IsEmpty());
+  EXPECT_TRUE(
+      GetRenderSurfaceImpl(child_1)->mask_filter_info().HasRoundedCorners());
+  EXPECT_TRUE(
+      GetRenderSurfaceImpl(child_2)->mask_filter_info().HasRoundedCorners());
+  EXPECT_TRUE(
+      GetRenderSurfaceImpl(child_3)->mask_filter_info().HasRoundedCorners());
 }
 
 }  // namespace
diff --git a/cc/trees/draw_property_utils.cc b/cc/trees/draw_property_utils.cc
index 1e007fd..e7537c57 100644
--- a/cc/trees/draw_property_utils.cc
+++ b/cc/trees/draw_property_utils.cc
@@ -706,27 +706,25 @@
                                 layer->clip_tree_index(), target_node->id);
 }
 
-std::pair<gfx::RRectF, bool> GetRoundedCornerRRect(
-    const PropertyTrees* property_trees,
-    int effect_tree_index,
-    bool for_render_surface) {
-  static const std::pair<gfx::RRectF, bool> kEmptyRoundedCornerInfo(
-      gfx::RRectF(), false);
+gfx::MaskFilterInfo GetMaskFilterInfo(const PropertyTrees* property_trees,
+                                      int effect_tree_index,
+                                      bool for_render_surface) {
+  static const gfx::MaskFilterInfo kEmptyMaskFilterInfo;
   const EffectTree* effect_tree = &property_trees->effect_tree;
   const EffectNode* effect_node = effect_tree->Node(effect_tree_index);
   const int target_id = effect_node->target_id;
 
-  // Return empty rrect if this node has a render surface but the function call
-  // was made for a non render surface.
+  // Return empty mask info if this node has a render surface but the function
+  // call was made for a non render surface.
   if (effect_node->HasRenderSurface() && !for_render_surface)
-    return kEmptyRoundedCornerInfo;
+    return kEmptyMaskFilterInfo;
 
   // Traverse the parent chain up to the render target to find a node which has
   // a rounded corner bounds set.
   const EffectNode* node = effect_node;
   bool found_rounded_corner = false;
   while (node) {
-    if (!node->rounded_corner_bounds.IsEmpty()) {
+    if (node->mask_filter_info.HasRoundedCorners()) {
       found_rounded_corner = true;
       break;
     }
@@ -749,17 +747,16 @@
   // While traversing up the parent chain we did not find any node with a
   // rounded corner.
   if (!node || !found_rounded_corner)
-    return kEmptyRoundedCornerInfo;
+    return kEmptyMaskFilterInfo;
 
   gfx::Transform to_target;
   if (!property_trees->GetToTarget(node->transform_id, target_id, &to_target))
-    return kEmptyRoundedCornerInfo;
+    return kEmptyMaskFilterInfo;
 
-  auto result =
-      std::make_pair(node->rounded_corner_bounds, node->is_fast_rounded_corner);
+  auto result = node->mask_filter_info;
 
-  if (!to_target.TransformRRectF(&result.first))
-    return kEmptyRoundedCornerInfo;
+  if (!result.Transform(to_target))
+    return kEmptyMaskFilterInfo;
 
   return result;
 }
@@ -839,10 +836,9 @@
   SetSurfaceDrawOpacity(property_trees->effect_tree, render_surface);
   SetSurfaceDrawTransform(property_trees, render_surface);
 
-  render_surface->SetRoundedCornerRRect(
-      GetRoundedCornerRRect(property_trees, render_surface->EffectTreeIndex(),
-                            /*for_render_surface*/ true)
-          .first);
+  render_surface->SetMaskFilterInfo(
+      GetMaskFilterInfo(property_trees, render_surface->EffectTreeIndex(),
+                        /*for_render_surface=*/true));
   render_surface->SetScreenSpaceTransform(
       property_trees->ToScreenSpaceTransformWithoutSurfaceContentsScale(
           render_surface->TransformTreeIndex(),
@@ -1142,12 +1138,10 @@
         layer, property_trees->transform_tree, property_trees->effect_tree);
     layer->draw_properties().screen_space_transform_is_animating =
         transform_node->to_screen_is_potentially_animated;
-    auto rounded_corner_info =
-        GetRoundedCornerRRect(property_trees, layer->effect_tree_index(),
-                              /*from_render_surface*/ false);
-    layer->draw_properties().rounded_corner_bounds = rounded_corner_info.first;
-    layer->draw_properties().is_fast_rounded_corner =
-        rounded_corner_info.second;
+    auto mask_filter_info =
+        GetMaskFilterInfo(property_trees, layer->effect_tree_index(),
+                          /*from_render_surface=*/false);
+    layer->draw_properties().mask_filter_info = mask_filter_info;
   }
 
   // Compute effects and determine if render surfaces have contributing layers
diff --git a/cc/trees/effect_node.cc b/cc/trees/effect_node.cc
index de8b74ca..0bf427a56 100644
--- a/cc/trees/effect_node.cc
+++ b/cc/trees/effect_node.cc
@@ -34,7 +34,6 @@
       has_masking_child(false),
       effect_changed(false),
       subtree_has_copy_request(false),
-      is_fast_rounded_corner(false),
       node_or_ancestor_has_filters(false),
       affected_by_backdrop_filter(false),
       render_surface_reason(RenderSurfaceReason::kNone),
@@ -60,8 +59,7 @@
          backdrop_filters == other.backdrop_filters &&
          backdrop_filter_bounds == other.backdrop_filter_bounds &&
          backdrop_mask_element_id == other.backdrop_mask_element_id &&
-         rounded_corner_bounds == other.rounded_corner_bounds &&
-         is_fast_rounded_corner == other.is_fast_rounded_corner &&
+         mask_filter_info == other.mask_filter_info &&
          node_or_ancestor_has_filters == other.node_or_ancestor_has_filters &&
          affected_by_backdrop_filter == other.affected_by_backdrop_filter &&
          // The specific reason is just for tracing/testing/debugging, so just
@@ -158,12 +156,18 @@
   if (!backdrop_filters.IsEmpty())
     value->SetString("backdrop_filters", backdrop_filters.ToString());
   value->SetDouble("backdrop_filter_quality", backdrop_filter_quality);
-  value->SetBoolean("is_fast_rounded_corner", is_fast_rounded_corner);
   value->SetBoolean("node_or_ancestor_has_filters",
                     node_or_ancestor_has_filters);
-  if (!rounded_corner_bounds.IsEmpty()) {
-    MathUtil::AddToTracedValue("rounded_corner_bounds", rounded_corner_bounds,
+  if (!mask_filter_info.IsEmpty()) {
+    MathUtil::AddToTracedValue("mask_filter_bounds", mask_filter_info.bounds(),
                                value);
+    if (mask_filter_info.HasRoundedCorners()) {
+      MathUtil::AddCornerRadiiToTracedValue(
+          "mask_filter_rounded_corner_raii",
+          mask_filter_info.rounded_corner_bounds(), value);
+      value->SetBoolean("mask_filter_is_fast_rounded_corner",
+                        mask_filter_info.is_fast_rounded_corner());
+    }
   }
   value->SetString("blend_mode", SkBlendMode_Name(blend_mode));
   value->SetBoolean("cache_render_surface", cache_render_surface);
diff --git a/cc/trees/effect_node.h b/cc/trees/effect_node.h
index 4834773c..e3b05ee 100644
--- a/cc/trees/effect_node.h
+++ b/cc/trees/effect_node.h
@@ -11,6 +11,7 @@
 #include "third_party/skia/include/core/SkBlendMode.h"
 #include "ui/gfx/geometry/point_f.h"
 #include "ui/gfx/geometry/size_f.h"
+#include "ui/gfx/mask_filter_info.h"
 #include "ui/gfx/rrect_f.h"
 
 namespace base {
@@ -80,9 +81,10 @@
   // image.
   ElementId backdrop_mask_element_id;
 
-  // Bounds of rounded corner rrect in the space of the transform node
-  // associated with this effect node.
-  gfx::RRectF rounded_corner_bounds;
+  // The mask filter information applied to this effect node. The coordinates of
+  // in the mask info is in the space of the transform node associated with this
+  // effect node.
+  gfx::MaskFilterInfo mask_filter_info;
 
   SkBlendMode blend_mode;
 
@@ -127,9 +129,6 @@
   // frame. Needed in order to compute damage rect.
   bool effect_changed : 1;
   bool subtree_has_copy_request : 1;
-  // If set, the effect node tries to not trigger a render surface due to it
-  // having a rounded corner.
-  bool is_fast_rounded_corner : 1;
   // If the node or it's parent has the filters, it sets to true.
   bool node_or_ancestor_has_filters : 1;
   // All node in the subtree starting from the containing render surface, and
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index 4d646ef..49e9d29 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -1053,9 +1053,9 @@
   viz::SharedQuadState* shared_quad_state =
       target_render_pass->CreateAndAppendSharedQuadState();
   shared_quad_state->SetAll(gfx::Transform(), root_target_rect,
-                            root_target_rect, gfx::RRectF(), root_target_rect,
-                            false, are_contents_opaque, opacity,
-                            SkBlendMode::kSrcOver, sorting_context_id);
+                            root_target_rect, gfx::MaskFilterInfo(),
+                            root_target_rect, false, are_contents_opaque,
+                            opacity, SkBlendMode::kSrcOver, sorting_context_id);
 
   for (gfx::Rect screen_space_rect : fill_region) {
     gfx::Rect visible_screen_space_rect = screen_space_rect;
diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc
index fa6ac027..99ed46b5f 100644
--- a/cc/trees/layer_tree_host_impl_unittest.cc
+++ b/cc/trees/layer_tree_host_impl_unittest.cc
@@ -18117,8 +18117,8 @@
 
   // Add rounded corners to the layer, which are unable to be hit tested by the
   // simple quad-based logic.
-  CreateEffectNode(rounded_frame_layer).rounded_corner_bounds =
-      gfx::RRectF(25, 25, 50, 50, 5);
+  CreateEffectNode(rounded_frame_layer).mask_filter_info =
+      gfx::MaskFilterInfo(gfx::RRectF(25, 25, 50, 50, 5), false);
 
   UpdateDrawProperties(host_impl_->active_tree());
 
diff --git a/cc/trees/occlusion_tracker.cc b/cc/trees/occlusion_tracker.cc
index 7a6bf206..b3ed41e 100644
--- a/cc/trees/occlusion_tracker.cc
+++ b/cc/trees/occlusion_tracker.cc
@@ -350,7 +350,7 @@
   if (layer->Is3dSorted())
     return;
 
-  if (!layer->draw_properties().rounded_corner_bounds.IsEmpty())
+  if (!layer->draw_properties().mask_filter_info.IsEmpty())
     return;
 
   SimpleEnclosedRegion opaque_layer_region = layer->VisibleOpaqueRegion();
diff --git a/cc/trees/occlusion_tracker_unittest.cc b/cc/trees/occlusion_tracker_unittest.cc
index c2dafbd..9f347c7b 100644
--- a/cc/trees/occlusion_tracker_unittest.cc
+++ b/cc/trees/occlusion_tracker_unittest.cc
@@ -924,8 +924,8 @@
     filters.Append(FilterOperation::CreateOpacityFilter(0.5f));
     GetEffectNode(opacity_layer)->filters = filters;
 
-    CreateEffectNode(rounded_corner_layer).rounded_corner_bounds =
-        gfx::RRectF(1, 2, 3, 4, 5, 6);
+    CreateEffectNode(rounded_corner_layer).mask_filter_info =
+        gfx::MaskFilterInfo(gfx::RRectF(1, 2, 3, 4, 5, 6), false);
 
     this->CalcDrawEtc();
     EXPECT_TRUE(rounded_corner_layer->contributes_to_drawn_render_surface());
diff --git a/cc/trees/property_tree.cc b/cc/trees/property_tree.cc
index a4a770d1..c188896f 100644
--- a/cc/trees/property_tree.cc
+++ b/cc/trees/property_tree.cc
@@ -1137,7 +1137,7 @@
   const EffectNode* effect_node = Node(effect_id);
   for (; effect_node->id != kContentsRootNodeId;
        effect_node = Node(effect_node->parent_id)) {
-    if (!effect_node->rounded_corner_bounds.IsEmpty() ||
+    if (!effect_node->mask_filter_info.IsEmpty() ||
         effect_node->has_masking_child)
       return true;
   }
diff --git a/cc/trees/property_tree_builder.cc b/cc/trees/property_tree_builder.cc
index ab2eb2f..3a4b6cf 100644
--- a/cc/trees/property_tree_builder.cc
+++ b/cc/trees/property_tree_builder.cc
@@ -7,7 +7,9 @@
 #include <stddef.h>
 
 #include <map>
+#include <memory>
 #include <set>
+#include <utility>
 
 #include "base/auto_reset.h"
 #include "cc/base/math_util.h"
@@ -165,10 +167,6 @@
          !layer->clip_rect().IsEmpty();
 }
 
-gfx::RRectF RoundedCornerBounds(Layer* layer) {
-  return gfx::RRectF(layer->EffectiveClipRect(), layer->corner_radii());
-}
-
 void PropertyTreeBuilderContext::AddClipNodeIfNeeded(
     const DataForRecursion& data_from_ancestor,
     Layer* layer,
@@ -491,8 +489,9 @@
     // This is currently in the local space of the layer and hence in an invalid
     // space. Once we have the associated transform node for this effect node,
     // we will update this to the transform node's coordinate space.
-    node->rounded_corner_bounds = RoundedCornerBounds(layer);
-    node->is_fast_rounded_corner = layer->is_fast_rounded_corner();
+    node->mask_filter_info =
+        gfx::MaskFilterInfo(layer->EffectiveClipRect(), layer->corner_radii(),
+                            layer->is_fast_rounded_corner());
   }
 
   if (!is_root) {
@@ -557,7 +556,8 @@
 
   EffectNode* effect_node =
       effect_tree_.Node(data_for_children->effect_tree_parent);
-  const bool has_rounded_corner = !effect_node->rounded_corner_bounds.IsEmpty();
+  const bool has_rounded_corner =
+      effect_node->mask_filter_info.HasRoundedCorners();
 
   // Having a rounded corner should trigger a transform node.
   if (has_rounded_corner)
diff --git a/cc/trees/property_tree_builder_unittest.cc b/cc/trees/property_tree_builder_unittest.cc
index 243de40..9e8baa2 100644
--- a/cc/trees/property_tree_builder_unittest.cc
+++ b/cc/trees/property_tree_builder_unittest.cc
@@ -859,7 +859,8 @@
   // Since this effect node has no descendants that draw and no descendant that
   // has a rounded corner, it does not need a render surface.
   const EffectNode* effect_node = GetEffectNode(rounded_corner_layer_1.get());
-  gfx::RRectF rounded_corner_bounds_1 = effect_node->rounded_corner_bounds;
+  gfx::RRectF rounded_corner_bounds_1 =
+      effect_node->mask_filter_info.rounded_corner_bounds();
   EXPECT_FALSE(effect_node->HasRenderSurface());
   EXPECT_FLOAT_EQ(rounded_corner_bounds_1.GetSimpleRadius(),
                   kRoundedCorner1Radius);
@@ -869,7 +870,8 @@
   // Since this node has descendants with roudned corners, it needs a render
   // surface. It also has 2 descendants that draw.
   effect_node = GetEffectNode(rounded_corner_layer_2.get());
-  gfx::RRectF rounded_corner_bounds_2 = effect_node->rounded_corner_bounds;
+  gfx::RRectF rounded_corner_bounds_2 =
+      effect_node->mask_filter_info.rounded_corner_bounds();
   EXPECT_TRUE(effect_node->HasRenderSurface());
   EXPECT_FLOAT_EQ(rounded_corner_bounds_2.GetSimpleRadius(),
                   kRoundedCorner2Radius);
@@ -879,7 +881,8 @@
   // Since this node has a descendant that has a rounded corner, it will trigger
   // the creation of a render surface.
   effect_node = GetEffectNode(rounded_corner_layer_3.get());
-  gfx::RRectF rounded_corner_bounds_3 = effect_node->rounded_corner_bounds;
+  gfx::RRectF rounded_corner_bounds_3 =
+      effect_node->mask_filter_info.rounded_corner_bounds();
   EXPECT_TRUE(effect_node->HasRenderSurface());
   EXPECT_FLOAT_EQ(rounded_corner_bounds_3.GetSimpleRadius(),
                   kRoundedCorner3Radius);
@@ -889,7 +892,8 @@
   // Since this node has no descendants that draw nor any descendant that has a
   // rounded corner, it does not need a render surface.
   effect_node = GetEffectNode(rounded_corner_layer_4.get());
-  gfx::RRectF rounded_corner_bounds_4 = effect_node->rounded_corner_bounds;
+  gfx::RRectF rounded_corner_bounds_4 =
+      effect_node->mask_filter_info.rounded_corner_bounds();
   EXPECT_FALSE(effect_node->HasRenderSurface());
   EXPECT_FLOAT_EQ(rounded_corner_bounds_4.GetSimpleRadius(),
                   kRoundedCorner4Radius);
@@ -918,7 +922,8 @@
   // scale factor is 1.6 thus giving the target space origin of [24, 24]. The
   // corner radius is also scaled by a factor of 1.6.
   const gfx::RRectF actual_rrect_1 =
-      rounded_corner_layer_1_impl->draw_properties().rounded_corner_bounds;
+      rounded_corner_layer_1_impl->draw_properties()
+          .mask_filter_info.rounded_corner_bounds();
   gfx::RectF bounds_in_target_space = kRoundedCornerLayer1Bound;
   bounds_in_target_space.Scale(kDeviceScale);
   EXPECT_EQ(actual_rrect_1.rect(), bounds_in_target_space);
@@ -931,13 +936,16 @@
   // scale factor is 1.6 thus giving the target space origin of [64, 64]. The
   // corner radius is also scaled by a factor of 1.6.
   const gfx::RRectF actual_self_rrect_2 =
-      rounded_corner_layer_2_impl->draw_properties().rounded_corner_bounds;
+      rounded_corner_layer_2_impl->draw_properties()
+          .mask_filter_info.rounded_corner_bounds();
   EXPECT_TRUE(actual_self_rrect_2.IsEmpty());
 
   bounds_in_target_space = kRoundedCornerLayer2Bound;
   bounds_in_target_space.Scale(kDeviceScale);
   const gfx::RRectF actual_render_target_rrect_2 =
-      rounded_corner_layer_2_impl->render_target()->rounded_corner_bounds();
+      rounded_corner_layer_2_impl->render_target()
+          ->mask_filter_info()
+          .rounded_corner_bounds();
   EXPECT_EQ(actual_render_target_rrect_2.rect(), bounds_in_target_space);
   EXPECT_FLOAT_EQ(actual_render_target_rrect_2.GetSimpleRadius(),
                   kRoundedCorner2Radius * kDeviceScale);
@@ -948,7 +956,8 @@
   // device scale factor is 1.6 thus giving the target space origin of [64, 88].
   // The corner radius is also scaled by a factor of 1.6 * transform scale.
   const gfx::RRectF actual_self_rrect_3 =
-      rounded_corner_layer_3_impl->draw_properties().rounded_corner_bounds;
+      rounded_corner_layer_3_impl->draw_properties()
+          .mask_filter_info.rounded_corner_bounds();
   EXPECT_TRUE(actual_self_rrect_3.IsEmpty());
 
   bounds_in_target_space = kRoundedCornerLayer3Bound;
@@ -960,7 +969,9 @@
   bounds_in_target_space.set_size(transformed_size);
 
   const gfx::RRectF actual_render_target_rrect_3 =
-      rounded_corner_layer_3_impl->render_target()->rounded_corner_bounds();
+      rounded_corner_layer_3_impl->render_target()
+          ->mask_filter_info()
+          .rounded_corner_bounds();
   EXPECT_EQ(actual_render_target_rrect_3.rect(), bounds_in_target_space);
   EXPECT_FLOAT_EQ(actual_render_target_rrect_3.GetSimpleRadius(),
                   kRoundedCorner3Radius * kDeviceScale * kRoundedCorner3Scale);
@@ -972,7 +983,8 @@
   // rigin of [3.2, 3.2].
   // The corner radius is also scaled by a factor of 3.2.
   const gfx::RRectF actual_rrect_4 =
-      rounded_corner_layer_4_impl->draw_properties().rounded_corner_bounds;
+      rounded_corner_layer_4_impl->draw_properties()
+          .mask_filter_info.rounded_corner_bounds();
   bounds_in_target_space = kRoundedCornerLayer4Bound;
   bounds_in_target_space.Scale(kDeviceScale * kRoundedCorner3Scale);
   EXPECT_EQ(actual_rrect_4.rect(), bounds_in_target_space);
@@ -1042,7 +1054,8 @@
   // that has a rounded corner before the render surface, it does not need a
   // render surface.
   const EffectNode* effect_node = GetEffectNode(rounded_corner_layer_1.get());
-  gfx::RRectF rounded_corner_bounds_1 = effect_node->rounded_corner_bounds;
+  gfx::RRectF rounded_corner_bounds_1 =
+      effect_node->mask_filter_info.rounded_corner_bounds();
   EXPECT_FALSE(effect_node->HasRenderSurface());
   EXPECT_FLOAT_EQ(rounded_corner_bounds_1.GetSimpleRadius(),
                   kRoundedCorner1Radius);
@@ -1052,7 +1065,8 @@
   // Since this effect node has no descendants that draw and no descendant that
   // has a rounded corner, it does not need a render surface.
   effect_node = GetEffectNode(rounded_corner_layer_2.get());
-  gfx::RRectF rounded_corner_bounds_2 = effect_node->rounded_corner_bounds;
+  gfx::RRectF rounded_corner_bounds_2 =
+      effect_node->mask_filter_info.rounded_corner_bounds();
   EXPECT_FALSE(effect_node->HasRenderSurface());
   EXPECT_FLOAT_EQ(rounded_corner_bounds_2.GetSimpleRadius(),
                   kRoundedCorner2Radius);
@@ -1077,7 +1091,8 @@
   // scale factor is 1.6 thus giving the target space origin of [96, 0]. The
   // corner radius is also scaled by a factor of 1.6.
   const gfx::RRectF actual_rrect_1 =
-      rounded_corner_layer_1_impl->draw_properties().rounded_corner_bounds;
+      rounded_corner_layer_1_impl->draw_properties()
+          .mask_filter_info.rounded_corner_bounds();
   gfx::RectF bounds_in_target_space = kRoundedCornerLayer1Bound;
   bounds_in_target_space.Scale(kDeviceScale);
   EXPECT_EQ(actual_rrect_1.rect(), bounds_in_target_space);
@@ -1088,7 +1103,8 @@
   // The render target for this layer is |render_surface|.
   // The offset from the origin of the render target is [0, 0].
   const gfx::RRectF actual_rrect_2 =
-      rounded_corner_layer_2_impl->draw_properties().rounded_corner_bounds;
+      rounded_corner_layer_2_impl->draw_properties()
+          .mask_filter_info.rounded_corner_bounds();
   bounds_in_target_space = kRoundedCornerLayer2Bound;
   bounds_in_target_space.Scale(kDeviceScale);
   EXPECT_EQ(actual_rrect_2.rect(), bounds_in_target_space);
@@ -1157,7 +1173,8 @@
   // Since this effect node has 1 descendant with a rounded corner without a
   // render surface along the chain, it need a render surface.
   const EffectNode* effect_node = GetEffectNode(rounded_corner_layer_1.get());
-  gfx::RRectF rounded_corner_bounds_1 = effect_node->rounded_corner_bounds;
+  gfx::RRectF rounded_corner_bounds_1 =
+      effect_node->mask_filter_info.rounded_corner_bounds();
   EXPECT_TRUE(effect_node->HasRenderSurface());
   EXPECT_FLOAT_EQ(rounded_corner_bounds_1.GetSimpleRadius(),
                   kRoundedCorner1Radius);
@@ -1167,7 +1184,8 @@
   // Since this effect node has no descendants that draw and no descendant that
   // has a rounded corner, it does not need a render surface.
   effect_node = GetEffectNode(rounded_corner_layer_2.get());
-  gfx::RRectF rounded_corner_bounds_2 = effect_node->rounded_corner_bounds;
+  gfx::RRectF rounded_corner_bounds_2 =
+      effect_node->mask_filter_info.rounded_corner_bounds();
   EXPECT_FALSE(effect_node->HasRenderSurface());
   EXPECT_FLOAT_EQ(rounded_corner_bounds_2.GetSimpleRadius(),
                   kRoundedCorner2Radius);
@@ -1192,13 +1210,16 @@
   // scale factor is 1.6 thus giving the target space origin of [0, 96]. The
   // corner radius is also scaled by a factor of 1.6.
   const gfx::RRectF actual_self_rrect_1 =
-      rounded_corner_layer_1_impl->draw_properties().rounded_corner_bounds;
+      rounded_corner_layer_1_impl->draw_properties()
+          .mask_filter_info.rounded_corner_bounds();
   EXPECT_TRUE(actual_self_rrect_1.IsEmpty());
 
   gfx::RectF bounds_in_target_space = kRoundedCornerLayer1Bound;
   bounds_in_target_space.Scale(kDeviceScale);
   const gfx::RRectF actual_render_target_rrect_1 =
-      rounded_corner_layer_1_impl->render_target()->rounded_corner_bounds();
+      rounded_corner_layer_1_impl->render_target()
+          ->mask_filter_info()
+          .rounded_corner_bounds();
   EXPECT_EQ(actual_render_target_rrect_1.rect(), bounds_in_target_space);
   EXPECT_FLOAT_EQ(actual_render_target_rrect_1.GetSimpleRadius(),
                   kRoundedCorner1Radius * kDeviceScale);
@@ -1207,7 +1228,8 @@
   // The render target for this layer is |render_surface|.
   // The offset from the origin of the render target is [0, 0].
   const gfx::RRectF actual_rrect_2 =
-      rounded_corner_layer_2_impl->draw_properties().rounded_corner_bounds;
+      rounded_corner_layer_2_impl->draw_properties()
+          .mask_filter_info.rounded_corner_bounds();
   bounds_in_target_space = kRoundedCornerLayer2Bound;
   bounds_in_target_space.Scale(kDeviceScale);
   EXPECT_EQ(actual_rrect_2.rect(), bounds_in_target_space);
@@ -1297,9 +1319,10 @@
   // surface even though it has 2 layers in the subtree that draws content.
   const EffectNode* effect_node =
       GetEffectNode(fast_rounded_corner_layer.get());
-  gfx::RRectF rounded_corner_bounds_1 = effect_node->rounded_corner_bounds;
+  gfx::RRectF rounded_corner_bounds_1 =
+      effect_node->mask_filter_info.rounded_corner_bounds();
   EXPECT_FALSE(effect_node->HasRenderSurface());
-  EXPECT_TRUE(effect_node->is_fast_rounded_corner);
+  EXPECT_TRUE(effect_node->mask_filter_info.is_fast_rounded_corner());
   EXPECT_FLOAT_EQ(rounded_corner_bounds_1.GetSimpleRadius(),
                   kRoundedCorner1Radius);
   EXPECT_EQ(rounded_corner_bounds_1.rect(),
@@ -1307,9 +1330,10 @@
 
   // Since this node has 2 descendants that draw, it will have a rounded corner.
   effect_node = GetEffectNode(rounded_corner_layer.get());
-  gfx::RRectF rounded_corner_bounds_2 = effect_node->rounded_corner_bounds;
+  gfx::RRectF rounded_corner_bounds_2 =
+      effect_node->mask_filter_info.rounded_corner_bounds();
   EXPECT_TRUE(effect_node->HasRenderSurface());
-  EXPECT_FALSE(effect_node->is_fast_rounded_corner);
+  EXPECT_FALSE(effect_node->mask_filter_info.is_fast_rounded_corner());
   EXPECT_FLOAT_EQ(rounded_corner_bounds_2.GetSimpleRadius(),
                   kRoundedCorner2Radius);
   EXPECT_EQ(rounded_corner_bounds_2.rect(),
@@ -1336,7 +1360,8 @@
   // The offset from the origin of the render target is [0, 0] and the device
   // scale factor is 1.6.
   const gfx::RRectF actual_rrect_1 =
-      fast_rounded_corner_layer_impl->draw_properties().rounded_corner_bounds;
+      fast_rounded_corner_layer_impl->draw_properties()
+          .mask_filter_info.rounded_corner_bounds();
   gfx::RectF bounds_in_target_space = kRoundedCornerLayer1Bound;
   bounds_in_target_space.Scale(kDeviceScale);
   EXPECT_EQ(actual_rrect_1.rect(), bounds_in_target_space);
@@ -1347,9 +1372,9 @@
   // This should have the same rounded corner boudns as fast rounded corner
   // layer.
   const gfx::RRectF layer_1_rrect =
-      layer_1_impl->draw_properties().rounded_corner_bounds;
+      layer_1_impl->draw_properties().mask_filter_info.rounded_corner_bounds();
   const gfx::RRectF layer_2_rrect =
-      layer_2_impl->draw_properties().rounded_corner_bounds;
+      layer_2_impl->draw_properties().mask_filter_info.rounded_corner_bounds();
   EXPECT_EQ(actual_rrect_1, layer_1_rrect);
   EXPECT_EQ(actual_rrect_1, layer_2_rrect);
 
@@ -1359,13 +1384,16 @@
   // scale factor is 1.6 thus giving the target space origin of [64, 64]. The
   // corner radius is also scaled by a factor of 1.6.
   const gfx::RRectF actual_self_rrect_2 =
-      rounded_corner_layer_impl->draw_properties().rounded_corner_bounds;
+      rounded_corner_layer_impl->draw_properties()
+          .mask_filter_info.rounded_corner_bounds();
   EXPECT_TRUE(actual_self_rrect_2.IsEmpty());
 
   bounds_in_target_space = kRoundedCornerLayer2Bound;
   bounds_in_target_space.Scale(kDeviceScale);
   const gfx::RRectF actual_render_target_rrect_2 =
-      rounded_corner_layer_impl->render_target()->rounded_corner_bounds();
+      rounded_corner_layer_impl->render_target()
+          ->mask_filter_info()
+          .rounded_corner_bounds();
   EXPECT_EQ(actual_render_target_rrect_2.rect(), bounds_in_target_space);
   EXPECT_FLOAT_EQ(actual_render_target_rrect_2.GetSimpleRadius(),
                   kRoundedCorner2Radius * kDeviceScale);
@@ -1373,9 +1401,9 @@
   // Layer 3 and layer 4 should have no rounded corner bounds set as their
   // parent is a render surface.
   const gfx::RRectF layer_3_rrect =
-      layer_3_impl->draw_properties().rounded_corner_bounds;
+      layer_3_impl->draw_properties().mask_filter_info.rounded_corner_bounds();
   const gfx::RRectF layer_4_rrect =
-      layer_4_impl->draw_properties().rounded_corner_bounds;
+      layer_4_impl->draw_properties().mask_filter_info.rounded_corner_bounds();
   EXPECT_TRUE(layer_3_rrect.IsEmpty());
   EXPECT_TRUE(layer_4_rrect.IsEmpty());
 }
@@ -1462,9 +1490,10 @@
   // Since this layer has a descendant that has rounded corner, this node will
   // require a render surface.
   const EffectNode* effect_node = GetEffectNode(rounded_corner_layer_1.get());
-  gfx::RRectF rounded_corner_bounds_1 = effect_node->rounded_corner_bounds;
+  gfx::RRectF rounded_corner_bounds_1 =
+      effect_node->mask_filter_info.rounded_corner_bounds();
   EXPECT_TRUE(effect_node->HasRenderSurface());
-  EXPECT_FALSE(effect_node->is_fast_rounded_corner);
+  EXPECT_FALSE(effect_node->mask_filter_info.is_fast_rounded_corner());
   EXPECT_FLOAT_EQ(rounded_corner_bounds_1.GetSimpleRadius(),
                   kRoundedCorner1Radius);
   EXPECT_EQ(rounded_corner_bounds_1.rect(),
@@ -1473,9 +1502,10 @@
   // Since this layer has no descendant with rounded corner or drawable, it will
   // not have a render surface.
   effect_node = GetEffectNode(fast_rounded_corner_layer_2.get());
-  gfx::RRectF rounded_corner_bounds_2 = effect_node->rounded_corner_bounds;
+  gfx::RRectF rounded_corner_bounds_2 =
+      effect_node->mask_filter_info.rounded_corner_bounds();
   EXPECT_FALSE(effect_node->HasRenderSurface());
-  EXPECT_TRUE(effect_node->is_fast_rounded_corner);
+  EXPECT_TRUE(effect_node->mask_filter_info.is_fast_rounded_corner());
   EXPECT_FLOAT_EQ(rounded_corner_bounds_2.GetSimpleRadius(),
                   kRoundedCorner2Radius);
   EXPECT_EQ(rounded_corner_bounds_2.rect(),
@@ -1484,9 +1514,10 @@
   // Since this layer has 1 descendant with a rounded corner, it should have a
   // render surface.
   effect_node = GetEffectNode(rounded_corner_layer_3.get());
-  gfx::RRectF rounded_corner_bounds_3 = effect_node->rounded_corner_bounds;
+  gfx::RRectF rounded_corner_bounds_3 =
+      effect_node->mask_filter_info.rounded_corner_bounds();
   EXPECT_TRUE(effect_node->HasRenderSurface());
-  EXPECT_FALSE(effect_node->is_fast_rounded_corner);
+  EXPECT_FALSE(effect_node->mask_filter_info.is_fast_rounded_corner());
   EXPECT_FLOAT_EQ(rounded_corner_bounds_3.GetSimpleRadius(),
                   kRoundedCorner3Radius);
   EXPECT_EQ(rounded_corner_bounds_3.rect(),
@@ -1494,9 +1525,10 @@
 
   // Since this layer no descendants, it would no thave a render pass.
   effect_node = GetEffectNode(rounded_corner_layer_4.get());
-  gfx::RRectF rounded_corner_bounds_4 = effect_node->rounded_corner_bounds;
+  gfx::RRectF rounded_corner_bounds_4 =
+      effect_node->mask_filter_info.rounded_corner_bounds();
   EXPECT_FALSE(effect_node->HasRenderSurface());
-  EXPECT_FALSE(effect_node->is_fast_rounded_corner);
+  EXPECT_FALSE(effect_node->mask_filter_info.is_fast_rounded_corner());
   EXPECT_FLOAT_EQ(rounded_corner_bounds_4.GetSimpleRadius(),
                   kRoundedCorner4Radius);
   EXPECT_EQ(rounded_corner_bounds_4.rect(),
@@ -1523,13 +1555,16 @@
   // The offset from the origin of the render target is [5, 5] and the device
   // scale factor is 1.6 giving a total offset of [8, 8].
   const gfx::RRectF actual_self_rrect_1 =
-      rounded_corner_layer_impl_1->draw_properties().rounded_corner_bounds;
+      rounded_corner_layer_impl_1->draw_properties()
+          .mask_filter_info.rounded_corner_bounds();
   EXPECT_TRUE(actual_self_rrect_1.IsEmpty());
 
   gfx::RectF bounds_in_target_space = kRoundedCornerLayer1Bound;
   bounds_in_target_space.Scale(kDeviceScale);
   const gfx::RRectF actual_render_target_rrect_1 =
-      rounded_corner_layer_impl_1->render_target()->rounded_corner_bounds();
+      rounded_corner_layer_impl_1->render_target()
+          ->mask_filter_info()
+          .rounded_corner_bounds();
   EXPECT_EQ(actual_render_target_rrect_1.rect(), bounds_in_target_space);
   EXPECT_FLOAT_EQ(actual_render_target_rrect_1.GetSimpleRadius(),
                   kRoundedCorner1Radius * kDeviceScale);
@@ -1539,7 +1574,8 @@
   // The offset from the origin of the render target is [0, 0] and the device
   // scale factor is 1.6. The corner radius is also scaled by a factor of 1.6.
   const gfx::RRectF actual_self_rrect_2 =
-      fast_rounded_corner_layer_impl_2->draw_properties().rounded_corner_bounds;
+      fast_rounded_corner_layer_impl_2->draw_properties()
+          .mask_filter_info.rounded_corner_bounds();
   bounds_in_target_space = kRoundedCornerLayer2Bound;
   bounds_in_target_space.Scale(kDeviceScale);
   EXPECT_EQ(actual_self_rrect_2.rect(), bounds_in_target_space);
@@ -1552,13 +1588,16 @@
   // scale factor is 1.6 thus giving the target space origin of [64, 64]. The
   // corner radius is also scaled by a factor of 1.6.
   const gfx::RRectF actual_self_rrect_3 =
-      rounded_corner_layer_impl_3->draw_properties().rounded_corner_bounds;
+      rounded_corner_layer_impl_3->draw_properties()
+          .mask_filter_info.rounded_corner_bounds();
   EXPECT_TRUE(actual_self_rrect_3.IsEmpty());
 
   bounds_in_target_space = kRoundedCornerLayer3Bound;
   bounds_in_target_space.Scale(kDeviceScale);
   const gfx::RRectF actual_render_target_rrect_3 =
-      rounded_corner_layer_impl_3->render_target()->rounded_corner_bounds();
+      rounded_corner_layer_impl_3->render_target()
+          ->mask_filter_info()
+          .rounded_corner_bounds();
   EXPECT_EQ(actual_render_target_rrect_3.rect(), bounds_in_target_space);
   EXPECT_FLOAT_EQ(actual_render_target_rrect_3.GetSimpleRadius(),
                   kRoundedCorner3Radius * kDeviceScale);
@@ -1569,7 +1608,8 @@
   // scale factor is 1.6 thus giving the target space origin of [48, 0]. The
   // corner radius is also scaled by a factor of 1.6.
   const gfx::RRectF actual_self_rrect_4 =
-      rounded_corner_layer_impl_4->draw_properties().rounded_corner_bounds;
+      rounded_corner_layer_impl_4->draw_properties()
+          .mask_filter_info.rounded_corner_bounds();
   bounds_in_target_space = kRoundedCornerLayer4Bound;
   bounds_in_target_space.Scale(kDeviceScale);
   EXPECT_EQ(actual_self_rrect_4.rect(), bounds_in_target_space);
@@ -1658,9 +1698,10 @@
   // surface.
   const EffectNode* effect_node =
       GetEffectNode(fast_rounded_corner_layer_1.get());
-  gfx::RRectF rounded_corner_bounds_1 = effect_node->rounded_corner_bounds;
+  gfx::RRectF rounded_corner_bounds_1 =
+      effect_node->mask_filter_info.rounded_corner_bounds();
   EXPECT_TRUE(effect_node->HasRenderSurface());
-  EXPECT_TRUE(effect_node->is_fast_rounded_corner);
+  EXPECT_TRUE(effect_node->mask_filter_info.is_fast_rounded_corner());
   EXPECT_FLOAT_EQ(rounded_corner_bounds_1.GetSimpleRadius(),
                   kRoundedCorner1Radius);
   EXPECT_EQ(rounded_corner_bounds_1.rect(),
@@ -1669,9 +1710,10 @@
   // Since this layer has no descendant with rounded corner or drawable, it will
   // not have a render surface.
   effect_node = GetEffectNode(rounded_corner_layer_1.get());
-  gfx::RRectF rounded_corner_bounds_2 = effect_node->rounded_corner_bounds;
+  gfx::RRectF rounded_corner_bounds_2 =
+      effect_node->mask_filter_info.rounded_corner_bounds();
   EXPECT_FALSE(effect_node->HasRenderSurface());
-  EXPECT_FALSE(effect_node->is_fast_rounded_corner);
+  EXPECT_FALSE(effect_node->mask_filter_info.is_fast_rounded_corner());
   EXPECT_FLOAT_EQ(rounded_corner_bounds_2.GetSimpleRadius(),
                   kRoundedCorner2Radius);
   EXPECT_EQ(rounded_corner_bounds_2.rect(),
@@ -1680,9 +1722,10 @@
   // Since this layer has a descendant with rounded corner, it should have a
   // render surface.
   effect_node = GetEffectNode(rounded_corner_layer_2.get());
-  gfx::RRectF rounded_corner_bounds_3 = effect_node->rounded_corner_bounds;
+  gfx::RRectF rounded_corner_bounds_3 =
+      effect_node->mask_filter_info.rounded_corner_bounds();
   EXPECT_TRUE(effect_node->HasRenderSurface());
-  EXPECT_FALSE(effect_node->is_fast_rounded_corner);
+  EXPECT_FALSE(effect_node->mask_filter_info.is_fast_rounded_corner());
   EXPECT_FLOAT_EQ(rounded_corner_bounds_3.GetSimpleRadius(),
                   kRoundedCorner3Radius);
   EXPECT_EQ(rounded_corner_bounds_3.rect(),
@@ -1690,9 +1733,10 @@
 
   // Since this layer has no descendant, it does not need a render surface.
   effect_node = GetEffectNode(rounded_corner_layer_3.get());
-  gfx::RRectF rounded_corner_bounds_4 = effect_node->rounded_corner_bounds;
+  gfx::RRectF rounded_corner_bounds_4 =
+      effect_node->mask_filter_info.rounded_corner_bounds();
   EXPECT_FALSE(effect_node->HasRenderSurface());
-  EXPECT_FALSE(effect_node->is_fast_rounded_corner);
+  EXPECT_FALSE(effect_node->mask_filter_info.is_fast_rounded_corner());
   EXPECT_FLOAT_EQ(rounded_corner_bounds_4.GetSimpleRadius(),
                   kRoundedCorner4Radius);
   EXPECT_EQ(rounded_corner_bounds_4.rect(),
@@ -1719,14 +1763,16 @@
   // The offset from the origin of the render target is [5, 5] and the device
   // scale factor is 1.6.
   const gfx::RRectF actual_self_rrect_1 =
-      fast_rounded_corner_layer_impl_1->draw_properties().rounded_corner_bounds;
+      fast_rounded_corner_layer_impl_1->draw_properties()
+          .mask_filter_info.rounded_corner_bounds();
   EXPECT_TRUE(actual_self_rrect_1.IsEmpty());
 
   gfx::RectF bounds_in_target_space = kRoundedCornerLayer1Bound;
   bounds_in_target_space.Scale(kDeviceScale);
   const gfx::RRectF actual_render_target_rrect_1 =
       fast_rounded_corner_layer_impl_1->render_target()
-          ->rounded_corner_bounds();
+          ->mask_filter_info()
+          .rounded_corner_bounds();
   EXPECT_EQ(actual_render_target_rrect_1.rect(), bounds_in_target_space);
   EXPECT_FLOAT_EQ(actual_render_target_rrect_1.GetSimpleRadius(),
                   kRoundedCorner1Radius * kDeviceScale);
@@ -1736,7 +1782,8 @@
   // The offset from the origin of the render target is [0, 0] and the device
   // scale factor is 1.6. The corner radius is also scaled by a factor of 1.6.
   const gfx::RRectF actual_self_rrect_2 =
-      rounded_corner_layer_impl_1->draw_properties().rounded_corner_bounds;
+      rounded_corner_layer_impl_1->draw_properties()
+          .mask_filter_info.rounded_corner_bounds();
   bounds_in_target_space = kRoundedCornerLayer2Bound;
   bounds_in_target_space.Scale(kDeviceScale);
   EXPECT_EQ(actual_self_rrect_2.rect(), bounds_in_target_space);
@@ -1749,13 +1796,16 @@
   // scale factor is 1.6 thus giving the target space origin of [8, 8]. The
   // corner radius is also scaled by a factor of 1.6.
   const gfx::RRectF actual_self_rrect_3 =
-      rounded_corner_layer_impl_2->draw_properties().rounded_corner_bounds;
+      rounded_corner_layer_impl_2->draw_properties()
+          .mask_filter_info.rounded_corner_bounds();
   EXPECT_TRUE(actual_self_rrect_3.IsEmpty());
 
   bounds_in_target_space = kRoundedCornerLayer3Bound;
   bounds_in_target_space.Scale(kDeviceScale);
   const gfx::RRectF actual_render_target_rrect_3 =
-      rounded_corner_layer_impl_2->render_target()->rounded_corner_bounds();
+      rounded_corner_layer_impl_2->render_target()
+          ->mask_filter_info()
+          .rounded_corner_bounds();
   EXPECT_EQ(actual_render_target_rrect_3.rect(), bounds_in_target_space);
   EXPECT_FLOAT_EQ(actual_render_target_rrect_3.GetSimpleRadius(),
                   kRoundedCorner3Radius * kDeviceScale);
@@ -1766,7 +1816,8 @@
   // scale factor is 1.6 thus giving the target space origin of [0, 8]. The
   // corner radius is also scaled by a factor of 1.6.
   const gfx::RRectF actual_self_rrect_4 =
-      rounded_corner_layer_impl_3->draw_properties().rounded_corner_bounds;
+      rounded_corner_layer_impl_3->draw_properties()
+          .mask_filter_info.rounded_corner_bounds();
   bounds_in_target_space = kRoundedCornerLayer4Bound;
   bounds_in_target_space.Scale(kDeviceScale);
   EXPECT_EQ(actual_self_rrect_4.rect(), bounds_in_target_space);
diff --git a/chrome/BUILD.gn b/chrome/BUILD.gn
index 6571383c..d497a5e 100644
--- a/chrome/BUILD.gn
+++ b/chrome/BUILD.gn
@@ -1369,6 +1369,10 @@
     ]
   }
 
+  if (is_linux || is_chromeos) {
+    public_deps += [ "//chrome/browser/resources:webui_js_exception_resources" ]
+  }
+
   if (!is_android && !is_chromeos) {
     public_deps += [
       "//chrome/browser/resources:profile_picker_resources",
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index 760e114..72685ff2 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -208,6 +208,7 @@
   "java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/sharing/TwaSharingController.java",
   "java/src/org/chromium/chrome/browser/browserservices/ui/SharedActivityCoordinator.java",
   "java/src/org/chromium/chrome/browser/browserservices/ui/controller/CurrentPageVerifier.java",
+  "java/src/org/chromium/chrome/browser/browserservices/ui/controller/DisclosureController.java",
   "java/src/org/chromium/chrome/browser/browserservices/ui/controller/EmptyVerifier.java",
   "java/src/org/chromium/chrome/browser/browserservices/ui/controller/Verifier.java",
   "java/src/org/chromium/chrome/browser/browserservices/ui/controller/trustedwebactivity/ClientAppDataRecorder.java",
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantOverlayIntegrationTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantOverlayIntegrationTest.java
index 40de2ae..1f000ba 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantOverlayIntegrationTest.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantOverlayIntegrationTest.java
@@ -6,6 +6,7 @@
 
 import static androidx.test.espresso.Espresso.onView;
 import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.assertion.ViewAssertions.matches;
 import static androidx.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed;
 import static androidx.test.espresso.matcher.ViewMatchers.withContentDescription;
 import static androidx.test.espresso.matcher.ViewMatchers.withText;
@@ -15,12 +16,15 @@
 
 import static org.chromium.chrome.browser.autofill_assistant.AutofillAssistantUiTestUtil.checkElementExists;
 import static org.chromium.chrome.browser.autofill_assistant.AutofillAssistantUiTestUtil.checkElementOnScreen;
+import static org.chromium.chrome.browser.autofill_assistant.AutofillAssistantUiTestUtil.getBitmapFromView;
 import static org.chromium.chrome.browser.autofill_assistant.AutofillAssistantUiTestUtil.startAutofillAssistant;
 import static org.chromium.chrome.browser.autofill_assistant.AutofillAssistantUiTestUtil.tapElement;
 import static org.chromium.chrome.browser.autofill_assistant.AutofillAssistantUiTestUtil.waitUntil;
 import static org.chromium.chrome.browser.autofill_assistant.AutofillAssistantUiTestUtil.waitUntilViewMatchesCondition;
 
 import android.support.test.InstrumentationRegistry;
+import android.view.View;
+import android.view.ViewGroup;
 
 import androidx.test.filters.MediumTest;
 
@@ -30,13 +34,19 @@
 import org.junit.runner.RunWith;
 
 import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.chrome.autofill_assistant.R;
 import org.chromium.chrome.browser.autofill_assistant.proto.ActionProto;
+import org.chromium.chrome.browser.autofill_assistant.proto.BitmapDrawableProto;
 import org.chromium.chrome.browser.autofill_assistant.proto.ChipProto;
+import org.chromium.chrome.browser.autofill_assistant.proto.ClientDimensionProto;
+import org.chromium.chrome.browser.autofill_assistant.proto.ClientSettingsProto;
 import org.chromium.chrome.browser.autofill_assistant.proto.ConfigureUiStateProto;
 import org.chromium.chrome.browser.autofill_assistant.proto.ConfigureUiStateProto.OverlayBehavior;
+import org.chromium.chrome.browser.autofill_assistant.proto.DrawableProto;
 import org.chromium.chrome.browser.autofill_assistant.proto.ElementAreaProto;
 import org.chromium.chrome.browser.autofill_assistant.proto.ElementAreaProto.Rectangle;
 import org.chromium.chrome.browser.autofill_assistant.proto.FocusElementProto;
+import org.chromium.chrome.browser.autofill_assistant.proto.OverlayImageProto;
 import org.chromium.chrome.browser.autofill_assistant.proto.PromptProto;
 import org.chromium.chrome.browser.autofill_assistant.proto.PromptProto.Choice;
 import org.chromium.chrome.browser.autofill_assistant.proto.SelectorProto;
@@ -432,6 +442,61 @@
         assertThat(checkElementExists(mTestRule.getWebContents(), "touch_area_four"), is(true));
     }
 
+    /**
+     * Tests that an image works with an overlay
+     */
+    @Test
+    @MediumTest
+    public void testShowImageOnOverlay() throws Exception {
+        String redDotBase64Url =
+                "data:image/png;base64, iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==";
+        int imageSize = 50;
+        ClientSettingsProto clientSettings =
+                (ClientSettingsProto) ClientSettingsProto.newBuilder()
+                        .setOverlayImage(
+                                OverlayImageProto.newBuilder()
+                                        .setImageDrawable(DrawableProto.newBuilder().setBitmap(
+                                                BitmapDrawableProto.newBuilder()
+                                                        .setUrl(redDotBase64Url)
+                                                        .setWidth(ClientDimensionProto.newBuilder()
+                                                                          .setDp(imageSize))
+                                                        .setHeight(ClientDimensionProto.newBuilder()
+                                                                           .setDp(imageSize))))
+                                        .setImageSize(
+                                                ClientDimensionProto.newBuilder().setDp(imageSize)))
+                        .build();
+
+        AutofillAssistantTestScript script = new AutofillAssistantTestScript(
+                (SupportedScriptProto) SupportedScriptProto.newBuilder()
+                        .setPath("autofill_assistant_target_website.html")
+                        .setPresentation(PresentationProto.newBuilder().setAutostart(true).setChip(
+                                ChipProto.newBuilder().setText("Done")))
+                        .build(),
+                new ArrayList<>());
+        AutofillAssistantTestService testService =
+                new AutofillAssistantTestService(Collections.singletonList(script), clientSettings);
+        startAutofillAssistant(mTestRule.getActivity(), testService);
+
+        ViewGroup chromeCoordinatorView = mTestRule.getActivity().findViewById(R.id.coordinator);
+        View scrim = mTestRule.getActivity()
+                             .getRootUiCoordinatorForTesting()
+                             .getScrimCoordinator()
+                             .getViewForTesting();
+
+        onView(is(scrim)).check(matches(isCompletelyDisplayed()));
+        int image_center_x = scrim.getWidth() / 2;
+        int yTopContentOffset = mTestRule.getActivity()
+                                        .getRootUiCoordinatorForTesting()
+                                        .getBrowserControlsManager()
+                                        .getContentOffset();
+        int image_center_y = yTopContentOffset + imageSize / 2;
+
+        // Testing that central pixel of overlay image is different from (0,0) pixel
+        waitUntil(()
+                          -> getBitmapFromView(scrim).getPixel(image_center_x, image_center_y)
+                        != getBitmapFromView(scrim).getPixel(0, 0));
+    }
+
     private void runScript(AutofillAssistantTestScript script) {
         AutofillAssistantTestService testService =
                 new AutofillAssistantTestService(Collections.singletonList(script));
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiTestUtil.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiTestUtil.java
index 7a9e736..b97e0ea 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiTestUtil.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiTestUtil.java
@@ -12,6 +12,7 @@
 
 import android.content.Context;
 import android.graphics.Bitmap;
+import android.graphics.Canvas;
 import android.graphics.Rect;
 import android.graphics.Typeface;
 import android.graphics.drawable.ColorDrawable;
@@ -718,6 +719,22 @@
         return result.getString(0);
     }
 
+    /**
+     * Converts a view into a bitmap.
+     */
+    public static Bitmap getBitmapFromView(View view) {
+        Bitmap resultBitmap =
+                Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888);
+        Drawable backgroundDrawable = view.getBackground();
+        if (backgroundDrawable == null) {
+            return resultBitmap;
+        }
+        Canvas canvas = new Canvas(resultBitmap);
+        backgroundDrawable.draw(canvas);
+        view.draw(canvas);
+        return resultBitmap;
+    }
+
     private static String getElementSelectorString(String[] elementIds) {
         StringBuilder builder = new StringBuilder();
         builder.append("document");
diff --git a/chrome/android/features/cablev2_authenticator/java/src/org/chromium/chrome/browser/webauth/authenticator/CableAuthenticatorUI.java b/chrome/android/features/cablev2_authenticator/java/src/org/chromium/chrome/browser/webauth/authenticator/CableAuthenticatorUI.java
index e099c99..eab2cfe4 100644
--- a/chrome/android/features/cablev2_authenticator/java/src/org/chromium/chrome/browser/webauth/authenticator/CableAuthenticatorUI.java
+++ b/chrome/android/features/cablev2_authenticator/java/src/org/chromium/chrome/browser/webauth/authenticator/CableAuthenticatorUI.java
@@ -173,8 +173,8 @@
     }
 
     @Override
-    public void onDestroy() {
-        super.onDestroy();
+    public void onStop() {
+        super.onStop();
         mAuthenticator.close();
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ui/controller/DisclosureController.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ui/controller/DisclosureController.java
new file mode 100644
index 0000000..8a739871
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ui/controller/DisclosureController.java
@@ -0,0 +1,96 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.browserservices.ui.controller;
+
+import static org.chromium.chrome.browser.browserservices.ui.trustedwebactivity.TrustedWebActivityModel.DISCLOSURE_EVENTS_CALLBACK;
+import static org.chromium.chrome.browser.browserservices.ui.trustedwebactivity.TrustedWebActivityModel.DISCLOSURE_FIRST_TIME;
+import static org.chromium.chrome.browser.browserservices.ui.trustedwebactivity.TrustedWebActivityModel.DISCLOSURE_SCOPE;
+import static org.chromium.chrome.browser.browserservices.ui.trustedwebactivity.TrustedWebActivityModel.DISCLOSURE_STATE;
+import static org.chromium.chrome.browser.browserservices.ui.trustedwebactivity.TrustedWebActivityModel.DISCLOSURE_STATE_DISMISSED_BY_USER;
+import static org.chromium.chrome.browser.browserservices.ui.trustedwebactivity.TrustedWebActivityModel.DISCLOSURE_STATE_NOT_SHOWN;
+import static org.chromium.chrome.browser.browserservices.ui.trustedwebactivity.TrustedWebActivityModel.DISCLOSURE_STATE_SHOWN;
+import static org.chromium.chrome.browser.browserservices.ui.trustedwebactivity.TrustedWebActivityModel.PACKAGE_NAME;
+
+import androidx.annotation.CallSuper;
+import androidx.annotation.Nullable;
+
+import org.chromium.chrome.browser.browserservices.ui.trustedwebactivity.TrustedWebActivityModel;
+import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
+import org.chromium.chrome.browser.lifecycle.NativeInitObserver;
+
+/**
+ * Contains common implementation between WebappDisclosureController and
+ * TrustedWebActivityDisclosureController.
+ */
+public abstract class DisclosureController
+        implements NativeInitObserver, TrustedWebActivityModel.DisclosureEventsCallback {
+    private final TrustedWebActivityModel mModel;
+
+    public DisclosureController(TrustedWebActivityModel model,
+            ActivityLifecycleDispatcher lifecycleDispatcher, String packageName) {
+        mModel = model;
+
+        model.set(DISCLOSURE_EVENTS_CALLBACK, this);
+        model.set(PACKAGE_NAME, packageName);
+
+        lifecycleDispatcher.register(this);
+    }
+
+    @Override
+    public void onFinishNativeInitialization() {
+        // We want to show disclosure ASAP, which is limited by SnackbarManager requiring
+        // native.
+        if (shouldShowInCurrentState()) {
+            showIfNeeded();
+        }
+    }
+
+    @Override
+    @CallSuper
+    public void onDisclosureAccepted() {
+        mModel.set(DISCLOSURE_STATE, DISCLOSURE_STATE_DISMISSED_BY_USER);
+    }
+
+    @Override
+    @CallSuper
+    public void onDisclosureShown() {
+        mModel.set(DISCLOSURE_FIRST_TIME, false);
+    }
+
+    protected boolean shouldShowInCurrentState() {
+        return false;
+    }
+
+    /** Shows the disclosure if it is not already showing and hasn't been accepted. */
+    protected void showIfNeeded() {
+        if (!isShowing() && shouldShowDisclosure()) {
+            showDisclosure();
+        }
+    }
+
+    protected abstract boolean shouldShowDisclosure();
+
+    /** Is this the first time the user has seen the disclosure? */
+    protected abstract boolean isFirstTime();
+
+    protected void showDisclosure() {
+        mModel.set(DISCLOSURE_FIRST_TIME, isFirstTime());
+        mModel.set(DISCLOSURE_STATE, DISCLOSURE_STATE_SHOWN);
+    }
+
+    /** Dismisses the disclosure if it is showing. */
+    protected void dismiss() {
+        if (isShowing()) {
+            mModel.set(DISCLOSURE_STATE, DISCLOSURE_STATE_NOT_SHOWN);
+        }
+    }
+    protected boolean isShowing() {
+        return mModel.get(DISCLOSURE_STATE) == DISCLOSURE_STATE_SHOWN;
+    }
+
+    protected void setDisclosureScope(@Nullable String scope) {
+        mModel.set(DISCLOSURE_SCOPE, scope);
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ui/controller/trustedwebactivity/TrustedWebActivityDisclosureController.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ui/controller/trustedwebactivity/TrustedWebActivityDisclosureController.java
index 6c1c5a6..b122380 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ui/controller/trustedwebactivity/TrustedWebActivityDisclosureController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ui/controller/trustedwebactivity/TrustedWebActivityDisclosureController.java
@@ -4,23 +4,14 @@
 
 package org.chromium.chrome.browser.browserservices.ui.controller.trustedwebactivity;
 
-import static org.chromium.chrome.browser.browserservices.ui.trustedwebactivity.TrustedWebActivityModel.DISCLOSURE_EVENTS_CALLBACK;
-import static org.chromium.chrome.browser.browserservices.ui.trustedwebactivity.TrustedWebActivityModel.DISCLOSURE_FIRST_TIME;
-import static org.chromium.chrome.browser.browserservices.ui.trustedwebactivity.TrustedWebActivityModel.DISCLOSURE_SCOPE;
-import static org.chromium.chrome.browser.browserservices.ui.trustedwebactivity.TrustedWebActivityModel.DISCLOSURE_STATE;
-import static org.chromium.chrome.browser.browserservices.ui.trustedwebactivity.TrustedWebActivityModel.DISCLOSURE_STATE_DISMISSED_BY_USER;
-import static org.chromium.chrome.browser.browserservices.ui.trustedwebactivity.TrustedWebActivityModel.DISCLOSURE_STATE_NOT_SHOWN;
-import static org.chromium.chrome.browser.browserservices.ui.trustedwebactivity.TrustedWebActivityModel.DISCLOSURE_STATE_SHOWN;
-import static org.chromium.chrome.browser.browserservices.ui.trustedwebactivity.TrustedWebActivityModel.PACKAGE_NAME;
-
 import org.chromium.chrome.browser.browserservices.BrowserServicesStore;
 import org.chromium.chrome.browser.browserservices.TrustedWebActivityUmaRecorder;
 import org.chromium.chrome.browser.browserservices.ui.controller.CurrentPageVerifier;
 import org.chromium.chrome.browser.browserservices.ui.controller.CurrentPageVerifier.VerificationState;
 import org.chromium.chrome.browser.browserservices.ui.controller.CurrentPageVerifier.VerificationStatus;
+import org.chromium.chrome.browser.browserservices.ui.controller.DisclosureController;
 import org.chromium.chrome.browser.browserservices.ui.trustedwebactivity.TrustedWebActivityModel;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
-import org.chromium.chrome.browser.lifecycle.NativeInitObserver;
 
 import javax.inject.Inject;
 
@@ -28,10 +19,8 @@
  * Controls when Trusted Web Activity disclosure should be shown and hidden, reacts to interaction
  * with it.
  */
-public class TrustedWebActivityDisclosureController
-        implements NativeInitObserver, TrustedWebActivityModel.DisclosureEventsCallback {
+public class TrustedWebActivityDisclosureController extends DisclosureController {
     private final BrowserServicesStore mBrowserServicesStore;
-    private final TrustedWebActivityModel mModel;
     private final CurrentPageVerifier mCurrentPageVerifier;
     private final TrustedWebActivityUmaRecorder mRecorder;
     private final ClientPackageNameProvider mClientPackageNameProvider;
@@ -41,23 +30,20 @@
             TrustedWebActivityModel model, ActivityLifecycleDispatcher lifecycleDispatcher,
             CurrentPageVerifier currentPageVerifier, TrustedWebActivityUmaRecorder recorder,
             ClientPackageNameProvider clientPackageNameProvider) {
+        super(model, lifecycleDispatcher, clientPackageNameProvider.get());
         mBrowserServicesStore = browserServicesStore;
-        mModel = model;
         mCurrentPageVerifier = currentPageVerifier;
         mRecorder = recorder;
         mClientPackageNameProvider = clientPackageNameProvider;
-        model.set(DISCLOSURE_EVENTS_CALLBACK, this);
-        model.set(PACKAGE_NAME, mClientPackageNameProvider.get());
         currentPageVerifier.addVerificationObserver(this::onVerificationStatusChanged);
-        lifecycleDispatcher.register(this);
     }
 
     private void onVerificationStatusChanged() {
         if (shouldShowInCurrentState()) {
-            mModel.set(DISCLOSURE_SCOPE, mCurrentPageVerifier.getState().scope);
+            setDisclosureScope(mCurrentPageVerifier.getState().scope);
             showIfNeeded();
         } else {
-            mModel.set(DISCLOSURE_SCOPE, null);
+            setDisclosureScope(null);
             dismiss();
         }
     }
@@ -67,57 +53,32 @@
         mRecorder.recordDisclosureAccepted();
         mBrowserServicesStore.setUserAcceptedTwaDisclosureForPackage(
                 mClientPackageNameProvider.get());
-        mModel.set(DISCLOSURE_STATE, DISCLOSURE_STATE_DISMISSED_BY_USER);
+        super.onDisclosureAccepted();
     }
 
     @Override
     public void onDisclosureShown() {
+        mRecorder.recordDisclosureShown();
         mBrowserServicesStore.setUserSeenTwaDisclosureForPackage(mClientPackageNameProvider.get());
-        mModel.set(DISCLOSURE_FIRST_TIME, false);
+        super.onDisclosureShown();
     }
 
-    /** Shows the disclosure if it is not already showing and hasn't been accepted. */
-    private void showIfNeeded() {
-        if (!isShowing() && !wasDismissed()) {
-            mRecorder.recordDisclosureShown();
-            mModel.set(DISCLOSURE_FIRST_TIME, isFirstTime());
-            mModel.set(DISCLOSURE_STATE, DISCLOSURE_STATE_SHOWN);
-        }
-    }
-
-    /** Dismisses the disclosure if it is showing. */
-    private void dismiss() {
-        if (isShowing()) {
-            mModel.set(DISCLOSURE_STATE, DISCLOSURE_STATE_NOT_SHOWN);
-        }
-    }
-
-    /** Has a disclosure been dismissed for this client package before? */
-    private boolean wasDismissed() {
-        return mBrowserServicesStore.hasUserAcceptedTwaDisclosureForPackage(
+    @Override
+    protected boolean shouldShowDisclosure() {
+        /** Has a disclosure been dismissed for this client package before? */
+        return !mBrowserServicesStore.hasUserAcceptedTwaDisclosureForPackage(
                 mClientPackageNameProvider.get());
     }
 
-    /** Is this the first time the user has seen the disclosure? */
-    private boolean isFirstTime() {
+    @Override
+    protected boolean isFirstTime() {
         return !mBrowserServicesStore.hasUserSeenTwaDisclosureForPackage(
                 mClientPackageNameProvider.get());
     }
 
     @Override
-    public void onFinishNativeInitialization() {
-        // We want to show disclosure ASAP, which is limited by SnackbarManager requiring native.
-        if (shouldShowInCurrentState()) {
-            showIfNeeded();
-        }
-    }
-
-    private boolean shouldShowInCurrentState() {
+    protected boolean shouldShowInCurrentState() {
         VerificationState state = mCurrentPageVerifier.getState();
         return state != null && state.status != VerificationStatus.FAILURE;
     }
-
-    public boolean isShowing() {
-        return mModel.get(DISCLOSURE_STATE) == DISCLOSURE_STATE_SHOWN;
-    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ui/controller/webapps/WebappDisclosureController.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ui/controller/webapps/WebappDisclosureController.java
index 15dba99..7bf3864c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ui/controller/webapps/WebappDisclosureController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ui/controller/webapps/WebappDisclosureController.java
@@ -4,24 +4,16 @@
 
 package org.chromium.chrome.browser.browserservices.ui.controller.webapps;
 
-import static org.chromium.chrome.browser.browserservices.ui.trustedwebactivity.TrustedWebActivityModel.DISCLOSURE_EVENTS_CALLBACK;
-import static org.chromium.chrome.browser.browserservices.ui.trustedwebactivity.TrustedWebActivityModel.DISCLOSURE_STATE;
-import static org.chromium.chrome.browser.browserservices.ui.trustedwebactivity.TrustedWebActivityModel.DISCLOSURE_STATE_DISMISSED_BY_USER;
-import static org.chromium.chrome.browser.browserservices.ui.trustedwebactivity.TrustedWebActivityModel.DISCLOSURE_STATE_SHOWN;
-import static org.chromium.chrome.browser.browserservices.ui.trustedwebactivity.TrustedWebActivityModel.PACKAGE_NAME;
-
 import androidx.annotation.Nullable;
 
 import org.chromium.chrome.browser.app.ChromeActivity;
 import org.chromium.chrome.browser.browserservices.BrowserServicesIntentDataProvider;
+import org.chromium.chrome.browser.browserservices.ui.controller.DisclosureController;
 import org.chromium.chrome.browser.browserservices.ui.trustedwebactivity.TrustedWebActivityModel;
 import org.chromium.chrome.browser.dependency_injection.ActivityScope;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
-import org.chromium.chrome.browser.lifecycle.NativeInitObserver;
-import org.chromium.chrome.browser.webapps.WebApkExtras;
 import org.chromium.chrome.browser.webapps.WebappDataStorage;
 import org.chromium.chrome.browser.webapps.WebappDeferredStartupWithStorageHandler;
-import org.chromium.chrome.browser.webapps.WebappExtras;
 import org.chromium.chrome.browser.webapps.WebappRegistry;
 import org.chromium.components.webapk.lib.common.WebApkConstants;
 
@@ -37,26 +29,16 @@
  * next time the app is opened if it hasn't been acknowledged.
  */
 @ActivityScope
-public class WebappDisclosureController
-        implements NativeInitObserver, TrustedWebActivityModel.DisclosureEventsCallback {
+public class WebappDisclosureController extends DisclosureController {
     private final BrowserServicesIntentDataProvider mIntentDataProvider;
-    private final TrustedWebActivityModel mModel;
 
     @Inject
     public WebappDisclosureController(ChromeActivity<?> activity,
             BrowserServicesIntentDataProvider intentDataProvider, TrustedWebActivityModel model,
             WebappDeferredStartupWithStorageHandler deferredStartupWithStorageHandler,
             ActivityLifecycleDispatcher lifecycleDispatcher) {
+        super(model, lifecycleDispatcher, intentDataProvider.getClientPackageName());
         mIntentDataProvider = intentDataProvider;
-        mModel = model;
-
-        model.set(DISCLOSURE_EVENTS_CALLBACK, this);
-        WebApkExtras webApkExtras = mIntentDataProvider.getWebApkExtras();
-        if (webApkExtras != null && webApkExtras.webApkPackageName != null) {
-            model.set(PACKAGE_NAME, webApkExtras.webApkPackageName);
-        }
-
-        lifecycleDispatcher.register(this);
 
         deferredStartupWithStorageHandler.addTask((storage, didCreateStorage) -> {
             if (activity.isActivityFinishingOrDestroyed()) return;
@@ -65,76 +47,62 @@
         });
     }
 
-    public void onDeferredStartupWithStorage(
+    void onDeferredStartupWithStorage(
             @Nullable WebappDataStorage storage, boolean didCreateStorage) {
-        if (didCreateStorage) {
-            // Set force == true to indicate that we need to show a privacy disclosure for the newly
+        if (didCreateStorage && storage != null) {
+            // SetShowDisclosure to indicate that we need to show a privacy disclosure for the newly
             // installed unbound WebAPKs which have no storage yet. We can't simply default to a
             // showing if the storage has a default value as we don't want to show this disclosure
             // for pre-existing unbound WebAPKs.
-            maybeShowDisclosure(storage, true /* force */);
-        }
-    }
-
-    @Override
-    public void onFinishNativeInitialization() {
-        WebappExtras webappExtras = mIntentDataProvider.getWebappExtras();
-        WebappDataStorage storage = WebappRegistry.getInstance().getWebappDataStorage(
-                mIntentDataProvider.getWebappExtras().id);
-        if (storage != null) {
-            maybeShowDisclosure(storage, false /* force */);
+            storage.setShowDisclosure();
+            if (shouldShowInCurrentState()) {
+                showIfNeeded();
+            }
         }
     }
 
     @Override
     public void onDisclosureAccepted() {
-        WebappExtras webappExtras = mIntentDataProvider.getWebappExtras();
         WebappDataStorage storage = WebappRegistry.getInstance().getWebappDataStorage(
                 mIntentDataProvider.getWebappExtras().id);
-        if (storage != null) {
-            storage.clearShowDisclosure();
-        }
-        mModel.set(DISCLOSURE_STATE, DISCLOSURE_STATE_DISMISSED_BY_USER);
-    }
+        assert storage != null;
 
-    @Override
-    public void onDisclosureShown() {}
-
-    /**
-     * Shows the disclosure informing the user the Webapp is running in Chrome.
-     * @param storage Storage for the Webapp.
-     * @param force Whether to force showing the Snackbar (if no storage is available on start).
-     */
-    private void maybeShowDisclosure(WebappDataStorage storage, boolean force) {
-        if (storage == null) return;
-
-        // If forced we set the bit to show the disclosure. This persists to future instances.
-        if (force) storage.setShowDisclosure();
-
-        if (!isShowing() && shouldShowDisclosure(storage)) {
-            mModel.set(DISCLOSURE_STATE, DISCLOSURE_STATE_SHOWN);
-        }
+        storage.clearShowDisclosure();
+        super.onDisclosureAccepted();
     }
 
     /**
      * Restricts showing to unbound WebAPKs that haven't dismissed the disclosure.
-     * @param storage Storage for the Webapp.
      * @return boolean indicating whether to show the privacy disclosure.
      */
-    private boolean shouldShowDisclosure(WebappDataStorage storage) {
-        // Show only if the correct flag is set.
-        if (!storage.shouldShowDisclosure()) {
+    @Override
+    protected boolean shouldShowDisclosure() {
+        // Only show disclosure for unbound WebAPKs.
+        if (mIntentDataProvider.getClientPackageName() == null
+                || mIntentDataProvider.getClientPackageName().startsWith(
+                        WebApkConstants.WEBAPK_PACKAGE_PREFIX)) {
             return false;
         }
 
-        // Show for unbound WebAPKs.
-        WebApkExtras webApkExtras = mIntentDataProvider.getWebApkExtras();
-        return webApkExtras != null && webApkExtras.webApkPackageName != null
-                && !webApkExtras.webApkPackageName.startsWith(
-                        WebApkConstants.WEBAPK_PACKAGE_PREFIX);
+        WebappDataStorage storage = WebappRegistry.getInstance().getWebappDataStorage(
+                mIntentDataProvider.getWebappExtras().id);
+        if (storage == null) return false;
+
+        // Show only if the correct flag is set.
+        return storage.shouldShowDisclosure();
     }
 
-    public boolean isShowing() {
-        return mModel.get(DISCLOSURE_STATE) == DISCLOSURE_STATE_SHOWN;
+    @Override
+    protected boolean isFirstTime() {
+        // TODO(crbug.com/1128675): isFirstTime is used for showing notification disclosure for
+        // TWAs, not used in Webapk for now.
+        return false;
+    }
+
+    @Override
+    protected boolean shouldShowInCurrentState() {
+        // TODO(crbug.com/1128675): TWA hides snackbar when move out of verified origin, we should
+        // do the same for WebApk.
+        return true;
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChrome.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChrome.java
index e962c16..fa611a0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChrome.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChrome.java
@@ -105,7 +105,7 @@
                 if (mOverviewLayout != null) {
                     mOverviewLayout.setTabContentManager(manager);
                 }
-                mTabContentManagerSupplier.removeObserver(this);
+                tabContentManagerSupplier.removeObserver(this);
             }
         });
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/homepage/HomepageManager.java b/chrome/android/java/src/org/chromium/chrome/browser/homepage/HomepageManager.java
index d477d25..3f30323f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/homepage/HomepageManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/homepage/HomepageManager.java
@@ -12,9 +12,6 @@
 import org.chromium.base.ObserverList;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.metrics.RecordUserAction;
-import org.chromium.chrome.browser.flags.CachedFeatureFlags;
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
-import org.chromium.chrome.browser.homepage.settings.HomepageMetricsEnums.HomeButtonPreferenceState;
 import org.chromium.chrome.browser.homepage.settings.HomepageMetricsEnums.HomepageLocationType;
 import org.chromium.chrome.browser.ntp.NewTabPage;
 import org.chromium.chrome.browser.partnercustomizations.PartnerBrowserCustomizations;
@@ -175,9 +172,6 @@
      */
     public void setPrefHomepageEnabled(boolean enabled) {
         mSharedPreferencesManager.writeBoolean(ChromePreferenceKeys.HOMEPAGE_ENABLED, enabled);
-        RecordHistogram.recordBooleanHistogram(
-                "Settings.ShowHomeButtonPreferenceStateChanged", enabled);
-        recordHomeButtonPreferenceState();
         notifyHomepageUpdated();
     }
 
@@ -238,7 +232,6 @@
         }
 
         if (wasUseDefaultUri != useDefaultUri) {
-            recordHomepageIsCustomized(!useDefaultUri);
             mSharedPreferencesManager.writeBoolean(
                     ChromePreferenceKeys.HOMEPAGE_USE_DEFAULT_URI, useDefaultUri);
         }
@@ -253,31 +246,6 @@
     }
 
     /**
-     * Get the homepage button preference state.
-     */
-    public static void recordHomeButtonPreferenceState() {
-        if (!CachedFeatureFlags.isEnabled(ChromeFeatureList.HOMEPAGE_LOCATION_POLICY)) {
-            RecordHistogram.recordBooleanHistogram(
-                    "Settings.ShowHomeButtonPreferenceState", HomepageManager.isHomepageEnabled());
-            return;
-        }
-
-        int state = HomeButtonPreferenceState.USER_DISABLED;
-        if (HomepagePolicyManager.isHomepageManagedByPolicy()) {
-            state = HomeButtonPreferenceState.MANAGED_ENABLED;
-        } else if (isHomepageEnabled()) {
-            state = HomeButtonPreferenceState.USER_ENABLED;
-        }
-
-        RecordHistogram.recordEnumeratedHistogram("Settings.ShowHomeButtonPreferenceStateManaged",
-                state, HomeButtonPreferenceState.NUM_ENTRIES);
-    }
-
-    public static void recordHomepageIsCustomized(boolean isCustomized) {
-        RecordHistogram.recordBooleanHistogram("Settings.HomePageIsCustomized", isCustomized);
-    }
-
-    /**
      * Record histogram "Settings.Homepage.LocationType" with the current homepage location type.
      */
     public static void recordHomepageLocationTypeIfEnabled() {
@@ -320,11 +288,6 @@
     @Override
     public void onHomepagePolicyUpdate() {
         notifyHomepageUpdated();
-
-        boolean isPolicyEnabled = HomepagePolicyManager.isHomepageManagedByPolicy();
-        if (isPolicyEnabled) {
-            recordHomepageIsCustomized(false);
-        }
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/homepage/settings/HomepageMetricsEnums.java b/chrome/android/java/src/org/chromium/chrome/browser/homepage/settings/HomepageMetricsEnums.java
index 2d95728..ea2ce2d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/homepage/settings/HomepageMetricsEnums.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/homepage/settings/HomepageMetricsEnums.java
@@ -16,25 +16,6 @@
     private HomepageMetricsEnums() {}
 
     /**
-     * Possible states for HomeButton. Used for Histogram
-     * Settings.ShowHomeButtonPreferenceStateManaged. Currently {@link
-     * HomeButtonPreferenceState.MANAGED_DISABLED } is not used.
-     *
-     * These values are persisted to logs, and should therefore never be renumbered nor reused.
-     */
-    @IntDef({HomeButtonPreferenceState.USER_DISABLED, HomeButtonPreferenceState.USER_ENABLED,
-            HomeButtonPreferenceState.MANAGED_DISABLED, HomeButtonPreferenceState.MANAGED_ENABLED})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface HomeButtonPreferenceState {
-        int USER_DISABLED = 0;
-        int USER_ENABLED = 1;
-        int MANAGED_DISABLED = 2;
-        int MANAGED_ENABLED = 3;
-
-        int NUM_ENTRIES = 4;
-    }
-
-    /**
      * Possible location type for homepage. Used for Histogram "Settings.Homepage.LocationType"
      * recorded in {@link HomepageManager#recordHomepageLocationType()}.
      *
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/init/ProcessInitializationHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/init/ProcessInitializationHandler.java
index acbdea0..c611668 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/init/ProcessInitializationHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/init/ProcessInitializationHandler.java
@@ -342,8 +342,6 @@
         deferredStartupHandler.addDeferredTask(new Runnable() {
             @Override
             public void run() {
-                HomepageManager.recordHomeButtonPreferenceState();
-                HomepageManager.recordHomepageIsCustomized(HomepageManager.isHomepageCustomized());
                 HomepageManager.recordHomepageLocationTypeIfEnabled();
             }
         });
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/RecentTabsManager.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/RecentTabsManager.java
index 143e75e..5ef015b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/RecentTabsManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/RecentTabsManager.java
@@ -7,7 +7,6 @@
 import android.content.Context;
 
 import androidx.annotation.IntDef;
-import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
 import org.chromium.base.metrics.RecordUserAction;
@@ -66,8 +65,6 @@
 
     private static final int RECENTLY_CLOSED_MAX_TAB_COUNT = 5;
 
-    private static @Nullable @PromoState Integer sPromoStateForTests;
-
     private static RecentlyClosedTabManager sRecentlyClosedTabManagerForTests;
 
     private final Profile mProfile;
@@ -362,10 +359,6 @@
      */
     @PromoState
     int getPromoType() {
-        if (sPromoStateForTests != null) {
-            return sPromoStateForTests;
-        }
-
         if (!mSignInManager.getIdentityManager().hasPrimaryAccount()) {
             if (!mSignInManager.isSignInAllowed()) {
                 return PromoState.PROMO_NONE;
@@ -445,16 +438,6 @@
         });
     }
 
-    /**
-     * Forces the promo state to a particular value for testing purposes.
-     * @param promoState The promo state to which the manager will be set to.
-     * TODO(https://crbug.com/1123478): Create a different method to enforce promo state.
-     */
-    @VisibleForTesting
-    static void forcePromoStateForTests(@Nullable @PromoState Integer promoState) {
-        sPromoStateForTests = promoState;
-    }
-
     @VisibleForTesting
     public static void setRecentlyClosedTabManagerForTests(RecentlyClosedTabManager manager) {
         sRecentlyClosedTabManagerForTests = manager;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentAppService.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentAppService.java
index 2f2e458..3f514a9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentAppService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentAppService.java
@@ -46,6 +46,12 @@
         mFactories.add(factory);
     }
 
+    /** Resets the instance, used by //clank tests. */
+    @VisibleForTesting
+    public void resetForTest() {
+        sInstance = null;
+    }
+
     // PaymentAppFactoryInterface implementation.
     @Override
     public void create(PaymentAppFactoryDelegate delegate) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/ConfirmImportSyncDataDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/ConfirmImportSyncDataDialog.java
index 4d7b810..0b04684a2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/ConfirmImportSyncDataDialog.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/ConfirmImportSyncDataDialog.java
@@ -107,22 +107,36 @@
         mConfirmImportOption.setRadioButtonGroup(radioGroup);
         mKeepSeparateOption.setRadioButtonGroup(radioGroup);
 
-        SigninManager signinManager = IdentityServicesProvider.get().getSigninManager(
-                Profile.getLastUsedRegularProfile());
+        boolean isManagedAccount = IdentityServicesProvider.get()
+                                           .getSigninManager(Profile.getLastUsedRegularProfile())
+                                           .getManagementDomain()
+                != null;
+        final AlertDialog alertDialog =
+                new AlertDialog.Builder(getActivity(), R.style.Theme_Chromium_AlertDialog)
+                        .setPositiveButton(R.string.continue_button, this)
+                        .setNegativeButton(R.string.cancel, this)
+                        .setView(v)
+                        .create();
+        // For non-managed accounts, the confirmation button starts out disabled, since none of the
+        // options are chosen by default.
+        alertDialog.setOnShowListener(dialog
+                -> alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(isManagedAccount));
+
         // If the account is managed, disallow merging information.
-        if (signinManager.getManagementDomain() != null) {
+        if (isManagedAccount) {
             mKeepSeparateOption.setChecked(true);
             mConfirmImportOption.setOnClickListener(
                     view -> ManagedPreferencesUtils.showManagedByAdministratorToast(getActivity()));
         } else {
-            mConfirmImportOption.setChecked(true);
+            // The confirmation button gets enabled as soon as either of the radio button options
+            // was selected.
+            mConfirmImportOption.setOnCheckedChangeListener(radioButton
+                    -> alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(true));
+            mKeepSeparateOption.setOnCheckedChangeListener(radioButton
+                    -> alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(true));
         }
 
-        return new AlertDialog.Builder(getActivity(), R.style.Theme_Chromium_AlertDialog)
-                .setPositiveButton(R.string.continue_button, this)
-                .setNegativeButton(R.string.cancel, this)
-                .setView(v)
-                .create();
+        return alertDialog;
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/account_picker/AccountConsistencyPromoAction.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/account_picker/AccountConsistencyPromoAction.java
index 4bbea88f..21ce1ae 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/account_picker/AccountConsistencyPromoAction.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/account_picker/AccountConsistencyPromoAction.java
@@ -31,6 +31,7 @@
         AccountConsistencyPromoAction.DISMISSED_OTHER,
         AccountConsistencyPromoAction.AUTH_ERROR_SHOWN,
         AccountConsistencyPromoAction.GENERIC_ERROR_SHOWN,
+        AccountConsistencyPromoAction.DISMISSED_BUTTON,
 })
 @Retention(RetentionPolicy.SOURCE)
 public @interface AccountConsistencyPromoAction {
@@ -109,5 +110,10 @@
      */
     int GENERIC_ERROR_SHOWN = 13;
 
-    int MAX = 14;
+    /**
+     * User has dismissed the promo by tapping on the dismissal button in the bottom sheet.
+     */
+    int DISMISSED_BUTTON = 14;
+
+    int MAX = 15;
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/account_picker/AccountPickerBottomSheetMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/account_picker/AccountPickerBottomSheetMediator.java
index 415ded2..48673df 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/account_picker/AccountPickerBottomSheetMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/account_picker/AccountPickerBottomSheetMediator.java
@@ -7,6 +7,7 @@
 import android.accounts.Account;
 import android.content.Context;
 import android.text.TextUtils;
+import android.view.View.OnClickListener;
 
 import androidx.annotation.Nullable;
 
@@ -50,8 +51,13 @@
         mProfileDataCache = new ProfileDataCache(
                 context, context.getResources().getDimensionPixelSize(R.dimen.user_picture_size));
 
-        mModel = AccountPickerBottomSheetProperties.createModel(this::onSelectedAccountClicked,
-                this::onContinueAsClicked, dismissBottomSheetRunnable);
+        OnClickListener onDismissClicked = v -> {
+            AccountPickerDelegate.recordAccountConsistencyPromoAction(
+                    AccountConsistencyPromoAction.DISMISSED_BUTTON);
+            dismissBottomSheetRunnable.run();
+        };
+        mModel = AccountPickerBottomSheetProperties.createModel(
+                this::onSelectedAccountClicked, this::onContinueAsClicked, onDismissClicked);
         mProfileDataCache.addObserver(mProfileDataSourceObserver);
 
         mAccountManagerFacade = AccountManagerFacadeProvider.getInstance();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/account_picker/AccountPickerBottomSheetProperties.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/account_picker/AccountPickerBottomSheetProperties.java
index 2e2e7ade..455e2180 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/account_picker/AccountPickerBottomSheetProperties.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/account_picker/AccountPickerBottomSheetProperties.java
@@ -130,12 +130,12 @@
      * state {@link ViewState#NO_ACCOUNTS}.
      */
     static PropertyModel createModel(Runnable onSelectedAccountClicked,
-            Runnable onContinueAsClicked, Runnable onDismissClicked) {
+            Runnable onContinueAsClicked, OnClickListener onDismissClicked) {
         return new PropertyModel.Builder(ALL_KEYS)
                 .with(ON_SELECTED_ACCOUNT_CLICKED, v -> onSelectedAccountClicked.run())
                 .with(SELECTED_ACCOUNT_DATA, null)
                 .with(ON_CONTINUE_AS_CLICKED, v -> onContinueAsClicked.run())
-                .with(ON_DISMISS_CLICKED, v -> onDismissClicked.run())
+                .with(ON_DISMISS_CLICKED, onDismissClicked)
                 .with(VIEW_STATE, ViewState.NO_ACCOUNTS)
                 .build();
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/ProfileSyncService.java b/chrome/android/java/src/org/chromium/chrome/browser/sync/ProfileSyncService.java
index 83b9815..24fe0cb 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/sync/ProfileSyncService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/ProfileSyncService.java
@@ -498,16 +498,6 @@
                 mNativeProfileSyncServiceAndroid, ProfileSyncService.this);
     }
 
-    /**
-     * Turns on encryption of all data types. This only takes effect after sync configuration is
-     * completed and setChosenDataTypes() is invoked.
-     */
-    public void enableEncryptEverything() {
-        assert isEngineInitialized();
-        ProfileSyncServiceJni.get().enableEncryptEverything(
-                mNativeProfileSyncServiceAndroid, ProfileSyncService.this);
-    }
-
     public void setEncryptionPassphrase(String passphrase) {
         assert isEngineInitialized();
         ProfileSyncServiceJni.get().setEncryptionPassphrase(
@@ -675,8 +665,6 @@
                 long nativeProfileSyncServiceAndroid, ProfileSyncService caller);
         boolean isTransportStateActive(
                 long nativeProfileSyncServiceAndroid, ProfileSyncService caller);
-        void enableEncryptEverything(
-                long nativeProfileSyncServiceAndroid, ProfileSyncService caller);
         boolean isPassphraseRequiredForPreferredDataTypes(
                 long nativeProfileSyncServiceAndroid, ProfileSyncService caller);
         boolean isTrustedVaultKeyRequired(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/ManageSyncSettings.java b/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/ManageSyncSettings.java
index 2838a12..c437801 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/ManageSyncSettings.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/ManageSyncSettings.java
@@ -525,7 +525,6 @@
             // If the engine was shut down since the dialog was opened, do nothing.
             return;
         }
-        mProfileSyncService.enableEncryptEverything();
         mProfileSyncService.setEncryptionPassphrase(passphrase);
         // Save the current state of data types - this tells the sync engine to
         // apply our encryption configuration changes.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelJniBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelJniBridge.java
index a6c2c32..ff1412d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelJniBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelJniBridge.java
@@ -11,6 +11,7 @@
 
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.NativeMethods;
+import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabLaunchType;
@@ -214,20 +215,26 @@
     private static void flushTabSwitchLatencyMetric(boolean perceived) {
         if (sTabSwitchStartTime <= 0) return;
         final long ms = SystemClock.uptimeMillis() - sTabSwitchStartTime;
+        String baseHistogram;
         switch (sTabSelectionType) {
             case TabSelectionType.FROM_CLOSE:
-                TabModelJniBridgeJni.get().logFromCloseMetric(ms, perceived);
+                baseHistogram = "Tabs.SwitchFromCloseLatency";
                 break;
             case TabSelectionType.FROM_EXIT:
-                TabModelJniBridgeJni.get().logFromExitMetric(ms, perceived);
+                baseHistogram = "Tabs.SwitchFromExitLatency";
                 break;
             case TabSelectionType.FROM_NEW:
-                TabModelJniBridgeJni.get().logFromNewMetric(ms, perceived);
+                baseHistogram = "Tabs.SwitchFromNewLatency";
                 break;
             case TabSelectionType.FROM_USER:
-                TabModelJniBridgeJni.get().logFromUserMetric(ms, perceived);
+                baseHistogram = "Tabs.SwitchFromUserLatency";
                 break;
+            default:
+                assert false;
+                return;
         }
+        String histogramSuffix = perceived ? "_Perceived" : "_Actual";
+        RecordHistogram.recordTimesHistogram(baseHistogram + histogramSuffix, ms);
     }
 
     @NativeMethods
@@ -238,11 +245,5 @@
                 long nativeTabModelJniBridge, TabModelJniBridge caller);
         void destroy(long nativeTabModelJniBridge, TabModelJniBridge caller);
         void tabAddedToModel(long nativeTabModelJniBridge, TabModelJniBridge caller, Tab tab);
-
-        // Methods for tab switch latency metrics.
-        void logFromCloseMetric(long ms, boolean perceived);
-        void logFromExitMetric(long ms, boolean perceived);
-        void logFromNewMetric(long ms, boolean perceived);
-        void logFromUserMetric(long ms, boolean perceived);
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/TabSwitcherModeTTCoordinatorPhone.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/TabSwitcherModeTTCoordinatorPhone.java
index 3d21e38..8f7be496 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/TabSwitcherModeTTCoordinatorPhone.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/TabSwitcherModeTTCoordinatorPhone.java
@@ -4,13 +4,9 @@
 
 package org.chromium.chrome.browser.toolbar.top;
 
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
 import android.view.View;
 import android.view.ViewStub;
-
 import androidx.annotation.Nullable;
-
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.incognito.IncognitoUtils;
@@ -129,71 +125,7 @@
         if (mTabSwitcherModeToolbar != null) {
             mTabSwitcherModeToolbar.setTabModelSelector(selector);
         }
-    }
 
-    /**
-     * @param provider The provider used to determine incognito state.
-     */
-    void setIncognitoStateProvider(IncognitoStateProvider provider) {
-        mIncognitoStateProvider = provider;
-        if (mTabSwitcherModeToolbar != null) {
-            mTabSwitcherModeToolbar.setIncognitoStateProvider(provider);
-        }
-    }
-
-    /** Called when accessibility status changes. */
-    void onAccessibilityStatusChanged(boolean enabled) {
-        mAccessibilityEnabled = enabled;
-        if (mTabSwitcherModeToolbar != null) {
-            mTabSwitcherModeToolbar.onAccessibilityStatusChanged(enabled);
-        }
-    }
-
-    void setTabSwitcherToolbarVisibility(boolean shouldShowTabSwitcherToolbar) {
-        if (mTabSwitcherModeToolbar == null
-                || (mTabSwitcherModeToolbar.getVisibility() == View.VISIBLE)
-                        == shouldShowTabSwitcherToolbar) {
-            return;
-        }
-
-        final float targetAlpha = shouldShowTabSwitcherToolbar ? 1.0f : 0.0f;
-        mTabSwitcherModeToolbar.animate()
-                .alpha(targetAlpha)
-                .setDuration(TopToolbarCoordinator.TAB_SWITCHER_MODE_NORMAL_ANIMATION_DURATION_MS)
-                .setListener(new AnimatorListenerAdapter() {
-                    @Override
-                    public void onAnimationStart(Animator animation) {
-                        if (shouldShowTabSwitcherToolbar) {
-                            mTabSwitcherModeToolbar.setVisibility(View.VISIBLE);
-                        }
-                    }
-
-                    @Override
-                    public void onAnimationEnd(Animator animation) {
-                        if (!shouldShowTabSwitcherToolbar) {
-                            mTabSwitcherModeToolbar.setVisibility(View.GONE);
-                        }
-                    }
-                });
-    }
-
-    private void initializeTabSwitcherToolbar() {
-        mTabSwitcherModeToolbar = (TabSwitcherModeTTPhone) mTabSwitcherToolbarStub.inflate();
-        mMenuButtonCoordinator.setMenuButton(
-                mTabSwitcherModeToolbar.findViewById(R.id.menu_button_wrapper));
-
-        // It's expected that these properties are set by the time the tab switcher is entered.
-        assert mTabSwitcherListener != null;
-        mTabSwitcherModeToolbar.setOnTabSwitcherClickHandler(mTabSwitcherListener);
-
-        assert mNewTabListener != null;
-        mTabSwitcherModeToolbar.setOnNewTabClickHandler(mNewTabListener);
-
-        assert mTabCountProvider != null;
-        mTabSwitcherModeToolbar.setTabCountProvider(mTabCountProvider);
-
-        assert mTabModelSelector != null;
-        mTabSwitcherModeToolbar.setTabModelSelector(mTabModelSelector);
         if (isNewTabVariationEnabled()) {
             mTabModelObserver = new TabModelObserver() {
                 @Override
@@ -216,17 +148,64 @@
 
                 private void updateIncognitoTabsCount() {
                     int incognitoTabsCount = mTabModelSelector.getModel(true).getCount();
-                    mTabSwitcherModeToolbar.onIncognitoTabsCountChanged(incognitoTabsCount);
+                    if (mTabSwitcherModeToolbar != null) {
+                        mTabSwitcherModeToolbar.onIncognitoTabsCountChanged(incognitoTabsCount);
+                    }
                 }
             };
             TabModel incognitoTabModel = mTabModelSelector.getModel(true);
             incognitoTabModel.addObserver(mTabModelObserver);
-            mTabSwitcherModeToolbar.onIncognitoTabsCountChanged(incognitoTabModel.getCount());
+            if (mTabSwitcherModeToolbar != null) {
+                mTabSwitcherModeToolbar.onIncognitoTabsCountChanged(incognitoTabModel.getCount());
+            }
         }
+    }
+
+    /**
+     * @param provider The provider used to determine incognito state.
+     */
+    void setIncognitoStateProvider(IncognitoStateProvider provider) {
+        mIncognitoStateProvider = provider;
+        if (mTabSwitcherModeToolbar != null) {
+            mTabSwitcherModeToolbar.setIncognitoStateProvider(provider);
+        }
+    }
+
+    /** Called when accessibility status changes. */
+    void onAccessibilityStatusChanged(boolean enabled) {
+        mAccessibilityEnabled = enabled;
+        if (mTabSwitcherModeToolbar != null) {
+            mTabSwitcherModeToolbar.onAccessibilityStatusChanged(enabled);
+        }
+    }
+
+    private void initializeTabSwitcherToolbar() {
+        mTabSwitcherModeToolbar = (TabSwitcherModeTTPhone) mTabSwitcherToolbarStub.inflate();
+        mMenuButtonCoordinator.setMenuButton(
+                mTabSwitcherModeToolbar.findViewById(R.id.menu_button_wrapper));
+
+        // It's expected that these properties are set by the time the tab switcher is entered.
+        assert mTabSwitcherListener != null;
+        mTabSwitcherModeToolbar.setOnTabSwitcherClickHandler(mTabSwitcherListener);
+
+        assert mNewTabListener != null;
+        mTabSwitcherModeToolbar.setOnNewTabClickHandler(mNewTabListener);
+
+        assert mTabCountProvider != null;
+        mTabSwitcherModeToolbar.setTabCountProvider(mTabCountProvider);
+
+        assert mTabModelSelector != null;
+        mTabSwitcherModeToolbar.setTabModelSelector(mTabModelSelector);
 
         assert mIncognitoStateProvider != null;
         mTabSwitcherModeToolbar.setIncognitoStateProvider(mIncognitoStateProvider);
 
+        if (isNewTabVariationEnabled()) {
+            int incognitoTabsCount =
+                    mTabModelSelector == null ? 0 : mTabModelSelector.getModel(true).getCount();
+            mTabSwitcherModeToolbar.onIncognitoTabsCountChanged(incognitoTabsCount);
+        }
+
         if (mAccessibilityEnabled) {
             mTabSwitcherModeToolbar.onAccessibilityStatusChanged(mAccessibilityEnabled);
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappIntentDataProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappIntentDataProvider.java
index fde8a1a5..2e35f4c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappIntentDataProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappIntentDataProvider.java
@@ -66,6 +66,15 @@
 
     @Override
     @Nullable
+    public String getClientPackageName() {
+        if (mWebApkExtras != null) {
+            return mWebApkExtras.webApkPackageName;
+        }
+        return null;
+    }
+
+    @Override
+    @Nullable
     public String getUrlToLoad() {
         return mWebappExtras.url;
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkTest.java
index 80e8a31..c6badbf69 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkTest.java
@@ -51,7 +51,6 @@
 import org.chromium.base.test.params.ParameterizedRunner;
 import org.chromium.base.test.util.CallbackHelper;
 import org.chromium.base.test.util.CommandLineFlags;
-import org.chromium.base.test.util.DisableIf;
 import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.Feature;
 import org.chromium.base.test.util.FlakyTest;
@@ -259,7 +258,6 @@
 
     @Test
     @SmallTest
-    @DisableIf.Build(sdk_is_less_than = 21, message = "crbug.com/807807")
     public void testAddBookmark() {
         mActivityTestRule.loadUrl(mTestPage);
         // Check partner bookmarks are lazily loaded.
@@ -290,7 +288,6 @@
 
     @Test
     @SmallTest
-    @DisableIf.Build(sdk_is_less_than = 21, message = "crbug.com/807807")
     public void testAddBookmarkSnackbar() {
         mActivityTestRule.loadUrl(mTestPage);
         // Check partner bookmarks are lazily loaded.
@@ -323,7 +320,6 @@
 
     @Test
     @SmallTest
-    @DisableIf.Build(sdk_is_less_than = 21, message = "crbug.com/807807")
     public void testAddBookmarkToOtherFolder() {
         mActivityTestRule.loadUrl(mTestPage);
         readPartnerBookmarks();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/homepage/HomepagePolicyIntegrationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/homepage/HomepagePolicyIntegrationTest.java
index da2279b..ffc54c8 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/homepage/HomepagePolicyIntegrationTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/homepage/HomepagePolicyIntegrationTest.java
@@ -28,7 +28,6 @@
 import org.chromium.chrome.browser.ChromeTabbedActivity;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
-import org.chromium.chrome.browser.homepage.settings.HomepageMetricsEnums.HomeButtonPreferenceState;
 import org.chromium.chrome.browser.homepage.settings.HomepageMetricsEnums.HomepageLocationType;
 import org.chromium.chrome.browser.homepage.settings.HomepageSettings;
 import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
@@ -67,9 +66,6 @@
     public static final String TEST_URL = "http://127.0.0.1:8000/foo.html";
     public static final String GOOGLE_HTML = "/chrome/test/data/android/google.html";
 
-    private static final String METRICS_HOME_BUTTON_STATE_ENUM =
-            "Settings.ShowHomeButtonPreferenceStateManaged";
-    private static final String METRICS_HOMEPAGE_IS_CUSTOMIZED = "Settings.HomePageIsCustomized";
     private static final String METRICS_HOMEPAGE_LOCATION_TYPE = "Settings.Homepage.LocationType";
 
     private EmbeddedTestServer mTestServer;
@@ -119,18 +115,6 @@
                 SharedPreferencesManager.getInstance().readString(
                         ChromePreferenceKeys.HOMEPAGE_LOCATION_POLICY, ""));
 
-        // METRICS_HOMEPAGE_IS_CUSTOMIZED Should be collected twice it is called in:
-        // 1. ProcessInitializationHandler#handleDeferredStartupTasksInitialization;
-        // 2. HomepageManager#onHomepagePolicyUpdate, which will be called when native initialized.
-        Assert.assertEquals(
-                "Settings.HomepageIsCustomized should be recorded twice when policy enabled", 2,
-                RecordHistogram.getHistogramTotalCountForTesting(METRICS_HOMEPAGE_IS_CUSTOMIZED));
-
-        // METRICS_HOME_BUTTON_STATE_ENUM should be collected once in deferred start up tasks.
-        Assert.assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        METRICS_HOME_BUTTON_STATE_ENUM, HomeButtonPreferenceState.MANAGED_ENABLED));
-
         // METRICS_HOMEPAGE_LOCATION_TYPE is recorded once in deferred start up tasks.
         Assert.assertEquals("Settings.Homepage.LocationType should record POLICY_OTHER once.", 1,
                 RecordHistogram.getHistogramValueCountForTesting(
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/RecentTabsPageTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/RecentTabsPageTest.java
index cc9c05c9..5e4a9a38 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/RecentTabsPageTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/RecentTabsPageTest.java
@@ -4,7 +4,6 @@
 
 package org.chromium.chrome.browser.ntp;
 
-import android.accounts.Account;
 import android.app.Activity;
 import android.view.View;
 
@@ -21,12 +20,14 @@
 
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.Feature;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
 import org.chromium.chrome.test.util.ChromeRenderTestRule;
 import org.chromium.chrome.test.util.RecentTabsPageTestUtils;
+import org.chromium.chrome.test.util.browser.Features;
 import org.chromium.chrome.test.util.browser.signin.AccountManagerTestRule;
 import org.chromium.components.embedder_support.util.UrlConstants;
 import org.chromium.components.signin.test.util.FakeProfileDataSource;
@@ -74,7 +75,6 @@
     @After
     public void tearDown() {
         leaveRecentTabsPage();
-        RecentTabsManager.forcePromoStateForTests(null);
         RecentTabsManager.setRecentlyClosedTabManagerForTests(null);
     }
 
@@ -100,9 +100,8 @@
     @LargeTest
     @Feature("RenderTest")
     public void testPersonalizedSigninPromoInRecentTabsPage() throws Exception {
-        Account account = mAccountManagerTestRule.addTestAccountThenSigninAndEnableSync();
-        RecentTabsManager.forcePromoStateForTests(
-                RecentTabsManager.PromoState.PROMO_SIGNIN_PERSONALIZED);
+        mAccountManagerTestRule.addAccount(mAccountManagerTestRule.createProfileDataFromName(
+                AccountManagerTestRule.TEST_ACCOUNT_EMAIL));
         mPage = loadRecentTabsPage();
         mRenderTestRule.render(mPage.getView(), "personalized_signin_promo_recent_tabs_page");
     }
@@ -110,10 +109,9 @@
     @Test
     @LargeTest
     @Feature("RenderTest")
+    @Features.EnableFeatures({ChromeFeatureList.MOBILE_IDENTITY_CONSISTENCY})
     public void testPersonalizedSyncPromoInRecentTabsPage() throws Exception {
-        Account account = mAccountManagerTestRule.addTestAccountThenSigninAndEnableSync();
-        RecentTabsManager.forcePromoStateForTests(
-                RecentTabsManager.PromoState.PROMO_SYNC_PERSONALIZED);
+        mAccountManagerTestRule.addTestAccountThenSignin();
         mPage = loadRecentTabsPage();
         mRenderTestRule.render(mPage.getView(), "personalized_sync_promo_recent_tabs_page");
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/settings/MainSettingsFragmentTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/settings/MainSettingsFragmentTest.java
index ac4d893b..4c56d8e 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/settings/MainSettingsFragmentTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/settings/MainSettingsFragmentTest.java
@@ -418,20 +418,6 @@
     }
 
     @Test
-    @LargeTest
-    @Feature({"RenderTest"})
-    @Features.EnableFeatures({ChromeFeatureList.MOBILE_IDENTITY_CONSISTENCY})
-    public void testSyncPromoView() throws Exception {
-        mSyncTestRule.setUpAccountAndSignInForTesting();
-        launchSettingsActivity();
-
-        Preference syncPromoPreference = mMainSettings.findPreference(MainSettings.PREF_SYNC_PROMO);
-        CriteriaHelper.pollUiThread(() -> syncPromoPreference.isVisible());
-        View syncPromoView = mMainSettings.getView().findViewById(R.id.signin_promo_view_container);
-        mRenderTestRule.render(syncPromoView, "main_settings_sync_promo");
-    }
-
-    @Test
     @SmallTest
     public void testRemoveSettings() {
         // Disable night mode
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/FakeProfileSyncService.java b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/FakeProfileSyncService.java
index 7077c961..6f138237 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/FakeProfileSyncService.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/FakeProfileSyncService.java
@@ -128,11 +128,6 @@
     }
 
     @Override
-    public void enableEncryptEverything() {
-        mEncryptEverythingEnabled = true;
-    }
-
-    @Override
     public boolean canSyncFeatureStart() {
         return mCanSyncFeatureStart;
     }
@@ -149,4 +144,8 @@
     public void setRequiresClientUpgrade(boolean requiresClientUpgrade) {
         mRequiresClientUpgrade = requiresClientUpgrade;
     }
+
+    public void setEncryptEverythingEnabled(boolean encryptEverythingEnabled) {
+        mEncryptEverythingEnabled = encryptEverythingEnabled;
+    }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/SyncErrorCardPreferenceTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/SyncErrorCardPreferenceTest.java
index a5ae71c..7f560b4b 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/SyncErrorCardPreferenceTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/SyncErrorCardPreferenceTest.java
@@ -24,6 +24,7 @@
 import org.chromium.base.test.params.ParameterAnnotations;
 import org.chromium.base.test.params.ParameterizedRunner;
 import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.Feature;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
@@ -49,6 +50,7 @@
 @ParameterAnnotations.UseRunnerDelegate(ChromeJUnit4RunnerDelegate.class)
 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
 @Features.EnableFeatures(ChromeFeatureList.MOBILE_IDENTITY_CONSISTENCY)
+@DisabledTest(message = "https://crbug.com/1139399")
 public class SyncErrorCardPreferenceTest {
     // FakeProfileDataSource is required to create the ProfileDataCache entry with sync_error badge
     // for Sync error card.
@@ -211,7 +213,7 @@
     public void testSyncErrorCardForTrustedVaultKey(boolean nightModeEnabled) throws Exception {
         mFakeProfileSyncService.setEngineInitialized(true);
         mFakeProfileSyncService.setTrustedVaultKeyRequiredForPreferredDataTypes(true);
-        mFakeProfileSyncService.enableEncryptEverything();
+        mFakeProfileSyncService.setEncryptEverythingEnabled(true);
         when(mAndroidSyncSettingsMock.doesMasterSyncSettingAllowChromeSync()).thenReturn(true);
         mAccountManagerTestRule.addTestAccountThenSigninAndEnableSync(mFakeProfileSyncService);
         TestThreadUtils.runOnUiThreadBlocking(
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/ui/controller/webapps/WebappDisclosureControllerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/ui/controller/webapps/WebappDisclosureControllerTest.java
index 05b9e86..8f8ad69 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/ui/controller/webapps/WebappDisclosureControllerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/ui/controller/webapps/WebappDisclosureControllerTest.java
@@ -101,6 +101,7 @@
 
         // Simulates the case that shows the disclosure when finish native initialization.
         storage.setShowDisclosure();
+        assertTrue(storage.shouldShowDisclosure());
         controller.onFinishNativeInitialization();
         assertSnackbarShown();
 
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/signin/ConfirmImportSyncDataDialogTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/signin/ConfirmImportSyncDataDialogTest.java
index f7654436..8c9e89940 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/signin/ConfirmImportSyncDataDialogTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/signin/ConfirmImportSyncDataDialogTest.java
@@ -68,6 +68,7 @@
     @Test
     public void testPositiveButtonWhenAccountIsNotManaged() {
         AlertDialog alertDialog = getConfirmImportSyncDataDialog();
+        alertDialog.findViewById(R.id.sync_confirm_import_choice).performClick();
         alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).performClick();
         verify(mMockListener).onConfirm(false);
     }
@@ -91,6 +92,7 @@
     @Test
     public void testListenerOnCancelNotCalledOnDismissWhenButtonClicked() {
         AlertDialog dialog = getConfirmImportSyncDataDialog();
+        dialog.findViewById(R.id.sync_confirm_import_choice).performClick();
         dialog.getButton(AlertDialog.BUTTON_POSITIVE).performClick();
         dialog.dismiss();
         verify(mMockListener, never()).onCancel();
diff --git a/chrome/app/os_settings_strings.grdp b/chrome/app/os_settings_strings.grdp
index 44a71eca..34bfc543 100644
--- a/chrome/app/os_settings_strings.grdp
+++ b/chrome/app/os_settings_strings.grdp
@@ -1622,6 +1622,9 @@
   <message name="IDS_SETTINGS_MOUSE_TITLE" desc="In Device Settings, the title of the mouse settings subpage.">
     Mouse
   </message>
+  <message name="IDS_SETTINGS_POINTING_STICK_TITLE" desc="In Device Settings, the title of the pointing stick settings subpage. In most cases this will just be 'TrackPoint', the brand name of the pointing sticks used on Chromebooks.">
+    TrackPoint
+  </message>
   <message name="IDS_SETTINGS_TOUCHPAD_TITLE" desc="In Device Settings, the title of the touchpad settings subpage.">
     Touchpad
   </message>
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_POINTING_STICK_TITLE.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_POINTING_STICK_TITLE.png.sha1
new file mode 100644
index 0000000..2ab527c
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_POINTING_STICK_TITLE.png.sha1
@@ -0,0 +1 @@
+ec6fb452c11f3dd8c97d90f63bc1e614c5633d32
\ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 022ad2e5..b08cbe760 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -1267,6 +1267,12 @@
     "predictors/resource_prefetch_predictor.h",
     "predictors/resource_prefetch_predictor_tables.cc",
     "predictors/resource_prefetch_predictor_tables.h",
+    "prefetch/search_prefetch/field_trial_settings.cc",
+    "prefetch/search_prefetch/field_trial_settings.h",
+    "prefetch/search_prefetch/search_prefetch_service.cc",
+    "prefetch/search_prefetch/search_prefetch_service.h",
+    "prefetch/search_prefetch/search_prefetch_service_factory.cc",
+    "prefetch/search_prefetch/search_prefetch_service_factory.h",
     "prefs/browser_prefs.cc",
     "prefs/browser_prefs.h",
     "prefs/chrome_command_line_pref_store.cc",
diff --git a/chrome/browser/android/autofill_assistant/lite_service_bridge.cc b/chrome/browser/android/autofill_assistant/lite_service_bridge.cc
index 18bef3e..7bfd29ea 100644
--- a/chrome/browser/android/autofill_assistant/lite_service_bridge.cc
+++ b/chrome/browser/android/autofill_assistant/lite_service_bridge.cc
@@ -48,15 +48,17 @@
   }
 
   ServerUrlFetcher url_fetcher{ServerUrlFetcher::GetDefaultServerUrl()};
+  auto request_sender = std::make_unique<ServiceRequestSender>(
+      web_contents->GetBrowserContext(), /* access_token_fetcher = */ nullptr,
+      std::make_unique<NativeURLLoaderFactory>(),
+      ApiKeyFetcher().GetAPIKey(chrome::GetChannel()),
+      /* auth_enabled = */ false, /* disable_auth_if_no_access_token = */ true);
+
   return reinterpret_cast<jlong>(new LiteService(
-      std::make_unique<ServiceImpl>(
-          ApiKeyFetcher().GetAPIKey(chrome::GetChannel()),
-          url_fetcher.GetSupportsScriptEndpoint(),
-          url_fetcher.GetNextActionsEndpoint(),
-          web_contents->GetBrowserContext(),
-          std::make_unique<EmptyClientContext>(),
-          /* access_token_fetcher = */ nullptr,
-          /* auth_enabled = */ false),
+      std::make_unique<ServiceImpl>(std::move(request_sender),
+                                    url_fetcher.GetSupportsScriptEndpoint(),
+                                    url_fetcher.GetNextActionsEndpoint(),
+                                    std::make_unique<EmptyClientContext>()),
       base::android::ConvertJavaStringToUTF8(env, jtrigger_script_path),
       base::BindOnce(&OnFinished, base::android::ScopedJavaGlobalRef<jobject>(
                                       java_lite_service)),
diff --git a/chrome/browser/android/bookmarks/bookmark_bridge.cc b/chrome/browser/android/bookmarks/bookmark_bridge.cc
index 22330d0..ff4fd75 100644
--- a/chrome/browser/android/bookmarks/bookmark_bridge.cc
+++ b/chrome/browser/android/bookmarks/bookmark_bridge.cc
@@ -741,6 +741,7 @@
   // why this is called with an uneditable node.
   // See https://crbug.com/981172.
   if (!IsEditable(node)) {
+    LOG(ERROR) << "Deleting non editable bookmark, type:" << type;
     NOTREACHED();
     return;
   }
diff --git a/chrome/browser/android/history/history_deletion_bridge.cc b/chrome/browser/android/history/history_deletion_bridge.cc
index 5427200..bbc944af 100644
--- a/chrome/browser/android/history/history_deletion_bridge.cc
+++ b/chrome/browser/android/history/history_deletion_bridge.cc
@@ -24,6 +24,17 @@
   return reinterpret_cast<intptr_t>(new HistoryDeletionBridge(jobj));
 }
 
+// static
+history::DeletionInfo HistoryDeletionBridge::SanitizeDeletionInfo(
+    const history::DeletionInfo& deletion_info) {
+  std::vector<history::URLRow> sanitized_rows;
+  for (auto row : deletion_info.deleted_rows()) {
+    if (!row.url().is_empty() && row.url().is_valid())
+      sanitized_rows.push_back(row);
+  }
+  return history::DeletionInfo::ForUrls(sanitized_rows, {});
+}
+
 HistoryDeletionBridge::HistoryDeletionBridge(const JavaRef<jobject>& jobj)
     : jobj_(ScopedJavaGlobalRef<jobject>(jobj)),
       profile_(ProfileManager::GetLastUsedProfile()->GetOriginalProfile()) {
@@ -47,6 +58,7 @@
     const history::DeletionInfo& deletion_info) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   JNIEnv* env = base::android::AttachCurrentThread();
+  history::DeletionInfo sanitized_info = SanitizeDeletionInfo(deletion_info);
   Java_HistoryDeletionBridge_onURLsDeleted(
-      env, jobj_, CreateHistoryDeletionInfo(env, &deletion_info));
+      env, jobj_, CreateHistoryDeletionInfo(env, &sanitized_info));
 }
diff --git a/chrome/browser/android/history/history_deletion_bridge.h b/chrome/browser/android/history/history_deletion_bridge.h
index 0edddc61..a6e49e8 100644
--- a/chrome/browser/android/history/history_deletion_bridge.h
+++ b/chrome/browser/android/history/history_deletion_bridge.h
@@ -25,6 +25,12 @@
   void OnURLsDeleted(history::HistoryService* history_service,
                      const history::DeletionInfo& deletion_info) override;
 
+  // Sanitize the DeletionInfo of empty/invalid urls before passing to java.
+  // Fix for empty java strings being passed to the content capture service
+  // (crbug.com/1136486).
+  static history::DeletionInfo SanitizeDeletionInfo(
+      const history::DeletionInfo& deletion_info);
+
  private:
   ~HistoryDeletionBridge() override;
 
diff --git a/chrome/browser/android/history/history_deletion_bridge_unittest.cc b/chrome/browser/android/history/history_deletion_bridge_unittest.cc
new file mode 100644
index 0000000..4e881bf8
--- /dev/null
+++ b/chrome/browser/android/history/history_deletion_bridge_unittest.cc
@@ -0,0 +1,30 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/android/history/history_deletion_bridge.h"
+
+#include "base/time/time.h"
+#include "components/history/core/browser/history_types.h"
+#include "components/history/core/browser/url_row.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+TEST(HistoryDeletionBridge, TestSanitizeDeletionInfo) {
+  history::DeletionInfo info = history::DeletionInfo::ForUrls(
+      {history::URLResult(GURL("https://google.com/"), base::Time()),
+       history::URLResult(GURL("https://google.com/foo"), base::Time()),
+       history::URLResult(GURL("htt\\invalido\\gle.com"), base::Time()),
+       history::URLResult(GURL(""), base::Time())},
+      {});
+
+  std::vector<GURL> expected = {GURL("https://google.com/"),
+                                GURL("https://google.com/foo")};
+  std::vector<history::URLRow> actual =
+      HistoryDeletionBridge::SanitizeDeletionInfo(info).deleted_rows();
+  EXPECT_EQ(expected.size(), actual.size());
+
+  for (auto row : actual)
+    EXPECT_NE(expected.end(),
+              std::find(expected.begin(), expected.end(), row.url()));
+}
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index b8bf5229..d986e54 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -223,6 +223,7 @@
     "//chromeos/timezone",
     "//chromeos/tpm",
     "//chromeos/ui/base",
+    "//chromeos/ui/frame",
     "//components/arc",
     "//components/arc/media_session",
     "//components/arc/mojom:mojom_traits",
@@ -3860,6 +3861,7 @@
     "//chromeos/system",
     "//chromeos/tpm",
     "//chromeos/tpm:test_support",
+    "//chromeos/ui/frame:test_support",
     "//components/arc",
     "//components/arc:arc_test_support",
     "//components/arc:notification_test_support",
diff --git a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
index 8cfebf0c..34d6e4a0d 100644
--- a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
+++ b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
@@ -24,7 +24,6 @@
 #include "ash/public/cpp/default_frame_header.h"
 #include "ash/public/cpp/desks_helper.h"
 #include "ash/public/cpp/frame_header.h"
-#include "ash/public/cpp/immersive/immersive_fullscreen_controller.h"
 #include "ash/public/cpp/login_screen.h"
 #include "ash/public/cpp/metrics_util.h"
 #include "ash/public/cpp/overview_test_api.h"
@@ -123,6 +122,7 @@
 #include "chromeos/services/machine_learning/public/cpp/service_connection.h"
 #include "chromeos/settings/cros_settings_names.h"
 #include "chromeos/ui/base/window_properties.h"
+#include "chromeos/ui/frame/immersive/immersive_fullscreen_controller.h"
 #include "components/arc/arc_prefs.h"
 #include "components/arc/metrics/arc_metrics_constants.h"
 #include "components/policy/core/browser/policy_conversions.h"
@@ -3622,7 +3622,7 @@
     }
 
     // Frame information
-    auto* immersive_controller = ash::ImmersiveFullscreenController::Get(
+    auto* immersive_controller = chromeos::ImmersiveFullscreenController::Get(
         views::Widget::GetWidgetForNativeWindow(window));
     if (immersive_controller) {
       // The widget that hosts the immersive frame can be different from the
diff --git a/chrome/browser/chromeos/policy/status_collector/device_status_collector_browsertest.cc b/chrome/browser/chromeos/policy/status_collector/device_status_collector_browsertest.cc
index 0d8f2b5d1..f001c09 100644
--- a/chrome/browser/chromeos/policy/status_collector/device_status_collector_browsertest.cc
+++ b/chrome/browser/chromeos/policy/status_collector/device_status_collector_browsertest.cc
@@ -169,6 +169,8 @@
 // Since this number is divided by the result of the sysconf(_SC_CLK_TCK)
 // syscall, we need it to be 0 to avoid flaky tests,
 constexpr uint32_t kFakeIdleTime = 0;
+constexpr uint64_t kFakeUserTime = 789;
+constexpr uint64_t kFakeSystemTime = 4680;
 constexpr char kFakeCStateName[] = "fake_c_state_name";
 constexpr uint64_t kFakeTimeInStateSinceLastBoot = 87;
 // CPU Temperature test values:
@@ -538,7 +540,7 @@
   std::vector<cros_healthd::LogicalCpuInfoPtr> logical_cpus;
   logical_cpus.push_back(cros_healthd::LogicalCpuInfo::New(
       kFakeMaxClockSpeed, kFakeScalingMaxFrequency, kFakeScalingCurFrequency,
-      kFakeIdleTime, CreateCStateInfo()));
+      kFakeUserTime, kFakeSystemTime, kFakeIdleTime, CreateCStateInfo()));
   return logical_cpus;
 }
 
diff --git a/chrome/browser/chromeos/scoped_test_system_nss_key_slot_mixin.cc b/chrome/browser/chromeos/scoped_test_system_nss_key_slot_mixin.cc
index 7c5cb6e..103d76b 100644
--- a/chrome/browser/chromeos/scoped_test_system_nss_key_slot_mixin.cc
+++ b/chrome/browser/chromeos/scoped_test_system_nss_key_slot_mixin.cc
@@ -4,25 +4,44 @@
 
 #include "chrome/browser/chromeos/scoped_test_system_nss_key_slot_mixin.h"
 
+#include <memory>
+
 #include "base/bind.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/path_service.h"
 #include "base/run_loop.h"
+#include "chrome/common/chrome_paths.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
+#include "crypto/nss_util.h"
+#include "crypto/nss_util_internal.h"
+#include "crypto/scoped_nss_types.h"
 #include "crypto/scoped_test_system_nss_key_slot.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace chromeos {
 
+namespace {
+
+// Returns a subdirectory under the user data directory (which is not cleared
+// after pre-tests).
+base::FilePath GetNssDbTestDir() {
+  base::FilePath nss_db_subdir;
+  base::PathService::Get(chrome::DIR_USER_DATA, &nss_db_subdir);
+  nss_db_subdir = nss_db_subdir.AppendASCII("nss_db_subdir");
+  return nss_db_subdir;
+}
+
+}  // namespace
+
 ScopedTestSystemNSSKeySlotMixin::ScopedTestSystemNSSKeySlotMixin(
     InProcessBrowserTestMixinHost* host)
     : InProcessBrowserTestMixin(host) {}
 
 ScopedTestSystemNSSKeySlotMixin::~ScopedTestSystemNSSKeySlotMixin() = default;
 
-PK11SlotInfo* ScopedTestSystemNSSKeySlotMixin::slot() {
-  return scoped_test_system_nss_key_slot_->slot();
-}
-
 void ScopedTestSystemNSSKeySlotMixin::SetUpOnMainThread() {
   bool system_slot_initialized_successfully = false;
   base::RunLoop loop;
@@ -48,14 +67,36 @@
 
 void ScopedTestSystemNSSKeySlotMixin::InitializeOnIo(bool* out_success) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-  scoped_test_system_nss_key_slot_ =
-      std::make_unique<crypto::ScopedTestSystemNSSKeySlot>();
-  *out_success = scoped_test_system_nss_key_slot_->ConstructedSuccessfully();
+
+  crypto::EnsureNSSInit();
+  // NSS is allowed to do IO on the current thread since dispatching
+  // to a dedicated thread would still have the affect of blocking
+  // the current thread, due to NSS's internal locking requirements
+  base::ScopedAllowBlockingForTesting allow_blocking;
+
+  base::FilePath nss_db_subdir = GetNssDbTestDir();
+  ASSERT_TRUE(base::CreateDirectory(nss_db_subdir));
+
+  const char kTestDescription[] = "Test DB";
+  slot_ = crypto::OpenSoftwareNSSDB(nss_db_subdir, kTestDescription);
+  *out_success = !!slot_;
+
+  if (slot_) {
+    crypto::SetSystemKeySlotForTesting(
+        crypto::ScopedPK11Slot(PK11_ReferenceSlot(slot_.get())));
+  }
 }
 
 void ScopedTestSystemNSSKeySlotMixin::DestroyOnIo() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-  scoped_test_system_nss_key_slot_.reset();
+
+  crypto::SetSystemKeySlotForTesting(nullptr);
+
+  if (slot_) {
+    SECStatus status = SECMOD_CloseUserDB(slot_.get());
+    if (status != SECSuccess)
+      PLOG(ERROR) << "SECMOD_CloseUserDB failed: " << PORT_GetError();
+  }
 }
 
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/scoped_test_system_nss_key_slot_mixin.h b/chrome/browser/chromeos/scoped_test_system_nss_key_slot_mixin.h
index 2793c90..4031a9f 100644
--- a/chrome/browser/chromeos/scoped_test_system_nss_key_slot_mixin.h
+++ b/chrome/browser/chromeos/scoped_test_system_nss_key_slot_mixin.h
@@ -6,18 +6,20 @@
 #define CHROME_BROWSER_CHROMEOS_SCOPED_TEST_SYSTEM_NSS_KEY_SLOT_MIXIN_H_
 
 #include <pk11pub.h>
+
 #include <memory>
 
 #include "chrome/test/base/mixin_based_in_process_browser_test.h"
-
-namespace crypto {
-class ScopedTestSystemNSSKeySlot;
-}
+#include "crypto/scoped_nss_types.h"
 
 namespace chromeos {
 
-// Owns a persistent NSS software database in a temporary directory and the
+// Owns a persistent NSS software database in the user directory and the
 // association of the system slot with this database.
+// Note: The database is persisted in the user data directory
+// (chrome::DIR_USER_DATA) so it persists between PRE_ and non-PRE_ tests. This
+// allows simulating browser restarts after doing some operations on the
+// database without losing its state.
 //
 // This mixin performs the blocking initialization/destruction in the
 // {SetUp|TearDown}OnMainThread methods.
@@ -30,14 +32,7 @@
       const ScopedTestSystemNSSKeySlotMixin&) = delete;
   ~ScopedTestSystemNSSKeySlotMixin() override;
 
-  crypto::ScopedTestSystemNSSKeySlot* scoped_test_system_nss_key_slot() {
-    return scoped_test_system_nss_key_slot_.get();
-  }
-  const crypto::ScopedTestSystemNSSKeySlot* scoped_test_system_nss_key_slot()
-      const {
-    return scoped_test_system_nss_key_slot_.get();
-  }
-  PK11SlotInfo* slot();
+  PK11SlotInfo* slot() { return slot_.get(); }
 
   void SetUpOnMainThread() override;
   void TearDownOnMainThread() override;
@@ -46,8 +41,7 @@
   void InitializeOnIo(bool* out_success);
   void DestroyOnIo();
 
-  std::unique_ptr<crypto::ScopedTestSystemNSSKeySlot>
-      scoped_test_system_nss_key_slot_;
+  crypto::ScopedPK11Slot slot_;
 };
 
 }  // namespace chromeos
diff --git a/chrome/browser/download/android/BUILD.gn b/chrome/browser/download/android/BUILD.gn
index 9eb6e8d..607ac29 100644
--- a/chrome/browser/download/android/BUILD.gn
+++ b/chrome/browser/download/android/BUILD.gn
@@ -16,6 +16,7 @@
     "java/src/org/chromium/chrome/browser/download/DownloadFilter.java",
     "java/src/org/chromium/chrome/browser/download/DownloadInfo.java",
     "java/src/org/chromium/chrome/browser/download/DownloadLaterMetrics.java",
+    "java/src/org/chromium/chrome/browser/download/DownloadLocationDialogMetrics.java",
     "java/src/org/chromium/chrome/browser/download/DownloadManagerBridge.java",
     "java/src/org/chromium/chrome/browser/download/DownloadStartupUtils.java",
     "java/src/org/chromium/chrome/browser/download/DownloadStatus.java",
diff --git a/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/DownloadDialogBridge.java b/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/DownloadDialogBridge.java
index 7b5cceb..f2a7ab3 100644
--- a/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/DownloadDialogBridge.java
+++ b/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/DownloadDialogBridge.java
@@ -232,6 +232,12 @@
     @Override
     public void onDownloadLocationDialogComplete(String returnedPath) {
         mSuggestedPath = returnedPath;
+
+        if (mLocationDialogType == DownloadLocationDialogType.LOCATION_SUGGESTION) {
+            boolean isSelected = !mSuggestedPath.equals(getDownloadDefaultDirectory());
+            DownloadLocationDialogMetrics.recordDownloadLocationSuggestionChoice(isSelected);
+        }
+
         // The location dialog is triggered automatically, complete the flow.
         if (!mEditLocation) {
             onComplete();
diff --git a/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/DownloadLocationDialogMetrics.java b/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/DownloadLocationDialogMetrics.java
new file mode 100644
index 0000000..5a738bf
--- /dev/null
+++ b/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/DownloadLocationDialogMetrics.java
@@ -0,0 +1,23 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.download;
+
+import org.chromium.base.metrics.RecordHistogram;
+
+/**
+ * Class that contains helper functions for download location download feature metrics recording.
+ */
+public final class DownloadLocationDialogMetrics {
+    private DownloadLocationDialogMetrics() {}
+
+    /**
+     * Records the user choice on the locaction suggestion spinner.
+     * @param isChosen The user choice, true if the user chooses the suggestion.
+     */
+    public static void recordDownloadLocationSuggestionChoice(boolean isSelected) {
+        RecordHistogram.recordBooleanHistogram(
+                "MobileDownload.Location.Dialog.SuggestionSelected", isSelected);
+    }
+}
diff --git a/chrome/browser/lookalikes/lookalike_url_blocking_page.cc b/chrome/browser/lookalikes/lookalike_url_blocking_page.cc
index 6d7c101d..aa0af3df 100644
--- a/chrome/browser/lookalikes/lookalike_url_blocking_page.cc
+++ b/chrome/browser/lookalikes/lookalike_url_blocking_page.cc
@@ -30,6 +30,7 @@
     const GURL& request_url,
     ukm::SourceId source_id,
     LookalikeUrlMatchType match_type,
+    bool is_signed_exchange,
     std::unique_ptr<
         security_interstitials::SecurityInterstitialControllerClient>
         controller_client)
@@ -39,7 +40,8 @@
           std::move(controller_client)),
       safe_url_(safe_url),
       source_id_(source_id),
-      match_type_(match_type) {
+      match_type_(match_type),
+      is_signed_exchange_(is_signed_exchange) {
   controller()->metrics_helper()->RecordUserDecision(MetricsHelper::SHOW);
   controller()->metrics_helper()->RecordUserInteraction(
       MetricsHelper::TOTAL_VISITS);
diff --git a/chrome/browser/lookalikes/lookalike_url_blocking_page.h b/chrome/browser/lookalikes/lookalike_url_blocking_page.h
index f82f292..d7281e9 100644
--- a/chrome/browser/lookalikes/lookalike_url_blocking_page.h
+++ b/chrome/browser/lookalikes/lookalike_url_blocking_page.h
@@ -32,6 +32,7 @@
       const GURL& request_url,
       ukm::SourceId source_id,
       LookalikeUrlMatchType match_type,
+      bool is_signed_exchange,
       std::unique_ptr<
           security_interstitials::SecurityInterstitialControllerClient>
           controller);
@@ -42,6 +43,8 @@
   security_interstitials::SecurityInterstitialPage::TypeID GetTypeForTesting()
       override;
 
+  bool is_signed_exchange_for_testing() const { return is_signed_exchange_; }
+
  protected:
   // SecurityInterstitialPage implementation:
   void CommandReceived(const std::string& command) override;
@@ -59,6 +62,9 @@
   const GURL safe_url_;
   ukm::SourceId source_id_;
   LookalikeUrlMatchType match_type_;
+  // True if the throttle encountered a response with
+  // is_signed_exchange_inner_response flag. Only checked in tests.
+  const bool is_signed_exchange_;
 
   DISALLOW_COPY_AND_ASSIGN(LookalikeUrlBlockingPage);
 };
diff --git a/chrome/browser/lookalikes/lookalike_url_navigation_throttle.cc b/chrome/browser/lookalikes/lookalike_url_navigation_throttle.cc
index 4f9db24d..fe25266 100644
--- a/chrome/browser/lookalikes/lookalike_url_navigation_throttle.cc
+++ b/chrome/browser/lookalikes/lookalike_url_navigation_throttle.cc
@@ -143,8 +143,9 @@
       web_contents, url, safe_url);
 
   std::unique_ptr<LookalikeUrlBlockingPage> blocking_page(
-      new LookalikeUrlBlockingPage(web_contents, safe_url, url, source_id,
-                                   match_type, std::move(controller)));
+      new LookalikeUrlBlockingPage(
+          web_contents, safe_url, url, source_id, match_type,
+          handle->IsSignedExchangeInnerResponse(), std::move(controller)));
 
   base::Optional<std::string> error_page_contents =
       blocking_page->GetHTMLContents();
@@ -225,6 +226,28 @@
     first_is_lookalike = false;
   }
 
+  // Allow signed exchange cache URLs such as
+  // https://example-com.site.test/package.sxg.
+  // Navigation throttles see signed exchanges as a redirect chain where
+  // Url 0: Cache URL (i.e. outer URL)
+  // Url 1: URL of the sgx package
+  // Url 2: Inner URL (the URL whose contents the sgx package contains)
+  //
+  // We want to allow lookalike cache URLs but not lookalike inner URLs, so we
+  // make an exception for this condition.
+  // TODO(meacer): Confirm that the assumption about cache URL being the 1st
+  // and inner URL being the last URL in the redirect chain is correct.
+  //
+  // Note that the signed exchange logic can still redirect the initial
+  // navigation to the fallback URL even if SGX checks fail (invalid cert,
+  // missing headers etc, see crbug.com/874323 for an example). Such navigations
+  // are not considered SGX navigations and IsSignedExchangeInnerResponse()
+  // will return false. We treat such navigations as simple redirects.
+  if (first_is_lookalike &&
+      navigation_handle()->IsSignedExchangeInnerResponse()) {
+    first_is_lookalike = false;
+  }
+
   if (!first_is_lookalike && !last_is_lookalike) {
     return content::NavigationThrottle::PROCEED;
   }
diff --git a/chrome/browser/lookalikes/lookalike_url_navigation_throttle_browsertest.cc b/chrome/browser/lookalikes/lookalike_url_navigation_throttle_browsertest.cc
index 11d5c763..8aacc0be 100644
--- a/chrome/browser/lookalikes/lookalike_url_navigation_throttle_browsertest.cc
+++ b/chrome/browser/lookalikes/lookalike_url_navigation_throttle_browsertest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "base/bind.h"
+#include "base/path_service.h"
 #include "base/strings/pattern.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
@@ -28,20 +29,26 @@
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/lookalikes/core/features.h"
 #include "components/lookalikes/core/lookalike_url_util.h"
+#include "components/network_session_configurator/common/network_switches.h"
 #include "components/security_interstitials/content/security_interstitial_page.h"
 #include "components/security_interstitials/content/security_interstitial_tab_helper.h"
 #include "components/security_interstitials/core/metrics_helper.h"
 #include "components/ukm/test_ukm_recorder.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/content_features.h"
+#include "content/public/common/content_paths.h"
 #include "content/public/test/browser_test.h"
+#include "content/public/test/content_mock_cert_verifier.h"
 #include "content/public/test/signed_exchange_browser_test_helper.h"
 #include "content/public/test/test_navigation_observer.h"
+#include "net/cert/mock_cert_verifier.h"
 #include "net/dns/mock_host_resolver.h"
+#include "net/test/cert_test_util.h"
 #include "net/test/embedded_test_server/http_request.h"
 #include "net/test/embedded_test_server/http_response.h"
 #include "services/metrics/public/cpp/ukm_builders.h"
 #include "services/metrics/public/cpp/ukm_source.h"
+#include "services/network/public/cpp/network_switches.h"
 #include "ui/base/window_open_disposition.h"
 
 namespace {
@@ -281,7 +288,8 @@
       Browser* browser,
       const GURL& navigated_url,
       const GURL& expected_suggested_url,
-      NavigationSuggestionEvent expected_event) {
+      NavigationSuggestionEvent expected_event,
+      bool expect_signed_exchange = false) {
     base::HistogramTester histograms;
 
     history::HistoryService* const history_service =
@@ -290,6 +298,14 @@
     ui_test_utils::WaitForHistoryToLoad(history_service);
 
     LoadAndCheckInterstitialAt(browser, navigated_url);
+
+    if (expect_signed_exchange) {
+      LookalikeUrlBlockingPage* interstitial =
+          static_cast<LookalikeUrlBlockingPage*>(GetCurrentInterstitial(
+              browser->tab_strip_model()->GetActiveWebContents()));
+      EXPECT_TRUE(interstitial->is_signed_exchange_for_testing());
+    }
+
     SendInterstitialCommandSync(browser,
                                 SecurityInterstitialCommand::CMD_DONT_PROCEED);
     EXPECT_EQ(expected_suggested_url,
@@ -1293,18 +1309,56 @@
                            embedded_test_server()->GetURL("example.net", "/"));
 }
 
+scoped_refptr<net::X509Certificate> LoadCertificate() {
+  constexpr char kCertFileName[] = "prime256v1-sha256-google-com.public.pem";
+
+  base::ScopedAllowBlockingForTesting allow_io;
+  base::FilePath dir_path;
+  base::PathService::Get(content::DIR_TEST_DATA, &dir_path);
+  dir_path = dir_path.Append(FILE_PATH_LITERAL("sxg"));
+
+  return net::CreateCertificateChainFromFile(
+      dir_path, kCertFileName, net::X509Certificate::FORMAT_PEM_CERT_SEQUENCE);
+}
+
 // Tests for Signed Exchanges.
 class LookalikeUrlNavigationThrottleSignedExchangeBrowserTest
     : public LookalikeUrlNavigationThrottleBrowserTest {
  public:
-  void SetUpOnMainThread() override {
-    sxg_test_helper_.SetUp();
+  LookalikeUrlNavigationThrottleSignedExchangeBrowserTest() {
+    net::EmbeddedTestServer::RegisterTestCerts();
+  }
 
-    embedded_test_server()->ServeFilesFromSourceDirectory("content/test/data");
-    embedded_test_server()->RegisterRequestMonitor(base::BindRepeating(
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    // HTTPS server only serves a valid cert for localhost, so this is needed
+    // to load pages from other hosts without an error.
+    command_line->AppendSwitch(switches::kIgnoreCertificateErrors);
+    mock_cert_verifier_.SetUpCommandLine(command_line);
+  }
+
+  void SetUpInProcessBrowserTestFixture() override {
+    mock_cert_verifier_.SetUpInProcessBrowserTestFixture();
+  }
+
+  void TearDownInProcessBrowserTestFixture() override {
+    mock_cert_verifier_.TearDownInProcessBrowserTestFixture();
+  }
+
+  void SetUp() override {
+    sxg_test_helper_.SetUp();
+    LookalikeUrlNavigationThrottleBrowserTest::SetUp();
+  }
+
+  void SetUpOnMainThread() override {
+    https_server_.AddDefaultHandlers(
+        base::FilePath(FILE_PATH_LITERAL("content/test/data")));
+    https_server_.ServeFilesFromSourceDirectory("content/test/data");
+    https_server_.RegisterRequestMonitor(base::BindRepeating(
         &LookalikeUrlNavigationThrottleSignedExchangeBrowserTest::
             MonitorRequest,
         base::Unretained(this)));
+    ASSERT_TRUE(https_server_.Start());
+
     LookalikeUrlNavigationThrottleBrowserTest::SetUpOnMainThread();
   }
 
@@ -1319,8 +1373,34 @@
     return it->second.find("application/signed-exchange") != std::string::npos;
   }
 
+  void InstallMockCert() {
+    sxg_test_helper_.InstallMockCert(mock_cert_verifier_.mock_cert_verifier());
+
+    // Make the MockCertVerifier treat the certificate
+    // "prime256v1-sha256-google-com.public.pem" as valid for
+    // "google-com.example.org".
+    scoped_refptr<net::X509Certificate> original_cert = LoadCertificate();
+    net::CertVerifyResult dummy_result;
+    dummy_result.verified_cert = original_cert;
+    dummy_result.cert_status = net::OK;
+    dummy_result.ocsp_result.response_status = net::OCSPVerifyResult::PROVIDED;
+    dummy_result.ocsp_result.revocation_status =
+        net::OCSPRevocationStatus::GOOD;
+    mock_cert_verifier_.mock_cert_verifier()->AddResultForCertAndHost(
+        original_cert, "google-com.example.org", dummy_result, net::OK);
+  }
+
+  void InstallMockCertChainInterceptor() {
+    sxg_test_helper_.InstallMockCertChainInterceptor();
+    sxg_test_helper_.InstallUrlInterceptor(
+        GURL("https://google-com.example.org/cert.msg"),
+        "content/test/data/sxg/google-com.example.org.public.pem.cbor");
+  }
+
  protected:
+  net::EmbeddedTestServer https_server_{net::EmbeddedTestServer::TYPE_HTTPS};
   content::SignedExchangeBrowserTestHelper sxg_test_helper_;
+  content::ContentMockCertVerifier mock_cert_verifier_;
 
  private:
   void MonitorRequest(const net::test_server::HttpRequest& request) {
@@ -1337,7 +1417,8 @@
 INSTANTIATE_TEST_SUITE_P(
     All,
     LookalikeUrlNavigationThrottleSignedExchangeBrowserTest,
-    testing::Combine(testing::Bool(), testing::Bool()));
+    testing::Combine(testing::Bool() /* target_embedding_enabled */,
+                     testing::Bool() /* punycode_interstitial_enabled */));
 
 // Navigates to a 127.0.0.1 URL that serves a signed exchange for
 // google-com.example.org. This navigation should be blocked by the target
@@ -1346,47 +1427,117 @@
 // code). Testing an ETLD+1 such as googlé.com would require generating a custom
 // cert.
 IN_PROC_BROWSER_TEST_P(LookalikeUrlNavigationThrottleSignedExchangeBrowserTest,
-                       SignedExchange_ShouldBlockTarget) {
+                       InnerUrlIsLookalike_ShouldBlock) {
   if (!target_embedding_enabled()) {
     return;
   }
+  InstallMockCert();
+  InstallMockCertChainInterceptor();
+
   sxg_test_helper_.InstallUrlInterceptor(
       GURL("https://google-com.example.org/test/"),
       "content/test/data/sxg/fallback.html");
   const GURL kNavigatedUrl =
-      embedded_test_server()->GetURL("/sxg/google-com.example.org_test.sxg");
+      https_server_.GetURL("/sxg/google-com.example.org_test.sxg");
   const GURL kExpectedSuggestedUrl("https://google.com");
 
   TestMetricsRecordedAndInterstitialShown(
       browser(), kNavigatedUrl, kExpectedSuggestedUrl,
-      NavigationSuggestionEvent::kMatchTargetEmbedding);
+      NavigationSuggestionEvent::kMatchTargetEmbedding,
+      true /* expect_signed_exchange */);
 
   // Check that the SXG file was handled as a Signed Exchange.
   ASSERT_TRUE(HadSignedExchangeInAcceptHeader(kNavigatedUrl));
 }
 
-// Navigates to a lookalike URL that serves a signed exchange for
-// test.example.org. This should also be blocked by the lookalike interstitial,
-// even though the URL that serves the signed exchange is never visible to
-// the user.
+// Navigates to a lookalike URL (google-com.test.com) that serves a signed
+// exchange for test.example.org. This should not be blocked.
 IN_PROC_BROWSER_TEST_P(LookalikeUrlNavigationThrottleSignedExchangeBrowserTest,
-                       SignedExchange_ShouldBlockCacheUrl) {
+                       OuterUrlIsLookalike_ShouldNotBlock) {
   if (!target_embedding_enabled()) {
     return;
   }
+
+  InstallMockCert();
+  InstallMockCertChainInterceptor();
+
   const GURL kSgxTargetUrl("https://test.example.org/test/");
   sxg_test_helper_.InstallUrlInterceptor(kSgxTargetUrl,
                                          "content/test/data/sxg/fallback.html");
-  const GURL kNavigatedUrl = embedded_test_server()->GetURL(
+  const GURL kNavigatedUrl = https_server_.GetURL(
       "google-com.test.com", "/sxg/test.example.org_test.sxg");
-  const GURL kExpectedSuggestedUrl =
-      embedded_test_server()->GetURL("google.com", "/");
+
+  TestInterstitialNotShown(browser(), kNavigatedUrl);
+
+  // Check that the SXG file was handled as a Signed Exchange.
+  // MonitorRequest() sees kNavigatedUrl with an IP address instead of
+  // domain name, so check it instead.
+  const GURL kResolvedNavigatedUrl =
+      https_server_.GetURL("/sxg/test.example.org_test.sxg");
+  ASSERT_TRUE(HadSignedExchangeInAcceptHeader(kResolvedNavigatedUrl));
+}
+
+// Navigates to a lookalike URL (google-com.test.com) that serves a signed
+// exchange for test.example.org. This should not be blocked.
+IN_PROC_BROWSER_TEST_P(LookalikeUrlNavigationThrottleSignedExchangeBrowserTest,
+                       OuterUrlIsLookalikeButNotSignedExchange_ShouldNotBlock) {
+  if (!target_embedding_enabled()) {
+    return;
+  }
+
+  InstallMockCert();
+  InstallMockCertChainInterceptor();
+
+  const GURL kSgxTargetUrl("https://test.example.org/test/");
+  sxg_test_helper_.InstallUrlInterceptor(kSgxTargetUrl,
+                                         "content/test/data/sxg/fallback.html");
+  const GURL kSgxCacheUrl = https_server_.GetURL(
+      "google-com.test.com", "/sxg/test.example.org_test.sxg");
+  const GURL kNavigatedUrl = embedded_test_server()->GetURL(
+      "apple-com.site.com", "/server-redirect?" + kSgxCacheUrl.spec());
+
+  TestInterstitialNotShown(browser(), kNavigatedUrl);
+
+  // Check that the SXG file was handled as a Signed Exchange.
+  // MonitorRequest() sees kNavigatedUrl with an IP address instead of
+  // domain name, so check it instead.
+  const GURL kResolvedNavigatedUrl =
+      https_server_.GetURL("/sxg/test.example.org_test.sxg");
+  ASSERT_TRUE(HadSignedExchangeInAcceptHeader(kResolvedNavigatedUrl));
+}
+
+// Navigates to a lookalike URL (google-com.test.com) that serves a signed
+// exchange for google-com.example.org.
+// Both the outer URL (i.e. cache) and the inner URL are lookalikes so this
+// should be blocked.
+IN_PROC_BROWSER_TEST_P(LookalikeUrlNavigationThrottleSignedExchangeBrowserTest,
+                       InnerAndOuterUrlsAreLookalikes_ShouldBlock) {
+  if (!target_embedding_enabled()) {
+    return;
+  }
+  InstallMockCert();
+  InstallMockCertChainInterceptor();
+
+  sxg_test_helper_.InstallUrlInterceptor(
+      GURL("https://google-com.example.org/test/"),
+      "content/test/data/sxg/fallback.html");
+  const GURL kNavigatedUrl = https_server_.GetURL(
+      "google-com.test.com", "/sxg/google-com.example.org_test.sxg");
+  const GURL kExpectedSuggestedUrl("https://google.com");
 
   TestMetricsRecordedAndInterstitialShown(
       browser(), kNavigatedUrl, kExpectedSuggestedUrl,
-      NavigationSuggestionEvent::kMatchTargetEmbedding);
+      NavigationSuggestionEvent::kMatchTargetEmbedding,
+      true /* expect_signed_exchange */);
 
-  // Check that no SXG response was handled.
-  ASSERT_FALSE(HadSignedExchangeInAcceptHeader(kNavigatedUrl));
-  ASSERT_FALSE(HadSignedExchangeInAcceptHeader(kSgxTargetUrl));
+  // Check that the SXG file was handled as a Signed Exchange.
+  // MonitorRequest() sees kNavigatedUrl with an IP address instead of
+  // domain name, so check it instead.
+  const GURL kResolvedNavigatedUrl =
+      https_server_.GetURL("/sxg/google-com.example.org_test.sxg");
+  ASSERT_TRUE(HadSignedExchangeInAcceptHeader(kResolvedNavigatedUrl));
 }
+
+// TODO(meacer): Add a test for a failed SGX response. It should be treated
+// as a normal redirect. In fact, InnerAndOuterUrlsLookalikes_ShouldBlock
+// is actually testing this right now, fix it.
diff --git a/chrome/browser/media/router/providers/cast/app_activity.cc b/chrome/browser/media/router/providers/cast/app_activity.cc
index 1a7989d..09f89c1 100644
--- a/chrome/browser/media/router/providers/cast/app_activity.cc
+++ b/chrome/browser/media/router/providers/cast/app_activity.cc
@@ -13,6 +13,7 @@
 #include "base/stl_util.h"
 #include "chrome/browser/media/router/providers/cast/cast_activity_manager.h"
 #include "chrome/browser/media/router/providers/cast/cast_session_client.h"
+#include "components/cast_channel/enum_table.h"
 #include "url/origin.h"
 
 using blink::mojom::PresentationConnectionCloseReason;
@@ -111,7 +112,10 @@
     if (session) {
       media_controller_->SetSession(*session);
       base::Value status_request(base::Value::Type::DICTIONARY);
-      status_request.SetKey("type", base::Value("MEDIA_GET_STATUS"));
+      status_request.SetStringKey(
+          "type", cast_util::EnumToString<
+                      cast_channel::V2MessageType,
+                      cast_channel::V2MessageType::kMediaGetStatus>());
       message_handler_->SendMediaRequest(cast_channel_id(), status_request,
                                          media_controller_->sender_id(),
                                          session->transport_id());
diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
index 5f0c303..ff3e40d 100644
--- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc
+++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
@@ -931,6 +931,9 @@
   { key::kPhoneHubTaskContinuationAllowed,
     chromeos::multidevice_setup::kPhoneHubTaskContinuationAllowedPrefName,
     base::Value::Type::BOOLEAN },
+  { key::kWifiSyncAndroidAllowed,
+    chromeos::multidevice_setup::kWifiSyncAllowedPrefName,
+    base::Value::Type::BOOLEAN },
   { key::kCaptivePortalAuthenticationIgnoresProxy,
     prefs::kCaptivePortalAuthenticationIgnoresProxy,
     base::Value::Type::BOOLEAN },
diff --git a/chrome/browser/prefetch/search_prefetch/field_trial_settings.cc b/chrome/browser/prefetch/search_prefetch/field_trial_settings.cc
new file mode 100644
index 0000000..e556dc6
--- /dev/null
+++ b/chrome/browser/prefetch/search_prefetch/field_trial_settings.cc
@@ -0,0 +1,12 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/prefetch/search_prefetch/field_trial_settings.h"
+
+const base::Feature kSearchPrefetchService{"SearchPrefecthService",
+                                           base::FEATURE_DISABLED_BY_DEFAULT};
+
+bool SearchPrefetchServiceIsEnabled() {
+  return base::FeatureList::IsEnabled(kSearchPrefetchService);
+}
diff --git a/chrome/browser/prefetch/search_prefetch/field_trial_settings.h b/chrome/browser/prefetch/search_prefetch/field_trial_settings.h
new file mode 100644
index 0000000..b9fb4e0
--- /dev/null
+++ b/chrome/browser/prefetch/search_prefetch/field_trial_settings.h
@@ -0,0 +1,14 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_PREFETCH_SEARCH_PREFETCH_FIELD_TRIAL_SETTINGS_H_
+#define CHROME_BROWSER_PREFETCH_SEARCH_PREFETCH_FIELD_TRIAL_SETTINGS_H_
+
+#include "base/feature_list.h"
+
+extern const base::Feature kSearchPrefetchService;
+
+bool SearchPrefetchServiceIsEnabled();
+
+#endif  // CHROME_BROWSER_PREFETCH_SEARCH_PREFETCH_FIELD_TRIAL_SETTINGS_H_
diff --git a/chrome/browser/prefetch/search_prefetch/search_prefetch_service.cc b/chrome/browser/prefetch/search_prefetch/search_prefetch_service.cc
new file mode 100644
index 0000000..33a898c
--- /dev/null
+++ b/chrome/browser/prefetch/search_prefetch/search_prefetch_service.cc
@@ -0,0 +1,14 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/prefetch/search_prefetch/search_prefetch_service.h"
+
+#include "chrome/browser/profiles/profile.h"
+
+SearchPrefetchService::SearchPrefetchService(Profile* profile)
+    : profile_(profile) {
+  DCHECK(!profile_->IsOffTheRecord());
+}
+
+SearchPrefetchService::~SearchPrefetchService() = default;
diff --git a/chrome/browser/prefetch/search_prefetch/search_prefetch_service.h b/chrome/browser/prefetch/search_prefetch/search_prefetch_service.h
new file mode 100644
index 0000000..ed61519
--- /dev/null
+++ b/chrome/browser/prefetch/search_prefetch/search_prefetch_service.h
@@ -0,0 +1,21 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_PREFETCH_SEARCH_PREFETCH_SEARCH_PREFETCH_SERVICE_H_
+#define CHROME_BROWSER_PREFETCH_SEARCH_PREFETCH_SEARCH_PREFETCH_SERVICE_H_
+
+#include "components/keyed_service/core/keyed_service.h"
+
+class Profile;
+
+class SearchPrefetchService : public KeyedService {
+ public:
+  explicit SearchPrefetchService(Profile* profile);
+  ~SearchPrefetchService() override;
+
+ private:
+  Profile* profile_;
+};
+
+#endif  // CHROME_BROWSER_PREFETCH_SEARCH_PREFETCH_SEARCH_PREFETCH_SERVICE_H_
diff --git a/chrome/browser/prefetch/search_prefetch/search_prefetch_service_browsertest.cc b/chrome/browser/prefetch/search_prefetch/search_prefetch_service_browsertest.cc
new file mode 100644
index 0000000..0ce8b1f
--- /dev/null
+++ b/chrome/browser/prefetch/search_prefetch/search_prefetch_service_browsertest.cc
@@ -0,0 +1,52 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/scoped_feature_list.h"
+#include "chrome/browser/prefetch/search_prefetch/field_trial_settings.h"
+#include "chrome/browser/prefetch/search_prefetch/search_prefetch_service.h"
+#include "chrome/browser/prefetch/search_prefetch/search_prefetch_service_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "content/public/test/browser_test.h"
+#include "content/public/test/browser_test_utils.h"
+
+class SearchPrefetchServiceDisabledBrowserTest : public InProcessBrowserTest {
+ public:
+  SearchPrefetchServiceDisabledBrowserTest() {
+    feature_list_.InitAndDisableFeature(kSearchPrefetchService);
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(SearchPrefetchServiceDisabledBrowserTest,
+                       ServiceNotCreatedWhenDisabled) {
+  EXPECT_EQ(nullptr,
+            SearchPrefetchServiceFactory::GetForProfile(browser()->profile()));
+}
+
+class SearchPrefetchServiceEnabledBrowserTest : public InProcessBrowserTest {
+ public:
+  SearchPrefetchServiceEnabledBrowserTest() {
+    feature_list_.InitAndEnableFeature(kSearchPrefetchService);
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(SearchPrefetchServiceEnabledBrowserTest,
+                       ServiceNotCreatedWhenIncognito) {
+  EXPECT_EQ(nullptr, SearchPrefetchServiceFactory::GetForProfile(
+                         browser()->profile()->GetPrimaryOTRProfile()));
+}
+
+IN_PROC_BROWSER_TEST_F(SearchPrefetchServiceEnabledBrowserTest,
+                       ServiceCreatedWhenFeatureEnabled) {
+  EXPECT_NE(nullptr,
+            SearchPrefetchServiceFactory::GetForProfile(browser()->profile()));
+}
diff --git a/chrome/browser/prefetch/search_prefetch/search_prefetch_service_factory.cc b/chrome/browser/prefetch/search_prefetch/search_prefetch_service_factory.cc
new file mode 100644
index 0000000..5c7c53e1
--- /dev/null
+++ b/chrome/browser/prefetch/search_prefetch/search_prefetch_service_factory.cc
@@ -0,0 +1,40 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/prefetch/search_prefetch/search_prefetch_service_factory.h"
+
+#include "chrome/browser/prefetch/search_prefetch/field_trial_settings.h"
+#include "chrome/browser/prefetch/search_prefetch/search_prefetch_service.h"
+#include "chrome/browser/profiles/profile.h"
+#include "components/keyed_service/content/browser_context_dependency_manager.h"
+#include "content/public/browser/browser_context.h"
+
+// static
+SearchPrefetchService* SearchPrefetchServiceFactory::GetForProfile(
+    Profile* profile) {
+  if (SearchPrefetchServiceIsEnabled()) {
+    return static_cast<SearchPrefetchService*>(
+        GetInstance()->GetServiceForBrowserContext(profile, true));
+  }
+  return nullptr;
+}
+
+// static
+SearchPrefetchServiceFactory* SearchPrefetchServiceFactory::GetInstance() {
+  static base::NoDestructor<SearchPrefetchServiceFactory> factory;
+  return factory.get();
+}
+
+SearchPrefetchServiceFactory::SearchPrefetchServiceFactory()
+    : BrowserContextKeyedServiceFactory(
+          "SearchPrefetchService",
+          BrowserContextDependencyManager::GetInstance()) {}
+
+SearchPrefetchServiceFactory::~SearchPrefetchServiceFactory() = default;
+
+KeyedService* SearchPrefetchServiceFactory::BuildServiceInstanceFor(
+    content::BrowserContext* context) const {
+  Profile* profile = Profile::FromBrowserContext(context);
+  return new SearchPrefetchService(profile);
+}
diff --git a/chrome/browser/prefetch/search_prefetch/search_prefetch_service_factory.h b/chrome/browser/prefetch/search_prefetch/search_prefetch_service_factory.h
new file mode 100644
index 0000000..c8bdbacd
--- /dev/null
+++ b/chrome/browser/prefetch/search_prefetch/search_prefetch_service_factory.h
@@ -0,0 +1,44 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_PREFETCH_SEARCH_PREFETCH_SEARCH_PREFETCH_SERVICE_FACTORY_H_
+#define CHROME_BROWSER_PREFETCH_SEARCH_PREFETCH_SEARCH_PREFETCH_SERVICE_FACTORY_H_
+
+#include "base/macros.h"
+#include "base/no_destructor.h"
+#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
+
+namespace content {
+class BrowserContext;
+}  // namespace content
+
+class SearchPrefetchService;
+class Profile;
+
+// LazyInstance that owns all SearchPrefetchServices and associates them
+// with Profiles.
+class SearchPrefetchServiceFactory : public BrowserContextKeyedServiceFactory {
+ public:
+  // Gets the SearchPrefetchService for the profile.
+  //
+  // Returns null if the features if not enabled or incognito.
+  static SearchPrefetchService* GetForProfile(Profile* profile);
+
+  // Gets the LazyInstance that owns all SearchPrefetchService(s).
+  static SearchPrefetchServiceFactory* GetInstance();
+
+ private:
+  friend base::NoDestructor<SearchPrefetchServiceFactory>;
+
+  SearchPrefetchServiceFactory();
+  ~SearchPrefetchServiceFactory() override;
+
+  // BrowserContextKeyedServiceFactory:
+  KeyedService* BuildServiceInstanceFor(
+      content::BrowserContext* context) const override;
+
+  DISALLOW_COPY_AND_ASSIGN(SearchPrefetchServiceFactory);
+};
+
+#endif  // CHROME_BROWSER_PREFETCH_SEARCH_PREFETCH_SEARCH_PREFETCH_SERVICE_FACTORY_H_
diff --git a/chrome/browser/reading_list/android/reading_list_manager_impl.cc b/chrome/browser/reading_list/android/reading_list_manager_impl.cc
index 27385a5..10dd4749 100644
--- a/chrome/browser/reading_list/android/reading_list_manager_impl.cc
+++ b/chrome/browser/reading_list/android/reading_list_manager_impl.cc
@@ -114,7 +114,13 @@
   if (root_.get() == node)
     return true;
 
-  return Get(node->url());
+  // Not recursive since there is only one level of children.
+  for (const auto& child : root_->children()) {
+    if (node == child.get())
+      return true;
+  }
+
+  return false;
 }
 
 void ReadingListManagerImpl::Delete(const GURL& url) {
diff --git a/chrome/browser/reading_list/android/reading_list_manager_impl_unittest.cc b/chrome/browser/reading_list/android/reading_list_manager_impl_unittest.cc
index e0a97cc..612ed88 100644
--- a/chrome/browser/reading_list/android/reading_list_manager_impl_unittest.cc
+++ b/chrome/browser/reading_list/android/reading_list_manager_impl_unittest.cc
@@ -8,6 +8,7 @@
 #include <string>
 #include <utility>
 
+#include "base/guid.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/simple_test_clock.h"
 #include "chrome/browser/reading_list/android/reading_list_manager.h"
@@ -141,6 +142,11 @@
   node = manager()->GetNodeByID(12345);
   EXPECT_FALSE(node);
   EXPECT_FALSE(manager()->IsReadingListBookmark(node));
+
+  // Node with the same URL but not in the tree.
+  auto node_same_url =
+      std::make_unique<BookmarkNode>(0, base::GenerateGUID(), url);
+  EXPECT_FALSE(manager()->IsReadingListBookmark(node_same_url.get()));
 }
 
 // Verifies Add() the same URL twice will not invalidate returned pointers, and
diff --git a/chrome/browser/resources/BUILD.gn b/chrome/browser/resources/BUILD.gn
index 788683f..407faa9 100644
--- a/chrome/browser/resources/BUILD.gn
+++ b/chrome/browser/resources/BUILD.gn
@@ -60,6 +60,9 @@
     if (is_win || is_android || is_linux || is_chromeos) {
       deps += [ "sandbox_internals:closure_compile" ]
     }
+    if (is_linux || is_chromeos) {
+      deps += [ "webui_js_exception:closure_compile" ]
+    }
     if (is_chromeos) {
       deps += [
         "chromeos:closure_compile",
@@ -663,9 +666,23 @@
 
 if (enable_webui_tab_strip) {
   grit("tab_strip_resources") {
-    source = "tab_strip/tab_strip_resources.grd"
+    grit_flags = [
+      "-E",
+      "root_gen_dir=" + rebase_path(root_gen_dir, root_build_dir),
+      "-E",
+      "root_src_dir=" + rebase_path("//", root_build_dir),
+    ]
+
     defines = chrome_grit_defines
-    deps = [ "tab_strip:web_components" ]
+
+    # These arguments are needed since the grd is generated at build time.
+    enable_input_discovery_for_gn_analyze = false
+    defines += [ "SHARED_INTERMEDIATE_DIR=" +
+                 rebase_path(root_gen_dir, root_build_dir) ]
+    tab_strip_gen_dir = "$root_gen_dir/chrome/browser/resources/tab_strip"
+    source = "$tab_strip_gen_dir/tab_strip_resources.grd"
+    deps = [ "//chrome/browser/resources/tab_strip:build_grd" ]
+
     outputs = [
       "grit/tab_strip_resources.h",
       "grit/tab_strip_resources_map.cc",
@@ -673,10 +690,33 @@
       "tab_strip_resources.pak",
     ]
     output_dir = "$root_gen_dir/chrome"
+  }
+}
+
+if (is_linux || is_chromeos) {
+  grit("webui_js_exception_resources") {
+    if (optimize_webui) {
+      source = "webui_js_exception/webui_js_exception_resources_vulcanized.grd"
+      deps = [ "//chrome/browser/resources/webui_js_exception:build" ]
+    } else {
+      source = "webui_js_exception/webui_js_exception_resources.grd"
+      deps =
+          [ "//chrome/browser/resources/webui_js_exception:webui_js_exception" ]
+    }
+
     grit_flags = [
       "-E",
       "root_gen_dir=" + rebase_path(root_gen_dir, root_build_dir),
     ]
+
+    defines = chrome_grit_defines
+    outputs = [
+      "grit/webui_js_exception_resources.h",
+      "grit/webui_js_exception_resources_map.cc",
+      "grit/webui_js_exception_resources_map.h",
+      "webui_js_exception_resources.pak",
+    ]
+    output_dir = "$root_gen_dir/chrome"
   }
 }
 
diff --git a/chrome/browser/resources/chromeos/login/sync_consent.html b/chrome/browser/resources/chromeos/login/sync_consent.html
index ed3c9546..21712caf 100644
--- a/chrome/browser/resources/chromeos/login/sync_consent.html
+++ b/chrome/browser/resources/chromeos/login/sync_consent.html
@@ -89,7 +89,8 @@
 
         <!-- "Chrome OS settings sync" -->
         <div class="overview-list-item layout horizontal center">
-          <img class="overview-list-item-icon" src="images/settings_gear.svg" width="24" height="24">
+          <img class="overview-list-item-icon" src="images/settings_gear.svg"
+              width="24" height="24" aria-hidden="true">
           <div class="flex layout vertical center-justified">
             <div role="heading" aria-level="2" class="overview-list-item-title" id="osSyncName" consent-description>
               [[i18nDynamic(locale, 'syncConsentScreenOsSyncName')]]
@@ -102,7 +103,8 @@
 
         <!-- "Chrome browser sync" -->
         <div class="overview-list-item layout horizontal center">
-          <img class="overview-list-item-icon" src="images/browser_sync.svg" width="24" height="24">
+          <img class="overview-list-item-icon" src="images/browser_sync.svg"
+              width="24" height="24" aria-hidden="true">
           <div class="flex layout vertical center-justified">
             <div role="heading" aria-level="2" class="overview-list-item-title" consent-description
               id="browserSyncName">
diff --git a/chrome/browser/resources/chromeos/login/user_creation.html b/chrome/browser/resources/chromeos/login/user_creation.html
index c29afed4..4e1eccd6 100644
--- a/chrome/browser/resources/chromeos/login/user_creation.html
+++ b/chrome/browser/resources/chromeos/login/user_creation.html
@@ -11,6 +11,9 @@
   <template>
     <style include="oobe-dialog-host">
       cr-card-radio-button {
+        /* Removes the highlight that appears on tap. It has sharp
+         * corners, which don't match the rounded corners of the card. */
+        -webkit-tap-highlight-color: transparent;
         border-radius: 16px;
         height: 210px;
         margin-bottom: 20px;
diff --git a/chrome/browser/resources/safe_browsing/README.md b/chrome/browser/resources/safe_browsing/README.md
index 4b6f8f2..a446ae9 100644
--- a/chrome/browser/resources/safe_browsing/README.md
+++ b/chrome/browser/resources/safe_browsing/README.md
@@ -16,7 +16,7 @@
     * Wait 1-3 day for this to run on Canary to verify it doesn't crash Chrome.
     * In a synced checkout, run the following to generate protos for all
       platforms and push them to GCS. Replace the arg with your build directory:
-        * % `chrome/browser/resources/safe_browsing/push_file_type_proto.py -d
+        * % `components/safe_browsing/core/resources/push_file_type_proto.py -d
           out-gn/Debug`
     * It will ask you to double check its actions before proceeding.  It will
       fail if you're not a member of
@@ -42,7 +42,7 @@
   * **Upload** the new version of the file types.
     * In a synced checkout, run the following to generate protos for all
       platforms and push them to GCS. Replace the arg with your build directory:
-        * % `chrome/browser/resources/safe_browsing/push_file_type_proto.py -d
+        * % `components/safe_browsing/core/resources/push_file_type_proto.py -d
           out-gn/Debug`
   * Create a new cohort.
     * Under _Cohorts_, click _Manage_, then _Create Subcohort_ of Auto.
diff --git a/chrome/browser/resources/settings/a11y_page/a11y_page.html b/chrome/browser/resources/settings/a11y_page/a11y_page.html
index 44d8b67..c050e22 100644
--- a/chrome/browser/resources/settings/a11y_page/a11y_page.html
+++ b/chrome/browser/resources/settings/a11y_page/a11y_page.html
@@ -27,7 +27,7 @@
           </template>
         </template>
         <template is="dom-if" if="[[!captionSettingsOpensExternally_]]">
-          <cr-link-row class="first"
+          <cr-link-row class="first" id="captions"
               label="$i18n{captionsTitle}"
               on-click="onCaptionsClick_"
               role-description="$i18n{subpageArrowRoleDescription}">
diff --git a/chrome/browser/resources/settings/a11y_page/captions_subpage.html b/chrome/browser/resources/settings/a11y_page/captions_subpage.html
index f6e0e9e..ae06ffa 100644
--- a/chrome/browser/resources/settings/a11y_page/captions_subpage.html
+++ b/chrome/browser/resources/settings/a11y_page/captions_subpage.html
@@ -156,7 +156,7 @@
             pref="{{prefs.accessibility.captions.live_caption_enabled}}"
             on-change="onA11yLiveCaptionChange_"
             label="$i18n{captionsEnableLiveCaptionTitle}"
-            sub-label="$i18n{captionsEnableLiveCaptionSubtitle}">
+            sub-label="[[enableLiveCaptionSubtitle_]]">
         </settings-toggle-button>
       </div>
     </template>
diff --git a/chrome/browser/resources/settings/a11y_page/captions_subpage.js b/chrome/browser/resources/settings/a11y_page/captions_subpage.js
index 7dd750267..c4d29e2 100644
--- a/chrome/browser/resources/settings/a11y_page/captions_subpage.js
+++ b/chrome/browser/resources/settings/a11y_page/captions_subpage.js
@@ -6,6 +6,7 @@
  * @fileoverview 'settings-captions' is a component for showing captions
  * settings subpage (chrome://settings/captions).
  */
+
 (function() {
 
 Polymer({
@@ -175,6 +176,17 @@
         return loadTimeData.getBoolean('enableLiveCaption');
       },
     },
+
+    /**
+     * The subtitle to display under the Live Caption heading. Generally, this
+     * is a generic subtitle describing the feature. While the SODA model is
+     * being downloading, this displays the download progress.
+     * @private
+     */
+    enableLiveCaptionSubtitle_: {
+      type: String,
+      value: loadTimeData.getString('captionsEnableLiveCaptionSubtitle'),
+    },
   },
 
   /** @private {?settings.FontsBrowserProxy} */
@@ -188,6 +200,11 @@
   /** @override */
   ready() {
     this.browserProxy_.fetchFontsData().then(this.setFontsData_.bind(this));
+
+    this.addWebUIListener(
+        'enable-live-caption-subtitle-changed',
+        this.onEnableLiveCaptionSubtitleChanged_.bind(this));
+    chrome.send('captionsSubpageReady');
   },
 
   /**
@@ -288,5 +305,14 @@
     chrome.metricsPrivate.recordBoolean(
         'Accessibility.LiveCaption.ToggleEnabled', a11yLiveCaptionOn);
   },
+
+  /**
+   * @private
+   * @param {!string} enableLiveCaptionSubtitle The message sent from the webui
+   *     to be displayed as a subtitle to Live Captions.
+   */
+  onEnableLiveCaptionSubtitleChanged_(enableLiveCaptionSubtitle) {
+    this.enableLiveCaptionSubtitle_ = enableLiveCaptionSubtitle;
+  },
 });
 })();
diff --git a/chrome/browser/resources/settings/chromeos/bluetooth_page/bluetooth_device_list_item.js b/chrome/browser/resources/settings/chromeos/bluetooth_page/bluetooth_device_list_item.js
index e9d7c096..1a9cf089 100644
--- a/chrome/browser/resources/settings/chromeos/bluetooth_page/bluetooth_device_list_item.js
+++ b/chrome/browser/resources/settings/chromeos/bluetooth_page/bluetooth_device_list_item.js
@@ -126,16 +126,22 @@
    * @private
    */
   getAriaLabel_(device) {
-    // We need to turn the device name and type into a single localized string.
+    // We need to turn the device name, connection status and type into a single
+    // localized string.
     // The possible device type enum values can be seen here:
     // https://developer.chrome.com/apps/bluetooth#type-Device.
     // The localization id is computed dynamically to avoid maintaining a
     // mapping from the enum string value to the localization id.
     // if device.type is not defined, we fall back to an unknown device string.
     const deviceName = this.getDeviceName_(device);
-    return (device.type) ?
+    const deviceStatus = this.getConnectionStatusText_(device);
+
+    const a11ydeviceNameAndType = (device.type) ?
         this.i18n('bluetoothDeviceType_' + device.type, deviceName) :
         this.i18n('bluetoothDeviceType_unknown', deviceName);
+    return this.i18n(
+        'bluetoothDeviceWithConnectionStatus', a11ydeviceNameAndType,
+        deviceStatus);
   },
 
   /**
diff --git a/chrome/browser/resources/settings/chromeos/device_page/pointers.html b/chrome/browser/resources/settings/chromeos/device_page/pointers.html
index a58258f..5c781ea9 100644
--- a/chrome/browser/resources/settings/chromeos/device_page/pointers.html
+++ b/chrome/browser/resources/settings/chromeos/device_page/pointers.html
@@ -36,9 +36,9 @@
       }
     </style>
     <div id="mouse" hidden$="[[!showMouseSection_]]">
-      <!-- Subsection title only appears if both mouse and touchpad exist. -->
-      <h2 hidden$="[[!hasTouchpad]]">$i18n{mouseTitle}</h2>
-      <div class$="[[getSubsectionClass_(showMouseSection_, hasTouchpad)]]">
+      <!-- Subsection title only appears if multiple sections are visible. -->
+      <h2 hidden$="[[!showHeadings_]]">$i18n{mouseTitle}</h2>
+      <div class$="[[subsectionClass_]]">
         <!-- Do not change the mouse button pref before the mouse is released.
              See crbug.com/686949 -->
         <div class="settings-box">
@@ -108,10 +108,19 @@
         </div>
       </div>
     </div>
+    <template is="dom-if" if="[[separatePointingStickSettings_]]">
+      <div id="pointingStick" hidden$="[[!hasPointingStick]]">
+        <!-- Subsection title only appears if multiple sections are visible. -->
+        <h2 hidden$="[[!showHeadings_]]">$i18n{pointingStickTitle}</h2>
+        <div class$="[[subsectionClass_]]">
+          (Settings not implemented yet)
+        </div>
+      </div>
+    </template>
     <div id="touchpad" hidden$="[[!hasTouchpad]]">
-      <!-- Subsection title only appears if both mouse and touchpad exist. -->
-      <h2 hidden$="[[!showMouseSection_]]">$i18n{touchpadTitle}</h2>
-      <div class$="[[getSubsectionClass_(showMouseSection_, hasTouchpad)]]">
+      <!-- Subsection title only appears if multiple sections are visible. -->
+      <h2 hidden$="[[!showHeadings_]]">$i18n{touchpadTitle}</h2>
+      <div class$="[[subsectionClass_]]">
         <settings-toggle-button id="enableTapToClick"
             pref="{{prefs.settings.touchpad.enable_tap_to_click}}"
             label="$i18n{touchpadTapToClickEnabledLabel}"
diff --git a/chrome/browser/resources/settings/chromeos/device_page/pointers.js b/chrome/browser/resources/settings/chromeos/device_page/pointers.js
index 065a483..88d4767e 100644
--- a/chrome/browser/resources/settings/chromeos/device_page/pointers.js
+++ b/chrome/browser/resources/settings/chromeos/device_page/pointers.js
@@ -51,7 +51,20 @@
      */
     showMouseSection_: {
       type: Boolean,
-      computed: 'computeShowMouseSection_(hasMouse, hasPointingStick)',
+      computed: 'computeShowMouseSection_(separatePointingStickSettings_, ' +
+          'hasMouse, hasPointingStick)',
+    },
+
+    showHeadings_: {
+      type: Boolean,
+      computed: 'computeShowHeadings_(separatePointingStickSettings_, ' +
+          'hasMouse, hasPointingStick, hasTouchpad)',
+    },
+
+    subsectionClass_: {
+      type: String,
+      computed: 'computeSubsectionClass_(separatePointingStickSettings_, ' +
+          'hasMouse, hasPointingStick, hasTouchpad)',
     },
 
     /**
@@ -90,6 +103,18 @@
     },
 
     /**
+     * TODO(crbug.com/1114828): Remove this conditional once the feature is
+     * launched.
+     * @private
+     */
+    separatePointingStickSettings_: {
+      type: Boolean,
+      value() {
+        return loadTimeData.getBoolean('separatePointingStickSettings');
+      },
+    },
+
+    /**
      * Used by DeepLinkingBehavior to focus this page's deep links.
      * @type {!Set<!chromeos.settings.mojom.Setting>}
      */
@@ -112,11 +137,49 @@
   },
 
   /**
+   * @param {boolean} separateSettings
    * @param {boolean} hasMouse
    * @param {boolean} hasPointingStick
    */
-  computeShowMouseSection_(hasMouse, hasPointingStick) {
-    return hasMouse || hasPointingStick;
+  computeShowMouseSection_(separateSettings, hasMouse, hasPointingStick) {
+    return separateSettings ? hasMouse : hasMouse || hasPointingStick;
+  },
+
+  /**
+   * Headings should only be visible if more than one subsection is present.
+   * @param {boolean} separateSettings
+   * @param {boolean} hasMouse
+   * @param {boolean} hasPointingStick
+   * @param {boolean} hasTouchpad
+   * @return {boolean}
+   * @private
+   */
+  computeShowHeadings_(
+      separateSettings, hasMouse, hasPointingStick, hasTouchpad) {
+    if (!separateSettings) {
+      return (hasMouse || hasPointingStick) && hasTouchpad;
+    }
+    const sectionVisibilities = [hasMouse, hasPointingStick, hasTouchpad];
+    // Count the number of true values in sectionVisibilities.
+    const numVisibleSections = sectionVisibilities.filter(x => x).length;
+    return numVisibleSections > 1;
+  },
+
+  /**
+   * Mouse, pointing stick, and touchpad sections are only subsections if more
+   * than one is present.
+   * @param {boolean} separateSettings
+   * @param {boolean} hasMouse
+   * @param {boolean} hasPointingStick
+   * @param {boolean} hasTouchpad
+   * @return {string}
+   * @private
+   */
+  computeSubsectionClass_(
+      separateSettings, hasMouse, hasPointingStick, hasTouchpad) {
+    const subsections = this.computeShowHeadings_(
+        separateSettings, hasMouse, hasPointingStick, hasTouchpad);
+    return subsections ? 'subsection' : '';
   },
 
   /**
@@ -133,17 +196,6 @@
   },
 
   /**
-   * Mouse and touchpad sections are only subsections if they are both present.
-   * @param {boolean} showMouseSection
-   * @param {boolean} hasTouchpad
-   * @return {string}
-   * @private
-   */
-  getSubsectionClass_(showMouseSection, hasTouchpad) {
-    return showMouseSection && hasTouchpad ? 'subsection' : '';
-  },
-
-  /**
    * @param {!Event} event
    * @private
    */
diff --git a/chrome/browser/resources/settings/people_page/sync_browser_proxy.js b/chrome/browser/resources/settings/people_page/sync_browser_proxy.js
index 97f632a..1524971 100644
--- a/chrome/browser/resources/settings/people_page/sync_browser_proxy.js
+++ b/chrome/browser/resources/settings/people_page/sync_browser_proxy.js
@@ -199,6 +199,8 @@
      * @param {!settings.SyncPrefs} syncPrefs
      * @return {!Promise<!settings.PageStatus>}
      */
+    // TODO(crbug.com/1139060): Use a clear signature which doesn't rely on
+    // syncPrefs.
     setSyncEncryption(syncPrefs) {}
 
     /**
diff --git a/chrome/browser/resources/settings/people_page/sync_encryption_options.js b/chrome/browser/resources/settings/people_page/sync_encryption_options.js
index ae8e9e8..98d8f48 100644
--- a/chrome/browser/resources/settings/people_page/sync_encryption_options.js
+++ b/chrome/browser/resources/settings/people_page/sync_encryption_options.js
@@ -139,7 +139,7 @@
     chrome.metricsPrivate.recordUserAction('Sync_SaveNewPassphraseClicked');
     // Might happen within the transient time between the request to
     // |setSyncEncryption| and receiving the response.
-    if (this.syncPrefs.encryptAllData) {
+    if (this.syncPrefs.setNewPassphrase) {
       return;
     }
     // If a new password has been entered but it is invalid, do not send the
@@ -148,7 +148,6 @@
       return;
     }
 
-    this.syncPrefs.encryptAllData = true;
     this.syncPrefs.setNewPassphrase = true;
     this.syncPrefs.passphrase = this.passphrase_;
 
diff --git a/chrome/browser/resources/tab_strip/BUILD.gn b/chrome/browser/resources/tab_strip/BUILD.gn
index 93030f62..0707412 100644
--- a/chrome/browser/resources/tab_strip/BUILD.gn
+++ b/chrome/browser/resources/tab_strip/BUILD.gn
@@ -3,7 +3,68 @@
 # found in the LICENSE file.
 
 import("//third_party/closure_compiler/compile_js.gni")
+import("//tools/grit/preprocess_grit.gni")
 import("//tools/polymer/html_to_js.gni")
+import("//ui/webui/resources/tools/generate_grd.gni")
+
+preprocess_folder = "preprocessed"
+preprocess_manifest = "preprocessed_manifest.json"
+preprocess_gen_manifest = "preprocessed_gen_manifest.json"
+
+generate_grd("build_grd") {
+  grd_prefix = "tab_strip"
+  out_grd = "$target_gen_dir/${grd_prefix}_resources.grd"
+  input_files = [
+    "alert_indicators/picture_in_picture_alt.svg",
+    "alert_indicators/serial_port.svg",
+    "alert_indicators/tab_audio_muting_rounded.svg",
+    "alert_indicators/tab_audio_rounded.svg",
+    "alert_indicators/tab_bluetooth_connected.svg",
+    "alert_indicators/tab_hid_connected.svg",
+    "alert_indicators/tab_media_capturing_with_arrow.svg",
+    "alert_indicators/tab_media_recording.svg",
+    "alert_indicators/tab_usb_connected.svg",
+    "alert_indicators/vr_headset.svg",
+    "tab_strip.html",
+  ]
+  input_files_base_dir = rebase_path(".", "//")
+
+  deps = [
+    ":preprocess",
+    ":preprocess_generated",
+  ]
+  manifest_files = [
+    "$target_gen_dir/$preprocess_manifest",
+    "$target_gen_dir/$preprocess_gen_manifest",
+  ]
+}
+
+preprocess_grit("preprocess") {
+  in_folder = "./"
+  out_folder = "$target_gen_dir/$preprocess_folder"
+  out_manifest = "$target_gen_dir/$preprocess_manifest"
+  in_files = [
+    "custom_element.js",
+    "drag_manager.js",
+    "tab_strip_embedder_proxy.js",
+    "tab_swiper.js",
+    "tabs_api_proxy.js",
+  ]
+}
+
+preprocess_grit("preprocess_generated") {
+  deps = [ ":web_components" ]
+  in_folder = target_gen_dir
+  out_folder = "$target_gen_dir/$preprocess_folder"
+  out_manifest = "$target_gen_dir/$preprocess_gen_manifest"
+  in_files = [
+    "alert_indicator.js",
+    "alert_indicators.js",
+    "tab_group.js",
+    "tab_list.js",
+    "tab.js",
+  ]
+}
 
 js_type_check("closure_compile") {
   uses_js_modules = true
diff --git a/chrome/browser/resources/tab_strip/tab_strip_resources.grd b/chrome/browser/resources/tab_strip/tab_strip_resources.grd
deleted file mode 100644
index 7f8d71a..0000000
--- a/chrome/browser/resources/tab_strip/tab_strip_resources.grd
+++ /dev/null
@@ -1,113 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<grit latest_public_release="0" current_release="1" output_all_resource_defines="false">
-  <outputs>
-    <output filename="grit/tab_strip_resources.h" type="rc_header">
-      <emit emit_type='prepend'></emit>
-    </output>
-    <output filename="grit/tab_strip_resources_map.cc"
-            type="resource_file_map_source" />
-    <output filename="grit/tab_strip_resources_map.h"
-            type="resource_map_header" />
-    <output filename="tab_strip_resources.pak" type="data_package" />
-  </outputs>
-  <release seq="1">
-    <structures>
-      <structure
-          name="IDR_TAB_STRIP_HTML"
-          file="tab_strip.html"
-          type="chrome_html"
-          preprocess="true"/>
-      <structure
-          name="IDR_TAB_STRIP_TABS_API_PROXY_JS"
-          file="tabs_api_proxy.js"
-          type="chrome_html"/>
-      <structure
-          name="IDR_TAB_STRIP_CUSTOM_ELEMENT_JS"
-          file="custom_element.js"
-          type="chrome_html"/>
-      <structure
-          name="IDR_TAB_STRIP_TAB_GROUP_JS"
-          file="${root_gen_dir}/chrome/browser/resources/tab_strip/tab_group.js"
-          use_base_dir="false"
-          type="chrome_html"/>
-      <structure
-          name="IDR_TAB_STRIP_TAB_LIST_JS"
-          file="${root_gen_dir}/chrome/browser/resources/tab_strip/tab_list.js"
-          use_base_dir="false"
-          type="chrome_html"
-          preprocess="true"/>
-      <structure
-          name="IDR_TAB_STRIP_TAB_JS"
-          file="${root_gen_dir}/chrome/browser/resources/tab_strip/tab.js"
-          use_base_dir="false"
-          type="chrome_html"/>
-      <structure
-          name="IDR_TAB_STRIP_ALERT_INDICATOR_JS"
-          file="${root_gen_dir}/chrome/browser/resources/tab_strip/alert_indicator.js"
-          use_base_dir="false"
-          type="chrome_html"/>
-      <structure
-          name="IDR_TAB_STRIP_ALERT_INDICATORS_JS"
-          file="${root_gen_dir}/chrome/browser/resources/tab_strip/alert_indicators.js"
-          use_base_dir="false"
-          type="chrome_html"/>
-      <structure
-          name="IDR_TAB_STRIP_EMBEDDER_PROXY_JS"
-          file="tab_strip_embedder_proxy.js"
-          type="chrome_html"/>
-      <structure
-          name="IDR_TAB_STRIP_TAB_SWIPER_JS"
-          file="tab_swiper.js"
-          type="chrome_html"/>
-      <structure
-          name="IDR_TAB_STRIP_DRAG_MANAGER_JS"
-          file="drag_manager.js"
-          type="chrome_html"
-          preprocess="true"/>
-    </structures>
-
-    <includes>
-      <!-- Alert indicators -->
-      <include
-          name="IDR_TAB_STRIP_PICTURE_IN_PICTURE_ALT_SVG"
-          file="alert_indicators/picture_in_picture_alt.svg"
-          type="BINDATA" />
-      <include
-          name="IDR_TAB_STRIP_SERIAL_PORT_SVG"
-          file="alert_indicators/serial_port.svg"
-          type="BINDATA" />
-      <include
-          name="IDR_TAB_STRIP_TAB_AUDIO_MUTING_ROUNDED_SVG"
-          file="alert_indicators/tab_audio_muting_rounded.svg"
-          type="BINDATA" />
-      <include
-          name="IDR_TAB_STRIP_TAB_AUDIO_ROUNDED_SVG"
-          file="alert_indicators/tab_audio_rounded.svg"
-          type="BINDATA" />
-      <include
-          name="IDR_TAB_STRIP_TAB_BLUETOOTH_CONNECTED_SVG"
-          file="alert_indicators/tab_bluetooth_connected.svg"
-          type="BINDATA" />
-      <include
-          name="IDR_TAB_STRIP_TAB_HID_CONNECTED_SVG"
-          file="alert_indicators/tab_hid_connected.svg"
-          type="BINDATA" />
-      <include
-          name="IDR_TAB_STRIP_TAB_MEDIA_CAPTURING_WITH_ARROW_SVG"
-          file="alert_indicators/tab_media_capturing_with_arrow.svg"
-          type="BINDATA" />
-      <include
-          name="IDR_TAB_STRIP_TAB_MEDIA_RECORING_SVG"
-          file="alert_indicators/tab_media_recording.svg"
-          type="BINDATA" />
-      <include
-          name="IDR_TAB_STRIP_TAB_USB_CONNECTED_SVG"
-          file="alert_indicators/tab_usb_connected.svg"
-          type="BINDATA" />
-      <include
-          name="IDR_TAB_STRIP_VR_HEADSET_SVG"
-          file="alert_indicators/vr_headset.svg"
-          type="BINDATA" />
-    </includes>
-  </release>
-</grit>
diff --git a/chrome/browser/resources/webui_js_exception/BUILD.gn b/chrome/browser/resources/webui_js_exception/BUILD.gn
new file mode 100644
index 0000000..c62fa2f
--- /dev/null
+++ b/chrome/browser/resources/webui_js_exception/BUILD.gn
@@ -0,0 +1,54 @@
+# Copyright 2020 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//chrome/browser/resources/optimize_webui.gni")
+import("//third_party/closure_compiler/compile_js.gni")
+import("//tools/grit/grit_rule.gni")
+import("//ui/webui/webui_features.gni")
+
+js_library("webui_js_exception") {
+}
+
+if (optimize_webui) {
+  grit("flattened_resources") {
+    source = "webui_js_exception_resources.grd"
+
+    deps = [ ":webui_js_exception" ]
+
+    grit_flags = [
+      "-E",
+      "root_gen_dir=" + rebase_path(root_gen_dir, root_build_dir),
+    ]
+
+    outputs = [
+      "grit/webui_js_exception_resources.h",
+      "grit/webui_js_exception_resources_map.cc",
+      "grit/webui_js_exception_resources_map.h",
+      "webui_js_exception_resources.pak",
+    ]
+    output_dir = "$root_gen_dir/chrome/browser/resources/webui_js_exception"
+  }
+
+  unpak("unpak") {
+    pak_file = "webui_js_exception_resources.pak"
+    out_folder = "unpak"
+    deps = [ ":flattened_resources" ]
+  }
+
+  optimize_webui("build") {
+    host = "webui_js_exception"
+    input = rebase_path("$target_gen_dir/unpak", root_build_dir)
+    deps = [
+      ":unpak",
+      "//ui/webui/resources:modulize",
+    ]
+    js_module_in_files = [ "webui_js_exception.js" ]
+    js_out_files = [ "webui_js_exception.rollup.js" ]
+    excludes = [ "chrome://resources/js/cr.m.js" ]
+  }
+}
+
+js_type_check("closure_compile") {
+  deps = [ ":webui_js_exception" ]
+}
diff --git a/chrome/browser/resources/webui_js_exception/webui_js_exception.html b/chrome/browser/resources/webui_js_exception/webui_js_exception.html
new file mode 100644
index 0000000..180d788
--- /dev/null
+++ b/chrome/browser/resources/webui_js_exception/webui_js_exception.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <!-- Title is not localized since this is just a debugging page -->
+    <title>WebUI JS Exception Page</title>
+    <style>
+      html,
+      body {
+        height: 100%;
+        overflow: hidden;
+      }
+
+      body {
+        margin: 0;
+      }
+    </style>
+  </head>
+  <body>
+    <!-- Text not localized since this is just a debugging page -->
+    This page generates a JavaScript exception on load and then another 3
+    seconds later.
+    <script type="module" src="webui_js_exception.js"></script>
+  </body>
+</html>
diff --git a/chrome/browser/resources/webui_js_exception/webui_js_exception.js b/chrome/browser/resources/webui_js_exception/webui_js_exception.js
new file mode 100644
index 0000000..685521c
--- /dev/null
+++ b/chrome/browser/resources/webui_js_exception/webui_js_exception.js
@@ -0,0 +1,45 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview This JavaScript throws an unhandled exception on page load, and
+ * then another 3 seconds later.
+ *
+ * Note that function names and error text are referenced in integration tests
+ * (particularly tast tests); do not change them without updating the tests.
+ */
+
+/**
+ * Throws an exception when called. This throws the "after 3 seconds" exception.
+ */
+function throwExceptionAfterTimeoutFunction() {
+  throwExceptionAfterTimeoutInner();
+}
+
+/**
+ * Throws an exception when called. This is a separate function from
+ * throwExceptionAfterTimeoutFunction() so that we get an interesting stack.
+ */
+function throwExceptionAfterTimeoutInner() {
+  throw new Error('WebUI JS Exception: timeout expired');
+}
+
+/**
+ * Throws an exception when called. This throws the "during page load"
+ * exception.
+ */
+function throwExceptionDuringPageLoadFunction() {
+  throwExceptionDuringPageLoadInner();
+}
+
+/**
+ * Throws an exception when called. This is a separate function from
+ * throwExceptionDuringPageLoadFunction() so that we get an interesting stack.
+ */
+function throwExceptionDuringPageLoadInner() {
+  throw new Error('WebUI JS Exception: exception on page load');
+}
+
+setTimeout(throwExceptionAfterTimeoutFunction, 3000);
+throwExceptionDuringPageLoadFunction();
diff --git a/chrome/browser/resources/webui_js_exception/webui_js_exception_resources.grd b/chrome/browser/resources/webui_js_exception/webui_js_exception_resources.grd
new file mode 100644
index 0000000..9946d94
--- /dev/null
+++ b/chrome/browser/resources/webui_js_exception/webui_js_exception_resources.grd
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<grit latest_public_release="0" current_release="1" output_all_resource_defines="false">
+  <outputs>
+    <output filename="grit/webui_js_exception_resources.h" type="rc_header">
+      <emit emit_type='prepend'></emit>
+    </output>
+    <output filename="grit/webui_js_exception_resources_map.cc"
+            type="resource_file_map_source" />
+    <output filename="grit/webui_js_exception_resources_map.h"
+            type="resource_map_header" />
+    <output filename="webui_js_exception_resources.pak" type="data_package" />
+  </outputs>
+  <release seq="1">
+    <structures>
+      <structure name="IDR_WEBUI_JS_EXCEPTION_UI_WEBUI_JS_EXCEPTION_JS"
+          file="webui_js_exception.js" type="chrome_html" compress="false" />
+      <structure name="IDR_WEBUI_JS_EXCEPTION_UI_WEBUI_JS_EXCEPTION_HTML"
+          file="webui_js_exception.html" type="chrome_html" compress="false" />
+    </structures>
+  </release>
+</grit>
diff --git a/chrome/browser/resources/webui_js_exception/webui_js_exception_resources_vulcanized.grd b/chrome/browser/resources/webui_js_exception/webui_js_exception_resources_vulcanized.grd
new file mode 100644
index 0000000..e410c66
--- /dev/null
+++ b/chrome/browser/resources/webui_js_exception/webui_js_exception_resources_vulcanized.grd
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<grit latest_public_release="0" current_release="1" output_all_resource_defines="false">
+  <outputs>
+    <output filename="grit/webui_js_exception_resources.h" type="rc_header">
+      <emit emit_type='prepend'></emit>
+    </output>
+    <output filename="grit/webui_js_exception_resources_map.cc"
+            type="resource_map_source" />
+    <output filename="grit/webui_js_exception_resources_map.h"
+            type="resource_map_header" />
+    <output filename="webui_js_exception_resources.pak" type="data_package" />
+  </outputs>
+  <release seq="1">
+    <includes>
+      <include name="IDR_WEBUI_JS_EXCEPTION_UI_WEBUI_JS_EXCEPTION_ROLLUP_JS"
+          file="${root_gen_dir}/chrome/browser/resources/webui_js_exception/webui_js_exception.rollup.js"
+          use_base_dir="false" type="BINDATA" />
+      <include name="IDR_WEBUI_JS_EXCEPTION_UI_WEBUI_JS_EXCEPTION_HTML"
+          file="webui_js_exception.html" preprocess="true" type="BINDATA" />
+    </includes>
+  </release>
+</grit>
diff --git a/chrome/browser/safe_browsing/incident_reporting/incident_reporting_service.cc b/chrome/browser/safe_browsing/incident_reporting/incident_reporting_service.cc
index e6277e89..d3df33d 100644
--- a/chrome/browser/safe_browsing/incident_reporting/incident_reporting_service.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/incident_reporting_service.cc
@@ -342,6 +342,12 @@
 
   if (g_browser_process->profile_manager())
     g_browser_process->profile_manager()->RemoveObserver(this);
+
+  for (const auto& profile_and_context : profiles_) {
+    Profile* profile = profile_and_context.first;
+    if (profile)
+      profile->RemoveObserver(this);
+  }
 }
 
 std::unique_ptr<IncidentReceiver>
@@ -478,6 +484,28 @@
   BeginDownloadCollection();
 }
 
+void IncidentReportingService::OnProfileWillBeDestroyed(Profile* profile) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  profile->RemoveObserver(this);
+
+  auto it = profiles_.find(profile);
+  DCHECK(it != profiles_.end());
+
+  // Take ownership of the context.
+  std::unique_ptr<ProfileContext> context = std::move(it->second);
+
+  // TODO(grt): Persist incidents for upload on future profile load.
+
+  // Remove the association with this profile context from all pending uploads.
+  for (const auto& upload : uploads_)
+    upload->profiles_to_state.erase(context.get());
+
+  // Forget about this profile. Incidents not yet sent for upload are lost.
+  // No new incidents will be accepted for it.
+  profiles_.erase(it);
+}
+
 std::unique_ptr<LastDownloadFinder>
 IncidentReportingService::CreateDownloadFinder(
     const LastDownloadFinder::LastDownloadCallback& callback) {
@@ -502,8 +530,11 @@
 IncidentReportingService::ProfileContext*
 IncidentReportingService::GetOrCreateProfileContext(Profile* profile) {
   std::unique_ptr<ProfileContext>& context = profiles_[profile];
-  if (!context)
+  if (!context) {
     context = std::make_unique<ProfileContext>();
+    if (profile)
+      profile->AddObserver(this);
+  }
   return context.get();
 }
 
diff --git a/chrome/browser/safe_browsing/incident_reporting/incident_reporting_service.h b/chrome/browser/safe_browsing/incident_reporting/incident_reporting_service.h
index 0c170b3..037a06c 100644
--- a/chrome/browser/safe_browsing/incident_reporting/incident_reporting_service.h
+++ b/chrome/browser/safe_browsing/incident_reporting/incident_reporting_service.h
@@ -74,7 +74,8 @@
 // the initial incident. Finally, already-reported incidents are pruned and any
 // remaining are uploaded in an incident report.
 // Lives on the UI thread.
-class IncidentReportingService : public ProfileManagerObserver {
+class IncidentReportingService : public ProfileManagerObserver,
+                                 public ProfileObserver {
  public:
   explicit IncidentReportingService(SafeBrowsingService* safe_browsing_service);
 
@@ -107,6 +108,9 @@
   // ProfileManagerObserver:
   void OnProfileAdded(Profile* profile) override;
 
+  // ProfileObserver:
+  void OnProfileWillBeDestroyed(Profile* profile) override;
+
  protected:
   // A pointer to a function that populates a protobuf with environment data.
   typedef void (*CollectEnvironmentDataFn)(
diff --git a/chrome/browser/safe_browsing/incident_reporting/last_download_finder.cc b/chrome/browser/safe_browsing/incident_reporting/last_download_finder.cc
index 0e70841a..0c587165 100644
--- a/chrome/browser/safe_browsing/incident_reporting/last_download_finder.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/last_download_finder.cc
@@ -236,6 +236,8 @@
 
 LastDownloadFinder::~LastDownloadFinder() {
   g_browser_process->profile_manager()->RemoveObserver(this);
+  for (const auto& state : profile_states_)
+    state.first->RemoveObserver(this);
 }
 
 // static
@@ -278,6 +280,8 @@
   if (profile_states_.count(profile))
     return;
 
+  profile->AddObserver(this);
+
   // Initiate a metadata search. As with IncidentReportingService, it's assumed
   // that all passed profiles will outlive |this|.
   profile_states_[profile] = WAITING_FOR_METADATA;
@@ -361,6 +365,7 @@
 void LastDownloadFinder::RemoveProfileAndReportIfDone(
     std::map<Profile*, ProfileWaitState>::iterator iter) {
   DCHECK(iter != profile_states_.end());
+  iter->first->RemoveObserver(this);
   profile_states_.erase(iter);
 
   // Finish processing if all results are in.
@@ -400,6 +405,13 @@
   SearchInProfile(profile);
 }
 
+void LastDownloadFinder::OnProfileWillBeDestroyed(Profile* profile) {
+  // |profile| may not be present in the set of profiles.
+  auto iter = profile_states_.find(profile);
+  DCHECK(iter != profile_states_.end());
+  RemoveProfileAndReportIfDone(iter);
+}
+
 void LastDownloadFinder::OnHistoryServiceLoaded(
     history::HistoryService* history_service) {
   for (const auto& pair : profile_states_) {
diff --git a/chrome/browser/safe_browsing/incident_reporting/last_download_finder.h b/chrome/browser/safe_browsing/incident_reporting/last_download_finder.h
index cf4f652b..804b9f8 100644
--- a/chrome/browser/safe_browsing/incident_reporting/last_download_finder.h
+++ b/chrome/browser/safe_browsing/incident_reporting/last_download_finder.h
@@ -15,6 +15,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/scoped_observer.h"
 #include "chrome/browser/profiles/profile_manager_observer.h"
+#include "chrome/browser/profiles/profile_observer.h"
 #include "chrome/browser/safe_browsing/incident_reporting/download_metadata_manager.h"
 #include "components/history/core/browser/download_row.h"
 #include "components/history/core/browser/history_service.h"
@@ -31,6 +32,7 @@
 // any on-the-record profile with history that participates in safe browsing
 // extended reporting.
 class LastDownloadFinder : public ProfileManagerObserver,
+                           public ProfileObserver,
                            public history::HistoryServiceObserver {
  public:
   typedef base::Callback<void(
@@ -101,6 +103,9 @@
   // ProfileManagerObserver:
   void OnProfileAdded(Profile* profile) override;
 
+  // ProfileObserver:
+  void OnProfileWillBeDestroyed(Profile* profile) override;
+
   // history::HistoryServiceObserver:
   void OnHistoryServiceLoaded(history::HistoryService* service) override;
   void HistoryServiceBeingDeleted(
diff --git a/chrome/browser/sync/profile_sync_service_android.cc b/chrome/browser/sync/profile_sync_service_android.cc
index d790875..301624cf0 100644
--- a/chrome/browser/sync/profile_sync_service_android.cc
+++ b/chrome/browser/sync/profile_sync_service_android.cc
@@ -278,13 +278,6 @@
   return sync_service_->GetUserSettings()->IsEncryptEverythingEnabled();
 }
 
-void ProfileSyncServiceAndroid::EnableEncryptEverything(
-    JNIEnv* env,
-    const JavaParamRef<jobject>& obj) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  sync_service_->GetUserSettings()->EnableEncryptEverything();
-}
-
 jboolean ProfileSyncServiceAndroid::IsPassphraseRequiredForPreferredDataTypes(
     JNIEnv* env,
     const JavaParamRef<jobject>& obj) {
diff --git a/chrome/browser/sync/profile_sync_service_android.h b/chrome/browser/sync/profile_sync_service_android.h
index 28dbd24..50d8921 100644
--- a/chrome/browser/sync/profile_sync_service_android.h
+++ b/chrome/browser/sync/profile_sync_service_android.h
@@ -87,8 +87,6 @@
   jboolean IsEncryptEverythingEnabled(
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& obj);
-  void EnableEncryptEverything(JNIEnv* env,
-                               const base::android::JavaParamRef<jobject>& obj);
   jboolean IsPassphraseRequiredForPreferredDataTypes(
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& obj);
diff --git a/chrome/browser/sync/profile_sync_service_factory.cc b/chrome/browser/sync/profile_sync_service_factory.cc
index 8b3b7aa..e1dc8ce5 100644
--- a/chrome/browser/sync/profile_sync_service_factory.cc
+++ b/chrome/browser/sync/profile_sync_service_factory.cc
@@ -47,19 +47,14 @@
 #include "chrome/common/buildflags.h"
 #include "chrome/common/channel_info.h"
 #include "components/autofill/core/browser/personal_data_manager.h"
-#include "components/autofill/core/common/autofill_features.h"
-#include "components/browser_sync/browser_sync_switches.h"
-#include "components/invalidation/impl/invalidation_switches.h"
 #include "components/invalidation/impl/profile_identity_provider.h"
 #include "components/invalidation/impl/profile_invalidation_provider.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "components/network_time/network_time_tracker.h"
-#include "components/password_manager/core/common/password_manager_features.h"
 #include "components/sync/driver/profile_sync_service.h"
 #include "components/sync/driver/sync_driver_switches.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_task_traits.h"
-#include "content/public/browser/browser_thread.h"
 #include "content/public/browser/network_service_instance.h"
 #include "content/public/browser/storage_partition.h"
 #include "extensions/buildflags/buildflags.h"
@@ -190,7 +185,8 @@
           : std::make_unique<browser_sync::ChromeSyncClient>(profile);
 
   init_params.sync_client = std::move(sync_client);
-  init_params.network_time_update_callback = base::Bind(&UpdateNetworkTime);
+  init_params.network_time_update_callback =
+      base::BindRepeating(&UpdateNetworkTime);
   init_params.url_loader_factory =
       content::BrowserContext::GetDefaultStoragePartition(profile)
           ->GetURLLoaderFactoryForBrowserProcess();
@@ -198,11 +194,6 @@
       content::GetNetworkConnectionTracker();
   init_params.channel = chrome::GetChannel();
   init_params.debug_identifier = profile->GetDebugName();
-  init_params.autofill_enable_account_wallet_storage =
-      base::FeatureList::IsEnabled(
-          autofill::features::kAutofillEnableAccountWalletStorage);
-  init_params.enable_passwords_account_storage = base::FeatureList::IsEnabled(
-      password_manager::features::kEnablePasswordsAccountStorage);
 
   bool local_sync_backend_enabled = false;
 
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index c9226f8..1daa9fb 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -306,13 +306,6 @@
     "webui/webui_util.h",
   ]
 
-  if (is_win || is_mac) {
-    sources += [
-      "webui/settings/captions_handler.cc",
-      "webui/settings/captions_handler.h",
-    ]
-  }
-
   if (safe_browsing_mode == 1) {
     sources += [
       "webui/reset_password/reset_password_ui.cc",
@@ -1563,6 +1556,13 @@
       deps += [ "//ui/base/ime/linux" ]
     }
 
+    if (!is_chromeos) {
+      sources += [
+        "webui/settings/captions_handler.cc",
+        "webui/settings/captions_handler.h",
+      ]
+    }
+
     if (!toolkit_views) {
       sources += [ "media_router/cloud_services_dialog.cc" ]
     }
@@ -2443,6 +2443,8 @@
       "webui/settings/chromeos/bluetooth_section.h",
       "webui/settings/chromeos/calculator/size_calculator.cc",
       "webui/settings/chromeos/calculator/size_calculator.h",
+      "webui/settings/chromeos/captions_handler.cc",
+      "webui/settings/chromeos/captions_handler.h",
       "webui/settings/chromeos/change_picture_handler.cc",
       "webui/settings/chromeos/change_picture_handler.h",
       "webui/settings/chromeos/constants/constants_util.cc",
@@ -3294,6 +3296,8 @@
       "webui/certificate_viewer_ui.h",
       "webui/certificate_viewer_webui.cc",
       "webui/certificate_viewer_webui.h",
+      "webui/webui_js_exception/webui_js_exception_ui.cc",
+      "webui/webui_js_exception/webui_js_exception_ui.h",
     ]
     if (use_aura) {
       deps += [ "//third_party/fontconfig" ]
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd
index 3bb7730..d145aec 100644
--- a/chrome/browser/ui/android/strings/android_chrome_strings.grd
+++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd
@@ -3999,7 +3999,7 @@
 
       <message name="IDS_CABLEV2_ACTIVITY_TITLE"
         desc="The label of the Activity for using your phone as a security key. A 'security key' in this context is generally a small USB device that is used for logging into websites. This feature allows Chrome on an Android phone to act as a security key. A user may see it in Android permissions prompts (see screenshot).">
-         Google <ph name="APP_NAME">%1$s<ex>Chrome</ex></ph> as a Security Key
+         Google Chrome as a Security Key
       </message>
 
       <!-- QR Code -->
diff --git a/chrome/browser/ui/android/tab_model/tab_model_jni_bridge.cc b/chrome/browser/ui/android/tab_model/tab_model_jni_bridge.cc
index 1932583..2f3290a 100644
--- a/chrome/browser/ui/android/tab_model/tab_model_jni_bridge.cc
+++ b/chrome/browser/ui/android/tab_model/tab_model_jni_bridge.cc
@@ -203,61 +203,6 @@
   TabModel::BroadcastSessionRestoreComplete();
 }
 
-inline static base::TimeDelta GetTimeDelta(jlong ms) {
-  return base::TimeDelta::FromMilliseconds(static_cast<int64_t>(ms));
-}
-
-void JNI_TabModelJniBridge_LogFromCloseMetric(
-    JNIEnv* env,
-    jlong ms,
-    jboolean perceived) {
-  if (perceived) {
-    UMA_HISTOGRAM_TIMES("Tabs.SwitchFromCloseLatency_Perceived",
-                        GetTimeDelta(ms));
-  } else {
-    UMA_HISTOGRAM_TIMES("Tabs.SwitchFromCloseLatency_Actual",
-                        GetTimeDelta(ms));
-  }
-}
-
-void JNI_TabModelJniBridge_LogFromExitMetric(
-    JNIEnv* env,
-    jlong ms,
-    jboolean perceived) {
-  if (perceived) {
-    UMA_HISTOGRAM_TIMES("Tabs.SwitchFromExitLatency_Perceived",
-                        GetTimeDelta(ms));
-  } else {
-    UMA_HISTOGRAM_TIMES("Tabs.SwitchFromExitLatency_Actual",
-                        GetTimeDelta(ms));
-  }
-}
-
-void JNI_TabModelJniBridge_LogFromNewMetric(JNIEnv* env,
-                                            jlong ms,
-                                            jboolean perceived) {
-  if (perceived) {
-    UMA_HISTOGRAM_TIMES("Tabs.SwitchFromNewLatency_Perceived",
-                        GetTimeDelta(ms));
-  } else {
-    UMA_HISTOGRAM_TIMES("Tabs.SwitchFromNewLatency_Actual",
-                        GetTimeDelta(ms));
-  }
-}
-
-void JNI_TabModelJniBridge_LogFromUserMetric(
-    JNIEnv* env,
-    jlong ms,
-    jboolean perceived) {
-  if (perceived) {
-    UMA_HISTOGRAM_TIMES("Tabs.SwitchFromUserLatency_Perceived",
-                        GetTimeDelta(ms));
-  } else {
-    UMA_HISTOGRAM_TIMES("Tabs.SwitchFromUserLatency_Actual",
-                        GetTimeDelta(ms));
-  }
-}
-
 TabModelJniBridge::~TabModelJniBridge() {
   TabModelList::RemoveTabModel(this);
 }
diff --git a/chrome/browser/ui/ash/accelerator_commands_browsertest.cc b/chrome/browser/ui/ash/accelerator_commands_browsertest.cc
index e839496..540dad32 100644
--- a/chrome/browser/ui/ash/accelerator_commands_browsertest.cc
+++ b/chrome/browser/ui/ash/accelerator_commands_browsertest.cc
@@ -5,7 +5,6 @@
 #include "ash/accelerators/accelerator_commands.h"
 
 #include "ash/public/cpp/test/shell_test_api.h"
-#include "ash/public/cpp/window_properties.h"
 #include "base/command_line.h"
 #include "base/macros.h"
 #include "build/build_config.h"
@@ -18,6 +17,7 @@
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/test/base/in_process_browser_test.h"
+#include "chromeos/ui/base/window_properties.h"
 #include "content/public/test/browser_test.h"
 #include "extensions/browser/app_window/app_window.h"
 #include "extensions/browser/app_window/native_app_window.h"
@@ -34,7 +34,7 @@
 namespace {
 
 bool IsInImmersive(aura::Window* window) {
-  return window->GetProperty(ash::kImmersiveIsActive);
+  return window->GetProperty(chromeos::kImmersiveIsActive);
 }
 
 }  // namespace
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_client_impl_browsertest.cc b/chrome/browser/ui/ash/holding_space/holding_space_client_impl_browsertest.cc
index c2864c0..666d3e6 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_client_impl_browsertest.cc
+++ b/chrome/browser/ui/ash/holding_space/holding_space_client_impl_browsertest.cc
@@ -100,7 +100,8 @@
 }
 
 // Verifies that `HoldingSpaceClient::OpenDownloads()` works as intended.
-IN_PROC_BROWSER_TEST_F(HoldingSpaceClientImplTest, OpenDownloads) {
+// TODO(crbug.com/1139299): Flaky.
+IN_PROC_BROWSER_TEST_F(HoldingSpaceClientImplTest, DISABLED_OpenDownloads) {
   ASSERT_TRUE(HoldingSpaceController::Get());
 
   auto* holding_space_client = HoldingSpaceController::Get()->client();
diff --git a/chrome/browser/ui/views/apps/chrome_native_app_window_views.cc b/chrome/browser/ui/views/apps/chrome_native_app_window_views.cc
index 729dd66..41fcd29 100644
--- a/chrome/browser/ui/views/apps/chrome_native_app_window_views.cc
+++ b/chrome/browser/ui/views/apps/chrome_native_app_window_views.cc
@@ -325,6 +325,7 @@
       region->op(gfx::RectToSkIRect(input_rect), SkRegion::kUnion_Op);
   }
   shape_ = std::move(region);
+  OnWidgetHasHitTestMaskChanged();
   widget()->SetShape(shape() ? std::make_unique<ShapeRects>(*shape_rects_)
                              : nullptr);
   widget()->OnSizeConstraintsChanged();
diff --git a/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.cc b/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.cc
index f42f0dd..37ff38a3 100644
--- a/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.cc
+++ b/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.cc
@@ -10,7 +10,6 @@
 #include "ash/frame/non_client_frame_view_ash.h"
 #include "ash/public/cpp/app_types.h"
 #include "ash/public/cpp/ash_switches.h"
-#include "ash/public/cpp/immersive/immersive_fullscreen_controller.h"
 #include "ash/public/cpp/shelf_types.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/public/cpp/tablet_mode.h"
@@ -31,7 +30,9 @@
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/extensions/extension_constants.h"
 #include "chromeos/ui/base/chromeos_ui_constants.h"
+#include "chromeos/ui/base/window_properties.h"
 #include "chromeos/ui/base/window_state_type.h"
+#include "chromeos/ui/frame/immersive/immersive_fullscreen_controller.h"
 #include "components/session_manager/core/session_manager.h"
 #include "extensions/browser/app_window/app_delegate.h"
 #include "extensions/common/constants.h"
@@ -90,12 +91,12 @@
   window->SetProperty(aura::client::kAppType,
                       static_cast<int>(ash::AppType::CHROME_APP));
   window->SetProperty(
-      ash::kImmersiveWindowType,
+      chromeos::kImmersiveWindowType,
       static_cast<int>(
-          ash::ImmersiveFullscreenController::WINDOW_TYPE_PACKAGED_APP));
+          chromeos::ImmersiveFullscreenController::WINDOW_TYPE_PACKAGED_APP));
   // Fullscreen doesn't always imply immersive mode (see
   // ShouldEnableImmersive()).
-  window->SetProperty(ash::kImmersiveImpliedByFullscreen, false);
+  window->SetProperty(chromeos::kImmersiveImpliedByFullscreen, false);
   // TODO(https://crbug.com/997480): Determine if all non-resizable windows
   // should have this behavior, or just the feedback app.
   if (app_window->extension_id() == extension_misc::kFeedbackExtensionId) {
@@ -438,12 +439,13 @@
 }
 
 bool ChromeNativeAppWindowViewsAuraAsh::IsImmersiveModeEnabled() const {
-  return GetWidget()->GetNativeWindow()->GetProperty(ash::kImmersiveIsActive);
+  return GetWidget()->GetNativeWindow()->GetProperty(
+      chromeos::kImmersiveIsActive);
 }
 
 gfx::Rect ChromeNativeAppWindowViewsAuraAsh::GetTopContainerBoundsInScreen() {
   gfx::Rect* bounds = GetWidget()->GetNativeWindow()->GetProperty(
-      ash::kImmersiveTopContainerBoundsInScreen);
+      chromeos::kImmersiveTopContainerBoundsInScreen);
   return bounds ? *bounds : gfx::Rect();
 }
 
@@ -546,7 +548,7 @@
 }
 
 void ChromeNativeAppWindowViewsAuraAsh::UpdateImmersiveMode() {
-  ash::ImmersiveFullscreenController::EnableForWidget(
+  chromeos::ImmersiveFullscreenController::EnableForWidget(
       widget(), ShouldEnableImmersiveMode());
 }
 
diff --git a/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash_browsertest.cc b/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash_browsertest.cc
index 8a18f76..4ddddc94 100644
--- a/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash_browsertest.cc
+++ b/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash_browsertest.cc
@@ -4,9 +4,7 @@
 
 #include "chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.h"
 
-#include "ash/public/cpp/immersive/immersive_fullscreen_controller.h"
 #include "ash/public/cpp/test/shell_test_api.h"
-#include "ash/public/cpp/window_properties.h"
 #include "base/macros.h"
 #include "base/run_loop.h"
 #include "base/scoped_observer.h"
@@ -17,6 +15,8 @@
 #include "chrome/test/base/interactive_test_utils.h"
 #include "chromeos/login/login_state/login_state.h"
 #include "chromeos/login/login_state/scoped_test_public_session_login_state.h"
+#include "chromeos/ui/base/window_properties.h"
+#include "chromeos/ui/frame/immersive/immersive_fullscreen_controller.h"
 #include "content/public/test/browser_test.h"
 #include "extensions/browser/app_window/app_window.h"
 #include "extensions/test/extension_test_message_listener.h"
@@ -66,7 +66,7 @@
 
   bool IsImmersiveActive() {
     return window()->widget()->GetNativeWindow()->GetProperty(
-        ash::kImmersiveIsActive);
+        chromeos::kImmersiveIsActive);
   }
 
   ChromeNativeAppWindowViewsAuraAsh* window() {
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc
index 814bb0b0d..89b4065 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc
@@ -7,7 +7,6 @@
 #include "ash/public/cpp/ash_switches.h"
 #include "ash/public/cpp/default_frame_header.h"
 #include "ash/public/cpp/frame_header.h"
-#include "ash/public/cpp/immersive/immersive_fullscreen_controller_test_api.h"
 #include "ash/public/cpp/shelf_test_api.h"
 #include "ash/public/cpp/split_view_test_api.h"
 #include "ash/public/cpp/test/shell_test_api.h"
@@ -74,6 +73,7 @@
 #include "chrome/test/base/ui_test_utils.h"
 #include "chromeos/ui/base/chromeos_ui_constants.h"
 #include "chromeos/ui/frame/caption_buttons/frame_caption_button_container_view.h"
+#include "chromeos/ui/frame/immersive/immersive_fullscreen_controller_test_api.h"
 #include "components/account_id/account_id.h"
 #include "components/autofill/core/common/password_form.h"
 #include "components/keep_alive_registry/keep_alive_types.h"
@@ -481,7 +481,7 @@
 
     BrowserView::SetDisableRevealerDelayForTesting(true);
 
-    ash::ImmersiveFullscreenControllerTestApi(
+    chromeos::ImmersiveFullscreenControllerTestApi(
         static_cast<ImmersiveModeControllerAsh*>(
             BrowserView::GetBrowserViewForBrowser(browser())
                 ->immersive_mode_controller())
@@ -680,7 +680,7 @@
   ASSERT_TRUE(browser->is_type_app());
   BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser);
 
-  ash::ImmersiveFullscreenControllerTestApi(
+  chromeos::ImmersiveFullscreenControllerTestApi(
       static_cast<ImmersiveModeControllerAsh*>(
           browser_view->immersive_mode_controller())
           ->controller())
diff --git a/chrome/browser/ui/views/frame/immersive_mode_controller_ash.cc b/chrome/browser/ui/views/frame/immersive_mode_controller_ash.cc
index 9818063..6ff5b686 100644
--- a/chrome/browser/ui/views/frame/immersive_mode_controller_ash.cc
+++ b/chrome/browser/ui/views/frame/immersive_mode_controller_ash.cc
@@ -4,7 +4,6 @@
 
 #include "chrome/browser/ui/views/frame/immersive_mode_controller_ash.h"
 
-#include "ash/public/cpp/immersive/immersive_revealed_lock.h"
 #include "ash/public/cpp/tablet_mode.h"
 #include "ash/public/cpp/window_properties.h"
 #include "base/macros.h"
@@ -14,6 +13,8 @@
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/frame/top_container_view.h"
 #include "chrome/browser/ui/views/tabs/tab_strip.h"
+#include "chromeos/ui/base/window_properties.h"
+#include "chromeos/ui/frame/immersive/immersive_revealed_lock.h"
 #include "content/public/browser/web_contents.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/window_targeter.h"
@@ -30,27 +31,27 @@
 namespace {
 
 // Converts from ImmersiveModeController::AnimateReveal to
-// ash::ImmersiveFullscreenController::AnimateReveal.
-ash::ImmersiveFullscreenController::AnimateReveal
+// chromeos::ImmersiveFullscreenController::AnimateReveal.
+chromeos::ImmersiveFullscreenController::AnimateReveal
 ToImmersiveFullscreenControllerAnimateReveal(
     ImmersiveModeController::AnimateReveal animate_reveal) {
   switch (animate_reveal) {
     case ImmersiveModeController::ANIMATE_REVEAL_YES:
-      return ash::ImmersiveFullscreenController::ANIMATE_REVEAL_YES;
+      return chromeos::ImmersiveFullscreenController::ANIMATE_REVEAL_YES;
     case ImmersiveModeController::ANIMATE_REVEAL_NO:
-      return ash::ImmersiveFullscreenController::ANIMATE_REVEAL_NO;
+      return chromeos::ImmersiveFullscreenController::ANIMATE_REVEAL_NO;
   }
   NOTREACHED();
-  return ash::ImmersiveFullscreenController::ANIMATE_REVEAL_NO;
+  return chromeos::ImmersiveFullscreenController::ANIMATE_REVEAL_NO;
 }
 
 class ImmersiveRevealedLockAsh : public ImmersiveRevealedLock {
  public:
-  explicit ImmersiveRevealedLockAsh(ash::ImmersiveRevealedLock* lock)
+  explicit ImmersiveRevealedLockAsh(chromeos::ImmersiveRevealedLock* lock)
       : lock_(lock) {}
 
  private:
-  std::unique_ptr<ash::ImmersiveRevealedLock> lock_;
+  std::unique_ptr<chromeos::ImmersiveRevealedLock> lock_;
 
   DISALLOW_COPY_AND_ASSIGN(ImmersiveRevealedLockAsh);
 };
@@ -69,11 +70,11 @@
   observed_windows_.Add(browser_view_->GetNativeWindow());
 
   browser_view_->GetNativeWindow()->SetProperty(
-      ash::kImmersiveWindowType,
+      chromeos::kImmersiveWindowType,
       static_cast<int>(
           browser_view_->browser()->deprecated_is_app()
-              ? ash::ImmersiveFullscreenController::WINDOW_TYPE_HOSTED_APP
-              : ash::ImmersiveFullscreenController::WINDOW_TYPE_BROWSER));
+              ? chromeos::ImmersiveFullscreenController::WINDOW_TYPE_HOSTED_APP
+              : chromeos::ImmersiveFullscreenController::WINDOW_TYPE_BROWSER));
 }
 
 void ImmersiveModeControllerAsh::SetEnabled(bool enabled) {
@@ -86,8 +87,8 @@
                                  ->fullscreen_controller());
   }
 
-  ash::ImmersiveFullscreenController::EnableForWidget(browser_view_->frame(),
-                                                      enabled);
+  chromeos::ImmersiveFullscreenController::EnableForWidget(
+      browser_view_->frame(), enabled);
 }
 
 bool ImmersiveModeControllerAsh::IsEnabled() const {
@@ -143,7 +144,7 @@
 
   // Enable immersive mode if the widget is activated. Do not disable immersive
   // mode if the widget deactivates, but is not minimized.
-  ash::ImmersiveFullscreenController::EnableForWidget(
+  chromeos::ImmersiveFullscreenController::EnableForWidget(
       browser_view_->frame(), active || !widget->IsMinimized());
 }
 
diff --git a/chrome/browser/ui/views/frame/immersive_mode_controller_ash.h b/chrome/browser/ui/views/frame/immersive_mode_controller_ash.h
index 0950ed91..303c25bd 100644
--- a/chrome/browser/ui/views/frame/immersive_mode_controller_ash.h
+++ b/chrome/browser/ui/views/frame/immersive_mode_controller_ash.h
@@ -7,27 +7,27 @@
 
 #include <memory>
 
-#include "ash/public/cpp/immersive/immersive_fullscreen_controller.h"
-#include "ash/public/cpp/immersive/immersive_fullscreen_controller_delegate.h"
 #include "base/macros.h"
 #include "base/scoped_observer.h"
 #include "chrome/browser/ui/exclusive_access/fullscreen_controller.h"
 #include "chrome/browser/ui/exclusive_access/fullscreen_observer.h"
 #include "chrome/browser/ui/views/frame/immersive_mode_controller.h"
+#include "chromeos/ui/frame/immersive/immersive_fullscreen_controller.h"
+#include "chromeos/ui/frame/immersive/immersive_fullscreen_controller_delegate.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_observer.h"
 #include "ui/gfx/geometry/rect.h"
 
 class ImmersiveModeControllerAsh
     : public ImmersiveModeController,
-      public ash::ImmersiveFullscreenControllerDelegate,
+      public chromeos::ImmersiveFullscreenControllerDelegate,
       public FullscreenObserver,
       public aura::WindowObserver {
  public:
   ImmersiveModeControllerAsh();
   ~ImmersiveModeControllerAsh() override;
 
-  ash::ImmersiveFullscreenController* controller() { return &controller_; }
+  chromeos::ImmersiveFullscreenController* controller() { return &controller_; }
 
   // ImmersiveModeController overrides:
   void Init(BrowserView* browser_view) override;
@@ -65,7 +65,7 @@
                                intptr_t old) override;
   void OnWindowDestroying(aura::Window* window) override;
 
-  ash::ImmersiveFullscreenController controller_;
+  chromeos::ImmersiveFullscreenController controller_;
 
   BrowserView* browser_view_ = nullptr;
 
diff --git a/chrome/browser/ui/views/frame/immersive_mode_controller_ash_browsertest.cc b/chrome/browser/ui/views/frame/immersive_mode_controller_ash_browsertest.cc
index 6a3e4574..37b21a24 100644
--- a/chrome/browser/ui/views/frame/immersive_mode_controller_ash_browsertest.cc
+++ b/chrome/browser/ui/views/frame/immersive_mode_controller_ash_browsertest.cc
@@ -2,7 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ash/public/cpp/immersive/immersive_fullscreen_controller_test_api.h"
 #include "ash/public/cpp/test/shell_test_api.h"
 #include "base/macros.h"
 #include "base/test/test_mock_time_task_runner.h"
@@ -26,6 +25,7 @@
 #include "chrome/test/base/ui_test_utils.h"
 #include "chrome/test/permissions/permission_request_manager_test_api.h"
 #include "chromeos/ui/frame/caption_buttons/frame_caption_button_container_view.h"
+#include "chromeos/ui/frame/immersive/immersive_fullscreen_controller_test_api.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/content_mock_cert_verifier.h"
 #include "net/cert/mock_cert_verifier.h"
@@ -71,7 +71,7 @@
 
     // Disable animations in immersive fullscreen before we show the window,
     // which triggers an animation.
-    ash::ImmersiveFullscreenControllerTestApi(
+    chromeos::ImmersiveFullscreenControllerTestApi(
         static_cast<ImmersiveModeControllerAsh*>(controller_)->controller())
         .SetupForTest();
 
diff --git a/chrome/browser/ui/views/frame/immersive_mode_controller_ash_unittest.cc b/chrome/browser/ui/views/frame/immersive_mode_controller_ash_unittest.cc
index 5abe576..8bc2966c 100644
--- a/chrome/browser/ui/views/frame/immersive_mode_controller_ash_unittest.cc
+++ b/chrome/browser/ui/views/frame/immersive_mode_controller_ash_unittest.cc
@@ -4,7 +4,6 @@
 
 #include "chrome/browser/ui/views/frame/immersive_mode_controller_ash.h"
 
-#include "ash/public/cpp/immersive/immersive_fullscreen_controller_test_api.h"
 #include "ash/public/cpp/window_pin_type.h"
 #include "ash/public/cpp/window_properties.h"
 #include "ash/root_window_controller.h"
@@ -27,6 +26,7 @@
 #include "chrome/browser/ui/views/fullscreen_control/fullscreen_control_host.h"
 #include "chrome/browser/ui/views/tabs/tab_strip.h"
 #include "chrome/browser/ui/views/toolbar/toolbar_view.h"
+#include "chromeos/ui/frame/immersive/immersive_fullscreen_controller_test_api.h"
 #include "ui/aura/window.h"
 #include "ui/base/pointer/touch_ui_controller.h"
 #include "ui/events/event.h"
@@ -46,7 +46,7 @@
     browser()->window()->Show();
 
     controller_ = browser_view()->immersive_mode_controller();
-    ash::ImmersiveFullscreenControllerTestApi(
+    chromeos::ImmersiveFullscreenControllerTestApi(
         static_cast<ImmersiveModeControllerAsh*>(controller_)->controller())
         .SetupForTest();
   }
diff --git a/chrome/browser/ui/views/frame/webui_tab_strip_interactive_uitest.cc b/chrome/browser/ui/views/frame/webui_tab_strip_interactive_uitest.cc
index 152bc701..d80480a 100644
--- a/chrome/browser/ui/views/frame/webui_tab_strip_interactive_uitest.cc
+++ b/chrome/browser/ui/views/frame/webui_tab_strip_interactive_uitest.cc
@@ -22,8 +22,8 @@
 #include "ui/views/controls/webview/webview.h"
 
 #if defined(OS_CHROMEOS)
-#include "ash/public/cpp/immersive/immersive_fullscreen_controller.h"
-#include "ash/public/cpp/immersive/immersive_fullscreen_controller_test_api.h"
+#include "chromeos/ui/frame/immersive/immersive_fullscreen_controller.h"
+#include "chromeos/ui/frame/immersive/immersive_fullscreen_controller_test_api.h"
 #endif  // defined(OS_CHROMEOS)
 
 class WebUITabStripInteractiveTest : public InProcessBrowserTest {
@@ -138,8 +138,8 @@
   BrowserView* const browser_view =
       BrowserView::GetBrowserViewForBrowser(browser());
 
-  ash::ImmersiveFullscreenControllerTestApi immersive_test_api(
-      ash::ImmersiveFullscreenController::Get(browser_view->GetWidget()));
+  chromeos::ImmersiveFullscreenControllerTestApi immersive_test_api(
+      chromeos::ImmersiveFullscreenController::Get(browser_view->GetWidget()));
   immersive_test_api.SetupForTest();
 
   ImmersiveModeController* const immersive_mode_controller =
diff --git a/chrome/browser/ui/views/location_bar/zoom_bubble_view_browsertest.cc b/chrome/browser/ui/views/location_bar/zoom_bubble_view_browsertest.cc
index 30e1b14..3b5dc3b 100644
--- a/chrome/browser/ui/views/location_bar/zoom_bubble_view_browsertest.cc
+++ b/chrome/browser/ui/views/location_bar/zoom_bubble_view_browsertest.cc
@@ -23,8 +23,8 @@
 #include "ui/views/test/widget_test.h"
 
 #if defined(OS_CHROMEOS)
-#include "ash/public/cpp/immersive/immersive_fullscreen_controller_test_api.h"
 #include "chrome/browser/ui/views/frame/immersive_mode_controller_ash.h"
+#include "chromeos/ui/frame/immersive/immersive_fullscreen_controller_test_api.h"
 #endif
 
 #if defined(OS_MAC)
@@ -176,7 +176,7 @@
 
   ImmersiveModeController* immersive_controller =
       browser_view->immersive_mode_controller();
-  ash::ImmersiveFullscreenControllerTestApi(
+  chromeos::ImmersiveFullscreenControllerTestApi(
       static_cast<ImmersiveModeControllerAsh*>(immersive_controller)
           ->controller())
       .SetupForTest();
diff --git a/chrome/browser/ui/views/passwords/account_chooser_dialog_view.cc b/chrome/browser/ui/views/passwords/account_chooser_dialog_view.cc
index 347239e1..f02929c4 100644
--- a/chrome/browser/ui/views/passwords/account_chooser_dialog_view.cc
+++ b/chrome/browser/ui/views/passwords/account_chooser_dialog_view.cc
@@ -104,18 +104,6 @@
   return false;
 }
 
-void AccountChooserDialogView::ButtonPressed(views::Button* sender,
-                                             const ui::Event& event) {
-  CredentialsItemView* view = static_cast<CredentialsItemView*>(sender);
-  // On Mac the button click event may be dispatched after the dialog was
-  // hidden. Thus, the controller can be null.
-  if (controller_) {
-    controller_->OnChooseCredentials(
-        *view->form(),
-        password_manager::CredentialType::CREDENTIAL_TYPE_PASSWORD);
-  }
-}
-
 void AccountChooserDialogView::InitWindow() {
   SetLayoutManager(std::make_unique<views::FillLayout>());
 
@@ -129,7 +117,10 @@
     const auto titles = GetCredentialLabelsForAccountChooser(*form);
     auto* credential_view =
         list_view->AddChildView(std::make_unique<CredentialsItemView>(
-            this, titles.first, titles.second, form.get(),
+            base::BindRepeating(
+                &AccountChooserDialogView::CredentialsItemPressed,
+                base::Unretained(this), base::Unretained(form.get())),
+            titles.first, titles.second, form.get(),
             content::BrowserContext::GetDefaultStoragePartition(
                 Profile::FromBrowserContext(web_contents_->GetBrowserContext()))
                 ->GetURLLoaderFactoryForBrowserProcess()
@@ -148,6 +139,16 @@
   scroll_view->ClipHeightTo(0, kMaxVisibleItems * item_height);
 }
 
+void AccountChooserDialogView::CredentialsItemPressed(
+    const autofill::PasswordForm* form) {
+  // On Mac the button click event may be dispatched after the dialog was
+  // hidden. Thus, the controller can be null.
+  if (controller_) {
+    controller_->OnChooseCredentials(
+        *form, password_manager::CredentialType::CREDENTIAL_TYPE_PASSWORD);
+  }
+}
+
 AccountChooserPrompt* CreateAccountChooserPromptView(
     CredentialManagerDialogController* controller,
     content::WebContents* web_contents) {
diff --git a/chrome/browser/ui/views/passwords/account_chooser_dialog_view.h b/chrome/browser/ui/views/passwords/account_chooser_dialog_view.h
index f48d5cf6..32ae645a 100644
--- a/chrome/browser/ui/views/passwords/account_chooser_dialog_view.h
+++ b/chrome/browser/ui/views/passwords/account_chooser_dialog_view.h
@@ -8,7 +8,10 @@
 #include "base/macros.h"
 #include "chrome/browser/ui/passwords/password_dialog_prompts.h"
 #include "ui/views/bubble/bubble_dialog_delegate_view.h"
-#include "ui/views/controls/button/button.h"
+
+namespace autofill {
+struct PasswordForm;
+}
 
 namespace content {
 class WebContents;
@@ -17,7 +20,6 @@
 class CredentialManagerDialogController;
 
 class AccountChooserDialogView : public views::BubbleDialogDelegateView,
-                                 public views::ButtonListener,
                                  public AccountChooserPrompt {
  public:
   AccountChooserDialogView(CredentialManagerDialogController* controller,
@@ -38,12 +40,11 @@
   // DialogDelegate:
   bool Accept() override;
 
-  // ButtonListener:
-  void ButtonPressed(views::Button* sender, const ui::Event& event) override;
-
   // Sets up the child views.
   void InitWindow();
 
+  void CredentialsItemPressed(const autofill::PasswordForm* form);
+
   // A weak pointer to the controller.
   CredentialManagerDialogController* controller_;
   content::WebContents* web_contents_;
diff --git a/chrome/browser/ui/views/passwords/credentials_item_view.cc b/chrome/browser/ui/views/passwords/credentials_item_view.cc
index b4fff3b7..642e3e9 100644
--- a/chrome/browser/ui/views/passwords/credentials_item_view.cc
+++ b/chrome/browser/ui/views/passwords/credentials_item_view.cc
@@ -56,14 +56,14 @@
 }  // namespace
 
 CredentialsItemView::CredentialsItemView(
-    views::ButtonListener* button_listener,
+    PressedCallback callback,
     const base::string16& upper_text,
     const base::string16& lower_text,
     const autofill::PasswordForm* form,
     network::mojom::URLLoaderFactory* loader_factory,
     int upper_text_style,
     int lower_text_style)
-    : Button(button_listener), form_(form) {
+    : Button(std::move(callback)) {
   SetNotifyEnterExitOnChild(true);
   views::BoxLayout* layout =
       SetLayoutManager(std::make_unique<views::BoxLayout>(
@@ -83,10 +83,10 @@
   DCHECK(image.Width() >= kAvatarImageSize &&
          image.Height() >= kAvatarImageSize);
   UpdateAvatar(image.AsImageSkia());
-  if (form_->icon_url.is_valid()) {
+  if (form->icon_url.is_valid()) {
     // Fetch the actual avatar.
     AccountAvatarFetcher* fetcher = new AccountAvatarFetcher(
-        form_->icon_url, weak_ptr_factory_.GetWeakPtr());
+        form->icon_url, weak_ptr_factory_.GetWeakPtr());
     fetcher->Start(loader_factory);
   }
   AddChildView(std::move(image_view));
@@ -121,9 +121,9 @@
     lower_label_ = text_container->AddChildView(std::move(lower_label));
   }
 
-  if (form_->is_public_suffix_match) {
+  if (form->is_public_suffix_match) {
     info_icon_ = AddChildView(std::make_unique<views::TooltipIcon>(
-        base::UTF8ToUTF16(form_->url.GetOrigin().spec())));
+        base::UTF8ToUTF16(form->url.GetOrigin().spec())));
   }
 
   if (!upper_text.empty() && !lower_text.empty())
diff --git a/chrome/browser/ui/views/passwords/credentials_item_view.h b/chrome/browser/ui/views/passwords/credentials_item_view.h
index c2747f0..be7fddc 100644
--- a/chrome/browser/ui/views/passwords/credentials_item_view.h
+++ b/chrome/browser/ui/views/passwords/credentials_item_view.h
@@ -34,7 +34,7 @@
 class CredentialsItemView : public AccountAvatarFetcherDelegate,
                             public views::Button {
  public:
-  CredentialsItemView(views::ButtonListener* button_listener,
+  CredentialsItemView(PressedCallback callback,
                       const base::string16& upper_text,
                       const base::string16& lower_text,
                       const autofill::PasswordForm* form,
@@ -47,8 +47,6 @@
   // to the view. If |store| is kProfileStore, removes any existing icon.
   void SetStoreIndicatorIcon(autofill::PasswordForm::Store store);
 
-  const autofill::PasswordForm* form() const { return form_; }
-
   // AccountAvatarFetcherDelegate:
   void UpdateAvatar(const gfx::ImageSkia& image) override;
 
@@ -58,8 +56,6 @@
   // views::View:
   void OnPaintBackground(gfx::Canvas* canvas) override;
 
-  const autofill::PasswordForm* const form_;
-
   views::ImageView* image_view_;
 
   // Optional right-aligned icon to distinguish account store credentials and
diff --git a/chrome/browser/ui/views/passwords/password_auto_sign_in_view.cc b/chrome/browser/ui/views/passwords/password_auto_sign_in_view.cc
index 79c215f..96bbe57 100644
--- a/chrome/browser/ui/views/passwords/password_auto_sign_in_view.cc
+++ b/chrome/browser/ui/views/passwords/password_auto_sign_in_view.cc
@@ -43,7 +43,7 @@
 
   CredentialsItemView* credential =
       AddChildView(std::make_unique<CredentialsItemView>(
-          nullptr,
+          views::Button::PressedCallback(),
           l10n_util::GetStringUTF16(IDS_MANAGE_PASSWORDS_AUTO_SIGNIN_TITLE_MD),
           form.username_value, &form,
           content::BrowserContext::GetDefaultStoragePartition(
diff --git a/chrome/browser/ui/views/passwords/password_items_view.cc b/chrome/browser/ui/views/passwords/password_items_view.cc
index 69dad05..2e02647d 100644
--- a/chrome/browser/ui/views/passwords/password_items_view.cc
+++ b/chrome/browser/ui/views/passwords/password_items_view.cc
@@ -46,9 +46,6 @@
 
 namespace {
 
-constexpr int kDeleteButtonTag = 1;
-constexpr int kUndoButtonTag = 2;
-
 // Column set identifiers for displaying or undoing removal of credentials.
 // All of them allocate space differently.
 enum PasswordItemsViewColumnSetType {
@@ -139,7 +136,7 @@
 
 // An entry for each credential. Relays delete/undo actions associated with
 // this password row to parent dialog.
-class PasswordItemsView::PasswordRow : public views::ButtonListener {
+class PasswordItemsView::PasswordRow {
  public:
   PasswordRow(PasswordItemsView* parent,
               const autofill::PasswordForm* password_form);
@@ -152,8 +149,8 @@
   void AddPasswordRow(views::GridLayout* layout,
                       PasswordItemsViewColumnSetType type_id);
 
-  // views::ButtonListener:
-  void ButtonPressed(views::Button* sender, const ui::Event& event) override;
+  void DeleteButtonPressed();
+  void UndoButtonPressed();
 
   PasswordItemsView* const parent_;
   const autofill::PasswordForm* const password_form_;
@@ -184,8 +181,9 @@
           views::style::CONTEXT_DIALOG_BODY_TEXT))
       ->SetHorizontalAlignment(gfx::ALIGN_LEFT);
   auto* undo_button = layout->AddView(std::make_unique<views::MdTextButton>(
-      this, l10n_util::GetStringUTF16(IDS_MANAGE_PASSWORDS_UNDO)));
-  undo_button->set_tag(kUndoButtonTag);
+      base::BindRepeating(&PasswordRow::UndoButtonPressed,
+                          base::Unretained(this)),
+      l10n_util::GetStringUTF16(IDS_MANAGE_PASSWORDS_UNDO)));
   undo_button->SetFocusForPlatform();
   undo_button->SetTooltipText(l10n_util::GetStringFUTF16(
       IDS_MANAGE_PASSWORDS_UNDO_TOOLTIP, GetDisplayUsername(*password_form_)));
@@ -233,22 +231,28 @@
     separator->SetCanProcessEventsWithinSubtree(false);
   }
 
-  auto* delete_button = layout->AddView(
-      views::CreateVectorImageButtonWithNativeTheme(this, kTrashCanIcon));
-  delete_button->set_tag(kDeleteButtonTag);
+  auto* delete_button =
+      layout->AddView(views::CreateVectorImageButtonWithNativeTheme(
+          base::BindRepeating(&PasswordRow::DeleteButtonPressed,
+                              base::Unretained(this)),
+          kTrashCanIcon));
   delete_button->SetFocusForPlatform();
   delete_button->SetTooltipText(l10n_util::GetStringFUTF16(
       IDS_MANAGE_PASSWORDS_DELETE, GetDisplayUsername(*password_form_)));
 }
 
-void PasswordItemsView::PasswordRow::ButtonPressed(views::Button* sender,
-                                                   const ui::Event& event) {
-  DCHECK(sender->tag() == kDeleteButtonTag || sender->tag() == kUndoButtonTag);
-  deleted_ = sender->tag() == kDeleteButtonTag;
+void PasswordItemsView::PasswordRow::DeleteButtonPressed() {
+  deleted_ = true;
   parent_->NotifyPasswordFormAction(
       *password_form_,
-      deleted_ ? PasswordBubbleControllerBase::PasswordAction::kRemovePassword
-               : PasswordBubbleControllerBase::PasswordAction::kAddPassword);
+      PasswordBubbleControllerBase::PasswordAction::kRemovePassword);
+}
+
+void PasswordItemsView::PasswordRow::UndoButtonPressed() {
+  deleted_ = false;
+  parent_->NotifyPasswordFormAction(
+      *password_form_,
+      PasswordBubbleControllerBase::PasswordAction::kAddPassword);
 }
 
 PasswordItemsView::PasswordItemsView(content::WebContents* web_contents,
@@ -259,7 +263,14 @@
       controller_(PasswordsModelDelegateFromWebContents(web_contents)) {
   SetButtons(ui::DIALOG_BUTTON_OK);
   SetExtraView(std::make_unique<views::MdTextButton>(
-      this,
+      base::BindRepeating(
+          [](PasswordItemsView* items) {
+            items->controller_.OnManageClicked(
+                password_manager::ManagePasswordsReferrer::
+                    kManagePasswordsBubble);
+            items->CloseBubble();
+          },
+          base::Unretained(this)),
       l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_MANAGE_PASSWORDS_BUTTON)));
 
   if (controller_.local_credentials().empty()) {
@@ -341,13 +352,6 @@
   return gfx::Size(width, GetHeightForWidth(width));
 }
 
-void PasswordItemsView::ButtonPressed(views::Button* sender,
-                                      const ui::Event& event) {
-  controller_.OnManageClicked(
-      password_manager::ManagePasswordsReferrer::kManagePasswordsBubble);
-  CloseBubble();
-}
-
 void PasswordItemsView::OnFaviconReady(const gfx::Image& favicon) {
   if (!favicon.IsEmpty()) {
     favicon_ = favicon;
diff --git a/chrome/browser/ui/views/passwords/password_items_view.h b/chrome/browser/ui/views/passwords/password_items_view.h
index 5ef440c..d262d05 100644
--- a/chrome/browser/ui/views/passwords/password_items_view.h
+++ b/chrome/browser/ui/views/passwords/password_items_view.h
@@ -12,14 +12,12 @@
 #include "chrome/browser/ui/passwords/bubble_controllers/items_bubble_controller.h"
 #include "chrome/browser/ui/views/passwords/password_bubble_view_base.h"
 #include "components/autofill/core/common/password_form.h"
-#include "ui/views/controls/button/button.h"
 #include "ui/views/view.h"
 
 // A dialog for managing stored password and federated login information for a
 // specific site. A user can remove managed credentials for the site via this
 // dialog.
-class PasswordItemsView : public PasswordBubbleViewBase,
-                          public views::ButtonListener {
+class PasswordItemsView : public PasswordBubbleViewBase {
  public:
   PasswordItemsView(content::WebContents* web_contents,
                     views::View* anchor_view);
@@ -41,9 +39,6 @@
   bool ShouldShowCloseButton() const override;
   gfx::Size CalculatePreferredSize() const override;
 
-  // views::ButtonListener:
-  void ButtonPressed(views::Button* sender, const ui::Event& event) override;
-
   // Called when the favicon is loaded. If |favicon| isn't empty, it sets
   // |favicon_| and invokes RecreateLayout().
   void OnFaviconReady(const gfx::Image& favicon);
diff --git a/chrome/browser/ui/views/passwords/password_save_unsynced_credentials_locally_view.cc b/chrome/browser/ui/views/passwords/password_save_unsynced_credentials_locally_view.cc
index 78d4849..8a2ecb98 100644
--- a/chrome/browser/ui/views/passwords/password_save_unsynced_credentials_locally_view.cc
+++ b/chrome/browser/ui/views/passwords/password_save_unsynced_credentials_locally_view.cc
@@ -54,16 +54,6 @@
   return &controller_;
 }
 
-void PasswordSaveUnsyncedCredentialsLocallyView::ButtonPressed(
-    views::Button* sender,
-    const ui::Event&) {
-  auto* checkbox = static_cast<views::Checkbox*>(sender);
-  num_selected_checkboxes_ += checkbox->GetChecked() ? 1 : -1;
-  GetOkButton()->SetState(num_selected_checkboxes_
-                              ? views::Button::ButtonState::STATE_NORMAL
-                              : views::Button::ButtonState::STATE_DISABLED);
-}
-
 const PasswordBubbleControllerBase*
 PasswordSaveUnsyncedCredentialsLocallyView::GetController() const {
   return &controller_;
@@ -90,8 +80,11 @@
   for (const autofill::PasswordForm& form :
        controller_.unsynced_credentials()) {
     auto* row_view = AddChildView(std::make_unique<views::View>());
-    auto* checkbox = row_view->AddChildView(
-        std::make_unique<views::Checkbox>(base::string16(), this));
+    auto* checkbox = row_view->AddChildView(std::make_unique<views::Checkbox>(
+        base::string16(), views::Button::PressedCallback()));
+    checkbox->set_callback(base::BindRepeating(
+        &PasswordSaveUnsyncedCredentialsLocallyView::ButtonPressed,
+        base::Unretained(this), base::Unretained(checkbox)));
     checkbox->SetBorder(views::CreateEmptyBorder(
         0, 0, 0, /*right=*/
         ChromeLayoutProvider::Get()->GetDistanceMetric(
@@ -112,6 +105,14 @@
   }
 }
 
+void PasswordSaveUnsyncedCredentialsLocallyView::ButtonPressed(
+    views::Checkbox* checkbox) {
+  num_selected_checkboxes_ += checkbox->GetChecked() ? 1 : -1;
+  GetOkButton()->SetState(num_selected_checkboxes_
+                              ? views::Button::ButtonState::STATE_NORMAL
+                              : views::Button::ButtonState::STATE_DISABLED);
+}
+
 void PasswordSaveUnsyncedCredentialsLocallyView::OnSaveClicked() {
   std::vector<bool> was_credential_selected;
   for (const auto* checkbox : checkboxes_) {
diff --git a/chrome/browser/ui/views/passwords/password_save_unsynced_credentials_locally_view.h b/chrome/browser/ui/views/passwords/password_save_unsynced_credentials_locally_view.h
index 81e56e114..e30f348 100644
--- a/chrome/browser/ui/views/passwords/password_save_unsynced_credentials_locally_view.h
+++ b/chrome/browser/ui/views/passwords/password_save_unsynced_credentials_locally_view.h
@@ -13,7 +13,6 @@
 #include "chrome/browser/ui/views/passwords/password_bubble_view_base.h"
 #include "components/autofill/core/common/password_form.h"
 #include "content/public/browser/web_contents.h"
-#include "ui/views/controls/button/button.h"
 #include "ui/views/controls/button/checkbox.h"
 #include "ui/views/view.h"
 
@@ -21,8 +20,7 @@
 // to the user account. By clicking the save button, the user can save those
 // passwords locally.
 class PasswordSaveUnsyncedCredentialsLocallyView
-    : public PasswordBubbleViewBase,
-      public views::ButtonListener {
+    : public PasswordBubbleViewBase {
  public:
   PasswordSaveUnsyncedCredentialsLocallyView(content::WebContents* web_contents,
                                              views::View* anchor_view);
@@ -33,15 +31,14 @@
   PasswordBubbleControllerBase* GetController() override;
   const PasswordBubbleControllerBase* GetController() const override;
 
-  // views::ButtonListener overrides.
-  void ButtonPressed(views::Button* sender, const ui::Event&) override;
-
   // LocationBarBubbleDelegateView overrides.
   bool ShouldShowCloseButton() const override;
   gfx::Size CalculatePreferredSize() const override;
 
   void CreateLayout();
 
+  void ButtonPressed(views::Checkbox* checkbox);
+
   void OnSaveClicked();
 
   SaveUnsyncedCredentialsLocallyBubbleController controller_;
diff --git a/chrome/browser/ui/views/passwords/password_save_update_view.cc b/chrome/browser/ui/views/passwords/password_save_update_view.cc
index e62db143..0cf6b33a 100644
--- a/chrome/browser/ui/views/passwords/password_save_update_view.cc
+++ b/chrome/browser/ui/views/passwords/password_save_update_view.cc
@@ -157,9 +157,9 @@
 }
 
 std::unique_ptr<views::ToggleImageButton> CreatePasswordViewButton(
-    views::ButtonListener* listener,
+    views::Button::PressedCallback callback,
     bool are_passwords_revealed) {
-  auto button = std::make_unique<views::ToggleImageButton>(listener);
+  auto button = std::make_unique<views::ToggleImageButton>(std::move(callback));
   button->SetFocusForPlatform();
   button->SetInstallFocusRingOnFocus(true);
   button->SetRequestFocusOnPress(true);
@@ -276,7 +276,8 @@
     SetLayoutManager(std::make_unique<views::FillLayout>());
     const auto titles = GetCredentialLabelsForAccountChooser(password_form);
     AddChildView(std::make_unique<CredentialsItemView>(
-                     nullptr, titles.first, titles.second, &password_form,
+                     views::Button::PressedCallback(), titles.first,
+                     titles.second, &password_form,
                      content::BrowserContext::GetDefaultStoragePartition(
                          controller_.GetProfile())
                          ->GetURLLoaderFactoryForBrowserProcess()
@@ -293,7 +294,11 @@
         &PasswordSaveUpdateView::OnContentChanged, base::Unretained(this)));
 
     std::unique_ptr<views::ToggleImageButton> password_view_button =
-        CreatePasswordViewButton(this, are_passwords_revealed_);
+        CreatePasswordViewButton(
+            base::BindRepeating(
+                &PasswordSaveUpdateView::TogglePasswordVisibility,
+                base::Unretained(this)),
+            are_passwords_revealed_);
 
     views::GridLayout* layout =
         SetLayoutManager(std::make_unique<views::GridLayout>());
@@ -338,12 +343,6 @@
   return true;
 }
 
-void PasswordSaveUpdateView::ButtonPressed(views::Button* sender,
-                                           const ui::Event& event) {
-  DCHECK(sender == password_view_button_);
-  TogglePasswordVisibility();
-}
-
 gfx::Size PasswordSaveUpdateView::CalculatePreferredSize() const {
   const int width = ChromeLayoutProvider::Get()->GetDistanceMetric(
                         views::DISTANCE_BUBBLE_PREFERRED_WIDTH) -
diff --git a/chrome/browser/ui/views/passwords/password_save_update_view.h b/chrome/browser/ui/views/passwords/password_save_update_view.h
index 78620ed3d..87f108d 100644
--- a/chrome/browser/ui/views/passwords/password_save_update_view.h
+++ b/chrome/browser/ui/views/passwords/password_save_update_view.h
@@ -7,7 +7,6 @@
 
 #include "chrome/browser/ui/passwords/bubble_controllers/save_update_bubble_controller.h"
 #include "chrome/browser/ui/views/passwords/password_bubble_view_base.h"
-#include "ui/views/controls/button/button.h"
 #include "ui/views/view.h"
 
 namespace views {
@@ -20,8 +19,7 @@
 // A view offering the user the ability to save or update credentials (depending
 // on |is_update_bubble|). Contains a username and password field, along with a
 // "Save"/"Update" button and a "Never"/"Nope" button.
-class PasswordSaveUpdateView : public PasswordBubbleViewBase,
-                               public views::ButtonListener {
+class PasswordSaveUpdateView : public PasswordBubbleViewBase {
  public:
   PasswordSaveUpdateView(content::WebContents* web_contents,
                          views::View* anchor_view,
@@ -36,9 +34,6 @@
   PasswordBubbleControllerBase* GetController() override;
   const PasswordBubbleControllerBase* GetController() const override;
 
-  // views::ButtonListener:
-  void ButtonPressed(views::Button* sender, const ui::Event& event) override;
-
   // PasswordBubbleViewBase:
   gfx::Size CalculatePreferredSize() const override;
   views::View* GetInitiallyFocusedView() override;
diff --git a/chrome/browser/ui/views/passwords/password_save_update_with_account_store_view.cc b/chrome/browser/ui/views/passwords/password_save_update_with_account_store_view.cc
index 3318c019..9612985 100644
--- a/chrome/browser/ui/views/passwords/password_save_update_with_account_store_view.cc
+++ b/chrome/browser/ui/views/passwords/password_save_update_with_account_store_view.cc
@@ -194,9 +194,9 @@
 }
 
 std::unique_ptr<views::ToggleImageButton> CreatePasswordViewButton(
-    views::ButtonListener* listener,
+    views::Button::PressedCallback callback,
     bool are_passwords_revealed) {
-  auto button = std::make_unique<views::ToggleImageButton>(listener);
+  auto button = std::make_unique<views::ToggleImageButton>(std::move(callback));
   button->SetFocusForPlatform();
   button->SetInstallFocusRingOnFocus(true);
   button->SetRequestFocusOnPress(true);
@@ -402,16 +402,15 @@
     if (destination_dropdown)
       AddChildView(std::move(destination_dropdown));
 
-    std::pair<base::string16, base::string16> titles =
-        GetCredentialLabelsForAccountChooser(password_form);
-    CredentialsItemView* credential_view = new CredentialsItemView(
-        nullptr, titles.first, titles.second, &password_form,
-        content::BrowserContext::GetDefaultStoragePartition(
-            controller_.GetProfile())
-            ->GetURLLoaderFactoryForBrowserProcess()
-            .get());
-    credential_view->SetEnabled(false);
-    AddChildView(credential_view);
+    const auto titles = GetCredentialLabelsForAccountChooser(password_form);
+    AddChildView(std::make_unique<CredentialsItemView>(
+                     views::Button::PressedCallback(), titles.first,
+                     titles.second, &password_form,
+                     content::BrowserContext::GetDefaultStoragePartition(
+                         controller_.GetProfile())
+                         ->GetURLLoaderFactoryForBrowserProcess()
+                         .get()))
+        ->SetEnabled(false);
   } else {
     std::unique_ptr<views::EditableCombobox> username_dropdown =
         CreateUsernameEditableCombobox(password_form);
@@ -424,7 +423,11 @@
         &PasswordSaveUpdateWithAccountStoreView::OnContentChanged,
         base::Unretained(this)));
     std::unique_ptr<views::ToggleImageButton> password_view_button =
-        CreatePasswordViewButton(this, are_passwords_revealed_);
+        CreatePasswordViewButton(
+            base::BindRepeating(&PasswordSaveUpdateWithAccountStoreView::
+                                    TogglePasswordVisibility,
+                                base::Unretained(this)),
+            are_passwords_revealed_);
     // Set up layout:
     SetLayoutManager(std::make_unique<AutoResizingLayout>());
     views::View* root_view = AddChildView(std::make_unique<views::View>());
@@ -518,14 +521,6 @@
   return &controller_;
 }
 
-void PasswordSaveUpdateWithAccountStoreView::ButtonPressed(
-    views::Button* sender,
-    const ui::Event& event) {
-  DCHECK(sender);
-  DCHECK(sender == password_view_button_);
-  TogglePasswordVisibility();
-}
-
 void PasswordSaveUpdateWithAccountStoreView::DestinationChanged() {
   bool is_account_store_selected =
       destination_dropdown_->GetSelectedIndex() == 0;
diff --git a/chrome/browser/ui/views/passwords/password_save_update_with_account_store_view.h b/chrome/browser/ui/views/passwords/password_save_update_with_account_store_view.h
index cd8e9465..0ea3aba 100644
--- a/chrome/browser/ui/views/passwords/password_save_update_with_account_store_view.h
+++ b/chrome/browser/ui/views/passwords/password_save_update_with_account_store_view.h
@@ -7,7 +7,6 @@
 
 #include "chrome/browser/ui/passwords/bubble_controllers/save_update_with_account_store_bubble_controller.h"
 #include "chrome/browser/ui/views/passwords/password_bubble_view_base.h"
-#include "ui/views/controls/button/button.h"
 #include "ui/views/layout/animating_layout_manager.h"
 #include "ui/views/view.h"
 
@@ -31,7 +30,6 @@
 // button.
 class PasswordSaveUpdateWithAccountStoreView
     : public PasswordBubbleViewBase,
-      public views::ButtonListener,
       public views::WidgetObserver,
       public views::AnimatingLayoutManager::Observer {
  public:
@@ -60,9 +58,6 @@
   PasswordBubbleControllerBase* GetController() override;
   const PasswordBubbleControllerBase* GetController() const override;
 
-  // views::ButtonListener:
-  void ButtonPressed(views::Button* sender, const ui::Event& event) override;
-
   // views::WidgetObserver:
   void OnWidgetDestroying(views::Widget* widget) override;
 
diff --git a/chrome/browser/ui/views/profiles/avatar_toolbar_button_delegate.cc b/chrome/browser/ui/views/profiles/avatar_toolbar_button_delegate.cc
index b4f5bc98..8106d518 100644
--- a/chrome/browser/ui/views/profiles/avatar_toolbar_button_delegate.cc
+++ b/chrome/browser/ui/views/profiles/avatar_toolbar_button_delegate.cc
@@ -265,7 +265,7 @@
   base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
       FROM_HERE,
       base::BindOnce(&AvatarToolbarButtonDelegate::OnIdentityAnimationTimeout,
-                     weak_ptr_factory_.GetWeakPtr()),
+                     weak_ptr_factory_.GetWeakPtr(), user_identity.account_id),
       kIdentityAnimationDuration);
 }
 
@@ -386,7 +386,17 @@
   avatar_toolbar_button_->UpdateIcon();
 }
 
-void AvatarToolbarButtonDelegate::OnIdentityAnimationTimeout() {
+void AvatarToolbarButtonDelegate::OnIdentityAnimationTimeout(
+    CoreAccountId account_id) {
+  CoreAccountInfo user_identity =
+      IdentityManagerFactory::GetForProfile(profile_)->GetPrimaryAccountInfo(
+          signin::ConsentLevel::kNotRequired);
+  // If another account is signed-in then the one that initiated this animation,
+  // don't hide it. There's one more pending OnIdentityAnimationTimeout() that
+  // will properly hide it after the proper delay.
+  if (!user_identity.IsEmpty() && user_identity.account_id != account_id)
+    return;
+
   DCHECK_EQ(identity_animation_state_,
             IdentityAnimationState::kShowingUntilTimeout);
   identity_animation_state_ =
diff --git a/chrome/browser/ui/views/profiles/avatar_toolbar_button_delegate.h b/chrome/browser/ui/views/profiles/avatar_toolbar_button_delegate.h
index 89ab666..d18a83d6 100644
--- a/chrome/browser/ui/views/profiles/avatar_toolbar_button_delegate.h
+++ b/chrome/browser/ui/views/profiles/avatar_toolbar_button_delegate.h
@@ -95,8 +95,8 @@
   // Initiates showing the identity.
   void OnUserIdentityChanged();
 
+  void OnIdentityAnimationTimeout(CoreAccountId account_id);
   // Called after the user interacted with the button or after some timeout.
-  void OnIdentityAnimationTimeout();
   void MaybeHideIdentityAnimation();
   void HideHighlightAnimation();
 
diff --git a/chrome/browser/ui/views/read_later/read_later_button.cc b/chrome/browser/ui/views/read_later/read_later_button.cc
index 00b2f4d..89027e6 100644
--- a/chrome/browser/ui/views/read_later/read_later_button.cc
+++ b/chrome/browser/ui/views/read_later/read_later_button.cc
@@ -29,6 +29,7 @@
   GetViewAccessibility().OverrideHasPopup(ax::mojom::HasPopup::kMenu);
   button_controller()->set_notify_action(
       views::ButtonController::NotifyAction::kOnPress);
+  SetHorizontalAlignment(gfx::ALIGN_LEFT);
   // We don't want to use ToolbarButton::SetHighlight here because it adds a
   // border around the button.
   LabelButton::SetText(l10n_util::GetStringUTF16(IDS_READ_LATER_TITLE));
diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc b/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc
index b0891c30..88a6c75 100644
--- a/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc
+++ b/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc
@@ -82,7 +82,6 @@
 
 #if defined(OS_CHROMEOS)
 #include "ash/public/cpp/ash_switches.h"
-#include "ash/public/cpp/immersive/immersive_fullscreen_controller_test_api.h"
 #include "ash/public/cpp/split_view_test_api.h"
 #include "ash/public/cpp/test/shell_test_api.h"
 #include "ash/public/cpp/window_properties.h"
@@ -94,6 +93,7 @@
 #include "chrome/browser/ui/views/frame/immersive_mode_controller_ash.h"
 #include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
 #include "chrome/browser/web_applications/test/test_system_web_app_installation.h"
+#include "chromeos/ui/frame/immersive/immersive_fullscreen_controller_test_api.h"
 #include "content/public/common/service_names.mojom.h"
 #include "ui/aura/client/cursor_client.h"
 #include "ui/aura/client/screen_position_client.h"
@@ -3851,7 +3851,7 @@
   BrowserView* browser_view2 = BrowserView::GetBrowserViewForBrowser(browser2);
   ImmersiveModeController* immersive_controller2 =
       browser_view2->immersive_mode_controller();
-  ash::ImmersiveFullscreenControllerTestApi(
+  chromeos::ImmersiveFullscreenControllerTestApi(
       static_cast<ImmersiveModeControllerAsh*>(immersive_controller2)
           ->controller())
       .SetupForTest();
diff --git a/chrome/browser/ui/views/translate/translate_bubble_view.cc b/chrome/browser/ui/views/translate/translate_bubble_view.cc
index 1c738c4..34911e3d 100644
--- a/chrome/browser/ui/views/translate/translate_bubble_view.cc
+++ b/chrome/browser/ui/views/translate/translate_bubble_view.cc
@@ -246,48 +246,6 @@
     model_->ShowError(error_type_);
 }
 
-void TranslateBubbleView::ButtonPressed(views::Button* sender,
-                                        const ui::Event& event) {
-  switch (static_cast<ButtonID>(sender->GetID())) {
-    case BUTTON_ID_DONE: {
-      ConfirmAdvancedOptions();
-      break;
-    }
-    case BUTTON_ID_TRY_AGAIN: {
-      model_->Translate();
-      translate::ReportUiAction(translate::TRY_AGAIN_BUTTON_CLICKED);
-      break;
-    }
-    case BUTTON_ID_ALWAYS_TRANSLATE: {
-      views::Checkbox* always_checkbox = GetAlwaysTranslateCheckbox();
-      DCHECK(always_checkbox);
-      should_always_translate_ = always_checkbox->GetChecked();
-      // In the tab UI the always translate button should apply immediately
-      // except for in an advanced view.
-      if (GetViewState() != TranslateBubbleModel::VIEW_STATE_SOURCE_LANGUAGE) {
-        model_->SetAlwaysTranslate(should_always_translate_);
-      }
-      translate::ReportUiAction(should_always_translate_
-                                    ? translate::ALWAYS_TRANSLATE_CHECKED
-                                    : translate::ALWAYS_TRANSLATE_UNCHECKED);
-      break;
-    }
-    case BUTTON_ID_OPTIONS_MENU: {
-      ShowOptionsMenu(sender);
-      break;
-    }
-    case BUTTON_ID_CLOSE: {
-      translate::ReportUiAction(translate::CLOSE_BUTTON_CLICKED);
-      GetWidget()->Close();
-      break;
-    }
-    case BUTTON_ID_RESET: {
-      ResetLanguage();
-      break;
-    }
-  }
-}
-
 views::View* TranslateBubbleView::GetInitiallyFocusedView() {
   return GetCurrentView()->GetNextFocusableView();
 }
@@ -601,6 +559,17 @@
   translate::ReportUiAction(translate::TARGET_LANGUAGE_MENU_CLICKED);
 }
 
+void TranslateBubbleView::AlwaysTranslatePressed() {
+  should_always_translate_ = GetAlwaysTranslateCheckbox()->GetChecked();
+  translate::ReportUiAction(should_always_translate_
+                                ? translate::ALWAYS_TRANSLATE_CHECKED
+                                : translate::ALWAYS_TRANSLATE_UNCHECKED);
+  // In the tab UI the always translate button should apply immediately
+  // except for in an advanced view.
+  if (GetViewState() != TranslateBubbleModel::VIEW_STATE_SOURCE_LANGUAGE)
+    model_->SetAlwaysTranslate(should_always_translate_);
+}
+
 void TranslateBubbleView::UpdateChildVisibilities() {
   // Update the state of the always translate checkbox
   if (advanced_always_translate_checkbox_)
@@ -679,7 +648,8 @@
         l10n_util::GetStringFUTF16(
             IDS_TRANSLATE_BUBBLE_ALWAYS_TRANSLATE_LANG,
             model_->GetLanguageNameAt(model_->GetOriginalLanguageIndex())),
-        this);
+        base::BindRepeating(&TranslateBubbleView::AlwaysTranslatePressed,
+                            base::Unretained(this)));
     before_always_translate_checkbox->SetID(BUTTON_ID_ALWAYS_TRANSLATE);
     always_translate_checkbox_ =
         view->AddChildView(std::move(before_always_translate_checkbox));
@@ -724,8 +694,11 @@
 std::unique_ptr<views::View> TranslateBubbleView::CreateViewError() {
   auto translate_options_button =
       std::make_unique<views::MdTextButtonWithDownArrow>(
-          this,
+          views::Button::PressedCallback(),
           l10n_util::GetStringUTF16(IDS_TRANSLATE_BUBBLE_OPTIONS_MENU_BUTTON));
+  translate_options_button->set_callback(base::BindRepeating(
+      &TranslateBubbleView::ShowOptionsMenu, base::Unretained(this),
+      base::Unretained(translate_options_button.get())));
   translate_options_button->SetID(BUTTON_ID_OPTIONS_MENU);
   translate_options_button->SetRequestFocusOnPress(true);
   return CreateViewErrorNoTitle(std::move(translate_options_button));
@@ -783,7 +756,13 @@
       views::GridLayout::kFixedSize,
       provider->GetDistanceMetric(views::DISTANCE_UNRELATED_CONTROL_VERTICAL));
   auto try_again_button = std::make_unique<views::MdTextButton>(
-      this, l10n_util::GetStringUTF16(IDS_TRANSLATE_BUBBLE_TRY_AGAIN));
+      base::BindRepeating(
+          [](TranslateBubbleModel* model) {
+            translate::ReportUiAction(translate::TRY_AGAIN_BUTTON_CLICKED);
+            model->Translate();
+          },
+          base::Unretained(model_.get())),
+      l10n_util::GetStringUTF16(IDS_TRANSLATE_BUBBLE_TRY_AGAIN));
   try_again_button->SetID(BUTTON_ID_TRY_AGAIN);
   layout->AddView(std::move(try_again_button));
 
@@ -819,7 +798,9 @@
       model_->GetLanguageNameAt(model_->GetOriginalLanguageIndex());
   if (!is_in_incognito_window_ && !original_language.empty()) {
     advanced_always_translate_checkbox = std::make_unique<views::Checkbox>(
-        l10n_util::GetStringUTF16(IDS_TRANSLATE_BUBBLE_ALWAYS), this);
+        l10n_util::GetStringUTF16(IDS_TRANSLATE_BUBBLE_ALWAYS),
+        base::BindRepeating(&TranslateBubbleView::AlwaysTranslatePressed,
+                            base::Unretained(this)));
     advanced_always_translate_checkbox->SetID(BUTTON_ID_ALWAYS_TRANSLATE);
   }
 
@@ -828,7 +809,9 @@
   source_language_combobox_ = source_language_combobox.get();
 
   auto advanced_done_button = std::make_unique<views::MdTextButton>(
-      this, l10n_util::GetStringUTF16(IDS_DONE));
+      base::BindRepeating(&TranslateBubbleView::ConfirmAdvancedOptions,
+                          base::Unretained(this)),
+      l10n_util::GetStringUTF16(IDS_DONE));
   advanced_done_button->SetID(BUTTON_ID_DONE);
   advanced_done_button->SetIsDefault(true);
   advanced_done_button_source_ = advanced_done_button.get();
@@ -860,7 +843,9 @@
   target_language_combobox_ = target_language_combobox.get();
 
   auto advanced_done_button = std::make_unique<views::MdTextButton>(
-      this, l10n_util::GetStringUTF16(IDS_DONE));
+      base::BindRepeating(&TranslateBubbleView::ConfirmAdvancedOptions,
+                          base::Unretained(this)),
+      l10n_util::GetStringUTF16(IDS_DONE));
   advanced_done_button->SetID(BUTTON_ID_DONE);
   advanced_done_button->SetIsDefault(true);
   advanced_done_button_target_ = advanced_done_button.get();
@@ -995,7 +980,9 @@
   layout->SkipColumns(1);
 
   auto advanced_reset_button = std::make_unique<views::MdTextButton>(
-      this, l10n_util::GetStringUTF16(IDS_TRANSLATE_BUBBLE_RESET));
+      base::BindRepeating(&TranslateBubbleView::ResetLanguage,
+                          base::Unretained(this)),
+      l10n_util::GetStringUTF16(IDS_TRANSLATE_BUBBLE_RESET));
   advanced_reset_button->SetID(BUTTON_ID_RESET);
   layout->AddView(std::move(advanced_reset_button));
   layout->AddView(std::move(advanced_done_button));
@@ -1019,7 +1006,11 @@
 std::unique_ptr<views::Button> TranslateBubbleView::CreateOptionsMenuButton() {
   // Three dots options menu button
   auto tab_translate_options_button =
-      views::CreateVectorImageButtonWithNativeTheme(this, kBrowserToolsIcon);
+      views::CreateVectorImageButtonWithNativeTheme(
+          views::Button::PressedCallback(), kBrowserToolsIcon);
+  tab_translate_options_button->set_callback(base::BindRepeating(
+      &TranslateBubbleView::ShowOptionsMenu, base::Unretained(this),
+      base::Unretained(tab_translate_options_button.get())));
   InstallCircleHighlightPathGenerator(tab_translate_options_button.get());
   tab_translate_options_button->SetFocusForPlatform();
   tab_translate_options_button->SetAccessibleName(
@@ -1031,7 +1022,13 @@
 }
 
 std::unique_ptr<views::Button> TranslateBubbleView::CreateCloseButton() {
-  auto close_button = views::BubbleFrameView::CreateCloseButton(this);
+  auto close_button =
+      views::BubbleFrameView::CreateCloseButton(base::BindRepeating(
+          [](View* view) {
+            translate::ReportUiAction(translate::CLOSE_BUTTON_CLICKED);
+            view->GetWidget()->Close();
+          },
+          base::Unretained(this)));
   close_button->SetVisible(true);
   close_button->SetID(BUTTON_ID_CLOSE);
   return close_button;
diff --git a/chrome/browser/ui/views/translate/translate_bubble_view.h b/chrome/browser/ui/views/translate/translate_bubble_view.h
index 440325d..65ded91 100644
--- a/chrome/browser/ui/views/translate/translate_bubble_view.h
+++ b/chrome/browser/ui/views/translate/translate_bubble_view.h
@@ -24,7 +24,6 @@
 #include "components/translate/core/common/translate_errors.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "ui/base/models/simple_menu_model.h"
-#include "ui/views/controls/button/button.h"
 #include "ui/views/controls/image_view.h"
 #include "ui/views/controls/label.h"
 #include "ui/views/controls/menu/menu_runner.h"
@@ -42,7 +41,6 @@
 }  // namespace views
 
 class TranslateBubbleView : public LocationBarBubbleDelegateView,
-                            public views::ButtonListener,
                             public ui::SimpleMenuModel::Delegate,
                             public views::TabbedPaneListener {
  public:
@@ -90,9 +88,6 @@
   gfx::Size CalculatePreferredSize() const override;
   void OnWidgetClosing(views::Widget* widget) override;
 
-  // views::ButtonListener:
-  void ButtonPressed(views::Button* source, const ui::Event& event) override;
-
   // ui::SimpleMenuModel::Delegate:
   bool IsCommandIdChecked(int command_id) const override;
   bool IsCommandIdEnabled(int command_id) const override;
@@ -107,7 +102,7 @@
 
  private:
   enum ButtonID {
-    BUTTON_ID_DONE,
+    BUTTON_ID_DONE = 1,
     BUTTON_ID_TRY_AGAIN,
     BUTTON_ID_ALWAYS_TRANSLATE,
     BUTTON_ID_OPTIONS_MENU,
@@ -165,6 +160,8 @@
   void SourceLanguageChanged();
   void TargetLanguageChanged();
 
+  void AlwaysTranslatePressed();
+
   // Updates the visibilities of child views according to the current view type.
   void UpdateChildVisibilities();
 
diff --git a/chrome/browser/ui/views/translate/translate_bubble_view_unittest.cc b/chrome/browser/ui/views/translate/translate_bubble_view_unittest.cc
index 8e3b849..09cbdd54f 100644
--- a/chrome/browser/ui/views/translate/translate_bubble_view_unittest.cc
+++ b/chrome/browser/ui/views/translate/translate_bubble_view_unittest.cc
@@ -27,6 +27,7 @@
 #include "ui/views/controls/button/menu_button.h"
 #include "ui/views/controls/combobox/combobox.h"
 #include "ui/views/controls/styled_label.h"
+#include "ui/views/test/button_test_api.h"
 #include "ui/views/widget/widget.h"
 
 namespace {
@@ -189,12 +190,10 @@
   }
 
   void PressButton(TranslateBubbleView::ButtonID id) {
-    views::LabelButton button(nullptr, base::ASCIIToUTF16("hello"));
-    button.SetID(id);
-
-    bubble_->ButtonPressed(&button,
-                           ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_RETURN,
-                                        ui::DomCode::ENTER, ui::EF_NONE));
+    views::Button* button =
+        static_cast<views::Button*>(bubble_->GetViewByID(id));
+    views::test::ButtonTestApi(button).NotifyClick(ui::KeyEvent(
+        ui::ET_KEY_PRESSED, ui::VKEY_RETURN, ui::DomCode::ENTER, ui::EF_NONE));
   }
 
   void TearDown() override {
@@ -207,12 +206,7 @@
   bool denial_button_clicked() { return mock_model_->translation_declined_; }
 
   void TriggerOptionsMenu() {
-    views::Button* button = static_cast<views::Button*>(
-        bubble_->GetViewByID(TranslateBubbleView::BUTTON_ID_OPTIONS_MENU));
-    LOG(INFO) << button->GetID();
-    bubble_->ButtonPressed(button,
-                           ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_RETURN,
-                                        ui::DomCode::ENTER, ui::EF_NONE));
+    PressButton(TranslateBubbleView::BUTTON_ID_OPTIONS_MENU);
   }
 
   ui::SimpleMenuModel* options_menu_model() {
@@ -283,7 +277,6 @@
   EXPECT_FALSE(bubble_->always_translate_checkbox_->GetChecked());
 
   // Click the checkbox. The state is saved.
-  bubble_->always_translate_checkbox_->SetChecked(true);
   PressButton(TranslateBubbleView::BUTTON_ID_ALWAYS_TRANSLATE);
   EXPECT_TRUE(mock_model_->should_always_translate_);
   EXPECT_EQ(1, mock_model_->set_always_translate_called_count_);
@@ -327,7 +320,6 @@
   EXPECT_FALSE(bubble_->advanced_always_translate_checkbox_->GetChecked());
 
   // Click the checkbox. The state is not saved yet.
-  bubble_->advanced_always_translate_checkbox_->SetChecked(true);
   PressButton(TranslateBubbleView::BUTTON_ID_ALWAYS_TRANSLATE);
   EXPECT_FALSE(mock_model_->should_always_translate_);
   EXPECT_EQ(0, mock_model_->set_always_translate_called_count_);
diff --git a/chrome/browser/ui/views/web_apps/web_app_ash_interactive_ui_test.cc b/chrome/browser/ui/views/web_apps/web_app_ash_interactive_ui_test.cc
index 636a319..1811ce7e 100644
--- a/chrome/browser/ui/views/web_apps/web_app_ash_interactive_ui_test.cc
+++ b/chrome/browser/ui/views/web_apps/web_app_ash_interactive_ui_test.cc
@@ -2,7 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ash/public/cpp/immersive/immersive_fullscreen_controller_test_api.h"
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/exclusive_access/exclusive_access_manager.h"
 #include "chrome/browser/ui/exclusive_access/exclusive_access_test.h"
@@ -14,6 +13,7 @@
 #include "chrome/browser/ui/web_applications/web_app_controller_browsertest.h"
 #include "chrome/common/web_application_info.h"
 #include "chrome/test/base/interactive_test_utils.h"
+#include "chromeos/ui/frame/immersive/immersive_fullscreen_controller_test_api.h"
 #include "content/public/test/browser_test.h"
 
 class WebAppAshInteractiveUITest : public web_app::WebAppControllerBrowserTest {
@@ -31,7 +31,7 @@
     browser_view_ = BrowserView::GetBrowserViewForBrowser(browser);
 
     controller_ = browser_view_->immersive_mode_controller();
-    ash::ImmersiveFullscreenControllerTestApi(
+    chromeos::ImmersiveFullscreenControllerTestApi(
         static_cast<ImmersiveModeControllerAsh*>(controller_)->controller())
         .SetupForTest();
     WebAppFrameToolbarView::DisableAnimationForTesting();
diff --git a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
index 3c5ab4f..e53282f 100644
--- a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
+++ b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
@@ -230,6 +230,10 @@
 #include "chrome/browser/ui/webui/app_launcher_page_ui.h"
 #endif
 
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+#include "chrome/browser/ui/webui/webui_js_exception/webui_js_exception_ui.h"
+#endif
+
 #if !defined(OS_CHROMEOS) && !defined(OS_ANDROID)
 #include "chrome/browser/ui/sync/sync_promo_ui.h"
 #include "chrome/browser/ui/webui/browser_switch/browser_switch_ui.h"
@@ -751,6 +755,10 @@
   }
 #endif  // !defined(OFFICIAL_BUILD)
 #endif  // defined(OS_CHROMEOS)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+  if (url.host_piece() == chrome::kChromeUIWebUIJsExceptionHost)
+    return &NewWebUI<WebUIJsExceptionUI>;
+#endif
 #if defined(OS_ANDROID)
   if (url.host_piece() == chrome::kChromeUIExploreSitesInternalsHost &&
       !profile->IsOffTheRecord())
diff --git a/chrome/browser/ui/webui/interstitials/interstitial_ui.cc b/chrome/browser/ui/webui/interstitials/interstitial_ui.cc
index f0f6eca3..5d25311 100644
--- a/chrome/browser/ui/webui/interstitials/interstitial_ui.cc
+++ b/chrome/browser/ui/webui/interstitials/interstitial_ui.cc
@@ -273,7 +273,7 @@
   }
   return std::make_unique<LookalikeUrlBlockingPage>(
       web_contents, safe_url, request_url, ukm::kInvalidSourceId,
-      LookalikeUrlMatchType::kNone,
+      LookalikeUrlMatchType::kNone, false,
       std::make_unique<LookalikeUrlControllerClient>(web_contents, request_url,
                                                      safe_url));
 }
diff --git a/chrome/browser/ui/webui/settings/accessibility_main_handler.cc b/chrome/browser/ui/webui/settings/accessibility_main_handler.cc
index 5b6d2119..4a5a81f 100644
--- a/chrome/browser/ui/webui/settings/accessibility_main_handler.cc
+++ b/chrome/browser/ui/webui/settings/accessibility_main_handler.cc
@@ -5,7 +5,6 @@
 #include "chrome/browser/ui/webui/settings/accessibility_main_handler.h"
 
 #include <utility>
-#include <vector>
 
 #include "base/bind.h"
 #include "base/values.h"
@@ -19,57 +18,9 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_ui.h"
 
-#if !defined(OS_CHROMEOS)
-#include "base/check_op.h"
-#include "base/numerics/ranges.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/component_updater/soda_component_installer.h"
-#include "chrome/browser/component_updater/soda_en_us_component_installer.h"
-#include "chrome/browser/component_updater/soda_ja_jp_component_installer.h"
-#include "chrome/common/pref_names.h"
-#include "chrome/grit/chromium_strings.h"
-#include "chrome/grit/generated_resources.h"
-#include "components/prefs/pref_service.h"
-#include "components/update_client/crx_update_item.h"
-#include "content/public/browser/browser_accessibility_state.h"
-#include "ui/base/l10n/l10n_util.h"
-
-namespace {
-
-int GetDownloadProgress(
-    std::unordered_map<std::string, update_client::CrxUpdateItem>
-        downloading_components) {
-  int total_bytes = 0;
-  int downloaded_bytes = 0;
-
-  for (auto component : downloading_components) {
-    if (component.second.downloaded_bytes >= 0 &&
-        component.second.total_bytes > 0) {
-      downloaded_bytes += component.second.downloaded_bytes;
-      total_bytes += component.second.total_bytes;
-    }
-  }
-
-  if (total_bytes == 0)
-    return -1;
-
-  DCHECK_LE(downloaded_bytes, total_bytes);
-  return 100 *
-         base::ClampToRange(double{downloaded_bytes} / total_bytes, 0.0, 1.0);
-}
-
-}  // namespace
-
-#endif  // !defined(OS_CHROMEOS)
-
 namespace settings {
 
-#if defined(OS_CHROMEOS)
 AccessibilityMainHandler::AccessibilityMainHandler() = default;
-#else
-AccessibilityMainHandler::AccessibilityMainHandler(PrefService* prefs)
-    : prefs_(prefs) {}
-#endif  // defined(OS_CHROMEOS)
 
 AccessibilityMainHandler::~AccessibilityMainHandler() = default;
 
@@ -92,16 +43,12 @@
           base::BindRepeating(
               &AccessibilityMainHandler::OnAccessibilityStatusChanged,
               base::Unretained(this)));
-#else
-  component_updater_observer_.Add(g_browser_process->component_updater());
 #endif  // defined(OS_CHROMEOS)
 }
 
 void AccessibilityMainHandler::OnJavascriptDisallowed() {
 #if defined(OS_CHROMEOS)
   accessibility_subscription_.reset();
-#else
-  component_updater_observer_.RemoveAll();
 #endif  // defined(OS_CHROMEOS)
 }
 
@@ -139,54 +86,6 @@
     SendScreenReaderStateChanged();
   }
 }
-#else
-void AccessibilityMainHandler::OnEvent(Events event, const std::string& id) {
-  if (id != component_updater::SODAComponentInstallerPolicy::GetExtensionId() &&
-      id != component_updater::SodaEnUsComponentInstallerPolicy::
-                GetExtensionId() &&
-      id !=
-          component_updater::SodaJaJpComponentInstallerPolicy::GetExtensionId())
-    return;
-
-  switch (event) {
-    case Events::COMPONENT_UPDATE_FOUND:
-    case Events::COMPONENT_UPDATE_READY:
-    case Events::COMPONENT_WAIT:
-    case Events::COMPONENT_UPDATE_DOWNLOADING:
-    case Events::COMPONENT_UPDATE_UPDATING: {
-      update_client::CrxUpdateItem item;
-      g_browser_process->component_updater()->GetComponentDetails(id, &item);
-      downloading_components_[id] = item;
-      const int progress = GetDownloadProgress(downloading_components_);
-      // When GetDownloadProgress returns -1, do nothing. It returns -1 when the
-      // downloaded or total bytes is unknown.
-      if (progress != -1) {
-        FireWebUIListener(
-            "enable-live-caption-subtitle-changed",
-            base::Value(l10n_util::GetStringFUTF16Int(
-                IDS_SETTINGS_CAPTIONS_LIVE_CAPTION_DOWNLOAD_PROGRESS,
-                progress)));
-      }
-    } break;
-    case Events::COMPONENT_UPDATED:
-    case Events::COMPONENT_NOT_UPDATED:
-      FireWebUIListener(
-          "enable-live-caption-subtitle-changed",
-          base::Value(l10n_util::GetStringUTF16(
-              IDS_SETTINGS_CAPTIONS_LIVE_CAPTION_DOWNLOAD_COMPLETE)));
-      break;
-    case Events::COMPONENT_UPDATE_ERROR:
-      prefs_->SetBoolean(prefs::kLiveCaptionEnabled, false);
-      FireWebUIListener(
-          "enable-live-caption-subtitle-changed",
-          base::Value(l10n_util::GetStringUTF16(
-              IDS_SETTINGS_CAPTIONS_LIVE_CAPTION_DOWNLOAD_ERROR)));
-      break;
-    case Events::COMPONENT_CHECKING_FOR_UPDATES:
-      // Do nothing.
-      break;
-  }
-}
 #endif  // defined(OS_CHROMEOS)
 
 }  // namespace settings
diff --git a/chrome/browser/ui/webui/settings/accessibility_main_handler.h b/chrome/browser/ui/webui/settings/accessibility_main_handler.h
index f0007018..ebc1f51b 100644
--- a/chrome/browser/ui/webui/settings/accessibility_main_handler.h
+++ b/chrome/browser/ui/webui/settings/accessibility_main_handler.h
@@ -6,43 +6,28 @@
 #define CHROME_BROWSER_UI_WEBUI_SETTINGS_ACCESSIBILITY_MAIN_HANDLER_H_
 
 #include <memory>
-#include <string>
 
 #include "base/macros.h"
 #include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
-#include "components/update_client/crx_update_item.h"
 
 #if defined(OS_CHROMEOS)
 #include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
-#else
-#include "base/scoped_observer.h"
-#include "components/component_updater/component_updater_service.h"
 #endif  // defined(OS_CHROMEOS)
 
 namespace base {
 class ListValue;
 }
 
-class PrefService;
-
 namespace settings {
 
 // Settings handler for the main accessibility settings page,
 // chrome://settings/accessibility.
-// TODO(1055150) Implement the SODA download progress handling on ChromeOS and
-// remove the ChromeOS-only class declaration.
-#if defined(OS_CHROMEOS)
 class AccessibilityMainHandler : public ::settings::SettingsPageUIHandler {
  public:
   AccessibilityMainHandler();
-#else
-class AccessibilityMainHandler : public ::settings::SettingsPageUIHandler,
-                                 public component_updater::ServiceObserver {
- public:
-  explicit AccessibilityMainHandler(PrefService* prefs);
-#endif  // defined(OS_CHROMEOS)
-
   ~AccessibilityMainHandler() override;
+  AccessibilityMainHandler(const AccessibilityMainHandler&) = delete;
+  AccessibilityMainHandler& operator=(const AccessibilityMainHandler&) = delete;
 
   // SettingsPageUIHandler implementation.
   void RegisterMessages() override;
@@ -61,19 +46,7 @@
 
   std::unique_ptr<chromeos::AccessibilityStatusSubscription>
       accessibility_subscription_;
-#else
-  // component_updater::ServiceObserver:
-  void OnEvent(Events event, const std::string& id) override;
-
-  std::unordered_map<std::string, update_client::CrxUpdateItem>
-      downloading_components_;
-  PrefService* prefs_;
-  ScopedObserver<component_updater::ComponentUpdateService,
-                 component_updater::ComponentUpdateService::Observer>
-      component_updater_observer_{this};
 #endif  // defined(OS_CHROMEOS)
-
-  DISALLOW_COPY_AND_ASSIGN(AccessibilityMainHandler);
 };
 
 }  // namespace settings
diff --git a/chrome/browser/ui/webui/settings/captions_handler.cc b/chrome/browser/ui/webui/settings/captions_handler.cc
index f91f2fdd..4533f31 100644
--- a/chrome/browser/ui/webui/settings/captions_handler.cc
+++ b/chrome/browser/ui/webui/settings/captions_handler.cc
@@ -5,30 +5,134 @@
 #include "chrome/browser/ui/webui/settings/captions_handler.h"
 
 #include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "chrome/browser/accessibility/caption_settings_dialog.h"
+#include "base/check_op.h"
+#include "base/numerics/ranges.h"
+#include "base/values.h"
+#include "build/build_config.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/component_updater/soda_component_installer.h"
+#include "chrome/browser/component_updater/soda_en_us_component_installer.h"
+#include "chrome/browser/component_updater/soda_ja_jp_component_installer.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/grit/chromium_strings.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/prefs/pref_service.h"
+#include "components/update_client/crx_update_item.h"
 #include "content/public/browser/web_ui.h"
+#include "ui/base/l10n/l10n_util.h"
+
+#if defined(OS_WIN) || defined(OS_MAC)
+#include "chrome/browser/accessibility/caption_settings_dialog.h"
+#endif
+
+namespace {
+
+int GetDownloadProgress(std::map<std::string, update_client::CrxUpdateItem>
+                            downloading_components) {
+  int total_bytes = 0;
+  int downloaded_bytes = 0;
+
+  for (auto component : downloading_components) {
+    if (component.second.downloaded_bytes >= 0 &&
+        component.second.total_bytes > 0) {
+      downloaded_bytes += component.second.downloaded_bytes;
+      total_bytes += component.second.total_bytes;
+    }
+  }
+
+  if (total_bytes == 0)
+    return -1;
+
+  DCHECK_LE(downloaded_bytes, total_bytes);
+  return 100 *
+         base::ClampToRange(double{downloaded_bytes} / total_bytes, 0.0, 1.0);
+}
+
+}  // namespace
 
 namespace settings {
 
-CaptionsHandler::CaptionsHandler() {}
+CaptionsHandler::CaptionsHandler(PrefService* prefs) : prefs_(prefs) {}
 
-CaptionsHandler::~CaptionsHandler() {}
+CaptionsHandler::~CaptionsHandler() = default;
 
 void CaptionsHandler::RegisterMessages() {
   web_ui()->RegisterMessageCallback(
       "openSystemCaptionsDialog",
       base::BindRepeating(&CaptionsHandler::HandleOpenSystemCaptionsDialog,
                           base::Unretained(this)));
+  web_ui()->RegisterMessageCallback(
+      "captionsSubpageReady",
+      base::BindRepeating(&CaptionsHandler::HandleCaptionsSubpageReady,
+                          base::Unretained(this)));
 }
 
-void CaptionsHandler::OnJavascriptAllowed() {}
+void CaptionsHandler::OnJavascriptAllowed() {
+  component_updater_observer_.Add(g_browser_process->component_updater());
+}
 
-void CaptionsHandler::OnJavascriptDisallowed() {}
+void CaptionsHandler::OnJavascriptDisallowed() {
+  component_updater_observer_.RemoveAll();
+}
+
+void CaptionsHandler::HandleCaptionsSubpageReady(const base::ListValue* args) {
+  AllowJavascript();
+}
 
 void CaptionsHandler::HandleOpenSystemCaptionsDialog(
     const base::ListValue* args) {
+#if defined(OS_WIN) || defined(OS_MAC)
   captions::CaptionSettingsDialog::ShowCaptionSettingsDialog();
+#endif
+}
+
+void CaptionsHandler::OnEvent(Events event, const std::string& id) {
+  if (id != component_updater::SODAComponentInstallerPolicy::GetExtensionId() &&
+      id != component_updater::SodaEnUsComponentInstallerPolicy::
+                GetExtensionId() &&
+      id !=
+          component_updater::SodaJaJpComponentInstallerPolicy::GetExtensionId())
+    return;
+
+  switch (event) {
+    case Events::COMPONENT_UPDATE_FOUND:
+    case Events::COMPONENT_UPDATE_READY:
+    case Events::COMPONENT_WAIT:
+    case Events::COMPONENT_UPDATE_DOWNLOADING:
+    case Events::COMPONENT_UPDATE_UPDATING: {
+      update_client::CrxUpdateItem item;
+      g_browser_process->component_updater()->GetComponentDetails(id, &item);
+      downloading_components_[id] = item;
+      const int progress = GetDownloadProgress(downloading_components_);
+      // When GetDownloadProgress returns -1, do nothing. It returns -1 when the
+      // downloaded or total bytes is unknown.
+      if (progress != -1) {
+        FireWebUIListener(
+            "enable-live-caption-subtitle-changed",
+            base::Value(l10n_util::GetStringFUTF16Int(
+                IDS_SETTINGS_CAPTIONS_LIVE_CAPTION_DOWNLOAD_PROGRESS,
+                progress)));
+      }
+    } break;
+    case Events::COMPONENT_UPDATED:
+    case Events::COMPONENT_NOT_UPDATED:
+      FireWebUIListener(
+          "enable-live-caption-subtitle-changed",
+          base::Value(l10n_util::GetStringUTF16(
+              IDS_SETTINGS_CAPTIONS_LIVE_CAPTION_DOWNLOAD_COMPLETE)));
+      break;
+    case Events::COMPONENT_UPDATE_ERROR:
+      prefs_->SetBoolean(prefs::kLiveCaptionEnabled, false);
+      FireWebUIListener(
+          "enable-live-caption-subtitle-changed",
+          base::Value(l10n_util::GetStringUTF16(
+              IDS_SETTINGS_CAPTIONS_LIVE_CAPTION_DOWNLOAD_ERROR)));
+      break;
+    case Events::COMPONENT_CHECKING_FOR_UPDATES:
+      // Do nothing.
+      break;
+  }
 }
 
 }  // namespace settings
diff --git a/chrome/browser/ui/webui/settings/captions_handler.h b/chrome/browser/ui/webui/settings/captions_handler.h
index 28ea30d..4203cc6c 100644
--- a/chrome/browser/ui/webui/settings/captions_handler.h
+++ b/chrome/browser/ui/webui/settings/captions_handler.h
@@ -5,18 +5,32 @@
 #ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_CAPTIONS_HANDLER_H_
 #define CHROME_BROWSER_UI_WEBUI_SETTINGS_CAPTIONS_HANDLER_H_
 
+#include <map>
+#include <string>
+
 #include "base/macros.h"
-#include "build/build_config.h"
+#include "base/scoped_observer.h"
 #include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
+#include "components/component_updater/component_updater_service.h"
+
+class PrefService;
+
+namespace update_client {
+struct CrxUpdateItem;
+}
 
 namespace settings {
 
-// UI handler for Chrome caption settings subpage on operating systems other
-// than Chrome OS and Linux.
-class CaptionsHandler : public SettingsPageUIHandler {
+// Settings handler for the captions settings page, chrome://settings/captions,
+// and for caption settings on the main accessibility page,
+// chrome://settings/accessibility, on non-ChromeOS desktop browsers.
+class CaptionsHandler : public ::settings::SettingsPageUIHandler,
+                        public component_updater::ServiceObserver {
  public:
-  CaptionsHandler();
+  explicit CaptionsHandler(PrefService* prefs);
   ~CaptionsHandler() override;
+  CaptionsHandler(const CaptionsHandler&) = delete;
+  CaptionsHandler& operator=(const CaptionsHandler&) = delete;
 
   // SettingsPageUIHandler overrides:
   void RegisterMessages() override;
@@ -24,9 +38,17 @@
   void OnJavascriptDisallowed() override;
 
  private:
+  void HandleCaptionsSubpageReady(const base::ListValue* args);
   void HandleOpenSystemCaptionsDialog(const base::ListValue* args);
 
-  DISALLOW_COPY_AND_ASSIGN(CaptionsHandler);
+  // component_updater::ServiceObserver:
+  void OnEvent(Events event, const std::string& id) override;
+
+  std::map<std::string, update_client::CrxUpdateItem> downloading_components_;
+  PrefService* prefs_;
+  ScopedObserver<component_updater::ComponentUpdateService,
+                 component_updater::ComponentUpdateService::Observer>
+      component_updater_observer_{this};
 };
 
 }  // namespace settings
diff --git a/chrome/browser/ui/webui/settings/chromeos/accessibility_section.cc b/chrome/browser/ui/webui/settings/chromeos/accessibility_section.cc
index e0df357..7378aa0 100644
--- a/chrome/browser/ui/webui/settings/chromeos/accessibility_section.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/accessibility_section.cc
@@ -15,6 +15,7 @@
 #include "chrome/browser/speech/extension_api/tts_engine_extension_observer.h"
 #include "chrome/browser/ui/webui/settings/accessibility_main_handler.h"
 #include "chrome/browser/ui/webui/settings/chromeos/accessibility_handler.h"
+#include "chrome/browser/ui/webui/settings/chromeos/captions_handler.h"
 #include "chrome/browser/ui/webui/settings/chromeos/search/search_tag_registry.h"
 #include "chrome/browser/ui/webui/settings/font_handler.h"
 #include "chrome/browser/ui/webui/settings/shared_settings_localized_strings_provider.h"
@@ -591,6 +592,7 @@
   web_ui->AddMessageHandler(std::make_unique<::settings::TtsHandler>());
   web_ui->AddMessageHandler(
       std::make_unique<::settings::FontHandler>(profile()));
+  web_ui->AddMessageHandler(std::make_unique<CaptionsHandler>());
 }
 
 int AccessibilitySection::GetSectionNameMessageId() const {
diff --git a/chrome/browser/ui/webui/settings/chromeos/bluetooth_section.cc b/chrome/browser/ui/webui/settings/chromeos/bluetooth_section.cc
index 819a030b..24211de 100644
--- a/chrome/browser/ui/webui/settings/chromeos/bluetooth_section.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/bluetooth_section.cc
@@ -159,6 +159,8 @@
       {"bluetoothRemove", IDS_SETTINGS_BLUETOOTH_REMOVE},
       {"bluetoothPrimaryUserControlled",
        IDS_SETTINGS_BLUETOOTH_PRIMARY_USER_CONTROLLED},
+      {"bluetoothDeviceWithConnectionStatus",
+       IDS_BLUETOOTH_ACCESSIBILITY_DEVICE_TYPE_AND_CONNECTION_STATUS},
       {"bluetoothDeviceType_computer",
        IDS_BLUETOOTH_ACCESSIBILITY_DEVICE_TYPE_COMPUTER},
       {"bluetoothDeviceType_phone",
diff --git a/chrome/browser/ui/webui/settings/chromeos/captions_handler.cc b/chrome/browser/ui/webui/settings/chromeos/captions_handler.cc
new file mode 100644
index 0000000..833793db
--- /dev/null
+++ b/chrome/browser/ui/webui/settings/chromeos/captions_handler.cc
@@ -0,0 +1,24 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/settings/chromeos/captions_handler.h"
+
+#include "base/bind_helpers.h"
+#include "content/public/browser/web_ui.h"
+
+namespace chromeos {
+namespace settings {
+
+CaptionsHandler::CaptionsHandler() = default;
+
+CaptionsHandler::~CaptionsHandler() = default;
+
+void CaptionsHandler::RegisterMessages() {
+  // TODO(crbug.com/1111002): Show download progress of SODA component in the
+  // Live Caption subtitle.
+  web_ui()->RegisterMessageCallback("captionsSubpageReady", base::DoNothing());
+}
+
+}  // namespace settings
+}  // namespace chromeos
diff --git a/chrome/browser/ui/webui/settings/chromeos/captions_handler.h b/chrome/browser/ui/webui/settings/chromeos/captions_handler.h
new file mode 100644
index 0000000..542ceeb
--- /dev/null
+++ b/chrome/browser/ui/webui/settings/chromeos/captions_handler.h
@@ -0,0 +1,30 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_CAPTIONS_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_CAPTIONS_HANDLER_H_
+
+#include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
+
+namespace chromeos {
+namespace settings {
+
+// Settings handler for the captions settings page on ChromeOS.
+class CaptionsHandler : public ::settings::SettingsPageUIHandler {
+ public:
+  CaptionsHandler();
+  ~CaptionsHandler() override;
+  CaptionsHandler(const CaptionsHandler&) = delete;
+  CaptionsHandler& operator=(const CaptionsHandler&) = delete;
+
+  // SettingsPageUIHandler overrides:
+  void RegisterMessages() override;
+  void OnJavascriptAllowed() override {}
+  void OnJavascriptDisallowed() override {}
+};
+
+}  // namespace settings
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_CAPTIONS_HANDLER_H_
diff --git a/chrome/browser/ui/webui/settings/chromeos/device_section.cc b/chrome/browser/ui/webui/settings/chromeos/device_section.cc
index 978d34f..506de1f 100644
--- a/chrome/browser/ui/webui/settings/chromeos/device_section.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/device_section.cc
@@ -15,6 +15,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/chromeos/login/demo_mode/demo_session.h"
+#include "chrome/browser/ui/ui_features.h"
 #include "chrome/browser/ui/webui/settings/chromeos/device_display_handler.h"
 #include "chrome/browser/ui/webui/settings/chromeos/device_keyboard_handler.h"
 #include "chrome/browser/ui/webui/settings/chromeos/device_pointer_handler.h"
@@ -1133,6 +1134,7 @@
     content::WebUIDataSource* html_source) {
   static constexpr webui::LocalizedString kPointersStrings[] = {
       {"mouseTitle", IDS_SETTINGS_MOUSE_TITLE},
+      {"pointingStickTitle", IDS_SETTINGS_POINTING_STICK_TITLE},
       {"touchpadTitle", IDS_SETTINGS_TOUCHPAD_TITLE},
       {"mouseAndTouchpadTitle", IDS_SETTINGS_MOUSE_AND_TOUCHPAD_TITLE},
       {"touchpadTapToClickEnabledLabel",
@@ -1166,6 +1168,9 @@
   html_source->AddBoolean(
       "allowScrollSettings",
       base::FeatureList::IsEnabled(::chromeos::features::kAllowScrollSettings));
+  html_source->AddBoolean(
+      "separatePointingStickSettings",
+      base::FeatureList::IsEnabled(::features::kSeparatePointingStickSettings));
 }
 
 }  // namespace settings
diff --git a/chrome/browser/ui/webui/settings/people_handler.cc b/chrome/browser/ui/webui/settings/people_handler.cc
index 753c611..137aea3 100644
--- a/chrome/browser/ui/webui/settings/people_handler.cc
+++ b/chrome/browser/ui/webui/settings/people_handler.cc
@@ -80,7 +80,6 @@
   SyncConfigInfo();
   ~SyncConfigInfo();
 
-  bool encrypt_all;
   bool sync_everything;
   syncer::UserSelectableTypeSet selected_types;
   bool payments_integration_enabled;
@@ -93,8 +92,7 @@
 }
 
 SyncConfigInfo::SyncConfigInfo()
-    : encrypt_all(false),
-      sync_everything(false),
+    : sync_everything(false),
       payments_integration_enabled(false),
       set_new_passphrase(false) {}
 
@@ -133,12 +131,6 @@
       config->selected_types.Put(type);
   }
 
-  // Encryption settings.
-  if (!result->GetBoolean("encryptAllData", &config->encrypt_all)) {
-    DLOG(ERROR) << "GetConfiguration() not passed a value for encryptAllData";
-    return false;
-  }
-
   // Passphrase settings.
   if (result->GetString("passphrase", &config->passphrase) &&
       !config->passphrase.empty() &&
@@ -551,21 +543,15 @@
     return;
   }
 
-  // Don't allow "encrypt all" if the SyncService doesn't allow it.
-  // The UI is hidden, but the user may have enabled it e.g. by fiddling with
-  // the web inspector.
-  if (!service->GetUserSettings()->IsEncryptEverythingAllowed()) {
-    configuration.encrypt_all = false;
+  if (service->GetUserSettings()->IsEncryptEverythingAllowed()) {
+    ProfileMetrics::LogProfileSyncInfo(ProfileMetrics::SYNC_ENCRYPT);
+  } else {
+    // Don't allow "encrypt all" if the SyncService doesn't allow it.
+    // The UI is hidden, but the user may have enabled it e.g. by fiddling with
+    // the web inspector.
     configuration.set_new_passphrase = false;
   }
 
-  // Note: Data encryption will not occur until configuration is complete
-  // (when the PSS receives its CONFIGURE_DONE notification from the sync
-  // engine), so the user still has a chance to cancel out of the operation
-  // if (for example) some kind of passphrase error is encountered.
-  if (configuration.encrypt_all)
-    service->GetUserSettings()->EnableEncryptEverything();
-
   bool passphrase_failed = false;
   if (!configuration.passphrase.empty()) {
     // We call IsPassphraseRequired() here (instead of
@@ -611,8 +597,6 @@
     ResolveJavascriptCallback(*callback_id, base::Value(kConfigurePageStatus));
   }
 
-  if (configuration.encrypt_all)
-    ProfileMetrics::LogProfileSyncInfo(ProfileMetrics::SYNC_ENCRYPT);
   if (!configuration.set_new_passphrase && !configuration.passphrase.empty())
     ProfileMetrics::LogProfileSyncInfo(ProfileMetrics::SYNC_PASSPHRASE);
 }
diff --git a/chrome/browser/ui/webui/settings/people_handler_unittest.cc b/chrome/browser/ui/webui/settings/people_handler_unittest.cc
index ba703de..abd46e7 100644
--- a/chrome/browser/ui/webui/settings/people_handler_unittest.cc
+++ b/chrome/browser/ui/webui/settings/people_handler_unittest.cc
@@ -1094,9 +1094,6 @@
   list_args.AppendString(args);
 
   EXPECT_CALL(*mock_sync_service_->GetMockUserSettings(),
-              EnableEncryptEverything())
-      .Times(0);
-  EXPECT_CALL(*mock_sync_service_->GetMockUserSettings(),
               SetEncryptionPassphrase(_))
       .Times(0);
 
diff --git a/chrome/browser/ui/webui/settings/settings_ui.cc b/chrome/browser/ui/webui/settings/settings_ui.cc
index 7f2c3259..c266e474 100644
--- a/chrome/browser/ui/webui/settings/settings_ui.cc
+++ b/chrome/browser/ui/webui/settings/settings_ui.cc
@@ -31,7 +31,6 @@
 #include "chrome/browser/ui/webui/settings/accessibility_main_handler.h"
 #include "chrome/browser/ui/webui/settings/appearance_handler.h"
 #include "chrome/browser/ui/webui/settings/browser_lifetime_handler.h"
-#include "chrome/browser/ui/webui/settings/captions_handler.h"
 #include "chrome/browser/ui/webui/settings/downloads_handler.h"
 #include "chrome/browser/ui/webui/settings/extension_control_handler.h"
 #include "chrome/browser/ui/webui/settings/font_handler.h"
@@ -119,6 +118,7 @@
 #else  // !defined(OS_CHROMEOS)
 #include "chrome/browser/signin/account_consistency_mode_manager.h"
 #include "chrome/browser/ui/webui/customize_themes/chrome_customize_themes_handler.h"
+#include "chrome/browser/ui/webui/settings/captions_handler.h"
 #include "chrome/browser/ui/webui/settings/settings_default_browser_handler.h"
 #include "chrome/browser/ui/webui/settings/settings_manage_profile_handler.h"
 #include "chrome/browser/ui/webui/settings/system_handler.h"
@@ -181,12 +181,7 @@
           CreateForProfile(profile));
 #endif
 
-#if defined(OS_CHROMEOS)
   AddSettingsPageUIHandler(std::make_unique<AccessibilityMainHandler>());
-#else
-  AddSettingsPageUIHandler(
-      std::make_unique<AccessibilityMainHandler>(profile->GetPrefs()));
-#endif  // defined(OS_CHROMEOS)
   AddSettingsPageUIHandler(std::make_unique<BrowserLifetimeHandler>());
   AddSettingsPageUIHandler(
       std::make_unique<ClearBrowsingDataHandler>(web_ui, profile));
@@ -225,13 +220,11 @@
   AddSettingsPageUIHandler(
       std::make_unique<SecurityKeysBioEnrollmentHandler>());
 
-#if defined(OS_WIN) || defined(OS_MAC)
-  AddSettingsPageUIHandler(std::make_unique<CaptionsHandler>());
-#endif
-
 #if defined(OS_CHROMEOS)
   InitBrowserSettingsWebUIHandlers();
 #else
+  AddSettingsPageUIHandler(
+      std::make_unique<CaptionsHandler>(profile->GetPrefs()));
   AddSettingsPageUIHandler(std::make_unique<DefaultBrowserHandler>());
   AddSettingsPageUIHandler(std::make_unique<ManageProfileHandler>(profile));
   AddSettingsPageUIHandler(std::make_unique<SystemHandler>());
diff --git a/chrome/browser/ui/webui/tab_strip/tab_strip_ui.cc b/chrome/browser/ui/webui/tab_strip/tab_strip_ui.cc
index fe50b4d..e8d9e91 100644
--- a/chrome/browser/ui/webui/tab_strip/tab_strip_ui.cc
+++ b/chrome/browser/ui/webui/tab_strip/tab_strip_ui.cc
@@ -45,11 +45,9 @@
   Profile* profile = Profile::FromWebUI(web_ui);
   content::WebUIDataSource* html_source =
       content::WebUIDataSource::Create(chrome::kChromeUITabStripHost);
-  std::string generated_path =
-      "@out_folder@/gen/chrome/browser/resources/tab_strip/";
   webui::SetupWebUIDataSource(
       html_source, base::make_span(kTabStripResources, kTabStripResourcesSize),
-      generated_path, IDR_TAB_STRIP_HTML);
+      "", IDR_TAB_STRIP_TAB_STRIP_HTML);
 
   html_source->AddString("tabIdDataType", kWebUITabIdDataType);
   html_source->AddString("tabGroupIdDataType", kWebUITabGroupIdDataType);
diff --git a/chrome/browser/ui/webui/webui_js_exception/webui_js_exception_ui.cc b/chrome/browser/ui/webui/webui_js_exception/webui_js_exception_ui.cc
new file mode 100644
index 0000000..d06afb9
--- /dev/null
+++ b/chrome/browser/ui/webui/webui_js_exception/webui_js_exception_ui.cc
@@ -0,0 +1,62 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/webui_js_exception/webui_js_exception_ui.h"
+
+#include <ios>
+
+#include "base/feature_list.h"
+#include "base/logging.h"
+#include "build/build_config.h"
+#include "build/buildflag.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/webui_util.h"
+#include "chrome/common/buildflags.h"
+#include "chrome/common/webui_url_constants.h"
+#include "chrome/grit/webui_js_exception_resources.h"
+#include "chrome/grit/webui_js_exception_resources_map.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "content/public/common/content_features.h"
+
+#if !BUILDFLAG(OPTIMIZE_WEBUI)
+namespace {
+constexpr char kGeneratedPath[] =
+    "@out_folder@/gen/chrome/browser/resources/webui_js_exception/";
+}  // namespace
+#endif  // !BUILDFLAG(OPTIMIZE_WEBUI)
+
+WebUIJsExceptionUI::WebUIJsExceptionUI(content::WebUI* web_ui)
+    : content::WebUIController(web_ui) {
+#if !defined(OS_WIN) && !defined(OS_FUCHSIA)
+  VLOG(3) << std::boolalpha << "chrome://webuijsexception loading. "
+          << "Experiment state: send javascript errors is "
+          << base::FeatureList::IsEnabled(
+                 features::kSendWebUIJavaScriptErrorReports)
+          << " and send to prod is "
+          << features::kWebUIJavaScriptErrorReportsSendToProductionParam.Get();
+#else
+  VLOG(3) << std::boolalpha << "chrome://webuijsexception loading.";
+#endif
+
+  content::WebUIDataSource* source =
+      content::WebUIDataSource::Create(chrome::kChromeUIWebUIJsExceptionHost);
+#if BUILDFLAG(OPTIMIZE_WEBUI)
+  webui::SetupBundledWebUIDataSource(
+      source, "webui_js_exception.js",
+      IDR_WEBUI_JS_EXCEPTION_UI_WEBUI_JS_EXCEPTION_ROLLUP_JS,
+      IDR_WEBUI_JS_EXCEPTION_UI_WEBUI_JS_EXCEPTION_HTML);
+#else   // if !BUILDFLAG(OPTIMIZE_WEBUI)
+  webui::SetupWebUIDataSource(
+      source,
+      base::make_span(kWebuiJsExceptionResources,
+                      kWebuiJsExceptionResourcesSize),
+      kGeneratedPath, IDR_WEBUI_JS_EXCEPTION_UI_WEBUI_JS_EXCEPTION_HTML);
+#endif  // !BUILDFLAG(OPTIMIZE_WEBUI)
+  Profile* profile = Profile::FromWebUI(web_ui);
+  content::WebUIDataSource::Add(profile, source);
+}
+
+WebUIJsExceptionUI::~WebUIJsExceptionUI() = default;
+
+WEB_UI_CONTROLLER_TYPE_IMPL(WebUIJsExceptionUI)
diff --git a/chrome/browser/ui/webui/webui_js_exception/webui_js_exception_ui.h b/chrome/browser/ui/webui/webui_js_exception/webui_js_exception_ui.h
new file mode 100644
index 0000000..d49f35a5
--- /dev/null
+++ b/chrome/browser/ui/webui/webui_js_exception/webui_js_exception_ui.h
@@ -0,0 +1,22 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_WEBUI_JS_EXCEPTION_WEBUI_JS_EXCEPTION_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_WEBUI_JS_EXCEPTION_WEBUI_JS_EXCEPTION_UI_H_
+
+#include "content/public/browser/web_ui_controller.h"
+
+// The WebUI that controls chrome://webuijsexception.
+class WebUIJsExceptionUI : public content::WebUIController {
+ public:
+  explicit WebUIJsExceptionUI(content::WebUI* web_ui);
+  ~WebUIJsExceptionUI() override;
+  WebUIJsExceptionUI(const WebUIJsExceptionUI&) = delete;
+  WebUIJsExceptionUI& operator=(const WebUIJsExceptionUI&) = delete;
+
+ private:
+  WEB_UI_CONTROLLER_TYPE_DECL();
+};
+
+#endif  // CHROME_BROWSER_UI_WEBUI_WEBUI_JS_EXCEPTION_WEBUI_JS_EXCEPTION_UI_H_
diff --git a/chrome/browser/video_tutorials/internal/config.cc b/chrome/browser/video_tutorials/internal/config.cc
index e9ee89a..ed1de3f0 100644
--- a/chrome/browser/video_tutorials/internal/config.cc
+++ b/chrome/browser/video_tutorials/internal/config.cc
@@ -11,13 +11,14 @@
 namespace video_tutorials {
 
 // Default base URL string for the server.
-constexpr char kDefaultBaseURL[] = "https://chromeupboarding-pa.googleapis.com";
+constexpr char kDefaultBaseURL[] =
+    "https://staging-gsaprototype-pa.sandbox.googleapis.com";
 
 // Default URL string for GetTutorials RPC.
 constexpr char kDefaultGetTutorialsPath[] = "/v1/videotutorials";
 
 // Hindi is the default locale.
-constexpr char kDefaultPreferredLocale[] = "hi";
+constexpr char kDefaultPreferredLocale[] = "en";
 
 // Finch parameter key for base server URL to retrieve the tutorials.
 constexpr char kBaseURLKey[] = "base_url";
diff --git a/chrome/browser/video_tutorials/internal/config_unittest.cc b/chrome/browser/video_tutorials/internal/config_unittest.cc
index 1d292e1..4c0c0bd 100644
--- a/chrome/browser/video_tutorials/internal/config_unittest.cc
+++ b/chrome/browser/video_tutorials/internal/config_unittest.cc
@@ -29,8 +29,9 @@
   base::test::ScopedFeatureList feature_list;
   feature_list.InitAndEnableFeature(features::kVideoTutorials);
   EXPECT_EQ(Config::GetTutorialsServerURL().spec(),
-            "https://chromeupboarding-pa.googleapis.com/v1/videotutorials");
-  EXPECT_EQ(Config::GetDefaultPreferredLocale(), "hi");
+            "https://staging-gsaprototype-pa.sandbox.googleapis.com/v1/"
+            "videotutorials");
+  EXPECT_EQ(Config::GetDefaultPreferredLocale(), "en");
 }
 
 }  // namespace video_tutorials
diff --git a/chrome/browser/video_tutorials/internal/tutorial_manager_impl_unittest.cc b/chrome/browser/video_tutorials/internal/tutorial_manager_impl_unittest.cc
index 734bed7b..56e99772 100644
--- a/chrome/browser/video_tutorials/internal/tutorial_manager_impl_unittest.cc
+++ b/chrome/browser/video_tutorials/internal/tutorial_manager_impl_unittest.cc
@@ -161,6 +161,7 @@
 
   auto languages = manager()->GetSupportedLanguages();
   EXPECT_EQ(languages.size(), 2u);
+  manager()->SetPreferredLocale("hi");
   GetTutorials();
   EXPECT_EQ(last_results().size(), 2u);
 }
@@ -171,6 +172,7 @@
   tutorial_store->InitStoreData("hi", groups);
   CreateTutorialManager(std::move(tutorial_store));
 
+  manager()->SetPreferredLocale("hi");
   auto languages = manager()->GetSupportedLanguages();
   EXPECT_EQ(languages.size(), 2u);
   GetTutorials();
diff --git a/chrome/browser/video_tutorials/internal/tutorial_service_impl.cc b/chrome/browser/video_tutorials/internal/tutorial_service_impl.cc
index 0c10699f..6caca4f 100644
--- a/chrome/browser/video_tutorials/internal/tutorial_service_impl.cc
+++ b/chrome/browser/video_tutorials/internal/tutorial_service_impl.cc
@@ -20,7 +20,9 @@
     PrefService* pref_service)
     : tutorial_manager_(std::move(tutorial_manager)),
       tutorial_fetcher_(std::move(tutorial_fetcher)),
-      pref_service_(pref_service) {}
+      pref_service_(pref_service) {
+  StartFetchIfNecessary();
+}
 
 TutorialServiceImpl::~TutorialServiceImpl() = default;
 
@@ -61,7 +63,8 @@
 void TutorialServiceImpl::OnFetchFinished(
     bool success,
     std::unique_ptr<std::string> response_body) {
-  // TODO(shaktisahu): Save tutorials to the database.
+  pref_service_->SetTime(kLastUpdatedTimeKey, base::Time::Now());
+
   if (!success || !response_body)
     return;
 
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index 79a5b52..0c57458 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-master-1602827193-4287d7521da41bae907bcfa87edebb4158ef284f.profdata
+chrome-linux-master-1602849330-e626db1a42614bd1b3dbd5e4d956205e419c269d.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 359e973..26ad8fb 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-master-1602827193-a11ab02463ee64ac6dd1eb410ceea8e9dc281351.profdata
+chrome-mac-master-1602849330-a9b7aee9583f24ea33d238dab5bbe64a57e40269.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index ad65eeb..5654285 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-master-1602795589-2bd353ec3fee304a6bd3887aca77b7d3fb1210e3.profdata
+chrome-win32-master-1602838722-3d3847c86317a9aa85069273b7be64c1533b4e7a.profdata
diff --git a/chrome/chrome_paks.gni b/chrome/chrome_paks.gni
index 9323a774..683f931 100644
--- a/chrome/chrome_paks.gni
+++ b/chrome/chrome_paks.gni
@@ -221,6 +221,10 @@
         ]
       }
     }
+    if (is_linux || is_chromeos) {
+      sources += [ "$root_gen_dir/chrome/webui_js_exception_resources.pak" ]
+      deps += [ "//chrome/browser/resources:webui_js_exception_resources" ]
+    }
     if (!is_android && !is_chromeos) {
       sources += [
         "$root_gen_dir/chrome/profile_picker_resources.pak",
diff --git a/chrome/common/webui_url_constants.cc b/chrome/common/webui_url_constants.cc
index 9012476..2f6f4f4 100644
--- a/chrome/common/webui_url_constants.cc
+++ b/chrome/common/webui_url_constants.cc
@@ -338,6 +338,11 @@
 }
 #endif  // defined(OS_CHROMEOS)
 
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+const char kChromeUIWebUIJsExceptionHost[] = "webuijsexception";
+const char kChromeUIWebUIJsExceptionURL[] = "chrome://webuijsexception/";
+#endif
+
 #if defined(OS_WIN) || defined(OS_MAC) || defined(OS_LINUX) || \
     defined(OS_CHROMEOS)
 const char kChromeUIDiscardsHost[] = "discards";
@@ -609,6 +614,9 @@
     content::kChromeUIGpuJavaCrashURL,
     kChromeUIJavaCrashURL,
 #endif
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+    kChromeUIWebUIJsExceptionURL,
+#endif
     kChromeUIQuitURL,
     kChromeUIRestartURL};
 const size_t kNumberOfChromeDebugURLs = base::size(kChromeDebugURLs);
diff --git a/chrome/common/webui_url_constants.h b/chrome/common/webui_url_constants.h
index bc28c1b..c57f9936 100644
--- a/chrome/common/webui_url_constants.h
+++ b/chrome/common/webui_url_constants.h
@@ -291,6 +291,11 @@
 
 #endif  // defined(OS_CHROMEOS)
 
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+extern const char kChromeUIWebUIJsExceptionHost[];
+extern const char kChromeUIWebUIJsExceptionURL[];
+#endif
+
 #if defined(OS_WIN) || defined(OS_MAC) || defined(OS_LINUX) || \
     defined(OS_CHROMEOS)
 extern const char kChromeUIDiscardsHost[];
diff --git a/chrome/installer/setup/user_hive_visitor_unittest.cc b/chrome/installer/setup/user_hive_visitor_unittest.cc
index 70bbb0f..a614c5e 100644
--- a/chrome/installer/setup/user_hive_visitor_unittest.cc
+++ b/chrome/installer/setup/user_hive_visitor_unittest.cc
@@ -7,6 +7,7 @@
 #include <vector>
 
 #include "base/bind.h"
+#include "base/callback.h"
 #include "base/macros.h"
 #include "base/strings/string16.h"
 #include "base/win/registry.h"
diff --git a/chrome/installer/util/beacons.cc b/chrome/installer/util/beacons.cc
index ee2c1bb..4bbbb71 100644
--- a/chrome/installer/util/beacons.cc
+++ b/chrome/installer/util/beacons.cc
@@ -6,6 +6,7 @@
 
 #include <stdint.h>
 
+#include "base/notreached.h"
 #include "base/win/registry.h"
 #include "base/win/win_util.h"
 #include "chrome/install_static/install_details.h"
diff --git a/chrome/installer/util/install_util.cc b/chrome/installer/util/install_util.cc
index 45a89af7..5647b0e 100644
--- a/chrome/installer/util/install_util.cc
+++ b/chrome/installer/util/install_util.cc
@@ -13,10 +13,13 @@
 #include <algorithm>
 #include <iterator>
 
+#include "base/check.h"
+#include "base/check_op.h"
 #include "base/command_line.h"
 #include "base/files/file_util.h"
 #include "base/logging.h"
 #include "base/metrics/histogram_macros.h"
+#include "base/notreached.h"
 #include "base/path_service.h"
 #include "base/process/launch.h"
 #include "base/stl_util.h"
diff --git a/chrome/installer/util/registry_entry.cc b/chrome/installer/util/registry_entry.cc
index b4bf253..7e5c8ff 100644
--- a/chrome/installer/util/registry_entry.cc
+++ b/chrome/installer/util/registry_entry.cc
@@ -5,6 +5,7 @@
 #include "chrome/installer/util/registry_entry.h"
 
 #include "base/check.h"
+#include "base/numerics/safe_conversions.h"
 #include "base/strings/string_util.h"
 #include "base/win/registry.h"
 #include "chrome/installer/util/work_item.h"
diff --git a/chrome/installer/util/registry_key_backup.cc b/chrome/installer/util/registry_key_backup.cc
index c10d476..8909cb4 100644
--- a/chrome/installer/util/registry_key_backup.cc
+++ b/chrome/installer/util/registry_key_backup.cc
@@ -11,6 +11,7 @@
 #include <utility>
 #include <vector>
 
+#include "base/check.h"
 #include "base/logging.h"
 #include "base/win/registry.h"
 
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 31b5dee..25ebf2f 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1215,6 +1215,7 @@
       "../browser/portal/portal_recently_audible_browsertest.cc",
       "../browser/predictors/loading_predictor_browsertest.cc",
       "../browser/prefetch/prefetch_browsertest.cc",
+      "../browser/prefetch/search_prefetch/search_prefetch_service_browsertest.cc",
       "../browser/prefs/pref_functional_browsertest.cc",
       "../browser/prefs/pref_service_browsertest.cc",
       "../browser/prefs/tracked/pref_hash_browsertest.cc",
@@ -2817,7 +2818,10 @@
         "../browser/policy/cloud/chrome_browser_cloud_management_browsertest.cc",
       ]
 
-      deps += [ "//components/crash/content/browser/error_reporting:mock_crash_endpoint" ]
+      deps += [
+        "//chromeos/ui/frame:test_support",
+        "//components/crash/content/browser/error_reporting:mock_crash_endpoint",
+      ]
     } else {  # !is_chromeos
       sources -= [
         "../browser/invalidation/profile_invalidation_provider_factory_browsertest.cc",
@@ -3786,6 +3790,7 @@
       "../browser/android/explore_sites/ntp_json_fetcher_unittest.cc",
       "../browser/android/explore_sites/record_site_click_task_unittest.cc",
       "../browser/android/favicon_helper_unittest.cc",
+      "../browser/android/history/history_deletion_bridge_unittest.cc",
       "../browser/android/history_report/data_observer_unittest.cc",
       "../browser/android/history_report/delta_file_backend_leveldb_unittest.cc",
       "../browser/android/history_report/delta_file_commons_unittest.cc",
@@ -6595,6 +6600,7 @@
       deps += [
         "//chrome/browser/media/router:test_support",
         "//chromeos/dbus",
+        "//chromeos/ui/frame:test_support",
       ]
       sources -=
           [ "../browser/ui/signin_view_controller_interactive_uitest.cc" ]
diff --git a/chrome/test/data/policy/policy_test_cases.json b/chrome/test/data/policy/policy_test_cases.json
index a7c2739..3cbb9fc 100644
--- a/chrome/test/data/policy/policy_test_cases.json
+++ b/chrome/test/data/policy/policy_test_cases.json
@@ -4983,6 +4983,16 @@
     ]
   },
 
+  "WifiSyncAndroidAllowed": {
+    "os": ["chromeos"],
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "WifiSyncAndroidAllowed": false },
+        "prefs": { "wifi_sync.allowed": {} }
+      }
+    ]
+  },
+
   "SmartLockSigninAllowed": {
     "os": ["chromeos"],
     "policy_pref_mapping_test": [
diff --git a/chrome/test/data/webui/chromeos/diagnostics/realtime_cpu_chart_test.js b/chrome/test/data/webui/chromeos/diagnostics/realtime_cpu_chart_test.js
index 40170d2e..07b162f 100644
--- a/chrome/test/data/webui/chromeos/diagnostics/realtime_cpu_chart_test.js
+++ b/chrome/test/data/webui/chromeos/diagnostics/realtime_cpu_chart_test.js
@@ -62,12 +62,44 @@
     return initializeRealtimeCpuChart(user, system).then(() => {
       const svg = realtimeCpuChartElement.$$('#chart');
       const boundary = realtimeCpuChartElement.$$('#defClip>rect');
+
+      // Chart area boundary must fit within svg.
       assertGT(
           Number(svg.getAttribute('width')),
           Number(boundary.getAttribute('width')));
       assertGT(
           Number(svg.getAttribute('height')),
           Number(boundary.getAttribute('height')));
+
+      const chartGroup = realtimeCpuChartElement.$$('#chartGroup');
+
+      // Margins are in effect.
+      assertEquals(
+          `translate(${realtimeCpuChartElement.margin_.left},${
+              realtimeCpuChartElement.margin_.top})`,
+          chartGroup.getAttribute('transform'));
+    });
+  });
+
+  test('InitializePlot', () => {
+    const user = 10;
+    const system = 30;
+    return initializeRealtimeCpuChart(user, system).then(() => {
+      // yAxis is drawn.
+      assertTrue(!!realtimeCpuChartElement.$$('#gridLines>path.domain'));
+
+      // Correct number of yAxis ticks drawn.
+      assertEquals(
+          3,
+          realtimeCpuChartElement.shadowRoot
+              .querySelectorAll('#gridLines>g.tick')
+              .length);
+
+      // Plot lines are drawn.
+      assertTrue(!!realtimeCpuChartElement.$$('#plotGroup>path.user-line')
+                       .getAttribute('d'));
+      assertTrue(!!realtimeCpuChartElement.$$('#plotGroup>path.system-line')
+                       .getAttribute('d'));
     });
   });
 });
diff --git a/chrome/test/data/webui/chromeos/diagnostics/routine_result_entry_test.js b/chrome/test/data/webui/chromeos/diagnostics/routine_result_entry_test.js
index f7ef766..979dd69 100644
--- a/chrome/test/data/webui/chromeos/diagnostics/routine_result_entry_test.js
+++ b/chrome/test/data/webui/chromeos/diagnostics/routine_result_entry_test.js
@@ -5,6 +5,9 @@
 // TODO(jimmyxgong): Use es6 module for mojo binding (crbug/1004256).
 import 'chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.js';
 import 'chrome://diagnostics/routine_result_entry.js';
+
+import {RoutineName, RoutineResult, StandardRoutineResult} from 'chrome://diagnostics/diagnostics_types.js';
+import {ExecutionProgress, ResultStatusItem} from 'chrome://diagnostics/routine_list_executor.js';
 import {flushTasks} from 'chrome://test/test_util.m.js';
 
 suite('RoutineResultEntryTest', () => {
@@ -33,6 +36,71 @@
     return flushTasks();
   }
 
+  /**
+   * Updates the item in the element.
+   * @param {!ResultStatusItem} item
+   * @return {!Promise}
+   */
+  function updateItem(item) {
+    routineResultEntryElement.item = item;
+    return flushTasks();
+  }
+
+  /**
+   * Initializes the entry then updates the item.
+   * @param {!ResultStatusItem} item
+   * @return {!Promise}
+   */
+  function initializeEntryWithItem(item) {
+    return initializeRoutineResultEntry().then(() => {
+      return updateItem(item);
+    });
+  }
+
+  /**
+   * Creates a result status item without a final result.
+   * @param {!RoutineName} routine
+   * @param {!ExecutionProgress} progress
+   * @return {!ResultStatusItem}
+   */
+  function createIncompleteStatus(routine, progress) {
+    let status = new ResultStatusItem(routine);
+    status.progress = progress;
+    return status;
+  }
+
+  /**
+   * Creates a completed result status item with a result.
+   * @param {!RoutineName} routine
+   * @param {!RoutineResult} result
+   * @return {!ResultStatusItem}
+   */
+  function createCompletedStatus(routine, result) {
+    let status = createIncompleteStatus(routine, ExecutionProgress.kCompleted);
+    status.result = result;
+    return status;
+  }
+
+  /**
+   * Returns the routine name element text content.
+   * @return {string}
+   */
+  function getNameText() {
+    const name = routineResultEntryElement.$$('#routine');
+    assertTrue(!!name);
+    return name.textContent.trim();
+  }
+
+  /**
+   * Returns the status element text content.
+   * @return {string}
+   */
+  function getStatusText() {
+    const status = routineResultEntryElement.$$('#status');
+    assertTrue(!!status);
+    return status.textContent.trim();
+  }
+
   test('ElementRendered', () => {
     return initializeRoutineResultEntry().then(() => {
       // Verify the element rendered.
@@ -40,4 +108,54 @@
       assertTrue(!!div);
     });
   });
+
+  test('NotStartedTest', () => {
+    const item = createIncompleteStatus(
+        RoutineName.kCpuStress, ExecutionProgress.kNotStarted);
+    return initializeEntryWithItem(item).then(() => {
+      // TODO(zentaro): Localize the test.
+      assertEquals(getNameText(), 'kCpuStress');
+
+      // Status should be empty if the test is not started.
+      assertEquals(getStatusText(), '');
+    });
+  });
+
+  test('RunningTest', () => {
+    const item = createIncompleteStatus(
+        RoutineName.kCpuStress, ExecutionProgress.kRunning);
+    return initializeEntryWithItem(item).then(() => {
+      // TODO(zentaro): Localize the test.
+      assertEquals(getNameText(), 'kCpuStress');
+
+      // Status should be running.
+      assertEquals(getStatusText(), 'kRunning');
+    });
+  });
+
+  test('PassedTest', () => {
+    const item = createCompletedStatus(
+        RoutineName.kCpuStress,
+        {simple_result: StandardRoutineResult.kTestPassed});
+    return initializeEntryWithItem(item).then(() => {
+      // TODO(zentaro): Localize the test.
+      assertEquals(getNameText(), 'kCpuStress');
+
+      // Status should show the passed result.
+      assertEquals(getStatusText(), 'kTestPassed');
+    });
+  });
+
+  test('FailedTest', () => {
+    const item = createCompletedStatus(
+        RoutineName.kCpuStress,
+        {simple_result: StandardRoutineResult.kTestFailed});
+    return initializeEntryWithItem(item).then(() => {
+      // TODO(zentaro): Localize the test.
+      assertEquals(getNameText(), 'kCpuStress');
+
+      // Status should show the passed result.
+      assertEquals(getStatusText(), 'kTestFailed');
+    });
+  });
 });
diff --git a/chrome/test/data/webui/settings/chromeos/device_page_tests.js b/chrome/test/data/webui/settings/chromeos/device_page_tests.js
index 4995bea..4a5e2c03 100644
--- a/chrome/test/data/webui/settings/chromeos/device_page_tests.js
+++ b/chrome/test/data/webui/settings/chromeos/device_page_tests.js
@@ -10,6 +10,7 @@
     Keyboard: 'keyboard',
     NightLight: 'night light',
     Pointers: 'pointers',
+    PointingStick: 'pointing stick',
     Power: 'power',
     Storage: 'storage',
     Stylus: 'stylus',
@@ -740,6 +741,101 @@
       });
     });
 
+    suite(assert(TestNames.PointingStick), function() {
+      // TODO(crbug.com/1114828): merge this suite into the Pointers one when
+      // the flag is removed.
+      let pointersPage;
+
+      setup(function() {
+        // We have to set separatePointingStickSettings here so it's in effect
+        // when the template is rendered.
+        loadTimeData.overrideValues({separatePointingStickSettings: true});
+        return showAndGetDeviceSubpage('pointers', settings.routes.POINTERS)
+            .then(function(page) {
+              pointersPage = page;
+            });
+      });
+
+      test('subpage responds to pointer attach/detach', function() {
+        const assertElementVisible = (element) => {
+          assertNotEquals(element, null, 'element should exist');
+          // offsetWidth and offsetHeight reflect more ways that an element
+          // could be hidden than checking the hidden attribute directly.
+          assertTrue(
+              element.offsetWidth > 0 && element.offsetHeight > 0,
+              'element should be visible');
+        };
+
+        const assertElementHidden = (element) => {
+          assertNotEquals(element, null, 'element should exist');
+          assertTrue(
+              element.offsetWidth === 0 && element.offsetHeight === 0,
+              'element should be hidden');
+        };
+
+        assertEquals(
+            settings.routes.POINTERS,
+            settings.Router.getInstance().getCurrentRoute());
+        assertElementVisible(pointersPage.$$('#mouse'));
+        assertElementVisible(pointersPage.$$('#mouse h2'));
+        assertElementVisible(pointersPage.$$('#pointingStick'));
+        assertElementVisible(pointersPage.$$('#pointingStick h2'));
+        assertElementVisible(pointersPage.$$('#touchpad'));
+        assertElementVisible(pointersPage.$$('#touchpad h2'));
+
+        cr.webUIListenerCallback('has-touchpad-changed', false);
+        assertEquals(
+            settings.routes.POINTERS,
+            settings.Router.getInstance().getCurrentRoute());
+        assertElementVisible(pointersPage.$$('#mouse'));
+        assertElementVisible(pointersPage.$$('#mouse h2'));
+        assertElementVisible(pointersPage.$$('#pointingStick'));
+        assertElementVisible(pointersPage.$$('#pointingStick h2'));
+        assertElementHidden(pointersPage.$$('#touchpad'));
+        assertElementHidden(pointersPage.$$('#touchpad h2'));
+
+        cr.webUIListenerCallback('has-pointing-stick-changed', false);
+        assertEquals(
+            settings.routes.POINTERS,
+            settings.Router.getInstance().getCurrentRoute());
+        assertElementVisible(pointersPage.$$('#mouse'));
+        assertElementHidden(pointersPage.$$('#mouse h2'));
+        assertElementHidden(pointersPage.$$('#pointingStick'));
+        assertElementHidden(pointersPage.$$('#pointingStick h2'));
+        assertElementHidden(pointersPage.$$('#touchpad'));
+        assertElementHidden(pointersPage.$$('#touchpad h2'));
+
+        cr.webUIListenerCallback('has-mouse-changed', false);
+        assertEquals(
+            settings.routes.DEVICE,
+            settings.Router.getInstance().getCurrentRoute());
+        assertElementHidden(devicePage.$$('#main #pointersRow'));
+
+        cr.webUIListenerCallback('has-touchpad-changed', true);
+        assertElementVisible(devicePage.$$('#main #pointersRow'));
+        return showAndGetDeviceSubpage('pointers', settings.routes.POINTERS)
+            .then(function(page) {
+              assertElementHidden(page.$$('#mouse'));
+              assertElementHidden(page.$$('#mouse h2'));
+              assertElementHidden(page.$$('#pointingStick'));
+              assertElementHidden(page.$$('#pointingStick h2'));
+              assertElementVisible(page.$$('#touchpad'));
+              assertElementHidden(page.$$('#touchpad h2'));
+
+              cr.webUIListenerCallback('has-mouse-changed', true);
+              assertEquals(
+                  settings.routes.POINTERS,
+                  settings.Router.getInstance().getCurrentRoute());
+              assertElementVisible(page.$$('#mouse'));
+              assertElementVisible(page.$$('#mouse h2'));
+              assertElementHidden(page.$$('#pointingStick'));
+              assertElementHidden(page.$$('#pointingStick h2'));
+              assertElementVisible(page.$$('#touchpad'));
+              assertElementVisible(page.$$('#touchpad h2'));
+            });
+      });
+    });
+
     suite(assert(TestNames.Keyboard), function() {
       const name = k => `prefs.settings.language.${k}.value`;
       const get = k => devicePage.get(name(k));
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js b/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
index 41c8ccc..2cfeb7c 100644
--- a/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
+++ b/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
@@ -687,6 +687,10 @@
   mocha.grep(assert(device_page_tests.TestNames.Pointers)).run();
 });
 
+TEST_F('OSSettingsDevicePageTest', 'PointersWithPointingStickTest', () => {
+  mocha.grep(assert(device_page_tests.TestNames.PointingStick)).run();
+});
+
 TEST_F('OSSettingsDevicePageTest', 'PowerTest', () => {
   mocha.grep(assert(device_page_tests.TestNames.Power)).run();
 });
diff --git a/chrome/test/data/webui/settings/people_page_sync_page_test.js b/chrome/test/data/webui/settings/people_page_sync_page_test.js
index 8f09e01b..624cd363 100644
--- a/chrome/test/data/webui/settings/people_page_sync_page_test.js
+++ b/chrome/test/data/webui/settings/people_page_sync_page_test.js
@@ -335,8 +335,6 @@
 
     assertFalse(passphraseInput.invalid);
     assertTrue(passphraseConfirmationInput.invalid);
-
-    assertFalse(syncPage.syncPrefs.encryptAllData);
   });
 
   test('CreatingPassphraseValidPassphrase', function() {
@@ -354,15 +352,15 @@
     passphraseConfirmationInput.value = 'foo';
     saveNewPassphrase.click();
 
-    function verifyPrefs(prefs) {
-      const expected = getSyncAllPrefs();
-      expected.setNewPassphrase = true;
-      expected.passphrase = 'foo';
-      expected.encryptAllData = true;
-      assertEquals(JSON.stringify(expected), JSON.stringify(prefs));
+    function verifySetSyncEncryption(args) {
+      assertTrue(args.setNewPassphrase);
+      assertEquals('foo', args.passphrase);
 
-      expected.fullEncryptionBody = 'Encrypted with custom passphrase';
-      webUIListenerCallback('sync-prefs-changed', expected);
+      // Fake backend response.
+      const newPrefs = getSyncAllPrefs();
+      newPrefs.fullEncryptionBody = 'Encrypted with custom passphrase';
+      newPrefs.encryptAllData = true;
+      webUIListenerCallback('sync-prefs-changed', newPrefs);
 
       flush();
 
@@ -378,7 +376,8 @@
         assertEquals(-1, encryptWithPassphrase.$.button.tabIndex);
       });
     }
-    return browserProxy.whenCalled('setSyncEncryption').then(verifyPrefs);
+    return browserProxy.whenCalled('setSyncEncryption')
+        .then(verifySetSyncEncryption);
   });
 
   test('RadioBoxesHiddenWhenPassphraseRequired', function() {
@@ -432,16 +431,10 @@
     assertTrue(!!submitExistingPassphrase);
     submitExistingPassphrase.click();
 
-    return browserProxy.whenCalled('setSyncEncryption').then(function(prefs) {
-      const expected = getSyncAllPrefs();
-      expected.setNewPassphrase = false;
-      expected.passphrase = 'wrong';
-      expected.encryptAllData = true;
-      expected.passphraseRequired = true;
-      assertEquals(JSON.stringify(expected), JSON.stringify(prefs));
-
-      flush();
-
+    return browserProxy.whenCalled('setSyncEncryption').then(function(args) {
+      assertFalse(args.setNewPassphrase);
+      assertEquals('wrong', args.passphrase);
+      assertTrue(args.passphraseRequired);
       assertTrue(existingPassphraseInput.invalid);
     });
   });
@@ -462,14 +455,12 @@
     assertTrue(!!submitExistingPassphrase);
     submitExistingPassphrase.click();
 
-    return browserProxy.whenCalled('setSyncEncryption').then(function(prefs) {
-      const expected = getSyncAllPrefs();
-      expected.setNewPassphrase = false;
-      expected.passphrase = 'right';
-      expected.encryptAllData = true;
-      expected.passphraseRequired = true;
-      assertEquals(JSON.stringify(expected), JSON.stringify(prefs));
+    return browserProxy.whenCalled('setSyncEncryption').then(function(args) {
+      assertFalse(args.setNewPassphrase);
+      assertEquals('right', args.passphrase);
+      assertTrue(args.passphraseRequired);
 
+      // Fake backend response.
       const newPrefs = getSyncAllPrefs();
       newPrefs.encryptAllData = true;
       webUIListenerCallback('sync-prefs-changed', newPrefs);
diff --git a/chrome/updater/app/server/win/BUILD.gn b/chrome/updater/app/server/win/BUILD.gn
index 5506d9d..7cc50e2 100644
--- a/chrome/updater/app/server/win/BUILD.gn
+++ b/chrome/updater/app/server/win/BUILD.gn
@@ -4,10 +4,14 @@
 
 import("//build/toolchain/win/midl.gni")
 
+UpdaterClass_replaceable_uuid = "158428a4-6014-4978-83ba-9fad0dabe791"
+IUpdater_replaceable_uuid = "63B8FFB1-5314-48C9-9C57-93EC8BC6184B"
+
 # TODO(crbug.com/1099904): this CLSID needs to be regenerated for every build.
 # TODO(crbug.com/1099904): we need to use an indirection mechanism for the
 # common GUIDs in the SxS versions.
-updater_clsid = "158428a4-6014-4978-83ba-9fad0dabe791"
+updater_clsid = "3d852661-c795-4d20-9b95-5561e9a1d2d9"
+IUPDATER_IID = "D0E1CACC-C63C-4192-94AB-BF8EAD0E3B83"
 
 action("generate_updater_idl") {
   script = "//build/util/version.py"
@@ -17,14 +21,17 @@
 
   args = [
     "-e",
-    "UPDATER_CLSID='$updater_clsid'",
+    "$UpdaterClass_replaceable_uuid='$updater_clsid'",
+    "-e",
+    "$IUpdater_replaceable_uuid='$IUPDATER_IID'",
     rebase_path(inputs[0], root_build_dir),
     rebase_path(outputs[0], root_build_dir),
   ]
 }
 
 midl("updater_idl_idl") {
-  dynamic_guid = updater_clsid
+  dynamic_guid = "$UpdaterClass_replaceable_uuid=$updater_clsid," +
+                 "$IUpdater_replaceable_uuid=$IUPDATER_IID"
   deps = [ ":generate_updater_idl" ]
   header_file = "updater_idl.h"
   sources = get_target_outputs(":generate_updater_idl")
@@ -33,7 +40,7 @@
     "IUPDATESTATE_IID=46ACF70B-AC13-406D-B53B-B2C4BF091FF6",
     "ICOMPLETESTATUS_IID=2FCD14AF-B645-4351-8359-E80A0E202A0B",
     "IUPDATEROBSERVER_IID=7B416CFD-4216-4FD6-BD83-7C586054676E",
-    "IUPDATER_IID=63B8FFB1-5314-48C9-9C57-93EC8BC6184B",
+    "IUPDATER_IID=$IUPDATER_IID",
     "IUPDATERCONTROL_IID=526DA036-9BD3-4697-865A-DA12D37DFFCA",
     "UPDATER_LIB_UUID=69464FF0-D9EC-4037-A35F-8AE4358106CC",
     "ICURRENTSTATE_IID=247954F9-9EDC-4E68-8CC3-150C2B89EADF",
diff --git a/chrome/updater/app/server/win/updater_idl.template b/chrome/updater/app/server/win/updater_idl.template
index 4122276..322e973 100644
--- a/chrome/updater/app/server/win/updater_idl.template
+++ b/chrome/updater/app/server/win/updater_idl.template
@@ -257,7 +257,7 @@
 [
   object,
   dual,
-  uuid(IUPDATER_IID),
+  uuid(@63B8FFB1-5314-48C9-9C57-93EC8BC6184B@),
   helpstring("IUpdater Interface"),
   pointer_default(unique)
 ]
@@ -298,7 +298,7 @@
   // single 'coclass' definition. Any other coclasses need to be defined in
   // constants.h/cc.
   [
-    uuid(@UPDATER_CLSID@),
+    uuid(@158428a4-6014-4978-83ba-9fad0dabe791@),
     helpstring("Updater Class")
   ]
   coclass UpdaterClass
diff --git a/chromeos/chromeos_strings.grd b/chromeos/chromeos_strings.grd
index 7dafa85..d56d66e 100644
--- a/chromeos/chromeos_strings.grd
+++ b/chromeos/chromeos_strings.grd
@@ -511,6 +511,12 @@
       <message name="IDS_DIAGNOSTICS_CPU_USAGE_LABEL" desc="The label for the percentage of CPU that is currently being used." translateable="false">
         Currently using
       </message>
+      <message name="IDS_DIAGNOSTICS_CPU_USAGE_SYSTEM_LABEL" desc="The label for the percentage of CPU that is currently being used by the system." translateable="false">
+        System
+      </message>
+      <message name="IDS_DIAGNOSTICS_CPU_USAGE_USER_LABEL" desc="The label for the percentage of CPU that is currently being used by the user." translateable="false">
+        User
+      </message>
       <message name="IDS_DIAGNOSTICS_CPU_TEMPERATURE_LABEL" desc="The label for the current CPU temperature." translateable="false">
         Temperature
       </message>
diff --git a/chromeos/components/cdm_factory_daemon/chromeos_cdm_factory.cc b/chromeos/components/cdm_factory_daemon/chromeos_cdm_factory.cc
index f258083..b9d1c91 100644
--- a/chromeos/components/cdm_factory_daemon/chromeos_cdm_factory.cc
+++ b/chromeos/components/cdm_factory_daemon/chromeos_cdm_factory.cc
@@ -73,6 +73,9 @@
   if (!platform_verification_) {
     frame_interfaces_->BindEmbedderReceiver(mojo::GenericPendingReceiver(
         platform_verification_.BindNewPipeAndPassReceiver()));
+    platform_verification_.set_disconnect_handler(
+        base::BindOnce(&ChromeOsCdmFactory::OnVerificationMojoConnectionError,
+                       weak_factory_.GetWeakPtr()));
   }
   platform_verification_->IsVerifiedAccessEnabled(base::BindOnce(
       &ChromeOsCdmFactory::OnVerifiedAccessEnabled, weak_factory_.GetWeakPtr(),
@@ -147,7 +150,7 @@
   if (!remote_factory_) {
     remote_factory_.Bind(std::move(remote_factory));
     remote_factory_.set_disconnect_handler(
-        base::BindOnce(&ChromeOsCdmFactory::OnMojoConnectionError,
+        base::BindOnce(&ChromeOsCdmFactory::OnFactoryMojoConnectionError,
                        weak_factory_.GetWeakPtr()));
   }
 
@@ -202,9 +205,14 @@
       FROM_HERE, base::BindOnce(std::move(cdm_created_cb), std::move(cdm), ""));
 }
 
-void ChromeOsCdmFactory::OnMojoConnectionError() {
+void ChromeOsCdmFactory::OnFactoryMojoConnectionError() {
   DVLOG(1) << __func__;
   remote_factory_.reset();
 }
 
+void ChromeOsCdmFactory::OnVerificationMojoConnectionError() {
+  DVLOG(1) << __func__;
+  platform_verification_.reset();
+}
+
 }  // namespace chromeos
diff --git a/chromeos/components/cdm_factory_daemon/chromeos_cdm_factory.h b/chromeos/components/cdm_factory_daemon/chromeos_cdm_factory.h
index c5a4925..ce41d36 100644
--- a/chromeos/components/cdm_factory_daemon/chromeos_cdm_factory.h
+++ b/chromeos/components/cdm_factory_daemon/chromeos_cdm_factory.h
@@ -73,7 +73,8 @@
       const media::SessionKeysChangeCB& session_keys_change_cb,
       const media::SessionExpirationUpdateCB& session_expiration_update_cb,
       media::CdmCreatedCB cdm_created_cb);
-  void OnMojoConnectionError();
+  void OnFactoryMojoConnectionError();
+  void OnVerificationMojoConnectionError();
 
   media::mojom::FrameInterfaceFactory* frame_interfaces_;
   mojo::Remote<cdm::mojom::CdmFactory> remote_factory_;
diff --git a/chromeos/components/diagnostics_ui/diagnostics_ui.cc b/chromeos/components/diagnostics_ui/diagnostics_ui.cc
index f8a4c9d..6226236 100644
--- a/chromeos/components/diagnostics_ui/diagnostics_ui.cc
+++ b/chromeos/components/diagnostics_ui/diagnostics_ui.cc
@@ -36,6 +36,8 @@
       {"cpuTemp", IDS_DIAGNOSTICS_CPU_TEMPERATURE_LABEL},
       {"cpuTitle", IDS_DIAGNOSTICS_CPU_TITLE},
       {"cpuUsage", IDS_DIAGNOSTICS_CPU_USAGE_LABEL},
+      {"cpuUsageSystem", IDS_DIAGNOSTICS_CPU_USAGE_SYSTEM_LABEL},
+      {"cpuUsageUser", IDS_DIAGNOSTICS_CPU_USAGE_USER_LABEL},
       {"currentNow", IDS_DIAGNOSTICS_CURRENT_NOW_LABEL},
       {"cycleCount", IDS_DIAGNOSTICS_CYCLE_COUNT_LABEL},
       {"diagnosticsTitle", IDS_DIAGNOSTICS_TITLE},
diff --git a/chromeos/components/diagnostics_ui/resources/battery_status_card.js b/chromeos/components/diagnostics_ui/resources/battery_status_card.js
index a0adeb6f..7f73f4ca 100644
--- a/chromeos/components/diagnostics_ui/resources/battery_status_card.js
+++ b/chromeos/components/diagnostics_ui/resources/battery_status_card.js
@@ -6,12 +6,13 @@
 import './diagnostics_card.js';
 import './diagnostics_shared_css.js';
 import './percent_bar_chart.js';
+import './strings.m.js';
 
+import {I18nBehavior} from 'chrome://resources/js/i18n_behavior.m.js';
 import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
 import {BatteryChargeStatus, BatteryHealth, BatteryInfo, SystemDataProviderInterface} from './diagnostics_types.js'
 import {getSystemDataProvider} from './mojo_interface_provider.js';
-import {I18nBehavior} from 'chrome://resources/js/i18n_behavior.m.js';
-import './strings.js';
 
 /**
  * @fileoverview
diff --git a/chromeos/components/diagnostics_ui/resources/cpu_card.js b/chromeos/components/diagnostics_ui/resources/cpu_card.js
index aec8b026..5b1ffe6 100644
--- a/chromeos/components/diagnostics_ui/resources/cpu_card.js
+++ b/chromeos/components/diagnostics_ui/resources/cpu_card.js
@@ -6,13 +6,14 @@
 import './diagnostics_card.js';
 import './diagnostics_shared_css.js';
 import './realtime_cpu_chart.js';
+import './strings.m.js';
 
+import {I18nBehavior} from 'chrome://resources/js/i18n_behavior.m.js';
 import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {CpuUsage, SystemDataProviderInterface} from './diagnostics_types.js'
 import {getSystemDataProvider} from './mojo_interface_provider.js';
-import {I18nBehavior} from 'chrome://resources/js/i18n_behavior.m.js';
-import './strings.js';
+
 
 /**
  * @fileoverview
diff --git a/chromeos/components/diagnostics_ui/resources/diagnostics_app.js b/chromeos/components/diagnostics_ui/resources/diagnostics_app.js
index 56b7825..156ad46 100644
--- a/chromeos/components/diagnostics_ui/resources/diagnostics_app.js
+++ b/chromeos/components/diagnostics_ui/resources/diagnostics_app.js
@@ -9,10 +9,10 @@
 import './diagnostics_shared_css.js';
 import './memory_card.js';
 import './overview_card.js';
+import './strings.m.js';
 
-import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {I18nBehavior} from 'chrome://resources/js/i18n_behavior.m.js';
-import './strings.js';
+import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {SystemDataProviderInterface, SystemInfo} from './diagnostics_types.js'
 import {getSystemDataProvider} from './mojo_interface_provider.js';
diff --git a/chromeos/components/diagnostics_ui/resources/fake_data.js b/chromeos/components/diagnostics_ui/resources/fake_data.js
index 90c0e8c..2f14de9b 100644
--- a/chromeos/components/diagnostics_ui/resources/fake_data.js
+++ b/chromeos/components/diagnostics_ui/resources/fake_data.js
@@ -78,7 +78,27 @@
     cpu_temp_degrees_celcius: 109,
     percent_usage_system: 55,
     percent_usage_user: 24,
-  }
+  },
+  {
+    cpu_temp_degrees_celcius: 109,
+    percent_usage_system: 49,
+    percent_usage_user: 10,
+  },
+  {
+    cpu_temp_degrees_celcius: 161,
+    percent_usage_system: 1,
+    percent_usage_user: 99,
+  },
+  {
+    cpu_temp_degrees_celcius: 118,
+    percent_usage_system: 35,
+    percent_usage_user: 37,
+  },
+  {
+    cpu_temp_degrees_celcius: 110,
+    percent_usage_system: 26,
+    percent_usage_user: 30,
+  },
 ];
 
 /* @type {!Array<!MemoryUsage>} */
diff --git a/chromeos/components/diagnostics_ui/resources/memory_card.js b/chromeos/components/diagnostics_ui/resources/memory_card.js
index 65fc777..bb0d1ac 100644
--- a/chromeos/components/diagnostics_ui/resources/memory_card.js
+++ b/chromeos/components/diagnostics_ui/resources/memory_card.js
@@ -6,12 +6,13 @@
 import './diagnostics_card.js';
 import './diagnostics_shared_css.js';
 import './percent_bar_chart.js';
+import './strings.m.js';
 
+import {I18nBehavior} from 'chrome://resources/js/i18n_behavior.m.js';
 import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
 import {MemoryUsage, SystemDataProviderInterface} from './diagnostics_types.js'
 import {getSystemDataProvider} from './mojo_interface_provider.js';
-import {I18nBehavior} from 'chrome://resources/js/i18n_behavior.m.js';
-import './strings.js';
 
 /**
  * @fileoverview
diff --git a/chromeos/components/diagnostics_ui/resources/realtime_cpu_chart.html b/chromeos/components/diagnostics_ui/resources/realtime_cpu_chart.html
index 485651d..efc43bb 100644
--- a/chromeos/components/diagnostics_ui/resources/realtime_cpu_chart.html
+++ b/chromeos/components/diagnostics_ui/resources/realtime_cpu_chart.html
@@ -50,13 +50,13 @@
   </g>
 </svg>
 <div id="chart-legend">
-  <!-- TODO(joonbug): Localize strings, including the % -->
+  <!-- TODO(joonbug): Add i18n string for percent number format -->
   <div id="legend-user">
-    <label>User</label>
+    <label>[[i18n('cpuUsageUser')]]</label>
     <span>[[user]]</span>
   </div>
   <div id="legend-system">
-    <label>System</label>
+    <label>[[i18n('cpuUsageSystem')]]</label>
     <span>[[system]]</span>
   </div>
 </div>
diff --git a/chromeos/components/diagnostics_ui/resources/realtime_cpu_chart.js b/chromeos/components/diagnostics_ui/resources/realtime_cpu_chart.js
index fa749c9d..5a069c0 100644
--- a/chromeos/components/diagnostics_ui/resources/realtime_cpu_chart.js
+++ b/chromeos/components/diagnostics_ui/resources/realtime_cpu_chart.js
@@ -2,12 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import './d3.min.js';
 import './diagnostics_fonts_css.js';
 import './diagnostics_shared_css.js';
-
-import './d3.min.js';
+import './strings.m.js';
 
 import {assert} from 'chrome://resources/js/assert.m.js';
+import {I18nBehavior} from 'chrome://resources/js/i18n_behavior.m.js';
 import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 /**
@@ -20,6 +21,8 @@
 
   _template: html`{__html_template__}`,
 
+  behaviors: [I18nBehavior],
+
   /**
    * Helper function to map range of x coordinates to graph width.
    * @private {?d3.LinearScale}
diff --git a/chromeos/components/diagnostics_ui/resources/routine_result_entry.html b/chromeos/components/diagnostics_ui/resources/routine_result_entry.html
index cb332227..567fcfab 100644
--- a/chromeos/components/diagnostics_ui/resources/routine_result_entry.html
+++ b/chromeos/components/diagnostics_ui/resources/routine_result_entry.html
@@ -1,5 +1,13 @@
 <style include="diagnostics-shared">
+  .entryRow {
+    display: flex;
+    flex-direction: row;
+    justify-content: space-between;
+  }
 </style>
 
 <div class="entryRow">
+  <!-- TODO(zentaro): Create mapping to localized strings. -->
+  <div id="routine">[[routineName_]]</div>
+  <div id="status">[[status_]]</div>
 </div>
diff --git a/chromeos/components/diagnostics_ui/resources/routine_result_entry.js b/chromeos/components/diagnostics_ui/resources/routine_result_entry.js
index 2ea61864..659f8aa 100644
--- a/chromeos/components/diagnostics_ui/resources/routine_result_entry.js
+++ b/chromeos/components/diagnostics_ui/resources/routine_result_entry.js
@@ -5,7 +5,31 @@
 import './diagnostics_card.js';
 import './diagnostics_shared_css.js';
 
+import {assert} from 'chrome://resources/js/assert.m.js';
 import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {RoutineName, RoutineResult, StandardRoutineResult} from './diagnostics_types.js';
+import {ExecutionProgress, ResultStatusItem} from './routine_list_executor.js';
+
+/**
+ * Resolves an enum value to the string name. This is used temporarily to
+ * provide a human readable string until the final mapping of enum values to
+ * localized strings is finalized.
+ * TODO(zentaro): Remove this function when strings are finalized.
+ * @param {!Object} enumType
+ * @param {number} enumValue
+ * @return {string}
+ */
+function lookupEnumValueName(enumType, enumValue) {
+  for (const [key, value] of Object.entries(enumType)) {
+    if (value === enumValue) {
+      return key;
+    }
+  }
+
+  // Values should always be found in the enum.
+  assert(false);
+  return '';
+}
 
 /**
  * @fileoverview
@@ -16,7 +40,56 @@
 
   _template: html`{__html_template__}`,
 
-  properties: {},
+  properties: {
+    /** @type {!ResultStatusItem} */
+    item: {
+      type: Object,
+    },
+
+    /** @private */
+    routineName_: {
+      type: String,
+      computed: 'getRoutineName_(item.routine)',
+    },
+
+    /** @private */
+    status_: {
+      type: String,
+      computed: 'getRoutineStatus_(item.progress, item.result)',
+    },
+  },
+
+  /**
+   * Get the string name for the routine.
+   * TODO(zentaro): Replace with a mapping to localized string when they are
+   * finalized.
+   * @param {!RoutineName} routine
+   * @return {string}
+   */
+  getRoutineName_(routine) {
+    return lookupEnumValueName(RoutineName, routine);
+  },
+
+  /**
+   * Get the status for the routine. This is a combination of progress and
+   * result.
+   * TODO(zentaro): Replace with a mapping to localized string when they are
+   * finalized.
+   * @param {!ExecutionProgress} progress
+   * @param {!RoutineResult} result
+   * @return {string}
+   */
+  getRoutineStatus_(progress, result) {
+    if (progress === ExecutionProgress.kNotStarted) {
+      return '';
+    }
+
+    if (progress === ExecutionProgress.kRunning) {
+      return lookupEnumValueName(ExecutionProgress, ExecutionProgress.kRunning);
+    }
+
+    return lookupEnumValueName(StandardRoutineResult, result.simple_result);
+  },
 
   /** @override */
   created() {},
diff --git a/chromeos/profiles/orderfile.newest.txt b/chromeos/profiles/orderfile.newest.txt
index 9069250c..c7a1d6ef1 100644
--- a/chromeos/profiles/orderfile.newest.txt
+++ b/chromeos/profiles/orderfile.newest.txt
@@ -1 +1 @@
-chromeos-chrome-orderfile-field-87-4277.0-1602498224-benchmark-87.0.4280.20-r1.orderfile.xz
+chromeos-chrome-orderfile-field-88-4277.0-1602497703-benchmark-88.0.4288.0-r1.orderfile.xz
diff --git a/chromeos/services/cros_healthd/public/mojom/cros_healthd_probe.mojom b/chromeos/services/cros_healthd/public/mojom/cros_healthd_probe.mojom
index 2b0e51a..310188119 100644
--- a/chromeos/services/cros_healthd/public/mojom/cros_healthd_probe.mojom
+++ b/chromeos/services/cros_healthd/public/mojom/cros_healthd_probe.mojom
@@ -304,6 +304,12 @@
   uint32 scaling_max_frequency_khz;
   // Current frequency the CPU is running at.
   uint32 scaling_current_frequency_khz;
+  // Time spent in user mode since last boot. USER_HZ can be converted to
+  // seconds with the conversion factor given by sysconf(_SC_CLK_TCK).
+  uint64 user_time_user_hz;
+  // Time spent in system mode since last boot. USER_HZ can be converted to
+  // seconds with the conversion factor given by sysconf(_SC_CLK_TCK).
+  uint64 system_time_user_hz;
   // Idle time since last boot. USER_HZ can be converted to seconds with the
   // conversion factor given by sysconf(_SC_CLK_TCK).
   uint32 idle_time_user_hz;
diff --git a/chromeos/services/multidevice_setup/wifi_sync_feature_manager_impl.cc b/chromeos/services/multidevice_setup/wifi_sync_feature_manager_impl.cc
index 2702b81..b7126f74 100644
--- a/chromeos/services/multidevice_setup/wifi_sync_feature_manager_impl.cc
+++ b/chromeos/services/multidevice_setup/wifi_sync_feature_manager_impl.cc
@@ -15,6 +15,7 @@
 #include "chromeos/constants/chromeos_features.h"
 #include "chromeos/services/device_sync/feature_status_change.h"
 #include "chromeos/services/multidevice_setup/host_status_provider.h"
+#include "chromeos/services/multidevice_setup/public/cpp/prefs.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 
@@ -96,11 +97,8 @@
       !ShouldEnableOnVerify()) {
     ResetPendingWifiSyncHostNetworkRequest();
   }
-  // kHostSetLocallyButWaitingForBackendConfirmation is only possible if the
-  // setup flow has been completed on the local device.
-  if (host_status_with_device.host_status() ==
-          mojom::HostStatus::kHostSetLocallyButWaitingForBackendConfirmation &&
-      features::IsWifiSyncAndroidEnabled()) {
+
+  if (ShouldAttemptToEnableAfterHostVerified()) {
     SetPendingWifiSyncHostNetworkRequest(
         PendingState::kSetPendingEnableOnVerify);
     return;
@@ -317,6 +315,33 @@
   SetIsWifiSyncEnabled(true);
 }
 
+bool WifiSyncFeatureManagerImpl::ShouldAttemptToEnableAfterHostVerified() {
+  HostStatusProvider::HostStatusWithDevice host_status_with_device =
+      host_status_provider_->GetHostWithStatus();
+
+  // kHostSetLocallyButWaitingForBackendConfirmation is only possible if the
+  // setup flow has been completed on the local device.
+  if (host_status_with_device.host_status() !=
+      mojom::HostStatus::kHostSetLocallyButWaitingForBackendConfirmation) {
+    return false;
+  }
+
+  // Check if enterprise policy prohibits Wifi Sync or if feature flag is
+  // disabled.
+  if (!IsFeatureAllowed(mojom::Feature::kWifiSync, pref_service_)) {
+    return false;
+  }
+
+  // Check if wifi sync is supported by host device.
+  if (host_status_with_device.host_device()->GetSoftwareFeatureState(
+          multidevice::SoftwareFeature::kWifiSyncHost) ==
+      multidevice::SoftwareFeatureState::kNotSupported) {
+    return false;
+  }
+
+  return true;
+}
+
 }  // namespace multidevice_setup
 
 }  // namespace chromeos
diff --git a/chromeos/services/multidevice_setup/wifi_sync_feature_manager_impl.h b/chromeos/services/multidevice_setup/wifi_sync_feature_manager_impl.h
index 7acfaa5..d5c747b 100644
--- a/chromeos/services/multidevice_setup/wifi_sync_feature_manager_impl.h
+++ b/chromeos/services/multidevice_setup/wifi_sync_feature_manager_impl.h
@@ -123,6 +123,7 @@
       device_sync::mojom::NetworkRequestResult result_code);
   bool ShouldEnableOnVerify();
   void ProcessEnableOnVerifyAttempt();
+  bool ShouldAttemptToEnableAfterHostVerified();
 
   HostStatusProvider* host_status_provider_;
   PrefService* pref_service_;
diff --git a/chromeos/services/multidevice_setup/wifi_sync_feature_manager_impl_unittest.cc b/chromeos/services/multidevice_setup/wifi_sync_feature_manager_impl_unittest.cc
index 5d5378e..95a20d1b 100644
--- a/chromeos/services/multidevice_setup/wifi_sync_feature_manager_impl_unittest.cc
+++ b/chromeos/services/multidevice_setup/wifi_sync_feature_manager_impl_unittest.cc
@@ -18,6 +18,7 @@
 #include "chromeos/constants/chromeos_features.h"
 #include "chromeos/services/device_sync/public/cpp/fake_device_sync_client.h"
 #include "chromeos/services/multidevice_setup/fake_host_status_provider.h"
+#include "chromeos/services/multidevice_setup/public/cpp/prefs.h"
 #include "chromeos/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -70,7 +71,9 @@
     test_pref_service_ =
         std::make_unique<sync_preferences::TestingPrefServiceSyncable>();
     WifiSyncFeatureManagerImpl::RegisterPrefs(test_pref_service_->registry());
-
+    // Allow Wifi Sync by policy
+    test_pref_service_->registry()->RegisterBooleanPref(
+        kWifiSyncAllowedPrefName, true);
     fake_device_sync_client_ =
         std::make_unique<device_sync::FakeDeviceSyncClient>();
     fake_device_sync_client_->set_synced_devices(test_devices_);
@@ -794,6 +797,47 @@
 }
 
 TEST_P(MultiDeviceSetupWifiSyncFeatureManagerImplTest,
+       SetPendingEnableOnVerify_WifiSyncNotAllowedByPolicy) {
+  SetFeatureFlags(GetParam() /* use_v1_devicesync */,
+                  true /* enable_wifi_sync */);
+  // Disable by policy
+  test_pref_service()->SetBoolean(kWifiSyncAllowedPrefName, false);
+  CreateDelegate(base::nullopt /* initial_host */);
+
+  // kHostSetLocallyButWaitingForBackendConfirmation is only possible if the
+  // setup flow has been completed on the local device.
+  SetHostInDeviceSyncClient(test_devices()[0]);
+  fake_host_status_provider()->SetHostWithStatus(
+      mojom::HostStatus::kHostSetLocallyButWaitingForBackendConfirmation,
+      test_devices()[0]);
+  EXPECT_EQ(
+      test_pref_service()->GetInteger(kPendingWifiSyncRequestEnabledPrefName),
+      kPendingNone);
+  EXPECT_FALSE(delegate()->IsWifiSyncEnabled());
+}
+
+TEST_P(MultiDeviceSetupWifiSyncFeatureManagerImplTest,
+       SetPendingEnableOnVerify_WifiSyncNotSupportedOnHostDevice) {
+  SetFeatureFlags(GetParam() /* use_v1_devicesync */,
+                  true /* enable_wifi_sync */);
+  CreateDelegate(base::nullopt /* initial_host */);
+  GetMutableRemoteDevice(test_devices()[0])
+      ->software_features[multidevice::SoftwareFeature::kWifiSyncHost] =
+      multidevice::SoftwareFeatureState::kNotSupported;
+
+  // kHostSetLocallyButWaitingForBackendConfirmation is only possible if the
+  // setup flow has been completed on the local device.
+  SetHostInDeviceSyncClient(test_devices()[0]);
+  fake_host_status_provider()->SetHostWithStatus(
+      mojom::HostStatus::kHostSetLocallyButWaitingForBackendConfirmation,
+      test_devices()[0]);
+  EXPECT_EQ(
+      test_pref_service()->GetInteger(kPendingWifiSyncRequestEnabledPrefName),
+      kPendingNone);
+  EXPECT_FALSE(delegate()->IsWifiSyncEnabled());
+}
+
+TEST_P(MultiDeviceSetupWifiSyncFeatureManagerImplTest,
        SetPendingEnableOnVerify_HostRemoved) {
   SetFeatureFlags(GetParam() /* use_v1_devicesync */,
                   true /* enable_wifi_sync */);
diff --git a/chromeos/ui/base/window_properties.cc b/chromeos/ui/base/window_properties.cc
index 57b76565..3e74f3c 100644
--- a/chromeos/ui/base/window_properties.cc
+++ b/chromeos/ui/base/window_properties.cc
@@ -5,13 +5,27 @@
 #include "chromeos/ui/base/window_properties.h"
 
 #include "chromeos/ui/base/window_state_type.h"
+// TODO(crbug.com/1138662): Remove this include and the associated property
+// and histogram entry.
+#include "chromeos/ui/frame/immersive/immersive_fullscreen_controller.h"  // nogncheck
 #include "ui/aura/window.h"
 
 namespace chromeos {
 
+DEFINE_UI_CLASS_PROPERTY_KEY(bool, kImmersiveImpliedByFullscreen, true)
+DEFINE_UI_CLASS_PROPERTY_KEY(bool, kImmersiveIsActive, false)
+DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(gfx::Rect,
+                                   kImmersiveTopContainerBoundsInScreen,
+                                   nullptr)
+DEFINE_UI_CLASS_PROPERTY_KEY(
+    int,
+    kImmersiveWindowType,
+    ImmersiveFullscreenController::WindowType::WINDOW_TYPE_OTHER)
+
 DEFINE_UI_CLASS_PROPERTY_KEY(bool, kIsShowingInOverviewKey, false)
 
 DEFINE_UI_CLASS_PROPERTY_KEY(WindowStateType,
                              kWindowStateTypeKey,
                              WindowStateType::kDefault)
+
 }  // namespace chromeos
diff --git a/chromeos/ui/base/window_properties.h b/chromeos/ui/base/window_properties.h
index aab2a63..d39a78c 100644
--- a/chromeos/ui/base/window_properties.h
+++ b/chromeos/ui/base/window_properties.h
@@ -13,6 +13,10 @@
 using WindowProperty = ui::ClassProperty<T>;
 }  // namespace aura
 
+namespace gfx {
+class Rect;
+}
+
 namespace chromeos {
 
 enum class WindowStateType;
@@ -21,6 +25,26 @@
 
 // Alphabetical sort.
 
+// Whether entering fullscreen means that a window should automatically enter
+// immersive mode. This is false for some client windows, such as Chrome Apps.
+COMPONENT_EXPORT(CHROMEOS_UI_BASE)
+extern const aura::WindowProperty<bool>* const kImmersiveImpliedByFullscreen;
+
+// Whether immersive is currently active (in ImmersiveFullscreenController
+// parlance, "enabled").
+COMPONENT_EXPORT(CHROMEOS_UI_BASE)
+extern const aura::WindowProperty<bool>* const kImmersiveIsActive;
+
+// The bounds of the top container in screen coordinates.
+COMPONENT_EXPORT(CHROMEOS_UI_BASE)
+extern const aura::WindowProperty<gfx::Rect*>* const
+    kImmersiveTopContainerBoundsInScreen;
+
+// The type of window for logging immersive metrics. Type:
+// ImmersiveFullscreenController::WindowType.
+COMPONENT_EXPORT(CHROMEOS_UI_BASE)
+extern const aura::WindowProperty<int>* const kImmersiveWindowType;
+
 // If true, the window is currently showing in overview mode.
 COMPONENT_EXPORT(CHROMEOS_UI_BASE)
 extern const aura::WindowProperty<bool>* const kIsShowingInOverviewKey;
diff --git a/chromeos/ui/frame/BUILD.gn b/chromeos/ui/frame/BUILD.gn
index 88a9f08..fcb0c91 100644
--- a/chromeos/ui/frame/BUILD.gn
+++ b/chromeos/ui/frame/BUILD.gn
@@ -20,6 +20,15 @@
     "caption_buttons/frame_size_button_delegate.h",
     "caption_buttons/snap_controller.cc",
     "caption_buttons/snap_controller.h",
+    "immersive/immersive_context.cc",
+    "immersive/immersive_context.h",
+    "immersive/immersive_focus_watcher.cc",
+    "immersive/immersive_focus_watcher.h",
+    "immersive/immersive_fullscreen_controller.cc",
+    "immersive/immersive_fullscreen_controller.h",
+    "immersive/immersive_fullscreen_controller_delegate.h",
+    "immersive/immersive_revealed_lock.cc",
+    "immersive/immersive_revealed_lock.h",
   ]
 
   deps = [
@@ -31,5 +40,26 @@
     "//ui/base",
     "//ui/strings:ui_strings_grit",
     "//ui/views",
+    "//ui/wm/public",
+  ]
+}
+
+source_set("test_support") {
+  testonly = true
+  sources = [
+    "immersive/immersive_fullscreen_controller_test_api.cc",
+    "immersive/immersive_fullscreen_controller_test_api.h",
+  ]
+
+  deps = [
+    ":frame",
+    "//base",
+    "//services/device/public/cpp:test_support",
+    "//services/network/public/cpp:cpp",
+    "//ui/aura",
+    "//ui/aura:test_support",
+    "//ui/gfx",
+    "//ui/gfx:test_support",
+    "//ui/views",
   ]
 }
diff --git a/chromeos/ui/frame/DEPS b/chromeos/ui/frame/DEPS
index b2b9550..cb567ae 100644
--- a/chromeos/ui/frame/DEPS
+++ b/chromeos/ui/frame/DEPS
@@ -2,9 +2,11 @@
   "+ui/aura",
   "+ui/base",
   "+ui/compositor/scoped_animation_duration_scale_mode.h",
-  "+ui/events/event_sink.h",
+  "+ui/display",
+  "+ui/events",
   "+ui/gfx",
   "+ui/strings/grit/ui_strings.h",
   "+ui/views",
+  "+ui/wm/public",
   "+third_party/skia",
 ]
diff --git a/ash/public/cpp/immersive/immersive_context.cc b/chromeos/ui/frame/immersive/immersive_context.cc
similarity index 83%
rename from ash/public/cpp/immersive/immersive_context.cc
rename to chromeos/ui/frame/immersive/immersive_context.cc
index 7ab1e54..b914243 100644
--- a/ash/public/cpp/immersive/immersive_context.cc
+++ b/chromeos/ui/frame/immersive/immersive_context.cc
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ash/public/cpp/immersive/immersive_context.h"
+#include "chromeos/ui/frame/immersive/immersive_context.h"
 
 #include "base/check_op.h"
 
-namespace ash {
+namespace chromeos {
 
 namespace {
 
@@ -29,4 +29,4 @@
   g_instance = this;
 }
 
-}  // namespace ash
+}  // namespace chromeos
diff --git a/ash/public/cpp/immersive/immersive_context.h b/chromeos/ui/frame/immersive/immersive_context.h
similarity index 77%
rename from ash/public/cpp/immersive/immersive_context.h
rename to chromeos/ui/frame/immersive/immersive_context.h
index 1240bd7..47aa7f8 100644
--- a/ash/public/cpp/immersive/immersive_context.h
+++ b/chromeos/ui/frame/immersive/immersive_context.h
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef ASH_PUBLIC_CPP_IMMERSIVE_IMMERSIVE_CONTEXT_H_
-#define ASH_PUBLIC_CPP_IMMERSIVE_IMMERSIVE_CONTEXT_H_
+#ifndef CHROMEOS_UI_FRAME_IMMERSIVE_IMMERSIVE_CONTEXT_H_
+#define CHROMEOS_UI_FRAME_IMMERSIVE_IMMERSIVE_CONTEXT_H_
 
-#include "ash/public/cpp/ash_public_export.h"
+#include "base/component_export.h"
 
 namespace gfx {
 class Rect;
@@ -15,13 +15,13 @@
 class Widget;
 }
 
-namespace ash {
+namespace chromeos {
 
 class ImmersiveFullscreenController;
 
 // ImmersiveContext abstracts away all the windowing related calls so that
 // ImmersiveFullscreenController does not depend upon aura or ash.
-class ASH_PUBLIC_EXPORT ImmersiveContext {
+class COMPONENT_EXPORT(CHROMEOS_UI_FRAME) ImmersiveContext {
  public:
   virtual ~ImmersiveContext();
 
@@ -45,6 +45,6 @@
   ImmersiveContext();
 };
 
-}  // namespace ash
+}  // namespace chromeos
 
-#endif  // ASH_PUBLIC_CPP_IMMERSIVE_IMMERSIVE_CONTEXT_H_
+#endif  // CHROMEOS_UI_FRAME_IMMERSIVE_IMMERSIVE_CONTEXT_H_
diff --git a/ash/public/cpp/immersive/immersive_focus_watcher.cc b/chromeos/ui/frame/immersive/immersive_focus_watcher.cc
similarity index 97%
rename from ash/public/cpp/immersive/immersive_focus_watcher.cc
rename to chromeos/ui/frame/immersive/immersive_focus_watcher.cc
index 3817d3f0..400518b 100644
--- a/ash/public/cpp/immersive/immersive_focus_watcher.cc
+++ b/chromeos/ui/frame/immersive/immersive_focus_watcher.cc
@@ -2,9 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ash/public/cpp/immersive/immersive_focus_watcher.h"
+#include "chromeos/ui/frame/immersive/immersive_focus_watcher.h"
 
-#include "ash/public/cpp/immersive/immersive_fullscreen_controller.h"
+#include "chromeos/ui/frame/immersive/immersive_fullscreen_controller.h"
 #include "ui/aura/client/transient_window_client.h"
 #include "ui/aura/window.h"
 #include "ui/views/bubble/bubble_dialog_delegate_view.h"
@@ -12,7 +12,7 @@
 #include "ui/views/widget/widget.h"
 #include "ui/wm/public/activation_client.h"
 
-namespace ash {
+namespace chromeos {
 
 namespace {
 
@@ -276,7 +276,7 @@
       immersive_fullscreen_controller_->top_container()->Contains(anchor)) {
     // Observe the aura::Window because the BubbleDelegateView may not be
     // parented to the widget's root view yet so |bubble_delegate->GetWidget()|
-    // may still return NULL.
+    // may still return null.
     bubble_observer_->StartObserving(transient);
   }
 }
@@ -287,4 +287,4 @@
   bubble_observer_->StopObserving(transient);
 }
 
-}  // namespace ash
+}  // namespace chromeos
diff --git a/ash/public/cpp/immersive/immersive_focus_watcher.h b/chromeos/ui/frame/immersive/immersive_focus_watcher.h
similarity index 92%
rename from ash/public/cpp/immersive/immersive_focus_watcher.h
rename to chromeos/ui/frame/immersive/immersive_focus_watcher.h
index 7799b06f..c4b0f11 100644
--- a/ash/public/cpp/immersive/immersive_focus_watcher.h
+++ b/chromeos/ui/frame/immersive/immersive_focus_watcher.h
@@ -2,15 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef ASH_PUBLIC_CPP_IMMERSIVE_IMMERSIVE_FOCUS_WATCHER_H_
-#define ASH_PUBLIC_CPP_IMMERSIVE_IMMERSIVE_FOCUS_WATCHER_H_
+#ifndef CHROMEOS_UI_FRAME_IMMERSIVE_IMMERSIVE_FOCUS_WATCHER_H_
+#define CHROMEOS_UI_FRAME_IMMERSIVE_IMMERSIVE_FOCUS_WATCHER_H_
 
 #include "ui/aura/client/focus_change_observer.h"
 #include "ui/aura/client/transient_window_client_observer.h"
 #include "ui/views/focus/focus_manager.h"
 #include "ui/wm/public/activation_change_observer.h"
 
-namespace ash {
+namespace chromeos {
 class ImmersiveFullscreenController;
 class ImmersiveRevealedLock;
 
@@ -76,6 +76,6 @@
   DISALLOW_COPY_AND_ASSIGN(ImmersiveFocusWatcher);
 };
 
-}  // namespace ash
+}  // namespace chromeos
 
-#endif  // ASH_PUBLIC_CPP_IMMERSIVE_IMMERSIVE_FOCUS_WATCHER_H_
+#endif  // CHROMEOS_UI_FRAME_IMMERSIVE_IMMERSIVE_FOCUS_WATCHER_H_
diff --git a/ash/public/cpp/immersive/immersive_fullscreen_controller.cc b/chromeos/ui/frame/immersive/immersive_fullscreen_controller.cc
similarity index 97%
rename from ash/public/cpp/immersive/immersive_fullscreen_controller.cc
rename to chromeos/ui/frame/immersive/immersive_fullscreen_controller.cc
index c9cf255..f28077b9 100644
--- a/ash/public/cpp/immersive/immersive_fullscreen_controller.cc
+++ b/chromeos/ui/frame/immersive/immersive_fullscreen_controller.cc
@@ -2,16 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ash/public/cpp/immersive/immersive_fullscreen_controller.h"
+#include "chromeos/ui/frame/immersive/immersive_fullscreen_controller.h"
 
 #include <set>
 
-#include "ash/public/cpp/immersive/immersive_context.h"
-#include "ash/public/cpp/immersive/immersive_focus_watcher.h"
-#include "ash/public/cpp/immersive/immersive_fullscreen_controller_delegate.h"
-#include "ash/public/cpp/window_properties.h"
 #include "base/bind.h"
 #include "base/metrics/histogram_macros.h"
+#include "chromeos/ui/base/window_properties.h"
+#include "chromeos/ui/frame/immersive/immersive_context.h"
+#include "chromeos/ui/frame/immersive/immersive_focus_watcher.h"
+#include "chromeos/ui/frame/immersive/immersive_fullscreen_controller_delegate.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/client/cursor_client.h"
 #include "ui/aura/env.h"
@@ -28,9 +28,9 @@
 #include "ui/views/view.h"
 #include "ui/views/widget/widget.h"
 
-DEFINE_UI_CLASS_PROPERTY_TYPE(ash::ImmersiveFullscreenController*)
+DEFINE_UI_CLASS_PROPERTY_TYPE(chromeos::ImmersiveFullscreenController*)
 
-namespace ash {
+namespace chromeos {
 
 DEFINE_UI_CLASS_PROPERTY_KEY(ImmersiveFullscreenController*,
                              kImmersiveFullscreenControllerKey,
@@ -463,8 +463,8 @@
   for (size_t i = 0; i < hit_bounds_in_screen.size(); ++i) {
     // Allow the cursor to move slightly off the top-of-window views before
     // sliding closed. In the case of ImmersiveModeControllerAsh, this helps
-    // when the user is attempting to click on the bookmark bar and overshoots
-    // slightly.
+    // when the user is attempting to click on the bookmark bar and
+    // overshoots slightly.
     if (event && event->type() == ui::ET_MOUSE_MOVED) {
       const int kBoundsOffsetY = 8;
       hit_bounds_in_screen[i].Inset(0, 0, 0, -kBoundsOffsetY);
@@ -768,6 +768,7 @@
   }
 
   if (enabled_) {
+    // TODO(https://crbug.com/1138662): Remove this expired histogram entry.
     UMA_HISTOGRAM_ENUMERATION(
         "Ash.ImmersiveFullscreen.WindowType",
         static_cast<WindowType>(
@@ -784,4 +785,4 @@
       {}, gfx::Insets(enable ? kImmersiveFullscreenTopEdgeInset : 0, 0, 0, 0));
 }
 
-}  // namespace ash
+}  // namespace chromeos
diff --git a/ash/public/cpp/immersive/immersive_fullscreen_controller.h b/chromeos/ui/frame/immersive/immersive_fullscreen_controller.h
similarity index 93%
rename from ash/public/cpp/immersive/immersive_fullscreen_controller.h
rename to chromeos/ui/frame/immersive/immersive_fullscreen_controller.h
index 9bf77ea..ba83ea8 100644
--- a/ash/public/cpp/immersive/immersive_fullscreen_controller.h
+++ b/chromeos/ui/frame/immersive/immersive_fullscreen_controller.h
@@ -2,16 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef ASH_PUBLIC_CPP_IMMERSIVE_IMMERSIVE_FULLSCREEN_CONTROLLER_H_
-#define ASH_PUBLIC_CPP_IMMERSIVE_IMMERSIVE_FULLSCREEN_CONTROLLER_H_
+#ifndef CHROMEOS_UI_FRAME_IMMERSIVE_IMMERSIVE_FULLSCREEN_CONTROLLER_H_
+#define CHROMEOS_UI_FRAME_IMMERSIVE_IMMERSIVE_FULLSCREEN_CONTROLLER_H_
 
 #include <memory>
 #include <vector>
 
-#include "ash/public/cpp/ash_public_export.h"
-#include "ash/public/cpp/immersive/immersive_revealed_lock.h"
+#include "base/component_export.h"
 #include "base/macros.h"
 #include "base/timer/timer.h"
+#include "chromeos/ui/frame/immersive/immersive_revealed_lock.h"
 #include "ui/aura/window_observer.h"
 #include "ui/events/event_handler.h"
 #include "ui/events/event_observer.h"
@@ -22,7 +22,11 @@
 
 namespace aura {
 class WindowTargeter;
-}
+}  // namespace aura
+
+namespace ash {
+class ImmersiveFullscreenControllerTest;
+}  // namespace ash
 
 namespace gfx {
 class Point;
@@ -40,13 +44,15 @@
 class Widget;
 }  // namespace views
 
-namespace ash {
+namespace chromeos {
 
 class ImmersiveFocusWatcher;
 class ImmersiveFullscreenControllerDelegate;
 class ImmersiveFullscreenControllerTestApi;
 
-class ASH_PUBLIC_EXPORT ImmersiveFullscreenController
+// This class is tested as part of //ash, eg ImmersiveFullscreenControllerTest,
+// which inherits from AshTestBase.
+class COMPONENT_EXPORT(CHROMEOS_UI_FRAME) ImmersiveFullscreenController
     : public aura::WindowObserver,
       public gfx::AnimationDelegate,
       public ui::EventObserver,
@@ -130,7 +136,7 @@
   void AnimationEnded(const gfx::Animation* animation) override;
   void AnimationProgressed(const gfx::Animation* animation) override;
 
-  // ash::ImmersiveRevealedLock::Delegate overrides:
+  // chromeos::ImmersiveRevealedLock::Delegate overrides:
   void LockRevealedState(AnimateReveal animate_reveal) override;
   void UnlockRevealedState() override;
 
@@ -139,7 +145,7 @@
   static ImmersiveFullscreenController* Get(views::Widget* widget);
 
  private:
-  friend class ImmersiveFullscreenControllerTest;
+  friend class ash::ImmersiveFullscreenControllerTest;
   friend class ImmersiveFullscreenControllerTestApi;
 
   enum Animate {
@@ -179,7 +185,7 @@
 
   // Updates |located_event_revealed_lock_| based on the current mouse state and
   // the current touch state.
-  // |event| is NULL if the source event is not known.
+  // |event| is null if the source event is not known.
   void UpdateLocatedEventRevealedLock(const ui::LocatedEvent* event,
                                       const gfx::Point& location_in_screen);
 
@@ -309,6 +315,6 @@
   DISALLOW_COPY_AND_ASSIGN(ImmersiveFullscreenController);
 };
 
-}  // namespace ash
+}  // namespace chromeos
 
-#endif  // ASH_PUBLIC_CPP_IMMERSIVE_IMMERSIVE_FULLSCREEN_CONTROLLER_H_
+#endif  // CHROMEOS_UI_FRAME_IMMERSIVE_IMMERSIVE_FULLSCREEN_CONTROLLER_H_
diff --git a/ash/public/cpp/immersive/immersive_fullscreen_controller_delegate.h b/chromeos/ui/frame/immersive/immersive_fullscreen_controller_delegate.h
similarity index 79%
rename from ash/public/cpp/immersive/immersive_fullscreen_controller_delegate.h
rename to chromeos/ui/frame/immersive/immersive_fullscreen_controller_delegate.h
index f2e5b7a..2a2b2de 100644
--- a/ash/public/cpp/immersive/immersive_fullscreen_controller_delegate.h
+++ b/chromeos/ui/frame/immersive/immersive_fullscreen_controller_delegate.h
@@ -2,20 +2,21 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef ASH_PUBLIC_CPP_IMMERSIVE_IMMERSIVE_FULLSCREEN_CONTROLLER_DELEGATE_H_
-#define ASH_PUBLIC_CPP_IMMERSIVE_IMMERSIVE_FULLSCREEN_CONTROLLER_DELEGATE_H_
+#ifndef CHROMEOS_UI_FRAME_IMMERSIVE_IMMERSIVE_FULLSCREEN_CONTROLLER_DELEGATE_H_
+#define CHROMEOS_UI_FRAME_IMMERSIVE_IMMERSIVE_FULLSCREEN_CONTROLLER_DELEGATE_H_
 
 #include <vector>
 
-#include "ash/public/cpp/ash_public_export.h"
+#include "base/component_export.h"
 
 namespace gfx {
 class Rect;
 }
 
-namespace ash {
+namespace chromeos {
 
-class ASH_PUBLIC_EXPORT ImmersiveFullscreenControllerDelegate {
+class COMPONENT_EXPORT(CHROMEOS_UI_FRAME)
+    ImmersiveFullscreenControllerDelegate {
  public:
   // Called when a reveal of the top-of-window views starts.
   virtual void OnImmersiveRevealStarted() = 0;
@@ -49,6 +50,6 @@
   virtual ~ImmersiveFullscreenControllerDelegate() {}
 };
 
-}  // namespace ash
+}  // namespace chromeos
 
-#endif  // ASH_PUBLIC_CPP_IMMERSIVE_IMMERSIVE_FULLSCREEN_CONTROLLER_DELEGATE_H_
+#endif  // CHROMEOS_UI_FRAME_IMMERSIVE_IMMERSIVE_FULLSCREEN_CONTROLLER_DELEGATE_H_
diff --git a/ash/public/cpp/immersive/immersive_fullscreen_controller_test_api.cc b/chromeos/ui/frame/immersive/immersive_fullscreen_controller_test_api.cc
similarity index 87%
rename from ash/public/cpp/immersive/immersive_fullscreen_controller_test_api.cc
rename to chromeos/ui/frame/immersive/immersive_fullscreen_controller_test_api.cc
index 9f1d2081a..c52d866 100644
--- a/ash/public/cpp/immersive/immersive_fullscreen_controller_test_api.cc
+++ b/chromeos/ui/frame/immersive/immersive_fullscreen_controller_test_api.cc
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ash/public/cpp/immersive/immersive_fullscreen_controller_test_api.h"
+#include "chromeos/ui/frame/immersive/immersive_fullscreen_controller_test_api.h"
 
-#include "ash/public/cpp/immersive/immersive_fullscreen_controller.h"
-#include "ash/public/cpp/immersive/immersive_fullscreen_controller_delegate.h"
+#include "chromeos/ui/frame/immersive/immersive_fullscreen_controller.h"
+#include "chromeos/ui/frame/immersive/immersive_fullscreen_controller_delegate.h"
 #include "ui/aura/env.h"
 #include "ui/gfx/geometry/rect.h"
 
-namespace ash {
+namespace chromeos {
 
 ImmersiveFullscreenControllerTestApi::ImmersiveFullscreenControllerTestApi(
     ImmersiveFullscreenController* controller)
@@ -54,4 +54,4 @@
       false;
 }
 
-}  // namespace ash
+}  // namespace chromeos
diff --git a/ash/public/cpp/immersive/immersive_fullscreen_controller_test_api.h b/chromeos/ui/frame/immersive/immersive_fullscreen_controller_test_api.h
similarity index 69%
rename from ash/public/cpp/immersive/immersive_fullscreen_controller_test_api.h
rename to chromeos/ui/frame/immersive/immersive_fullscreen_controller_test_api.h
index d8fb97e..18fa4ba 100644
--- a/ash/public/cpp/immersive/immersive_fullscreen_controller_test_api.h
+++ b/chromeos/ui/frame/immersive/immersive_fullscreen_controller_test_api.h
@@ -2,17 +2,18 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef ASH_PUBLIC_CPP_IMMERSIVE_IMMERSIVE_FULLSCREEN_CONTROLLER_TEST_API_H_
-#define ASH_PUBLIC_CPP_IMMERSIVE_IMMERSIVE_FULLSCREEN_CONTROLLER_TEST_API_H_
+#ifndef CHROMEOS_UI_FRAME_IMMERSIVE_IMMERSIVE_FULLSCREEN_CONTROLLER_TEST_API_H_
+#define CHROMEOS_UI_FRAME_IMMERSIVE_IMMERSIVE_FULLSCREEN_CONTROLLER_TEST_API_H_
 
+#include "base/component_export.h"
 #include "base/macros.h"
 
-namespace ash {
+namespace chromeos {
 
 class ImmersiveFullscreenController;
 
 // Use by tests to access private state of ImmersiveFullscreenController.
-class ImmersiveFullscreenControllerTestApi {
+class COMPONENT_EXPORT(CHROMEOS_UI_FRAME) ImmersiveFullscreenControllerTestApi {
  public:
   explicit ImmersiveFullscreenControllerTestApi(
       ImmersiveFullscreenController* controller);
@@ -20,7 +21,7 @@
 
   // Disables animations for any ImmersiveFullscreenControllers created while
   // GlobalAnimationDisabler exists.
-  class GlobalAnimationDisabler {
+  class COMPONENT_EXPORT(CHROMEOS_UI_FRAME) GlobalAnimationDisabler {
    public:
     GlobalAnimationDisabler();
     ~GlobalAnimationDisabler();
@@ -41,6 +42,6 @@
   DISALLOW_COPY_AND_ASSIGN(ImmersiveFullscreenControllerTestApi);
 };
 
-}  // namespace ash
+}  // namespace chromeos
 
-#endif  // ASH_PUBLIC_CPP_IMMERSIVE_IMMERSIVE_FULLSCREEN_CONTROLLER_TEST_API_H_
+#endif  // CHROMEOS_UI_FRAME_IMMERSIVE_IMMERSIVE_FULLSCREEN_CONTROLLER_TEST_API_H_
diff --git a/ash/public/cpp/immersive/immersive_revealed_lock.cc b/chromeos/ui/frame/immersive/immersive_revealed_lock.cc
similarity index 81%
rename from ash/public/cpp/immersive/immersive_revealed_lock.cc
rename to chromeos/ui/frame/immersive/immersive_revealed_lock.cc
index 25e7aaa..057af36a 100644
--- a/ash/public/cpp/immersive/immersive_revealed_lock.cc
+++ b/chromeos/ui/frame/immersive/immersive_revealed_lock.cc
@@ -2,9 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ash/public/cpp/immersive/immersive_revealed_lock.h"
+#include "chromeos/ui/frame/immersive/immersive_revealed_lock.h"
 
-namespace ash {
+namespace chromeos {
 
 ImmersiveRevealedLock::ImmersiveRevealedLock(
     const base::WeakPtr<Delegate>& delegate,
@@ -18,4 +18,4 @@
     delegate_->UnlockRevealedState();
 }
 
-}  // namespace ash
+}  // namespace chromeos
diff --git a/ash/public/cpp/immersive/immersive_revealed_lock.h b/chromeos/ui/frame/immersive/immersive_revealed_lock.h
similarity index 72%
rename from ash/public/cpp/immersive/immersive_revealed_lock.h
rename to chromeos/ui/frame/immersive/immersive_revealed_lock.h
index 1a5622d..9c008731a 100644
--- a/ash/public/cpp/immersive/immersive_revealed_lock.h
+++ b/chromeos/ui/frame/immersive/immersive_revealed_lock.h
@@ -2,23 +2,23 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef ASH_PUBLIC_CPP_IMMERSIVE_IMMERSIVE_REVEALED_LOCK_H_
-#define ASH_PUBLIC_CPP_IMMERSIVE_IMMERSIVE_REVEALED_LOCK_H_
+#ifndef CHROMEOS_UI_FRAME_IMMERSIVE_IMMERSIVE_REVEALED_LOCK_H_
+#define CHROMEOS_UI_FRAME_IMMERSIVE_IMMERSIVE_REVEALED_LOCK_H_
 
-#include "ash/public/cpp/ash_public_export.h"
+#include "base/component_export.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 
-namespace ash {
+namespace chromeos {
 
 // Class which keeps the top-of-window views revealed for the duration of its
 // lifetime. If acquiring the lock causes a reveal, the top-of-window views
 // will animate according to the |animate_reveal| parameter passed in the
 // constructor. See ImmersiveFullscreenController::GetRevealedLock() for more
 // details.
-class ASH_PUBLIC_EXPORT ImmersiveRevealedLock {
+class COMPONENT_EXPORT(CHROMEOS_UI_FRAME) ImmersiveRevealedLock {
  public:
-  class ASH_PUBLIC_EXPORT Delegate {
+  class COMPONENT_EXPORT(CHROMEOS_UI_FRAME) Delegate {
    public:
     enum AnimateReveal { ANIMATE_REVEAL_YES, ANIMATE_REVEAL_NO };
 
@@ -39,6 +39,6 @@
   DISALLOW_COPY_AND_ASSIGN(ImmersiveRevealedLock);
 };
 
-}  // namespace ash
+}  // namespace chromeos
 
-#endif  // ASH_PUBLIC_CPP_IMMERSIVE_IMMERSIVE_REVEALED_LOCK_H_
+#endif  // CHROMEOS_UI_FRAME_IMMERSIVE_IMMERSIVE_REVEALED_LOCK_H_
diff --git a/components/autofill/core/browser/payments/autofill_wallet_model_type_controller.cc b/components/autofill/core/browser/payments/autofill_wallet_model_type_controller.cc
index 11885fc..b330910 100644
--- a/components/autofill/core/browser/payments/autofill_wallet_model_type_controller.cc
+++ b/components/autofill/core/browser/payments/autofill_wallet_model_type_controller.cc
@@ -14,14 +14,11 @@
 #include "components/autofill/core/common/autofill_prefs.h"
 #include "components/prefs/pref_service.h"
 #include "components/sync/driver/sync_auth_util.h"
+#include "components/sync/driver/sync_driver_switches.h"
 #include "components/sync/driver/sync_service.h"
 #include "components/sync/driver/sync_user_settings.h"
 #include "google_apis/gaia/google_service_auth_error.h"
 
-namespace {
-
-}  // namespace
-
 namespace browser_sync {
 
 AutofillWalletModelTypeController::AutofillWalletModelTypeController(
@@ -111,6 +108,22 @@
                            : PreconditionState::kMustStopAndClearData;
 }
 
+bool AutofillWalletModelTypeController::ShouldRunInTransportOnlyMode() const {
+  if (type() != syncer::AUTOFILL_WALLET_DATA) {
+    return false;
+  }
+  if (!base::FeatureList::IsEnabled(
+          autofill::features::kAutofillEnableAccountWalletStorage)) {
+    return false;
+  }
+  if (sync_service_->GetUserSettings()->IsUsingSecondaryPassphrase() &&
+      !base::FeatureList::IsEnabled(
+          switches::kSyncAllowWalletDataInTransportModeWithCustomPassphrase)) {
+    return false;
+  }
+  return true;
+}
+
 void AutofillWalletModelTypeController::OnUserPrefChanged() {
   DCHECK(CalledOnValidThread());
   sync_service_->DataTypePreconditionChanged(type());
diff --git a/components/autofill/core/browser/payments/autofill_wallet_model_type_controller.h b/components/autofill/core/browser/payments/autofill_wallet_model_type_controller.h
index 03d0312c..8a016e0 100644
--- a/components/autofill/core/browser/payments/autofill_wallet_model_type_controller.h
+++ b/components/autofill/core/browser/payments/autofill_wallet_model_type_controller.h
@@ -47,6 +47,7 @@
   void Stop(syncer::ShutdownReason shutdown_reason,
             StopCallback callback) override;
   PreconditionState GetPreconditionState() const override;
+  bool ShouldRunInTransportOnlyMode() const override;
 
   // syncer::SyncServiceObserver implementation.
   void OnStateChanged(syncer::SyncService* sync) override;
diff --git a/components/autofill_assistant/browser/BUILD.gn b/components/autofill_assistant/browser/BUILD.gn
index cf7a038c..76d0ea5 100644
--- a/components/autofill_assistant/browser/BUILD.gn
+++ b/components/autofill_assistant/browser/BUILD.gn
@@ -166,6 +166,10 @@
     "service/service.h",
     "service/service_impl.cc",
     "service/service_impl.h",
+    "service/service_request_sender.cc",
+    "service/service_request_sender.h",
+    "service/simple_url_loader_factory.cc",
+    "service/simple_url_loader_factory.h",
     "state.h",
     "string_conversions_util.cc",
     "string_conversions_util.h",
@@ -302,6 +306,8 @@
     "event_handler_unittest.cc",
     "field_formatter_unittest.cc",
     "generic_ui_replace_placeholders_unittest.cc",
+    "mock_client_context.cc",
+    "mock_client_context.h",
     "protocol_utils_unittest.cc",
     "radio_button_controller_unittest.cc",
     "retry_timer_unittest.cc",
@@ -312,7 +318,17 @@
     "service/api_key_fetcher_unittest.cc",
     "service/lite_service_unittest.cc",
     "service/lite_service_util_unittest.cc",
+    "service/mock_access_token_fetcher.cc",
+    "service/mock_access_token_fetcher.h",
+    "service/mock_service_request_sender.cc",
+    "service/mock_service_request_sender.h",
+    "service/mock_simple_url_loader_factory.cc",
+    "service/mock_simple_url_loader_factory.h",
+    "service/mock_url_loader.cc",
+    "service/mock_url_loader.h",
     "service/server_url_fetcher_unittest.cc",
+    "service/service_impl_unittest.cc",
+    "service/service_request_sender_unittest.cc",
     "string_conversions_util_unittest.cc",
     "test_util.cc",
     "test_util.h",
@@ -388,6 +404,7 @@
       ":proto",
       "//base",
       "//chrome/android/features/autofill_assistant:test_support_jni_headers",
+      "//net",
       "//url:url",
     ]
   }
diff --git a/components/autofill_assistant/browser/DEPS b/components/autofill_assistant/browser/DEPS
index ff90e40..856d035 100644
--- a/components/autofill_assistant/browser/DEPS
+++ b/components/autofill_assistant/browser/DEPS
@@ -15,6 +15,7 @@
   "+google_apis",
   "+net",
   "+services/network/public/cpp",
+  "+services/network/public/mojom",
   "+third_party/blink/public/mojom/payments/payment_request.mojom.h",
   "+third_party/icu/source/common/unicode",
   "+third_party/libaddressinput/chromium/addressinput_util.h",
diff --git a/components/autofill_assistant/browser/controller.cc b/components/autofill_assistant/browser/controller.cc
index 3a19d11e..b6c41de 100644
--- a/components/autofill_assistant/browser/controller.cc
+++ b/components/autofill_assistant/browser/controller.cc
@@ -31,6 +31,7 @@
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/web_contents.h"
+#include "net/http/http_status_code.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "url/gurl.h"
 
@@ -885,7 +886,7 @@
 }
 
 void Controller::OnGetScripts(const GURL& url,
-                              bool result,
+                              int http_status,
                               const std::string& response) {
   if (state_ == AutofillAssistantState::STOPPED)
     return;
@@ -895,11 +896,13 @@
   if (!HasSameDomainAs(script_url_, url))
     return;
 
-  if (!result) {
+  if (http_status != net::HTTP_OK) {
 #ifdef NDEBUG
-    VLOG(1) << "Failed to get assistant scripts for <redacted>";
+    VLOG(1) << "Failed to get assistant scripts for <redacted>, http-status="
+            << http_status;
 #else
-    VLOG(1) << "Failed to get assistant scripts for " << script_url_.host();
+    VLOG(1) << "Failed to get assistant scripts for " << script_url_.host()
+            << ", http-status=" << http_status;
 #endif
     OnFatalError(l10n_util::GetStringUTF8(IDS_AUTOFILL_ASSISTANT_DEFAULT_ERROR),
                  Metrics::DropOutReason::GET_SCRIPTS_FAILED);
diff --git a/components/autofill_assistant/browser/controller.h b/components/autofill_assistant/browser/controller.h
index 066e0d2..b755b62 100644
--- a/components/autofill_assistant/browser/controller.h
+++ b/components/autofill_assistant/browser/controller.h
@@ -268,7 +268,9 @@
   // once right now and schedule regular checks. Otherwise, do nothing.
   void GetOrCheckScripts();
 
-  void OnGetScripts(const GURL& url, bool result, const std::string& response);
+  void OnGetScripts(const GURL& url,
+                    int http_status,
+                    const std::string& response);
 
   // Execute |script_path| and, if execution succeeds, enter |end_state| and
   // call |on_success|.
diff --git a/components/autofill_assistant/browser/controller_unittest.cc b/components/autofill_assistant/browser/controller_unittest.cc
index 8b8faeb..8f9a485 100644
--- a/components/autofill_assistant/browser/controller_unittest.cc
+++ b/components/autofill_assistant/browser/controller_unittest.cc
@@ -31,6 +31,7 @@
 #include "content/public/test/test_browser_context.h"
 #include "content/public/test/test_renderer_host.h"
 #include "content/public/test/web_contents_tester.h"
+#include "net/http/http_status_code.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "ui/base/l10n/l10n_util.h"
 
@@ -118,14 +119,14 @@
 
     // Fetching scripts succeeds for all URLs, but return nothing.
     ON_CALL(*mock_service_, OnGetScriptsForUrl(_, _, _))
-        .WillByDefault(RunOnceCallback<2>(true, ""));
+        .WillByDefault(RunOnceCallback<2>(net::HTTP_OK, ""));
 
     // Scripts run, but have no actions.
     ON_CALL(*mock_service_, OnGetActions(_, _, _, _, _, _))
-        .WillByDefault(RunOnceCallback<5>(true, ""));
+        .WillByDefault(RunOnceCallback<5>(net::HTTP_OK, ""));
 
     ON_CALL(*mock_service_, OnGetNextActions(_, _, _, _, _, _))
-        .WillByDefault(RunOnceCallback<5>(true, ""));
+        .WillByDefault(RunOnceCallback<5>(net::HTTP_OK, ""));
 
     ON_CALL(*mock_service_, IsLiteService).WillByDefault(Return(false));
 
@@ -163,7 +164,7 @@
     std::string scripts_str;
     scripts.SerializeToString(&scripts_str);
     EXPECT_CALL(*mock_service_, OnGetScriptsForUrl(_, _, _))
-        .WillOnce(RunOnceCallback<2>(true, scripts_str));
+        .WillOnce(RunOnceCallback<2>(net::HTTP_OK, scripts_str));
   }
 
   void SetupActionsForScript(const std::string& path,
@@ -171,7 +172,7 @@
     std::string actions_response_str;
     actions_response.SerializeToString(&actions_response_str);
     EXPECT_CALL(*mock_service_, OnGetActions(StrEq(path), _, _, _, _, _))
-        .WillOnce(RunOnceCallback<5>(true, actions_response_str));
+        .WillOnce(RunOnceCallback<5>(net::HTTP_OK, actions_response_str));
   }
 
   void Start() { Start("http://initialurl.com"); }
@@ -209,7 +210,7 @@
     response.SerializeToString(&response_str);
 
     EXPECT_CALL(*mock_service_, OnGetScriptsForUrl(_, _, _))
-        .WillOnce(RunOnceCallback<2>(true, response_str));
+        .WillOnce(RunOnceCallback<2>(net::HTTP_OK, response_str));
   }
 
   // Sets up all calls to the service for scripts to return |response|.
@@ -218,7 +219,7 @@
     response.SerializeToString(&response_str);
 
     EXPECT_CALL(*mock_service_, OnGetScriptsForUrl(_, _, _))
-        .WillRepeatedly(RunOnceCallback<2>(true, response_str));
+        .WillRepeatedly(RunOnceCallback<2>(net::HTTP_OK, response_str));
   }
 
   UserData* GetUserData() { return controller_->user_data_.get(); }
@@ -325,7 +326,7 @@
 
   // Choose script2 and run it successfully.
   EXPECT_CALL(*mock_service_, OnGetActions(StrEq("script2"), _, _, _, _, _))
-      .WillOnce(RunOnceCallback<5>(true, ""));
+      .WillOnce(RunOnceCallback<5>(net::HTTP_OK, ""));
   EXPECT_TRUE(controller_->PerformUserAction(1));
 
   // Offering the remaining choice: script1 as script2 can only run once.
@@ -548,7 +549,7 @@
   std::string actions_response_str;
   actions_response.SerializeToString(&actions_response_str);
   EXPECT_CALL(*mock_service_, OnGetActions(StrEq("stop"), _, _, _, _, _))
-      .WillOnce(RunOnceCallback<5>(true, actions_response_str));
+      .WillOnce(RunOnceCallback<5>(net::HTTP_OK, actions_response_str));
 
   Start();
   ASSERT_THAT(controller_->GetUserActions(), SizeIs(1));
@@ -568,7 +569,7 @@
   std::string actions_response_str;
   actions_response.SerializeToString(&actions_response_str);
   EXPECT_CALL(*mock_service_, OnGetActions(StrEq("stop"), _, _, _, _, _))
-      .WillOnce(RunOnceCallback<5>(true, actions_response_str));
+      .WillOnce(RunOnceCallback<5>(net::HTTP_OK, actions_response_str));
 
   Start();
   ASSERT_THAT(controller_->GetUserActions(), SizeIs(1));
@@ -588,10 +589,10 @@
 
   EXPECT_CALL(*mock_service_,
               OnGetScriptsForUrl(Eq(GURL("http://a.example.com/path1")), _, _))
-      .WillOnce(RunOnceCallback<2>(true, scripts_str));
+      .WillOnce(RunOnceCallback<2>(net::HTTP_OK, scripts_str));
   EXPECT_CALL(*mock_service_,
               OnGetScriptsForUrl(Eq(GURL("http://b.example.com/path1")), _, _))
-      .WillOnce(RunOnceCallback<2>(true, scripts_str));
+      .WillOnce(RunOnceCallback<2>(net::HTTP_OK, scripts_str));
 
   Start("http://a.example.com/path1");
   SimulateNavigateToUrl(GURL("http://a.example.com/path2"));
@@ -608,7 +609,7 @@
   SetNextScriptResponse(script_response);
 
   EXPECT_CALL(*mock_service_, OnGetActions(StrEq("autostart"), _, _, _, _, _))
-      .WillOnce(RunOnceCallback<5>(true, ""));
+      .WillOnce(RunOnceCallback<5>(net::HTTP_OK, ""));
 
   EXPECT_CALL(mock_client_, AttachUI());
   Start("http://a.example.com/path");
@@ -651,7 +652,7 @@
 TEST_F(ControllerTest, InitialUrlLoads) {
   GURL initialUrl("http://a.example.com/path");
   EXPECT_CALL(*mock_service_, OnGetScriptsForUrl(Eq(initialUrl), _, _))
-      .WillOnce(RunOnceCallback<2>(true, ""));
+      .WillOnce(RunOnceCallback<2>(net::HTTP_OK, ""));
 
   controller_->Start(initialUrl, TriggerContext::CreateEmpty());
 }
@@ -904,7 +905,7 @@
   on_timeout_error.SerializeToString(&on_timeout_error_str);
   EXPECT_CALL(*mock_service_,
               OnGetActions(StrEq("on_timeout_error"), _, _, _, _, _))
-      .WillOnce(RunOnceCallback<5>(true, on_timeout_error_str));
+      .WillOnce(RunOnceCallback<5>(net::HTTP_OK, on_timeout_error_str));
 
   Start("http://a.example.com/path");
   for (int i = 0; i < 30; i++) {
@@ -936,7 +937,7 @@
   on_timeout_error.SerializeToString(&on_timeout_error_str);
   EXPECT_CALL(*mock_service_,
               OnGetActions(StrEq("on_timeout_error"), _, _, _, _, _))
-      .WillOnce(RunOnceCallback<5>(true, on_timeout_error_str));
+      .WillOnce(RunOnceCallback<5>(net::HTTP_OK, on_timeout_error_str));
 
   Start("http://a.example.com/path");
 
@@ -1100,7 +1101,7 @@
   std::vector<ProcessedActionProto> processed_actions_capture;
   EXPECT_CALL(*mock_service_, OnGetNextActions(_, _, _, _, _, _))
       .WillOnce(DoAll(SaveArg<3>(&processed_actions_capture),
-                      RunOnceCallback<5>(true, "")));
+                      RunOnceCallback<5>(net::HTTP_OK, "")));
 
   Start("http://a.example.com/path");
   EXPECT_THAT(controller_->GetUserActions(), SizeIs(1));
@@ -1133,7 +1134,7 @@
   std::vector<ProcessedActionProto> processed_actions_capture;
   EXPECT_CALL(*mock_service_, OnGetNextActions(_, _, _, _, _, _))
       .WillOnce(DoAll(SaveArg<3>(&processed_actions_capture),
-                      RunOnceCallback<5>(true, "")));
+                      RunOnceCallback<5>(net::HTTP_OK, "")));
 
   Start("http://a.example.com/path");
   EXPECT_THAT(controller_->GetUserActions(), SizeIs(1));
@@ -1179,11 +1180,11 @@
   script_response.SerializeToString(&response_str);
   EXPECT_CALL(*mock_service_,
               OnGetScriptsForUrl(GURL("http://example.com/"), _, _))
-      .WillOnce(RunOnceCallback<2>(true, response_str));
+      .WillOnce(RunOnceCallback<2>(net::HTTP_OK, response_str));
 
   EXPECT_CALL(*mock_service_,
               OnGetScriptsForUrl(GURL("http://b.example.com/"), _, _))
-      .WillOnce(RunOnceCallback<2>(true, ""));
+      .WillOnce(RunOnceCallback<2>(net::HTTP_OK, ""));
 
   // Start tracking at example.com, with one script matching
   SetLastCommittedUrl(GURL("http://example.com/"));
@@ -1287,7 +1288,7 @@
   // Running the script fails, due to a backend issue. The error message should
   // be shown.
   EXPECT_CALL(*mock_service_, OnGetActions(_, _, _, _, _, _))
-      .WillOnce(RunOnceCallback<5>(false, ""));
+      .WillOnce(RunOnceCallback<5>(net::HTTP_UNAUTHORIZED, ""));
 
   // Start tracking at example.com, with one script matching
   SetLastCommittedUrl(GURL("http://example.com/"));
@@ -1317,7 +1318,7 @@
   script_response.SerializeToString(&response_str);
   EXPECT_CALL(*mock_service_,
               OnGetScriptsForUrl(GURL("http://example.com/"), _, _))
-      .WillOnce(RunOnceCallback<2>(true, response_str));
+      .WillOnce(RunOnceCallback<2>(net::HTTP_OK, response_str));
 
   // Start tracking at example.com, with one script matching
   SetLastCommittedUrl(GURL("http://example.com/"));
@@ -1327,7 +1328,7 @@
   ASSERT_THAT(controller_->GetUserActions(), SizeIs(1));
 
   EXPECT_CALL(*mock_service_, OnGetActions(StrEq("runnable"), _, _, _, _, _))
-      .WillOnce(RunOnceCallback<5>(false, ""));
+      .WillOnce(RunOnceCallback<5>(net::HTTP_UNAUTHORIZED, ""));
 
   // When the script fails, the controller transitions to STOPPED state, then
   // right away back to TRACKING state.
@@ -1373,7 +1374,7 @@
   AddRunnableScript(&script_response, "runnable");
   std::string response_str;
   script_response.SerializeToString(&response_str);
-  std::move(get_scripts_callback).Run(true, response_str);
+  std::move(get_scripts_callback).Run(net::HTTP_OK, response_str);
 
   EXPECT_TRUE(first_check_done);
   EXPECT_TRUE(controller_->HasRunFirstCheck());
@@ -1451,7 +1452,7 @@
   EXPECT_THAT(controller_->GetUserActions(), SizeIs(1));
 
   EXPECT_CALL(*mock_service_, OnGetActions(StrEq("autostart"), _, _, _, _, _))
-      .WillOnce(RunOnceCallback<5>(true, ""));
+      .WillOnce(RunOnceCallback<5>(net::HTTP_OK, ""));
 
   ActionsResponseProto runnable_script;
   runnable_script.add_actions()->mutable_tell()->set_message("runnable");
@@ -1491,7 +1492,7 @@
   script_response.SerializeToString(&response_str);
   EXPECT_CALL(*mock_service_,
               OnGetScriptsForUrl(GURL("http://example.com/"), _, _))
-      .WillOnce(RunOnceCallback<2>(true, response_str));
+      .WillOnce(RunOnceCallback<2>(net::HTTP_OK, response_str));
   EXPECT_CALL(*mock_service_,
               OnGetScriptsForUrl(GURL("http://b.example.com/"), _, _))
       .Times(0);
@@ -1536,7 +1537,7 @@
   script_response.SerializeToString(&response_str);
   EXPECT_CALL(*mock_service_,
               OnGetScriptsForUrl(GURL("http://a.example.com/"), _, _))
-      .WillOnce(RunOnceCallback<2>(true, response_str));
+      .WillOnce(RunOnceCallback<2>(net::HTTP_OK, response_str));
 
   Start("http://a.example.com/");
   EXPECT_EQ(AutofillAssistantState::BROWSE, controller_->GetState());
@@ -1579,7 +1580,7 @@
   script_response.SerializeToString(&response_str);
   EXPECT_CALL(*mock_service_,
               OnGetScriptsForUrl(GURL("http://a.example.com/"), _, _))
-      .WillOnce(RunOnceCallback<2>(true, response_str));
+      .WillOnce(RunOnceCallback<2>(net::HTTP_OK, response_str));
 
   Start("http://a.example.com/");
   EXPECT_EQ(AutofillAssistantState::BROWSE, controller_->GetState());
@@ -1614,7 +1615,7 @@
   script_response.SerializeToString(&response_str);
   EXPECT_CALL(*mock_service_,
               OnGetScriptsForUrl(GURL("http://example.com/"), _, _))
-      .WillOnce(RunOnceCallback<2>(true, response_str));
+      .WillOnce(RunOnceCallback<2>(net::HTTP_OK, response_str));
 
   Start("http://example.com/");
   EXPECT_EQ(AutofillAssistantState::PROMPT, controller_->GetState());
@@ -1934,7 +1935,7 @@
   script_response.SerializeToString(&response_str);
   EXPECT_CALL(*mock_service_,
               OnGetScriptsForUrl(GURL("http://a.example.com/"), _, _))
-      .WillOnce(RunOnceCallback<2>(true, response_str));
+      .WillOnce(RunOnceCallback<2>(net::HTTP_OK, response_str));
 
   Start("http://a.example.com/");
   EXPECT_EQ(AutofillAssistantState::BROWSE, controller_->GetState());
@@ -2624,7 +2625,7 @@
 TEST_F(ControllerTest, StartPasswordChangeFlow) {
   GURL initialUrl("http://example.com/password");
   EXPECT_CALL(*mock_service_, OnGetScriptsForUrl(Eq(initialUrl), _, _))
-      .WillOnce(RunOnceCallback<2>(true, ""));
+      .WillOnce(RunOnceCallback<2>(net::HTTP_OK, ""));
   std::map<std::string, std::string> parameters;
   std::string username = "test_username";
   parameters["PASSWORD_CHANGE_USERNAME"] = username;
@@ -2658,7 +2659,7 @@
   std::vector<ProcessedActionProto> processed_actions_capture;
   EXPECT_CALL(*mock_service_, OnGetNextActions(_, _, _, _, _, _))
       .WillOnce(DoAll(SaveArg<3>(&processed_actions_capture),
-                      RunOnceCallback<5>(true, "")));
+                      RunOnceCallback<5>(net::HTTP_OK, "")));
 
   Start("http://a.example.com/path");
 
diff --git a/components/autofill_assistant/browser/mock_client_context.cc b/components/autofill_assistant/browser/mock_client_context.cc
new file mode 100644
index 0000000..1c989ab0
--- /dev/null
+++ b/components/autofill_assistant/browser/mock_client_context.cc
@@ -0,0 +1,12 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill_assistant/browser/mock_client_context.h"
+
+namespace autofill_assistant {
+
+MockClientContext::MockClientContext() = default;
+MockClientContext::~MockClientContext() = default;
+
+}  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/mock_client_context.h b/components/autofill_assistant/browser/mock_client_context.h
new file mode 100644
index 0000000..cc9a593c
--- /dev/null
+++ b/components/autofill_assistant/browser/mock_client_context.h
@@ -0,0 +1,27 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_MOCK_CLIENT_CONTEXT_H_
+#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_MOCK_CLIENT_CONTEXT_H_
+
+#include "components/autofill_assistant/browser/client_context.h"
+
+#include "components/autofill_assistant/browser/service.pb.h"
+#include "components/autofill_assistant/browser/trigger_context.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace autofill_assistant {
+
+class MockClientContext : public ClientContext {
+ public:
+  MockClientContext();
+  ~MockClientContext() override;
+
+  MOCK_METHOD1(Update, void(const TriggerContext& trigger_context));
+  MOCK_CONST_METHOD0(AsProto, ClientContextProto());
+};
+
+}  // namespace autofill_assistant
+
+#endif  // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_MOCK_CLIENT_CONTEXT_H_
diff --git a/components/autofill_assistant/browser/script_executor.cc b/components/autofill_assistant/browser/script_executor.cc
index de7f320..628bc78ae 100644
--- a/components/autofill_assistant/browser/script_executor.cc
+++ b/components/autofill_assistant/browser/script_executor.cc
@@ -27,6 +27,7 @@
 #include "components/autofill_assistant/browser/web/element_finder.h"
 #include "components/autofill_assistant/browser/web/web_controller.h"
 #include "components/strings/grit/components_strings.h"
+#include "net/http/http_status_code.h"
 #include "ui/base/l10n/l10n_util.h"
 
 namespace autofill_assistant {
@@ -809,13 +810,14 @@
 }
 
 void ScriptExecutor::OnGetActions(base::TimeTicks start_time,
-                                  bool result,
+                                  int http_status,
                                   const std::string& response) {
+  VLOG(2) << __func__ << " http-status=" << http_status;
   batch_start_time_ = base::TimeTicks::Now();
   roundtrip_timing_stats_.set_roundtrip_time_ms(
       (batch_start_time_ - start_time).InMilliseconds());
-  bool success = result && ProcessNextActionResponse(response);
-  VLOG(2) << __func__ << " result=" << result;
+  bool success =
+      http_status == net::HTTP_OK && ProcessNextActionResponse(response);
   if (should_stop_script_) {
     // The last action forced the script to stop. Sending the result of the
     // action is considered best effort in this situation. Report a successful
diff --git a/components/autofill_assistant/browser/script_executor.h b/components/autofill_assistant/browser/script_executor.h
index 5ea7b55..54d1d91 100644
--- a/components/autofill_assistant/browser/script_executor.h
+++ b/components/autofill_assistant/browser/script_executor.h
@@ -380,7 +380,7 @@
   };
 
   void OnGetActions(base::TimeTicks start_time,
-                    bool result,
+                    int http_status,
                     const std::string& response);
   bool ProcessNextActionResponse(const std::string& response);
   void ReportPayloadsToListener();
diff --git a/components/autofill_assistant/browser/script_executor_unittest.cc b/components/autofill_assistant/browser/script_executor_unittest.cc
index aa45c404..306a74d 100644
--- a/components/autofill_assistant/browser/script_executor_unittest.cc
+++ b/components/autofill_assistant/browser/script_executor_unittest.cc
@@ -15,6 +15,7 @@
 #include "components/autofill_assistant/browser/service/mock_service.h"
 #include "components/autofill_assistant/browser/service/service.h"
 #include "components/autofill_assistant/browser/web/mock_web_controller.h"
+#include "net/http/http_status_code.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
 namespace autofill_assistant {
@@ -110,7 +111,8 @@
     wait_action->set_allow_interrupt(true);
     interruptible.add_actions()->mutable_tell()->set_message(path);
     EXPECT_CALL(mock_service_, OnGetActions(StrEq(path), _, _, _, _, _))
-        .WillRepeatedly(RunOnceCallback<5>(true, Serialize(interruptible)));
+        .WillRepeatedly(
+            RunOnceCallback<5>(net::HTTP_OK, Serialize(interruptible)));
   }
 
   // Creates an interrupt that contains a tell. It will always succeed.
@@ -120,7 +122,8 @@
     ActionsResponseProto interrupt_actions;
     InitInterruptActions(&interrupt_actions, path);
     EXPECT_CALL(mock_service_, OnGetActions(StrEq(path), _, _, _, _, _))
-        .WillRepeatedly(RunOnceCallback<5>(true, Serialize(interrupt_actions)));
+        .WillRepeatedly(
+            RunOnceCallback<5>(net::HTTP_OK, Serialize(interrupt_actions)));
   }
 
   void InitInterruptActions(ActionsResponseProto* interrupt_actions,
@@ -168,7 +171,7 @@
 
 TEST_F(ScriptExecutorTest, GetActionsFails) {
   EXPECT_CALL(mock_service_, OnGetActions(_, _, _, _, _, _))
-      .WillOnce(RunOnceCallback<5>(false, ""));
+      .WillOnce(RunOnceCallback<5>(net::HTTP_UNAUTHORIZED, ""));
   EXPECT_CALL(executor_callback_,
               Run(AllOf(Field(&ScriptExecutor::Result::success, false),
                         Field(&ScriptExecutor::Result::at_end,
@@ -196,7 +199,7 @@
         EXPECT_THAT("value",
                     trigger_context.GetParameter("param").value_or(""));
 
-        std::move(callback).Run(true, "");
+        std::move(callback).Run(net::HTTP_OK, "");
       }));
 
   EXPECT_CALL(executor_callback_,
@@ -210,12 +213,12 @@
       ToSelectorProto("will fail");
 
   EXPECT_CALL(mock_service_, OnGetActions(_, _, _, _, _, _))
-      .WillOnce(RunOnceCallback<5>(true, Serialize(actions_response)));
+      .WillOnce(RunOnceCallback<5>(net::HTTP_OK, Serialize(actions_response)));
 
   std::vector<ProcessedActionProto> processed_actions_capture;
   EXPECT_CALL(mock_service_, OnGetNextActions(_, _, _, _, _, _))
       .WillOnce(DoAll(SaveArg<3>(&processed_actions_capture),
-                      RunOnceCallback<5>(true, "")));
+                      RunOnceCallback<5>(net::HTTP_OK, "")));
   EXPECT_CALL(executor_callback_,
               Run(AllOf(Field(&ScriptExecutor::Result::success, true),
                         Field(&ScriptExecutor::Result::at_end,
@@ -233,18 +236,19 @@
   initial_actions_response.add_actions()->mutable_tell()->set_message("1");
   initial_actions_response.add_actions()->mutable_tell()->set_message("2");
   EXPECT_CALL(mock_service_, OnGetActions(StrEq(kScriptPath), _, _, _, _, _))
-      .WillOnce(RunOnceCallback<5>(true, Serialize(initial_actions_response)));
+      .WillOnce(RunOnceCallback<5>(net::HTTP_OK,
+                                   Serialize(initial_actions_response)));
 
   ActionsResponseProto next_actions_response;
   next_actions_response.add_actions()->mutable_tell()->set_message("3");
   std::vector<ProcessedActionProto> processed_actions1_capture;
   std::vector<ProcessedActionProto> processed_actions2_capture;
   EXPECT_CALL(mock_service_, OnGetNextActions(_, _, _, _, _, _))
-      .WillOnce(
-          DoAll(SaveArg<3>(&processed_actions1_capture),
-                RunOnceCallback<5>(true, Serialize(next_actions_response))))
+      .WillOnce(DoAll(
+          SaveArg<3>(&processed_actions1_capture),
+          RunOnceCallback<5>(net::HTTP_OK, Serialize(next_actions_response))))
       .WillOnce(DoAll(SaveArg<3>(&processed_actions2_capture),
-                      RunOnceCallback<5>(true, "")));
+                      RunOnceCallback<5>(net::HTTP_OK, "")));
   EXPECT_CALL(executor_callback_,
               Run(Field(&ScriptExecutor::Result::success, true)));
   executor_->Run(&user_data_, executor_callback_.Get());
@@ -258,12 +262,12 @@
   actions_response.add_actions();  // action definition missing
 
   EXPECT_CALL(mock_service_, OnGetActions(_, _, _, _, _, _))
-      .WillOnce(RunOnceCallback<5>(true, Serialize(actions_response)));
+      .WillOnce(RunOnceCallback<5>(net::HTTP_OK, Serialize(actions_response)));
 
   std::vector<ProcessedActionProto> processed_actions_capture;
   EXPECT_CALL(mock_service_, OnGetNextActions(_, _, _, _, _, _))
       .WillOnce(DoAll(SaveArg<3>(&processed_actions_capture),
-                      RunOnceCallback<5>(true, "")));
+                      RunOnceCallback<5>(net::HTTP_OK, "")));
   EXPECT_CALL(executor_callback_,
               Run(Field(&ScriptExecutor::Result::success, true)));
   executor_->Run(&user_data_, executor_callback_.Get());
@@ -277,10 +281,10 @@
   actions_response.add_actions()->mutable_stop();
 
   EXPECT_CALL(mock_service_, OnGetActions(_, _, _, _, _, _))
-      .WillOnce(RunOnceCallback<5>(true, Serialize(actions_response)));
+      .WillOnce(RunOnceCallback<5>(net::HTTP_OK, Serialize(actions_response)));
 
   EXPECT_CALL(mock_service_, OnGetNextActions(_, _, _, _, _, _))
-      .WillOnce(RunOnceCallback<5>(true, ""));
+      .WillOnce(RunOnceCallback<5>(net::HTTP_OK, ""));
   EXPECT_CALL(executor_callback_,
               Run(AllOf(Field(&ScriptExecutor::Result::success, true),
                         Field(&ScriptExecutor::Result::at_end,
@@ -299,7 +303,8 @@
       "never run");
 
   EXPECT_CALL(mock_service_, OnGetActions(_, _, _, _, _, _))
-      .WillOnce(RunOnceCallback<5>(true, Serialize(initial_actions_response)));
+      .WillOnce(RunOnceCallback<5>(net::HTTP_OK,
+                                   Serialize(initial_actions_response)));
 
   ActionsResponseProto next_actions_response;
   next_actions_response.add_actions()->mutable_tell()->set_message(
@@ -307,11 +312,11 @@
   std::vector<ProcessedActionProto> processed_actions1_capture;
   std::vector<ProcessedActionProto> processed_actions2_capture;
   EXPECT_CALL(mock_service_, OnGetNextActions(_, _, _, _, _, _))
-      .WillOnce(
-          DoAll(SaveArg<3>(&processed_actions1_capture),
-                RunOnceCallback<5>(true, Serialize(next_actions_response))))
+      .WillOnce(DoAll(
+          SaveArg<3>(&processed_actions1_capture),
+          RunOnceCallback<5>(net::HTTP_OK, Serialize(next_actions_response))))
       .WillOnce(DoAll(SaveArg<3>(&processed_actions2_capture),
-                      RunOnceCallback<5>(true, "")));
+                      RunOnceCallback<5>(net::HTTP_OK, "")));
   EXPECT_CALL(executor_callback_,
               Run(Field(&ScriptExecutor::Result::success, true)));
   executor_->Run(&user_data_, executor_callback_.Get());
@@ -334,12 +339,12 @@
   action->set_action_delay_ms(1000);
 
   EXPECT_CALL(mock_service_, OnGetActions(_, _, _, _, _, _))
-      .WillOnce(RunOnceCallback<5>(true, Serialize(actions_response)));
+      .WillOnce(RunOnceCallback<5>(net::HTTP_OK, Serialize(actions_response)));
 
   std::vector<ProcessedActionProto> processed_actions_capture;
   EXPECT_CALL(mock_service_, OnGetNextActions(_, _, _, _, _, _))
       .WillOnce(DoAll(SaveArg<3>(&processed_actions_capture),
-                      RunOnceCallback<5>(true, "")));
+                      RunOnceCallback<5>(net::HTTP_OK, "")));
 
   // executor_callback_.Run() not expected to be run just yet, as the action is
   // delayed.
@@ -362,9 +367,9 @@
   *actions_response.add_actions() = click_with_clean_contextual_ui;
 
   EXPECT_CALL(mock_service_, OnGetActions(_, _, _, _, _, _))
-      .WillOnce(RunOnceCallback<5>(true, Serialize(actions_response)));
+      .WillOnce(RunOnceCallback<5>(net::HTTP_OK, Serialize(actions_response)));
   EXPECT_CALL(mock_service_, OnGetNextActions(_, _, _, _, _, _))
-      .WillOnce(RunOnceCallback<5>(true, ""));
+      .WillOnce(RunOnceCallback<5>(net::HTTP_OK, ""));
   EXPECT_CALL(executor_callback_,
               Run(Field(&ScriptExecutor::Result::success, true)));
 
@@ -382,9 +387,9 @@
   actions_response.add_actions()->mutable_tell()->set_message("Wait no!");
 
   EXPECT_CALL(mock_service_, OnGetActions(_, _, _, _, _, _))
-      .WillOnce(RunOnceCallback<5>(true, Serialize(actions_response)));
+      .WillOnce(RunOnceCallback<5>(net::HTTP_OK, Serialize(actions_response)));
   EXPECT_CALL(mock_service_, OnGetNextActions(_, _, _, _, _, _))
-      .WillOnce(RunOnceCallback<5>(true, ""));
+      .WillOnce(RunOnceCallback<5>(net::HTTP_OK, ""));
   EXPECT_CALL(executor_callback_,
               Run(Field(&ScriptExecutor::Result::success, true)));
 
@@ -397,9 +402,9 @@
   ActionsResponseProto actions_response;
   actions_response.add_actions()->mutable_tell()->set_message("Hello");
   EXPECT_CALL(mock_service_, OnGetActions(_, _, _, _, _, _))
-      .WillOnce(RunOnceCallback<5>(true, Serialize(actions_response)));
+      .WillOnce(RunOnceCallback<5>(net::HTTP_OK, Serialize(actions_response)));
   EXPECT_CALL(mock_service_, OnGetNextActions(_, _, _, _, _, _))
-      .WillOnce(RunOnceCallback<5>(false, ""));
+      .WillOnce(RunOnceCallback<5>(net::HTTP_UNAUTHORIZED, ""));
   EXPECT_CALL(executor_callback_,
               Run(Field(&ScriptExecutor::Result::success, false)));
   delegate_.SetDetails(std::make_unique<Details>());  // empty, but not null
@@ -420,7 +425,7 @@
 
 TEST_F(ScriptExecutorTest, UpdateScriptStateOnError) {
   EXPECT_CALL(mock_service_, OnGetActions(_, _, _, _, _, _))
-      .WillOnce(RunOnceCallback<5>(false, ""));
+      .WillOnce(RunOnceCallback<5>(net::HTTP_UNAUTHORIZED, ""));
   EXPECT_CALL(executor_callback_,
               Run(Field(&ScriptExecutor::Result::success, false)));
   executor_->Run(&user_data_, executor_callback_.Get());
@@ -433,9 +438,10 @@
   ActionsResponseProto initial_actions_response;
   initial_actions_response.add_actions()->mutable_tell()->set_message("ok");
   EXPECT_CALL(mock_service_, OnGetActions(StrEq(kScriptPath), _, _, _, _, _))
-      .WillOnce(RunOnceCallback<5>(true, Serialize(initial_actions_response)));
+      .WillOnce(RunOnceCallback<5>(net::HTTP_OK,
+                                   Serialize(initial_actions_response)));
   EXPECT_CALL(mock_service_, OnGetNextActions(_, _, _, _, _, _))
-      .WillOnce(RunOnceCallback<5>(true, ""));
+      .WillOnce(RunOnceCallback<5>(net::HTTP_OK, ""));
   EXPECT_CALL(executor_callback_,
               Run(Field(&ScriptExecutor::Result::success, true)));
   executor_->Run(&user_data_, executor_callback_.Get());
@@ -452,14 +458,15 @@
 
   EXPECT_CALL(mock_service_, OnGetActions(_, _, _, "initial global payload",
                                           "initial payload", _))
-      .WillOnce(RunOnceCallback<5>(true, Serialize(actions_response)));
+      .WillOnce(RunOnceCallback<5>(net::HTTP_OK, Serialize(actions_response)));
 
   ActionsResponseProto next_actions_response;
   next_actions_response.set_global_payload("last global payload");
   next_actions_response.set_script_payload("last payload");
   EXPECT_CALL(mock_service_, OnGetNextActions(_, "actions global payload",
                                               "actions payload", _, _, _))
-      .WillOnce(RunOnceCallback<5>(true, Serialize(next_actions_response)));
+      .WillOnce(
+          RunOnceCallback<5>(net::HTTP_OK, Serialize(next_actions_response)));
 
   EXPECT_CALL(executor_callback_, Run(_));
   executor_->Run(&user_data_, executor_callback_.Get());
@@ -476,11 +483,11 @@
 
   EXPECT_CALL(mock_service_, OnGetActions(_, _, _, "initial global payload",
                                           "initial payload", _))
-      .WillOnce(RunOnceCallback<5>(true, Serialize(actions_response)));
+      .WillOnce(RunOnceCallback<5>(net::HTTP_OK, Serialize(actions_response)));
 
   EXPECT_CALL(mock_service_, OnGetNextActions(_, "actions global payload",
                                               "actions payload", _, _, _))
-      .WillOnce(RunOnceCallback<5>(false, ""));
+      .WillOnce(RunOnceCallback<5>(net::HTTP_UNAUTHORIZED, ""));
 
   EXPECT_CALL(executor_callback_, Run(_));
   executor_->Run(&user_data_, executor_callback_.Get());
@@ -496,11 +503,11 @@
       ToSelectorProto("element");
 
   EXPECT_CALL(mock_service_, OnGetActions(_, _, _, _, _, _))
-      .WillOnce(RunOnceCallback<5>(true, Serialize(actions_response)));
+      .WillOnce(RunOnceCallback<5>(net::HTTP_OK, Serialize(actions_response)));
   std::vector<ProcessedActionProto> processed_actions_capture;
   EXPECT_CALL(mock_service_, OnGetNextActions(_, _, _, _, _, _))
       .WillOnce(DoAll(SaveArg<3>(&processed_actions_capture),
-                      RunOnceCallback<5>(true, "")));
+                      RunOnceCallback<5>(net::HTTP_OK, "")));
 
   // First check does not find the element, wait for dom waits 1s, then the
   // element is found, and the action succeeds.
@@ -530,9 +537,9 @@
   std::vector<ProcessedActionProto> processed_actions2_capture;
   EXPECT_CALL(mock_service_, OnGetNextActions(_, _, _, _, _, _))
       .WillOnce(DoAll(SaveArg<3>(&processed_actions1_capture),
-                      RunOnceCallback<5>(true, "")))
+                      RunOnceCallback<5>(net::HTTP_OK, "")))
       .WillOnce(DoAll(SaveArg<3>(&processed_actions2_capture),
-                      RunOnceCallback<5>(true, "")));
+                      RunOnceCallback<5>(net::HTTP_OK, "")));
 
   EXPECT_CALL(executor_callback_,
               Run(Field(&ScriptExecutor::Result::success, true)));
@@ -569,13 +576,13 @@
     testing::InSequence seq;
     EXPECT_CALL(mock_service_,
                 OnGetNextActions(_, _, "payload for interrupt1", _, _, _))
-        .WillOnce(RunOnceCallback<5>(true, ""));
+        .WillOnce(RunOnceCallback<5>(net::HTTP_OK, ""));
     EXPECT_CALL(mock_service_,
                 OnGetNextActions(_, _, "payload for interrupt2", _, _, _))
-        .WillOnce(RunOnceCallback<5>(true, ""));
+        .WillOnce(RunOnceCallback<5>(net::HTTP_OK, ""));
     EXPECT_CALL(mock_service_,
                 OnGetNextActions(_, _, "main script payload", _, _, _))
-        .WillOnce(RunOnceCallback<5>(true, ""));
+        .WillOnce(RunOnceCallback<5>(net::HTTP_OK, ""));
   }
 
   EXPECT_CALL(executor_callback_,
@@ -600,7 +607,8 @@
     wait_action->set_allow_interrupt(true);
   }
   EXPECT_CALL(mock_service_, OnGetActions(StrEq("script_path"), _, _, _, _, _))
-      .WillRepeatedly(RunOnceCallback<5>(true, Serialize(interruptible)));
+      .WillRepeatedly(
+          RunOnceCallback<5>(net::HTTP_OK, Serialize(interruptible)));
 
   // 'interrupt' with matching preconditions runs exactly three times.
   RegisterInterrupt("interrupt", "interrupt_trigger");
@@ -608,11 +616,12 @@
   InitInterruptActions(&interrupt_actions, "interrupt");
   EXPECT_CALL(mock_service_, OnGetActions(StrEq("interrupt"), _, _, _, _, _))
       .Times(3)
-      .WillRepeatedly(RunOnceCallback<5>(true, Serialize(interrupt_actions)));
+      .WillRepeatedly(
+          RunOnceCallback<5>(net::HTTP_OK, Serialize(interrupt_actions)));
 
   // All scripts succeed with no more actions.
   EXPECT_CALL(mock_service_, OnGetNextActions(_, _, _, _, _, _))
-      .WillRepeatedly(RunOnceCallback<5>(true, ""));
+      .WillRepeatedly(RunOnceCallback<5>(net::HTTP_OK, ""));
 
   EXPECT_CALL(executor_callback_,
               Run(Field(&ScriptExecutor::Result::success, true)));
@@ -630,8 +639,8 @@
       "last payload from interrupt");
   EXPECT_CALL(mock_service_, OnGetNextActions(_, "global payload for interrupt",
                                               "payload for interrupt", _, _, _))
-      .WillOnce(
-          RunOnceCallback<5>(true, Serialize(next_interrupt_actions_response)));
+      .WillOnce(RunOnceCallback<5>(net::HTTP_OK,
+                                   Serialize(next_interrupt_actions_response)));
 
   ActionsResponseProto next_main_actions_response;
   next_main_actions_response.set_global_payload(
@@ -640,8 +649,8 @@
   EXPECT_CALL(mock_service_,
               OnGetNextActions(_, "last global payload from interrupt",
                                "main script payload", _, _, _))
-      .WillOnce(
-          RunOnceCallback<5>(true, Serialize(next_main_actions_response)));
+      .WillOnce(RunOnceCallback<5>(net::HTTP_OK,
+                                   Serialize(next_main_actions_response)));
 
   EXPECT_CALL(executor_callback_,
               Run(Field(&ScriptExecutor::Result::success, true)));
@@ -657,11 +666,11 @@
 
   EXPECT_CALL(mock_service_, OnGetNextActions(_, "global payload for interrupt",
                                               "payload for interrupt", _, _, _))
-      .WillOnce(RunOnceCallback<5>(false, ""));
+      .WillOnce(RunOnceCallback<5>(net::HTTP_UNAUTHORIZED, ""));
 
   EXPECT_CALL(mock_service_, OnGetNextActions(_, "global payload for interrupt",
                                               "main script payload", _, _, _))
-      .WillOnce(RunOnceCallback<5>(false, ""));
+      .WillOnce(RunOnceCallback<5>(net::HTTP_UNAUTHORIZED, ""));
 
   EXPECT_CALL(executor_callback_, Run(_));
   executor_->Run(&user_data_, executor_callback_.Get());
@@ -684,7 +693,7 @@
       .WillRepeatedly(RunOnceCallback<1>(ClientStatus()));
 
   EXPECT_CALL(mock_service_, OnGetNextActions(_, _, _, _, _, _))
-      .WillRepeatedly(RunOnceCallback<5>(true, ""));
+      .WillRepeatedly(RunOnceCallback<5>(net::HTTP_OK, ""));
 
   EXPECT_CALL(executor_callback_,
               Run(Field(&ScriptExecutor::Result::success, true)));
@@ -703,14 +712,14 @@
       ToSelectorProto("element");
   // allow_interrupt is not set
   EXPECT_CALL(mock_service_, OnGetActions(StrEq(kScriptPath), _, _, _, _, _))
-      .WillOnce(RunOnceCallback<5>(true, Serialize(interruptible)));
+      .WillOnce(RunOnceCallback<5>(net::HTTP_OK, Serialize(interruptible)));
 
   // The interrupt would trigger, since interrupt_trigger exits, but it's not
   // given an opportunity to.
   SetupInterrupt("interrupt", "interrupt_trigger");
 
   EXPECT_CALL(mock_service_, OnGetNextActions(_, _, _, _, _, _))
-      .WillRepeatedly(RunOnceCallback<5>(true, ""));
+      .WillRepeatedly(RunOnceCallback<5>(net::HTTP_OK, ""));
 
   EXPECT_CALL(executor_callback_,
               Run(Field(&ScriptExecutor::Result::success, true)));
@@ -729,7 +738,7 @@
   // The interrupt fails.
   EXPECT_CALL(mock_service_,
               OnGetNextActions(_, _, "payload for interrupt", _, _, _))
-      .WillOnce(RunOnceCallback<5>(false, ""));
+      .WillOnce(RunOnceCallback<5>(net::HTTP_UNAUTHORIZED, ""));
 
   // The main script gets a report of the failure from the interrupt, and fails
   // in turn.
@@ -740,7 +749,7 @@
           ElementsAre(Property(&ProcessedActionProto::status,
                                ProcessedActionStatusProto::INTERRUPT_FAILED)),
           _, _))
-      .WillOnce(RunOnceCallback<5>(false, ""));
+      .WillOnce(RunOnceCallback<5>(net::HTTP_UNAUTHORIZED, ""));
 
   EXPECT_CALL(executor_callback_,
               Run(Field(&ScriptExecutor::Result::success, false)));
@@ -763,13 +772,14 @@
 
   // Get interrupt actions
   EXPECT_CALL(mock_service_, OnGetActions(StrEq("interrupt"), _, _, _, _, _))
-      .WillRepeatedly(RunOnceCallback<5>(true, Serialize(interrupt_actions)));
+      .WillRepeatedly(
+          RunOnceCallback<5>(net::HTTP_OK, Serialize(interrupt_actions)));
 
   // We expect to get result of interrupt action, then result of the main script
   // action.
   EXPECT_CALL(mock_service_, OnGetNextActions(_, _, _, _, _, _))
       .Times(2)
-      .WillRepeatedly(RunOnceCallback<5>(true, ""));
+      .WillRepeatedly(RunOnceCallback<5>(net::HTTP_OK, ""));
 
   EXPECT_CALL(executor_callback_,
               Run(AllOf(Field(&ScriptExecutor::Result::success, true),
@@ -796,7 +806,8 @@
       ToSelectorProto("end_prompt");
   interruptible.add_actions()->mutable_tell()->set_message("done");
   EXPECT_CALL(mock_service_, OnGetActions(kScriptPath, _, _, _, _, _))
-      .WillRepeatedly(RunOnceCallback<5>(true, Serialize(interruptible)));
+      .WillRepeatedly(
+          RunOnceCallback<5>(net::HTTP_OK, Serialize(interruptible)));
 
   EXPECT_CALL(mock_web_controller_,
               OnElementCheck(Eq(Selector({"interrupt_trigger"})), _))
@@ -809,7 +820,7 @@
       .WillRepeatedly(RunOnceCallback<1>(OkClientStatus()));
 
   EXPECT_CALL(mock_service_, OnGetNextActions(_, _, _, _, _, _))
-      .WillRepeatedly(RunOnceCallback<5>(true, ""));
+      .WillRepeatedly(RunOnceCallback<5>(net::HTTP_OK, ""));
 
   EXPECT_CALL(executor_callback_,
               Run(Field(&ScriptExecutor::Result::success, true)));
@@ -843,7 +854,7 @@
   prompt->set_browse_mode(true);
 
   EXPECT_CALL(mock_service_, OnGetActions(_, _, _, _, _, _))
-      .WillOnce(RunOnceCallback<5>(true, Serialize(actions_response)));
+      .WillOnce(RunOnceCallback<5>(net::HTTP_OK, Serialize(actions_response)));
 
   executor_->Run(&user_data_, executor_callback_.Get());
   EXPECT_EQ(AutofillAssistantState::BROWSE, delegate_.GetState());
@@ -855,7 +866,7 @@
   prompt->add_choices()->mutable_chip()->set_text("done");
 
   EXPECT_CALL(mock_service_, OnGetActions(_, _, _, _, _, _))
-      .WillOnce(RunOnceCallback<5>(true, Serialize(actions_response)));
+      .WillOnce(RunOnceCallback<5>(net::HTTP_OK, Serialize(actions_response)));
 
   executor_->Run(&user_data_, executor_callback_.Get());
   EXPECT_EQ(AutofillAssistantState::PROMPT, delegate_.GetState());
@@ -873,7 +884,8 @@
   *prompt_action->add_choices()->mutable_auto_select_when()->mutable_match() =
       ToSelectorProto("end_prompt");
   EXPECT_CALL(mock_service_, OnGetActions(kScriptPath, _, _, _, _, _))
-      .WillRepeatedly(RunOnceCallback<5>(true, Serialize(interruptible)));
+      .WillRepeatedly(
+          RunOnceCallback<5>(net::HTTP_OK, Serialize(interruptible)));
 
   // interrupt_trigger goes away and come back, which means that the interrupt
   // will be run twice.
@@ -896,7 +908,7 @@
       .WillRepeatedly(RunOnceCallback<1>(OkClientStatus()));
 
   EXPECT_CALL(mock_service_, OnGetNextActions(_, _, _, _, _, _))
-      .WillRepeatedly(RunOnceCallback<5>(true, ""));
+      .WillRepeatedly(RunOnceCallback<5>(net::HTTP_OK, ""));
 
   EXPECT_CALL(executor_callback_,
               Run(Field(&ScriptExecutor::Result::success, true)));
@@ -927,7 +939,8 @@
   ActionsResponseProto initial_actions_response;
   initial_actions_response.add_actions()->mutable_tell()->set_message("1");
   EXPECT_CALL(mock_service_, OnGetActions(StrEq(kScriptPath), _, _, _, _, _))
-      .WillOnce(RunOnceCallback<5>(true, Serialize(initial_actions_response)));
+      .WillOnce(RunOnceCallback<5>(net::HTTP_OK,
+                                   Serialize(initial_actions_response)));
 
   ActionsResponseProto next_actions_response;
   next_actions_response.add_actions()->mutable_tell()->set_message("2");
@@ -939,8 +952,9 @@
   presentation->mutable_precondition();
 
   EXPECT_CALL(mock_service_, OnGetNextActions(_, _, _, _, _, _))
-      .WillOnce(RunOnceCallback<5>(true, Serialize(next_actions_response)))
-      .WillOnce(RunOnceCallback<5>(true, ""));
+      .WillOnce(
+          RunOnceCallback<5>(net::HTTP_OK, Serialize(next_actions_response)))
+      .WillOnce(RunOnceCallback<5>(net::HTTP_OK, ""));
 
   EXPECT_CALL(executor_callback_,
               Run(Field(&ScriptExecutor::Result::success, true)));
@@ -967,12 +981,12 @@
   presentation->mutable_precondition();
 
   EXPECT_CALL(mock_service_, OnGetActions(StrEq(kScriptPath), _, _, _, _, _))
-      .WillOnce(RunOnceCallback<5>(true, Serialize(actions_response)));
+      .WillOnce(RunOnceCallback<5>(net::HTTP_OK, Serialize(actions_response)));
 
   script->set_path("path2");
   EXPECT_CALL(mock_service_, OnGetNextActions(_, _, _, _, _, _))
-      .WillOnce(RunOnceCallback<5>(true, Serialize(actions_response)))
-      .WillOnce(RunOnceCallback<5>(true, ""));
+      .WillOnce(RunOnceCallback<5>(net::HTTP_OK, Serialize(actions_response)))
+      .WillOnce(RunOnceCallback<5>(net::HTTP_OK, ""));
 
   EXPECT_CALL(executor_callback_,
               Run(Field(&ScriptExecutor::Result::success, true)));
@@ -996,7 +1010,7 @@
   interrupt_actions.add_actions()->mutable_tell()->set_message("abc");
 
   EXPECT_CALL(mock_service_, OnGetActions(StrEq("interrupt"), _, _, _, _, _))
-      .WillOnce(RunOnceCallback<5>(true, Serialize(interrupt_actions)));
+      .WillOnce(RunOnceCallback<5>(net::HTTP_OK, Serialize(interrupt_actions)));
 
   auto* script = interrupt_actions.mutable_update_script_list()->add_scripts();
   script->set_path("path");
@@ -1009,8 +1023,8 @@
   // script which will finish without running any actions.
   EXPECT_CALL(mock_service_, OnGetNextActions(_, _, _, _, _, _))
       .Times(3)
-      .WillOnce(RunOnceCallback<5>(true, Serialize(interrupt_actions)))
-      .WillRepeatedly(RunOnceCallback<5>(true, ""));
+      .WillOnce(RunOnceCallback<5>(net::HTTP_OK, Serialize(interrupt_actions)))
+      .WillRepeatedly(RunOnceCallback<5>(net::HTTP_OK, ""));
 
   EXPECT_CALL(executor_callback_,
               Run(Field(&ScriptExecutor::Result::success, true)));
@@ -1037,17 +1051,19 @@
       ToSelectorProto("element");
   wait_action->set_allow_interrupt(true);
   EXPECT_CALL(mock_service_, OnGetActions(kScriptPath, _, _, _, _, _))
-      .WillRepeatedly(RunOnceCallback<5>(true, Serialize(interruptible)));
+      .WillRepeatedly(
+          RunOnceCallback<5>(net::HTTP_OK, Serialize(interruptible)));
 
   RegisterInterrupt("interrupt", "interrupt_trigger");
   ActionsResponseProto interrupt_actions;
   interrupt_actions.add_actions()->mutable_tell()->set_message(
       "interrupt status");
   EXPECT_CALL(mock_service_, OnGetActions(StrEq("interrupt"), _, _, _, _, _))
-      .WillRepeatedly(RunOnceCallback<5>(true, Serialize(interrupt_actions)));
+      .WillRepeatedly(
+          RunOnceCallback<5>(net::HTTP_OK, Serialize(interrupt_actions)));
 
   EXPECT_CALL(mock_service_, OnGetNextActions(_, _, _, _, _, _))
-      .WillRepeatedly(RunOnceCallback<5>(true, ""));
+      .WillRepeatedly(RunOnceCallback<5>(net::HTTP_OK, ""));
 
   EXPECT_CALL(executor_callback_,
               Run(Field(&ScriptExecutor::Result::success, true)));
@@ -1066,10 +1082,11 @@
       ToSelectorProto("element");
   wait_action->set_allow_interrupt(true);
   EXPECT_CALL(mock_service_, OnGetActions(kScriptPath, _, _, _, _, _))
-      .WillRepeatedly(RunOnceCallback<5>(true, Serialize(interruptible)));
+      .WillRepeatedly(
+          RunOnceCallback<5>(net::HTTP_OK, Serialize(interruptible)));
 
   EXPECT_CALL(mock_service_, OnGetNextActions(_, _, _, _, _, _))
-      .WillRepeatedly(RunOnceCallback<5>(true, ""));
+      .WillRepeatedly(RunOnceCallback<5>(net::HTTP_OK, ""));
 
   EXPECT_CALL(executor_callback_,
               Run(Field(&ScriptExecutor::Result::success, true)));
@@ -1087,11 +1104,11 @@
       ToSelectorProto("element");
 
   EXPECT_CALL(mock_service_, OnGetActions(_, _, _, _, _, _))
-      .WillOnce(RunOnceCallback<5>(true, Serialize(actions_response)));
+      .WillOnce(RunOnceCallback<5>(net::HTTP_OK, Serialize(actions_response)));
   std::vector<ProcessedActionProto> processed_actions_capture;
   EXPECT_CALL(mock_service_, OnGetNextActions(_, _, _, _, _, _))
       .WillOnce(DoAll(SaveArg<3>(&processed_actions_capture),
-                      RunOnceCallback<5>(true, "")));
+                      RunOnceCallback<5>(net::HTTP_OK, "")));
 
   // First check does not find the element, wait for dom waits 1s.
   EXPECT_CALL(mock_web_controller_,
@@ -1126,11 +1143,11 @@
       ToSelectorProto("element");
 
   EXPECT_CALL(mock_service_, OnGetActions(_, _, _, _, _, _))
-      .WillOnce(RunOnceCallback<5>(true, Serialize(actions_response)));
+      .WillOnce(RunOnceCallback<5>(net::HTTP_OK, Serialize(actions_response)));
   std::vector<ProcessedActionProto> processed_actions_capture;
   EXPECT_CALL(mock_service_, OnGetNextActions(_, _, _, _, _, _))
       .WillOnce(DoAll(SaveArg<3>(&processed_actions_capture),
-                      RunOnceCallback<5>(true, "")));
+                      RunOnceCallback<5>(net::HTTP_OK, "")));
 
   // Navigation starts before WaitForDom starts. WaitForDom does not wait and
   // completes successfully.
@@ -1153,11 +1170,11 @@
       ToSelectorProto("will fail");
 
   EXPECT_CALL(mock_service_, OnGetActions(_, _, _, _, _, _))
-      .WillOnce(RunOnceCallback<5>(true, Serialize(actions_response)));
+      .WillOnce(RunOnceCallback<5>(net::HTTP_OK, Serialize(actions_response)));
   std::vector<ProcessedActionProto> processed_actions_capture;
   EXPECT_CALL(mock_service_, OnGetNextActions(_, _, _, _, _, _))
       .WillOnce(DoAll(SaveArg<3>(&processed_actions_capture),
-                      RunOnceCallback<5>(true, "")));
+                      RunOnceCallback<5>(net::HTTP_OK, "")));
 
   delegate_.UpdateNavigationState(/* navigating= */ false, /* error= */ true);
   EXPECT_CALL(executor_callback_, Run(_));
@@ -1185,7 +1202,7 @@
       .WillRepeatedly(
           DoAll(InvokeWithoutArgs(
                     [this]() { delegate_.UpdateNavigationState(true, false); }),
-                RunOnceCallback<5>(true, Serialize(interrupt_actions)),
+                RunOnceCallback<5>(net::HTTP_OK, Serialize(interrupt_actions)),
                 InvokeWithoutArgs([this]() {
                   delegate_.UpdateNavigationState(false, false);
                 })));
@@ -1194,9 +1211,9 @@
   std::vector<ProcessedActionProto> processed_actions2_capture;
   EXPECT_CALL(mock_service_, OnGetNextActions(_, _, _, _, _, _))
       .WillOnce(DoAll(SaveArg<3>(&processed_actions1_capture),
-                      RunOnceCallback<5>(true, "")))
+                      RunOnceCallback<5>(net::HTTP_OK, "")))
       .WillOnce(DoAll(SaveArg<3>(&processed_actions2_capture),
-                      RunOnceCallback<5>(true, "")));
+                      RunOnceCallback<5>(net::HTTP_OK, "")));
 
   EXPECT_CALL(executor_callback_, Run(_));
   executor_->Run(&user_data_, executor_callback_.Get());
@@ -1211,11 +1228,11 @@
   actions_response.add_actions()->mutable_tell()->set_message("b");
 
   EXPECT_CALL(mock_service_, OnGetActions(_, _, _, _, _, _))
-      .WillOnce(RunOnceCallback<5>(true, Serialize(actions_response)));
+      .WillOnce(RunOnceCallback<5>(net::HTTP_OK, Serialize(actions_response)));
   std::vector<ProcessedActionProto> processed_actions_capture;
   EXPECT_CALL(mock_service_, OnGetNextActions(_, _, _, _, _, _))
       .WillOnce(DoAll(SaveArg<3>(&processed_actions_capture),
-                      RunOnceCallback<5>(true, "")));
+                      RunOnceCallback<5>(net::HTTP_OK, "")));
 
   delegate_.UpdateNavigationState(/* navigating= */ false, /* error= */ true);
   EXPECT_CALL(executor_callback_, Run(_));
@@ -1235,11 +1252,11 @@
       ToSelectorProto("element");
 
   EXPECT_CALL(mock_service_, OnGetActions(_, _, _, _, _, _))
-      .WillOnce(RunOnceCallback<5>(true, Serialize(actions_response)));
+      .WillOnce(RunOnceCallback<5>(net::HTTP_OK, Serialize(actions_response)));
   std::vector<ProcessedActionProto> processed_actions_capture;
   EXPECT_CALL(mock_service_, OnGetNextActions(_, _, _, _, _, _))
       .WillOnce(DoAll(SaveArg<3>(&processed_actions_capture),
-                      RunOnceCallback<5>(true, "")));
+                      RunOnceCallback<5>(net::HTTP_OK, "")));
 
   // WaitForDom does NOT wait for navigation to end, it immediately checks for
   // the element, which fails.
@@ -1272,11 +1289,11 @@
       ToSelectorProto("element");
 
   EXPECT_CALL(mock_service_, OnGetActions(_, _, _, _, _, _))
-      .WillOnce(RunOnceCallback<5>(true, Serialize(actions_response)));
+      .WillOnce(RunOnceCallback<5>(net::HTTP_OK, Serialize(actions_response)));
   std::vector<ProcessedActionProto> processed_actions_capture;
   EXPECT_CALL(mock_service_, OnGetNextActions(_, _, _, _, _, _))
       .WillOnce(DoAll(SaveArg<3>(&processed_actions_capture),
-                      RunOnceCallback<5>(true, "")));
+                      RunOnceCallback<5>(net::HTTP_OK, "")));
 
   // As the element doesn't exist, WaitForDom returns and waits for 1s.
   EXPECT_CALL(mock_web_controller_,
@@ -1307,11 +1324,11 @@
       ToSelectorProto("element");
 
   EXPECT_CALL(mock_service_, OnGetActions(_, _, _, _, _, _))
-      .WillOnce(RunOnceCallback<5>(true, Serialize(actions_response)));
+      .WillOnce(RunOnceCallback<5>(net::HTTP_OK, Serialize(actions_response)));
   std::vector<ProcessedActionProto> processed_actions_capture;
   EXPECT_CALL(mock_service_, OnGetNextActions(_, _, _, _, _, _))
       .WillOnce(DoAll(SaveArg<3>(&processed_actions_capture),
-                      RunOnceCallback<5>(true, "")));
+                      RunOnceCallback<5>(net::HTTP_OK, "")));
 
   // As the element doesn't exist, WaitForDom returns and waits for 1s.
   EXPECT_CALL(mock_web_controller_,
@@ -1340,11 +1357,11 @@
   actions_response.add_actions()->mutable_wait_for_navigation();
 
   EXPECT_CALL(mock_service_, OnGetActions(_, _, _, _, _, _))
-      .WillOnce(RunOnceCallback<5>(true, Serialize(actions_response)));
+      .WillOnce(RunOnceCallback<5>(net::HTTP_OK, Serialize(actions_response)));
   std::vector<ProcessedActionProto> processed_actions_capture;
   EXPECT_CALL(mock_service_, OnGetNextActions(_, _, _, _, _, _))
       .WillOnce(DoAll(SaveArg<3>(&processed_actions_capture),
-                      RunOnceCallback<5>(true, "")));
+                      RunOnceCallback<5>(net::HTTP_OK, "")));
 
   // WaitForNavigation returns immediately
   EXPECT_CALL(executor_callback_, Run(_));
@@ -1360,11 +1377,11 @@
   actions_response.add_actions()->mutable_wait_for_navigation();
 
   EXPECT_CALL(mock_service_, OnGetActions(_, _, _, _, _, _))
-      .WillOnce(RunOnceCallback<5>(true, Serialize(actions_response)));
+      .WillOnce(RunOnceCallback<5>(net::HTTP_OK, Serialize(actions_response)));
   std::vector<ProcessedActionProto> processed_actions_capture;
   EXPECT_CALL(mock_service_, OnGetNextActions(_, _, _, _, _, _))
       .WillOnce(DoAll(SaveArg<3>(&processed_actions_capture),
-                      RunOnceCallback<5>(true, "")));
+                      RunOnceCallback<5>(net::HTTP_OK, "")));
 
   // WaitForNavigation waits for navigation to start after expect_navigation
   EXPECT_CALL(executor_callback_, Run(_));
@@ -1385,11 +1402,11 @@
   actions_response.add_actions()->mutable_wait_for_navigation();
 
   EXPECT_CALL(mock_service_, OnGetActions(_, _, _, _, _, _))
-      .WillOnce(RunOnceCallback<5>(true, Serialize(actions_response)));
+      .WillOnce(RunOnceCallback<5>(net::HTTP_OK, Serialize(actions_response)));
   std::vector<ProcessedActionProto> processed_actions_capture;
   EXPECT_CALL(mock_service_, OnGetNextActions(_, _, _, _, _, _))
       .WillOnce(DoAll(SaveArg<3>(&processed_actions_capture),
-                      RunOnceCallback<5>(true, "")));
+                      RunOnceCallback<5>(net::HTTP_OK, "")));
 
   // The first wait_for_navigation waits for the navigation to happen. After
   // that, the other wait_for_navigation return immediately.
@@ -1411,11 +1428,11 @@
   actions_response.add_actions()->mutable_wait_for_navigation();
 
   EXPECT_CALL(mock_service_, OnGetActions(_, _, _, _, _, _))
-      .WillOnce(RunOnceCallback<5>(true, Serialize(actions_response)));
+      .WillOnce(RunOnceCallback<5>(net::HTTP_OK, Serialize(actions_response)));
   std::vector<ProcessedActionProto> processed_actions_capture;
   EXPECT_CALL(mock_service_, OnGetNextActions(_, _, _, _, _, _))
       .WillOnce(DoAll(SaveArg<3>(&processed_actions_capture),
-                      RunOnceCallback<5>(true, "")));
+                      RunOnceCallback<5>(net::HTTP_OK, "")));
 
   delegate_.UpdateNavigationState(/* navigating= */ true, /* error= */ false);
 
@@ -1445,11 +1462,11 @@
   actions_response.add_actions()->mutable_wait_for_navigation();
 
   EXPECT_CALL(mock_service_, OnGetActions(_, _, _, _, _, _))
-      .WillOnce(RunOnceCallback<5>(true, Serialize(actions_response)));
+      .WillOnce(RunOnceCallback<5>(net::HTTP_OK, Serialize(actions_response)));
   std::vector<ProcessedActionProto> processed_actions_capture;
   EXPECT_CALL(mock_service_, OnGetNextActions(_, _, _, _, _, _))
       .WillOnce(DoAll(SaveArg<3>(&processed_actions_capture),
-                      RunOnceCallback<5>(true, "")));
+                      RunOnceCallback<5>(net::HTTP_OK, "")));
 
   // WaitForNavigation waits for navigation to start after expect_navigation
   EXPECT_CALL(executor_callback_, Run(_));
@@ -1471,7 +1488,7 @@
       ->set_text("done");
 
   EXPECT_CALL(mock_service_, OnGetActions(_, _, _, _, _, _))
-      .WillOnce(RunOnceCallback<5>(true, Serialize(actions_response)));
+      .WillOnce(RunOnceCallback<5>(net::HTTP_OK, Serialize(actions_response)));
 
   executor_->Run(&user_data_, executor_callback_.Get());
   EXPECT_EQ(AutofillAssistantState::PROMPT, delegate_.GetState());
@@ -1495,7 +1512,7 @@
       ->add_names("done");
 
   EXPECT_CALL(mock_service_, OnGetActions(_, _, _, _, _, _))
-      .WillOnce(RunOnceCallback<5>(true, Serialize(actions_response)));
+      .WillOnce(RunOnceCallback<5>(net::HTTP_OK, Serialize(actions_response)));
 
   std::vector<ProcessedActionProto> processed_actions_capture;
   EXPECT_CALL(mock_service_, OnGetNextActions(_, _, _, _, _, _))
@@ -1523,7 +1540,7 @@
       ->set_text("Chip");
 
   EXPECT_CALL(mock_service_, OnGetActions(_, _, _, _, _, _))
-      .WillOnce(RunOnceCallback<5>(true, Serialize(actions_response)));
+      .WillOnce(RunOnceCallback<5>(net::HTTP_OK, Serialize(actions_response)));
 
   executor_->Run(&user_data_, executor_callback_.Get());
   EXPECT_EQ("Tell", delegate_.GetStatusMessage());
@@ -1561,7 +1578,7 @@
   actions_response.add_actions()->mutable_tell()->set_message("Finished");
 
   EXPECT_CALL(mock_service_, OnGetActions(_, _, _, _, _, _))
-      .WillOnce(RunOnceCallback<5>(true, Serialize(actions_response)));
+      .WillOnce(RunOnceCallback<5>(net::HTTP_OK, Serialize(actions_response)));
 
   // At first we don't find the element, to keep the |WaitForDomAction| running.
   EXPECT_CALL(mock_web_controller_,
@@ -1609,14 +1626,16 @@
   action->mutable_tell()->set_message("1");
   action->set_action_delay_ms(1000);
   EXPECT_CALL(mock_service_, OnGetActions(_, _, _, _, _, _))
-      .WillOnce(DoAll(Delay(&task_environment_, 200),
-                      RunOnceCallback<5>(true, Serialize(actions_response))));
+      .WillOnce(
+          DoAll(Delay(&task_environment_, 200),
+                RunOnceCallback<5>(net::HTTP_OK, Serialize(actions_response))));
 
   ActionsResponseProto next_actions_response;
   next_actions_response.add_actions()->mutable_tell()->set_message("3");
   RoundtripTimingStats timing_stats;
   EXPECT_CALL(mock_service_, OnGetNextActions(_, _, _, _, _, _))
-      .WillOnce(DoAll(SaveArg<4>(&timing_stats), RunOnceCallback<5>(true, "")));
+      .WillOnce(DoAll(SaveArg<4>(&timing_stats),
+                      RunOnceCallback<5>(net::HTTP_OK, "")));
   executor_->Run(&user_data_, executor_callback_.Get());
   EXPECT_TRUE(task_environment_.NextTaskIsDelayed());
 
diff --git a/components/autofill_assistant/browser/script_tracker_unittest.cc b/components/autofill_assistant/browser/script_tracker_unittest.cc
index 7122534..a2e22a4 100644
--- a/components/autofill_assistant/browser/script_tracker_unittest.cc
+++ b/components/autofill_assistant/browser/script_tracker_unittest.cc
@@ -14,6 +14,7 @@
 #include "components/autofill_assistant/browser/service/mock_service.h"
 #include "components/autofill_assistant/browser/service/service.h"
 #include "components/autofill_assistant/browser/web/mock_web_controller.h"
+#include "net/http/http_status_code.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
 namespace autofill_assistant {
@@ -43,7 +44,7 @@
 
     // Scripts run, but have no actions.
     ON_CALL(mock_service_, OnGetActions(_, _, _, _, _, _))
-        .WillByDefault(RunOnceCallback<5>(true, ""));
+        .WillByDefault(RunOnceCallback<5>(net::HTTP_OK, ""));
   }
 
  protected:
@@ -303,9 +304,9 @@
 
   EXPECT_CALL(mock_service_,
               OnGetActions(StrEq("runnable name"), _, _, _, _, _))
-      .WillOnce(RunOnceCallback<5>(true, Serialize(actions_response)));
+      .WillOnce(RunOnceCallback<5>(net::HTTP_OK, Serialize(actions_response)));
   EXPECT_CALL(mock_service_, OnGetNextActions(_, _, _, _, _, _))
-      .WillOnce(RunOnceCallback<5>(true, ""));
+      .WillOnce(RunOnceCallback<5>(net::HTTP_OK, ""));
 
   base::MockCallback<ScriptExecutor::RunScriptCallback> execute_callback;
   EXPECT_CALL(execute_callback,
@@ -345,9 +346,9 @@
 
   EXPECT_CALL(mock_service_,
               OnGetActions(StrEq("runnable name"), _, _, _, _, _))
-      .WillOnce(RunOnceCallback<5>(true, Serialize(actions_response)));
+      .WillOnce(RunOnceCallback<5>(net::HTTP_OK, Serialize(actions_response)));
   EXPECT_CALL(mock_service_, OnGetNextActions(_, _, _, _, _, _))
-      .WillOnce(RunOnceCallback<5>(true, ""));
+      .WillOnce(RunOnceCallback<5>(net::HTTP_OK, ""));
 
   base::MockCallback<ScriptExecutor::RunScriptCallback> execute_callback;
   EXPECT_CALL(execute_callback,
@@ -384,16 +385,16 @@
   interrupt_proto->mutable_presentation()->set_interrupt(true);
 
   EXPECT_CALL(mock_service_, OnGetActions("main", _, _, _, _, _))
-      .WillOnce(RunOnceCallback<5>(true, Serialize(actions_response)));
+      .WillOnce(RunOnceCallback<5>(net::HTTP_OK, Serialize(actions_response)));
   EXPECT_CALL(mock_service_, OnGetNextActions(_, _, _, _, _, _))
-      .WillRepeatedly(RunOnceCallback<5>(true, ""));
+      .WillRepeatedly(RunOnceCallback<5>(net::HTTP_OK, ""));
 
   ActionsResponseProto actions_interrupt;
   actions_response.set_script_payload("from interrupt");
   actions_response.add_actions()->mutable_tell()->set_message("interrupt");
 
   EXPECT_CALL(mock_service_, OnGetActions("interrupt", _, _, _, _, _))
-      .WillOnce(RunOnceCallback<5>(true, Serialize(actions_interrupt)));
+      .WillOnce(RunOnceCallback<5>(net::HTTP_OK, Serialize(actions_interrupt)));
 
   base::MockCallback<ScriptExecutor::RunScriptCallback> execute_callback;
   EXPECT_CALL(execute_callback,
diff --git a/components/autofill_assistant/browser/service/java_service.cc b/components/autofill_assistant/browser/service/java_service.cc
index 71735e89..63c54611 100644
--- a/components/autofill_assistant/browser/service/java_service.cc
+++ b/components/autofill_assistant/browser/service/java_service.cc
@@ -12,6 +12,7 @@
 #include "base/android/jni_android.h"
 #include "base/android/jni_array.h"
 #include "base/android/jni_string.h"
+#include "net/http/http_status_code.h"
 
 namespace autofill_assistant {
 
@@ -41,7 +42,7 @@
       base::android::ConvertUTF8ToJavaString(env, url.spec()));
   std::string response;
   base::android::JavaByteArrayToString(env, jresponse, &response);
-  std::move(callback).Run(true, response);
+  std::move(callback).Run(net::HTTP_OK, response);
 }
 
 void JavaService::GetActions(const std::string& script_path,
@@ -59,7 +60,7 @@
       base::android::ToJavaByteArray(env, script_payload));
   std::string response;
   base::android::JavaByteArrayToString(env, jresponse, &response);
-  std::move(callback).Run(true, response);
+  std::move(callback).Run(net::HTTP_OK, response);
 }
 
 void JavaService::GetNextActions(
@@ -87,7 +88,7 @@
       jprocessed_actions);
   std::string response;
   base::android::JavaByteArrayToString(env, jresponse, &response);
-  std::move(callback).Run(true, response);
+  std::move(callback).Run(net::HTTP_OK, response);
 }
 
 }  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/service/lite_service.cc b/components/autofill_assistant/browser/service/lite_service.cc
index 0cb277f2..097a17f69 100644
--- a/components/autofill_assistant/browser/service/lite_service.cc
+++ b/components/autofill_assistant/browser/service/lite_service.cc
@@ -12,6 +12,7 @@
 #include "base/bind.h"
 #include "base/logging.h"
 #include "base/strings/string_number_conversions.h"
+#include "net/http/http_status_code.h"
 
 namespace autofill_assistant {
 
@@ -50,7 +51,7 @@
 
   std::string serialized_response;
   response.SerializeToString(&serialized_response);
-  std::move(callback).Run(true, serialized_response);
+  std::move(callback).Run(net::HTTP_OK, serialized_response);
 }
 
 void LiteService::GetActions(const std::string& script_path,
@@ -78,9 +79,9 @@
 }
 
 void LiteService::OnGetActions(ResponseCallback callback,
-                               bool result,
+                               int http_status,
                                const std::string& response) {
-  if (!result) {
+  if (http_status != net::HTTP_OK) {
     StopWithoutErrorMessage(
         std::move(callback),
         Metrics::LiteScriptFinishedState::LITE_SCRIPT_GET_ACTIONS_FAILED);
@@ -131,7 +132,7 @@
 
   std::string serialized_first_part;
   split_actions->first.SerializeToString(&serialized_first_part);
-  std::move(callback).Run(result, serialized_first_part);
+  std::move(callback).Run(http_status, serialized_first_part);
   notify_script_running_callback_.Run(/*ui_shown = */ false);
 }
 
@@ -146,7 +147,7 @@
     // The lite script has already terminated. We need to run |callback| with
     // |success|=true and an empty response to ensure a graceful stop of the
     // script (i.e., without error message).
-    std::move(callback).Run(true, std::string());
+    std::move(callback).Run(net::HTTP_OK, std::string());
     return;
   }
 
@@ -179,7 +180,7 @@
         std::string serialized_second_part;
         trigger_script_second_part_->SerializeToString(&serialized_second_part);
         trigger_script_second_part_.reset();
-        std::move(callback).Run(true, serialized_second_part);
+        std::move(callback).Run(net::HTTP_OK, serialized_second_part);
         notify_script_running_callback_.Run(/*ui_shown = */ true);
         return;
     }
@@ -234,7 +235,7 @@
   response.add_actions()->mutable_stop();
   std::string serialized_response;
   response.SerializeToString(&serialized_response);
-  std::move(callback).Run(true, serialized_response);
+  std::move(callback).Run(net::HTTP_OK, serialized_response);
 }
 
 }  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/service/lite_service.h b/components/autofill_assistant/browser/service/lite_service.h
index 721c52d..dd846e1 100644
--- a/components/autofill_assistant/browser/service/lite_service.h
+++ b/components/autofill_assistant/browser/service/lite_service.h
@@ -69,7 +69,7 @@
   friend class LiteServiceTest;
 
   void OnGetActions(ResponseCallback callback,
-                    bool result,
+                    int http_status,
                     const std::string& response);
 
   // Stops the script and closes autofill assistant without showing an error
diff --git a/components/autofill_assistant/browser/service/lite_service_unittest.cc b/components/autofill_assistant/browser/service/lite_service_unittest.cc
index 632aadc6..5e2bfb2 100644
--- a/components/autofill_assistant/browser/service/lite_service_unittest.cc
+++ b/components/autofill_assistant/browser/service/lite_service_unittest.cc
@@ -20,6 +20,7 @@
 #include "base/test/gmock_callback_support.h"
 #include "base/test/gtest_util.h"
 #include "base/test/mock_callback.h"
+#include "net/http/http_status_code.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
 namespace autofill_assistant {
@@ -54,7 +55,7 @@
                       Service::ResponseCallback& callback) {
           std::string serialized_response;
           get_actions_response_.SerializeToString(&serialized_response);
-          std::move(callback).Run(true, serialized_response);
+          std::move(callback).Run(net::HTTP_OK, serialized_response);
         });
   }
   ~LiteServiceTest() override {}
@@ -72,7 +73,8 @@
     stop.add_actions()->mutable_stop();
     std::string serialized_stop;
     stop.SerializeToString(&serialized_stop);
-    EXPECT_CALL(mock_response_callback_, Run(true, serialized_stop)).Times(1);
+    EXPECT_CALL(mock_response_callback_, Run(net::HTTP_OK, serialized_stop))
+        .Times(1);
     EXPECT_CALL(mock_finished_callback_, Run(state));
   }
 
@@ -80,7 +82,7 @@
       mock_finished_callback_;
   base::MockCallback<base::RepeatingCallback<void(bool)>>
       mock_script_running_callback_;
-  base::MockCallback<base::OnceCallback<void(bool, const std::string&)>>
+  base::MockCallback<base::OnceCallback<void(int, const std::string&)>>
       mock_response_callback_;
   MockService* mock_native_service_ = nullptr;
   std::unique_ptr<LiteService> lite_service_;
@@ -103,9 +105,9 @@
 }
 
 TEST_F(LiteServiceTest, GetScriptsForUrl) {
-  EXPECT_CALL(mock_response_callback_, Run(true, _))
+  EXPECT_CALL(mock_response_callback_, Run(net::HTTP_OK, _))
       .Times(1)
-      .WillOnce([](bool success, const std::string& response) {
+      .WillOnce([](int http_status, const std::string& response) {
         SupportsScriptResponseProto proto;
         ASSERT_TRUE(proto.ParseFromString(response));
 
@@ -163,7 +165,7 @@
                     const std::string& global_payload,
                     const std::string& script_payload,
                     Service::ResponseCallback& callback) {
-        std::move(callback).Run(false, std::string());
+        std::move(callback).Run(net::HTTP_UNAUTHORIZED, std::string());
       });
 
   EXPECT_CALL(mock_script_running_callback_, Run).Times(0);
@@ -183,7 +185,7 @@
                     const std::string& global_payload,
                     const std::string& script_payload,
                     Service::ResponseCallback& callback) {
-        std::move(callback).Run(true, std::string("invalid proto"));
+        std::move(callback).Run(net::HTTP_OK, std::string("invalid proto"));
       });
   EXPECT_CALL(mock_script_running_callback_, Run).Times(0);
   lite_service_->GetActions(kFakeScriptPath, GURL(kFakeUrl),
@@ -206,7 +208,7 @@
 TEST_F(LiteServiceTest, GetActionsSucceedsForMinimalViableScript) {
   get_actions_response_.add_actions()->mutable_prompt()->set_browse_mode(true);
   get_actions_response_.add_actions()->mutable_prompt();
-  EXPECT_CALL(mock_response_callback_, Run(true, _));
+  EXPECT_CALL(mock_response_callback_, Run(net::HTTP_OK, _));
   EXPECT_CALL(mock_script_running_callback_, Run(/*ui_shown =*/false)).Times(1);
   lite_service_->GetActions(kFakeScriptPath, GURL(kFakeUrl),
                             TriggerContextImpl(), "", "",
@@ -233,9 +235,9 @@
   }
 
   std::vector<ProcessedActionProto> processed_actions;
-  EXPECT_CALL(mock_response_callback_, Run(true, _))
+  EXPECT_CALL(mock_response_callback_, Run(net::HTTP_OK, _))
       .Times(1)
-      .WillOnce([&](bool result, const std::string& response) {
+      .WillOnce([&](int http_status, const std::string& response) {
         // Can't directly compare the protos here, because the lite service
         // will have automatically assigned unique payloads to prompts.
         ActionsResponseProto proto;
@@ -269,9 +271,9 @@
                             TriggerContextImpl(), "", "",
                             mock_response_callback_.Get());
 
-  EXPECT_CALL(mock_response_callback_, Run(true, _))
+  EXPECT_CALL(mock_response_callback_, Run(net::HTTP_OK, _))
       .Times(1)
-      .WillOnce([&](bool result, const std::string& response) {
+      .WillOnce([&](int http_result, const std::string& response) {
         ActionsResponseProto proto;
         ASSERT_TRUE(proto.ParseFromString(response));
         ASSERT_TRUE(proto.actions().size() == 1);
@@ -314,7 +316,7 @@
   processed_actions.back().mutable_prompt_choice()->set_server_payload(
       "payload");
 
-  EXPECT_CALL(mock_response_callback_, Run(true, ""));
+  EXPECT_CALL(mock_response_callback_, Run(net::HTTP_OK, ""));
   EXPECT_CALL(mock_script_running_callback_, Run(/*ui_shown =*/true)).Times(1);
   lite_service_->GetNextActions(TriggerContextImpl(), "", "", processed_actions,
                                 RoundtripTimingStats(),
@@ -334,7 +336,7 @@
   processed_actions.back().mutable_prompt_choice()->set_server_payload(
       "payload");
 
-  EXPECT_CALL(mock_response_callback_, Run(true, "")).Times(1);
+  EXPECT_CALL(mock_response_callback_, Run(net::HTTP_OK, "")).Times(1);
   lite_service_->GetNextActions(TriggerContextImpl(), "", "", processed_actions,
                                 RoundtripTimingStats(),
                                 mock_response_callback_.Get());
diff --git a/components/autofill_assistant/browser/service/mock_access_token_fetcher.cc b/components/autofill_assistant/browser/service/mock_access_token_fetcher.cc
new file mode 100644
index 0000000..a724f6b56
--- /dev/null
+++ b/components/autofill_assistant/browser/service/mock_access_token_fetcher.cc
@@ -0,0 +1,12 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill_assistant/browser/service/mock_access_token_fetcher.h"
+
+namespace autofill_assistant {
+
+MockAccessTokenFetcher::MockAccessTokenFetcher() = default;
+MockAccessTokenFetcher::~MockAccessTokenFetcher() = default;
+
+}  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/service/mock_access_token_fetcher.h b/components/autofill_assistant/browser/service/mock_access_token_fetcher.h
new file mode 100644
index 0000000..7f3b640c
--- /dev/null
+++ b/components/autofill_assistant/browser/service/mock_access_token_fetcher.h
@@ -0,0 +1,35 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_SERVICE_MOCK_ACCESS_TOKEN_FETCHER_H_
+#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_SERVICE_MOCK_ACCESS_TOKEN_FETCHER_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "components/autofill_assistant/browser/service/access_token_fetcher.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace autofill_assistant {
+
+class MockAccessTokenFetcher : public AccessTokenFetcher {
+ public:
+  MockAccessTokenFetcher();
+  ~MockAccessTokenFetcher() override;
+
+  void FetchAccessToken(
+      base::OnceCallback<void(bool, const std::string&)> callback) override {
+    OnFetchAccessToken(callback);
+  }
+
+  MOCK_METHOD1(
+      OnFetchAccessToken,
+      void(base::OnceCallback<void(bool, const std::string&)>& callback));
+
+  MOCK_METHOD1(InvalidateAccessToken, void(const std::string& access_token));
+};
+
+}  // namespace autofill_assistant
+
+#endif  // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_SERVICE_MOCK_ACCESS_TOKEN_FETCHER_H_
diff --git a/components/autofill_assistant/browser/service/mock_service.cc b/components/autofill_assistant/browser/service/mock_service.cc
index d9dbd56e..0b6ce7b 100644
--- a/components/autofill_assistant/browser/service/mock_service.cc
+++ b/components/autofill_assistant/browser/service/mock_service.cc
@@ -11,13 +11,10 @@
 namespace autofill_assistant {
 
 MockService::MockService()
-    : ServiceImpl(std::string("api_key"),
-                  GURL("http://fake"),
-                  GURL("http://fake"),
-                  nullptr,
-                  nullptr,
-                  nullptr,
-                  true) {}
+    : ServiceImpl(/* request_sender = */ nullptr,
+                  /* script_server_url = */ GURL("http://fake"),
+                  /* action_server_url = */ GURL("http://fake"),
+                  /* client_context = */ nullptr) {}
 MockService::~MockService() {}
 
 }  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/service/mock_service_request_sender.cc b/components/autofill_assistant/browser/service/mock_service_request_sender.cc
new file mode 100644
index 0000000..3c29d38c
--- /dev/null
+++ b/components/autofill_assistant/browser/service/mock_service_request_sender.cc
@@ -0,0 +1,18 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill_assistant/browser/service/mock_service_request_sender.h"
+
+namespace autofill_assistant {
+
+MockServiceRequestSender::MockServiceRequestSender()
+    : ServiceRequestSender(/* context = */ nullptr,
+                           /* access_token_fetcher = */ nullptr,
+                           /* loader_factory = */ nullptr,
+                           /* api_key = */ std::string("fake_api_key"),
+                           /* auth_enabled = */ false,
+                           /* disable_auth_if_no_access_token = */ true) {}
+MockServiceRequestSender::~MockServiceRequestSender() = default;
+
+}  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/service/mock_service_request_sender.h b/components/autofill_assistant/browser/service/mock_service_request_sender.h
new file mode 100644
index 0000000..2911f6f9
--- /dev/null
+++ b/components/autofill_assistant/browser/service/mock_service_request_sender.h
@@ -0,0 +1,35 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_SERVICE_MOCK_SERVICE_REQUEST_SENDER_H_
+#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_SERVICE_MOCK_SERVICE_REQUEST_SENDER_H_
+
+#include <string>
+
+#include "components/autofill_assistant/browser/service/service_request_sender.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "url/gurl.h"
+
+namespace autofill_assistant {
+
+class MockServiceRequestSender : public ServiceRequestSender {
+ public:
+  MockServiceRequestSender();
+  ~MockServiceRequestSender() override;
+
+  void SendRequest(const GURL& url,
+                   const std::string& request_body,
+                   ResponseCallback callback) override {
+    OnSendRequest(url, request_body, callback);
+  }
+
+  MOCK_METHOD3(OnSendRequest,
+               void(const GURL& url,
+                    const std::string& request_body,
+                    ResponseCallback& callback));
+};
+
+}  // namespace autofill_assistant
+
+#endif  // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_SERVICE_MOCK_SERVICE_REQUEST_SENDER_H_
diff --git a/components/autofill_assistant/browser/service/mock_simple_url_loader_factory.cc b/components/autofill_assistant/browser/service/mock_simple_url_loader_factory.cc
new file mode 100644
index 0000000..10fd7fb8
--- /dev/null
+++ b/components/autofill_assistant/browser/service/mock_simple_url_loader_factory.cc
@@ -0,0 +1,12 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill_assistant/browser/service/mock_simple_url_loader_factory.h"
+
+namespace autofill_assistant {
+
+MockSimpleURLLoaderFactory::MockSimpleURLLoaderFactory() = default;
+MockSimpleURLLoaderFactory::~MockSimpleURLLoaderFactory() = default;
+
+}  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/service/mock_simple_url_loader_factory.h b/components/autofill_assistant/browser/service/mock_simple_url_loader_factory.h
new file mode 100644
index 0000000..9890af3
--- /dev/null
+++ b/components/autofill_assistant/browser/service/mock_simple_url_loader_factory.h
@@ -0,0 +1,37 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_SERVICE_MOCK_SIMPLE_URL_LOADER_FACTORY_H_
+#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_SERVICE_MOCK_SIMPLE_URL_LOADER_FACTORY_H_
+
+#include <memory>
+
+#include "components/autofill_assistant/browser/service/simple_url_loader_factory.h"
+#include "net/traffic_annotation/network_traffic_annotation.h"
+#include "services/network/public/cpp/resource_request.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace autofill_assistant {
+
+class MockSimpleURLLoaderFactory : public SimpleURLLoaderFactory {
+ public:
+  MockSimpleURLLoaderFactory();
+  ~MockSimpleURLLoaderFactory() override;
+
+  std::unique_ptr<::network::SimpleURLLoader> CreateLoader(
+      std::unique_ptr<::network::ResourceRequest> resource_request,
+      const ::net::NetworkTrafficAnnotationTag& annotation_tag) const override {
+    return OnCreateLoader(resource_request.get(), annotation_tag);
+  }
+
+  MOCK_CONST_METHOD2(
+      OnCreateLoader,
+      std::unique_ptr<::network::SimpleURLLoader>(
+          ::network::ResourceRequest* resource_request,
+          const ::net::NetworkTrafficAnnotationTag& annotation_tag));
+};
+
+}  // namespace autofill_assistant
+
+#endif  // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_SERVICE_MOCK_SIMPLE_URL_LOADER_FACTORY_H_
diff --git a/components/autofill_assistant/browser/service/mock_url_loader.cc b/components/autofill_assistant/browser/service/mock_url_loader.cc
new file mode 100644
index 0000000..5d5bd0f
--- /dev/null
+++ b/components/autofill_assistant/browser/service/mock_url_loader.cc
@@ -0,0 +1,12 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill_assistant/browser/service/mock_url_loader.h"
+
+namespace autofill_assistant {
+
+MockURLLoader::MockURLLoader() = default;
+MockURLLoader::~MockURLLoader() = default;
+
+}  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/service/mock_url_loader.h b/components/autofill_assistant/browser/service/mock_url_loader.h
new file mode 100644
index 0000000..7dd7fea0
--- /dev/null
+++ b/components/autofill_assistant/browser/service/mock_url_loader.h
@@ -0,0 +1,84 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_SERVICE_MOCK_URL_LOADER_H_
+#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_SERVICE_MOCK_URL_LOADER_H_
+
+#include "components/autofill_assistant/browser/service/service_request_sender.h"
+#include "services/network/public/cpp/simple_url_loader.h"
+
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace network {
+namespace mojom {
+class URLLoaderFactory;
+}  // namespace mojom
+}  // namespace network
+
+namespace autofill_assistant {
+
+// TODO(arbesser): Remove this once we pass in the mojom interface to our
+// service, instead of the SimpleURLLoader.
+class MockURLLoader : public ::network::SimpleURLLoader {
+ public:
+  MockURLLoader();
+  ~MockURLLoader() override;
+  MOCK_METHOD3(DownloadToString,
+               void(::network::mojom::URLLoaderFactory* url_loader_factory,
+                    BodyAsStringCallback body_as_string_callback,
+                    size_t max_body_size));
+  MOCK_METHOD2(DownloadToStringOfUnboundedSizeUntilCrashAndDie,
+               void(::network::mojom::URLLoaderFactory* url_loader_factory,
+                    BodyAsStringCallback body_as_string_callback));
+  MOCK_METHOD2(DownloadHeadersOnly,
+               void(::network::mojom::URLLoaderFactory* url_loader_factory,
+                    HeadersOnlyCallback headers_only_callback));
+  MOCK_METHOD4(
+      DownloadToFile,
+      void(::network::mojom::URLLoaderFactory* url_loader_factory,
+           DownloadToFileCompleteCallback download_to_file_complete_callback,
+           const base::FilePath& file_path,
+           int64_t max_body_size));
+  MOCK_METHOD3(
+      DownloadToTempFile,
+      void(::network::mojom::URLLoaderFactory* url_loader_factory,
+           DownloadToFileCompleteCallback download_to_file_complete_callback,
+           int64_t max_body_size));
+  MOCK_METHOD2(DownloadAsStream,
+               void(::network::mojom::URLLoaderFactory* url_loader_factory,
+                    ::network::SimpleURLLoaderStreamConsumer* stream_consumer));
+  MOCK_METHOD1(SetOnRedirectCallback,
+               void(const OnRedirectCallback& on_redirect_callback));
+  MOCK_METHOD1(SetOnResponseStartedCallback,
+               void(OnResponseStartedCallback on_response_started_callback));
+  MOCK_METHOD1(SetOnUploadProgressCallback,
+               void(UploadProgressCallback on_upload_progress_callback));
+  MOCK_METHOD1(SetOnDownloadProgressCallback,
+               void(DownloadProgressCallback on_download_progress_callback));
+  MOCK_METHOD1(SetAllowPartialResults, void(bool allow_partial_results));
+  MOCK_METHOD1(SetAllowHttpErrorResults, void(bool allow_http_error_results));
+  MOCK_METHOD2(AttachStringForUpload,
+               void(const std::string& upload_data,
+                    const std::string& upload_content_type));
+  MOCK_METHOD4(AttachFileForUpload,
+               void(const base::FilePath& upload_file_path,
+                    const std::string& upload_content_type,
+                    uint64_t offset,
+                    uint64_t length));
+  MOCK_METHOD2(SetRetryOptions, void(int max_retries, int retry_mode));
+  MOCK_METHOD1(SetURLLoaderFactoryOptions, void(uint32_t options));
+  MOCK_METHOD1(SetRequestID, void(int32_t request_id));
+  MOCK_METHOD1(SetTimeoutDuration, void(base::TimeDelta timeout_duration));
+  MOCK_CONST_METHOD0(NetError, int());
+  MOCK_CONST_METHOD0(ResponseInfo, const ::network::mojom::URLResponseHead*());
+  MOCK_CONST_METHOD0(CompletionStatus,
+                     base::Optional<::network::URLLoaderCompletionStatus>&());
+  MOCK_CONST_METHOD0(GetFinalURL, const GURL&());
+  MOCK_CONST_METHOD0(LoadedFromCache, bool());
+  MOCK_CONST_METHOD0(GetContentSize, int64_t());
+};
+
+}  // namespace autofill_assistant
+
+#endif  // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_SERVICE_MOCK_URL_LOADER_H_
diff --git a/components/autofill_assistant/browser/service/service.h b/components/autofill_assistant/browser/service/service.h
index 611aae16..ca413cf 100644
--- a/components/autofill_assistant/browser/service/service.h
+++ b/components/autofill_assistant/browser/service/service.h
@@ -27,7 +27,7 @@
   virtual bool IsLiteService() const = 0;
 
   using ResponseCallback =
-      base::OnceCallback<void(bool result, const std::string&)>;
+      base::OnceCallback<void(int http_status, const std::string&)>;
   // Get scripts for a given |url|, which should be a valid URL.
   virtual void GetScriptsForUrl(const GURL& url,
                                 const TriggerContext& trigger_context,
diff --git a/components/autofill_assistant/browser/service/service_impl.cc b/components/autofill_assistant/browser/service/service_impl.cc
index 1542d159..8302dee 100644
--- a/components/autofill_assistant/browser/service/service_impl.cc
+++ b/components/autofill_assistant/browser/service/service_impl.cc
@@ -25,59 +25,35 @@
 #include "net/traffic_annotation/network_traffic_annotation.h"
 
 namespace autofill_assistant {
-namespace {
-
-net::NetworkTrafficAnnotationTag traffic_annotation =
-    net::DefineNetworkTrafficAnnotation("autofill_service", R"(
-        semantics {
-          sender: "Autofill Assistant"
-          description:
-            "Chromium posts requests to autofill assistant server to get
-            scripts for a URL."
-          trigger:
-            "Matching URL."
-          data: "None."
-          destination: GOOGLE_OWNED_SERVICE
-        }
-        policy {
-          cookies_allowed: NO
-          setting:
-            "This feature can be disabled in settings."
-          policy_exception_justification: "Not implemented."
-        })");
-
-}  // namespace
 
 // static
 std::unique_ptr<ServiceImpl> ServiceImpl::Create(
     content::BrowserContext* context,
     Client* client) {
   ServerUrlFetcher url_fetcher{ServerUrlFetcher::GetDefaultServerUrl()};
-  return std::make_unique<ServiceImpl>(
+  auto request_sender = std::make_unique<ServiceRequestSender>(
+      context, client->GetAccessTokenFetcher(),
+      std::make_unique<NativeURLLoaderFactory>(),
       ApiKeyFetcher().GetAPIKey(client->GetChannel()),
-      url_fetcher.GetSupportsScriptEndpoint(),
-      url_fetcher.GetNextActionsEndpoint(), context,
-      std::make_unique<ClientContextImpl>(client),
-      client->GetAccessTokenFetcher(),
       /* auth_enabled = */ "false" !=
           base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
-              switches::kAutofillAssistantAuth));
+              switches::kAutofillAssistantAuth),
+      /* disable_auth_if_no_access_token = */ true);
+
+  return std::make_unique<ServiceImpl>(
+      std::move(request_sender), url_fetcher.GetSupportsScriptEndpoint(),
+      url_fetcher.GetNextActionsEndpoint(),
+      std::make_unique<ClientContextImpl>(client));
 }
 
-ServiceImpl::ServiceImpl(const std::string& api_key,
+ServiceImpl::ServiceImpl(std::unique_ptr<ServiceRequestSender> request_sender,
                          const GURL& script_server_url,
                          const GURL& action_server_url,
-                         content::BrowserContext* context,
-                         std::unique_ptr<ClientContext> client_context,
-                         AccessTokenFetcher* access_token_fetcher,
-                         bool auth_enabled)
-    : context_(context),
+                         std::unique_ptr<ClientContext> client_context)
+    : request_sender_(std::move(request_sender)),
       script_server_url_(script_server_url),
       script_action_server_url_(action_server_url),
-      api_key_(api_key),
-      client_context_(std::move(client_context)),
-      access_token_fetcher_(access_token_fetcher),
-      auth_enabled_(auth_enabled) {
+      client_context_(std::move(client_context)) {
   DCHECK(script_server_url.is_valid());
   DCHECK(action_server_url.is_valid());
 }
@@ -88,13 +64,12 @@
                                    const TriggerContext& trigger_context,
                                    ResponseCallback callback) {
   DCHECK(url.is_valid());
-
   client_context_->Update(trigger_context);
-  SendRequest(AddLoader(
+  request_sender_->SendRequest(
       script_server_url_,
       ProtocolUtils::CreateGetScriptsRequest(url, client_context_->AsProto(),
                                              trigger_context.GetParameters()),
-      std::move(callback)));
+      std::move(callback));
 }
 
 bool ServiceImpl::IsLiteService() const {
@@ -108,14 +83,13 @@
                              const std::string& script_payload,
                              ResponseCallback callback) {
   DCHECK(!script_path.empty());
-
   client_context_->Update(trigger_context);
-  SendRequest(AddLoader(
+  request_sender_->SendRequest(
       script_action_server_url_,
       ProtocolUtils::CreateInitialScriptActionsRequest(
           script_path, url, global_payload, script_payload,
           client_context_->AsProto(), trigger_context.GetParameters()),
-      std::move(callback)));
+      std::move(callback));
 }
 
 void ServiceImpl::GetNextActions(
@@ -126,147 +100,12 @@
     const RoundtripTimingStats& timing_stats,
     ResponseCallback callback) {
   client_context_->Update(trigger_context);
-  SendRequest(AddLoader(
+  request_sender_->SendRequest(
       script_action_server_url_,
       ProtocolUtils::CreateNextScriptActionsRequest(
           previous_global_payload, previous_script_payload, processed_actions,
           timing_stats, client_context_->AsProto()),
-      std::move(callback)));
-}
-
-void ServiceImpl::SendRequest(Loader* loader) {
-  if (access_token_.empty() && auth_enabled_) {
-    // Trigger a fetch of the access token. All loaders in loaders_ will be
-    // started later on, the access token is available.
-    FetchAccessToken();
-    return;
-  }
-
-  StartLoader(loader);
-}
-
-ServiceImpl::Loader::Loader() : retried_with_fresh_access_token(false) {}
-ServiceImpl::Loader::~Loader() {}
-
-ServiceImpl::Loader* ServiceImpl::AddLoader(const GURL& url,
-                                            const std::string& request_body,
-                                            ResponseCallback callback) {
-  std::unique_ptr<Loader> loader = std::make_unique<Loader>();
-  loader->url = url;
-  loader->request_body = request_body;
-  loader->callback = std::move(callback);
-  Loader* loader_ptr = loader.get();
-  loaders_[loader_ptr] = std::move(loader);
-  return loader_ptr;
-}
-
-void ServiceImpl::StartLoader(Loader* loader) {
-  if (loader->loader)
-    return;
-
-  auto resource_request = std::make_unique<::network::ResourceRequest>();
-  resource_request->method = "POST";
-  resource_request->redirect_mode = ::network::mojom::RedirectMode::kError;
-  resource_request->credentials_mode = ::network::mojom::CredentialsMode::kOmit;
-  if (access_token_.empty()) {
-    std::string query_str = base::StrCat({"key=", api_key_});
-    // query_str must remain valid until ReplaceComponents() has returned.
-    url::StringPieceReplacements<std::string> add_key;
-    add_key.SetQueryStr(query_str);
-    resource_request->url = loader->url.ReplaceComponents(add_key);
-  } else {
-    resource_request->url = loader->url;
-    resource_request->headers.SetHeader(
-        "Authorization", base::StrCat({"Bearer ", access_token_}));
-  }
-
-  loader->loader = ::network::SimpleURLLoader::Create(
-      std::move(resource_request), traffic_annotation);
-  loader->loader->AttachStringForUpload(loader->request_body,
-                                        "application/x-protobuffer");
-#ifdef DEBUG
-  loader->loader->SetAllowHttpErrorResults(true);
-#endif
-  loader->loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
-      content::BrowserContext::GetDefaultStoragePartition(context_)
-          ->GetURLLoaderFactoryForBrowserProcess()
-          .get(),
-      base::BindOnce(&ServiceImpl::OnURLLoaderComplete, base::Unretained(this),
-                     loader));
-}
-
-void ServiceImpl::OnURLLoaderComplete(
-    Loader* loader,
-    std::unique_ptr<std::string> response_body) {
-  auto loader_it = loaders_.find(loader);
-  DCHECK(loader_it != loaders_.end());
-
-  int response_code = 0;
-  if (loader->loader->ResponseInfo() &&
-      loader->loader->ResponseInfo()->headers) {
-    response_code = loader->loader->ResponseInfo()->headers->response_code();
-  }
-
-  // When getting a 401, refresh the auth token - but only try this once.
-  if (response_code == 401 && auth_enabled_ && !access_token_.empty() &&
-      !loader->retried_with_fresh_access_token) {
-    loader->retried_with_fresh_access_token = true;
-    loader->loader.reset();
-    // Invalidate access token and load a new one.
-    access_token_fetcher_->InvalidateAccessToken(access_token_);
-    access_token_.clear();
-    SendRequest(loader);
-    return;
-  }
-
-  // Take ownership of loader.
-  std::unique_ptr<Loader> loader_instance = std::move(loader_it->second);
-  loaders_.erase(loader_it);
-  DCHECK(loader_instance);
-
-  std::string response_body_str;
-  if (loader_instance->loader->NetError() != net::OK || response_code != 200) {
-    LOG(ERROR) << "Communicating with autofill assistant server error NetError="
-               << loader_instance->loader->NetError()
-               << " response_code=" << response_code << " message="
-               << (response_body == nullptr ? "" : *response_body);
-    // TODO(crbug.com/806868): Pass an enum to be able to distinguish errors
-    // downstream. Also introduce a metric for this.
-    std::move(loader_instance->callback).Run(false, response_body_str);
-    return;
-  }
-
-  if (response_body)
-    response_body_str = std::move(*response_body);
-  std::move(loader_instance->callback).Run(true, response_body_str);
-}
-
-void ServiceImpl::FetchAccessToken() {
-  if (fetching_token_)
-    return;
-
-  fetching_token_ = true;
-  access_token_fetcher_->FetchAccessToken(base::BindOnce(
-      &ServiceImpl::OnFetchAccessToken, weak_ptr_factory_.GetWeakPtr()));
-}
-
-void ServiceImpl::OnFetchAccessToken(bool success,
-                                     const std::string& access_token) {
-  fetching_token_ = false;
-
-  if (!success) {
-    auth_enabled_ = false;
-    // Give up on authentication for this run. Let the pending requests through,
-    // which might be rejected, depending on the server configuration.
-    return;
-  }
-
-  access_token_ = access_token;
-
-  // Start any pending requests with the access token.
-  for (const auto& entry : loaders_) {
-    StartLoader(entry.first);
-  }
+      std::move(callback));
 }
 
 }  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/service/service_impl.h b/components/autofill_assistant/browser/service/service_impl.h
index ff8a208..64b53f2 100644
--- a/components/autofill_assistant/browser/service/service_impl.h
+++ b/components/autofill_assistant/browser/service/service_impl.h
@@ -17,6 +17,7 @@
 #include "components/autofill_assistant/browser/service.pb.h"
 #include "components/autofill_assistant/browser/service/access_token_fetcher.h"
 #include "components/autofill_assistant/browser/service/service.h"
+#include "components/autofill_assistant/browser/service/service_request_sender.h"
 #include "components/signin/public/identity_manager/access_token_fetcher.h"
 #include "google_apis/gaia/google_service_auth_error.h"
 #include "services/network/public/cpp/simple_url_loader.h"
@@ -43,15 +44,12 @@
 
   bool IsLiteService() const override;
 
-  // |context| and |access_token_fetcher| must remain valid for the lifetime of
-  // the service instance.
-  ServiceImpl(const std::string& api_key,
+  ServiceImpl(std::unique_ptr<ServiceRequestSender> request_sender,
               const GURL& script_server_url,
               const GURL& action_server_url,
-              content::BrowserContext* context,
-              std::unique_ptr<ClientContext> client_context,
-              AccessTokenFetcher* access_token_fetcher,
-              bool auth_enabled);
+              std::unique_ptr<ClientContext> client_context);
+  ServiceImpl(const ServiceImpl&) = delete;
+  ServiceImpl& operator=(const ServiceImpl&) = delete;
   ~ServiceImpl() override;
 
   // Get scripts for a given |url|, which should be a valid URL.
@@ -78,69 +76,17 @@
       ResponseCallback callback) override;
 
  private:
-  friend class ServiceImplTest;
+  // The request sender responsible for communicating with a remote endpoint.
+  std::unique_ptr<ServiceRequestSender> request_sender_;
 
-  // Struct to store scripts and actions request.
-  struct Loader {
-    Loader();
-    ~Loader();
-
-    GURL url;
-    std::string request_body;
-    ResponseCallback callback;
-    std::unique_ptr<::network::SimpleURLLoader> loader;
-    bool retried_with_fresh_access_token;
-  };
-
-  void SendRequest(Loader* loader);
-
-  // Creates a loader and adds it to |loaders_|.
-  Loader* AddLoader(const GURL& url,
-                    const std::string& request_body,
-                    ResponseCallback callback);
-
-  // Sends a request with the given loader, using the current auth token, if one
-  // is available.
-  void StartLoader(Loader* loader);
-  void OnURLLoaderComplete(Loader* loader,
-                           std::unique_ptr<std::string> response_body);
-
-  // Fetches the access token and, once this is done, starts all pending loaders
-  // in |loaders_|.
-  void FetchAccessToken();
-  void OnFetchAccessToken(bool success, const std::string& access_token);
-
-  content::BrowserContext* context_;
+  // The RPC endpoints to send requests to.
   GURL script_server_url_;
   GURL script_action_server_url_;
 
-  // Destroying this object will cancel ongoing requests.
-  std::map<Loader*, std::unique_ptr<Loader>> loaders_;
-
-  // API key to add to the URL of unauthenticated requests.
-  std::string api_key_;
-
   // The client context to send to the backend.
   std::unique_ptr<ClientContext> client_context_;
 
-  // Pointer must remain valid for the lifetime of the Service instance.
-  AccessTokenFetcher* access_token_fetcher_;
-
-  // True while waiting for a response from AccessTokenFetcher.
-  bool fetching_token_ = false;
-
-  // Whether requests should be authenticated.
-  bool auth_enabled_ = true;
-
-  // An OAuth 2 token. Empty if not fetched yet or if the token has been
-  // invalidated.
-  std::string access_token_;
-
   base::WeakPtrFactory<ServiceImpl> weak_ptr_factory_{this};
-
-  FRIEND_TEST_ALL_PREFIXES(ServiceImplTestSignedInStatus, SetsSignedInStatus);
-
-  DISALLOW_COPY_AND_ASSIGN(ServiceImpl);
 };
 
 }  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/service/service_impl_unittest.cc b/components/autofill_assistant/browser/service/service_impl_unittest.cc
new file mode 100644
index 0000000..c95f4637
--- /dev/null
+++ b/components/autofill_assistant/browser/service/service_impl_unittest.cc
@@ -0,0 +1,101 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill_assistant/browser/service/service_impl.h"
+
+#include "components/autofill_assistant/browser/mock_client_context.h"
+#include "components/autofill_assistant/browser/service/mock_service_request_sender.h"
+#include "components/autofill_assistant/browser/service/service.h"
+
+#include "base/test/gmock_callback_support.h"
+#include "base/test/mock_callback.h"
+#include "net/http/http_status_code.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace autofill_assistant {
+
+using ::base::test::RunOnceCallback;
+using ::testing::_;
+using ::testing::NiceMock;
+using ::testing::Return;
+
+namespace {
+
+const char kScriptServerUrl[] = "https://www.fake.backend.com/script_server";
+const char kActionServerUrl[] = "https://www.fake.backend.com/action_server";
+
+class ServiceImplTest : public testing::Test {
+ public:
+  ServiceImplTest() {
+    auto mock_client_context = std::make_unique<NiceMock<MockClientContext>>();
+    mock_client_context_ = mock_client_context.get();
+
+    auto mock_request_sender =
+        std::make_unique<NiceMock<MockServiceRequestSender>>();
+    mock_request_sender_ = mock_request_sender.get();
+
+    service_ = std::make_unique<ServiceImpl>(
+        std::move(mock_request_sender), GURL(kScriptServerUrl),
+        GURL(kActionServerUrl), std::move(mock_client_context));
+  }
+  ~ServiceImplTest() override = default;
+
+ protected:
+  base::MockCallback<Service::ResponseCallback> mock_response_callback_;
+  NiceMock<MockClientContext>* mock_client_context_;
+  NiceMock<MockServiceRequestSender>* mock_request_sender_;
+  std::unique_ptr<ServiceImpl> service_;
+};
+
+TEST_F(ServiceImplTest, GetScriptsForUrl) {
+  EXPECT_CALL(*mock_client_context_, Update).Times(1);
+  // TODO(b/158998456), here and in other tests of service_impl: check that
+  // protocol utils is called with the correct parameters.
+  EXPECT_CALL(*mock_request_sender_,
+              OnSendRequest(GURL(kScriptServerUrl), _, _))
+      .WillOnce(RunOnceCallback<2>(net::HTTP_OK, std::string("response")));
+  EXPECT_CALL(mock_response_callback_,
+              Run(net::HTTP_OK, std::string("response")))
+      .Times(1);
+
+  TriggerContextImpl trigger_context;
+  service_->GetScriptsForUrl(GURL("https://www.example.com"), trigger_context,
+                             mock_response_callback_.Get());
+}
+
+TEST_F(ServiceImplTest, GetActions) {
+  EXPECT_CALL(*mock_client_context_, Update).Times(1);
+  EXPECT_CALL(*mock_request_sender_,
+              OnSendRequest(GURL(kActionServerUrl), _, _))
+      .WillOnce(RunOnceCallback<2>(net::HTTP_OK, std::string("response")));
+  EXPECT_CALL(mock_response_callback_,
+              Run(net::HTTP_OK, std::string("response")))
+      .Times(1);
+
+  TriggerContextImpl trigger_context;
+  service_->GetActions(
+      std::string("fake_script_path"), GURL("https://www.example.com"),
+      trigger_context, std::string("fake_global_payload"),
+      std::string("fake_script_payload"), mock_response_callback_.Get());
+}
+
+TEST_F(ServiceImplTest, GetNextActions) {
+  EXPECT_CALL(*mock_client_context_, Update).Times(1);
+  EXPECT_CALL(*mock_request_sender_,
+              OnSendRequest(GURL(kActionServerUrl), _, _))
+      .WillOnce(RunOnceCallback<2>(net::HTTP_OK, std::string("response")));
+  EXPECT_CALL(mock_response_callback_,
+              Run(net::HTTP_OK, std::string("response")))
+      .Times(1);
+
+  TriggerContextImpl trigger_context;
+  service_->GetNextActions(
+      trigger_context, std::string("fake_previous_global_payload"),
+      std::string("fake_previous_script_payload"), /* processed_actions = */ {},
+      /* timing_stats = */ RoundtripTimingStats(),
+      mock_response_callback_.Get());
+}
+
+}  // namespace
+}  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/service/service_request_sender.cc b/components/autofill_assistant/browser/service/service_request_sender.cc
new file mode 100644
index 0000000..4009e7ef
--- /dev/null
+++ b/components/autofill_assistant/browser/service/service_request_sender.cc
@@ -0,0 +1,211 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill_assistant/browser/service/service_request_sender.h"
+
+#include "base/strings/strcat.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/storage_partition.h"
+#include "net/base/load_flags.h"
+#include "net/http/http_request_headers.h"
+#include "net/http/http_status_code.h"
+#include "net/traffic_annotation/network_traffic_annotation.h"
+#include "services/network/public/cpp/resource_request.h"
+#include "services/network/public/cpp/simple_url_loader.h"
+
+namespace {
+
+constexpr net::NetworkTrafficAnnotationTag kTrafficAnnotation =
+    net::DefineNetworkTrafficAnnotation("autofill_service", R"(
+        semantics {
+          sender: "Autofill Assistant"
+          description:
+            "Chromium posts requests to autofill assistant server to get
+            scripts for a URL."
+          trigger:
+            "Matching URL."
+          data: "None."
+          destination: GOOGLE_OWNED_SERVICE
+        }
+        policy {
+          cookies_allowed: NO
+          setting:
+            "This feature can be disabled in settings."
+          policy_exception_justification: "Not implemented."
+        })");
+
+void OnURLLoaderComplete(
+    autofill_assistant::ServiceRequestSender::ResponseCallback callback,
+    std::unique_ptr<::network::SimpleURLLoader> loader,
+    std::unique_ptr<std::string> response_body) {
+  std::string response_str;
+  if (response_body != nullptr) {
+    response_str = std::move(*response_body);
+  }
+
+  int response_code = 0;
+  if (loader->ResponseInfo() && loader->ResponseInfo()->headers) {
+    response_code = loader->ResponseInfo()->headers->response_code();
+  }
+  std::move(callback).Run(response_code, response_str);
+}
+
+std::unique_ptr<::network::ResourceRequest> CreateResourceRequest(GURL url) {
+  auto resource_request = std::make_unique<::network::ResourceRequest>();
+  resource_request->url = url;
+  resource_request->method = "POST";
+  resource_request->redirect_mode = ::network::mojom::RedirectMode::kError;
+  resource_request->credentials_mode = ::network::mojom::CredentialsMode::kOmit;
+  return resource_request;
+}
+
+void SendRequestImpl(
+    std::unique_ptr<::network::ResourceRequest> request,
+    const std::string& request_body,
+    content::BrowserContext* context,
+    autofill_assistant::SimpleURLLoaderFactory* loader_factory,
+    autofill_assistant::ServiceRequestSender::ResponseCallback callback) {
+  auto loader =
+      loader_factory->CreateLoader(std::move(request), kTrafficAnnotation);
+  loader->AttachStringForUpload(request_body, "application/x-protobuffer");
+#ifdef DEBUG
+  loader->SetAllowHttpErrorResults(true);
+#endif
+  loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
+      content::BrowserContext::GetDefaultStoragePartition(context)
+          ->GetURLLoaderFactoryForBrowserProcess()
+          .get(),
+      base::BindOnce(&OnURLLoaderComplete, std::move(callback),
+                     std::move(loader)));
+}
+
+void SendRequestNoAuth(
+    const GURL& url,
+    const std::string& request_body,
+    content::BrowserContext* context,
+    autofill_assistant::SimpleURLLoaderFactory* loader_factory,
+    const std::string& api_key,
+    autofill_assistant::ServiceRequestSender::ResponseCallback callback) {
+  if (callback.IsCancelled()) {
+    return;
+  }
+  if (api_key.empty()) {
+    LOG(ERROR) << "no api key provided";
+    std::move(callback).Run(net::HTTP_UNAUTHORIZED, std::string());
+    return;
+  }
+
+  std::string query_str = base::StrCat({"key=", api_key});
+  // query_str must remain valid until ReplaceComponents() has returned.
+  url::StringPieceReplacements<std::string> add_key;
+  add_key.SetQueryStr(query_str);
+  GURL modified_url = url.ReplaceComponents(add_key);
+
+  SendRequestImpl(CreateResourceRequest(modified_url), request_body, context,
+                  loader_factory, std::move(callback));
+}
+
+}  // namespace
+
+namespace autofill_assistant {
+
+ServiceRequestSender::ServiceRequestSender(
+    content::BrowserContext* context,
+    AccessTokenFetcher* access_token_fetcher,
+    std::unique_ptr<SimpleURLLoaderFactory> loader_factory,
+    const std::string& api_key,
+    bool auth_enabled,
+    bool disable_auth_if_no_access_token)
+    : context_(context),
+      access_token_fetcher_(access_token_fetcher),
+      loader_factory_(std::move(loader_factory)),
+      api_key_(api_key),
+      auth_enabled_(auth_enabled),
+      disable_auth_if_no_access_token_(disable_auth_if_no_access_token) {
+  DCHECK(!auth_enabled || access_token_fetcher != nullptr);
+  DCHECK(auth_enabled || !api_key.empty());
+}
+ServiceRequestSender::~ServiceRequestSender() = default;
+
+void ServiceRequestSender::SendRequest(const GURL& url,
+                                       const std::string& request_body,
+                                       ResponseCallback callback) {
+  if (auth_enabled_ && access_token_fetcher_ == nullptr) {
+    LOG(ERROR) << "auth requested, but no access token fetcher provided";
+    std::move(callback).Run(net::HTTP_UNAUTHORIZED, std::string());
+    return;
+  }
+  if (auth_enabled_) {
+    access_token_fetcher_->FetchAccessToken(
+        base::BindOnce(&ServiceRequestSender::OnFetchAccessToken,
+                       weak_ptr_factory_.GetWeakPtr(), url, request_body,
+                       std::move(callback)));
+    return;
+  }
+
+  SendRequestNoAuth(url, request_body, context_, loader_factory_.get(),
+                    api_key_, std::move(callback));
+}
+
+void ServiceRequestSender::OnFetchAccessToken(GURL url,
+                                              std::string request_body,
+                                              ResponseCallback callback,
+                                              bool access_token_fetched,
+                                              const std::string& access_token) {
+  if (!access_token_fetched) {
+    if (disable_auth_if_no_access_token_) {
+      // Give up on authentication for this run. Without access token, requests
+      // might be successful or rejected, depending on the server configuration.
+      auth_enabled_ = false;
+      SendRequestNoAuth(url, request_body, context_, loader_factory_.get(),
+                        api_key_, std::move(callback));
+      return;
+    }
+    VLOG(1) << "Failed to fetch access token, but "
+               "disable_auth_if_no_access_token not set";
+    std::move(callback).Run(net::HTTP_UNAUTHORIZED, std::string());
+    return;
+  }
+
+  SendRequestAuth(url, request_body, access_token, std::move(callback));
+}
+
+void ServiceRequestSender::SendRequestAuth(const GURL& url,
+                                           const std::string& request_body,
+                                           const std::string& access_token,
+                                           ResponseCallback callback) {
+  if (callback.IsCancelled()) {
+    return;
+  }
+  auto resource_request = CreateResourceRequest(url);
+  resource_request->headers.SetHeader("Authorization",
+                                      base::StrCat({"Bearer ", access_token}));
+
+  if (!retried_with_fresh_access_token_) {
+    callback = base::BindOnce(&ServiceRequestSender::RetryIfUnauthorized,
+                              weak_ptr_factory_.GetWeakPtr(), url, access_token,
+                              request_body, std::move(callback));
+  }
+  SendRequestImpl(std::move(resource_request), request_body, context_,
+                  loader_factory_.get(), std::move(callback));
+}
+
+void ServiceRequestSender::RetryIfUnauthorized(const GURL& url,
+                                               const std::string& access_token,
+                                               const std::string& request_body,
+                                               ResponseCallback callback,
+                                               int http_status,
+                                               const std::string& response) {
+  // On first UNAUTHORIZED error, invalidate access token and try again.
+  if (auth_enabled_ && http_status == net::HTTP_UNAUTHORIZED) {
+    DCHECK(!retried_with_fresh_access_token_);
+    retried_with_fresh_access_token_ = true;
+    access_token_fetcher_->InvalidateAccessToken(access_token);
+    SendRequest(url, request_body, std::move(callback));
+    return;
+  }
+  std::move(callback).Run(http_status, response);
+}
+
+}  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/service/service_request_sender.h b/components/autofill_assistant/browser/service/service_request_sender.h
new file mode 100644
index 0000000..7754e666
--- /dev/null
+++ b/components/autofill_assistant/browser/service/service_request_sender.h
@@ -0,0 +1,93 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_SERVICE_SERVICE_REQUEST_SENDER_H_
+#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_SERVICE_SERVICE_REQUEST_SENDER_H_
+
+#include <memory>
+#include <string>
+
+#include "base/callback.h"
+#include "base/memory/weak_ptr.h"
+#include "components/autofill_assistant/browser/service/access_token_fetcher.h"
+#include "components/autofill_assistant/browser/service/simple_url_loader_factory.h"
+#include "content/public/browser/browser_context.h"
+#include "url/gurl.h"
+
+namespace content {
+class BrowserContext;
+}  // namespace content
+
+namespace autofill_assistant {
+
+class ServiceRequestSender {
+ public:
+  using ResponseCallback =
+      base::OnceCallback<void(int http_status, const std::string& response)>;
+
+  // Constructor. |access_token_fetcher| is optional if |auth_enabled| is false.
+  // Pointers to |context| and, if provided, |access_token_fetcher| must remain
+  // valid during the lifetime of this instance.
+
+  // If |disable_auth_if_no_access_token| is true, authentication will
+  // automatically be disabled in case fetching the access token fails.
+  ServiceRequestSender(content::BrowserContext* context,
+                       AccessTokenFetcher* access_token_fetcher,
+                       std::unique_ptr<SimpleURLLoaderFactory> loader_factory,
+                       const std::string& api_key,
+                       bool auth_enabled,
+                       bool disable_auth_if_no_access_token);
+  virtual ~ServiceRequestSender();
+  ServiceRequestSender(const ServiceRequestSender&) = delete;
+  ServiceRequestSender& operator=(const ServiceRequestSender&) = delete;
+
+  // Sends |request_body| to |url|. Depending on configuration, the request
+  // will be authenticated either with an Oauth access token or the api key.
+  // Returns the http status code and the response itself. If the returned http
+  // headers could not be parsed, the http code will be 0.
+  //
+  // When an auth-request first fails with a 401, the access token is
+  // invalidated and fetched again. If the request fails again, the request
+  // is considered failed and the callback is invoked.
+  virtual void SendRequest(const GURL& url,
+                           const std::string& request_body,
+                           ResponseCallback callback);
+
+ private:
+  void SendRequestAuth(const GURL& url,
+                       const std::string& request_body,
+                       const std::string& access_token,
+                       ResponseCallback callback);
+
+  void RetryIfUnauthorized(const GURL& url,
+                           const std::string& access_token,
+                           const std::string& request_body,
+                           ResponseCallback callback,
+                           int http_status,
+                           const std::string& response);
+
+  void OnFetchAccessToken(GURL url,
+                          std::string request_body,
+                          ResponseCallback callback,
+                          bool access_token_fetched,
+                          const std::string& access_token);
+
+  content::BrowserContext* context_ = nullptr;
+  AccessTokenFetcher* access_token_fetcher_ = nullptr;
+  std::unique_ptr<SimpleURLLoaderFactory> loader_factory_;
+
+  // API key to add to the URL of unauthenticated requests.
+  std::string api_key_;
+
+  // Whether requests should be authenticated.
+  bool auth_enabled_ = true;
+
+  bool disable_auth_if_no_access_token_ = true;
+  bool retried_with_fresh_access_token_ = false;
+  base::WeakPtrFactory<ServiceRequestSender> weak_ptr_factory_{this};
+};
+
+}  // namespace autofill_assistant
+
+#endif  // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_SERVICE_SERVICE_REQUEST_SENDER_H_
diff --git a/components/autofill_assistant/browser/service/service_request_sender_unittest.cc b/components/autofill_assistant/browser/service/service_request_sender_unittest.cc
new file mode 100644
index 0000000..d6990e0
--- /dev/null
+++ b/components/autofill_assistant/browser/service/service_request_sender_unittest.cc
@@ -0,0 +1,148 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill_assistant/browser/service/service_request_sender.h"
+
+#include <memory>
+#include <vector>
+
+#include "base/memory/scoped_refptr.h"
+#include "base/strings/strcat.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/test/gmock_callback_support.h"
+#include "base/test/mock_callback.h"
+#include "components/autofill_assistant/browser/service/access_token_fetcher.h"
+#include "components/autofill_assistant/browser/service/mock_access_token_fetcher.h"
+#include "components/autofill_assistant/browser/service/mock_simple_url_loader_factory.h"
+#include "components/autofill_assistant/browser/service/mock_url_loader.h"
+#include "content/public/test/browser_task_environment.h"
+#include "content/public/test/test_browser_context.h"
+#include "net/http/http_response_headers.h"
+#include "net/http/http_status_code.h"
+#include "net/traffic_annotation/network_traffic_annotation.h"
+#include "services/network/public/cpp/resource_request.h"
+#include "services/network/public/cpp/simple_url_loader.h"
+#include "services/network/public/mojom/url_response_head.mojom.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace autofill_assistant {
+
+using ::base::test::RunOnceCallback;
+using ::testing::_;
+using ::testing::NiceMock;
+using ::testing::Return;
+using ::testing::WithArgs;
+
+namespace {
+
+std::unique_ptr<::network::mojom::URLResponseHead> CreateResponseInfo(
+    int status_code,
+    const std::string& status) {
+  auto response = std::make_unique<::network::mojom::URLResponseHead>();
+  response->headers =
+      base::MakeRefCounted<::net::HttpResponseHeaders>(base::StrCat(
+          {"HTTP/1.1 ", base::NumberToString(status_code), " ", status}));
+  return response;
+}
+
+class ServiceRequestSenderTest : public testing::Test {
+ public:
+  ServiceRequestSenderTest() = default;
+  ~ServiceRequestSenderTest() override = default;
+
+ protected:
+  base::MockCallback<base::OnceCallback<void(int, const std::string&)>>
+      mock_response_callback_;
+  // Note: |task_environment_| must be created before |context_|, else creation
+  // of |context_| will fail (see content/public/test/test_browser_context.cc).
+  content::BrowserTaskEnvironment task_environment_;
+  content::TestBrowserContext context_;
+  NiceMock<MockAccessTokenFetcher> mock_access_token_fetcher_;
+};
+
+TEST_F(ServiceRequestSenderTest, SendUnauthenticatedRequest) {
+  auto loader_factory =
+      std::make_unique<NiceMock<MockSimpleURLLoaderFactory>>();
+  auto loader = std::make_unique<NiceMock<MockURLLoader>>();
+  auto response_info = CreateResponseInfo(net::HTTP_OK, "OK");
+  EXPECT_CALL(*loader_factory, OnCreateLoader(_, _))
+      .WillOnce([&](::network::ResourceRequest* resource_request,
+                    const ::net::NetworkTrafficAnnotationTag& annotation_tag) {
+        EXPECT_FALSE(resource_request->headers.HasHeader("Authorization"));
+        EXPECT_EQ(resource_request->url,
+                  GURL("https://www.example.com/?key=fake_api_key"));
+        return std::move(loader);
+      });
+  EXPECT_CALL(*loader,
+              AttachStringForUpload(std::string("request"),
+                                    std::string("application/x-protobuffer")))
+      .Times(1);
+  EXPECT_CALL(*loader, DownloadToStringOfUnboundedSizeUntilCrashAndDie(_, _))
+      .WillOnce(WithArgs<1>([&](auto&& callback) {
+        std::move(callback).Run(std::make_unique<std::string>("response"));
+      }));
+  EXPECT_CALL(*loader, ResponseInfo)
+      .WillRepeatedly(Return(response_info.get()));
+
+  EXPECT_CALL(mock_response_callback_, Run(net::HTTP_OK, "response"));
+  ServiceRequestSender request_sender{
+      &context_,
+      /* access_token_fetcher = */ nullptr,
+      std::move(loader_factory),
+      std::string("fake_api_key"),
+      /* auth_enabled = */ false,
+      /* disable_auth_if_no_access_token = */ true};
+  request_sender.SendRequest(GURL("https://www.example.com"),
+                             std::string("request"),
+                             mock_response_callback_.Get());
+}
+
+TEST_F(ServiceRequestSenderTest, SendAuthenticatedRequest) {
+  auto loader_factory =
+      std::make_unique<NiceMock<MockSimpleURLLoaderFactory>>();
+  auto loader = std::make_unique<NiceMock<MockURLLoader>>();
+  auto response_info = CreateResponseInfo(net::HTTP_OK, "OK");
+  EXPECT_CALL(*loader_factory, OnCreateLoader(_, _))
+      .WillOnce([&](::network::ResourceRequest* resource_request,
+                    const ::net::NetworkTrafficAnnotationTag& annotation_tag) {
+        std::string authorization;
+        EXPECT_TRUE(resource_request->headers.GetHeader("Authorization",
+                                                        &authorization));
+        EXPECT_EQ(authorization, "Bearer access_token");
+        EXPECT_EQ(resource_request->url, GURL("https://www.example.com"));
+        return std::move(loader);
+      });
+  EXPECT_CALL(*loader,
+              AttachStringForUpload(std::string("request"),
+                                    std::string("application/x-protobuffer")))
+      .Times(1);
+  EXPECT_CALL(*loader, DownloadToStringOfUnboundedSizeUntilCrashAndDie(_, _))
+      .WillOnce(WithArgs<1>([&](auto&& callback) {
+        std::move(callback).Run(std::make_unique<std::string>("response"));
+      }));
+  EXPECT_CALL(*loader, ResponseInfo)
+      .WillRepeatedly(Return(response_info.get()));
+  EXPECT_CALL(mock_access_token_fetcher_, OnFetchAccessToken)
+      .Times(1)
+      .WillOnce(RunOnceCallback<0>(true, "access_token"));
+  EXPECT_CALL(mock_access_token_fetcher_, InvalidateAccessToken).Times(0);
+
+  EXPECT_CALL(mock_response_callback_, Run(net::HTTP_OK, "response"));
+  ServiceRequestSender request_sender{
+      &context_,
+      /* access_token_fetcher = */ &mock_access_token_fetcher_,
+      std::move(loader_factory),
+      /* api_key = */ std::string(""),
+      /* auth_enabled = */ true,
+      /* disable_auth_if_no_access_token = */ true};
+  request_sender.SendRequest(GURL("https://www.example.com"),
+                             std::string("request"),
+                             mock_response_callback_.Get());
+}
+
+// TODO(b/170934170): Add tests for full unit test coverage of
+// service_request_sender.
+
+}  // namespace
+}  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/service/simple_url_loader_factory.cc b/components/autofill_assistant/browser/service/simple_url_loader_factory.cc
new file mode 100644
index 0000000..00a4c05
--- /dev/null
+++ b/components/autofill_assistant/browser/service/simple_url_loader_factory.cc
@@ -0,0 +1,17 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill_assistant/browser/service/simple_url_loader_factory.h"
+
+namespace autofill_assistant {
+
+std::unique_ptr<::network::SimpleURLLoader>
+NativeURLLoaderFactory::CreateLoader(
+    std::unique_ptr<::network::ResourceRequest> resource_request,
+    const ::net::NetworkTrafficAnnotationTag& annotation_tag) const {
+  return ::network::SimpleURLLoader::Create(std::move(resource_request),
+                                            annotation_tag);
+}
+
+}  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/service/simple_url_loader_factory.h b/components/autofill_assistant/browser/service/simple_url_loader_factory.h
new file mode 100644
index 0000000..b23ce8b
--- /dev/null
+++ b/components/autofill_assistant/browser/service/simple_url_loader_factory.h
@@ -0,0 +1,41 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_SERVICE_SIMPLE_URL_LOADER_FACTORY_H_
+#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_SERVICE_SIMPLE_URL_LOADER_FACTORY_H_
+
+#include <memory>
+
+#include "net/traffic_annotation/network_traffic_annotation.h"
+#include "services/network/public/cpp/resource_request.h"
+#include "services/network/public/cpp/simple_url_loader.h"
+
+namespace autofill_assistant {
+
+// Base interface for creators of URL loaders.
+class SimpleURLLoaderFactory {
+ public:
+  virtual ~SimpleURLLoaderFactory() = default;
+
+  virtual std::unique_ptr<::network::SimpleURLLoader> CreateLoader(
+      std::unique_ptr<::network::ResourceRequest> resource_request,
+      const ::net::NetworkTrafficAnnotationTag& annotation_tag) const = 0;
+};
+
+// The native implementation of |SimpleURLLoaderFactory|.
+class NativeURLLoaderFactory : public SimpleURLLoaderFactory {
+ public:
+  NativeURLLoaderFactory() = default;
+  ~NativeURLLoaderFactory() override = default;
+  NativeURLLoaderFactory(const NativeURLLoaderFactory&) = delete;
+  NativeURLLoaderFactory& operator=(const NativeURLLoaderFactory&) = delete;
+
+  std::unique_ptr<::network::SimpleURLLoader> CreateLoader(
+      std::unique_ptr<::network::ResourceRequest> resource_request,
+      const ::net::NetworkTrafficAnnotationTag& annotation_tag) const override;
+};
+
+}  //  namespace autofill_assistant
+
+#endif  // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_SERVICE_SIMPLE_URL_LOADER_FACTORY_H_
diff --git a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonWithDescription.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonWithDescription.java
index 530862c..143a520 100644
--- a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonWithDescription.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonWithDescription.java
@@ -57,7 +57,7 @@
     /**
      * Interface to listen to radio button changes.
      */
-    interface ButtonCheckedStateChangedListener {
+    public interface ButtonCheckedStateChangedListener {
         /**
          * Invoked when a {@link RadioButtonWithDescription} is selected.
          * @param checkedRadioButton The radio button that was selected.
diff --git a/components/component_updater/component_updater_service.h b/components/component_updater/component_updater_service.h
index f926034a2..a9f9307 100644
--- a/components/component_updater/component_updater_service.h
+++ b/components/component_updater/component_updater_service.h
@@ -27,7 +27,7 @@
 }
 
 namespace settings {
-class AccessibilityMainHandler;
+class CaptionsHandler;
 }
 
 namespace update_client {
@@ -147,7 +147,7 @@
   virtual bool GetComponentDetails(const std::string& id,
                                    CrxUpdateItem* item) const = 0;
 
-  friend class settings::AccessibilityMainHandler;
+  friend class settings::CaptionsHandler;
   friend class ::ComponentsHandler;
   FRIEND_TEST_ALL_PREFIXES(ComponentInstallerTest, RegisterComponent);
 };
diff --git a/components/embedder_support/android/metrics/android_metrics_service_client.cc b/components/embedder_support/android/metrics/android_metrics_service_client.cc
index c4508da..b59d1ce 100644
--- a/components/embedder_support/android/metrics/android_metrics_service_client.cc
+++ b/components/embedder_support/android/metrics/android_metrics_service_client.cc
@@ -190,6 +190,20 @@
   return file_metrics_provider;
 }
 
+base::OnceClosure CreateChainedClosure(base::OnceClosure cb1,
+                                       base::OnceClosure cb2) {
+  return base::BindOnce(
+      [](base::OnceClosure cb1, base::OnceClosure cb2) {
+        if (cb1) {
+          std::move(cb1).Run();
+        }
+        if (cb2) {
+          std::move(cb2).Run();
+        }
+      },
+      std::move(cb1), std::move(cb2));
+}
+
 }  // namespace
 
 // Needs to be kept in sync with the writer in
@@ -442,8 +456,11 @@
   // Set up the callback task to call after we receive histograms from all
   // child processes. |timeout| specifies how long to wait before absolutely
   // calling us back on the task.
-  content::FetchHistogramsAsynchronously(base::ThreadTaskRunnerHandle::Get(),
-                                         std::move(done_callback), timeout);
+  content::FetchHistogramsAsynchronously(
+      base::ThreadTaskRunnerHandle::Get(),
+      CreateChainedClosure(std::move(done_callback),
+                           on_final_metrics_collected_listener_),
+      timeout);
 
   if (collect_final_metrics_for_log_closure_)
     std::move(collect_final_metrics_for_log_closure_).Run();
@@ -519,6 +536,11 @@
   collect_final_metrics_for_log_closure_ = std::move(closure);
 }
 
+void AndroidMetricsServiceClient::SetOnFinalMetricsCollectedListenerForTesting(
+    base::RepeatingClosure listener) {
+  on_final_metrics_collected_listener_ = std::move(listener);
+}
+
 int AndroidMetricsServiceClient::GetSampleBucketValue() {
   return UintToPerMille(base::PersistentHash(metrics_service_->GetClientId()));
 }
diff --git a/components/embedder_support/android/metrics/android_metrics_service_client.h b/components/embedder_support/android/metrics/android_metrics_service_client.h
index d0edeeae..7d6a9f6f 100644
--- a/components/embedder_support/android/metrics/android_metrics_service_client.h
+++ b/components/embedder_support/android/metrics/android_metrics_service_client.h
@@ -154,9 +154,14 @@
                const content::NotificationSource& source,
                const content::NotificationDetails& details) override;
 
-  // Runs |closure| when CollectFinalMetricsForLog() is called.
+  // Runs |closure| when CollectFinalMetricsForLog() is called, when we begin
+  // collecting final metrics.
   void SetCollectFinalMetricsForLogClosureForTesting(base::OnceClosure closure);
 
+  // Runs |listener| after all final metrics have been collected.
+  void SetOnFinalMetricsCollectedListenerForTesting(
+      base::RepeatingClosure listener);
+
   metrics::MetricsStateManager* metrics_state_manager() const {
     return metrics_state_manager_.get();
   }
@@ -256,6 +261,7 @@
   base::TimeDelta overridden_upload_interval_;
 
   base::OnceClosure collect_final_metrics_for_log_closure_;
+  base::RepeatingClosure on_final_metrics_collected_listener_;
 
   // MetricsServiceClient may be created before the UI thread is promoted to
   // BrowserThread::UI. Use |sequence_checker_| to enforce that the
diff --git a/components/exo/DEPS b/components/exo/DEPS
index 9b0c1e4..948a6a70 100644
--- a/components/exo/DEPS
+++ b/components/exo/DEPS
@@ -4,8 +4,9 @@
   "+chromeos/audio/chromeos_sounds.h",
   "+chromeos/constants/chromeos_features.h",
   "+chromeos/crosapi/cpp/crosapi_constants.h",
-  "+chromeos/ui/base/window_state_type.h",
+  "+chromeos/ui/base",
   "+chromeos/ui/frame/caption_buttons",
+  "+chromeos/ui/frame/immersive",
   "+components/viz/common",
   "+components/viz/host",
   "+device/gamepad",
diff --git a/components/exo/client_controlled_shell_surface.cc b/components/exo/client_controlled_shell_surface.cc
index 5bbc164..99bbca0 100644
--- a/components/exo/client_controlled_shell_surface.cc
+++ b/components/exo/client_controlled_shell_surface.cc
@@ -12,7 +12,6 @@
 #include "ash/frame/wide_frame_view.h"
 #include "ash/public/cpp/ash_features.h"
 #include "ash/public/cpp/default_frame_header.h"
-#include "ash/public/cpp/immersive/immersive_fullscreen_controller.h"
 #include "ash/public/cpp/rounded_corner_decorator.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/public/cpp/window_backdrop.h"
@@ -39,6 +38,7 @@
 #include "base/trace_event/traced_value.h"
 #include "chromeos/ui/base/window_state_type.h"
 #include "chromeos/ui/frame/caption_buttons/caption_button_model.h"
+#include "chromeos/ui/frame/immersive/immersive_fullscreen_controller.h"
 #include "components/exo/shell_surface_util.h"
 #include "components/exo/surface.h"
 #include "components/exo/wm_helper.h"
@@ -567,7 +567,7 @@
     bool enabled = (frame_type_ == SurfaceFrameType::AUTOHIDE &&
                     (GetWindowState()->IsMaximizedOrFullscreenOrPinned() ||
                      GetWindowState()->IsSnapped()));
-    ash::ImmersiveFullscreenController::EnableForWidget(widget_, enabled);
+    chromeos::ImmersiveFullscreenController::EnableForWidget(widget_, enabled);
   }
 }
 
@@ -798,7 +798,7 @@
   auto frame_view =
       CreateNonClientFrameViewInternal(widget, /*client_controlled=*/true);
   immersive_fullscreen_controller_ =
-      std::make_unique<ash::ImmersiveFullscreenController>();
+      std::make_unique<chromeos::ImmersiveFullscreenController>();
   static_cast<ash::NonClientFrameViewAsh*>(frame_view.get())
       ->InitImmersiveFullscreenControllerForView(
           immersive_fullscreen_controller_.get());
@@ -1259,7 +1259,7 @@
     if (!wide_frame_) {
       update_frame = true;
       wide_frame_ = std::make_unique<ash::WideFrameView>(widget_);
-      ash::ImmersiveFullscreenController::EnableForWidget(widget_, false);
+      chromeos::ImmersiveFullscreenController::EnableForWidget(widget_, false);
       wide_frame_->Init(immersive_fullscreen_controller_.get());
       wide_frame_->header_view()->GetFrameHeader()->SetFrameTextOverride(
           GetFrameView()
@@ -1278,7 +1278,7 @@
   } else {
     if (wide_frame_) {
       update_frame = true;
-      ash::ImmersiveFullscreenController::EnableForWidget(widget_, false);
+      chromeos::ImmersiveFullscreenController::EnableForWidget(widget_, false);
       wide_frame_.reset();
       GetFrameView()->InitImmersiveFullscreenControllerForView(
           immersive_fullscreen_controller_.get());
diff --git a/components/exo/client_controlled_shell_surface.h b/components/exo/client_controlled_shell_surface.h
index c321ec7..3dcb07e 100644
--- a/components/exo/client_controlled_shell_surface.h
+++ b/components/exo/client_controlled_shell_surface.h
@@ -19,7 +19,6 @@
 
 namespace ash {
 class NonClientFrameViewAsh;
-class ImmersiveFullscreenController;
 class RoundedCornerDecorator;
 class WideFrameView;
 
@@ -28,6 +27,10 @@
 }
 }  // namespace ash
 
+namespace chromeos {
+class ImmersiveFullscreenController;
+}  // namespace chromeos
+
 namespace exo {
 class Surface;
 class ClientControlledAcceleratorTarget;
@@ -351,7 +354,7 @@
 
   bool can_maximize_ = true;
 
-  std::unique_ptr<ash::ImmersiveFullscreenController>
+  std::unique_ptr<chromeos::ImmersiveFullscreenController>
       immersive_fullscreen_controller_;
 
   std::unique_ptr<ash::WideFrameView> wide_frame_;
diff --git a/components/exo/shell_surface_base.cc b/components/exo/shell_surface_base.cc
index 4367cbcd..40b08f2 100644
--- a/components/exo/shell_surface_base.cc
+++ b/components/exo/shell_surface_base.cc
@@ -26,6 +26,7 @@
 #include "base/trace_event/traced_value.h"
 #include "cc/trees/layer_tree_frame_sink.h"
 #include "chromeos/crosapi/cpp/crosapi_constants.h"
+#include "chromeos/ui/base//window_properties.h"
 #include "chromeos/ui/base/window_state_type.h"
 #include "components/exo/shell_surface_util.h"
 #include "components/exo/surface.h"
@@ -1084,7 +1085,7 @@
                                                    bool client_controlled) {
   aura::Window* window = widget_->GetNativeWindow();
   // ShellSurfaces always use immersive mode.
-  window->SetProperty(ash::kImmersiveIsActive, true);
+  window->SetProperty(chromeos::kImmersiveIsActive, true);
   ash::WindowState* window_state = ash::WindowState::Get(window);
   if (!frame_enabled() && !window_state->HasDelegate()) {
     window_state->SetDelegate(std::make_unique<CustomWindowStateDelegate>());
diff --git a/components/exo/shell_surface_util.cc b/components/exo/shell_surface_util.cc
index 960f92e..271c9e0 100644
--- a/components/exo/shell_surface_util.cc
+++ b/components/exo/shell_surface_util.cc
@@ -24,6 +24,7 @@
 
 #if defined(OS_CHROMEOS)
 #include "ash/public/cpp/window_properties.h"
+#include "chromeos/ui/base/window_properties.h"
 #endif  // defined(OS_CHROMEOS)
 
 DEFINE_UI_CLASS_PROPERTY_TYPE(exo::Permission*)
@@ -118,7 +119,7 @@
 
 void SetShellUseImmersiveForFullscreen(aura::Window* window, bool value) {
 #if defined(OS_CHROMEOS)
-  window->SetProperty(ash::kImmersiveImpliedByFullscreen, value);
+  window->SetProperty(chromeos::kImmersiveImpliedByFullscreen, value);
 
   // Ensure the shelf is fully hidden in plain fullscreen, but shown
   // (auto-hides based on mouse movement) when in immersive fullscreen.
diff --git a/components/exo/surface.cc b/components/exo/surface.cc
index fb9675ed..53b0425 100644
--- a/components/exo/surface.cc
+++ b/components/exo/surface.cc
@@ -1108,7 +1108,7 @@
   quad_state->SetAll(
       quad_to_target_transform, quad_rect /*quad_layer_rect=*/,
       quad_rect /*visible_quad_layer_rect=*/,
-      gfx::RRectF() /*rounded_corner_bounds=*/, gfx::Rect() /*clip_rect=*/,
+      gfx::MaskFilterInfo() /*mask_filter_info=*/, gfx::Rect() /*clip_rect=*/,
       false /*is_clipped=*/, are_contents_opaque, state_.alpha /*opacity=*/,
       SkBlendMode::kSrcOver /*blend_mode=*/, 0 /*sorting_context_id=*/);
   quad_state->no_damage = damage_rect.IsEmpty();
diff --git a/components/exo/surface_tree_host.cc b/components/exo/surface_tree_host.cc
index ceb5e06a..d125275 100644
--- a/components/exo/surface_tree_host.cc
+++ b/components/exo/surface_tree_host.cc
@@ -300,7 +300,7 @@
   quad_state->SetAll(
       gfx::Transform(), /*quad_layer_rect=*/quad_rect,
       /*visible_quad_layer_rect=*/quad_rect,
-      /*rounded_corner_bounds=*/gfx::RRectF(), /*clip_rect=*/gfx::Rect(),
+      /*mask_filter_info=*/gfx::MaskFilterInfo(), /*clip_rect=*/gfx::Rect(),
       /*is_clipped=*/false, /*are_contents_opaque=*/true, /*opacity=*/1.f,
       /*blend_mode=*/SkBlendMode::kSrcOver, /*sorting_context_id=*/0);
 
diff --git a/components/exo/ui_lock_controller.cc b/components/exo/ui_lock_controller.cc
index ef4a1605..cb417e0 100644
--- a/components/exo/ui_lock_controller.cc
+++ b/components/exo/ui_lock_controller.cc
@@ -5,10 +5,10 @@
 #include "components/exo/ui_lock_controller.h"
 
 #include "ash/public/cpp/app_types.h"
-#include "ash/public/cpp/window_properties.h"
 #include "ash/wm/window_state.h"
 #include "base/time/time.h"
 #include "base/timer/timer.h"
+#include "chromeos/ui/base/window_properties.h"
 #include "components/exo/seat.h"
 #include "components/exo/shell_surface_util.h"
 #include "components/exo/surface.h"
@@ -67,7 +67,7 @@
     return false;
 
   aura::Window* window = widget->GetNativeWindow();
-  if (!window || window->GetProperty(ash::kImmersiveImpliedByFullscreen))
+  if (!window || window->GetProperty(chromeos::kImmersiveImpliedByFullscreen))
     return false;
 
   // TODO(b/165865831): Add the Borealis AppType if/when we add one.
diff --git a/components/media_router/browser/android/java/src/org/chromium/components/media_router/caf/remoting/RemotingMediaSource.java b/components/media_router/browser/android/java/src/org/chromium/components/media_router/caf/remoting/RemotingMediaSource.java
index 01208e2..91984bae 100644
--- a/components/media_router/browser/android/java/src/org/chromium/components/media_router/caf/remoting/RemotingMediaSource.java
+++ b/components/media_router/browser/android/java/src/org/chromium/components/media_router/caf/remoting/RemotingMediaSource.java
@@ -1,6 +1,7 @@
 // Copyright 2017 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
+
 package org.chromium.components.media_router.caf.remoting;
 
 import android.content.Context;
@@ -26,9 +27,13 @@
 public class RemotingMediaSource implements MediaSource {
     private static final String TAG = "MediaRemoting";
 
-    // Need to be in sync with third_party/WebKit/Source/modules/remoteplayback/RemotePlayback.cpp.
+    // Needs to be in sync with
+    // third_party/blink/renderer/modules/remoteplayback/remote_playback.cc.
     // TODO(avayvod): Find a way to share the constants somehow.
     private static final String SOURCE_PREFIX = "remote-playback://";
+
+    // Needs to be in sync with AndroidManifest meta-data key (used both by Clank and WebLayer
+    // clients).
     private static final String REMOTE_PLAYBACK_APP_ID_KEY =
             "org.chromium.content.browser.REMOTE_PLAYBACK_APP_ID";
 
diff --git a/components/password_manager/core/browser/sync/password_model_type_controller.cc b/components/password_manager/core/browser/sync/password_model_type_controller.cc
index 425ad22..a8e00c03 100644
--- a/components/password_manager/core/browser/sync/password_model_type_controller.cc
+++ b/components/password_manager/core/browser/sync/password_model_type_controller.cc
@@ -14,8 +14,8 @@
 #include "components/prefs/pref_service.h"
 #include "components/signin/public/identity_manager/accounts_in_cookie_jar_info.h"
 #include "components/sync/base/model_type.h"
-#include "components/sync/driver/sync_client.h"
 #include "components/sync/driver/sync_service.h"
+#include "components/sync/driver/sync_user_settings.h"
 #include "components/sync/model/model_type_controller_delegate.h"
 
 namespace password_manager {
@@ -140,6 +140,16 @@
              : PreconditionState::kMustStopAndClearData;
 }
 
+bool PasswordModelTypeController::ShouldRunInTransportOnlyMode() const {
+  if (!base::FeatureList::IsEnabled(features::kEnablePasswordsAccountStorage)) {
+    return false;
+  }
+  if (sync_service_->GetUserSettings()->IsUsingSecondaryPassphrase()) {
+    return false;
+  }
+  return true;
+}
+
 void PasswordModelTypeController::OnStateChanged(syncer::SyncService* sync) {
   DCHECK(CalledOnValidThread());
   sync_service_->DataTypePreconditionChanged(syncer::PASSWORDS);
diff --git a/components/password_manager/core/browser/sync/password_model_type_controller.h b/components/password_manager/core/browser/sync/password_model_type_controller.h
index f535438..bc5362b 100644
--- a/components/password_manager/core/browser/sync/password_model_type_controller.h
+++ b/components/password_manager/core/browser/sync/password_model_type_controller.h
@@ -49,6 +49,7 @@
   void Stop(syncer::ShutdownReason shutdown_reason,
             StopCallback callback) override;
   PreconditionState GetPreconditionState() const override;
+  bool ShouldRunInTransportOnlyMode() const override;
 
   // SyncServiceObserver overrides.
   void OnStateChanged(syncer::SyncService* sync) override;
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index c5724f3..7358f07 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -16215,6 +16215,29 @@
       Leaving the policy unset means that by default, the feature isn't allowed for managed users but is allowed for other users.''',
     },
     {
+      'name': 'WifiSyncAndroidAllowed',
+      'owners': ['jonmann@chromium.org', 'cvandermerwe@google.com'],
+      'type': 'main',
+      'schema': { 'type': 'boolean' },
+      'future_on': ['chrome_os'],
+      'features': {
+        'dynamic_refresh': True,
+        'per_profile': True,
+      },
+      'example_value': True,
+      'default_for_enterprise_users': False,
+      'id': 798,
+      'caption': '''Allow Wi-Fi network configurations to be synced across <ph name="PRODUCT_OS_NAME">$2<ex>Google Chrome OS</ex></ph> devices and a connected Android phone.''',
+      'tags': ['local-data-access', 'google-sharing'],
+      'desc': '''If this setting is enabled, users will be allowed to sync Wi-Fi network configurations between their <ph name="PRODUCT_OS_NAME">$2<ex>Google Chrome OS</ex></ph> device(s) and a connected Android phone. Before Wi-Fi network configurations can sync, users must explicitly opt in to this feature by completing a setup flow.
+
+      If this setting is disabled, users will not be allowed to sync Wi-Fi network configurations.
+
+      This feature depends on the <ph name="WIFI_CONFIGURATIONS_DATATYPE_NAME">wifiConfigurations</ph> datatype in Chrome Sync being enabled. If <ph name="WIFI_CONFIGURATIONS_DATATYPE_NAME">wifiConfigurations</ph> is disabled in the <ph name="SYNC_TYPES_LIST_DISABLED_POLICY_NAME">SyncTypesListDisabled</ph> policy, or Chrome Sync is disabled in the <ph name="SYNC_DISABLED_POLICY_NAME">SyncDisabled</ph> policy this feature will not be enabled.
+
+      If this policy is left not set, the default is not allowed for managed users.''',
+    },
+    {
       'name': 'SmartLockSigninAllowed',
       'owners': ['hansberry@chromium.org', 'jhawkins@chromium.org'],
       'type': 'main',
@@ -21484,6 +21507,7 @@
       'features': {
         'dynamic_refresh': True,
         'per_profile': False,
+        'cloud_only': True,
       },
       'id': 697,
       'supported_on': ['chrome.*:84-', 'chrome_os:84-'],
@@ -21521,6 +21545,7 @@
       'features': {
         'dynamic_refresh': True,
         'per_profile': False,
+        'cloud_only': True,
       },
       'id': 699,
       'supported_on': ['chrome.*:84-', 'chrome_os:84-'],
@@ -21559,7 +21584,8 @@
       ],
       'features': {
         'dynamic_refresh': True,
-        'per_profile': True,
+        'per_profile': False,
+        'cloud_only': True,
       },
       'example_value': 1,
       'id': 728,
@@ -24527,6 +24553,6 @@
   'placeholders': [],
   'deleted_policy_ids': [114, 115, 412, 476, 544, 546, 562, 569, 578, 583, 589],
   'deleted_atomic_policy_group_ids': [],
-  'highest_id_currently_used': 797,
+  'highest_id_currently_used': 798,
   'highest_atomic_group_id_currently_used': 40
 }
diff --git a/components/safe_browsing/core/features.cc b/components/safe_browsing/core/features.cc
index 4b565625..7461d97 100644
--- a/components/safe_browsing/core/features.cc
+++ b/components/safe_browsing/core/features.cc
@@ -104,7 +104,7 @@
 
 const base::Feature kRealTimeUrlLookupEnabledForEnterprise{
     "SafeBrowsingRealTimeUrlLookupEnabledForEnterprise",
-    base::FEATURE_DISABLED_BY_DEFAULT};
+    base::FEATURE_ENABLED_BY_DEFAULT};
 
 const base::Feature kRealTimeUrlLookupEnabledForEP{
     "SafeBrowsingRealTimeUrlLookupEnabledForEP",
diff --git a/components/shared_highlighting/core/common/shared_highlighting_metrics.cc b/components/shared_highlighting/core/common/shared_highlighting_metrics.cc
index 653cc9e..30f4136d 100644
--- a/components/shared_highlighting/core/common/shared_highlighting_metrics.cc
+++ b/components/shared_highlighting/core/common/shared_highlighting_metrics.cc
@@ -13,7 +13,8 @@
 namespace {
 
 TextFragmentLinkOpenSource GetLinkSource(const GURL& referrer) {
-  bool from_search_engine = SearchEngineUtils::GetEngineType(referrer) > 0;
+  bool from_search_engine =
+      referrer.is_valid() && SearchEngineUtils::GetEngineType(referrer) > 0;
   return from_search_engine ? TextFragmentLinkOpenSource::kSearchEngine
                             : TextFragmentLinkOpenSource::kUnknown;
 }
diff --git a/components/shared_highlighting/core/common/shared_highlighting_metrics.h b/components/shared_highlighting/core/common/shared_highlighting_metrics.h
index cb1e9826..a9c1190 100644
--- a/components/shared_highlighting/core/common/shared_highlighting_metrics.h
+++ b/components/shared_highlighting/core/common/shared_highlighting_metrics.h
@@ -9,26 +9,31 @@
 
 namespace shared_highlighting {
 
-// Update corresponding |LinkGenerationError| in enums.xml.
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+// The type of errors that can happen during link generation.
 enum class LinkGenerationError {
-  kIncorrectSelector,
-  kNoRange,
-  kNoContext,
-  kContextExhausted,
-  kContextLimitReached,
-  kEmptySelection,
+  kIncorrectSelector = 0,
+  kNoRange = 1,
+  kNoContext = 2,
+  kContextExhausted = 3,
+  kContextLimitReached = 4,
+  kEmptySelection = 5,
 
-  kTabHidden,
-  kOmniboxNavigation,
-  kTabCrash,
+  // Android specific.
+  kTabHidden = 5,
+  kOmniboxNavigation = 6,
+  kTabCrash = 7,
 
   kMaxValue = kTabCrash
 };
 
-// Update corresponding |TextFragmentLinkOpenSource| in enums.xml.
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+// The different sources from which a text fragment URL can come from.
 enum class TextFragmentLinkOpenSource {
-  kUnknown,
-  kSearchEngine,
+  kUnknown = 0,
+  kSearchEngine = 1,
 
   kMaxValue = kSearchEngine,
 };
diff --git a/components/shared_highlighting/core/common/shared_highlighting_metrics_unittest.cc b/components/shared_highlighting/core/common/shared_highlighting_metrics_unittest.cc
index 136d8ab..1185c36 100644
--- a/components/shared_highlighting/core/common/shared_highlighting_metrics_unittest.cc
+++ b/components/shared_highlighting/core/common/shared_highlighting_metrics_unittest.cc
@@ -26,15 +26,23 @@
 TEST(SharedHighlightingMetricsTest, LogTextFragmentLinkOpenSource) {
   base::HistogramTester histogram_tester;
 
-  GURL search_engine_url = GURL("https://google.com");
-  GURL non_search_engine_url = GURL("https://example.com");
-
+  GURL search_engine_url("https://google.com");
   LogTextFragmentLinkOpenSource(search_engine_url);
-  histogram_tester.ExpectBucketCount("TextFragmentAnchor.LinkOpenSource", 1, 1);
+  histogram_tester.ExpectBucketCount("TextFragmentAnchor.LinkOpenSource",
+                                     TextFragmentLinkOpenSource::kSearchEngine,
+                                     1);
 
+  GURL non_search_engine_url("https://example.com");
   LogTextFragmentLinkOpenSource(non_search_engine_url);
-  histogram_tester.ExpectBucketCount("TextFragmentAnchor.LinkOpenSource", 0, 1);
+  histogram_tester.ExpectBucketCount("TextFragmentAnchor.LinkOpenSource",
+                                     TextFragmentLinkOpenSource::kUnknown, 1);
   histogram_tester.ExpectTotalCount("TextFragmentAnchor.LinkOpenSource", 2);
+
+  GURL empty_gurl("");
+  LogTextFragmentLinkOpenSource(empty_gurl);
+  histogram_tester.ExpectBucketCount("TextFragmentAnchor.LinkOpenSource",
+                                     TextFragmentLinkOpenSource::kUnknown, 2);
+  histogram_tester.ExpectTotalCount("TextFragmentAnchor.LinkOpenSource", 3);
 }
 
 TEST(SharedHighlightingMetricsTest, LogTextFragmentMatchRate) {
diff --git a/components/sync/driver/data_type_controller.h b/components/sync/driver/data_type_controller.h
index 8688e515f..579ec2d 100644
--- a/components/sync/driver/data_type_controller.h
+++ b/components/sync/driver/data_type_controller.h
@@ -115,6 +115,10 @@
   };
   virtual PreconditionState GetPreconditionState() const;
 
+  // Returns whether the datatype knows how to, and wants to, run in
+  // transport-only mode (see syncer::SyncMode enum).
+  virtual bool ShouldRunInTransportOnlyMode() const = 0;
+
   // Returns a ListValue representing all nodes for this data type through
   // |callback| on this thread. Can only be called if state() != NOT_RUNNING.
   // Used for populating nodes in Sync Node Browser of chrome://sync-internals.
diff --git a/components/sync/driver/model_type_controller.cc b/components/sync/driver/model_type_controller.cc
index 1d3ec57..c417309f 100644
--- a/components/sync/driver/model_type_controller.cc
+++ b/components/sync/driver/model_type_controller.cc
@@ -208,6 +208,14 @@
   return state_;
 }
 
+bool ModelTypeController::ShouldRunInTransportOnlyMode() const {
+  // By default, running in transport-only mode is enabled if the corresponding
+  // delegate exists, i.e. the controller is aware of transport-only mode and
+  // supports it in principle. Subclass can still override this with more
+  // specific logic.
+  return delegate_map_.count(SyncMode::kTransportOnly) != 0;
+}
+
 void ModelTypeController::GetAllNodes(AllNodesCallback callback) {
   DCHECK(delegate_);
   delegate_->GetAllNodesForDebugging(std::move(callback));
diff --git a/components/sync/driver/model_type_controller.h b/components/sync/driver/model_type_controller.h
index 5fb62c3..dadf2c2 100644
--- a/components/sync/driver/model_type_controller.h
+++ b/components/sync/driver/model_type_controller.h
@@ -53,6 +53,7 @@
   void DeactivateDataType(ModelTypeConfigurer* configurer) override;
   void Stop(ShutdownReason shutdown_reason, StopCallback callback) override;
   State state() const override;
+  bool ShouldRunInTransportOnlyMode() const override;
   void GetAllNodes(AllNodesCallback callback) override;
   void RecordMemoryUsageAndCountsHistograms() override;
 
diff --git a/components/sync/driver/profile_sync_service.cc b/components/sync/driver/profile_sync_service.cc
index 202986d..3f6007e7 100644
--- a/components/sync/driver/profile_sync_service.cc
+++ b/components/sync/driver/profile_sync_service.cc
@@ -217,10 +217,6 @@
                               base::Unretained(this)))),
       channel_(init_params.channel),
       debug_identifier_(init_params.debug_identifier),
-      autofill_enable_account_wallet_storage_(
-          init_params.autofill_enable_account_wallet_storage),
-      enable_passwords_account_storage_(
-          init_params.enable_passwords_account_storage),
       sync_service_url_(
           GetSyncServiceURL(*base::CommandLine::ForCurrentProcess(), channel_)),
       crypto_(
@@ -1359,42 +1355,15 @@
 }
 
 ModelTypeSet ProfileSyncService::GetModelTypesForTransportOnlyMode() const {
-  ModelTypeSet allowed_types = {
-      DEVICE_INFO,
-      SECURITY_EVENTS,
-      SHARING_MESSAGE,
-      SUPERVISED_USER_SETTINGS,
-      SUPERVISED_USER_ALLOWLISTS,
-      USER_CONSENTS,
-  };
-
-  if (autofill_enable_account_wallet_storage_) {
-    if (!GetUserSettings()->IsUsingSecondaryPassphrase() ||
-        base::FeatureList::IsEnabled(
-            switches::
-                kSyncAllowWalletDataInTransportModeWithCustomPassphrase)) {
-      allowed_types.Put(AUTOFILL_WALLET_DATA);
+  // Collect the types from all controllers that support transport-only mode.
+  ModelTypeSet allowed_types;
+  for (const auto& type_and_controller : data_type_controllers_) {
+    ModelType type = type_and_controller.first;
+    const DataTypeController* controller = type_and_controller.second.get();
+    if (controller->ShouldRunInTransportOnlyMode()) {
+      allowed_types.Put(type);
     }
   }
-
-  if (enable_passwords_account_storage_ &&
-      !GetUserSettings()->IsUsingSecondaryPassphrase()) {
-    allowed_types.Put(PASSWORDS);
-  }
-
-  // Outside the #if so non-Chrome OS developers will hit it before uploading.
-  static_assert(41 == ModelType::NUM_ENTRIES,
-                "If a new ModelType is Chrome OS-only and uses OS sync "
-                "consent, add it below.");
-#if defined(OS_CHROMEOS)
-  // Chrome OS system types are not tied to browser sync-the-feature.
-  if (chromeos::features::IsSplitSettingsSyncEnabled()) {
-    allowed_types.PutAll({APP_LIST, APP_SETTINGS, APPS, ARC_PACKAGE,
-                          OS_PREFERENCES, OS_PRIORITY_PREFERENCES, PRINTERS,
-                          WEB_APPS, WIFI_CONFIGURATIONS});
-  }
-#endif  // defined(OS_CHROMEOS)
-
   return allowed_types;
 }
 
diff --git a/components/sync/driver/profile_sync_service.h b/components/sync/driver/profile_sync_service.h
index 03ead02..bd17fe7c 100644
--- a/components/sync/driver/profile_sync_service.h
+++ b/components/sync/driver/profile_sync_service.h
@@ -92,8 +92,6 @@
     network::NetworkConnectionTracker* network_connection_tracker = nullptr;
     version_info::Channel channel = version_info::Channel::UNKNOWN;
     std::string debug_identifier;
-    bool autofill_enable_account_wallet_storage = false;
-    bool enable_passwords_account_storage = false;
 
    private:
     DISALLOW_COPY_AND_ASSIGN(InitParams);
@@ -393,9 +391,6 @@
   // An identifier representing this instance for debugging purposes.
   const std::string debug_identifier_;
 
-  const bool autofill_enable_account_wallet_storage_;
-  const bool enable_passwords_account_storage_;
-
   // This specifies where to find the sync server.
   const GURL sync_service_url_;
 
diff --git a/components/sync/driver/profile_sync_service_unittest.cc b/components/sync/driver/profile_sync_service_unittest.cc
index bbb281b..560aff4 100644
--- a/components/sync/driver/profile_sync_service_unittest.cc
+++ b/components/sync/driver/profile_sync_service_unittest.cc
@@ -179,14 +179,20 @@
   void SignIn() { identity_test_env()->MakePrimaryAccountAvailable(kTestUser); }
 
   void CreateService(ProfileSyncService::StartBehavior behavior,
-                     ModelTypeSet registered_types =
-                         ModelTypeSet(BOOKMARKS, SUPERVISED_USER_SETTINGS)) {
+                     std::vector<std::pair<ModelType, bool>>
+                         registered_types_and_transport_mode_support = {
+                             {BOOKMARKS, false},
+                             {DEVICE_INFO, true}}) {
     DCHECK(!service_);
 
     // Default includes a regular controller and a transport-mode controller.
     DataTypeController::TypeVector controllers;
-    for (const ModelType type : registered_types) {
-      controllers.push_back(std::make_unique<FakeDataTypeController>(type));
+    for (const auto& type_and_transport_mode_support :
+         registered_types_and_transport_mode_support) {
+      ModelType type = type_and_transport_mode_support.first;
+      bool transport_mode_support = type_and_transport_mode_support.second;
+      controllers.push_back(std::make_unique<FakeDataTypeController>(
+          type, transport_mode_support));
     }
 
     std::unique_ptr<SyncClientMock> sync_client =
@@ -211,8 +217,8 @@
     // Include a regular controller and a transport-mode controller.
     DataTypeController::TypeVector controllers;
     controllers.push_back(std::make_unique<FakeDataTypeController>(BOOKMARKS));
-    controllers.push_back(
-        std::make_unique<FakeDataTypeController>(SUPERVISED_USER_SETTINGS));
+    controllers.push_back(std::make_unique<FakeDataTypeController>(
+        DEVICE_INFO, /*enable_transport_only_modle=*/true));
 
     std::unique_ptr<SyncClientMock> sync_client =
         profile_sync_service_bundle_.CreateSyncClientMock();
@@ -468,7 +474,7 @@
   EXPECT_FALSE(service()->GetActiveDataTypes().Has(BOOKMARKS));
 
   // ModelTypes for sync-the-transport are configured.
-  EXPECT_TRUE(service()->GetActiveDataTypes().Has(SUPERVISED_USER_SETTINGS));
+  EXPECT_TRUE(service()->GetActiveDataTypes().Has(DEVICE_INFO));
 }
 
 // Verify that the SetSetupInProgress function call updates state
@@ -1634,7 +1640,7 @@
 TEST_F(ProfileSyncServiceTestWithSyncInvalidationsServiceCreated,
        ShouldEnableAndDisableInvalidationsForSessions) {
   CreateService(ProfileSyncService::AUTO_START,
-                ModelTypeSet(SESSIONS, TYPED_URLS));
+                {{SESSIONS, false}, {TYPED_URLS, false}});
   SignIn();
   InitializeForNthSync();
 
diff --git a/components/sync/driver/sync_service_crypto.cc b/components/sync/driver/sync_service_crypto.cc
index fa658927e..f597cb8 100644
--- a/components/sync/driver/sync_service_crypto.cc
+++ b/components/sync/driver/sync_service_crypto.cc
@@ -279,16 +279,6 @@
          RequiredUserAction::kTrustedVaultRecoverabilityDegraded;
 }
 
-void SyncServiceCrypto::EnableEncryptEverything() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(state_.engine);
-
-  // TODO(atwilson): Persist the encryption_pending flag to address the various
-  // problems around cancelling encryption in the background (crbug.com/119649).
-  if (!state_.encrypt_everything)
-    state_.encryption_pending = true;
-}
-
 bool SyncServiceCrypto::IsEncryptEverythingEnabled() const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(state_.engine);
diff --git a/components/sync/driver/sync_service_crypto.h b/components/sync/driver/sync_service_crypto.h
index 5cfde56..1f60911 100644
--- a/components/sync/driver/sync_service_crypto.h
+++ b/components/sync/driver/sync_service_crypto.h
@@ -50,7 +50,6 @@
   bool IsUsingSecondaryPassphrase() const;
   bool IsTrustedVaultKeyRequired() const;
   bool IsTrustedVaultRecoverabilityDegraded() const;
-  void EnableEncryptEverything();
   bool IsEncryptEverythingEnabled() const;
   void SetEncryptionPassphrase(const std::string& passphrase);
   bool SetDecryptionPassphrase(const std::string& passphrase);
diff --git a/components/sync/driver/sync_user_settings.h b/components/sync/driver/sync_user_settings.h
index 765d422..c360552 100644
--- a/components/sync/driver/sync_user_settings.h
+++ b/components/sync/driver/sync_user_settings.h
@@ -91,12 +91,6 @@
   virtual bool IsEncryptEverythingAllowed() const = 0;
   // Whether we are currently set to encrypt all the Sync data.
   virtual bool IsEncryptEverythingEnabled() const = 0;
-  // Turns on encryption for all data. Callers must call SetChosenDataTypes()
-  // after calling this to force the encryption to occur.
-  // TODO(crbug.com/1033040): This was only relevant for the Directory
-  // implementation of Nigori, and should be cleaned up. The same goes for
-  // related code in PeopleHandler and possibly its Javascript counterpart.
-  virtual void EnableEncryptEverything() = 0;
 
   // The current set of encrypted data types.
   virtual ModelTypeSet GetEncryptedDataTypes() const = 0;
diff --git a/components/sync/driver/sync_user_settings_impl.cc b/components/sync/driver/sync_user_settings_impl.cc
index b37454b..ba8b9e4 100644
--- a/components/sync/driver/sync_user_settings_impl.cc
+++ b/components/sync/driver/sync_user_settings_impl.cc
@@ -173,11 +173,6 @@
   return crypto_->IsEncryptEverythingEnabled();
 }
 
-void SyncUserSettingsImpl::EnableEncryptEverything() {
-  DCHECK(IsEncryptEverythingAllowed());
-  crypto_->EnableEncryptEverything();
-}
-
 bool SyncUserSettingsImpl::IsPassphraseRequired() const {
   return crypto_->IsPassphraseRequired();
 }
diff --git a/components/sync/driver/sync_user_settings_impl.h b/components/sync/driver/sync_user_settings_impl.h
index 4a6ed38..8e7889c 100644
--- a/components/sync/driver/sync_user_settings_impl.h
+++ b/components/sync/driver/sync_user_settings_impl.h
@@ -59,7 +59,6 @@
 
   bool IsEncryptEverythingAllowed() const override;
   bool IsEncryptEverythingEnabled() const override;
-  void EnableEncryptEverything() override;
 
   ModelTypeSet GetEncryptedDataTypes() const override;
   bool IsPassphraseRequired() const override;
diff --git a/components/sync/driver/sync_user_settings_mock.h b/components/sync/driver/sync_user_settings_mock.h
index c412959..f51b26b 100644
--- a/components/sync/driver/sync_user_settings_mock.h
+++ b/components/sync/driver/sync_user_settings_mock.h
@@ -44,7 +44,6 @@
 
   MOCK_CONST_METHOD0(IsEncryptEverythingAllowed, bool());
   MOCK_CONST_METHOD0(IsEncryptEverythingEnabled, bool());
-  MOCK_METHOD0(EnableEncryptEverything, void());
 
   MOCK_CONST_METHOD0(GetEncryptedDataTypes, ModelTypeSet());
   MOCK_CONST_METHOD0(IsPassphraseRequired, bool());
diff --git a/components/sync/driver/test_sync_user_settings.cc b/components/sync/driver/test_sync_user_settings.cc
index 5031631..dc207fd 100644
--- a/components/sync/driver/test_sync_user_settings.cc
+++ b/components/sync/driver/test_sync_user_settings.cc
@@ -149,8 +149,6 @@
   return false;
 }
 
-void TestSyncUserSettings::EnableEncryptEverything() {}
-
 ModelTypeSet TestSyncUserSettings::GetEncryptedDataTypes() const {
   if (!IsUsingSecondaryPassphrase()) {
     // PASSWORDS and WIFI_CONFIGURATIONS are always encrypted.
diff --git a/components/sync/driver/test_sync_user_settings.h b/components/sync/driver/test_sync_user_settings.h
index b46f829c..a72ba9e 100644
--- a/components/sync/driver/test_sync_user_settings.h
+++ b/components/sync/driver/test_sync_user_settings.h
@@ -48,7 +48,6 @@
 
   bool IsEncryptEverythingAllowed() const override;
   bool IsEncryptEverythingEnabled() const override;
-  void EnableEncryptEverything() override;
 
   syncer::ModelTypeSet GetEncryptedDataTypes() const override;
   bool IsPassphraseRequired() const override;
diff --git a/components/sync_sessions/proxy_tabs_data_type_controller.cc b/components/sync_sessions/proxy_tabs_data_type_controller.cc
index 19dc01f1..3f868e0 100644
--- a/components/sync_sessions/proxy_tabs_data_type_controller.cc
+++ b/components/sync_sessions/proxy_tabs_data_type_controller.cc
@@ -61,6 +61,10 @@
   return state_;
 }
 
+bool ProxyTabsDataTypeController::ShouldRunInTransportOnlyMode() const {
+  return false;
+}
+
 void ProxyTabsDataTypeController::DeactivateDataType(
     syncer::ModelTypeConfigurer* configurer) {
   if (state_ == RUNNING) {
diff --git a/components/sync_sessions/proxy_tabs_data_type_controller.h b/components/sync_sessions/proxy_tabs_data_type_controller.h
index 1df714f5..62a1d8f 100644
--- a/components/sync_sessions/proxy_tabs_data_type_controller.h
+++ b/components/sync_sessions/proxy_tabs_data_type_controller.h
@@ -31,6 +31,7 @@
   void Stop(syncer::ShutdownReason shutdown_reason,
             StopCallback callback) override;
   State state() const override;
+  bool ShouldRunInTransportOnlyMode() const override;
   void DeactivateDataType(syncer::ModelTypeConfigurer* configurer) override;
   void GetAllNodes(AllNodesCallback callback) override;
   void RecordMemoryUsageAndCountsHistograms() override;
diff --git a/components/viz/common/quads/compositor_render_pass_unittest.cc b/components/viz/common/quads/compositor_render_pass_unittest.cc
index cda0bebd..e6010a50 100644
--- a/components/viz/common/quads/compositor_render_pass_unittest.cc
+++ b/components/viz/common/quads/compositor_render_pass_unittest.cc
@@ -85,7 +85,7 @@
   // Stick a quad in the pass, this should not get copied.
   SharedQuadState* shared_state = pass->CreateAndAppendSharedQuadState();
   shared_state->SetAll(gfx::Transform(), gfx::Rect(), gfx::Rect(),
-                       gfx::RRectF(), gfx::Rect(), false, false, 1,
+                       gfx::MaskFilterInfo(), gfx::Rect(), false, false, 1,
                        SkBlendMode::kSrcOver, 0);
 
   auto* color_quad = pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
@@ -144,7 +144,7 @@
   // Two quads using one shared state.
   SharedQuadState* shared_state1 = pass->CreateAndAppendSharedQuadState();
   shared_state1->SetAll(gfx::Transform(), gfx::Rect(0, 0, 1, 1), gfx::Rect(),
-                        gfx::RRectF(), gfx::Rect(), false, false, 1,
+                        gfx::MaskFilterInfo(), gfx::Rect(), false, false, 1,
                         SkBlendMode::kSrcOver, 0);
 
   auto* color_quad1 = pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
@@ -160,7 +160,7 @@
   // And two quads using another shared state.
   SharedQuadState* shared_state2 = pass->CreateAndAppendSharedQuadState();
   shared_state2->SetAll(gfx::Transform(), gfx::Rect(0, 0, 2, 2), gfx::Rect(),
-                        gfx::RRectF(), gfx::Rect(), false, false, 1,
+                        gfx::MaskFilterInfo(), gfx::Rect(), false, false, 1,
                         SkBlendMode::kSrcOver, 0);
 
   auto* color_quad3 = pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
@@ -201,8 +201,8 @@
   SharedQuadState* contrib_shared_state =
       contrib->CreateAndAppendSharedQuadState();
   contrib_shared_state->SetAll(gfx::Transform(), gfx::Rect(0, 0, 2, 2),
-                               gfx::Rect(), gfx::RRectF(), gfx::Rect(), false,
-                               false, 1, SkBlendMode::kSrcOver, 0);
+                               gfx::Rect(), gfx::MaskFilterInfo(), gfx::Rect(),
+                               false, false, 1, SkBlendMode::kSrcOver, 0);
 
   auto* contrib_quad = contrib->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
   contrib_quad->SetNew(contrib->shared_quad_state_list.back(),
@@ -254,7 +254,7 @@
   // A shared state with a quad.
   SharedQuadState* shared_state1 = pass->CreateAndAppendSharedQuadState();
   shared_state1->SetAll(gfx::Transform(), gfx::Rect(0, 0, 1, 1), gfx::Rect(),
-                        gfx::RRectF(), gfx::Rect(), false, false, 1,
+                        gfx::MaskFilterInfo(), gfx::Rect(), false, false, 1,
                         SkBlendMode::kSrcOver, 0);
 
   auto* color_quad1 = pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
@@ -265,19 +265,19 @@
   // A shared state with no quads, they were culled.
   SharedQuadState* shared_state2 = pass->CreateAndAppendSharedQuadState();
   shared_state2->SetAll(gfx::Transform(), gfx::Rect(0, 0, 2, 2), gfx::Rect(),
-                        gfx::RRectF(), gfx::Rect(), false, false, 1,
+                        gfx::MaskFilterInfo(), gfx::Rect(), false, false, 1,
                         SkBlendMode::kSrcOver, 0);
 
   // A second shared state with no quads.
   SharedQuadState* shared_state3 = pass->CreateAndAppendSharedQuadState();
   shared_state3->SetAll(gfx::Transform(), gfx::Rect(0, 0, 2, 2), gfx::Rect(),
-                        gfx::RRectF(), gfx::Rect(), false, false, 1,
+                        gfx::MaskFilterInfo(), gfx::Rect(), false, false, 1,
                         SkBlendMode::kSrcOver, 0);
 
   // A last shared state with a quad again.
   SharedQuadState* shared_state4 = pass->CreateAndAppendSharedQuadState();
   shared_state4->SetAll(gfx::Transform(), gfx::Rect(0, 0, 2, 2), gfx::Rect(),
-                        gfx::RRectF(), gfx::Rect(), false, false, 1,
+                        gfx::MaskFilterInfo(), gfx::Rect(), false, false, 1,
                         SkBlendMode::kSrcOver, 0);
 
   auto* color_quad2 = pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
diff --git a/components/viz/common/quads/draw_quad.h b/components/viz/common/quads/draw_quad.h
index 313e208f..4900fb6 100644
--- a/components/viz/common/quads/draw_quad.h
+++ b/components/viz/common/quads/draw_quad.h
@@ -79,7 +79,7 @@
   bool ShouldDrawWithBlending() const {
     return needs_blending || shared_quad_state->opacity < 1.0f ||
            shared_quad_state->blend_mode != SkBlendMode::kSrcOver ||
-           !shared_quad_state->rounded_corner_bounds.IsEmpty();
+           !shared_quad_state->mask_filter_info.IsEmpty();
   }
 
   // Is the left edge of this tile aligned with the originating layer's
diff --git a/components/viz/common/quads/draw_quad_perftest.cc b/components/viz/common/quads/draw_quad_perftest.cc
index 00f0de929..3c0d2e63 100644
--- a/components/viz/common/quads/draw_quad_perftest.cc
+++ b/components/viz/common/quads/draw_quad_perftest.cc
@@ -42,9 +42,9 @@
   SkBlendMode blend_mode = SkBlendMode::kSrcOver;
 
   SharedQuadState* state = render_pass->CreateAndAppendSharedQuadState();
-  state->SetAll(quad_transform, content_rect, visible_layer_rect, gfx::RRectF(),
-                clip_rect, is_clipped, are_contents_opaque, opacity, blend_mode,
-                sorting_context_id);
+  state->SetAll(quad_transform, content_rect, visible_layer_rect,
+                gfx::MaskFilterInfo(), clip_rect, is_clipped,
+                are_contents_opaque, opacity, blend_mode, sorting_context_id);
   return state;
 }
 
diff --git a/components/viz/common/quads/draw_quad_unittest.cc b/components/viz/common/quads/draw_quad_unittest.cc
index c4097751..3cb4975 100644
--- a/components/viz/common/quads/draw_quad_unittest.cc
+++ b/components/viz/common/quads/draw_quad_unittest.cc
@@ -53,9 +53,9 @@
   int sorting_context_id = 65536;
 
   auto state = std::make_unique<SharedQuadState>();
-  state->SetAll(quad_transform, layer_rect, visible_layer_rect, gfx::RRectF(),
-                clip_rect, is_clipped, are_contents_opaque, opacity, blend_mode,
-                sorting_context_id);
+  state->SetAll(quad_transform, layer_rect, visible_layer_rect,
+                gfx::MaskFilterInfo(), clip_rect, is_clipped,
+                are_contents_opaque, opacity, blend_mode, sorting_context_id);
 
   auto copy = std::make_unique<SharedQuadState>(*state);
   EXPECT_EQ(quad_transform, copy->quad_to_target_transform);
@@ -79,9 +79,9 @@
   SkBlendMode blend_mode = SkBlendMode::kSrcOver;
 
   SharedQuadState* state = render_pass->CreateAndAppendSharedQuadState();
-  state->SetAll(quad_transform, layer_rect, visible_layer_rect, gfx::RRectF(),
-                clip_rect, is_clipped, are_contents_opaque, opacity, blend_mode,
-                sorting_context_id);
+  state->SetAll(quad_transform, layer_rect, visible_layer_rect,
+                gfx::MaskFilterInfo(), clip_rect, is_clipped,
+                are_contents_opaque, opacity, blend_mode, sorting_context_id);
   return state;
 }
 
diff --git a/components/viz/common/quads/render_pass_io.cc b/components/viz/common/quads/render_pass_io.cc
index 8bd58e1..e988c2c 100644
--- a/components/viz/common/quads/render_pass_io.cc
+++ b/components/viz/common/quads/render_pass_io.cc
@@ -1602,14 +1602,16 @@
   dict.SetKey("quad_layer_rect", RectToDict(sqs.quad_layer_rect));
   dict.SetKey("visible_quad_layer_rect",
               RectToDict(sqs.visible_quad_layer_rect));
-  dict.SetKey("rounded_corner_bounds", RRectFToDict(sqs.rounded_corner_bounds));
+  dict.SetKey("rounded_corner_bounds",
+              RRectFToDict(sqs.mask_filter_info.rounded_corner_bounds()));
   dict.SetKey("clip_rect", RectToDict(sqs.clip_rect));
   dict.SetBoolKey("is_clipped", sqs.is_clipped);
   dict.SetBoolKey("are_contents_opaque", sqs.are_contents_opaque);
   dict.SetDoubleKey("opacity", sqs.opacity);
   dict.SetStringKey("blend_mode", BlendModeToString(sqs.blend_mode));
   dict.SetIntKey("sorting_context_id", sqs.sorting_context_id);
-  dict.SetBoolKey("is_fast_rounded_corner", sqs.is_fast_rounded_corner);
+  dict.SetBoolKey("is_fast_rounded_corner",
+                  sqs.mask_filter_info.is_fast_rounded_corner());
   dict.SetDoubleKey("de_jelly_delta_y", sqs.de_jelly_delta_y);
   return dict;
 }
@@ -1697,13 +1699,13 @@
   if (blend_mode_index < 0)
     return false;
   SkBlendMode t_blend_mode = static_cast<SkBlendMode>(blend_mode_index);
-
+  gfx::MaskFilterInfo mask_filter_info(t_rounded_corner_bounds,
+                                       is_fast_rounded_corner.value());
   sqs->SetAll(t_quad_to_target_transform, t_quad_layer_rect,
-              t_visible_quad_layer_rect, t_rounded_corner_bounds, t_clip_rect,
+              t_visible_quad_layer_rect, mask_filter_info, t_clip_rect,
               is_clipped.value(), are_contents_opaque.value(),
               static_cast<float>(opacity.value()), t_blend_mode,
               sorting_context_id.value());
-  sqs->is_fast_rounded_corner = is_fast_rounded_corner.value();
   sqs->de_jelly_delta_y = static_cast<float>(de_jelly_delta_y.value());
   return true;
 }
diff --git a/components/viz/common/quads/render_pass_io_unittest.cc b/components/viz/common/quads/render_pass_io_unittest.cc
index e9f8144..6644bc5e 100644
--- a/components/viz/common/quads/render_pass_io_unittest.cc
+++ b/components/viz/common/quads/render_pass_io_unittest.cc
@@ -123,10 +123,10 @@
     transform.MakeIdentity();
     sqs1->SetAll(transform, gfx::Rect(0, 0, 640, 480),
                  gfx::Rect(10, 10, 600, 400),
-                 gfx::RRectF(gfx::RectF(2.f, 3.f, 4.f, 5.f), 1.5f),
+                 gfx::MaskFilterInfo(
+                     gfx::RRectF(gfx::RectF(2.f, 3.f, 4.f, 5.f), 1.5f), true),
                  gfx::Rect(5, 20, 1000, 200), true, false, 0.5f,
                  SkBlendMode::kDstOver, 101);
-    sqs1->is_fast_rounded_corner = true;
     sqs1->de_jelly_delta_y = 0.7f;
   }
   base::Value dict0 = CompositorRenderPassToDict(*render_pass0);
@@ -141,14 +141,14 @@
     EXPECT_TRUE(sqs0->quad_to_target_transform.IsIdentity());
     EXPECT_EQ(gfx::Rect(), sqs0->quad_layer_rect);
     EXPECT_EQ(gfx::Rect(), sqs0->visible_quad_layer_rect);
-    EXPECT_TRUE(sqs0->rounded_corner_bounds.IsEmpty());
+    EXPECT_FALSE(sqs0->mask_filter_info.HasRoundedCorners());
+    EXPECT_FALSE(sqs0->mask_filter_info.is_fast_rounded_corner());
     EXPECT_EQ(gfx::Rect(), sqs0->clip_rect);
     EXPECT_FALSE(sqs0->is_clipped);
     EXPECT_TRUE(sqs0->are_contents_opaque);
     EXPECT_EQ(1.0f, sqs0->opacity);
     EXPECT_EQ(SkBlendMode::kSrcOver, sqs0->blend_mode);
     EXPECT_EQ(0, sqs0->sorting_context_id);
-    EXPECT_FALSE(sqs0->is_fast_rounded_corner);
     EXPECT_EQ(0.0f, sqs0->de_jelly_delta_y);
 
     const SharedQuadState* sqs1 =
@@ -158,17 +158,17 @@
     EXPECT_EQ(gfx::Rect(0, 0, 640, 480), sqs1->quad_layer_rect);
     EXPECT_EQ(gfx::Rect(10, 10, 600, 400), sqs1->visible_quad_layer_rect);
     EXPECT_EQ(gfx::RRectF::Type::kSingle,
-              sqs1->rounded_corner_bounds.GetType());
-    EXPECT_EQ(1.5f, sqs1->rounded_corner_bounds.GetSimpleRadius());
-    EXPECT_EQ(gfx::RectF(2.f, 3.f, 4.f, 5.f),
-              sqs1->rounded_corner_bounds.rect());
+              sqs1->mask_filter_info.rounded_corner_bounds().GetType());
+    EXPECT_EQ(1.5f,
+              sqs1->mask_filter_info.rounded_corner_bounds().GetSimpleRadius());
+    EXPECT_EQ(gfx::RectF(2.f, 3.f, 4.f, 5.f), sqs1->mask_filter_info.bounds());
+    EXPECT_TRUE(sqs1->mask_filter_info.is_fast_rounded_corner());
     EXPECT_EQ(gfx::Rect(5, 20, 1000, 200), sqs1->clip_rect);
     EXPECT_TRUE(sqs1->is_clipped);
     EXPECT_FALSE(sqs1->are_contents_opaque);
     EXPECT_EQ(0.5f, sqs1->opacity);
     EXPECT_EQ(SkBlendMode::kDstOver, sqs1->blend_mode);
     EXPECT_EQ(101, sqs1->sorting_context_id);
-    EXPECT_TRUE(sqs1->is_fast_rounded_corner);
     EXPECT_EQ(0.7f, sqs1->de_jelly_delta_y);
   }
   base::Value dict1 = CompositorRenderPassToDict(*render_pass1);
diff --git a/components/viz/common/quads/shared_quad_state.cc b/components/viz/common/quads/shared_quad_state.cc
index 42cab6b..5d7d3b3 100644
--- a/components/viz/common/quads/shared_quad_state.cc
+++ b/components/viz/common/quads/shared_quad_state.cc
@@ -23,7 +23,7 @@
 void SharedQuadState::SetAll(const gfx::Transform& quad_to_target_transform,
                              const gfx::Rect& quad_layer_rect,
                              const gfx::Rect& visible_quad_layer_rect,
-                             const gfx::RRectF& rounded_corner_bounds,
+                             const gfx::MaskFilterInfo& mask_filter_info,
                              const gfx::Rect& clip_rect,
                              bool is_clipped,
                              bool are_contents_opaque,
@@ -33,7 +33,7 @@
   this->quad_to_target_transform = quad_to_target_transform;
   this->quad_layer_rect = quad_layer_rect;
   this->visible_quad_layer_rect = visible_quad_layer_rect;
-  this->rounded_corner_bounds = rounded_corner_bounds;
+  this->mask_filter_info = mask_filter_info;
   this->clip_rect = clip_rect;
   this->is_clipped = is_clipped;
   this->are_contents_opaque = are_contents_opaque;
@@ -47,8 +47,11 @@
   cc::MathUtil::AddToTracedValue("layer_content_rect", quad_layer_rect, value);
   cc::MathUtil::AddToTracedValue("layer_visible_content_rect",
                                  visible_quad_layer_rect, value);
-  cc::MathUtil::AddToTracedValue("rounded_corner_bounds", rounded_corner_bounds,
-                                 value);
+  cc::MathUtil::AddToTracedValue("mask_filter_bounds",
+                                 mask_filter_info.bounds(), value);
+  cc::MathUtil::AddCornerRadiiToTracedValue(
+      "mask_filter_rounded_corners_radii",
+      mask_filter_info.rounded_corner_bounds(), value);
 
   value->SetBoolean("is_clipped", is_clipped);
   cc::MathUtil::AddToTracedValue("clip_rect", clip_rect, value);
diff --git a/components/viz/common/quads/shared_quad_state.h b/components/viz/common/quads/shared_quad_state.h
index 97681e500..ba4650b3 100644
--- a/components/viz/common/quads/shared_quad_state.h
+++ b/components/viz/common/quads/shared_quad_state.h
@@ -11,6 +11,7 @@
 #include "components/viz/common/viz_common_export.h"
 #include "third_party/skia/include/core/SkBlendMode.h"
 #include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/mask_filter_info.h"
 #include "ui/gfx/rrect_f.h"
 #include "ui/gfx/transform.h"
 
@@ -36,7 +37,7 @@
   void SetAll(const gfx::Transform& quad_to_target_transform,
               const gfx::Rect& quad_layer_rect,
               const gfx::Rect& visible_layer_rect,
-              const gfx::RRectF& rounded_corner_bounds,
+              const gfx::MaskFilterInfo& mask_filter_info,
               const gfx::Rect& clip_rect,
               bool is_clipped,
               bool are_contents_opaque,
@@ -55,9 +56,11 @@
   // The size of the visible area in the quads' originating layer, in the space
   // of the quad rects.
   gfx::Rect visible_quad_layer_rect;
-  // This rect lives in the target content space. It defines the corner radius
-  // to clip the quads with.
-  gfx::RRectF rounded_corner_bounds;
+  // This mask filter's coordinates is in the target content space. It defines
+  // the corner radius to clip the quads with, and the |is_fast_rounded_corner|
+  // used by SurfaceAggregator to decide whether to merge quads for a surface
+  // into their target render pass for performance optimization.
+  gfx::MaskFilterInfo mask_filter_info;
   // This rect lives in the target content space.
   gfx::Rect clip_rect;
   bool is_clipped = false;
@@ -66,10 +69,6 @@
   float opacity = 1.f;
   SkBlendMode blend_mode = SkBlendMode::kSrcOver;
   int sorting_context_id = 0;
-  // Used by SurfaceAggregator to decide whether to merge quads for a surface
-  // into their target render pass. It is a performance optimization by avoiding
-  // render passes as much as possible.
-  bool is_fast_rounded_corner = false;
   // This is for underlay optimization and used only in the SurfaceAggregator
   // and the OverlayProcessor. Do not set the value in CompositorRenderPass.
   // This index points to the damage rect in the surface damage rect list where
diff --git a/components/viz/demo/client/demo_client.cc b/components/viz/demo/client/demo_client.cc
index 75e6f81..6f255f94 100644
--- a/components/viz/demo/client/demo_client.cc
+++ b/components/viz/demo/client/demo_client.cc
@@ -102,7 +102,7 @@
         transform,
         /*quad_layer_rect=*/child_bounds,
         /*visible_quad_layer_rect=*/child_bounds,
-        /*rounded_corner_bounds=*/gfx::RRectF(),
+        /*mask_filter_info=*/gfx::MaskFilterInfo(),
         /*clip_rect=*/gfx::Rect(),
         /*is_clipped=*/false, /*are_contents_opaque=*/false, /*opacity=*/1.f,
         /*blend_mode=*/SkBlendMode::kSrcOver, /*sorting_context_id=*/0);
@@ -127,7 +127,7 @@
       gfx::Transform(),
       /*quad_layer_rect=*/output_rect,
       /*visible_quad_layer_rect=*/output_rect,
-      /*rounded_corner_bounds=*/gfx::RRectF(),
+      /*mask_filter_info=*/gfx::MaskFilterInfo(),
       /*clip_rect=*/gfx::Rect(),
       /*is_clipped=*/false, /*are_contents_opaque=*/false, /*opacity=*/1.f,
       /*blend_mode=*/SkBlendMode::kSrcOver, /*sorting_context_id=*/0);
diff --git a/components/viz/service/compositor_frame_fuzzer/compositor_frame_fuzzer_util.cc b/components/viz/service/compositor_frame_fuzzer/compositor_frame_fuzzer_util.cc
index 6a316432..226e84a89 100644
--- a/components/viz/service/compositor_frame_fuzzer/compositor_frame_fuzzer_util.cc
+++ b/components/viz/service/compositor_frame_fuzzer/compositor_frame_fuzzer_util.cc
@@ -15,6 +15,7 @@
 #include "components/viz/common/resources/bitmap_allocation.h"
 #include "components/viz/common/resources/resource_sizes.h"
 #include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/gfx/mask_filter_info.h"
 
 namespace viz {
 namespace {
@@ -343,8 +344,8 @@
     shared_quad_state->SetAll(
         GetTransformFromProtobuf(quad_spec.sqs().transform()),
         GetRectFromProtobuf(quad_spec.sqs().layer_rect()),
-        GetRectFromProtobuf(quad_spec.sqs().visible_rect()), gfx::RRectF(),
-        GetRectFromProtobuf(quad_spec.sqs().clip_rect()),
+        GetRectFromProtobuf(quad_spec.sqs().visible_rect()),
+        gfx::MaskFilterInfo(), GetRectFromProtobuf(quad_spec.sqs().clip_rect()),
         quad_spec.sqs().is_clipped(), quad_spec.sqs().are_contents_opaque(),
         Normalize(quad_spec.sqs().opacity()), SkBlendMode::kSrcOver,
         quad_spec.sqs().sorting_context_id());
@@ -360,12 +361,12 @@
                                                .transform_to_root_target());
     }
 
-    shared_quad_state->SetAll(transform, GetRectFromProtobuf(quad_spec.rect()),
-                              GetRectFromProtobuf(quad_spec.visible_rect()),
-                              gfx::RRectF(), gfx::Rect(), /*is_clipped=*/false,
-                              /*are_contents_opaque=*/true, /*opacity=*/1.0,
-                              SkBlendMode::kSrcOver,
-                              /*sorting_context_id=*/0);
+    shared_quad_state->SetAll(
+        transform, GetRectFromProtobuf(quad_spec.rect()),
+        GetRectFromProtobuf(quad_spec.visible_rect()), gfx::MaskFilterInfo(),
+        gfx::Rect(), /*is_clipped=*/false,
+        /*are_contents_opaque=*/true, /*opacity=*/1.0, SkBlendMode::kSrcOver,
+        /*sorting_context_id=*/0);
   }
 }
 
diff --git a/components/viz/service/compositor_frame_fuzzer/fuzzer_browser_process.cc b/components/viz/service/compositor_frame_fuzzer/fuzzer_browser_process.cc
index 628c046..8820b5c3 100644
--- a/components/viz/service/compositor_frame_fuzzer/fuzzer_browser_process.cc
+++ b/components/viz/service/compositor_frame_fuzzer/fuzzer_browser_process.cc
@@ -130,7 +130,7 @@
   renderer_sqs->SetAll(gfx::Transform(1.0, 0.0, 0.0, 1.0, 0, 80),
                        gfx::Rect(kRendererFrameSize),
                        gfx::Rect(kRendererFrameSize),
-                       /*rounded_corner_bounds=*/gfx::RRectF(),
+                       /*mask_filter_info=*/gfx::MaskFilterInfo(),
                        gfx::Rect(kRendererFrameSize), /*is_clipped=*/false,
                        /*are_contents_opaque=*/false, /*opacity=*/1,
                        SkBlendMode::kSrcOver, /*sorting_context_id=*/0);
@@ -144,7 +144,7 @@
   auto* toolbar_sqs = pass->CreateAndAppendSharedQuadState();
   toolbar_sqs->SetAll(
       gfx::Transform(), gfx::Rect(kTopBarSize), gfx::Rect(kTopBarSize),
-      /*rounded_corner_bounds=*/gfx::RRectF(), gfx::Rect(kTopBarSize),
+      /*mask_filter_info=*/gfx::MaskFilterInfo(), gfx::Rect(kTopBarSize),
       /*is_clipped=*/false, /*are_contents_opaque=*/false,
       /*opacity=*/1, SkBlendMode::kSrcOver,
       /*sorting_context_id=*/0);
diff --git a/components/viz/service/display/ca_layer_overlay.cc b/components/viz/service/display/ca_layer_overlay.cc
index cf09d0d..72426617 100644
--- a/components/viz/service/display/ca_layer_overlay.cc
+++ b/components/viz/service/display/ca_layer_overlay.cc
@@ -208,10 +208,10 @@
     // possible to make rounded corner rects independent of clip rect (by adding
     // another CALayer to the tree). Handling non-single border radii is also,
     // but requires APIs not supported on all macOS versions.
-    if (!quad->shared_quad_state->rounded_corner_bounds.IsEmpty()) {
+    if (quad->shared_quad_state->mask_filter_info.HasRoundedCorners()) {
       DCHECK(quad->shared_quad_state->is_clipped);
-      if (quad->shared_quad_state->rounded_corner_bounds.GetType() >
-          gfx::RRectF::Type::kSingle) {
+      if (quad->shared_quad_state->mask_filter_info.rounded_corner_bounds()
+              .GetType() > gfx::RRectF::Type::kSingle) {
         return CA_LAYER_FAILED_QUAD_ROUNDED_CORNER_NOT_UNIFORM;
       }
     }
@@ -238,7 +238,7 @@
       most_recent_overlay_shared_state_->clip_rect =
           gfx::RectF(quad->shared_quad_state->clip_rect);
       most_recent_overlay_shared_state_->rounded_corner_bounds =
-          quad->shared_quad_state->rounded_corner_bounds;
+          quad->shared_quad_state->mask_filter_info.rounded_corner_bounds();
 
       most_recent_overlay_shared_state_->opacity =
           quad->shared_quad_state->opacity;
diff --git a/components/viz/service/display/damage_frame_annotator.cc b/components/viz/service/display/damage_frame_annotator.cc
index a65710d..38d85d4e 100644
--- a/components/viz/service/display/damage_frame_annotator.cc
+++ b/components/viz/service/display/damage_frame_annotator.cc
@@ -53,7 +53,7 @@
     SharedQuadState* new_sqs = render_pass->shared_quad_state_list
                                    .AllocateAndConstruct<SharedQuadState>();
     new_sqs->SetAll(annotation.transform, output_rect, output_rect,
-                    gfx::RRectF(), output_rect, true, false, 1.f,
+                    gfx::MaskFilterInfo(), output_rect, true, false, 1.f,
                     SkBlendMode::kSrcOver, 0);
 
     DebugBorderDrawQuad* new_quad =
diff --git a/components/viz/service/display/dc_layer_overlay.cc b/components/viz/service/display/dc_layer_overlay.cc
index 680d771f..52cbb6ea 100644
--- a/components/viz/service/display/dc_layer_overlay.cc
+++ b/components/viz/service/display/dc_layer_overlay.cc
@@ -104,7 +104,7 @@
     return DC_LAYER_FAILED_TOO_MANY_OVERLAYS;
 
   // Rounded corner on overlays are not supported.
-  if (!quad->shared_quad_state->rounded_corner_bounds.IsEmpty())
+  if (quad->shared_quad_state->mask_filter_info.HasRoundedCorners())
     return DC_LAYER_FAILED_ROUNDED_CORNERS;
 
   auto quad_target_rect = gfx::ToEnclosingRect(ClippedQuadRectangle(quad));
@@ -172,7 +172,7 @@
   }
 
   // Rounded corner on overlays are not supported.
-  if (!quad->shared_quad_state->rounded_corner_bounds.IsEmpty())
+  if (quad->shared_quad_state->mask_filter_info.HasRoundedCorners())
     return DC_LAYER_FAILED_ROUNDED_CORNERS;
 
   auto quad_target_rect = gfx::ToEnclosingRect(ClippedQuadRectangle(quad));
diff --git a/components/viz/service/display/direct_renderer.cc b/components/viz/service/display/direct_renderer.cc
index 3b9c1195..5b094621 100644
--- a/components/viz/service/display/direct_renderer.cc
+++ b/components/viz/service/display/direct_renderer.cc
@@ -919,12 +919,15 @@
 
 bool DirectRenderer::ShouldApplyRoundedCorner(const DrawQuad* quad) const {
   const SharedQuadState* sqs = quad->shared_quad_state;
-  const gfx::RRectF& rounded_corner_bounds = sqs->rounded_corner_bounds;
+  const gfx::MaskFilterInfo& mask_filter_info = sqs->mask_filter_info;
 
   // There is no rounded corner set.
-  if (rounded_corner_bounds.IsEmpty())
+  if (!mask_filter_info.HasRoundedCorners())
     return false;
 
+  const gfx::RRectF& rounded_corner_bounds =
+      mask_filter_info.rounded_corner_bounds();
+
   const gfx::RectF target_quad = cc::MathUtil::MapClippedRect(
       sqs->quad_to_target_transform, gfx::RectF(quad->visible_rect));
 
diff --git a/components/viz/service/display/display.cc b/components/viz/service/display/display.cc
index 6df3fba..249aa00 100644
--- a/components/viz/service/display/display.cc
+++ b/components/viz/service/display/display.cc
@@ -1107,9 +1107,10 @@
           // If a rounded corner is being applied then the visible rect for the
           // sqs is actually even smaller. Reduce the rect size to get a
           // rounded corner adjusted occluding region.
-          if (!last_sqs->rounded_corner_bounds.IsEmpty()) {
-            sqs_rect_in_target.Intersect(gfx::ToEnclosedRect(
-                GetOccludingRectForRRectF(last_sqs->rounded_corner_bounds)));
+          if (last_sqs->mask_filter_info.HasRoundedCorners()) {
+            sqs_rect_in_target.Intersect(
+                gfx::ToEnclosedRect(GetOccludingRectForRRectF(
+                    last_sqs->mask_filter_info.rounded_corner_bounds())));
           }
 
           if (last_sqs->is_clipped)
diff --git a/components/viz/service/display/display_perftest.cc b/components/viz/service/display/display_perftest.cc
index 37faed27..59e18d36 100644
--- a/components/viz/service/display/display_perftest.cc
+++ b/components/viz/service/display/display_perftest.cc
@@ -98,7 +98,7 @@
 
     SharedQuadState* state = render_pass->CreateAndAppendSharedQuadState();
     state->SetAll(quad_transform, rect, rect,
-                  /*rounded_corner_bounds=*/gfx::RRectF(), rect, is_clipped,
+                  /*mask_filter_info=*/gfx::MaskFilterInfo(), rect, is_clipped,
                   are_contents_opaque, opacity, blend_mode, sorting_context_id);
     return state;
   }
diff --git a/components/viz/service/display/display_unittest.cc b/components/viz/service/display/display_unittest.cc
index 6213ce7..7ffe50e 100644
--- a/components/viz/service/display/display_unittest.cc
+++ b/components/viz/service/display/display_unittest.cc
@@ -771,7 +771,7 @@
       shared_quad_state1->SetAll(
           gfx::Transform(), /*quad_layer_rect=*/sub_surface_rect,
           /*visible_quad_layer_rect=*/sub_surface_rect,
-          /*rounded_corner_bounds=*/gfx::RRectF(),
+          /*mask_filter_info=*/gfx::MaskFilterInfo(),
           /*clip_rect=*/sub_surface_rect, /*is_clipped=*/false,
           /*are_contents_opaque=*/true, /*opacity=*/1.0f, SkBlendMode::kSrcOver,
           /*sorting_context_id=*/0);
@@ -787,7 +787,7 @@
       gfx::Rect rect1(display_size);
       shared_quad_state2->SetAll(gfx::Transform(), /*quad_layer_rect=*/rect1,
                                  /*visible_quad_layer_rect=*/rect1,
-                                 /*rounded_corner_bounds=*/gfx::RRectF(),
+                                 /*mask_filter_info=*/gfx::MaskFilterInfo(),
                                  /*clip_rect=*/rect1, /*is_clipped=*/false,
                                  /*are_contents_opaque=*/true, /*opacity=*/1.0f,
                                  SkBlendMode::kSrcOver,
@@ -927,13 +927,13 @@
 
     auto* src_sqs = render_pass->CreateAndAppendSharedQuadState();
     src_sqs->SetAll(
-        gfx::Transform(), src_rect, src_rect, gfx::RRectF(), src_rect,
+        gfx::Transform(), src_rect, src_rect, gfx::MaskFilterInfo(), src_rect,
         is_clipped, are_contents_opaque, opacity,
         is_root_render_pass ? SkBlendMode::kSrcOver : SkBlendMode::kSrcIn, 0);
     auto* dest_sqs = render_pass->CreateAndAppendSharedQuadState();
     dest_sqs->SetAll(
-        gfx::Transform(), dest_rect, dest_rect, gfx::RRectF(), dest_rect,
-        is_clipped, are_contents_opaque, opacity,
+        gfx::Transform(), dest_rect, dest_rect, gfx::MaskFilterInfo(),
+        dest_rect, is_clipped, are_contents_opaque, opacity,
         is_root_render_pass ? SkBlendMode::kSrcOver : SkBlendMode::kDstIn, 0);
     auto* src_quad =
         render_pass->quad_list.AllocateAndConstruct<SolidColorDrawQuad>();
@@ -993,7 +993,7 @@
   for (int i = 0; i < 3; i++) {
     shared_quad_states[i] = root_render_pass->CreateAndAppendSharedQuadState();
     shared_quad_states[i]->SetAll(
-        gfx::Transform(), rects[i], rects[i], gfx::RRectF(), rects[i],
+        gfx::Transform(), rects[i], rects[i], gfx::MaskFilterInfo(), rects[i],
         is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
 
     if (i == 0) {  // Backdrop filter quad
@@ -1058,9 +1058,9 @@
   // |    |
   // +----+
   {
-    shared_quad_state->SetAll(gfx::Transform(), rect1, rect1, gfx::RRectF(),
-                              rect1, is_clipped, are_contents_opaque, opacity,
-                              SkBlendMode::kSrcOver, 0);
+    shared_quad_state->SetAll(
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
+        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
 
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
     EXPECT_EQ(1u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
@@ -1083,13 +1083,13 @@
   // +----+ |
   //   +----+
   {
-    shared_quad_state->SetAll(gfx::Transform(), rect1, rect1, gfx::RRectF(),
-                              rect1, is_clipped, are_contents_opaque, opacity,
-                              SkBlendMode::kSrcOver, 0);
+    shared_quad_state->SetAll(
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
+        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
 
-    shared_quad_state2->SetAll(gfx::Transform(), rect2, rect2, gfx::RRectF(),
-                               rect2, is_clipped, are_contents_opaque, opacity,
-                               SkBlendMode::kSrcOver, 0);
+    shared_quad_state2->SetAll(
+        gfx::Transform(), rect2, rect2, gfx::MaskFilterInfo(), rect2,
+        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
 
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect2, rect2, SK_ColorBLACK, false);
@@ -1118,13 +1118,13 @@
   //   |  |                                    |  |
   //   +--+                                    +--+
   {
-    shared_quad_state->SetAll(gfx::Transform(), rect1, rect1, gfx::RRectF(),
-                              rect1, is_clipped, are_contents_opaque, opacity,
-                              SkBlendMode::kSrcOver, 0);
+    shared_quad_state->SetAll(
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
+        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
 
-    shared_quad_state2->SetAll(gfx::Transform(), rect3, rect3, gfx::RRectF(),
-                               rect3, is_clipped, are_contents_opaque, opacity,
-                               SkBlendMode::kSrcOver, 0);
+    shared_quad_state2->SetAll(
+        gfx::Transform(), rect3, rect3, gfx::MaskFilterInfo(), rect3,
+        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
 
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect3, rect3, SK_ColorBLACK, false);
@@ -1152,13 +1152,13 @@
   // +----+                                      +----+
   //  +--+                                        +--+
   {
-    shared_quad_state->SetAll(gfx::Transform(), rect7, rect7, gfx::RRectF(),
-                              rect7, is_clipped, are_contents_opaque, opacity,
-                              SkBlendMode::kSrcOver, 0);
+    shared_quad_state->SetAll(
+        gfx::Transform(), rect7, rect7, gfx::MaskFilterInfo(), rect7,
+        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
 
-    shared_quad_state2->SetAll(gfx::Transform(), rect6, rect6, gfx::RRectF(),
-                               rect6, is_clipped, are_contents_opaque, opacity,
-                               SkBlendMode::kSrcOver, 0);
+    shared_quad_state2->SetAll(
+        gfx::Transform(), rect6, rect6, gfx::MaskFilterInfo(), rect6,
+        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
 
     quad->SetNew(shared_quad_state, rect7, rect7, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect6, rect6, SK_ColorBLACK, false);
@@ -1185,13 +1185,13 @@
   // |    |   +--+
   // +----+
   {
-    shared_quad_state->SetAll(gfx::Transform(), rect1, rect1, gfx::RRectF(),
-                              rect1, is_clipped, are_contents_opaque, opacity,
-                              SkBlendMode::kSrcOver, 0);
+    shared_quad_state->SetAll(
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
+        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
 
-    shared_quad_state2->SetAll(gfx::Transform(), rect4, rect4, gfx::RRectF(),
-                               rect4, is_clipped, are_contents_opaque, opacity,
-                               SkBlendMode::kSrcOver, 0);
+    shared_quad_state2->SetAll(
+        gfx::Transform(), rect4, rect4, gfx::MaskFilterInfo(), rect4,
+        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
 
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect4, rect4, SK_ColorBLACK, false);
@@ -1217,13 +1217,13 @@
   // +-----+|
   // +------+
   {
-    shared_quad_state->SetAll(gfx::Transform(), rect1, rect1, gfx::RRectF(),
-                              rect1, is_clipped, are_contents_opaque, opacity,
-                              SkBlendMode::kSrcOver, 0);
+    shared_quad_state->SetAll(
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
+        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
 
-    shared_quad_state2->SetAll(gfx::Transform(), rect5, rect5, gfx::RRectF(),
-                               rect5, is_clipped, are_contents_opaque, opacity,
-                               SkBlendMode::kSrcOver, 0);
+    shared_quad_state2->SetAll(
+        gfx::Transform(), rect5, rect5, gfx::MaskFilterInfo(), rect5,
+        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
 
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect5, rect5, SK_ColorBLACK, false);
@@ -1275,9 +1275,9 @@
         frame.render_pass_list.front()->CreateAndAppendSharedQuadState();
     auto* quad = frame.render_pass_list.front()
                      ->quad_list.AllocateAndConstruct<SolidColorDrawQuad>();
-    shared_quad_state->SetAll(gfx::Transform(), rect, rect, gfx::RRectF(), rect,
-                              is_clipped, are_contents_opaque, opacity,
-                              SkBlendMode::kSrcOver, 0);
+    shared_quad_state->SetAll(
+        gfx::Transform(), rect, rect, gfx::MaskFilterInfo(), rect, is_clipped,
+        are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
     quad->SetNew(shared_quad_state, rect, rect, SK_ColorBLACK, false);
   }
 
@@ -1331,9 +1331,9 @@
         frame.render_pass_list.front()->CreateAndAppendSharedQuadState();
     auto* quad = frame.render_pass_list.front()
                      ->quad_list.AllocateAndConstruct<SolidColorDrawQuad>();
-    shared_quad_state->SetAll(gfx::Transform(), rect, rect, gfx::RRectF(), rect,
-                              is_clipped, are_contents_opaque, opacity,
-                              SkBlendMode::kSrcOver, 0);
+    shared_quad_state->SetAll(
+        gfx::Transform(), rect, rect, gfx::MaskFilterInfo(), rect, is_clipped,
+        are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
     quad->SetNew(shared_quad_state, rect, rect, SK_ColorBLACK, false);
   }
 
@@ -1390,12 +1390,12 @@
   //                         |     |
   //                         +-----+
   {
-    shared_quad_state->SetAll(gfx::Transform(), rect1, rect1, gfx::RRectF(),
-                              rect1, is_clipped, are_contents_opaque, opacity,
-                              SkBlendMode::kSrcOver, 0);
-    shared_quad_state2->SetAll(gfx::Transform(), rect1, rect1, gfx::RRectF(),
-                               rect1, is_clipped, are_contents_opaque, opacity,
-                               SkBlendMode::kSrcOver, 0);
+    shared_quad_state->SetAll(
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
+        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
+    shared_quad_state2->SetAll(
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
+        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
 
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect1, rect1, SK_ColorBLACK, false);
@@ -1415,12 +1415,12 @@
   {
     quad2 = frame.render_pass_list.front()
                 ->quad_list.AllocateAndConstruct<SolidColorDrawQuad>();
-    shared_quad_state->SetAll(gfx::Transform(), rect1, rect1, gfx::RRectF(),
-                              rect1, is_clipped, are_contents_opaque, opacity,
-                              SkBlendMode::kSrcOver, 0);
-    shared_quad_state2->SetAll(gfx::Transform(), rect2, rect2, gfx::RRectF(),
-                               rect2, is_clipped, are_contents_opaque, opacity,
-                               SkBlendMode::kSrcOver, 0);
+    shared_quad_state->SetAll(
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
+        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
+    shared_quad_state2->SetAll(
+        gfx::Transform(), rect2, rect2, gfx::MaskFilterInfo(), rect2,
+        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
 
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect2, rect2, SK_ColorBLACK, false);
@@ -1441,12 +1441,12 @@
   {
     quad2 = frame.render_pass_list.front()
                 ->quad_list.AllocateAndConstruct<SolidColorDrawQuad>();
-    shared_quad_state->SetAll(gfx::Transform(), rect1, rect1, gfx::RRectF(),
-                              rect1, is_clipped, are_contents_opaque, opacity,
-                              SkBlendMode::kSrcOver, 0);
-    shared_quad_state2->SetAll(gfx::Transform(), rect3, rect3, gfx::RRectF(),
-                               rect3, is_clipped, are_contents_opaque, opacity,
-                               SkBlendMode::kSrcOver, 0);
+    shared_quad_state->SetAll(
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
+        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
+    shared_quad_state2->SetAll(
+        gfx::Transform(), rect3, rect3, gfx::MaskFilterInfo(), rect3,
+        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
 
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect3, rect3, SK_ColorBLACK, false);
@@ -1467,13 +1467,13 @@
   {
     quad2 = frame.render_pass_list.front()
                 ->quad_list.AllocateAndConstruct<SolidColorDrawQuad>();
-    shared_quad_state->SetAll(gfx::Transform(), rect1, rect1, gfx::RRectF(),
-                              rect1, is_clipped, are_contents_opaque, opacity,
-                              SkBlendMode::kSrcOver, 0);
+    shared_quad_state->SetAll(
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
+        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
 
-    shared_quad_state2->SetAll(gfx::Transform(), rect4, rect4, gfx::RRectF(),
-                               rect4, is_clipped, are_contents_opaque, opacity,
-                               SkBlendMode::kSrcOver, 0);
+    shared_quad_state2->SetAll(
+        gfx::Transform(), rect4, rect4, gfx::MaskFilterInfo(), rect4,
+        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
 
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect4, rect4, SK_ColorBLACK, false);
@@ -1532,11 +1532,11 @@
                     ->quad_list.AllocateAndConstruct<SolidColorDrawQuad>();
 
   {
-    shared_quad_state->SetAll(gfx::Transform(), rect1, rect1, gfx::RRectF(),
-                              rect1, is_clipped, are_contents_opaque, opacity,
-                              SkBlendMode::kSrcOver, 0);
-    shared_quad_state2->SetAll(half_scale, rect2, rect2, gfx::RRectF(), rect2,
-                               is_clipped, are_contents_opaque, opacity,
+    shared_quad_state->SetAll(
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
+        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
+    shared_quad_state2->SetAll(half_scale, rect2, rect2, gfx::MaskFilterInfo(),
+                               rect2, is_clipped, are_contents_opaque, opacity,
                                SkBlendMode::kSrcOver, 0);
 
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
@@ -1555,11 +1555,11 @@
   {
     quad2 = frame.render_pass_list.front()
                 ->quad_list.AllocateAndConstruct<SolidColorDrawQuad>();
-    shared_quad_state->SetAll(gfx::Transform(), rect1, rect1, gfx::RRectF(),
-                              rect1, is_clipped, are_contents_opaque, opacity,
-                              SkBlendMode::kSrcOver, 0);
-    shared_quad_state2->SetAll(half_scale, rect3, rect3, gfx::RRectF(), rect3,
-                               is_clipped, are_contents_opaque, opacity,
+    shared_quad_state->SetAll(
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
+        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
+    shared_quad_state2->SetAll(half_scale, rect3, rect3, gfx::MaskFilterInfo(),
+                               rect3, is_clipped, are_contents_opaque, opacity,
                                SkBlendMode::kSrcOver, 0);
 
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
@@ -1578,12 +1578,12 @@
   {
     quad2 = frame.render_pass_list.front()
                 ->quad_list.AllocateAndConstruct<SolidColorDrawQuad>();
-    shared_quad_state->SetAll(gfx::Transform(), rect1, rect1, gfx::RRectF(),
-                              rect1, is_clipped, are_contents_opaque, opacity,
-                              SkBlendMode::kSrcOver, 0);
+    shared_quad_state->SetAll(
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
+        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
 
-    shared_quad_state2->SetAll(half_scale, rect4, rect4, gfx::RRectF(), rect4,
-                               is_clipped, are_contents_opaque, opacity,
+    shared_quad_state2->SetAll(half_scale, rect4, rect4, gfx::MaskFilterInfo(),
+                               rect4, is_clipped, are_contents_opaque, opacity,
                                SkBlendMode::kSrcOver, 0);
 
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
@@ -1601,8 +1601,8 @@
   }
 
   {
-    shared_quad_state->SetAll(double_scale, rect1, rect1, gfx::RRectF(), rect1,
-                              is_clipped, are_contents_opaque, opacity,
+    shared_quad_state->SetAll(double_scale, rect1, rect1, gfx::MaskFilterInfo(),
+                              rect1, is_clipped, are_contents_opaque, opacity,
                               SkBlendMode::kSrcOver, 0);
 
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
@@ -1619,13 +1619,13 @@
   {
     quad2 = frame.render_pass_list.front()
                 ->quad_list.AllocateAndConstruct<SolidColorDrawQuad>();
-    shared_quad_state->SetAll(gfx::Transform(), rect1, rect1, gfx::RRectF(),
-                              rect1, is_clipped, are_contents_opaque, opacity,
-                              SkBlendMode::kSrcOver, 0);
+    shared_quad_state->SetAll(
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
+        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
 
-    shared_quad_state2->SetAll(double_scale, rect5, rect5, gfx::RRectF(), rect5,
-                               is_clipped, are_contents_opaque, opacity,
-                               SkBlendMode::kSrcOver, 0);
+    shared_quad_state2->SetAll(
+        double_scale, rect5, rect5, gfx::MaskFilterInfo(), rect5, is_clipped,
+        are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
 
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect5, rect5, SK_ColorBLACK, false);
@@ -1647,13 +1647,13 @@
   }
 
   {
-    shared_quad_state->SetAll(gfx::Transform(), rect1, rect1, gfx::RRectF(),
-                              rect1, is_clipped, are_contents_opaque, opacity,
-                              SkBlendMode::kSrcOver, 0);
+    shared_quad_state->SetAll(
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
+        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
 
-    shared_quad_state2->SetAll(double_scale, rect6, rect6, gfx::RRectF(), rect6,
-                               is_clipped, are_contents_opaque, opacity,
-                               SkBlendMode::kSrcOver, 0);
+    shared_quad_state2->SetAll(
+        double_scale, rect6, rect6, gfx::MaskFilterInfo(), rect6, is_clipped,
+        are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
 
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect6, rect6, SK_ColorBLACK, false);
@@ -1676,13 +1676,13 @@
   }
 
   {
-    shared_quad_state->SetAll(gfx::Transform(), rect1, rect1, gfx::RRectF(),
-                              rect1, is_clipped, are_contents_opaque, opacity,
-                              SkBlendMode::kSrcOver, 0);
+    shared_quad_state->SetAll(
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
+        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
 
-    shared_quad_state2->SetAll(double_scale, rect7, rect7, gfx::RRectF(), rect7,
-                               is_clipped, are_contents_opaque, opacity,
-                               SkBlendMode::kSrcOver, 0);
+    shared_quad_state2->SetAll(
+        double_scale, rect7, rect7, gfx::MaskFilterInfo(), rect7, is_clipped,
+        are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
 
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect7, rect7, SK_ColorBLACK, false);
@@ -1704,13 +1704,13 @@
   }
 
   {
-    shared_quad_state->SetAll(gfx::Transform(), rect1, rect1, gfx::RRectF(),
-                              rect1, is_clipped, are_contents_opaque, opacity,
-                              SkBlendMode::kSrcOver, 0);
+    shared_quad_state->SetAll(
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
+        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
 
-    shared_quad_state2->SetAll(double_scale, rect8, rect8, gfx::RRectF(), rect8,
-                               is_clipped, are_contents_opaque, opacity,
-                               SkBlendMode::kSrcOver, 0);
+    shared_quad_state2->SetAll(
+        double_scale, rect8, rect8, gfx::MaskFilterInfo(), rect8, is_clipped,
+        are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
 
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect8, rect8, SK_ColorBLACK, false);
@@ -1732,13 +1732,13 @@
   }
 
   {
-    shared_quad_state->SetAll(double_scale, rect10, rect10, gfx::RRectF(),
-                              rect10, is_clipped, are_contents_opaque, opacity,
-                              SkBlendMode::kSrcOver, 0);
+    shared_quad_state->SetAll(
+        double_scale, rect10, rect10, gfx::MaskFilterInfo(), rect10, is_clipped,
+        are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
 
-    shared_quad_state2->SetAll(double_scale, rect9, rect9, gfx::RRectF(), rect9,
-                               is_clipped, are_contents_opaque, opacity,
-                               SkBlendMode::kSrcOver, 0);
+    shared_quad_state2->SetAll(
+        double_scale, rect9, rect9, gfx::MaskFilterInfo(), rect9, is_clipped,
+        are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
 
     quad->SetNew(shared_quad_state, rect10, rect10, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect9, rect9, SK_ColorBLACK, false);
@@ -1792,11 +1792,11 @@
   gfx::Transform inverted;
 
   {
-    shared_quad_state->SetAll(gfx::Transform(), rect, rect, gfx::RRectF(), rect,
-                              is_clipped, are_contents_opaque, opacity,
-                              SkBlendMode::kSrcOver, 0);
-    shared_quad_state2->SetAll(zero_scale, rect, rect, gfx::RRectF(), rect,
-                               is_clipped, are_contents_opaque, opacity,
+    shared_quad_state->SetAll(
+        gfx::Transform(), rect, rect, gfx::MaskFilterInfo(), rect, is_clipped,
+        are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
+    shared_quad_state2->SetAll(zero_scale, rect, rect, gfx::MaskFilterInfo(),
+                               rect, is_clipped, are_contents_opaque, opacity,
                                SkBlendMode::kSrcOver, 0);
 
     quad->SetNew(shared_quad_state, rect, rect, SK_ColorBLACK, false);
@@ -1816,11 +1816,11 @@
   }
 
   {
-    shared_quad_state->SetAll(gfx::Transform(), rect, rect, gfx::RRectF(), rect,
-                              is_clipped, are_contents_opaque, opacity,
-                              SkBlendMode::kSrcOver, 0);
-    shared_quad_state2->SetAll(epsilon_scale, rect, rect, gfx::RRectF(), rect,
-                               is_clipped, are_contents_opaque, opacity,
+    shared_quad_state->SetAll(
+        gfx::Transform(), rect, rect, gfx::MaskFilterInfo(), rect, is_clipped,
+        are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
+    shared_quad_state2->SetAll(epsilon_scale, rect, rect, gfx::MaskFilterInfo(),
+                               rect, is_clipped, are_contents_opaque, opacity,
                                SkBlendMode::kSrcOver, 1);
 
     quad->SetNew(shared_quad_state, rect, rect, SK_ColorBLACK, false);
@@ -1846,12 +1846,12 @@
   }
 
   {
-    shared_quad_state->SetAll(gfx::Transform(), rect, rect, gfx::RRectF(), rect,
-                              is_clipped, are_contents_opaque, opacity,
-                              SkBlendMode::kSrcOver, 0);
-    shared_quad_state2->SetAll(larger_epsilon_scale, rect, rect, gfx::RRectF(),
-                               rect, is_clipped, are_contents_opaque, opacity,
-                               SkBlendMode::kSrcOver, 0);
+    shared_quad_state->SetAll(
+        gfx::Transform(), rect, rect, gfx::MaskFilterInfo(), rect, is_clipped,
+        are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
+    shared_quad_state2->SetAll(
+        larger_epsilon_scale, rect, rect, gfx::MaskFilterInfo(), rect,
+        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
 
     quad->SetNew(shared_quad_state, rect, rect, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect, rect, SK_ColorBLACK, false);
@@ -1895,12 +1895,12 @@
 
   {
     negative_scale.Scale3d(-1, 1, 1);
-    shared_quad_state->SetAll(gfx::Transform(), rect, rect, gfx::RRectF(), rect,
-                              is_clipped, are_contents_opaque, opacity,
-                              SkBlendMode::kSrcOver, 0);
-    shared_quad_state2->SetAll(negative_scale, rect, rect, gfx::RRectF(), rect,
-                               is_clipped, are_contents_opaque, opacity,
-                               SkBlendMode::kSrcOver, 0);
+    shared_quad_state->SetAll(
+        gfx::Transform(), rect, rect, gfx::MaskFilterInfo(), rect, is_clipped,
+        are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
+    shared_quad_state2->SetAll(
+        negative_scale, rect, rect, gfx::MaskFilterInfo(), rect, is_clipped,
+        are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
 
     quad->SetNew(shared_quad_state, rect, rect, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect, rect, SK_ColorBLACK, false);
@@ -1927,12 +1927,12 @@
   {
     negative_scale.MakeIdentity();
     negative_scale.Scale3d(1, -1, 1);
-    shared_quad_state->SetAll(gfx::Transform(), rect, rect, gfx::RRectF(), rect,
-                              is_clipped, are_contents_opaque, opacity,
-                              SkBlendMode::kSrcOver, 0);
-    shared_quad_state2->SetAll(negative_scale, rect, rect, gfx::RRectF(), rect,
-                               is_clipped, are_contents_opaque, opacity,
-                               SkBlendMode::kSrcOver, 0);
+    shared_quad_state->SetAll(
+        gfx::Transform(), rect, rect, gfx::MaskFilterInfo(), rect, is_clipped,
+        are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
+    shared_quad_state2->SetAll(
+        negative_scale, rect, rect, gfx::MaskFilterInfo(), rect, is_clipped,
+        are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
 
     quad->SetNew(shared_quad_state, rect, rect, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect, rect, SK_ColorBLACK, false);
@@ -1959,12 +1959,12 @@
   {
     negative_scale.MakeIdentity();
     negative_scale.Scale3d(1, 1, -1);
-    shared_quad_state->SetAll(gfx::Transform(), rect, rect, gfx::RRectF(), rect,
-                              is_clipped, are_contents_opaque, opacity,
-                              SkBlendMode::kSrcOver, 0);
-    shared_quad_state2->SetAll(negative_scale, rect, rect, gfx::RRectF(), rect,
-                               is_clipped, are_contents_opaque, opacity,
-                               SkBlendMode::kSrcOver, 0);
+    shared_quad_state->SetAll(
+        gfx::Transform(), rect, rect, gfx::MaskFilterInfo(), rect, is_clipped,
+        are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
+    shared_quad_state2->SetAll(
+        negative_scale, rect, rect, gfx::MaskFilterInfo(), rect, is_clipped,
+        are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
 
     quad->SetNew(shared_quad_state, rect, rect, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect, rect, SK_ColorBLACK, false);
@@ -2021,12 +2021,12 @@
                     ->quad_list.AllocateAndConstruct<SolidColorDrawQuad>();
   {
     // Apply rotation transform on |rect1| only.
-    shared_quad_state->SetAll(rotate, rect1, rect1, gfx::RRectF(), rect1,
-                              is_clipped, are_contents_opaque, opacity,
+    shared_quad_state->SetAll(rotate, rect1, rect1, gfx::MaskFilterInfo(),
+                              rect1, is_clipped, are_contents_opaque, opacity,
                               SkBlendMode::kSrcOver, 0);
-    shared_quad_state2->SetAll(gfx::Transform(), rect2, rect2, gfx::RRectF(),
-                               rect2, is_clipped, are_contents_opaque, opacity,
-                               SkBlendMode::kSrcOver, 0);
+    shared_quad_state2->SetAll(
+        gfx::Transform(), rect2, rect2, gfx::MaskFilterInfo(), rect2,
+        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect2, rect2, SK_ColorBLACK, false);
     EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
@@ -2045,11 +2045,11 @@
 
   {
     // Apply rotation transform on |rect1| and |rect2|.
-    shared_quad_state->SetAll(rotate, rect1, rect1, gfx::RRectF(), rect1,
-                              is_clipped, are_contents_opaque, opacity,
+    shared_quad_state->SetAll(rotate, rect1, rect1, gfx::MaskFilterInfo(),
+                              rect1, is_clipped, are_contents_opaque, opacity,
                               SkBlendMode::kSrcOver, 0);
-    shared_quad_state2->SetAll(rotate, rect2, rect2, gfx::RRectF(), rect2,
-                               is_clipped, are_contents_opaque, opacity,
+    shared_quad_state2->SetAll(rotate, rect2, rect2, gfx::MaskFilterInfo(),
+                               rect2, is_clipped, are_contents_opaque, opacity,
                                SkBlendMode::kSrcOver, 0);
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect2, rect2, SK_ColorBLACK, false);
@@ -2067,12 +2067,12 @@
   {
     quad2 = frame.render_pass_list.front()
                 ->quad_list.AllocateAndConstruct<SolidColorDrawQuad>();
-    shared_quad_state->SetAll(rotate, rect1, rect1, gfx::RRectF(), rect1,
-                              is_clipped, are_contents_opaque, opacity,
+    shared_quad_state->SetAll(rotate, rect1, rect1, gfx::MaskFilterInfo(),
+                              rect1, is_clipped, are_contents_opaque, opacity,
                               SkBlendMode::kSrcOver, 0);
-    shared_quad_state2->SetAll(gfx::Transform(), rect3, rect3, gfx::RRectF(),
-                               rect3, is_clipped, are_contents_opaque, opacity,
-                               SkBlendMode::kSrcOver, 0);
+    shared_quad_state2->SetAll(
+        gfx::Transform(), rect3, rect3, gfx::MaskFilterInfo(), rect3,
+        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect3, rect3, SK_ColorBLACK, false);
     EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
@@ -2093,11 +2093,11 @@
     // Since we only support updating |visible_rect| of DrawQuad with scale
     // or translation transform and rotation transform applies to quads,
     // |visible_rect| of |quad2| should not be changed.
-    shared_quad_state->SetAll(rotate, rect1, rect1, gfx::RRectF(), rect1,
-                              is_clipped, are_contents_opaque, opacity,
+    shared_quad_state->SetAll(rotate, rect1, rect1, gfx::MaskFilterInfo(),
+                              rect1, is_clipped, are_contents_opaque, opacity,
                               SkBlendMode::kSrcOver, 0);
-    shared_quad_state2->SetAll(rotate, rect3, rect3, gfx::RRectF(), rect3,
-                               is_clipped, are_contents_opaque, opacity,
+    shared_quad_state2->SetAll(rotate, rect3, rect3, gfx::MaskFilterInfo(),
+                               rect3, is_clipped, are_contents_opaque, opacity,
                                SkBlendMode::kSrcOver, 0);
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect3, rect3, SK_ColorBLACK, false);
@@ -2146,12 +2146,12 @@
   auto* quad2 = frame.render_pass_list.front()
                     ->quad_list.AllocateAndConstruct<SolidColorDrawQuad>();
   {
-    shared_quad_state->SetAll(perspective, rect1, rect1, gfx::RRectF(), rect1,
-                              is_clipped, are_contents_opaque, opacity,
+    shared_quad_state->SetAll(perspective, rect1, rect1, gfx::MaskFilterInfo(),
+                              rect1, is_clipped, are_contents_opaque, opacity,
                               SkBlendMode::kSrcOver, 0);
-    shared_quad_state2->SetAll(gfx::Transform(), rect1, rect1, gfx::RRectF(),
-                               rect1, is_clipped, are_contents_opaque, opacity,
-                               SkBlendMode::kSrcOver, 0);
+    shared_quad_state2->SetAll(
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
+        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect1, rect1, SK_ColorBLACK, false);
     EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
@@ -2170,11 +2170,11 @@
   }
 
   {
-    shared_quad_state->SetAll(gfx::Transform(), rect1, rect1, gfx::RRectF(),
-                              rect1, is_clipped, are_contents_opaque, opacity,
-                              SkBlendMode::kSrcOver, 0);
-    shared_quad_state2->SetAll(perspective, rect2, rect2, gfx::RRectF(), rect2,
-                               is_clipped, are_contents_opaque, opacity,
+    shared_quad_state->SetAll(
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
+        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
+    shared_quad_state2->SetAll(perspective, rect2, rect2, gfx::MaskFilterInfo(),
+                               rect2, is_clipped, are_contents_opaque, opacity,
                                SkBlendMode::kSrcOver, 0);
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect2, rect2, SK_ColorBLACK, false);
@@ -2216,12 +2216,13 @@
   auto* quad2 = frame.render_pass_list.front()
                     ->quad_list.AllocateAndConstruct<SolidColorDrawQuad>();
   {
-    shared_quad_state->SetAll(gfx::Transform(), rect1, rect1, gfx::RRectF(),
-                              rect1, is_clipped, are_contents_opaque,
-                              opacityLess1, SkBlendMode::kSrcOver, 0);
-    shared_quad_state2->SetAll(gfx::Transform(), rect2, rect2, gfx::RRectF(),
-                               rect2, is_clipped, are_contents_opaque, opacity1,
-                               SkBlendMode::kSrcOver, 0);
+    shared_quad_state->SetAll(gfx::Transform(), rect1, rect1,
+                              gfx::MaskFilterInfo(), rect1, is_clipped,
+                              are_contents_opaque, opacityLess1,
+                              SkBlendMode::kSrcOver, 0);
+    shared_quad_state2->SetAll(
+        gfx::Transform(), rect2, rect2, gfx::MaskFilterInfo(), rect2,
+        is_clipped, are_contents_opaque, opacity1, SkBlendMode::kSrcOver, 0);
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect2, rect2, SK_ColorBLACK, false);
     EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
@@ -2238,12 +2239,12 @@
   }
 
   {
-    shared_quad_state->SetAll(gfx::Transform(), rect1, rect1, gfx::RRectF(),
-                              rect1, is_clipped, are_contents_opaque, opacity1,
-                              SkBlendMode::kSrcOver, 0);
-    shared_quad_state2->SetAll(gfx::Transform(), rect2, rect2, gfx::RRectF(),
-                               rect2, is_clipped, are_contents_opaque, opacity1,
-                               SkBlendMode::kSrcOver, 0);
+    shared_quad_state->SetAll(
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
+        is_clipped, are_contents_opaque, opacity1, SkBlendMode::kSrcOver, 0);
+    shared_quad_state2->SetAll(
+        gfx::Transform(), rect2, rect2, gfx::MaskFilterInfo(), rect2,
+        is_clipped, are_contents_opaque, opacity1, SkBlendMode::kSrcOver, 0);
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect2, rect2, SK_ColorBLACK, false);
     EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
@@ -2280,12 +2281,12 @@
   auto* quad2 = frame.render_pass_list.front()
                     ->quad_list.AllocateAndConstruct<SolidColorDrawQuad>();
   {
-    shared_quad_state->SetAll(gfx::Transform(), rect1, rect1, gfx::RRectF(),
-                              rect1, is_clipped, transparent_content, opacity,
-                              SkBlendMode::kSrcOver, 0);
-    shared_quad_state2->SetAll(gfx::Transform(), rect2, rect2, gfx::RRectF(),
-                               rect2, is_clipped, opaque_content, opacity,
-                               SkBlendMode::kSrcOver, 0);
+    shared_quad_state->SetAll(
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
+        is_clipped, transparent_content, opacity, SkBlendMode::kSrcOver, 0);
+    shared_quad_state2->SetAll(
+        gfx::Transform(), rect2, rect2, gfx::MaskFilterInfo(), rect2,
+        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect2, rect2, SK_ColorBLACK, false);
     EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
@@ -2302,12 +2303,12 @@
   }
 
   {
-    shared_quad_state->SetAll(gfx::Transform(), rect1, rect1, gfx::RRectF(),
-                              rect1, is_clipped, opaque_content, opacity,
-                              SkBlendMode::kSrcOver, 0);
-    shared_quad_state2->SetAll(gfx::Transform(), rect2, rect2, gfx::RRectF(),
-                               rect2, is_clipped, opaque_content, opacity,
-                               SkBlendMode::kSrcOver, 0);
+    shared_quad_state->SetAll(
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
+        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
+    shared_quad_state2->SetAll(
+        gfx::Transform(), rect2, rect2, gfx::MaskFilterInfo(), rect2,
+        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect2, rect2, SK_ColorBLACK, false);
     EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
@@ -2351,12 +2352,12 @@
   //                         |     |
   //                         +-----+
   {
-    shared_quad_state->SetAll(translate_back, rect1, rect1, gfx::RRectF(),
-                              rect1, is_clipped, are_contents_opaque, opacity,
-                              SkBlendMode::kSrcOver, 1);
-    shared_quad_state2->SetAll(gfx::Transform(), rect1, rect1, gfx::RRectF(),
-                               rect1, is_clipped, are_contents_opaque, opacity,
-                               SkBlendMode::kSrcOver, 1);
+    shared_quad_state->SetAll(
+        translate_back, rect1, rect1, gfx::MaskFilterInfo(), rect1, is_clipped,
+        are_contents_opaque, opacity, SkBlendMode::kSrcOver, 1);
+    shared_quad_state2->SetAll(
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
+        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 1);
 
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect2, rect1, SK_ColorBLACK, false);
@@ -2409,12 +2410,12 @@
     //   +----+
     //           +-+
     //           +-+
-    shared_quad_state->SetAll(gfx::Transform(), rect1, rect1, gfx::RRectF(),
-                              rect1, is_clipped, transparent_content, opacity,
-                              SkBlendMode::kSrcOver, 0);
-    shared_quad_state2->SetAll(gfx::Transform(), rect2, rect2, gfx::RRectF(),
-                               rect2, is_clipped, opaque_content, opacity,
-                               SkBlendMode::kSrcOver, 0);
+    shared_quad_state->SetAll(
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
+        is_clipped, transparent_content, opacity, SkBlendMode::kSrcOver, 0);
+    shared_quad_state2->SetAll(
+        gfx::Transform(), rect2, rect2, gfx::MaskFilterInfo(), rect2,
+        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect2, rect2, SK_ColorBLACK, false);
     EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
@@ -2438,12 +2439,12 @@
     //   +----+                       =>                              | +-+ |
     //           +-+                                                  | +-+ |
     //           +-+                                                  +-----+
-    shared_quad_state->SetAll(translate_up, rect1, rect1, gfx::RRectF(), rect1,
-                              is_clipped, opaque_content, opacity,
+    shared_quad_state->SetAll(translate_up, rect1, rect1, gfx::MaskFilterInfo(),
+                              rect1, is_clipped, opaque_content, opacity,
                               SkBlendMode::kSrcOver, 0);
-    shared_quad_state2->SetAll(gfx::Transform(), rect2, rect2, gfx::RRectF(),
-                               rect2, is_clipped, opaque_content, opacity,
-                               SkBlendMode::kSrcOver, 0);
+    shared_quad_state2->SetAll(
+        gfx::Transform(), rect2, rect2, gfx::MaskFilterInfo(), rect2,
+        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect2, rect2, SK_ColorBLACK, false);
     EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
@@ -2468,12 +2469,12 @@
     //           +---+                                                 +----+
     quad2 = frame.render_pass_list.front()
                 ->quad_list.AllocateAndConstruct<SolidColorDrawQuad>();
-    shared_quad_state->SetAll(translate_up, rect1, rect1, gfx::RRectF(), rect1,
-                              is_clipped, opaque_content, opacity,
+    shared_quad_state->SetAll(translate_up, rect1, rect1, gfx::MaskFilterInfo(),
+                              rect1, is_clipped, opaque_content, opacity,
                               SkBlendMode::kSrcOver, 0);
-    shared_quad_state2->SetAll(gfx::Transform(), rect3, rect3, gfx::RRectF(),
-                               rect3, is_clipped, opaque_content, opacity,
-                               SkBlendMode::kSrcOver, 0);
+    shared_quad_state2->SetAll(
+        gfx::Transform(), rect3, rect3, gfx::MaskFilterInfo(), rect3,
+        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect3, rect3, SK_ColorBLACK, false);
     EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
@@ -2532,15 +2533,15 @@
     //   |    |----+             =>        |    |----+
     //   +----+                            +----+
     //
-    shared_quad_state->SetAll(gfx::Transform(), rect1, rect1, gfx::RRectF(),
-                              rect1, is_clipped, opaque_content, opacity,
-                              SkBlendMode::kSrcOver, 0);
-    shared_quad_state2->SetAll(gfx::Transform(), rect2, rect2, gfx::RRectF(),
-                               rect2, is_clipped, opaque_content, opacity,
-                               SkBlendMode::kSrcOver, 0);
-    shared_quad_state3->SetAll(gfx::Transform(), rect3, rect3, gfx::RRectF(),
-                               rect3, is_clipped, opaque_content, opacity,
-                               SkBlendMode::kSrcOver, 0);
+    shared_quad_state->SetAll(
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
+        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
+    shared_quad_state2->SetAll(
+        gfx::Transform(), rect2, rect2, gfx::MaskFilterInfo(), rect2,
+        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
+    shared_quad_state3->SetAll(
+        gfx::Transform(), rect3, rect3, gfx::MaskFilterInfo(), rect3,
+        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect2, rect2, SK_ColorBLACK, false);
     quad3->SetNew(shared_quad_state3, rect3, rect3, SK_ColorBLACK, false);
@@ -2567,9 +2568,9 @@
     //
     quad3 = frame.render_pass_list.front()
                 ->quad_list.AllocateAndConstruct<SolidColorDrawQuad>();
-    shared_quad_state3->SetAll(gfx::Transform(), rect4, rect4, gfx::RRectF(),
-                               rect4, is_clipped, opaque_content, opacity,
-                               SkBlendMode::kSrcOver, 0);
+    shared_quad_state3->SetAll(
+        gfx::Transform(), rect4, rect4, gfx::MaskFilterInfo(), rect4,
+        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
     quad3->SetNew(shared_quad_state3, rect4, rect4, SK_ColorBLACK, false);
     EXPECT_EQ(3u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
     display_->RemoveOverdrawQuads(&frame);
@@ -2596,9 +2597,9 @@
     //   |    |----+           =>          | |  |--|-+
     //   +----+                            +-|--+  |
     //                                       +-----+
-    shared_quad_state3->SetAll(gfx::Transform(), rect5, rect5, gfx::RRectF(),
-                               rect5, is_clipped, opaque_content, opacity,
-                               SkBlendMode::kSrcOver, 0);
+    shared_quad_state3->SetAll(
+        gfx::Transform(), rect5, rect5, gfx::MaskFilterInfo(), rect5,
+        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
     quad3->SetNew(shared_quad_state3, rect5, rect5, SK_ColorBLACK, false);
     EXPECT_EQ(3u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
     display_->RemoveOverdrawQuads(&frame);
@@ -2646,7 +2647,7 @@
     quads[i] =
         render_pass->quad_list.AllocateAndConstruct<SolidColorDrawQuad>();
     shared_quad_states[i]->SetAll(
-        gfx::Transform(), rects[i], rects[i], gfx::RRectF(), rects[i],
+        gfx::Transform(), rects[i], rects[i], gfx::MaskFilterInfo(), rects[i],
         false /*is_clipped*/, true /*are_contents_opaque*/, 1.f /*opacity*/,
         SkBlendMode::kSrcOver, 0 /*sorting_context_id*/);
     quads[i]->SetNew(shared_quad_states[i], rects[i], rects[i], SK_ColorBLACK,
@@ -2709,15 +2710,15 @@
     //   |    |----+             =>        |    |----+
     //   +----+                            +----+
     //
-    shared_quad_state->SetAll(gfx::Transform(), rect1, rect1, gfx::RRectF(),
-                              rect1, is_clipped, opaque_content, opacity,
-                              SkBlendMode::kSrcOver, 0);
-    shared_quad_state2->SetAll(gfx::Transform(), rect2, rect2, gfx::RRectF(),
-                               rect2, is_clipped, opaque_content, opacity,
-                               SkBlendMode::kSrcOver, 0);
-    shared_quad_state3->SetAll(gfx::Transform(), rect3, rect3, gfx::RRectF(),
-                               rect3, is_clipped, opaque_content, opacity,
-                               SkBlendMode::kSrcOver, 0);
+    shared_quad_state->SetAll(
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
+        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
+    shared_quad_state2->SetAll(
+        gfx::Transform(), rect2, rect2, gfx::MaskFilterInfo(), rect2,
+        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
+    shared_quad_state3->SetAll(
+        gfx::Transform(), rect3, rect3, gfx::MaskFilterInfo(), rect3,
+        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect2, rect2, SK_ColorBLACK, false);
     quad3->SetNew(shared_quad_state3, rect3, rect3, SK_ColorBLACK, false);
@@ -2782,12 +2783,12 @@
     //   +----+
     //
 
-    shared_quad_state->SetAll(gfx::Transform(), rect1, rect1, gfx::RRectF(),
-                              rect1, is_clipped, opaque_content, opacity,
-                              SkBlendMode::kSrcOver, 0);
-    shared_quad_state2->SetAll(gfx::Transform(), rect1, rect1, gfx::RRectF(),
-                               rect1, is_clipped, opaque_content, opacity,
-                               SkBlendMode::kSrcOver, 0);
+    shared_quad_state->SetAll(
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
+        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
+    shared_quad_state2->SetAll(
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
+        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
     quad1->SetNew(shared_quad_state2, rect1, rect1, render_pass_id,
                   mask_resource_id, gfx::RectF(), gfx::Size(),
@@ -2843,12 +2844,12 @@
     //   |   | ||
     //   +------+
     //
-    shared_quad_state->SetAll(gfx::Transform(), rect1, rect1, gfx::RRectF(),
-                              rect1, non_clipped, opaque_content, opacity,
-                              SkBlendMode::kSrcOver, 0);
-    shared_quad_state2->SetAll(gfx::Transform(), rect2, rect2, gfx::RRectF(),
-                               rect2, non_clipped, opaque_content, opacity,
-                               SkBlendMode::kSrcOver, 0);
+    shared_quad_state->SetAll(
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
+        non_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
+    shared_quad_state2->SetAll(
+        gfx::Transform(), rect2, rect2, gfx::MaskFilterInfo(), rect2,
+        non_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect2, rect2, SK_ColorBLACK, false);
     EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
@@ -2870,12 +2871,12 @@
     //
     quad2 = frame.render_pass_list.front()
                 ->quad_list.AllocateAndConstruct<SolidColorDrawQuad>();
-    shared_quad_state->SetAll(gfx::Transform(), rect1, rect1, gfx::RRectF(),
-                              clip_rect, clipped, opaque_content, opacity,
-                              SkBlendMode::kSrcOver, 0);
-    shared_quad_state2->SetAll(gfx::Transform(), rect2, rect2, gfx::RRectF(),
-                               rect2, non_clipped, opaque_content, opacity,
-                               SkBlendMode::kSrcOver, 0);
+    shared_quad_state->SetAll(
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), clip_rect,
+        clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
+    shared_quad_state2->SetAll(
+        gfx::Transform(), rect2, rect2, gfx::MaskFilterInfo(), rect2,
+        non_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect2, rect2, SK_ColorBLACK, false);
     EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
@@ -2899,12 +2900,12 @@
     //   |   +-+|             =>                      +--+++
     //   +------+
     //
-    shared_quad_state->SetAll(gfx::Transform(), rect1, rect1, gfx::RRectF(),
-                              clip_rect, clipped, opaque_content, opacity,
-                              SkBlendMode::kSrcOver, 0);
-    shared_quad_state2->SetAll(gfx::Transform(), rect3, rect3, gfx::RRectF(),
-                               rect3, non_clipped, opaque_content, opacity,
-                               SkBlendMode::kSrcOver, 0);
+    shared_quad_state->SetAll(
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), clip_rect,
+        clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
+    shared_quad_state2->SetAll(
+        gfx::Transform(), rect3, rect3, gfx::MaskFilterInfo(), rect3,
+        non_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect3, rect3, SK_ColorBLACK, false);
     EXPECT_EQ(2u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
@@ -2948,12 +2949,12 @@
   auto* quad2 = frame.render_pass_list.front()
                     ->quad_list.AllocateAndConstruct<SolidColorDrawQuad>();
   {
-    shared_quad_state->SetAll(gfx::Transform(), rect1, rect1, gfx::RRectF(),
-                              rect1, is_clipped, opaque_content, opacity,
-                              SkBlendMode::kSrcOver, 0);
-    shared_quad_state2->SetAll(gfx::Transform(), rect2, rect2, gfx::RRectF(),
-                               rect2, is_clipped, opaque_content, opacity,
-                               SkBlendMode::kSrcOver, 0);
+    shared_quad_state->SetAll(
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
+        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
+    shared_quad_state2->SetAll(
+        gfx::Transform(), rect2, rect2, gfx::MaskFilterInfo(), rect2,
+        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect2, rect2, SK_ColorBLACK, false);
     frame.render_pass_list.front()->copy_requests.push_back(
@@ -3018,18 +3019,18 @@
     // |       |   |        |
     // |   R1  |   |    R2  |
     // +-------+---+--------+
-    shared_quad_state->SetAll(gfx::Transform(), rect1, rect1, gfx::RRectF(),
-                              rect1, is_clipped, opaque_content, opacity,
-                              SkBlendMode::kSrcOver, 0);
-    shared_quad_state2->SetAll(gfx::Transform(), rect2, rect2, gfx::RRectF(),
-                               rect2, is_clipped, opaque_content, opacity,
-                               SkBlendMode::kSrcOver, 0);
-    shared_quad_state3->SetAll(gfx::Transform(), rect3, rect3, gfx::RRectF(),
-                               rect3, is_clipped, opaque_content, opacity,
-                               SkBlendMode::kSrcOver, 0);
-    shared_quad_state4->SetAll(gfx::Transform(), rect4, rect4, gfx::RRectF(),
-                               rect4, is_clipped, opaque_content, opacity,
-                               SkBlendMode::kSrcOver, 0);
+    shared_quad_state->SetAll(
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
+        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
+    shared_quad_state2->SetAll(
+        gfx::Transform(), rect2, rect2, gfx::MaskFilterInfo(), rect2,
+        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
+    shared_quad_state3->SetAll(
+        gfx::Transform(), rect3, rect3, gfx::MaskFilterInfo(), rect3,
+        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
+    shared_quad_state4->SetAll(
+        gfx::Transform(), rect4, rect4, gfx::MaskFilterInfo(), rect4,
+        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
     R1->SetNew(shared_quad_state, rect1, rect1, render_pass_id,
                mask_resource_id, gfx::RectF(), gfx::Size(),
                gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false, 1.0f);
@@ -3066,18 +3067,18 @@
     // |       |           |
     // |   R2  |       R1  |
     // +-------+-----------+
-    shared_quad_state->SetAll(gfx::Transform(), rect5, rect5, gfx::RRectF(),
-                              rect5, is_clipped, opaque_content, opacity,
-                              SkBlendMode::kSrcOver, 0);
-    shared_quad_state2->SetAll(gfx::Transform(), rect1, rect1, gfx::RRectF(),
-                               rect1, is_clipped, opaque_content, opacity,
-                               SkBlendMode::kSrcOver, 0);
-    shared_quad_state3->SetAll(gfx::Transform(), rect3, rect3, gfx::RRectF(),
-                               rect3, is_clipped, opaque_content, opacity,
-                               SkBlendMode::kSrcOver, 0);
-    shared_quad_state4->SetAll(gfx::Transform(), rect6, rect6, gfx::RRectF(),
-                               rect6, is_clipped, opaque_content, opacity,
-                               SkBlendMode::kSrcOver, 0);
+    shared_quad_state->SetAll(
+        gfx::Transform(), rect5, rect5, gfx::MaskFilterInfo(), rect5,
+        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
+    shared_quad_state2->SetAll(
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
+        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
+    shared_quad_state3->SetAll(
+        gfx::Transform(), rect3, rect3, gfx::MaskFilterInfo(), rect3,
+        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
+    shared_quad_state4->SetAll(
+        gfx::Transform(), rect6, rect6, gfx::MaskFilterInfo(), rect6,
+        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
     R1->SetNew(shared_quad_state, rect5, rect5, render_pass_id,
                mask_resource_id, gfx::RectF(), gfx::Size(),
                gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false, 1.0f);
@@ -3113,18 +3114,18 @@
     // |-----+     |       |
     // |   R2      |   R1  |
     // +-----------+-------+
-    shared_quad_state->SetAll(gfx::Transform(), rect5, rect5, gfx::RRectF(),
-                              rect5, is_clipped, opaque_content, opacity,
-                              SkBlendMode::kSrcOver, 0);
-    shared_quad_state2->SetAll(gfx::Transform(), rect1, rect1, gfx::RRectF(),
-                               rect1, is_clipped, opaque_content, opacity,
-                               SkBlendMode::kSrcOver, 0);
-    shared_quad_state3->SetAll(gfx::Transform(), rect3, rect3, gfx::RRectF(),
-                               rect3, is_clipped, opaque_content, opacity,
-                               SkBlendMode::kSrcOver, 0);
-    shared_quad_state4->SetAll(gfx::Transform(), rect7, rect7, gfx::RRectF(),
-                               rect7, is_clipped, opaque_content, opacity,
-                               SkBlendMode::kSrcOver, 0);
+    shared_quad_state->SetAll(
+        gfx::Transform(), rect5, rect5, gfx::MaskFilterInfo(), rect5,
+        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
+    shared_quad_state2->SetAll(
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
+        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
+    shared_quad_state3->SetAll(
+        gfx::Transform(), rect3, rect3, gfx::MaskFilterInfo(), rect3,
+        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
+    shared_quad_state4->SetAll(
+        gfx::Transform(), rect7, rect7, gfx::MaskFilterInfo(), rect7,
+        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
     R1->SetNew(shared_quad_state, rect5, rect5, render_pass_id,
                mask_resource_id, gfx::RectF(), gfx::Size(),
                gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false, 1.0f);
@@ -3202,11 +3203,11 @@
     // +--+--+
     // |  |  |
     // +--+--+
-    shared_quad_state->SetAll(gfx::Transform(), rect1, rect1, gfx::RRectF(),
-                              rect1, is_clipped, opaque_content, opacity,
-                              SkBlendMode::kSrcOver, 0);
+    shared_quad_state->SetAll(
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
+        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
     shared_quad_state2->SetAll(gfx::Transform(), rect_in_rect1, rect_in_rect1,
-                               gfx::RRectF(), rect_in_rect1, is_clipped,
+                               gfx::MaskFilterInfo(), rect_in_rect1, is_clipped,
                                opaque_content, opacity, SkBlendMode::kSrcOver,
                                0);
     quad1->SetNew(shared_quad_state, rect1_1, rect1_1, SK_ColorBLACK, false);
@@ -3246,8 +3247,8 @@
                 ->quad_list.AllocateAndConstruct<SolidColorDrawQuad>();
     shared_quad_state2->SetAll(
         gfx::Transform(), rect_intersects_rect1, rect_intersects_rect1,
-        gfx::RRectF(), rect_intersects_rect1, is_clipped, opaque_content,
-        opacity, SkBlendMode::kSrcOver, 0);
+        gfx::MaskFilterInfo(), rect_intersects_rect1, is_clipped,
+        opaque_content, opacity, SkBlendMode::kSrcOver, 0);
     quad5->SetNew(shared_quad_state2, rect_intersects_rect1,
                   rect_intersects_rect1, SK_ColorBLACK, false);
     EXPECT_EQ(5u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
@@ -3285,12 +3286,12 @@
 
     auto* quad6 = frame.render_pass_list.front()
                       ->quad_list.AllocateAndConstruct<SolidColorDrawQuad>();
-    shared_quad_state->SetAll(gfx::Transform(), rect2, rect2, gfx::RRectF(),
-                              rect2, is_clipped, opaque_content, opacity,
-                              SkBlendMode::kSrcOver, 0);
-    shared_quad_state2->SetAll(gfx::Transform(), rect3, rect3, gfx::RRectF(),
-                               rect3, is_clipped, opaque_content, opacity,
-                               SkBlendMode::kSrcOver, 0);
+    shared_quad_state->SetAll(
+        gfx::Transform(), rect2, rect2, gfx::MaskFilterInfo(), rect2,
+        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
+    shared_quad_state2->SetAll(
+        gfx::Transform(), rect3, rect3, gfx::MaskFilterInfo(), rect3,
+        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
     quad1->SetNew(shared_quad_state, rect2_1, rect2_1, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state, rect2_2, rect2_2, SK_ColorBLACK, false);
     quad3->SetNew(shared_quad_state, rect2_3, rect2_3, SK_ColorBLACK, false);
@@ -3375,15 +3376,15 @@
     // |quad1| forms an occlusion rect; |quad2| follows a invertible transform
     // and is hiding behind quad1; |quad3| follows a non-invertible transform
     // and it is not covered by the occlusion rect.
-    shared_quad_state1->SetAll(invertible, rect1, rect1, gfx::RRectF(), rect1,
-                               is_clipped, opaque_content, opacity,
+    shared_quad_state1->SetAll(invertible, rect1, rect1, gfx::MaskFilterInfo(),
+                               rect1, is_clipped, opaque_content, opacity,
                                SkBlendMode::kSrcOver, 0);
-    shared_quad_state2->SetAll(invertible, rect2, rect2, gfx::RRectF(), rect2,
-                               is_clipped, opaque_content, opacity,
+    shared_quad_state2->SetAll(invertible, rect2, rect2, gfx::MaskFilterInfo(),
+                               rect2, is_clipped, opaque_content, opacity,
                                SkBlendMode::kSrcOver, 0);
-    shared_quad_state3->SetAll(non_invertible, rect3, rect3, gfx::RRectF(),
-                               rect3, is_clipped, opaque_content, opacity,
-                               SkBlendMode::kSrcOver, 0);
+    shared_quad_state3->SetAll(
+        non_invertible, rect3, rect3, gfx::MaskFilterInfo(), rect3, is_clipped,
+        opaque_content, opacity, SkBlendMode::kSrcOver, 0);
     quad1->SetNew(shared_quad_state1, rect1, rect1, SK_ColorBLACK, false);
     quad2->SetNew(shared_quad_state2, rect2, rect2, SK_ColorBLACK, false);
     quad3->SetNew(shared_quad_state3, rect3, rect3, SK_ColorBLACK, false);
@@ -3408,13 +3409,13 @@
     // |        |                 |        |
     // +--------+                 +--------+
     // Verify if draw occlusion can occlude quad with non-invertible
-    // transfrom.
-    shared_quad_state1->SetAll(invertible, rect1, rect1, gfx::RRectF(), rect1,
-                               is_clipped, opaque_content, opacity,
+    // transform.
+    shared_quad_state1->SetAll(invertible, rect1, rect1, gfx::MaskFilterInfo(),
+                               rect1, is_clipped, opaque_content, opacity,
                                SkBlendMode::kSrcOver, 0);
-    shared_quad_state3->SetAll(non_invertible_miss_z, rect3, rect3,
-                               gfx::RRectF(), rect3, is_clipped, opaque_content,
-                               opacity, SkBlendMode::kSrcOver, 0);
+    shared_quad_state3->SetAll(
+        non_invertible_miss_z, rect3, rect3, gfx::MaskFilterInfo(), rect3,
+        is_clipped, opaque_content, opacity, SkBlendMode::kSrcOver, 0);
     quad1->SetNew(shared_quad_state1, rect1, rect1, SK_ColorBLACK, false);
     quad3->SetNew(shared_quad_state3, rect3, rect3, SK_ColorBLACK, false);
 
@@ -3453,9 +3454,9 @@
   // |    |
   // +----+
   {
-    shared_quad_state->SetAll(gfx::Transform(), rect1, rect1, gfx::RRectF(),
-                              rect1, is_clipped, are_contents_opaque, opacity,
-                              SkBlendMode::kSrcOver, 0);
+    shared_quad_state->SetAll(
+        gfx::Transform(), rect1, rect1, gfx::MaskFilterInfo(), rect1,
+        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
 
     quad->SetNew(shared_quad_state, rect1, rect1, SK_ColorBLACK, false);
     EXPECT_EQ(1u, NumVisibleRects(frame.render_pass_list.front()->quad_list));
@@ -3520,7 +3521,7 @@
     shared_quad_state1->SetAll(
         gfx::Transform(), rect1 /* quad_layer_rect */,
         rect1 /* visible_quad_layer_rect */,
-        gfx::RRectF() /* rounded_corner_bounds*/, rect1 /*clip_rect */,
+        gfx::MaskFilterInfo() /* mask_filter_info */, rect1 /*clip_rect */,
         false /* is_clipped */, false /* are_contents_opaque */,
         0.5f /* opacity */, SkBlendMode::kSrcOver, 0 /* sorting_context_id */);
     auto* quad1 = pass->quad_list.AllocateAndConstruct<SolidColorDrawQuad>();
@@ -3533,7 +3534,7 @@
     shared_quad_state2->SetAll(
         gfx::Transform(), rect2 /* quad_layer_rect */,
         rect2 /* visible_quad_layer_rect */,
-        gfx::RRectF() /* rounded_corner_bounds */, rect2 /*clip_rect */,
+        gfx::MaskFilterInfo() /* mask_filter_info */, rect2 /*clip_rect */,
         false /* is_clipped */, true /* are_contents_opaque */,
         1.0f /* opacity */, SkBlendMode::kSrcOver, 0 /* sorting_context_id */);
     auto* quad2 = pass->quad_list.AllocateAndConstruct<SurfaceDrawQuad>();
@@ -3938,7 +3939,8 @@
   // The quad with rounded corner does not completely cover the quad below it.
   // The corners of the below quad are visiblg through the clipped corners.
   gfx::Rect quad_rect(10, 10, 100, 100);
-  gfx::RRectF rounded_corner_bounds(gfx::RectF(quad_rect), 10.f);
+  gfx::MaskFilterInfo mask_filter_info(gfx::RRectF(gfx::RectF(quad_rect), 10.f),
+                                       false);
 
   bool is_clipped = false;
   bool are_contents_opaque = true;
@@ -3956,16 +3958,16 @@
           ->quad_list.AllocateAndConstruct<SolidColorDrawQuad>();
 
   {
-    shared_quad_state_occluded->SetAll(
-        gfx::Transform(), quad_rect, quad_rect, gfx::RRectF(), quad_rect,
-        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
+    shared_quad_state_occluded->SetAll(gfx::Transform(), quad_rect, quad_rect,
+                                       gfx::MaskFilterInfo(), quad_rect,
+                                       is_clipped, are_contents_opaque, opacity,
+                                       SkBlendMode::kSrcOver, 0);
     occluded_quad->SetNew(shared_quad_state_occluded, quad_rect, quad_rect,
                           SK_ColorRED, false);
 
-    shared_quad_state_with_rrect->SetAll(gfx::Transform(), quad_rect, quad_rect,
-                                         rounded_corner_bounds, quad_rect,
-                                         is_clipped, are_contents_opaque,
-                                         opacity, SkBlendMode::kSrcOver, 0);
+    shared_quad_state_with_rrect->SetAll(
+        gfx::Transform(), quad_rect, quad_rect, mask_filter_info, quad_rect,
+        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
     rounded_corner_quad->SetNew(shared_quad_state_with_rrect, quad_rect,
                                 quad_rect, SK_ColorBLUE, false);
 
@@ -3992,8 +3994,9 @@
   // The quad with rounded corner completely covers the quad below it.
   AggregatedFrame frame = MakeDefaultAggregatedFrame();
   gfx::Rect quad_rect(10, 10, 1000, 1000);
-  gfx::RRectF rounded_corner_bounds(gfx::RectF(quad_rect), 10.f);
   gfx::Rect occluded_quad_rect(13, 13, 994, 994);
+  gfx::MaskFilterInfo mask_filter_info(gfx::RRectF(gfx::RectF(quad_rect), 10.f),
+                                       false);
 
   bool is_clipped = false;
   bool are_contents_opaque = true;
@@ -4012,16 +4015,15 @@
 
   {
     shared_quad_state_occluded->SetAll(
-        gfx::Transform(), occluded_quad_rect, occluded_quad_rect, gfx::RRectF(),
-        occluded_quad_rect, is_clipped, are_contents_opaque, opacity,
-        SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), occluded_quad_rect, occluded_quad_rect,
+        gfx::MaskFilterInfo(), occluded_quad_rect, is_clipped,
+        are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
     occluded_quad->SetNew(shared_quad_state_occluded, occluded_quad_rect,
                           occluded_quad_rect, SK_ColorRED, false);
 
-    shared_quad_state_with_rrect->SetAll(gfx::Transform(), quad_rect, quad_rect,
-                                         rounded_corner_bounds, quad_rect,
-                                         is_clipped, are_contents_opaque,
-                                         opacity, SkBlendMode::kSrcOver, 0);
+    shared_quad_state_with_rrect->SetAll(
+        gfx::Transform(), quad_rect, quad_rect, mask_filter_info, quad_rect,
+        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
     rounded_corner_quad->SetNew(shared_quad_state_with_rrect, quad_rect,
                                 quad_rect, SK_ColorBLUE, false);
 
@@ -4081,16 +4083,16 @@
 
   {
     shared_quad_state_occluder->SetAll(
-        gfx::Transform(), occluding_rect, occluding_rect, gfx::RRectF(),
+        gfx::Transform(), occluding_rect, occluding_rect, gfx::MaskFilterInfo(),
         occluding_rect, is_clipped, are_contents_opaque, opacity,
         SkBlendMode::kSrcOver, 0);
     quads[0]->SetNew(shared_quad_state_occluder, occluding_rect, occluding_rect,
                      SK_ColorRED, false);
 
     shared_quad_state_occluded->SetAll(
-        gfx::Transform(), occluded_sqs_rect, occluded_sqs_rect, gfx::RRectF(),
-        occluded_sqs_rect, is_clipped, are_contents_opaque, opacity,
-        SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), occluded_sqs_rect, occluded_sqs_rect,
+        gfx::MaskFilterInfo(), occluded_sqs_rect, is_clipped,
+        are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
     for (int i = 1; i < 4; i++) {
       quads[i]->SetNew(shared_quad_state_occluded, quad_rects[i - 1],
                        quad_rects[i - 1], SK_ColorRED, false);
@@ -4184,9 +4186,9 @@
   for (const auto& r : occluding_rects) {
     SharedQuadState* shared_quad_state_occluder =
         frame.render_pass_list.front()->CreateAndAppendSharedQuadState();
-    shared_quad_state_occluder->SetAll(gfx::Transform(), r, r, gfx::RRectF(), r,
-                                       is_clipped, are_contents_opaque, opacity,
-                                       SkBlendMode::kSrcOver, 0);
+    shared_quad_state_occluder->SetAll(
+        gfx::Transform(), r, r, gfx::MaskFilterInfo(), r, is_clipped,
+        are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
     SolidColorDrawQuad* quad =
         frame.render_pass_list.front()
             ->quad_list.AllocateAndConstruct<SolidColorDrawQuad>();
@@ -4198,7 +4200,7 @@
     SharedQuadState* shared_quad_state_occluded =
         frame.render_pass_list.front()->CreateAndAppendSharedQuadState();
     shared_quad_state_occluded->SetAll(
-        gfx::Transform(), occluded_rect, occluded_rect, gfx::RRectF(),
+        gfx::Transform(), occluded_rect, occluded_rect, gfx::MaskFilterInfo(),
         occluded_rect, is_clipped, are_contents_opaque, opacity,
         SkBlendMode::kSrcOver, 0);
     SolidColorDrawQuad* occluded_quad =
@@ -4266,7 +4268,7 @@
       frame.render_pass_list.front()
           ->quad_list.AllocateAndConstruct<SolidColorDrawQuad>();
   shared_quad_state_occluding->SetAll(
-      gfx::Transform(), occluding_rect, occluding_rect, gfx::RRectF(),
+      gfx::Transform(), occluding_rect, occluding_rect, gfx::MaskFilterInfo(),
       occluding_rect, is_clipped, are_contents_opaque, opacity,
       SkBlendMode::kSrcOver, 0);
   occluding_quad->SetNew(shared_quad_state_occluding, occluding_rect,
@@ -4279,7 +4281,7 @@
       frame.render_pass_list.front()
           ->quad_list.AllocateAndConstruct<SolidColorDrawQuad>();
   shared_quad_state_occluded->SetAll(
-      gfx::Transform(), occluded_rect, occluded_rect, gfx::RRectF(),
+      gfx::Transform(), occluded_rect, occluded_rect, gfx::MaskFilterInfo(),
       occluded_rect, is_clipped, are_contents_opaque, opacity,
       SkBlendMode::kSrcOver, 0);
   occluded_quad->SetNew(shared_quad_state_occluded, occluded_rect,
@@ -4315,7 +4317,8 @@
   //
   // * -> Visible rect for the quads.
   gfx::Rect quad_rect(10, 10, 1000, 1000);
-  gfx::RRectF rounded_corner_bounds(gfx::RectF(quad_rect), 10.f);
+  gfx::MaskFilterInfo mask_filter_info(gfx::RRectF(gfx::RectF(quad_rect), 10.f),
+                                       false);
   gfx::Rect occluded_quad_rect_1(0, 20, 600, 490);
   gfx::Rect occluded_quad_rect_2(600, 20, 600, 490);
   gfx::Rect occluded_quad_rect_3(0, 510, 600, 490);
@@ -4352,9 +4355,9 @@
 
   {
     shared_quad_state_occluded->SetAll(
-        gfx::Transform(), occluded_sqs_rect, occluded_sqs_rect, gfx::RRectF(),
-        occluded_sqs_rect, is_clipped, are_contents_opaque, opacity,
-        SkBlendMode::kSrcOver, 0);
+        gfx::Transform(), occluded_sqs_rect, occluded_sqs_rect,
+        gfx::MaskFilterInfo(), occluded_sqs_rect, is_clipped,
+        are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
     occluded_quad_1->SetNew(shared_quad_state_occluded, occluded_quad_rect_1,
                             occluded_quad_rect_1, SK_ColorRED, false);
     occluded_quad_2->SetNew(shared_quad_state_occluded, occluded_quad_rect_2,
@@ -4364,10 +4367,9 @@
     occluded_quad_4->SetNew(shared_quad_state_occluded, occluded_quad_rect_4,
                             occluded_quad_rect_4, SK_ColorRED, false);
 
-    shared_quad_state_with_rrect->SetAll(gfx::Transform(), quad_rect, quad_rect,
-                                         rounded_corner_bounds, quad_rect,
-                                         is_clipped, are_contents_opaque,
-                                         opacity, SkBlendMode::kSrcOver, 0);
+    shared_quad_state_with_rrect->SetAll(
+        gfx::Transform(), quad_rect, quad_rect, mask_filter_info, quad_rect,
+        is_clipped, are_contents_opaque, opacity, SkBlendMode::kSrcOver, 0);
     rounded_corner_quad->SetNew(shared_quad_state_with_rrect, quad_rect,
                                 quad_rect, SK_ColorBLUE, false);
 
diff --git a/components/viz/service/display/gl_renderer.cc b/components/viz/service/display/gl_renderer.cc
index 31fa3d2..83af241 100644
--- a/components/viz/service/display/gl_renderer.cc
+++ b/components/viz/service/display/gl_renderer.cc
@@ -1798,9 +1798,9 @@
 
   SetShaderOpacity(params->quad->shared_quad_state->opacity);
   if (current_program_->rounded_corner_rect_location() != -1) {
-    SetShaderRoundedCorner(
-        params->quad->shared_quad_state->rounded_corner_bounds,
-        params->window_matrix * params->projection_matrix);
+    SetShaderRoundedCorner(params->quad->shared_quad_state->mask_filter_info
+                               .rounded_corner_bounds(),
+                           params->window_matrix * params->projection_matrix);
   }
   SetShaderQuadF(params->surface_quad);
 }
@@ -2201,7 +2201,7 @@
     SetShaderColor(color, opacity);
     if (current_program_->rounded_corner_rect_location() != -1) {
       SetShaderRoundedCorner(
-          quad->shared_quad_state->rounded_corner_bounds,
+          quad->shared_quad_state->mask_filter_info.rounded_corner_bounds(),
           current_frame()->window_matrix * current_frame()->projection_matrix);
     }
 
@@ -2388,7 +2388,7 @@
   SetShaderOpacity(quad->shared_quad_state->opacity);
   if (current_program_->rounded_corner_rect_location() != -1) {
     SetShaderRoundedCorner(
-        quad->shared_quad_state->rounded_corner_bounds,
+        quad->shared_quad_state->mask_filter_info.rounded_corner_bounds(),
         current_frame()->window_matrix * current_frame()->projection_matrix);
   }
   DCHECK(CanApplyBlendModeUsingBlendFunc(quad->shared_quad_state->blend_mode));
@@ -2488,7 +2488,7 @@
   SetShaderOpacity(quad->shared_quad_state->opacity);
   if (current_program_->rounded_corner_rect_location() != -1) {
     SetShaderRoundedCorner(
-        quad->shared_quad_state->rounded_corner_bounds,
+        quad->shared_quad_state->mask_filter_info.rounded_corner_bounds(),
         current_frame()->window_matrix * current_frame()->projection_matrix);
   }
 
@@ -2628,7 +2628,7 @@
 
   if (current_program_->rounded_corner_rect_location() != -1) {
     SetShaderRoundedCorner(
-        quad->shared_quad_state->rounded_corner_bounds,
+        quad->shared_quad_state->mask_filter_info.rounded_corner_bounds(),
         current_frame()->window_matrix * current_frame()->projection_matrix);
   }
 
@@ -2772,7 +2772,7 @@
   SetShaderOpacity(quad->shared_quad_state->opacity);
   if (current_program_->rounded_corner_rect_location() != -1) {
     SetShaderRoundedCorner(
-        quad->shared_quad_state->rounded_corner_bounds,
+        quad->shared_quad_state->mask_filter_info.rounded_corner_bounds(),
         current_frame()->window_matrix * current_frame()->projection_matrix);
   }
   gfx::Size texture_size = lock.size();
@@ -2832,7 +2832,7 @@
 
   if (current_program_->rounded_corner_rect_location() != -1) {
     SetShaderRoundedCorner(
-        draw_cache_.rounded_corner_bounds,
+        draw_cache_.mask_filter_info.rounded_corner_bounds(),
         current_frame()->window_matrix * current_frame()->projection_matrix);
   }
 
@@ -2945,8 +2945,8 @@
       draw_cache_.needs_blending != quad->ShouldDrawWithBlending() ||
       draw_cache_.nearest_neighbor != quad->nearest_neighbor ||
       draw_cache_.background_color != quad->background_color ||
-      draw_cache_.rounded_corner_bounds !=
-          quad->shared_quad_state->rounded_corner_bounds ||
+      draw_cache_.mask_filter_info !=
+          quad->shared_quad_state->mask_filter_info ||
       draw_cache_.matrix_data.size() >= max_quads ||
       draw_cache_.is_video_frame != quad->is_video_frame) {
     FlushTextureQuadCache(SHARED_BINDING);
@@ -2956,8 +2956,7 @@
     draw_cache_.needs_blending = quad->ShouldDrawWithBlending();
     draw_cache_.nearest_neighbor = quad->nearest_neighbor;
     draw_cache_.background_color = quad->background_color;
-    draw_cache_.rounded_corner_bounds =
-        quad->shared_quad_state->rounded_corner_bounds;
+    draw_cache_.mask_filter_info = quad->shared_quad_state->mask_filter_info;
     draw_cache_.is_video_frame = quad->is_video_frame;
   }
 
@@ -4207,7 +4206,6 @@
                           ca_layer_overlay->shared_state->clip_rect.y(),
                           ca_layer_overlay->shared_state->clip_rect.width(),
                           ca_layer_overlay->shared_state->clip_rect.height()};
-
   const gfx::RectF& rect =
       ca_layer_overlay->shared_state->rounded_corner_bounds.rect();
   GLfloat rounded_corner_rect[5] = {
@@ -4354,9 +4352,9 @@
   if (!use_fast_path_solid_color_quad_)
     return false;
 
-  // Rounded corners require blending with the background, which is not possible
+  // Mask filters require blending with the background, which is not possible
   // with the glClear draw method.
-  if (!sqs->rounded_corner_bounds.IsEmpty())
+  if (!sqs->mask_filter_info.IsEmpty())
     return false;
 
   // 3D transforms need vertex computation in 3D and cannot be handled using
diff --git a/components/viz/service/display/gl_renderer_draw_cache.h b/components/viz/service/display/gl_renderer_draw_cache.h
index a637154e..6abe06cc 100644
--- a/components/viz/service/display/gl_renderer_draw_cache.h
+++ b/components/viz/service/display/gl_renderer_draw_cache.h
@@ -10,7 +10,7 @@
 #include "base/macros.h"
 #include "components/viz/service/display/program_binding.h"
 #include "third_party/skia/include/core/SkColor.h"
-#include "ui/gfx/rrect_f.h"
+#include "ui/gfx/mask_filter_info.h"
 
 namespace viz {
 
@@ -39,7 +39,7 @@
   bool needs_blending = false;
   bool nearest_neighbor = false;
   SkColor background_color = 0;
-  gfx::RRectF rounded_corner_bounds;
+  gfx::MaskFilterInfo mask_filter_info;
 
   // A cache for the coalesced quad data.
   std::vector<Float4> uv_xform_data;
diff --git a/components/viz/service/display/gl_renderer_unittest.cc b/components/viz/service/display/gl_renderer_unittest.cc
index 6be5c8f..cb48150 100644
--- a/components/viz/service/display/gl_renderer_unittest.cc
+++ b/components/viz/service/display/gl_renderer_unittest.cc
@@ -792,7 +792,7 @@
       root_pass->CreateAndAppendDrawQuad<TextureDrawQuad>();
   SharedQuadState* shared_state = root_pass->CreateAndAppendSharedQuadState();
   shared_state->SetAll(gfx::Transform(), gfx::Rect(viewport_size),
-                       gfx::Rect(1023, 1023), gfx::RRectF(),
+                       gfx::Rect(1023, 1023), gfx::MaskFilterInfo(),
                        gfx::Rect(1023, 1023), false, false, 1,
                        SkBlendMode::kSrcOver, 0);
   overlay_quad->SetNew(shared_state, gfx::Rect(1023, 1023),
@@ -855,7 +855,7 @@
       root_pass->CreateAndAppendDrawQuad<TextureDrawQuad>();
   SharedQuadState* shared_state = root_pass->CreateAndAppendSharedQuadState();
   shared_state->SetAll(gfx::Transform(), gfx::Rect(viewport_size),
-                       gfx::Rect(1025, 1025), gfx::RRectF(),
+                       gfx::Rect(1025, 1025), gfx::MaskFilterInfo(),
                        gfx::Rect(1025, 1025), false, false, 1,
                        SkBlendMode::kSrcOver, 0);
   overlay_quad->SetNew(shared_state, gfx::Rect(1025, 1025),
@@ -913,7 +913,7 @@
         root_pass->CreateAndAppendDrawQuad<TextureDrawQuad>();
     SharedQuadState* shared_state = root_pass->CreateAndAppendSharedQuadState();
     shared_state->SetAll(gfx::Transform(), gfx::Rect(viewport_size),
-                         gfx::Rect(kTextureSize), gfx::RRectF(),
+                         gfx::Rect(kTextureSize), gfx::MaskFilterInfo(),
                          gfx::Rect(kTextureSize), false, false, 1,
                          SkBlendMode::kSrcOver, 0);
     overlay_quad->SetNew(shared_state, gfx::Rect(kTextureSize),
@@ -1450,8 +1450,9 @@
   visible_rect.Inset(10, 20, 30, 40);
 
   SharedQuadState* shared_state = root_pass->CreateAndAppendSharedQuadState();
-  shared_state->SetAll(gfx::Transform(), gfx::Rect(), rect, gfx::RRectF(), rect,
-                       false, false, 1, SkBlendMode::kSrcOver, 0);
+  shared_state->SetAll(gfx::Transform(), gfx::Rect(), rect,
+                       gfx::MaskFilterInfo(), rect, false, false, 1,
+                       SkBlendMode::kSrcOver, 0);
 
   YUVVideoDrawQuad* quad =
       root_pass->CreateAndAppendDrawQuad<YUVVideoDrawQuad>();
@@ -2003,8 +2004,8 @@
 
   // Add rounded corners to the solid color draw quad so that the fast path
   // of drawing using glClear is not used.
-  root_pass->shared_quad_state_list.front()->rounded_corner_bounds =
-      gfx::RRectF(gfx::RectF(quad_rect), 2.f);
+  root_pass->shared_quad_state_list.front()->mask_filter_info =
+      gfx::MaskFilterInfo(gfx::RRectF(gfx::RectF(quad_rect), 2.f), false);
 
   renderer_->DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_);
   DrawFrame(renderer_.get(), viewport_size);
@@ -2031,8 +2032,8 @@
 
   // Add rounded corners to the solid color draw quad so that the fast path
   // of drawing using glClear is not used.
-  root_pass->shared_quad_state_list.front()->rounded_corner_bounds =
-      gfx::RRectF(gfx::RectF(quad_rect), 1.f);
+  root_pass->shared_quad_state_list.front()->mask_filter_info =
+      gfx::MaskFilterInfo(gfx::RRectF(gfx::RectF(quad_rect), 1.f), false);
 
   renderer_->DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_);
   DrawFrame(renderer_.get(), viewport_size);
@@ -2910,7 +2911,7 @@
       root_pass->CreateAndAppendDrawQuad<TextureDrawQuad>();
   SharedQuadState* shared_state = root_pass->CreateAndAppendSharedQuadState();
   shared_state->SetAll(gfx::Transform(), gfx::Rect(viewport_size),
-                       gfx::Rect(viewport_size), gfx::RRectF(),
+                       gfx::Rect(viewport_size), gfx::MaskFilterInfo(),
                        gfx::Rect(viewport_size), false, false, 1,
                        SkBlendMode::kSrcOver, 0);
   overlay_quad->SetNew(shared_state, gfx::Rect(viewport_size),
@@ -3222,8 +3223,8 @@
   root_pass->damage_rect = root_pass_damage_rect;
   cc::AddQuad(root_pass, quad_rect, SK_ColorRED);
 
-  root_pass->shared_quad_state_list.front()->rounded_corner_bounds =
-      gfx::RRectF(gfx::RectF(quad_rect), 5.f);
+  root_pass->shared_quad_state_list.front()->mask_filter_info =
+      gfx::MaskFilterInfo(gfx::RRectF(gfx::RectF(quad_rect), 5.f), false);
 
   // Fast Solid color draw quads should not be executed.
   AddExpectations(false /*use_fast_path*/, gfx::Rect());
@@ -3695,8 +3696,8 @@
       gfx::RectF tex_coord_rect(0, 0, 1, 1);
       SharedQuadState* shared_state =
           root_pass->CreateAndAppendSharedQuadState();
-      shared_state->SetAll(gfx::Transform(), rect, rect, gfx::RRectF(), rect,
-                           false, false, 1, SkBlendMode::kSrcOver, 0);
+      shared_state->SetAll(gfx::Transform(), rect, rect, gfx::MaskFilterInfo(),
+                           rect, false, false, 1, SkBlendMode::kSrcOver, 0);
       YUVVideoDrawQuad* quad =
           root_pass->CreateAndAppendDrawQuad<YUVVideoDrawQuad>();
       quad->SetNew(shared_state, rect, rect, needs_blending, tex_coord_rect,
@@ -4110,8 +4111,8 @@
     sqs->is_clipped = true;
     sqs->clip_rect = gfx::Rect(2, 2, 6, 6);
     const float radius = 2;
-    sqs->rounded_corner_bounds =
-        gfx::RRectF(gfx::RectF(sqs->clip_rect), radius);
+    sqs->mask_filter_info = gfx::MaskFilterInfo(
+        gfx::RRectF(gfx::RectF(sqs->clip_rect), radius), false);
 
     switch (subtest) {
       case 0:
@@ -4131,8 +4132,12 @@
         break;
       case 2:
         // Subtest 2 has a non-simple rounded rect.
-        sqs->rounded_corner_bounds.SetCornerRadii(
-            gfx::RRectF::Corner::kUpperLeft, 1, 1);
+        gfx::RRectF rounded_corner_bounds =
+            sqs->mask_filter_info.rounded_corner_bounds();
+        rounded_corner_bounds.SetCornerRadii(gfx::RRectF::Corner::kUpperLeft, 1,
+                                             1);
+        sqs->mask_filter_info =
+            gfx::MaskFilterInfo(rounded_corner_bounds, false);
         // Called 2 extra times in order to set up the rounded corner
         // parameters in the shader, because the CALayer is not handling
         // the rounded corners.
@@ -5094,7 +5099,7 @@
       root_pass->CreateAndAppendDrawQuad<TextureDrawQuad>();
   SharedQuadState* shared_state = root_pass->CreateAndAppendSharedQuadState();
   shared_state->SetAll(gfx::Transform(), gfx::Rect(viewport_size),
-                       gfx::Rect(50, 50), gfx::RRectF(),
+                       gfx::Rect(50, 50), gfx::MaskFilterInfo(),
                        gfx::Rect(viewport_size), false, false, 1,
                        SkBlendMode::kSrcOver, 0);
   overlay_quad->SetNew(
diff --git a/components/viz/service/display/overlay_candidate.cc b/components/viz/service/display/overlay_candidate.cc
index d320f89..2ef62f7 100644
--- a/components/viz/service/display/overlay_candidate.cc
+++ b/components/viz/service/display/overlay_candidate.cc
@@ -142,8 +142,8 @@
   // We don't support an opacity value different than one for an overlay plane.
   if (quad->shared_quad_state->opacity != 1.f)
     return false;
-  // We can't support overlays with rounded corner clipping.
-  if (!quad->shared_quad_state->rounded_corner_bounds.IsEmpty())
+  // We can't support overlays with mask filter.
+  if (!quad->shared_quad_state->mask_filter_info.IsEmpty())
     return false;
   // We support only kSrc (no blending) and kSrcOver (blending with premul).
   if (!(quad->shared_quad_state->blend_mode == SkBlendMode::kSrc ||
diff --git a/components/viz/service/display/overlay_processor_using_strategy.cc b/components/viz/service/display/overlay_processor_using_strategy.cc
index f8645cf..beddfaf3 100644
--- a/components/viz/service/display/overlay_processor_using_strategy.cc
+++ b/components/viz/service/display/overlay_processor_using_strategy.cc
@@ -159,6 +159,15 @@
       bool always_unoccluded =
           overlay.is_unoccluded && previous_frame_underlay_was_unoccluded;
 
+      // We need to make sure that when we change the overlay we damage the
+      // region where the underlay will be positioned. This is because a
+      // black transparent hole is made for the underlay to show through
+      // but its possible that the damage for this quad is less than the
+      // complete size of the underlay.  https://crbug.com/1130733
+      if (!same_underlay_rect) {
+        damage_rect->Union(this_frame_underlay_rect);
+      }
+
       if (same_underlay_rect && !transition_from_occluded_to_unoccluded &&
           (always_unoccluded || overlay.no_occluding_damage)) {
         damage_rect->Subtract(this_frame_underlay_rect);
diff --git a/components/viz/service/display/overlay_unittest.cc b/components/viz/service/display/overlay_unittest.cc
index dbd4763..02838d8d 100644
--- a/components/viz/service/display/overlay_unittest.cc
+++ b/components/viz/service/display/overlay_unittest.cc
@@ -2043,18 +2043,22 @@
 }
 
 TEST_F(UnderlayTest, UpdateDamageRectWhenNoPromotion) {
-  // In the first pass there is an overlay promotion and the expected damage
-  // size should be unchanged.
-  // In the second pass there is no overlay promotion, but the damage should be
-  // the union of the damage_rect with CreateRenderPass's output_rect which is
-  // {0, 0, 256, 256}.
-  bool has_fullscreen_candidate[] = {true, false};
-  gfx::Rect damages[] = {gfx::Rect(0, 0, 32, 32), gfx::Rect(0, 0, 312, 16)};
-  gfx::Rect expected_damages[] = {gfx::Rect(0, 0, 32, 32),
+  // In the first pass there is an overlay promotion and the expected damage is
+  // a union of the hole made for the underlay and the incoming damage. In the
+  // second pass there is no occluding damage so the incoming damage is
+  // attributed to the overlay candidate and the final output damage is zero. In
+  // the third pass there is no overlay promotion, but the damage should be the
+  // union of the damage_rect with CreateRenderPass's output_rect which is {0,
+  // 0, 256, 256}. This is due to the demotion of the current overlay.
+  bool has_fullscreen_candidate[] = {true, true, false};
+  gfx::Rect damages[] = {gfx::Rect(0, 0, 32, 32), gfx::Rect(0, 0, 32, 32),
+                         gfx::Rect(0, 0, 312, 16)};
+  gfx::Rect expected_damages[] = {gfx::Rect(0, 0, 256, 256),
+                                  gfx::Rect(0, 0, 0, 0),
                                   gfx::Rect(0, 0, 312, 256)};
-  size_t expected_candidate_size[] = {1, 0};
+  size_t expected_candidate_size[] = {1, 1, 0};
 
-  for (int i = 0; i < 2; ++i) {
+  for (size_t i = 0; i < base::size(expected_damages); ++i) {
     auto pass = CreateRenderPass();
 
     if (has_fullscreen_candidate[i]) {
@@ -3191,7 +3195,7 @@
   quad_state->SetAll(
       /*quad_layer_rect=*/quad_to_target_transform, quad_rect,
       /*visible_quad_layer_rect=*/quad_rect,
-      /*rounded_corner_bounds=*/gfx::RRectF(), /*clip_rect=*/gfx::Rect(),
+      /*mask_filter_info=*/gfx::MaskFilterInfo(), /*clip_rect=*/gfx::Rect(),
       /*is_clipped=*/false,
       /*are contents opaque=*/true,
       /*opacity=*/1.f,
diff --git a/components/viz/service/display/renderer_perftest.cc b/components/viz/service/display/renderer_perftest.cc
index 6589178..83154b0 100644
--- a/components/viz/service/display/renderer_perftest.cc
+++ b/components/viz/service/display/renderer_perftest.cc
@@ -143,19 +143,18 @@
     gfx::Transform quad_to_target_transform,
     const gfx::Rect& rect,
     CompositorRenderPass* render_pass,
-    const gfx::RRectF& rrect) {
+    const gfx::MaskFilterInfo& mask_filter_info) {
   const gfx::Rect layer_rect = rect;
   const gfx::Rect visible_layer_rect = rect;
   const gfx::Rect clip_rect = rect;
   const bool is_clipped = false;
   const bool are_contents_opaque = false;
   const float opacity = 1.0f;
-  const gfx::RRectF rounded_corner_bounds = rrect;
   const SkBlendMode blend_mode = SkBlendMode::kSrcOver;
   const int sorting_context_id = 0;
   SharedQuadState* shared_state = render_pass->CreateAndAppendSharedQuadState();
   shared_state->SetAll(quad_to_target_transform, layer_rect, visible_layer_rect,
-                       rounded_corner_bounds, clip_rect, is_clipped,
+                       mask_filter_info, clip_rect, is_clipped,
                        are_contents_opaque, opacity, blend_mode,
                        sorting_context_id);
   return shared_state;
@@ -499,7 +498,7 @@
       std::unique_ptr<CompositorRenderPass> pass = CreateTestRootRenderPass();
 
       SharedQuadState* shared_state = CreateTestSharedQuadState(
-          gfx::Transform(), kSurfaceRect, pass.get(), gfx::RRectF());
+          gfx::Transform(), kSurfaceRect, pass.get(), gfx::MaskFilterInfo());
 
       CreateTestTextureDrawQuad(resource_list_.back().id, kSurfaceRect,
                                 /*background_color=*/SK_ColorTRANSPARENT,
@@ -534,7 +533,7 @@
     do {
       std::unique_ptr<CompositorRenderPass> pass = CreateTestRootRenderPass();
       SharedQuadState* shared_state = CreateTestSharedQuadState(
-          gfx::Transform(), kSurfaceRect, pass.get(), gfx::RRectF());
+          gfx::Transform(), kSurfaceRect, pass.get(), gfx::MaskFilterInfo());
 
       for (int i = 0; i < 5; i++) {
         for (int j = 0; j < 5; j++) {
@@ -571,7 +570,7 @@
     do {
       std::unique_ptr<CompositorRenderPass> pass = CreateTestRootRenderPass();
       SharedQuadState* shared_state = CreateTestSharedQuadState(
-          gfx::Transform(), kSurfaceRect, pass.get(), gfx::RRectF());
+          gfx::Transform(), kSurfaceRect, pass.get(), gfx::MaskFilterInfo());
 
       for (int i = 0; i < 5; i++) {
         for (int j = 0; j < 5; j++) {
@@ -630,7 +629,7 @@
         // SharedQuadState
         SharedQuadState* shared_state = CreateTestSharedQuadState(
             current_transform, gfx::Rect(kSurfaceSize), pass.get(),
-            gfx::RRectF());
+            gfx::MaskFilterInfo());
         ResourceId resource_id =
             share_resources ? resource_list_[0].id : resource_list_[i].id;
         CreateTestTileDrawQuad(resource_id, gfx::Rect(kTileSize), kTextureSize,
diff --git a/components/viz/service/display/renderer_pixeltest.cc b/components/viz/service/display/renderer_pixeltest.cc
index 744d39f..aac9365 100644
--- a/components/viz/service/display/renderer_pixeltest.cc
+++ b/components/viz/service/display/renderer_pixeltest.cc
@@ -151,12 +151,12 @@
   const bool is_clipped = false;
   const bool are_contents_opaque = false;
   const float opacity = 1.0f;
-  const gfx::RRectF rounded_corner_bounds = rrect;
+  const gfx::MaskFilterInfo mask_filter_info(rrect, false);
   const SkBlendMode blend_mode = SkBlendMode::kSrcOver;
   int sorting_context_id = 0;
   SharedQuadState* shared_state = render_pass->CreateAndAppendSharedQuadState();
   shared_state->SetAll(quad_to_target_transform, layer_rect, visible_layer_rect,
-                       rounded_corner_bounds, clip_rect, is_clipped,
+                       mask_filter_info, clip_rect, is_clipped,
                        are_contents_opaque, opacity, blend_mode,
                        sorting_context_id);
   return shared_state;
@@ -176,7 +176,7 @@
   int sorting_context_id = 0;
   SharedQuadState* shared_state = render_pass->CreateAndAppendSharedQuadState();
   shared_state->SetAll(quad_to_target_transform, layer_rect, visible_layer_rect,
-                       /*rounded_corner_bounds=*/gfx::RRectF(), clip_rect,
+                       /*mask_filter_info=*/gfx::MaskFilterInfo(), clip_rect,
                        is_clipped, are_contents_opaque, opacity, blend_mode,
                        sorting_context_id);
   return shared_state;
@@ -5279,6 +5279,10 @@
 
 // Confirm that the trail can't be drawn beyond the presentation area.
 TEST_P(DelegatedInkTest, TrailExtendsBeyondPresentationArea) {
+  // TODO(crbug.com/1021566): Enable this test for SkiaRenderer Dawn.
+  if (renderer_type() == RendererType::kSkiaDawn)
+    return;
+
   const gfx::RectF kPresentationArea(30, 30, 100, 100);
   CreateAndSendMetadata(gfx::PointF(50.2f, 89.999f), 15.22f, SK_ColorCYAN,
                         kPresentationArea);
@@ -5334,7 +5338,7 @@
       &pass_list,
       base::FilePath(
           FILE_PATH_LITERAL("delegated_ink_trail_on_batched_quads.png")),
-      cc::ExactPixelComparator(true)));
+      cc::FuzzyPixelOffByOneComparator(true)));
 }
 #endif  // !defined(OS_ANDROID)
 
diff --git a/components/viz/service/display/skia_readback_pixeltest.cc b/components/viz/service/display/skia_readback_pixeltest.cc
index 509179b4..64d15de 100644
--- a/components/viz/service/display/skia_readback_pixeltest.cc
+++ b/components/viz/service/display/skia_readback_pixeltest.cc
@@ -49,7 +49,7 @@
   const gfx::Rect clip_rect = rect;
   SharedQuadState* shared_state = render_pass->CreateAndAppendSharedQuadState();
   shared_state->SetAll(gfx::Transform(), layer_rect, visible_layer_rect,
-                       gfx::RRectF(), clip_rect, /*is_clipped=*/false,
+                       gfx::MaskFilterInfo(), clip_rect, /*is_clipped=*/false,
                        /*are_contents_opaque=*/false, /*opacity=*/1.0f,
                        SkBlendMode::kSrcOver,
                        /*sorting_context_id=*/0);
diff --git a/components/viz/service/display/skia_renderer.cc b/components/viz/service/display/skia_renderer.cc
index af54c9f..7e5fb225 100644
--- a/components/viz/service/display/skia_renderer.cc
+++ b/components/viz/service/display/skia_renderer.cc
@@ -1237,8 +1237,8 @@
   if (ShouldApplyRoundedCorner(quad)) {
     // Transform by the window and projection matrix to go from target to
     // device space, which should always be a scale+translate.
-    SkRRect corner_bounds =
-        SkRRect(quad->shared_quad_state->rounded_corner_bounds);
+    SkRRect corner_bounds = SkRRect(
+        quad->shared_quad_state->mask_filter_info.rounded_corner_bounds());
     SkMatrix to_device;
     gfx::TransformToFlattenedSkMatrix(target_to_device, &to_device);
 
@@ -2591,13 +2591,10 @@
   overlay->rpdq = nullptr;
   gfx::Transform target_to_device =
       current_frame()->window_matrix * current_frame()->projection_matrix;
-  // Use nullptr scissor, so we can always render the whole render pass in an
-  // overlay backing.
-  // TODO(penghuang): reusing overlay backing from previous frame to avoid
-  // reproducing the overlay backing if the render pass content quad properties
-  // and content are not changed.
+  const gfx::Rect* scissor = is_scissor_enabled_ ? &scissor_rect_ : nullptr;
+
   DrawQuadParams params = CalculateDrawQuadParams(
-      target_to_device, /*scissor=*/nullptr, quad, /*draw_region=*/nullptr);
+      target_to_device, scissor, quad, /*draw_region=*/nullptr);
   DrawRPDQParams rpdq_params = CalculateRPDQParams(quad, &params);
 
   // |filter_bounds| is the content space bounds that includes any filtered
diff --git a/components/viz/service/display/software_renderer.cc b/components/viz/service/display/software_renderer.cc
index 3aafdbd..a7db4ee 100644
--- a/components/viz/service/display/software_renderer.cc
+++ b/components/viz/service/display/software_renderer.cc
@@ -257,7 +257,8 @@
   }
 
   if (should_apply_rounded_corner)
-    SetClipRRect(quad->shared_quad_state->rounded_corner_bounds);
+    SetClipRRect(
+        quad->shared_quad_state->mask_filter_info.rounded_corner_bounds());
 
   gfx::Transform quad_rect_matrix;
   QuadRectTransform(&quad_rect_matrix,
diff --git a/components/viz/service/display/software_renderer_unittest.cc b/components/viz/service/display/software_renderer_unittest.cc
index 83b8301f..eaeb103c 100644
--- a/components/viz/service/display/software_renderer_unittest.cc
+++ b/components/viz/service/display/software_renderer_unittest.cc
@@ -153,7 +153,7 @@
   SharedQuadState* shared_quad_state =
       root_render_pass->CreateAndAppendSharedQuadState();
   shared_quad_state->SetAll(gfx::Transform(), outer_rect, outer_rect,
-                            gfx::RRectF(), outer_rect, false, true, 1.0,
+                            gfx::MaskFilterInfo(), outer_rect, false, true, 1.0,
                             SkBlendMode::kSrcOver, 0);
   auto* inner_quad =
       root_render_pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
@@ -221,7 +221,7 @@
   SharedQuadState* shared_quad_state =
       root_render_pass->CreateAndAppendSharedQuadState();
   shared_quad_state->SetAll(gfx::Transform(), outer_rect, outer_rect,
-                            gfx::RRectF(), outer_rect, false, true, 1.0,
+                            gfx::MaskFilterInfo(), outer_rect, false, true, 1.0,
                             SkBlendMode::kSrcOver, 0);
   auto* inner_quad = root_render_pass->CreateAndAppendDrawQuad<TileDrawQuad>();
   inner_quad->SetNew(shared_quad_state, inner_rect, inner_rect, needs_blending,
@@ -283,7 +283,7 @@
   SharedQuadState* shared_quad_state =
       root_render_pass->CreateAndAppendSharedQuadState();
   shared_quad_state->SetAll(gfx::Transform(), tile_rect, tile_rect,
-                            gfx::RRectF(), tile_rect, false, true, 1.0,
+                            gfx::MaskFilterInfo(), tile_rect, false, true, 1.0,
                             SkBlendMode::kSrcOver, 0);
   auto* quad = root_render_pass->CreateAndAppendDrawQuad<TileDrawQuad>();
   quad->SetNew(shared_quad_state, tile_rect, tile_rect, needs_blending,
@@ -444,8 +444,8 @@
     SharedQuadState* shared_quad_state =
         root_pass->CreateAndAppendSharedQuadState();
     shared_quad_state->SetAll(gfx::Transform(), outer_rect, outer_rect,
-                              gfx::RRectF(), gfx::Rect(1, 1, 30, 30), true,
-                              true, 1.0, SkBlendMode::kSrcOver, 0);
+                              gfx::MaskFilterInfo(), gfx::Rect(1, 1, 30, 30),
+                              true, true, 1.0, SkBlendMode::kSrcOver, 0);
     auto* outer_quad = root_pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
     outer_quad->SetNew(shared_quad_state, outer_rect, outer_rect, SK_ColorGREEN,
                        false);
@@ -458,10 +458,10 @@
 
     SharedQuadState* shared_quad_state =
         root_pass->CreateAndAppendSharedQuadState();
-    shared_quad_state->SetAll(gfx::Transform(), inner_rect, inner_rect,
-                              gfx::RRectF(gfx::RectF(5, 5, 10, 10), 2),
-                              inner_rect, false, true, 1.0,
-                              SkBlendMode::kSrcOver, 0);
+    shared_quad_state->SetAll(
+        gfx::Transform(), inner_rect, inner_rect,
+        gfx::MaskFilterInfo(gfx::RRectF(gfx::RectF(5, 5, 10, 10), 2), false),
+        inner_rect, false, true, 1.0, SkBlendMode::kSrcOver, 0);
     auto* inner_quad = root_pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
     inner_quad->SetNew(shared_quad_state, inner_rect, inner_rect, SK_ColorRED,
                        false);
diff --git a/components/viz/service/display/surface_aggregator.cc b/components/viz/service/display/surface_aggregator.cc
index fb1fd60..c4dbdd5 100644
--- a/components/viz/service/display/surface_aggregator.cc
+++ b/components/viz/service/display/surface_aggregator.cc
@@ -100,25 +100,6 @@
   gfx::ContentColorUsage content_color_usage = gfx::ContentColorUsage::kSRGB;
 };
 
-struct SurfaceAggregator::RoundedCornerInfo {
-  RoundedCornerInfo() = default;
-
-  // |target_transform| is the transform that maps |bounds_arg| from its current
-  // space into the desired target space. It must be an axis aligned transform.
-  RoundedCornerInfo(const gfx::RRectF& bounds_arg,
-                    bool is_fast_rounded_corner,
-                    const gfx::Transform target_transform)
-      : bounds(bounds_arg), is_fast_rounded_corner(is_fast_rounded_corner) {
-    if (bounds.IsEmpty())
-      return;
-    bool success = target_transform.TransformRRectF(&bounds);
-    DCHECK(success);
-  }
-
-  gfx::RRectF bounds;
-  bool is_fast_rounded_corner;
-};
-
 struct SurfaceAggregator::RenderPassMapEntry {
   explicit RenderPassMapEntry(CompositorRenderPass* render_pass)
       : render_pass(render_pass) {}
@@ -388,7 +369,7 @@
     bool ignore_undamaged,
     gfx::Rect* damage_rect_in_quad_space,
     bool* damage_rect_in_quad_space_valid,
-    const RoundedCornerInfo& rounded_corner_info) {
+    const gfx::MaskFilterInfo& mask_filter_info) {
   SurfaceId primary_surface_id = surface_quad->surface_range.end();
   Surface* latest_surface =
       manager_->GetLatestInFlightSurface(surface_quad->surface_range);
@@ -412,7 +393,7 @@
   // can happen after a Viz process crash.
   if (!latest_surface || !latest_surface->HasActiveFrame()) {
     EmitDefaultBackgroundColorQuad(surface_quad, target_transform, clip_rect,
-                                   dest_pass, rounded_corner_info);
+                                   dest_pass, mask_filter_info);
     return;
   }
 
@@ -432,13 +413,13 @@
                                surface_quad->shared_quad_state,
                                target_transform, clip_rect,
                                fallback_frame.metadata.root_background_color,
-                               dest_pass, rounded_corner_info);
+                               dest_pass, mask_filter_info);
   }
 
   EmitSurfaceContent(latest_surface, parent_device_scale_factor, surface_quad,
                      target_transform, clip_rect, dest_pass, ignore_undamaged,
                      damage_rect_in_quad_space, damage_rect_in_quad_space_valid,
-                     rounded_corner_info);
+                     mask_filter_info);
 }
 
 void SurfaceAggregator::EmitSurfaceContent(
@@ -451,7 +432,7 @@
     bool ignore_undamaged,
     gfx::Rect* damage_rect_in_quad_space,
     bool* damage_rect_in_quad_space_valid,
-    const RoundedCornerInfo& rounded_corner_info) {
+    const gfx::MaskFilterInfo& mask_filter_info) {
   // If this surface's id is already in our referenced set then it creates
   // a cycle in the graph and should be dropped.
   SurfaceId surface_id = surface->surface_id();
@@ -540,7 +521,7 @@
   bool merge_pass =
       CanPotentiallyMergePass(*surface_quad) && !reflected_and_scaled &&
       copy_requests.empty() && combined_transform.Preserves2dAxisAlignment() &&
-      CanMergeRoundedCorner(rounded_corner_info, *render_pass_list.back());
+      CanMergeMaskFilterInfo(mask_filter_info, *render_pass_list.back());
 
   if (frame.metadata.delegated_ink_metadata) {
     // The metadata must be taken off of the surface, rather than a copy being
@@ -594,7 +575,7 @@
 
     CopyQuadsToPass(source, copy_pass.get(), frame.device_scale_factor(),
                     child_to_parent_map, gfx::Transform(), {}, surface_id,
-                    RoundedCornerInfo());
+                    gfx::MaskFilterInfo());
 
     // If the render pass has copy requests, or should be cached, or has
     // moving-pixel filters, or in a moving-pixel surface, we should damage the
@@ -640,7 +621,7 @@
 
     CopyQuadsToPass(last_pass, dest_pass, frame.device_scale_factor(),
                     child_to_parent_map, combined_transform, quads_clip,
-                    surface_id, rounded_corner_info);
+                    surface_id, mask_filter_info);
   } else {
     auto* shared_quad_state = CopyAndScaleSharedQuadState(
         source_sqs, scaled_quad_to_target_transform, target_transform,
@@ -650,7 +631,7 @@
         gfx::ScaleToEnclosingRect(source_sqs->visible_quad_layer_rect,
                                   inverse_extra_content_scale_x,
                                   inverse_extra_content_scale_y),
-        clip_rect, dest_pass, rounded_corner_info);
+        clip_rect, dest_pass, mask_filter_info);
 
     // At this point, we need to calculate three values in order to construct
     // the CompositorRenderPassDrawQuad:
@@ -710,14 +691,14 @@
     const gfx::Transform& target_transform,
     const ClipData& clip_rect,
     AggregatedRenderPass* dest_pass,
-    const RoundedCornerInfo& rounded_corner_info) {
+    const gfx::MaskFilterInfo& mask_filter_info) {
   // The primary surface is unavailable and there is no fallback
   // surface specified so create a SolidColorDrawQuad with the default
   // background color.
   SkColor background_color = surface_quad->default_background_color;
   auto* shared_quad_state =
       CopySharedQuadState(surface_quad->shared_quad_state, target_transform,
-                          clip_rect, dest_pass, rounded_corner_info);
+                          clip_rect, dest_pass, mask_filter_info);
 
   auto* solid_color_quad =
       dest_pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
@@ -733,7 +714,7 @@
     const ClipData& clip_rect,
     SkColor background_color,
     AggregatedRenderPass* dest_pass,
-    const RoundedCornerInfo& rounded_corner_info) {
+    const gfx::MaskFilterInfo& mask_filter_info) {
   bool has_transparent_background = background_color == SK_ColorTRANSPARENT;
 
   // If the fallback Surface's active CompositorFrame has a non-transparent
@@ -751,7 +732,7 @@
         primary_shared_quad_state,
         primary_shared_quad_state->quad_to_target_transform, target_transform,
         right_gutter_rect, right_gutter_rect, clip_rect, dest_pass,
-        rounded_corner_info);
+        mask_filter_info);
 
     auto* right_gutter =
         dest_pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
@@ -768,7 +749,7 @@
         primary_shared_quad_state,
         primary_shared_quad_state->quad_to_target_transform, target_transform,
         bottom_gutter_rect, bottom_gutter_rect, clip_rect, dest_pass,
-        rounded_corner_info);
+        mask_filter_info);
 
     auto* bottom_gutter =
         dest_pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
@@ -820,7 +801,7 @@
       /*quad_to_target_transform=*/gfx::Transform(),
       /*quad_layer_rect=*/output_rect,
       /*visible_quad_layer_rect=*/output_rect,
-      /*rounded_corner_bounds=*/gfx::RRectF(),
+      /*mask_filter_info=*/gfx::MaskFilterInfo(),
       /*clip_rect=*/gfx::Rect(),
       /*is_clipped=*/false, /*are_contents_opaque=*/false, /*opacity=*/1.f,
       /*blend_mode=*/SkBlendMode::kSrc, /*sorting_context_id=*/0);
@@ -877,7 +858,7 @@
       /*quad_to_target_transform=*/root_surface_transform_,
       /*quad_layer_rect=*/output_rect,
       /*visible_quad_layer_rect=*/output_rect,
-      /*rounded_corner_bounds=*/gfx::RRectF(),
+      /*mask_filter_info=*/gfx::MaskFilterInfo(),
       /*clip_rect=*/gfx::Rect(),
       /*is_clipped=*/false, are_contents_opaque, /*opacity=*/1.f,
       /*blend_mode=*/SkBlendMode::kSrcOver, /*sorting_context_id=*/0);
@@ -897,11 +878,11 @@
     const gfx::Transform& target_transform,
     const ClipData& clip_rect,
     AggregatedRenderPass* dest_render_pass,
-    const RoundedCornerInfo& rounded_corner_info) {
+    const gfx::MaskFilterInfo& mask_filter_info) {
   return CopyAndScaleSharedQuadState(
       source_sqs, source_sqs->quad_to_target_transform, target_transform,
       source_sqs->quad_layer_rect, source_sqs->visible_quad_layer_rect,
-      clip_rect, dest_render_pass, rounded_corner_info);
+      clip_rect, dest_render_pass, mask_filter_info);
 }
 
 SharedQuadState* SurfaceAggregator::CopyAndScaleSharedQuadState(
@@ -912,7 +893,7 @@
     const gfx::Rect& visible_quad_layer_rect,
     const ClipData& clip_rect,
     AggregatedRenderPass* dest_render_pass,
-    const RoundedCornerInfo& rounded_corner_info) {
+    const gfx::MaskFilterInfo& mask_filter_info) {
   auto* shared_quad_state = dest_render_pass->CreateAndAppendSharedQuadState();
   ClipData new_clip_rect = CalculateClipRect(
       clip_rect, {source_sqs->is_clipped, source_sqs->clip_rect},
@@ -928,12 +909,10 @@
   new_transform.ConcatTransform(target_transform);
 
   shared_quad_state->SetAll(
-      new_transform, quad_layer_rect, visible_quad_layer_rect,
-      rounded_corner_info.bounds, new_clip_rect.rect, new_clip_rect.is_clipped,
+      new_transform, quad_layer_rect, visible_quad_layer_rect, mask_filter_info,
+      new_clip_rect.rect, new_clip_rect.is_clipped,
       source_sqs->are_contents_opaque, source_sqs->opacity,
       source_sqs->blend_mode, source_sqs->sorting_context_id);
-  shared_quad_state->is_fast_rounded_corner =
-      rounded_corner_info.is_fast_rounded_corner;
 
   shared_quad_state->de_jelly_delta_y = source_sqs->de_jelly_delta_y;
 
@@ -948,7 +927,7 @@
     const gfx::Transform& target_transform,
     const ClipData& clip_rect,
     const SurfaceId& surface_id,
-    const RoundedCornerInfo& parent_rounded_corner_info) {
+    const gfx::MaskFilterInfo& parent_mask_filter_info) {
   const QuadList& source_quad_list = source_pass.quad_list;
   const SharedQuadState* last_copied_source_shared_quad_state = nullptr;
 
@@ -988,12 +967,12 @@
       ProcessOverlayDamageList(source_pass, dest_pass, target_transform,
                                surface_id, clip_rect, &overlay_damage_index);
 
-  RoundedCornerInfo new_rounded_corner_info = parent_rounded_corner_info;
+  gfx::MaskFilterInfo new_mask_filter_info = parent_mask_filter_info;
   for (auto* quad : source_quad_list) {
     // Both cannot be set at once. If this happens then a surface is being
     // merged when it should not.
-    DCHECK(quad->shared_quad_state->rounded_corner_bounds.IsEmpty() ||
-           parent_rounded_corner_info.bounds.IsEmpty());
+    DCHECK(quad->shared_quad_state->mask_filter_info.IsEmpty() ||
+           parent_mask_filter_info.IsEmpty());
 
     if (quad->material == DrawQuad::Material::kSurfaceContent) {
       const auto* surface_quad = SurfaceDrawQuad::MaterialCast(quad);
@@ -1004,27 +983,24 @@
       if (!surface_quad->surface_range.end().is_valid())
         continue;
 
-      if (parent_rounded_corner_info.bounds.IsEmpty()) {
-        new_rounded_corner_info = RoundedCornerInfo(
-            quad->shared_quad_state->rounded_corner_bounds,
-            quad->shared_quad_state->is_fast_rounded_corner, target_transform);
+      if (parent_mask_filter_info.IsEmpty()) {
+        new_mask_filter_info = quad->shared_quad_state->mask_filter_info;
+        new_mask_filter_info.Transform(target_transform);
       }
 
-      HandleSurfaceQuad(
-          surface_quad, parent_device_scale_factor, target_transform, clip_rect,
-          dest_pass, ignore_undamaged, &damage_rect_in_quad_space,
-          &damage_rect_in_quad_space_valid, new_rounded_corner_info);
+      HandleSurfaceQuad(surface_quad, parent_device_scale_factor,
+                        target_transform, clip_rect, dest_pass,
+                        ignore_undamaged, &damage_rect_in_quad_space,
+                        &damage_rect_in_quad_space_valid, new_mask_filter_info);
     } else {
       if (quad->shared_quad_state != last_copied_source_shared_quad_state) {
-        if (parent_rounded_corner_info.bounds.IsEmpty()) {
-          new_rounded_corner_info =
-              RoundedCornerInfo(quad->shared_quad_state->rounded_corner_bounds,
-                                quad->shared_quad_state->is_fast_rounded_corner,
-                                target_transform);
+        if (parent_mask_filter_info.IsEmpty()) {
+          new_mask_filter_info = quad->shared_quad_state->mask_filter_info;
+          new_mask_filter_info.Transform(target_transform);
         }
         SharedQuadState* dest_shared_quad_state =
             CopySharedQuadState(quad->shared_quad_state, target_transform,
-                                clip_rect, dest_pass, new_rounded_corner_info);
+                                clip_rect, dest_pass, new_mask_filter_info);
 
         if (quad == quad_with_overlay_damage_index)
           dest_shared_quad_state->overlay_damage_index = overlay_damage_index;
@@ -1182,7 +1158,7 @@
                     child_to_parent_map,
                     apply_surface_transform_to_root_pass ? surface_transform
                                                          : gfx::Transform(),
-                    {}, surface->surface_id(), RoundedCornerInfo());
+                    {}, surface->surface_id(), gfx::MaskFilterInfo());
 
     // If the render pass has copy requests, or should be cached, or has
     // moving-pixel filters, or in a moving-pixel surface, we should damage the
@@ -1631,23 +1607,24 @@
   }
 }
 
-bool SurfaceAggregator::CanMergeRoundedCorner(
-    const RoundedCornerInfo& rounded_corner_info,
+bool SurfaceAggregator::CanMergeMaskFilterInfo(
+    const gfx::MaskFilterInfo& mask_filter_info,
     const CompositorRenderPass& root_render_pass) {
-  // If the quad has no rounded corner, then we do not have to block merging.
-  if (rounded_corner_info.bounds.IsEmpty())
+  // If the quad has no mask filter, then we do not have to block merging.
+  if (mask_filter_info.IsEmpty())
     return true;
 
   // If the quad has rounded corner and it is not a fast rounded corner, we
   // cannot merge.
-  if (!rounded_corner_info.is_fast_rounded_corner)
+  if (mask_filter_info.HasRoundedCorners() &&
+      !mask_filter_info.is_fast_rounded_corner())
     return false;
 
-  // If any of the quads in the root render pass has a rounded corner of its
+  // If any of the quads in the root render pass has a mask filter of its
   // own, then we cannot merge.
   const SharedQuadStateList& sqs_list = root_render_pass.shared_quad_state_list;
   for (const auto* sqs : sqs_list) {
-    if (!sqs->rounded_corner_bounds.IsEmpty())
+    if (!sqs->mask_filter_info.IsEmpty())
       return false;
   }
   return true;
@@ -2145,8 +2122,8 @@
   auto* new_state = root_pass->CreateAndAppendSharedQuadState();
   gfx::Transform transform;
   new_state->SetAll(transform, render_pass->output_rect,
-                    render_pass->output_rect, gfx::RRectF(), jelly_clip, true,
-                    false, opacity, blend_mode, 0);
+                    render_pass->output_rect, gfx::MaskFilterInfo(), jelly_clip,
+                    true, false, opacity, blend_mode, 0);
   auto* quad =
       root_pass->CreateAndAppendDrawQuad<AggregatedRenderPassDrawQuad>();
   quad->SetNew(new_state, render_pass->output_rect, render_pass->output_rect,
diff --git a/components/viz/service/display/surface_aggregator.h b/components/viz/service/display/surface_aggregator.h
index 7f49924..fa40470a 100644
--- a/components/viz/service/display/surface_aggregator.h
+++ b/components/viz/service/display/surface_aggregator.h
@@ -93,7 +93,6 @@
  private:
   struct ClipData;
   struct PrewalkResult;
-  struct RoundedCornerInfo;
   struct ChildSurfaceInfo;
   struct RenderPassMapEntry;
 
@@ -115,7 +114,7 @@
                          bool ignore_undamaged,
                          gfx::Rect* damage_rect_in_quad_space,
                          bool* damage_rect_in_quad_space_valid,
-                         const RoundedCornerInfo& rounded_corner_info);
+                         const gfx::MaskFilterInfo& rounded_corner_info);
 
   void EmitSurfaceContent(Surface* surface,
                           float parent_device_scale_factor,
@@ -126,14 +125,14 @@
                           bool ignore_undamaged,
                           gfx::Rect* damage_rect_in_quad_space,
                           bool* damage_rect_in_quad_space_valid,
-                          const RoundedCornerInfo& rounded_corner_info);
+                          const gfx::MaskFilterInfo& rounded_corner_info);
 
   void EmitDefaultBackgroundColorQuad(
       const SurfaceDrawQuad* surface_quad,
       const gfx::Transform& target_transform,
       const ClipData& clip_rect,
       AggregatedRenderPass* dest_pass,
-      const RoundedCornerInfo& rounded_corner_info);
+      const gfx::MaskFilterInfo& rounded_corner_info);
 
   void EmitGutterQuadsIfNecessary(
       const gfx::Rect& primary_rect,
@@ -143,14 +142,14 @@
       const ClipData& clip_rect,
       SkColor background_color,
       AggregatedRenderPass* dest_pass,
-      const RoundedCornerInfo& rounded_corner_info);
+      const gfx::MaskFilterInfo& rounded_corner_info);
 
   SharedQuadState* CopySharedQuadState(
       const SharedQuadState* source_sqs,
       const gfx::Transform& target_transform,
       const ClipData& clip_rect,
       AggregatedRenderPass* dest_render_pass,
-      const RoundedCornerInfo& rounded_corner_info);
+      const gfx::MaskFilterInfo& rounded_corner_info);
 
   SharedQuadState* CopyAndScaleSharedQuadState(
       const SharedQuadState* source_sqs,
@@ -160,7 +159,7 @@
       const gfx::Rect& visible_quad_layer_rect,
       const ClipData& clip_rect,
       AggregatedRenderPass* dest_render_pass,
-      const RoundedCornerInfo& rounded_corner_info);
+      const gfx::MaskFilterInfo& rounded_corner_info);
 
   void CopyQuadsToPass(
       const CompositorRenderPass& source_pass,
@@ -170,7 +169,7 @@
       const gfx::Transform& target_transform,
       const ClipData& clip_rect,
       const SurfaceId& surface_id,
-      const RoundedCornerInfo& rounded_corner_info);
+      const gfx::MaskFilterInfo& rounded_corner_info);
 
   // Recursively walks through the render pass and updates the
   // |can_use_backdrop_filter_cache| flag on all RenderPassDrawQuads(RPDQ).
@@ -235,9 +234,9 @@
   void PropagateCopyRequestPasses();
 
   // Returns true if the quad list from the render pass provided can be merged
-  // with its target render pass based on rounded corners.
-  bool CanMergeRoundedCorner(const RoundedCornerInfo& rounded_corner_info,
-                             const CompositorRenderPass& root_render_pass);
+  // with its target render pass based on mask filter info.
+  bool CanMergeMaskFilterInfo(const gfx::MaskFilterInfo& rounded_corner_info,
+                              const CompositorRenderPass& root_render_pass);
 
   int ChildIdForSurface(Surface* surface);
   bool IsSurfaceFrameIndexSameAsPrevious(const Surface* surface) const;
diff --git a/components/viz/service/display/surface_aggregator_pixeltest.cc b/components/viz/service/display/surface_aggregator_pixeltest.cc
index e5ff4f3e..96203ed4 100644
--- a/components/viz/service/display/surface_aggregator_pixeltest.cc
+++ b/components/viz/service/display/surface_aggregator_pixeltest.cc
@@ -72,7 +72,7 @@
     const gfx::Size& size) {
   const gfx::Rect layer_rect = gfx::Rect(size);
   const gfx::Rect visible_layer_rect = gfx::Rect(size);
-  const gfx::RRectF rounded_corner_bounds = gfx::RRectF();
+  const gfx::MaskFilterInfo mask_filter_info;
   const gfx::Rect clip_rect = gfx::Rect(size);
   bool is_clipped = false;
   bool are_contents_opaque = false;
@@ -80,7 +80,7 @@
   const SkBlendMode blend_mode = SkBlendMode::kSrcOver;
   auto* shared_state = render_pass->CreateAndAppendSharedQuadState();
   shared_state->SetAll(transform, layer_rect, visible_layer_rect,
-                       rounded_corner_bounds, clip_rect, is_clipped,
+                       mask_filter_info, clip_rect, is_clipped,
                        are_contents_opaque, opacity, blend_mode, 0);
   return shared_state;
 }
@@ -381,7 +381,7 @@
       root_surface_id, this->GetNextDisplayTime(), gfx::OVERLAY_TRANSFORM_NONE);
 
   bool discard_alpha = false;
-  cc::ExactPixelComparator pixel_comparator(discard_alpha);
+  cc::FuzzyPixelOffByOneComparator pixel_comparator(discard_alpha);
   auto* pass_list = &aggregated_frame.render_pass_list;
   EXPECT_TRUE(this->RunPixelTest(
       pass_list, base::FilePath(FILE_PATH_LITERAL("delegated_ink_trail.png")),
diff --git a/components/viz/service/display/surface_aggregator_unittest.cc b/components/viz/service/display/surface_aggregator_unittest.cc
index dd7a8022..77e457b 100644
--- a/components/viz/service/display/surface_aggregator_unittest.cc
+++ b/components/viz/service/display/surface_aggregator_unittest.cc
@@ -183,8 +183,7 @@
                             float opacity,
                             const gfx::Transform& transform,
                             bool stretch_content_to_fill_bounds,
-                            const gfx::RRectF& rounded_corner_bounds,
-                            bool is_fast_rounded_corner) {
+                            const gfx::MaskFilterInfo& mask_filter_info) {
       Quad quad;
       quad.material = DrawQuad::Material::kSurfaceContent;
       quad.primary_surface_rect = primary_surface_rect;
@@ -193,8 +192,7 @@
       quad.surface_range = surface_range;
       quad.default_background_color = default_background_color;
       quad.stretch_content_to_fill_bounds = stretch_content_to_fill_bounds;
-      quad.rounded_corner_bounds = rounded_corner_bounds;
-      quad.is_fast_rounded_corner = is_fast_rounded_corner;
+      quad.mask_filter_info = mask_filter_info;
       return quad;
     }
 
@@ -218,8 +216,7 @@
     gfx::Rect primary_surface_rect;
     float opacity;
     gfx::Transform to_target_transform;
-    gfx::RRectF rounded_corner_bounds;
-    bool is_fast_rounded_corner;
+    gfx::MaskFilterInfo mask_filter_info;
     bool allow_merge = true;
 
     // Set when material==DrawQuad::Material::kSolidColor.
@@ -279,8 +276,7 @@
                        desc.to_target_transform, desc.surface_range,
                        desc.default_background_color,
                        desc.stretch_content_to_fill_bounds,
-                       desc.rounded_corner_bounds, desc.is_fast_rounded_corner,
-                       desc.allow_merge);
+                       desc.mask_filter_info, desc.allow_merge);
         break;
       case DrawQuad::Material::kCompositorRenderPass:
         AddRenderPassQuad(pass, desc.render_pass_id, desc.transform,
@@ -371,8 +367,7 @@
                              const SurfaceRange& surface_range,
                              SkColor default_background_color,
                              bool stretch_content_to_fill_bounds,
-                             const gfx::RRectF& rounded_corner_bounds,
-                             bool is_fast_rounded_corner,
+                             const gfx::MaskFilterInfo& mask_filter_info,
                              bool allow_merge) {
     gfx::Transform layer_to_target_transform = transform;
     gfx::Rect layer_bounds(primary_surface_rect);
@@ -384,10 +379,9 @@
 
     auto* shared_quad_state = pass->CreateAndAppendSharedQuadState();
     shared_quad_state->SetAll(layer_to_target_transform, layer_bounds,
-                              visible_layer_rect, rounded_corner_bounds,
-                              clip_rect, is_clipped, are_contents_opaque,
-                              opacity, blend_mode, 0);
-    shared_quad_state->is_fast_rounded_corner = is_fast_rounded_corner;
+                              visible_layer_rect, mask_filter_info, clip_rect,
+                              is_clipped, are_contents_opaque, opacity,
+                              blend_mode, 0);
 
     SurfaceDrawQuad* surface_quad =
         pass->CreateAndAppendDrawQuad<SurfaceDrawQuad>();
@@ -404,9 +398,9 @@
                                 bool can_use_backdrop_filter_cache) {
     gfx::Rect output_rect = gfx::Rect(0, 0, 5, 5);
     auto* shared_state = pass->CreateAndAppendSharedQuadState();
-    shared_state->SetAll(transform, output_rect, output_rect, gfx::RRectF(),
-                         output_rect, false, false, 1, SkBlendMode::kSrcOver,
-                         0);
+    shared_state->SetAll(transform, output_rect, output_rect,
+                         gfx::MaskFilterInfo(), output_rect, false, false, 1,
+                         SkBlendMode::kSrcOver, 0);
     auto* quad = pass->CreateAndAppendDrawQuad<CompositorRenderPassDrawQuad>();
     quad->SetAll(
         shared_state, output_rect, output_rect, /*needs_blending=*/true,
@@ -419,7 +413,7 @@
                               const gfx::Rect& output_rect) {
     auto* shared_state = pass->CreateAndAppendSharedQuadState();
     shared_state->SetAll(gfx::Transform(), output_rect, output_rect,
-                         gfx::RRectF(), output_rect, false, false, 1,
+                         gfx::MaskFilterInfo(), output_rect, false, false, 1,
                          SkBlendMode::kSrcOver, 0);
     auto* quad = pass->CreateAndAppendDrawQuad<YUVVideoDrawQuad>();
     quad->SetNew(shared_state, output_rect, output_rect, false,
@@ -553,8 +547,7 @@
     for (const SurfaceRange& range : ranges) {
       quads.push_back(Quad::SurfaceQuad(
           range, SK_ColorWHITE, gfx::Rect(5, 5), 1.f, gfx::Transform(),
-          /*stretch_content_to_fill_bounds=*/false, gfx::RRectF(),
-          /*is_fast_border_radius*/ false));
+          /*stretch_content_to_fill_bounds=*/false, gfx::MaskFilterInfo()));
     }
     std::vector<Pass> passes = {Pass(quads, SurfaceSize())};
     CompositorRenderPassList pass_list;
@@ -661,8 +654,7 @@
     std::vector<Quad> quads = {Quad::SurfaceQuad(
         SurfaceRange(base::nullopt, embedded_surface_id), SK_ColorWHITE,
         gfx::Rect(5, 5), .5f, gfx::Transform(),
-        /*stretch_content_to_fill_bounds=*/false, gfx::RRectF(),
-        /*is_fast_border_radius*/ false)};
+        /*stretch_content_to_fill_bounds=*/false, gfx::MaskFilterInfo())};
     std::vector<Pass> passes = {Pass(quads, SurfaceSize())};
 
     SubmitCompositorFrame(root_sink_.get(), passes, root_local_surface_id_,
@@ -684,8 +676,7 @@
     std::vector<Quad> quads = {Quad::SurfaceQuad(
         SurfaceRange(base::nullopt, embedded_surface_id), SK_ColorWHITE,
         gfx::Rect(5, 5), .9999f, gfx::Transform(),
-        /*stretch_content_to_fill_bounds=*/false, gfx::RRectF(),
-        /*is_fast_border_radius*/ false)};
+        /*stretch_content_to_fill_bounds=*/false, gfx::MaskFilterInfo())};
     std::vector<Pass> passes = {Pass(quads, SurfaceSize())};
 
     SubmitCompositorFrame(root_sink_.get(), passes, root_local_surface_id_,
@@ -720,11 +711,10 @@
                         embedded_local_surface_id, device_scale_factor);
   gfx::Transform rotate;
   rotate.Rotate(30);
-  std::vector<Quad> quads = {
-      Quad::SurfaceQuad(SurfaceRange(base::nullopt, embedded_surface_id),
-                        SK_ColorWHITE, gfx::Rect(5, 5), 1.f, rotate,
-                        /*stretch_content_to_fill_bounds=*/false, gfx::RRectF(),
-                        /*is_fast_border_radius*/ false)};
+  std::vector<Quad> quads = {Quad::SurfaceQuad(
+      SurfaceRange(base::nullopt, embedded_surface_id), SK_ColorWHITE,
+      gfx::Rect(5, 5), 1.f, rotate,
+      /*stretch_content_to_fill_bounds=*/false, gfx::MaskFilterInfo())};
   std::vector<Pass> passes = {Pass(quads, SurfaceSize())};
 
   SubmitCompositorFrame(root_sink_.get(), passes, root_local_surface_id_,
@@ -2204,14 +2194,14 @@
                 ->render_pass_id);
 }
 
-void AddSolidColorQuadWithBlendMode(const gfx::Size& size,
-                                    CompositorRenderPass* pass,
-                                    const SkBlendMode blend_mode,
-                                    const gfx::RRectF& corner_bounds) {
+void AddSolidColorQuadWithBlendMode(
+    const gfx::Size& size,
+    CompositorRenderPass* pass,
+    const SkBlendMode blend_mode,
+    const gfx::MaskFilterInfo& mask_filter_info) {
   const gfx::Transform layer_to_target_transform;
   const gfx::Rect layer_rect(size);
   const gfx::Rect visible_layer_rect(size);
-  const gfx::RRectF rounded_corner_bounds = corner_bounds;
   const gfx::Rect clip_rect(size);
 
   bool is_clipped = false;
@@ -2222,7 +2212,7 @@
   bool force_anti_aliasing_off = false;
   auto* sqs = pass->CreateAndAppendSharedQuadState();
   sqs->SetAll(layer_to_target_transform, layer_rect, visible_layer_rect,
-              rounded_corner_bounds, clip_rect, is_clipped, are_contents_opaque,
+              mask_filter_info, clip_rect, is_clipped, are_contents_opaque,
               opacity, blend_mode, 0);
 
   auto* color_quad = pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
@@ -2291,7 +2281,7 @@
   grandchild_pass->SetNew(pass_id, output_rect, damage_rect,
                           transform_to_root_target);
   AddSolidColorQuadWithBlendMode(SurfaceSize(), grandchild_pass.get(),
-                                 blend_modes[2], gfx::RRectF());
+                                 blend_modes[2], gfx::MaskFilterInfo());
   QueuePassAsFrame(std::move(grandchild_pass), grandchild_local_surface_id,
                    device_scale_factor, grandchild_support.get());
 
@@ -2305,7 +2295,7 @@
   child_one_pass->SetNew(pass_id, output_rect, damage_rect,
                          transform_to_root_target);
   AddSolidColorQuadWithBlendMode(SurfaceSize(), child_one_pass.get(),
-                                 blend_modes[1], gfx::RRectF());
+                                 blend_modes[1], gfx::MaskFilterInfo());
   auto* grandchild_surface_quad =
       child_one_pass->CreateAndAppendDrawQuad<SurfaceDrawQuad>();
   grandchild_surface_quad->SetNew(
@@ -2314,7 +2304,7 @@
       SurfaceRange(base::nullopt, grandchild_surface_id), SK_ColorWHITE,
       /*stretch_content_to_fill_bounds=*/false);
   AddSolidColorQuadWithBlendMode(SurfaceSize(), child_one_pass.get(),
-                                 blend_modes[3], gfx::RRectF());
+                                 blend_modes[3], gfx::MaskFilterInfo());
   QueuePassAsFrame(std::move(child_one_pass), child_one_local_surface_id,
                    device_scale_factor, child_one_support.get());
 
@@ -2328,7 +2318,7 @@
   child_two_pass->SetNew(pass_id, output_rect, damage_rect,
                          transform_to_root_target);
   AddSolidColorQuadWithBlendMode(SurfaceSize(), child_two_pass.get(),
-                                 blend_modes[5], gfx::RRectF());
+                                 blend_modes[5], gfx::MaskFilterInfo());
   QueuePassAsFrame(std::move(child_two_pass), child_two_local_surface_id,
                    device_scale_factor, child_two_support.get());
 
@@ -2337,7 +2327,7 @@
                     transform_to_root_target);
 
   AddSolidColorQuadWithBlendMode(SurfaceSize(), root_pass.get(), blend_modes[0],
-                                 gfx::RRectF());
+                                 gfx::MaskFilterInfo());
   auto* child_one_surface_quad =
       root_pass->CreateAndAppendDrawQuad<SurfaceDrawQuad>();
   child_one_surface_quad->SetNew(
@@ -2346,7 +2336,7 @@
       SurfaceRange(base::nullopt, child_one_surface_id), SK_ColorWHITE,
       /*stretch_content_to_fill_bounds=*/false);
   AddSolidColorQuadWithBlendMode(SurfaceSize(), root_pass.get(), blend_modes[4],
-                                 gfx::RRectF());
+                                 gfx::MaskFilterInfo());
   auto* child_two_surface_quad =
       root_pass->CreateAndAppendDrawQuad<SurfaceDrawQuad>();
   child_two_surface_quad->SetNew(
@@ -2355,7 +2345,7 @@
       SurfaceRange(base::nullopt, child_two_surface_id), SK_ColorWHITE,
       /*stretch_content_to_fill_bounds=*/false);
   AddSolidColorQuadWithBlendMode(SurfaceSize(), root_pass.get(), blend_modes[6],
-                                 gfx::RRectF());
+                                 gfx::MaskFilterInfo());
 
   QueuePassAsFrame(std::move(root_pass), root_local_surface_id_,
                    device_scale_factor, root_sink_.get());
@@ -2406,8 +2396,10 @@
 //  quad(d)           - rounded corner [2]
 TEST_F(SurfaceAggregatorValidSurfaceTest,
        AggregateSharedQuadStateRoundedCornerBounds) {
-  const gfx::RRectF kFastRoundedCornerBounds = gfx::RRectF(0, 0, 640, 480, 5);
-  const gfx::RRectF kRoundedCornerBounds = gfx::RRectF(0, 0, 100, 100, 2);
+  const gfx::MaskFilterInfo kMaskFilterInfoWithFastRoundedCorners(
+      gfx::RRectF(0, 0, 640, 480, 5), true);
+  const gfx::MaskFilterInfo kMaskFilterInfoWithRoundedCorners(
+      gfx::RRectF(0, 0, 100, 100, 2), false);
 
   ParentLocalSurfaceIdAllocator child_root_allocator;
   ParentLocalSurfaceIdAllocator child_one_allocator;
@@ -2439,7 +2431,7 @@
   child_three_pass->SetNew(pass_id, output_rect, damage_rect,
                            transform_to_root_target);
   AddSolidColorQuadWithBlendMode(SurfaceSize(), child_three_pass.get(),
-                                 SkBlendMode::kSrcOver, gfx::RRectF());
+                                 SkBlendMode::kSrcOver, gfx::MaskFilterInfo());
   QueuePassAsFrame(std::move(child_three_pass), child_three_local_surface_id,
                    device_scale_factor, child_three_support.get());
 
@@ -2454,7 +2446,7 @@
   child_one_pass->SetNew(pass_id, output_rect, damage_rect,
                          transform_to_root_target);
   AddSolidColorQuadWithBlendMode(SurfaceSize(), child_one_pass.get(),
-                                 SkBlendMode::kSrcOver, gfx::RRectF());
+                                 SkBlendMode::kSrcOver, gfx::MaskFilterInfo());
 
   // Add child three surface quad
   auto* child_three_surface_sqs =
@@ -2482,9 +2474,10 @@
   child_two_pass->SetNew(pass_id, output_rect, damage_rect,
                          transform_to_root_target);
   AddSolidColorQuadWithBlendMode(SurfaceSize(), child_two_pass.get(),
-                                 SkBlendMode::kSrcOver, gfx::RRectF());
+                                 SkBlendMode::kSrcOver, gfx::MaskFilterInfo());
   AddSolidColorQuadWithBlendMode(SurfaceSize(), child_two_pass.get(),
-                                 SkBlendMode::kSrcOver, kRoundedCornerBounds);
+                                 SkBlendMode::kSrcOver,
+                                 kMaskFilterInfoWithRoundedCorners);
   QueuePassAsFrame(std::move(child_two_pass), child_two_local_surface_id,
                    device_scale_factor, child_two_support.get());
 
@@ -2523,7 +2516,7 @@
 
   // Add solid color quad
   AddSolidColorQuadWithBlendMode(SurfaceSize(), child_root_pass.get(),
-                                 SkBlendMode::kSrcOver, gfx::RRectF());
+                                 SkBlendMode::kSrcOver, gfx::MaskFilterInfo());
   QueuePassAsFrame(std::move(child_root_pass), child_root_local_surface_id,
                    device_scale_factor, child_root_support.get());
 
@@ -2535,8 +2528,8 @@
   auto* child_root_surface_quad =
       root_pass->CreateAndAppendDrawQuad<SurfaceDrawQuad>();
   child_root_surface_sqs->opacity = 1.f;
-  child_root_surface_sqs->rounded_corner_bounds = kFastRoundedCornerBounds;
-  child_root_surface_sqs->is_fast_rounded_corner = true;
+  child_root_surface_sqs->mask_filter_info =
+      kMaskFilterInfoWithFastRoundedCorners;
   child_root_surface_quad->SetNew(
       child_root_surface_sqs, gfx::Rect(SurfaceSize()),
       gfx::Rect(SurfaceSize()),
@@ -2561,17 +2554,17 @@
   const auto& aggregated_quad_list_of_surface =
       aggregated_pass_list[0]->quad_list;
   EXPECT_EQ(2u, aggregated_quad_list_of_surface.size());
-  EXPECT_EQ(kRoundedCornerBounds,
+  EXPECT_EQ(kMaskFilterInfoWithRoundedCorners,
             aggregated_quad_list_of_surface.back()
-                ->shared_quad_state->rounded_corner_bounds);
+                ->shared_quad_state->mask_filter_info);
 
   // The root render pass will have all the remaining quads with the rounded
   // corner set on them.
   const auto& aggregated_quad_list_of_root = aggregated_pass_list[1]->quad_list;
   EXPECT_EQ(4u, aggregated_quad_list_of_root.size());
   for (const auto* q : aggregated_quad_list_of_root) {
-    EXPECT_EQ(q->shared_quad_state->rounded_corner_bounds,
-              kFastRoundedCornerBounds);
+    EXPECT_EQ(q->shared_quad_state->mask_filter_info,
+              kMaskFilterInfoWithFastRoundedCorners);
   }
 }
 
@@ -6760,12 +6753,11 @@
   // root surface quads
   std::vector<Quad> root_surface_quads = {
       Quad::SolidColorQuad(SK_ColorRED, gfx::Rect(60, 0, 40, 40)),
-      Quad::SurfaceQuad(SurfaceRange(base::nullopt, child_surface_id),
-                        SK_ColorWHITE,
-                        /*primary_surface_rect*/ gfx::Rect(0, 0, 100, 100),
-                        /*opacity*/ 1.f, video_transform,
-                        /*stretch_content_to_fill_bounds=*/false, gfx::RRectF(),
-                        /*is_fast_border_radius*/ false)};
+      Quad::SurfaceQuad(
+          SurfaceRange(base::nullopt, child_surface_id), SK_ColorWHITE,
+          /*primary_surface_rect*/ gfx::Rect(0, 0, 100, 100),
+          /*opacity*/ 1.f, video_transform,
+          /*stretch_content_to_fill_bounds=*/false, gfx::MaskFilterInfo())};
 
   std::vector<Pass> root_passes = {
       Pass(root_surface_quads,
@@ -6885,8 +6877,7 @@
         SurfaceRange(base::nullopt, child_surface_id), SK_ColorWHITE,
         /*primary_surface_rect*/ gfx::Rect(0, 0, 100, 100),
         /*opacity*/ 1.f, video_transform,
-        /*stretch_content_to_fill_bounds=*/false, gfx::RRectF(),
-        /*is_fast_border_radius*/ false)};
+        /*stretch_content_to_fill_bounds=*/false, gfx::MaskFilterInfo())};
 
     std::vector<Pass> root_passes = {
         Pass(root_surface_quads,
@@ -6983,8 +6974,7 @@
             SurfaceRange(base::nullopt, child_surface_id), SK_ColorWHITE,
             /*primary_surface_rect*/ gfx::Rect(0, 0, 100, 100),
             /*opacity*/ 1.f, video_transform,
-            /*stretch_content_to_fill_bounds=*/false, gfx::RRectF(),
-            /*is_fast_border_radius*/ false)};
+            /*stretch_content_to_fill_bounds=*/false, gfx::MaskFilterInfo())};
 
     std::vector<Pass> root_passes = {
         Pass(root_surface_quads,
@@ -7308,7 +7298,8 @@
 
     child_frame.render_pass_list[0]
         ->shared_quad_state_list.front()
-        ->rounded_corner_bounds = gfx::RRectF(0, 0, 100, 10, 5);
+        ->mask_filter_info =
+        gfx::MaskFilterInfo(gfx::RRectF(0, 0, 100, 10, 5), false);
 
     child_sink_->SubmitCompositorFrame(child_local_surface_id,
                                        std::move(child_frame));
@@ -7337,8 +7328,9 @@
   auto* aggregated_first_pass_sqs =
       aggregated_frame.render_pass_list[0]->shared_quad_state_list.front();
 
-  EXPECT_EQ(gfx::RRectF(0, 7, 100, 10, 5),
-            aggregated_first_pass_sqs->rounded_corner_bounds);
+  EXPECT_EQ(
+      gfx::RRectF(0, 7, 100, 10, 5),
+      aggregated_first_pass_sqs->mask_filter_info.rounded_corner_bounds());
 }
 
 // Tests that the rounded corner bounds of a surface quad that gets transformed
@@ -7380,11 +7372,11 @@
                              child_local_surface_id);
   {
     // Set an opacity in order to prevent merging into the root render pass.
-    std::vector<Quad> child_quads = {
-        Quad::SurfaceQuad(SurfaceRange(base::nullopt, grandchild_surface_id),
-                          SK_ColorWHITE, gfx::Rect(5, 5), 0.5f,
-                          gfx::Transform(), false, gfx::RRectF(0, 0, 96, 10, 5),
-                          /*is_fast_border_radius*/ false)};
+    std::vector<Quad> child_quads = {Quad::SurfaceQuad(
+        SurfaceRange(base::nullopt, grandchild_surface_id), SK_ColorWHITE,
+        gfx::Rect(5, 5), 0.5f, gfx::Transform(), false,
+        gfx::MaskFilterInfo(gfx::RRectF(0, 0, 96, 10, 5),
+                            /*is_fast_rounded_corner=*/false))};
 
     std::vector<Pass> child_passes = {
         Pass(child_quads, CompositorRenderPassId{1}, SurfaceSize())};
@@ -7402,7 +7394,7 @@
   surface_transform.Translate(3, 4);
   std::vector<Quad> secondary_quads = {Quad::SurfaceQuad(
       SurfaceRange(base::nullopt, child_surface_id), SK_ColorWHITE,
-      gfx::Rect(5, 5), 1.f, surface_transform, false, gfx::RRectF(), false)};
+      gfx::Rect(5, 5), 1.f, surface_transform, false, gfx::MaskFilterInfo())};
 
   std::vector<Pass> root_passes = {Pass(secondary_quads, SurfaceSize())};
 
@@ -7427,8 +7419,9 @@
   // Original rounded rect is (0, 0, 96, 10, 5). This then gets multiplied
   // by a device scale factor of 2 to (0, 0, 192, 20, 10), then moved
   // by a (3, 4) translation followed by a (0, 7) translation.
-  EXPECT_EQ(gfx::RRectF(3, 11, 192, 20, 10),
-            aggregated_first_pass_sqs->rounded_corner_bounds);
+  EXPECT_EQ(
+      gfx::RRectF(3, 11, 192, 20, 10),
+      aggregated_first_pass_sqs->mask_filter_info.rounded_corner_bounds());
 }
 
 // This is a variant of RoundedCornerTransformedSurfaceQuad that does not
@@ -7467,11 +7460,10 @@
   SurfaceId child_surface_id(child_sink_->frame_sink_id(),
                              child_local_surface_id);
   {
-    std::vector<Quad> child_quads = {
-        Quad::SurfaceQuad(SurfaceRange(base::nullopt, grandchild_surface_id),
-                          SK_ColorWHITE, gfx::Rect(5, 5), 1.f, gfx::Transform(),
-                          false, gfx::RRectF(0, 0, 96, 10, 5),
-                          /*is_fast_border_radius*/ false)};
+    std::vector<Quad> child_quads = {Quad::SurfaceQuad(
+        SurfaceRange(base::nullopt, grandchild_surface_id), SK_ColorWHITE,
+        gfx::Rect(5, 5), 1.f, gfx::Transform(), false,
+        gfx::MaskFilterInfo(gfx::RRectF(0, 0, 96, 10, 5), false))};
 
     std::vector<Pass> child_passes = {
         Pass(child_quads, CompositorRenderPassId{1}, SurfaceSize())};
@@ -7489,8 +7481,7 @@
   surface_transform.Translate(3, 4);
   std::vector<Quad> secondary_quads = {Quad::SurfaceQuad(
       SurfaceRange(base::nullopt, child_surface_id), SK_ColorWHITE,
-      gfx::Rect(5, 5), 1.f, surface_transform, false, gfx::RRectF(),
-      /*is_fast_border_radius*/ false)};
+      gfx::Rect(5, 5), 1.f, surface_transform, false, gfx::MaskFilterInfo())};
 
   std::vector<Pass> root_passes = {Pass(secondary_quads, SurfaceSize())};
 
@@ -7515,8 +7506,9 @@
   // Original rounded rect is (0, 0, 96, 10, 5). This then gets multiplied
   // by a device scale factor of 2 to (0, 0, 192, 20, 10), then moved
   // by a (3, 4) translation followed by a (0, 7) translation.
-  EXPECT_EQ(gfx::RRectF(3, 11, 192, 20, 10),
-            aggregated_first_pass_sqs->rounded_corner_bounds);
+  EXPECT_EQ(
+      gfx::RRectF(3, 11, 192, 20, 10),
+      aggregated_first_pass_sqs->mask_filter_info.rounded_corner_bounds());
 }
 
 TEST_F(SurfaceAggregatorValidSurfaceTest, TransformedRoundedSurfaceQuad) {
@@ -7550,11 +7542,11 @@
   // Root surface.
   gfx::Transform surface_transform;
   surface_transform.Translate(3, 4);
-  std::vector<Quad> secondary_quads = {
-      Quad::SurfaceQuad(SurfaceRange(base::nullopt, child_surface_id),
-                        SK_ColorWHITE, gfx::Rect(5, 5), 1.f, surface_transform,
-                        false, gfx::RRectF(0, 0, 96, 10, 5),
-                        /* is_fast_border_radius */ true)};
+  std::vector<Quad> secondary_quads = {Quad::SurfaceQuad(
+      SurfaceRange(base::nullopt, child_surface_id), SK_ColorWHITE,
+      gfx::Rect(5, 5), 1.f, surface_transform, false,
+      gfx::MaskFilterInfo(gfx::RRectF(0, 0, 96, 10, 5),
+                          /*is_fast_rounded_corner=*/true))};
 
   std::vector<Pass> root_passes = {Pass(secondary_quads, SurfaceSize())};
 
@@ -7581,8 +7573,9 @@
 
   // The rounded rect on the surface quad is already in the space of the root
   // surface, so the (3, 4) translation should not apply to it.
-  EXPECT_EQ(gfx::RRectF(0, 0, 96, 10, 5),
-            aggregated_first_pass_sqs->rounded_corner_bounds);
+  EXPECT_EQ(
+      gfx::RRectF(0, 0, 96, 10, 5),
+      aggregated_first_pass_sqs->mask_filter_info.rounded_corner_bounds());
 }
 
 // Verifies that if a child surface is embedded twice in the root surface,
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl.cc b/components/viz/service/display_embedder/skia_output_surface_impl.cc
index 2a8379f..73c56d47 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl.cc
+++ b/components/viz/service/display_embedder/skia_output_surface_impl.cc
@@ -527,6 +527,7 @@
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   // Make sure there is no unsubmitted PaintFrame or PaintRenderPass.
   DCHECK(!current_paint_);
+  DCHECK(resource_sync_tokens_.empty());
 
   SkSurfaceCharacterization characterization = CreateSkSurfaceCharacterization(
       size, BufferFormat(format), mipmap, std::move(color_space),
@@ -705,9 +706,6 @@
 void SkiaOutputSurfaceImpl::ScheduleOverlays(
     OverlayList overlays,
     std::vector<gpu::SyncToken> sync_tokens) {
-  std::move(resource_sync_tokens_.begin(), resource_sync_tokens_.end(),
-            std::back_inserter(sync_tokens));
-  resource_sync_tokens_.clear();
   auto task =
       base::BindOnce(&SkiaOutputSurfaceImplOnGpu::ScheduleOverlays,
                      base::Unretained(impl_on_gpu_.get()), std::move(overlays),
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 04f3d64..810e77d 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -208,7 +208,6 @@
     "//third_party/blink/public:scaled_resources",
     "//third_party/blink/public/common",
     "//third_party/blink/public/common:font_enumeration_table_proto",
-    "//third_party/blink/public/mojom:mojom_broadcastchannel_bindings",
     "//third_party/blink/public/mojom/dom_storage",
     "//third_party/blink/public/mojom/frame",
     "//third_party/blink/public/strings",
@@ -261,6 +260,7 @@
     "//ipc",
     "//media/mojo/mojom:remoting",
     "//third_party/blink/public/mojom:embedded_frame_sink_mojo_bindings",
+    "//third_party/blink/public/mojom:mojom_broadcastchannel_bindings",
     "//third_party/leveldatabase",
   ]
 
diff --git a/content/browser/accessibility/browser_accessibility_android.cc b/content/browser/accessibility/browser_accessibility_android.cc
index 7dc456d..bfe1241f 100644
--- a/content/browser/accessibility/browser_accessibility_android.cc
+++ b/content/browser/accessibility/browser_accessibility_android.cc
@@ -549,30 +549,37 @@
 }
 
 base::string16 BrowserAccessibilityAndroid::GetStateDescription() const {
+  std::vector<base::string16> state_descs;
+
   // For multiselectable state, generate a state description. We do not set a
   // state description for pop up/<select> to prevent double utterances.
   if (IsMultiselectable() && GetRole() != ax::mojom::Role::kPopUpButton)
-    return GetMultiselectableStateDescription();
-
-  // For Toggle buttons, we will append "on"/"off" in the state description.
-  if (GetRole() == ax::mojom::Role::kToggleButton)
-    return GetToggleButtonStateDescription();
+    state_descs.push_back(GetMultiselectableStateDescription());
 
   // For Checkboxes, if we are in a kMixed state, we will communicate
-  // "partially checked" through the state description.
-  if (IsCheckable() && !IsReportingCheckable())
-    return GetCheckboxStateDescription();
+  // "partially checked" through the state description. This is mutually
+  // exclusive with the on/off of toggle buttons below.
+  if (IsCheckable() && !IsReportingCheckable()) {
+    state_descs.push_back(GetCheckboxStateDescription());
+  } else if (GetRole() == ax::mojom::Role::kToggleButton) {
+    // For Toggle buttons, we will append "on"/"off" in the state description.
+    state_descs.push_back(GetToggleButtonStateDescription());
+  }
 
-  // For list boxes, use state description to communicate child item count.
-  if (GetRole() == ax::mojom::Role::kListBox)
-    return GetListBoxStateDescription();
+  // For list boxes, use state description to communicate child item count. We
+  // will not communicate this in the case that the listbox is also
+  // multiselectable and has some items selected, since the same info would be
+  // communicated as "x of y selected".
+  if (GetRole() == ax::mojom::Role::kListBox &&
+      (!IsMultiselectable() || !GetSelectedItemCount()))
+    state_descs.push_back(GetListBoxStateDescription());
 
   // For list box items, use state description to communicate index of item.
   if (GetRole() == ax::mojom::Role::kListBoxOption)
-    return GetListBoxItemStateDescription();
+    state_descs.push_back(GetListBoxItemStateDescription());
 
-  // Otherwise we will not use state description
-  return base::string16();
+  // Concatenate all state descriptions and return.
+  return base::JoinString(state_descs, base::ASCIIToUTF16(" "));
 }
 
 base::string16 BrowserAccessibilityAndroid::GetMultiselectableStateDescription()
@@ -1315,6 +1322,20 @@
   return count;
 }
 
+int BrowserAccessibilityAndroid::GetSelectedItemCount() const {
+  // Count the number of selected children.
+  int selected_count = 0;
+  for (PlatformChildIterator it = PlatformChildrenBegin();
+       it != PlatformChildrenEnd(); ++it) {
+    BrowserAccessibilityAndroid* child =
+        static_cast<BrowserAccessibilityAndroid*>(it.get());
+    if (child->IsSelected())
+      selected_count++;
+  }
+
+  return selected_count;
+}
+
 bool BrowserAccessibilityAndroid::CanScrollForward() const {
   if (IsSlider()) {
     // If it's not a native INPUT element, then increment and decrement
diff --git a/content/browser/accessibility/browser_accessibility_android.h b/content/browser/accessibility/browser_accessibility_android.h
index e99ea62b..66347b5 100644
--- a/content/browser/accessibility/browser_accessibility_android.h
+++ b/content/browser/accessibility/browser_accessibility_android.h
@@ -101,6 +101,7 @@
 
   int GetItemIndex() const;
   int GetItemCount() const;
+  int GetSelectedItemCount() const;
 
   bool CanScrollForward() const;
   bool CanScrollBackward() const;
diff --git a/content/browser/appcache/appcache_disk_cache.cc b/content/browser/appcache/appcache_disk_cache.cc
index 13f2f352..e5263754 100644
--- a/content/browser/appcache/appcache_disk_cache.cc
+++ b/content/browser/appcache/appcache_disk_cache.cc
@@ -382,6 +382,12 @@
         return_value = DoomEntry(call.key, copyable_callback);
         break;
     }
+
+    // disk_cache::{Create,Open,Doom}Entry() call their callbacks iff they
+    // return net::ERR_IO_PENDING. In this case, the callback was not called.
+    // However, the corresponding ServiceWorkerDiskCache wrapper returned
+    // net::ERR_IO_PENDING as it queued up the pending call. To follow the
+    // disk_cache API contract, we need to call the callback ourselves here.
     if (return_value != net::ERR_IO_PENDING)
       copyable_callback.Run(return_value);
   }
diff --git a/content/browser/font_unique_name_lookup/font_unique_name_browsertest.cc b/content/browser/font_unique_name_lookup/font_unique_name_browsertest.cc
index 358b07a8..b3c6b8c 100644
--- a/content/browser/font_unique_name_lookup/font_unique_name_browsertest.cc
+++ b/content/browser/font_unique_name_lookup/font_unique_name_browsertest.cc
@@ -20,18 +20,10 @@
 #include "content/browser/renderer_host/dwrite_font_lookup_table_builder_win.h"
 #endif
 
-#if defined(OS_ANDROID)
-#include "base/android/build_info.h"
-#include "base/feature_list.h"
-#include "components/viz/common/features.h"
-#include "gpu/config/gpu_finch_features.h"
-#endif
-
 namespace content {
 namespace {
 
 #if defined(OS_ANDROID)
-const char* kGoogleSans = "Google Sans";
 const char* kExpectedFontFamilyNames[] = {"AndroidClock",
                                           "Roboto",
                                           "Droid Sans Mono",
@@ -72,10 +64,7 @@
                                           "Roboto Condensed",
                                           "Roboto Condensed",
                                           "Roboto Condensed",
-                                          "Roboto",
-                                          kGoogleSans,
-                                          kGoogleSans,
-                                          kGoogleSans};
+                                          "Roboto"};
 #elif defined(OS_LINUX) || defined(OS_CHROMEOS)
 const char* kExpectedFontFamilyNames[] = {"Ahem",
                                           "Arimo",
@@ -215,22 +204,6 @@
     ASSERT_TRUE(first_font_name);
     ASSERT_TRUE(first_font_name->is_string());
     ASSERT_GT(first_font_name->GetString().size(), 0u);
-#if defined(OS_ANDROID)
-    // Skip Android Google Sans test on Pixel devices < Marshmallow SDK level,
-    // as the firmware font files do not contain this font.
-    bool at_least_marshmallow =
-        base::android::BuildInfo::GetInstance()->sdk_int() >=
-        base::android::SDK_VERSION_MARSHMALLOW;
-    // https://crbug.com/1129552 work around the SkiaRenderer Vulkan bot not
-    // having the Google Sans font.
-    bool on_vulkan_bot =
-        base::FeatureList::IsEnabled(features::kUseSkiaRenderer) &&
-        base::FeatureList::IsEnabled(features::kVulkan);
-    if ((!at_least_marshmallow || on_vulkan_bot) &&
-        std::string(kExpectedFontFamilyNames[i]) == std::string(kGoogleSans)) {
-      continue;
-    }
-#endif
     ASSERT_EQ(first_font_name->GetString(), kExpectedFontFamilyNames[i]);
   }
 }
diff --git a/content/browser/renderer_host/navigation_request.cc b/content/browser/renderer_host/navigation_request.cc
index f9fb1e2..510e880 100644
--- a/content/browser/renderer_host/navigation_request.cc
+++ b/content/browser/renderer_host/navigation_request.cc
@@ -1045,8 +1045,6 @@
   DCHECK(browser_initiated_ || common_params_->initiator_origin.has_value());
   DCHECK(!IsRendererDebugURL(common_params_->url));
   DCHECK(common_params_->method == "POST" || !common_params_->post_data);
-  DCHECK((IsInMainFrame() && browser_initiated) ||
-         commit_params_->frame_policy.has_value());
 
   TRACE_EVENT_NESTABLE_ASYNC_BEGIN1("navigation", "NavigationRequest",
                                     navigation_id_, "navigation_request",
@@ -5012,24 +5010,8 @@
   DCHECK(!HasCommitted());
   DCHECK(!IsErrorPage());
 
-  network::mojom::WebSandboxFlags out;
-  if (commit_params_->frame_policy) {
-    // This corresponds to the sandbox policy of the frame embedding the
-    // document that were active when the navigation started.
-    out = commit_params_->frame_policy->sandbox_flags;
-  } else {
-    // The document doesn't have a sandbox policy. This case should in theory
-    // contains only the navigations that are:
-    // - main frame.
-    // - browser initiated.
-    // - non-history.
-    // - non-error.
-    //
-    // TODO(arthursonzogni): In practice, a few navigations not complying with
-    // one of the 4 items above are using this path. They must be identified
-    // and removed. A set of DCHECK must be added.
-    out = network::mojom::WebSandboxFlags::kNone;
-  }
+  network::mojom::WebSandboxFlags out =
+      commit_params_->frame_policy.sandbox_flags;
 
   // The response can also restrict the policy further.
   if (response_head_) {
diff --git a/content/browser/renderer_host/navigator.cc b/content/browser/renderer_host/navigator.cc
index 36f30f7e..4bac1a60 100644
--- a/content/browser/renderer_host/navigator.cc
+++ b/content/browser/renderer_host/navigator.cc
@@ -301,12 +301,6 @@
     }
   }
 
-  // For browser initiated navigation and same document navigation, frame policy
-  // in commit_params is nullopt and should use fallback value instead.
-  const blink::FramePolicy pending_frame_policy =
-      navigation_request->commit_params().frame_policy.value_or(
-          frame_tree_node->pending_frame_policy());
-
   // DidNavigateFrame() must be called before replicating the new origin and
   // other properties to proxies.  This is because it destroys the subframes of
   // the frame we're navigating from, which might trigger those subframes to
@@ -317,7 +311,7 @@
       is_same_document_navigation,
       navigation_request->coop_status()
           .require_browsing_instance_swap() /* clear_proxies_on_commit */,
-      pending_frame_policy);
+      navigation_request->commit_params().frame_policy);
 
   // Save the new page's origin and other properties, and replicate them to
   // proxies, including the proxy created in DidNavigateFrame() to replace the
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index 161a379..700b495 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -720,12 +720,12 @@
     const url::Origin& subframe_origin) {
   // For main frame loads, the frame's feature policy is determined entirely by
   // response headers, which are provided by the renderer.
-  if (!parent || !commit_params.frame_policy)
+  if (!parent)
     return network::mojom::TrustTokenRedemptionPolicy::kPotentiallyPermit;
 
   const blink::FeaturePolicy* parent_policy = parent->feature_policy();
   blink::ParsedFeaturePolicy container_policy =
-      commit_params.frame_policy->container_policy;
+      commit_params.frame_policy.container_policy;
 
   auto subframe_policy = blink::FeaturePolicy::CreateFromParentPolicy(
       parent_policy, container_policy, subframe_origin);
diff --git a/content/browser/site_instance_impl.h b/content/browser/site_instance_impl.h
index 1420d0ba..7f1af8fa 100644
--- a/content/browser/site_instance_impl.h
+++ b/content/browser/site_instance_impl.h
@@ -124,7 +124,7 @@
 
   bool is_guest() const { return is_guest_; }
 
-  // Returns false if the site_url() is empty.
+  // Returns true if the site_url() is empty.
   bool is_empty() const { return site_url().possibly_invalid_spec().empty(); }
 
   SiteInfo& operator=(const SiteInfo& rhs);
diff --git a/content/common/navigation_params.mojom b/content/common/navigation_params.mojom
index fb5b7a8..3f115208 100644
--- a/content/common/navigation_params.mojom
+++ b/content/common/navigation_params.mojom
@@ -436,24 +436,25 @@
   // Documents that will emit UKM events need valid ids.
   int64 document_ukm_source_id;
 
-  // A snapshot value of frame policy(both sandbox flags and container policy)
+  // A snapshot value of frame policy (both sandbox flags and container policy)
   // of the frame that is being navigated. The snapshot value is captured at the
-  // start of navigation.
-  // - The value is set to null for top-level browser-initiated navigation.
-  // - The value is also set to null when Ctrl-click was used to create a new
-  // tab and navigate, i.e. the document in new tab will inherit no
-  // frame_policy.
-  // - For navigation created from NavigationControllerImpl::
-  // CreateNavigationRequestFromEntry which corresponds to history navigation,
-  // the value is set to current FrameTreeNode::pending_frame_policy in
-  // frame_tree_node. This behavior is currently undocumented and probably need
-  // further discussion. Another potential approach is to record frame policy
-  // value in NavigationEntry and reuse the historical value.
+  // start of navigation:
   // - For local frame navigation, the value is set at NavigationRequest::
   // CreateRendererInitiated.
   // - For remote frame navigation, the value is set at
   // NavigationControllerImpl::CreateNavigationRequestFromLoadParams.
-  blink.mojom.FramePolicy? frame_policy;
+  // - For navigation created from
+  // NavigationControllerImpl::CreateNavigationRequestFromEntry which
+  // corresponds to history navigation, the value is set to current
+  // FrameTreeNode::pending_frame_policy in frame_tree_node. This behavior is
+  // currently undocumented and probably need further discussion. Another
+  // potential approach is to record frame policy value in NavigationEntry and
+  // reuse the historical value.
+  //
+  // The default FramePolicy is the laxest. It is used for:
+  // - Top-level browser-initiated navigations.
+  // - Ctrl+click navigation (opening a link in a new tab).
+  blink.mojom.FramePolicy frame_policy;
 
   // The names of origin trials to be force enabled for this navigation.
   array<string> force_enabled_origin_trials;
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 4eb225a..166d63cf 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -1296,7 +1296,6 @@
     "//testing/gmock",
     "//testing/gtest",
     "//third_party/blink/public:blink",
-    "//third_party/blink/public/mojom:mojom_broadcastchannel_bindings",
     "//third_party/blink/public/mojom/frame",
     "//third_party/leveldatabase",
     "//third_party/mesa_headers",
diff --git a/content/test/data/accessibility/aria/aria-multiselectable-expected-android.txt b/content/test/data/accessibility/aria/aria-multiselectable-expected-android.txt
index 3ea87b03..1873c8c 100644
--- a/content/test/data/accessibility/aria/aria-multiselectable-expected-android.txt
+++ b/content/test/data/accessibility/aria/aria-multiselectable-expected-android.txt
@@ -1,5 +1,5 @@
 android.webkit.WebView focusable focused scrollable
-++android.widget.ListView role_description='list box' clickable collection focusable multiselectable name='My Listbox' state_description='multiselectable, none selected.' item_count=4 row_count=4
+++android.widget.ListView role_description='list box' clickable collection focusable multiselectable name='My Listbox' state_description='multiselectable, none selected. 4 items' item_count=4 row_count=4
 ++++android.view.View clickable collection_item focusable name='Example 1' state_description='in list, item 1 of 4'
 ++++android.view.View clickable collection_item focusable name='Example 2' state_description='in list, item 2 of 4' item_index=1 row_index=1
 ++++android.view.View clickable collection_item focusable name='Example 3' state_description='in list, item 3 of 4' item_index=2 row_index=2
diff --git a/content/test/data/accessibility/aria/aria-pressed-expected-android.txt b/content/test/data/accessibility/aria/aria-pressed-expected-android.txt
index 87b308d..d878a67 100644
--- a/content/test/data/accessibility/aria/aria-pressed-expected-android.txt
+++ b/content/test/data/accessibility/aria/aria-pressed-expected-android.txt
@@ -2,4 +2,4 @@
 ++android.widget.Button role_description='button' clickable focusable name='Regular button'
 ++android.widget.ToggleButton role_description='toggle button' checkable clickable focusable name='Toggle button unpressed' state_description='Off'
 ++android.widget.ToggleButton role_description='toggle button' checkable checked clickable focusable name='Toggle button pressed' state_description='On'
-++android.widget.ToggleButton role_description='toggle button' checkable clickable focusable name='Toggle button mixed' state_description='Off'
\ No newline at end of file
+++android.widget.ToggleButton role_description='toggle button' checkable clickable focusable name='Toggle button mixed' state_description='Partially Checked'
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-togglebutton-expected-android.txt b/content/test/data/accessibility/aria/aria-togglebutton-expected-android.txt
index 5989142..dab075f 100644
--- a/content/test/data/accessibility/aria/aria-togglebutton-expected-android.txt
+++ b/content/test/data/accessibility/aria/aria-togglebutton-expected-android.txt
@@ -2,4 +2,4 @@
 ++android.widget.Button role_description='button' clickable focusable name='Regular button'
 ++android.widget.ToggleButton role_description='toggle button' checkable clickable focusable name='Toggle button' state_description='Off'
 ++android.widget.ToggleButton role_description='toggle button' checkable checked clickable focusable name='Toggle button' state_description='On'
-++android.widget.ToggleButton role_description='toggle button' checkable clickable focusable name='Toggle button' state_description='Off'
\ No newline at end of file
+++android.widget.ToggleButton role_description='toggle button' checkable clickable focusable name='Toggle button' state_description='Partially Checked'
\ No newline at end of file
diff --git a/content/test/data/font_src_local_matching.html b/content/test/data/font_src_local_matching.html
index ade3d664..b0de52f 100644
--- a/content/test/data/font_src_local_matching.html
+++ b/content/test/data/font_src_local_matching.html
@@ -51,9 +51,6 @@
       ["RobotoCondensed-Italic", "0"],
       ["RobotoCondensed-Regular", "0"],
       ["Roboto-Italic", "0"],
-      ["Google Sans", "0"],
-      ["Google Sans Medium", "0"],
-      ["Google Sans Bold", "0"],
     ];
   } else if (navigator.userAgent.indexOf("Linux") !== -1
              || navigator.userAgent.indexOf("CrOS") !== -1) {
diff --git a/content/test/data/sxg/generate-test-certs.sh b/content/test/data/sxg/generate-test-certs.sh
index b011697..1570b929 100755
--- a/content/test/data/sxg/generate-test-certs.sh
+++ b/content/test/data/sxg/generate-test-certs.sh
@@ -24,6 +24,11 @@
 openssl req -new -sha256 -key prime256v1.key -out prime256v1-sha256.csr \
   -subj '/CN=test.example.org/O=Test/C=US'
 
+openssl req -new -sha256 -key prime256v1.key -out \
+  prime256v1-sha256-google-com.csr \
+  -subj '/CN=google-com.example.org/O=Test/C=US'
+
+
 # Generate a certificate whose validity period starts at 2019-06-01 and
 # valid for 90 days.
 openssl ca -batch \
@@ -34,6 +39,17 @@
   -in  prime256v1-sha256.csr \
   -out prime256v1-sha256.public.pem
 
+# Generate a certificate whose validity period starts at 2019-06-01 and
+# valid for 90 days. Same as above, but for google-com.example.org.
+openssl ca -batch \
+  -config google-com-ca.cnf \
+  -extensions sxg_cert \
+  -startdate 190601000000Z \
+  -enddate   190830000000Z \
+  -in  prime256v1-sha256-google-com.csr \
+  -out prime256v1-sha256-google-com.public.pem
+
+
 # Generate a certificate without CanSignHttpExchangesDraft extension.
 openssl ca -batch \
   -config ca.cnf \
diff --git a/content/test/data/sxg/generate-test-sxgs.sh b/content/test/data/sxg/generate-test-sxgs.sh
index e7c6e19f..3685b89 100755
--- a/content/test/data/sxg/generate-test-sxgs.sh
+++ b/content/test/data/sxg/generate-test-sxgs.sh
@@ -33,6 +33,11 @@
 gen-certurl -pem prime256v1-sha256.public.pem \
   -ocsp $tmpdir/ocsp -sctDir $sctdir > test.example.org.public.pem.cbor
 
+
+# Same as above, but for google-com.example.org.
+gen-certurl -pem prime256v1-sha256-google-com.public.pem \
+  -ocsp $tmpdir/ocsp -sctDir $sctdir > google-com.example.org.public.pem.cbor
+
 # Generate the certificate chain of "*.example.org", whose validity period is
 # more than 90 days.
 gen-certurl -pem prime256v1-sha256-validity-too-long.public.pem \
@@ -72,8 +77,8 @@
   -uri https://google-com.example.org/test/ \
   -status 200 \
   -content test.html \
-  -certificate prime256v1-sha256.public.pem \
-  -certUrl https://cert.example.org/cert.msg \
+  -certificate prime256v1-sha256-google-com.public.pem \
+  -certUrl https://google-com.example.org/cert.msg \
   -validityUrl https://google-com.example.org/resource.validity.msg \
   -privateKey prime256v1.key \
   -date $signature_date \
diff --git a/content/test/data/sxg/google-com-ca.cnf b/content/test/data/sxg/google-com-ca.cnf
new file mode 100644
index 0000000..1a0f042
--- /dev/null
+++ b/content/test/data/sxg/google-com-ca.cnf
@@ -0,0 +1,35 @@
+# This file is same as ca.cnf with the exception of subjectAltName field.
+[ca]
+default_ca = CA_root
+preserve   = yes
+
+[CA_root]
+dir            = out
+new_certs_dir  = $dir
+database       = $dir/index.txt
+serial         = $dir/serial
+certificate    = ../../../../net/data/ssl/certificates/root_ca_cert.pem
+private_key    = ../../../../net/data/ssl/certificates/root_ca_cert.pem
+default_md     = sha256
+unique_subject = no
+policy         = policy_anything
+
+[sxg_cert]
+basicConstraints = CA:FALSE
+# OID required for sxg since d54c469
+1.3.6.1.4.1.11129.2.1.22 = ASN1:NULL
+keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+extendedKeyUsage = serverAuth
+subjectKeyIdentifier = hash
+authorityKeyIdentifier = keyid,issuer
+subjectAltName=DNS:google-com.example.org
+
+[policy_anything]
+# Default signing policy
+countryName            = optional
+stateOrProvinceName    = optional
+localityName           = optional
+organizationName       = optional
+organizationalUnitName = optional
+commonName             = optional
+emailAddress           = optional
diff --git a/content/test/data/sxg/google-com.example.org.public.pem.cbor b/content/test/data/sxg/google-com.example.org.public.pem.cbor
new file mode 100644
index 0000000..a29246a8
--- /dev/null
+++ b/content/test/data/sxg/google-com.example.org.public.pem.cbor
Binary files differ
diff --git a/content/test/data/sxg/google-com.example.org.public.pem.cbor.mock-http-headers b/content/test/data/sxg/google-com.example.org.public.pem.cbor.mock-http-headers
new file mode 100644
index 0000000..ba8c1b7
--- /dev/null
+++ b/content/test/data/sxg/google-com.example.org.public.pem.cbor.mock-http-headers
@@ -0,0 +1,2 @@
+HTTP/1.1 200 OK
+Content-Type: application/cert-chain+cbor
diff --git a/content/test/data/sxg/google-com.example.org_test.sxg b/content/test/data/sxg/google-com.example.org_test.sxg
index 7bf176a3..1a53ee27f 100644
--- a/content/test/data/sxg/google-com.example.org_test.sxg
+++ b/content/test/data/sxg/google-com.example.org_test.sxg
Binary files differ
diff --git a/content/test/data/sxg/prime256v1-sha256-google-com.csr b/content/test/data/sxg/prime256v1-sha256-google-com.csr
new file mode 100644
index 0000000..dbb47f6
--- /dev/null
+++ b/content/test/data/sxg/prime256v1-sha256-google-com.csr
@@ -0,0 +1,8 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIH4MIGfAgEAMD0xHzAdBgNVBAMMFmdvb2dsZS1jb20uZXhhbXBsZS5vcmcxDTAL
+BgNVBAoMBFRlc3QxCzAJBgNVBAYTAlVTMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcD
+QgAElFfdVfWVnYyDSfXmIkjMokHtKd1SLpMcJiV1gzYsTABuyq5PUhaghqpelLbG
+vTBUEYH8BupWh2DdIF01fR3Kw6AAMAoGCCqGSM49BAMCA0gAMEUCIQC4wI2Lo4CN
+/8MDiD14faGKqizZZURk1wNln5qQ7ZhLngIgZ3XkkcNcemHTc5srzamFFNKJU2L3
+zpJjorgaC+H3rn8=
+-----END CERTIFICATE REQUEST-----
diff --git a/content/test/data/sxg/prime256v1-sha256-google-com.public.pem b/content/test/data/sxg/prime256v1-sha256-google-com.public.pem
new file mode 100644
index 0000000..1ba6d8ad
--- /dev/null
+++ b/content/test/data/sxg/prime256v1-sha256-google-com.public.pem
@@ -0,0 +1,71 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 2 (0x2)
+        Signature Algorithm: sha256WithRSAEncryption
+        Issuer: C=US, ST=California, L=Mountain View, O=Test CA, CN=Test Root CA
+        Validity
+            Not Before: Jun  1 00:00:00 2019 GMT
+            Not After : Aug 30 00:00:00 2019 GMT
+        Subject: CN=google-com.example.org, O=Test, C=US
+        Subject Public Key Info:
+            Public Key Algorithm: id-ecPublicKey
+                Public-Key: (256 bit)
+                pub:
+                    04:94:57:dd:55:f5:95:9d:8c:83:49:f5:e6:22:48:
+                    cc:a2:41:ed:29:dd:52:2e:93:1c:26:25:75:83:36:
+                    2c:4c:00:6e:ca:ae:4f:52:16:a0:86:aa:5e:94:b6:
+                    c6:bd:30:54:11:81:fc:06:ea:56:87:60:dd:20:5d:
+                    35:7d:1d:ca:c3
+                ASN1 OID: prime256v1
+                NIST CURVE: P-256
+        X509v3 extensions:
+            X509v3 Basic Constraints: 
+                CA:FALSE
+            1.3.6.1.4.1.11129.2.1.22: 
+                ..
+            X509v3 Key Usage: 
+                Digital Signature, Non Repudiation, Key Encipherment
+            X509v3 Extended Key Usage: 
+                TLS Web Server Authentication
+            X509v3 Subject Key Identifier: 
+                CF:65:B9:9B:EE:0E:EC:8E:A6:B9:69:96:82:20:F4:F7:8B:8A:B2:FA
+            X509v3 Authority Key Identifier: 
+                keyid:9B:26:0B:8A:98:A9:BB:1D:B9:1F:1C:E3:1A:40:33:ED:8E:17:88:AB
+
+            X509v3 Subject Alternative Name: 
+                DNS:google-com.example.org
+    Signature Algorithm: sha256WithRSAEncryption
+         ae:16:c3:1d:a9:b1:53:86:22:ee:90:e6:07:e6:03:c1:a1:10:
+         79:98:32:d9:61:31:f2:27:5c:6a:a2:80:c6:0b:d1:40:16:06:
+         65:5e:b9:72:e1:77:e1:a9:02:bf:b9:18:56:8a:24:0f:b7:84:
+         94:d7:53:51:e6:9f:51:fd:7e:fe:d2:64:9e:73:d0:97:2d:2f:
+         ad:64:41:28:ef:9e:e7:86:ca:18:04:39:7d:7d:b1:9f:3c:20:
+         f4:44:5b:a0:d4:00:28:bf:8c:c2:50:c9:c8:5e:cf:d5:1c:37:
+         98:8e:2e:d8:81:71:43:79:77:60:6b:85:01:34:14:70:73:33:
+         1d:df:6e:2e:30:b5:99:ff:0c:ac:82:b5:23:c2:f4:8c:8a:e0:
+         53:f2:f4:3f:cb:18:78:3c:b3:f4:f9:41:e3:d4:83:75:24:c8:
+         b6:16:15:d7:36:d1:06:a3:9a:0b:59:6b:cd:e4:05:8e:25:d8:
+         1f:44:bf:30:20:3b:92:dd:66:74:3b:e6:d2:91:0c:5a:81:ac:
+         d1:9d:3f:9e:fe:cd:31:a0:36:40:58:6f:01:01:f5:9c:f7:ab:
+         81:91:3f:d3:f1:3c:29:a3:47:a0:60:71:55:86:8c:15:3e:9e:
+         0c:54:45:04:4c:10:33:36:09:c5:88:56:3a:8e:78:3b:dc:5d:
+         43:5a:cd:84
+-----BEGIN CERTIFICATE-----
+MIIC9jCCAd6gAwIBAgIBAjANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJVUzET
+MBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzEQMA4G
+A1UECgwHVGVzdCBDQTEVMBMGA1UEAwwMVGVzdCBSb290IENBMB4XDTE5MDYwMTAw
+MDAwMFoXDTE5MDgzMDAwMDAwMFowPTEfMB0GA1UEAwwWZ29vZ2xlLWNvbS5leGFt
+cGxlLm9yZzENMAsGA1UECgwEVGVzdDELMAkGA1UEBhMCVVMwWTATBgcqhkjOPQIB
+BggqhkjOPQMBBwNCAASUV91V9ZWdjINJ9eYiSMyiQe0p3VIukxwmJXWDNixMAG7K
+rk9SFqCGql6Utsa9MFQRgfwG6laHYN0gXTV9HcrDo4GlMIGiMAkGA1UdEwQCMAAw
+EAYKKwYBBAHWeQIBFgQCBQAwCwYDVR0PBAQDAgXgMBMGA1UdJQQMMAoGCCsGAQUF
+BwMBMB0GA1UdDgQWBBTPZbmb7g7sjqa5aZaCIPT3i4qy+jAfBgNVHSMEGDAWgBSb
+JguKmKm7HbkfHOMaQDPtjheIqzAhBgNVHREEGjAYghZnb29nbGUtY29tLmV4YW1w
+bGUub3JnMA0GCSqGSIb3DQEBCwUAA4IBAQCuFsMdqbFThiLukOYH5gPBoRB5mDLZ
+YTHyJ1xqooDGC9FAFgZlXrly4XfhqQK/uRhWiiQPt4SU11NR5p9R/X7+0mSec9CX
+LS+tZEEo757nhsoYBDl9fbGfPCD0RFug1AAov4zCUMnIXs/VHDeYji7YgXFDeXdg
+a4UBNBRwczMd324uMLWZ/wysgrUjwvSMiuBT8vQ/yxh4PLP0+UHj1IN1JMi2FhXX
+NtEGo5oLWWvN5AWOJdgfRL8wIDuS3WZ0O+bSkQxagazRnT+e/s0xoDZAWG8BAfWc
+96uBkT/T8Twpo0egYHFVhowVPp4MVEUETBAzNgnFiFY6jng73F1DWs2E
+-----END CERTIFICATE-----
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
index d53ef98..e9f0c46 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
@@ -933,48 +933,6 @@
 crbug.com/1126631 [ android android-pixel-2 passthrough ] conformance2/textures/webgl_canvas/tex-2d-rgb9_e5-rgb-half_float.html [ RetryOnFailure ]
 crbug.com/1126631 [ android android-pixel-2 passthrough ] conformance2/textures/webgl_canvas/tex-2d-r11f_g11f_b10f-rgb-unsigned_int_10f_11f_11f_rev.html [ RetryOnFailure ]
 crbug.com/1126631 [ android android-pixel-2 passthrough ] conformance/textures/webgl_canvas/tex-2d-luminance-luminance-unsigned_byte.html [ RetryOnFailure ]
-crbug.com/1095679 [ android android-pixel-2 no-passthrough ] conformance/textures/canvas/tex-2d-alpha-alpha-unsigned_byte.html [ RetryOnFailure ]
-crbug.com/1095679 [ android android-pixel-2 no-passthrough ] conformance/textures/canvas/tex-2d-luminance-luminance-unsigned_byte.html [ RetryOnFailure ]
-crbug.com/1095679 [ android android-pixel-2 no-passthrough ] conformance/textures/canvas/tex-2d-luminance_alpha-luminance_alpha-unsigned_byte.html [ RetryOnFailure ]
-crbug.com/1095679 [ android android-pixel-2 no-passthrough ] conformance/textures/canvas/tex-2d-rgb-rgb-unsigned_byte.html [ RetryOnFailure ]
-crbug.com/1095679 [ android android-pixel-2 no-passthrough ] conformance/textures/canvas/tex-2d-rgb-rgb-unsigned_short_5_6_5.html [ RetryOnFailure ]
-crbug.com/1095679 [ android android-pixel-2 no-passthrough ] conformance/textures/canvas/tex-2d-rgba-rgba-unsigned_byte.html [ RetryOnFailure ]
-crbug.com/1095679 [ android android-pixel-2 no-passthrough ] conformance/textures/canvas/tex-2d-rgba-rgba-unsigned_short_4_4_4_4.html [ RetryOnFailure ]
-crbug.com/1095679 [ android android-pixel-2 no-passthrough ] conformance/textures/canvas/tex-2d-rgba-rgba-unsigned_short_5_5_5_1.html [ RetryOnFailure ]
-crbug.com/1095679 [ android android-pixel-2 no-passthrough ] conformance2/textures/canvas/tex-2d-r11f_g11f_b10f-rgb-float.html [ RetryOnFailure ]
-crbug.com/1095679 [ android android-pixel-2 no-passthrough ] conformance2/textures/canvas/tex-2d-r11f_g11f_b10f-rgb-half_float.html [ RetryOnFailure ]
-crbug.com/1095679 [ android android-pixel-2 no-passthrough ] conformance2/textures/canvas/tex-2d-r11f_g11f_b10f-rgb-unsigned_int_10f_11f_11f_rev.html [ RetryOnFailure ]
-crbug.com/1095679 [ android android-pixel-2 no-passthrough ] conformance2/textures/canvas/tex-2d-r16f-red-float.html [ RetryOnFailure ]
-crbug.com/1095679 [ android android-pixel-2 no-passthrough ] conformance2/textures/canvas/tex-2d-r16f-red-half_float.html [ RetryOnFailure ]
-crbug.com/1095679 [ android android-pixel-2 no-passthrough ] conformance2/textures/canvas/tex-2d-r32f-red-float.html [ RetryOnFailure ]
-crbug.com/1095679 [ android android-pixel-2 no-passthrough ] conformance2/textures/canvas/tex-2d-r8-red-unsigned_byte.html [ RetryOnFailure ]
-crbug.com/1095679 [ android android-pixel-2 no-passthrough ] conformance2/textures/canvas/tex-2d-r8ui-red_integer-unsigned_byte.html [ RetryOnFailure ]
-crbug.com/1095679 [ android android-pixel-2 no-passthrough ] conformance2/textures/canvas/tex-2d-rg16f-rg-float.html [ RetryOnFailure ]
-crbug.com/1095679 [ android android-pixel-2 no-passthrough ] conformance2/textures/canvas/tex-2d-rg16f-rg-half_float.html [ RetryOnFailure ]
-crbug.com/1095679 [ android android-pixel-2 no-passthrough ] conformance2/textures/canvas/tex-2d-rg32f-rg-float.html [ RetryOnFailure ]
-crbug.com/1095679 [ android android-pixel-2 no-passthrough ] conformance2/textures/canvas/tex-2d-rg8-rg-unsigned_byte.html [ RetryOnFailure ]
-crbug.com/1095679 [ android android-pixel-2 no-passthrough ] conformance2/textures/canvas/tex-2d-rg8ui-rg_integer-unsigned_byte.html [ RetryOnFailure ]
-crbug.com/1095679 [ android android-pixel-2 no-passthrough ] conformance2/textures/canvas/tex-2d-rgb10_a2-rgba-unsigned_int_2_10_10_10_rev.html [ RetryOnFailure ]
-crbug.com/1095679 [ android android-pixel-2 no-passthrough ] conformance2/textures/canvas/tex-2d-rgb16f-rgb-float.html [ RetryOnFailure ]
-crbug.com/1095679 [ android android-pixel-2 no-passthrough ] conformance2/textures/canvas/tex-2d-rgb16f-rgb-half_float.html [ RetryOnFailure ]
-crbug.com/1095679 [ android android-pixel-2 no-passthrough ] conformance2/textures/canvas/tex-2d-rgb32f-rgb-float.html [ RetryOnFailure ]
-crbug.com/1095679 [ android android-pixel-2 no-passthrough ] conformance2/textures/canvas/tex-2d-rgb565-rgb-unsigned_byte.html [ RetryOnFailure ]
-crbug.com/1095679 [ android android-pixel-2 no-passthrough ] conformance2/textures/canvas/tex-2d-rgb565-rgb-unsigned_short_5_6_5.html [ RetryOnFailure ]
-crbug.com/1095679 [ android android-pixel-2 no-passthrough ] conformance2/textures/canvas/tex-2d-rgb5_a1-rgba-unsigned_byte.html [ RetryOnFailure ]
-crbug.com/1095679 [ android android-pixel-2 no-passthrough ] conformance2/textures/canvas/tex-2d-rgb5_a1-rgba-unsigned_short_5_5_5_1.html [ RetryOnFailure ]
-crbug.com/1095679 [ android android-pixel-2 no-passthrough ] conformance2/textures/canvas/tex-2d-rgb8-rgb-unsigned_byte.html [ RetryOnFailure ]
-crbug.com/1095679 [ android android-pixel-2 no-passthrough ] conformance2/textures/canvas/tex-2d-rgb8ui-rgb_integer-unsigned_byte.html [ RetryOnFailure ]
-crbug.com/1095679 [ android android-pixel-2 no-passthrough ] conformance2/textures/canvas/tex-2d-rgb9_e5-rgb-float.html [ RetryOnFailure ]
-crbug.com/1095679 [ android android-pixel-2 no-passthrough ] conformance2/textures/canvas/tex-2d-rgb9_e5-rgb-half_float.html [ RetryOnFailure ]
-crbug.com/1095679 [ android android-pixel-2 no-passthrough ] conformance2/textures/canvas/tex-2d-rgba16f-rgba-float.html [ RetryOnFailure ]
-crbug.com/1095679 [ android android-pixel-2 no-passthrough ] conformance2/textures/canvas/tex-2d-rgba16f-rgba-half_float.html [ RetryOnFailure ]
-crbug.com/1095679 [ android android-pixel-2 no-passthrough ] conformance2/textures/canvas/tex-2d-rgba32f-rgba-float.html [ RetryOnFailure ]
-crbug.com/1095679 [ android android-pixel-2 no-passthrough ] conformance2/textures/canvas/tex-2d-rgba4-rgba-unsigned_byte.html [ RetryOnFailure ]
-crbug.com/1095679 [ android android-pixel-2 no-passthrough ] conformance2/textures/canvas/tex-2d-rgba4-rgba-unsigned_short_4_4_4_4.html [ RetryOnFailure ]
-crbug.com/1095679 [ android android-pixel-2 no-passthrough ] conformance2/textures/canvas/tex-2d-rgba8-rgba-unsigned_byte.html [ RetryOnFailure ]
-crbug.com/1095679 [ android android-pixel-2 no-passthrough ] conformance2/textures/canvas/tex-2d-rgba8ui-rgba_integer-unsigned_byte.html [ RetryOnFailure ]
-crbug.com/1095679 [ android android-pixel-2 no-passthrough ] conformance2/textures/canvas/tex-2d-srgb8-rgb-unsigned_byte.html [ RetryOnFailure ]
-crbug.com/1095679 [ android android-pixel-2 no-passthrough ] conformance2/textures/canvas/tex-2d-srgb8_alpha8-rgba-unsigned_byte.html [ RetryOnFailure ]
 
 # This test is failing on Android Pixel 2 and 3 (Qualcomm)
 # Seems to be an OpenGL ES bug.
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
index 4719e8b..7290ed8f 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
@@ -624,14 +624,6 @@
 crbug.com/951628 [ android android-nexus-5x no-passthrough no-swiftshader-gl ] conformance/rendering/blending.html [ Failure ]
 crbug.com/1083320 [ android android-nexus-5x ] conformance/misc/uninitialized-test.html [ Skip ]
 crbug.com/1056830 [ android android-nexus-5x ] conformance/extensions/webgl-compressed-texture-astc.html [ Failure ]
-crbug.com/1095679 [ android android-nexus-5x no-passthrough ] conformance/textures/canvas/tex-2d-alpha-alpha-unsigned_byte.html [ Skip ]
-crbug.com/1095679 [ android android-nexus-5x no-passthrough ] conformance/textures/canvas/tex-2d-luminance-luminance-unsigned_byte.html [ Skip ]
-crbug.com/1095679 [ android android-nexus-5x no-passthrough ] conformance/textures/canvas/tex-2d-luminance_alpha-luminance_alpha-unsigned_byte.html [ Skip ]
-crbug.com/1095679 [ android android-nexus-5x no-passthrough ] conformance/textures/canvas/tex-2d-rgb-rgb-unsigned_byte.html [ RetryOnFailure ]
-crbug.com/1095679 [ android android-nexus-5x no-passthrough ] conformance/textures/canvas/tex-2d-rgb-rgb-unsigned_short_5_6_5.html [ Skip ]
-crbug.com/1095679 [ android android-nexus-5x no-passthrough ] conformance/textures/canvas/tex-2d-rgba-rgba-unsigned_byte.html [ RetryOnFailure ]
-crbug.com/1095679 [ android android-nexus-5x no-passthrough ] conformance/textures/canvas/tex-2d-rgba-rgba-unsigned_short_4_4_4_4.html [ Skip ]
-crbug.com/1095679 [ android android-nexus-5x no-passthrough ] conformance/textures/canvas/tex-2d-rgba-rgba-unsigned_short_5_5_5_1.html [ Skip ]
 crbug.com/1135785 [ android android-nexus-5x no-passthrough ] conformance/textures/misc/texture-video-transparent.html [ RetryOnFailure ]
 # Timing out on this device for unknown reasons.
 crbug.com/1099148 [ android android-nexus-5x ] deqp/data/gles2/shaders/swizzles.html [ Skip ]
diff --git a/device/BUILD.gn b/device/BUILD.gn
index 747e5b6..2113a942 100644
--- a/device/BUILD.gn
+++ b/device/BUILD.gn
@@ -147,6 +147,7 @@
   if (!is_android) {
     sources += [
       "fido/attestation_statement_formats_unittest.cc",
+      "fido/auth_token_requester_unittest.cc",
       "fido/bio/enrollment_handler_unittest.cc",
       "fido/ble_adapter_manager_unittest.cc",
       "fido/cable/fido_ble_connection_unittest.cc",
diff --git a/device/bluetooth/bluetooth_strings.grd b/device/bluetooth/bluetooth_strings.grd
index 2827178..ccffde0 100644
--- a/device/bluetooth/bluetooth_strings.grd
+++ b/device/bluetooth/bluetooth_strings.grd
@@ -278,6 +278,9 @@
       <message name="IDS_BLUETOOTH_ACCESSIBILITY_DEVICE_TYPE_UNKNOWN" desc="accessibility description for a device type that is not one of the known/supported device types for Chrome OS">
         <ph name="DEVICE_NAME">$1<ex>Device XYZ</ex></ph>, Unknown device type
       </message>
+      <message name="IDS_BLUETOOTH_ACCESSIBILITY_DEVICE_TYPE_AND_CONNECTION_STATUS" desc="accessibility description for a Bluetooth connectable device that includes its status. IDS_BLUETOOTH_ACCESSIBILITY_DEVICE_TYPE_* will be used as an input to DEVICE_NAME_AND_TYPE">
+        <ph name="DEVICE_NAME_AND_TYPE">$1<ex>Device XYZ, Audio Device</ex></ph> <ph name="CONNECTION_STATUS">$2<ex>Not connected</ex></ph>
+      </message>
     </messages>
   </release>
 </grit>
diff --git a/device/bluetooth/bluetooth_strings_grd/IDS_BLUETOOTH_ACCESSIBILITY_DEVICE_TYPE_AND_CONNECTION_STATUS.png.sha1 b/device/bluetooth/bluetooth_strings_grd/IDS_BLUETOOTH_ACCESSIBILITY_DEVICE_TYPE_AND_CONNECTION_STATUS.png.sha1
new file mode 100644
index 0000000..3cdee77
--- /dev/null
+++ b/device/bluetooth/bluetooth_strings_grd/IDS_BLUETOOTH_ACCESSIBILITY_DEVICE_TYPE_AND_CONNECTION_STATUS.png.sha1
@@ -0,0 +1 @@
+fb77f1fc922da677235a6cc7dcfcb31c9082897f
\ No newline at end of file
diff --git a/device/fido/BUILD.gn b/device/fido/BUILD.gn
index 6e58545..5a6df05 100644
--- a/device/fido/BUILD.gn
+++ b/device/fido/BUILD.gn
@@ -82,6 +82,8 @@
       "attestation_statement.h",
       "attestation_statement_formats.cc",
       "attestation_statement_formats.h",
+      "auth_token_requester.cc",
+      "auth_token_requester.h",
       "authenticator_data.cc",
       "authenticator_data.h",
       "authenticator_get_assertion_response.cc",
diff --git a/device/fido/auth_token_requester.cc b/device/fido/auth_token_requester.cc
new file mode 100644
index 0000000..6dc8b5c
--- /dev/null
+++ b/device/fido/auth_token_requester.cc
@@ -0,0 +1,326 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/fido/auth_token_requester.h"
+
+#include <set>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "components/device_event_log/device_event_log.h"
+#include "device/fido/authenticator_supported_options.h"
+#include "device/fido/fido_authenticator.h"
+
+namespace device {
+
+using ClientPinAvailability =
+    AuthenticatorSupportedOptions::ClientPinAvailability;
+using UserVerificationAvailability =
+    AuthenticatorSupportedOptions::UserVerificationAvailability;
+using BioEnrollmentAvailability =
+    AuthenticatorSupportedOptions::BioEnrollmentAvailability;
+
+AuthTokenRequester::Delegate::~Delegate() = default;
+
+AuthTokenRequester::Options::Options() = default;
+AuthTokenRequester::Options::Options(Options&&) = default;
+AuthTokenRequester::Options& AuthTokenRequester::Options::operator=(Options&&) =
+    default;
+AuthTokenRequester::Options::~Options() = default;
+
+AuthTokenRequester::AuthTokenRequester(Delegate* delegate,
+                                       FidoAuthenticator* authenticator,
+                                       Options options)
+    : delegate_(delegate),
+      authenticator_(authenticator),
+      options_(std::move(options)) {
+  DCHECK(delegate_);
+  DCHECK(authenticator_);
+  DCHECK(authenticator_->Options());
+  DCHECK(!options_.token_permissions.empty());
+  DCHECK(!options_.rp_id || !options_.rp_id->empty());
+  // Authenticators with CTAP2.0-style pinToken support only support certain
+  // default permissions.
+  DCHECK(
+      authenticator_->Options()->supports_pin_uv_auth_token ||
+      base::STLSetDifference<std::set<pin::Permissions>>(
+          options_.token_permissions,
+          std::set<pin::Permissions>{pin::Permissions::kMakeCredential,
+                                     pin::Permissions::kGetAssertion,
+                                     pin::Permissions::kBioEnrollment,
+                                     pin::Permissions::kCredentialManagement})
+          .empty());
+}
+
+AuthTokenRequester::~AuthTokenRequester() = default;
+
+void AuthTokenRequester::ObtainPINUVAuthToken() {
+  if (authenticator_->Options()->supports_pin_uv_auth_token) {
+    // Only attempt to obtain a token through internal UV if the authenticator
+    // supports CTAP 2.1 pinUvAuthTokens. If it does not, it could be a 2.0
+    // authenticator that supports UV without any sort of token.
+    const UserVerificationAvailability user_verification_availability =
+        authenticator_->Options()->user_verification_availability;
+    switch (user_verification_availability) {
+      case UserVerificationAvailability::kNotSupported:
+      case UserVerificationAvailability::kSupportedButNotConfigured:
+        // Try PIN first.
+        break;
+      case UserVerificationAvailability::kSupportedAndConfigured:
+        ObtainTokenFromInternalUV();
+        return;
+    }
+  }
+
+  const ClientPinAvailability client_pin_availability =
+      authenticator_->Options()->client_pin_availability;
+  switch (client_pin_availability) {
+    case ClientPinAvailability::kNotSupported:
+      delegate_->HavePINUVAuthTokenResultForAuthenticator(
+          authenticator_, Result::kPreTouchUnsatisfiableRequest, base::nullopt);
+      return;
+    case ClientPinAvailability::kSupportedAndPinSet:
+      if (options_.skip_pin_touch) {
+        ObtainTokenFromPIN();
+        return;
+      }
+      authenticator_->GetTouch(base::BindOnce(
+          &AuthTokenRequester::ObtainTokenFromPIN, weak_factory_.GetWeakPtr()));
+      return;
+    case ClientPinAvailability::kSupportedButPinNotSet:
+      if (options_.skip_pin_touch) {
+        ObtainTokenFromNewPIN();
+        return;
+      }
+      authenticator_->GetTouch(
+          base::BindOnce(&AuthTokenRequester::ObtainTokenFromNewPIN,
+                         weak_factory_.GetWeakPtr()));
+      return;
+  }
+}
+
+void AuthTokenRequester::ObtainTokenFromInternalUV() {
+  authenticator_->GetUvRetries(base::BindOnce(
+      &AuthTokenRequester::OnGetUVRetries, weak_factory_.GetWeakPtr()));
+}
+
+void AuthTokenRequester::OnGetUVRetries(
+    CtapDeviceResponseCode status,
+    base::Optional<pin::RetriesResponse> response) {
+  if (status != CtapDeviceResponseCode::kSuccess) {
+    delegate_->HavePINUVAuthTokenResultForAuthenticator(
+        authenticator_, Result::kPreTouchAuthenticatorResponseInvalid,
+        base::nullopt);
+    return;
+  }
+
+  if (response->retries == 0) {
+    // The authenticator was locked prior to calling
+    // ObtainTokenFromInternalUV(). Fall back to PIN if able.
+    if (authenticator_->Options()->client_pin_availability ==
+        ClientPinAvailability::kSupportedAndPinSet) {
+      delegate_->InternalUVLockedForAuthToken();
+      if (options_.skip_pin_touch) {
+        ObtainTokenFromPIN();
+        return;
+      }
+      authenticator_->GetTouch(base::BindOnce(
+          &AuthTokenRequester::ObtainTokenFromPIN, weak_factory_.GetWeakPtr()));
+      return;
+    }
+    authenticator_->GetTouch(base::BindOnce(
+        &AuthTokenRequester::NotifyAuthenticatorSelectedAndFailWithResult,
+        weak_factory_.GetWeakPtr(),
+        Result::kPostTouchAuthenticatorInternalUVLock));
+    return;
+  }
+
+  delegate_->PromptForInternalUVRetry(response->retries);
+  authenticator_->GetUvToken({std::begin(options_.token_permissions),
+                              std::end(options_.token_permissions)},
+                             options_.rp_id,
+                             base::BindOnce(&AuthTokenRequester::OnGetUVToken,
+                                            weak_factory_.GetWeakPtr()));
+}
+
+void AuthTokenRequester::OnGetUVToken(
+    CtapDeviceResponseCode status,
+    base::Optional<pin::TokenResponse> response) {
+  if (!base::Contains(
+          std::set<CtapDeviceResponseCode>{
+              CtapDeviceResponseCode::kCtap2ErrUvInvalid,
+              CtapDeviceResponseCode::kCtap2ErrOperationDenied,
+              CtapDeviceResponseCode::kCtap2ErrUvBlocked,
+              CtapDeviceResponseCode::kSuccess},
+          status)) {
+    // The request was rejected outright, no touch occurred.
+    FIDO_LOG(ERROR) << "Ignoring status " << static_cast<int>(status)
+                    << " from " << authenticator_->GetDisplayName();
+    delegate_->HavePINUVAuthTokenResultForAuthenticator(
+        authenticator_, Result::kPreTouchAuthenticatorResponseInvalid,
+        base::nullopt);
+    return;
+  }
+
+  NotifyAuthenticatorSelected();
+
+  if (status == CtapDeviceResponseCode::kCtap2ErrOperationDenied) {
+    // The user explicitly denied to the operation on an authenticator with
+    // a display.
+    delegate_->HavePINUVAuthTokenResultForAuthenticator(
+        authenticator_, Result::kPostTouchAuthenticatorOperationDenied,
+        base::nullopt);
+    return;
+  }
+
+  if (status == CtapDeviceResponseCode::kCtap2ErrUvInvalid) {
+    authenticator_->GetUvRetries(base::BindOnce(
+        &AuthTokenRequester::OnGetUVRetries, weak_factory_.GetWeakPtr()));
+    return;
+  }
+
+  if (status == CtapDeviceResponseCode::kCtap2ErrUvBlocked) {
+    // Fall back to PIN if able.
+    if (authenticator_->Options()->client_pin_availability ==
+        ClientPinAvailability::kSupportedAndPinSet) {
+      delegate_->InternalUVLockedForAuthToken();
+      ObtainTokenFromPIN();
+      return;
+    }
+    // This can be returned pre-touch if the authenticator was already locked at
+    // the time GetUvToken() was called. However, we checked the number of
+    // remaining retries just before that to handle that case.
+    delegate_->HavePINUVAuthTokenResultForAuthenticator(
+        authenticator_, Result::kPostTouchAuthenticatorInternalUVLock,
+        base::nullopt);
+    return;
+  }
+
+  if (status != CtapDeviceResponseCode::kSuccess) {
+    NOTREACHED();
+    return;
+  }
+
+  delegate_->HavePINUVAuthTokenResultForAuthenticator(
+      authenticator_, Result::kSuccess, *response);
+}
+
+void AuthTokenRequester::ObtainTokenFromPIN() {
+  NotifyAuthenticatorSelected();
+  authenticator_->GetPinRetries(base::BindOnce(
+      &AuthTokenRequester::OnGetPINRetries, weak_factory_.GetWeakPtr()));
+}
+
+void AuthTokenRequester::OnGetPINRetries(
+    CtapDeviceResponseCode status,
+    base::Optional<pin::RetriesResponse> response) {
+  if (status != CtapDeviceResponseCode::kSuccess) {
+    delegate_->HavePINUVAuthTokenResultForAuthenticator(
+        authenticator_, Result::kPostTouchAuthenticatorResponseInvalid,
+        base::nullopt);
+    return;
+  }
+  if (response->retries == 0) {
+    delegate_->HavePINUVAuthTokenResultForAuthenticator(
+        authenticator_, Result::kPostTouchAuthenticatorPINHardLock,
+        base::nullopt);
+    return;
+  }
+  delegate_->CollectExistingPIN(
+      response->retries,
+      base::BindOnce(&AuthTokenRequester::HavePIN, weak_factory_.GetWeakPtr()));
+}
+
+void AuthTokenRequester::HavePIN(std::string pin) {
+  DCHECK(pin::IsValid(pin));
+  authenticator_->GetPINToken(std::move(pin),
+                              {std::begin(options_.token_permissions),
+                               std::end(options_.token_permissions)},
+                              options_.rp_id,
+                              base::BindOnce(&AuthTokenRequester::OnGetPINToken,
+                                             weak_factory_.GetWeakPtr()));
+  return;
+}
+
+void AuthTokenRequester::OnGetPINToken(
+    CtapDeviceResponseCode status,
+    base::Optional<pin::TokenResponse> response) {
+  if (status == CtapDeviceResponseCode::kCtap2ErrPinInvalid) {
+    ObtainTokenFromPIN();
+    return;
+  }
+
+  if (status != CtapDeviceResponseCode::kSuccess) {
+    Result ret;
+    switch (status) {
+      case CtapDeviceResponseCode::kCtap2ErrPinAuthBlocked:
+        ret = Result::kPostTouchAuthenticatorPINSoftLock;
+        break;
+      case CtapDeviceResponseCode::kCtap2ErrPinBlocked:
+        ret = Result::kPostTouchAuthenticatorPINHardLock;
+        break;
+      default:
+        ret = Result::kPostTouchAuthenticatorResponseInvalid;
+        break;
+    }
+    delegate_->HavePINUVAuthTokenResultForAuthenticator(authenticator_, ret,
+                                                        base::nullopt);
+    return;
+  }
+
+  delegate_->HavePINUVAuthTokenResultForAuthenticator(
+      authenticator_, Result::kSuccess, std::move(*response));
+}
+
+void AuthTokenRequester::ObtainTokenFromNewPIN() {
+  NotifyAuthenticatorSelected();
+  delegate_->CollectNewPIN(base::BindOnce(&AuthTokenRequester::HaveNewPIN,
+                                          weak_factory_.GetWeakPtr()));
+}
+
+void AuthTokenRequester::HaveNewPIN(const std::string pin) {
+  DCHECK(pin::IsValid(pin));
+  authenticator_->SetPIN(pin, base::BindOnce(&AuthTokenRequester::OnSetPIN,
+                                             weak_factory_.GetWeakPtr(), pin));
+  return;
+}
+
+void AuthTokenRequester::OnSetPIN(std::string pin,
+                                  CtapDeviceResponseCode status,
+                                  base::Optional<pin::EmptyResponse> response) {
+  if (status != CtapDeviceResponseCode::kSuccess) {
+    delegate_->HavePINUVAuthTokenResultForAuthenticator(
+        authenticator_, Result::kPostTouchAuthenticatorResponseInvalid,
+        base::nullopt);
+    return;
+  }
+
+  // Having just set the PIN, we need to immediately turn around and use it to
+  // get a PIN token.
+  authenticator_->GetPINToken(std::move(pin),
+                              {std::begin(options_.token_permissions),
+                               std::end(options_.token_permissions)},
+                              options_.rp_id,
+                              base::BindOnce(&AuthTokenRequester::OnGetPINToken,
+                                             weak_factory_.GetWeakPtr()));
+}
+
+void AuthTokenRequester::NotifyAuthenticatorSelected() {
+  if (authenticator_was_selected_) {
+    return;
+  }
+  authenticator_was_selected_ = true;
+  delegate_->AuthenticatorSelectedForPINUVAuthToken(authenticator_);
+}
+
+void AuthTokenRequester::NotifyAuthenticatorSelectedAndFailWithResult(
+    Result result) {
+  NotifyAuthenticatorSelected();
+  delegate_->HavePINUVAuthTokenResultForAuthenticator(authenticator_, result,
+                                                      base::nullopt);
+}
+
+}  // namespace device
diff --git a/device/fido/auth_token_requester.h b/device/fido/auth_token_requester.h
new file mode 100644
index 0000000..97ae8ad3
--- /dev/null
+++ b/device/fido/auth_token_requester.h
@@ -0,0 +1,173 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_FIDO_AUTH_TOKEN_REQUESTER_H_
+#define DEVICE_FIDO_AUTH_TOKEN_REQUESTER_H_
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/component_export.h"
+#include "base/memory/weak_ptr.h"
+#include "base/optional.h"
+#include "device/fido/fido_constants.h"
+#include "device/fido/pin.h"
+
+namespace device {
+
+class FidoAuthenticator;
+
+// AuthTokenRequester obtains a pinUvAuthToken from a CTAP2 device.
+class COMPONENT_EXPORT(DEVICE_FIDO) AuthTokenRequester {
+ public:
+  // Result indicates the outcome of running ObtainPINUVAuthToken().  beginning
+  // with `kPreTouch` are returned without interaction from the user, which
+  // generally means the caller ought to silently ignore this device.
+  enum class Result {
+    kSuccess,
+    kPreTouchUnsatisfiableRequest,
+    kPreTouchAuthenticatorResponseInvalid,
+    kPostTouchAuthenticatorResponseInvalid,
+    kPostTouchAuthenticatorOperationDenied,
+    kPostTouchAuthenticatorPINSoftLock,
+    kPostTouchAuthenticatorPINHardLock,
+    kPostTouchAuthenticatorInternalUVLock,
+  };
+
+  // Options configures a AuthTokenRequester.
+  struct COMPONENT_EXPORT(DEVICE_FIDO) Options {
+    Options();
+    Options(Options&&);
+    Options& operator=(Options&&);
+    ~Options();
+
+    // token_permissions are the pinUvAuthToken permissions to request with the
+    // token.
+    std::set<pin::Permissions> token_permissions;
+
+    // rp_id is the permissions RP ID for the token to be requested.
+    base::Optional<std::string> rp_id;
+
+    // skip_pin_touch indicates whether not to request a touch before attempting
+    // to obtain a token using a PIN.
+    bool skip_pin_touch = false;
+  };
+
+  class COMPONENT_EXPORT(DEVICE_FIDO) Delegate {
+   public:
+    // ProvidePINCallback is used to provide the AuthTokenRequester with a PIN
+    // entered by the user. Callers must use |pin::IsValid()| to validate |pin|
+    // before invoking this callback.
+    using ProvidePINCallback = base::OnceCallback<void(std::string pin)>;
+
+    virtual ~Delegate();
+
+    // AuthenticatorSelectedForPINUVAuthToken is invoked to indicate that the
+    // user has interacted with this authenticator (i.e. tapped its button).
+    // The Delegate typically uses this signal to cancel outstanding requests to
+    // other authenticators.
+    //
+    // This method is guaranteed to be called first and at exactly once
+    // throughout the handler's lifetime, *unless*
+    // HavePINUVAuthTokenResultForAuthenticator() is invoked first with one of
+    // the Result codes starting with `kPreTouch`.
+    virtual void AuthenticatorSelectedForPINUVAuthToken(
+        FidoAuthenticator* authenticator) = 0;
+
+    // CollectNewPIN is invoked to prompt the user to enter a new PIN for an
+    // authenticator.
+    //
+    // The callee must provide the PIN by invoking |provide_pin_cb|. The
+    // callback is weakly bound and safe to invoke even after the
+    // AuthTokenRequester was freed.
+    virtual void CollectNewPIN(ProvidePINCallback provide_pin_cb) = 0;
+
+    // CollectExistingPIN is invoked to prompt the user to provide the existing
+    // PIN to an authenticator. |attempts| is the number of remaining attempts
+    // before the authenticator is locked.
+    //
+    // The callee must provide the PIN by invoking |provide_pin_cb|. The
+    // callback is weakly bound and safe to invoke even after the
+    // AuthTokenRequester was freed. If CollectExistingPIN() is called again
+    // after callback invocation, the provided PIN was incorrect.
+    virtual void CollectExistingPIN(int attempts,
+                                    ProvidePINCallback provide_pin_cb) = 0;
+
+    // InternalUVLockedForAuthToken() notifies the delegate that the
+    // authenticator's internal modality was locked due to too many invalid
+    // authentication attempts. It is followed by a call to CollectExistingPIN()
+    // to prompt for a PIN as a fallback authentication mechanism.
+    virtual void InternalUVLockedForAuthToken() = 0;
+
+    // PromptForInternalUVRetry is invoked to prompt the user to retry internal
+    // user verification (usually on a fingerprint sensor). |attempts| is the
+    // number of remaining attempts before the authenticator is locked. This
+    // method may be then be called again if the user verification attempt fails
+    // again.
+    virtual void PromptForInternalUVRetry(int attempts) = 0;
+
+    // HavePINUVAuthTokenResultForAuthenticator notifies the delegate of the
+    // outcome of ObtainPINUVAuthToken(). |response| is `base::nullopt`, unless
+    // |result| is |Result::kSuccess|.
+    virtual void HavePINUVAuthTokenResultForAuthenticator(
+        FidoAuthenticator* authenticator,
+        Result result,
+        base::Optional<pin::TokenResponse> response) = 0;
+  };
+
+  // Instantiates a new AuthTokenRequester. |delegate| and |authenticator| must
+  // outlive this instance.
+  AuthTokenRequester(Delegate* delegate,
+                     FidoAuthenticator* authenticator,
+                     Options options);
+  ~AuthTokenRequester();
+  AuthTokenRequester(AuthTokenRequester&) = delete;
+  AuthTokenRequester& operator=(AuthTokenRequester&) = delete;
+  AuthTokenRequester(AuthTokenRequester&&) = delete;
+  AuthTokenRequester& operator=(AuthTokenRequester&&) = delete;
+
+  // ObtainPINUVAuthToken attempts to obtain a pinUvAuthToken from the
+  // authenticator.
+  void ObtainPINUVAuthToken();
+
+  FidoAuthenticator* authenticator() { return authenticator_; }
+
+ private:
+  void ObtainTokenFromInternalUV();
+  void OnGetUVRetries(CtapDeviceResponseCode status,
+                      base::Optional<pin::RetriesResponse> response);
+  void OnGetUVToken(CtapDeviceResponseCode status,
+                    base::Optional<pin::TokenResponse> response);
+
+  void ObtainTokenFromPIN();
+  void OnGetPINRetries(CtapDeviceResponseCode status,
+                       base::Optional<pin::RetriesResponse> response);
+  void HavePIN(std::string pin);
+  void OnGetPINToken(CtapDeviceResponseCode status,
+                     base::Optional<pin::TokenResponse> response);
+
+  void ObtainTokenFromNewPIN();
+  void HaveNewPIN(std::string pin);
+  void OnSetPIN(std::string pin,
+                CtapDeviceResponseCode status,
+                base::Optional<pin::EmptyResponse> response);
+
+  void NotifyAuthenticatorSelected();
+  void NotifyAuthenticatorSelectedAndFailWithResult(Result result);
+
+  Delegate* delegate_;
+  FidoAuthenticator* authenticator_;
+
+  Options options_;
+
+  bool authenticator_was_selected_ = false;
+
+  base::WeakPtrFactory<AuthTokenRequester> weak_factory_{this};
+};
+
+}  // namespace device
+
+#endif  // DEVICE_FIDO_AUTH_TOKEN_REQUESTER_H_
diff --git a/device/fido/auth_token_requester_unittest.cc b/device/fido/auth_token_requester_unittest.cc
new file mode 100644
index 0000000..b38dafef
--- /dev/null
+++ b/device/fido/auth_token_requester_unittest.cc
@@ -0,0 +1,396 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/fido/auth_token_requester.h"
+
+#include <string>
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#include "base/containers/span.h"
+#include "base/logging.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/optional.h"
+#include "base/run_loop.h"
+#include "base/test/task_environment.h"
+#include "device/fido/fido_constants.h"
+#include "device/fido/fido_device_authenticator.h"
+#include "device/fido/pin.h"
+#include "device/fido/virtual_ctap2_device.h"
+
+namespace device {
+namespace {
+
+using ::testing::ElementsAreArray;
+
+using ClientPinAvailability =
+    device::AuthenticatorSupportedOptions::ClientPinAvailability;
+using UserVerificationAvailability =
+    device::AuthenticatorSupportedOptions::UserVerificationAvailability;
+
+constexpr char kTestPIN[] = "1234";
+
+class TestAuthTokenRequesterDelegate : public AuthTokenRequester::Delegate {
+ public:
+  explicit TestAuthTokenRequesterDelegate(std::string pin)
+      : pin_(std::move(pin)) {}
+
+  void WaitForResult() { wait_for_result_loop_.Run(); }
+  base::Optional<AuthTokenRequester::Result>& result() { return result_; }
+  base::Optional<pin::TokenResponse>& response() { return response_; }
+  bool pin_was_set() { return pin_was_set_; }
+  bool pin_was_collected() { return pin_was_collected_; }
+  bool internal_uv_was_prompted() { return internal_uv_was_prompted_; }
+  bool internal_uv_was_locked() { return internal_uv_was_locked_; }
+
+ private:
+  // AuthTokenRequester::Delegate:
+  void AuthenticatorSelectedForPINUVAuthToken(
+      FidoAuthenticator* authenticator) override {}
+  void CollectNewPIN(ProvidePINCallback provide_pin_cb) override {
+    DCHECK(!pin_.empty());
+    pin_was_set_ = true;
+    std::move(provide_pin_cb).Run(pin_);
+  }
+  void CollectExistingPIN(int attempts,
+                          ProvidePINCallback provide_pin_cb) override {
+    DCHECK(!pin_.empty());
+    pin_was_collected_ = true;
+    std::move(provide_pin_cb).Run(pin_);
+  }
+  void PromptForInternalUVRetry(int attempts) override {
+    internal_uv_was_prompted_ = true;
+  }
+  void InternalUVLockedForAuthToken() override {
+    internal_uv_was_locked_ = true;
+  }
+  void HavePINUVAuthTokenResultForAuthenticator(
+      FidoAuthenticator* authenticator,
+      AuthTokenRequester::Result result,
+      base::Optional<pin::TokenResponse> response) override {
+    DCHECK(!result_);
+    result_ = result;
+    response_ = std::move(response);
+    wait_for_result_loop_.Quit();
+  }
+
+  std::string pin_;
+
+  base::Optional<AuthTokenRequester::Result> result_;
+  base::Optional<pin::TokenResponse> response_;
+
+  bool pin_was_collected_ = false;
+  bool pin_was_set_ = false;
+  bool internal_uv_was_prompted_ = false;
+  bool internal_uv_was_locked_ = false;
+
+  base::RunLoop wait_for_result_loop_;
+};
+
+struct TestCase {
+  ClientPinAvailability client_pin;
+  UserVerificationAvailability user_verification;
+  bool success;
+};
+
+class AuthTokenRequesterTest : public ::testing::Test {
+ protected:
+  void SetUp() override {}
+
+  void RunTestCase(VirtualCtap2Device::Config config,
+                   scoped_refptr<VirtualFidoDevice::State> state,
+                   const TestCase& test_case) {
+    state_ = state;
+
+    switch (test_case.client_pin) {
+      case ClientPinAvailability::kNotSupported:
+        config.pin_support = false;
+        break;
+      case ClientPinAvailability::kSupportedButPinNotSet:
+        config.pin_support = true;
+        break;
+      case ClientPinAvailability::kSupportedAndPinSet:
+        config.pin_support = true;
+        state_->pin = kTestPIN;
+        break;
+    }
+    switch (test_case.user_verification) {
+      case UserVerificationAvailability::kNotSupported:
+        config.internal_uv_support = false;
+        break;
+      case UserVerificationAvailability::kSupportedButNotConfigured:
+        config.internal_uv_support = true;
+        break;
+      case UserVerificationAvailability::kSupportedAndConfigured:
+        config.internal_uv_support = true;
+        state_->fingerprints_enrolled = true;
+        break;
+    }
+
+    auto authenticator = std::make_unique<FidoDeviceAuthenticator>(
+        std::make_unique<VirtualCtap2Device>(state_, std::move(config)));
+
+    base::RunLoop init_loop;
+    authenticator->InitializeAuthenticator(init_loop.QuitClosure());
+    init_loop.Run();
+
+    delegate_ = std::make_unique<TestAuthTokenRequesterDelegate>(kTestPIN);
+    AuthTokenRequester::Options options;
+    options.token_permissions = {pin::Permissions::kMakeCredential};
+    options.rp_id = "foobar.com";
+    AuthTokenRequester requester(delegate_.get(), authenticator.get(),
+                                 std::move(options));
+    requester.ObtainPINUVAuthToken();
+    delegate_->WaitForResult();
+  }
+
+  base::test::TaskEnvironment task_environment_{
+      base::test::TaskEnvironment::TimeSource::MOCK_TIME};
+
+  scoped_refptr<VirtualFidoDevice::State> state_;
+  std::unique_ptr<TestAuthTokenRequesterDelegate> delegate_;
+};
+
+TEST_F(AuthTokenRequesterTest, AuthenticatorWithoutUVTokenSupport) {
+  constexpr TestCase kTestCases[]{
+      {
+          ClientPinAvailability::kNotSupported,
+          UserVerificationAvailability::kNotSupported,
+          false,
+      },
+      {
+          ClientPinAvailability::kNotSupported,
+          UserVerificationAvailability::kSupportedButNotConfigured,
+          false,
+      },
+      {
+          ClientPinAvailability::kNotSupported,
+          UserVerificationAvailability::kSupportedAndConfigured,
+          false,
+      },
+      {
+          ClientPinAvailability::kSupportedButPinNotSet,
+          UserVerificationAvailability::kNotSupported,
+          true,
+      },
+      {
+          ClientPinAvailability::kSupportedButPinNotSet,
+          UserVerificationAvailability::kSupportedButNotConfigured,
+          true,
+      },
+      {
+          ClientPinAvailability::kSupportedButPinNotSet,
+          UserVerificationAvailability::kSupportedAndConfigured,
+          true,
+      },
+      {
+          ClientPinAvailability::kSupportedAndPinSet,
+          UserVerificationAvailability::kNotSupported,
+          true,
+      },
+      {
+          ClientPinAvailability::kSupportedAndPinSet,
+          UserVerificationAvailability::kSupportedButNotConfigured,
+          true,
+      },
+      {
+          ClientPinAvailability::kSupportedAndPinSet,
+          UserVerificationAvailability::kSupportedAndConfigured,
+          true,
+      },
+  };
+
+  int i = 0;
+  for (const TestCase& t : kTestCases) {
+    SCOPED_TRACE(i++);
+    VirtualCtap2Device::Config config;
+    config.pin_uv_auth_token_support = false;
+    RunTestCase(std::move(config),
+                base::MakeRefCounted<VirtualFidoDevice::State>(), t);
+
+    if (t.success) {
+      EXPECT_EQ(*delegate_->result(), AuthTokenRequester::Result::kSuccess);
+      EXPECT_THAT(delegate_->response()->token_for_testing(),
+                  ElementsAreArray(state_->pin_token));
+      EXPECT_EQ(delegate_->pin_was_set(),
+                t.client_pin == ClientPinAvailability::kSupportedButPinNotSet);
+      EXPECT_EQ(delegate_->pin_was_collected(),
+                t.client_pin == ClientPinAvailability::kSupportedAndPinSet);
+    } else {
+      EXPECT_EQ(*delegate_->result(),
+                AuthTokenRequester::Result::kPreTouchUnsatisfiableRequest);
+      EXPECT_FALSE(delegate_->response());
+      EXPECT_FALSE(delegate_->pin_was_set());
+      EXPECT_FALSE(delegate_->pin_was_collected());
+    }
+    EXPECT_FALSE(delegate_->internal_uv_was_prompted());
+    EXPECT_FALSE(delegate_->internal_uv_was_locked());
+  }
+}
+
+TEST_F(AuthTokenRequesterTest, AuthenticatorWithUVTokenSupport) {
+  constexpr TestCase kTestCases[]{
+      {
+          ClientPinAvailability::kNotSupported,
+          UserVerificationAvailability::kNotSupported,
+          false,
+      },
+      {
+          ClientPinAvailability::kNotSupported,
+          UserVerificationAvailability::kSupportedButNotConfigured,
+          false,
+      },
+      {
+          ClientPinAvailability::kNotSupported,
+          UserVerificationAvailability::kSupportedAndConfigured,
+          true,
+      },
+      {
+          ClientPinAvailability::kSupportedButPinNotSet,
+          UserVerificationAvailability::kNotSupported,
+          true,
+      },
+      {
+          ClientPinAvailability::kSupportedButPinNotSet,
+          UserVerificationAvailability::kSupportedButNotConfigured,
+          true,
+      },
+      {
+          ClientPinAvailability::kSupportedButPinNotSet,
+          UserVerificationAvailability::kSupportedAndConfigured,
+          true,
+      },
+      {
+          ClientPinAvailability::kSupportedAndPinSet,
+          UserVerificationAvailability::kNotSupported,
+          true,
+      },
+      {
+          ClientPinAvailability::kSupportedAndPinSet,
+          UserVerificationAvailability::kSupportedButNotConfigured,
+          true,
+      },
+      {
+          ClientPinAvailability::kSupportedAndPinSet,
+          UserVerificationAvailability::kSupportedAndConfigured,
+          true,
+      },
+  };
+
+  int i = 0;
+  for (const TestCase& t : kTestCases) {
+    SCOPED_TRACE(i++);
+
+    VirtualCtap2Device::Config config;
+    config.pin_uv_auth_token_support = true;
+    config.ctap2_versions = {std::begin(kCtap2Versions2_1),
+                             std::end(kCtap2Versions2_1)};
+    RunTestCase(std::move(config),
+                base::MakeRefCounted<VirtualFidoDevice::State>(), t);
+
+    if (t.success) {
+      EXPECT_EQ(*delegate_->result(), AuthTokenRequester::Result::kSuccess);
+      EXPECT_EQ(state_->pin_uv_token_rpid, "foobar.com");
+      EXPECT_EQ(state_->pin_uv_token_permissions,
+                static_cast<uint8_t>(pin::Permissions::kMakeCredential));
+      EXPECT_THAT(delegate_->response()->token_for_testing(),
+                  ElementsAreArray(state_->pin_token));
+      EXPECT_EQ(delegate_->pin_was_set(),
+                t.client_pin == ClientPinAvailability::kSupportedButPinNotSet &&
+                    t.user_verification !=
+                        UserVerificationAvailability::kSupportedAndConfigured);
+      EXPECT_EQ(delegate_->pin_was_collected(),
+                t.client_pin == ClientPinAvailability::kSupportedAndPinSet &&
+                    t.user_verification !=
+                        UserVerificationAvailability::kSupportedAndConfigured);
+      EXPECT_EQ(delegate_->internal_uv_was_prompted(),
+                t.user_verification ==
+                    UserVerificationAvailability::kSupportedAndConfigured);
+      EXPECT_FALSE(delegate_->internal_uv_was_locked());
+    } else {
+      EXPECT_EQ(*delegate_->result(),
+                AuthTokenRequester::Result::kPreTouchUnsatisfiableRequest);
+      EXPECT_FALSE(delegate_->response());
+      EXPECT_FALSE(delegate_->pin_was_set());
+      EXPECT_FALSE(delegate_->pin_was_collected());
+      EXPECT_FALSE(delegate_->internal_uv_was_prompted());
+      EXPECT_FALSE(delegate_->internal_uv_was_locked());
+    }
+  }
+}
+
+TEST_F(AuthTokenRequesterTest, PINSoftLock) {
+  VirtualCtap2Device::Config config;
+  config.pin_uv_auth_token_support = true;
+  config.ctap2_versions = {std::begin(kCtap2Versions2_1),
+                           std::end(kCtap2Versions2_1)};
+  auto state = base::MakeRefCounted<VirtualFidoDevice::State>();
+  state->soft_locked = true;
+
+  RunTestCase(std::move(config), state,
+              TestCase{
+                  ClientPinAvailability::kSupportedAndPinSet,
+                  UserVerificationAvailability::kNotSupported,
+                  false,
+              });
+
+  EXPECT_EQ(*delegate_->result(),
+            AuthTokenRequester::Result::kPostTouchAuthenticatorPINSoftLock);
+  EXPECT_FALSE(delegate_->response());
+  EXPECT_FALSE(delegate_->pin_was_set());
+  EXPECT_TRUE(delegate_->pin_was_collected());
+  EXPECT_FALSE(delegate_->internal_uv_was_prompted());
+  EXPECT_FALSE(delegate_->internal_uv_was_locked());
+}
+
+TEST_F(AuthTokenRequesterTest, PINHardLock) {
+  VirtualCtap2Device::Config config;
+  config.pin_uv_auth_token_support = true;
+  config.ctap2_versions = {std::begin(kCtap2Versions2_1),
+                           std::end(kCtap2Versions2_1)};
+  auto state = base::MakeRefCounted<VirtualFidoDevice::State>();
+  state->pin_retries = 0;
+
+  RunTestCase(std::move(config), state,
+              TestCase{
+                  ClientPinAvailability::kSupportedAndPinSet,
+                  UserVerificationAvailability::kNotSupported,
+                  false,
+              });
+
+  EXPECT_EQ(*delegate_->result(),
+            AuthTokenRequester::Result::kPostTouchAuthenticatorPINHardLock);
+  EXPECT_FALSE(delegate_->response());
+  EXPECT_FALSE(delegate_->pin_was_set());
+  EXPECT_FALSE(delegate_->pin_was_collected());
+  EXPECT_FALSE(delegate_->internal_uv_was_prompted());
+  EXPECT_FALSE(delegate_->internal_uv_was_locked());
+}
+
+TEST_F(AuthTokenRequesterTest, UVLockedPINFallback) {
+  VirtualCtap2Device::Config config;
+  config.pin_uv_auth_token_support = true;
+  config.ctap2_versions = {std::begin(kCtap2Versions2_1),
+                           std::end(kCtap2Versions2_1)};
+  auto state = base::MakeRefCounted<VirtualFidoDevice::State>();
+  state->uv_retries = 0;
+
+  RunTestCase(std::move(config), state,
+              TestCase{
+                  ClientPinAvailability::kSupportedAndPinSet,
+                  UserVerificationAvailability::kSupportedAndConfigured,
+                  true,
+              });
+
+  EXPECT_EQ(*delegate_->result(), AuthTokenRequester::Result::kSuccess);
+  EXPECT_TRUE(delegate_->response());
+  EXPECT_FALSE(delegate_->pin_was_set());
+  EXPECT_TRUE(delegate_->pin_was_collected());
+  EXPECT_FALSE(delegate_->internal_uv_was_prompted());
+  EXPECT_TRUE(delegate_->internal_uv_was_locked());
+}
+
+}  // namespace
+}  // namespace device
diff --git a/device/fido/fido_device_authenticator.cc b/device/fido/fido_device_authenticator.cc
index 5ea5655..fcf6742 100644
--- a/device/fido/fido_device_authenticator.cc
+++ b/device/fido/fido_device_authenticator.cc
@@ -757,9 +757,6 @@
   size_t bytes_to_read = max_large_blob_fragment_length();
   LargeBlobsRequest request =
       LargeBlobsRequest::ForRead(bytes_to_read, large_blob_array_reader.size());
-  if (pin_uv_auth_token) {
-    request.SetPinParam(*pin_uv_auth_token);
-  }
   RunOperation<LargeBlobsRequest, LargeBlobsResponse>(
       std::move(request),
       base::BindOnce(&FidoDeviceAuthenticator::OnReadLargeBlobFragment,
diff --git a/device/fido/large_blob.cc b/device/fido/large_blob.cc
index 2b8e7b56..2c5e1b1 100644
--- a/device/fido/large_blob.cc
+++ b/device/fido/large_blob.cc
@@ -83,6 +83,7 @@
 
 void LargeBlobsRequest::SetPinParam(
     const pin::TokenResponse& pin_uv_auth_token) {
+  DCHECK(set_) << "SetPinParam should only be used for write requests";
   std::vector<uint8_t> pin_auth(pin::kPinUvAuthTokenSafetyPadding.begin(),
                                 pin::kPinUvAuthTokenSafetyPadding.end());
   pin_auth.insert(pin_auth.end(), kLargeBlobPinPrefix.begin(),
diff --git a/device/fido/pin.h b/device/fido/pin.h
index 1449442..bda30b1 100644
--- a/device/fido/pin.h
+++ b/device/fido/pin.h
@@ -287,6 +287,7 @@
       base::span<const uint8_t> client_data_hash) const;
 
   PINUVAuthProtocol protocol() const { return protocol_; }
+  const std::vector<uint8_t>& token_for_testing() const { return token_; }
 
  private:
   explicit TokenResponse(PINUVAuthProtocol protocol);
diff --git a/device/fido/virtual_ctap2_device.cc b/device/fido/virtual_ctap2_device.cc
index 4f71fcf8..68a21a48 100644
--- a/device/fido/virtual_ctap2_device.cc
+++ b/device/fido/virtual_ctap2_device.cc
@@ -2281,7 +2281,11 @@
   const size_t max_fragment_length = kLargeBlobDefaultMaxFragmentLength;
 
   if (get_it != request_map.end()) {
-    if (length_it != request_map.end()) {
+    if (length_it != request_map.end() ||
+        request_map.find(cbor::Value(static_cast<uint8_t>(
+            LargeBlobsRequestKey::kPinUvAuthParam))) != request_map.end() ||
+        request_map.find(cbor::Value(static_cast<uint8_t>(
+            LargeBlobsRequestKey::kPinUvAuthProtocol))) != request_map.end()) {
       return CtapDeviceResponseCode::kCtap1ErrInvalidParameter;
     }
     const uint64_t get = get_it->second.GetUnsigned();
diff --git a/docs/vscode_python.md b/docs/vscode_python.md
index f640e7a..47c973b 100644
--- a/docs/vscode_python.md
+++ b/docs/vscode_python.md
@@ -52,31 +52,4 @@
 
 ## Locally
 
-1. On the debug tab, on the drop-down next to the play button, select “Add
-   Config”
-2. Add the following to the configurations array in “launch”:
-
-```
-{
-    "name":"Python: Local",
-    "type":"python",
-    "request":"attach",
-    "processId":"${command:pickProcess}"
-}
-```
-
-3. Add the following to your program:
-
-```
-import debugpy
-
-# Your code here!
-
-print("Wait for attach...")
-debugpy.wait_for_attach()
-debugpy.brerakpoint()
-```
-
-4. Start your program.
-5. Start the debugger. A dialog box will pop up asking you to select your
-   running program.
+Follow the same steps as above, but start from step 5.
diff --git a/extensions/browser/extension_web_contents_observer.cc b/extensions/browser/extension_web_contents_observer.cc
index 90bba91d..c66cf55f 100644
--- a/extensions/browser/extension_web_contents_observer.cc
+++ b/extensions/browser/extension_web_contents_observer.cc
@@ -292,12 +292,14 @@
 
   if (verify_url) {
     const url::Origin& origin(render_frame_host->GetLastCommittedOrigin());
-    // Without site isolation, this check is needed to eliminate non-extension
-    // schemes. With site isolation, this is still needed to exclude sandboxed
-    // extension frames with an opaque origin.
-    const GURL site_url(render_frame_host->GetSiteInstance()->GetSiteURL());
-    if (origin.opaque() || site_url != content::SiteInstance::GetSiteForURL(
-                                           browser_context, origin.GetURL()))
+    // This check is needed to eliminate origins that are not within a
+    // hosted-app's web extent, and sandboxed extension frames with an opaque
+    // origin.
+    // TODO(1139108) See if extension check is still needed after bug is fixed.
+    auto* extension_for_origin = ExtensionRegistry::Get(browser_context)
+                                     ->enabled_extensions()
+                                     .GetExtensionOrAppByURL(origin.GetURL());
+    if (origin.opaque() || extension_for_origin != extension)
       return nullptr;
   }
 
diff --git a/extensions/components/native_app_window/native_app_window_views.cc b/extensions/components/native_app_window/native_app_window_views.cc
index 7f61f12..0b01393e 100644
--- a/extensions/components/native_app_window/native_app_window_views.cc
+++ b/extensions/components/native_app_window/native_app_window_views.cc
@@ -41,6 +41,10 @@
   web_view_ = AddChildView(std::make_unique<views::WebView>(nullptr));
   web_view_->SetWebContents(app_window_->web_contents());
 
+  SetCanMinimize(!app_window_->show_on_lock_screen());
+  SetCanMaximize(CanMaximizeWindow());
+  SetCanResize(CanResizeWindow());
+
   widget_ = new views::Widget;
   widget_->AddObserver(this);
   InitializeWindow(app_window, create_params);
@@ -183,20 +187,6 @@
   return web_view_;
 }
 
-bool NativeAppWindowViews::CanResize() const {
-  return resizable_ && !size_constraints_.HasFixedSize() &&
-         !WidgetHasHitTestMask();
-}
-
-bool NativeAppWindowViews::CanMaximize() const {
-  return resizable_ && !size_constraints_.HasMaximumSize() &&
-         !WidgetHasHitTestMask();
-}
-
-bool NativeAppWindowViews::CanMinimize() const {
-  return !app_window_->show_on_lock_screen();
-}
-
 base::string16 NativeAppWindowViews::GetWindowTitle() const {
   return app_window_->GetTitle();
 }
@@ -384,6 +374,8 @@
     const gfx::Size& max_size) {
   size_constraints_.set_minimum_size(min_size);
   size_constraints_.set_maximum_size(max_size);
+  SetCanMaximize(CanMaximizeWindow());
+  SetCanResize(CanResizeWindow());
   widget_->OnSizeConstraintsChanged();
 }
 
@@ -420,9 +412,24 @@
   observer_list_.RemoveObserver(observer);
 }
 
+void NativeAppWindowViews::OnWidgetHasHitTestMaskChanged() {
+  SetCanMaximize(CanMaximizeWindow());
+  SetCanResize(CanResizeWindow());
+}
+
 void NativeAppWindowViews::OnViewWasResized() {
   for (auto& observer : observer_list_)
     observer.OnPositionRequiresUpdate();
 }
 
+bool NativeAppWindowViews::CanResizeWindow() const {
+  return resizable_ && !size_constraints_.HasFixedSize() &&
+         !WidgetHasHitTestMask();
+}
+
+bool NativeAppWindowViews::CanMaximizeWindow() const {
+  return resizable_ && !size_constraints_.HasMaximumSize() &&
+         !WidgetHasHitTestMask();
+}
+
 }  // namespace native_app_window
diff --git a/extensions/components/native_app_window/native_app_window_views.h b/extensions/components/native_app_window/native_app_window_views.h
index db06ef0..438b197 100644
--- a/extensions/components/native_app_window/native_app_window_views.h
+++ b/extensions/components/native_app_window/native_app_window_views.h
@@ -91,9 +91,6 @@
   // WidgetDelegate:
   void OnWidgetMove() override;
   views::View* GetInitiallyFocusedView() override;
-  bool CanResize() const override;
-  bool CanMaximize() const override;
-  bool CanMinimize() const override;
   base::string16 GetWindowTitle() const override;
   bool ShouldShowWindowTitle() const override;
   void SaveWindowPlacement(const gfx::Rect& bounds,
@@ -150,10 +147,15 @@
   void AddObserver(web_modal::ModalDialogHostObserver* observer) override;
   void RemoveObserver(web_modal::ModalDialogHostObserver* observer) override;
 
+  void OnWidgetHasHitTestMaskChanged();
+
  private:
   // Informs modal dialogs that they need to update their positions.
   void OnViewWasResized();
 
+  bool CanMaximizeWindow() const;
+  bool CanResizeWindow() const;
+
   extensions::AppWindow* app_window_ = nullptr;  // Not owned.
   views::WebView* web_view_ = nullptr;
   views::Widget* widget_ = nullptr;
diff --git a/fuchsia/base/context_provider_test_connector.cc b/fuchsia/base/context_provider_test_connector.cc
index 6796771..90301612 100644
--- a/fuchsia/base/context_provider_test_connector.cc
+++ b/fuchsia/base/context_provider_test_connector.cc
@@ -8,6 +8,7 @@
 
 #include <fuchsia/sys/cpp/fidl.h>
 #include <lib/fdio/directory.h>
+#include <lib/fdio/fd.h>
 #include <lib/sys/cpp/component_context.h>
 #include <zircon/processargs.h>
 #include <utility>
diff --git a/gpu/command_buffer/service/external_vk_image_factory_unittest.cc b/gpu/command_buffer/service/external_vk_image_factory_unittest.cc
index 36587be2..bcd7759 100644
--- a/gpu/command_buffer/service/external_vk_image_factory_unittest.cc
+++ b/gpu/command_buffer/service/external_vk_image_factory_unittest.cc
@@ -41,7 +41,15 @@
 
 class ExternalVkImageFactoryTest : public testing::Test {
  protected:
+  bool VulkanSupported() const {
+    // crbug.com(941685, 1139366): Vulkan driver crashes on Linux FYI Release
+    // (AMD R7 240).
+    return !GPUTestBotConfig::CurrentConfigMatches("Linux AMD");
+  }
   void SetUp() override {
+    if (!VulkanSupported()) {
+      return;
+    }
     // Set up the Vulkan implementation and context provider.
     vulkan_implementation_ = gpu::CreateVulkanImplementation();
     DCHECK(vulkan_implementation_) << "Failed to create Vulkan implementation";
@@ -129,6 +137,10 @@
 #if BUILDFLAG(USE_DAWN)
 
 TEST_F(ExternalVkImageFactoryTest, DawnWrite_SkiaVulkanRead) {
+  if (!VulkanSupported()) {
+    DLOG(ERROR) << "Test skipped because Vulkan isn't supported.";
+    return;
+  }
   // Create a backing using mailbox.
   auto mailbox = Mailbox::GenerateForSharedImage();
   const auto format = viz::ResourceFormat::RGBA_8888;
@@ -242,6 +254,10 @@
 }
 
 TEST_F(ExternalVkImageFactoryTest, SkiaVulkanWrite_DawnRead) {
+  if (!VulkanSupported()) {
+    DLOG(ERROR) << "Test skipped because Vulkan isn't supported.";
+    return;
+  }
   // Create a backing using mailbox.
   auto mailbox = Mailbox::GenerateForSharedImage();
   const auto format = viz::ResourceFormat::RGBA_8888;
diff --git a/gpu/command_buffer/service/gpu_command_buffer_memory_tracker.cc b/gpu/command_buffer/service/gpu_command_buffer_memory_tracker.cc
index 97b6d43..e30519a 100644
--- a/gpu/command_buffer/service/gpu_command_buffer_memory_tracker.cc
+++ b/gpu/command_buffer/service/gpu_command_buffer_memory_tracker.cc
@@ -11,58 +11,19 @@
 #include "gpu/ipc/common/command_buffer_id.h"
 #include "gpu/ipc/common/gpu_peak_memory.h"
 
-// Macro to reduce code duplication when logging memory in
-// GpuCommandBufferMemoryTracker. This is needed as the UMA_HISTOGRAM_* macros
-// require a unique call-site per histogram (you can't funnel multiple strings
-// into the same call-site).
-#define GPU_COMMAND_BUFFER_MEMORY_BLOCK(category)                          \
-  do {                                                                     \
-    uint64_t mb_used = size_ / (1024 * 1024);                              \
-    switch (context_type_) {                                               \
-      case CONTEXT_TYPE_WEBGL1:                                            \
-      case CONTEXT_TYPE_WEBGL2:                                            \
-      case CONTEXT_TYPE_WEBGL2_COMPUTE:                                    \
-        UMA_HISTOGRAM_MEMORY_LARGE_MB("GPU.ContextMemory.WebGL." category, \
-                                      mb_used);                            \
-        break;                                                             \
-      case CONTEXT_TYPE_OPENGLES2:                                         \
-      case CONTEXT_TYPE_OPENGLES3:                                         \
-        UMA_HISTOGRAM_MEMORY_LARGE_MB("GPU.ContextMemory.GLES." category,  \
-                                      mb_used);                            \
-        break;                                                             \
-      case CONTEXT_TYPE_WEBGPU:                                            \
-        break;                                                             \
-    }                                                                      \
-  } while (false)
-
 namespace gpu {
 
 GpuCommandBufferMemoryTracker::GpuCommandBufferMemoryTracker(
     CommandBufferId command_buffer_id,
     uint64_t client_tracing_id,
-    ContextType context_type,
     scoped_refptr<base::SingleThreadTaskRunner> task_runner,
     Observer* observer)
     : command_buffer_id_(command_buffer_id),
       client_tracing_id_(client_tracing_id),
-      context_type_(context_type),
-      memory_pressure_listener_(
-          FROM_HERE,
-          base::BindRepeating(
-              &GpuCommandBufferMemoryTracker::LogMemoryStatsPressure,
-              base::Unretained(this))),
       observer_(observer) {
-  // Set up |memory_stats_timer_| to call LogMemoryPeriodic periodically
-  // via the provided |task_runner|.
-  memory_stats_timer_.SetTaskRunner(std::move(task_runner));
-  memory_stats_timer_.Start(
-      FROM_HERE, base::TimeDelta::FromSeconds(30), this,
-      &GpuCommandBufferMemoryTracker::LogMemoryStatsPeriodic);
 }
 
-GpuCommandBufferMemoryTracker::~GpuCommandBufferMemoryTracker() {
-  LogMemoryStatsShutdown();
-}
+GpuCommandBufferMemoryTracker::~GpuCommandBufferMemoryTracker() = default;
 
 void GpuCommandBufferMemoryTracker::TrackMemoryAllocatedChange(int64_t delta) {
   DCHECK(delta >= 0 || size_ >= static_cast<uint64_t>(-delta));
@@ -90,21 +51,4 @@
   return command_buffer_id_.GetUnsafeValue();
 }
 
-void GpuCommandBufferMemoryTracker::LogMemoryStatsPeriodic() {
-  GPU_COMMAND_BUFFER_MEMORY_BLOCK("Periodic");
-}
-
-void GpuCommandBufferMemoryTracker::LogMemoryStatsShutdown() {
-  GPU_COMMAND_BUFFER_MEMORY_BLOCK("Shutdown");
-}
-
-void GpuCommandBufferMemoryTracker::LogMemoryStatsPressure(
-    base::MemoryPressureListener::MemoryPressureLevel pressure_level) {
-  // Only log on CRITICAL memory pressure.
-  if (pressure_level ==
-      base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL) {
-    GPU_COMMAND_BUFFER_MEMORY_BLOCK("Pressure");
-  }
-}
-
 }  // namespace gpu
diff --git a/gpu/command_buffer/service/gpu_command_buffer_memory_tracker.h b/gpu/command_buffer/service/gpu_command_buffer_memory_tracker.h
index 731b0b1..4161af9 100644
--- a/gpu/command_buffer/service/gpu_command_buffer_memory_tracker.h
+++ b/gpu/command_buffer/service/gpu_command_buffer_memory_tracker.h
@@ -24,7 +24,6 @@
   GpuCommandBufferMemoryTracker(
       CommandBufferId command_buffer_id,
       uint64_t client_tracing_id,
-      ContextType context_type,
       scoped_refptr<base::SingleThreadTaskRunner> task_runner,
       Observer* observer);
   ~GpuCommandBufferMemoryTracker() override;
@@ -37,20 +36,10 @@
   uint64_t ContextGroupTracingId() const override;
 
  private:
-  void LogMemoryStatsPeriodic();
-  void LogMemoryStatsShutdown();
-  void LogMemoryStatsPressure(
-      base::MemoryPressureListener::MemoryPressureLevel pressure_level);
-
   uint64_t size_ = 0;
   const CommandBufferId command_buffer_id_;
   const uint64_t client_tracing_id_;
 
-  // Variables used in memory stat histogram logging.
-  const ContextType context_type_;
-  base::RepeatingTimer memory_stats_timer_;
-  base::MemoryPressureListener memory_pressure_listener_;
-
   MemoryTracker::Observer* const observer_;
 
   DISALLOW_COPY_AND_ASSIGN(GpuCommandBufferMemoryTracker);
diff --git a/gpu/command_buffer/tests/shared_image_gl_backing_produce_dawn_unittest.cc b/gpu/command_buffer/tests/shared_image_gl_backing_produce_dawn_unittest.cc
index 6eb7757e..ac165e5e 100644
--- a/gpu/command_buffer/tests/shared_image_gl_backing_produce_dawn_unittest.cc
+++ b/gpu/command_buffer/tests/shared_image_gl_backing_produce_dawn_unittest.cc
@@ -40,6 +40,10 @@
     WebGPUTest::Options option;
     Initialize(option);
 
+    if (ShouldSkipTest()) {
+      return;
+    }
+
     gpu::ContextCreationAttribs attributes;
     attributes.alpha_size = 8;
     attributes.depth_size = 24;
diff --git a/gpu/command_buffer/tests/webgpu_test.cc b/gpu/command_buffer/tests/webgpu_test.cc
index c25739ce..58f8e24 100644
--- a/gpu/command_buffer/tests/webgpu_test.cc
+++ b/gpu/command_buffer/tests/webgpu_test.cc
@@ -39,8 +39,6 @@
 WebGPUTest::~WebGPUTest() = default;
 
 bool WebGPUTest::WebGPUSupported() const {
-  DCHECK(is_initialized_);  // Did you call WebGPUTest::Initialize?
-
   // crbug.com(941685): Vulkan driver crashes on Linux FYI Release (AMD R7 240).
   // Win7 does not support WebGPU
   if (GPUTestBotConfig::CurrentConfigMatches("Linux AMD") ||
@@ -63,6 +61,10 @@
 }
 
 void WebGPUTest::SetUp() {
+  if (!WebGPUSupported()) {
+    return;
+  }
+
   gpu::GpuPreferences gpu_preferences;
   gpu_preferences.enable_webgpu = true;
 #if (defined(OS_LINUX) || defined(OS_CHROMEOS)) && BUILDFLAG(USE_DAWN)
@@ -81,8 +83,6 @@
 }
 
 void WebGPUTest::Initialize(const Options& options) {
-  is_initialized_ = true;
-
   if (!WebGPUSupported()) {
     return;
   }
@@ -175,25 +175,25 @@
 }
 
 TEST_F(WebGPUTest, FlushNoCommands) {
-  Initialize(WebGPUTest::Options());
-
   if (!WebGPUSupported()) {
     LOG(ERROR) << "Test skipped because WebGPU isn't supported";
     return;
   }
 
+  Initialize(WebGPUTest::Options());
+
   webgpu()->FlushCommands();
 }
 
 // Referred from GLES2ImplementationTest/ReportLoss
 TEST_F(WebGPUTest, ReportLoss) {
-  Initialize(WebGPUTest::Options());
-
   if (!WebGPUSupported()) {
     LOG(ERROR) << "Test skipped because WebGPU isn't supported";
     return;
   }
 
+  Initialize(WebGPUTest::Options());
+
   GpuControlClient* webgpu_as_client = webgpu();
   int lost_count = 0;
   webgpu()->SetLostContextCallback(base::BindOnce(&CountCallback, &lost_count));
@@ -207,13 +207,13 @@
 
 // Referred from GLES2ImplementationTest/ReportLossReentrant
 TEST_F(WebGPUTest, ReportLossReentrant) {
-  Initialize(WebGPUTest::Options());
-
   if (!WebGPUSupported()) {
     LOG(ERROR) << "Test skipped because WebGPU isn't supported";
     return;
   }
 
+  Initialize(WebGPUTest::Options());
+
   GpuControlClient* webgpu_as_client = webgpu();
   int lost_count = 0;
   webgpu()->SetLostContextCallback(base::BindOnce(&CountCallback, &lost_count));
@@ -226,13 +226,13 @@
 }
 
 TEST_F(WebGPUTest, RequestAdapterAfterContextLost) {
-  Initialize(WebGPUTest::Options());
-
   if (!WebGPUSupported()) {
     LOG(ERROR) << "Test skipped because WebGPU isn't supported";
     return;
   }
 
+  Initialize(WebGPUTest::Options());
+
   webgpu()->OnGpuControlLostContext();
   ASSERT_FALSE(
       webgpu()->RequestAdapterAsync(webgpu::PowerPreference::kDefault,
@@ -240,13 +240,13 @@
 }
 
 TEST_F(WebGPUTest, RequestDeviceAfterContextLost) {
-  Initialize(WebGPUTest::Options());
-
   if (!WebGPUSupported()) {
     LOG(ERROR) << "Test skipped because WebGPU isn't supported";
     return;
   }
 
+  Initialize(WebGPUTest::Options());
+
   webgpu()->OnGpuControlLostContext();
   ASSERT_FALSE(webgpu()->RequestDeviceAsync(
       kAdapterServiceID, {},
diff --git a/gpu/command_buffer/tests/webgpu_test.h b/gpu/command_buffer/tests/webgpu_test.h
index af7e58e2..cacc34f 100644
--- a/gpu/command_buffer/tests/webgpu_test.h
+++ b/gpu/command_buffer/tests/webgpu_test.h
@@ -86,7 +86,6 @@
   // SharedImages on macOS require a valid image factory.
   GpuMemoryBufferFactoryIOSurface image_factory_;
 #endif
-  bool is_initialized_ = false;
 
   webgpu::DawnDeviceClientID next_device_client_id_ = 1;
 };
diff --git a/gpu/ipc/in_process_command_buffer.cc b/gpu/ipc/in_process_command_buffer.cc
index 462ec1550..74db6a8 100644
--- a/gpu/ipc/in_process_command_buffer.cc
+++ b/gpu/ipc/in_process_command_buffer.cc
@@ -358,7 +358,7 @@
         base::trace_event::MemoryDumpManager::GetInstance()
             ->GetTracingProcessId();
     memory_tracker = std::make_unique<GpuCommandBufferMemoryTracker>(
-        command_buffer_id_, client_tracing_id, params.attribs.context_type,
+        command_buffer_id_, client_tracing_id,
         base::ThreadTaskRunnerHandle::Get(), /* obserer=*/nullptr);
   }
 
diff --git a/gpu/ipc/service/command_buffer_stub.cc b/gpu/ipc/service/command_buffer_stub.cc
index dea796c9..8ba84ade 100644
--- a/gpu/ipc/service/command_buffer_stub.cc
+++ b/gpu/ipc/service/command_buffer_stub.cc
@@ -649,15 +649,14 @@
   destruction_observers_.RemoveObserver(observer);
 }
 
-std::unique_ptr<MemoryTracker> CommandBufferStub::CreateMemoryTracker(
-    const GPUCreateCommandBufferConfig& init_params) const {
+std::unique_ptr<MemoryTracker> CommandBufferStub::CreateMemoryTracker() const {
   MemoryTrackerFactory current_factory = GetMemoryTrackerFactory();
   if (current_factory)
-    return current_factory.Run(init_params);
+    return current_factory.Run();
 
   return std::make_unique<GpuCommandBufferMemoryTracker>(
       command_buffer_id_, channel_->client_tracing_id(),
-      init_params.attribs.context_type, channel_->task_runner(),
+      channel_->task_runner(),
       channel_->gpu_channel_manager()->peak_memory_monitor());
 }
 
diff --git a/gpu/ipc/service/command_buffer_stub.h b/gpu/ipc/service/command_buffer_stub.h
index 641306f..3e829e4 100644
--- a/gpu/ipc/service/command_buffer_stub.h
+++ b/gpu/ipc/service/command_buffer_stub.h
@@ -106,8 +106,7 @@
   void HandleReturnData(base::span<const uint8_t> data) override;
 
   using MemoryTrackerFactory =
-      base::RepeatingCallback<std::unique_ptr<MemoryTracker>(
-          const GPUCreateCommandBufferConfig&)>;
+      base::RepeatingCallback<std::unique_ptr<MemoryTracker>()>;
 
   // Overrides the way CreateMemoryTracker() uses to create a MemoryTracker.
   // This is intended for mocking the MemoryTracker in tests.
@@ -146,8 +145,7 @@
  protected:
   virtual bool HandleMessage(const IPC::Message& message) = 0;
 
-  std::unique_ptr<MemoryTracker> CreateMemoryTracker(
-      const GPUCreateCommandBufferConfig& init_params) const;
+  std::unique_ptr<MemoryTracker> CreateMemoryTracker() const;
 
   // Must be called during Initialize(). Takes ownership to co-ordinate
   // teardown in Destroy().
diff --git a/gpu/ipc/service/gles2_command_buffer_stub.cc b/gpu/ipc/service/gles2_command_buffer_stub.cc
index 9ccf7ef..f0d3b58 100644
--- a/gpu/ipc/service/gles2_command_buffer_stub.cc
+++ b/gpu/ipc/service/gles2_command_buffer_stub.cc
@@ -107,7 +107,7 @@
         manager->gpu_memory_buffer_factory();
     context_group_ = new gles2::ContextGroup(
         manager->gpu_preferences(), gles2::PassthroughCommandDecoderSupported(),
-        manager->mailbox_manager(), CreateMemoryTracker(init_params),
+        manager->mailbox_manager(), CreateMemoryTracker(),
         manager->shader_translator_cache(),
         manager->framebuffer_completeness_cache(), feature_info,
         init_params.attribs.bind_generates_resource, channel_->image_manager(),
diff --git a/gpu/ipc/service/image_decode_accelerator_stub_unittest.cc b/gpu/ipc/service/image_decode_accelerator_stub_unittest.cc
index 626b6f0..8861813 100644
--- a/gpu/ipc/service/image_decode_accelerator_stub_unittest.cc
+++ b/gpu/ipc/service/image_decode_accelerator_stub_unittest.cc
@@ -100,8 +100,7 @@
   SkISize dimensions;
 };
 
-std::unique_ptr<MemoryTracker> CreateMockMemoryTracker(
-    const GPUCreateCommandBufferConfig& init_params) {
+std::unique_ptr<MemoryTracker> CreateMockMemoryTracker() {
   return std::make_unique<NiceMock<gles2::MockMemoryTracker>>();
 }
 
diff --git a/gpu/ipc/service/raster_command_buffer_stub.cc b/gpu/ipc/service/raster_command_buffer_stub.cc
index e56d432..70e63c3 100644
--- a/gpu/ipc/service/raster_command_buffer_stub.cc
+++ b/gpu/ipc/service/raster_command_buffer_stub.cc
@@ -119,7 +119,7 @@
   use_virtualized_gl_context_ =
       shared_context_state->use_virtualized_gl_contexts();
 
-  memory_tracker_ = CreateMemoryTracker(init_params);
+  memory_tracker_ = CreateMemoryTracker();
 
   command_buffer_ =
       std::make_unique<CommandBufferService>(this, memory_tracker_.get());
diff --git a/gpu/ipc/service/webgpu_command_buffer_stub.cc b/gpu/ipc/service/webgpu_command_buffer_stub.cc
index 790ebc7..88fb12f 100644
--- a/gpu/ipc/service/webgpu_command_buffer_stub.cc
+++ b/gpu/ipc/service/webgpu_command_buffer_stub.cc
@@ -106,7 +106,7 @@
   share_group_ = manager->share_group();
   use_virtualized_gl_context_ = false;
 
-  memory_tracker_ = CreateMemoryTracker(init_params);
+  memory_tracker_ = CreateMemoryTracker();
 
   command_buffer_ =
       std::make_unique<CommandBufferService>(this, memory_tracker_.get());
diff --git a/infra/config/generated/cr-buildbucket.cfg b/infra/config/generated/cr-buildbucket.cfg
index 1134fa35..01e06d66 100644
--- a/infra/config/generated/cr-buildbucket.cfg
+++ b/infra/config/generated/cr-buildbucket.cfg
@@ -11109,7 +11109,7 @@
         cipd_version: "refs/heads/master"
         cmd: "recipes"
       }
-      properties: "{\"$build/code_coverage\":{\"use_java_coverage\":true},\"$build/goma\":{\"enable_ats\":true,\"jobs\":300,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\",\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.fyi\",\"recipe\":\"chromium\"}"
+      properties: "{\"$build/goma\":{\"enable_ats\":true,\"jobs\":300,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\",\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.fyi\",\"recipe\":\"chromium\"}"
       execution_timeout_secs: 36000
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
@@ -11144,7 +11144,7 @@
         cipd_version: "refs/heads/master"
         cmd: "recipes"
       }
-      properties: "{\"$build/code_coverage\":{\"use_java_coverage\":true},\"$build/goma\":{\"enable_ats\":true,\"jobs\":300,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\",\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.fyi\",\"recipe\":\"chromium\"}"
+      properties: "{\"$build/goma\":{\"enable_ats\":true,\"jobs\":300,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\",\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.fyi\",\"recipe\":\"chromium\"}"
       execution_timeout_secs: 36000
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
@@ -12759,7 +12759,7 @@
         cipd_version: "refs/heads/master"
         cmd: "recipes"
       }
-      properties: "{\"$build/code_coverage\":{\"use_clang_coverage\":true},\"$build/goma\":{\"enable_ats\":true,\"jobs\":150,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\",\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.fyi\",\"recipe\":\"chromium\"}"
+      properties: "{\"$build/goma\":{\"enable_ats\":true,\"jobs\":150,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\",\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.fyi\",\"recipe\":\"chromium\"}"
       execution_timeout_secs: 36000
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
@@ -12794,7 +12794,7 @@
         cipd_version: "refs/heads/master"
         cmd: "recipes"
       }
-      properties: "{\"$build/code_coverage\":{\"use_clang_coverage\":true},\"$build/goma\":{\"enable_ats\":true,\"jobs\":150,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\",\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.fyi\",\"recipe\":\"chromium\"}"
+      properties: "{\"$build/goma\":{\"enable_ats\":true,\"jobs\":150,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\",\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.fyi\",\"recipe\":\"chromium\"}"
       execution_timeout_secs: 36000
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
@@ -12829,7 +12829,7 @@
         cipd_version: "refs/heads/master"
         cmd: "recipes"
       }
-      properties: "{\"$build/code_coverage\":{\"use_clang_coverage\":true},\"$build/goma\":{\"enable_ats\":true,\"jobs\":150,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\",\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.fyi\",\"recipe\":\"chromium\"}"
+      properties: "{\"$build/goma\":{\"enable_ats\":true,\"jobs\":150,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\",\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.fyi\",\"recipe\":\"chromium\"}"
       execution_timeout_secs: 36000
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
@@ -14440,7 +14440,7 @@
         cmd: "recipes"
       }
       properties: "{\"$build/goma\":{\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.fyi\",\"recipe\":\"chromium\"}"
-      execution_timeout_secs: 10800
+      execution_timeout_secs: 21600
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
@@ -15000,7 +15000,7 @@
         cipd_version: "refs/heads/master"
         cmd: "recipes"
       }
-      properties: "{\"$build/code_coverage\":{\"use_clang_coverage\":true},\"$build/goma\":{\"enable_ats\":true,\"jobs\":150,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\",\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.fyi\",\"recipe\":\"chromium\"}"
+      properties: "{\"$build/goma\":{\"enable_ats\":true,\"jobs\":150,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\",\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.fyi\",\"recipe\":\"chromium\"}"
       execution_timeout_secs: 36000
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
@@ -15035,7 +15035,7 @@
         cipd_version: "refs/heads/master"
         cmd: "recipes"
       }
-      properties: "{\"$build/code_coverage\":{\"use_clang_coverage\":true},\"$build/goma\":{\"enable_ats\":true,\"jobs\":150,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\",\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.fyi\",\"recipe\":\"chromium\"}"
+      properties: "{\"$build/goma\":{\"enable_ats\":true,\"jobs\":150,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\",\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.fyi\",\"recipe\":\"chromium\"}"
       execution_timeout_secs: 36000
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
@@ -15070,7 +15070,7 @@
         cipd_version: "refs/heads/master"
         cmd: "recipes"
       }
-      properties: "{\"$build/code_coverage\":{\"use_clang_coverage\":true},\"$build/goma\":{\"enable_ats\":true,\"jobs\":150,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\",\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.fyi\",\"recipe\":\"chromium\"}"
+      properties: "{\"$build/goma\":{\"enable_ats\":true,\"jobs\":150,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\",\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.fyi\",\"recipe\":\"chromium\"}"
       execution_timeout_secs: 36000
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
@@ -25291,11 +25291,11 @@
       swarming_host: "chromium-swarm.appspot.com"
       swarming_tags: "vpython:native-python-wrapper"
       dimensions: "builderless:1"
-      dimensions: "cores:8"
+      dimensions: "cores:16"
       dimensions: "cpu:x86-64"
       dimensions: "os:Ubuntu-16.04"
       dimensions: "pool:luci.chromium.try"
-      dimensions: "ssd:0"
+      dimensions: "ssd:1"
       exe {
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
         cipd_version: "refs/heads/master"
diff --git a/infra/config/subprojects/chromium/ci.star b/infra/config/subprojects/chromium/ci.star
index 8bbecf7..f811df7 100644
--- a/infra/config/subprojects/chromium/ci.star
+++ b/infra/config/subprojects/chromium/ci.star
@@ -2300,7 +2300,6 @@
     builderless = False,
     cores = None,
     goma_jobs = goma.jobs.J300,
-    use_java_coverage = True,
 )
 
 ci.fyi_builder(
@@ -2312,7 +2311,6 @@
     builderless = False,
     cores = None,
     goma_jobs = goma.jobs.J300,
-    use_java_coverage = True,
 )
 
 ci.fyi_builder(
@@ -2585,7 +2583,6 @@
     ),
     builderless = False,
     cores = None,
-    use_clang_coverage = True,
     goma_jobs = goma.jobs.J150,
 )
 
@@ -2597,7 +2594,6 @@
     ),
     builderless = False,
     cores = None,
-    use_clang_coverage = True,
     goma_jobs = goma.jobs.J150,
 )
 
@@ -2609,7 +2605,6 @@
     ),
     builderless = False,
     cores = None,
-    use_clang_coverage = True,
     goma_jobs = goma.jobs.J150,
 )
 
@@ -2779,7 +2774,6 @@
     ),
     builderless = False,
     cores = None,
-    use_clang_coverage = True,
     goma_jobs = goma.jobs.J150,
 )
 
@@ -2791,7 +2785,6 @@
     ),
     builderless = False,
     cores = None,
-    use_clang_coverage = True,
     goma_jobs = goma.jobs.J150,
 )
 
@@ -2803,7 +2796,6 @@
     ),
     builderless = False,
     cores = None,
-    use_clang_coverage = True,
     goma_jobs = goma.jobs.J150,
 )
 
@@ -4328,6 +4320,9 @@
         category = "mac",
         short_name = "a64",
     ),
+    # TODO(gbeaty) Once we have sufficient test capacity to not need to
+    # serialize tests, use the default execution_timout
+    execution_timeout = 6 * time.hour,
     tree_closing = False,
     triggered_by = [builder_name("mac-arm64-rel")],
 )
diff --git a/infra/config/subprojects/chromium/try.star b/infra/config/subprojects/chromium/try.star
index 7fbf5f4..59ea7df5 100644
--- a/infra/config/subprojects/chromium/try.star
+++ b/infra/config/subprojects/chromium/try.star
@@ -678,6 +678,8 @@
 
 try_.chromium_chromiumos_builder(
     name = "linux-lacros-rel",
+    cores = 16,
+    ssd = True,
     goma_jobs = goma.jobs.J150,
     main_list_view = "try",
     tryjob = try_.job(),
diff --git a/ios/chrome/app/main_controller.mm b/ios/chrome/app/main_controller.mm
index 5c065ee..5287606 100644
--- a/ios/chrome/app/main_controller.mm
+++ b/ios/chrome/app/main_controller.mm
@@ -49,8 +49,7 @@
 #include "ios/chrome/browser/chrome_paths.h"
 #include "ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_keyed_service.h"
 #include "ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_keyed_service_factory.h"
-#include "ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_keyed_service.h"
-#include "ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_keyed_service_factory.h"
+#include "ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_manager.h"
 #include "ios/chrome/browser/crash_report/breadcrumbs/features.h"
 #include "ios/chrome/browser/crash_report/breakpad_helper.h"
 #include "ios/chrome/browser/crash_report/crash_keys_helper.h"
@@ -689,14 +688,19 @@
 
   if (base::FeatureList::IsEnabled(kLogBreadcrumbs)) {
     if (self.appState.mainBrowserState->HasOffTheRecordChromeBrowserState()) {
-      breakpad::StopMonitoringBreadcrumbManagerService(
+      BreadcrumbManagerKeyedService* service =
           BreadcrumbManagerKeyedServiceFactory::GetForBrowserState(
               self.appState.mainBrowserState
-                  ->GetOffTheRecordChromeBrowserState()));
+                  ->GetOffTheRecordChromeBrowserState());
+      service->StopPersisting();
+      breakpad::StopMonitoringBreadcrumbManagerService(service);
     }
-    breakpad::StopMonitoringBreadcrumbManagerService(
+
+    BreadcrumbManagerKeyedService* service =
         BreadcrumbManagerKeyedServiceFactory::GetForBrowserState(
-            self.appState.mainBrowserState));
+            self.appState.mainBrowserState);
+    service->StopPersisting();
+    breakpad::StopMonitoringBreadcrumbManagerService(service);
   }
 
   _extensionSearchEngineDataUpdater = nullptr;
@@ -949,29 +953,19 @@
           self.appState.mainBrowserState);
   breakpad::MonitorBreadcrumbManagerService(breadcrumbService);
 
-  __weak __typeof(self) weakSelf = self;
-  BreadcrumbPersistentStorageKeyedService* persistentStorageService =
-      BreadcrumbPersistentStorageKeyedServiceFactory::GetForBrowserState(
-          self.appState.mainBrowserState);
-  // Get stored persistent breadcrumbs from last run and set them on the
-  // breadcrumb manager.
-  persistentStorageService->GetStoredEvents(
+  BreadcrumbPersistentStorageManager* persistentStorageManager =
+      GetApplicationContext()->GetBreadcrumbPersistentStorageManager();
+
+  // Application context can return a null persistent storage manager if
+  // breadcrumbs are not being persisted.
+  if (persistentStorageManager) {
+    breadcrumbService->StartPersisting(persistentStorageManager);
+  }
+
+  // Get stored persistent breadcrumbs from last run to set on crash reports.
+  persistentStorageManager->GetStoredEvents(
       base::BindOnce(^(std::vector<std::string> events) {
-        __strong __typeof(weakSelf) strongSelf = weakSelf;
-        if (!strongSelf || !strongSelf.appState.mainBrowserState) {
-          return;
-        }
-
-        BreadcrumbManagerKeyedServiceFactory::GetForBrowserState(
-            strongSelf.appState.mainBrowserState)
-            ->SetPreviousEvents(events);
         breakpad::SetPreviousSessionEvents(events);
-
-        // Notify persistent breadcrumb service to clear old breadcrumbs and
-        // start storing breadcrumbs for this session.
-        BreadcrumbPersistentStorageKeyedServiceFactory::GetForBrowserState(
-            strongSelf.appState.mainBrowserState)
-            ->StartStoringEvents();
       }));
 }
 
diff --git a/ios/chrome/browser/application_context.h b/ios/chrome/browser/application_context.h
index 000d44d..ee32f71 100644
--- a/ios/chrome/browser/application_context.h
+++ b/ios/chrome/browser/application_context.h
@@ -64,6 +64,7 @@
 }
 
 class ApplicationContext;
+class BreadcrumbPersistentStorageManager;
 class BrowserPolicyConnectorIOS;
 class IOSChromeIOThread;
 class PrefService;
@@ -155,6 +156,11 @@
   // system. May be |nullptr| if policy is not enabled.
   virtual BrowserPolicyConnectorIOS* GetBrowserPolicyConnector() = 0;
 
+  // Returns the BreadcrumbPersistentStorageManager writing breadcrumbs to disk.
+  // Will be null if breadcrumb collection is not enabled.
+  virtual BreadcrumbPersistentStorageManager*
+  GetBreadcrumbPersistentStorageManager() = 0;
+
  protected:
   // Sets the global ApplicationContext instance.
   static void SetApplicationContext(ApplicationContext* context);
diff --git a/ios/chrome/browser/application_context_impl.h b/ios/chrome/browser/application_context_impl.h
index 5cd6926..026c01e 100644
--- a/ios/chrome/browser/application_context_impl.h
+++ b/ios/chrome/browser/application_context_impl.h
@@ -18,8 +18,9 @@
 class SequencedTaskRunner;
 }
 
-class BreadcrumbManager;
 class ApplicationBreadcrumbsLogger;
+class BreadcrumbManager;
+class BreadcrumbPersistentStorageManager;
 
 namespace network {
 class NetworkChangeManager;
@@ -73,6 +74,8 @@
   SafeBrowsingService* GetSafeBrowsingService() override;
   network::NetworkConnectionTracker* GetNetworkConnectionTracker() override;
   BrowserPolicyConnectorIOS* GetBrowserPolicyConnector() override;
+  BreadcrumbPersistentStorageManager* GetBreadcrumbPersistentStorageManager()
+      override;
 
  private:
   // Sets the locale used by the application.
@@ -92,6 +95,10 @@
   // Logger which observers and logs application wide events to
   // |breadcrumb_manager_|. Will be null if breadcrumbs feature is not enabled.
   std::unique_ptr<ApplicationBreadcrumbsLogger> application_breadcrumbs_logger_;
+  // Persistent storage manager to write breadcrumbs to disk for storage
+  // between sessions. Will be null if breadcrumbs feature is not enabled.
+  std::unique_ptr<BreadcrumbPersistentStorageManager>
+      breadcrumb_persistent_storage_manager_;
 
   // Must be destroyed after |local_state_|.
   std::unique_ptr<BrowserPolicyConnectorIOS> browser_policy_connector_;
diff --git a/ios/chrome/browser/application_context_impl.mm b/ios/chrome/browser/application_context_impl.mm
index f2d1668c9..705f9dee 100644
--- a/ios/chrome/browser/application_context_impl.mm
+++ b/ios/chrome/browser/application_context_impl.mm
@@ -49,6 +49,7 @@
 #include "ios/chrome/browser/component_updater/ios_component_updater_configurator.h"
 #import "ios/chrome/browser/crash_report/breadcrumbs/application_breadcrumbs_logger.h"
 #include "ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager.h"
+#include "ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_manager.h"
 #include "ios/chrome/browser/crash_report/breadcrumbs/features.h"
 #include "ios/chrome/browser/gcm/ios_chrome_gcm_profile_service_factory.h"
 #include "ios/chrome/browser/history/history_service_factory.h"
@@ -229,6 +230,15 @@
     application_breadcrumbs_logger_ =
         std::make_unique<ApplicationBreadcrumbsLogger>(
             breadcrumb_manager_.get());
+
+    base::FilePath storage_dir;
+    bool result = base::PathService::Get(ios::DIR_USER_DATA, &storage_dir);
+    DCHECK(result);
+    breadcrumb_persistent_storage_manager_ =
+        std::make_unique<BreadcrumbPersistentStorageManager>(storage_dir);
+
+    application_breadcrumbs_logger_->SetPersistentStorageManager(
+        breadcrumb_persistent_storage_manager_.get());
   }
 }
 
@@ -455,6 +465,12 @@
   return browser_policy_connector_.get();
 }
 
+BreadcrumbPersistentStorageManager*
+ApplicationContextImpl::GetBreadcrumbPersistentStorageManager() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  return breadcrumb_persistent_storage_manager_.get();
+}
+
 void ApplicationContextImpl::SetApplicationLocale(const std::string& locale) {
   DCHECK(thread_checker_.CalledOnValidThread());
   application_locale_ = locale;
diff --git a/ios/chrome/browser/browser_state/browser_state_keyed_service_factories.mm b/ios/chrome/browser/browser_state/browser_state_keyed_service_factories.mm
index fddb4249..c460e9d 100644
--- a/ios/chrome/browser/browser_state/browser_state_keyed_service_factories.mm
+++ b/ios/chrome/browser/browser_state/browser_state_keyed_service_factories.mm
@@ -14,7 +14,6 @@
 #include "ios/chrome/browser/browsing_data/browsing_data_remover_factory.h"
 #include "ios/chrome/browser/content_settings/cookie_settings_factory.h"
 #include "ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_keyed_service_factory.h"
-#include "ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_keyed_service_factory.h"
 #include "ios/chrome/browser/credential_provider/credential_provider_service_factory.h"
 #import "ios/chrome/browser/device_sharing/device_sharing_manager_factory.h"
 #include "ios/chrome/browser/discover_feed/discover_feed_service_factory.h"
@@ -106,7 +105,6 @@
   suggestions::SuggestionsServiceFactory::GetInstance();
   AuthenticationServiceFactory::GetInstance();
   BreadcrumbManagerKeyedServiceFactory::GetInstance();
-  BreadcrumbPersistentStorageKeyedServiceFactory::GetInstance();
   BrowserDownloadServiceFactory::GetInstance();
   BrowsingDataRemoverFactory::GetInstance();
   ConsentAuditorFactory::GetInstance();
diff --git a/ios/chrome/browser/crash_report/breadcrumbs/BUILD.gn b/ios/chrome/browser/crash_report/breadcrumbs/BUILD.gn
index 640bba3..df027900 100644
--- a/ios/chrome/browser/crash_report/breadcrumbs/BUILD.gn
+++ b/ios/chrome/browser/crash_report/breadcrumbs/BUILD.gn
@@ -46,10 +46,8 @@
     "breadcrumb_manager_observer_bridge.mm",
     "breadcrumb_manager_tab_helper.h",
     "breadcrumb_manager_tab_helper.mm",
-    "breadcrumb_persistent_storage_keyed_service.cc",
-    "breadcrumb_persistent_storage_keyed_service.h",
-    "breadcrumb_persistent_storage_keyed_service_factory.cc",
-    "breadcrumb_persistent_storage_keyed_service_factory.h",
+    "breadcrumb_persistent_storage_manager.h",
+    "breadcrumb_persistent_storage_manager.mm",
     "breadcrumb_persistent_storage_util.cc",
     "breadcrumb_persistent_storage_util.h",
   ]
@@ -114,7 +112,7 @@
     "breadcrumb_manager_observer_unittest.mm",
     "breadcrumb_manager_tab_helper_unittest.mm",
     "breadcrumb_manager_unittest.mm",
-    "breadcrumb_persistent_storage_keyed_service_unittest.mm",
+    "breadcrumb_persistent_storage_manager_unittest.mm",
     "breadcrumb_persistent_storage_util_unittest.mm",
   ]
 }
diff --git a/ios/chrome/browser/crash_report/breadcrumbs/application_breadcrumbs_logger.h b/ios/chrome/browser/crash_report/breadcrumbs/application_breadcrumbs_logger.h
index b157b0c..c3d5004 100644
--- a/ios/chrome/browser/crash_report/breadcrumbs/application_breadcrumbs_logger.h
+++ b/ios/chrome/browser/crash_report/breadcrumbs/application_breadcrumbs_logger.h
@@ -17,6 +17,7 @@
 }  // namespace base
 
 class BreadcrumbManager;
+class BreadcrumbPersistentStorageManager;
 
 // Name of event logged when device orientation is changed.
 extern const char kBreadcrumbOrientation[];
@@ -28,6 +29,11 @@
   explicit ApplicationBreadcrumbsLogger(BreadcrumbManager* breadcrumb_manager);
   ~ApplicationBreadcrumbsLogger();
 
+  // Sets a BreadcrumbPersistentStorageManager to persist application breadcrumb
+  // events logged by this ApplicationBreadcrumbsLogger instance.
+  void SetPersistentStorageManager(
+      BreadcrumbPersistentStorageManager* persistent_storage_manager);
+
  private:
   ApplicationBreadcrumbsLogger(const ApplicationBreadcrumbsLogger&) = delete;
 
@@ -52,6 +58,10 @@
   // Observes device orientation.
   id<NSObject> orientation_observer_;
 
+  // A weak reference to the persistent breadcrumb manager listening for events
+  // from |breadcrumb_manager_| to store to disk.
+  BreadcrumbPersistentStorageManager* persistent_storage_manager_ = nullptr;
+
   // Used to avoid logging the same orientation twice.
   base::Optional<UIDeviceOrientation> last_orientation_;
 };
diff --git a/ios/chrome/browser/crash_report/breadcrumbs/application_breadcrumbs_logger.mm b/ios/chrome/browser/crash_report/breadcrumbs/application_breadcrumbs_logger.mm
index 046cd7e3..ef8ae51 100644
--- a/ios/chrome/browser/crash_report/breadcrumbs/application_breadcrumbs_logger.mm
+++ b/ios/chrome/browser/crash_report/breadcrumbs/application_breadcrumbs_logger.mm
@@ -8,6 +8,7 @@
 #include "base/strings/stringprintf.h"
 #include "ios/chrome/browser/crash_report/breadcrumbs/application_breadcrumbs_not_user_action.inc"
 #include "ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager.h"
+#include "ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_manager.h"
 #import "ios/chrome/browser/crash_report/crash_report_helper.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -27,6 +28,7 @@
           base::BindRepeating(&ApplicationBreadcrumbsLogger::OnMemoryPressure,
                               base::Unretained(this)))) {
   base::AddActionCallback(user_action_callback_);
+
   breakpad::MonitorBreadcrumbManager(breadcrumb_manager_);
   breadcrumb_manager_->AddEvent("Startup");
 
@@ -72,6 +74,21 @@
   breadcrumb_manager_->AddEvent("Shutdown");
   base::RemoveActionCallback(user_action_callback_);
   breakpad::StopMonitoringBreadcrumbManager(breadcrumb_manager_);
+  if (persistent_storage_manager_) {
+    persistent_storage_manager_->StopMonitoringBreadcrumbManager(
+        breadcrumb_manager_);
+  }
+}
+
+void ApplicationBreadcrumbsLogger::SetPersistentStorageManager(
+    BreadcrumbPersistentStorageManager* persistent_storage_manager) {
+  if (persistent_storage_manager_) {
+    persistent_storage_manager_->StopMonitoringBreadcrumbManager(
+        breadcrumb_manager_);
+  }
+
+  persistent_storage_manager_ = persistent_storage_manager;
+  persistent_storage_manager->MonitorBreadcrumbManager(breadcrumb_manager_);
 }
 
 void ApplicationBreadcrumbsLogger::OnUserAction(const std::string& action,
diff --git a/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager.cc b/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager.cc
index 8845561..ddd0c26 100644
--- a/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager.cc
+++ b/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager.cc
@@ -73,29 +73,6 @@
   return events;
 }
 
-void BreadcrumbManager::SetPreviousEvents(
-    const std::vector<std::string>& events) {
-  if (events.empty()) {
-    return;
-  }
-
-  // Create a new bucket with a fake timestamp before the application started.
-  // This ensures that these initial events will be dropped before new events
-  // from the current session.
-  base::TimeDelta time_since_construction = base::Time::Now() - start_time_;
-  base::Time previous_events_bucket_time =
-      base::Time::Now() -
-      base::TimeDelta::FromSeconds(60 + time_since_construction.InSeconds());
-  std::pair<base::Time, std::list<std::string>> bucket(
-      EventBucket(previous_events_bucket_time), std::list<std::string>());
-
-  for (auto event_it = events.rbegin(); event_it != events.rend(); ++event_it) {
-    std::string event = *event_it;
-    bucket.second.push_front(event);
-  }
-  event_buckets_.push_front(bucket);
-}
-
 void BreadcrumbManager::AddEvent(const std::string& event) {
   base::Time time = base::Time::Now();
   base::Time bucket_time = EventBucket(time);
diff --git a/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager.h b/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager.h
index 2ac8462..fe4ae0c 100644
--- a/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager.h
+++ b/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager.h
@@ -34,9 +34,6 @@
   // even if no new events have been added, but time has passed.
   const std::list<std::string> GetEvents(size_t event_count_limit);
 
-  // Sets previous events by inserting them before all existing events.
-  void SetPreviousEvents(const std::vector<std::string>& events);
-
   // Logs a breadcrumb event with message data |event|.
   // NOTE: |event| must not include newline characters as newlines are used by
   // BreadcrumbPersistentStore as a deliminator.
diff --git a/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_keyed_service.cc b/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_keyed_service.cc
index a61b1ab..47d519c 100644
--- a/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_keyed_service.cc
+++ b/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_keyed_service.cc
@@ -6,13 +6,9 @@
 
 #include "base/strings/stringprintf.h"
 #include "ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager.h"
+#include "ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_manager.h"
 #include "ios/web/public/browser_state.h"
 
-void BreadcrumbManagerKeyedService::SetPreviousEvents(
-    const std::vector<std::string>& events) {
-  breadcrumb_manager_->SetPreviousEvents(events);
-}
-
 void BreadcrumbManagerKeyedService::AddEvent(const std::string& event) {
   std::string event_log =
       base::StringPrintf("%s%s", browsing_mode_.c_str(), event.c_str());
@@ -38,6 +34,32 @@
   return breadcrumb_manager_->GetEvents(event_count_limit);
 }
 
+void BreadcrumbManagerKeyedService::StartPersisting(
+    BreadcrumbPersistentStorageManager* persistent_storage_manager) {
+  DCHECK(persistent_storage_manager);
+
+  if (persistent_storage_manager_) {
+    StopPersisting();
+  }
+
+  persistent_storage_manager_ = persistent_storage_manager;
+  persistent_storage_manager_->MonitorBreadcrumbManagerService(this);
+}
+
+void BreadcrumbManagerKeyedService::StopPersisting() {
+  if (!persistent_storage_manager_) {
+    return;
+  }
+
+  persistent_storage_manager_->StopMonitoringBreadcrumbManagerService(this);
+  persistent_storage_manager_ = nullptr;
+}
+
+BreadcrumbPersistentStorageManager*
+BreadcrumbManagerKeyedService::GetPersistentStorageManager() {
+  return persistent_storage_manager_;
+}
+
 BreadcrumbManagerKeyedService::BreadcrumbManagerKeyedService(
     web::BrowserState* browser_state)
     // Set "I" for Incognito (Chrome branded OffTheRecord implementation) and
diff --git a/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_keyed_service.h b/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_keyed_service.h
index 3f515f8..a0dcca0 100644
--- a/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_keyed_service.h
+++ b/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_keyed_service.h
@@ -13,6 +13,7 @@
 
 class BreadcrumbManager;
 class BreadcrumbManagerObserver;
+class BreadcrumbPersistentStorageManager;
 
 namespace web {
 class BrowserState;
@@ -24,9 +25,6 @@
   explicit BreadcrumbManagerKeyedService(web::BrowserState* browser_state);
   ~BreadcrumbManagerKeyedService() override;
 
-  // Sets previous events by inserting them before all existing events.
-  void SetPreviousEvents(const std::vector<std::string>& events);
-
   // Logs a breadcrumb |event| associated with the BrowserState passed in at
   // initialization of this instance. Prepends the |browsing_mode_| identifier
   // to the event before passing it to the |breadcrumb_manager_|.
@@ -45,6 +43,19 @@
   // details.
   const std::list<std::string> GetEvents(size_t event_count_limit) const;
 
+  // Persists all events logged to |breadcrumb_manager_| to
+  // |persistent_storage_manager|. If StartPersisting has already been called,
+  // breadcrumbs will no longer be persisted to the previous
+  // |persistent_storage_manager|.
+  // NOTE: |persistent_storage_manager| must be non-null.
+  void StartPersisting(
+      BreadcrumbPersistentStorageManager* persistent_storage_manager);
+  // Stops persisting events to |persistent_storage_manager_|. No-op if
+  // |persistent_storage_manager_| is not set.
+  void StopPersisting();
+  // Returns the current |persistent_storage_manager_|.
+  BreadcrumbPersistentStorageManager* GetPersistentStorageManager();
+
  private:
   // A short string identifying the browser state used to initialize the
   // receiver. For example, "I" for "I"ncognito browsing mode. This value is
@@ -57,6 +68,10 @@
   // The associated BreadcrumbManager to store events added with |AddEvent|.
   std::unique_ptr<BreadcrumbManager> breadcrumb_manager_;
 
+  // The current BreadcrumbPersistentStorageManager persisting events logged to
+  // |breadcrumb_manager_|, set by StartPersisting. May be null.
+  BreadcrumbPersistentStorageManager* persistent_storage_manager_ = nullptr;
+
   DISALLOW_COPY_AND_ASSIGN(BreadcrumbManagerKeyedService);
 };
 
diff --git a/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_unittest.mm b/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_unittest.mm
index 5704bf4a..7d404af 100644
--- a/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_unittest.mm
+++ b/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_unittest.mm
@@ -86,19 +86,3 @@
   std::list<std::string> events = breadcrumb_manager_.GetEvents(0);
   EXPECT_EQ(2ul, events.size());
 }
-
-// Tests that previous events are set as expected.
-TEST_F(BreadcrumbManagerTest, SetPreviousEvents) {
-  breadcrumb_manager_.SetPreviousEvents({"event1", "event2"});
-  ASSERT_EQ(2ul, breadcrumb_manager_.GetEvents(0).size());
-
-  task_env_.FastForwardBy(base::TimeDelta::FromMinutes(3));
-  breadcrumb_manager_.AddEvent("event3");
-
-  std::list<std::string> events = breadcrumb_manager_.GetEvents(0);
-  EXPECT_NE(std::string::npos, events.front().find("event1"));
-  events.pop_front();
-  EXPECT_NE(std::string::npos, events.front().find("event2"));
-  events.pop_front();
-  EXPECT_NE(std::string::npos, events.front().find("event3"));
-}
diff --git a/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_keyed_service.cc b/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_keyed_service.cc
deleted file mode 100644
index 73cfd51..0000000
--- a/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_keyed_service.cc
+++ /dev/null
@@ -1,240 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_keyed_service.h"
-
-#include <string>
-
-#include "base/bind.h"
-#include "base/files/file_util.h"
-#include "base/files/memory_mapped_file.h"
-#include "base/sequenced_task_runner.h"
-#include "base/strings/string_split.h"
-#include "base/strings/string_util.h"
-#include "base/task/post_task.h"
-#include "base/task/thread_pool.h"
-#include "ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager.h"
-#include "ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_keyed_service.h"
-#include "ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_keyed_service_factory.h"
-#include "ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_util.h"
-
-namespace {
-
-const char kEventSeparator[] = "\n";
-
-// Minimum time between breadcrumb writes to disk.
-constexpr auto kMinDelayBetweenWrites = base::TimeDelta::FromMilliseconds(250);
-
-// Writes |events| to |file_path| at |position|.
-void DoInsertEventsIntoMemoryMappedFile(const base::FilePath& file_path,
-                                        const size_t position,
-                                        const std::string& events) {
-  auto file = std::make_unique<base::MemoryMappedFile>();
-  const base::MemoryMappedFile::Region region = {0, kPersistedFilesizeInBytes};
-  const bool file_valid = file->Initialize(
-      base::File(file_path, base::File::FLAG_OPEN_ALWAYS |
-                                base::File::FLAG_READ | base::File::FLAG_WRITE),
-      region, base::MemoryMappedFile::READ_WRITE_EXTEND);
-
-  if (file_valid) {
-    char* data = reinterpret_cast<char*>(file->data());
-    std::strcpy(&data[position], events.data());
-  }
-}
-
-// Writes |events| to |file_path| overwriting any existing data.
-void DoWriteEventsToFile(const base::FilePath& file_path,
-                         const std::string& events) {
-  const base::MemoryMappedFile::Region region = {0, kPersistedFilesizeInBytes};
-  base::MemoryMappedFile file;
-  const bool file_valid = file.Initialize(
-      base::File(file_path, base::File::FLAG_CREATE_ALWAYS |
-                                base::File::FLAG_READ | base::File::FLAG_WRITE),
-      region, base::MemoryMappedFile::READ_WRITE_EXTEND);
-
-  if (file_valid) {
-    char* data = reinterpret_cast<char*>(file.data());
-    std::strcpy(data, events.data());
-  }
-}
-
-void DoReplaceFile(const base::FilePath& from_path,
-                   const base::FilePath& to_path) {
-  base::ReplaceFile(from_path, to_path, nullptr);
-}
-
-// Returns breadcrumb events stored at |file_path|.
-std::vector<std::string> DoGetStoredEvents(const base::FilePath& file_path) {
-  base::File events_file(file_path,
-                         base::File::FLAG_OPEN | base::File::FLAG_READ);
-  if (!events_file.IsValid()) {
-    // File may not yet exist.
-    return std::vector<std::string>();
-  }
-
-  size_t file_size = events_file.GetLength();
-  if (file_size <= 0) {
-    return std::vector<std::string>();
-  }
-
-  // Do not read more than |kPersistedFilesizeInBytes|, in case the file was
-  // corrupted. If |kPersistedFilesizeInBytes| has been reduced since the last
-  // breadcrumbs file was saved, this could result in a one time loss of the
-  // oldest breadcrumbs which is ok because the decision has already been made
-  // to reduce the size of the stored breadcrumbs.
-  if (file_size > kPersistedFilesizeInBytes) {
-    file_size = kPersistedFilesizeInBytes;
-  }
-
-  std::vector<uint8_t> data;
-  data.resize(file_size);
-  if (!events_file.ReadAndCheck(/*offset=*/0, data)) {
-    return std::vector<std::string>();
-  }
-  std::string persisted_events(data.begin(), data.end());
-  std::string all_events =
-      persisted_events.substr(/*pos=*/0, strlen(persisted_events.c_str()));
-  return base::SplitString(all_events, kEventSeparator, base::TRIM_WHITESPACE,
-                           base::SPLIT_WANT_NONEMPTY);
-}
-
-}  // namespace
-
-using breadcrumb_persistent_storage_util::
-    GetBreadcrumbPersistentStorageFilePath;
-using breadcrumb_persistent_storage_util::
-    GetBreadcrumbPersistentStorageTempFilePath;
-
-BreadcrumbPersistentStorageKeyedService::
-    BreadcrumbPersistentStorageKeyedService(web::BrowserState* browser_state)
-    :  // Ensure first event will not be delayed by initializing with a time in
-       // the past.
-      last_written_time_(base::TimeTicks::Now() - kMinDelayBetweenWrites),
-      browser_state_(browser_state),
-      breadcrumbs_file_path_(
-          GetBreadcrumbPersistentStorageFilePath(browser_state_)),
-      task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
-          {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
-           base::TaskShutdownBehavior::BLOCK_SHUTDOWN})),
-      weak_factory_(this) {}
-
-BreadcrumbPersistentStorageKeyedService::
-    ~BreadcrumbPersistentStorageKeyedService() = default;
-
-void BreadcrumbPersistentStorageKeyedService::GetStoredEvents(
-    base::OnceCallback<void(std::vector<std::string>)> callback) {
-  task_runner_->PostTaskAndReplyWithResult(
-      FROM_HERE, base::BindOnce(&DoGetStoredEvents, breadcrumbs_file_path_),
-      std::move(callback));
-}
-
-void BreadcrumbPersistentStorageKeyedService::StartStoringEvents() {
-  RewriteAllExistingBreadcrumbs();
-
-  BreadcrumbManagerKeyedServiceFactory::GetForBrowserState(browser_state_)
-      ->AddObserver(this);
-}
-
-void BreadcrumbPersistentStorageKeyedService::RewriteAllExistingBreadcrumbs() {
-  // Cancel writing out individual breadcrumbs as they are all being re-written.
-  pending_breadcrumbs_.clear();
-  write_timer_.Stop();
-
-  last_written_time_ = base::TimeTicks::Now();
-
-  current_mapped_file_position_ = 0;
-
-  std::list<std::string> events =
-      BreadcrumbManagerKeyedServiceFactory::GetForBrowserState(browser_state_)
-          ->GetEvents(/*event_count_limit=*/0);
-
-  std::vector<std::string> breadcrumbs;
-  for (auto event_it = events.rbegin(); event_it != events.rend(); ++event_it) {
-    // Reduce saved events to only fill the amount which would be included on
-    // a crash log. This allows future events to be appended individually up to
-    // |kPersistedFilesizeInBytes|, which is more efficient than writing out the
-    const int event_with_seperator_size =
-        event_it->size() + strlen(kEventSeparator);
-    if (event_with_seperator_size + current_mapped_file_position_ >=
-        kMaxBreadcrumbsDataLength) {
-      break;
-    }
-
-    breadcrumbs.push_back(kEventSeparator);
-    breadcrumbs.push_back(*event_it);
-    current_mapped_file_position_ += event_with_seperator_size;
-  }
-
-  std::reverse(breadcrumbs.begin(), breadcrumbs.end());
-  std::string breadcrumbs_string = base::JoinString(breadcrumbs, "");
-
-  task_runner_->PostTask(
-      FROM_HERE,
-      base::BindOnce(&DoWriteEventsToFile,
-                     base::Passed(GetBreadcrumbPersistentStorageTempFilePath(
-                         browser_state_)),
-                     std::string(breadcrumbs_string)));
-
-  task_runner_->PostTask(
-      FROM_HERE,
-      base::BindOnce(&DoReplaceFile,
-                     base::Passed(GetBreadcrumbPersistentStorageTempFilePath(
-                         browser_state_)),
-                     breadcrumbs_file_path_));
-}
-
-void BreadcrumbPersistentStorageKeyedService::WritePendingBreadcrumbs() {
-  if (pending_breadcrumbs_.empty()) {
-    return;
-  }
-
-  task_runner_->PostTask(
-      FROM_HERE,
-      base::BindOnce(&DoInsertEventsIntoMemoryMappedFile,
-                     breadcrumbs_file_path_, current_mapped_file_position_,
-                     std::string(pending_breadcrumbs_)));
-
-  current_mapped_file_position_ += pending_breadcrumbs_.size();
-  last_written_time_ = base::TimeTicks::Now();
-
-  pending_breadcrumbs_.clear();
-}
-
-void BreadcrumbPersistentStorageKeyedService::EventAdded(
-    BreadcrumbManager* manager,
-    const std::string& event) {
-  // If the event doesn not fit within |kPersistedFilesizeInBytes|, rewrite the
-  // file to trim old events.
-  if ((current_mapped_file_position_ + pending_breadcrumbs_.size() +
-       // Use >= here instead of > to allow space for \0 to terminate file.
-       event.size()) >= kPersistedFilesizeInBytes) {
-    RewriteAllExistingBreadcrumbs();
-    return;
-  }
-
-  write_timer_.Stop();
-
-  pending_breadcrumbs_ += event + kEventSeparator;
-
-  const base::TimeDelta time_delta_since_last_write =
-      base::TimeTicks::Now() - last_written_time_;
-  // Delay writing the event to disk if an event was just written.
-  if (time_delta_since_last_write < kMinDelayBetweenWrites) {
-    write_timer_.Start(
-        FROM_HERE, kMinDelayBetweenWrites - time_delta_since_last_write, this,
-        &BreadcrumbPersistentStorageKeyedService::WritePendingBreadcrumbs);
-  } else {
-    WritePendingBreadcrumbs();
-  }
-}
-
-void BreadcrumbPersistentStorageKeyedService::OldEventsRemoved(
-    BreadcrumbManager* manager) {
-  RewriteAllExistingBreadcrumbs();
-}
-
-void BreadcrumbPersistentStorageKeyedService::Shutdown() {
-  BreadcrumbManagerKeyedServiceFactory::GetForBrowserState(browser_state_)
-      ->RemoveObserver(this);
-}
diff --git a/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_keyed_service.h b/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_keyed_service.h
deleted file mode 100644
index b7e22e4..0000000
--- a/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_keyed_service.h
+++ /dev/null
@@ -1,102 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_CRASH_REPORT_BREADCRUMBS_BREADCRUMB_PERSISTENT_STORAGE_KEYED_SERVICE_H_
-#define IOS_CHROME_BROWSER_CRASH_REPORT_BREADCRUMBS_BREADCRUMB_PERSISTENT_STORAGE_KEYED_SERVICE_H_
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/callback.h"
-#include "base/files/file.h"
-#include "base/files/file_path.h"
-#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-#include "base/timer/timer.h"
-#include "components/keyed_service/core/keyed_service.h"
-#include "ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_observer.h"
-#include "ios/chrome/browser/crash_report/crash_reporter_breadcrumb_constants.h"
-
-namespace web {
-class BrowserState;
-}  // namespace web
-
-// The filesize for the file at |breadcrumbs_file_path_|. The file will always
-// be this constant size because it is accessed using a memory mapped file. The
-// file is twice as large as |kMaxBreadcrumbsDataLength| which leaves room for
-// appending breadcrumb events. Once the file is full of events, the contents
-// will be reduced to kMaxBreadcrumbsDataLength.
-constexpr size_t kPersistedFilesizeInBytes = kMaxBreadcrumbsDataLength * 2;
-
-// Saves and retrieves breadcrumb events to and from disk.
-class BreadcrumbPersistentStorageKeyedService
-    : public BreadcrumbManagerObserver,
-      public KeyedService {
- public:
-  // Creates an instance to save and retrieve breadcrumb events from the file at
-  // |file_path|. The file will be created if necessary.
-  //  explicit BreadcrumbPersistentStorageKeyedService(const base::FilePath&
-  //  file_path);
-  explicit BreadcrumbPersistentStorageKeyedService(
-      web::BrowserState* browser_state);
-  ~BreadcrumbPersistentStorageKeyedService() override;
-
-  // Returns the stored breadcrumb events from disk to |callback|. If called
-  // before |StartStoringEvents|, these events (if any) will be from the prior
-  // application session. After |StartStoringEvents| has been called, the
-  // returned events will be from the current application session.
-  void GetStoredEvents(
-      base::OnceCallback<void(std::vector<std::string>)> callback);
-
-  // Starts persisting breadcrumbs from the BreadcrumbManagerKeyedService
-  // associated with |browser_state_|. This will overwrite any breadcrumbs which
-  // may be stored from a previous application run.
-  void StartStoringEvents();
-
- private:
-  // Writes events from |observered_manager_| to |breadcrumbs_file_|,
-  // overwriting any existing persisted breadcrumbs.
-  void RewriteAllExistingBreadcrumbs();
-
-  // Writes breadcrumbs stored in |pending_breadcrumbs_| to |breadcrumbs_file_|.
-  void WritePendingBreadcrumbs();
-
-  // BreadcrumbManagerObserver
-  void EventAdded(BreadcrumbManager* manager,
-                  const std::string& event) override;
-  void OldEventsRemoved(BreadcrumbManager* manager) override;
-
-  // KeyedService overrides
-  void Shutdown() override;
-
-  // Individual beadcrumbs which have not yet been written to disk.
-  std::string pending_breadcrumbs_;
-
-  // The last time a breadcrumb was written to |breadcrumbs_file_|. This
-  // timestamp prevents breadcrumbs from being written to disk too often.
-  base::TimeTicks last_written_time_;
-
-  // A timer to delay writing to disk too often.
-  base::OneShotTimer write_timer_;
-
-  // The associated browser state.
-  web::BrowserState* browser_state_ = nullptr;
-
-  // The path to the file for storing persisted breadcrumbs.
-  base::FilePath breadcrumbs_file_path_;
-
-  // NOTE: Since this value represents the breadcrumbs written during this
-  // session, it will remain 0 until |StartStoringEvents| is called.
-  size_t current_mapped_file_position_ = 0;
-
-  // The SequencedTaskRunner on which File IO operations are performed.
-  scoped_refptr<base::SequencedTaskRunner> task_runner_;
-
-  base::WeakPtrFactory<BreadcrumbPersistentStorageKeyedService> weak_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(BreadcrumbPersistentStorageKeyedService);
-};
-
-#endif  // IOS_CHROME_BROWSER_CRASH_REPORT_BREADCRUMBS_BREADCRUMB_PERSISTENT_STORAGE_KEYED_SERVICE_H_
diff --git a/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_keyed_service_factory.cc b/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_keyed_service_factory.cc
deleted file mode 100644
index 1ea1406..0000000
--- a/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_keyed_service_factory.cc
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_keyed_service_factory.h"
-
-#include "components/keyed_service/ios/browser_state_dependency_manager.h"
-#include "ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_keyed_service.h"
-#include "ios/web/public/browser_state.h"
-
-// static
-BreadcrumbPersistentStorageKeyedServiceFactory*
-BreadcrumbPersistentStorageKeyedServiceFactory::GetInstance() {
-  static base::NoDestructor<BreadcrumbPersistentStorageKeyedServiceFactory>
-      instance;
-  return instance.get();
-}
-
-// static
-BreadcrumbPersistentStorageKeyedService*
-BreadcrumbPersistentStorageKeyedServiceFactory::GetForBrowserState(
-    web::BrowserState* browser_state) {
-  return static_cast<BreadcrumbPersistentStorageKeyedService*>(
-      GetInstance()->GetServiceForBrowserState(browser_state, true));
-}
-
-BreadcrumbPersistentStorageKeyedServiceFactory::
-    BreadcrumbPersistentStorageKeyedServiceFactory()
-    : BrowserStateKeyedServiceFactory(
-          "BreadcrumbPersistentStorageService",
-          BrowserStateDependencyManager::GetInstance()) {}
-
-BreadcrumbPersistentStorageKeyedServiceFactory::
-    ~BreadcrumbPersistentStorageKeyedServiceFactory() {}
-
-std::unique_ptr<KeyedService>
-BreadcrumbPersistentStorageKeyedServiceFactory::BuildServiceInstanceFor(
-    web::BrowserState* context) const {
-  return std::make_unique<BreadcrumbPersistentStorageKeyedService>(context);
-}
-
-web::BrowserState*
-BreadcrumbPersistentStorageKeyedServiceFactory::GetBrowserStateToUse(
-    web::BrowserState* context) const {
-  // Create the service for both normal and incognito browser states.
-  return context;
-}
diff --git a/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_keyed_service_factory.h b/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_keyed_service_factory.h
deleted file mode 100644
index a4045c3..0000000
--- a/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_keyed_service_factory.h
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_CRASH_REPORT_BREADCRUMBS_BREADCRUMB_PERSISTENT_STORAGE_KEYED_SERVICE_FACTORY_H_
-#define IOS_CHROME_BROWSER_CRASH_REPORT_BREADCRUMBS_BREADCRUMB_PERSISTENT_STORAGE_KEYED_SERVICE_FACTORY_H_
-
-#include "base/no_destructor.h"
-#include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
-
-class BreadcrumbPersistentStorageKeyedService;
-
-namespace web {
-class BrowserState;
-}  // namespace web
-
-class BreadcrumbPersistentStorageKeyedServiceFactory
-    : public BrowserStateKeyedServiceFactory {
- public:
-  static BreadcrumbPersistentStorageKeyedServiceFactory* GetInstance();
-  static BreadcrumbPersistentStorageKeyedService* GetForBrowserState(
-      web::BrowserState* browser_state);
-
- private:
-  friend class base::NoDestructor<
-      BreadcrumbPersistentStorageKeyedServiceFactory>;
-
-  BreadcrumbPersistentStorageKeyedServiceFactory();
-  ~BreadcrumbPersistentStorageKeyedServiceFactory() override;
-
-  // BrowserStateKeyedServiceFactory implementation.
-  std::unique_ptr<KeyedService> BuildServiceInstanceFor(
-      web::BrowserState* context) const override;
-  web::BrowserState* GetBrowserStateToUse(
-      web::BrowserState* context) const override;
-
-  BreadcrumbPersistentStorageKeyedServiceFactory(
-      const BreadcrumbPersistentStorageKeyedServiceFactory&) = delete;
-};
-
-#endif  // IOS_CHROME_BROWSER_CRASH_REPORT_BREADCRUMBS_BREADCRUMB_PERSISTENT_STORAGE_KEYED_SERVICE_FACTORY_H_
diff --git a/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_manager.h b/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_manager.h
new file mode 100644
index 0000000..a68270a
--- /dev/null
+++ b/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_manager.h
@@ -0,0 +1,106 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_CRASH_REPORT_BREADCRUMBS_BREADCRUMB_PERSISTENT_STORAGE_MANAGER_H_
+#define IOS_CHROME_BROWSER_CRASH_REPORT_BREADCRUMBS_BREADCRUMB_PERSISTENT_STORAGE_MANAGER_H_
+
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/timer/timer.h"
+#include "ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_observer.h"
+#include "ios/chrome/browser/crash_report/crash_reporter_breadcrumb_constants.h"
+
+// The filesize for the file at |breadcrumbs_file_path_|. The file will always
+// be this constant size because it is accessed using a memory mapped file. The
+// file is twice as large as |kMaxBreadcrumbsDataLength| which leaves room for
+// appending breadcrumb events. Once the file is full of events, the contents
+// will be reduced to kMaxBreadcrumbsDataLength.
+constexpr size_t kPersistedFilesizeInBytes = kMaxBreadcrumbsDataLength * 2;
+
+namespace base {
+class FilePath;
+}  // namespace base
+
+class BreadcrumbManagerKeyedService;
+
+// Stores breadcrumb events to and retireves them from a file on disk.
+// Persisting these events allows access to breadcrumb events from previous
+// application sessions.
+class BreadcrumbPersistentStorageManager : public BreadcrumbManagerObserver {
+ public:
+  explicit BreadcrumbPersistentStorageManager(base::FilePath directory);
+  ~BreadcrumbPersistentStorageManager() override;
+
+  // Returns the stored breadcrumb events from disk to |callback|.
+  void GetStoredEvents(
+      base::OnceCallback<void(std::vector<std::string>)> callback);
+
+  // Starts observing |manager| for events. Existing events will be persisted
+  // immediately.
+  void MonitorBreadcrumbManager(BreadcrumbManager* manager);
+  // Starts observing |service| for events. Existing events will be persisted
+  // immediately.
+  void MonitorBreadcrumbManagerService(BreadcrumbManagerKeyedService* service);
+
+  // Stops observing |manager|.
+  void StopMonitoringBreadcrumbManager(BreadcrumbManager* manager);
+  // Stops observing |service|.
+  void StopMonitoringBreadcrumbManagerService(
+      BreadcrumbManagerKeyedService* service);
+
+ private:
+  // Writes |pending_breadcrumbs_| to |breadcrumbs_file_| if it fits, otherwise
+  // rewrites the file. NOTE: Writing may be delayed if the file has recently
+  // been written into.
+  void WriteEvents();
+
+  // Writes events from |observered_manager_| to |breadcrumbs_file_|,
+  // overwriting any existing persisted breadcrumbs.
+  void RewriteAllExistingBreadcrumbs();
+
+  // Writes breadcrumbs stored in |pending_breadcrumbs_| to |breadcrumbs_file_|.
+  void WritePendingBreadcrumbs();
+
+  // Writes |event| to |breadcrumbs_file_|.
+  // NOTE: Writing may be delayed if the file has recently been written into.
+  void WriteEvent(const std::string& event);
+
+  // BreadcrumbManagerObserver
+  void EventAdded(BreadcrumbManager* manager,
+                  const std::string& event) override;
+  void OldEventsRemoved(BreadcrumbManager* manager) override;
+
+  // Individual beadcrumbs which have not yet been written to disk.
+  std::string pending_breadcrumbs_;
+
+  // The last time a breadcrumb was written to |breadcrumbs_file_|. This
+  // timestamp prevents breadcrumbs from being written to disk too often.
+  base::TimeTicks last_written_time_;
+
+  // A timer to delay writing to disk too often.
+  base::OneShotTimer write_timer_;
+
+  // The path to the file for storing persisted breadcrumbs.
+  base::FilePath breadcrumbs_file_path_;
+
+  // The path to the temporary file for writing persisted breadcrumbs.
+  base::FilePath breadcrumbs_temp_file_path_;
+
+  // NOTE: Since this value represents the breadcrumbs written during this
+  // session, it will remain 0 until |StartStoringEvents| is called.
+  size_t current_mapped_file_position_ = 0;
+
+  // The SequencedTaskRunner on which File IO operations are performed.
+  scoped_refptr<base::SequencedTaskRunner> task_runner_;
+
+  BreadcrumbPersistentStorageManager(
+      const BreadcrumbPersistentStorageManager&) = delete;
+  BreadcrumbPersistentStorageManager& operator=(
+      const BreadcrumbPersistentStorageManager&) = delete;
+};
+
+#endif  // IOS_CHROME_BROWSER_CRASH_REPORT_BREADCRUMBS_BREADCRUMB_PERSISTENT_STORAGE_MANAGER_H_
diff --git a/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_manager.mm b/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_manager.mm
new file mode 100644
index 0000000..61fafa8
--- /dev/null
+++ b/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_manager.mm
@@ -0,0 +1,276 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_manager.h"
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/files/file_util.h"
+#include "base/files/memory_mapped_file.h"
+#include "base/sequenced_task_runner.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "base/task/post_task.h"
+#include "base/task/thread_pool.h"
+#include "ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager.h"
+#include "ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_keyed_service.h"
+#include "ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_util.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace {
+
+const char kEventSeparator[] = "\n";
+
+// Minimum time between breadcrumb writes to disk.
+constexpr auto kMinDelayBetweenWrites = base::TimeDelta::FromMilliseconds(250);
+
+// Writes |events| to |file_path| at |position|.
+void DoInsertEventsIntoMemoryMappedFile(const base::FilePath& file_path,
+                                        const size_t position,
+                                        const std::string& events) {
+  auto file = std::make_unique<base::MemoryMappedFile>();
+  const base::MemoryMappedFile::Region region = {0, kPersistedFilesizeInBytes};
+  const bool file_valid = file->Initialize(
+      base::File(file_path, base::File::FLAG_OPEN_ALWAYS |
+                                base::File::FLAG_READ | base::File::FLAG_WRITE),
+      region, base::MemoryMappedFile::READ_WRITE_EXTEND);
+
+  if (file_valid) {
+    char* data = reinterpret_cast<char*>(file->data());
+    std::strcpy(&data[position], events.data());
+  }
+}
+
+// Writes |events| to |file_path| overwriting any existing data.
+void DoWriteEventsToFile(const base::FilePath& file_path,
+                         const std::string& events) {
+  const base::MemoryMappedFile::Region region = {0, kPersistedFilesizeInBytes};
+  base::MemoryMappedFile file;
+  const bool file_valid = file.Initialize(
+      base::File(file_path, base::File::FLAG_CREATE_ALWAYS |
+                                base::File::FLAG_READ | base::File::FLAG_WRITE),
+      region, base::MemoryMappedFile::READ_WRITE_EXTEND);
+
+  if (file_valid) {
+    char* data = reinterpret_cast<char*>(file.data());
+    std::strcpy(data, events.data());
+  }
+}
+
+void DoReplaceFile(const base::FilePath& from_path,
+                   const base::FilePath& to_path) {
+  base::ReplaceFile(from_path, to_path, nullptr);
+}
+
+// Returns breadcrumb events stored at |file_path|.
+std::vector<std::string> DoGetStoredEvents(const base::FilePath& file_path) {
+  base::File events_file(file_path,
+                         base::File::FLAG_OPEN | base::File::FLAG_READ);
+  if (!events_file.IsValid()) {
+    // File may not yet exist.
+    return std::vector<std::string>();
+  }
+
+  size_t file_size = events_file.GetLength();
+  if (file_size <= 0) {
+    return std::vector<std::string>();
+  }
+
+  // Do not read more than |kPersistedFilesizeInBytes|, in case the file was
+  // corrupted. If |kPersistedFilesizeInBytes| has been reduced since the last
+  // breadcrumbs file was saved, this could result in a one time loss of the
+  // oldest breadcrumbs which is ok because the decision has already been made
+  // to reduce the size of the stored breadcrumbs.
+  if (file_size > kPersistedFilesizeInBytes) {
+    file_size = kPersistedFilesizeInBytes;
+  }
+
+  std::vector<uint8_t> data;
+  data.resize(file_size);
+  if (!events_file.ReadAndCheck(/*offset=*/0, data)) {
+    return std::vector<std::string>();
+  }
+  std::string persisted_events(data.begin(), data.end());
+  std::string all_events =
+      persisted_events.substr(/*pos=*/0, strlen(persisted_events.c_str()));
+  return base::SplitString(all_events, kEventSeparator, base::TRIM_WHITESPACE,
+                           base::SPLIT_WANT_NONEMPTY);
+}
+
+}  // namespace
+
+using breadcrumb_persistent_storage_util::
+    GetBreadcrumbPersistentStorageFilePath;
+using breadcrumb_persistent_storage_util::
+    GetBreadcrumbPersistentStorageTempFilePath;
+
+BreadcrumbPersistentStorageManager::BreadcrumbPersistentStorageManager(
+    base::FilePath directory)
+    :  // Ensure first event will not be delayed by initializing with a time in
+       // the past.
+      last_written_time_(base::TimeTicks::Now() - kMinDelayBetweenWrites),
+      breadcrumbs_file_path_(GetBreadcrumbPersistentStorageFilePath(directory)),
+      breadcrumbs_temp_file_path_(
+          GetBreadcrumbPersistentStorageTempFilePath(directory)),
+      task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
+          {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
+           base::TaskShutdownBehavior::BLOCK_SHUTDOWN})) {}
+
+BreadcrumbPersistentStorageManager::~BreadcrumbPersistentStorageManager() =
+    default;
+
+void BreadcrumbPersistentStorageManager::GetStoredEvents(
+    base::OnceCallback<void(std::vector<std::string>)> callback) {
+  task_runner_->PostTaskAndReplyWithResult(
+      FROM_HERE, base::BindOnce(&DoGetStoredEvents, breadcrumbs_file_path_),
+      std::move(callback));
+}
+
+void BreadcrumbPersistentStorageManager::MonitorBreadcrumbManager(
+    BreadcrumbManager* manager) {
+  // Write already existing events.
+  std::list<std::string> events = manager->GetEvents(/*event_count_limit=*/0);
+  for (auto event : events) {
+    WriteEvent(event);
+  }
+
+  manager->AddObserver(this);
+}
+
+void BreadcrumbPersistentStorageManager::MonitorBreadcrumbManagerService(
+    BreadcrumbManagerKeyedService* service) {
+  // Write already existing events.
+  std::list<std::string> events = service->GetEvents(/*event_count_limit=*/0);
+  for (auto event : events) {
+    WriteEvent(event);
+  }
+
+  service->AddObserver(this);
+}
+
+void BreadcrumbPersistentStorageManager::StopMonitoringBreadcrumbManager(
+    BreadcrumbManager* manager) {
+  manager->RemoveObserver(this);
+}
+
+void BreadcrumbPersistentStorageManager::StopMonitoringBreadcrumbManagerService(
+    BreadcrumbManagerKeyedService* service) {
+  service->RemoveObserver(this);
+}
+
+void BreadcrumbPersistentStorageManager::RewriteAllExistingBreadcrumbs() {
+  // Collect breadcrumbs which haven't been written yet to include in this full
+  // re-write.
+  std::vector<std::string> pending_breadcrumbs =
+      base::SplitString(pending_breadcrumbs_, kEventSeparator,
+                        base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+  pending_breadcrumbs_.clear();
+  write_timer_.Stop();
+
+  last_written_time_ = base::TimeTicks::Now();
+  current_mapped_file_position_ = 0;
+
+  // Load persisted events directly from file because the correct order can not
+  // be reconstructed from the multiple BreadcrumbManagers with the partial
+  // timestamps embedded in each event.
+  GetStoredEvents(base::BindOnce(^(std::vector<std::string> events) {
+    // Add events which had not yet been written.
+    for (auto event : pending_breadcrumbs) {
+      events.push_back(event);
+    }
+
+    std::vector<std::string> breadcrumbs;
+    for (auto event_it = events.rbegin(); event_it != events.rend();
+         ++event_it) {
+      // Reduce saved events to only fill the amount which would be included on
+      // a crash log. This allows future events to be appended individually up
+      // to |kPersistedFilesizeInBytes|, which is more efficient than writing
+      // out the
+      const int event_with_seperator_size =
+          event_it->size() + strlen(kEventSeparator);
+      if (event_with_seperator_size + current_mapped_file_position_ >=
+          kMaxBreadcrumbsDataLength) {
+        break;
+      }
+
+      breadcrumbs.push_back(kEventSeparator);
+      breadcrumbs.push_back(*event_it);
+      current_mapped_file_position_ += event_with_seperator_size;
+    }
+
+    std::reverse(breadcrumbs.begin(), breadcrumbs.end());
+    std::string breadcrumbs_string = base::JoinString(breadcrumbs, "");
+
+    task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce(&DoWriteEventsToFile, breadcrumbs_temp_file_path_,
+                       std::string(breadcrumbs_string)));
+
+    task_runner_->PostTask(
+        FROM_HERE, base::BindOnce(&DoReplaceFile, breadcrumbs_temp_file_path_,
+                                  breadcrumbs_file_path_));
+  }));
+}
+
+void BreadcrumbPersistentStorageManager::WritePendingBreadcrumbs() {
+  if (pending_breadcrumbs_.empty()) {
+    return;
+  }
+
+  task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&DoInsertEventsIntoMemoryMappedFile,
+                     breadcrumbs_file_path_, current_mapped_file_position_,
+                     std::string(pending_breadcrumbs_)));
+
+  current_mapped_file_position_ += pending_breadcrumbs_.size();
+  last_written_time_ = base::TimeTicks::Now();
+
+  pending_breadcrumbs_.clear();
+}
+
+void BreadcrumbPersistentStorageManager::EventAdded(BreadcrumbManager* manager,
+                                                    const std::string& event) {
+  WriteEvent(event);
+}
+
+void BreadcrumbPersistentStorageManager::WriteEvent(const std::string& event) {
+  pending_breadcrumbs_ += event + kEventSeparator;
+
+  WriteEvents();
+}
+
+void BreadcrumbPersistentStorageManager::WriteEvents() {
+  write_timer_.Stop();
+
+  const base::TimeDelta time_delta_since_last_write =
+      base::TimeTicks::Now() - last_written_time_;
+  // Delay writing the event to disk if an event was just written.
+  if (time_delta_since_last_write < kMinDelayBetweenWrites) {
+    write_timer_.Start(FROM_HERE,
+                       kMinDelayBetweenWrites - time_delta_since_last_write,
+                       this, &BreadcrumbPersistentStorageManager::WriteEvents);
+  } else {
+    // If the event does not fit within |kPersistedFilesizeInBytes|, rewrite the
+    // file to trim old events.
+    if ((current_mapped_file_position_ + pending_breadcrumbs_.size())
+        // Use >= here instead of > to allow space for \0 to terminate file.
+        >= kPersistedFilesizeInBytes) {
+      RewriteAllExistingBreadcrumbs();
+      return;
+    }
+
+    // Otherwise, simply append the pending breadcrumbs.
+    WritePendingBreadcrumbs();
+  }
+}
+
+void BreadcrumbPersistentStorageManager::OldEventsRemoved(
+    BreadcrumbManager* manager) {
+  RewriteAllExistingBreadcrumbs();
+}
diff --git a/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_keyed_service_unittest.mm b/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_manager_unittest.mm
similarity index 83%
rename from ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_keyed_service_unittest.mm
rename to ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_manager_unittest.mm
index 3b7b81b..1ba6304 100644
--- a/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_keyed_service_unittest.mm
+++ b/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_manager_unittest.mm
@@ -1,8 +1,8 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_keyed_service.h"
+#import "ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_manager.h"
 
 #include <string>
 #include <vector>
@@ -17,7 +17,6 @@
 #include "ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager.h"
 #include "ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_keyed_service.h"
 #include "ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_keyed_service_factory.h"
-#include "ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_keyed_service_factory.h"
 #include "ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_util.h"
 #import "ios/chrome/browser/crash_report/crash_reporter_breadcrumb_observer.h"
 #include "ios/chrome/test/ios_chrome_scoped_testing_chrome_browser_state_manager.h"
@@ -42,13 +41,6 @@
   return std::make_unique<BreadcrumbManagerKeyedService>(browser_state);
 }
 
-// Creates a new BreadcrumbPersistentStorageKeyedService for |browser_state|.
-std::unique_ptr<KeyedService> BuildBreadcrumbPersistentStorageKeyedService(
-    web::BrowserState* browser_state) {
-  return std::make_unique<BreadcrumbPersistentStorageKeyedService>(
-      browser_state);
-}
-
 // Validates that the events in |persisted_events| are contiguous and that the
 // |last_logged_event| matches the last persisted event.
 bool ValidatePersistedEvents(std::string last_logged_event,
@@ -87,9 +79,9 @@
 
 }  // namespace
 
-class BreadcrumbPersistentStorageKeyedServiceTest : public PlatformTest {
+class BreadcrumbPersistentStorageManagerTest : public PlatformTest {
  protected:
-  BreadcrumbPersistentStorageKeyedServiceTest()
+  BreadcrumbPersistentStorageManagerTest()
       : scoped_browser_state_manager_(
             std::make_unique<TestChromeBrowserStateManager>(base::FilePath())) {
     EXPECT_TRUE(scoped_temp_directory_.CreateUniqueTempDir());
@@ -100,19 +92,19 @@
     test_cbs_builder.AddTestingFactory(
         BreadcrumbManagerKeyedServiceFactory::GetInstance(),
         base::BindRepeating(&BuildBreadcrumbManagerKeyedService));
-    test_cbs_builder.AddTestingFactory(
-        BreadcrumbPersistentStorageKeyedServiceFactory::GetInstance(),
-        base::BindRepeating(&BuildBreadcrumbPersistentStorageKeyedService));
     chrome_browser_state_ = test_cbs_builder.Build();
 
     breadcrumb_manager_service_ = static_cast<BreadcrumbManagerKeyedService*>(
         BreadcrumbManagerKeyedServiceFactory::GetForBrowserState(
             chrome_browser_state_.get()));
-    persistent_storage_ = static_cast<BreadcrumbPersistentStorageKeyedService*>(
-        BreadcrumbPersistentStorageKeyedServiceFactory::GetForBrowserState(
-            chrome_browser_state_.get()));
+    persistent_storage_ =
+        std::make_unique<BreadcrumbPersistentStorageManager>(directory_name);
+    breadcrumb_manager_service_->StartPersisting(persistent_storage_.get());
   }
-  ~BreadcrumbPersistentStorageKeyedServiceTest() override = default;
+
+  ~BreadcrumbPersistentStorageManagerTest() override {
+    breadcrumb_manager_service_->StopPersisting();
+  }
 
   web::WebTaskEnvironment task_env_{
       web::WebTaskEnvironment::Options::DEFAULT,
@@ -121,13 +113,15 @@
   std::unique_ptr<TestChromeBrowserState> chrome_browser_state_;
   base::ScopedTempDir scoped_temp_directory_;
   BreadcrumbManagerKeyedService* breadcrumb_manager_service_;
-  BreadcrumbPersistentStorageKeyedService* persistent_storage_;
+  std::unique_ptr<BreadcrumbPersistentStorageManager> persistent_storage_;
 };
 
-// Ensures that logged events are persisted.
-TEST_F(BreadcrumbPersistentStorageKeyedServiceTest, PersistEvents) {
-  persistent_storage_->StartStoringEvents();
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
 
+// Ensures that logged events are persisted.
+TEST_F(BreadcrumbPersistentStorageManagerTest, PersistEvents) {
   breadcrumb_manager_service_->AddEvent("event");
 
   // Advance clock to trigger writing final events.
@@ -149,9 +143,7 @@
 
 // Ensures that persisted events do not grow too large for a single large event
 // bucket when events are logged very quickly one after the other.
-TEST_F(BreadcrumbPersistentStorageKeyedServiceTest, PersistLargeBucket) {
-  persistent_storage_->StartStoringEvents();
-
+TEST_F(BreadcrumbPersistentStorageManagerTest, PersistLargeBucket) {
   std::string event;
   unsigned long event_count = 0;
   while (event_count < kEventCountTooManyForPersisting) {
@@ -182,9 +174,7 @@
 
 // Ensures that persisted events do not grow too large for events logged a few
 // seconds apart from each other.
-TEST_F(BreadcrumbPersistentStorageKeyedServiceTest, PersistManyEventsOverTime) {
-  persistent_storage_->StartStoringEvents();
-
+TEST_F(BreadcrumbPersistentStorageManagerTest, PersistManyEventsOverTime) {
   std::string event;
   unsigned long event_count = 0;
   while (event_count < kEventCountTooManyForPersisting) {
@@ -217,10 +207,8 @@
 
 // Ensures that old events are removed from the persisted file when old buckets
 // are dropped.
-TEST_F(BreadcrumbPersistentStorageKeyedServiceTest,
+TEST_F(BreadcrumbPersistentStorageManagerTest,
        OldEventsRemovedFromPersistedFile) {
-  persistent_storage_->StartStoringEvents();
-
   std::string event;
   unsigned long event_counter = 0;
   const int kNumEventsPerBucket = 200;
@@ -259,12 +247,12 @@
 }
 
 // Ensures that events are read correctly if the persisted file becomes
-// corrupted by losing the EOF token or if kPersistedFilesizeInBytes is reduced.
-TEST_F(BreadcrumbPersistentStorageKeyedServiceTest,
+// corrupted by losing the EOF token or if kPersistedFilesizeInBytes is
+// reduced.
+TEST_F(BreadcrumbPersistentStorageManagerTest,
        GetStoredEventsAfterFilesizeReduction) {
   const base::FilePath breadcrumbs_file_path =
-      breadcrumb_persistent_storage_util::
-          GetBreadcrumbPersistentStorageFilePath(chrome_browser_state_.get());
+      GetBreadcrumbPersistentStorageFilePath(scoped_temp_directory_.GetPath());
 
   auto file = std::make_unique<base::File>(
       breadcrumbs_file_path,
diff --git a/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_util.cc b/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_util.cc
index 76187090..32dc9e0d 100644
--- a/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_util.cc
+++ b/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_util.cc
@@ -5,7 +5,6 @@
 #include "ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_util.h"
 
 #include "base/path_service.h"
-#include "ios/web/public/browser_state.h"
 
 namespace breadcrumb_persistent_storage_util {
 
@@ -16,13 +15,13 @@
     FILE_PATH_LITERAL("iOS Breadcrumbs.temp");
 
 base::FilePath GetBreadcrumbPersistentStorageFilePath(
-    web::BrowserState* browser_state) {
-  return browser_state->GetStatePath().Append(kBreadcrumbsFile);
+    base::FilePath storage_dir) {
+  return storage_dir.Append(kBreadcrumbsFile);
 }
 
 base::FilePath GetBreadcrumbPersistentStorageTempFilePath(
-    web::BrowserState* browser_state) {
-  return browser_state->GetStatePath().Append(kBreadcrumbsTempFile);
+    base::FilePath storage_dir) {
+  return storage_dir.Append(kBreadcrumbsTempFile);
 }
 
 }  // namespace breadcrumb_persistent_storage_util
diff --git a/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_util.h b/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_util.h
index ce374ca0..e7ca6c62 100644
--- a/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_util.h
+++ b/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_util.h
@@ -7,27 +7,21 @@
 
 #include "base/files/file_path.h"
 
-namespace web {
-class BrowserState;
-}  // namespace web
-
 namespace breadcrumb_persistent_storage_util {
 
-// Returns the path to a file for storing breadcrumbs within |browser_state|'s
-// storage directory.
+// Returns the path to a file for storing breadcrumbs within |storage_dir|.
 base::FilePath GetBreadcrumbPersistentStorageFilePath(
-    web::BrowserState* browser_state);
+    base::FilePath storage_dir);
 
-// Returns the path to a file for storing breadcrumbs within |browser_state|'s
-// storage directory. This second file is used to write the new breadcrumbs to
-// so that the primary breadcrumbs file at
-// |GetBreadcrumbPersistentStorageFilePath()| is always in a state correctly
-// describing the application. (If the contents of a single file was instead
-// cleared and re-written, the most recent breadcrumbs would be missing if the
-// application crashed during this timeframe which will happen often whenever
-// old breadcrumbs are removed.)
+// Returns the path to a file for storing breadcrumbs within ||storage_dir||.
+// This second file is used to write the new breadcrumbs to so that the primary
+// breadcrumbs file at |GetBreadcrumbPersistentStorageFilePath()| is always in a
+// state correctly describing the application. (If the contents of a single file
+// was instead cleared and re-written, the most recent breadcrumbs would be
+// missing if the application crashed during this timeframe which will happen
+// often whenever old breadcrumbs are removed.)
 base::FilePath GetBreadcrumbPersistentStorageTempFilePath(
-    web::BrowserState* browser_state);
+    base::FilePath storage_dir);
 
 }  // namespace breadcrumb_persistent_storage_util
 
diff --git a/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_util_unittest.mm b/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_util_unittest.mm
index cbbf8ab..a144a07 100644
--- a/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_util_unittest.mm
+++ b/ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_util_unittest.mm
@@ -4,9 +4,8 @@
 
 #include "ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_util.h"
 
-#include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
-#include "ios/chrome/browser/web/chrome_web_test.h"
-#include "ios/web/public/browser_state.h"
+#include "base/files/scoped_temp_dir.h"
+#include "testing/platform_test.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -14,49 +13,19 @@
 
 using breadcrumb_persistent_storage_util::
     GetBreadcrumbPersistentStorageFilePath;
-
 using breadcrumb_persistent_storage_util::
     GetBreadcrumbPersistentStorageTempFilePath;
 
 // Test fixture to test BreadcrumbPersistentStorageUtil.
-typedef ChromeWebTest BreadcrumbPersistentStorageUtilTest;
+typedef PlatformTest BreadcrumbPersistentStorageUtilTest;
 
-// Tests that the storage paths are unique for different BrowserStates.
-TEST_F(BreadcrumbPersistentStorageUtilTest, UniqueStorage) {
-  base::FilePath path1 =
-      GetBreadcrumbPersistentStorageFilePath(GetBrowserState());
-  base::FilePath tempFilePath1 =
-      GetBreadcrumbPersistentStorageTempFilePath(GetBrowserState());
-  // Verify that the temp file path is different.
-  EXPECT_NE(path1, tempFilePath1);
+// Tests that the breadcrumb storage file path is different from the temp file
+// path.
+TEST_F(BreadcrumbPersistentStorageUtilTest, UniqueTempStorage) {
+  base::ScopedTempDir scoped_temp_directory;
+  EXPECT_TRUE(scoped_temp_directory.CreateUniqueTempDir());
 
-  std::unique_ptr<TestChromeBrowserState> local_browser_state =
-      TestChromeBrowserState::Builder().Build();
-  base::FilePath path2 =
-      GetBreadcrumbPersistentStorageFilePath(local_browser_state.get());
-  base::FilePath tempFilePath2 =
-      GetBreadcrumbPersistentStorageTempFilePath(local_browser_state.get());
-
-  EXPECT_NE(path1, path2);
-  EXPECT_NE(tempFilePath1, tempFilePath2);
-}
-
-// Tests that the BrowserState returned by
-// |BrowserState::GetOffTheRecordChromeBrowserState| does not share a storage
-// path with the original BrowserState.
-TEST_F(BreadcrumbPersistentStorageUtilTest, UniqueIncognitoStorage) {
-  base::FilePath path1 =
-      GetBreadcrumbPersistentStorageFilePath(GetBrowserState());
-  base::FilePath tempFilePath1 =
-      GetBreadcrumbPersistentStorageTempFilePath(GetBrowserState());
-
-  ChromeBrowserState* off_the_record_browser_state =
-      chrome_browser_state_->GetOffTheRecordChromeBrowserState();
-  base::FilePath path2 =
-      GetBreadcrumbPersistentStorageFilePath(off_the_record_browser_state);
-  base::FilePath tempFilePath2 =
-      GetBreadcrumbPersistentStorageTempFilePath(off_the_record_browser_state);
-
-  EXPECT_NE(path1, path2);
-  EXPECT_NE(tempFilePath1, tempFilePath2);
+  base::FilePath directory = scoped_temp_directory.GetPath();
+  EXPECT_NE(GetBreadcrumbPersistentStorageFilePath(directory),
+            GetBreadcrumbPersistentStorageTempFilePath(directory));
 }
diff --git a/ios/chrome/browser/sync/profile_sync_service_factory.cc b/ios/chrome/browser/sync/profile_sync_service_factory.cc
index 0d0aa87..176f390 100644
--- a/ios/chrome/browser/sync/profile_sync_service_factory.cc
+++ b/ios/chrome/browser/sync/profile_sync_service_factory.cc
@@ -7,19 +7,15 @@
 #include <utility>
 
 #include "base/bind.h"
-#include "base/feature_list.h"
 #include "base/no_destructor.h"
 #include "base/task/post_task.h"
 #include "base/time/time.h"
 #include "components/autofill/core/browser/personal_data_manager.h"
-#include "components/autofill/core/common/autofill_features.h"
-#include "components/invalidation/impl/invalidation_switches.h"
 #include "components/invalidation/impl/profile_invalidation_provider.h"
 #include "components/keyed_service/ios/browser_state_dependency_manager.h"
 #include "components/network_time/network_time_tracker.h"
 #include "components/sync/base/sync_util.h"
 #include "components/sync/driver/profile_sync_service.h"
-#include "components/sync/driver/startup_controller.h"
 #include "components/sync/driver/sync_driver_switches.h"
 #include "components/sync/driver/sync_service.h"
 #include "ios/chrome/browser/application_context.h"
@@ -171,9 +167,6 @@
       GetApplicationContext()->GetNetworkConnectionTracker();
   init_params.channel = ::GetChannel();
   init_params.debug_identifier = browser_state->GetDebugName();
-  init_params.autofill_enable_account_wallet_storage =
-      base::FeatureList::IsEnabled(
-          autofill::features::kAutofillEnableAccountWalletStorage);
 
   auto* fcm_invalidation_provider =
       IOSChromeProfileInvalidationProviderFactory::GetForBrowserState(
diff --git a/ios/chrome/browser/ui/main/scene_controller.mm b/ios/chrome/browser/ui/main/scene_controller.mm
index 7ca7917d..8d8ccf7 100644
--- a/ios/chrome/browser/ui/main/scene_controller.mm
+++ b/ios/chrome/browser/ui/main/scene_controller.mm
@@ -32,6 +32,7 @@
 #include "ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_browser_agent.h"
 #include "ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_keyed_service.h"
 #include "ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager_keyed_service_factory.h"
+#include "ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_persistent_storage_manager.h"
 #include "ios/chrome/browser/crash_report/breadcrumbs/features.h"
 #include "ios/chrome/browser/crash_report/crash_keys_helper.h"
 #include "ios/chrome/browser/crash_report/crash_report_helper.h"
@@ -2363,10 +2364,14 @@
     [sceneController willDestroyIncognitoBrowserState];
   }
 
+  BreadcrumbPersistentStorageManager* persistentStorageManager = nullptr;
   if (base::FeatureList::IsEnabled(kLogBreadcrumbs)) {
-    breakpad::StopMonitoringBreadcrumbManagerService(
+    BreadcrumbManagerKeyedService* service =
         BreadcrumbManagerKeyedServiceFactory::GetForBrowserState(
-            mainBrowserState->GetOffTheRecordChromeBrowserState()));
+            mainBrowserState->GetOffTheRecordChromeBrowserState());
+    persistentStorageManager = service->GetPersistentStorageManager();
+    breakpad::StopMonitoringBreadcrumbManagerService(service);
+    service->StopPersisting();
   }
 
   // Record off-the-record metrics before detroying the BrowserState.
@@ -2387,9 +2392,14 @@
   }
 
   if (base::FeatureList::IsEnabled(kLogBreadcrumbs)) {
-    breakpad::MonitorBreadcrumbManagerService(
+    BreadcrumbManagerKeyedService* service =
         BreadcrumbManagerKeyedServiceFactory::GetForBrowserState(
-            mainBrowserState->GetOffTheRecordChromeBrowserState()));
+            mainBrowserState->GetOffTheRecordChromeBrowserState());
+
+    if (persistentStorageManager) {
+      service->StartPersisting(persistentStorageManager);
+    }
+    breakpad::MonitorBreadcrumbManagerService(service);
   }
 
   // This seems the best place to deem the destroying and rebuilding the
diff --git a/ios/chrome/browser/ui/settings/google_services/accounts_table_egtest.mm b/ios/chrome/browser/ui/settings/google_services/accounts_table_egtest.mm
index bcfa5e0..b58c41c 100644
--- a/ios/chrome/browser/ui/settings/google_services/accounts_table_egtest.mm
+++ b/ios/chrome/browser/ui/settings/google_services/accounts_table_egtest.mm
@@ -23,8 +23,8 @@
 #error "This file requires ARC support."
 #endif
 
-using chrome_test_util::AddAccountButton;
 using chrome_test_util::ButtonWithAccessibilityLabel;
+using chrome_test_util::ButtonWithAccessibilityLabelId;
 using chrome_test_util::PrimarySignInButton;
 using chrome_test_util::SettingsAccountButton;
 using chrome_test_util::SettingsDoneButton;
@@ -153,27 +153,6 @@
       performAction:grey_tap()];
 }
 
-// Tests that the Settings screen is displayed after the user signs in through
-// the add account flow from the account list screen entrypoint.
-- (void)testSSOAddAccount {
-  FakeChromeIdentity* fakeIdentity = [SigninEarlGrey fakeIdentity1];
-  [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
-
-  [ChromeEarlGreyUI openSettingsMenu];
-  [ChromeEarlGreyUI tapSettingsMenuButton:SettingsAccountButton()];
-  [[EarlGrey selectElementWithMatcher:AddAccountButton()]
-      performAction:grey_tap()];
-  [[EarlGrey
-      selectElementWithMatcher:grey_allOf(
-                                   ButtonWithAccessibilityLabel(@"Sign in"),
-                                   grey_sufficientlyVisible(), nil)]
-      performAction:grey_tap()];
-
-  [ChromeEarlGreyUI waitForAppToIdle];
-  [[EarlGrey selectElementWithMatcher:SettingsAccountButton()]
-      assertWithMatcher:grey_sufficientlyVisible()];
-}
-
 // Tests that the Account Settings screen is popped and the user signed out
 // when the account is removed.
 - (void)testSignOutOnRemoveAccount {
diff --git a/ios/chrome/browser/ui/settings/google_services/accounts_table_view_controller.mm b/ios/chrome/browser/ui/settings/google_services/accounts_table_view_controller.mm
index a3aa98f5..2485838 100644
--- a/ios/chrome/browser/ui/settings/google_services/accounts_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/google_services/accounts_table_view_controller.mm
@@ -389,16 +389,9 @@
 
 - (void)onEndBatchOfRefreshTokenStateChanges {
   [self reloadData];
-  if (self.authService->IsAuthenticated() ||
-      _authenticationOperationInProgress) {
-    // The signed out state might be temporary (e.g. account switch, ...).
-    // Don't pop this view based on intermediary values.
-    return;
-  }
-
-  [self dismissSelfAnimated:NO];
-
-  if (_dimissAccountDetailsViewControllerBlock) {
+  [self popViewIfSignedOut];
+  if (![self authService] -> IsAuthenticated() &&
+                                 _dimissAccountDetailsViewControllerBlock) {
     _dimissAccountDetailsViewControllerBlock(/*animated=*/YES);
     _dimissAccountDetailsViewControllerBlock = nil;
   }
@@ -425,12 +418,8 @@
 }
 
 - (void)handleDidAddAccount:(BOOL)success {
-  if (!success) {
-    return;
-  }
-
   [self handleAuthenticationOperationDidFinish];
-  if (_closeSettingsOnAddAccount) {
+  if (success && _closeSettingsOnAddAccount) {
     [self.dispatcher closeSettingsUI];
   }
 }
@@ -580,10 +569,23 @@
   }
 }
 
-// Finishes the authentication flow and dismisses the accounts view.
+// Sets |_authenticationOperationInProgress| to NO and pops this accounts
+// table view controller if the user is signed out.
 - (void)handleAuthenticationOperationDidFinish {
   DCHECK(_authenticationOperationInProgress);
   _authenticationOperationInProgress = NO;
+  [self popViewIfSignedOut];
+}
+
+- (void)popViewIfSignedOut {
+  if ([self authService] -> IsAuthenticated()) {
+    return;
+  }
+  if (_authenticationOperationInProgress) {
+    // The signed out state might be temporary (e.g. account switch, ...).
+    // Don't pop this view based on intermediary values.
+    return;
+  }
   [self dismissSelfAnimated:NO];
 }
 
diff --git a/ios/chrome/browser/ui/settings/sync/sync_encryption_passphrase_table_view_controller.mm b/ios/chrome/browser/ui/settings/sync/sync_encryption_passphrase_table_view_controller.mm
index d66cf595..41b1f7d8 100644
--- a/ios/chrome/browser/ui/settings/sync/sync_encryption_passphrase_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/sync/sync_encryption_passphrase_table_view_controller.mm
@@ -328,7 +328,6 @@
       [self hideDecryptionProgress];
     }
   } else {
-    service->GetUserSettings()->EnableEncryptEverything();
     service->GetUserSettings()->SetEncryptionPassphrase(passphrase);
   }
   [self reloadData];
diff --git a/ios/chrome/test/testing_application_context.h b/ios/chrome/test/testing_application_context.h
index 9b78e5e..ebdcd6b 100644
--- a/ios/chrome/test/testing_application_context.h
+++ b/ios/chrome/test/testing_application_context.h
@@ -63,6 +63,8 @@
   SafeBrowsingService* GetSafeBrowsingService() override;
   network::NetworkConnectionTracker* GetNetworkConnectionTracker() override;
   BrowserPolicyConnectorIOS* GetBrowserPolicyConnector() override;
+  BreadcrumbPersistentStorageManager* GetBreadcrumbPersistentStorageManager()
+      override;
 
  private:
   base::ThreadChecker thread_checker_;
diff --git a/ios/chrome/test/testing_application_context.mm b/ios/chrome/test/testing_application_context.mm
index b75a9db..0011950 100644
--- a/ios/chrome/test/testing_application_context.mm
+++ b/ios/chrome/test/testing_application_context.mm
@@ -213,3 +213,9 @@
   // unittesting and return a mock or fake here.
   return nullptr;
 }
+
+BreadcrumbPersistentStorageManager*
+TestingApplicationContext::GetBreadcrumbPersistentStorageManager() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  return nullptr;
+}
diff --git a/ios/public/provider/chrome/browser/signin/fake_chrome_identity_interaction_manager.mm b/ios/public/provider/chrome/browser/signin/fake_chrome_identity_interaction_manager.mm
index 331ada5..2b0272a 100644
--- a/ios/public/provider/chrome/browser/signin/fake_chrome_identity_interaction_manager.mm
+++ b/ios/public/provider/chrome/browser/signin/fake_chrome_identity_interaction_manager.mm
@@ -125,12 +125,8 @@
 }
 
 - (void)addAccountViewControllerDidTapSignIn {
-  // Fake sign-in is used to show a fake add an account screen. In this
-  // case _fakeIdentity will be nil.
-  if (_fakeIdentity) {
-    ios::FakeChromeIdentityService::GetInstanceFromChromeProvider()
-        ->AddIdentity(_fakeIdentity);
-  }
+  ios::FakeChromeIdentityService::GetInstanceFromChromeProvider()
+      ->AddIdentity(_fakeIdentity);
   [self dismissAndRunCompletionCallbackWithError:nil
                                         animated:YES
                                       completion:nil];
diff --git a/ios/web_view/internal/sync/web_view_profile_sync_service_factory.mm b/ios/web_view/internal/sync/web_view_profile_sync_service_factory.mm
index d5c3b0c..837daff 100644
--- a/ios/web_view/internal/sync/web_view_profile_sync_service_factory.mm
+++ b/ios/web_view/internal/sync/web_view_profile_sync_service_factory.mm
@@ -7,19 +7,15 @@
 #include <utility>
 
 #include "base/bind_helpers.h"
-#include "base/feature_list.h"
 #include "base/no_destructor.h"
 #include "base/time/time.h"
 #include "components/autofill/core/browser/personal_data_manager.h"
-#include "components/autofill/core/common/autofill_features.h"
 #include "components/invalidation/impl/profile_invalidation_provider.h"
 #include "components/keyed_service/ios/browser_state_dependency_manager.h"
-#include "components/password_manager/core/common/password_manager_features.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
 #include "components/sync/base/model_type.h"
 #include "components/sync/base/sync_util.h"
 #include "components/sync/driver/profile_sync_service.h"
-#include "components/sync/driver/startup_controller.h"
 #include "components/sync/driver/sync_service.h"
 #include "ios/web/public/thread/web_thread.h"
 #include "ios/web_view/internal/app/application_context.h"
@@ -103,11 +99,6 @@
       WebViewProfileInvalidationProviderFactory::GetForBrowserState(
           browser_state)
           ->GetIdentityProvider();
-  init_params.autofill_enable_account_wallet_storage =
-      base::FeatureList::IsEnabled(
-          autofill::features::kAutofillEnableAccountWalletStorage);
-  init_params.enable_passwords_account_storage = base::FeatureList::IsEnabled(
-      password_manager::features::kEnablePasswordsAccountStorage);
 
   auto profile_sync_service =
       std::make_unique<syncer::ProfileSyncService>(std::move(init_params));
diff --git a/media/renderers/paint_canvas_video_renderer.cc b/media/renderers/paint_canvas_video_renderer.cc
index c09fc76..a9fc4e8 100644
--- a/media/renderers/paint_canvas_video_renderer.cc
+++ b/media/renderers/paint_canvas_video_renderer.cc
@@ -254,10 +254,10 @@
   texture_info.fFormat = GL_RGBA8_OES;
   GrBackendTexture backend_texture(size.width(), size.height(),
                                    GrMipMapped::kNo, texture_info);
-  return SkImage::MakeFromTexture(
+  return SkImage::MakeFromAdoptedTexture(
       raster_context_provider->GrContext(), backend_texture,
       kTopLeft_GrSurfaceOrigin, kRGBA_8888_SkColorType, kPremul_SkAlphaType,
-      color_space.ToSkColorSpace(), nullptr, nullptr);
+      color_space.ToSkColorSpace());
 }
 
 void VideoFrameCopyTextureOrSubTexture(gpu::gles2::GLES2Interface* gl,
@@ -854,7 +854,7 @@
 
   const bool need_rotation = video_transformation.rotation != VIDEO_ROTATION_0;
   const bool need_scaling =
-      dest_rect.size() != gfx::SizeF(image.width(), image.height());
+      dest_rect.size() != gfx::SizeF(video_frame->visible_rect().size());
   const bool need_translation = !dest_rect.origin().IsOrigin();
   // TODO(tmathmeyer): apply horizontal / vertical mirroring if needed.
   bool need_transform = need_rotation || need_scaling || need_translation;
@@ -885,10 +885,13 @@
       rotated_dest_size =
           gfx::SizeF(rotated_dest_size.height(), rotated_dest_size.width());
     }
-    canvas->scale(SkFloatToScalar(rotated_dest_size.width() / image.width()),
-                  SkFloatToScalar(rotated_dest_size.height() / image.height()));
-    canvas->translate(-SkFloatToScalar(image.width() * 0.5f),
-                      -SkFloatToScalar(image.height() * 0.5f));
+    canvas->scale(SkFloatToScalar(rotated_dest_size.width() /
+                                  video_frame->visible_rect().width()),
+                  SkFloatToScalar(rotated_dest_size.height() /
+                                  video_frame->visible_rect().height()));
+    canvas->translate(
+        -SkFloatToScalar(video_frame->visible_rect().width() * 0.5f),
+        -SkFloatToScalar(video_frame->visible_rect().height() * 0.5f));
   }
 
   SkImageInfo info;
@@ -907,7 +910,15 @@
     const size_t offset = info.computeOffset(origin.x(), origin.y(), row_bytes);
     void* const pixels_offset = reinterpret_cast<char*>(pixels) + offset;
     ConvertVideoFrameToRGBPixels(video_frame.get(), pixels_offset, row_bytes);
+  } else if (video_frame->HasTextures()) {
+    DCHECK_EQ(video_frame->coded_size(),
+              gfx::Size(image.width(), image.height()));
+    canvas->drawImageRect(image, gfx::RectToSkRect(video_frame->visible_rect()),
+                          dest, &video_flags,
+                          SkCanvas::kStrict_SrcRectConstraint);
   } else {
+    DCHECK_EQ(video_frame->visible_rect().size(),
+              gfx::Size(image.width(), image.height()));
     canvas->drawImage(image, 0, 0, &video_flags);
   }
 
@@ -1636,8 +1647,6 @@
   DCHECK(!source_mailbox.IsZero());
   DCHECK(source_texture);
   auto* ri = raster_context_provider->RasterInterface();
-  if (!texture_ownership_in_skia)
-    ri->DeleteGpuRasterTexture(source_texture);
   if (!wraps_video_frame_texture) {
     gpu::SyncToken sync_token;
     ri->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData());
@@ -1647,9 +1656,7 @@
 }
 
 bool PaintCanvasVideoRenderer::Cache::Recycle() {
-  if (!texture_ownership_in_skia)
-    return true;
-
+  DCHECK(!wraps_video_frame_texture);
   if (!paint_image.HasExclusiveTextureAccess())
     return false;
 
@@ -1659,7 +1666,6 @@
   paint_image = cc::PaintImage();
   // We need a new texture ID because skia will destroy the previous one with
   // the SkImage.
-  texture_ownership_in_skia = false;
   source_texture = 0;
   return true;
 }
@@ -1705,7 +1711,6 @@
         } else {
           cache_.emplace(video_frame->unique_id());
           auto* sii = raster_context_provider->SharedImageInterface();
-
           // TODO(nazabris): Sort out what to do when GLES2 is needed but the
           // cached shared image is created without it.
           uint32_t flags =
@@ -1721,8 +1726,6 @@
           ri->WaitSyncTokenCHROMIUM(
               sii->GenUnverifiedSyncToken().GetConstData());
         }
-
-        DCHECK(!cache_->texture_ownership_in_skia);
         if (video_frame->NumTextures() == 1) {
           auto frame_mailbox =
               SynchronizeVideoFrameSingleMailbox(ri, video_frame.get());
@@ -1763,35 +1766,10 @@
       cache_->raster_context_provider = raster_context_provider;
       cache_->coded_size = video_frame->coded_size();
       cache_->visible_rect = video_frame->visible_rect();
-      GrDirectContext* direct =
-          GrAsDirectContext(raster_context_provider->GrContext());
-      sk_sp<SkImage> source_subset = source_image->makeSubset(
-          gfx::RectToSkIRect(cache_->visible_rect), direct);
-      if (source_subset) {
-        // We use the flushPendingGrContextIO = true so we can flush any pending
-        // GPU work on the GrContext to ensure that skia exectues the work for
-        // generating the subset and it can be safely destroyed.
-        GrBackendTexture image_backend =
-            source_image->getBackendTexture(/*flushPendingGrContextIO*/ true);
-        GrBackendTexture subset_backend =
-            source_subset->getBackendTexture(/*flushPendingGrContextIO*/ true);
-#if DCHECK_IS_ON()
-        GrGLTextureInfo backend_info;
-        if (image_backend.getGLTextureInfo(&backend_info))
-          DCHECK_EQ(backend_info.fID, cache_->source_texture);
-#endif
-        if (subset_backend.isValid() &&
-            subset_backend.isSameTexture(image_backend)) {
-          cache_->texture_ownership_in_skia = true;
-          source_subset = SkImage::MakeFromAdoptedTexture(
-              cache_->raster_context_provider->GrContext(), image_backend,
-              kTopLeft_GrSurfaceOrigin, kRGBA_8888_SkColorType,
-              kPremul_SkAlphaType, source_image->imageInfo().refColorSpace());
-        }
-      }
+
       paint_image_builder.set_texture_backing(
           sk_sp<VideoTextureBacking>(new VideoTextureBacking(
-              std::move(source_subset), raster_context_provider)),
+              std::move(source_image), raster_context_provider)),
           cc::PaintImage::GetNextContentId());
     } else {
       cache_.emplace(video_frame->unique_id());
diff --git a/media/renderers/paint_canvas_video_renderer.h b/media/renderers/paint_canvas_video_renderer.h
index 943cc37..a422e23 100644
--- a/media/renderers/paint_canvas_video_renderer.h
+++ b/media/renderers/paint_canvas_video_renderer.h
@@ -239,9 +239,6 @@
     // (if true), or to an allocated shared image (if false).
     bool wraps_video_frame_texture = false;
 
-    // Whether the texture pointed by |paint_image| is owned by skia or not.
-    bool texture_ownership_in_skia = false;
-
     // Used to allow recycling of the previous shared image. This requires that
     // no external users have access to this resource via SkImage. Returns true
     // if the existing resource can be recycled.
diff --git a/media/renderers/video_resource_updater.cc b/media/renderers/video_resource_updater.cc
index da01bca..558f840 100644
--- a/media/renderers/video_resource_updater.cc
+++ b/media/renderers/video_resource_updater.cc
@@ -528,23 +528,24 @@
   frame_resources_.clear();
 }
 
-void VideoResourceUpdater::AppendQuads(viz::CompositorRenderPass* render_pass,
-                                       scoped_refptr<VideoFrame> frame,
-                                       gfx::Transform transform,
-                                       gfx::Rect quad_rect,
-                                       gfx::Rect visible_quad_rect,
-                                       const gfx::RRectF& rounded_corner_bounds,
-                                       gfx::Rect clip_rect,
-                                       bool is_clipped,
-                                       bool contents_opaque,
-                                       float draw_opacity,
-                                       int sorting_context_id) {
+void VideoResourceUpdater::AppendQuads(
+    viz::CompositorRenderPass* render_pass,
+    scoped_refptr<VideoFrame> frame,
+    gfx::Transform transform,
+    gfx::Rect quad_rect,
+    gfx::Rect visible_quad_rect,
+    const gfx::MaskFilterInfo& mask_filter_info,
+    gfx::Rect clip_rect,
+    bool is_clipped,
+    bool contents_opaque,
+    float draw_opacity,
+    int sorting_context_id) {
   DCHECK(frame.get());
 
   viz::SharedQuadState* shared_quad_state =
       render_pass->CreateAndAppendSharedQuadState();
   shared_quad_state->SetAll(transform, quad_rect, visible_quad_rect,
-                            rounded_corner_bounds, clip_rect, is_clipped,
+                            mask_filter_info, clip_rect, is_clipped,
                             contents_opaque, draw_opacity,
                             SkBlendMode::kSrcOver, sorting_context_id);
 
diff --git a/media/renderers/video_resource_updater.h b/media/renderers/video_resource_updater.h
index ec732002..ab49f3c2 100644
--- a/media/renderers/video_resource_updater.h
+++ b/media/renderers/video_resource_updater.h
@@ -29,7 +29,6 @@
 
 namespace gfx {
 class Rect;
-class RRectF;
 class Transform;
 }  // namespace gfx
 
@@ -45,6 +44,10 @@
 class SharedBitmapReporter;
 }  // namespace viz
 
+namespace gfx {
+class MaskFilterInfo;
+}
+
 namespace media {
 class PaintCanvasVideoRenderer;
 class VideoFrame;
@@ -120,7 +123,7 @@
                    gfx::Transform transform,
                    gfx::Rect quad_rect,
                    gfx::Rect visible_quad_rect,
-                   const gfx::RRectF& rounded_corner_bounds,
+                   const gfx::MaskFilterInfo& mask_filter_info,
                    gfx::Rect clip_rect,
                    bool is_clipped,
                    bool context_opaque,
diff --git a/net/base/network_isolation_key.cc b/net/base/network_isolation_key.cc
index 175ee3a02..a621586 100644
--- a/net/base/network_isolation_key.cc
+++ b/net/base/network_isolation_key.cc
@@ -67,6 +67,9 @@
 NetworkIsolationKey::NetworkIsolationKey(
     const NetworkIsolationKey& network_isolation_key) = default;
 
+NetworkIsolationKey::NetworkIsolationKey(
+    NetworkIsolationKey&& network_isolation_key) = default;
+
 NetworkIsolationKey::~NetworkIsolationKey() = default;
 
 NetworkIsolationKey& NetworkIsolationKey::operator=(
diff --git a/net/base/network_isolation_key.h b/net/base/network_isolation_key.h
index 0f661f7..2304f5d5 100644
--- a/net/base/network_isolation_key.h
+++ b/net/base/network_isolation_key.h
@@ -41,6 +41,7 @@
   NetworkIsolationKey();
 
   NetworkIsolationKey(const NetworkIsolationKey& network_isolation_key);
+  NetworkIsolationKey(NetworkIsolationKey&& network_isolation_key);
 
   ~NetworkIsolationKey();
 
diff --git a/net/dns/context_host_resolver_unittest.cc b/net/dns/context_host_resolver_unittest.cc
index e50efce..3612768 100644
--- a/net/dns/context_host_resolver_unittest.cc
+++ b/net/dns/context_host_resolver_unittest.cc
@@ -96,7 +96,7 @@
 
   MockDnsClientRuleList rules;
   rules.emplace_back("example.com", dns_protocol::kTypeA, false /* secure */,
-                     MockDnsClientRule::Result(BuildTestDnsResponse(
+                     MockDnsClientRule::Result(BuildTestDnsAddressResponse(
                          "example.com", kEndpoint.address())),
                      false /* delay */, &context);
   rules.emplace_back("example.com", dns_protocol::kTypeAAAA, false /* secure */,
@@ -126,7 +126,7 @@
   // Set up delayed results for "example.com".
   MockDnsClientRuleList rules;
   rules.emplace_back("example.com", dns_protocol::kTypeA, false /* secure */,
-                     MockDnsClientRule::Result(BuildTestDnsResponse(
+                     MockDnsClientRule::Result(BuildTestDnsAddressResponse(
                          "example.com", IPAddress(1, 2, 3, 4))),
                      true /* delay */);
   rules.emplace_back("example.com", dns_protocol::kTypeAAAA, false /* secure */,
@@ -222,14 +222,14 @@
   // Set up delayed results for "example.com" and "google.com".
   MockDnsClientRuleList rules;
   rules.emplace_back("example.com", dns_protocol::kTypeA, false /* secure */,
-                     MockDnsClientRule::Result(BuildTestDnsResponse(
+                     MockDnsClientRule::Result(BuildTestDnsAddressResponse(
                          "example.com", IPAddress(2, 3, 4, 5))),
                      true /* delay */);
   rules.emplace_back("example.com", dns_protocol::kTypeAAAA, false /* secure */,
                      MockDnsClientRule::Result(MockDnsClientRule::EMPTY),
                      false /* delay */);
   rules.emplace_back("google.com", dns_protocol::kTypeA, false /* secure */,
-                     MockDnsClientRule::Result(BuildTestDnsResponse(
+                     MockDnsClientRule::Result(BuildTestDnsAddressResponse(
                          "google.com", kEndpoint.address())),
                      true /* delay */);
   rules.emplace_back("google.com", dns_protocol::kTypeAAAA, false /* secure */,
@@ -278,7 +278,7 @@
 TEST_F(ContextHostResolverTest, DestroyResolver_CompletedRequests) {
   MockDnsClientRuleList rules;
   rules.emplace_back("example.com", dns_protocol::kTypeA, false /* secure */,
-                     MockDnsClientRule::Result(BuildTestDnsResponse(
+                     MockDnsClientRule::Result(BuildTestDnsAddressResponse(
                          "example.com", kEndpoint.address())),
                      false /* delay */);
   rules.emplace_back("example.com", dns_protocol::kTypeAAAA, false /* secure */,
@@ -334,7 +334,7 @@
   // Set up delayed result for "example.com".
   MockDnsClientRuleList rules;
   rules.emplace_back("example.com", dns_protocol::kTypeA, false /* secure */,
-                     MockDnsClientRule::Result(BuildTestDnsResponse(
+                     MockDnsClientRule::Result(BuildTestDnsAddressResponse(
                          "example.com", IPAddress(2, 3, 4, 5))),
                      true /* delay */);
   rules.emplace_back("example.com", dns_protocol::kTypeAAAA, false /* secure */,
@@ -384,7 +384,7 @@
   // Set up delayed result for "example.com".
   MockDnsClientRuleList rules;
   rules.emplace_back("example.com", dns_protocol::kTypeA, false /* secure */,
-                     MockDnsClientRule::Result(BuildTestDnsResponse(
+                     MockDnsClientRule::Result(BuildTestDnsAddressResponse(
                          "example.com", IPAddress(2, 3, 4, 5))),
                      true /* delay */);
   rules.emplace_back("example.com", dns_protocol::kTypeAAAA, false /* secure */,
@@ -440,7 +440,7 @@
 TEST_F(ContextHostResolverTest, OnShutdown_CompletedRequests) {
   MockDnsClientRuleList rules;
   rules.emplace_back("example.com", dns_protocol::kTypeA, false /* secure */,
-                     MockDnsClientRule::Result(BuildTestDnsResponse(
+                     MockDnsClientRule::Result(BuildTestDnsAddressResponse(
                          "example.com", kEndpoint.address())),
                      false /* delay */);
   rules.emplace_back("example.com", dns_protocol::kTypeAAAA, false /* secure */,
@@ -526,7 +526,7 @@
   // Set up delayed result for "example.com".
   MockDnsClientRuleList rules;
   rules.emplace_back("example.com", dns_protocol::kTypeA, false /* secure */,
-                     MockDnsClientRule::Result(BuildTestDnsResponse(
+                     MockDnsClientRule::Result(BuildTestDnsAddressResponse(
                          "example.com", IPAddress(2, 3, 4, 5))),
                      true /* delay */);
   rules.emplace_back("example.com", dns_protocol::kTypeAAAA, false /* secure */,
@@ -621,7 +621,7 @@
 TEST_F(ContextHostResolverTest, ResultsAddedToCache) {
   MockDnsClientRuleList rules;
   rules.emplace_back("example.com", dns_protocol::kTypeA, false /* secure */,
-                     MockDnsClientRule::Result(BuildTestDnsResponse(
+                     MockDnsClientRule::Result(BuildTestDnsAddressResponse(
                          "example.com", kEndpoint.address())),
                      false /* delay */);
   rules.emplace_back("example.com", dns_protocol::kTypeAAAA, false /* secure */,
@@ -670,7 +670,7 @@
 
   MockDnsClientRuleList rules;
   rules.emplace_back("example.com", dns_protocol::kTypeA, false /* secure */,
-                     MockDnsClientRule::Result(BuildTestDnsResponse(
+                     MockDnsClientRule::Result(BuildTestDnsAddressResponse(
                          "example.com", kEndpoint.address())),
                      false /* delay */);
   rules.emplace_back("example.com", dns_protocol::kTypeAAAA, false /* secure */,
diff --git a/net/dns/dns_response.cc b/net/dns/dns_response.cc
index e34e0c8..f4a01c3 100644
--- a/net/dns/dns_response.cc
+++ b/net/dns/dns_response.cc
@@ -355,6 +355,9 @@
   memcpy(io_buffer_->data(), data, length);
 }
 
+DnsResponse::DnsResponse(DnsResponse&& other) = default;
+DnsResponse& DnsResponse::operator=(DnsResponse&& other) = default;
+
 DnsResponse::~DnsResponse() = default;
 
 bool DnsResponse::InitParse(size_t nbytes, const DnsQuery& query) {
diff --git a/net/dns/dns_response.h b/net/dns/dns_response.h
index 7eda8076..c6320ae 100644
--- a/net/dns/dns_response.h
+++ b/net/dns/dns_response.h
@@ -150,11 +150,16 @@
   // Constructs a response from |data|. Used for testing purposes only!
   DnsResponse(const void* data, size_t length, size_t answer_offset);
 
+  // Move-only.
+  DnsResponse(DnsResponse&& other);
+  DnsResponse& operator=(DnsResponse&& other);
+
   ~DnsResponse();
 
   // Internal buffer accessor into which actual bytes of response will be
   // read.
   IOBuffer* io_buffer() { return io_buffer_.get(); }
+  const IOBuffer* io_buffer() const { return io_buffer_.get(); }
 
   // Size of the internal buffer.
   size_t io_buffer_size() const { return io_buffer_size_; }
@@ -229,8 +234,6 @@
   // It is never updated afterwards, so can be used in accessors.
   DnsRecordParser parser_;
   bool id_available_ = false;
-
-  DISALLOW_COPY_AND_ASSIGN(DnsResponse);
 };
 
 }  // namespace net
diff --git a/net/dns/dns_test_util.cc b/net/dns/dns_test_util.cc
index 00ff9ea..e9ed2d7 100644
--- a/net/dns/dns_test_util.cc
+++ b/net/dns/dns_test_util.cc
@@ -38,8 +38,7 @@
 
 // Create a response containing a valid question (as would normally be validated
 // in DnsTransaction) but completely missing a header-declared answer.
-std::unique_ptr<DnsResponse> CreateMalformedResponse(std::string hostname,
-                                                     uint16_t type) {
+DnsResponse CreateMalformedResponse(std::string hostname, uint16_t type) {
   std::string dns_name;
   CHECK(DNSDomainFromDot(hostname, &dns_name));
   DnsQuery query(0x14 /* id */, dns_name, type);
@@ -53,8 +52,8 @@
   memcpy(buffer->data() + sizeof(kMalformedResponseHeader),
          query.question().data(), query.question().size());
 
-  auto response = std::make_unique<DnsResponse>(buffer, buffer->size());
-  CHECK(response->InitParseWithoutQuery(buffer->size()));
+  DnsResponse response(buffer, buffer->size());
+  CHECK(response.InitParseWithoutQuery(buffer->size()));
 
   return response;
 }
@@ -206,9 +205,7 @@
   return record;
 }
 
-
-std::unique_ptr<DnsResponse> BuildTestDnsResponse(std::string name,
-                                                  const IPAddress& ip) {
+DnsResponse BuildTestDnsAddressResponse(std::string name, const IPAddress& ip) {
   DCHECK(ip.IsValid());
 
   std::vector<DnsResourceRecord> answers = {BuildTestAddressRecord(name, ip)};
@@ -217,16 +214,15 @@
   base::Optional<DnsQuery> query(
       base::in_place, 0, dns_name,
       ip.IsIPv4() ? dns_protocol::kTypeA : dns_protocol::kTypeAAAA);
-  return std::make_unique<DnsResponse>(
-      0, false, std::move(answers),
-      std::vector<DnsResourceRecord>() /* authority_records */,
-      std::vector<DnsResourceRecord>() /* additional_records */, query);
+  return DnsResponse(0, false, std::move(answers),
+                     std::vector<DnsResourceRecord>() /* authority_records */,
+                     std::vector<DnsResourceRecord>() /* additional_records */,
+                     query);
 }
 
-std::unique_ptr<DnsResponse> BuildTestDnsResponseWithCname(
-    std::string name,
-    const IPAddress& ip,
-    std::string cannonname) {
+DnsResponse BuildTestDnsAddressResponseWithCname(std::string name,
+                                                 const IPAddress& ip,
+                                                 std::string cannonname) {
   DCHECK(ip.IsValid());
   DCHECK(!cannonname.empty());
 
@@ -238,13 +234,13 @@
   base::Optional<DnsQuery> query(
       base::in_place, 0, dns_name,
       ip.IsIPv4() ? dns_protocol::kTypeA : dns_protocol::kTypeAAAA);
-  return std::make_unique<DnsResponse>(
-      0, false, std::move(answers),
-      std::vector<DnsResourceRecord>() /* authority_records */,
-      std::vector<DnsResourceRecord>() /* additional_records */, query);
+  return DnsResponse(0, false, std::move(answers),
+                     std::vector<DnsResourceRecord>() /* authority_records */,
+                     std::vector<DnsResourceRecord>() /* additional_records */,
+                     query);
 }
 
-std::unique_ptr<DnsResponse> BuildTestDnsTextResponse(
+DnsResponse BuildTestDnsTextResponse(
     std::string name,
     std::vector<std::vector<std::string>> text_records,
     std::string answer_name) {
@@ -261,16 +257,15 @@
   base::Optional<DnsQuery> query(base::in_place, 0, dns_name,
                                  dns_protocol::kTypeTXT);
 
-  return std::make_unique<DnsResponse>(
-      0, false, std::move(answers),
-      std::vector<DnsResourceRecord>() /* authority_records */,
-      std::vector<DnsResourceRecord>() /* additional_records */, query);
+  return DnsResponse(0, false, std::move(answers),
+                     std::vector<DnsResourceRecord>() /* authority_records */,
+                     std::vector<DnsResourceRecord>() /* additional_records */,
+                     query);
 }
 
-std::unique_ptr<DnsResponse> BuildTestDnsPointerResponse(
-    std::string name,
-    std::vector<std::string> pointer_names,
-    std::string answer_name) {
+DnsResponse BuildTestDnsPointerResponse(std::string name,
+                                        std::vector<std::string> pointer_names,
+                                        std::string answer_name) {
   if (answer_name.empty())
     answer_name = name;
 
@@ -284,13 +279,13 @@
   base::Optional<DnsQuery> query(base::in_place, 0, dns_name,
                                  dns_protocol::kTypePTR);
 
-  return std::make_unique<DnsResponse>(
-      0, false, std::move(answers),
-      std::vector<DnsResourceRecord>() /* authority_records */,
-      std::vector<DnsResourceRecord>() /* additional_records */, query);
+  return DnsResponse(0, false, std::move(answers),
+                     std::vector<DnsResourceRecord>() /* authority_records */,
+                     std::vector<DnsResourceRecord>() /* additional_records */,
+                     query);
 }
 
-std::unique_ptr<DnsResponse> BuildTestDnsServiceResponse(
+DnsResponse BuildTestDnsServiceResponse(
     std::string name,
     std::vector<TestServiceRecord> service_records,
     std::string answer_name) {
@@ -308,13 +303,13 @@
   base::Optional<DnsQuery> query(base::in_place, 0, dns_name,
                                  dns_protocol::kTypeSRV);
 
-  return std::make_unique<DnsResponse>(
-      0, false, std::move(answers),
-      std::vector<DnsResourceRecord>() /* authority_records */,
-      std::vector<DnsResourceRecord>() /* additional_records */, query);
+  return DnsResponse(0, false, std::move(answers),
+                     std::vector<DnsResourceRecord>() /* authority_records */,
+                     std::vector<DnsResourceRecord>() /* additional_records */,
+                     query);
 }
 
-std::unique_ptr<DnsResponse> BuildTestDnsIntegrityResponse(
+DnsResponse BuildTestDnsIntegrityResponse(
     std::string hostname,
     const std::vector<uint8_t>& serialized_rdata) {
   CHECK(!hostname.empty());
@@ -327,17 +322,17 @@
   base::Optional<DnsQuery> query(base::in_place, 0, dns_name,
                                  dns_protocol::kExperimentalTypeIntegrity);
 
-  return std::make_unique<DnsResponse>(
-      0, false, std::move(answers),
-      std::vector<DnsResourceRecord>() /*  authority_records  */,
-      std::vector<DnsResourceRecord>() /*  additional_records */, query);
+  return DnsResponse(0, false, std::move(answers),
+                     std::vector<DnsResourceRecord>() /*  authority_records  */,
+                     std::vector<DnsResourceRecord>() /*  additional_records */,
+                     query);
 }
 
 MockDnsClientRule::Result::Result(ResultType type,
-                                  std::unique_ptr<DnsResponse> response)
+                                  base::Optional<DnsResponse> response)
     : type(type), response(std::move(response)) {}
 
-MockDnsClientRule::Result::Result(std::unique_ptr<DnsResponse> response)
+MockDnsClientRule::Result::Result(DnsResponse response)
     : type(OK), response(std::move(response)) {}
 
 MockDnsClientRule::Result::Result(Result&& result) = default;
@@ -412,7 +407,7 @@
             case MockDnsClientRule::EMPTY:
               DCHECK(!result->response);  // Not expected to be provided.
               authority_records = {BuildSoaRecord(hostname_)};
-              result_.response = std::make_unique<DnsResponse>(
+              result_.response = DnsResponse(
                   22 /* id */, false /* is_authoritative */,
                   std::vector<DnsResourceRecord>() /* answers */,
                   authority_records,
@@ -472,18 +467,22 @@
     if (result->response) {
       // Copy response in case |result| is destroyed before the transaction
       // completes.
-      result_.response = std::make_unique<DnsResponse>(
-          result->response->io_buffer(), result->response->io_buffer_size());
+      auto buffer_copy =
+          base::MakeRefCounted<IOBuffer>(result->response->io_buffer_size());
+      memcpy(buffer_copy->data(), result->response->io_buffer()->data(),
+             result->response->io_buffer_size());
+      result_.response = DnsResponse(std::move(buffer_copy),
+                                     result->response->io_buffer_size());
       CHECK(result_.response->InitParseWithoutQuery(
           result->response->io_buffer_size()));
     } else {
       // Generated response only available for address types.
       DCHECK(qtype_ == dns_protocol::kTypeA ||
              qtype_ == dns_protocol::kTypeAAAA);
-      result_.response =
-          BuildTestDnsResponse(hostname_, qtype_ == dns_protocol::kTypeA
-                                              ? IPAddress::IPv4Localhost()
-                                              : IPAddress::IPv6Localhost());
+      result_.response = BuildTestDnsAddressResponse(
+          hostname_, qtype_ == dns_protocol::kTypeA
+                         ? IPAddress::IPv4Localhost()
+                         : IPAddress::IPv6Localhost());
     }
   }
 
@@ -491,14 +490,17 @@
     switch (result_.type) {
       case MockDnsClientRule::NODOMAIN:
       case MockDnsClientRule::FAIL:
-        std::move(callback_).Run(this, ERR_NAME_NOT_RESOLVED,
-                                 result_.response.get(), base::nullopt);
+        std::move(callback_).Run(
+            this, ERR_NAME_NOT_RESOLVED,
+            result_.response ? &result_.response.value() : nullptr,
+            base::nullopt);
         break;
       case MockDnsClientRule::EMPTY:
       case MockDnsClientRule::OK:
       case MockDnsClientRule::MALFORMED:
-        std::move(callback_).Run(this, OK, result_.response.get(),
-                                 base::nullopt);
+        std::move(callback_).Run(
+            this, OK, result_.response ? &result_.response.value() : nullptr,
+            base::nullopt);
         break;
       case MockDnsClientRule::TIMEOUT:
         std::move(callback_).Run(this, ERR_DNS_TIMED_OUT, nullptr,
@@ -506,8 +508,9 @@
         break;
       case MockDnsClientRule::SLOW:
         if (result_.response) {
-          std::move(callback_).Run(this, OK, result_.response.get(),
-                                   base::nullopt);
+          std::move(callback_).Run(
+              this, OK, result_.response ? &result_.response.value() : nullptr,
+              base::nullopt);
         } else {
           std::move(callback_).Run(this, ERR_DNS_TIMED_OUT, nullptr,
                                    base::nullopt);
diff --git a/net/dns/dns_test_util.h b/net/dns/dns_test_util.h
index 2cc0928..a3e74b9 100644
--- a/net/dns/dns_test_util.h
+++ b/net/dns/dns_test_util.h
@@ -16,6 +16,7 @@
 
 #include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
+#include "base/optional.h"
 #include "base/stl_util.h"
 #include "base/time/time.h"
 #include "net/dns/dns_client.h"
@@ -195,23 +196,20 @@
 DnsResourceRecord BuildTestAddressRecord(std::string name, const IPAddress& ip);
 
 // Builds a DNS response that includes address records.
-std::unique_ptr<DnsResponse> BuildTestDnsResponse(std::string name,
-                                                  const IPAddress& ip);
-std::unique_ptr<DnsResponse> BuildTestDnsResponseWithCname(
-    std::string name,
-    const IPAddress& ip,
-    std::string cannonname);
+DnsResponse BuildTestDnsAddressResponse(std::string name, const IPAddress& ip);
+DnsResponse BuildTestDnsAddressResponseWithCname(std::string name,
+                                                 const IPAddress& ip,
+                                                 std::string cannonname);
 
 // If |answer_name| is empty, |name| will be used for all answer records, as is
 // the normal behavior.
-std::unique_ptr<DnsResponse> BuildTestDnsTextResponse(
+DnsResponse BuildTestDnsTextResponse(
     std::string name,
     std::vector<std::vector<std::string>> text_records,
     std::string answer_name = "");
-std::unique_ptr<DnsResponse> BuildTestDnsPointerResponse(
-    std::string name,
-    std::vector<std::string> pointer_names,
-    std::string answer_name = "");
+DnsResponse BuildTestDnsPointerResponse(std::string name,
+                                        std::vector<std::string> pointer_names,
+                                        std::string answer_name = "");
 
 struct TestServiceRecord {
   uint16_t priority;
@@ -220,12 +218,12 @@
   std::string target;
 };
 
-std::unique_ptr<DnsResponse> BuildTestDnsServiceResponse(
+DnsResponse BuildTestDnsServiceResponse(
     std::string name,
     std::vector<TestServiceRecord> service_records,
     std::string answer_name = "");
 
-std::unique_ptr<DnsResponse> BuildTestDnsIntegrityResponse(
+DnsResponse BuildTestDnsIntegrityResponse(
     std::string hostname,
     const std::vector<uint8_t>& serialized_rdata);
 
@@ -254,15 +252,15 @@
 
   struct Result {
     explicit Result(ResultType type,
-                    std::unique_ptr<DnsResponse> response = nullptr);
-    explicit Result(std::unique_ptr<DnsResponse> response);
+                    base::Optional<DnsResponse> response = base::nullopt);
+    explicit Result(DnsResponse response);
     Result(Result&& result);
     ~Result();
 
     Result& operator=(Result&& result);
 
     ResultType type;
-    std::unique_ptr<DnsResponse> response;
+    base::Optional<DnsResponse> response;
   };
 
   // If |delay| is true, matching transactions will be delayed until triggered
diff --git a/net/dns/host_resolver_manager_unittest.cc b/net/dns/host_resolver_manager_unittest.cc
index cd1af040..99aacc2 100644
--- a/net/dns/host_resolver_manager_unittest.cc
+++ b/net/dns/host_resolver_manager_unittest.cc
@@ -3991,10 +3991,10 @@
                          uint16_t qtype,
                          const IPAddress& result_ip,
                          bool delay) {
-    rules->emplace_back(
-        prefix, qtype, false /* secure */,
-        MockDnsClientRule::Result(BuildTestDnsResponse(prefix, result_ip)),
-        delay);
+    rules->emplace_back(prefix, qtype, false /* secure */,
+                        MockDnsClientRule::Result(
+                            BuildTestDnsAddressResponse(prefix, result_ip)),
+                        delay);
   }
 
   static void AddDnsRule(MockDnsClientRuleList* rules,
@@ -4003,10 +4003,11 @@
                          IPAddress result_ip,
                          std::string cannonname,
                          bool delay) {
-    rules->emplace_back(prefix, qtype, false /* secure */,
-                        MockDnsClientRule::Result(BuildTestDnsResponseWithCname(
-                            prefix, result_ip, std::move(cannonname))),
-                        delay);
+    rules->emplace_back(
+        prefix, qtype, false /* secure */,
+        MockDnsClientRule::Result(BuildTestDnsAddressResponseWithCname(
+            prefix, result_ip, std::move(cannonname))),
+        delay);
   }
 
   static void AddSecureDnsRule(MockDnsClientRuleList* rules,
@@ -6898,7 +6899,7 @@
 
     rules.emplace_back(
         "duplicate", dns_protocol::kTypeA, false /* secure */,
-        MockDnsClientRule::Result(std::make_unique<DnsResponse>(
+        MockDnsClientRule::Result(DnsResponse(
             0, false, std::move(answers),
             std::vector<DnsResourceRecord>() /* authority_records */,
             std::vector<DnsResourceRecord>() /* additional_records */, query)),
@@ -6915,7 +6916,7 @@
 
     rules.emplace_back(
         "duplicate", dns_protocol::kTypeAAAA, false /* secure */,
-        MockDnsClientRule::Result(std::make_unique<DnsResponse>(
+        MockDnsClientRule::Result(DnsResponse(
             0, false, std::move(answers),
             std::vector<DnsResourceRecord>() /* authority_records */,
             std::vector<DnsResourceRecord>() /* additional_records */, query)),
@@ -8165,8 +8166,8 @@
   // Respond to a TXT query with an A response.
   MockDnsClientRuleList rules;
   rules.emplace_back("host", dns_protocol::kTypeTXT, false /* secure */,
-                     MockDnsClientRule::Result(
-                         BuildTestDnsResponse("host", IPAddress(1, 2, 3, 4))),
+                     MockDnsClientRule::Result(BuildTestDnsAddressResponse(
+                         "host", IPAddress(1, 2, 3, 4))),
                      false /* delay */);
 
   CreateResolver();
@@ -8469,8 +8470,8 @@
   // Respond to a TXT query with an A response.
   MockDnsClientRuleList rules;
   rules.emplace_back("host", dns_protocol::kTypePTR, false /* secure */,
-                     MockDnsClientRule::Result(
-                         BuildTestDnsResponse("host", IPAddress(1, 2, 3, 4))),
+                     MockDnsClientRule::Result(BuildTestDnsAddressResponse(
+                         "host", IPAddress(1, 2, 3, 4))),
                      false /* delay */);
 
   CreateResolver();
@@ -8761,8 +8762,8 @@
   // Respond to a SRV query with an A response.
   MockDnsClientRuleList rules;
   rules.emplace_back("host", dns_protocol::kTypeSRV, false /* secure */,
-                     MockDnsClientRule::Result(
-                         BuildTestDnsResponse("host", IPAddress(1, 2, 3, 4))),
+                     MockDnsClientRule::Result(BuildTestDnsAddressResponse(
+                         "host", IPAddress(1, 2, 3, 4))),
                      false /* delay */);
 
   CreateResolver();
diff --git a/net/extras/sqlite/sqlite_persistent_cookie_store.cc b/net/extras/sqlite/sqlite_persistent_cookie_store.cc
index 474db6e..9a05110 100644
--- a/net/extras/sqlite/sqlite_persistent_cookie_store.cc
+++ b/net/extras/sqlite/sqlite_persistent_cookie_store.cc
@@ -1023,7 +1023,6 @@
   }
 
   if (cur_version == 10) {
-    SCOPED_UMA_HISTOGRAM_TIMER("Cookie.TimeDatabaseMigrationToV11");
     sql::Transaction transaction(db());
     if (!transaction.Begin())
       return base::nullopt;
diff --git a/net/quic/quic_flags_list.h b/net/quic/quic_flags_list.h
index 4d568f6..d644abf 100644
--- a/net/quic/quic_flags_list.h
+++ b/net/quic/quic_flags_list.h
@@ -475,3 +475,28 @@
 // If true, address is validated by successfully processing a HANDSHAKE or 1-RTT
 // packet.
 QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_fix_address_validation, false)
+
+// If true, QuicStream will explicitly specify which RST_STREAM, STOP_SENDING
+// frame to send.
+QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_split_up_send_rst, false)
+
+// If true, send HTTP/3 GOAWAY frame when sending CONNECTION_CLOSE.
+QUIC_FLAG(bool,
+          FLAGS_quic_reloadable_flag_quic_send_goaway_with_connection_close,
+          false)
+
+// If true, ack frequency frame can be sent from server to client.
+QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_can_send_ack_frequency, false)
+
+// If true, QUIC BBRv2 will support NetworkParams.max_initial_congestion_window
+// when bootstrapping cwnd.
+QUIC_FLAG(bool,
+          FLAGS_quic_reloadable_flag_quic_bbr2_support_max_bootstrap_cwnd,
+          false)
+
+// If true, QUIC BBR2 will not exit STARTUP on excessive loss, if there was
+// enough bandwidth growth in round.
+QUIC_FLAG(
+    bool,
+    FLAGS_quic_reloadable_flag_quic_bbr2_no_exit_startup_on_loss_with_bw_growth,
+    false)
diff --git a/services/DIR_METADATA b/services/DIR_METADATA
new file mode 100644
index 0000000..52d3644
--- /dev/null
+++ b/services/DIR_METADATA
@@ -0,0 +1,11 @@
+# Metadata information for this directory.
+#
+# For more information on DIR_METADATA files, see:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
+#
+# For the schema of this file, see Metadata message:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
+
+monorail {
+  component: "Internals>Services"
+}
\ No newline at end of file
diff --git a/services/OWNERS b/services/OWNERS
index 1bcc466..aa84af86 100644
--- a/services/OWNERS
+++ b/services/OWNERS
@@ -1,5 +1,4 @@
 blundell@chromium.org
 jam@chromium.org
 rockot@google.com
-sky@chromium.org
-# COMPONENT: Internals>Services
+sky@chromium.org
\ No newline at end of file
diff --git a/services/audio/DIR_METADATA b/services/audio/DIR_METADATA
new file mode 100644
index 0000000..99031e0
--- /dev/null
+++ b/services/audio/DIR_METADATA
@@ -0,0 +1,11 @@
+# Metadata information for this directory.
+#
+# For more information on DIR_METADATA files, see:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
+#
+# For the schema of this file, see Metadata message:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
+
+monorail {
+  component: "Internals>Media>Audio"
+}
\ No newline at end of file
diff --git a/services/audio/OWNERS b/services/audio/OWNERS
index dfd776d..0d82a02 100644
--- a/services/audio/OWNERS
+++ b/services/audio/OWNERS
@@ -3,5 +3,3 @@
 miu@chromium.org
 
 per-file audio_sandbox_hook_linux.*=file://sandbox/linux/OWNERS
-
-# COMPONENT: Internals>Media>Audio
diff --git a/services/content/DIR_METADATA b/services/content/DIR_METADATA
new file mode 100644
index 0000000..ac7f3a4
--- /dev/null
+++ b/services/content/DIR_METADATA
@@ -0,0 +1,11 @@
+# Metadata information for this directory.
+#
+# For more information on DIR_METADATA files, see:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
+#
+# For the schema of this file, see Metadata message:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
+
+monorail {
+  component: "Internals>Services>Content"
+}
\ No newline at end of file
diff --git a/services/content/OWNERS b/services/content/OWNERS
index 672cb65..9624e1d 100644
--- a/services/content/OWNERS
+++ b/services/content/OWNERS
@@ -1,5 +1,4 @@
 alexmos@chromium.org
 clamy@chromium.org
 jam@chromium.org
-rockot@google.com
-# COMPONENT: Internals>Services>Content
+rockot@google.com
\ No newline at end of file
diff --git a/services/data_decoder/DIR_METADATA b/services/data_decoder/DIR_METADATA
new file mode 100644
index 0000000..1af08c8
--- /dev/null
+++ b/services/data_decoder/DIR_METADATA
@@ -0,0 +1,12 @@
+# Metadata information for this directory.
+#
+# For more information on DIR_METADATA files, see:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
+#
+# For the schema of this file, see Metadata message:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
+
+monorail {
+  component: "Internals>Mojo>Bindings"
+}
+team_email: "chrome-security@google.com"
\ No newline at end of file
diff --git a/services/data_decoder/OWNERS b/services/data_decoder/OWNERS
index 915d04e..e645ed54 100644
--- a/services/data_decoder/OWNERS
+++ b/services/data_decoder/OWNERS
@@ -1,4 +1,2 @@
 palmer@chromium.org
-rsesek@chromium.org
-# COMPONENT: Internals>Mojo>Bindings
-# TEAM: chrome-security@google.com
+rsesek@chromium.org
\ No newline at end of file
diff --git a/services/media_session/DIR_METADATA b/services/media_session/DIR_METADATA
new file mode 100644
index 0000000..e278b5a0
--- /dev/null
+++ b/services/media_session/DIR_METADATA
@@ -0,0 +1,12 @@
+# Metadata information for this directory.
+#
+# For more information on DIR_METADATA files, see:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
+#
+# For the schema of this file, see Metadata message:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
+
+monorail {
+  component: "Internals>Media>Session"
+}
+team_email: "media-dev@chromium.org"
\ No newline at end of file
diff --git a/services/media_session/OWNERS b/services/media_session/OWNERS
index 0728e3a..70313512 100644
--- a/services/media_session/OWNERS
+++ b/services/media_session/OWNERS
@@ -1,7 +1,2 @@
 beccahughes@chromium.org
 mlamouri@chromium.org
-
-
-
-# COMPONENT: Internals>Media>Session
-# TEAM: media-dev@chromium.org
diff --git a/services/metrics/DIR_METADATA b/services/metrics/DIR_METADATA
new file mode 100644
index 0000000..35d6bbc8
--- /dev/null
+++ b/services/metrics/DIR_METADATA
@@ -0,0 +1,11 @@
+# Metadata information for this directory.
+#
+# For more information on DIR_METADATA files, see:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
+#
+# For the schema of this file, see Metadata message:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
+
+monorail {
+  component: "Internals>Metrics"
+}
\ No newline at end of file
diff --git a/services/metrics/OWNERS b/services/metrics/OWNERS
index f4be013..9795904 100644
--- a/services/metrics/OWNERS
+++ b/services/metrics/OWNERS
@@ -1,5 +1 @@
 file://base/metrics/OWNERS
-
-
-# COMPONENT: Internals>Metrics
-# TEAM: chromium-dev@chromium.org
diff --git a/services/network/network_context_unittest.cc b/services/network/network_context_unittest.cc
index e7b3686..040bdaa 100644
--- a/services/network/network_context_unittest.cc
+++ b/services/network/network_context_unittest.cc
@@ -3730,11 +3730,11 @@
   net::IPAddress result;
   CHECK(result.AssignFromIPLiteral(kResult));
   net::MockDnsClientRuleList rules;
-  rules.emplace_back(kQueryHostname, net::dns_protocol::kTypeA,
-                     false /* secure */,
-                     net::MockDnsClientRule::Result(
-                         net::BuildTestDnsResponse(kQueryHostname, result)),
-                     false /* delay */);
+  rules.emplace_back(
+      kQueryHostname, net::dns_protocol::kTypeA, false /* secure */,
+      net::MockDnsClientRule::Result(
+          net::BuildTestDnsAddressResponse(kQueryHostname, result)),
+      false /* delay */);
   rules.emplace_back(
       kQueryHostname, net::dns_protocol::kTypeAAAA, false /* secure */,
       net::MockDnsClientRule::Result(net::MockDnsClientRule::ResultType::EMPTY),
diff --git a/services/preferences/DIR_METADATA b/services/preferences/DIR_METADATA
new file mode 100644
index 0000000..3b080ff
--- /dev/null
+++ b/services/preferences/DIR_METADATA
@@ -0,0 +1,11 @@
+# Metadata information for this directory.
+#
+# For more information on DIR_METADATA files, see:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
+#
+# For the schema of this file, see Metadata message:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
+
+monorail {
+  component: "Internals>Preferences>Service"
+}
\ No newline at end of file
diff --git a/services/preferences/OWNERS b/services/preferences/OWNERS
index b1261bd..6662cdd 100644
--- a/services/preferences/OWNERS
+++ b/services/preferences/OWNERS
@@ -1,4 +1,2 @@
 jonross@chromium.org
 sammc@chromium.org
-
-# COMPONENT: Internals>Preferences>Service
diff --git a/services/proxy_resolver/DIR_METADATA b/services/proxy_resolver/DIR_METADATA
new file mode 100644
index 0000000..e05317a
--- /dev/null
+++ b/services/proxy_resolver/DIR_METADATA
@@ -0,0 +1,11 @@
+# Metadata information for this directory.
+#
+# For more information on DIR_METADATA files, see:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
+#
+# For the schema of this file, see Metadata message:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
+
+monorail {
+  component: "Internals>Network>Proxy"
+}
\ No newline at end of file
diff --git a/services/proxy_resolver/OWNERS b/services/proxy_resolver/OWNERS
index d8ae3263..2fdb6e7 100644
--- a/services/proxy_resolver/OWNERS
+++ b/services/proxy_resolver/OWNERS
@@ -1,2 +1 @@
-file://services/network/OWNERS
-# COMPONENT: Internals>Network>Proxy
+file://services/network/OWNERS
\ No newline at end of file
diff --git a/services/resource_coordinator/DIR_METADATA b/services/resource_coordinator/DIR_METADATA
new file mode 100644
index 0000000..07ecfd7
--- /dev/null
+++ b/services/resource_coordinator/DIR_METADATA
@@ -0,0 +1,12 @@
+# Metadata information for this directory.
+#
+# For more information on DIR_METADATA files, see:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
+#
+# For the schema of this file, see Metadata message:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
+
+monorail {
+  component: "Internals>ResourceCoordinator"
+}
+team_email: "catan-team@chromium.org"
\ No newline at end of file
diff --git a/services/resource_coordinator/OWNERS b/services/resource_coordinator/OWNERS
index 2b8c9bcc..021ff20 100644
--- a/services/resource_coordinator/OWNERS
+++ b/services/resource_coordinator/OWNERS
@@ -8,6 +8,3 @@
 zhenw@chromium.org
 
 per-file BUILD.gn=file://services/resource_coordinator/memory_instrumentation/OWNERS
-
-# TEAM: catan-team@chromium.org
-# COMPONENT: Internals>ResourceCoordinator
diff --git a/services/resource_coordinator/memory_instrumentation/DIR_METADATA b/services/resource_coordinator/memory_instrumentation/DIR_METADATA
new file mode 100644
index 0000000..970fc89
--- /dev/null
+++ b/services/resource_coordinator/memory_instrumentation/DIR_METADATA
@@ -0,0 +1,11 @@
+# Metadata information for this directory.
+#
+# For more information on DIR_METADATA files, see:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
+#
+# For the schema of this file, see Metadata message:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
+
+monorail {
+  component: "Internals>Instrumentation>Memory"
+}
\ No newline at end of file
diff --git a/services/resource_coordinator/memory_instrumentation/OWNERS b/services/resource_coordinator/memory_instrumentation/OWNERS
index e13658d..822acf15 100644
--- a/services/resource_coordinator/memory_instrumentation/OWNERS
+++ b/services/resource_coordinator/memory_instrumentation/OWNERS
@@ -1,4 +1,3 @@
 hjd@chromium.org
 primiano@chromium.org
-ssid@chromium.org
-# COMPONENT: Internals>Instrumentation>Memory
+ssid@chromium.org
\ No newline at end of file
diff --git a/services/service_manager/OWNERS b/services/service_manager/OWNERS
index db7d7c4..fd5952b 100644
--- a/services/service_manager/OWNERS
+++ b/services/service_manager/OWNERS
@@ -1,5 +1,3 @@
 jam@chromium.org
 rockot@google.com
 sky@chromium.org
-
-# COMPONENT: Internals>Services
diff --git a/services/shape_detection/DIR_METADATA b/services/shape_detection/DIR_METADATA
new file mode 100644
index 0000000..88c96d75
--- /dev/null
+++ b/services/shape_detection/DIR_METADATA
@@ -0,0 +1,12 @@
+# Metadata information for this directory.
+#
+# For more information on DIR_METADATA files, see:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
+#
+# For the schema of this file, see Metadata message:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
+
+monorail {
+  component: "Blink>ShapeDetection"
+}
+team_email: "device-dev@chromium.org"
\ No newline at end of file
diff --git a/services/shape_detection/OWNERS b/services/shape_detection/OWNERS
index aee8252f..47d63bc 100644
--- a/services/shape_detection/OWNERS
+++ b/services/shape_detection/OWNERS
@@ -1,4 +1 @@
 file://third_party/blink/renderer/modules/shapedetection/OWNERS
-
-# COMPONENT: Blink>ShapeDetection
-# TEAM: device-dev@chromium.org
diff --git a/services/test/OWNERS b/services/test/OWNERS
deleted file mode 100644
index eaf0589..0000000
--- a/services/test/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-# COMPONENT: Internals>Services
diff --git a/services/test/data/DIR_METADATA b/services/test/data/DIR_METADATA
new file mode 100644
index 0000000..5bd66c6
--- /dev/null
+++ b/services/test/data/DIR_METADATA
@@ -0,0 +1,11 @@
+# Metadata information for this directory.
+#
+# For more information on DIR_METADATA files, see:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
+#
+# For the schema of this file, see Metadata message:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
+
+monorail {
+  component: "Test"
+}
\ No newline at end of file
diff --git a/services/test/data/OWNERS b/services/test/data/OWNERS
index 177e36d..f59ec20 100644
--- a/services/test/data/OWNERS
+++ b/services/test/data/OWNERS
@@ -1,2 +1 @@
-*
-# COMPONENT: Test
+*
\ No newline at end of file
diff --git a/services/test/echo/OWNERS b/services/test/echo/OWNERS
deleted file mode 100644
index eaf0589..0000000
--- a/services/test/echo/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-# COMPONENT: Internals>Services
diff --git a/services/tracing/DIR_METADATA b/services/tracing/DIR_METADATA
new file mode 100644
index 0000000..2b8a972
--- /dev/null
+++ b/services/tracing/DIR_METADATA
@@ -0,0 +1,11 @@
+# Metadata information for this directory.
+#
+# For more information on DIR_METADATA files, see:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
+#
+# For the schema of this file, see Metadata message:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
+
+monorail {
+  component: "Speed>Tracing"
+}
\ No newline at end of file
diff --git a/services/tracing/OWNERS b/services/tracing/OWNERS
index 2f82ce6..2c2f79b 100644
--- a/services/tracing/OWNERS
+++ b/services/tracing/OWNERS
@@ -1,2 +1 @@
-file://base/trace_event/OWNERS
-# COMPONENT: Speed>Tracing
+file://base/trace_event/OWNERS
\ No newline at end of file
diff --git a/services/video_capture/DIR_METADATA b/services/video_capture/DIR_METADATA
new file mode 100644
index 0000000..f49aeaf
--- /dev/null
+++ b/services/video_capture/DIR_METADATA
@@ -0,0 +1,12 @@
+# Metadata information for this directory.
+#
+# For more information on DIR_METADATA files, see:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
+#
+# For the schema of this file, see Metadata message:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
+
+monorail {
+  component: "Blink>GetUserMedia"
+}
+team_email: "webrtc-dev@chromium.org"
\ No newline at end of file
diff --git a/services/video_capture/OWNERS b/services/video_capture/OWNERS
index 7e7e66b..a7bd387 100644
--- a/services/video_capture/OWNERS
+++ b/services/video_capture/OWNERS
@@ -3,6 +3,3 @@
 # Original (legacy) owners.
 chfremer@chromium.org
 per-file *video*=mcasas@chromium.org
-
-# COMPONENT: Blink>GetUserMedia
-# TEAM: webrtc-dev@chromium.org
diff --git a/services/viz/DIR_METADATA b/services/viz/DIR_METADATA
new file mode 100644
index 0000000..e72351b34
--- /dev/null
+++ b/services/viz/DIR_METADATA
@@ -0,0 +1,11 @@
+# Metadata information for this directory.
+#
+# For more information on DIR_METADATA files, see:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
+#
+# For the schema of this file, see Metadata message:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
+
+monorail {
+  component: "Internals>Services>Viz"
+}
\ No newline at end of file
diff --git a/services/viz/OWNERS b/services/viz/OWNERS
index 0452193..d60220b 100644
--- a/services/viz/OWNERS
+++ b/services/viz/OWNERS
@@ -1,3 +1 @@
 file://components/viz/OWNERS
-
-# COMPONENT: Internals>Services>Viz
diff --git a/services/viz/public/cpp/compositing/mojom_traits_perftest.cc b/services/viz/public/cpp/compositing/mojom_traits_perftest.cc
index ec6e512..815f0c2 100644
--- a/services/viz/public/cpp/compositing/mojom_traits_perftest.cc
+++ b/services/viz/public/cpp/compositing/mojom_traits_perftest.cc
@@ -217,8 +217,9 @@
           pass_in->CreateAndAppendSharedQuadState();
       shared_state1_in->SetAll(
           arbitrary_matrix1, arbitrary_rect1, arbitrary_rect1,
-          arbitrary_rrectf1, arbitrary_rect2, arbitrary_bool1, arbitrary_bool1,
-          arbitrary_float1, arbitrary_blend_mode1, arbitrary_context_id1);
+          gfx::MaskFilterInfo(arbitrary_rrectf1, false), arbitrary_rect2,
+          arbitrary_bool1, arbitrary_bool1, arbitrary_float1,
+          arbitrary_blend_mode1, arbitrary_context_id1);
 
       auto* texture_in = pass_in->CreateAndAppendDrawQuad<TextureDrawQuad>();
       texture_in->SetAll(
@@ -259,8 +260,9 @@
           pass_in->CreateAndAppendSharedQuadState();
       shared_state2_in->SetAll(
           arbitrary_matrix2, arbitrary_rect2, arbitrary_rect2,
-          arbitrary_rrectf2, arbitrary_rect3, arbitrary_bool1, arbitrary_bool1,
-          arbitrary_float2, arbitrary_blend_mode2, arbitrary_context_id2);
+          gfx::MaskFilterInfo(arbitrary_rrectf2, false), arbitrary_rect3,
+          arbitrary_bool1, arbitrary_bool1, arbitrary_float2,
+          arbitrary_blend_mode2, arbitrary_context_id2);
       for (uint32_t j = 0; j < 6; ++j) {
         auto* tile_in = pass_in->CreateAndAppendDrawQuad<TileDrawQuad>();
         tile_in->SetAll(
@@ -276,8 +278,9 @@
           pass_in->CreateAndAppendSharedQuadState();
       shared_state3_in->SetAll(
           arbitrary_matrix1, arbitrary_rect3, arbitrary_rect3,
-          arbitrary_rrectf3, arbitrary_rect1, arbitrary_bool1, arbitrary_bool1,
-          arbitrary_float3, arbitrary_blend_mode3, arbitrary_context_id3);
+          gfx::MaskFilterInfo(arbitrary_rrectf3, false), arbitrary_rect1,
+          arbitrary_bool1, arbitrary_bool1, arbitrary_float3,
+          arbitrary_blend_mode3, arbitrary_context_id3);
       for (uint32_t j = 0; j < 5; ++j) {
         auto* solidcolor_in =
             pass_in->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
diff --git a/services/viz/public/cpp/compositing/mojom_traits_unittest.cc b/services/viz/public/cpp/compositing/mojom_traits_unittest.cc
index 90e3b112..326f6af 100644
--- a/services/viz/public/cpp/compositing/mojom_traits_unittest.cc
+++ b/services/viz/public/cpp/compositing/mojom_traits_unittest.cc
@@ -416,33 +416,34 @@
                                                 13.f, 14.f, 15.f, 16.f);
   const gfx::Rect layer_rect(1234, 5678);
   const gfx::Rect visible_layer_rect(12, 34, 56, 78);
-  const gfx::RRectF rounded_corner_bounds(gfx::RectF(1.f, 2.f, 30.f, 40.f), 5);
+  bool is_fast_rounded_corner = true;
+  const gfx::MaskFilterInfo mask_filter_info(
+      gfx::RRectF(gfx::RectF(1.f, 2.f, 30.f, 40.f), 5), is_fast_rounded_corner);
   const gfx::Rect clip_rect(123, 456, 789, 101112);
   const bool is_clipped = true;
   bool are_contents_opaque = true;
   const float opacity = 0.9f;
   const SkBlendMode blend_mode = SkBlendMode::kSrcOver;
   const int sorting_context_id = 1337;
-  bool is_fast_rounded_corner = true;
   SharedQuadState input_sqs;
   input_sqs.SetAll(quad_to_target_transform, layer_rect, visible_layer_rect,
-                   rounded_corner_bounds, clip_rect, is_clipped,
-                   are_contents_opaque, opacity, blend_mode,
-                   sorting_context_id);
-  input_sqs.is_fast_rounded_corner = is_fast_rounded_corner;
+                   mask_filter_info, clip_rect, is_clipped, are_contents_opaque,
+                   opacity, blend_mode, sorting_context_id);
   SharedQuadState output_sqs;
   mojo::test::SerializeAndDeserialize<mojom::SharedQuadState>(&input_sqs,
                                                               &output_sqs);
   EXPECT_EQ(quad_to_target_transform, output_sqs.quad_to_target_transform);
   EXPECT_EQ(layer_rect, output_sqs.quad_layer_rect);
   EXPECT_EQ(visible_layer_rect, output_sqs.visible_quad_layer_rect);
-  EXPECT_EQ(rounded_corner_bounds, output_sqs.rounded_corner_bounds);
+  EXPECT_EQ(mask_filter_info.rounded_corner_bounds(),
+            output_sqs.mask_filter_info.rounded_corner_bounds());
+  EXPECT_EQ(is_fast_rounded_corner,
+            output_sqs.mask_filter_info.is_fast_rounded_corner());
   EXPECT_EQ(clip_rect, output_sqs.clip_rect);
   EXPECT_EQ(is_clipped, output_sqs.is_clipped);
   EXPECT_EQ(opacity, output_sqs.opacity);
   EXPECT_EQ(blend_mode, output_sqs.blend_mode);
   EXPECT_EQ(sorting_context_id, output_sqs.sorting_context_id);
-  EXPECT_EQ(is_fast_rounded_corner, output_sqs.is_fast_rounded_corner);
 }
 
 // Note that this is a fairly trivial test of CompositorFrame serialization as
@@ -459,8 +460,8 @@
       15.f, 16.f);
   const gfx::Rect sqs_layer_rect(1234, 5678);
   const gfx::Rect sqs_visible_layer_rect(12, 34, 56, 78);
-  const gfx::RRectF sqs_rounded_corner_bounds(gfx::RectF(3.f, 4.f, 50.f, 15.f),
-                                              3);
+  const gfx::MaskFilterInfo sqs_mask_filter_info(
+      gfx::RRectF(gfx::RectF(3.f, 4.f, 50.f, 15.f), 3), false);
   const gfx::Rect sqs_clip_rect(123, 456, 789, 101112);
   const bool sqs_is_clipped = true;
   bool sqs_are_contents_opaque = false;
@@ -469,7 +470,7 @@
   const int sqs_sorting_context_id = 1337;
   SharedQuadState* sqs = render_pass->CreateAndAppendSharedQuadState();
   sqs->SetAll(sqs_quad_to_target_transform, sqs_layer_rect,
-              sqs_visible_layer_rect, sqs_rounded_corner_bounds, sqs_clip_rect,
+              sqs_visible_layer_rect, sqs_mask_filter_info, sqs_clip_rect,
               sqs_is_clipped, sqs_are_contents_opaque, sqs_opacity,
               sqs_blend_mode, sqs_sorting_context_id);
 
@@ -544,7 +545,7 @@
   EXPECT_EQ(sqs_quad_to_target_transform, out_sqs->quad_to_target_transform);
   EXPECT_EQ(sqs_layer_rect, out_sqs->quad_layer_rect);
   EXPECT_EQ(sqs_visible_layer_rect, out_sqs->visible_quad_layer_rect);
-  EXPECT_EQ(sqs_rounded_corner_bounds, out_sqs->rounded_corner_bounds);
+  EXPECT_EQ(sqs_mask_filter_info, out_sqs->mask_filter_info);
   EXPECT_EQ(sqs_clip_rect, out_sqs->clip_rect);
   EXPECT_EQ(sqs_is_clipped, out_sqs->is_clipped);
   EXPECT_EQ(sqs_are_contents_opaque, out_sqs->are_contents_opaque);
@@ -723,7 +724,8 @@
       gfx::Transform(16.1f, 15.3f, 14.3f, 13.7f, 12.2f, 11.4f, 10.4f, 9.8f,
                      8.1f, 7.3f, 6.3f, 5.7f, 4.8f, 3.4f, 2.4f, 1.2f),
       gfx::Rect(1, 2), gfx::Rect(1337, 5679, 9101112, 131415),
-      gfx::RRectF(gfx::RectF(5.f, 6.f, 70.f, 89.f), 10.f),
+      gfx::MaskFilterInfo(gfx::RRectF(gfx::RectF(5.f, 6.f, 70.f, 89.f), 10.f),
+                          false),
       gfx::Rect(1357, 2468, 121314, 1337), true, true, 2, SkBlendMode::kSrcOver,
       1);
 
@@ -732,7 +734,8 @@
       gfx::Transform(1.1f, 2.3f, 3.3f, 4.7f, 5.2f, 6.4f, 7.4f, 8.8f, 9.1f,
                      10.3f, 11.3f, 12.7f, 13.8f, 14.4f, 15.4f, 16.2f),
       gfx::Rect(1337, 1234), gfx::Rect(1234, 5678, 9101112, 13141516),
-      gfx::RRectF(gfx::RectF(23.f, 45.f, 60.f, 70.f), 8.f),
+      gfx::MaskFilterInfo(gfx::RRectF(gfx::RectF(23.f, 45.f, 60.f, 70.f), 8.f),
+                          false),
       gfx::Rect(1357, 2468, 121314, 1337), true, true, 2, SkBlendMode::kSrcOver,
       1);
 
@@ -792,8 +795,7 @@
   EXPECT_EQ(shared_state_1->quad_layer_rect, out_sqs1->quad_layer_rect);
   EXPECT_EQ(shared_state_1->visible_quad_layer_rect,
             out_sqs1->visible_quad_layer_rect);
-  EXPECT_EQ(shared_state_1->rounded_corner_bounds,
-            out_sqs1->rounded_corner_bounds);
+  EXPECT_EQ(shared_state_1->mask_filter_info, out_sqs1->mask_filter_info);
   EXPECT_EQ(shared_state_1->clip_rect, out_sqs1->clip_rect);
   EXPECT_EQ(shared_state_1->is_clipped, out_sqs1->is_clipped);
   EXPECT_EQ(shared_state_1->opacity, out_sqs1->opacity);
@@ -806,8 +808,7 @@
   EXPECT_EQ(shared_state_2->quad_layer_rect, out_sqs2->quad_layer_rect);
   EXPECT_EQ(shared_state_2->visible_quad_layer_rect,
             out_sqs2->visible_quad_layer_rect);
-  EXPECT_EQ(shared_state_2->rounded_corner_bounds,
-            out_sqs2->rounded_corner_bounds);
+  EXPECT_EQ(shared_state_2->mask_filter_info, out_sqs2->mask_filter_info);
   EXPECT_EQ(shared_state_2->clip_rect, out_sqs2->clip_rect);
   EXPECT_EQ(shared_state_2->is_clipped, out_sqs2->is_clipped);
   EXPECT_EQ(shared_state_2->opacity, out_sqs2->opacity);
diff --git a/services/viz/public/cpp/compositing/shared_quad_state_mojom_traits.h b/services/viz/public/cpp/compositing/shared_quad_state_mojom_traits.h
index 830191111..66f70ec 100644
--- a/services/viz/public/cpp/compositing/shared_quad_state_mojom_traits.h
+++ b/services/viz/public/cpp/compositing/shared_quad_state_mojom_traits.h
@@ -7,6 +7,7 @@
 
 #include "components/viz/common/quads/shared_quad_state.h"
 #include "services/viz/public/mojom/compositing/shared_quad_state.mojom-shared.h"
+#include "ui/gfx/mojom/mask_filter_info_mojom_traits.h"
 #include "ui/gfx/mojom/rrect_f_mojom_traits.h"
 
 namespace mojo {
@@ -35,9 +36,9 @@
     return input.sqs->visible_quad_layer_rect;
   }
 
-  static const gfx::RRectF& rounded_corner_bounds(
+  static const gfx::MaskFilterInfo& mask_filter_info(
       const OptSharedQuadState& input) {
-    return input.sqs->rounded_corner_bounds;
+    return input.sqs->mask_filter_info;
   }
 
   static const gfx::Rect& clip_rect(const OptSharedQuadState& input) {
@@ -64,10 +65,6 @@
     return input.sqs->sorting_context_id;
   }
 
-  static bool is_fast_rounded_corner(const OptSharedQuadState& input) {
-    return input.sqs->is_fast_rounded_corner;
-  }
-
   static float de_jelly_delta_y(const OptSharedQuadState& input) {
     return input.sqs->de_jelly_delta_y;
   }
@@ -93,9 +90,9 @@
     return sqs.visible_quad_layer_rect;
   }
 
-  static const gfx::RRectF& rounded_corner_bounds(
+  static const gfx::MaskFilterInfo& mask_filter_info(
       const viz::SharedQuadState& sqs) {
-    return sqs.rounded_corner_bounds;
+    return sqs.mask_filter_info;
   }
 
   static const gfx::Rect& clip_rect(const viz::SharedQuadState& sqs) {
@@ -120,10 +117,6 @@
     return sqs.sorting_context_id;
   }
 
-  static bool is_fast_rounded_corner(const viz::SharedQuadState& sqs) {
-    return sqs.is_fast_rounded_corner;
-  }
-
   static float de_jelly_delta_y(const viz::SharedQuadState& sqs) {
     return sqs.de_jelly_delta_y;
   }
@@ -137,7 +130,7 @@
     if (!data.ReadQuadToTargetTransform(&out->quad_to_target_transform) ||
         !data.ReadQuadLayerRect(&out->quad_layer_rect) ||
         !data.ReadVisibleQuadLayerRect(&out->visible_quad_layer_rect) ||
-        !data.ReadRoundedCornerBounds(&out->rounded_corner_bounds) ||
+        !data.ReadMaskFilterInfo(&out->mask_filter_info) ||
         !data.ReadClipRect(&out->clip_rect)) {
       return false;
     }
@@ -149,7 +142,6 @@
       return false;
     out->blend_mode = static_cast<SkBlendMode>(data.blend_mode());
     out->sorting_context_id = data.sorting_context_id();
-    out->is_fast_rounded_corner = data.is_fast_rounded_corner();
     out->de_jelly_delta_y = data.de_jelly_delta_y();
     out->no_damage = data.no_damage();
     return true;
diff --git a/services/viz/public/cpp/gpu/DIR_METADATA b/services/viz/public/cpp/gpu/DIR_METADATA
new file mode 100644
index 0000000..8e87cca
--- /dev/null
+++ b/services/viz/public/cpp/gpu/DIR_METADATA
@@ -0,0 +1,11 @@
+# Metadata information for this directory.
+#
+# For more information on DIR_METADATA files, see:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
+#
+# For the schema of this file, see Metadata message:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
+
+monorail {
+  component: "Internals>GPU>Internals"
+}
\ No newline at end of file
diff --git a/services/viz/public/cpp/gpu/OWNERS b/services/viz/public/cpp/gpu/OWNERS
index 50daca7..1db50ad 100644
--- a/services/viz/public/cpp/gpu/OWNERS
+++ b/services/viz/public/cpp/gpu/OWNERS
@@ -1,3 +1 @@
 file://gpu/OWNERS
-
-# COMPONENT: Internals>GPU>Internals
diff --git a/services/viz/public/mojom/compositing/shared_quad_state.mojom b/services/viz/public/mojom/compositing/shared_quad_state.mojom
index 214f508b..95ea9dd 100644
--- a/services/viz/public/mojom/compositing/shared_quad_state.mojom
+++ b/services/viz/public/mojom/compositing/shared_quad_state.mojom
@@ -7,6 +7,7 @@
 import "ui/gfx/geometry/mojom/geometry.mojom";
 import "ui/gfx/mojom/rrect_f.mojom";
 import "ui/gfx/mojom/transform.mojom";
+import "ui/gfx/mojom/mask_filter_info.mojom";
 
 // See viz::SharedQuadState.
 struct SharedQuadState {
@@ -20,9 +21,10 @@
   // of the quad rects.
   gfx.mojom.Rect visible_quad_layer_rect;
 
-  // This rect lives in the target content space. It defines the corner radius
-  // to clip the quads with.
-  gfx.mojom.RRectF rounded_corner_bounds;
+  // This rect lives in the target content space. It defines the mask filter
+  // info applied to the quad, and also defines rounded corner rects to clip the
+  // quads with.
+  gfx.mojom.MaskFilterInfo mask_filter_info;
 
   // This rect lives in the target content space.
   gfx.mojom.Rect clip_rect;
@@ -38,8 +40,6 @@
   uint32 blend_mode;
   int32 sorting_context_id;
 
-  bool is_fast_rounded_corner;
-
   // The y offset by which to skew quads in this layer. For experimental
   // de-jelly effect.
   float de_jelly_delta_y;
diff --git a/testing/buildbot/chromium.android.fyi.json b/testing/buildbot/chromium.android.fyi.json
index e29b1b7..36b92545 100644
--- a/testing/buildbot/chromium.android.fyi.json
+++ b/testing/buildbot/chromium.android.fyi.json
@@ -2708,9 +2708,8 @@
       {
         "args": [
           "--enable-features=NetworkService,NetworkServiceInProcess",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_webview_instrumentation_test_apk.filter",
-          "--avd-config=../../tools/android/avd/proto/generic_android28.textpb",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/android.emulator.webview_instrumentation_test_apk.filter"
+          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_webview_instrumentation_test_apk.filter;../../testing/buildbot/filters/android.emulator.webview_instrumentation_test_apk.filter",
+          "--avd-config=../../tools/android/avd/proto/generic_android28.textpb"
         ],
         "merge": {
           "args": [],
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json
index 79aeb81..3c925102 100644
--- a/testing/buildbot/chromium.android.json
+++ b/testing/buildbot/chromium.android.json
@@ -42851,11 +42851,10 @@
       {
         "args": [
           "--enable-features=NetworkService,NetworkServiceInProcess",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_webview_instrumentation_test_apk.filter",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_webview_instrumentation_test_apk.filter;../../testing/buildbot/filters/android.emulator.webview_instrumentation_test_apk.filter",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android28.textpb",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/android.emulator.webview_instrumentation_test_apk.filter"
+          "--avd-config=../../tools/android/avd/proto/generic_android28.textpb"
         ],
         "merge": {
           "args": [
diff --git a/testing/buildbot/chromium.gpu.fyi.json b/testing/buildbot/chromium.gpu.fyi.json
index 5ef1182..781bf61 100644
--- a/testing/buildbot/chromium.gpu.fyi.json
+++ b/testing/buildbot/chromium.gpu.fyi.json
@@ -13146,8 +13146,8 @@
     "gtest_tests": [
       {
         "args": [
-          "--no-xvfb",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/gpu.linux.skiarenderer_dawn_cc_unittests.filter"
+          "--test-launcher-filter-file=../../testing/buildbot/filters/gpu.skiarenderer_dawn_cc_unittests.filter;../../testing/buildbot/filters/gpu.linux.skiarenderer_dawn_cc_unittests.filter",
+          "--no-xvfb"
         ],
         "merge": {
           "args": [],
@@ -31923,6 +31923,9 @@
   "Win10 FYI x64 SkiaRenderer Dawn Release (NVIDIA)": {
     "gtest_tests": [
       {
+        "args": [
+          "--test-launcher-filter-file=../../testing/buildbot/filters/gpu.skiarenderer_dawn_cc_unittests.filter"
+        ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
diff --git a/testing/buildbot/chromium.linux.json b/testing/buildbot/chromium.linux.json
index be4f8a1d..ccada60 100644
--- a/testing/buildbot/chromium.linux.json
+++ b/testing/buildbot/chromium.linux.json
@@ -4869,8 +4869,7 @@
       {
         "args": [
           "--ozone-platform=x11",
-          "--enable-features=UseOzonePlatform",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/ozone-linux.x11_browser_tests.filter"
+          "--enable-features=UseOzonePlatform"
         ],
         "merge": {
           "args": [],
@@ -5010,6 +5009,28 @@
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
+        "name": "interactive_ui_tests_x11",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "interactive_ui_tests",
+        "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/"
+      },
+      {
+        "args": [
+          "--ozone-platform=x11",
+          "--enable-features=UseOzonePlatform"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
         "name": "ozone_x11_unittests_x11",
         "swarming": {
           "can_use_on_swarming_builders": true,
diff --git a/testing/buildbot/chromium.swangle.json b/testing/buildbot/chromium.swangle.json
index 64cfc67..e4e4ab8 100644
--- a/testing/buildbot/chromium.swangle.json
+++ b/testing/buildbot/chromium.swangle.json
@@ -1562,7 +1562,7 @@
             {
               "cpu": "x86-64",
               "gpu": "none",
-              "os": "Windows-10-15063",
+              "os": "Windows-10",
               "pool": "chromium.tests.gpu"
             }
           ],
@@ -1594,7 +1594,7 @@
             {
               "cpu": "x86-64",
               "gpu": "none",
-              "os": "Windows-10-15063",
+              "os": "Windows-10",
               "pool": "chromium.tests.gpu"
             }
           ],
@@ -1623,7 +1623,7 @@
             {
               "cpu": "x86-64",
               "gpu": "none",
-              "os": "Windows-10-15063",
+              "os": "Windows-10",
               "pool": "chromium.tests.gpu"
             }
           ],
@@ -1650,7 +1650,7 @@
             {
               "cpu": "x86-64",
               "gpu": "none",
-              "os": "Windows-10-15063",
+              "os": "Windows-10",
               "pool": "chromium.tests.gpu"
             }
           ],
@@ -1678,7 +1678,7 @@
             {
               "cpu": "x86-64",
               "gpu": "none",
-              "os": "Windows-10-15063",
+              "os": "Windows-10",
               "pool": "chromium.tests.gpu"
             }
           ],
@@ -1706,7 +1706,7 @@
             {
               "cpu": "x86-64",
               "gpu": "none",
-              "os": "Windows-10-15063",
+              "os": "Windows-10",
               "pool": "chromium.tests.gpu"
             }
           ],
@@ -1734,7 +1734,7 @@
             {
               "cpu": "x86-64",
               "gpu": "none",
-              "os": "Windows-10-15063",
+              "os": "Windows-10",
               "pool": "chromium.tests.gpu"
             }
           ],
@@ -1761,7 +1761,7 @@
             {
               "cpu": "x86-64",
               "gpu": "none",
-              "os": "Windows-10-15063",
+              "os": "Windows-10",
               "pool": "chromium.tests.gpu"
             }
           ],
@@ -1788,7 +1788,7 @@
             {
               "cpu": "x86-64",
               "gpu": "none",
-              "os": "Windows-10-15063",
+              "os": "Windows-10",
               "pool": "chromium.tests.gpu"
             }
           ],
@@ -1815,7 +1815,7 @@
             {
               "cpu": "x86-64",
               "gpu": "none",
-              "os": "Windows-10-15063",
+              "os": "Windows-10",
               "pool": "chromium.tests.gpu"
             }
           ],
@@ -1847,7 +1847,7 @@
             {
               "cpu": "x86-64",
               "gpu": "none",
-              "os": "Windows-10-15063",
+              "os": "Windows-10",
               "pool": "chromium.tests.gpu"
             }
           ],
@@ -1874,7 +1874,7 @@
             {
               "cpu": "x86-64",
               "gpu": "none",
-              "os": "Windows-10-15063",
+              "os": "Windows-10",
               "pool": "chromium.tests.gpu"
             }
           ],
@@ -1902,7 +1902,7 @@
             {
               "cpu": "x86-64",
               "gpu": "none",
-              "os": "Windows-10-15063",
+              "os": "Windows-10",
               "pool": "chromium.tests.gpu"
             }
           ],
@@ -1930,7 +1930,7 @@
             {
               "cpu": "x86-64",
               "gpu": "none",
-              "os": "Windows-10-15063",
+              "os": "Windows-10",
               "pool": "chromium.tests.gpu"
             }
           ],
@@ -1958,7 +1958,7 @@
             {
               "cpu": "x86-64",
               "gpu": "none",
-              "os": "Windows-10-15063",
+              "os": "Windows-10",
               "pool": "chromium.tests.gpu"
             }
           ],
@@ -1985,7 +1985,7 @@
             {
               "cpu": "x86-64",
               "gpu": "none",
-              "os": "Windows-10-15063",
+              "os": "Windows-10",
               "pool": "chromium.tests.gpu"
             }
           ],
@@ -2012,7 +2012,7 @@
             {
               "cpu": "x86-64",
               "gpu": "none",
-              "os": "Windows-10-15063",
+              "os": "Windows-10",
               "pool": "chromium.tests.gpu"
             }
           ],
@@ -2039,7 +2039,7 @@
             {
               "cpu": "x86-64",
               "gpu": "none",
-              "os": "Windows-10-15063",
+              "os": "Windows-10",
               "pool": "chromium.tests.gpu"
             }
           ],
@@ -2071,7 +2071,7 @@
             {
               "cpu": "x86-64",
               "gpu": "none",
-              "os": "Windows-10-15063",
+              "os": "Windows-10",
               "pool": "chromium.tests.gpu"
             }
           ],
@@ -2098,7 +2098,7 @@
             {
               "cpu": "x86-64",
               "gpu": "none",
-              "os": "Windows-10-15063",
+              "os": "Windows-10",
               "pool": "chromium.tests.gpu"
             }
           ],
@@ -2126,7 +2126,7 @@
             {
               "cpu": "x86-64",
               "gpu": "none",
-              "os": "Windows-10-15063",
+              "os": "Windows-10",
               "pool": "chromium.tests.gpu"
             }
           ],
@@ -2154,7 +2154,7 @@
             {
               "cpu": "x86-64",
               "gpu": "none",
-              "os": "Windows-10-15063",
+              "os": "Windows-10",
               "pool": "chromium.tests.gpu"
             }
           ],
@@ -2182,7 +2182,7 @@
             {
               "cpu": "x86-64",
               "gpu": "none",
-              "os": "Windows-10-15063",
+              "os": "Windows-10",
               "pool": "chromium.tests.gpu"
             }
           ],
@@ -2209,7 +2209,7 @@
             {
               "cpu": "x86-64",
               "gpu": "none",
-              "os": "Windows-10-15063",
+              "os": "Windows-10",
               "pool": "chromium.tests.gpu"
             }
           ],
@@ -2236,7 +2236,7 @@
             {
               "cpu": "x86-64",
               "gpu": "none",
-              "os": "Windows-10-15063",
+              "os": "Windows-10",
               "pool": "chromium.tests.gpu"
             }
           ],
@@ -2263,7 +2263,7 @@
             {
               "cpu": "x86-64",
               "gpu": "none",
-              "os": "Windows-10-15063",
+              "os": "Windows-10",
               "pool": "chromium.tests.gpu"
             }
           ],
@@ -2295,7 +2295,7 @@
             {
               "cpu": "x86-64",
               "gpu": "none",
-              "os": "Windows-10-15063",
+              "os": "Windows-10",
               "pool": "chromium.tests.gpu"
             }
           ],
@@ -2322,7 +2322,7 @@
             {
               "cpu": "x86-64",
               "gpu": "none",
-              "os": "Windows-10-15063",
+              "os": "Windows-10",
               "pool": "chromium.tests.gpu"
             }
           ],
@@ -2350,7 +2350,7 @@
             {
               "cpu": "x86-64",
               "gpu": "none",
-              "os": "Windows-10-15063",
+              "os": "Windows-10",
               "pool": "chromium.tests.gpu"
             }
           ],
@@ -2378,7 +2378,7 @@
             {
               "cpu": "x86-64",
               "gpu": "none",
-              "os": "Windows-10-15063",
+              "os": "Windows-10",
               "pool": "chromium.tests.gpu"
             }
           ],
@@ -2406,7 +2406,7 @@
             {
               "cpu": "x86-64",
               "gpu": "none",
-              "os": "Windows-10-15063",
+              "os": "Windows-10",
               "pool": "chromium.tests.gpu"
             }
           ],
@@ -2433,7 +2433,7 @@
             {
               "cpu": "x86-64",
               "gpu": "none",
-              "os": "Windows-10-15063",
+              "os": "Windows-10",
               "pool": "chromium.tests.gpu"
             }
           ],
@@ -2460,7 +2460,7 @@
             {
               "cpu": "x86-64",
               "gpu": "none",
-              "os": "Windows-10-15063",
+              "os": "Windows-10",
               "pool": "chromium.tests.gpu"
             }
           ],
@@ -2487,7 +2487,7 @@
             {
               "cpu": "x86-64",
               "gpu": "none",
-              "os": "Windows-10-15063",
+              "os": "Windows-10",
               "pool": "chromium.tests.gpu"
             }
           ],
@@ -2519,7 +2519,7 @@
             {
               "cpu": "x86-64",
               "gpu": "none",
-              "os": "Windows-10-15063",
+              "os": "Windows-10",
               "pool": "chromium.tests.gpu"
             }
           ],
@@ -2546,7 +2546,7 @@
             {
               "cpu": "x86-64",
               "gpu": "none",
-              "os": "Windows-10-15063",
+              "os": "Windows-10",
               "pool": "chromium.tests.gpu"
             }
           ],
@@ -2574,7 +2574,7 @@
             {
               "cpu": "x86-64",
               "gpu": "none",
-              "os": "Windows-10-15063",
+              "os": "Windows-10",
               "pool": "chromium.tests.gpu"
             }
           ],
@@ -2602,7 +2602,7 @@
             {
               "cpu": "x86-64",
               "gpu": "none",
-              "os": "Windows-10-15063",
+              "os": "Windows-10",
               "pool": "chromium.tests.gpu"
             }
           ],
@@ -2630,7 +2630,7 @@
             {
               "cpu": "x86-64",
               "gpu": "none",
-              "os": "Windows-10-15063",
+              "os": "Windows-10",
               "pool": "chromium.tests.gpu"
             }
           ],
@@ -2657,7 +2657,7 @@
             {
               "cpu": "x86-64",
               "gpu": "none",
-              "os": "Windows-10-15063",
+              "os": "Windows-10",
               "pool": "chromium.tests.gpu"
             }
           ],
@@ -2684,7 +2684,7 @@
             {
               "cpu": "x86-64",
               "gpu": "none",
-              "os": "Windows-10-15063",
+              "os": "Windows-10",
               "pool": "chromium.tests.gpu"
             }
           ],
@@ -2711,7 +2711,7 @@
             {
               "cpu": "x86-64",
               "gpu": "none",
-              "os": "Windows-10-15063",
+              "os": "Windows-10",
               "pool": "chromium.tests.gpu"
             }
           ],
@@ -2743,7 +2743,7 @@
             {
               "cpu": "x86-64",
               "gpu": "none",
-              "os": "Windows-10-15063",
+              "os": "Windows-10",
               "pool": "chromium.tests.gpu"
             }
           ],
@@ -2770,7 +2770,7 @@
             {
               "cpu": "x86-64",
               "gpu": "none",
-              "os": "Windows-10-15063",
+              "os": "Windows-10",
               "pool": "chromium.tests.gpu"
             }
           ],
@@ -2798,7 +2798,7 @@
             {
               "cpu": "x86-64",
               "gpu": "none",
-              "os": "Windows-10-15063",
+              "os": "Windows-10",
               "pool": "chromium.tests.gpu"
             }
           ],
@@ -2826,7 +2826,7 @@
             {
               "cpu": "x86-64",
               "gpu": "none",
-              "os": "Windows-10-15063",
+              "os": "Windows-10",
               "pool": "chromium.tests.gpu"
             }
           ],
@@ -2854,7 +2854,7 @@
             {
               "cpu": "x86-64",
               "gpu": "none",
-              "os": "Windows-10-15063",
+              "os": "Windows-10",
               "pool": "chromium.tests.gpu"
             }
           ],
@@ -2881,7 +2881,7 @@
             {
               "cpu": "x86-64",
               "gpu": "none",
-              "os": "Windows-10-15063",
+              "os": "Windows-10",
               "pool": "chromium.tests.gpu"
             }
           ],
@@ -2908,7 +2908,7 @@
             {
               "cpu": "x86-64",
               "gpu": "none",
-              "os": "Windows-10-15063",
+              "os": "Windows-10",
               "pool": "chromium.tests.gpu"
             }
           ],
@@ -2935,7 +2935,7 @@
             {
               "cpu": "x86-64",
               "gpu": "none",
-              "os": "Windows-10-15063",
+              "os": "Windows-10",
               "pool": "chromium.tests.gpu"
             }
           ],
diff --git a/testing/buildbot/filters/BUILD.gn b/testing/buildbot/filters/BUILD.gn
index ba29e82a..17411e1 100644
--- a/testing/buildbot/filters/BUILD.gn
+++ b/testing/buildbot/filters/BUILD.gn
@@ -22,6 +22,7 @@
   data = [
     "//testing/buildbot/filters/android.emulator.cc_unittests.filter",
     "//testing/buildbot/filters/gpu.linux.skiarenderer_dawn_cc_unittests.filter",
+    "//testing/buildbot/filters/gpu.skiarenderer_dawn_cc_unittests.filter",
   ]
 }
 
@@ -48,7 +49,6 @@
     "//testing/buildbot/filters/code_coverage.browser_tests.filter",
     "//testing/buildbot/filters/lacros.browser_tests.filter",
     "//testing/buildbot/filters/ozone-linux.wayland_browser_tests.filter",
-    "//testing/buildbot/filters/ozone-linux.x11_browser_tests.filter",
     "//testing/buildbot/filters/pixel_browser_tests.filter",
     "//testing/buildbot/filters/webrtc_functional.browser_tests.filter",
   ]
diff --git a/testing/buildbot/filters/gpu.skiarenderer_dawn_cc_unittests.filter b/testing/buildbot/filters/gpu.skiarenderer_dawn_cc_unittests.filter
new file mode 100644
index 0000000..31e6b40
--- /dev/null
+++ b/testing/buildbot/filters/gpu.skiarenderer_dawn_cc_unittests.filter
@@ -0,0 +1,3 @@
+# crbug.com/1139118: new blur algorithm
+-All/LayerTreeHostFiltersPixelTest.BackdropFilterBlurRadius/SkiaDawn
+-All/LayerTreeHostFiltersPixelTest.BackdropFilterRotated/SkiaDawn
diff --git a/testing/buildbot/filters/ozone-linux.x11_browser_tests.filter b/testing/buildbot/filters/ozone-linux.x11_browser_tests.filter
deleted file mode 100644
index 66d18d3..0000000
--- a/testing/buildbot/filters/ozone-linux.x11_browser_tests.filter
+++ /dev/null
@@ -1,25 +0,0 @@
-# TODO(https://crbug.com/1084472): fix these failing tests.
-
-# Failed
--DesktopCaptureApiTest.ChooseDesktopMedia
--OutOfProcessPPAPITest.FlashClipboard
--WebRtcDesktopCaptureBrowserTest.RunsScreenshareFromOneTabToAnother
--WebRtcGetDisplayMediaBrowserTestWithPicker.GetDisplayMediaVideo
--WebRtcGetDisplayMediaBrowserTestWithPicker.GetDisplayMediaVideoAndAudio
--SaveType/SavePageOriginalVsSavedComparisonTest.CrossSiteObject/0
-
-# Flaky
--AutofillProviderBrowserTestWithSkipFlagOff.InferredLabelChangeImpactFormComparing
--AutofillProviderBrowserTestWithSkipFlagOn.LabelTagChangeImpactFormComparing
--ExecuteScriptApiTest/DestructiveScriptTest.SynchronousRemoval/0
--ExtensionInstallDialogViewTest.InstallButtonDelay
--OmniboxPopupContentsViewTest.ClickOmnibox
--PageInfoBubbleViewBrowserTest.FocusDoesNotReturnToContentsOnReloadPrompt
--PaymentRequestCreditCardEditorTest.EditingExpiredCard
--PaymentRequestShippingAddressEditorTest.FocusFirstField_Name
--PrintPreviewScalingSettingsTest.SetScaling
--SaveCardBubbleViewsFullFormBrowserTestForStatusChip.Local_ClickingSaveShowsSigninPromo
--TranslateLanguageBrowserTestWithTranslateRecentTarget.RecentTargetLanguage
--WebViewTest.Shim_TestLoadAbortIllegalChromeURL
--WebViewTest.Shim_testFindInMultipleWebViews
--WebViewTest.TestPlugin
diff --git a/testing/buildbot/generate_buildbot_json.py b/testing/buildbot/generate_buildbot_json.py
index b967485..607f48b 100755
--- a/testing/buildbot/generate_buildbot_json.py
+++ b/testing/buildbot/generate_buildbot_json.py
@@ -460,6 +460,7 @@
     #   --extra-browser-args=arg1 arg2
     arr = self.merge_command_line_args(arr, '--enable-features=', ',')
     arr = self.merge_command_line_args(arr, '--extra-browser-args=', ' ')
+    arr = self.merge_command_line_args(arr, '--test-launcher-filter-file=', ';')
     return arr
 
   def substitute_magic_args(self, test_config):
diff --git a/testing/buildbot/mixins.pyl b/testing/buildbot/mixins.pyl
index 0079343a..584c316 100644
--- a/testing/buildbot/mixins.pyl
+++ b/testing/buildbot/mixins.pyl
@@ -825,6 +825,16 @@
       },
     },
   },
+  'win10_gce_gpu_pool': {
+    'swarming': {
+      'dimensions': {
+        'cpu': 'x86-64',
+        'gpu': 'none',
+        'os': 'Windows-10',
+        'pool': 'chromium.tests.gpu',
+      },
+    },
+  },
   'win10_intel_hd_630_experimental': {
     'swarming': {
       'dimensions': {
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index bf0997f..45043ea 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -602,15 +602,6 @@
       },
     },
   },
-  'browser_tests_x11': {
-    'modifications': {
-      'Linux Ozone Tester (X11)': {
-        'args': [
-          '--test-launcher-filter-file=../../testing/buildbot/filters/ozone-linux.x11_browser_tests.filter',
-        ],
-      },
-    },
-  },
   'cc_unittests': {
     'modifications': {
       'Linux TSan Tests': {
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index 5fb1e07..2ff2794 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -2555,6 +2555,9 @@
           '--no-xvfb',
           '--test-launcher-filter-file=../../testing/buildbot/filters/gpu.linux.skiarenderer_dawn_cc_unittests.filter',
         ],
+        'args': [
+          '--test-launcher-filter-file=../../testing/buildbot/filters/gpu.skiarenderer_dawn_cc_unittests.filter',
+        ],
       },
       'viz_unittests': {
         'linux_args': [
@@ -3465,6 +3468,7 @@
     },
 
     'linux_ozone_x11_only_gtests': {
+      'interactive_ui_tests': {},
       'ozone_x11_unittests': {},
       'x11_unittests': {},
     },
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index d2791e8..ff484db 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -5109,10 +5109,7 @@
         'os_type': 'win',
         'browser_config': 'release',
         'mixins': [
-          'gpu-swarming-pool',
-          'no_gpu',
-          'x86-64',
-          'win10',
+          'win10_gce_gpu_pool',
         ],
         'test_suites': {
           'gpu_telemetry_tests': 'gpu_swangle_telemetry_tests',
@@ -5121,10 +5118,7 @@
       'win-swangle-tot-angle-x64' : {
         'os_type': 'win',
         'mixins': [
-          'gpu-swarming-pool',
-          'no_gpu',
-          'x86-64',
-          'win10',
+          'win10_gce_gpu_pool',
           'timeout_15m',
         ],
         'test_suites': {
@@ -5134,10 +5128,7 @@
       'win-swangle-tot-angle-x86' : {
         'os_type': 'win',
         'mixins': [
-          'gpu-swarming-pool',
-          'no_gpu',
-          'x86-64',
-          'win10',
+          'win10_gce_gpu_pool',
           'timeout_15m',
         ],
         'test_suites': {
@@ -5147,10 +5138,7 @@
       'win-swangle-tot-swiftshader-x64' : {
         'os_type': 'win',
         'mixins': [
-          'gpu-swarming-pool',
-          'no_gpu',
-          'x86-64',
-          'win10',
+          'win10_gce_gpu_pool',
           'timeout_15m',
         ],
         'test_suites': {
@@ -5160,10 +5148,7 @@
       'win-swangle-tot-swiftshader-x86' : {
         'os_type': 'win',
         'mixins': [
-          'gpu-swarming-pool',
-          'no_gpu',
-          'x86-64',
-          'win10',
+          'win10_gce_gpu_pool',
           'timeout_15m',
         ],
         'test_suites': {
@@ -5173,10 +5158,7 @@
       'win-swangle-x64' : {
         'os_type': 'win',
         'mixins': [
-          'gpu-swarming-pool',
-          'no_gpu',
-          'x86-64',
-          'win10',
+          'win10_gce_gpu_pool',
           'timeout_15m',
         ],
         'test_suites': {
@@ -5186,10 +5168,7 @@
       'win-swangle-x86' : {
         'os_type': 'win',
         'mixins': [
-          'gpu-swarming-pool',
-          'no_gpu',
-          'x86-64',
-          'win10',
+          'win10_gce_gpu_pool',
           'timeout_15m',
         ],
         'test_suites': {
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 9069f63..a915c46 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -147,6 +147,27 @@
             ]
         }
     ],
+    "AndroidInProductHelpContextualSearchPromoteLongpress": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "PromoteLongpressExperiment",
+                    "params": {
+                        "availability": "any",
+                        "event_trigger": "name:longpress_bubble_shown;comparator:<6;window:90;storage:90",
+                        "event_used": "name:contextual_search_panel_opened_after_longpress;comparator:==0;window:365;storage:365",
+                        "session_rate": "<1"
+                    },
+                    "enable_features": [
+                        "IPH_ContextualSearchTappedButShouldLongpress"
+                    ]
+                }
+            ]
+        }
+    ],
     "AndroidInProductHelpContextualSearchPromotePanelOpen": [
         {
             "platforms": [
@@ -1829,16 +1850,8 @@
             "experiments": [
                 {
                     "name": "ContextualSearchLongpressResolve",
-                    "params": {
-                        "availability": "any",
-                        "event_longpress_opened": "name:contextual_search_panel_opened_after_longpress;comparator:==0;window:365;storage:365",
-                        "event_trigger": "name:longpress_bubble_shown;comparator:<3;window:90;storage:90",
-                        "event_used": "name:contextual_search_triggered_by_longpress;comparator:==0;window:90;storage:90",
-                        "session_rate": "any"
-                    },
                     "enable_features": [
-                        "ContextualSearchLongpressResolve",
-                        "IPH_ContextualSearchTappedButShouldLongpress"
+                        "ContextualSearchLongpressResolve"
                     ]
                 }
             ]
@@ -1852,8 +1865,15 @@
             "experiments": [
                 {
                     "name": "ContextualSearchTranslations",
+                    "params": {
+                        "availability": "any",
+                        "event_trigger": "name:translations_bubble_shown;comparator:<5;window:90;storage:90",
+                        "event_used": "name:contextual_search_enabled_opt_in;comparator:==0;window:365;storage:365",
+                        "session_rate": "<1"
+                    },
                     "enable_features": [
-                        "ContextualSearchTranslations"
+                        "ContextualSearchTranslations",
+                        "IPH_ContextualSearchTranslationEnable"
                     ]
                 }
             ]
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc
index efd13fc..6e43d421 100644
--- a/third_party/blink/common/features.cc
+++ b/third_party/blink/common/features.cc
@@ -521,6 +521,10 @@
 const base::Feature kDawn2dCanvas{"Dawn2dCanvas",
                                   base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Enables small accelerated canvases for webview (crbug.com/1004304)
+const base::Feature kWebviewAccelerateSmallCanvases{
+    "WebviewAccelerateSmallCanvases", base::FEATURE_DISABLED_BY_DEFAULT};
+
 const base::Feature kCSSReducedFontLoadingLayoutInvalidations{
     "CSSReducedFontLoadingLayoutInvalidations",
     base::FEATURE_ENABLED_BY_DEFAULT};
diff --git a/third_party/blink/public/common/features.h b/third_party/blink/public/common/features.h
index eb42c94..0576d8a 100644
--- a/third_party/blink/public/common/features.h
+++ b/third_party/blink/public/common/features.h
@@ -158,6 +158,8 @@
 
 BLINK_COMMON_EXPORT extern const base::Feature kDawn2dCanvas;
 
+BLINK_COMMON_EXPORT extern const base::Feature kWebviewAccelerateSmallCanvases;
+
 BLINK_COMMON_EXPORT extern const base::Feature
     kCSSReducedFontLoadingLayoutInvalidations;
 
diff --git a/third_party/blink/public/web/web_navigation_params.h b/third_party/blink/public/web/web_navigation_params.h
index 122b5e7..1ea6790 100644
--- a/third_party/blink/public/web/web_navigation_params.h
+++ b/third_party/blink/public/web/web_navigation_params.h
@@ -172,8 +172,8 @@
       network::mojom::IPAddressSpace::kUnknown;
 
   // The frame policy specified by the frame owner element.
-  // Should be base::nullopt for top level navigations
-  base::Optional<FramePolicy> frame_policy;
+  // For top-level window with no opener, this is the default lax FramePolicy.
+  FramePolicy frame_policy;
 };
 
 // This structure holds all information provided by the embedder that is
diff --git a/third_party/blink/renderer/core/accessibility/ax_context.cc b/third_party/blink/renderer/core/accessibility/ax_context.cc
index 8fb146c..deb84151 100644
--- a/third_party/blink/renderer/core/accessibility/ax_context.cc
+++ b/third_party/blink/renderer/core/accessibility/ax_context.cc
@@ -29,4 +29,8 @@
   return document_ && document_->IsActive();
 }
 
+Document* AXContext::GetDocument() {
+  return document_;
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/accessibility/ax_context.h b/third_party/blink/renderer/core/accessibility/ax_context.h
index 55a4dcaa..5d99362a 100644
--- a/third_party/blink/renderer/core/accessibility/ax_context.h
+++ b/third_party/blink/renderer/core/accessibility/ax_context.h
@@ -34,6 +34,8 @@
   // (i.e. document has been initialized and hasn't been detached yet).
   bool HasActiveDocument();
 
+  Document* GetDocument();
+
  protected:
   WeakPersistent<Document> document_;
 };
diff --git a/third_party/blink/renderer/core/animation/css_default_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_default_interpolation_type.cc
index ab08978..95656fdb 100644
--- a/third_party/blink/renderer/core/animation/css_default_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_default_interpolation_type.cc
@@ -7,7 +7,6 @@
 #include "third_party/blink/renderer/core/animation/css_interpolation_environment.h"
 #include "third_party/blink/renderer/core/animation/string_keyframe.h"
 #include "third_party/blink/renderer/core/css/resolver/style_builder.h"
-#include "third_party/blink/renderer/core/css/scoped_css_value.h"
 
 namespace blink {
 
@@ -49,9 +48,7 @@
   StyleBuilder::ApplyProperty(
       GetProperty().GetCSSPropertyName(),
       To<CSSInterpolationEnvironment>(environment).GetState(),
-      ScopedCSSValue(*To<CSSDefaultNonInterpolableValue>(non_interpolable_value)
-                          ->CssValue(),
-                     nullptr));
+      *To<CSSDefaultNonInterpolableValue>(non_interpolable_value)->CssValue());
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/animation/css_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_interpolation_type.cc
index 69d289d..ffe1dab 100644
--- a/third_party/blink/renderer/core/animation/css_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_interpolation_type.cc
@@ -24,7 +24,6 @@
 #include "third_party/blink/renderer/core/css/resolver/style_builder.h"
 #include "third_party/blink/renderer/core/css/resolver/style_cascade.h"
 #include "third_party/blink/renderer/core/css/resolver/style_resolver_state.h"
-#include "third_party/blink/renderer/core/css/scoped_css_value.h"
 #include "third_party/blink/renderer/core/style/computed_style.h"
 #include "third_party/blink/renderer/core/style/data_equivalency.h"
 #include "third_party/blink/renderer/core/style_property_shorthand.h"
@@ -337,7 +336,7 @@
   const CSSValue* value = MakeGarbageCollected<CSSCustomPropertyDeclaration>(
       property.CustomPropertyName(), std::move(variable_data));
   StyleBuilder::ApplyProperty(GetProperty().GetCSSPropertyName(), state,
-                              ScopedCSSValue(*value, nullptr));
+                              *value);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/animation/css_length_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_length_interpolation_type.cc
index 12c18f3..93b6a92 100644
--- a/third_party/blink/renderer/core/animation/css_length_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_length_interpolation_type.cc
@@ -13,7 +13,6 @@
 #include "third_party/blink/renderer/core/css/css_identifier_value.h"
 #include "third_party/blink/renderer/core/css/resolver/style_builder.h"
 #include "third_party/blink/renderer/core/css/resolver/style_resolver_state.h"
-#include "third_party/blink/renderer/core/css/scoped_css_value.h"
 #include "third_party/blink/renderer/core/style/computed_style.h"
 #include "third_party/blink/renderer/platform/geometry/length_functions.h"
 
@@ -146,9 +145,8 @@
     Length before;
     Length after;
     DCHECK(LengthPropertyFunctions::GetLength(CssProperty(), style, before));
-    StyleBuilder::ApplyProperty(
-        GetProperty().GetCSSProperty(), state,
-        ScopedCSSValue(*CSSValue::Create(length, zoom), nullptr));
+    StyleBuilder::ApplyProperty(GetProperty().GetCSSProperty(), state,
+                                *CSSValue::Create(length, zoom));
     DCHECK(LengthPropertyFunctions::GetLength(CssProperty(), style, after));
     DCHECK(before.IsSpecified());
     DCHECK(after.IsSpecified());
@@ -164,9 +162,8 @@
 #endif
     return;
   }
-  StyleBuilder::ApplyProperty(
-      GetProperty().GetCSSProperty(), state,
-      ScopedCSSValue(*CSSValue::Create(length, zoom), nullptr));
+  StyleBuilder::ApplyProperty(GetProperty().GetCSSProperty(), state,
+                              *CSSValue::Create(length, zoom));
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/animation/css_number_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_number_interpolation_type.cc
index 4d9393f..e24be066 100644
--- a/third_party/blink/renderer/core/animation/css_number_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_number_interpolation_type.cc
@@ -12,7 +12,6 @@
 #include "third_party/blink/renderer/core/css/css_numeric_literal_value.h"
 #include "third_party/blink/renderer/core/css/resolver/style_builder.h"
 #include "third_party/blink/renderer/core/css/resolver/style_resolver_state.h"
-#include "third_party/blink/renderer/core/css/scoped_css_value.h"
 
 namespace blink {
 
@@ -111,10 +110,8 @@
                                           clamped_number)) {
     StyleBuilder::ApplyProperty(
         GetProperty().GetCSSProperty(), state,
-        ScopedCSSValue(
-            *CSSNumericLiteralValue::Create(
-                clamped_number, CSSPrimitiveValue::UnitType::kNumber),
-            nullptr));
+        *CSSNumericLiteralValue::Create(clamped_number,
+                                        CSSPrimitiveValue::UnitType::kNumber));
   }
 }
 
diff --git a/third_party/blink/renderer/core/animation/css_var_cycle_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_var_cycle_interpolation_type.cc
index 226e20f..ecf85c20 100644
--- a/third_party/blink/renderer/core/animation/css_var_cycle_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_var_cycle_interpolation_type.cc
@@ -14,7 +14,6 @@
 #include "third_party/blink/renderer/core/css/property_registration.h"
 #include "third_party/blink/renderer/core/css/resolver/style_builder.h"
 #include "third_party/blink/renderer/core/css/resolver/style_cascade.h"
-#include "third_party/blink/renderer/core/css/scoped_css_value.h"
 #include "third_party/blink/renderer/core/style/computed_style.h"
 
 namespace blink {
@@ -117,10 +116,8 @@
   StyleBuilder::ApplyProperty(
       GetProperty().GetCSSPropertyName(),
       To<CSSInterpolationEnvironment>(environment).GetState(),
-      ScopedCSSValue(
-          *MakeGarbageCollected<CSSCustomPropertyDeclaration>(
-              GetProperty().CustomPropertyName(), CSSValueID::kUnset),
-          nullptr));
+      *MakeGarbageCollected<CSSCustomPropertyDeclaration>(
+          GetProperty().CustomPropertyName(), CSSValueID::kUnset));
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/BUILD.gn b/third_party/blink/renderer/core/css/BUILD.gn
index 3f40690..a694146 100644
--- a/third_party/blink/renderer/core/css/BUILD.gn
+++ b/third_party/blink/renderer/core/css/BUILD.gn
@@ -533,7 +533,6 @@
     "rule_feature_set.h",
     "rule_set.cc",
     "rule_set.h",
-    "scoped_css_value.h",
     "select_rule_feature_set.cc",
     "select_rule_feature_set.h",
     "selector_checker.cc",
diff --git a/third_party/blink/renderer/core/css/media_query_evaluator.cc b/third_party/blink/renderer/core/css/media_query_evaluator.cc
index 4cbee31..5b94719 100644
--- a/third_party/blink/renderer/core/css/media_query_evaluator.cc
+++ b/third_party/blink/renderer/core/css/media_query_evaluator.cc
@@ -159,6 +159,7 @@
 
 bool MediaQueryEvaluator::DidResultsChange(
     const MediaQueryResultList& results) const {
+  base::AutoReset<bool> skip(&skip_ukm_reporting_, true);
   for (auto& result : results) {
     if (Eval(result.Expression()) != result.Result())
       return true;
@@ -166,6 +167,16 @@
   return false;
 }
 
+bool MediaQueryEvaluator::DidResultsChange(
+    const Vector<MediaQuerySetResult>& results) const {
+  base::AutoReset<bool> skip(&skip_ukm_reporting_, true);
+  for (const auto& result : results) {
+    if (result.Result() != Eval(result.MediaQueries()))
+      return true;
+  }
+  return false;
+}
+
 template <typename T>
 bool CompareValue(T a, T b, MediaFeaturePrefix op) {
   switch (op) {
diff --git a/third_party/blink/renderer/core/css/media_query_evaluator.h b/third_party/blink/renderer/core/css/media_query_evaluator.h
index a5502987..3a2f09d 100644
--- a/third_party/blink/renderer/core/css/media_query_evaluator.h
+++ b/third_party/blink/renderer/core/css/media_query_evaluator.h
@@ -38,6 +38,7 @@
 class MediaQueryExp;
 class MediaQueryResult;
 class MediaQuerySet;
+class MediaQuerySetResult;
 class MediaValues;
 class MediaValuesInitialViewport;
 
@@ -98,6 +99,10 @@
   // evaluation.
   bool DidResultsChange(const MediaQueryResultList& results) const;
 
+  // Returns true if any of the media queries in the results lists changed its
+  // evaluation.
+  bool DidResultsChange(const Vector<MediaQuerySetResult>& results) const;
+
   void Trace(Visitor*) const;
 
  private:
@@ -105,6 +110,10 @@
 
   String media_type_;
   Member<MediaValues> media_values_;
+
+  // Even if UKM reporting is enabled, do not report any media query evaluation
+  // results if this is set to true.
+  mutable bool skip_ukm_reporting_{false};
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/properties/css_property_test.cc b/third_party/blink/renderer/core/css/properties/css_property_test.cc
index 7495249a..eda96e1 100644
--- a/third_party/blink/renderer/core/css/properties/css_property_test.cc
+++ b/third_party/blink/renderer/core/css/properties/css_property_test.cc
@@ -10,7 +10,6 @@
 #include "third_party/blink/renderer/core/css/properties/css_property_ref.h"
 #include "third_party/blink/renderer/core/css/resolver/style_builder.h"
 #include "third_party/blink/renderer/core/css/resolver/style_resolver_state.h"
-#include "third_party/blink/renderer/core/css/scoped_css_value.h"
 #include "third_party/blink/renderer/core/html/html_element.h"
 #include "third_party/blink/renderer/core/style/computed_style.h"
 #include "third_party/blink/renderer/core/style/data_equivalency.h"
@@ -43,8 +42,7 @@
     state.Style()->SetBorderRightStyle(EBorderStyle::kSolid);
     state.Style()->SetBorderTopStyle(EBorderStyle::kSolid);
 
-    StyleBuilder::ApplyProperty(property, state,
-                                ScopedCSSValue(value, &GetDocument()));
+    StyleBuilder::ApplyProperty(property, state, value);
     return state.TakeStyle();
   }
 };
diff --git a/third_party/blink/renderer/core/css/resolver/cascade_expansion.cc b/third_party/blink/renderer/core/css/resolver/cascade_expansion.cc
index 29185ff..6162211 100644
--- a/third_party/blink/renderer/core/css/resolver/cascade_expansion.cc
+++ b/third_party/blink/renderer/core/css/resolver/cascade_expansion.cc
@@ -186,8 +186,4 @@
   return matched_properties_.properties->PropertyAt(index_);
 }
 
-uint16_t CascadeExpansion::TreeOrder() const {
-  return matched_properties_.types_.tree_order;
-}
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/resolver/cascade_expansion.h b/third_party/blink/renderer/core/css/resolver/cascade_expansion.h
index f1090cd..af6d8d2c 100644
--- a/third_party/blink/renderer/core/css/resolver/cascade_expansion.h
+++ b/third_party/blink/renderer/core/css/resolver/cascade_expansion.h
@@ -98,7 +98,6 @@
     return PropertyAt(index_).Value();
   }
   inline CascadePriority Priority() const { return priority_; }
-  uint16_t TreeOrder() const;
 
  private:
   static bool IsAffectedByAll(CSSPropertyID);
diff --git a/third_party/blink/renderer/core/css/resolver/style_builder.cc b/third_party/blink/renderer/core/css/resolver/style_builder.cc
index 29ea83ab..7b72837 100644
--- a/third_party/blink/renderer/core/css/resolver/style_builder.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_builder.cc
@@ -48,29 +48,27 @@
 #include "third_party/blink/renderer/core/css/properties/longhands/variable.h"
 #include "third_party/blink/renderer/core/css/resolver/style_builder.h"
 #include "third_party/blink/renderer/core/css/resolver/style_resolver_state.h"
-#include "third_party/blink/renderer/core/css/scoped_css_value.h"
 #include "third_party/blink/renderer/core/style/computed_style.h"
 
 namespace blink {
 
 void StyleBuilder::ApplyProperty(const CSSPropertyName& name,
                                  StyleResolverState& state,
-                                 const ScopedCSSValue& scoped_value) {
+                                 const CSSValue& value) {
   CSSPropertyRef ref(name, state.GetDocument());
   DCHECK(ref.IsValid());
 
-  ApplyProperty(ref.GetProperty(), state, scoped_value);
+  ApplyProperty(ref.GetProperty(), state, value);
 }
 
 void StyleBuilder::ApplyProperty(const CSSProperty& property,
                                  StyleResolverState& state,
-                                 const ScopedCSSValue& scoped_value) {
+                                 const CSSValue& value) {
   DCHECK(!Variable::IsStaticInstance(property))
       << "Please use a CustomProperty instance to apply custom properties";
 
   CSSPropertyID id = property.PropertyID();
   bool is_inherited = property.IsInherited();
-  const CSSValue& value = scoped_value.GetCSSValue();
 
   // These values must be resolved by StyleCascade before application:
   DCHECK(!value.IsVariableReferenceValue());
diff --git a/third_party/blink/renderer/core/css/resolver/style_builder.h b/third_party/blink/renderer/core/css/resolver/style_builder.h
index 5b20d50..455e2f8 100644
--- a/third_party/blink/renderer/core/css/resolver/style_builder.h
+++ b/third_party/blink/renderer/core/css/resolver/style_builder.h
@@ -39,7 +39,7 @@
 namespace blink {
 
 class CSSPropertyName;
-class ScopedCSSValue;
+class CSSValue;
 class StyleResolverState;
 
 class CORE_EXPORT StyleBuilder {
@@ -52,7 +52,7 @@
   // CustomProperty instance is created to carry out the application.
   static void ApplyProperty(const CSSPropertyName&,
                             StyleResolverState&,
-                            const ScopedCSSValue&);
+                            const CSSValue&);
 
   // Apply a property/value pair to the ComputedStyle.
   //
@@ -61,7 +61,7 @@
   // instance. See Variable::IsStaticInstance.
   static void ApplyProperty(const CSSProperty&,
                             StyleResolverState&,
-                            const ScopedCSSValue&);
+                            const CSSValue&);
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/resolver/style_builder_test.cc b/third_party/blink/renderer/core/css/resolver/style_builder_test.cc
index 323666b..4952293 100644
--- a/third_party/blink/renderer/core/css/resolver/style_builder_test.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_builder_test.cc
@@ -7,7 +7,6 @@
 #include "third_party/blink/renderer/core/css/css_inherited_value.h"
 #include "third_party/blink/renderer/core/css/css_initial_value.h"
 #include "third_party/blink/renderer/core/css/resolver/style_resolver_state.h"
-#include "third_party/blink/renderer/core/css/scoped_css_value.h"
 #include "third_party/blink/renderer/core/html/html_element.h"
 #include "third_party/blink/renderer/core/style/computed_style.h"
 #include "third_party/blink/renderer/core/testing/page_test_base.h"
@@ -42,8 +41,7 @@
       state.SetStyle(style);
 
       ASSERT_FALSE(state.GetFontBuilder().FontDirty());
-      StyleBuilder::ApplyProperty(*property, state,
-                                  ScopedCSSValue(*value, &GetDocument()));
+      StyleBuilder::ApplyProperty(*property, state, *value);
       EXPECT_TRUE(state.GetFontBuilder().FontDirty());
     }
   }
@@ -74,8 +72,7 @@
       state.SetStyle(style);
 
       ASSERT_FALSE(state.GetFontBuilder().FontDirty());
-      StyleBuilder::ApplyProperty(*property, state,
-                                  ScopedCSSValue(*value, &GetDocument()));
+      StyleBuilder::ApplyProperty(*property, state, *value);
       EXPECT_TRUE(state.GetFontBuilder().FontDirty());
     }
   }
@@ -89,14 +86,13 @@
   state.SetStyle(style);
   EXPECT_FALSE(style->HasExplicitInheritance());
 
-  ScopedCSSValue inherited(*CSSInheritedValue::Create(), &GetDocument());
-
   // Flag should not be set for properties which are inherited.
-  StyleBuilder::ApplyProperty(GetCSSPropertyColor(), state, inherited);
+  StyleBuilder::ApplyProperty(GetCSSPropertyColor(), state,
+                              *CSSInheritedValue::Create());
   EXPECT_FALSE(style->HasExplicitInheritance());
 
   StyleBuilder::ApplyProperty(GetCSSPropertyBackgroundColor(), state,
-                              inherited);
+                              *CSSInheritedValue::Create());
   EXPECT_TRUE(style->HasExplicitInheritance());
 }
 
diff --git a/third_party/blink/renderer/core/css/resolver/style_cascade.cc b/third_party/blink/renderer/core/css/resolver/style_cascade.cc
index a8bd278..34237b59 100644
--- a/third_party/blink/renderer/core/css/resolver/style_cascade.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_cascade.cc
@@ -28,7 +28,6 @@
 #include "third_party/blink/renderer/core/css/resolver/cascade_resolver.h"
 #include "third_party/blink/renderer/core/css/resolver/style_builder.h"
 #include "third_party/blink/renderer/core/css/resolver/style_resolver_state.h"
-#include "third_party/blink/renderer/core/css/scoped_css_value.h"
 #include "third_party/blink/renderer/core/css/style_engine.h"
 #include "third_party/blink/renderer/core/dom/shadow_root.h"
 #include "third_party/blink/renderer/core/frame/web_feature.h"
@@ -81,14 +80,6 @@
   return &set->PropertyAt(declaration_index).Value();
 }
 
-const TreeScope& TreeScopeAt(const MatchResult& result, uint32_t position) {
-  size_t matched_properties_index = DecodeMatchedPropertiesIndex(position);
-  const MatchedProperties& properties =
-      result.GetMatchedProperties()[matched_properties_index];
-  DCHECK_EQ(properties.types_.origin, CascadeOrigin::kAuthor);
-  return result.ScopeFromTreeOrder(properties.types_.tree_order);
-}
-
 PropertyHandle ToPropertyHandle(const CSSProperty& property,
                                 CascadePriority priority) {
   uint32_t position = priority.GetPosition();
@@ -406,14 +397,7 @@
       *p = priority;
       CascadeOrigin origin = priority.GetOrigin();
       const CSSValue* value = Resolve(property, e.Value(), origin, resolver);
-      // TODO(futhark): Use a user scope TreeScope to support tree-scoped names
-      // for animations in user stylesheets.
-      const TreeScope* tree_scope =
-          origin == CascadeOrigin::kAuthor
-              ? &match_result_.ScopeFromTreeOrder(e.TreeOrder())
-              : nullptr;
-      StyleBuilder::ApplyProperty(property, state_,
-                                  ScopedCSSValue(*value, tree_scope));
+      StyleBuilder::ApplyProperty(property, state_, *value);
     }
   }
 }
@@ -540,11 +524,7 @@
   value = Resolve(property, *value, origin, resolver);
   DCHECK(!value->IsVariableReferenceValue());
   DCHECK(!value->IsPendingSubstitutionValue());
-  const TreeScope* tree_scope{nullptr};
-  if (origin == CascadeOrigin::kAuthor)
-    tree_scope = &TreeScopeAt(match_result_, priority.GetPosition());
-  StyleBuilder::ApplyProperty(property, state_,
-                              ScopedCSSValue(*value, tree_scope));
+  StyleBuilder::ApplyProperty(property, state_, *value);
 }
 
 void StyleCascade::LookupAndApplyInterpolation(const CSSProperty& property,
@@ -622,14 +602,11 @@
   MaybeForceColor(GetCSSPropertyInternalVisitedTextEmphasisColor(),
                   style->InternalVisitedTextEmphasisColor());
 
-  ScopedCSSValue scoped_none(*CSSIdentifierValue::Create(CSSValueID::kNone),
-                             nullptr);
-  StyleBuilder::ApplyProperty(GetCSSPropertyTextShadow(), state_, scoped_none);
-  StyleBuilder::ApplyProperty(GetCSSPropertyBoxShadow(), state_, scoped_none);
-  if (!style->HasUrlBackgroundImage()) {
-    StyleBuilder::ApplyProperty(GetCSSPropertyBackgroundImage(), state_,
-                                scoped_none);
-  }
+  auto* none = CSSIdentifierValue::Create(CSSValueID::kNone);
+  StyleBuilder::ApplyProperty(GetCSSPropertyTextShadow(), state_, *none);
+  StyleBuilder::ApplyProperty(GetCSSPropertyBoxShadow(), state_, *none);
+  if (!style->HasUrlBackgroundImage())
+    StyleBuilder::ApplyProperty(GetCSSPropertyBackgroundImage(), state_, *none);
 
   // Preserve the author/user defined background alpha channel.
   style->SetBackgroundColor(
@@ -651,9 +628,7 @@
     return;
 
   StyleBuilder::ApplyProperty(
-      property, state_,
-      ScopedCSSValue(*GetForcedColorValue(property.GetCSSPropertyName()),
-                     nullptr));
+      property, state_, *GetForcedColorValue(property.GetCSSPropertyName()));
 }
 
 const CSSValue* StyleCascade::GetForcedColorValue(CSSPropertyName name) {
diff --git a/third_party/blink/renderer/core/css/resolver/style_cascade_test.cc b/third_party/blink/renderer/core/css/resolver/style_cascade_test.cc
index 66c2f1ec..4eccc4d 100644
--- a/third_party/blink/renderer/core/css/resolver/style_cascade_test.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_cascade_test.cc
@@ -125,12 +125,12 @@
   }
 
   void Apply(CascadeFilter filter = CascadeFilter()) {
-    EnsureAtLeast(CascadeOrigin::kAnimation);
+    EnsureAtLeast(CascadeOrigin::kAuthor);
     cascade_.Apply(filter);
   }
 
   void ApplySingle(const CSSProperty& property) {
-    EnsureAtLeast(CascadeOrigin::kAnimation);
+    EnsureAtLeast(CascadeOrigin::kAuthor);
     cascade_.AnalyzeIfNeeded();
     TestCascadeResolver resolver(++cascade_.generation_);
     cascade_.LookupAndApply(property, resolver.InnerResolver());
@@ -234,12 +234,6 @@
         current_origin_ = CascadeOrigin::kAuthor;
         break;
       case CascadeOrigin::kAuthor:
-        cascade_.MutableMatchResult().FinishAddingAuthorRulesForTreeScope(
-            GetDocument());
-        current_origin_ = CascadeOrigin::kAnimation;
-        break;
-      case CascadeOrigin::kAnimation:
-        break;
       default:
         NOTREACHED();
         break;
diff --git a/third_party/blink/renderer/core/css/resolver/style_resolver.cc b/third_party/blink/renderer/core/css/resolver/style_resolver.cc
index 76d6f2df..8717609 100644
--- a/third_party/blink/renderer/core/css/resolver/style_resolver.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_resolver.cc
@@ -62,7 +62,6 @@
 #include "third_party/blink/renderer/core/css/resolver/style_resolver_state.h"
 #include "third_party/blink/renderer/core/css/resolver/style_resolver_stats.h"
 #include "third_party/blink/renderer/core/css/resolver/style_rule_usage_tracker.h"
-#include "third_party/blink/renderer/core/css/scoped_css_value.h"
 #include "third_party/blink/renderer/core/css/style_engine.h"
 #include "third_party/blink/renderer/core/css/style_rule_import.h"
 #include "third_party/blink/renderer/core/css/style_sheet_contents.h"
@@ -1083,8 +1082,6 @@
     cascade.MutableMatchResult().FinishAddingUARules();
     cascade.MutableMatchResult().FinishAddingUserRules();
     cascade.MutableMatchResult().AddMatchedProperties(set);
-    cascade.MutableMatchResult().FinishAddingAuthorRulesForTreeScope(
-        element.GetTreeScope());
     cascade.Apply();
   }
   return CompositorKeyframeValueFactory::Create(property, *state.Style());
@@ -1663,8 +1660,6 @@
   cascade.MutableMatchResult().FinishAddingUARules();
   cascade.MutableMatchResult().FinishAddingUserRules();
   cascade.MutableMatchResult().AddMatchedProperties(set);
-  cascade.MutableMatchResult().FinishAddingAuthorRulesForTreeScope(
-      element->GetTreeScope());
   cascade.Apply();
 
   CSSPropertyRef property_ref(property_name, element->GetDocument());
@@ -1790,14 +1785,9 @@
   for (const CSSProperty* property : properties) {
     if (property->IDEquals(CSSPropertyID::kLineHeight))
       UpdateFont(state);
-    // TODO(futhark): If we start supporting fonts on ShadowRoot.fonts in
-    // addition to Document.fonts, we need to pass the correct TreeScope instead
-    // of GetDocument() in the ScopedCSSValue below.
     StyleBuilder::ApplyProperty(
         *property, state,
-        ScopedCSSValue(
-            *property_set.GetPropertyCSSValue(property->PropertyID()),
-            &GetDocument()));
+        *property_set.GetPropertyCSSValue(property->PropertyID()));
   }
 }
 
diff --git a/third_party/blink/renderer/core/css/rule_set.cc b/third_party/blink/renderer/core/css/rule_set.cc
index 185cc3e34..afbca72f 100644
--- a/third_party/blink/renderer/core/css/rule_set.cc
+++ b/third_party/blink/renderer/core/css/rule_set.cc
@@ -431,11 +431,7 @@
 
 bool RuleSet::DidMediaQueryResultsChange(
     const MediaQueryEvaluator& evaluator) const {
-  for (const auto& result : media_query_set_results_) {
-    if (result.Result() != evaluator.Eval(result.MediaQueries()))
-      return true;
-  }
-  return false;
+  return evaluator.DidResultsChange(media_query_set_results_);
 }
 
 void MinimalRuleData::Trace(Visitor* visitor) const {
diff --git a/third_party/blink/renderer/core/css/scoped_css_value.h b/third_party/blink/renderer/core/css/scoped_css_value.h
deleted file mode 100644
index 8c9f1f7..0000000
--- a/third_party/blink/renderer/core/css/scoped_css_value.h
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_CSS_SCOPED_CSS_VALUE_H_
-#define THIRD_PARTY_BLINK_RENDERER_CORE_CSS_SCOPED_CSS_VALUE_H_
-
-namespace blink {
-
-class CSSValue;
-class TreeScope;
-
-// Store a CSSValue along with a TreeScope to support tree-scoped names and
-// references for e.g. @font-face/font-family and @keyframes/animation-name.
-// If the TreeScope pointer is null, we do not support such references, for
-// instance for UA stylesheets.
-class ScopedCSSValue {
-  STACK_ALLOCATED();
-
- public:
-  ScopedCSSValue(const CSSValue& value, const TreeScope* tree_scope)
-      : value_(value), tree_scope_(tree_scope) {}
-  const CSSValue& GetCSSValue() const { return value_; }
-  const TreeScope* GetTreeScope() const { return tree_scope_; }
-
- private:
-  const CSSValue& value_;
-  const TreeScope* tree_scope_;
-};
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_CSS_SCOPED_CSS_VALUE_H_
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc
index a113cc58b..06c24ca 100644
--- a/third_party/blink/renderer/core/dom/element.cc
+++ b/third_party/blink/renderer/core/dom/element.cc
@@ -2195,10 +2195,7 @@
     }
   } else if (name == html_names::kClassAttr) {
     ClassAttributeChanged(params.new_value);
-    if (HasRareData() && GetElementRareData()->GetClassList()) {
-      GetElementRareData()->GetClassList()->DidUpdateAttributeValue(
-          params.old_value, params.new_value);
-    }
+    UpdateClassList(params.old_value, params.new_value);
   } else if (name == html_names::kNameAttr) {
     SetHasName(!params.new_value.IsNull());
   } else if (name == html_names::kPartAttr) {
@@ -2302,6 +2299,14 @@
   }
 }
 
+void Element::UpdateClassList(const AtomicString& old_class_string,
+                              const AtomicString& new_class_string) {
+  if (!HasRareData())
+    return;
+  if (DOMTokenList* class_list = GetElementRareData()->GetClassList())
+    class_list->DidUpdateAttributeValue(old_class_string, new_class_string);
+}
+
 bool Element::ShouldInvalidateDistributionWhenAttributeChanged(
     ShadowRoot& shadow_root,
     const QualifiedName& name,
diff --git a/third_party/blink/renderer/core/dom/element.h b/third_party/blink/renderer/core/dom/element.h
index 39d31f21..3229ad32 100644
--- a/third_party/blink/renderer/core/dom/element.h
+++ b/third_party/blink/renderer/core/dom/element.h
@@ -975,10 +975,13 @@
   // create layout objects is completed (e.g. in display-locked trees).
   bool IsFocusableStyleAfterUpdate() const;
 
-  // classAttributeChanged() exists to share code between
-  // parseAttribute (called via setAttribute()) and
-  // svgAttributeChanged (called when element.className.baseValue is set)
+  // ClassAttributeChanged() and UpdateClassList() exist to share code between
+  // ParseAttribute (called via setAttribute()) and SvgAttributeChanged (called
+  // when element.className.baseVal is set or when the 'class' attribute is
+  // animated by SMIL).
   void ClassAttributeChanged(const AtomicString& new_class_string);
+  void UpdateClassList(const AtomicString& old_class_string,
+                       const AtomicString& new_class_string);
 
   static bool AttributeValueIsJavaScriptURL(const Attribute&);
 
diff --git a/third_party/blink/renderer/core/exported/local_frame_client_impl.cc b/third_party/blink/renderer/core/exported/local_frame_client_impl.cc
index 168056e4..b7388db 100644
--- a/third_party/blink/renderer/core/exported/local_frame_client_impl.cc
+++ b/third_party/blink/renderer/core/exported/local_frame_client_impl.cc
@@ -691,7 +691,7 @@
 
   auto* owner = ToCoreFrame(web_frame_)->Owner();
   navigation_info->frame_policy =
-      owner ? base::make_optional(owner->GetFramePolicy()) : base::nullopt;
+      owner ? owner->GetFramePolicy() : FramePolicy();
 
   navigation_info->href_translate = href_translate;
 
diff --git a/third_party/blink/renderer/core/frame/history.cc b/third_party/blink/renderer/core/frame/history.cc
index 49b0c1b..022ca524 100644
--- a/third_party/blink/renderer/core/frame/history.cc
+++ b/third_party/blink/renderer/core/frame/history.cc
@@ -28,20 +28,16 @@
 #include "third_party/blink/public/mojom/web_feature/web_feature.mojom-shared.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
-#include "third_party/blink/renderer/core/frame/frame_console.h"
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/frame/local_frame_client.h"
-#include "third_party/blink/renderer/core/frame/settings.h"
 #include "third_party/blink/renderer/core/inspector/console_message.h"
 #include "third_party/blink/renderer/core/loader/document_loader.h"
-#include "third_party/blink/renderer/core/loader/frame_loader.h"
 #include "third_party/blink/renderer/core/loader/history_item.h"
 #include "third_party/blink/renderer/core/page/page.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
 #include "third_party/blink/renderer/platform/heap/heap.h"
-#include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
 #include "third_party/blink/renderer/platform/weborigin/security_origin.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_view.h"
@@ -71,13 +67,13 @@
 }
 
 unsigned History::length(ExceptionState& exception_state) const {
-  if (!GetFrame() || !GetFrame()->Client()) {
+  if (!DomWindow()) {
     exception_state.ThrowSecurityError(
         "May not use a History object associated with a Document that is not "
         "fully active");
     return 0;
   }
-  return GetFrame()->Client()->BackForwardLength();
+  return DomWindow()->GetFrame()->Client()->BackForwardLength();
 }
 
 ScriptValue History::state(ScriptState* script_state,
@@ -101,7 +97,7 @@
       return ScriptValue(isolate, v8_state);
   }
 
-  if (!GetFrame()) {
+  if (!DomWindow()) {
     exception_state.ThrowSecurityError(
         "May not use a History object associated with a Document that is "
         "not fully active");
@@ -119,21 +115,15 @@
 }
 
 SerializedScriptValue* History::StateInternal() const {
-  if (!GetFrame() || !GetFrame()->Loader().GetDocumentLoader())
-    return nullptr;
-
-  if (HistoryItem* history_item =
-          GetFrame()->Loader().GetDocumentLoader()->GetHistoryItem()) {
+  if (HistoryItem* history_item = GetHistoryItem())
     return history_item->StateObject();
-  }
-
   return nullptr;
 }
 
 void History::setScrollRestoration(const String& value,
                                    ExceptionState& exception_state) {
   DCHECK(value == "manual" || value == "auto");
-  if (!GetFrame() || !GetFrame()->Client()) {
+  if (!DomWindow()) {
     exception_state.ThrowSecurityError(
         "May not use a History object associated with a Document that is not "
         "fully active");
@@ -146,15 +136,12 @@
   if (scroll_restoration == ScrollRestorationInternal())
     return;
 
-  if (HistoryItem* history_item =
-          GetFrame()->Loader().GetDocumentLoader()->GetHistoryItem()) {
-    history_item->SetScrollRestorationType(scroll_restoration);
-    GetFrame()->Client()->DidUpdateCurrentHistoryItem();
-  }
+  GetHistoryItem()->SetScrollRestorationType(scroll_restoration);
+  DomWindow()->GetFrame()->Client()->DidUpdateCurrentHistoryItem();
 }
 
 String History::scrollRestoration(ExceptionState& exception_state) {
-  if (!GetFrame() || !GetFrame()->Client()) {
+  if (!DomWindow()) {
     exception_state.ThrowSecurityError(
         "May not use a History object associated with a Document that is not "
         "fully active");
@@ -167,22 +154,14 @@
 }
 
 mojom::blink::ScrollRestorationType History::ScrollRestorationInternal() const {
-  constexpr mojom::blink::ScrollRestorationType default_type =
-      mojom::blink::ScrollRestorationType::kAuto;
+  if (HistoryItem* history_item = GetHistoryItem())
+    return history_item->ScrollRestorationType();
+  return mojom::blink::ScrollRestorationType::kAuto;
+}
 
-  LocalFrame* frame = GetFrame();
-  if (!frame)
-    return default_type;
-
-  DocumentLoader* document_loader = frame->Loader().GetDocumentLoader();
-  if (!document_loader)
-    return default_type;
-
-  HistoryItem* history_item = document_loader->GetHistoryItem();
-  if (!history_item)
-    return default_type;
-
-  return history_item->ScrollRestorationType();
+HistoryItem* History::GetHistoryItem() const {
+  return DomWindow() ? DomWindow()->document()->Loader()->GetHistoryItem()
+                     : nullptr;
 }
 
 bool History::IsSameAsCurrentState(SerializedScriptValue* state) const {
@@ -201,7 +180,7 @@
 void History::go(ScriptState* script_state,
                  int delta,
                  ExceptionState& exception_state) {
-  if (!GetFrame() || !GetFrame()->Client()) {
+  if (!DomWindow()) {
     exception_state.ThrowSecurityError(
         "May not use a History object associated with a Document that is not "
         "fully active");
@@ -216,16 +195,16 @@
   if (!active_window->GetFrame() ||
       !active_window->GetFrame()->CanNavigate(*GetFrame()) ||
       !active_window->GetFrame()->IsNavigationAllowed() ||
-      !GetFrame()->IsNavigationAllowed()) {
+      !DomWindow()->GetFrame()->IsNavigationAllowed()) {
     return;
   }
 
-  if (!GetFrame()->navigation_rate_limiter().CanProceed())
+  if (!DomWindow()->GetFrame()->navigation_rate_limiter().CanProceed())
     return;
 
   if (delta) {
-    if (GetFrame()->Client()->NavigateBackForward(delta)) {
-      if (Page* page = GetFrame()->GetPage())
+    if (DomWindow()->GetFrame()->Client()->NavigateBackForward(delta)) {
+      if (Page* page = DomWindow()->GetFrame()->GetPage())
         page->HistoryNavigationVirtualTimePauser().PauseVirtualTime();
     }
   } else {
@@ -233,7 +212,7 @@
     // Otherwise, navigation happens on the root frame.
     // This behavior is designed in the following spec.
     // https://html.spec.whatwg.org/C/#dom-history-go
-    GetFrame()->Reload(WebFrameLoadType::kReload);
+    DomWindow()->GetFrame()->Reload(WebFrameLoadType::kReload);
   }
 }
 
@@ -244,9 +223,8 @@
                         ExceptionState& exception_state) {
   WebFrameLoadType load_type = WebFrameLoadType::kStandard;
   // Navigations in portal contexts do not create back/forward entries.
-  if (GetFrame() && GetFrame()->GetPage() &&
-      GetFrame()->GetPage()->InsidePortal()) {
-    GetFrame()->GetDocument()->AddConsoleMessage(
+  if (DomWindow() && DomWindow()->GetFrame()->GetPage()->InsidePortal()) {
+    DomWindow()->AddConsoleMessage(
         MakeGarbageCollected<ConsoleMessage>(
             mojom::ConsoleMessageSource::kJavaScript,
             mojom::ConsoleMessageLevel::kWarning,
@@ -287,14 +265,12 @@
 }
 
 KURL History::UrlForState(const String& url_string) {
-  Document* document = GetFrame()->GetDocument();
-
   if (url_string.IsNull())
-    return document->Url();
+    return DomWindow()->Url();
   if (url_string.IsEmpty())
-    return document->BaseURL();
+    return DomWindow()->BaseURL();
 
-  return KURL(document->BaseURL(), url_string);
+  return KURL(DomWindow()->BaseURL(), url_string);
 }
 
 bool History::CanChangeToUrl(const KURL& url,
@@ -332,8 +308,7 @@
     mojom::blink::ScrollRestorationType restoration_type,
     WebFrameLoadType type,
     ExceptionState& exception_state) {
-  if (!GetFrame() || !GetFrame()->GetPage() ||
-      !GetFrame()->Loader().GetDocumentLoader()) {
+  if (!DomWindow()) {
     exception_state.ThrowSecurityError(
         "May not use a History object associated with a Document that is not "
         "fully active");
@@ -341,20 +316,20 @@
   }
 
   KURL full_url = UrlForState(url_string);
-  if (!CanChangeToUrl(full_url, GetFrame()->DomWindow()->GetSecurityOrigin(),
-                      GetFrame()->GetDocument()->Url())) {
+  if (!CanChangeToUrl(full_url, DomWindow()->GetSecurityOrigin(),
+                      DomWindow()->Url())) {
     // We can safely expose the URL to JavaScript, as a) no redirection takes
     // place: JavaScript already had this URL, b) JavaScript can only access a
     // same-origin History object.
     exception_state.ThrowSecurityError(
         "A history state object with URL '" + full_url.ElidedString() +
         "' cannot be created in a document with origin '" +
-        GetFrame()->DomWindow()->GetSecurityOrigin()->ToString() +
-        "' and URL '" + GetFrame()->GetDocument()->Url().ElidedString() + "'.");
+        DomWindow()->GetSecurityOrigin()->ToString() + "' and URL '" +
+        DomWindow()->Url().ElidedString() + "'.");
     return;
   }
 
-  if (!GetFrame()->navigation_rate_limiter().CanProceed()) {
+  if (!DomWindow()->GetFrame()->navigation_rate_limiter().CanProceed()) {
     // TODO(769592): Get an API spec change so that we can throw an exception:
     //
     //  exception_state.ThrowDOMException(DOMExceptionCode::kQuotaExceededError,
@@ -365,9 +340,9 @@
     return;
   }
 
-  GetFrame()->GetDocument()->Loader()->UpdateForSameDocumentNavigation(
+  DomWindow()->document()->Loader()->UpdateForSameDocumentNavigation(
       full_url, kSameDocumentNavigationHistoryApi, std::move(data),
-      restoration_type, type, GetFrame()->GetDocument());
+      restoration_type, type, DomWindow()->document());
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/history.h b/third_party/blink/renderer/core/frame/history.h
index a228714..84d8edd 100644
--- a/third_party/blink/renderer/core/frame/history.h
+++ b/third_party/blink/renderer/core/frame/history.h
@@ -41,6 +41,7 @@
 class LocalFrame;
 class KURL;
 class ExceptionState;
+class HistoryItem;
 class SecurityOrigin;
 class ScriptState;
 
@@ -97,6 +98,7 @@
                         ExceptionState&);
   SerializedScriptValue* StateInternal() const;
   mojom::blink::ScrollRestorationType ScrollRestorationInternal() const;
+  HistoryItem* GetHistoryItem() const;
 
   scoped_refptr<SerializedScriptValue> last_state_object_requested_;
 };
diff --git a/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc b/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
index 62f85d6e..7587a08 100644
--- a/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
+++ b/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
@@ -1136,8 +1136,11 @@
     return false;
   }
 
-  // Webview crashes with accelerated small canvases TODO(crbug.com/1004304)
-  if (!RuntimeEnabledFeatures::AcceleratedSmallCanvasesEnabled()) {
+  // Webview crashes with accelerated small canvases (crbug.com/1004304)
+  // Experimenting to see if this still causes crashes (crbug.com/1136603)
+  if (!RuntimeEnabledFeatures::AcceleratedSmallCanvasesEnabled() &&
+      !base::FeatureList::IsEnabled(
+          features::kWebviewAccelerateSmallCanvases)) {
     base::CheckedNumeric<int> checked_canvas_pixel_count =
         Size().Width() * Size().Height();
     if (!checked_canvas_pixel_count.IsValid())
diff --git a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.cc
index 6911d05..435b7be 100644
--- a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.cc
@@ -35,6 +35,7 @@
         SetSpecifiedTracks();
         DetermineExplicitTrackStarts();
         ConstructAndAppendGridItems();
+
         // TODO(janewman): Split placement into its own GridLayoutAlgorithmState
         NGGridPlacement(
             automatic_row_repetitions_, automatic_column_repetitions_,
@@ -61,6 +62,7 @@
         algorithm_row_track_collection_ = NGGridLayoutAlgorithmTrackCollection(
             block_row_track_collection_, is_content_box_block_size_indefinite);
 
+        CacheItemSetIndices();
         state_ = GridLayoutAlgorithmState::kResolvingInlineSize;
         break;
       }
@@ -155,10 +157,19 @@
   return span.EndLine();
 }
 
+wtf_size_t NGGridLayoutAlgorithm::GridItemData::SpanSize(
+    GridTrackSizingDirection track_direction) const {
+  const GridSpan& span = (track_direction == kForColumns)
+                             ? resolved_position.columns
+                             : resolved_position.rows;
+  DCHECK(span.IsTranslatedDefinite());
+  return span.IntegerSpan();
+}
+
 const GridSpan& NGGridLayoutAlgorithm::GridItemData::Span(
-    GridTrackSizingDirection direction) const {
-  return (direction == kForColumns) ? resolved_position.columns
-                                    : resolved_position.rows;
+    GridTrackSizingDirection track_direction) const {
+  return (track_direction == kForColumns) ? resolved_position.columns
+                                          : resolved_position.rows;
 }
 
 void NGGridLayoutAlgorithm::GridItemData::SetSpan(
@@ -184,6 +195,12 @@
   return items_ != other.items_ || current_index_ != other.current_index_;
 }
 
+NGGridLayoutAlgorithm::GridItemData*
+NGGridLayoutAlgorithm::ReorderedGridItems::Iterator::operator->() {
+  DCHECK_LT(*current_index_, items_->size());
+  return &(items_->at(*current_index_));
+}
+
 NGGridLayoutAlgorithm::GridItemData&
 NGGridLayoutAlgorithm::ReorderedGridItems::Iterator::operator*() {
   DCHECK_LT(*current_index_, items_->size());
@@ -222,6 +239,43 @@
                                           : algorithm_row_track_collection_;
 }
 
+NGGridLayoutAlgorithmTrackCollection::SetIterator
+NGGridLayoutAlgorithm::GetSetIteratorForItem(
+    const GridItemData& item,
+    GridTrackSizingDirection track_direction) {
+  auto& track_collection = TrackCollection(track_direction);
+  return track_collection.GetSetIterator(
+      (track_direction == kForColumns) ? item.columns_begin_set_index
+                                       : item.rows_begin_set_index,
+      (track_direction == kForColumns) ? item.columns_end_set_index
+                                       : item.rows_end_set_index);
+}
+
+// TODO(ethavar): Current implementation of this method simply returns the
+// preferred size of the grid item in the relevant direction. We should follow
+// the definitions from https://drafts.csswg.org/css-grid-1/#algo-spanning-items
+// (i.e. compute minimum, min-content, and max-content contributions).
+LayoutUnit NGGridLayoutAlgorithm::ContributionSizeForGridItem(
+    const GridItemData& grid_item,
+    GridTrackSizingDirection track_direction,
+    NGGridItemContributionType contribution_type) const {
+  const ComputedStyle& grid_item_style = grid_item.node.Style();
+  GridTrackSizingDirection grid_item_track_direction = track_direction;
+
+  bool is_orthogonal_grid_item = Style().IsHorizontalWritingMode() ==
+                                 grid_item_style.IsHorizontalWritingMode();
+  if (is_orthogonal_grid_item) {
+    grid_item_track_direction =
+        (track_direction == kForColumns) ? kForRows : kForColumns;
+  }
+
+  Length length = (grid_item_track_direction == kForColumns)
+                      ? grid_item_style.LogicalWidth()
+                      : grid_item_style.LogicalHeight();
+  return length.IsFixed() ? MinimumValueForLength(length, kIndefiniteSize)
+                          : LayoutUnit();
+}
+
 void NGGridLayoutAlgorithm::ConstructAndAppendGridItems() {
   NGGridChildIterator iterator(Node());
   for (NGBlockNode child = iterator.NextChild(); child;
@@ -346,6 +400,41 @@
   }
 }
 
+void NGGridLayoutAlgorithm::CacheItemSetIndices() {
+  auto CacheItemSetIndices = [this](GridTrackSizingDirection track_direction) {
+    const auto& track_collection = TrackCollection(track_direction);
+    for (GridItemData& item : items_) {
+      wtf_size_t first_spanned_range =
+          track_collection.RangeIndexFromTrackNumber(
+              item.StartLine(track_direction));
+      wtf_size_t last_spanned_range =
+          track_collection.RangeIndexFromTrackNumber(
+              item.EndLine(track_direction) - 1);
+
+      DCHECK_LE(first_spanned_range, last_spanned_range);
+      wtf_size_t begin_set_index =
+          track_collection.RangeStartingSetIndex(first_spanned_range);
+      wtf_size_t end_set_index =
+          track_collection.RangeStartingSetIndex(last_spanned_range) +
+          track_collection.RangeSetCount(last_spanned_range);
+
+      DCHECK_LE(begin_set_index, end_set_index);
+      DCHECK_LE(end_set_index, track_collection.SetCount());
+
+      if (track_direction == kForColumns) {
+        item.columns_begin_set_index = begin_set_index;
+        item.columns_end_set_index = end_set_index;
+      } else {
+        item.rows_begin_set_index = begin_set_index;
+        item.rows_end_set_index = end_set_index;
+      }
+    }
+  };
+
+  CacheItemSetIndices(kForColumns);
+  CacheItemSetIndices(kForRows);
+}
+
 void NGGridLayoutAlgorithm::DetermineGridItemsSpanningIntrinsicOrFlexTracks(
     GridTrackSizingDirection track_direction) {
   auto CompareGridItemsByStartLine =
@@ -410,8 +499,7 @@
 // https://drafts.csswg.org/css-grid-1/#algo-track-sizing
 void NGGridLayoutAlgorithm::ComputeUsedTrackSizes(
     GridTrackSizingDirection track_direction) {
-  NGGridLayoutAlgorithmTrackCollection& track_collection =
-      TrackCollection(track_direction);
+  auto& track_collection = TrackCollection(track_direction);
   LayoutUnit content_box_size = (track_direction == kForColumns)
                                     ? child_percentage_size_.inline_size
                                     : child_percentage_size_.block_size;
@@ -429,7 +517,7 @@
 
       // A fixed sizing function: Resolve to an absolute length and use that
       // size as the track’s initial base size.
-      LayoutUnit fixed_min_breadth = ValueForLength(
+      LayoutUnit fixed_min_breadth = MinimumValueForLength(
           track_size.MinTrackBreadth().length(), content_box_size);
       current_set.SetBaseSize(fixed_min_breadth * current_set.TrackCount());
     } else {
@@ -438,6 +526,8 @@
       current_set.SetBaseSize(LayoutUnit());
     }
 
+    // Note that, since |NGGridSet| initializes its growth limit as indefinite,
+    // an intrinsic or flexible sizing function needs no further resolution.
     if (track_size.HasFixedMaxTrackBreadth()) {
       DCHECK(!track_size.MaxTrackBreadth().HasPercentage() ||
              content_box_size != kIndefiniteSize);
@@ -445,17 +535,434 @@
       // A fixed sizing function: Resolve to an absolute length and use that
       // size as the track’s initial growth limit; if the growth limit is less
       // than the base size, increase the growth limit to match the base size.
-      LayoutUnit fixed_max_breadth = ValueForLength(
+      LayoutUnit fixed_max_breadth = MinimumValueForLength(
           track_size.MaxTrackBreadth().length(), content_box_size);
       current_set.SetGrowthLimit(
           std::max(current_set.BaseSize(),
                    fixed_max_breadth * current_set.TrackCount()));
-    } else {
-      // An intrinsic or flexible sizing function: Use an initial growth limit
-      // of infinity.
-      current_set.SetGrowthLimit(kIndefiniteSize);
     }
   }
+
+  // 2. Resolve intrinsic track sizing functions to absolute lengths.
+  DetermineGridItemsSpanningIntrinsicOrFlexTracks(track_direction);
+  ResolveIntrinsicTrackSizes(track_direction);
+}
+
+// Helpers for the track sizing algorithm.
+namespace {
+
+// Returns the corresponding size to be increased by accommodating a grid item's
+// contribution; for intrinsic min track sizing functions, return the base size.
+// For intrinsic max track sizing functions, return the growth limit.
+static LayoutUnit AffectedSizeForContribution(
+    const NGGridSet& set,
+    NGGridItemContributionType contribution_type) {
+  switch (contribution_type) {
+    case NGGridItemContributionType::kForIntrinsicMinimums:
+    case NGGridItemContributionType::kForContentBasedMinimums:
+    case NGGridItemContributionType::kForMaxContentMinimums:
+      return set.BaseSize();
+    case NGGridItemContributionType::kForIntrinsicMaximums:
+    case NGGridItemContributionType::kForMaxContentMaximums:
+      LayoutUnit growth_limit = set.GrowthLimit();
+      // For infinite growth limits, substitute with the track's base size.
+      if (growth_limit == kIndefiniteSize)
+        return set.BaseSize();
+      return growth_limit;
+  }
+}
+
+static void GrowAffectedSizeByPlannedIncrease(
+    NGGridSet& set,
+    NGGridItemContributionType contribution_type) {
+  switch (contribution_type) {
+    case NGGridItemContributionType::kForIntrinsicMinimums:
+    case NGGridItemContributionType::kForContentBasedMinimums:
+    case NGGridItemContributionType::kForMaxContentMinimums:
+      set.SetBaseSize(set.BaseSize() + set.PlannedIncrease());
+      break;
+    case NGGridItemContributionType::kForIntrinsicMaximums:
+    case NGGridItemContributionType::kForMaxContentMaximums:
+      LayoutUnit growth_limit = set.GrowthLimit();
+      // If the affected size to grow is an infinite growth limit, set it to the
+      // track's base size plus the planned increase.
+      if (growth_limit == kIndefiniteSize)
+        set.SetGrowthLimit(set.BaseSize() + set.PlannedIncrease());
+      else
+        set.SetGrowthLimit(growth_limit + set.PlannedIncrease());
+      break;
+  }
+}
+
+// Returns true if a set should increase its used size according to the steps in
+// https://drafts.csswg.org/css-grid-1/#algo-spanning-items; false otherwise.
+static bool IsContributionAppliedToSet(
+    const NGGridSet& set,
+    NGGridItemContributionType contribution_type) {
+  switch (contribution_type) {
+    case NGGridItemContributionType::kForIntrinsicMinimums:
+      return set.TrackSize().HasIntrinsicMinTrackBreadth();
+    case NGGridItemContributionType::kForContentBasedMinimums:
+      return set.TrackSize().HasMinOrMaxContentMinTrackBreadth();
+    case NGGridItemContributionType::kForMaxContentMinimums:
+      // TODO(ethavar): Check if the grid container is being sized under a
+      // 'max-content' constraint to consider 'auto' min track sizing functions,
+      // see https://drafts.csswg.org/css-grid-1/#track-size-max-content-min.
+      return set.TrackSize().HasMaxContentMinTrackBreadth();
+    case NGGridItemContributionType::kForIntrinsicMaximums:
+      return set.TrackSize().HasIntrinsicMaxTrackBreadth();
+    case NGGridItemContributionType::kForMaxContentMaximums:
+      return set.TrackSize().HasMaxContentOrAutoMaxTrackBreadth();
+  }
+}
+
+// https://drafts.csswg.org/css-grid-1/#extra-space
+// Returns true if a set's used size should be consider to grow beyond its limit
+// (see the "Distribute space beyond limits" section); otherwise, false.
+// Note that we will deliberately return false in cases where we don't have a
+// collection of tracks different than "all affected tracks".
+static bool ShouldUsedSizeGrowBeyondLimit(
+    const NGGridSet& set,
+    NGGridItemContributionType contribution_type) {
+  // This function assumes that we already determined that extra space
+  // distribution will be applied to the specified set.
+  DCHECK(IsContributionAppliedToSet(set, contribution_type));
+
+  switch (contribution_type) {
+    case NGGridItemContributionType::kForIntrinsicMinimums:
+    case NGGridItemContributionType::kForContentBasedMinimums:
+      return set.TrackSize().HasIntrinsicMaxTrackBreadth();
+    case NGGridItemContributionType::kForMaxContentMinimums:
+      return set.TrackSize().HasMaxContentMaxTrackBreadth();
+    case NGGridItemContributionType::kForIntrinsicMaximums:
+    case NGGridItemContributionType::kForMaxContentMaximums:
+      return false;
+  }
+}
+
+static bool IsDistributionForGrowthLimits(
+    NGGridItemContributionType contribution_type) {
+  switch (contribution_type) {
+    case NGGridItemContributionType::kForIntrinsicMinimums:
+    case NGGridItemContributionType::kForContentBasedMinimums:
+    case NGGridItemContributionType::kForMaxContentMinimums:
+      return false;
+    case NGGridItemContributionType::kForIntrinsicMaximums:
+    case NGGridItemContributionType::kForMaxContentMaximums:
+      return true;
+  }
+}
+
+enum class InfinitelyGrowableBehavior { kEnforce, kIgnore };
+
+// We define growth potential = limit - affected size; for base sizes, the limit
+// is its growth limit. For growth limits, the limit is infinity if it is marked
+// as "infinitely growable", and equal to the growth limit otherwise.
+static LayoutUnit GrowthPotentialForSet(
+    const NGGridSet& set,
+    NGGridItemContributionType contribution_type,
+    InfinitelyGrowableBehavior infinitely_growable_behavior =
+        InfinitelyGrowableBehavior::kEnforce) {
+  switch (contribution_type) {
+    case NGGridItemContributionType::kForIntrinsicMinimums:
+    case NGGridItemContributionType::kForContentBasedMinimums:
+    case NGGridItemContributionType::kForMaxContentMinimums: {
+      LayoutUnit growth_limit = set.GrowthLimit();
+      return (growth_limit == kIndefiniteSize) ? kIndefiniteSize
+                                               : growth_limit - set.BaseSize();
+    }
+    case NGGridItemContributionType::kForIntrinsicMaximums:
+    case NGGridItemContributionType::kForMaxContentMaximums: {
+      if (infinitely_growable_behavior ==
+              InfinitelyGrowableBehavior::kEnforce &&
+          !set.IsInfinitelyGrowable()) {
+        // If the affected size was a growth limit and the track is not marked
+        // infinitely growable, then the item-incurred increase will be zero.
+        return LayoutUnit();
+      }
+
+      LayoutUnit growth_limit = set.GrowthLimit();
+      LayoutUnit fit_content_limit = set.FitContentLimit();
+      DCHECK(growth_limit >= 0 || growth_limit == kIndefiniteSize);
+      DCHECK(fit_content_limit >= 0 || fit_content_limit == kIndefiniteSize);
+
+      // The max track sizing function of a 'fit-content' track is treated as
+      // 'max-content' until it reaches the limit specified as the 'fit-content'
+      // argument, after which it is treated as having a fixed sizing function
+      // of that argument (with a growth potential of zero).
+      if (fit_content_limit != kIndefiniteSize) {
+        LayoutUnit growth_potential = (growth_limit != kIndefiniteSize)
+                                          ? fit_content_limit - growth_limit
+                                          : fit_content_limit;
+        return growth_potential.ClampNegativeToZero();
+      }
+      // Otherwise, this set has infinite growth potential.
+      return kIndefiniteSize;
+    }
+  }
+}
+
+}  // namespace
+
+// Follow the definitions from https://drafts.csswg.org/css-grid-1/#extra-space;
+// notice that this method replaces the notion of "tracks" with "sets".
+void NGGridLayoutAlgorithm::DistributeExtraSpaceToSets(
+    LayoutUnit extra_space,
+    NGGridItemContributionType contribution_type,
+    NGGridSetVector* sets_to_grow,
+    NGGridSetVector* sets_to_grow_beyond_limit) {
+  DCHECK(sets_to_grow && extra_space >= 0);
+  if (!extra_space)
+    return;
+
+#if DCHECK_IS_ON()
+  if (IsDistributionForGrowthLimits(contribution_type))
+    DCHECK_EQ(sets_to_grow, sets_to_grow_beyond_limit);
+#endif
+
+  wtf_size_t total_track_count = 0;
+  for (NGGridSet* set : *sets_to_grow) {
+    set->SetItemIncurredIncrease(LayoutUnit());
+
+    // From the first note in https://drafts.csswg.org/css-grid-1/#extra-space:
+    //   - If the affected size was a growth limit and the track is not marked
+    //   "infinitely growable", then each item-incurred increase will be zero.
+    //
+    // When distributing space to growth limits, we need to increase each track
+    // up to its 'fit-content' limit. However, because of the note above, first
+    // we should only grow tracks marked as "infinitely growable" up to limits
+    // and then grow all affected tracks beyond limits.
+    //
+    // We can correctly resolve every scenario by doing a single sort of
+    // |sets_to_grow|, purposely ignoring the "infinitely growable" flag, then
+    // filtering out which sets count toward the total track count at each step;
+    // for base sizes this is not required, but if there are no tracks with
+    // growth potential > 0, we can optimize by not sorting the sets.
+    LayoutUnit growth_potential =
+        GrowthPotentialForSet(*set, contribution_type);
+    DCHECK(growth_potential >= 0 || growth_potential == kIndefiniteSize);
+    if (growth_potential)
+      total_track_count += set->TrackCount();
+  }
+
+  // We will sort the tracks by growth potential in non-decreasing order to
+  // distribute space up to limits; notice that if we start distributing space
+  // equally among all tracks we will eventually reach the limit of a track or
+  // run out of space to distribute. If the former scenario happens, it should
+  // be easy to see that the group of tracks that will reach its limit first
+  // will be that with the least growth potential. Otherwise, if tracks in such
+  // group does not reach their limit, every upcoming track with greater growth
+  // potential must be able to increase its size by the same amount.
+  if (total_track_count || IsDistributionForGrowthLimits(contribution_type)) {
+    auto CompareSetsByGrowthPotential = [contribution_type](NGGridSet* set_a,
+                                                            NGGridSet* set_b) {
+      LayoutUnit growth_potential_a = GrowthPotentialForSet(
+          *set_a, contribution_type, InfinitelyGrowableBehavior::kIgnore);
+      LayoutUnit growth_potential_b = GrowthPotentialForSet(
+          *set_b, contribution_type, InfinitelyGrowableBehavior::kIgnore);
+
+      if (growth_potential_a == kIndefiniteSize ||
+          growth_potential_b == kIndefiniteSize) {
+        // At this point we know that there is at least one set with infinite
+        // growth potential; if |set_a| has a definite value, then |set_b| must
+        // have infinite growth potential, and thus, |set_a| < |set_b|.
+        return growth_potential_a != kIndefiniteSize;
+      }
+      // Straightforward comparison of definite growth potentials.
+      return growth_potential_a < growth_potential_b;
+    };
+    std::sort(sets_to_grow->begin(), sets_to_grow->end(),
+              CompareSetsByGrowthPotential);
+  }
+
+  auto ClampSize = [](LayoutUnit& size, LayoutUnit limit) {
+    size = (limit != kIndefiniteSize) ? std::min(size, limit) : size;
+  };
+
+  // Distribute space up to limits:
+  //   - For base sizes, grow the base size up to the growth limit.
+  //   - For growth limits, the only case where a growth limit should grow at
+  //   this step is when the set has already been marked "infinitely growable".
+  //   Increase the growth limit up to the 'fit-content' argument (if any); note
+  //   that these arguments could prevent this step to fulfill the entirety of
+  //   the extra space and further distribution would be needed.
+  if (total_track_count) {
+    for (NGGridSet* set : *sets_to_grow) {
+      LayoutUnit growth_potential =
+          GrowthPotentialForSet(*set, contribution_type);
+
+      if (growth_potential) {
+        wtf_size_t set_track_count = set->TrackCount();
+        LayoutUnit extra_space_share =
+            (extra_space * set_track_count) / total_track_count;
+        DCHECK_GE(extra_space_share, 0);
+
+        ClampSize(extra_space_share, growth_potential);
+        set->SetItemIncurredIncrease(extra_space_share);
+
+        total_track_count -= set_track_count;
+        extra_space -= extra_space_share;
+        DCHECK_GE(total_track_count, 0u);
+        DCHECK_GE(extra_space, 0);
+      }
+    }
+  }
+
+  // Distribute space beyond limits:
+  //   - For base sizes, every affected track can grow indefinitely.
+  //   - For growth limits, grow tracks up to their 'fit-content' argument.
+  if (sets_to_grow_beyond_limit && extra_space) {
+    total_track_count = 0;
+    for (NGGridSet* set : *sets_to_grow_beyond_limit)
+      total_track_count += set->TrackCount();
+
+    for (NGGridSet* set : *sets_to_grow_beyond_limit) {
+      wtf_size_t set_track_count = set->TrackCount();
+      LayoutUnit extra_space_share =
+          (extra_space * set_track_count) / total_track_count;
+      DCHECK_GE(extra_space_share, 0);
+
+      // Ignore the "infinitely growable" flag and grow all affected tracks.
+      if (IsDistributionForGrowthLimits(contribution_type)) {
+        LayoutUnit growth_potential = GrowthPotentialForSet(
+            *set, contribution_type, InfinitelyGrowableBehavior::kIgnore);
+        ClampSize(extra_space_share, growth_potential);
+      }
+      set->SetItemIncurredIncrease(set->ItemIncurredIncrease() +
+                                   extra_space_share);
+
+      total_track_count -= set_track_count;
+      extra_space -= extra_space_share;
+      DCHECK_GE(total_track_count, 0u);
+      DCHECK_GE(extra_space, 0);
+    }
+  }
+
+  // For each affected track, if the track's item-incurred increase is larger
+  // than its planned increase, set the planned increase to that value.
+  for (NGGridSet* set : *sets_to_grow) {
+    set->SetPlannedIncrease(
+        std::max(set->ItemIncurredIncrease(), set->PlannedIncrease()));
+  }
+}
+
+void NGGridLayoutAlgorithm::IncreaseTrackSizesToAccommodateGridItems(
+    GridTrackSizingDirection track_direction,
+    ReorderedGridItems::Iterator group_begin,
+    ReorderedGridItems::Iterator group_end,
+    NGGridItemContributionType contribution_type) {
+  auto& track_collection = TrackCollection(track_direction);
+  for (auto set_iterator = track_collection.GetSetIterator();
+       !set_iterator.IsAtEnd(); set_iterator.MoveToNextSet()) {
+    set_iterator.CurrentSet().SetPlannedIncrease(LayoutUnit());
+  }
+
+  NGGridSetVector sets_to_grow;
+  NGGridSetVector sets_to_grow_beyond_limit;
+  for (auto grid_item = group_begin; grid_item != group_end; ++grid_item) {
+    // TODO(ethavar): Remove the |IsOutOfFlowPositioned| condition once the
+    // out-of-flow items are stored separately.
+    if (!grid_item->is_spanning_intrinsic_track ||
+        grid_item->node.IsOutOfFlowPositioned()) {
+      // Don't consider items not spanning intrinsic tracks in this step;
+      // absolute positioned items don't affect track sizing.
+      continue;
+    }
+
+    sets_to_grow.Shrink(0);
+    sets_to_grow_beyond_limit.Shrink(0);
+
+    LayoutUnit spanned_tracks_size =
+        GridGap(track_direction) * (grid_item->SpanSize(track_direction) - 1);
+    for (auto set_iterator = GetSetIteratorForItem(*grid_item, track_direction);
+         !set_iterator.IsAtEnd(); set_iterator.MoveToNextSet()) {
+      NGGridSet& current_set = set_iterator.CurrentSet();
+
+      spanned_tracks_size +=
+          AffectedSizeForContribution(current_set, contribution_type);
+      if (IsContributionAppliedToSet(current_set, contribution_type)) {
+        sets_to_grow.push_back(&current_set);
+        if (ShouldUsedSizeGrowBeyondLimit(current_set, contribution_type))
+          sets_to_grow_beyond_limit.push_back(&current_set);
+      }
+    }
+
+    if (sets_to_grow.IsEmpty())
+      continue;
+
+    // Subtract the corresponding size (base size or growth limit) of every
+    // spanned track from the grid item's size contribution to find the item's
+    // remaining size contribution. For infinite growth limits, substitute with
+    // the track's base size. This is the space to distribute, floor it at zero.
+    LayoutUnit extra_space = ContributionSizeForGridItem(
+        *grid_item, track_direction, contribution_type);
+    extra_space -= spanned_tracks_size;
+
+    DistributeExtraSpaceToSets(
+        extra_space.ClampNegativeToZero(), contribution_type, &sets_to_grow,
+        sets_to_grow_beyond_limit.IsEmpty() ? &sets_to_grow
+                                            : &sets_to_grow_beyond_limit);
+  }
+
+  for (auto set_iterator = track_collection.GetSetIterator();
+       !set_iterator.IsAtEnd(); set_iterator.MoveToNextSet()) {
+    GrowAffectedSizeByPlannedIncrease(set_iterator.CurrentSet(),
+                                      contribution_type);
+  }
+}
+
+// https://drafts.csswg.org/css-grid-1/#algo-content
+void NGGridLayoutAlgorithm::ResolveIntrinsicTrackSizes(
+    GridTrackSizingDirection track_direction) {
+  // Reorder grid items to process them as follows:
+  //   - First, consider items spanning a single non-flexible track.
+  //   - Next, consider items with span size of 2 not spanning a flexible track.
+  //   - Repeat incrementally for items with greater span sizes until all items
+  //   not spanning a flexible track have been considered.
+  //   - Finally, consider all items spanning a flexible track.
+  auto CompareGridItemsForIntrinsicTrackResolution =
+      [this, track_direction](wtf_size_t index_a, wtf_size_t index_b) -> bool {
+    if (items_[index_a].is_spanning_flex_track ||
+        items_[index_b].is_spanning_flex_track) {
+      // Ignore span sizes if one of the items spans a track with a flexible
+      // sizing function; items not spanning such tracks should come first.
+      return !items_[index_a].is_spanning_flex_track;
+    }
+    return items_[index_a].SpanSize(track_direction) <
+           items_[index_b].SpanSize(track_direction);
+  };
+  std::sort(reordered_item_indices_.begin(), reordered_item_indices_.end(),
+            CompareGridItemsForIntrinsicTrackResolution);
+
+  // First, process the items that don't span a flexible track.
+  ReorderedGridItems grid_items = GetReorderedGridItems();
+  ReorderedGridItems::Iterator current_group_begin = grid_items.begin();
+
+  while (current_group_begin != grid_items.end() &&
+         !current_group_begin->is_spanning_flex_track) {
+    // Each iteration considers all items with the same span size.
+    wtf_size_t current_group_span_size =
+        current_group_begin->SpanSize(track_direction);
+    ReorderedGridItems::Iterator current_group_end = current_group_begin;
+    do {
+      DCHECK(!current_group_end->is_spanning_flex_track);
+      ++current_group_end;
+    } while (current_group_end != grid_items.end() &&
+             !current_group_end->is_spanning_flex_track &&
+             current_group_end->SpanSize(track_direction) ==
+                 current_group_span_size);
+
+    IncreaseTrackSizesToAccommodateGridItems(
+        track_direction, current_group_begin, current_group_end,
+        NGGridItemContributionType::kForIntrinsicMinimums);
+
+    // TODO(ethavar): Add remaining stages, mark infinitely growable sets...
+    current_group_begin = current_group_end;
+  }
+
+  // TODO(ethavar): drafts.csswg.org/css-grid-1/#algo-spanning-flex-items
+  // Repeat the previous step instead considering (together, rather than grouped
+  // by span) all items that do span a track with a flexible sizing function.
 }
 
 void NGGridLayoutAlgorithm::SetAutomaticTrackRepetitionsForTesting(
@@ -559,7 +1066,7 @@
   // auto-sized grids.
   if (gap->IsPercentOrCalc() && available_size == kIndefiniteSize)
     return LayoutUnit();
-  return ValueForLength(*gap, available_size);
+  return MinimumValueForLength(*gap, available_size);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.h b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.h
index aa1b4c029..1e565d1b5 100644
--- a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.h
+++ b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.h
@@ -14,11 +14,24 @@
 
 namespace blink {
 
+// This enum corresponds to each step used to accommodate grid items across
+// intrinsic tracks according to their min and max track sizing functions, as
+// defined in https://drafts.csswg.org/css-grid-1/#algo-spanning-items.
+enum class NGGridItemContributionType {
+  kForIntrinsicMinimums,
+  kForContentBasedMinimums,
+  kForMaxContentMinimums,
+  kForIntrinsicMaximums,
+  kForMaxContentMaximums
+};
+
 class CORE_EXPORT NGGridLayoutAlgorithm
     : public NGLayoutAlgorithm<NGBlockNode,
                                NGBoxFragmentBuilder,
                                NGBlockBreakToken> {
  public:
+  // TODO(janewman): Move this enum out of |NGGridLayoutAlgorithm| to be
+  // consistent with |NGGridItemContributionType|.
   enum class AutoPlacementType { kNotNeeded, kMajor, kMinor, kBoth };
 
   struct GridItemData {
@@ -26,10 +39,14 @@
 
     AutoPlacementType AutoPlacement(
         GridTrackSizingDirection flow_direction) const;
-    wtf_size_t StartLine(GridTrackSizingDirection direction) const;
-    wtf_size_t EndLine(GridTrackSizingDirection direction) const;
-    const GridSpan& Span(GridTrackSizingDirection direction) const;
-    void SetSpan(const GridSpan& span, GridTrackSizingDirection direction);
+    const GridSpan& Span(GridTrackSizingDirection track_direction) const;
+    void SetSpan(const GridSpan& span,
+                 GridTrackSizingDirection track_direction);
+
+    wtf_size_t StartLine(GridTrackSizingDirection track_direction) const;
+    wtf_size_t EndLine(GridTrackSizingDirection track_direction) const;
+    wtf_size_t SpanSize(GridTrackSizingDirection track_direction) const;
+
     const NGBlockNode node;
     GridArea resolved_position;
 
@@ -37,6 +54,13 @@
     LayoutUnit inline_size;
     MinMaxSizes min_max_sizes;
 
+    // These fields are used to determine the sets this item spans in the
+    // respective track collection; see |CacheItemSetIndices|.
+    wtf_size_t columns_begin_set_index;
+    wtf_size_t columns_end_set_index;
+    wtf_size_t rows_begin_set_index;
+    wtf_size_t rows_end_set_index;
+
     bool is_spanning_flex_track : 1;
     bool is_spanning_intrinsic_track : 1;
   };
@@ -51,6 +75,8 @@
   const NGGridLayoutAlgorithmTrackCollection& RowTrackCollection() const;
 
  private:
+  using NGGridSetVector = Vector<NGGridSet*, 16>;
+
   friend class NGGridLayoutAlgorithmTest;
 
   enum class GridLayoutAlgorithmState {
@@ -70,6 +96,7 @@
                Vector<GridItemData>* items);
 
       bool operator!=(const Iterator& other) const;
+      GridItemData* operator->();
       GridItemData& operator*();
       Iterator& operator++();
 
@@ -92,18 +119,32 @@
   NGGridLayoutAlgorithmTrackCollection& TrackCollection(
       GridTrackSizingDirection track_direction);
 
+  // Returns an iterator for every |NGGridSet| contained within an item's span
+  // in the relevant track collection.
+  NGGridLayoutAlgorithmTrackCollection::SetIterator GetSetIteratorForItem(
+      const GridItemData& item,
+      GridTrackSizingDirection track_direction);
+
+  // Returns the size that a grid item will distribute across the tracks with an
+  // intrinsic sizing function it spans in the relevant track direction.
+  LayoutUnit ContributionSizeForGridItem(
+      const GridItemData& grid_item,
+      GridTrackSizingDirection track_direction,
+      NGGridItemContributionType contribution_type) const;
+
   void ConstructAndAppendGridItems();
   GridItemData MeasureGridItem(const NGBlockNode node);
   NGConstraintSpace BuildSpaceForGridItem(const NGBlockNode node) const;
 
   // Sets the specified tracks for row and column track lists.
   void SetSpecifiedTracks();
-  // Ensures a range boundary will exist on the start and end of the grid item.
-  void EnsureTrackCoverageForGridItem(GridTrackSizingDirection track_direction,
-                                      GridItemData& grid_item);
   // Determines the explicit column and row track starts.
   void DetermineExplicitTrackStarts();
 
+  // For every item and track direction, computes and stores the pair of indices
+  // "begin" and "end" such that the item spans every set from the respective
+  // collection's |sets_| with an index in the range [begin, end).
+  void CacheItemSetIndices();
   // For every grid item, determines if it spans a track with an intrinsic or
   // flexible sizing function and caches the answer in its |GridItemData|.
   void DetermineGridItemsSpanningIntrinsicOrFlexTracks(
@@ -112,6 +153,19 @@
   // Calculates from the min and max track sizing functions the used track size.
   void ComputeUsedTrackSizes(GridTrackSizingDirection track_direction);
 
+  // These methods implement the steps of the algorithm for intrinsic track size
+  // resolution defined in https://drafts.csswg.org/css-grid-1/#algo-content.
+  void ResolveIntrinsicTrackSizes(GridTrackSizingDirection track_direction);
+  void IncreaseTrackSizesToAccommodateGridItems(
+      GridTrackSizingDirection track_direction,
+      ReorderedGridItems::Iterator group_begin,
+      ReorderedGridItems::Iterator group_end,
+      NGGridItemContributionType contribution_type);
+  void DistributeExtraSpaceToSets(LayoutUnit extra_space,
+                                  NGGridItemContributionType contribution_type,
+                                  NGGridSetVector* sets_to_grow,
+                                  NGGridSetVector* sets_to_grow_beyond_limit);
+
   // Allows a test to set the value for automatic track repetition.
   void SetAutomaticTrackRepetitionsForTesting(wtf_size_t auto_column,
                                               wtf_size_t auto_row);
diff --git a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_track_collection.cc b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_track_collection.cc
index 95d5d478..5976eb70 100644
--- a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_track_collection.cc
+++ b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_track_collection.cc
@@ -379,7 +379,10 @@
 // 'auto', but we will normalize it directly as 'minmax(auto, max-content)'.
 NGGridSet::NGGridSet(wtf_size_t track_count, bool is_collapsed)
     : track_count_(track_count),
-      track_size_(Length::Auto(), Length::MaxContent()) {
+      track_size_(Length::Auto(), Length::MaxContent()),
+      growth_limit_(kIndefiniteSize),
+      fit_content_limit_(kIndefiniteSize),
+      is_infinitely_growable_(false) {
   if (is_collapsed) {
     // From https://drafts.csswg.org/css-grid-1/#collapsed-track: "A collapsed
     // track is treated as having a fixed track sizing function of '0px'".
@@ -390,7 +393,11 @@
 NGGridSet::NGGridSet(wtf_size_t track_count,
                      const GridTrackSize& track_size,
                      bool is_content_box_size_indefinite)
-    : track_count_(track_count), track_size_(track_size) {
+    : track_count_(track_count),
+      track_size_(track_size),
+      growth_limit_(kIndefiniteSize),
+      fit_content_limit_(kIndefiniteSize),
+      is_infinitely_growable_(false) {
   if (track_size_.IsFitContent()) {
     DCHECK(track_size_.FitContentTrackBreadth().IsLength());
 
@@ -430,15 +437,39 @@
          track_size_.GetType() == kMinMaxTrackSizing);
 }
 
+bool NGGridSet::IsGrowthLimitLessThanBaseSize() const {
+  return growth_limit_ != kIndefiniteSize && growth_limit_ < base_size_;
+}
+
+void NGGridSet::EnsureGrowthLimitIsNotLessThanBaseSize() {
+  if (IsGrowthLimitLessThanBaseSize())
+    growth_limit_ = base_size_;
+}
+
+LayoutUnit NGGridSet::BaseSize() const {
+  DCHECK(!IsGrowthLimitLessThanBaseSize());
+  return base_size_;
+}
+
 void NGGridSet::SetBaseSize(LayoutUnit base_size) {
-  DCHECK_NE(base_size_, kIndefiniteSize);
+  // Expect base size to always grow monotonically.
+  DCHECK_NE(base_size, kIndefiniteSize);
   DCHECK_LE(base_size_, base_size);
   base_size_ = base_size;
+  EnsureGrowthLimitIsNotLessThanBaseSize();
+}
+
+LayoutUnit NGGridSet::GrowthLimit() const {
+  DCHECK(!IsGrowthLimitLessThanBaseSize());
+  return growth_limit_;
 }
 
 void NGGridSet::SetGrowthLimit(LayoutUnit growth_limit) {
-  DCHECK(growth_limit_ == kIndefiniteSize || growth_limit == kIndefiniteSize ||
-         growth_limit_ <= growth_limit);
+  // Growth limit is initialized as infinity; expect it to change from infinity
+  // to a definite value and then to always grow monotonically.
+  DCHECK_NE(growth_limit, kIndefiniteSize);
+  DCHECK(!IsGrowthLimitLessThanBaseSize());
+  DCHECK(growth_limit_ == kIndefiniteSize || growth_limit_ <= growth_limit);
   growth_limit_ = growth_limit;
 }
 
@@ -577,13 +608,23 @@
 }
 
 NGGridLayoutAlgorithmTrackCollection::SetIterator
-NGGridLayoutAlgorithmTrackCollection::IteratorForRange(wtf_size_t range_index) {
-  DCHECK_LT(range_index, RangeCount());
+NGGridLayoutAlgorithmTrackCollection::GetSetIterator(wtf_size_t begin_set_index,
+                                                     wtf_size_t end_set_index) {
+  DCHECK_LE(end_set_index, SetCount());
+  DCHECK_LE(begin_set_index, end_set_index);
+  return SetIterator(this, begin_set_index, end_set_index);
+}
 
-  const Range& range = ranges_[range_index];
-  DCHECK_LE(range.starting_set_index + range.set_count, SetCount());
-  return SetIterator(this, range.starting_set_index,
-                     range.starting_set_index + range.set_count);
+wtf_size_t NGGridLayoutAlgorithmTrackCollection::RangeSetCount(
+    wtf_size_t range_index) const {
+  DCHECK_LT(range_index, RangeCount());
+  return ranges_[range_index].set_count;
+}
+
+wtf_size_t NGGridLayoutAlgorithmTrackCollection::RangeStartingSetIndex(
+    wtf_size_t range_index) const {
+  DCHECK_LT(range_index, RangeCount());
+  return ranges_[range_index].starting_set_index;
 }
 
 bool NGGridLayoutAlgorithmTrackCollection::IsRangeSpanningIntrinsicTrack(
diff --git a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_track_collection.h b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_track_collection.h
index 905e9eb..aa0791f 100644
--- a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_track_collection.h
+++ b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_track_collection.h
@@ -180,21 +180,39 @@
   wtf_size_t TrackCount() const { return track_count_; }
   const GridTrackSize& TrackSize() const { return track_size_; }
 
-  LayoutUnit BaseSize() const { return base_size_; }
-  LayoutUnit GrowthLimit() const { return growth_limit_; }
+  LayoutUnit BaseSize() const;
+  LayoutUnit GrowthLimit() const;
+  LayoutUnit PlannedIncrease() const { return planned_increase_; }
+  LayoutUnit FitContentLimit() const { return fit_content_limit_; }
+  LayoutUnit ItemIncurredIncrease() const { return item_incurred_increase_; }
+  bool IsInfinitelyGrowable() const { return is_infinitely_growable_; }
 
-  // The following setters expect their respective member variables to grow
-  // monotonically; however, |growth_limit_| can also change from a definite
-  // value to |kIndefiniteSize| and vice versa.
   void SetBaseSize(LayoutUnit base_size);
   void SetGrowthLimit(LayoutUnit growth_limit);
+  void SetPlannedIncrease(LayoutUnit planned_increase) {
+    planned_increase_ = planned_increase;
+  }
+  void SetItemIncurredIncrease(LayoutUnit item_incurred_increase) {
+    item_incurred_increase_ = item_incurred_increase;
+  }
+  void SetInfinitelyGrowable(bool infinitely_growable) {
+    is_infinitely_growable_ = infinitely_growable;
+  }
 
  private:
+  bool IsGrowthLimitLessThanBaseSize() const;
+  void EnsureGrowthLimitIsNotLessThanBaseSize();
+
   wtf_size_t track_count_;
   GridTrackSize track_size_;
 
+  // Fields used by the track sizing algorithm.
   LayoutUnit base_size_;
   LayoutUnit growth_limit_;
+  LayoutUnit planned_increase_;
+  LayoutUnit fit_content_limit_;
+  LayoutUnit item_incurred_increase_;
+  bool is_infinitely_growable_ : 1;
 };
 
 class CORE_EXPORT NGGridLayoutAlgorithmTrackCollection
@@ -215,6 +233,7 @@
     bool is_collapsed : 1;
   };
 
+  // Note that this iterator can alter any set's data.
   class CORE_EXPORT SetIterator {
    public:
     SetIterator(NGGridLayoutAlgorithmTrackCollection* collection,
@@ -238,13 +257,19 @@
       const NGGridBlockTrackCollection& block_track_collection,
       bool is_content_box_size_indefinite);
 
+  // Returns the number of sets in the collection.
+  wtf_size_t SetCount() const;
   // Returns a reference to the set located at position |set_index|.
   NGGridSet& SetAt(wtf_size_t set_index);
   // Returns an iterator for all the sets contained in this collection.
   SetIterator GetSetIterator();
-  // Returns an iterator for all the sets contained within the |range_index|-th
-  // range of the collection. Note that this iterator can alter any set's data.
-  SetIterator IteratorForRange(wtf_size_t range_index);
+  // Returns an iterator for every set in this collection's |sets_| located at
+  // an index in the interval [begin_set_index, end_set_index).
+  SetIterator GetSetIterator(wtf_size_t begin_set_index,
+                             wtf_size_t end_set_index);
+
+  wtf_size_t RangeSetCount(wtf_size_t range_index) const;
+  wtf_size_t RangeStartingSetIndex(wtf_size_t range_index) const;
 
   // Returns true if the range contains a set with an intrinsic sizing function.
   bool IsRangeSpanningIntrinsicTrack(wtf_size_t range_index) const;
@@ -264,9 +289,6 @@
       const NGGridTrackList& specified_track_list,
       bool is_content_box_size_indefinite);
 
-  // Returns the number of sets in the collection.
-  wtf_size_t SetCount() const;
-
   Vector<Range> ranges_;
   // A vector of every set element that compose the entire collection's ranges;
   // track definitions from the same set are stored in consecutive positions,
diff --git a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_track_collection_test.cc b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_track_collection_test.cc
index 36cf894..8819b003 100644
--- a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_track_collection_test.cc
+++ b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_track_collection_test.cc
@@ -67,6 +67,16 @@
   Vector<GridTrackSize> CreateTrackSizes(wtf_size_t track_count) {
     return {track_count, GridTrackSize(Length::Auto())};
   }
+
+  NGGridLayoutAlgorithmTrackCollection::SetIterator IteratorForRange(
+      NGGridLayoutAlgorithmTrackCollection& algorithm_collection,
+      wtf_size_t range_index) {
+    wtf_size_t starting_set_index =
+        algorithm_collection.RangeStartingSetIndex(range_index);
+    return algorithm_collection.GetSetIterator(
+        starting_set_index,
+        starting_set_index + algorithm_collection.RangeSetCount(range_index));
+  }
 };
 
 TEST_F(NGGridTrackCollectionTest, TestRangeIndexFromTrackNumber) {
@@ -347,8 +357,8 @@
     EXPECT_RANGE(set_count, set_counts[range_count], range_iterator);
 
     wtf_size_t current_range_set_count = 0;
-    for (auto set_iterator =
-             algorithm_collection.IteratorForRange(range_iterator.RangeIndex());
+    for (auto set_iterator = IteratorForRange(algorithm_collection,
+                                              range_iterator.RangeIndex());
          !set_iterator.IsAtEnd(); set_iterator.MoveToNextSet()) {
       EXPECT_SET(GridTrackSize(GridLength(set_count++)), 1u, set_iterator);
       ++current_range_set_count;
@@ -394,7 +404,7 @@
 
   EXPECT_RANGE(0u, 2u, range_iterator);
   NGGridLayoutAlgorithmTrackCollection::SetIterator set_iterator =
-      algorithm_collection.IteratorForRange(range_iterator.RangeIndex());
+      IteratorForRange(algorithm_collection, range_iterator.RangeIndex());
   EXPECT_SET(GridTrackSize(Length::Fixed(1)), 1u, set_iterator);
   EXPECT_TRUE(set_iterator.MoveToNextSet());
   EXPECT_SET(GridTrackSize(Length::Fixed(2)), 1u, set_iterator);
@@ -403,7 +413,7 @@
 
   EXPECT_RANGE(2u, 4u, range_iterator);
   set_iterator =
-      algorithm_collection.IteratorForRange(range_iterator.RangeIndex());
+      IteratorForRange(algorithm_collection, range_iterator.RangeIndex());
   EXPECT_SET(GridTrackSize(Length::Fixed(3)), 2u, set_iterator);
   EXPECT_TRUE(set_iterator.MoveToNextSet());
   EXPECT_SET(GridTrackSize(Length::Fixed(1)), 1u, set_iterator);
@@ -414,7 +424,7 @@
 
   EXPECT_RANGE(6u, 3u, range_iterator);
   set_iterator =
-      algorithm_collection.IteratorForRange(range_iterator.RangeIndex());
+      IteratorForRange(algorithm_collection, range_iterator.RangeIndex());
   EXPECT_SET(GridTrackSize(Length::Fixed(1)), 1u, set_iterator);
   EXPECT_TRUE(set_iterator.MoveToNextSet());
   EXPECT_SET(GridTrackSize(Length::Fixed(2)), 1u, set_iterator);
@@ -425,14 +435,14 @@
 
   EXPECT_COLLAPSED_RANGE(9u, 3u, range_iterator);
   set_iterator =
-      algorithm_collection.IteratorForRange(range_iterator.RangeIndex());
+      IteratorForRange(algorithm_collection, range_iterator.RangeIndex());
   EXPECT_SET(GridTrackSize(Length::Fixed(0)), 3u, set_iterator);
   EXPECT_FALSE(set_iterator.MoveToNextSet());
   EXPECT_TRUE(range_iterator.MoveToNextRange());
 
   EXPECT_RANGE(12u, 4u, range_iterator);
   set_iterator =
-      algorithm_collection.IteratorForRange(range_iterator.RangeIndex());
+      IteratorForRange(algorithm_collection, range_iterator.RangeIndex());
   EXPECT_SET(GridTrackSize(Length::Fixed(5)), 2u, set_iterator);
   EXPECT_TRUE(set_iterator.MoveToNextSet());
   EXPECT_SET(GridTrackSize(Length::Fixed(4)), 2u, set_iterator);
@@ -441,14 +451,14 @@
 
   EXPECT_COLLAPSED_RANGE(16u, 1u, range_iterator);
   set_iterator =
-      algorithm_collection.IteratorForRange(range_iterator.RangeIndex());
+      IteratorForRange(algorithm_collection, range_iterator.RangeIndex());
   EXPECT_SET(GridTrackSize(Length::Fixed(0)), 1u, set_iterator);
   EXPECT_FALSE(set_iterator.MoveToNextSet());
   EXPECT_TRUE(range_iterator.MoveToNextRange());
 
   EXPECT_RANGE(17u, 2u, range_iterator);
   set_iterator =
-      algorithm_collection.IteratorForRange(range_iterator.RangeIndex());
+      IteratorForRange(algorithm_collection, range_iterator.RangeIndex());
   EXPECT_SET(GridTrackSize(Length::Fixed(4)), 1u, set_iterator);
   EXPECT_TRUE(set_iterator.MoveToNextSet());
   EXPECT_SET(GridTrackSize(Length::Fixed(5)), 1u, set_iterator);
@@ -457,21 +467,21 @@
 
   EXPECT_RANGE(19u, 1u, range_iterator);
   set_iterator =
-      algorithm_collection.IteratorForRange(range_iterator.RangeIndex());
+      IteratorForRange(algorithm_collection, range_iterator.RangeIndex());
   EXPECT_SET(GridTrackSize(Length::Auto()), 1u, set_iterator);
   EXPECT_FALSE(set_iterator.MoveToNextSet());
   EXPECT_TRUE(range_iterator.MoveToNextRange());
 
   EXPECT_RANGE(20u, 2u, range_iterator);
   set_iterator =
-      algorithm_collection.IteratorForRange(range_iterator.RangeIndex());
+      IteratorForRange(algorithm_collection, range_iterator.RangeIndex());
   EXPECT_SET(GridTrackSize(Length::Auto()), 2u, set_iterator);
   EXPECT_FALSE(set_iterator.MoveToNextSet());
   EXPECT_TRUE(range_iterator.MoveToNextRange());
 
   EXPECT_RANGE(22u, 5u, range_iterator);
   set_iterator =
-      algorithm_collection.IteratorForRange(range_iterator.RangeIndex());
+      IteratorForRange(algorithm_collection, range_iterator.RangeIndex());
   EXPECT_SET(GridTrackSize(Length::Auto()), 5u, set_iterator);
   EXPECT_FALSE(set_iterator.MoveToNextSet());
   EXPECT_FALSE(range_iterator.MoveToNextRange());
@@ -510,7 +520,7 @@
 
   EXPECT_RANGE(0u, 2u, range_iterator);
   NGGridLayoutAlgorithmTrackCollection::SetIterator set_iterator =
-      algorithm_collection.IteratorForRange(range_iterator.RangeIndex());
+      IteratorForRange(algorithm_collection, range_iterator.RangeIndex());
   EXPECT_SET(GridTrackSize(Length::Fixed(1)), 1u, set_iterator);
   EXPECT_TRUE(set_iterator.MoveToNextSet());
   EXPECT_SET(GridTrackSize(Length::Fixed(2)), 1u, set_iterator);
@@ -519,7 +529,7 @@
 
   EXPECT_RANGE(2u, 2u, range_iterator);
   set_iterator =
-      algorithm_collection.IteratorForRange(range_iterator.RangeIndex());
+      IteratorForRange(algorithm_collection, range_iterator.RangeIndex());
   EXPECT_SET(GridTrackSize(Length::Fixed(3)), 1u, set_iterator);
   EXPECT_TRUE(set_iterator.MoveToNextSet());
   EXPECT_SET(GridTrackSize(Length::Fixed(4)), 1u, set_iterator);
@@ -528,7 +538,7 @@
 
   EXPECT_RANGE(4u, 11u, range_iterator);
   set_iterator =
-      algorithm_collection.IteratorForRange(range_iterator.RangeIndex());
+      IteratorForRange(algorithm_collection, range_iterator.RangeIndex());
   EXPECT_SET(GridTrackSize(Length::Fixed(5)), 4u, set_iterator);
   EXPECT_TRUE(set_iterator.MoveToNextSet());
   EXPECT_SET(GridTrackSize(Length::Fixed(6)), 4u, set_iterator);
@@ -539,7 +549,7 @@
 
   EXPECT_RANGE(15u, 8u, range_iterator);
   set_iterator =
-      algorithm_collection.IteratorForRange(range_iterator.RangeIndex());
+      IteratorForRange(algorithm_collection, range_iterator.RangeIndex());
   EXPECT_SET(GridTrackSize(Length::Fixed(7)), 3u, set_iterator);
   EXPECT_TRUE(set_iterator.MoveToNextSet());
   EXPECT_SET(GridTrackSize(Length::Fixed(5)), 3u, set_iterator);
@@ -550,7 +560,7 @@
 
   EXPECT_RANGE(23u, 2u, range_iterator);
   set_iterator =
-      algorithm_collection.IteratorForRange(range_iterator.RangeIndex());
+      IteratorForRange(algorithm_collection, range_iterator.RangeIndex());
   EXPECT_SET(GridTrackSize(Length::Fixed(6)), 1u, set_iterator);
   EXPECT_TRUE(set_iterator.MoveToNextSet());
   EXPECT_SET(GridTrackSize(Length::Fixed(7)), 1u, set_iterator);
@@ -585,7 +595,7 @@
 
   EXPECT_RANGE(0u, 1u, range_iterator);
   NGGridLayoutAlgorithmTrackCollection::SetIterator set_iterator =
-      algorithm_collection.IteratorForRange(range_iterator.RangeIndex());
+      IteratorForRange(algorithm_collection, range_iterator.RangeIndex());
   EXPECT_SET(GridTrackSize(Length::MinContent()), 1u, set_iterator);
   EXPECT_FALSE(set_iterator.MoveToNextSet());
   wtf_size_t current_range_index = range_iterator.RangeIndex();
@@ -597,7 +607,7 @@
 
   EXPECT_RANGE(1u, 2u, range_iterator);
   set_iterator =
-      algorithm_collection.IteratorForRange(range_iterator.RangeIndex());
+      IteratorForRange(algorithm_collection, range_iterator.RangeIndex());
   EXPECT_SET(GridTrackSize(GridLength(1.0)), 1u, set_iterator);
   EXPECT_TRUE(set_iterator.MoveToNextSet());
   EXPECT_SET(GridTrackSize(Length::Fixed(2)), 1u, set_iterator);
@@ -611,7 +621,7 @@
 
   EXPECT_RANGE(3u, 4u, range_iterator);
   set_iterator =
-      algorithm_collection.IteratorForRange(range_iterator.RangeIndex());
+      IteratorForRange(algorithm_collection, range_iterator.RangeIndex());
   EXPECT_SET(GridTrackSize(Length::Fixed(3)), 1u, set_iterator);
   EXPECT_TRUE(set_iterator.MoveToNextSet());
   EXPECT_SET(GridTrackSize(Length::MinContent()), 1u, set_iterator);
@@ -629,7 +639,7 @@
 
   EXPECT_RANGE(7u, 1u, range_iterator);
   set_iterator =
-      algorithm_collection.IteratorForRange(range_iterator.RangeIndex());
+      IteratorForRange(algorithm_collection, range_iterator.RangeIndex());
   EXPECT_SET(GridTrackSize(Length::Fixed(3)), 1u, set_iterator);
   EXPECT_FALSE(set_iterator.MoveToNextSet());
   current_range_index = range_iterator.RangeIndex();
@@ -641,7 +651,7 @@
 
   EXPECT_RANGE(8u, 3u, range_iterator);
   set_iterator =
-      algorithm_collection.IteratorForRange(range_iterator.RangeIndex());
+      IteratorForRange(algorithm_collection, range_iterator.RangeIndex());
   EXPECT_SET(GridTrackSize(Length::Auto()), 3u, set_iterator);
   EXPECT_FALSE(set_iterator.MoveToNextSet());
   current_range_index = range_iterator.RangeIndex();
diff --git a/third_party/blink/renderer/core/loader/frame_loader.cc b/third_party/blink/renderer/core/loader/frame_loader.cc
index 2a6b072..c64be52c 100644
--- a/third_party/blink/renderer/core/loader/frame_loader.cc
+++ b/third_party/blink/renderer/core/loader/frame_loader.cc
@@ -252,8 +252,7 @@
   auto navigation_params = std::make_unique<WebNavigationParams>();
   navigation_params->url = KURL(g_empty_string);
   navigation_params->frame_policy =
-      frame_->Owner() ? base::make_optional(frame_->Owner()->GetFramePolicy())
-                      : base::nullopt;
+      frame_->Owner() ? frame_->Owner()->GetFramePolicy() : FramePolicy();
 
   DocumentLoader* new_document_loader = Client()->CreateDocumentLoader(
       frame_, kWebNavigationTypeOther, CreateCSPForInitialEmptyDocument(),
diff --git a/third_party/blink/renderer/core/svg/properties/svg_animated_property.cc b/third_party/blink/renderer/core/svg/properties/svg_animated_property.cc
index 16d907e..a35a865 100644
--- a/third_party/blink/renderer/core/svg/properties/svg_animated_property.cc
+++ b/third_party/blink/renderer/core/svg/properties/svg_animated_property.cc
@@ -79,8 +79,7 @@
   DCHECK(context_element_);
   DCHECK(attribute_name_ != QualifiedName::Null());
   base_value_needs_synchronization_ = true;
-  context_element_->InvalidateSVGAttributes();
-  context_element_->SvgAttributeBaseValChanged(attribute_name_);
+  context_element_->BaseValueChanged(*this);
 }
 
 void SVGAnimatedPropertyBase::EnsureAnimValUpdated() {
diff --git a/third_party/blink/renderer/core/svg/svg_element.cc b/third_party/blink/renderer/core/svg/svg_element.cc
index 1d700e7..53f7d1f1 100644
--- a/third_party/blink/renderer/core/svg/svg_element.cc
+++ b/third_party/blink/renderer/core/svg/svg_element.cc
@@ -926,7 +926,8 @@
   if (params.name == html_names::kStyleAttr)
     return;
 
-  SvgAttributeBaseValChanged(params.name);
+  SvgAttributeChanged(params.name);
+  UpdateWebAnimatedAttributeOnBaseValChange(params.name);
 }
 
 void SVGElement::SvgAttributeChanged(const QualifiedName& attr_name) {
@@ -944,8 +945,15 @@
   }
 }
 
-void SVGElement::SvgAttributeBaseValChanged(const QualifiedName& attribute) {
+void SVGElement::BaseValueChanged(
+    const SVGAnimatedPropertyBase& animated_property) {
+  const QualifiedName& attribute = animated_property.AttributeName();
+  EnsureUniqueElementData().SetSvgAttributesAreDirty(true);
   SvgAttributeChanged(attribute);
+  if (class_name_ == &animated_property) {
+    UpdateClassList(g_null_atom,
+                    AtomicString(class_name_->BaseValue()->Value()));
+  }
   UpdateWebAnimatedAttributeOnBaseValChange(attribute);
 }
 
diff --git a/third_party/blink/renderer/core/svg/svg_element.h b/third_party/blink/renderer/core/svg/svg_element.h
index f02a552..c92be82 100644
--- a/third_party/blink/renderer/core/svg/svg_element.h
+++ b/third_party/blink/renderer/core/svg/svg_element.h
@@ -93,6 +93,7 @@
   void SetWebAnimationsPending();
   void ApplyActiveWebAnimations();
 
+  void BaseValueChanged(const SVGAnimatedPropertyBase&);
   void EnsureAttributeAnimValUpdated();
 
   void SetWebAnimatedAttribute(const QualifiedName& attribute,
@@ -122,7 +123,6 @@
   virtual bool IsValid() const { return true; }
 
   virtual void SvgAttributeChanged(const QualifiedName&);
-  void SvgAttributeBaseValChanged(const QualifiedName&);
 
   SVGAnimatedPropertyBase* PropertyFromAttribute(
       const QualifiedName& attribute_name) const;
@@ -134,9 +134,6 @@
 
   virtual AffineTransform* AnimateMotionTransform() { return nullptr; }
 
-  void InvalidateSVGAttributes() {
-    EnsureUniqueElementData().SetSvgAttributesAreDirty(true);
-  }
   void InvalidateSVGPresentationAttributeStyle() {
     EnsureUniqueElementData().SetPresentationAttributeStyleIsDirty(true);
   }
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_state.cc b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_state.cc
index e067e23..54a7d0d 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_state.cc
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_state.cc
@@ -8,7 +8,6 @@
 #include "third_party/blink/renderer/core/css/resolver/filter_operation_resolver.h"
 #include "third_party/blink/renderer/core/css/resolver/style_builder.h"
 #include "third_party/blink/renderer/core/css/resolver/style_resolver_state.h"
-#include "third_party/blink/renderer/core/css/scoped_css_value.h"
 #include "third_party/blink/renderer/core/dom/node_computed_style.h"
 #include "third_party/blink/renderer/core/html/canvas/html_canvas_element.h"
 #include "third_party/blink/renderer/core/paint/filter_effect_builder.h"
@@ -366,9 +365,8 @@
                                       filter_style.get(), filter_style.get());
     resolver_state.SetStyle(filter_style);
 
-    StyleBuilder::ApplyProperty(
-        GetCSSPropertyFilter(), resolver_state,
-        ScopedCSSValue(*filter_value_, &style_resolution_host->GetDocument()));
+    StyleBuilder::ApplyProperty(GetCSSPropertyFilter(), resolver_state,
+                                *filter_value_);
     resolver_state.LoadPendingResources();
 
     // We can't reuse m_fillFlags and m_strokeFlags for the filter, since these
diff --git a/third_party/blink/renderer/modules/exported/web_ax_context.cc b/third_party/blink/renderer/modules/exported/web_ax_context.cc
index f6e94b1a..5553fb8b 100644
--- a/third_party/blink/renderer/modules/exported/web_ax_context.cc
+++ b/third_party/blink/renderer/modules/exported/web_ax_context.cc
@@ -24,6 +24,9 @@
   if (!private_->HasActiveDocument())
     return WebAXObject();
 
+  // Make sure that layout is updated before a root ax object is created.
+  WebAXObject::UpdateLayout(WebDocument(private_->GetDocument()));
+
   return WebAXObject(
       static_cast<AXObjectCacheImpl*>(&private_->GetAXObjectCache())->Root());
 }
diff --git a/third_party/blink/renderer/modules/webgpu/BUILD.gn b/third_party/blink/renderer/modules/webgpu/BUILD.gn
index 1099c436..0bb8627 100644
--- a/third_party/blink/renderer/modules/webgpu/BUILD.gn
+++ b/third_party/blink/renderer/modules/webgpu/BUILD.gn
@@ -6,8 +6,6 @@
 
 blink_modules_sources("webgpu") {
   sources = [
-    "client_validation.cc",
-    "client_validation.h",
     "dawn_callback.h",
     "dawn_conversions.cc",
     "dawn_conversions.h",
diff --git a/third_party/blink/renderer/modules/webgpu/client_validation.cc b/third_party/blink/renderer/modules/webgpu/client_validation.cc
deleted file mode 100644
index 6a8ffb2..0000000
--- a/third_party/blink/renderer/modules/webgpu/client_validation.cc
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "third_party/blink/renderer/modules/webgpu/client_validation.h"
-
-#include <dawn/webgpu.h>
-
-#include "third_party/blink/renderer/bindings/modules/v8/unsigned_long_enforce_range_sequence_or_gpu_extent_3d_dict.h"
-#include "third_party/blink/renderer/bindings/modules/v8/unsigned_long_enforce_range_sequence_or_gpu_origin_2d_dict.h"
-#include "third_party/blink/renderer/bindings/modules/v8/unsigned_long_enforce_range_sequence_or_gpu_origin_3d_dict.h"
-#include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_texture_copy_view.h"
-#include "third_party/blink/renderer/platform/bindings/exception_state.h"
-
-namespace blink {
-
-bool ValidateCopySize(
-    UnsignedLongEnforceRangeSequenceOrGPUExtent3DDict& copy_size,
-    ExceptionState& exception_state) {
-  if (copy_size.IsUnsignedLongEnforceRangeSequence() &&
-      copy_size.GetAsUnsignedLongEnforceRangeSequence().size() != 3) {
-    exception_state.ThrowRangeError("copySize length must be 3");
-    return false;
-  }
-  return true;
-}
-
-bool ValidateTextureCopyView(GPUTextureCopyView* texture_copy_view,
-                             ExceptionState& exception_state) {
-  DCHECK(texture_copy_view);
-
-  const UnsignedLongEnforceRangeSequenceOrGPUOrigin3DDict origin =
-      texture_copy_view->origin();
-  if (origin.IsUnsignedLongEnforceRangeSequence() &&
-      origin.GetAsUnsignedLongEnforceRangeSequence().size() != 3) {
-    exception_state.ThrowRangeError(
-        "texture copy view origin length must be 3");
-    return false;
-  }
-  return true;
-}
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/modules/webgpu/client_validation.h b/third_party/blink/renderer/modules/webgpu/client_validation.h
deleted file mode 100644
index 47179e4..0000000
--- a/third_party/blink/renderer/modules/webgpu/client_validation.h
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGPU_CLIENT_VALIDATION_H_
-#define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGPU_CLIENT_VALIDATION_H_
-
-#include <dawn/webgpu.h>
-
-// This file provides helpers for validating copy operation in WebGPU.
-
-namespace blink {
-
-class ExceptionState;
-class GPUTextureCopyView;
-class UnsignedLongEnforceRangeSequenceOrGPUExtent3DDict;
-
-bool ValidateCopySize(
-    UnsignedLongEnforceRangeSequenceOrGPUExtent3DDict& copy_size,
-    ExceptionState& exception_state);
-bool ValidateTextureCopyView(GPUTextureCopyView* texture_copy_view,
-                             ExceptionState& exception_state);
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGPU_CLIENT_VALIDATION_H_
diff --git a/third_party/blink/renderer/modules/webgpu/dawn_conversions.cc b/third_party/blink/renderer/modules/webgpu/dawn_conversions.cc
index 22bab49..d209724 100644
--- a/third_party/blink/renderer/modules/webgpu/dawn_conversions.cc
+++ b/third_party/blink/renderer/modules/webgpu/dawn_conversions.cc
@@ -764,15 +764,27 @@
     const UnsignedLongEnforceRangeSequenceOrGPUExtent3DDict* webgpu_extent) {
   DCHECK(webgpu_extent);
 
-  WGPUExtent3D dawn_extent = {};
+  WGPUExtent3D dawn_extent = {1, 1, 1};
 
   if (webgpu_extent->IsUnsignedLongEnforceRangeSequence()) {
     const Vector<uint32_t>& webgpu_extent_sequence =
         webgpu_extent->GetAsUnsignedLongEnforceRangeSequence();
-    DCHECK_EQ(webgpu_extent_sequence.size(), 3UL);
-    dawn_extent.width = webgpu_extent_sequence[0];
-    dawn_extent.height = webgpu_extent_sequence[1];
-    dawn_extent.depth = webgpu_extent_sequence[2];
+
+    // The WebGPU spec states that if the sequence isn't big enough then the
+    // default values of 1 are used (which are set above).
+    switch (webgpu_extent_sequence.size()) {
+      default:
+        dawn_extent.depth = webgpu_extent_sequence[2];
+        FALLTHROUGH;
+      case 2:
+        dawn_extent.height = webgpu_extent_sequence[1];
+        FALLTHROUGH;
+      case 1:
+        dawn_extent.width = webgpu_extent_sequence[0];
+        FALLTHROUGH;
+      case 0:
+        break;
+    }
 
   } else if (webgpu_extent->IsGPUExtent3DDict()) {
     const GPUExtent3DDict* webgpu_extent_3d_dict =
@@ -792,15 +804,27 @@
     const UnsignedLongEnforceRangeSequenceOrGPUOrigin3DDict* webgpu_origin) {
   DCHECK(webgpu_origin);
 
-  WGPUOrigin3D dawn_origin = {};
+  WGPUOrigin3D dawn_origin = {0, 0, 0};
 
   if (webgpu_origin->IsUnsignedLongEnforceRangeSequence()) {
     const Vector<uint32_t>& webgpu_origin_sequence =
         webgpu_origin->GetAsUnsignedLongEnforceRangeSequence();
-    DCHECK_EQ(webgpu_origin_sequence.size(), 3UL);
-    dawn_origin.x = webgpu_origin_sequence[0];
-    dawn_origin.y = webgpu_origin_sequence[1];
-    dawn_origin.z = webgpu_origin_sequence[2];
+
+    // The WebGPU spec states that if the sequence isn't big enough then the
+    // default values of 0 are used (which are set above).
+    switch (webgpu_origin_sequence.size()) {
+      default:
+        dawn_origin.z = webgpu_origin_sequence[2];
+        FALLTHROUGH;
+      case 2:
+        dawn_origin.y = webgpu_origin_sequence[1];
+        FALLTHROUGH;
+      case 1:
+        dawn_origin.x = webgpu_origin_sequence[0];
+        FALLTHROUGH;
+      case 0:
+        break;
+    }
 
   } else if (webgpu_origin->IsGPUOrigin3DDict()) {
     const GPUOrigin3DDict* webgpu_origin_3d_dict =
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_command_encoder.cc b/third_party/blink/renderer/modules/webgpu/gpu_command_encoder.cc
index 6569866..eecf83e 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_command_encoder.cc
+++ b/third_party/blink/renderer/modules/webgpu/gpu_command_encoder.cc
@@ -15,7 +15,6 @@
 #include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_render_pass_depth_stencil_attachment_descriptor.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_render_pass_descriptor.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_texture_copy_view.h"
-#include "third_party/blink/renderer/modules/webgpu/client_validation.h"
 #include "third_party/blink/renderer/modules/webgpu/dawn_conversions.h"
 #include "third_party/blink/renderer/modules/webgpu/gpu_buffer.h"
 #include "third_party/blink/renderer/modules/webgpu/gpu_command_buffer.h"
@@ -254,11 +253,6 @@
     GPUTextureCopyView* destination,
     UnsignedLongEnforceRangeSequenceOrGPUExtent3DDict& copy_size,
     ExceptionState& exception_state) {
-  if (!ValidateCopySize(copy_size, exception_state) ||
-      !ValidateTextureCopyView(destination, exception_state)) {
-    return;
-  }
-
   base::Optional<WGPUBufferCopyView> dawn_source = AsDawnType(source);
   if (!dawn_source) {
     return;
@@ -275,11 +269,6 @@
     GPUBufferCopyView* destination,
     UnsignedLongEnforceRangeSequenceOrGPUExtent3DDict& copy_size,
     ExceptionState& exception_state) {
-  if (!ValidateCopySize(copy_size, exception_state) ||
-      !ValidateTextureCopyView(source, exception_state)) {
-    return;
-  }
-
   WGPUTextureCopyView dawn_source = AsDawnType(source, device_);
   base::Optional<WGPUBufferCopyView> dawn_destination = AsDawnType(destination);
   if (!dawn_destination) {
@@ -296,12 +285,6 @@
     GPUTextureCopyView* destination,
     UnsignedLongEnforceRangeSequenceOrGPUExtent3DDict& copy_size,
     ExceptionState& exception_state) {
-  if (!ValidateCopySize(copy_size, exception_state) ||
-      !ValidateTextureCopyView(source, exception_state) ||
-      !ValidateTextureCopyView(destination, exception_state)) {
-    return;
-  }
-
   WGPUTextureCopyView dawn_source = AsDawnType(source, device_);
   WGPUTextureCopyView dawn_destination = AsDawnType(destination, device_);
   WGPUExtent3D dawn_copy_size = AsDawnType(&copy_size);
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_extent_3d_dict.idl b/third_party/blink/renderer/modules/webgpu/gpu_extent_3d_dict.idl
index a1ac2c84..6fc1fe2 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_extent_3d_dict.idl
+++ b/third_party/blink/renderer/modules/webgpu/gpu_extent_3d_dict.idl
@@ -5,7 +5,7 @@
 // https://gpuweb.github.io/gpuweb/
 
 dictionary GPUExtent3DDict {
-    required GPUIntegerCoordinate width;
-    required GPUIntegerCoordinate height;
-    required GPUIntegerCoordinate depth;
+    GPUIntegerCoordinate width = 1;
+    GPUIntegerCoordinate height = 1;
+    GPUIntegerCoordinate depth = 1;
 };
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_queue.cc b/third_party/blink/renderer/modules/webgpu/gpu_queue.cc
index 4ae0188..d11e0eb 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_queue.cc
+++ b/third_party/blink/renderer/modules/webgpu/gpu_queue.cc
@@ -17,7 +17,6 @@
 #include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_texture_copy_view.h"
 #include "third_party/blink/renderer/core/imagebitmap/image_bitmap.h"
 #include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h"
-#include "third_party/blink/renderer/modules/webgpu/client_validation.h"
 #include "third_party/blink/renderer/modules/webgpu/dawn_conversions.h"
 #include "third_party/blink/renderer/modules/webgpu/gpu_buffer.h"
 #include "third_party/blink/renderer/modules/webgpu/gpu_command_buffer.h"
@@ -305,11 +304,6 @@
     GPUTextureDataLayout* data_layout,
     UnsignedLongEnforceRangeSequenceOrGPUExtent3DDict& write_size,
     ExceptionState& exception_state) {
-  if (!ValidateCopySize(write_size, exception_state) ||
-      !ValidateTextureCopyView(destination, exception_state)) {
-    return;
-  }
-
   WGPUTextureCopyView dawn_destination = AsDawnType(destination, device_);
   WGPUTextureDataLayout dawn_data_layout = AsDawnType(data_layout);
   WGPUExtent3D dawn_write_size = AsDawnType(&write_size);
@@ -330,13 +324,6 @@
     return;
   }
 
-  // TODO(shaobo.yan@intel.com): only the same color format texture copy allowed
-  // now. Need to Explore compatible texture format copy.
-  if (!ValidateCopySize(copy_size, exception_state) ||
-      !ValidateTextureCopyView(destination, exception_state)) {
-    return;
-  }
-
   // ImageBitmap shouldn't in closed state.
   if (source->imageBitmap()->IsNeutered()) {
     exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
diff --git a/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_face.cc b/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_face.cc
index 435969f..f8cff9f 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_face.cc
+++ b/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_face.cc
@@ -47,7 +47,6 @@
 #include "third_party/blink/renderer/platform/fonts/simple_font_data.h"
 #include "third_party/blink/renderer/platform/fonts/skia/skia_text_metrics.h"
 #include "third_party/blink/renderer/platform/fonts/unicode_range_set.h"
-#include "third_party/blink/renderer/platform/instrumentation/histogram.h"
 #include "third_party/blink/renderer/platform/resolution_units.h"
 #include "third_party/blink/renderer/platform/wtf/hash_map.h"
 #include "third_party/blink/renderer/platform/wtf/math_extras.h"
@@ -368,8 +367,6 @@
 hb_face_t* HarfBuzzFace::CreateFace() {
   hb_face_t* face = nullptr;
 
-  DEFINE_THREAD_SAFE_STATIC_LOCAL(BooleanHistogram, zero_copy_success_histogram,
-                                  ("Blink.Fonts.HarfBuzzFaceZeroCopyAccess"));
   SkTypeface* typeface = platform_data_->Typeface();
   CHECK(typeface);
   // The attempt of doing zero copy-mmaped memory access to the font blobs does
@@ -394,9 +391,6 @@
   if (!face) {
     face = hb_face_create_for_tables(HarfBuzzSkiaGetTable,
                                      platform_data_->Typeface(), nullptr);
-    zero_copy_success_histogram.Count(false);
-  } else {
-    zero_copy_success_histogram.Count(true);
   }
 
   DCHECK(face);
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.cc b/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.cc
index b784445b..8828439 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.cc
@@ -237,7 +237,7 @@
                gfx::Transform());
 
   viz::SharedQuadState* sqs = pass->CreateAndAppendSharedQuadState();
-  sqs->SetAll(gfx::Transform(), bounds, bounds, gfx::RRectF(), bounds,
+  sqs->SetAll(gfx::Transform(), bounds, bounds, gfx::MaskFilterInfo(), bounds,
               is_clipped, is_opaque, 1.f, SkBlendMode::kSrcOver, 0);
 
   viz::TransferableResource resource;
diff --git a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc
index 5188b066..7637eb1 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc
@@ -1383,7 +1383,8 @@
   // of antialiasing issues on the rounded corner edges.
   // is_fast_rounded_corner means to intentionally prefer faster compositing
   // and less memory over highest quality.
-  if (!effect.rounded_corner_bounds.IsEmpty() && !effect.is_fast_rounded_corner)
+  if (effect.mask_filter_info.HasRoundedCorners() &&
+      !effect.mask_filter_info.is_fast_rounded_corner())
     return cc::RenderSurfaceReason::kRoundedCorner;
   return cc::RenderSurfaceReason::kNone;
 }
diff --git a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc
index c38add2..58c990d 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc
@@ -3048,9 +3048,9 @@
       *GetPropertyTrees().effect_tree.Node(mask_isolation_0_id);
   ASSERT_EQ(e0_id, mask_isolation_0.parent_id);
   EXPECT_EQ(SkBlendMode::kSrcOver, mask_isolation_0.blend_mode);
-  EXPECT_TRUE(mask_isolation_0.is_fast_rounded_corner);
+  EXPECT_TRUE(mask_isolation_0.mask_filter_info.is_fast_rounded_corner());
   EXPECT_EQ(gfx::RRectF(50, 50, 300, 200, 5),
-            mask_isolation_0.rounded_corner_bounds);
+            mask_isolation_0.mask_filter_info.rounded_corner_bounds());
   EXPECT_FALSE(mask_isolation_0.HasRenderSurface());
 }
 
@@ -3147,9 +3147,9 @@
       *GetPropertyTrees().effect_tree.Node(mask_isolation_0_id);
   ASSERT_EQ(e0_id, mask_isolation_0.parent_id);
   EXPECT_EQ(SkBlendMode::kSrcOver, mask_isolation_0.blend_mode);
-  EXPECT_TRUE(mask_isolation_0.is_fast_rounded_corner);
+  EXPECT_TRUE(mask_isolation_0.mask_filter_info.is_fast_rounded_corner());
   EXPECT_EQ(gfx::RRectF(50, 50, 300, 200, 5),
-            mask_isolation_0.rounded_corner_bounds);
+            mask_isolation_0.mask_filter_info.rounded_corner_bounds());
   EXPECT_FALSE(mask_isolation_0.HasRenderSurface());
 }
 
@@ -3302,27 +3302,27 @@
 
   ASSERT_EQ(e0_id, mask_isolation_0.parent_id);
   EXPECT_EQ(SkBlendMode::kSrcOver, mask_isolation_0.blend_mode);
-  EXPECT_TRUE(mask_isolation_0.is_fast_rounded_corner);
+  EXPECT_TRUE(mask_isolation_0.mask_filter_info.is_fast_rounded_corner());
   EXPECT_EQ(gfx::RRectF(50, 50, 300, 200, 5),
-            mask_isolation_0.rounded_corner_bounds);
+            mask_isolation_0.mask_filter_info.rounded_corner_bounds());
   EXPECT_FALSE(mask_isolation_0.HasRenderSurface());
 
   ASSERT_EQ(e1_id, cc_filter.parent_id);
   EXPECT_EQ(cc_filter.id, content0->effect_tree_index());
   EXPECT_EQ(SkBlendMode::kSrcOver, cc_filter.blend_mode);
-  EXPECT_FALSE(cc_filter.is_fast_rounded_corner);
+  EXPECT_FALSE(cc_filter.mask_filter_info.is_fast_rounded_corner());
   EXPECT_TRUE(cc_filter.HasRenderSurface());
 
   EXPECT_EQ(SkBlendMode::kSrcOver, mask_isolation_1.blend_mode);
-  EXPECT_TRUE(mask_isolation_1.is_fast_rounded_corner);
+  EXPECT_TRUE(mask_isolation_1.mask_filter_info.is_fast_rounded_corner());
   EXPECT_EQ(gfx::RRectF(50, 50, 300, 200, 5),
-            mask_isolation_1.rounded_corner_bounds);
+            mask_isolation_1.mask_filter_info.rounded_corner_bounds());
   EXPECT_TRUE(mask_isolation_1.HasRenderSurface());
 
   EXPECT_EQ(SkBlendMode::kSrcOver, mask_isolation_2.blend_mode);
-  EXPECT_TRUE(mask_isolation_2.is_fast_rounded_corner);
+  EXPECT_TRUE(mask_isolation_2.mask_filter_info.is_fast_rounded_corner());
   EXPECT_EQ(gfx::RRectF(50, 50, 300, 200, 5),
-            mask_isolation_2.rounded_corner_bounds);
+            mask_isolation_2.mask_filter_info.rounded_corner_bounds());
   EXPECT_FALSE(mask_isolation_2.HasRenderSurface());
 }
 
@@ -3507,9 +3507,9 @@
   EXPECT_EQ(c1_id, content1->clip_tree_index());
   EXPECT_EQ(mask_isolation_0_id, content1->effect_tree_index());
 
-  EXPECT_TRUE(mask_isolation_0.is_fast_rounded_corner);
+  EXPECT_TRUE(mask_isolation_0.mask_filter_info.is_fast_rounded_corner());
   EXPECT_EQ(gfx::RRectF(50, 50, 300, 200, 5),
-            mask_isolation_0.rounded_corner_bounds);
+            mask_isolation_0.mask_filter_info.rounded_corner_bounds());
   EXPECT_FALSE(mask_isolation_0.HasRenderSurface());
 }
 
@@ -3560,9 +3560,9 @@
       *GetPropertyTrees().effect_tree.Node(mask_isolation_0_id);
   ASSERT_EQ(e0_id, mask_isolation_0.parent_id);
   EXPECT_EQ(SkBlendMode::kSrcOver, mask_isolation_0.blend_mode);
-  EXPECT_TRUE(mask_isolation_0.is_fast_rounded_corner);
+  EXPECT_TRUE(mask_isolation_0.mask_filter_info.is_fast_rounded_corner());
   EXPECT_EQ(gfx::RRectF(50, 50, 300, 200, 5),
-            mask_isolation_0.rounded_corner_bounds);
+            mask_isolation_0.mask_filter_info.rounded_corner_bounds());
   EXPECT_FALSE(mask_isolation_0.HasRenderSurface());
 
   int t1_id = content1->transform_tree_index();
@@ -3580,9 +3580,9 @@
   EXPECT_NE(mask_isolation_0_id, mask_isolation_1_id);
   ASSERT_EQ(e0_id, mask_isolation_1.parent_id);
   EXPECT_EQ(SkBlendMode::kSrcOver, mask_isolation_1.blend_mode);
-  EXPECT_TRUE(mask_isolation_1.is_fast_rounded_corner);
+  EXPECT_TRUE(mask_isolation_1.mask_filter_info.is_fast_rounded_corner());
   EXPECT_EQ(gfx::RRectF(50, 50, 300, 200, 5),
-            mask_isolation_1.rounded_corner_bounds);
+            mask_isolation_1.mask_filter_info.rounded_corner_bounds());
   EXPECT_FALSE(mask_isolation_1.HasRenderSurface());
 }
 
@@ -3641,9 +3641,9 @@
 
   int e2_id = content2->effect_tree_index();
   const cc::EffectNode& cc_e2 = *GetPropertyTrees().effect_tree.Node(e2_id);
-  EXPECT_TRUE(cc_e2.is_fast_rounded_corner);
+  EXPECT_TRUE(cc_e2.mask_filter_info.is_fast_rounded_corner());
   EXPECT_EQ(gfx::RRectF(50, 50, 300, 200, 5),
-            mask_isolation_0.rounded_corner_bounds);
+            mask_isolation_0.mask_filter_info.rounded_corner_bounds());
 }
 
 TEST_P(PaintArtifactCompositorTest, SynthesizedClipRespectOutputClip) {
@@ -3696,9 +3696,9 @@
       *GetPropertyTrees().effect_tree.Node(mask_isolation_0_id);
   ASSERT_EQ(e0_id, mask_isolation_0.parent_id);
   EXPECT_EQ(SkBlendMode::kSrcOver, mask_isolation_0.blend_mode);
-  EXPECT_TRUE(mask_isolation_0.is_fast_rounded_corner);
+  EXPECT_TRUE(mask_isolation_0.mask_filter_info.is_fast_rounded_corner());
   EXPECT_EQ(gfx::RRectF(50, 50, 300, 200, 5),
-            mask_isolation_0.rounded_corner_bounds);
+            mask_isolation_0.mask_filter_info.rounded_corner_bounds());
   EXPECT_FALSE(mask_isolation_0.HasRenderSurface());
 
   EXPECT_EQ(c1_id, content1->clip_tree_index());
@@ -3710,9 +3710,9 @@
   int e1_id = mask_isolation_1.parent_id;
   const cc::EffectNode& cc_e1 = *GetPropertyTrees().effect_tree.Node(e1_id);
   ASSERT_EQ(e0_id, cc_e1.parent_id);
-  EXPECT_TRUE(mask_isolation_1.is_fast_rounded_corner);
+  EXPECT_TRUE(mask_isolation_1.mask_filter_info.is_fast_rounded_corner());
   EXPECT_EQ(gfx::RRectF(50, 50, 300, 200, 5),
-            mask_isolation_1.rounded_corner_bounds);
+            mask_isolation_1.mask_filter_info.rounded_corner_bounds());
   EXPECT_FALSE(mask_isolation_1.HasRenderSurface());
 
   EXPECT_EQ(c1_id, content2->clip_tree_index());
@@ -3723,9 +3723,9 @@
   EXPECT_NE(mask_isolation_1_id, mask_isolation_2_id);
   ASSERT_EQ(e0_id, mask_isolation_2.parent_id);
   EXPECT_EQ(SkBlendMode::kSrcOver, mask_isolation_2.blend_mode);
-  EXPECT_TRUE(mask_isolation_2.is_fast_rounded_corner);
+  EXPECT_TRUE(mask_isolation_2.mask_filter_info.is_fast_rounded_corner());
   EXPECT_EQ(gfx::RRectF(50, 50, 300, 200, 5),
-            mask_isolation_2.rounded_corner_bounds);
+            mask_isolation_2.mask_filter_info.rounded_corner_bounds());
   EXPECT_FALSE(mask_isolation_2.HasRenderSurface());
 }
 
@@ -3781,9 +3781,9 @@
       *GetPropertyTrees().effect_tree.Node(mask_isolation_0_id);
   ASSERT_EQ(e0_id, mask_isolation_0.parent_id);
   EXPECT_EQ(SkBlendMode::kSrcOver, mask_isolation_0.blend_mode);
-  EXPECT_TRUE(mask_isolation_0.is_fast_rounded_corner);
+  EXPECT_TRUE(mask_isolation_0.mask_filter_info.is_fast_rounded_corner());
   EXPECT_EQ(gfx::RRectF(50, 50, 300, 200, 5),
-            mask_isolation_0.rounded_corner_bounds);
+            mask_isolation_0.mask_filter_info.rounded_corner_bounds());
 
   EXPECT_EQ(c1_id, content1->clip_tree_index());
   int e1_id = content1->effect_tree_index();
@@ -3795,9 +3795,9 @@
   EXPECT_NE(mask_isolation_0_id, mask_isolation_1_id);
   ASSERT_EQ(e0_id, mask_isolation_1.parent_id);
   EXPECT_EQ(SkBlendMode::kMultiply, mask_isolation_1.blend_mode);
-  EXPECT_TRUE(mask_isolation_1.is_fast_rounded_corner);
+  EXPECT_TRUE(mask_isolation_1.mask_filter_info.is_fast_rounded_corner());
   EXPECT_EQ(gfx::RRectF(50, 50, 300, 200, 5),
-            mask_isolation_1.rounded_corner_bounds);
+            mask_isolation_1.mask_filter_info.rounded_corner_bounds());
 
   EXPECT_EQ(c1_id, content2->clip_tree_index());
   int mask_isolation_2_id = content2->effect_tree_index();
@@ -3807,9 +3807,9 @@
   EXPECT_NE(mask_isolation_1_id, mask_isolation_2_id);
   ASSERT_EQ(e0_id, mask_isolation_2.parent_id);
   EXPECT_EQ(SkBlendMode::kSrcOver, mask_isolation_0.blend_mode);
-  EXPECT_TRUE(mask_isolation_2.is_fast_rounded_corner);
+  EXPECT_TRUE(mask_isolation_2.mask_filter_info.is_fast_rounded_corner());
   EXPECT_EQ(gfx::RRectF(50, 50, 300, 200, 5),
-            mask_isolation_2.rounded_corner_bounds);
+            mask_isolation_2.mask_filter_info.rounded_corner_bounds());
 }
 
 TEST_P(PaintArtifactCompositorTest, SynthesizedClipDelegateBackdropFilter) {
@@ -3878,10 +3878,10 @@
   EXPECT_EQ(t0_id, mask_isolation_0.transform_id);
   EXPECT_EQ(c1_id, mask_isolation_0.clip_id);
   EXPECT_TRUE(mask_isolation_0.backdrop_filters.IsEmpty());
-  EXPECT_TRUE(mask_isolation_0.is_fast_rounded_corner);
+  EXPECT_TRUE(mask_isolation_0.mask_filter_info.is_fast_rounded_corner());
   EXPECT_EQ(1.0f, mask_isolation_0.opacity);
   EXPECT_EQ(gfx::RRectF(50, 50, 300, 200, 5),
-            mask_isolation_0.rounded_corner_bounds);
+            mask_isolation_0.mask_filter_info.rounded_corner_bounds());
 
   EXPECT_EQ(t1_id, content1->transform_tree_index());
   int c2_id = content1->clip_tree_index();
@@ -3905,10 +3905,11 @@
   EXPECT_EQ(t1_id, mask_isolation_1.transform_id);
   EXPECT_EQ(c2_id, mask_isolation_1.clip_id);
   EXPECT_FALSE(mask_isolation_1.backdrop_filters.IsEmpty());
-  EXPECT_FALSE(mask_isolation_1.is_fast_rounded_corner);
+  EXPECT_FALSE(mask_isolation_1.mask_filter_info.is_fast_rounded_corner());
   // Opacity should also be moved to mask_isolation_1.
   EXPECT_EQ(0.5f, mask_isolation_1.opacity);
-  EXPECT_EQ(gfx::RRectF(), mask_isolation_1.rounded_corner_bounds);
+  EXPECT_EQ(gfx::RRectF(),
+            mask_isolation_1.mask_filter_info.rounded_corner_bounds());
 
   EXPECT_EQ(t0_id, clip_mask1->transform_tree_index());
   EXPECT_EQ(c2_id, clip_mask1->clip_tree_index());
@@ -3933,10 +3934,10 @@
   EXPECT_EQ(t0_id, mask_isolation_2.transform_id);
   EXPECT_EQ(c1_id, mask_isolation_2.clip_id);
   EXPECT_TRUE(mask_isolation_2.backdrop_filters.IsEmpty());
-  EXPECT_TRUE(mask_isolation_2.is_fast_rounded_corner);
+  EXPECT_TRUE(mask_isolation_2.mask_filter_info.is_fast_rounded_corner());
   EXPECT_EQ(1.0f, mask_isolation_2.opacity);
   EXPECT_EQ(gfx::RRectF(50, 50, 300, 200, 5),
-            mask_isolation_2.rounded_corner_bounds);
+            mask_isolation_2.mask_filter_info.rounded_corner_bounds());
 }
 
 TEST_P(PaintArtifactCompositorTest, SynthesizedClipMultipleNonBackdropEffects) {
@@ -4002,9 +4003,9 @@
 
   ASSERT_EQ(e0_id, mask_isolation.parent_id);
   EXPECT_EQ(c1_id, mask_isolation.clip_id);
-  EXPECT_TRUE(mask_isolation.is_fast_rounded_corner);
+  EXPECT_TRUE(mask_isolation.mask_filter_info.is_fast_rounded_corner());
   EXPECT_EQ(gfx::RRectF(50, 50, 300, 200, 5),
-            mask_isolation.rounded_corner_bounds);
+            mask_isolation.mask_filter_info.rounded_corner_bounds());
 
   EXPECT_EQ(c0_id, content2->clip_tree_index());
   EXPECT_EQ(e0_id, content2->effect_tree_index());
diff --git a/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc b/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc
index bb9b299..ab79469 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc
@@ -639,7 +639,7 @@
   DCHECK(mask_isolation);
   bool needs_layer =
       !pending_synthetic_mask_layers_.Contains(mask_isolation->id) &&
-      mask_isolation->rounded_corner_bounds.IsEmpty();
+      mask_isolation->mask_filter_info.IsEmpty();
 
   CompositorElementId mask_isolation_id, mask_effect_id;
   SynthesizedClip& clip = client_.CreateOrReuseSynthesizedClipLayer(
@@ -984,9 +984,9 @@
       // is used. See PropertyTreeManager::EmitClipMaskLayer().
       if (SupportsShaderBasedRoundedCorner(*pending_clip.clip,
                                            pending_clip.type, next_effect)) {
-        synthetic_effect.rounded_corner_bounds =
-            gfx::RRectF(pending_clip.clip->PixelSnappedClipRect());
-        synthetic_effect.is_fast_rounded_corner = true;
+        synthetic_effect.mask_filter_info = gfx::MaskFilterInfo(
+            gfx::RRectF(pending_clip.clip->PixelSnappedClipRect()),
+            /*is_fast_rounded_corner=*/true);
 
         // Nested rounded corner clips need to force render surfaces for
         // clips other than the leaf ones, because the compositor doesn't
diff --git a/third_party/blink/renderer/platform/graphics/video_frame_resource_provider.cc b/third_party/blink/renderer/platform/graphics/video_frame_resource_provider.cc
index b902879..4d090c9 100644
--- a/third_party/blink/renderer/platform/graphics/video_frame_resource_provider.cc
+++ b/third_party/blink/renderer/platform/graphics/video_frame_resource_provider.cc
@@ -110,15 +110,15 @@
 
   gfx::Rect visible_quad_rect = quad_rect;
   gfx::Rect clip_rect;
-  gfx::RRectF rounded_corner_bounds;
+  gfx::MaskFilterInfo mask_filter_info;
   bool is_clipped = false;
   float draw_opacity = 1.0f;
   int sorting_context_id = 0;
 
   resource_updater_->AppendQuads(render_pass, std::move(frame), transform,
-                                 quad_rect, visible_quad_rect,
-                                 rounded_corner_bounds, clip_rect, is_clipped,
-                                 is_opaque, draw_opacity, sorting_context_id);
+                                 quad_rect, visible_quad_rect, mask_filter_info,
+                                 clip_rect, is_clipped, is_opaque, draw_opacity,
+                                 sorting_context_id);
 }
 
 void VideoFrameResourceProvider::ReleaseFrameResources() {
diff --git a/third_party/blink/renderer/platform/widget/compositing/android_webview/synchronous_layer_tree_frame_sink.cc b/third_party/blink/renderer/platform/widget/compositing/android_webview/synchronous_layer_tree_frame_sink.cc
index 64e049c..f7a81e19 100644
--- a/third_party/blink/renderer/platform/widget/compositing/android_webview/synchronous_layer_tree_frame_sink.cc
+++ b/third_party/blink/renderer/platform/widget/compositing/android_webview/synchronous_layer_tree_frame_sink.cc
@@ -355,9 +355,10 @@
         embed_render_pass->CreateAndAppendDrawQuad<viz::SurfaceDrawQuad>();
     shared_quad_state->SetAll(
         child_transform, gfx::Rect(child_size), gfx::Rect(child_size),
-        gfx::RRectF() /* rounded_corner_bounds */, gfx::Rect() /* clip_rect */,
-        false /* is_clipped */, are_contents_opaque /* are_contents_opaque */,
-        1.f /* opacity */, SkBlendMode::kSrcOver, 0 /* sorting_context_id */);
+        gfx::MaskFilterInfo() /* mask_filter_info */,
+        gfx::Rect() /* clip_rect */, false /* is_clipped */,
+        are_contents_opaque /* are_contents_opaque */, 1.f /* opacity */,
+        SkBlendMode::kSrcOver, 0 /* sorting_context_id */);
     surface_quad->SetNew(
         shared_quad_state, gfx::Rect(child_size), gfx::Rect(child_size),
         viz::SurfaceRange(
diff --git a/third_party/blink/tools/BUILD.gn b/third_party/blink/tools/BUILD.gn
index 62c41ca..b243b05 100644
--- a/third_party/blink/tools/BUILD.gn
+++ b/third_party/blink/tools/BUILD.gn
@@ -58,15 +58,32 @@
   ]
 }
 
-group("wpt_tests_mojo_bindings") {
+group("wpt_tests_base_mojo_bindings") {
   testonly = true
   data_deps = [
     "//:layout_test_data_mojo_bindings",
     "//:layout_test_data_mojo_bindings_lite",
+    "//content/test:mojo_bindings_web_test_mojom_js_data_deps",
     "//content/test:mojo_web_test_bindings_js_data_deps",
-    "//device/vr/public/mojom:mojom_js_data_deps",
     "//mojo/public/interfaces/bindings/tests:test_data_deps",
     "//mojo/public/js/ts/bindings/tests:test_interfaces_js_data_deps",
     "//mojo/public/mojom/base:base_js_data_deps",
   ]
 }
+
+group("wpt_tests_mojo_bindings") {
+  testonly = true
+  data_deps = [
+    ":wpt_tests_base_mojo_bindings",
+    "//device/bluetooth/public/mojom:fake_bluetooth_interfaces_js_data_deps",
+    "//device/vr/public/mojom:mojom_js_data_deps",
+    "//media/capture/mojom:image_capture_js_data_deps",
+    "//media/midi:mojo_js_data_deps",
+    "//services/device/public/mojom:generic_sensor_js_data_deps",
+    "//services/device/public/mojom:mojom_js_data_deps",
+    "//services/device/public/mojom:usb_js_data_deps",
+    "//services/shape_detection/public/mojom:mojom_js_data_deps",
+    "//skia/public/mojom:mojom_js_data_deps",
+    "//third_party/blink/public/mojom:mojom_platform_js_data_deps",
+  ]
+}
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index bae503e..86e27df 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -3393,7 +3393,6 @@
 crbug.com/618969 external/wpt/css/css-grid/subgrid/* [ Skip ]
 
 # [layout-ng-grid]
-crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/abspos/absolute-positioning-changing-containing-block-001.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/abspos/absolute-positioning-definite-sizes-001.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/abspos/absolute-positioning-grid-container-containing-block-001.html [ Failure Crash ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/abspos/absolute-positioning-grid-container-parent-001.html [ Failure ]
@@ -3685,9 +3684,6 @@
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-5.html [ Failure Crash ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-6.html [ Failure Crash ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-7.html [ Failure Crash ]
-crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-8.html [ Failure Crash ]
-crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-9.html [ Failure Crash ]
-crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-10.html [ Failure Crash ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/grid-place-content-001.html [ Failure Crash ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/grid-row-axis-alignment-positioned-items-001.html [ Failure Crash ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/grid-row-axis-alignment-positioned-items-002.html [ Failure Crash ]
@@ -3893,11 +3889,6 @@
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-definition/grid-template-columns-rows-resolved-values-001.html [ Failure Crash ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-definition/grid-template-columns-rows-resolved-values-001.tentative.html [ Failure Crash ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-definition/grid-template-rows-fit-content-001.html [ Failure ]
-crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-item-non-auto-height-stretch-001.html [ Failure ]
-crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-item-non-auto-height-stretch-002.html [ Failure ]
-crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-item-non-auto-height-stretch-003.html [ Failure ]
-crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-item-non-auto-height-stretch-004.html [ Failure ]
-crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-items/anonymous-grid-item-001.html [ Failure Crash ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-items/explicitly-sized-grid-item-as-table.html [ Failure Crash ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-items/grid-automatic-minimum-intrinsic-aspect-ratio-001.html [ Failure Crash ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-items/grid-inline-items-001.html [ Failure Crash ]
@@ -3925,14 +3916,9 @@
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-items/grid-inline-z-axis-ordering-overlapped-items-004.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-items/grid-inline-z-axis-ordering-overlapped-items-005.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-items/grid-inline-z-axis-ordering-overlapped-items-006.html [ Failure ]
-crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-items/grid-item-containing-block-001.html [ Pass ]
-crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-items/grid-item-containing-block-002.html [ Pass ]
-crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-items/grid-item-containing-block-003.html [ Pass ]
-crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-items/grid-item-containing-block-004.html [ Pass ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-items/grid-item-dynamic-min-contribution-001.html [ Failure Crash ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-items/grid-item-flex-container-001.html [ Failure Crash ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-items/grid-item-margins-and-writing-modes-001.html [ Failure ]
-crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-items/grid-item-min-auto-size-001.html [ Failure Crash ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-items/grid-item-percentage-sizes-001.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-items/grid-item-percentage-sizes-002.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-items/grid-item-percentage-sizes-003.html [ Failure ]
@@ -3974,8 +3960,6 @@
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-items/grid-items-percentage-paddings-005.html [ Failure Crash ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-items/grid-items-percentage-paddings-006.html [ Failure Crash ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-items/grid-items-percentage-paddings-007.html [ Failure Crash ]
-crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-items/grid-items-percentage-paddings-008.html [ Failure Crash ]
-crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-items/grid-items-percentage-paddings-009.html [ Failure Crash ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-items/grid-items-percentage-paddings-010.html [ Failure Crash ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-items/grid-items-percentage-paddings-011.html [ Failure Crash ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-items/grid-items-percentage-paddings-012.html [ Failure Crash ]
@@ -3991,17 +3975,13 @@
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-items/grid-layout-grid-in-grid.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-items/grid-layout-z-order-a.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-items/grid-layout-z-order-b.html [ Failure ]
-crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-items/grid-minimum-size-grid-items-007.html [ Failure Crash ]
-crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-items/grid-minimum-size-grid-items-009.html [ Failure Crash ]
+crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-items/grid-minimum-size-grid-items-007.html [ Failure ]
+crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-items/grid-minimum-size-grid-items-009.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-items/grid-minimum-size-grid-items-010.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-items/grid-minimum-size-grid-items-011.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-items/grid-minimum-size-grid-items-012.html [ Failure ]
-crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-items/grid-minimum-size-grid-items-013.html [ Pass ]
-crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-items/grid-minimum-size-grid-items-014.html [ Pass ]
-crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-items/grid-minimum-size-grid-items-015.html [ Pass ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-items/grid-minimum-size-grid-items-016.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-items/grid-minimum-size-grid-items-017.html [ Failure ]
-crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-items/grid-minimum-size-grid-items-018.html [ Pass ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-items/grid-minimum-size-grid-items-019.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-items/grid-minimum-size-grid-items-020.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-items/grid-minimum-size-grid-items-021.html [ Failure ]
@@ -4021,22 +4001,17 @@
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-items/grid-z-axis-ordering-overlapped-items-004.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-items/grid-z-axis-ordering-overlapped-items-005.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-items/grid-z-axis-ordering-overlapped-items-006.html [ Failure ]
-crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-items/item-with-table-with-infinite-max-intrinsic-width.html [ Failure Crash ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-items/percentage-size-replaced-subitems-001.html [ Failure Crash ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-items/percentage-size-subitems-001.html [ Failure Crash ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-items/remove-svg-grid-item-001.html [ Failure Crash ]
-crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-items/table-with-infinite-max-intrinsic-width.html [ Failure Crash ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-layout-properties.html [ Failure Crash ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-model/column-property-should-not-apply-on-grid-container-001.html [ Failure Crash ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-model/compute-intrinsic-widths-scrollbar-001.html [ Failure Crash ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-model/display-grid.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-model/display-inline-grid.html [ Failure ]
-crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-model/fixed-width-intrinsic-width-should-exclude-scrollbar-001.html [ Failure Crash ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-model/grid-areas-overflowing-grid-container-004.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-model/grid-areas-overflowing-grid-container-005.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-model/grid-areas-overflowing-grid-container-006.html [ Failure ]
-crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-model/grid-areas-overflowing-grid-container-007.html [ Failure ]
-crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-model/grid-areas-overflowing-grid-container-008.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-model/grid-box-sizing-001.html [ Failure Crash ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-model/grid-button-001.html [ Failure Crash ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-model/grid-container-ignores-first-letter-001.html [ Failure Crash ]
@@ -4049,7 +4024,6 @@
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-model/grid-container-scrollbars-sizing-002.html [ Failure Crash ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-model/grid-container-sizing-constraints-001.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-model/grid-display-grid-001.html [ Failure Crash ]
-crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-model/grid-display-inline-grid-001.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-model/grid-first-letter-001.html [ Failure Crash ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-model/grid-first-letter-002.html [ Failure Crash ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-model/grid-first-letter-003.html [ Failure Crash ]
@@ -4069,7 +4043,6 @@
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-model/grid-inline-first-line-002.html [ Failure Crash ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-model/grid-inline-first-line-003.html [ Failure Crash ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-model/grid-inline-float-001.html [ Failure Crash ]
-crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-model/grid-inline-floats-no-intrude-001.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-model/grid-inline-margins-no-collapse-001.html [ Failure Crash ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-model/grid-inline-vertical-align-001.html [ Failure Crash ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-model/grid-item-accepts-first-letter-001.html [ Failure Crash ]
@@ -4100,7 +4073,6 @@
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/layout-algorithm/grid-as-flex-item-should-not-shrink-to-fit-005.html [ Failure Crash ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/layout-algorithm/grid-as-flex-item-should-not-shrink-to-fit-006.html [ Failure Crash ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/layout-algorithm/grid-as-flex-item-should-not-shrink-to-fit-007.html [ Failure Crash ]
-crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/layout-algorithm/grid-as-flex-item-should-not-shrink-to-fit-008.html [ Failure Crash ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/layout-algorithm/grid-automatic-minimum-for-auto-columns-001.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/layout-algorithm/grid-container-percentage-001.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/layout-algorithm/grid-container-percentage-002.html [ Failure ]
@@ -4137,7 +4109,7 @@
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/placement/grid-auto-flow-sparse-001.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/placement/grid-auto-placement-implicit-tracks-001.html [ Failure Crash ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/placement/grid-container-change-grid-tracks-recompute-child-positions-001.html [ Failure Crash ]
-crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/placement/grid-container-change-named-grid-recompute-child-positions-001.html [ Failure ]
+crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/placement/grid-container-change-named-grid-recompute-child-positions-001.html [ Failure Crash ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/placement/grid-layout-grid-span.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/placement/grid-layout-lines-shorthands.html [ Failure Crash ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/placement/grid-layout-lines.html [ Failure Crash ]
@@ -4153,20 +4125,15 @@
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/placement/grid-placement-using-named-grid-lines-009.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/auto-content-resolution-columns.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/auto-content-resolution-rows.html [ Failure ]
-crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/breadth-size-resolution-grid.html [ Pass ]
-crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/calc-resolution-grid-item.html [ Pass ]
-crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/containing-block-grids.html [ Failure Crash ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/flex-and-content-sized-resolution-columns.html [ Failure ]
-crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/flex-and-intrinsic-sizes.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/flex-content-sized-column-use-available-width.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/flex-content-sized-columns-resize.html [ Failure Timeout ]
-crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/floating-empty-grids.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-auto-columns-rows-auto-flow-resolution.html [ Failure Crash ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-auto-columns-rows-get-set.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-auto-columns-rows-resolution.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-auto-columns-rows-update.html [ Failure ]
-crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-auto-flow-resolution.html [ Failure ]
-crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-auto-flow-update.html [ Failure ]
+crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-auto-flow-resolution.html [ Failure Crash ]
+crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-auto-flow-update.html [ Failure Crash ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-auto-repeat-huge-grid.html [ Failure Crash ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-columns-rows-get-set-multiple.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-columns-rows-get-set.html [ Failure Crash ]
@@ -4204,7 +4171,7 @@
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-item-negative-indexes.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-item-negative-integer-explicit-grid-resolution.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-item-negative-position-resolution.html [ Failure ]
-crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-item-order-auto-flow-resolution.html [ Failure ]
+crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-item-order-auto-flow-resolution.html [ Failure Crash ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-item-order-paint-order.html [ Failure Crash ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-item-overflow-paint.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-item-overflow.html [ Failure Crash ]
@@ -4223,7 +4190,6 @@
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-item-text-background-not-interleaved.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-item-unknown-named-grid-line-resolution.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-item-with-border-in-fr.html [ Failure Crash ]
-crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-item-with-border-in-intrinsic.html [ Failure Crash ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-item-with-percent-height-in-auto-height-grid-resolution.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-item-with-percent-height-replaced-element.html [ Failure Crash ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-item-with-percent-min-max-height-dynamic.html [ Failure ]
@@ -4277,14 +4243,11 @@
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/named-grid-areas-dynamic-with-media-query.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/named-grid-line-get-set.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/named-grid-lines-computed-style-implicit-tracks.html [ Failure Crash ]
-crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/named-grid-lines-with-named-grid-areas-dynamic-get-set.html [ Failure ]
-crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/named-grid-lines-with-named-grid-areas-resolution.html [ Failure ]
+crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/named-grid-lines-with-named-grid-areas-dynamic-get-set.html [ Failure Crash ]
+crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/named-grid-lines-with-named-grid-areas-resolution.html [ Failure Crash ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/negative-growth-share-as-infinity-crash.html [ Failure Crash ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/non-grid-columns-rows-get-set.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/painting-item-marginbox-overflowing-grid-area.html [ Failure ]
-crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/percent-grid-item-in-percent-grid-track-in-percent-grid.html [ Failure ]
-crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/percent-grid-item-in-percent-grid-track-update.html [ Failure ]
-crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/percent-grid-item-in-percent-grid-track.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/percent-intrinsic-track-breadth.html [ Failure Crash ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/percent-of-indefinite-track-size-in-auto.html [ Failure Crash ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/percent-of-indefinite-track-size.html [ Failure Crash ]
@@ -4293,7 +4256,6 @@
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/percent-resolution-grid-item-children.html [ Failure Crash ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/percent-track-breadths-regarding-container-size.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/place-cell-by-index.html [ Failure ]
-crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/positioned-grid-container-item-percentage-size.html [ Pass ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/positioned-grid-container-percentage-tracks.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/preferred-width-computed-after-layout.html [ Failure Crash ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/relayout-align-items-changed.html [ Failure ]
@@ -4302,17 +4264,10 @@
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/relayout-justify-items-changed.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/relayout-justify-self-changed.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/repeating-layout-must-produce-the-same-results.html [ Failure Crash ]
-crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/scrolled-grid-painting-overflow.html [ Pass ]
-crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/scrolled-grid-painting.html [ Pass ]
 
 # These pass but hit DCHECKS
-crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/changing-content-property-on-nested-grid-should-not-crash.html [ Crash ]
-crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-item-stretching-must-not-depend-on-previous-layouts.html [ Crash ]
-crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-self-baseline-and-flex-tracks-with-indefinite-container-crash.html [ Crash ]
-crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/grid-self-baseline-followed-by-item-style-change-should-not-crash.html [ Crash ]
-crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/percent-resolution-grid-item.html [ Crash ]
-crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/quirks-mode-percent-resolution-grid-item.html [ Crash ]
-crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/setting-node-properties-to-null-during-layout-should-not-crash.html [ Crash ]
+crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-definition/grid-add-item-with-positioned-items-crash.html [ Crash ]
+crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-definition/grid-add-positioned-block-item-after-inline-item-crash.html [ Crash ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/mozilla/grid-repeat-auto-fill-fit-001.html [ Crash ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/mozilla/grid-repeat-auto-fill-fit-002.html [ Crash ]
 crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/mozilla/grid-repeat-auto-fill-fit-003.html [ Crash ]
diff --git a/third_party/blink/web_tests/external/wpt/svg/types/scripted/SVGElement.className-01.svg b/third_party/blink/web_tests/external/wpt/svg/types/scripted/SVGElement.className-01.svg
new file mode 100644
index 0000000..3812135
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/svg/types/scripted/SVGElement.className-01.svg
@@ -0,0 +1,15 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:h="http://www.w3.org/1999/xhtml">
+  <title>SVGElement.prototype.className: Reflects to .classList</title>
+  <h:link rel="help" href="https://svgwg.org/svg2-draft/types.html#__svg__SVGElement__className"/>
+  <h:script src="/resources/testharness.js"/>
+  <h:script src="/resources/testharnessreport.js"/>
+  <script>
+    test(() => {
+      const element = document.createElementNS('http://www.w3.org/2000/svg', 'g');
+
+      assert_false(element.classList.contains('one'), "classList is initially empty");
+      element.className.baseVal = 'one';
+      assert_true(element.classList.contains('one'), "className should reflect to classList");
+    });
+</script>
+</svg>
diff --git a/third_party/blink/web_tests/fast/events/mouse-cursor-image-set-svg.html b/third_party/blink/web_tests/fast/events/mouse-cursor-image-set-svg.html
index 90e2464..35f566a 100644
--- a/third_party/blink/web_tests/fast/events/mouse-cursor-image-set-svg.html
+++ b/third_party/blink/web_tests/fast/events/mouse-cursor-image-set-svg.html
@@ -52,6 +52,13 @@
         checkCursors();
         // Repeat in high-dpi mode
         testRunner.setBackingScaleFactor(2, function() {
+            // Flush style (and layout) to ensure that requests for the (cursor) resources
+            // referenced by style has been issued. This means that the ordering request performed
+            // below will be able to re-use the same (internal) resource if it is still pending.
+            // If the actual cursor request has already completed before the ordering request is
+            // issued then the ordering request will not re-use the same resource, but ordering is
+            // still guaranteed in that case.
+            testContainer.offsetTop;
             // Failed images are apparently reset on scale factor change.
             loadImages([{ url: 'doesntexist.svg', error: true }],
                         function() {
diff --git a/third_party/blink/web_tests/fast/events/mouse-cursor-image-set.html b/third_party/blink/web_tests/fast/events/mouse-cursor-image-set.html
index 08d8341..9ab3a6a5 100644
--- a/third_party/blink/web_tests/fast/events/mouse-cursor-image-set.html
+++ b/third_party/blink/web_tests/fast/events/mouse-cursor-image-set.html
@@ -53,6 +53,13 @@
         checkCursors();
         // Repeat in high-dpi mode
         testRunner.setBackingScaleFactor(2, function() {
+            // Flush style (and layout) to ensure that requests for the (cursor) resources
+            // referenced by style has been issued. This means that the ordering request performed
+            // below will be able to re-use the same (internal) resource if it is still pending.
+            // If the actual cursor request has already completed before the ordering request is
+            // issued then the ordering request will not re-use the same resource, but ordering is
+            // still guaranteed in that case.
+            testContainer.offsetTop;
             // Failed images are apparently reset on scale factor change.
             loadImages([{ url: 'doesntexist.png', error: true }],
                         function() {
diff --git a/third_party/d3/OWNERS b/third_party/d3/OWNERS
index 47ddd432..1cebebb 100644
--- a/third_party/d3/OWNERS
+++ b/third_party/d3/OWNERS
@@ -1,4 +1,6 @@
 siggi@chromium.org
 chrisha@chromium.org
+joonbug@chromium.org
 
 # COMPONENT: Internals>ResourceCoordinator
+# COMPONENT: OS>Systems>Diagnostics
\ No newline at end of file
diff --git a/tools/android/dependency_analysis/print_class_dependencies.py b/tools/android/dependency_analysis/print_class_dependencies.py
index ce9bae7..e017b2b 100755
--- a/tools/android/dependency_analysis/print_class_dependencies.py
+++ b/tools/android/dependency_analysis/print_class_dependencies.py
@@ -16,12 +16,19 @@
 import serialization
 
 
+# Return values of categorize_dependency().
+IGNORE = 'ignore'
+CLEAR = 'clear'
+PRINT = 'print'
+
+
 @dataclass
 class PrintMode:
     """Options of how and which dependencies to output."""
     inbound: bool
     outbound: bool
     ignore_modularized: bool
+    ignore_same_package: bool
     fully_qualified: bool
 
 
@@ -100,15 +107,35 @@
     return class_name in IGNORED_CLASSES
 
 
-def print_class_nodes(class_nodes: List[class_dependency.JavaClass],
-                      print_mode: PrintMode, class_name: str,
-                      direction: str) -> TargetDependencies:
+def categorize_dependency(from_class: class_dependency.JavaClass,
+                          to_class: class_dependency.JavaClass,
+                          ignore_modularized: bool,
+                          ignore_same_package: bool) -> str:
+    """Decides if a class dependency should be printed, cleared, or ignored."""
+    if is_ignored_class_dependency(to_class.name):
+        return IGNORE
+    if ignore_modularized and all(
+            is_allowed_target_dependency(target)
+            for target in to_class.build_targets):
+        return CLEAR
+    if ignore_same_package and to_class.package == from_class.package:
+        return IGNORE
+    return PRINT
+
+
+def print_class_dependencies(to_classes: List[class_dependency.JavaClass],
+                             print_mode: PrintMode,
+                             from_class: class_dependency.JavaClass,
+                             direction: str) -> TargetDependencies:
     """Prints the class dependencies to or from a class, grouped by target.
 
     If direction is OUTBOUND and print_mode.ignore_modularized is True, omits
     modularized outbound dependencies and returns the build targets that need
     to be added for those dependencies. In other cases, returns an empty
     TargetDependencies.
+
+    If print_mode.ignore_same_package is True, omits outbound dependencies in
+    the same package.
     """
     ignore_modularized = direction == OUTBOUND and print_mode.ignore_modularized
     bullet_point = '<-' if direction == INBOUND else '->'
@@ -121,43 +148,48 @@
     suspect_dependencies = 0
 
     target_dependencies = TargetDependencies()
-    class_nodes = sorted(class_nodes, key=lambda c: str(c.build_targets))
+    to_classes = sorted(to_classes, key=lambda c: str(c.build_targets))
     last_build_target = None
-    for class_node in class_nodes:
-        if is_ignored_class_dependency(class_node.name):
+
+    for to_class in to_classes:
+        # Check if dependency should be ignored due to --ignore-modularized,
+        # --ignore-same-package, or due to being an ignored class.
+        # Check if dependency should be listed as a cleared dep.
+        ignore_allow = categorize_dependency(from_class, to_class,
+                                             ignore_modularized,
+                                             print_mode.ignore_same_package)
+        if ignore_allow == CLEAR:
+            target_dependencies.update_with_class_node(to_class)
             continue
-        if ignore_modularized:
-            if all(
-                    is_allowed_target_dependency(target)
-                    for target in class_node.build_targets):
-                target_dependencies.update_with_class_node(class_node)
-                continue
-            else:
-                suspect_dependencies += 1
-        build_target = str(class_node.build_targets)
+        elif ignore_allow == IGNORE:
+            continue
+
+        # Print the dependency
+        suspect_dependencies += 1
+        build_target = str(to_class.build_targets)
         if last_build_target != build_target:
             build_target_names = [
                 get_build_target_name_to_display(target, print_mode)
-                for target in class_node.build_targets
+                for target in to_class.build_targets
             ]
             build_target_names_string = ", ".join(build_target_names)
             print_backlog.append((4, f'[{build_target_names_string}]'))
             last_build_target = build_target
-        display_name = get_class_name_to_display(class_node.name, print_mode)
+        display_name = get_class_name_to_display(to_class.name, print_mode)
         print_backlog.append((8, f'{bullet_point} {display_name}'))
 
     # Print header
+    class_name = get_class_name_to_display(from_class.name, print_mode)
     if ignore_modularized:
-        cleared = len(class_nodes) - suspect_dependencies
+        cleared = len(to_classes) - suspect_dependencies
         print(f'{class_name} has {suspect_dependencies} outbound dependencies '
               f'that may need to be broken (omitted {cleared} cleared '
               f'dependencies):')
     else:
         if direction == INBOUND:
-            print(f'{class_name} has {len(class_nodes)} inbound dependencies:')
+            print(f'{class_name} has {len(to_classes)} inbound dependencies:')
         else:
-            print(
-                f'{class_name} has {len(class_nodes)} outbound dependencies:')
+            print(f'{class_name} has {len(to_classes)} outbound dependencies:')
 
     # Print build targets and dependencies
     for indent, message in print_backlog:
@@ -173,15 +205,14 @@
     """Prints dependencies for a valid key into the class graph."""
     target_dependencies = TargetDependencies()
     node: class_dependency.JavaClass = class_graph.get_node_by_key(key)
-    class_name = get_class_name_to_display(node.name, print_mode)
 
     if print_mode.inbound:
-        print_class_nodes(graph.sorted_nodes_by_name(node.inbound), print_mode,
-                          class_name, INBOUND)
+        print_class_dependencies(graph.sorted_nodes_by_name(node.inbound),
+                                 print_mode, node, INBOUND)
 
     if print_mode.outbound:
-        target_dependencies = print_class_nodes(
-            graph.sorted_nodes_by_name(node.outbound), print_mode, class_name,
+        target_dependencies = print_class_dependencies(
+            graph.sorted_nodes_by_name(node.outbound), print_mode, node,
             OUTBOUND)
     return target_dependencies
 
@@ -235,6 +266,10 @@
                             help='Do not print outbound dependencies on '
                             'allowed (modules, components, base, etc.) '
                             'dependencies.')
+    arg_parser.add_argument('--ignore-same-package',
+                            action='store_true',
+                            help='Do not print outbound dependencies on '
+                            'classes in the same package.')
     arguments = arg_parser.parse_args()
 
     if not arguments.class_names and not arguments.package_names:
@@ -244,6 +279,7 @@
     print_mode = PrintMode(inbound=not arguments.outbound_only,
                            outbound=not arguments.inbound_only,
                            ignore_modularized=arguments.ignore_modularized,
+                           ignore_same_package=arguments.ignore_same_package,
                            fully_qualified=arguments.fully_qualified)
 
     class_graph, package_graph = \
diff --git a/tools/binary_size/libsupersize/static/auth.js b/tools/binary_size/libsupersize/static/auth.js
index 537d21a..4a9a75e 100644
--- a/tools/binary_size/libsupersize/static/auth.js
+++ b/tools/binary_size/libsupersize/static/auth.js
@@ -38,6 +38,9 @@
 }
 
 function requiresAuthentication() {
-  let urlParams = new URLSearchParams(window.location.search);
-  return !!urlParams.get('authenticate');
+  // Assume everything requires auth except public trybot and one-offs.
+  const isPublicTrybot = window.location.search.indexOf(
+      'chromium-binary-size-trybot-results%2Fandroid-binary-size') != -1;
+  const isOneOff = window.location.search.indexOf('%2Foneoffs%2F') != -1;
+  return !isPublicTrybot && !isOneOff;
 }
diff --git a/tools/binary_size/libsupersize/static/index.js b/tools/binary_size/libsupersize/static/index.js
index 329fc44..bce93c64 100644
--- a/tools/binary_size/libsupersize/static/index.js
+++ b/tools/binary_size/libsupersize/static/index.js
@@ -30,7 +30,7 @@
     event.preventDefault();
     const dataUrl = fetchDataUrl();
     window.open(
-        `${FIREBASE_HOST}/viewer.html?load_url=${dataUrl}&authenticate=1`);
+        `${FIREBASE_HOST}/viewer.html?load_url=${dataUrl}`);
   });
 }
 
diff --git a/tools/binary_size/trybot_commit_size_checker.py b/tools/binary_size/trybot_commit_size_checker.py
index 2846ae9..d60c9cf 100755
--- a/tools/binary_size/trybot_commit_size_checker.py
+++ b/tools/binary_size/trybot_commit_size_checker.py
@@ -26,8 +26,9 @@
 _FOR_TESTING_LOG = 'for_test_log'
 _DEX_SYMBOLS_LOG = 'dex_symbols_log'
 _SIZEDIFF_FILENAME = 'supersize_diff.sizediff'
-_HTML_REPORT_BASE_URL = (
-    'https://chrome-supersize.firebaseapp.com/viewer.html?load_url=')
+_HTML_REPORT_URL = (
+    'https://chrome-supersize.firebaseapp.com/viewer.html?load_url={{' +
+    _SIZEDIFF_FILENAME + '}}')
 _MAX_DEX_METHOD_COUNT_INCREASE = 50
 _MAX_NORMALIZED_INCREASE = 16 * 1024
 _MAX_PAK_INCREASE = 1024
@@ -207,14 +208,7 @@
   return None
 
 
-def _CreateTigerViewerUrl(apk_name, sizediff_path):
-  ret = _HTML_REPORT_BASE_URL + sizediff_path
-  if 'Public' not in apk_name:
-    ret += '&authenticate=1'
-  return ret
-
-
-def _GenerateBinarySizePluginDetails(apk_name, metrics):
+def _GenerateBinarySizePluginDetails(metrics):
   binary_size_listings = []
   for delta, log_name in metrics:
     listing = {
@@ -237,8 +231,7 @@
   binary_size_extras = [
       {
           'text': 'APK Breakdown',
-          'url': _CreateTigerViewerUrl(apk_name,
-                                       '{{' + _SIZEDIFF_FILENAME + '}}')
+          'url': _HTML_REPORT_URL
       },
   ]
 
@@ -359,8 +352,6 @@
     status_code = 0
 
   summary = '<br>' + checks_text.replace('\n', '<br>')
-  supersize_url = _CreateTigerViewerUrl(args.apk_name,
-                                        '{{' + _SIZEDIFF_FILENAME + '}}')
   links_json = [
       {
           'name': 'Binary Size Details',
@@ -388,14 +379,13 @@
       },
       {
           'name': 'SuperSize HTML Diff',
-          'url': supersize_url,
+          'url': _HTML_REPORT_URL,
       },
   ]
   # Remove empty diffs (Mutable Constants, Dex Method, ...).
   links_json = [o for o in links_json if o.get('lines') or o.get('url')]
 
-  binary_size_plugin_json = _GenerateBinarySizePluginDetails(
-      args.apk_name, metrics)
+  binary_size_plugin_json = _GenerateBinarySizePluginDetails(metrics)
 
   results_json = {
       'status_code': status_code,
diff --git a/tools/gritsettings/resource_ids.spec b/tools/gritsettings/resource_ids.spec
index c996129..e00d9a1 100644
--- a/tools/gritsettings/resource_ids.spec
+++ b/tools/gritsettings/resource_ids.spec
@@ -188,8 +188,8 @@
   "chrome/browser/resources/tab_search/tab_search_resources.grd": {
     "includes": [1880],
   },
-  "chrome/browser/resources/tab_strip/tab_strip_resources.grd": {
-    "structures": [1900],
+  "<(SHARED_INTERMEDIATE_DIR)/chrome/browser/resources/tab_strip/tab_strip_resources.grd": {
+    "META": {"sizes": {"includes": [20]}},
     "includes": [1920],
   },
   "<(SHARED_INTERMEDIATE_DIR)/chrome/browser/resources/welcome/welcome_resources.grd": {
@@ -265,6 +265,13 @@
   "chrome/browser/resources/webapks/webapks_ui_resources.grd": {
     "includes": [2220],
   },
+  "chrome/browser/resources/webui_js_exception/webui_js_exception_resources.grd": {
+    "includes": [2230],
+    "structures": [2231],
+  },
+  "chrome/browser/resources/webui_js_exception/webui_js_exception_resources_vulcanized.grd": {
+    "includes": [2232],
+  },
   "components/sync/driver/resources.grd": {
     "includes": [2240],
   },
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index 3b90b45..0bd19e8 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -275,8 +275,8 @@
       'Win10 Tests x64 1909': 'gpu_tests_release_bot_minimal_symbols',
       'android-code-coverage': 'gpu_tests_android_release_bot_minimal_symbols_arm64_fastbuild_java_coverage',
       'android-code-coverage-native': 'gpu_tests_android_release_bot_minimal_symbols_arm64_fastbuild_native_coverage',
-      'android-marshmallow-arm64-16-core': 'gpu_tests_android_release_trybot_arm64_resource_whitelisting_fastbuild_java_coverage',
-      'android-marshmallow-arm64-32-core': 'gpu_tests_android_release_trybot_arm64_resource_whitelisting_fastbuild_java_coverage',
+      'android-marshmallow-arm64-16-core': 'gpu_tests_android_release_trybot_arm64_fastbuild',
+      'android-marshmallow-arm64-32-core': 'gpu_tests_android_release_trybot_arm64_fastbuild',
       'chromeos-amd64-generic-lacros-rel': 'chromeos_amd64-generic_lacros_rel',
       'fuchsia-fyi-arm64-dbg': 'debug_bot_fuchsia_arm64',
       'fuchsia-fyi-arm64-rel': 'release_bot_fuchsia_arm64',
@@ -294,9 +294,9 @@
       'ios-simulator-cronet': 'ios_cronet_xctest',
       'ios-simulator-multi-window': 'ios_simulator_debug_static_bot_multi_window_xctest',
       'ios-webkit-tot': 'ios_simulator_debug_static_rbe_bot_xctest',
-      'linux-8-core': 'gpu_tests_release_trybot_no_symbols_use_dummy_lastchange_code_coverage',
-      'linux-16-core': 'gpu_tests_release_trybot_no_symbols_use_dummy_lastchange_code_coverage',
-      'linux-32-core': 'gpu_tests_release_trybot_no_symbols_use_dummy_lastchange_code_coverage',
+      'linux-8-core': 'gpu_tests_release_trybot_no_symbols',
+      'linux-16-core': 'gpu_tests_release_trybot_no_symbols',
+      'linux-32-core': 'gpu_tests_release_trybot_no_symbols',
       'linux-annotator-rel': 'release_bot',
       'linux-blink-animation-use-time-delta': 'debug_bot_enable_blink_animation_use_time_delta',
       'linux-blink-heap-concurrent-marking-tsan-rel': 'release_trybot_tsan',
@@ -319,9 +319,9 @@
       'mac-hermetic-upgrade-rel': 'release_bot',
       'mac-omaha-builder-rel': 'updater_release_bot',
       'mac-upload-perfetto': 'release_bot',
-      'win-8-core': 'gpu_tests_release_trybot_resource_whitelisting_code_coverage',
-      'win-16-core': 'gpu_tests_release_trybot_resource_whitelisting_code_coverage',
-      'win-32-core': 'gpu_tests_release_trybot_resource_whitelisting_code_coverage',
+      'win-8-core': 'gpu_tests_release_trybot',
+      'win-16-core': 'gpu_tests_release_trybot',
+      'win-32-core': 'gpu_tests_release_trybot',
       'win-annotator-rel': 'release_bot',
       'win-celab-builder-rel': 'release_bot_minimal_symbols',
       'win-omaha-builder-rel': 'updater_release_bot',
@@ -1983,6 +1983,10 @@
       'use_clang_coverage', 'partial_code_coverage_instrumentation',
     ],
 
+    'gpu_tests_release_trybot_no_symbols': [
+      'gpu_tests', 'release_trybot', 'no_symbols',
+    ],
+
     'gpu_tests_release_trybot_no_symbols_use_dummy_lastchange': [
       'gpu_tests', 'release_trybot', 'no_symbols', 'use_dummy_lastchange',
     ],
diff --git a/tools/mb/mb_config_expectations/chromium.fyi.json b/tools/mb/mb_config_expectations/chromium.fyi.json
index 12a51d81..c591e96 100644
--- a/tools/mb/mb_config_expectations/chromium.fyi.json
+++ b/tools/mb/mb_config_expectations/chromium.fyi.json
@@ -253,41 +253,33 @@
   },
   "android-marshmallow-arm64-16-core": {
     "gn_args": {
-      "coverage_instrumentation_input_file": "//.code-coverage/files_to_instrument.txt",
       "dcheck_always_on": true,
       "disable_android_lint": true,
-      "enable_resource_allowlist_generation": true,
       "ffmpeg_branding": "Chrome",
       "is_component_build": false,
       "is_debug": false,
       "proprietary_codecs": true,
       "symbol_level": 1,
-      "system_webview_package_name": "com.google.android.webview",
       "target_cpu": "arm64",
       "target_os": "android",
       "use_errorprone_java_compiler": false,
       "use_goma": true,
-      "use_jacoco_coverage": true,
       "use_static_angle": true
     }
   },
   "android-marshmallow-arm64-32-core": {
     "gn_args": {
-      "coverage_instrumentation_input_file": "//.code-coverage/files_to_instrument.txt",
       "dcheck_always_on": true,
       "disable_android_lint": true,
-      "enable_resource_allowlist_generation": true,
       "ffmpeg_branding": "Chrome",
       "is_component_build": false,
       "is_debug": false,
       "proprietary_codecs": true,
       "symbol_level": 1,
-      "system_webview_package_name": "com.google.android.webview",
       "target_cpu": "arm64",
       "target_os": "android",
       "use_errorprone_java_compiler": false,
       "use_goma": true,
-      "use_jacoco_coverage": true,
       "use_static_angle": true
     }
   },
@@ -499,43 +491,34 @@
   },
   "linux-16-core": {
     "gn_args": {
-      "coverage_instrumentation_input_file": "//.code-coverage/files_to_instrument.txt",
       "dcheck_always_on": true,
       "ffmpeg_branding": "Chrome",
       "is_component_build": false,
       "is_debug": false,
       "proprietary_codecs": true,
       "symbol_level": 0,
-      "use_clang_coverage": true,
-      "use_dummy_lastchange": true,
       "use_goma": true
     }
   },
   "linux-32-core": {
     "gn_args": {
-      "coverage_instrumentation_input_file": "//.code-coverage/files_to_instrument.txt",
       "dcheck_always_on": true,
       "ffmpeg_branding": "Chrome",
       "is_component_build": false,
       "is_debug": false,
       "proprietary_codecs": true,
       "symbol_level": 0,
-      "use_clang_coverage": true,
-      "use_dummy_lastchange": true,
       "use_goma": true
     }
   },
   "linux-8-core": {
     "gn_args": {
-      "coverage_instrumentation_input_file": "//.code-coverage/files_to_instrument.txt",
       "dcheck_always_on": true,
       "ffmpeg_branding": "Chrome",
       "is_component_build": false,
       "is_debug": false,
       "proprietary_codecs": true,
       "symbol_level": 0,
-      "use_clang_coverage": true,
-      "use_dummy_lastchange": true,
       "use_goma": true
     }
   },
@@ -774,43 +757,34 @@
   },
   "win-16-core": {
     "gn_args": {
-      "coverage_instrumentation_input_file": "//.code-coverage/files_to_instrument.txt",
       "dcheck_always_on": true,
-      "enable_resource_allowlist_generation": true,
       "ffmpeg_branding": "Chrome",
       "is_component_build": false,
       "is_debug": false,
       "proprietary_codecs": true,
       "symbol_level": 1,
-      "use_clang_coverage": true,
       "use_goma": true
     }
   },
   "win-32-core": {
     "gn_args": {
-      "coverage_instrumentation_input_file": "//.code-coverage/files_to_instrument.txt",
       "dcheck_always_on": true,
-      "enable_resource_allowlist_generation": true,
       "ffmpeg_branding": "Chrome",
       "is_component_build": false,
       "is_debug": false,
       "proprietary_codecs": true,
       "symbol_level": 1,
-      "use_clang_coverage": true,
       "use_goma": true
     }
   },
   "win-8-core": {
     "gn_args": {
-      "coverage_instrumentation_input_file": "//.code-coverage/files_to_instrument.txt",
       "dcheck_always_on": true,
-      "enable_resource_allowlist_generation": true,
       "ffmpeg_branding": "Chrome",
       "is_component_build": false,
       "is_debug": false,
       "proprietary_codecs": true,
       "symbol_level": 1,
-      "use_clang_coverage": true,
       "use_goma": true
     }
   },
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 32cc42d..ac91895 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -990,6 +990,10 @@
   <int value="13" label="GenericErrorShown">
     The generic error screen was shown to the user.
   </int>
+  <int value="14" label="DismissedButton">
+    User has dismissed the promo by tapping on the dismissal button in the
+    bottom sheet.
+  </int>
 </enum>
 
 <enum name="AccountManagerAccountAdditionSource">
@@ -22253,6 +22257,7 @@
   <int value="795" label="PhoneHubAllowed"/>
   <int value="796" label="PhoneHubNotificationsAllowed"/>
   <int value="797" label="PhoneHubTaskContinuationAllowed"/>
+  <int value="798" label="WifiSyncAndroidAllowed"/>
 </enum>
 
 <enum name="EnterprisePolicyDeviceIdValidity">
@@ -35966,6 +35971,9 @@
 </enum>
 
 <enum name="HomeButtonPreferenceStateType">
+  <obsolete>
+    Removed from code as of 10/2020.
+  </obsolete>
   <int value="0" label="User Disabled"/>
   <int value="1" label="User Enabled"/>
   <int value="2" label="Managed Disabled"/>
diff --git a/tools/metrics/histograms/histograms_xml/blink/histograms.xml b/tools/metrics/histograms/histograms_xml/blink/histograms.xml
index 60d1c81..f305fb5 100644
--- a/tools/metrics/histograms/histograms_xml/blink/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/blink/histograms.xml
@@ -702,18 +702,6 @@
   </summary>
 </histogram>
 
-<histogram name="Blink.Fonts.HarfBuzzFaceZeroCopyAccess" enum="BooleanSuccess"
-    expires_after="M88">
-  <owner>drott@chromium.org</owner>
-  <owner>layout-dev@chromium.org</owner>
-  <summary>
-    Counts success or failure of attempting to access font tables using the zero
-    copy instantiation method in the HarfBuzz integration layer. This value is
-    only recorded on non-Mac platforms. Reported each time a HarfBuzz face
-    object is created.
-  </summary>
-</histogram>
-
 <histogram name="Blink.Fonts.ShapeCache" units="units"
     expires_after="2021-10-15">
   <owner>drott@chromium.org</owner>
diff --git a/tools/metrics/histograms/histograms_xml/browser/histograms.xml b/tools/metrics/histograms/histograms_xml/browser/histograms.xml
index 4486008e..02be1c9 100644
--- a/tools/metrics/histograms/histograms_xml/browser/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/browser/histograms.xml
@@ -576,7 +576,7 @@
 </histogram>
 
 <histogram name="BrowserServices.BrowsableIntentCheck" units="ms"
-    expires_after="M88">
+    expires_after="M92">
   <owner>peconn@chromium.org</owner>
   <owner>peter@chromium.org</owner>
   <summary>
@@ -587,7 +587,7 @@
 </histogram>
 
 <histogram name="BrowserServices.ClientAppDataLoad" units="ms"
-    expires_after="M88">
+    expires_after="M92">
   <owner>peconn@chromium.org</owner>
   <owner>peter@chromium.org</owner>
   <summary>
@@ -598,7 +598,7 @@
 </histogram>
 
 <histogram name="BrowserServices.ServiceTabResolveInfoQuery" units="ms"
-    expires_after="M88">
+    expires_after="M92">
   <owner>peconn@chromium.org</owner>
   <owner>peter@chromium.org</owner>
   <summary>
@@ -607,7 +607,7 @@
   </summary>
 </histogram>
 
-<histogram name="BrowserServices.TwaOpenTime.V2" units="ms" expires_after="M88">
+<histogram name="BrowserServices.TwaOpenTime.V2" units="ms" expires_after="M92">
   <owner>peconn@chromium.org</owner>
   <owner>peter@chromium.org</owner>
   <summary>
@@ -617,7 +617,7 @@
 </histogram>
 
 <histogram name="BrowserServices.VerificationResult"
-    enum="BrowserServicesVerificationResult" expires_after="M88">
+    enum="BrowserServicesVerificationResult" expires_after="M92">
   <owner>peconn@chromium.org</owner>
   <owner>peter@chromium.org</owner>
   <summary>
@@ -627,7 +627,7 @@
 </histogram>
 
 <histogram name="BrowserServices.VerificationTime.Offline" units="ms"
-    expires_after="M88">
+    expires_after="M92">
   <owner>peconn@chromium.org</owner>
   <owner>peter@chromium.org</owner>
   <summary>
@@ -638,7 +638,7 @@
 </histogram>
 
 <histogram name="BrowserServices.VerificationTime.Online" units="ms"
-    expires_after="M88">
+    expires_after="M92">
   <owner>peconn@chromium.org</owner>
   <owner>peter@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/custom_tabs/histograms.xml b/tools/metrics/histograms/histograms_xml/custom_tabs/histograms.xml
index 0d14620..c4c18b92 100644
--- a/tools/metrics/histograms/histograms_xml/custom_tabs/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/custom_tabs/histograms.xml
@@ -64,7 +64,7 @@
 </histogram>
 
 <histogram name="CustomTabs.ConnectionStatusOnReturn.GSA"
-    enum="CustomTabsConnection" expires_after="M88">
+    enum="CustomTabsConnection" expires_after="M92">
   <owner>peconn@chromium.org</owner>
   <owner>peter@chromium.org</owner>
   <summary>
@@ -77,7 +77,7 @@
 </histogram>
 
 <histogram name="CustomTabs.ConnectionStatusOnReturn.NonGSA"
-    enum="CustomTabsConnection" expires_after="M88">
+    enum="CustomTabsConnection" expires_after="M92">
   <owner>peconn@chromium.org</owner>
   <owner>peter@chromium.org</owner>
   <summary>
@@ -90,7 +90,7 @@
 </histogram>
 
 <histogram base="true" name="CustomTabs.DetachedResourceRequest.Duration"
-    units="ms" expires_after="M87">
+    units="ms" expires_after="M92">
   <owner>lizeb@chromium.org</owner>
   <owner>cct-team@google.com</owner>
   <summary>
@@ -101,7 +101,7 @@
 </histogram>
 
 <histogram name="CustomTabs.DetachedResourceRequest.FinalStatus"
-    enum="NetErrorCodes" expires_after="M87">
+    enum="NetErrorCodes" expires_after="M92">
   <owner>lizeb@chromium.org</owner>
   <owner>cct-team@google.com</owner>
   <summary>
@@ -113,7 +113,7 @@
 </histogram>
 
 <histogram base="true" name="CustomTabs.DetachedResourceRequest.RedirectsCount"
-    units="redirects" expires_after="M87">
+    units="redirects" expires_after="M92">
   <owner>lizeb@chromium.org</owner>
   <owner>cct-team@google.com</owner>
   <summary>
@@ -165,7 +165,7 @@
 </histogram>
 
 <histogram name="CustomTabs.ParallelRequestStatusOnStart"
-    enum="CustomTabsParallelRequestStatusOnStart" expires_after="M87">
+    enum="CustomTabsParallelRequestStatusOnStart" expires_after="M92">
   <owner>lizeb@chromium.org</owner>
   <owner>cct-team@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/gpu/histograms.xml b/tools/metrics/histograms/histograms_xml/gpu/histograms.xml
index e173c3d..ece9c804 100644
--- a/tools/metrics/histograms/histograms_xml/gpu/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/gpu/histograms.xml
@@ -273,6 +273,11 @@
 </histogram>
 
 <histogram name="GPU.ContextMemory" units="MB" expires_after="2020-09-13">
+  <obsolete>
+    Obsoleted since 2020/10/15. Unused and expired. Replaced by
+    Memory.Gpu.PrivateMemoryFootprint, Memory.GPU.PeakMemoryUsage, and
+    Memory.GPU.PeakMemoryAllocationSource.
+  </obsolete>
   <owner>ericrk@chromium.org</owner>
   <summary>The amount of memory used by a GL Context.</summary>
 </histogram>
diff --git a/tools/metrics/histograms/histograms_xml/histogram_suffixes_list.xml b/tools/metrics/histograms/histograms_xml/histogram_suffixes_list.xml
index 8ada5cd..0d26252 100644
--- a/tools/metrics/histograms/histograms_xml/histogram_suffixes_list.xml
+++ b/tools/metrics/histograms/histograms_xml/histogram_suffixes_list.xml
@@ -6820,6 +6820,9 @@
 </histogram_suffixes>
 
 <histogram_suffixes name="GPU_ContextType" separator=".">
+  <obsolete>
+    Expired in 2020/10/15.
+  </obsolete>
   <suffix name="GLES" label="GLES Context."/>
   <suffix name="WebGL" label="WebGL Context."/>
   <affected-histogram name="GPU.ContextMemory"/>
@@ -6835,6 +6838,9 @@
 </histogram_suffixes>
 
 <histogram_suffixes name="GPU_MemorySamplingTime" separator=".">
+  <obsolete>
+    Expired in 2020/10/15.
+  </obsolete>
   <suffix name="Periodic" label="Sampled periodically."/>
   <suffix name="Pressure" label="Sampled on CRITICAL memory pressure signal."/>
   <suffix name="Shutdown" label="Sampled at shutdown."/>
diff --git a/tools/metrics/histograms/histograms_xml/mobile/histograms.xml b/tools/metrics/histograms/histograms_xml/mobile/histograms.xml
index a981400b..4b07bf5 100644
--- a/tools/metrics/histograms/histograms_xml/mobile/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/mobile/histograms.xml
@@ -579,6 +579,13 @@
   </summary>
 </histogram>
 
+<histogram name="MobileDownload.Location.Dialog.SuggestionSelected"
+    enum="BooleanSelected" expires_after="2021-03-01">
+  <owner>xingliu@chromium.org</owner>
+  <owner>clank-downloads@google.com</owner>
+  <summary>Records the download location suggestion choice.</summary>
+</histogram>
+
 <histogram name="MobileDownload.Location.Dialog.Type"
     enum="DownloadLocationDialogType" expires_after="2021-03-01">
   <owner>xingliu@chromium.org</owner>
diff --git a/tools/metrics/histograms/histograms_xml/obsolete_histograms.xml b/tools/metrics/histograms/histograms_xml/obsolete_histograms.xml
index 1ddeaf6..0ff5c6c 100644
--- a/tools/metrics/histograms/histograms_xml/obsolete_histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/obsolete_histograms.xml
@@ -6520,6 +6520,21 @@
   </summary>
 </histogram>
 
+<histogram name="Blink.Fonts.HarfBuzzFaceZeroCopyAccess" enum="BooleanSuccess"
+    expires_after="M88">
+  <obsolete>
+    Removed as of 10/15/2020.
+  </obsolete>
+  <owner>drott@chromium.org</owner>
+  <owner>layout-dev@chromium.org</owner>
+  <summary>
+    Counts success or failure of attempting to access font tables using the zero
+    copy instantiation method in the HarfBuzz integration layer. This value is
+    only recorded on non-Mac platforms. Reported each time a HarfBuzz face
+    object is created.
+  </summary>
+</histogram>
+
 <histogram name="Blink.Gesture.Merged" enum="GestureMergeState"
     expires_after="M77">
   <obsolete>
diff --git a/tools/metrics/histograms/histograms_xml/omnibox/histograms.xml b/tools/metrics/histograms/histograms_xml/omnibox/histograms.xml
index 5f71d84..6fa9d932 100644
--- a/tools/metrics/histograms/histograms_xml/omnibox/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/omnibox/histograms.xml
@@ -232,7 +232,8 @@
 </histogram>
 
 <histogram name="Omnibox.DocumentSuggest.Requests"
-    enum="OmniboxDocumentSuggestRequests" expires_after="M88">
+    enum="OmniboxDocumentSuggestRequests" expires_after="M95">
+  <owner>manukh@chromium.org</owner>
   <owner>mpearson@chromium.org</owner>
   <owner>jdonnelly@chromium.org</owner>
   <owner>skare@chromium.org</owner>
@@ -243,7 +244,8 @@
 </histogram>
 
 <histogram name="Omnibox.DocumentSuggest.ResultCount" units="count"
-    expires_after="M88">
+    expires_after="M95">
+  <owner>manukh@chromium.org</owner>
   <owner>mpearson@chromium.org</owner>
   <owner>jdonnelly@chromium.org</owner>
   <owner>skare@chromium.org</owner>
diff --git a/tools/metrics/histograms/histograms_xml/others/histograms.xml b/tools/metrics/histograms/histograms_xml/others/histograms.xml
index e2895785..caa09e2 100644
--- a/tools/metrics/histograms/histograms_xml/others/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/others/histograms.xml
@@ -13258,7 +13258,7 @@
 </histogram>
 
 <histogram name="SingleWebsitePreferences.NavigatedFromToReset"
-    enum="SettingsNavigationSources" expires_after="M88">
+    enum="SettingsNavigationSources" expires_after="M92">
   <owner>peconn@chromium.org</owner>
   <owner>peter@chromium.org</owner>
   <summary>
@@ -13871,8 +13871,8 @@
   </summary>
 </histogram>
 
-<histogram name="Sqlite.OpenFailure" enum="SqliteErrorCode" expires_after="M88">
-  <owner>costan@google.com</owner>
+<histogram name="Sqlite.OpenFailure" enum="SqliteErrorCode" expires_after="M95">
+  <owner>pwnall@chromium.org</owner>
   <owner>src/storage/OWNERS</owner>
   <summary>Error which prevented database open.</summary>
 </histogram>
@@ -14538,14 +14538,14 @@
     (e.g. #:=:text=SELECTOR). Indicates source type that navigation originated
     from. It only know about search engines that come from the pre-populated
     list installed with Chrome. If the user uses a search engine not on the
-    list, it won't count as a search engine. This is even true if the user have
-    a custom search engine. These custom search engines can be created by hand
-    or auto-generated. In either case, it's not going to count as a search
-    engine. Also, it only checks to see if the *referrer* is from a domain
-    associated with search engine. If someone posts a link somewhere on a web
-    site that happens to have a search engine known to Google and that links
-    includes the fragment tokens, then if a user clicks that link, it'll look
-    like it came from a search engine even thought they did not.
+    list, it will be recorded as Unknown. This is even true if the user have a
+    custom search engine. These custom search engines can be created by hand or
+    auto-generated. In either case, it's not going to count as a search engine.
+    Also, it only checks to see if the *referrer* is from a domain associated
+    with search engine. If someone posts a link somewhere on a web site that
+    happens to have a search engine known to Google and that links includes the
+    fragment tokens, then if a user clicks that link, it'll look like it came
+    from a search engine even thought they did not.
   </summary>
 </histogram>
 
@@ -15410,7 +15410,7 @@
 </histogram>
 
 <histogram name="TrustedWebActivity.ClearDataDialogOnClearAppDataAccepted"
-    enum="Boolean" expires_after="M88">
+    enum="Boolean" expires_after="M92">
   <owner>peconn@chromium.org</owner>
   <owner>peter@chromium.org</owner>
   <summary>
@@ -15420,7 +15420,7 @@
 </histogram>
 
 <histogram name="TrustedWebActivity.ClearDataDialogOnUninstallAccepted"
-    enum="Boolean" expires_after="M88">
+    enum="Boolean" expires_after="M92">
   <owner>peconn@chromium.org</owner>
   <owner>peter@chromium.org</owner>
   <summary>
@@ -15431,7 +15431,7 @@
 
 <histogram name="TrustedWebActivity.DelegatedNotificationSmallIconFallback"
     enum="TrustedWebActivityDelegatedNotificationSmallIconFallback"
-    expires_after="M88">
+    expires_after="M92">
   <owner>peconn@chromium.org</owner>
   <owner>peter@chromium.org</owner>
   <summary>
@@ -15509,14 +15509,14 @@
 </histogram>
 
 <histogram name="TrustedWebActivity.ShareTargetRequest"
-    enum="WebShareTargetMethod" expires_after="M88">
+    enum="WebShareTargetMethod" expires_after="M92">
   <owner>peconn@chromium.org</owner>
   <owner>peter@chromium.org</owner>
   <summary>Recorded when data is shared via a Trusted Web Activity.</summary>
 </histogram>
 
 <histogram name="TrustedWebActivity.SplashScreenShown" enum="Boolean"
-    expires_after="M88">
+    expires_after="M92">
   <owner>peconn@chromium.org</owner>
   <owner>peter@chromium.org</owner>
   <summary>
@@ -15526,7 +15526,7 @@
 </histogram>
 
 <histogram name="TrustedWebActivity.TimeInVerifiedOrigin.V2" units="ms"
-    expires_after="M88">
+    expires_after="M92">
   <owner>peconn@chromium.org</owner>
   <owner>peter@chromium.org</owner>
   <summary>
@@ -15536,7 +15536,7 @@
 </histogram>
 
 <histogram name="TrustedWebActivity.TimeOutOfVerifiedOrigin.V2" units="ms"
-    expires_after="M88">
+    expires_after="M92">
   <owner>peconn@chromium.org</owner>
   <owner>peter@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/settings/histograms.xml b/tools/metrics/histograms/histograms_xml/settings/histograms.xml
index f7b360f..775cfd9 100644
--- a/tools/metrics/histograms/histograms_xml/settings/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/settings/histograms.xml
@@ -85,6 +85,9 @@
 
 <histogram name="Settings.HomePageIsCustomized" enum="Boolean"
     expires_after="2020-08-28">
+  <obsolete>
+    Expired and removed from code as of 10/2020.
+  </obsolete>
   <owner>twellington@chromium.org</owner>
   <owner>tedchoc@chromium.org</owner>
   <summary>
@@ -272,6 +275,9 @@
 
 <histogram name="Settings.ShowHomeButtonPreferenceState" enum="BooleanEnabled"
     expires_after="M85">
+  <obsolete>
+    Expired and removed from code as of 10/2020.
+  </obsolete>
   <owner>twellington@chromium.org</owner>
   <owner>tedchoc@chromium.org</owner>
   <summary>
@@ -287,6 +293,9 @@
 
 <histogram name="Settings.ShowHomeButtonPreferenceStateChanged"
     enum="BooleanEnabled" expires_after="M88">
+  <obsolete>
+    Removed from code as of 10/2020.
+  </obsolete>
   <owner>twellington@chromium.org</owner>
   <owner>tedchoc@chromium.org</owner>
   <summary>
@@ -297,6 +306,9 @@
 
 <histogram name="Settings.ShowHomeButtonPreferenceStateManaged"
     enum="HomeButtonPreferenceStateType" expires_after="M88">
+  <obsolete>
+    Removed from code as of 10/2020.
+  </obsolete>
   <owner>twellington@chromium.org</owner>
   <owner>tedchoc@chromium.org</owner>
   <summary>
diff --git a/ui/aura/native_window_occlusion_tracker_win.cc b/ui/aura/native_window_occlusion_tracker_win.cc
index 6dcc8e61..66fd0b9 100644
--- a/ui/aura/native_window_occlusion_tracker_win.cc
+++ b/ui/aura/native_window_occlusion_tracker_win.cc
@@ -519,6 +519,10 @@
   // maximize and native window restore events.
   RegisterGlobalEventHook(EVENT_OBJECT_STATECHANGE, EVENT_OBJECT_STATECHANGE);
 
+  // Cloaking and uncloaking of windows should trigger an occlusion calculation.
+  // In particular, switching virtual desktops seems to generate these events.
+  RegisterGlobalEventHook(EVENT_OBJECT_CLOAKED, EVENT_OBJECT_UNCLOAKED);
+
   // Determine which subset of processes to set EVENT_OBJECT_LOCATIONCHANGE on
   // because otherwise event throughput is very high, as it generates events
   // for location changes of all objects, including the mouse moving on top of a
diff --git a/ui/chromeos/OWNERS b/ui/chromeos/OWNERS
index f179ceafe..86f9ccc 100644
--- a/ui/chromeos/OWNERS
+++ b/ui/chromeos/OWNERS
@@ -1,7 +1,7 @@
 afakhry@chromium.org
 jamescook@chromium.org
+khorimoto@chromium.org
 oshima@chromium.org
-stevenjb@chromium.org
 xiyuan@chromium.org
 
 per-file file_manager_strings.grdp=*
diff --git a/ui/events/DIR_METADATA b/ui/events/DIR_METADATA
new file mode 100644
index 0000000..27f51aa
--- /dev/null
+++ b/ui/events/DIR_METADATA
@@ -0,0 +1,11 @@
+# Metadata information for this directory.
+#
+# For more information on DIR_METADATA files, see:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
+#
+# For the schema of this file, see Metadata message:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
+
+monorail {
+  component: "UI>Input"
+}
\ No newline at end of file
diff --git a/ui/events/OWNERS b/ui/events/OWNERS
index ba90608..230180c6 100644
--- a/ui/events/OWNERS
+++ b/ui/events/OWNERS
@@ -1,5 +1,4 @@
 sadrul@chromium.org
 
 # If you're doing structural changes get a review from one of the OWNERS.
-per-file BUILD.gn=*
-# COMPONENT: UI>Input
+per-file BUILD.gn=*
\ No newline at end of file
diff --git a/ui/events/android/DIR_METADATA b/ui/events/android/DIR_METADATA
new file mode 100644
index 0000000..1c611eb
--- /dev/null
+++ b/ui/events/android/DIR_METADATA
@@ -0,0 +1,12 @@
+# Metadata information for this directory.
+#
+# For more information on DIR_METADATA files, see:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
+#
+# For the schema of this file, see Metadata message:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
+
+monorail {
+  component: "Blink>Input"
+}
+team_email: "input-dev@chromium.org"
\ No newline at end of file
diff --git a/ui/events/android/OWNERS b/ui/events/android/OWNERS
index e19954f..6e91dc0e 100644
--- a/ui/events/android/OWNERS
+++ b/ui/events/android/OWNERS
@@ -1,5 +1,2 @@
 tdresser@chromium.org
 nzolghadr@chromium.org
-
-# TEAM: input-dev@chromium.org
-# COMPONENT: Blink>Input
diff --git a/ui/events/blink/DIR_METADATA b/ui/events/blink/DIR_METADATA
new file mode 100644
index 0000000..1c611eb
--- /dev/null
+++ b/ui/events/blink/DIR_METADATA
@@ -0,0 +1,12 @@
+# Metadata information for this directory.
+#
+# For more information on DIR_METADATA files, see:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
+#
+# For the schema of this file, see Metadata message:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
+
+monorail {
+  component: "Blink>Input"
+}
+team_email: "input-dev@chromium.org"
\ No newline at end of file
diff --git a/ui/events/blink/OWNERS b/ui/events/blink/OWNERS
index c81e7a79..8968c2d 100644
--- a/ui/events/blink/OWNERS
+++ b/ui/events/blink/OWNERS
@@ -2,6 +2,3 @@
 tdresser@chromium.org
 bokan@chromium.org
 nzolghadr@chromium.org
-
-# TEAM: input-dev@chromium.org
-# COMPONENT: Blink>Input
diff --git a/ui/events/fuchsia/DIR_METADATA b/ui/events/fuchsia/DIR_METADATA
new file mode 100644
index 0000000..de0c11f0
--- /dev/null
+++ b/ui/events/fuchsia/DIR_METADATA
@@ -0,0 +1,13 @@
+# Metadata information for this directory.
+#
+# For more information on DIR_METADATA files, see:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
+#
+# For the schema of this file, see Metadata message:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
+
+monorail {
+  component: "Fuchsia"
+}
+team_email: "cr-fuchsia@chromium.org"
+os: FUCHSIA
\ No newline at end of file
diff --git a/ui/events/fuchsia/OWNERS b/ui/events/fuchsia/OWNERS
index c1dc0d5..e69de29 100644
--- a/ui/events/fuchsia/OWNERS
+++ b/ui/events/fuchsia/OWNERS
@@ -1,3 +0,0 @@
-# COMPONENT: Fuchsia
-# OS: Fuchsia
-# TEAM: cr-fuchsia@chromium.org
diff --git a/ui/events/gesture_detection/DIR_METADATA b/ui/events/gesture_detection/DIR_METADATA
new file mode 100644
index 0000000..1c611eb
--- /dev/null
+++ b/ui/events/gesture_detection/DIR_METADATA
@@ -0,0 +1,12 @@
+# Metadata information for this directory.
+#
+# For more information on DIR_METADATA files, see:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
+#
+# For the schema of this file, see Metadata message:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
+
+monorail {
+  component: "Blink>Input"
+}
+team_email: "input-dev@chromium.org"
\ No newline at end of file
diff --git a/ui/events/gesture_detection/OWNERS b/ui/events/gesture_detection/OWNERS
index 7d088914c..234d059 100644
--- a/ui/events/gesture_detection/OWNERS
+++ b/ui/events/gesture_detection/OWNERS
@@ -3,6 +3,3 @@
 
 per-file gesture_configuration_cast.cc=alexst@chromium.org
 per-file gesture_configuration_cast.cc=kpschoedel@chromium.org
-
-# TEAM: input-dev@chromium.org
-# COMPONENT: Blink>Input
diff --git a/ui/events/gestures/DIR_METADATA b/ui/events/gestures/DIR_METADATA
new file mode 100644
index 0000000..44eb665
--- /dev/null
+++ b/ui/events/gestures/DIR_METADATA
@@ -0,0 +1,9 @@
+# Metadata information for this directory.
+#
+# For more information on DIR_METADATA files, see:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
+#
+# For the schema of this file, see Metadata message:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
+
+team_email: "input-dev@chromium.org"
\ No newline at end of file
diff --git a/ui/events/gestures/OWNERS b/ui/events/gestures/OWNERS
index 4b27ef7..7ac1c37 100644
--- a/ui/events/gestures/OWNERS
+++ b/ui/events/gestures/OWNERS
@@ -2,6 +2,3 @@
 sadrul@chromium.org
 tdresser@chromium.org
 nzolghadr@chromium.org
-
-# TEAM: input-dev@chromium.org
-# COMPONENT: UI>Input
diff --git a/ui/events/keycodes/DIR_METADATA b/ui/events/keycodes/DIR_METADATA
new file mode 100644
index 0000000..9a04abcb
--- /dev/null
+++ b/ui/events/keycodes/DIR_METADATA
@@ -0,0 +1,11 @@
+# Metadata information for this directory.
+#
+# For more information on DIR_METADATA files, see:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
+#
+# For the schema of this file, see Metadata message:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
+
+monorail {
+  component: "IO>Keyboard"
+}
\ No newline at end of file
diff --git a/ui/events/keycodes/OWNERS b/ui/events/keycodes/OWNERS
index 0fe96b8e..a49314c 100644
--- a/ui/events/keycodes/OWNERS
+++ b/ui/events/keycodes/OWNERS
@@ -1,4 +1,2 @@
 garykac@chromium.org
 wez@chromium.org
-
-# COMPONENT: IO>Keyboard
diff --git a/ui/events/ozone/DIR_METADATA b/ui/events/ozone/DIR_METADATA
new file mode 100644
index 0000000..b8b9277b
--- /dev/null
+++ b/ui/events/ozone/DIR_METADATA
@@ -0,0 +1,9 @@
+# Metadata information for this directory.
+#
+# For more information on DIR_METADATA files, see:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
+#
+# For the schema of this file, see Metadata message:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
+
+team_email: "ozone-dev@chromium.org"
\ No newline at end of file
diff --git a/ui/events/ozone/OWNERS b/ui/events/ozone/OWNERS
index cee2fef..10c6989 100644
--- a/ui/events/ozone/OWNERS
+++ b/ui/events/ozone/OWNERS
@@ -7,5 +7,3 @@
 
 # Touchscreen palm rejection
 robsc@chromium.org
-
-# TEAM: ozone-dev@chromium.org
diff --git a/ui/events/x/OWNERS b/ui/events/x/OWNERS
index 2f2cf8f..280ba478 100644
--- a/ui/events/x/OWNERS
+++ b/ui/events/x/OWNERS
@@ -1,3 +1 @@
 thomasanderson@chromium.org
-
-# COMPONENT: UI>Input
diff --git a/ui/gfx/BUILD.gn b/ui/gfx/BUILD.gn
index 1d7b3f7..1b1c5f4 100644
--- a/ui/gfx/BUILD.gn
+++ b/ui/gfx/BUILD.gn
@@ -39,6 +39,8 @@
 component("geometry_skia") {
   sources = [
     "geometry_skia_export.h",
+    "mask_filter_info.cc",
+    "mask_filter_info.h",
     "rrect_f.cc",
     "rrect_f.h",
     "rrect_f_builder.cc",
diff --git a/ui/gfx/DIR_METADATA b/ui/gfx/DIR_METADATA
new file mode 100644
index 0000000..6618219
--- /dev/null
+++ b/ui/gfx/DIR_METADATA
@@ -0,0 +1,12 @@
+# Metadata information for this directory.
+#
+# For more information on DIR_METADATA files, see:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
+#
+# For the schema of this file, see Metadata message:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
+
+monorail {
+  component: "UI>GFX"
+}
+team_email: "graphics-dev@chromium.org"
\ No newline at end of file
diff --git a/ui/gfx/OWNERS b/ui/gfx/OWNERS
index 1dbd16ec..e34675cb 100644
--- a/ui/gfx/OWNERS
+++ b/ui/gfx/OWNERS
@@ -60,6 +60,3 @@
 
 # If you're doing structural changes get a review from one of the OWNERS.
 per-file BUILD.gn=*
-
-# COMPONENT: UI>GFX
-# TEAM: graphics-dev@chromium.org
diff --git a/ui/gfx/android/OWNERS b/ui/gfx/android/OWNERS
index 8e64bbe..15a182a6 100644
--- a/ui/gfx/android/OWNERS
+++ b/ui/gfx/android/OWNERS
@@ -1,3 +1 @@
 skyostil@chromium.org
-
-# COMPONENT: UI>GFX
diff --git a/ui/gfx/animation/DIR_METADATA b/ui/gfx/animation/DIR_METADATA
new file mode 100644
index 0000000..087c8bf
--- /dev/null
+++ b/ui/gfx/animation/DIR_METADATA
@@ -0,0 +1,12 @@
+# Metadata information for this directory.
+#
+# For more information on DIR_METADATA files, see:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
+#
+# For the schema of this file, see Metadata message:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
+
+monorail {
+  component: "Internals>Compositing>Animation"
+}
+team_email: "threaded-rendering-dev@chromium.org"
\ No newline at end of file
diff --git a/ui/gfx/animation/OWNERS b/ui/gfx/animation/OWNERS
index 03d2109..a56274e 100644
--- a/ui/gfx/animation/OWNERS
+++ b/ui/gfx/animation/OWNERS
@@ -1,5 +1,2 @@
 flackr@chromium.org
 vollick@chromium.org
-
-# TEAM: threaded-rendering-dev@chromium.org
-# COMPONENT: Internals>Compositing>Animation
diff --git a/ui/gfx/codec/OWNERS b/ui/gfx/codec/OWNERS
index 3bcf86b..254c0ec 100644
--- a/ui/gfx/codec/OWNERS
+++ b/ui/gfx/codec/OWNERS
@@ -1,4 +1,2 @@
 dcheng@chromium.org
 scroggo@google.com
-
-# COMPONENT: UI>GFX
diff --git a/ui/gfx/image/OWNERS b/ui/gfx/image/OWNERS
index 8e89a42a..23dbb5a 100644
--- a/ui/gfx/image/OWNERS
+++ b/ui/gfx/image/OWNERS
@@ -2,5 +2,3 @@
 
 # ImageSkia related classes except for _mac/_ios stuff
 per-file image_skia*=oshima@chromium.org
-
-# COMPONENT: UI>GFX
diff --git a/ui/gfx/mask_filter_info.cc b/ui/gfx/mask_filter_info.cc
new file mode 100644
index 0000000..fd06a24
--- /dev/null
+++ b/ui/gfx/mask_filter_info.cc
@@ -0,0 +1,29 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/gfx/mask_filter_info.h"
+
+#include <sstream>
+
+#include "ui/gfx/transform.h"
+
+namespace gfx {
+
+MaskFilterInfo::MaskFilterInfo(const RectF& bounds,
+                               const RoundedCornersF& radii,
+                               bool is_fast_rounded_corner)
+    : rounded_corner_bounds_(bounds, radii),
+      is_fast_rounded_corner_(is_fast_rounded_corner) {}
+
+bool MaskFilterInfo::Transform(const gfx::Transform& transform) {
+  return transform.TransformRRectF(&rounded_corner_bounds_);
+}
+
+std::string MaskFilterInfo::ToString() const {
+  return "MaskFilterInfo{" + rounded_corner_bounds_.ToString() +
+         ", fast_rounded_corner=" +
+         (is_fast_rounded_corner_ ? "true" : "false") + "}";
+}
+
+}  // namespace gfx
diff --git a/ui/gfx/mask_filter_info.h b/ui/gfx/mask_filter_info.h
new file mode 100644
index 0000000..ef2e14a
--- /dev/null
+++ b/ui/gfx/mask_filter_info.h
@@ -0,0 +1,72 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_GFX_MASK_FILTER_INFO_H_
+#define UI_GFX_MASK_FILTER_INFO_H_
+
+#include "ui/gfx/geometry/rect_f.h"
+#include "ui/gfx/geometry_skia_export.h"
+#include "ui/gfx/rrect_f.h"
+
+namespace gfx {
+
+class Transform;
+
+class GEOMETRY_SKIA_EXPORT MaskFilterInfo {
+ public:
+  MaskFilterInfo() = default;
+  MaskFilterInfo(const RRectF& rrect, bool is_fast_rounded_corner)
+      : rounded_corner_bounds_(rrect),
+        is_fast_rounded_corner_(is_fast_rounded_corner) {}
+  MaskFilterInfo(const RectF& bounds,
+                 const RoundedCornersF& radii,
+                 bool is_fast_rounded_corner);
+  MaskFilterInfo(const MaskFilterInfo& copy) = default;
+  ~MaskFilterInfo() = default;
+
+  // The bounds the filter will be applied to.
+  RectF bounds() const { return rounded_corner_bounds_.rect(); }
+
+  // Defines the rounded corner bounds to clip.
+  const RRectF& rounded_corner_bounds() const { return rounded_corner_bounds_; }
+
+  // If true, it makes the filter not trigger a render surface when the rounded
+  // corners is defined if possible.
+  bool is_fast_rounded_corner() const { return is_fast_rounded_corner_; }
+
+  // True if this contains a rounded corner mask.
+  bool HasRoundedCorners() const {
+    return !IsEmpty() &&
+           rounded_corner_bounds_.GetType() != RRectF::Type::kRect;
+  }
+
+  // True if this contains no effective mask information.
+  bool IsEmpty() const { return rounded_corner_bounds_.IsEmpty(); }
+
+  // Transform the mask information. Returns false if the transform
+  // cannot be applied.
+  bool Transform(const Transform& transform);
+
+  std::string ToString() const;
+
+ private:
+  // The rounded corner bounds. This also defines the bounds that the mask
+  // filter will be applied to.
+  RRectF rounded_corner_bounds_;
+
+  bool is_fast_rounded_corner_ = false;
+};
+
+inline bool operator==(const MaskFilterInfo& lhs, const MaskFilterInfo& rhs) {
+  return lhs.rounded_corner_bounds() == rhs.rounded_corner_bounds() &&
+         lhs.is_fast_rounded_corner() == rhs.is_fast_rounded_corner();
+}
+
+inline bool operator!=(const MaskFilterInfo& lhs, const MaskFilterInfo& rhs) {
+  return !(lhs == rhs);
+}
+
+}  // namespace gfx
+
+#endif  // UI_GFX_MASK_FILTER_INFO_H_
diff --git a/ui/gfx/mojom/BUILD.gn b/ui/gfx/mojom/BUILD.gn
index 5faeacec..6e2665c5 100644
--- a/ui/gfx/mojom/BUILD.gn
+++ b/ui/gfx/mojom/BUILD.gn
@@ -14,6 +14,7 @@
     "display_color_spaces.mojom",
     "font_render_params.mojom",
     "gpu_fence_handle.mojom",
+    "mask_filter_info.mojom",
     "overlay_transform.mojom",
     "presentation_feedback.mojom",
     "rrect_f.mojom",
@@ -206,6 +207,17 @@
       traits_headers = [ "swap_result_mojom_traits.h" ]
       traits_public_deps = [ "//ui/gfx" ]
     },
+    {
+      types = [
+        {
+          mojom = "gfx.mojom.MaskFilterInfo"
+          cpp = "::gfx::MaskFilterInfo"
+        },
+      ]
+      traits_sources = [ "mask_filter_info_mojom_traits.cc" ]
+      traits_headers = [ "mask_filter_info_mojom_traits.h" ]
+      traits_public_deps = [ "//ui/gfx" ]
+    },
   ]
 
   cpp_typemaps += shared_cpp_typemaps
diff --git a/ui/gfx/mojom/mask_filter_info.mojom b/ui/gfx/mojom/mask_filter_info.mojom
new file mode 100644
index 0000000..47b5bd73
--- /dev/null
+++ b/ui/gfx/mojom/mask_filter_info.mojom
@@ -0,0 +1,13 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module gfx.mojom;
+
+import "ui/gfx/mojom/rrect_f.mojom";
+
+// See ui/gfx/mask_filter_info.h.
+struct MaskFilterInfo {
+  gfx.mojom.RRectF rounded_corner_bounds;
+  bool is_fast_rounded_corner;
+};
diff --git a/ui/gfx/mojom/mask_filter_info_mojom_traits.cc b/ui/gfx/mojom/mask_filter_info_mojom_traits.cc
new file mode 100644
index 0000000..23bccb1b
--- /dev/null
+++ b/ui/gfx/mojom/mask_filter_info_mojom_traits.cc
@@ -0,0 +1,20 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/gfx/mojom/mask_filter_info_mojom_traits.h"
+
+namespace mojo {
+
+// static
+bool StructTraits<gfx::mojom::MaskFilterInfoDataView, gfx::MaskFilterInfo>::
+    Read(gfx::mojom::MaskFilterInfoDataView data, gfx::MaskFilterInfo* out) {
+  gfx::RRectF bounds;
+  if (!data.ReadRoundedCornerBounds(&bounds))
+    return false;
+  *out = gfx::MaskFilterInfo(bounds, data.is_fast_rounded_corner());
+
+  return true;
+}
+
+}  // namespace mojo
diff --git a/ui/gfx/mojom/mask_filter_info_mojom_traits.h b/ui/gfx/mojom/mask_filter_info_mojom_traits.h
new file mode 100644
index 0000000..ca9b0bbf
--- /dev/null
+++ b/ui/gfx/mojom/mask_filter_info_mojom_traits.h
@@ -0,0 +1,29 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_GFX_MOJOM_MASK_FILTER_INFO_MOJOM_TRAITS_H_
+#define UI_GFX_MOJOM_MASK_FILTER_INFO_MOJOM_TRAITS_H_
+
+#include "ui/gfx/mask_filter_info.h"
+#include "ui/gfx/mojom/mask_filter_info.mojom-shared.h"
+#include "ui/gfx/mojom/rrect_f_mojom_traits.h"
+
+namespace mojo {
+template <>
+struct StructTraits<gfx::mojom::MaskFilterInfoDataView, gfx::MaskFilterInfo> {
+  static const gfx::RRectF& rounded_corner_bounds(
+      const gfx::MaskFilterInfo& info) {
+    return info.rounded_corner_bounds();
+  }
+
+  static bool is_fast_rounded_corner(const gfx::MaskFilterInfo& info) {
+    return info.is_fast_rounded_corner();
+  }
+
+  static bool Read(gfx::mojom::MaskFilterInfoDataView data,
+                   gfx::MaskFilterInfo* out);
+};
+
+}  // namespace mojo
+#endif  // UI_GFX_MOJOM_LINEAR_GRADIENT_MOJOM_TRAITS_H_
diff --git a/ui/gfx/range/OWNERS b/ui/gfx/range/OWNERS
index c93ccf46..14fce2ae 100644
--- a/ui/gfx/range/OWNERS
+++ b/ui/gfx/range/OWNERS
@@ -1,3 +1 @@
 rsesek@chromium.org
-
-# COMPONENT: UI>GFX
diff --git a/ui/gfx/x/OWNERS b/ui/gfx/x/OWNERS
index 079174d..c5442ff 100644
--- a/ui/gfx/x/OWNERS
+++ b/ui/gfx/x/OWNERS
@@ -2,5 +2,3 @@
 
 # Adding new atoms is allowed without OWNERS review.
 per-file x11_atom_cache.cc=*
-
-# COMPONENT: UI>GFX
diff --git a/ui/views/BUILD.gn b/ui/views/BUILD.gn
index b8e1f99..0b2f985 100644
--- a/ui/views/BUILD.gn
+++ b/ui/views/BUILD.gn
@@ -1342,6 +1342,10 @@
   if (use_ozone) {
     deps += [ "//ui/ozone" ]
   }
+
+  if (is_linux || is_chromeos) {
+    sources += [ "color_chooser/color_chooser_unittest.cc" ]
+  }
 }
 
 # This target is added as a dependency of browser interactive_ui_tests. It must
diff --git a/ui/views/color_chooser/color_chooser_unittest.cc b/ui/views/color_chooser/color_chooser_unittest.cc
new file mode 100644
index 0000000..e20ed34
--- /dev/null
+++ b/ui/views/color_chooser/color_chooser_unittest.cc
@@ -0,0 +1,163 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Very quick HSV primer for those unfamiliar with it:
+// It helps to think of HSV like this:
+//   h is in (0,360) and draws a circle of colors, with r = 0, b = 120, g = 240
+//   s is in (0,1) and is the distance from the center of that circle - higher
+//     values are more intense, with s = 0 being white, s = 1 being full color
+// and then HSV is the 3d space caused by projecting that circle into a
+// cylinder, with v in (0,1) being how far along the cylinder you are; v = 0 is
+// black, v = 1 is full color intensity
+
+#include <tuple>
+
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/events/test/event_generator.h"
+#include "ui/views/background.h"
+#include "ui/views/color_chooser/color_chooser_listener.h"
+#include "ui/views/color_chooser/color_chooser_view.h"
+#include "ui/views/controls/textfield/textfield.h"
+#include "ui/views/test/views_test_base.h"
+#include "ui/views/widget/widget_utils.h"
+
+namespace {
+
+class TestChooserListener : public views::ColorChooserListener {
+ public:
+  void OnColorChosen(SkColor color) override { color_ = color; }
+  void OnColorChooserDialogClosed() override { closed_ = true; }
+
+ private:
+  SkColor color_ = SK_ColorTRANSPARENT;
+  bool closed_ = false;
+};
+
+class ColorChooserTest : public views::ViewsTestBase {
+ public:
+  ~ColorChooserTest() override = default;
+
+  void SetUp() override {
+    ViewsTestBase::SetUp();
+    chooser_ =
+        std::make_unique<views::ColorChooserView>(&listener_, SK_ColorGREEN);
+    chooser_->SetBounds(0, 0, 400, 300);
+    widget_ = CreateTestWidget(views::Widget::InitParams::TYPE_WINDOW);
+    widget_->GetContentsView()->AddChildView(chooser());
+    generator_ = std::make_unique<ui::test::EventGenerator>(
+        views::GetRootWindow(widget_.get()), widget_->GetNativeWindow());
+    generator_->set_assume_window_at_origin(false);
+  }
+
+  void TearDown() override {
+    generator_.reset();
+    widget_.reset();
+    ViewsTestBase::TearDown();
+  }
+
+  views::ColorChooserView* chooser() { return chooser_.get(); }
+  ui::test::EventGenerator* generator() { return generator_.get(); }
+
+  void ExpectExactHSV(float h, float s, float v) const {
+    EXPECT_EQ(h, chooser_->hue());
+    EXPECT_EQ(s, chooser_->saturation());
+    EXPECT_EQ(v, chooser_->value());
+  }
+
+  void ExpectApproximateHSV(float h, float s, float v) const {
+    // At the usual size of the hue chooser it's possible to hit within
+    // roughly 5 points of hue in either direction.
+    EXPECT_NEAR(chooser_->hue(), h, 10.0);
+    EXPECT_NEAR(chooser_->saturation(), s, 0.1);
+    EXPECT_NEAR(chooser_->value(), v, 0.1);
+  }
+
+  SkColor GetShownColor() const {
+    return chooser_->selected_color_patch_for_testing()
+        ->background()
+        ->get_color();
+  }
+
+  SkColor GetTextualColor() const {
+    base::string16 text = chooser_->textfield_for_testing()->GetText();
+    if (text.empty() || text[0] != '#')
+      return SK_ColorTRANSPARENT;
+
+    uint32_t color;
+    return base::HexStringToUInt(base::UTF16ToUTF8(text.substr(1)), &color)
+               ? SkColorSetA(color, SK_AlphaOPAQUE)
+               : SK_ColorTRANSPARENT;
+  }
+
+  void TypeColor(const std::string& color) {
+    chooser_->textfield_for_testing()->SetText(base::UTF8ToUTF16(color));
+    // Synthesize ContentsChanged, since Textfield normally doesn't deliver it
+    // for SetText, only for user-typed text.
+    chooser_->ContentsChanged(chooser_->textfield_for_testing(),
+                              chooser_->textfield_for_testing()->GetText());
+  }
+
+  void PressMouseAt(views::View* view, const gfx::Point& p) {
+#if 0
+    // TODO(ellyjones): Why doesn't this work?
+    const gfx::Point po = view->GetBoundsInScreen().origin();
+    generator_->MoveMouseTo(po + p.OffsetFromOrigin());
+    generator_->ClickLeftButton();
+#endif
+    ui::MouseEvent press(ui::ET_MOUSE_PRESSED,
+                         gfx::Point(view->x() + p.x(), view->y() + p.y()),
+                         gfx::Point(0, 0), base::TimeTicks::Now(), 0, 0);
+    view->OnMousePressed(press);
+  }
+
+ private:
+  TestChooserListener listener_;
+  std::unique_ptr<views::ColorChooserView> chooser_;
+  std::unique_ptr<views::Widget> widget_;
+  std::unique_ptr<ui::test::EventGenerator> generator_;
+};
+
+TEST_F(ColorChooserTest, ShowsInitialColor) {
+  ExpectExactHSV(120, 1, 1);
+  EXPECT_EQ(GetShownColor(), SK_ColorGREEN);
+  EXPECT_EQ(GetTextualColor(), SK_ColorGREEN);
+}
+
+TEST_F(ColorChooserTest, AdjustingTextAdjustsShown) {
+  TypeColor("#ff0000");
+  ExpectExactHSV(0, 1, 1);
+  EXPECT_EQ(GetShownColor(), SK_ColorRED);
+
+  TypeColor("0000ff");
+  ExpectExactHSV(240, 1, 1);
+  EXPECT_EQ(GetShownColor(), SK_ColorBLUE);
+}
+
+TEST_F(ColorChooserTest, HueSliderChangesHue) {
+  ExpectExactHSV(120, 1, 1);
+
+  views::View* hv = chooser()->hue_view_for_testing();
+
+  PressMouseAt(hv, gfx::Point(1, hv->height()));
+  ExpectApproximateHSV(0, 1, 1);
+
+  PressMouseAt(hv, gfx::Point(1, (hv->height() * 3) / 4));
+  ExpectApproximateHSV(90, 1, 1);
+
+  PressMouseAt(hv, gfx::Point(1, hv->height() / 2));
+  ExpectApproximateHSV(180, 1, 1);
+
+  PressMouseAt(hv, gfx::Point(1, hv->height() / 4));
+  ExpectApproximateHSV(270, 1, 1);
+}
+
+// Missing tests, TODO:
+// TEST_F(ColorChooserTest, SatValueChooserChangesSatValue)
+// TEST_F(ColorChooserTest, UpdateFromWebUpdatesShownValues)
+// TEST_F(ColorChooserTest, AdjustingTextAffectsHue)
+// TEST_F(ColorChooserTest, AdjustingTextAffectsSatValue)
+
+}  // namespace
diff --git a/ui/views/color_chooser/color_chooser_view.cc b/ui/views/color_chooser/color_chooser_view.cc
index b8053d0..8470a67 100644
--- a/ui/views/color_chooser/color_chooser_view.cc
+++ b/ui/views/color_chooser/color_chooser_view.cc
@@ -434,17 +434,20 @@
   textfield_->SetText(GetColorText(color));
 }
 
-bool ColorChooserView::CanMinimize() const {
-  return false;
+View* ColorChooserView::hue_view_for_testing() {
+  return hue_;
 }
 
-View* ColorChooserView::GetInitiallyFocusedView() {
+View* ColorChooserView::saturation_value_view_for_testing() {
+  return saturation_value_;
+}
+
+Textfield* ColorChooserView::textfield_for_testing() {
   return textfield_;
 }
 
-void ColorChooserView::WindowClosing() {
-  if (listener_)
-    listener_->OnColorChooserDialogClosed();
+View* ColorChooserView::selected_color_patch_for_testing() {
+  return selected_color_patch_;
 }
 
 void ColorChooserView::ContentsChanged(Textfield* sender,
@@ -472,4 +475,17 @@
   return true;
 }
 
+bool ColorChooserView::CanMinimize() const {
+  return false;
+}
+
+View* ColorChooserView::GetInitiallyFocusedView() {
+  return textfield_;
+}
+
+void ColorChooserView::WindowClosing() {
+  if (listener_)
+    listener_->OnColorChooserDialogClosed();
+}
+
 }  // namespace views
diff --git a/ui/views/color_chooser/color_chooser_view.h b/ui/views/color_chooser/color_chooser_view.h
index c28c245..60bf2ac 100644
--- a/ui/views/color_chooser/color_chooser_view.h
+++ b/ui/views/color_chooser/color_chooser_view.h
@@ -42,6 +42,17 @@
   float value() const { return hsv_[2]; }
   void set_listener(ColorChooserListener* listener) { listener_ = listener; }
 
+  View* hue_view_for_testing();
+  View* saturation_value_view_for_testing();
+  Textfield* textfield_for_testing();
+  View* selected_color_patch_for_testing();
+
+  // TextfieldController overrides:
+  void ContentsChanged(Textfield* sender,
+                       const base::string16& new_contents) override;
+  bool HandleKeyEvent(Textfield* sender,
+                      const ui::KeyEvent& key_event) override;
+
  private:
   class HueView;
   class SaturationValueView;
@@ -52,12 +63,6 @@
   View* GetInitiallyFocusedView() override;
   void WindowClosing() override;
 
-  // TextfieldController overrides:
-  void ContentsChanged(Textfield* sender,
-                       const base::string16& new_contents) override;
-  bool HandleKeyEvent(Textfield* sender,
-                      const ui::KeyEvent& key_event) override;
-
   // The current color in HSV coordinate.
   SkScalar hsv_[3];
 
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/BrowserControlsHelper.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/BrowserControlsHelper.java
index 6c57322..dfff4d4 100644
--- a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/BrowserControlsHelper.java
+++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/BrowserControlsHelper.java
@@ -32,7 +32,7 @@
 
     // Blocks until browser controls are fully initialized. Should only be created in a test's
     // setUp() method; see BrowserControlsHelper#createInSetUp().
-    private BrowserControlsHelper(InstrumentationActivity activity) throws Throwable {
+    private BrowserControlsHelper(InstrumentationActivity activity) throws Exception {
         Assert.assertTrue(CommandLine.isInitialized());
         Assert.assertTrue(CommandLine.getInstance().hasSwitch("enable-features"));
         String enabledFeatures = CommandLine.getInstance().getSwitchValue("enable-features");
@@ -73,7 +73,7 @@
     }
 
     // Ensures that browser controls are fully initialized and ready for scrolls to be processed.
-    private void waitForBrowserControlsInitialization() throws Throwable {
+    private void waitForBrowserControlsInitialization() throws Exception {
         // Poll until the top view becomes visible.
         waitForBrowserControlsViewToBeVisible(mActivity.getTopContentsContainer());
         TestThreadUtils.runOnUiThreadBlocking(() -> {
@@ -88,7 +88,7 @@
     // Creates a BrowserControlsHelper instance and blocks until browser controls are fully
     // initialized. Should be called from a test's setUp() method.
     static BrowserControlsHelper createAndBlockUntilBrowserControlsInitializedInSetUp(
-            InstrumentationActivity activity) throws Throwable {
+            InstrumentationActivity activity) throws Exception {
         return new BrowserControlsHelper(activity);
     }
 
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/BrowserControlsTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/BrowserControlsTest.java
index af749fc..40527d6 100644
--- a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/BrowserControlsTest.java
+++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/BrowserControlsTest.java
@@ -10,6 +10,7 @@
 import static androidx.test.espresso.matcher.ViewMatchers.withText;
 
 import android.os.Build;
+import android.os.Bundle;
 import android.os.RemoteException;
 import android.view.View;
 import android.view.ViewGroup;
@@ -20,16 +21,17 @@
 
 import org.hamcrest.Matchers;
 import org.junit.Assert;
-import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import org.chromium.base.test.util.CallbackHelper;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.MinAndroidSdkLevel;
 import org.chromium.content_public.browser.test.util.Criteria;
 import org.chromium.content_public.browser.test.util.CriteriaHelper;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
+import org.chromium.weblayer.BrowserControlsOffsetCallback;
 import org.chromium.weblayer.Tab;
 import org.chromium.weblayer.TestWebLayer;
 import org.chromium.weblayer.shell.InstrumentationActivity;
@@ -88,8 +90,7 @@
         });
     }
 
-    @Before
-    public void setUp() throws Throwable {
+    private void createActivityWithTopView() throws Exception {
         final String url = mActivityTestRule.getTestDataURL("tall_page.html");
         InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(url);
 
@@ -106,6 +107,7 @@
     @Test
     @SmallTest
     public void testTopAndBottom() throws Exception {
+        createActivityWithTopView();
         InstrumentationActivity activity = mActivityTestRule.getActivity();
         View bottomView = TestThreadUtils.runOnUiThreadBlocking(() -> {
             TextView view = new TextView(activity);
@@ -164,6 +166,7 @@
     @Test
     @SmallTest
     public void testBottomOnly() throws Exception {
+        createActivityWithTopView();
         InstrumentationActivity activity = mActivityTestRule.getActivity();
         // Remove the top-view.
         TestThreadUtils.runOnUiThreadBlocking(() -> { activity.getBrowser().setTopView(null); });
@@ -219,6 +222,7 @@
     @Test
     @SmallTest
     public void testTopOnly() throws Exception {
+        createActivityWithTopView();
         InstrumentationActivity activity = mActivityTestRule.getActivity();
         View topView = activity.getTopContentsContainer();
 
@@ -249,6 +253,7 @@
     @Test
     @SmallTest
     public void testTopMinHeight() throws Exception {
+        createActivityWithTopView();
         final int minHeight = 20;
         InstrumentationActivity activity = mActivityTestRule.getActivity();
         View topContents = activity.getTopContentsContainer();
@@ -288,6 +293,7 @@
     @Test
     @SmallTest
     public void testOnlyExpandTopControlsAtPageTop() throws Exception {
+        createActivityWithTopView();
         InstrumentationActivity activity = mActivityTestRule.getActivity();
         View topContents = activity.getTopContentsContainer();
         TestThreadUtils.runOnUiThreadBlocking(
@@ -333,6 +339,7 @@
     @Test
     @SmallTest
     public void testAlertDoesntShowTopControlsIfOnlyExpandTopControlsAtPageTop() throws Exception {
+        createActivityWithTopView();
         InstrumentationActivity activity = mActivityTestRule.getActivity();
         View topContents = activity.getTopContentsContainer();
         TestThreadUtils.runOnUiThreadBlocking(
@@ -371,6 +378,7 @@
     @Test
     @SmallTest
     public void testAlertShowsTopControls() throws Exception {
+        createActivityWithTopView();
         InstrumentationActivity activity = mActivityTestRule.getActivity();
 
         // Move by the size of the top-controls.
@@ -400,6 +408,7 @@
     @Test
     @SmallTest
     public void testAccessibility() throws Exception {
+        createActivityWithTopView();
         InstrumentationActivity activity = mActivityTestRule.getActivity();
 
         // Scroll such that top-controls are hidden.
@@ -434,6 +443,7 @@
     @Test
     @SmallTest
     public void testRemoveAllFromTopView() throws Exception {
+        createActivityWithTopView();
         InstrumentationActivity activity = mActivityTestRule.getActivity();
 
         // Install a different top-view.
@@ -465,4 +475,64 @@
             Criteria.checkThat(getVisiblePageHeight(), Matchers.not(mPageHeightWithTopView));
         });
     }
+
+    private void registerBrowserControlsOffsetCallbackForOffset(
+            CallbackHelper helper, int targetOffset) {
+        InstrumentationActivity activity = mActivityTestRule.getActivity();
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            activity.getBrowser().registerBrowserControlsOffsetCallback(
+                    new BrowserControlsOffsetCallback() {
+                        @Override
+                        public void onTopViewOffsetChanged(int offset) {
+                            if (offset == targetOffset) {
+                                activity.getBrowser().unregisterBrowserControlsOffsetCallback(this);
+                                helper.notifyCalled();
+                            }
+                        }
+                    });
+        });
+    }
+
+    @MinAndroidSdkLevel(Build.VERSION_CODES.M)
+    @Test
+    @SmallTest
+    public void testTopExpandedWhenOnlyExpandAtTop() throws Exception {
+        Bundle extras = new Bundle();
+        extras.putBoolean(InstrumentationActivity.EXTRA_ONLY_EXPAND_CONTROLS_AT_TOP, true);
+        final String url = mActivityTestRule.getTestDataURL("tall_page.html");
+        InstrumentationActivity activity = mActivityTestRule.launchShell(extras);
+        CallbackHelper helper = new CallbackHelper();
+        registerBrowserControlsOffsetCallbackForOffset(helper, 0);
+        mActivityTestRule.navigateAndWait(url);
+        helper.waitForFirst();
+    }
+
+    @MinAndroidSdkLevel(Build.VERSION_CODES.M)
+    @Test
+    @SmallTest
+    public void testTopExpandedOnLoadWhenOnlyExpandAtTop() throws Exception {
+        Bundle extras = new Bundle();
+        extras.putBoolean(InstrumentationActivity.EXTRA_ONLY_EXPAND_CONTROLS_AT_TOP, true);
+        InstrumentationActivity activity = mActivityTestRule.launchShell(extras);
+        CallbackHelper helper = new CallbackHelper();
+        registerBrowserControlsOffsetCallbackForOffset(helper, 0);
+        mActivityTestRule.navigateAndWait(mActivityTestRule.getTestDataURL("tall_page.html"));
+        int callCount = 0;
+        helper.waitForCallback(callCount++);
+
+        mTopViewHeight = TestThreadUtils.runOnUiThreadBlocking(
+                () -> { return activity.getTopContentsContainer().getHeight(); });
+        Assert.assertNotEquals(mTopViewHeight, 0);
+
+        // Scroll such that top-controls are hidden.
+        registerBrowserControlsOffsetCallbackForOffset(helper, -mTopViewHeight);
+        EventUtils.simulateDragFromCenterOfView(
+                activity.getWindow().getDecorView(), 0, -mTopViewHeight);
+        helper.waitForCallback(callCount++);
+
+        // Load a new page. The top-controls should be shown again.
+        registerBrowserControlsOffsetCallbackForOffset(helper, 0);
+        mActivityTestRule.navigateAndWait(mActivityTestRule.getTestDataURL("simple_page.html"));
+        helper.waitForCallback(callCount++);
+    }
 }
diff --git a/weblayer/browser/controls_visibility_reason.h b/weblayer/browser/controls_visibility_reason.h
index dd03668..097b3ac 100644
--- a/weblayer/browser/controls_visibility_reason.h
+++ b/weblayer/browser/controls_visibility_reason.h
@@ -10,6 +10,10 @@
 // This enum represents actions or UI conditions that affect the visibility of
 // top UI, and is used to track concurrent concerns and to allow native and Java
 // code to coordinate.
+//
+// WARNING: only a subset of these are used if OnlyExpandTopControlsAtPageTop
+// is true.
+//
 // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.weblayer_private
 // GENERATED_JAVA_CLASS_NAME_OVERRIDE: ImplControlsVisibilityReason
 enum class ControlsVisibilityReason {
diff --git a/weblayer/browser/tab_impl.cc b/weblayer/browser/tab_impl.cc
index bbb893b..59bba86 100644
--- a/weblayer/browser/tab_impl.cc
+++ b/weblayer/browser/tab_impl.cc
@@ -1194,6 +1194,30 @@
     // bounce around.
     UpdateBrowserControlsState(content::BROWSER_CONTROLS_STATE_SHOWN, false);
   } else {
+    if (did_commit && current_browser_controls_visibility_constraint_ ==
+                          content::BROWSER_CONTROLS_STATE_BOTH) {
+      // If the current state is BROWSER_CONTROLS_STATE_BOTH, then
+      // TabImpl::UpdateBrowserControlsState() is going to call
+      // WebContents::UpdateBrowserControlsState() with both current and
+      // constraints set to BROWSER_CONTROLS_STATE_BOTH. cc does
+      // nothing in this case. During a navigation the top-view needs to be
+      // shown. To force the top-view to show, supply
+      // BROWSER_CONTROLS_STATE_SHOWN. This path is only hit if top-view
+      // is configured to only-expand-at-top, as in this case the top-view isn't
+      // forced shown during a page load.
+      //
+      // It's entirely possible the scroll offset is changed as part of the
+      // loading process (such as happens with back/forward navigation or
+      // links part way down a page). Trying to detect this and compensate
+      // here is likely to be racy, so the top-view is always shown.
+      const bool animate =
+          !base::FeatureList::IsEnabled(kImmediatelyHideBrowserControlsForTest);
+      web_contents_->GetMainFrame()->UpdateBrowserControlsState(
+          content::BROWSER_CONTROLS_STATE_BOTH,
+          content::BROWSER_CONTROLS_STATE_SHOWN, animate);
+      // This falls through to call UpdateBrowserControlsState() again to
+      // ensure the constraint is set back to BOTH.
+    }
     UpdateBrowserControlsState(
         content::BROWSER_CONTROLS_STATE_BOTH,
         current_browser_controls_visibility_constraint_ !=
diff --git a/weblayer/shell/android/shell_apk/src/org/chromium/weblayer/shell/InstrumentationActivity.java b/weblayer/shell/android/shell_apk/src/org/chromium/weblayer/shell/InstrumentationActivity.java
index eee82bc..09ca890b 100644
--- a/weblayer/shell/android/shell_apk/src/org/chromium/weblayer/shell/InstrumentationActivity.java
+++ b/weblayer/shell/android/shell_apk/src/org/chromium/weblayer/shell/InstrumentationActivity.java
@@ -43,6 +43,8 @@
 /**
  * Activity for running instrumentation tests.
  */
+// This isn't part of Chrome, so using explicit colors/sizes is ok.
+@SuppressWarnings("checkstyle:SetTextColorAndSetTextSizeCheck")
 public class InstrumentationActivity extends FragmentActivity {
     private static final String TAG = "WLInstrumentation";
     private static final String KEY_MAIN_VIEW_ID = "mainViewId";
@@ -55,6 +57,10 @@
     // True by default. If set to false, the test should call loadWebLayerSync.
     public static final String EXTRA_CREATE_WEBLAYER = "EXTRA_CREATE_WEBLAYER";
 
+    public static final String EXTRA_TOP_VIEW_MIN_HEIGHT = "EXTRA_TOP_VIEW_MIN_HEIGHT";
+    public static final String EXTRA_ONLY_EXPAND_CONTROLS_AT_TOP =
+            "EXTRA_ONLY_EXPAND_CONTROLS_AT_TOP";
+
     // Used in tests to specify whether WebLayer URL bar should set default click listeners
     // that show Page Info UI on its TextView.
     public static final String EXTRA_URLBAR_TEXT_CLICKABLE = "EXTRA_URLBAR_TEXT_CLICKABLE";
@@ -266,7 +272,18 @@
         mBrowser = Browser.fromFragment(mFragment);
         mProfile = mBrowser.getProfile();
 
-        mBrowser.setTopView(mTopContentsContainer);
+        final boolean onlyExpandControlsAtTop =
+                getIntent().getBooleanExtra(EXTRA_ONLY_EXPAND_CONTROLS_AT_TOP, false);
+        final int minTopViewHeight = getIntent().getIntExtra(EXTRA_TOP_VIEW_MIN_HEIGHT, -1);
+
+        if (onlyExpandControlsAtTop || minTopViewHeight != -1) {
+            // This was added in 86.
+            mBrowser.setTopView(mTopContentsContainer, Math.max(0, minTopViewHeight),
+                    onlyExpandControlsAtTop,
+                    /* animate */ false);
+        } else {
+            mBrowser.setTopView(mTopContentsContainer);
+        }
 
         mRendererCrashListener = new TabCallback() {
             @Override