diff --git a/.clang-tidy b/.clang-tidy
index 3f644c5..4381dda 100644
--- a/.clang-tidy
+++ b/.clang-tidy
@@ -4,17 +4,28 @@
                     google-build-explicit-make-pair,
                     google-explicit-constructor,
                     google-readability-casting,
+                    modernize-avoid-bind,
                     modernize-loop-convert,
                     modernize-make-shared,
+                    modernize-make-unique,
                     modernize-redundant-void-arg,
                     modernize-replace-random-shuffle,
                     modernize-shrink-to-fit,
                     modernize-use-bool-literals,
+                    modernize-use-default-member-init,
+                    modernize-use-emplace,
                     modernize-use-equals-default,
                     modernize-use-equals-delete,
+                    modernize-use-noexcept,
                     modernize-use-nullptr,
                     modernize-use-override,
                     modernize-use-transparent-functors,
                     readability-redundant-member-init'
+  CheckOptions:
+    # This relaxes modernize-use-emplace in some cases; we might want to make it
+    # more aggressive in the future. See discussion on
+    # https://groups.google.com/a/chromium.org/g/cxx/c/noMMTNYiM0w .
+    - key:          modernize-use-emplace.IgnoreImplicitConstructors
+      value:        1
 ...
 
diff --git a/DEPS b/DEPS
index bbe59ce..fec83f251 100644
--- a/DEPS
+++ b/DEPS
@@ -178,11 +178,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': 'd077e6a3d013e02c1f3bf0d3735b64ced28f31f5',
+  'skia_revision': '1b63b4ac6933ccddb15bcf650cd5747d5eba44d0',
   # 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': 'efeaf3d82f81d47b3bf40c9c50055493f48ce544',
+  'v8_revision': 'b072390cd31323aec324401a835bc319b94878d4',
   # 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.
@@ -190,7 +190,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': '0b5f3df1f97e4a1cb51f0d100ef27305afd83849',
+  'angle_revision': 'cdfc69c7f0f833e99b435715b3c012a2b61e55b1',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -225,7 +225,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling NaCl
   # and whatever else without interference from each other.
-  'nacl_revision': 'f45e60c61cc1ae967e6f5fefc8f3e560e3b3c501',
+  'nacl_revision': '7e802793566c7bafd2c0846595d22bf5f805284e',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling freetype
   # and whatever else without interference from each other.
@@ -249,7 +249,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': '9e4db33a563ef629331e4b24415c653929e5a45f',
+  'devtools_frontend_revision': '501473c8d1a3463327d1cdabb2708e5bf5d3cc07',
   # 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.
@@ -301,11 +301,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': '9b54466be408f6a07856da33e531111408edbe33',
+  'dawn_revision': '8d6b021a2a134068bc46a7773ae553f8d957b937',
   # 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': '281f7a98285d4e1543863c36f08d5e9266970fb1',
+  'quiche_revision': '9d0a88542030288b1e02646485b929abb1f0f481',
   # 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.
@@ -524,7 +524,7 @@
   },
 
   'src/ios/third_party/material_components_ios/src': {
-      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + 'e479b366e735e55056b93559b6dc5eb29e4af47a',
+      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + '9c26a7e67aa298a57e5a78e22d7fbe5abc6fe34f',
       'condition': 'checkout_ios',
   },
 
@@ -851,7 +851,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'e4ad8580e0f59bc1762a6e959009dfa9e843758d',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '9adfd61269a07e71c6a52e8ca5e86811213512fe',
       'condition': 'checkout_linux',
   },
 
@@ -1115,7 +1115,7 @@
   },
 
   'src/third_party/libunwindstack': {
-      'url': Var('chromium_git') + '/chromium/src/third_party/libunwindstack.git' + '@' + 'acf93761dc00ac67bd7534c4040699abed4f8d94',
+      'url': Var('chromium_git') + '/chromium/src/third_party/libunwindstack.git' + '@' + 'dfd3f3d84cfc222af93bc86b276414fc690977da',
       'condition': 'checkout_android',
   },
 
@@ -1453,7 +1453,7 @@
   },
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '48b82798132248dfa1aaf0ed3dcac57a44a6855f',
+    Var('webrtc_git') + '/src.git' + '@' + '71e9acb97c7701bf5c9c77e4fc7a525678d65edc',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1525,7 +1525,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@612fae5937affc683a8ed5142935a7a7631246be',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@e0d8925a3c32232ce0cdf0396522ef096730d2ee',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/BUILD.gn b/android_webview/BUILD.gn
index 5d31c91..990b82e 100644
--- a/android_webview/BUILD.gn
+++ b/android_webview/BUILD.gn
@@ -480,6 +480,7 @@
     ":common_variations_java",
     ":resources",
     "//android_webview/nonembedded:system_webview_manifest",
+    "//android_webview/proto:metrics_bridge_records_proto_java",
     "//base:base_java",
     "//base:jni_java",
     "//components/autofill/android:autofill_java",
@@ -506,6 +507,7 @@
     "//net/android:net_java",
     "//services/network/public/mojom:mojom_java",
     "//third_party/android_deps:androidx_annotation_annotation_java",
+    "//third_party/android_deps:com_google_protobuf_protobuf_javalite_java",
     "//third_party/blink/public:blink_headers_java",
     "//ui/android:ui_java",
     "//url:gurl_java",
diff --git a/android_webview/browser/gfx/aw_draw_fn_impl.cc b/android_webview/browser/gfx/aw_draw_fn_impl.cc
index beb6789..5544605 100644
--- a/android_webview/browser/gfx/aw_draw_fn_impl.cc
+++ b/android_webview/browser/gfx/aw_draw_fn_impl.cc
@@ -553,9 +553,12 @@
 
   // Flush so that we know the image's transition has been submitted and that
   // the |post_draw_semaphore| is pending.
+  GrFlushInfo flushInfo;
+  flushInfo.fNumSemaphores = 1;
+  flushInfo.fSignalSemaphores = &gr_post_draw_semaphore;
   GrSemaphoresSubmitted submitted =
-      vulkan_context_provider_->gr_context()->flushAndSignalSemaphores(
-          1, &gr_post_draw_semaphore);
+      vulkan_context_provider_->gr_context()->flush(flushInfo);
+  vulkan_context_provider_->gr_context()->submit();
   if (submitted != GrSemaphoresSubmitted::kYes) {
     LOG(ERROR) << "Skia could not submit GrSemaphore.";
     return;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsGarbageCollectionTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsGarbageCollectionTest.java
index e408038..c4cf171 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsGarbageCollectionTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsGarbageCollectionTest.java
@@ -301,7 +301,7 @@
 
             gcAndCheckAllAwContentsDestroyed();
         } finally {
-            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
                 Reference.reachabilityFence(heldObject);
             }
         }
diff --git a/android_webview/nonembedded/BUILD.gn b/android_webview/nonembedded/BUILD.gn
index 3c88eaa..a87c01a 100644
--- a/android_webview/nonembedded/BUILD.gn
+++ b/android_webview/nonembedded/BUILD.gn
@@ -23,6 +23,7 @@
     ":services_java",
     "//android_webview:android_webview_product_config_java",
     "//android_webview:common_java",
+    "//android_webview:common_metrics_java",
     "//base:base_java",
     "//base:jni_java",
     "//components/about_ui/android:aboutui_java",
@@ -85,11 +86,13 @@
     "//android_webview:common_metrics_java",
     "//android_webview:common_platform_services_java",
     "//android_webview:common_variations_java",
+    "//android_webview/proto:metrics_bridge_records_proto_java",
     "//base:base_java",
     "//components/background_task_scheduler:background_task_scheduler_task_ids_java",
     "//components/minidump_uploader:minidump_uploader_java",
     "//components/variations/android:variations_java",
     "//components/version_info/android:version_constants_java",
+    "//third_party/android_deps:com_google_protobuf_protobuf_javalite_java",
   ]
 }
 
diff --git a/android_webview/nonembedded/java/src/org/chromium/android_webview/devui/MainActivity.java b/android_webview/nonembedded/java/src/org/chromium/android_webview/devui/MainActivity.java
index 7d693b0..b608373 100644
--- a/android_webview/nonembedded/java/src/org/chromium/android_webview/devui/MainActivity.java
+++ b/android_webview/nonembedded/java/src/org/chromium/android_webview/devui/MainActivity.java
@@ -74,9 +74,34 @@
         int COUNT = 3;
     }
 
-    private static void logFragmentNavigation(@FragmentNavigation int selectedFragment) {
-        RecordHistogram.recordEnumeratedHistogram("Android.WebView.DevUi.FragmentNavigation",
-                selectedFragment, FragmentNavigation.COUNT);
+    /**
+     * Logs a navigation to a fragment. Requires a suffix from histograms.xml ("AnyMethod",
+     * "FromIntent", or "NavBar") to determine which histogram to log.
+     *
+     * @param histogramSuffix one of the suffixes listed in histograms.xml
+     * @param selectedFragmentId one of FRAGMENT_ID_HOME, FRAGMENT_ID_CRASHES, or FRAGMENT_ID_FLAGS
+     */
+    private static void logFragmentNavigation(String histogramSuffix, int selectedFragmentId) {
+        // Map FRAGMENT_ID_* to FragmentNavigation value (so FRAGMENT_ID_* values are permitted to
+        // change in the future without messing up logs).
+        @FragmentNavigation
+        int sample;
+        switch (selectedFragmentId) {
+            default:
+                // Fall through.
+            case FRAGMENT_ID_HOME:
+                sample = FragmentNavigation.HOME_FRAGMENT;
+                break;
+            case FRAGMENT_ID_CRASHES:
+                sample = FragmentNavigation.CRASHES_LIST_FRAGMENT;
+                break;
+            case FRAGMENT_ID_FLAGS:
+                sample = FragmentNavigation.FLAGS_FRAGMENT;
+                break;
+        }
+        RecordHistogram.recordEnumeratedHistogram(
+                "Android.WebView.DevUi.FragmentNavigation." + histogramSuffix, sample,
+                FragmentNavigation.COUNT);
     }
 
     @Override
@@ -100,6 +125,7 @@
             assert mFragmentIdMap.containsKey(view.getId()) : "Unexpected view ID: " + view.getId();
             int fragmentId = mFragmentIdMap.get(view.getId());
             switchFragment(fragmentId);
+            logFragmentNavigation("NavBar", fragmentId);
         };
         final int childCount = bottomNavBar.getChildCount();
         for (int i = 0; i < childCount; ++i) {
@@ -132,19 +158,17 @@
                 chosenFragmentId = FRAGMENT_ID_HOME;
                 // Fall through.
             case FRAGMENT_ID_HOME:
-                logFragmentNavigation(FragmentNavigation.HOME_FRAGMENT);
                 fragment = new HomeFragment();
                 break;
             case FRAGMENT_ID_CRASHES:
-                logFragmentNavigation(FragmentNavigation.CRASHES_LIST_FRAGMENT);
                 fragment = new CrashesListFragment();
                 break;
             case FRAGMENT_ID_FLAGS:
-                logFragmentNavigation(FragmentNavigation.FLAGS_FRAGMENT);
                 fragment = new FlagsFragment();
                 break;
         }
         assert fragment != null;
+        logFragmentNavigation("AnyMethod", chosenFragmentId);
 
         // Switch fragments
         FragmentManager fm = getSupportFragmentManager();
@@ -212,6 +236,7 @@
             fragmentId = extras.getInt(FRAGMENT_ID_INTENT_EXTRA, fragmentId);
         }
         switchFragment(fragmentId);
+        logFragmentNavigation("FromIntent", fragmentId);
     }
 
     @Override
diff --git a/android_webview/test/BUILD.gn b/android_webview/test/BUILD.gn
index 3abf0ad..8d183b62 100644
--- a/android_webview/test/BUILD.gn
+++ b/android_webview/test/BUILD.gn
@@ -159,17 +159,29 @@
   deps = [
     ":webview_instrumentation_test_utils_java",
     "//android_webview:android_webview_java",
+    "//android_webview:common_aidl_java",
+    "//android_webview:common_crash_java",
+    "//android_webview:common_metrics_java",
+    "//android_webview:common_platform_services_java",
+    "//android_webview:common_variations_java",
+    "//android_webview/nonembedded:devui_java",
+    "//android_webview/nonembedded:services_java",
+    "//android_webview/proto:aw_variations_seed_proto_java",
+    "//android_webview/proto:metrics_bridge_records_proto_java",
     "//android_webview/test/embedded_test_server:aw_net_java_test_support",
     "//base:base_java",
     "//base:base_java_test_support",
     "//components/autofill/android:provider_java",
+    "//components/content_capture/android:java",
     "//components/embedder_support/android:web_contents_delegate_java",
+    "//components/heap_profiling/multi_process:heap_profiling_java_test_support",
     "//components/metrics:metrics_java",
     "//components/minidump_uploader:minidump_uploader_java",
     "//components/minidump_uploader:minidump_uploader_javatests",
     "//components/policy/android:policy_java",
     "//components/policy/android:policy_java_test_support",
     "//components/safe_browsing/android:safe_browsing_java",
+    "//components/variations/android:variations_java",
     "//content/public/android:content_java",
     "//content/public/test/android:content_java_test_support",
     "//mojo/public/java:bindings_java",
@@ -179,9 +191,11 @@
     "//services/device/public/java:geolocation_java",
     "//services/device/public/java:geolocation_java_test_support",
     "//third_party/android_deps:com_google_guava_failureaccess_java",
+    "//third_party/android_deps:com_google_protobuf_protobuf_javalite_java",
     "//third_party/android_support_test_runner:rules_java",
     "//third_party/android_support_test_runner:runner_java",
     "//third_party/espresso:espresso_all_java",
+    "//third_party/guava:guava_android_java",
     "//third_party/hamcrest:hamcrest_java",
     "//third_party/junit",
     "//third_party/metrics_proto:metrics_proto_java",
@@ -463,9 +477,14 @@
 
   deps = [
     "//android_webview:android_webview_java",
+    "//android_webview:common_metrics_java",
+    "//android_webview/nonembedded:services_java",
+    "//android_webview/proto:metrics_bridge_records_proto_java",
+    "//base:base_java",
     "//base:base_java_test_support",
     "//base:base_junit_test_support",
     "//content/public/test/android:content_java_test_support",
+    "//third_party/android_deps:com_google_protobuf_protobuf_javalite_java",
     "//third_party/android_support_test_runner:runner_java",
   ]
 
diff --git a/android_webview/tools/system_webview_shell/BUILD.gn b/android_webview/tools/system_webview_shell/BUILD.gn
index 454403c..bb54081 100644
--- a/android_webview/tools/system_webview_shell/BUILD.gn
+++ b/android_webview/tools/system_webview_shell/BUILD.gn
@@ -116,6 +116,7 @@
     "//base:base_java",
     "//base:base_java_test_support",
     "//testing/android/reporter:reporter_java",
+    "//third_party/android_sdk:android_test_base_java",
     "//third_party/android_support_test_runner:rules_java",
     "//third_party/android_support_test_runner:runner_java",
     "//third_party/junit",
diff --git a/ash/assistant/assistant_interaction_controller_impl.cc b/ash/assistant/assistant_interaction_controller_impl.cc
index caa226c..94b71ec5 100644
--- a/ash/assistant/assistant_interaction_controller_impl.cc
+++ b/ash/assistant/assistant_interaction_controller_impl.cc
@@ -894,9 +894,6 @@
 
 bool AssistantInteractionControllerImpl::ShouldAttemptWarmerWelcome(
     AssistantEntryPoint entry_point) const {
-  if (!chromeos::assistant::features::IsWarmerWelcomeEnabled())
-    return false;
-
   if (number_of_times_shown_ > 1)
     return false;
 
diff --git a/ash/quick_answers/quick_answers_controller_impl.cc b/ash/quick_answers/quick_answers_controller_impl.cc
index 8831efb..bd08aae 100644
--- a/ash/quick_answers/quick_answers_controller_impl.cc
+++ b/ash/quick_answers/quick_answers_controller_impl.cc
@@ -85,6 +85,9 @@
 }
 
 void QuickAnswersControllerImpl::DismissQuickAnswers(bool is_active) {
+  if (!is_eligible_)
+    return;
+
   MaybeDismissQuickAnswersConsent();
   quick_answers_ui_controller_->CloseQuickAnswersView();
   quick_answers_client_->OnQuickAnswersDismissed(
diff --git a/ash/system/message_center/unified_message_center_view_unittest.cc b/ash/system/message_center/unified_message_center_view_unittest.cc
index b04e4292..a4778b6b 100644
--- a/ash/system/message_center/unified_message_center_view_unittest.cc
+++ b/ash/system/message_center/unified_message_center_view_unittest.cc
@@ -106,15 +106,21 @@
     return id;
   }
 
-  void CreateMessageCenterView(int max_height = kDefaultMaxHeight) {
-    message_center_view_ =
+  std::unique_ptr<TestUnifiedMessageCenterView> CreateMessageCenterViewImpl(
+      int max_height) {
+    auto message_center_view =
         std::make_unique<TestUnifiedMessageCenterView>(model_.get());
-    message_center_view_->AddObserver(this);
-    message_center_view_->SetMaxHeight(max_height);
-    message_center_view_->SetAvailableHeight(max_height);
-    message_center_view_->set_owned_by_client();
-    OnViewPreferredSizeChanged(message_center_view_.get());
+    message_center_view->AddObserver(this);
+    message_center_view->SetMaxHeight(max_height);
+    message_center_view->SetAvailableHeight(max_height);
+    OnViewPreferredSizeChanged(message_center_view.get());
     size_changed_count_ = 0;
+
+    return message_center_view;
+  }
+
+  virtual void CreateMessageCenterView(int max_height = kDefaultMaxHeight) {
+    message_center_view_ = CreateMessageCenterViewImpl(max_height);
   }
 
   void AnimateMessageListToValue(float value) {
@@ -201,7 +207,7 @@
     return focused_message_view;
   }
 
-  TestUnifiedMessageCenterView* message_center_view() {
+  virtual TestUnifiedMessageCenterView* message_center_view() {
     return message_center_view_.get();
   }
 
@@ -219,6 +225,40 @@
   DISALLOW_COPY_AND_ASSIGN(UnifiedMessageCenterViewTest);
 };
 
+class UnifiedMessageCenterViewInWidgetTest
+    : public UnifiedMessageCenterViewTest {
+ public:
+  UnifiedMessageCenterViewInWidgetTest() = default;
+  UnifiedMessageCenterViewInWidgetTest(
+      const UnifiedMessageCenterViewInWidgetTest&) = delete;
+  UnifiedMessageCenterViewInWidgetTest& operator=(
+      const UnifiedMessageCenterViewInWidgetTest&) = delete;
+  ~UnifiedMessageCenterViewInWidgetTest() override = default;
+
+  void TearDown() override {
+    widget_.reset();
+
+    UnifiedMessageCenterViewTest::TearDown();
+  }
+
+ protected:
+  void CreateMessageCenterView(int max_height = kDefaultMaxHeight) override {
+    widget_ = CreateTestWidget();
+    message_center_ = widget_->GetRootView()->AddChildView(
+        CreateMessageCenterViewImpl(max_height));
+  }
+
+  TestUnifiedMessageCenterView* message_center_view() override {
+    return message_center_;
+  }
+
+  views::Widget* widget() { return widget_.get(); }
+
+ private:
+  std::unique_ptr<views::Widget> widget_;
+  TestUnifiedMessageCenterView* message_center_ = nullptr;
+};
+
 TEST_F(UnifiedMessageCenterViewTest, AddAndRemoveNotification) {
   CreateMessageCenterView();
   EXPECT_FALSE(message_center_view()->GetVisible());
@@ -687,13 +727,12 @@
   EXPECT_EQ(0, message_center_view()->rect_below_scroll().height());
 }
 
-TEST_F(UnifiedMessageCenterViewTest, FocusClearedAfterNotificationRemoval) {
+// We need a widget to initialize a FocusManager.
+TEST_F(UnifiedMessageCenterViewInWidgetTest,
+       FocusClearedAfterNotificationRemoval) {
   CreateMessageCenterView();
 
-  // We need to create a widget in order to initialize a FocusManager.
-  auto widget = CreateTestWidget();
-  widget->GetRootView()->AddChildView(message_center_view());
-  widget->Show();
+  widget()->Show();
 
   // Add notifications and focus on a child view in the last notification.
   AddNotification();
@@ -709,8 +748,6 @@
   MessageCenter::Get()->RemoveNotification(id1, true /* by_user */);
   AnimateMessageListToEnd();
   EXPECT_FALSE(message_center_view()->GetFocusManager()->GetFocusedView());
-
-  widget->GetRootView()->RemoveChildView(message_center_view());
 }
 
 TEST_F(UnifiedMessageCenterViewTest, CollapseAndExpand_NonAnimated) {
diff --git a/base/memory/checked_ptr.h b/base/memory/checked_ptr.h
index fe430c1..cebea3f 100644
--- a/base/memory/checked_ptr.h
+++ b/base/memory/checked_ptr.h
@@ -123,6 +123,11 @@
     return *this;
   }
 
+  ALWAYS_INLINE CheckedPtr& operator=(std::nullptr_t) noexcept {
+    wrapped_ptr_ = Impl::GetWrappedNullPtr();
+    return *this;
+  }
+
   ~CheckedPtr() = default;
 
   // Avoid using. The goal of CheckedPtr is to be as close to raw pointer as
diff --git a/base/memory/checked_ptr_unittest.cc b/base/memory/checked_ptr_unittest.cc
index 5e6c091..2656087 100644
--- a/base/memory/checked_ptr_unittest.cc
+++ b/base/memory/checked_ptr_unittest.cc
@@ -50,12 +50,14 @@
 
 namespace {
 
+static int g_wrap_raw_ptr_cnt = INT_MIN;
 static int g_get_for_dereference_cnt = INT_MIN;
 static int g_get_for_extraction_cnt = INT_MIN;
 static int g_get_for_comparison_cnt = INT_MIN;
 static int g_checked_ptr_swap_cnt = INT_MIN;
 
 static void ClearCounters() {
+  g_wrap_raw_ptr_cnt = 0;
   g_get_for_dereference_cnt = 0;
   g_get_for_extraction_cnt = 0;
   g_get_for_comparison_cnt = 0;
@@ -65,6 +67,11 @@
 struct CheckedPtrCountingNoOpImpl : base::internal::CheckedPtrNoOpImpl {
   using Super = base::internal::CheckedPtrNoOpImpl;
 
+  static ALWAYS_INLINE uintptr_t WrapRawPtr(const void* const_ptr) {
+    ++g_wrap_raw_ptr_cnt;
+    return Super::WrapRawPtr(const_ptr);
+  }
+
   static ALWAYS_INLINE void* SafelyUnwrapPtrForDereference(
       uintptr_t wrapped_ptr) {
     ++g_get_for_dereference_cnt;
@@ -455,4 +462,16 @@
   }
 }
 
+TEST(CheckedPtr, AssignmentFromNullptr) {
+  CountingCheckedPtr<int> checked_ptr;
+
+  ClearCounters();
+  checked_ptr = nullptr;
+
+  EXPECT_EQ(g_wrap_raw_ptr_cnt, 0);
+  EXPECT_EQ(g_get_for_comparison_cnt, 0);
+  EXPECT_EQ(g_get_for_extraction_cnt, 0);
+  EXPECT_EQ(g_get_for_dereference_cnt, 0);
+}
+
 }  // namespace
diff --git a/base/strings/string_util.cc b/base/strings/string_util.cc
index 2e07ea77..ebd0767 100644
--- a/base/strings/string_util.cc
+++ b/base/strings/string_util.cc
@@ -120,19 +120,19 @@
 }  // namespace
 
 std::string ToLowerASCII(StringPiece str) {
-  return ToLowerASCIIImpl<std::string>(str);
+  return ToLowerASCIIImpl(str);
 }
 
 string16 ToLowerASCII(StringPiece16 str) {
-  return ToLowerASCIIImpl<string16>(str);
+  return ToLowerASCIIImpl(str);
 }
 
 std::string ToUpperASCII(StringPiece str) {
-  return ToUpperASCIIImpl<std::string>(str);
+  return ToUpperASCIIImpl(str);
 }
 
 string16 ToUpperASCII(StringPiece16 str) {
-  return ToUpperASCIIImpl<string16>(str);
+  return ToUpperASCIIImpl(str);
 }
 
 template<class StringType>
@@ -163,23 +163,19 @@
 }
 
 int CompareCaseInsensitiveASCII(StringPiece a, StringPiece b) {
-  return CompareCaseInsensitiveASCIIT<std::string>(a, b);
+  return CompareCaseInsensitiveASCIIT(a, b);
 }
 
 int CompareCaseInsensitiveASCII(StringPiece16 a, StringPiece16 b) {
-  return CompareCaseInsensitiveASCIIT<string16>(a, b);
+  return CompareCaseInsensitiveASCIIT(a, b);
 }
 
 bool EqualsCaseInsensitiveASCII(StringPiece a, StringPiece b) {
-  if (a.length() != b.length())
-    return false;
-  return CompareCaseInsensitiveASCIIT<std::string>(a, b) == 0;
+  return a.size() == b.size() && CompareCaseInsensitiveASCIIT(a, b) == 0;
 }
 
 bool EqualsCaseInsensitiveASCII(StringPiece16 a, StringPiece16 b) {
-  if (a.length() != b.length())
-    return false;
-  return CompareCaseInsensitiveASCIIT<string16>(a, b) == 0;
+  return a.size() == b.size() && CompareCaseInsensitiveASCIIT(a, b) == 0;
 }
 
 const std::string& EmptyString() {
@@ -193,32 +189,32 @@
 }
 
 template <class StringType>
-bool ReplaceCharsT(const StringType& input,
+bool ReplaceCharsT(BasicStringPiece<StringType> input,
                    BasicStringPiece<StringType> find_any_of_these,
                    BasicStringPiece<StringType> replace_with,
                    StringType* output);
 
-bool ReplaceChars(const string16& input,
+bool ReplaceChars(StringPiece16 input,
                   StringPiece16 replace_chars,
                   StringPiece16 replace_with,
                   string16* output) {
   return ReplaceCharsT(input, replace_chars, replace_with, output);
 }
 
-bool ReplaceChars(const std::string& input,
+bool ReplaceChars(StringPiece input,
                   StringPiece replace_chars,
                   StringPiece replace_with,
                   std::string* output) {
   return ReplaceCharsT(input, replace_chars, replace_with, output);
 }
 
-bool RemoveChars(const string16& input,
+bool RemoveChars(StringPiece16 input,
                  StringPiece16 remove_chars,
                  string16* output) {
   return ReplaceCharsT(input, remove_chars, StringPiece16(), output);
 }
 
-bool RemoveChars(const std::string& input,
+bool RemoveChars(StringPiece input,
                  StringPiece remove_chars,
                  std::string* output) {
   return ReplaceCharsT(input, remove_chars, StringPiece(), output);
@@ -353,8 +349,8 @@
   return TrimStringPieceT(input, StringPiece(kWhitespaceASCII), positions);
 }
 
-template<typename STR>
-STR CollapseWhitespaceT(const STR& text,
+template <typename STR>
+STR CollapseWhitespaceT(BasicStringPiece<STR> text,
                         bool trim_sequences_with_line_breaks) {
   STR result;
   result.resize(text.size());
@@ -365,15 +361,15 @@
   bool already_trimmed = true;
 
   int chars_written = 0;
-  for (typename STR::const_iterator i(text.begin()); i != text.end(); ++i) {
-    if (IsUnicodeWhitespace(*i)) {
+  for (auto c : text) {
+    if (IsUnicodeWhitespace(c)) {
       if (!in_whitespace) {
         // Reduce all whitespace sequences to a single space.
         in_whitespace = true;
         result[chars_written++] = L' ';
       }
       if (trim_sequences_with_line_breaks && !already_trimmed &&
-          ((*i == '\n') || (*i == '\r'))) {
+          ((c == '\n') || (c == '\r'))) {
         // Whitespace sequences containing CR or LF are eliminated entirely.
         already_trimmed = true;
         --chars_written;
@@ -382,7 +378,7 @@
       // Non-whitespace chracters are copied straight across.
       in_whitespace = false;
       already_trimmed = false;
-      result[chars_written++] = *i;
+      result[chars_written++] = c;
     }
   }
 
@@ -395,12 +391,12 @@
   return result;
 }
 
-string16 CollapseWhitespace(const string16& text,
+string16 CollapseWhitespace(StringPiece16 text,
                             bool trim_sequences_with_line_breaks) {
   return CollapseWhitespaceT(text, trim_sequences_with_line_breaks);
 }
 
-std::string CollapseWhitespaceASCII(const std::string& text,
+std::string CollapseWhitespaceASCII(StringPiece text,
                                     bool trim_sequences_with_line_breaks) {
   return CollapseWhitespaceT(text, trim_sequences_with_line_breaks);
 }
@@ -531,9 +527,7 @@
 }
 
 bool EqualsASCII(StringPiece16 str, StringPiece ascii) {
-  if (str.length() != ascii.length())
-    return false;
-  return std::equal(ascii.begin(), ascii.end(), str.begin());
+  return std::equal(ascii.begin(), ascii.end(), str.begin(), str.end());
 }
 
 template<typename Str>
@@ -836,13 +830,14 @@
 }
 
 template <class StringType>
-bool ReplaceCharsT(const StringType& input,
+bool ReplaceCharsT(BasicStringPiece<StringType> input,
                    BasicStringPiece<StringType> find_any_of_these,
                    BasicStringPiece<StringType> replace_with,
                    StringType* output) {
   // Commonly, this is called with output and input being the same string; in
-  // that case, this assignment is inexpensive.
-  *output = input;
+  // that case, skip the copy.
+  if (input.data() != output->data() || input.size() != output->size())
+    output->assign(input.data(), input.size());
 
   return DoReplaceMatchesAfterOffset(
       output, 0, CharacterMatcher<StringType>{find_any_of_these}, replace_with,
@@ -903,11 +898,11 @@
 }
 
 // Generic version for all JoinString overloads. |list_type| must be a sequence
-// (std::vector or std::initializer_list) of strings/StringPieces (std::string,
+// (base::span or std::initializer_list) of strings/StringPieces (std::string,
 // string16, StringPiece or StringPiece16). |string_type| is either std::string
 // or string16.
 template <typename list_type, typename string_type>
-static string_type JoinStringT(const list_type& parts,
+static string_type JoinStringT(list_type parts,
                                BasicStringPiece<string_type> sep) {
   if (base::empty(parts))
     return string_type();
@@ -936,23 +931,19 @@
   return result;
 }
 
-std::string JoinString(const std::vector<std::string>& parts,
-                       StringPiece separator) {
+std::string JoinString(span<const std::string> parts, StringPiece separator) {
   return JoinStringT(parts, separator);
 }
 
-string16 JoinString(const std::vector<string16>& parts,
-                    StringPiece16 separator) {
+string16 JoinString(span<const string16> parts, StringPiece16 separator) {
   return JoinStringT(parts, separator);
 }
 
-std::string JoinString(const std::vector<StringPiece>& parts,
-                       StringPiece separator) {
+std::string JoinString(span<const StringPiece> parts, StringPiece separator) {
   return JoinStringT(parts, separator);
 }
 
-string16 JoinString(const std::vector<StringPiece16>& parts,
-                    StringPiece16 separator) {
+string16 JoinString(span<const StringPiece16> parts, StringPiece16 separator) {
   return JoinStringT(parts, separator);
 }
 
@@ -966,10 +957,10 @@
   return JoinStringT(parts, separator);
 }
 
-template<class FormatStringType, class OutStringType>
-OutStringType DoReplaceStringPlaceholders(
-    const FormatStringType& format_string,
-    const std::vector<OutStringType>& subst,
+template <class StringType>
+StringType DoReplaceStringPlaceholders(
+    BasicStringPiece<StringType> format_string,
+    const std::vector<StringType>& subst,
     std::vector<size_t>* offsets) {
   size_t substitutions = subst.size();
   DCHECK_LT(substitutions, 10U);
@@ -978,7 +969,7 @@
   for (const auto& cur : subst)
     sub_length += cur.length();
 
-  OutStringType formatted;
+  StringType formatted;
   formatted.reserve(format_string.length() + sub_length);
 
   std::vector<ReplacementOffset> r_offsets;
@@ -1021,7 +1012,7 @@
   return formatted;
 }
 
-string16 ReplaceStringPlaceholders(const string16& format_string,
+string16 ReplaceStringPlaceholders(StringPiece16 format_string,
                                    const std::vector<string16>& subst,
                                    std::vector<size_t>* offsets) {
   return DoReplaceStringPlaceholders(format_string, subst, offsets);
@@ -1037,9 +1028,7 @@
                                    const string16& a,
                                    size_t* offset) {
   std::vector<size_t> offsets;
-  std::vector<string16> subst;
-  subst.push_back(a);
-  string16 result = ReplaceStringPlaceholders(format_string, subst, &offsets);
+  string16 result = ReplaceStringPlaceholders(format_string, {a}, &offsets);
 
   DCHECK_EQ(1U, offsets.size());
   if (offset)
@@ -1048,19 +1037,33 @@
 }
 
 #if defined(OS_WIN) && defined(BASE_STRING16_IS_STD_U16STRING)
-
-TrimPositions TrimWhitespace(WStringPiece input,
-                             TrimPositions positions,
-                             std::wstring* output) {
-  return TrimStringT(input, WStringPiece(kWhitespaceWide), positions, output);
+std::wstring ToLowerASCII(WStringPiece str) {
+  return ToLowerASCIIImpl(str);
 }
 
-WStringPiece TrimWhitespace(WStringPiece input, TrimPositions positions) {
-  return TrimStringPieceT(input, WStringPiece(kWhitespaceWide), positions);
+std::wstring ToUpperASCII(WStringPiece str) {
+  return ToUpperASCIIImpl(str);
 }
 
-bool LowerCaseEqualsASCII(WStringPiece str, StringPiece lowercase_ascii) {
-  return DoLowerCaseEqualsASCII(str, lowercase_ascii);
+int CompareCaseInsensitiveASCII(WStringPiece a, WStringPiece b) {
+  return CompareCaseInsensitiveASCIIT(a, b);
+}
+
+bool EqualsCaseInsensitiveASCII(WStringPiece a, WStringPiece b) {
+  return a.size() == b.size() && CompareCaseInsensitiveASCIIT(a, b) == 0;
+}
+
+bool RemoveChars(WStringPiece input,
+                 WStringPiece remove_chars,
+                 std::wstring* output) {
+  return ReplaceCharsT(input, remove_chars, WStringPiece(), output);
+}
+
+bool ReplaceChars(WStringPiece input,
+                  WStringPiece replace_chars,
+                  WStringPiece replace_with,
+                  std::wstring* output) {
+  return ReplaceCharsT(input, replace_chars, replace_with, output);
 }
 
 bool TrimString(WStringPiece input,
@@ -1075,16 +1078,88 @@
   return TrimStringPieceT(input, trim_chars, positions);
 }
 
+TrimPositions TrimWhitespace(WStringPiece input,
+                             TrimPositions positions,
+                             std::wstring* output) {
+  return TrimStringT(input, WStringPiece(kWhitespaceWide), positions, output);
+}
+
+WStringPiece TrimWhitespace(WStringPiece input, TrimPositions positions) {
+  return TrimStringPieceT(input, WStringPiece(kWhitespaceWide), positions);
+}
+
+std::wstring CollapseWhitespace(WStringPiece text,
+                                bool trim_sequences_with_line_breaks) {
+  return CollapseWhitespaceT(text, trim_sequences_with_line_breaks);
+}
+
+bool ContainsOnlyChars(WStringPiece input, WStringPiece characters) {
+  return input.find_first_not_of(characters) == StringPiece::npos;
+}
+
+bool LowerCaseEqualsASCII(WStringPiece str, StringPiece lowercase_ascii) {
+  return DoLowerCaseEqualsASCII(str, lowercase_ascii);
+}
+
+bool EqualsASCII(WStringPiece str, StringPiece ascii) {
+  return std::equal(ascii.begin(), ascii.end(), str.begin(), str.end());
+}
+
 bool StartsWith(WStringPiece str,
                 WStringPiece search_for,
                 CompareCase case_sensitivity) {
   return StartsWithT(str, search_for, case_sensitivity);
 }
 
+bool EndsWith(WStringPiece str,
+              WStringPiece search_for,
+              CompareCase case_sensitivity) {
+  return EndsWithT(str, search_for, case_sensitivity);
+}
+
+void ReplaceFirstSubstringAfterOffset(std::wstring* str,
+                                      size_t start_offset,
+                                      WStringPiece find_this,
+                                      WStringPiece replace_with) {
+  DoReplaceMatchesAfterOffset(str, start_offset,
+                              SubstringMatcher<std::wstring>{find_this},
+                              replace_with, ReplaceType::REPLACE_FIRST);
+}
+
+void ReplaceSubstringsAfterOffset(std::wstring* str,
+                                  size_t start_offset,
+                                  WStringPiece find_this,
+                                  WStringPiece replace_with) {
+  DoReplaceMatchesAfterOffset(str, start_offset,
+                              SubstringMatcher<std::wstring>{find_this},
+                              replace_with, ReplaceType::REPLACE_ALL);
+}
+
 wchar_t* WriteInto(std::wstring* str, size_t length_with_null) {
   return WriteIntoT(str, length_with_null);
 }
 
+std::wstring JoinString(span<const std::wstring> parts,
+                        WStringPiece separator) {
+  return JoinStringT(parts, separator);
+}
+
+std::wstring JoinString(span<const WStringPiece> parts,
+                        WStringPiece separator) {
+  return JoinStringT(parts, separator);
+}
+
+std::wstring JoinString(std::initializer_list<WStringPiece> parts,
+                        WStringPiece separator) {
+  return JoinStringT(parts, separator);
+}
+
+std::wstring ReplaceStringPlaceholders(WStringPiece format_string,
+                                       const std::vector<std::wstring>& subst,
+                                       std::vector<size_t>* offsets) {
+  return DoReplaceStringPlaceholders(format_string, subst, offsets);
+}
+
 #endif
 
 // The following code is compatible with the OpenBSD lcpy interface.  See:
diff --git a/base/strings/string_util.h b/base/strings/string_util.h
index b9eee73..b132fa4 100644
--- a/base/strings/string_util.h
+++ b/base/strings/string_util.h
@@ -19,6 +19,7 @@
 
 #include "base/base_export.h"
 #include "base/compiler_specific.h"
+#include "base/containers/span.h"
 #include "base/stl_util.h"
 #include "base/strings/string16.h"
 #include "base/strings/string_piece.h"  // For implicit conversions.
@@ -169,10 +170,10 @@
 // Removes characters in |remove_chars| from anywhere in |input|.  Returns true
 // if any characters were removed.  |remove_chars| must be null-terminated.
 // NOTE: Safe to use the same variable for both |input| and |output|.
-BASE_EXPORT bool RemoveChars(const string16& input,
+BASE_EXPORT bool RemoveChars(StringPiece16 input,
                              StringPiece16 remove_chars,
                              string16* output);
-BASE_EXPORT bool RemoveChars(const std::string& input,
+BASE_EXPORT bool RemoveChars(StringPiece input,
                              StringPiece remove_chars,
                              std::string* output);
 
@@ -181,11 +182,11 @@
 // the |replace_with| string.  Returns true if any characters were replaced.
 // |replace_chars| must be null-terminated.
 // NOTE: Safe to use the same variable for both |input| and |output|.
-BASE_EXPORT bool ReplaceChars(const string16& input,
+BASE_EXPORT bool ReplaceChars(StringPiece16 input,
                               StringPiece16 replace_chars,
                               StringPiece16 replace_with,
                               string16* output);
-BASE_EXPORT bool ReplaceChars(const std::string& input,
+BASE_EXPORT bool ReplaceChars(StringPiece input,
                               StringPiece replace_chars,
                               StringPiece replace_with,
                               std::string* output);
@@ -314,11 +315,10 @@
 // (2) If |trim_sequences_with_line_breaks| is true, any other whitespace
 //     sequences containing a CR or LF are trimmed.
 // (3) All other whitespace sequences are converted to single spaces.
-BASE_EXPORT string16 CollapseWhitespace(
-    const string16& text,
-    bool trim_sequences_with_line_breaks);
+BASE_EXPORT string16 CollapseWhitespace(StringPiece16 text,
+                                        bool trim_sequences_with_line_breaks);
 BASE_EXPORT std::string CollapseWhitespaceASCII(
-    const std::string& text,
+    StringPiece text,
     bool trim_sequences_with_line_breaks);
 
 // Returns true if |input| is empty or contains only characters found in
@@ -488,8 +488,8 @@
 BASE_EXPORT char* WriteInto(std::string* str, size_t length_with_null);
 BASE_EXPORT char16* WriteInto(string16* str, size_t length_with_null);
 
-// Joins a vector or list of strings into a single string, inserting |separator|
-// (which may be empty) in between all elements.
+// Joins a list of strings into a single string, inserting |separator| (which
+// may be empty) in between all elements.
 //
 // Note this is inverse of SplitString()/SplitStringPiece() defined in
 // string_split.h.
@@ -501,13 +501,13 @@
 // copies of those strings are created until the final join operation.
 //
 // Use StrCat (in base/strings/strcat.h) if you don't need a separator.
-BASE_EXPORT std::string JoinString(const std::vector<std::string>& parts,
+BASE_EXPORT std::string JoinString(span<const std::string> parts,
                                    StringPiece separator);
-BASE_EXPORT string16 JoinString(const std::vector<string16>& parts,
+BASE_EXPORT string16 JoinString(span<const string16> parts,
                                 StringPiece16 separator);
-BASE_EXPORT std::string JoinString(const std::vector<StringPiece>& parts,
+BASE_EXPORT std::string JoinString(span<const StringPiece> parts,
                                    StringPiece separator);
-BASE_EXPORT string16 JoinString(const std::vector<StringPiece16>& parts,
+BASE_EXPORT string16 JoinString(span<const StringPiece16> parts,
                                 StringPiece16 separator);
 // Explicit initializer_list overloads are required to break ambiguity when used
 // with a literal initializer list (otherwise the compiler would not be able to
@@ -521,10 +521,10 @@
 // Additionally, any number of consecutive '$' characters is replaced by that
 // number less one. Eg $$->$, $$$->$$, etc. The offsets parameter here can be
 // NULL. This only allows you to use up to nine replacements.
-BASE_EXPORT string16 ReplaceStringPlaceholders(
-    const string16& format_string,
-    const std::vector<string16>& subst,
-    std::vector<size_t>* offsets);
+BASE_EXPORT string16
+ReplaceStringPlaceholders(StringPiece16 format_string,
+                          const std::vector<string16>& subst,
+                          std::vector<size_t>* offsets);
 
 BASE_EXPORT std::string ReplaceStringPlaceholders(
     StringPiece format_string,
@@ -536,7 +536,36 @@
                                                const string16& a,
                                                size_t* offset);
 
+// The following section contains overloads of the previously defined APIs for
+// std::wstring and base::WStringPiece. In order to discourage using
+// std::wstring in cross-platform code, these overloads are only enabled on
+// Windows, and only if std::wstring is a different type than base::string16.
 #if defined(OS_WIN) && defined(BASE_STRING16_IS_STD_U16STRING)
+BASE_EXPORT std::wstring ToLowerASCII(WStringPiece str);
+
+BASE_EXPORT std::wstring ToUpperASCII(WStringPiece str);
+
+BASE_EXPORT int CompareCaseInsensitiveASCII(WStringPiece a, WStringPiece b);
+
+BASE_EXPORT bool EqualsCaseInsensitiveASCII(WStringPiece a, WStringPiece b);
+
+BASE_EXPORT bool RemoveChars(WStringPiece input,
+                             WStringPiece remove_chars,
+                             std::wstring* output);
+
+BASE_EXPORT bool ReplaceChars(WStringPiece input,
+                              WStringPiece replace_chars,
+                              WStringPiece replace_with,
+                              std::wstring* output);
+
+BASE_EXPORT bool TrimString(WStringPiece input,
+                            WStringPiece trim_chars,
+                            std::string* output);
+
+BASE_EXPORT WStringPiece TrimString(WStringPiece input,
+                                    WStringPiece trim_chars,
+                                    TrimPositions positions);
+
 BASE_EXPORT TrimPositions TrimWhitespace(WStringPiece input,
                                          TrimPositions positions,
                                          std::wstring* output);
@@ -544,22 +573,50 @@
 BASE_EXPORT WStringPiece TrimWhitespace(WStringPiece input,
                                         TrimPositions positions);
 
+BASE_EXPORT std::wstring CollapseWhitespace(
+    WStringPiece text,
+    bool trim_sequences_with_line_breaks);
+
+BASE_EXPORT bool ContainsOnlyChars(WStringPiece input, WStringPiece characters);
+
 BASE_EXPORT bool LowerCaseEqualsASCII(WStringPiece str,
                                       StringPiece lowecase_ascii);
 
-BASE_EXPORT bool TrimString(WStringPiece input,
-                            WStringPiece trim_chars,
-                            std::wstring* output);
-
-BASE_EXPORT WStringPiece TrimString(WStringPiece input,
-                                    WStringPiece trim_chars,
-                                    TrimPositions positions);
+BASE_EXPORT bool EqualsASCII(StringPiece16 str, StringPiece ascii);
 
 BASE_EXPORT bool StartsWith(WStringPiece str,
                             WStringPiece search_for,
                             CompareCase case_sensitivity);
 
+BASE_EXPORT bool EndsWith(WStringPiece str,
+                          WStringPiece search_for,
+                          CompareCase case_sensitivity);
+
+BASE_EXPORT void ReplaceFirstSubstringAfterOffset(std::wstring* str,
+                                                  size_t start_offset,
+                                                  WStringPiece find_this,
+                                                  WStringPiece replace_with);
+
+BASE_EXPORT void ReplaceSubstringsAfterOffset(std::wstring* str,
+                                              size_t start_offset,
+                                              WStringPiece find_this,
+                                              WStringPiece replace_with);
+
 BASE_EXPORT wchar_t* WriteInto(std::wstring* str, size_t length_with_null);
+
+BASE_EXPORT std::wstring JoinString(span<const std::wstring> parts,
+                                    WStringPiece separator);
+
+BASE_EXPORT std::wstring JoinString(span<const WStringPiece> parts,
+                                    WStringPiece separator);
+
+BASE_EXPORT std::wstring JoinString(std::initializer_list<WStringPiece> parts,
+                                    WStringPiece separator);
+
+BASE_EXPORT std::wstring ReplaceStringPlaceholders(
+    WStringPiece format_string,
+    const std::vector<string16>& subst,
+    std::vector<size_t>* offsets);
 #endif
 
 }  // namespace base
diff --git a/build/android/gyp/compile_resources.py b/build/android/gyp/compile_resources.py
index 2ca4ec7..eece2eb3 100755
--- a/build/android/gyp/compile_resources.py
+++ b/build/android/gyp/compile_resources.py
@@ -18,7 +18,6 @@
 import filecmp
 import hashlib
 import logging
-import multiprocessing.dummy
 import os
 import re
 import shutil
@@ -26,7 +25,6 @@
 import sys
 import tempfile
 import textwrap
-import time
 import zipfile
 from xml.etree import ElementTree
 
@@ -34,9 +32,11 @@
 from util import diff_utils
 from util import manifest_utils
 from util import md5_check
+from util import parallel
 from util import protoresources
 from util import resource_utils
 
+
 # Pngs that we shouldn't convert to webp. Please add rationale when updating.
 _PNG_WEBP_EXCLUSION_PATTERN = re.compile('|'.join([
     # Crashes on Galaxy S5 running L (https://crbug.com/807059).
@@ -546,68 +546,64 @@
       build_utils.MatchesGlob(path, resource_exclusion_exceptions))
 
 
-def _ConvertToWebP(webp_binary, png_paths, path_info, webp_cache_dir):
-  pool = multiprocessing.dummy.Pool(10)
+def _ComputeSha1(path):
+  with open(path, 'rb') as f:
+    data = f.read()
+  return hashlib.sha1(data).hexdigest()
+
+
+def _ConvertToWebPSingle(png_path, cwebp_binary, cwebp_version, webp_cache_dir):
+  sha1_hash = _ComputeSha1(png_path)
+
+  # The set of arguments that will appear in the cache key.
+  quality_args = ['-m', '6', '-q', '100', '-lossless']
+
+  webp_cache_path = os.path.join(
+      webp_cache_dir, '{}-{}-{}'.format(sha1_hash, cwebp_version,
+                                        ''.join(quality_args)))
+  # No need to add .webp. Android can load images fine without them.
+  webp_path = os.path.splitext(png_path)[0]
+
+  cache_hit = os.path.exists(webp_cache_path)
+  if cache_hit:
+    os.link(webp_cache_path, webp_path)
+  else:
+    # We place the generated webp image to webp_path, instead of in the
+    # webp_cache_dir to avoid concurrency issues.
+    args = [cwebp_binary, png_path, '-o', webp_path, '-quiet'] + quality_args
+    subprocess.check_call(args)
+
+    try:
+      os.link(webp_path, webp_cache_path)
+    except OSError:
+      # Because of concurrent run, a webp image may already exists in
+      # webp_cache_path.
+      pass
+
+  os.remove(png_path)
+  original_dir = os.path.dirname(os.path.dirname(png_path))
+  rename_tuple = (os.path.relpath(png_path, original_dir),
+                  os.path.relpath(webp_path, original_dir))
+  return rename_tuple, cache_hit
+
+
+def _ConvertToWebP(cwebp_binary, png_paths, path_info, webp_cache_dir):
+  cwebp_version = subprocess.check_output([cwebp_binary, '-version']).rstrip()
+  shard_args = [(f, ) for f in png_paths
+                if not _PNG_WEBP_EXCLUSION_PATTERN.match(f)]
 
   build_utils.MakeDirectory(webp_cache_dir)
+  results = parallel.BulkForkAndCall(_ConvertToWebPSingle,
+                                     shard_args,
+                                     cwebp_binary=cwebp_binary,
+                                     cwebp_version=cwebp_version,
+                                     webp_cache_dir=webp_cache_dir)
+  total_cache_hits = 0
+  for rename_tuple, cache_hit in results:
+    path_info.RegisterRename(*rename_tuple)
+    total_cache_hits += int(cache_hit)
 
-  cwebp_version = subprocess.check_output([webp_binary, '-version']).rstrip()
-  cwebp_arguments = ['-mt', '-quiet', '-m', '6', '-q', '100', '-lossless']
-
-  sha1_time = [0]
-  cwebp_time = [0]
-  cache_hits = [0]
-
-  def cal_sha1(png_path):
-    start = time.time()
-    with open(png_path, 'rb') as f:
-      png_content = f.read()
-
-    sha1_hex = hashlib.sha1(png_content).hexdigest()
-    sha1_time[0] += time.time() - start
-    return sha1_hex
-
-  def get_converted_image(png_path):
-    sha1_hash = cal_sha1(png_path)
-
-    webp_cache_path = os.path.join(
-        webp_cache_dir, '{}-{}-{}'.format(sha1_hash, cwebp_version,
-                                          ''.join(cwebp_arguments)))
-    # No need to add an extension, android can load images fine without them.
-    webp_path = os.path.splitext(png_path)[0]
-
-    if os.path.exists(webp_cache_path):
-      cache_hits[0] += 1
-      os.link(webp_cache_path, webp_path)
-    else:
-      # We place the generated webp image to webp_path, instead of in the
-      # webp_cache_dir to avoid concurrency issues.
-      start = time.time()
-      args = [webp_binary, png_path] + cwebp_arguments + ['-o', webp_path]
-      subprocess.check_call(args)
-      cwebp_time[0] += time.time() - start
-
-      try:
-        os.link(webp_path, webp_cache_path)
-      except OSError:
-        # Because of concurrent run, a webp image may already exists in
-        # webp_cache_path.
-        pass
-
-    os.remove(png_path)
-    original_dir = os.path.dirname(os.path.dirname(png_path))
-    path_info.RegisterRename(
-        os.path.relpath(png_path, original_dir),
-        os.path.relpath(webp_path, original_dir))
-
-  png_paths = [f for f in png_paths if not _PNG_WEBP_EXCLUSION_PATTERN.match(f)]
-  try:
-    pool.map(get_converted_image, png_paths)
-  finally:
-    pool.close()
-    pool.join()
-  logging.debug('png->webp: cache: %d/%d sha1 time: %.1fms cwebp time: %.1fms',
-                cache_hits[0], len(png_paths), sha1_time[0], cwebp_time[0])
+  logging.debug('png->webp cache: %d/%d', total_cache_hits, len(shard_args))
 
 
 def _RemoveImageExtensions(directory, path_info):
@@ -627,10 +623,9 @@
             os.path.relpath(path_no_extension, directory))
 
 
-def _CompileSingleDep(args):
-  index, dep_path, aapt2_path, partials_dir, exclusion_rules = args
-  basename = os.path.basename(dep_path)
-  unique_name = '{}_{}'.format(index, basename)
+def _CompileSingleDep(index, dep_subdir, keep_predicate, aapt2_path,
+                      partials_dir):
+  unique_name = '{}_{}'.format(index, os.path.basename(dep_subdir))
   partial_path = os.path.join(partials_dir, '{}.zip'.format(unique_name))
 
   compile_command = [
@@ -639,7 +634,7 @@
       # TODO(wnwen): Turn this on once aapt2 forces 9-patch to be crunched.
       # '--no-crunch',
       '--dir',
-      dep_path,
+      dep_subdir,
       '-o',
       partial_path
   ]
@@ -654,33 +649,16 @@
 
   # Filtering these files is expensive, so only apply filters to the partials
   # that have been explicitly targeted.
-  keep_predicate = _CreateValuesKeepPredicate(exclusion_rules, dep_path)
   if keep_predicate:
-    logging.debug('Applying .arsc filtering to %s', dep_path)
+    logging.debug('Applying .arsc filtering to %s', dep_subdir)
     protoresources.StripUnwantedResources(partial_path, keep_predicate)
   return partial_path
 
 
-def _CompileDeps(aapt2_path, dep_subdirs, temp_dir, exclusion_rules):
-  partials_dir = os.path.join(temp_dir, 'partials')
-  build_utils.MakeDirectory(partials_dir)
-
-  def iter_params():
-    for i, dep_path in enumerate(dep_subdirs):
-      yield i, dep_path, aapt2_path, partials_dir, exclusion_rules
-
-  pool = multiprocessing.dummy.Pool(10)
-  try:
-    return pool.map(_CompileSingleDep, iter_params())
-  finally:
-    pool.close()
-    pool.join()
-
-
-def _CreateValuesKeepPredicate(exclusion_rules, dep_path):
+def _CreateValuesKeepPredicate(exclusion_rules, dep_subdir):
   patterns = [
       x[1] for x in exclusion_rules
-      if build_utils.MatchesGlob(dep_path, [x[0]])
+      if build_utils.MatchesGlob(dep_subdir, [x[0]])
   ]
   if not patterns:
     return None
@@ -689,6 +667,23 @@
   return lambda x: not any(r.search(x) for r in regexes)
 
 
+def _CompileDeps(aapt2_path, dep_subdirs, temp_dir, exclusion_rules):
+  partials_dir = os.path.join(temp_dir, 'partials')
+  build_utils.MakeDirectory(partials_dir)
+
+  job_params = [(i, dep_subdir,
+                 _CreateValuesKeepPredicate(exclusion_rules, dep_subdir))
+                for i, dep_subdir in enumerate(dep_subdirs)]
+
+  # Filtering is slow, so ensure jobs with keep_predicate are started first.
+  job_params.sort(key=lambda x: not x[2])
+  return list(
+      parallel.BulkForkAndCall(_CompileSingleDep,
+                               job_params,
+                               aapt2_path=aapt2_path,
+                               partials_dir=partials_dir))
+
+
 def _CreateResourceInfoFile(path_info, info_path, dependencies_res_zips):
   for zip_file in dependencies_res_zips:
     zip_info_file_path = zip_file + '.info'
diff --git a/build/android/gyp/compile_resources.pydeps b/build/android/gyp/compile_resources.pydeps
index f34926c..4e5ce72 100644
--- a/build/android/gyp/compile_resources.pydeps
+++ b/build/android/gyp/compile_resources.pydeps
@@ -55,5 +55,6 @@
 util/diff_utils.py
 util/manifest_utils.py
 util/md5_check.py
+util/parallel.py
 util/protoresources.py
 util/resource_utils.py
diff --git a/build/android/gyp/util/parallel.py b/build/android/gyp/util/parallel.py
new file mode 100644
index 0000000..082ad97
--- /dev/null
+++ b/build/android/gyp/util/parallel.py
@@ -0,0 +1,214 @@
+# 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.
+"""Helpers related to multiprocessing.
+
+Based on: //tools/binary_size/libsupersize/parallel.py
+"""
+
+import atexit
+import logging
+import multiprocessing
+import os
+import sys
+import threading
+import traceback
+
+DISABLE_ASYNC = os.environ.get('DISABLE_ASYNC') == '1'
+if DISABLE_ASYNC:
+  logging.warning('Running in synchronous mode.')
+
+_all_pools = None
+_is_child_process = False
+_silence_exceptions = False
+
+# Used to pass parameters to forked processes without pickling.
+_fork_params = None
+_fork_kwargs = None
+
+
+class _ImmediateResult(object):
+  def __init__(self, value):
+    self._value = value
+
+  def get(self):
+    return self._value
+
+  def wait(self):
+    pass
+
+  def ready(self):
+    return True
+
+  def successful(self):
+    return True
+
+
+class _ExceptionWrapper(object):
+  """Used to marshal exception messages back to main process."""
+
+  def __init__(self, msg, exception_type=None):
+    self.msg = msg
+    self.exception_type = exception_type
+
+  def MaybeThrow(self):
+    if self.exception_type:
+      raise getattr(__builtins__,
+                    self.exception_type)('Originally caused by: ' + self.msg)
+
+
+class _FuncWrapper(object):
+  """Runs on the fork()'ed side to catch exceptions and spread *args."""
+
+  def __init__(self, func):
+    global _is_child_process
+    _is_child_process = True
+    self._func = func
+
+  def __call__(self, index, _=None):
+    try:
+      return self._func(*_fork_params[index], **_fork_kwargs)
+    except Exception as e:
+      # Only keep the exception type for builtin exception types or else risk
+      # further marshalling exceptions.
+      exception_type = None
+      if hasattr(__builtins__, type(e).__name__):
+        exception_type = type(e).__name__
+      # multiprocessing is supposed to catch and return exceptions automatically
+      # but it doesn't seem to work properly :(.
+      return _ExceptionWrapper(traceback.format_exc(), exception_type)
+    except:  # pylint: disable=bare-except
+      return _ExceptionWrapper(traceback.format_exc())
+
+
+class _WrappedResult(object):
+  """Allows for host-side logic to be run after child process has terminated.
+
+  * Unregisters associated pool _all_pools.
+  * Raises exception caught by _FuncWrapper.
+  """
+
+  def __init__(self, result, pool=None):
+    self._result = result
+    self._pool = pool
+
+  def get(self):
+    self.wait()
+    value = self._result.get()
+    _CheckForException(value)
+    return value
+
+  def wait(self):
+    self._result.wait()
+    if self._pool:
+      _all_pools.remove(self._pool)
+      self._pool = None
+
+  def ready(self):
+    return self._result.ready()
+
+  def successful(self):
+    return self._result.successful()
+
+
+def _TerminatePools():
+  """Calls .terminate() on all active process pools.
+
+  Not supposed to be necessary according to the docs, but seems to be required
+  when child process throws an exception or Ctrl-C is hit.
+  """
+  global _silence_exceptions
+  _silence_exceptions = True
+  # Child processes cannot have pools, but atexit runs this function because
+  # it was registered before fork()ing.
+  if _is_child_process:
+    return
+
+  def close_pool(pool):
+    try:
+      pool.terminate()
+    except:  # pylint: disable=bare-except
+      pass
+
+  for i, pool in enumerate(_all_pools):
+    # Without calling terminate() on a separate thread, the call can block
+    # forever.
+    thread = threading.Thread(name='Pool-Terminate-{}'.format(i),
+                              target=close_pool,
+                              args=(pool, ))
+    thread.daemon = True
+    thread.start()
+
+
+def _CheckForException(value):
+  if isinstance(value, _ExceptionWrapper):
+    global _silence_exceptions
+    if not _silence_exceptions:
+      value.MaybeThrow()
+      _silence_exceptions = True
+      logging.error('Subprocess raised an exception:\n%s', value.msg)
+    sys.exit(1)
+
+
+def _MakeProcessPool(job_params, **job_kwargs):
+  global _all_pools
+  global _fork_params
+  global _fork_kwargs
+  assert _fork_params is None
+  assert _fork_kwargs is None
+  pool_size = min(len(job_params), multiprocessing.cpu_count())
+  _fork_params = job_params
+  _fork_kwargs = job_kwargs
+  ret = multiprocessing.Pool(pool_size)
+  _fork_params = None
+  _fork_kwargs = None
+  if _all_pools is None:
+    _all_pools = []
+    atexit.register(_TerminatePools)
+  _all_pools.append(ret)
+  return ret
+
+
+def ForkAndCall(func, args):
+  """Runs |func| in a fork'ed process.
+
+  Returns:
+    A Result object (call .get() to get the return value)
+  """
+  if DISABLE_ASYNC:
+    pool = None
+    result = _ImmediateResult(func(*args))
+  else:
+    pool = _MakeProcessPool([args])  # Omit |kwargs|.
+    result = pool.apply_async(_FuncWrapper(func), (0, ))
+    pool.close()
+  return _WrappedResult(result, pool=pool)
+
+
+def BulkForkAndCall(func, arg_tuples, **kwargs):
+  """Calls |func| in a fork'ed process for each set of args within |arg_tuples|.
+
+  Args:
+    kwargs: Common keyword arguments to be passed to |func|.
+
+  Yields the return values in order.
+  """
+  arg_tuples = list(arg_tuples)
+  if not arg_tuples:
+    return
+
+  if DISABLE_ASYNC:
+    for args in arg_tuples:
+      yield func(*args, **kwargs)
+    return
+
+  pool = _MakeProcessPool(arg_tuples, **kwargs)
+  wrapped_func = _FuncWrapper(func)
+  try:
+    for result in pool.imap(wrapped_func, xrange(len(arg_tuples))):
+      _CheckForException(result)
+      yield result
+  finally:
+    pool.close()
+    pool.join()
+    _all_pools.remove(pool)
diff --git a/build/android/lint/suppressions.xml b/build/android/lint/suppressions.xml
index bf7ad48b..fa78146 100644
--- a/build/android/lint/suppressions.xml
+++ b/build/android/lint/suppressions.xml
@@ -275,8 +275,6 @@
     <ignore regexp="Field requires API level .*`android.app.TaskInfo"/>
     <!-- 1: This is for testonly target android_support_chromium_java in android_sdk. -->
     <ignore regexp="third_party/android_sdk/public/extras/chromium/support/src/org/chromium/android/support/PackageManagerWrapper.java"/>
-    <!-- 1: TODO(crbug.com/1081242): Fix -->
-    <ignore regexp="chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiWindowUtils.java"/>
     <!-- 1: TODO(crbug.com/1081243): Fix -->
     <ignore regexp="chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PickerVideoPlayer.java"/>
     <!-- 1: TODO(crbug.com/1082222): Fix -->
diff --git a/build/android/pylib/symbols/deobfuscator.py b/build/android/pylib/symbols/deobfuscator.py
index 42084dd..ffc23b8 100644
--- a/build/android/pylib/symbols/deobfuscator.py
+++ b/build/android/pylib/symbols/deobfuscator.py
@@ -150,7 +150,7 @@
 
       # De-obfuscation is broken.
       if self._num_restarts == _MAX_RESTARTS:
-        return lines
+        raise Exception('Deobfuscation seems broken.')
 
       # Restart any closed Deobfuscators.
       for i, d in enumerate(self._pool):
diff --git a/build/config/ios/ios_sdk.gni b/build/config/ios/ios_sdk.gni
index f3aaf81..822db3a 100644
--- a/build/config/ios/ios_sdk.gni
+++ b/build/config/ios/ios_sdk.gni
@@ -27,12 +27,18 @@
   # not work (see build/BUILDCONFIG.gn for pattern that would cause issue).
   ios_sdk_developer_dir = ""
 
-  # The iOS Code signing identity to use
-  # TODO(GYP), TODO(sdfresne): Consider having a separate
-  # ios_enable_code_signing_flag=<bool> flag to make the invocation clearer.
+  # Control whether codesiging is enabled (ignored for simulator builds).
   ios_enable_code_signing = true
+
+  # Explicitly select the identity to use for codesigning. If defined, must
+  # be set to a non-empty string that will be passed to codesigning. Can be
+  # left unspecified if ios_code_signing_identity_description is used instead.
   ios_code_signing_identity = ""
-  ios_code_signing_identity_description = "iPhone Developer"
+
+  # Pattern used to select the identity to use for codesigning. If defined,
+  # must be a substring of the description of exactly one of the identities by
+  # `security find-identity -v -p codesigning`.
+  ios_code_signing_identity_description = ""
 
   # Prefix for CFBundleIdentifier property of iOS bundles (correspond to the
   # "Organization Identifier" in Xcode). Code signing will fail if no mobile
@@ -68,6 +74,18 @@
 
 use_ios_simulator = current_cpu == "x86" || current_cpu == "x64"
 
+# If codesigning is enabled, use must configure either a codesigning identity
+# or a filter to automatically select the codesigning identity.
+if (!use_ios_simulator && ios_enable_code_signing) {
+  assert(ios_code_signing_identity == "" ||
+             ios_code_signing_identity_description == "",
+         "You should either specify the precise identity to use with " +
+             "ios_code_signing_identity or let the code select an identity " +
+             "automatically (via find_signing_identity.py which use the " +
+             "variable ios_code_signing_identity_description to set the " +
+             "pattern to match the identity to use).")
+}
+
 # Initialize additional_toolchains from additional_target_cpus. Assert here
 # that the list does not contains $target_cpu nor duplicates as this would
 # cause weird errors during the build.
@@ -135,12 +153,15 @@
   # Automatically select a codesigning identity if no identity is configured.
   # This only applies to device build as simulator builds are not signed.
   if (ios_code_signing_identity == "") {
-    ios_code_signing_identity =
-        exec_script("find_signing_identity.py",
-                    [
-                      "--matching-pattern",
-                      ios_code_signing_identity_description,
-                    ],
-                    "string")
+    find_signing_identity_args = []
+    if (ios_code_signing_identity_description != "") {
+      find_signing_identity_args = [
+        "--matching-pattern",
+        ios_code_signing_identity_description,
+      ]
+    }
+    ios_code_signing_identity = exec_script("find_signing_identity.py",
+                                            find_signing_identity_args,
+                                            "trim string")
   }
 }
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index a0a05ca..e18892a 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-0.20200519.0.1
+0.20200519.1.1
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index a0a05ca..e18892a 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-0.20200519.0.1
+0.20200519.1.1
diff --git a/build/fuchsia/qemu_image.py b/build/fuchsia/qemu_image.py
index 5126074..ab5e040a 100644
--- a/build/fuchsia/qemu_image.py
+++ b/build/fuchsia/qemu_image.py
@@ -18,6 +18,7 @@
 
 import logging
 import subprocess
+import tempfile
 import time
 
 
@@ -33,7 +34,9 @@
   """
 
   logging.info('qemu-img starting')
-  p = subprocess.Popen(command)
+  command_output_file = tempfile.NamedTemporaryFile('w')
+  p = subprocess.Popen(command, stdout=command_output_file,
+                       stderr=subprocess.STDOUT)
   start_sec = time.time()
   while p.poll() is None and time.time() - start_sec < QEMU_IMG_TIMEOUT_SEC:
     time.sleep(1)
@@ -41,10 +44,17 @@
   logging.info('qemu-img duration: %f' % float(stop_sec - start_sec))
 
   if p.poll() is None:
+    returncode = None
     p.kill()
-    return None
+    p.wait()
+  else:
+    returncode = p.returncode
 
-  return p.returncode
+  log_level = logging.WARN if returncode else logging.DEBUG
+  for line in open(command_output_file.name, 'r'):
+    logging.log(log_level, 'qemu-img stdout: ' + line.strip())
+
+  return returncode
 
 
 def ExecQemuImgWithRetry(command):
diff --git a/build/fuchsia/runner_exceptions.py b/build/fuchsia/runner_exceptions.py
index 88196e3..03f872e 100644
--- a/build/fuchsia/runner_exceptions.py
+++ b/build/fuchsia/runner_exceptions.py
@@ -67,9 +67,12 @@
       return 73
     return 72
   elif type is subprocess.CalledProcessError:
-    if value.cmd[0] == 'scp':
+    if os.path.basename(value.cmd[0]) == 'scp':
       print('Error: scp operation failed - %s' % str(value))
       return 81
+    if os.path.basename(value.cmd[0]) == 'qemu-img':
+      print('Error: qemu-img fuchsia image generation failed.')
+      return 82
     return 80
   else:
     return 1
diff --git a/cc/paint/image_transfer_cache_entry_unittest.cc b/cc/paint/image_transfer_cache_entry_unittest.cc
index 24a334e..0da5db2 100644
--- a/cc/paint/image_transfer_cache_entry_unittest.cc
+++ b/cc/paint/image_transfer_cache_entry_unittest.cc
@@ -144,7 +144,7 @@
       if (texture.isValid())
         gr_context_->deleteBackendTexture(texture);
     }
-    gr_context_->flush();
+    gr_context_->flushAndSubmit();
     textures_to_free_.clear();
   }
 
diff --git a/cc/paint/oop_pixeltest.cc b/cc/paint/oop_pixeltest.cc
index 6c9f1c1..7533b17 100644
--- a/cc/paint/oop_pixeltest.cc
+++ b/cc/paint/oop_pixeltest.cc
@@ -332,7 +332,7 @@
     raster_source->PlaybackToCanvas(
         canvas, options.content_size, options.full_raster_rect,
         options.playback_rect, raster_transform, settings);
-    surface->flush();
+    surface->flushAndSubmit();
     EXPECT_EQ(gles2_context_provider_->ContextGL()->GetError(),
               static_cast<unsigned>(GL_NO_ERROR));
 
diff --git a/cc/tiles/gpu_image_decode_cache.cc b/cc/tiles/gpu_image_decode_cache.cc
index 4a2d658..b745256 100644
--- a/cc/tiles/gpu_image_decode_cache.cc
+++ b/cc/tiles/gpu_image_decode_cache.cc
@@ -2526,7 +2526,7 @@
   CheckContextLockAcquiredIfNecessary();
   lock_.AssertAcquired();
   for (auto& image : *yuv_images) {
-    image->flush(context_->GrContext());
+    image->flushAndSubmit(context_->GrContext());
   }
   yuv_images->clear();
 }
diff --git a/cc/tiles/gpu_image_decode_cache_perftest.cc b/cc/tiles/gpu_image_decode_cache_perftest.cc
index 7a90390..416cd82 100644
--- a/cc/tiles/gpu_image_decode_cache_perftest.cc
+++ b/cc/tiles/gpu_image_decode_cache_perftest.cc
@@ -157,7 +157,7 @@
       surface->getCanvas()->drawImageRect(decoded_image.image().get(),
                                           SkRect::MakeWH(1024, 2048),
                                           SkRect::MakeWH(614, 1229), &paint);
-      surface->flush();
+      surface->flushAndSubmit();
     }
 
     cache_->DrawWithImageFinished(image, decoded_image);
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index d76f602..4fc24ad 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -431,6 +431,7 @@
     "//third_party/android_data_chart:android_data_chart_java",
     "//third_party/android_deps:android_support_v7_appcompat_java",
     "//third_party/android_deps:androidx_annotation_annotation_java",
+    "//third_party/android_deps:androidx_appcompat_appcompat_resources_java",
     "//third_party/android_deps:androidx_collection_collection_java",
     "//third_party/android_deps:androidx_coordinatorlayout_coordinatorlayout_java",
     "//third_party/android_deps:androidx_core_core_java",
@@ -725,56 +726,99 @@
     "//base:base_java",
     "//base:base_java_test_support",
     "//base:base_junit_test_support",
+    "//base:jni_java",
+    "//chrome/android:update_proto_java",
+    "//chrome/android:usage_stats_proto_java",
     "//chrome/android/features/keyboard_accessory:internal_java",
+    "//chrome/android/features/start_surface/internal:java",
     "//chrome/android/features/tab_ui:java",
     "//chrome/android/webapk/libs/client:client_java",
     "//chrome/android/webapk/libs/common:common_java",
+    "//chrome/android/webapk/libs/common:splash_java",
     "//chrome/android/webapk/test:junit_test_support",
+    "//chrome/browser/android/lifecycle:java",
+    "//chrome/browser/download/android:java",
     "//chrome/browser/flags:flags_junit_tests",
+    "//chrome/browser/flags:java",
     "//chrome/browser/image_fetcher:java",
     "//chrome/browser/optimization_guide/android:junit_tests",
+    "//chrome/browser/performance_hints/android:java",
+    "//chrome/browser/preferences:java",
     "//chrome/browser/preferences:preferences_junit_tests",
+    "//chrome/browser/profiles/android:java",
+    "//chrome/browser/tab:java",
     "//chrome/browser/thumbnail:java",
     "//chrome/browser/ui/android/appmenu/internal:junit",
+    "//chrome/browser/ui/android/favicon:java",
+    "//chrome/browser/ui/android/native_page:java",
+    "//chrome/browser/ui/messages/android:java",
     "//chrome/browser/ui/messages/android:junit",
+    "//chrome/browser/util:java",
+    "//chrome/browser/xsurface:java",
     "//chrome/test/android:chrome_java_test_support",
     "//components/background_task_scheduler:background_task_scheduler_java",
     "//components/bookmarks/common/android:bookmarks_java",
+    "//components/browser_ui/android/bottomsheet:java",
+    "//components/browser_ui/notifications/android:java",
+    "//components/browser_ui/site_settings/android:java",
+    "//components/browser_ui/util/android:java",
     "//components/browser_ui/util/android:junit",
     "//components/browser_ui/webshare/android:junit",
+    "//components/browser_ui/widget/android:java",
     "//components/browser_ui/widget/android:junit",
+    "//components/dom_distiller/core/android:dom_distiller_core_java",
+    "//components/embedder_support/android:browser_context_java",
+    "//components/embedder_support/android:content_view_java",
+    "//components/embedder_support/android:context_menu_java",
     "//components/embedder_support/android:junit_test_support",
+    "//components/embedder_support/android:util_java",
+    "//components/feature_engagement/public:public_java",
+    "//components/feed/core/proto:proto_java",
+    "//components/feed/core/proto:proto_java_v2",
     "//components/minidump_uploader:minidump_uploader_java",
     "//components/module_installer/android:module_installer_java",
     "//components/offline_items_collection/core:core_java",
+    "//components/omnibox/browser:browser_java",
     "//components/page_info/android:java",
     "//components/payments/content/android:java",
     "//components/payments/mojom:mojom_java",
     "//components/schema_org/common:mojom_java",
+    "//components/search_engines/android:java",
+    "//components/security_state/content/android:java",
     "//components/signin/core/browser/android:java",
     "//components/signin/core/browser/android:signin_java_test_support",
+    "//components/signin/public/android:java",
     "//components/sync:sync_java_test_support",
     "//components/sync/android:sync_java",
     "//components/url_formatter/android:url_formatter_java",
     "//components/variations/android:variations_java",
     "//content/public/android:content_java",
+    "//content/public/test/android:content_java_test_support",
     "//mojo/public/java:bindings_java",
     "//mojo/public/java:system_java",
     "//net/android:net_java",
     "//services/media_session/public/cpp/android:media_session_java",
     "//third_party/android_deps:android_support_v7_appcompat_java",
     "//third_party/android_deps:androidx_annotation_annotation_java",
+    "//third_party/android_deps:androidx_collection_collection_java",
     "//third_party/android_deps:androidx_lifecycle_lifecycle_common_java",
     "//third_party/android_deps:androidx_mediarouter_mediarouter_java",
     "//third_party/android_deps:androidx_recyclerview_recyclerview_java",
+    "//third_party/android_deps:androidx_swiperefreshlayout_swiperefreshlayout_java",
+    "//third_party/android_deps:androidx_test_core_java",
+    "//third_party/android_deps:com_google_dagger_dagger_java",
+    "//third_party/android_deps:com_google_protobuf_protobuf_javalite_java",
     "//third_party/android_deps:com_googlecode_java_diff_utils_diffutils_java",
     "//third_party/android_sdk/androidx_browser:androidx_browser_java",
     "//third_party/blink/public:blink_headers_java",
     "//third_party/blink/public/mojom:android_mojo_bindings_java",
     "//third_party/cacheinvalidation:cacheinvalidation_javalib",
+    "//third_party/gif_player:gif_player_java",
     "//third_party/google-truth:google_truth_java",
+    "//third_party/guava:guava_android_java",
     "//third_party/hamcrest:hamcrest_java",
     "//ui/android:ui_java",
+    "//url:gurl_java",
     "//url/mojom:url_mojom_gurl_java",
   ]
 
@@ -859,12 +903,14 @@
     "//chrome/browser/download/android:java",
     "//chrome/browser/enterprise/util:java",
     "//chrome/browser/flags:java",
+    "//chrome/browser/offline_pages/android:java",
     "//chrome/browser/password_manager/android_test_helpers:test_support_java",
     "//chrome/browser/performance_hints/android:java",
     "//chrome/browser/preferences:java",
     "//chrome/browser/profiles/android:java",
     "//chrome/browser/settings:java",
     "//chrome/browser/settings:javatests",
+    "//chrome/browser/settings:test_support_java",
     "//chrome/browser/tab:java",
     "//chrome/browser/thumbnail:java",
     "//chrome/browser/thumbnail:javatests",
@@ -884,14 +930,19 @@
     "//components/browser_ui/android/bottomsheet:java",
     "//components/browser_ui/modaldialog/android:java",
     "//components/browser_ui/modaldialog/android:javatests",
+    "//components/browser_ui/notifications/android:java",
+    "//components/browser_ui/notifications/android:test_support_java",
     "//components/browser_ui/settings/android:java",
     "//components/browser_ui/share/android:javatests",
+    "//components/browser_ui/site_settings/android:java",
     "//components/browser_ui/site_settings/android:javatests",
     "//components/browser_ui/styles/android:java",
     "//components/browser_ui/util/android:java",
     "//components/browser_ui/widget/android:java",
     "//components/browser_ui/widget/android:javatests",
     "//components/browser_ui/widget/android:test_support_java",
+    "//components/content_settings/android:content_settings_enums_java",
+    "//components/content_settings/android:java",
     "//components/crash/android:java",
     "//components/dom_distiller/core/android:dom_distiller_core_java",
     "//components/dom_distiller/core/mojom:mojom_java",
@@ -921,12 +972,14 @@
     "//components/offline_pages/core/prefetch:offline_prefetch_proto_java",
     "//components/omnibox/browser:browser_java",
     "//components/page_info/android:java",
+    "//components/page_info/android:page_info_action_enum_java",
     "//components/paint_preview/player/android:javatests",
     "//components/payments/content/android:java",
     "//components/payments/mojom:mojom_java",
     "//components/permissions/android:java",
     "//components/policy/android:policy_java",
     "//components/policy/android:policy_java_test_support",
+    "//components/query_tiles:public_java",
     "//components/safe_browsing/android:safe_browsing_java",
     "//components/schema_org/common:mojom_java",
     "//components/search_engines/android:java",
@@ -959,10 +1012,13 @@
     "//third_party/android_data_chart:android_data_chart_java",
     "//third_party/android_deps:android_support_v7_appcompat_java",
     "//third_party/android_deps:androidx_annotation_annotation_java",
+    "//third_party/android_deps:androidx_appcompat_appcompat_resources_java",
     "//third_party/android_deps:androidx_collection_collection_java",
     "//third_party/android_deps:androidx_lifecycle_lifecycle_common_java",
     "//third_party/android_deps:androidx_preference_preference_java",
     "//third_party/android_deps:androidx_recyclerview_recyclerview_java",
+    "//third_party/android_deps:androidx_viewpager_viewpager_java",
+    "//url:origin_java",
 
     # TODO (bjoyce): Remove recyclerview_v7 when espresso tests are migrated
     # to androidx.
@@ -1162,14 +1218,20 @@
         "javatests/src/org/chromium/chrome/browser/vr/util/VrTransitionUtils.java",
       ]
 
-      deps = chrome_test_xr_java_deps + [
-               ":chrome_test_util_java",
-               "//chrome/android:chrome_test_xr_java",
-               "//components/module_installer/android:module_installer_java",
-               "//third_party/gvr-android-sdk:controller_test_api_java",
-               "//third_party/gvr-android-sdk:gvr_common_java",
-               "//ui/android:ui_java_test_support",
-             ]
+      deps =
+          chrome_test_xr_java_deps + [
+            ":chrome_test_util_java",
+            "//chrome/android:chrome_test_xr_java",
+            "//components/module_installer/android:module_installer_java",
+            "//components/browser_ui/site_settings/android:java",
+            "//chrome/browser/settings:java",
+            "//components/content_settings/android:content_settings_enums_java",
+            "//chrome/browser/profiles/android:java",
+
+            "//third_party/gvr-android-sdk:controller_test_api_java",
+            "//third_party/gvr-android-sdk:gvr_common_java",
+            "//ui/android:ui_java_test_support",
+          ]
 
       data = [
         "//chrome/android/shared_preference_files/test/",
@@ -3035,20 +3097,24 @@
     "//chrome/android:chrome_java",
     "//chrome/browser/flags:java",
     "//chrome/browser/image_fetcher:java",
+    "//chrome/browser/preferences:java",
     "//chrome/browser/profiles/android:java",
     "//chrome/browser/tab:java",
+    "//chrome/browser/ui/android/favicon:java",
     "//chrome/browser/util:java",
     "//chrome/test/android:chrome_java_test_support",
     "//components/embedder_support/android:context_menu_java",
     "//components/embedder_support/android:util_java",
     "//components/omnibox/browser:browser_java",
     "//components/payments/content/android:java",
+    "//components/payments/mojom:mojom_java",
     "//components/search_engines/android:java",
     "//components/security_state/content/android:java",
     "//components/security_state/core:security_state_enums_java",
     "//content/public/android:content_java",
     "//content/public/test/android:android_test_message_pump_support_java",
     "//content/public/test/android:content_java_test_support",
+    "//third_party/android_deps:androidx_collection_collection_java",
     "//third_party/android_deps:com_google_code_findbugs_jsr305_java",
     "//third_party/blink/public:blink_headers_java",
     "//third_party/blink/public/mojom:android_mojo_bindings_java",
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index e7d4220..c312c53 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -895,7 +895,6 @@
   "java/src/org/chromium/chrome/browser/media/PictureInPictureActivity.java",
   "java/src/org/chromium/chrome/browser/media/PictureInPictureController.java",
   "java/src/org/chromium/chrome/browser/media/remote/RecordCastAction.java",
-  "java/src/org/chromium/chrome/browser/media/ui/MediaButtonReceiver.java",
   "java/src/org/chromium/chrome/browser/media/ui/MediaImageCallback.java",
   "java/src/org/chromium/chrome/browser/media/ui/MediaImageManager.java",
   "java/src/org/chromium/chrome/browser/media/ui/MediaNotificationInfo.java",
diff --git a/chrome/android/features/autofill_assistant/BUILD.gn b/chrome/android/features/autofill_assistant/BUILD.gn
index 7e20e80..2458468 100644
--- a/chrome/android/features/autofill_assistant/BUILD.gn
+++ b/chrome/android/features/autofill_assistant/BUILD.gn
@@ -16,6 +16,7 @@
     "//base:base_java",
     "//chrome/android:chrome_java",
     "//third_party/android_deps:android_support_v7_appcompat_java",
+    "//third_party/android_deps:androidx_appcompat_appcompat_resources_java",
   ]
 
   sources = [ "java/src/org/chromium/chrome/browser/" +
@@ -51,6 +52,7 @@
     "//mojo/public/java:bindings_java",
     "//third_party/android_deps:android_support_v7_appcompat_java",
     "//third_party/android_deps:androidx_annotation_annotation_java",
+    "//third_party/android_deps:androidx_appcompat_appcompat_resources_java",
     "//third_party/android_deps:androidx_collection_collection_java",
     "//third_party/android_deps:androidx_coordinatorlayout_coordinatorlayout_java",
     "//third_party/android_deps:androidx_core_core_java",
@@ -273,6 +275,8 @@
     "//chrome/browser/image_fetcher:java",
     "//chrome/browser/password_manager/android_test_helpers:test_support_java",
     "//chrome/browser/preferences:java",
+    "//chrome/browser/tab:java",
+    "//chrome/browser/util:java",
     "//chrome/test/android:chrome_java_test_support",
     "//components/autofill_assistant/browser:proto_java",
     "//components/browser_ui/android/bottomsheet:java",
@@ -290,6 +294,7 @@
     "//third_party/junit",
     "//third_party/mockito:mockito_java",
     "//ui/android:ui_full_java",
+    "//url:gurl_java",
   ]
 
   data = [ "//components/test/data/autofill_assistant/" ]
diff --git a/chrome/android/features/autofill_assistant/java/DEPS b/chrome/android/features/autofill_assistant/java/DEPS
index 37154db..2f948de 100644
--- a/chrome/android/features/autofill_assistant/java/DEPS
+++ b/chrome/android/features/autofill_assistant/java/DEPS
@@ -1,5 +1,6 @@
 include_rules = [
   "+chrome/browser/image_fetcher",
+  "+chrome/browser/profiles/android/java",
   "+chrome/browser/ui/messages/android/java",
   "+components/autofill/android",
   "+components/browser_ui/widget/android",
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantCollectUserDataBinder.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantCollectUserDataBinder.java
index 94de1a8..ef14834 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantCollectUserDataBinder.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantCollectUserDataBinder.java
@@ -23,6 +23,7 @@
 import org.chromium.chrome.browser.payments.BasicCardUtils;
 import org.chromium.chrome.browser.payments.CardEditor;
 import org.chromium.chrome.browser.payments.ContactEditor;
+import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.components.payments.MethodStrings;
 import org.chromium.content_public.browser.UiThreadTaskTraits;
 import org.chromium.content_public.browser.WebContents;
@@ -647,6 +648,7 @@
             return true;
         }
 
+        Profile profile = Profile.fromWebContents(webContents);
         if (shouldShowContactDetails(model)) {
             ContactEditor contactEditor =
                     new ContactEditor(model.get(AssistantCollectUserDataModel.REQUEST_NAME),
@@ -654,14 +656,14 @@
                             model.get(AssistantCollectUserDataModel.REQUEST_EMAIL),
                             !webContents.isIncognito());
             contactEditor.setEditorDialog(new EditorDialog(view.mActivity,
-                    /*deleteRunnable =*/null));
+                    /*deleteRunnable =*/null, profile));
             view.mContactDetailsSection.setEditor(contactEditor);
         }
 
         AddressEditor addressEditor = new AddressEditor(AddressEditor.Purpose.PAYMENT_REQUEST,
                 /* saveToDisk= */ !webContents.isIncognito());
         addressEditor.setEditorDialog(new EditorDialog(view.mActivity,
-                /*deleteRunnable =*/null));
+                /*deleteRunnable =*/null, profile));
 
         CardEditor cardEditor = new CardEditor(webContents, addressEditor,
                 /* includeOrgLabel= */ false, /* observerForTest= */ null);
@@ -673,7 +675,7 @@
         }
 
         EditorDialog cardEditorDialog = new EditorDialog(view.mActivity,
-                /*deleteRunnable =*/null);
+                /*deleteRunnable =*/null, profile);
         if (ChromeVersionInfo.isBetaBuild() || ChromeVersionInfo.isStableBuild()) {
             cardEditorDialog.disableScreenshots();
         }
diff --git a/chrome/android/features/keyboard_accessory/BUILD.gn b/chrome/android/features/keyboard_accessory/BUILD.gn
index 235b10d..fee568c 100644
--- a/chrome/android/features/keyboard_accessory/BUILD.gn
+++ b/chrome/android/features/keyboard_accessory/BUILD.gn
@@ -100,11 +100,26 @@
 
   deps = [
     ":internal_java",
+    "//base:base_java",
     "//base:base_java_test_support",
     "//base:base_junit_test_support",
+    "//chrome/android:chrome_java",
     "//chrome/android:chrome_test_util_java",
+    "//chrome/android/features/keyboard_accessory/public:public_java",
+    "//chrome/browser/flags:java",
+    "//chrome/browser/tab:java",
+    "//chrome/test/android:chrome_java_test_support",
+    "//components/autofill/android:autofill_java",
+    "//components/embedder_support/android:content_view_java",
     "//components/module_installer/android:module_installer_java",
+    "//content/public/android:content_java",
+    "//third_party/android_deps:androidx_recyclerview_recyclerview_java",
+    "//third_party/android_deps:com_google_android_material_material_java",
+    "//third_party/hamcrest:hamcrest_java",
     "//third_party/junit",
     "//third_party/mockito:mockito_java",
+    "//ui/android:ui_full_java",
+    "//ui/android:ui_java_test_support",
+    "//ui/android:ui_utils_java",
   ]
 }
diff --git a/chrome/android/features/keyboard_accessory/internal/BUILD.gn b/chrome/android/features/keyboard_accessory/internal/BUILD.gn
index 5bef9e6..827b821 100644
--- a/chrome/android/features/keyboard_accessory/internal/BUILD.gn
+++ b/chrome/android/features/keyboard_accessory/internal/BUILD.gn
@@ -26,6 +26,7 @@
     "//components/feature_engagement/public:public_java",
     "//content/public/android:content_java",
     "//third_party/android_deps:android_support_v7_appcompat_java",
+    "//third_party/android_deps:androidx_appcompat_appcompat_resources_java",
     "//third_party/android_deps:androidx_recyclerview_recyclerview_java",
     "//third_party/android_deps:androidx_viewpager_viewpager_java",
     "//third_party/android_deps:com_google_android_material_material_java",
diff --git a/chrome/android/features/media_router/BUILD.gn b/chrome/android/features/media_router/BUILD.gn
index 8b99219..4721796 100644
--- a/chrome/android/features/media_router/BUILD.gn
+++ b/chrome/android/features/media_router/BUILD.gn
@@ -122,8 +122,13 @@
 
   deps = [
     ":java",
+    "$google_play_services_package:google_play_services_basement_java",
+    "$google_play_services_package:google_play_services_cast_framework_java",
+    "$google_play_services_package:google_play_services_cast_java",
+    "//base:base_java",
     "//base:base_java_test_support",
     "//base:base_junit_test_support",
+    "//third_party/android_deps:androidx_mediarouter_mediarouter_java",
   ]
 }
 
diff --git a/chrome/android/features/tab_ui/BUILD.gn b/chrome/android/features/tab_ui/BUILD.gn
index 89dcb33..8da48ff 100644
--- a/chrome/android/features/tab_ui/BUILD.gn
+++ b/chrome/android/features/tab_ui/BUILD.gn
@@ -199,6 +199,7 @@
     "//content/public/android:content_java_resources",
     "//third_party/android_deps:android_support_v7_appcompat_java",
     "//third_party/android_deps:androidx_annotation_annotation_java",
+    "//third_party/android_deps:androidx_appcompat_appcompat_resources_java",
     "//third_party/android_deps:androidx_core_core_java",
     "//third_party/android_deps:androidx_legacy_legacy_support_v13_java",
     "//third_party/android_deps:androidx_lifecycle_lifecycle_common_java",
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/v2/FeedStreamSurface.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/v2/FeedStreamSurface.java
index c872d7d..e911a57 100644
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/v2/FeedStreamSurface.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/v2/FeedStreamSurface.java
@@ -337,6 +337,8 @@
         // TODO(jianli): Call this function at the appropriate time.
         void reportStreamScrolled(
                 long nativeFeedStreamSurface, FeedStreamSurface caller, int distanceDp);
+        // TODO(jianli): Call this function at the appropriate time.
+        void reportStreamScrollStart(long nativeFeedStreamSurface, FeedStreamSurface caller);
         void loadMore(long nativeFeedStreamSurface, FeedStreamSurface caller);
         void processThereAndBackAgain(
                 long nativeFeedStreamSurface, FeedStreamSurface caller, byte[] data);
diff --git a/chrome/android/java/AndroidManifest.xml b/chrome/android/java/AndroidManifest.xml
index 472536e..8cb812c 100644
--- a/chrome/android/java/AndroidManifest.xml
+++ b/chrome/android/java/AndroidManifest.xml
@@ -1173,24 +1173,6 @@
             </intent-filter>
         </service>
 
-
-        <receiver android:name="org.chromium.chrome.browser.media.ui.MediaNotificationManager$PlaybackMediaButtonReceiver">
-            <intent-filter>
-                <action android:name="android.intent.action.MEDIA_BUTTON" />
-            </intent-filter>
-        </receiver>
-        <receiver android:name="org.chromium.chrome.browser.media.ui.MediaNotificationManager$PresentationMediaButtonReceiver">
-            <intent-filter>
-                <action android:name="android.intent.action.MEDIA_BUTTON" />
-            </intent-filter>
-        </receiver>
-        <receiver android:name="org.chromium.chrome.browser.media.ui.MediaNotificationManager$CastMediaButtonReceiver">
-            <intent-filter>
-                <action android:name="android.intent.action.MEDIA_BUTTON" />
-            </intent-filter>
-        </receiver>
-
-
         <service android:name="org.chromium.chrome.browser.tracing.TracingNotificationService"
             android:exported="false"/>
 
diff --git a/chrome/android/java/monochrome_public_bundle__base_bundle_module.AndroidManifest.expected b/chrome/android/java/monochrome_public_bundle__base_bundle_module.AndroidManifest.expected
index 5b34e69..6472f9a 100644
--- a/chrome/android/java/monochrome_public_bundle__base_bundle_module.AndroidManifest.expected
+++ b/chrome/android/java/monochrome_public_bundle__base_bundle_module.AndroidManifest.expected
@@ -968,24 +968,6 @@
           android:name="android.appwidget.provider"
           android:resource="@xml/search_widget_info"/>
     </receiver>
-    <receiver
-        android:name="org.chromium.chrome.browser.media.ui.MediaNotificationManager$CastMediaButtonReceiver">
-      <intent-filter>
-        <action android:name="android.intent.action.MEDIA_BUTTON"/>
-      </intent-filter>
-    </receiver>
-    <receiver
-        android:name="org.chromium.chrome.browser.media.ui.MediaNotificationManager$PlaybackMediaButtonReceiver">
-      <intent-filter>
-        <action android:name="android.intent.action.MEDIA_BUTTON"/>
-      </intent-filter>
-    </receiver>
-    <receiver
-        android:name="org.chromium.chrome.browser.media.ui.MediaNotificationManager$PresentationMediaButtonReceiver">
-      <intent-filter>
-        <action android:name="android.intent.action.MEDIA_BUTTON"/>
-      </intent-filter>
-    </receiver>
     <receiver android:name="org.chromium.chrome.browser.services.AccountsChangedReceiver">
       <intent-filter>
         <action android:name="android.accounts.LOGIN_ACCOUNTS_CHANGED"/>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/LaunchIntentDispatcher.java b/chrome/android/java/src/org/chromium/chrome/browser/LaunchIntentDispatcher.java
index b024ff3..aaa2c83 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/LaunchIntentDispatcher.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/LaunchIntentDispatcher.java
@@ -276,9 +276,12 @@
             }
         }
 
+        boolean isIntentSenderChrome = IntentHandler.wasIntentSenderChrome(intent);
+
         // Use a custom tab with a unique theme for payment handlers.
         if (intent.getIntExtra(CustomTabIntentDataProvider.EXTRA_UI_TYPE, CustomTabsUiType.DEFAULT)
-                == CustomTabsUiType.PAYMENT_REQUEST) {
+                        == CustomTabsUiType.PAYMENT_REQUEST
+                && isIntentSenderChrome) {
             newIntent.setClassName(context, PaymentHandlerActivity.class.getName());
         }
 
@@ -314,7 +317,7 @@
         // If the previous caller was not Chrome, but added EXTRA_IS_OPENED_BY_CHROME
         // for malicious purpose, remove it. The new intent will be sent by Chrome, but was not
         // sent by Chrome initially.
-        if (!IntentHandler.wasIntentSenderChrome(intent)) {
+        if (!isIntentSenderChrome) {
             IntentUtils.safeRemoveExtra(
                     newIntent, CustomTabIntentDataProvider.EXTRA_IS_OPENED_BY_CHROME);
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskBridge.java
index 5eb8a1c..d0448916 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskBridge.java
@@ -25,10 +25,9 @@
 
     public CardUnmaskBridge(long nativeCardUnmaskPromptViewAndroid, String title,
             String instructions, String confirmButtonLabel, int iconId,
-            boolean shouldRequestExpirationDate, boolean canStoreLocally,
-            boolean defaultToStoringLocally, boolean shouldOfferWebauthn,
-            boolean defaultUseScreenlockChecked, long successMessageDurationMilliseconds,
-            WindowAndroid windowAndroid) {
+            boolean shouldRequestExpirationDate, boolean defaultToStoringLocally,
+            boolean shouldOfferWebauthn, boolean defaultUseScreenlockChecked,
+            long successMessageDurationMilliseconds, WindowAndroid windowAndroid) {
         mNativeCardUnmaskPromptViewAndroid = nativeCardUnmaskPromptViewAndroid;
         Activity activity = windowAndroid.getActivity().get();
         if (activity == null) {
@@ -38,7 +37,7 @@
             new Handler().post(() -> dismissed());
         } else {
             mCardUnmaskPrompt = new CardUnmaskPrompt(activity, this, title, instructions,
-                    confirmButtonLabel, iconId, shouldRequestExpirationDate, canStoreLocally,
+                    confirmButtonLabel, iconId, shouldRequestExpirationDate,
                     defaultToStoringLocally, shouldOfferWebauthn, defaultUseScreenlockChecked,
                     successMessageDurationMilliseconds);
         }
@@ -47,14 +46,12 @@
     @CalledByNative
     private static CardUnmaskBridge create(long nativeUnmaskPrompt, String title,
             String instructions, String confirmButtonLabel, int iconId,
-            boolean shouldRequestExpirationDate, boolean canStoreLocally,
-            boolean defaultToStoringLocally, boolean shouldOfferWebauthn,
-            boolean defaultUseScreenlockChecked, long successMessageDurationMilliseconds,
-            WindowAndroid windowAndroid) {
+            boolean shouldRequestExpirationDate, boolean defaultToStoringLocally,
+            boolean shouldOfferWebauthn, boolean defaultUseScreenlockChecked,
+            long successMessageDurationMilliseconds, WindowAndroid windowAndroid) {
         return new CardUnmaskBridge(nativeUnmaskPrompt, title, instructions, confirmButtonLabel,
-                iconId, shouldRequestExpirationDate, canStoreLocally, defaultToStoringLocally,
-                shouldOfferWebauthn, defaultUseScreenlockChecked,
-                successMessageDurationMilliseconds, windowAndroid);
+                iconId, shouldRequestExpirationDate, defaultToStoringLocally, shouldOfferWebauthn,
+                defaultUseScreenlockChecked, successMessageDurationMilliseconds, windowAndroid);
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskPrompt.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskPrompt.java
index da7ae9ae..9c5f5ce2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskPrompt.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskPrompt.java
@@ -145,9 +145,9 @@
 
     public CardUnmaskPrompt(Context context, CardUnmaskPromptDelegate delegate, String title,
             String instructions, String confirmButtonLabel, int drawableId,
-            boolean shouldRequestExpirationDate, boolean canStoreLocally,
-            boolean defaultToStoringLocally, boolean shouldOfferWebauthn,
-            boolean defaultUseScreenlockChecked, long successMessageDurationMilliseconds) {
+            boolean shouldRequestExpirationDate, boolean defaultToStoringLocally,
+            boolean shouldOfferWebauthn, boolean defaultUseScreenlockChecked,
+            long successMessageDurationMilliseconds) {
         mDelegate = delegate;
 
         LayoutInflater inflater = LayoutInflater.from(context);
@@ -165,16 +165,15 @@
         mNewCardLink.setOnClickListener(this);
         mErrorMessage = (TextView) v.findViewById(R.id.error_message);
         mStoreLocallyCheckbox = (CheckBox) v.findViewById(R.id.store_locally_checkbox);
-        mStoreLocallyCheckbox.setChecked(canStoreLocally && defaultToStoringLocally);
         mUseScreenlockCheckbox = (CheckBox) v.findViewById(R.id.use_screenlock_checkbox);
         mUseScreenlockCheckbox.setChecked(defaultUseScreenlockChecked);
-        if (canStoreLocally || !shouldOfferWebauthn) {
+        if (!shouldOfferWebauthn) {
             mUseScreenlockCheckbox.setVisibility(View.GONE);
             mUseScreenlockCheckbox.setChecked(false);
         }
         mStoreLocallyTooltipIcon = (ImageView) v.findViewById(R.id.store_locally_tooltip_icon);
         mStoreLocallyTooltipIcon.setOnClickListener(this);
-        if (!canStoreLocally) v.findViewById(R.id.store_locally_container).setVisibility(View.GONE);
+        v.findViewById(R.id.store_locally_container).setVisibility(View.GONE);
         mControlsContainer = (ViewGroup) v.findViewById(R.id.controls_container);
         mVerificationOverlay = v.findViewById(R.id.verification_overlay);
         mVerificationProgressBar = (ProgressBar) v.findViewById(R.id.verification_progress_bar);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/prefeditor/EditorDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/prefeditor/EditorDialog.java
index 0373fdf..2bff411 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/prefeditor/EditorDialog.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/prefeditor/EditorDialog.java
@@ -104,13 +104,15 @@
     @Nullable
     private Runnable mDeleteRunnable;
     private boolean mIsDismissed;
+    private Profile mProfile;
     /**
      * Builds the editor dialog.
      *
      * @param activity        The activity on top of which the UI should be displayed.
      * @param deleteRunnable  The runnable that when called will delete the profile.
+     * @param profile         The current profile that creates EditorDialog.
      */
-    public EditorDialog(Activity activity, Runnable deleteRunnable) {
+    public EditorDialog(Activity activity, Runnable deleteRunnable, Profile profile) {
         super(activity, R.style.Theme_Chromium_Fullscreen);
         // Sets transparent background for animating content view.
         getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
@@ -163,6 +165,7 @@
 
         mCardNumberFormatter = new CreditCardNumberFormattingTextWatcher();
         mDeleteRunnable = deleteRunnable;
+        mProfile = profile;
     }
 
     /** Prevents screenshots of this editor. */
@@ -172,14 +175,10 @@
         getWindow().setAttributes(attributes);
     }
 
-    /** Launches the Autofill help page on top of the current Context. */
-    public static void launchAutofillHelpPage(Context context) {
-        // TODO(https://crbug.com/1041781): Use the current profile (i.e., regular profile or
-        // incognito profile) instead of always using regular profile. It is wrong and need to be
-        // fixed not to cause data leakage from incognito to regular profile.
+    /** Launches the Autofill help page on top of the current Context and current Profile. */
+    public static void launchAutofillHelpPage(Context context, Profile profile) {
         HelpAndFeedback.getInstance().show((Activity) context,
-                context.getString(R.string.help_context_autofill),
-                Profile.getLastUsedRegularProfile(), null);
+                context.getString(R.string.help_context_autofill), profile, null);
     }
 
     /**
@@ -206,7 +205,7 @@
                     mDeleteRunnable.run();
                     animateOutDialog();
                 } else if (item.getItemId() == R.id.help_menu_id) {
-                    launchAutofillHelpPage(mContext);
+                    launchAutofillHelpPage(mContext, mProfile);
                 }
                 return true;
             }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillEditorBase.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillEditorBase.java
index 60f5f90..3a39dc4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillEditorBase.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillEditorBase.java
@@ -28,6 +28,7 @@
 
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.autofill.prefeditor.EditorDialog;
+import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.components.browser_ui.settings.SettingsUtils;
 import org.chromium.components.browser_ui.widget.FadingEdgeScrollView;
 
@@ -93,7 +94,7 @@
             getActivity().finish();
             return true;
         } else if (item.getItemId() == R.id.help_menu_id) {
-            EditorDialog.launchAutofillHelpPage(getActivity());
+            EditorDialog.launchAutofillHelpPage(getActivity(), Profile.getLastUsedRegularProfile());
             return true;
         }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillProfilesFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillProfilesFragment.java
index 9c39293d..3a13b0a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillProfilesFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillProfilesFragment.java
@@ -24,6 +24,7 @@
 import org.chromium.chrome.browser.payments.AddressEditor;
 import org.chromium.chrome.browser.payments.AutofillAddress;
 import org.chromium.chrome.browser.payments.SettingsAutofillAndPaymentsObserver;
+import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.settings.ChromeManagedPreferenceDelegate;
 import org.chromium.components.browser_ui.settings.ChromeSwitchPreference;
 
@@ -177,7 +178,7 @@
             }
         };
 
-        return new EditorDialog(getActivity(), runnable);
+        return new EditorDialog(getActivity(), runnable, Profile.getLastUsedRegularProfile());
     }
 
     private void editAddress(EditorDialog dialog, AutofillAddress autofillAddress) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaButtonReceiver.java b/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaButtonReceiver.java
deleted file mode 100644
index 437468f..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaButtonReceiver.java
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.media.ui;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.view.KeyEvent;
-
-import org.chromium.base.Log;
-import org.chromium.chrome.browser.notifications.ForegroundServiceUtils;
-
-/**
- * MediaButtonReceiver is a basic BroadcastReceiver class that receives
- * ACTION_MEDIA_BUTTON from a MediaSessionCompat. It then forward these intents
- * to the service listening to them.
- * This is there for backward compatibility with JB_MR0 and JB_MR1.
- */
-public abstract class MediaButtonReceiver extends BroadcastReceiver {
-    public abstract Class<?> getServiceClass();
-
-    private static final String TAG = "MediaButtonReceiver";
-
-    @Override
-    public void onReceive(Context context, Intent intent) {
-        if (intent == null || !Intent.ACTION_MEDIA_BUTTON.equals(intent.getAction())
-                || !intent.hasExtra(Intent.EXTRA_KEY_EVENT)) {
-            return;
-        }
-
-        Log.i(TAG, "Receive broadcast message, starting foreground service");
-
-        KeyEvent event = (KeyEvent) intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
-        if (event == null) {
-            Log.i(TAG, "no event");
-        } else {
-            Log.i(TAG, "action: " + event.getAction() + ", keycode: " + event.getKeyCode());
-        }
-
-        intent.setClass(context, getServiceClass());
-        ForegroundServiceUtils.getInstance().startForegroundService(intent);
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaNotificationManager.java b/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaNotificationManager.java
index dca8b19..4e9720a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaNotificationManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaNotificationManager.java
@@ -7,7 +7,6 @@
 import android.app.PendingIntent;
 import android.app.Service;
 import android.content.BroadcastReceiver;
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -27,7 +26,6 @@
 import android.support.v4.media.session.PlaybackStateCompat;
 import android.text.TextUtils;
 import android.util.SparseArray;
-import android.view.KeyEvent;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -102,15 +100,13 @@
 
         sMapNotificationIdToOptions.put(PlaybackListenerService.NOTIFICATION_ID,
                 new NotificationOptions(PlaybackListenerService.class,
-                        PlaybackMediaButtonReceiver.class,
                         NotificationConstants.GROUP_MEDIA_PLAYBACK));
         sMapNotificationIdToOptions.put(PresentationListenerService.NOTIFICATION_ID,
                 new NotificationOptions(PresentationListenerService.class,
-                        PresentationMediaButtonReceiver.class,
                         NotificationConstants.GROUP_MEDIA_PRESENTATION));
         sMapNotificationIdToOptions.put(CastListenerService.NOTIFICATION_ID,
-                new NotificationOptions(CastListenerService.class, CastMediaButtonReceiver.class,
-                        NotificationConstants.GROUP_MEDIA_REMOTE));
+                new NotificationOptions(
+                        CastListenerService.class, NotificationConstants.GROUP_MEDIA_REMOTE));
     }
 
     private final NotificationUmaTracker mNotificationUmaTracker;
@@ -275,13 +271,10 @@
     @VisibleForTesting
     static class NotificationOptions {
         public Class<?> serviceClass;
-        public Class<?> receiverClass;
         public String groupName;
 
-        public NotificationOptions(
-                Class<?> serviceClass, Class<?> receiverClass, String groupName) {
+        public NotificationOptions(Class<?> serviceClass, String groupName) {
             this.serviceClass = serviceClass;
-            this.receiverClass = receiverClass;
             this.groupName = groupName;
         }
     }
@@ -397,47 +390,7 @@
         void processAction(Intent intent, MediaNotificationManager manager) {
             String action = intent.getAction();
 
-            // Before Android L, instead of using the MediaSession callback, the system will fire
-            // ACTION_MEDIA_BUTTON intents which stores the information about the key event.
-            if (Intent.ACTION_MEDIA_BUTTON.equals(action)) {
-                KeyEvent event = (KeyEvent) intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
-                if (event == null) return;
-                if (event.getAction() != KeyEvent.ACTION_DOWN) return;
-                switch (event.getKeyCode()) {
-                    case KeyEvent.KEYCODE_MEDIA_PLAY:
-                        manager.onPlay(
-                                MediaNotificationListener.ACTION_SOURCE_MEDIA_SESSION);
-                        break;
-                    case KeyEvent.KEYCODE_MEDIA_PAUSE:
-                        manager.onPause(
-                                MediaNotificationListener.ACTION_SOURCE_MEDIA_SESSION);
-                        break;
-                    case KeyEvent.KEYCODE_HEADSETHOOK:
-                    case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
-                        if (manager.mMediaNotificationInfo.isPaused) {
-                            manager.onPlay(MediaNotificationListener.ACTION_SOURCE_MEDIA_SESSION);
-                        } else {
-                            manager.onPause(
-                                    MediaNotificationListener.ACTION_SOURCE_MEDIA_SESSION);
-                        }
-                        break;
-                    case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
-                        manager.onMediaSessionAction(MediaSessionAction.PREVIOUS_TRACK);
-                        break;
-                    case KeyEvent.KEYCODE_MEDIA_NEXT:
-                        manager.onMediaSessionAction(MediaSessionAction.NEXT_TRACK);
-                        break;
-                    case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
-                        manager.onMediaSessionAction(MediaSessionAction.SEEK_FORWARD);
-                        break;
-                    case KeyEvent.KEYCODE_MEDIA_REWIND:
-                        manager.onMediaSessionAction(MediaSessionAction.SEEK_BACKWARD);
-                        break;
-                    default:
-                        break;
-                }
-            } else if (ACTION_STOP.equals(action)
-                    || ACTION_SWIPE.equals(action)
+            if (ACTION_STOP.equals(action) || ACTION_SWIPE.equals(action)
                     || ACTION_CANCEL.equals(action)) {
                 manager.onStop(
                         MediaNotificationListener.ACTION_SOURCE_MEDIA_NOTIFICATION);
@@ -522,38 +475,6 @@
         }
     }
 
-    // Three classes to specify the right notification id in the intent.
-
-    /**
-     * This class is used internally but have to be public to be able to launch the service.
-     */
-    public static final class PlaybackMediaButtonReceiver extends MediaButtonReceiver {
-        @Override
-        public Class<?> getServiceClass() {
-            return PlaybackListenerService.class;
-        }
-    }
-
-    /**
-     * This class is used internally but have to be public to be able to launch the service.
-     */
-    public static final class PresentationMediaButtonReceiver extends MediaButtonReceiver {
-        @Override
-        public Class<?> getServiceClass() {
-            return PresentationListenerService.class;
-        }
-    }
-
-    /**
-     * This class is used internally but have to be public to be able to launch the service.
-     */
-    public static final class CastMediaButtonReceiver extends MediaButtonReceiver {
-        @Override
-        public Class<?> getServiceClass() {
-            return CastListenerService.class;
-        }
-    }
-
     @VisibleForTesting
     Intent createIntent() {
         Class<?> serviceClass = sMapNotificationIdToOptions.get(mNotificationId).serviceClass;
@@ -566,13 +487,6 @@
         return PendingIntent.getService(getContext(), 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
     }
 
-    private Class<?> getButtonReceiverClass() {
-        Class<?> receiverClass = sMapNotificationIdToOptions.get(mNotificationId).receiverClass;
-
-        assert receiverClass != null;
-        return receiverClass;
-    }
-
     // Returns the notification group name used to prevent automatic grouping.
     private String getNotificationGroupName() {
         String groupName = sMapNotificationIdToOptions.get(mNotificationId).groupName;
@@ -880,7 +794,6 @@
         manager.cancel(mMediaNotificationInfo.id);
 
         if (mMediaSession != null) {
-            mMediaSession.setMediaButtonReceiver(null);
             mMediaSession.setCallback(null);
             mMediaSession.setActive(false);
             mMediaSession.release();
@@ -1091,25 +1004,9 @@
     private MediaSessionCompat createMediaSession() {
         Context context = getContext();
         MediaSessionCompat mediaSession =
-                new MediaSessionCompat(context, context.getString(R.string.app_name),
-                        new ComponentName(context, getButtonReceiverClass()), null);
-        mediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS
-                | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
+                new MediaSessionCompat(context, context.getString(R.string.app_name));
         mediaSession.setCallback(mMediaSessionCallback);
-
-        // TODO(mlamouri): the following code is to work around a bug that hopefully
-        // MediaSessionCompat will handle directly. see b/24051980.
-        try {
-            mediaSession.setActive(true);
-        } catch (NullPointerException e) {
-            // Some versions of KitKat do not support AudioManager.registerMediaButtonIntent
-            // with a PendingIntent. They will throw a NullPointerException, in which case
-            // they should be able to activate a MediaSessionCompat with only transport
-            // controls.
-            mediaSession.setActive(false);
-            mediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
-            mediaSession.setActive(true);
-        }
+        mediaSession.setActive(true);
         return mediaSession;
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiWindowUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiWindowUtils.java
index 8ac9c8d..b11fa12 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiWindowUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiWindowUtils.java
@@ -228,7 +228,10 @@
      * Determines the name of an activity from its {@link AppTask}.
      * @param task The AppTask to get the name of.
      */
+    @TargetApi(Build.VERSION_CODES.M)
     public static String getActivityNameFromTask(AppTask task) {
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return "";
+
         if (task.getTaskInfo() == null || task.getTaskInfo().baseActivity == null) return "";
 
         String baseActivity = task.getTaskInfo().baseActivity.getClassName();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentAppFactoryParams.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentAppFactoryParams.java
index 2764b8a..0968afe 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentAppFactoryParams.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentAppFactoryParams.java
@@ -6,7 +6,7 @@
 
 import androidx.annotation.Nullable;
 
-import org.chromium.components.payments.PaymentApp.PaymentRequestUpdateEventListener;
+import org.chromium.components.payments.PaymentRequestUpdateEventListener;
 import org.chromium.content_public.browser.RenderFrameHost;
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.payments.mojom.PaymentDetailsModifier;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
index b848449..f73ee27e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
@@ -48,9 +48,6 @@
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.tabmodel.TabModelSelectorObserver;
 import org.chromium.chrome.browser.ui.favicon.FaviconHelper;
-import org.chromium.chrome.browser.widget.ScrimView;
-import org.chromium.chrome.browser.widget.ScrimView.EmptyScrimObserver;
-import org.chromium.chrome.browser.widget.ScrimView.ScrimParams;
 import org.chromium.components.autofill.Completable;
 import org.chromium.components.autofill.EditableOption;
 import org.chromium.components.embedder_support.util.UrlConstants;
@@ -64,7 +61,7 @@
 import org.chromium.components.payments.PaymentApp;
 import org.chromium.components.payments.PaymentDetailsConverter;
 import org.chromium.components.payments.PaymentHandlerHost;
-import org.chromium.components.payments.PaymentHandlerHost.PaymentHandlerHostDelegate;
+import org.chromium.components.payments.PaymentRequestUpdateEventListener;
 import org.chromium.components.payments.PaymentValidator;
 import org.chromium.components.payments.UrlUtil;
 import org.chromium.components.security_state.SecurityStateModel;
@@ -115,12 +112,11 @@
  */
 public class PaymentRequestImpl
         implements PaymentRequest, PaymentRequestUI.Client, PaymentAppFactoryDelegate,
-                   PaymentAppFactoryParams, PaymentApp.PaymentRequestUpdateEventListener,
+                   PaymentAppFactoryParams, PaymentRequestUpdateEventListener,
                    PaymentApp.AbortCallback, PaymentApp.InstrumentDetailsCallback,
                    PaymentResponseHelper.PaymentResponseRequesterDelegate, FocusChangedObserver,
                    NormalizedAddressRequestDelegate, SettingsAutofillAndPaymentsObserver.Observer,
-                   PaymentHandlerHostDelegate, PaymentDetailsConverter.MethodChecker,
-                   PaymentHandlerUiObserver {
+                   PaymentDetailsConverter.MethodChecker, PaymentHandlerUiObserver {
     /**
      * A delegate to ask questions about the system, that allows tests to inject behaviour without
      * having to modify the entire system. This partially mirrors a similar C++
@@ -897,14 +893,12 @@
         mUI = new PaymentRequestUI(activity, this, mMerchantSupportsAutofillCards,
                 !PaymentPreferencesUtil.isPaymentCompleteOnce(), mMerchantName, mTopLevelOrigin,
                 SecurityStateModel.getSecurityLevelForWebContents(mWebContents),
-                new ShippingStrings(mShippingType), mPaymentUisShowStateReconciler);
+                new ShippingStrings(mShippingType), mPaymentUisShowStateReconciler,
+                Profile.fromWebContents(mWebContents));
         activity.getLifecycleDispatcher().register(mUI);
 
-        // TODO(https://crbug.com/1048632): Use the current profile (i.e., regular profile or
-        // incognito profile) instead of always using regular profile. It works correctly now, but
-        // it is not safe.
         final FaviconHelper faviconHelper = new FaviconHelper();
-        faviconHelper.getLocalFaviconImageForURL(Profile.getLastUsedRegularProfile(),
+        faviconHelper.getLocalFaviconImageForURL(Profile.fromWebContents(mWebContents),
                 mWebContents.getLastCommittedUrl(),
                 activity.getResources().getDimensionPixelSize(R.dimen.payments_favicon_size),
                 (bitmap, iconUrl) -> {
@@ -1187,7 +1181,8 @@
     @Override
     public boolean changePaymentMethodFromInvokedApp(String methodName, String stringifiedDetails) {
         if (TextUtils.isEmpty(methodName) || stringifiedDetails == null || mClient == null
-                || mInvokedPaymentApp == null) {
+                || mInvokedPaymentApp == null
+                || mInvokedPaymentApp.isWaitingForPaymentDetailsUpdate()) {
             return false;
         }
 
@@ -1201,7 +1196,8 @@
     @Override
     public boolean changeShippingOptionFromInvokedApp(String shippingOptionId) {
         if (TextUtils.isEmpty(shippingOptionId) || mClient == null || mInvokedPaymentApp == null
-                || !mRequestShipping || mRawShippingOptions == null) {
+                || mInvokedPaymentApp.isWaitingForPaymentDetailsUpdate() || !mRequestShipping
+                || mRawShippingOptions == null) {
             return false;
         }
 
@@ -1225,7 +1221,7 @@
     @Override
     public boolean changeShippingAddressFromInvokedApp(PaymentAddress shippingAddress) {
         if (shippingAddress == null || mClient == null || mInvokedPaymentApp == null
-                || !mRequestShipping) {
+                || mInvokedPaymentApp.isWaitingForPaymentDetailsUpdate() || !mRequestShipping) {
             return false;
         }
 
@@ -1235,46 +1231,6 @@
     }
 
     /**
-     * Called by the web-based payment handler to get updated total based on the billing address,
-     * for example.
-     */
-    @Override
-    public boolean changePaymentMethodFromPaymentHandler(
-            String methodName, String stringifiedData) {
-        if (mPaymentHandlerHost == null || mPaymentHandlerHost.isWaitingForPaymentDetailsUpdate()) {
-            return false;
-        }
-
-        return changePaymentMethodFromInvokedApp(methodName, stringifiedData);
-    }
-
-    /**
-     * Called by the web-based payment handler to get updated payment details based on the shipping
-     * option.
-     */
-    @Override
-    public boolean changeShippingOptionFromPaymentHandler(String shippingOptionId) {
-        if (mPaymentHandlerHost == null || mPaymentHandlerHost.isWaitingForPaymentDetailsUpdate()) {
-            return false;
-        }
-
-        return changeShippingOptionFromInvokedApp(shippingOptionId);
-    }
-
-    /**
-     * Called by the web-based payment handler to get updated payment details based on the shipping
-     * address.
-     */
-    @Override
-    public boolean changeShippingAddressFromPaymentHandler(PaymentAddress shippingAddress) {
-        if (mPaymentHandlerHost == null || mPaymentHandlerHost.isWaitingForPaymentDetailsUpdate()) {
-            return false;
-        }
-
-        return changeShippingAddressFromInvokedApp(shippingAddress);
-    }
-
-    /**
      * Get the WebContents of the Expandable Payment Handler for testing purpose; return null if
      * nonexistent.
      *
@@ -1395,21 +1351,12 @@
     public void onPaymentHandlerUiClosed() {
         mPaymentUisShowStateReconciler.onBottomSheetClosed();
         mPaymentHandlerUi = null;
-        ChromeActivity activity = ChromeActivity.fromWebContents(mWebContents);
-        if (activity != null) activity.getScrim().hideScrim(true);
     }
 
     @Override
     public void onPaymentHandlerUiShown() {
         assert mPaymentHandlerUi != null;
         mPaymentUisShowStateReconciler.onBottomSheetShown();
-        // Using an empty scrim observer is to avoid the dismissal of the bottom-sheet on tapping.
-        ScrimParams params = ChromeActivity.fromWebContents(mWebContents)
-                                     .getBottomSheetController()
-                                     .createScrimParams(new EmptyScrimObserver());
-        ScrimView scrim = ChromeActivity.fromWebContents(mWebContents).getScrim();
-        scrim.showScrim(params);
-        scrim.setViewAlpha(0);
     }
 
     @Override
@@ -2612,7 +2559,7 @@
 
     // PaymentAppFactoryParams implementation.
     @Override
-    public PaymentApp.PaymentRequestUpdateEventListener getPaymentRequestUpdateEventListener() {
+    public PaymentRequestUpdateEventListener getPaymentRequestUpdateEventListener() {
         return this;
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/handler/PaymentHandlerCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/handler/PaymentHandlerCoordinator.java
index 55e17f2..b9653e4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/handler/PaymentHandlerCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/handler/PaymentHandlerCoordinator.java
@@ -113,13 +113,13 @@
             changeProcessor.destroy();
             bottomSheetController.removeObserver(mediator);
             bottomSheetController.hideContent(/*content=*/view, /*animate=*/true);
-            mWebContents.destroy();
             uiObserver.onPaymentHandlerUiClosed();
             assert activity.getWindow() != null;
             assert activity.getWindow().getDecorView() != null;
             activity.getWindow().getDecorView().removeOnLayoutChangeListener(mediator);
-            thinWebView.destroy();
             mediator.destroy();
+            thinWebView.destroy();
+            mWebContents.destroy();
         };
         return bottomSheetController.requestShowContent(view, /*animate=*/true);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/handler/PaymentHandlerMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/handler/PaymentHandlerMediator.java
index 63fc3a3..3229734 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/handler/PaymentHandlerMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/handler/PaymentHandlerMediator.java
@@ -7,11 +7,14 @@
 import android.os.Handler;
 import android.view.View;
 
+import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.compositor.bottombar.OverlayPanel.StateChangeReason;
 import org.chromium.chrome.browser.payments.ServiceWorkerPaymentAppBridge;
 import org.chromium.chrome.browser.payments.SslValidityChecker;
 import org.chromium.chrome.browser.payments.handler.PaymentHandlerCoordinator.PaymentHandlerUiObserver;
 import org.chromium.chrome.browser.payments.handler.toolbar.PaymentHandlerToolbarCoordinator.PaymentHandlerToolbarObserver;
+import org.chromium.chrome.browser.ui.TabObscuringHandler;
+import org.chromium.chrome.browser.widget.ScrimView;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetController;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetController.SheetState;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetObserver;
@@ -21,6 +24,7 @@
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.content_public.browser.WebContentsObserver;
 import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.ui.util.TokenHolder;
 
 /**
  * PaymentHandler mediator, which is responsible for receiving events from the view and notifies the
@@ -49,6 +53,9 @@
     private final int mToolbarViewHeightPx;
     private final int mContainerTopPaddingPx;
 
+    /** A token held while the payment sheet is obscuring all visible tabs. */
+    private int mTabObscuringToken;
+
     /**
      * Build a new mediator that handle events from outside the payment handler component.
      * @param model The {@link PaymentHandlerProperties} that holds all the view state for the
@@ -74,15 +81,11 @@
         mPaymentHandlerUiObserver = observer;
         mContainerTopPaddingPx = containerTopPaddingPx;
         mModel.set(PaymentHandlerProperties.CONTENT_VISIBLE_HEIGHT_PX, contentVisibleHeight());
+
+        mTabObscuringToken = TokenHolder.INVALID_TOKEN;
     }
 
-    @Override
-    public void destroy() {
-        super.destroy(); // Stops observing the web contents and cleans up associated references.
-        mHandler.removeCallbacksAndMessages(null);
-    }
-
-    // View.OnLayoutChangeListener:
+    // Implement View.OnLayoutChangeListener:
     // This is the Tab View's layout change listener, invoked in response to phone rotation.
     // TODO(crbug.com/1057825): It should listen to the BottomSheet container's layout change
     // instead of the Tab View layout change for better encapsulation.
@@ -92,7 +95,7 @@
         mModel.set(PaymentHandlerProperties.CONTENT_VISIBLE_HEIGHT_PX, contentVisibleHeight());
     }
 
-    // BottomSheetObserver:
+    // Implement BottomSheetObserver:
     @Override
     public void onSheetStateChanged(@SheetState int newState) {
         switch (newState) {
@@ -112,9 +115,41 @@
     @Override
     public void onSheetOffsetChanged(float heightFraction, float offsetPx) {}
 
+    /**
+     * Set whether PaymentHandler UI is obscuring all tabs.
+     * @param activity The ChromeActivity of the tab.
+     * @param isObscuring Whether PaymentHandler UI is considered to be obscuring.
+     */
+    private void setIsObscuringAllTabs(ChromeActivity activity, boolean isObscuring) {
+        TabObscuringHandler obscuringHandler = activity.getTabObscuringHandler();
+        if (obscuringHandler == null) return;
+        if (isObscuring) {
+            assert mTabObscuringToken == TokenHolder.INVALID_TOKEN;
+            mTabObscuringToken = obscuringHandler.obscureAllTabs();
+        } else {
+            obscuringHandler.unobscureAllTabs(mTabObscuringToken);
+            mTabObscuringToken = TokenHolder.INVALID_TOKEN;
+        }
+    }
+
+    private void showScrim() {
+        // Using an empty scrim observer is to avoid the dismissal of the bottom-sheet on tapping.
+        ChromeActivity activity = ChromeActivity.fromWebContents(mWebContentsRef);
+        assert activity != null;
+
+        ScrimView.ScrimParams params = activity.getBottomSheetController().createScrimParams(
+                new ScrimView.EmptyScrimObserver());
+        ScrimView scrim = activity.getScrim();
+        scrim.showScrim(params);
+        scrim.setViewAlpha(0);
+
+        setIsObscuringAllTabs(activity, true);
+    }
+
     @Override
     public void onSheetOpened(@StateChangeReason int reason) {
         mPaymentHandlerUiObserver.onPaymentHandlerUiShown();
+        showScrim();
     }
 
     @Override
@@ -129,7 +164,22 @@
     @Override
     public void onSheetContentChanged(BottomSheetContent newContent) {}
 
-    // WebContentsObserver:
+    // Implement WebContentsObserver:
+    @Override
+    public void destroy() {
+        super.destroy(); // Stops observing the web contents and cleans up associated references.
+        mHandler.removeCallbacksAndMessages(null);
+        hideScrim();
+    }
+
+    private void hideScrim() {
+        ChromeActivity activity = ChromeActivity.fromWebContents(mWebContentsRef);
+        assert activity != null;
+
+        activity.getScrim().hideScrim(true);
+        setIsObscuringAllTabs(activity, false);
+    }
+
     @Override
     public void didFinishNavigation(NavigationHandle navigationHandle) {
         if (navigationHandle.isSameDocument()) return;
@@ -164,7 +214,7 @@
         });
     }
 
-    // PaymentHandlerToolbarObserver:
+    // Implement PaymentHandlerToolbarObserver:
     @Override
     public void onToolbarCloseButtonClicked() {
         ServiceWorkerPaymentAppBridge.onClosingPaymentAppWindow(mWebContentsRef);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestUI.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestUI.java
index 1d2071c..da87e99 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestUI.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestUI.java
@@ -46,6 +46,7 @@
 import org.chromium.chrome.browser.payments.ui.PaymentRequestSection.LineItemBreakdownSection;
 import org.chromium.chrome.browser.payments.ui.PaymentRequestSection.OptionSection;
 import org.chromium.chrome.browser.payments.ui.PaymentRequestSection.SectionSeparator;
+import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.signin.IdentityServicesProvider;
 import org.chromium.components.autofill.EditableOption;
 import org.chromium.components.browser_ui.widget.FadingEdgeScrollView;
@@ -352,11 +353,12 @@
      * https://www.chromium.org/Home/chromium-security/enamel#TOC-Eliding-Origin-Names-And-Hostnames
      * @param securityLevel   The security level of the page that invoked PaymentRequest.
      * @param shippingStrings The string resource identifiers to use in the shipping sections.
+     * @param profile         The current profile that creates the PaymentRequestUI.
      */
     public PaymentRequestUI(Activity activity, Client client, boolean canAddCards,
             boolean showDataSource, String title, String origin, int securityLevel,
             ShippingStrings shippingStrings,
-            PaymentUisShowStateReconciler paymentUisShowStateReconciler) {
+            PaymentUisShowStateReconciler paymentUisShowStateReconciler, Profile profile) {
         mContext = activity;
         mClient = client;
         mShowDataSource = showDataSource;
@@ -405,10 +407,10 @@
                 (ViewGroup) LayoutInflater.from(mContext).inflate(R.layout.payment_request, null);
         prepareRequestView(mContext, title, origin, securityLevel, canAddCards);
 
-        mEditorDialog = new EditorDialog(activity, /*deleteRunnable =*/null);
+        mEditorDialog = new EditorDialog(activity, /*deleteRunnable =*/null, profile);
         DimmingDialog.setVisibleStatusBarIconColor(mEditorDialog.getWindow());
 
-        mCardEditorDialog = new EditorDialog(activity, /*deleteRunnable =*/null);
+        mCardEditorDialog = new EditorDialog(activity, /*deleteRunnable =*/null, profile);
         DimmingDialog.setVisibleStatusBarIconColor(mCardEditorDialog.getWindow());
 
         // Allow screenshots of the credit card number in Canary, Dev, and developer builds.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/policy/EnterpriseInfo.java b/chrome/android/java/src/org/chromium/chrome/browser/policy/EnterpriseInfo.java
index 9099807..4822fb3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/policy/EnterpriseInfo.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/policy/EnterpriseInfo.java
@@ -10,6 +10,7 @@
 import android.content.pm.PackageManager;
 import android.os.Build.VERSION;
 import android.os.Build.VERSION_CODES;
+import android.os.SystemClock;
 
 import org.chromium.base.ContextUtils;
 import org.chromium.base.metrics.RecordHistogram;
@@ -31,6 +32,8 @@
                 private Boolean calculateIsRunningOnManagedProfile(Context context) {
                     if (VERSION.SDK_INT < VERSION_CODES.M) return null;
 
+                    long startTime = SystemClock.elapsedRealtime();
+                    boolean hasProfileOwnerApp = false;
                     PackageManager packageManager = context.getPackageManager();
                     DevicePolicyManager devicePolicyManager =
                             context.getSystemService(DevicePolicyManager.class);
@@ -38,11 +41,17 @@
                     for (PackageInfo pkg : packageManager.getInstalledPackages(/* flags= */ 0)) {
                         assert devicePolicyManager != null;
                         if (devicePolicyManager.isProfileOwnerApp(pkg.packageName)) {
-                            return true;
+                            hasProfileOwnerApp = true;
+                            break;
                         }
                     }
 
-                    return false;
+                    long endTime = SystemClock.elapsedRealtime();
+                    RecordHistogram.recordTimesHistogram(
+                            "EnterpriseCheck.IsRunningOnManagedProfileDuration",
+                            endTime - startTime);
+
+                    return hasProfileOwnerApp;
                 }
 
                 @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchWidgetProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchWidgetProvider.java
index 79d3c88..5dd69de 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchWidgetProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchWidgetProvider.java
@@ -11,10 +11,8 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.content.SharedPreferences;
 import android.net.Uri;
 import android.os.Bundle;
-import android.os.StrictMode;
 import android.text.TextUtils;
 import android.view.View;
 import android.widget.RemoteViews;
@@ -33,6 +31,8 @@
 import org.chromium.chrome.browser.firstrun.FirstRunFlowSequencer;
 import org.chromium.chrome.browser.locale.LocaleManager;
 import org.chromium.chrome.browser.omnibox.UrlBarData;
+import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
+import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
 import org.chromium.chrome.browser.search_engines.TemplateUrlServiceFactory;
 import org.chromium.components.search_engines.TemplateUrl;
 import org.chromium.components.search_engines.TemplateUrlService;
@@ -69,9 +69,9 @@
             return mContext;
         }
 
-        /** See {@link ContextUtils#getAppSharedPreferences}. */
-        protected SharedPreferences getSharedPreferences() {
-            return ContextUtils.getAppSharedPreferences();
+        /** Returns the {@link SharedPreferencesManager} to store prefs. */
+        protected SharedPreferencesManager getSharedPreferencesManager() {
+            return SharedPreferencesManager.getInstance();
         }
 
         /** Returns IDs for all search widgets that exist. */
@@ -117,13 +117,6 @@
     public static final String EXTRA_START_VOICE_SEARCH =
             "org.chromium.chrome.browser.searchwidget.START_VOICE_SEARCH";
 
-    private static final String PREF_IS_VOICE_SEARCH_AVAILABLE =
-            "org.chromium.chrome.browser.searchwidget.IS_VOICE_SEARCH_AVAILABLE";
-    private static final String PREF_NUM_CONSECUTIVE_CRASHES =
-            "org.chromium.chrome.browser.searchwidget.NUM_CONSECUTIVE_CRASHES";
-    static final String PREF_SEARCH_ENGINE_SHORTNAME =
-            "org.chromium.chrome.browser.searchwidget.SEARCH_ENGINE_SHORTNAME";
-
     /** Number of consecutive crashes this widget will absorb before giving up. */
     private static final int CRASH_LIMIT = 3;
 
@@ -163,10 +156,9 @@
 
     /** Nukes all cached information and forces all widgets to start with a blank slate. */
     public static void reset() {
-        SharedPreferences.Editor editor = getDelegate().getSharedPreferences().edit();
-        editor.remove(PREF_IS_VOICE_SEARCH_AVAILABLE);
-        editor.remove(PREF_SEARCH_ENGINE_SHORTNAME);
-        editor.apply();
+        SharedPreferencesManager prefs = getDelegate().getSharedPreferencesManager();
+        prefs.removeKey(ChromePreferenceKeys.SEARCH_WIDGET_IS_VOICE_SEARCH_AVAILABLE);
+        prefs.removeKey(ChromePreferenceKeys.SEARCH_WIDGET_SEARCH_ENGINE_SHORTNAME);
 
         performUpdate(null);
     }
@@ -240,7 +232,7 @@
         if (ids == null) ids = delegate.getAllSearchWidgetIds();
         if (ids.length == 0) return;
 
-        SharedPreferences prefs = delegate.getSharedPreferences();
+        SharedPreferencesManager prefs = delegate.getSharedPreferencesManager();
         boolean isVoiceSearchAvailable = getCachedVoiceSearchAvailability(prefs);
         String engineName = getCachedEngineName(prefs);
 
@@ -293,9 +285,10 @@
 
     /** Caches whether or not a voice search is possible. */
     static void updateCachedVoiceSearchAvailability(boolean isVoiceSearchAvailable) {
-        SharedPreferences prefs = getDelegate().getSharedPreferences();
+        SharedPreferencesManager prefs = getDelegate().getSharedPreferencesManager();
         if (getCachedVoiceSearchAvailability(prefs) != isVoiceSearchAvailable) {
-            prefs.edit().putBoolean(PREF_IS_VOICE_SEARCH_AVAILABLE, isVoiceSearchAvailable).apply();
+            prefs.writeBoolean(ChromePreferenceKeys.SEARCH_WIDGET_IS_VOICE_SEARCH_AVAILABLE,
+                    isVoiceSearchAvailable);
             performUpdate(null);
         }
     }
@@ -333,12 +326,13 @@
      * TemplateUrlService whenever the widget is updated.
      */
     static void updateCachedEngineName(String engineName) {
-        SharedPreferences prefs = getDelegate().getSharedPreferences();
+        SharedPreferencesManager prefs = getDelegate().getSharedPreferencesManager();
 
         if (!shouldShowFullString()) engineName = null;
 
         if (!TextUtils.equals(getCachedEngineName(prefs), engineName)) {
-            prefs.edit().putString(PREF_SEARCH_ENGINE_SHORTNAME, engineName).apply();
+            prefs.writeString(
+                    ChromePreferenceKeys.SEARCH_WIDGET_SEARCH_ENGINE_SHORTNAME, engineName);
             performUpdate(null);
         }
     }
@@ -346,36 +340,25 @@
     /** Updates the number of consecutive crashes this widget has absorbed. */
     @SuppressLint({"ApplySharedPref", "CommitPrefEdits"})
     static void updateNumConsecutiveCrashes(int newValue) {
-        SharedPreferences prefs = getDelegate().getSharedPreferences();
+        SharedPreferencesManager prefs = getDelegate().getSharedPreferencesManager();
         if (getNumConsecutiveCrashes(prefs) == newValue) return;
 
-        SharedPreferences.Editor editor = prefs.edit();
-        if (newValue == 0) {
-            editor.remove(PREF_NUM_CONSECUTIVE_CRASHES);
-        } else {
-            editor.putInt(PREF_NUM_CONSECUTIVE_CRASHES, newValue);
-        }
-
         // This metric is committed synchronously because it relates to crashes.
-        StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
-        try {
-            editor.commit();
-        } finally {
-            StrictMode.setThreadPolicy(oldPolicy);
-        }
+        prefs.writeIntSync(ChromePreferenceKeys.SEARCH_WIDGET_NUM_CONSECUTIVE_CRASHES, newValue);
     }
 
-    private static boolean getCachedVoiceSearchAvailability(SharedPreferences prefs) {
-        return prefs.getBoolean(PREF_IS_VOICE_SEARCH_AVAILABLE, true);
+    private static boolean getCachedVoiceSearchAvailability(SharedPreferencesManager prefs) {
+        return prefs.readBoolean(
+                ChromePreferenceKeys.SEARCH_WIDGET_IS_VOICE_SEARCH_AVAILABLE, true);
     }
 
-    private static String getCachedEngineName(SharedPreferences prefs) {
-        return prefs.getString(PREF_SEARCH_ENGINE_SHORTNAME, null);
+    private static String getCachedEngineName(SharedPreferencesManager prefs) {
+        return prefs.readString(ChromePreferenceKeys.SEARCH_WIDGET_SEARCH_ENGINE_SHORTNAME, null);
     }
 
     @VisibleForTesting
-    static int getNumConsecutiveCrashes(SharedPreferences prefs) {
-        return prefs.getInt(PREF_NUM_CONSECUTIVE_CRASHES, 0);
+    static int getNumConsecutiveCrashes(SharedPreferencesManager prefs) {
+        return prefs.readInt(ChromePreferenceKeys.SEARCH_WIDGET_NUM_CONSECUTIVE_CRASHES);
     }
 
     private static SearchWidgetProviderDelegate getDelegate() {
@@ -391,7 +374,8 @@
             runnable.run();
             updateNumConsecutiveCrashes(0);
         } catch (Exception e) {
-            int numCrashes = getNumConsecutiveCrashes(getDelegate().getSharedPreferences()) + 1;
+            int numCrashes =
+                    getNumConsecutiveCrashes(getDelegate().getSharedPreferencesManager()) + 1;
             updateNumConsecutiveCrashes(numCrashes);
 
             if (numCrashes < CRASH_LIMIT) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/vr/ArCoreInstallUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/vr/ArCoreInstallUtils.java
index 4c4e533..1c90e11 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/vr/ArCoreInstallUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/vr/ArCoreInstallUtils.java
@@ -70,6 +70,7 @@
         try {
             return getArCoreShimInstance().checkAvailability(ContextUtils.getApplicationContext());
         } catch (RuntimeException e) {
+            Log.w(TAG, "ARCore availability check failed with error: %s", e.toString());
             return ArCoreShim.Availability.UNSUPPORTED_DEVICE_NOT_CAPABLE;
         }
     }
@@ -112,6 +113,13 @@
                 break;
         }
 
+        if (infobarText == null || buttonText == null) {
+            // The action was something other than "install" or "update", log this
+            // and exit early to avoid showing an empty infobar.
+            Log.w(TAG, "ARCore unavailable, status code %d", arCoreAvailability);
+            return;
+        }
+
         SimpleConfirmInfoBarBuilder.Listener listener = new SimpleConfirmInfoBarBuilder.Listener() {
             @Override
             public void onInfoBarDismissed() {
diff --git a/chrome/android/java/trichrome_chrome_bundle__base_bundle_module.AndroidManifest.expected b/chrome/android/java/trichrome_chrome_bundle__base_bundle_module.AndroidManifest.expected
index 6ad395c..4303d9a 100644
--- a/chrome/android/java/trichrome_chrome_bundle__base_bundle_module.AndroidManifest.expected
+++ b/chrome/android/java/trichrome_chrome_bundle__base_bundle_module.AndroidManifest.expected
@@ -911,24 +911,6 @@
           android:name="android.appwidget.provider"
           android:resource="@xml/search_widget_info"/>
     </receiver>
-    <receiver
-        android:name="org.chromium.chrome.browser.media.ui.MediaNotificationManager$CastMediaButtonReceiver">
-      <intent-filter>
-        <action android:name="android.intent.action.MEDIA_BUTTON"/>
-      </intent-filter>
-    </receiver>
-    <receiver
-        android:name="org.chromium.chrome.browser.media.ui.MediaNotificationManager$PlaybackMediaButtonReceiver">
-      <intent-filter>
-        <action android:name="android.intent.action.MEDIA_BUTTON"/>
-      </intent-filter>
-    </receiver>
-    <receiver
-        android:name="org.chromium.chrome.browser.media.ui.MediaNotificationManager$PresentationMediaButtonReceiver">
-      <intent-filter>
-        <action android:name="android.intent.action.MEDIA_BUTTON"/>
-      </intent-filter>
-    </receiver>
     <receiver android:name="org.chromium.chrome.browser.services.AccountsChangedReceiver">
       <intent-filter>
         <action android:name="android.accounts.LOGIN_ACCOUNTS_CHANGED"/>
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabFromChromeExternalNavigationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabFromChromeExternalNavigationTest.java
index 8cff3f8..8d073c4 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabFromChromeExternalNavigationTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabFromChromeExternalNavigationTest.java
@@ -4,6 +4,7 @@
 
 package org.chromium.chrome.browser.customtabs;
 
+import android.app.Activity;
 import android.content.Intent;
 import android.net.Uri;
 import android.support.test.InstrumentationRegistry;
@@ -23,6 +24,7 @@
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.Feature;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ChromeTabbedActivity;
 import org.chromium.chrome.browser.LaunchIntentDispatcher;
 import org.chromium.chrome.browser.customtabs.CustomTabDelegateFactory.CustomTabNavigationDelegate;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
@@ -30,14 +32,17 @@
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabDelegateFactory;
 import org.chromium.chrome.browser.tab.TabTestUtils;
+import org.chromium.chrome.test.ChromeActivityTestRule;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.components.external_intents.ExternalNavigationHandler.OverrideUrlLoadingResult;
 import org.chromium.components.external_intents.InterceptNavigationDelegateImpl;
 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.DOMUtils;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.net.test.EmbeddedTestServerRule;
 
+import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicReference;
 
 /**
@@ -50,6 +55,9 @@
     public CustomTabActivityTestRule mActivityRule = new CustomTabActivityTestRule();
 
     @Rule
+    public ChromeActivityTestRule mChromeActivityTestRule =
+            new ChromeActivityTestRule(ChromeTabbedActivity.class);
+
     public EmbeddedTestServerRule mServerRule = new EmbeddedTestServerRule();
 
     private Intent getCustomTabFromChromeIntent(final String url, final boolean markFromChrome) {
@@ -169,4 +177,43 @@
         Assert.assertFalse(menu.findItem(R.id.add_to_homescreen_id).isVisible());
         Assert.assertFalse(menu.findItem(R.id.open_webapk_id).isVisible());
     }
+
+    /**
+     * This test verifies that untrusted intents are not launched by PaymentHandlerActivity.
+     * The source of untrusted intent for our test is IntentURI.
+     */
+    @Test
+    @LargeTest
+    public void testCCTOpenedFromIntentUriWithPaymentsUI() throws Exception {
+        final String initialUrl =
+                mServerRule.getServer().getURL("/chrome/test/data/android/url_overriding/"
+                        + "navigation_to_cct_via_intent_uri_spoofing.html");
+
+        mChromeActivityTestRule.startMainActivityOnBlankPage();
+        mChromeActivityTestRule.loadUrlInNewTab(initialUrl);
+
+        mChromeActivityTestRule.getActivity().onUserInteraction();
+        // Simulate the click on the link that fires the IntentURI for external navigations.
+        try {
+            DOMUtils.clickNode(mChromeActivityTestRule.getWebContents(), "link");
+        } catch (TimeoutException e) {
+            Assert.fail("Failed to click on the target node.");
+            return;
+        }
+
+        // We poll to check that a CustomTabActivity is fired because of our IntentURI.
+        // We also check that this CustomTabActivity should not be of PaymentHandlerActivity
+        // type as it lacks the trusted extras which can only be added by Chrome.
+        CriteriaHelper.pollUiThread(() -> {
+            boolean isCCTLaunched = false;
+            for (Activity runningActivity : ApplicationStatus.getRunningActivities()) {
+                if (runningActivity instanceof CustomTabActivity) {
+                    isCCTLaunched = true;
+                    CustomTabActivity cta = (CustomTabActivity) runningActivity;
+                    if (PaymentHandlerActivity.class == cta.getClass()) return false;
+                }
+            }
+            return isCCTLaunched;
+        });
+    }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/permissions/PermissionTestRule.java b/chrome/android/javatests/src/org/chromium/chrome/browser/permissions/PermissionTestRule.java
index 6d968db..3979014 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/permissions/PermissionTestRule.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/permissions/PermissionTestRule.java
@@ -27,6 +27,7 @@
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.content_public.browser.test.util.TouchCommon;
 import org.chromium.net.test.EmbeddedTestServer;
+import org.chromium.net.test.ServerCertificate;
 import org.chromium.ui.modaldialog.ModalDialogManager;
 import org.chromium.ui.modaldialog.ModalDialogManager.ModalDialogType;
 
@@ -53,6 +54,7 @@
 public class PermissionTestRule extends ChromeActivityTestRule<ChromeActivity> {
     private InfoBarTestAnimationListener mListener;
     private EmbeddedTestServer mTestServer;
+    private boolean mUseHttpsServer;
 
     /**
      * Waits till a JavaScript callback which updates the page title is called the specified number
@@ -80,6 +82,11 @@
 
         public void waitForNumUpdates(int numUpdates) throws Exception {
             mExpectedCount = numUpdates;
+
+            // Update might have already happened, check before waiting for title udpdates.
+            String expectedTitle = mPrefix + mExpectedCount;
+            if (mActivity.getActivityTab().getTitle().equals(expectedTitle)) return;
+
             mCallbackHelper.waitForCallback(0);
         }
     }
@@ -132,12 +139,20 @@
     }
 
     public PermissionTestRule() {
+        this(false);
+    }
+
+    public PermissionTestRule(boolean useHttpsServer) {
         super(ChromeActivity.class);
+        mUseHttpsServer = useHttpsServer;
     }
 
     private void ruleSetUp() {
         // TODO(https://crbug.com/867446): Refactor to use EmbeddedTestServerRule.
-        mTestServer = EmbeddedTestServer.createAndStartServer(InstrumentationRegistry.getContext());
+        mTestServer = mUseHttpsServer
+                ? EmbeddedTestServer.createAndStartHTTPSServer(
+                        InstrumentationRegistry.getContext(), ServerCertificate.CERT_OK)
+                : EmbeddedTestServer.createAndStartServer(InstrumentationRegistry.getContext());
     }
 
     /**
@@ -153,7 +168,7 @@
         mTestServer.stopAndDestroyServer();
     }
 
-    protected void setUpUrl(final String url) {
+    public void setUpUrl(final String url) {
         loadUrl(getURL(url));
     }
 
@@ -165,6 +180,10 @@
         return mTestServer.getURL("/");
     }
 
+    public String getURLWithHostName(String hostName, String url) {
+        return mTestServer.getURLWithHostName(hostName, url);
+    }
+
     /**
      * Runs a permission prompt test that grants the permission and expects the page title to be
      * updated in response.
@@ -190,8 +209,8 @@
     }
 
     /**
-     * Runs a permission prompt test that grants the permission and expects the page title to be
-     * updated in response.
+     * Runs a permission prompt test that does not grant the permission and expects the page title
+     * to be updated in response.
      * @param updateWaiter  The update waiter to wait for callbacks. Should be added as an observer
      *                      to the current tab prior to calling this method.
      * @param javascript    The JS function to run in the current tab to execute the test and update
@@ -213,20 +232,53 @@
         replyToPromptAndWaitForUpdates(updateWaiter, false, nUpdates, isDialog);
     }
 
+    /**
+     * Runs a permission prompt test that expects no prompt to be displayed because the permission
+     * is already granted/blocked and expects the page title to be updated.
+     * @param updateWaiter  The update waiter to wait for callbacks. Should be added as an observer
+     *                      to the current tab prior to calling this method.
+     * @param javascript    The JS function to run in the current tab to execute the test and update
+     *                      the page title.
+     * @param nUpdates      How many updates of the page title to wait for.
+     * @param withGesture   True if we require a user gesture.
+     * @param isDialog      True if we are testing a permission dialog, false for an infobar.
+     * @throws Exception
+     */
+    public void runNoPromptTest(PermissionUpdateWaiter updateWaiter, final String url,
+            String javascript, int nUpdates, boolean withGesture, boolean isDialog)
+            throws Exception {
+        setUpUrl(url);
+        if (withGesture) {
+            runJavaScriptCodeInCurrentTabWithGesture(javascript);
+        } else {
+            runJavaScriptCodeInCurrentTab(javascript);
+        }
+        waitForUpdatesAndAssertNoPrompt(updateWaiter, nUpdates, isDialog);
+    }
+
     private void replyToPromptAndWaitForUpdates(PermissionUpdateWaiter updateWaiter, boolean allow,
             int nUpdates, boolean isDialog) throws Exception {
         if (isDialog) {
-            ModalDialogManager dialogManager = TestThreadUtils.runOnUiThreadBlockingNoException(
-                    getActivity()::getModalDialogManager);
-            DialogShownCriteria criteria =
-                    new DialogShownCriteria(dialogManager, "Dialog not shown", true);
-            CriteriaHelper.pollUiThread(criteria);
+            waitForDialog(getActivity());
             replyToDialogAndWaitForUpdates(updateWaiter, nUpdates, allow);
         } else {
             replyToInfoBarAndWaitForUpdates(updateWaiter, nUpdates, allow);
         }
     }
 
+    private void waitForUpdatesAndAssertNoPrompt(
+            PermissionUpdateWaiter updateWaiter, int nUpdates, boolean isDialog) throws Exception {
+        updateWaiter.waitForNumUpdates(nUpdates);
+
+        if (isDialog) {
+            Assert.assertFalse("Modal permission prompt shown when none expected",
+                    PermissionDialogController.getInstance().isDialogShownForTest());
+        } else {
+            Assert.assertEquals(
+                    "Permission infobar shown when none expected", getInfoBars().size(), 0);
+        }
+    }
+
     private void runJavaScriptCodeInCurrentTabWithGesture(String javascript)
             throws java.util.concurrent.TimeoutException {
         runJavaScriptCodeInCurrentTab("functionToRun = '" + javascript + "'");
@@ -258,13 +310,27 @@
      */
     private void replyToDialogAndWaitForUpdates(
             PermissionUpdateWaiter updateWaiter, int nUpdates, boolean allow) throws Exception {
+        replyToDialog(allow, getActivity());
+        updateWaiter.waitForNumUpdates(nUpdates);
+    }
+
+    /**
+     * Utility functions to support permissions testing in other contexts.
+     */
+    public static void replyToDialog(boolean allow, ChromeActivity activity) {
         TestThreadUtils.runOnUiThreadBlocking(() -> {
-            TabModalPresenter presenter = (TabModalPresenter) getActivity()
-                                                  .getModalDialogManager()
+            TabModalPresenter presenter = (TabModalPresenter) activity.getModalDialogManager()
                                                   .getCurrentPresenterForTest();
             TouchCommon.singleClickView(presenter.getDialogContainerForTest().findViewById(
                     allow ? R.id.positive_button : R.id.negative_button));
         });
-        updateWaiter.waitForNumUpdates(nUpdates);
+    }
+
+    public static void waitForDialog(ChromeActivity activity) {
+        ModalDialogManager dialogManager =
+                TestThreadUtils.runOnUiThreadBlockingNoException(activity::getModalDialogManager);
+        DialogShownCriteria criteria =
+                new DialogShownCriteria(dialogManager, "Dialog not shown", true);
+        CriteriaHelper.pollUiThread(criteria);
     }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/permissions/QuotaTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/permissions/QuotaTest.java
index b32d890..bb9c089 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/permissions/QuotaTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/permissions/QuotaTest.java
@@ -12,7 +12,6 @@
 import org.junit.runner.RunWith;
 
 import org.chromium.base.test.util.CommandLineFlags;
-import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.Feature;
 import org.chromium.base.test.util.RetryOnFailure;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
@@ -57,8 +56,7 @@
     @Test
     @MediumTest
     @Feature({"QuotaPermissions"})
-    @DisabledTest(message = "Modals are now enabled and test needs to be reworked crbug.com/935900")
-    public void testQuotaShowsInfobar() throws Exception {
-        testQuotaPermissionsPlumbing("initiate_requestQuota(1024)", 1, false, false);
+    public void testQuotaPermissionRequestShowsModal() throws Exception {
+        testQuotaPermissionsPlumbing("initiate_requestQuota(1024)", 1, false, true);
     }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/push_messaging/PushMessagingTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/push_messaging/PushMessagingTest.java
index ff64643..867d03cf 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/push_messaging/PushMessagingTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/push_messaging/PushMessagingTest.java
@@ -27,23 +27,20 @@
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.init.ChromeBrowserInitializer;
 import org.chromium.chrome.browser.notifications.NotificationTestRule;
+import org.chromium.chrome.browser.permissions.PermissionTestRule;
 import org.chromium.chrome.browser.tab.Tab;
-import org.chromium.chrome.browser.ui.messages.infobar.InfoBar;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
-import org.chromium.chrome.test.util.InfoBarUtil;
 import org.chromium.chrome.test.util.browser.TabTitleObserver;
 import org.chromium.components.browser_ui.notifications.MockNotificationManagerProxy.NotificationEntry;
 import org.chromium.components.content_settings.ContentSettingValues;
 import org.chromium.components.gcm_driver.GCMDriver;
 import org.chromium.components.gcm_driver.GCMMessage;
 import org.chromium.components.gcm_driver.instance_id.FakeInstanceIDWithSubtype;
-import org.chromium.content_public.browser.test.util.Criteria;
-import org.chromium.content_public.browser.test.util.CriteriaHelper;
+import org.chromium.components.permissions.PermissionDialogController;
 import org.chromium.content_public.browser.test.util.JavaScriptUtils;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.net.test.EmbeddedTestServerRule;
 
-import java.util.List;
 import java.util.concurrent.TimeoutException;
 
 /**
@@ -112,10 +109,11 @@
         // Reload page to ensure the block is persisted.
         mNotificationTestRule.loadUrl(mPushTestPage);
 
-        // PushManager.subscribePush() should fail immediately without showing an infobar.
+        // PushManager.subscribePush() should fail immediately without showing a prompt.
         runScriptAndWaitForTitle("subscribePush()",
                 "subscribe fail: NotAllowedError: Registration failed - permission denied");
-        Assert.assertEquals(0, mNotificationTestRule.getInfoBars().size());
+        Assert.assertFalse("Permission prompt should not be shown",
+                PermissionDialogController.getInstance().isDialogShownForTest());
 
         // Notifications permission should still be denied.
         Assert.assertEquals("\"denied\"", runScriptBlocking("Notification.permission"));
@@ -124,39 +122,37 @@
     /**
      * Verifies that PushManager.subscribe() fails if permission is dismissed or blocked.
      */
-    //@MediumTest
-    //@Feature({"Browser", "PushMessaging"})
-    //@CommandLineFlags.Add("disable-features=ModalPermissionPrompts")
     @Test
-    @DisabledTest
+    @MediumTest
+    @Feature({"Browser", "PushMessaging"})
     public void testPushPermissionDenied() throws TimeoutException {
         // Notifications permission should initially be prompt.
         Assert.assertEquals("\"default\"", runScriptBlocking("Notification.permission"));
 
-        // PushManager.subscribePush() should show the notifications infobar.
-        Assert.assertEquals(0, mNotificationTestRule.getInfoBars().size());
+        // PushManager.subscribePush() should show the notifications permission prompt.
+        Assert.assertFalse("Permission prompt should not be shown",
+                PermissionDialogController.getInstance().isDialogShownForTest());
         runScript("subscribePush()");
-        InfoBar infoBar = getInfobarBlocking();
 
-        // Dismissing the infobar should cause subscribe() to fail.
-        Assert.assertTrue(InfoBarUtil.clickCloseButton(infoBar));
-        waitForInfobarToClose();
+        // Dismissing the prompt should cause subscribe() to fail.
+        PermissionTestRule.waitForDialog(mNotificationTestRule.getActivity());
+        TestThreadUtils.runOnUiThreadBlocking(
+                () -> { mNotificationTestRule.getActivity().onBackPressed(); });
+
         waitForTitle(mNotificationTestRule.getActivity().getActivityTab(),
                 "subscribe fail: NotAllowedError: Registration failed - permission denied");
 
         // Notifications permission should still be prompt.
         Assert.assertEquals("\"default\"", runScriptBlocking("Notification.permission"));
 
-        runScriptAndWaitForTitle("sendToTest('reset title')",
-                "clearCachedVerificationsForTesting title");
+        runScriptAndWaitForTitle("sendToTest('reset title')", "reset title");
 
-        // PushManager.subscribePush() should show the notifications infobar again.
+        // PushManager.subscribePush() should show the notifications permission prompt again.
         runScript("subscribePush()");
-        infoBar = getInfobarBlocking();
 
-        // Denying the infobar should cause subscribe() to fail.
-        Assert.assertTrue(InfoBarUtil.clickSecondaryButton(infoBar));
-        waitForInfobarToClose();
+        // Denying the prompt should cause subscribe() to fail.
+        PermissionTestRule.waitForDialog(mNotificationTestRule.getActivity());
+        PermissionTestRule.replyToDialog(false, mNotificationTestRule.getActivity());
         waitForTitle(mNotificationTestRule.getActivity().getActivityTab(),
                 "subscribe fail: NotAllowedError: Registration failed - permission denied");
 
@@ -166,10 +162,12 @@
         // Reload page to ensure the block is persisted.
         mNotificationTestRule.loadUrl(mPushTestPage);
 
-        // PushManager.subscribePush() should now fail immediately without showing an infobar.
+        // PushManager.subscribePush() should now fail immediately without showing a permission
+        // prompt.
         runScriptAndWaitForTitle("subscribePush()",
                 "subscribe fail: NotAllowedError: Registration failed - permission denied");
-        Assert.assertEquals(0, mNotificationTestRule.getInfoBars().size());
+        Assert.assertFalse("Permission prompt should not be shown",
+                PermissionDialogController.getInstance().isDialogShownForTest());
 
         // Notifications permission should still be denied.
         Assert.assertEquals("\"denied\"", runScriptBlocking("Notification.permission"));
@@ -181,19 +179,18 @@
     @Test
     @MediumTest
     @Feature({"Browser", "PushMessaging"})
-    @DisabledTest(message = "Modals are now enabled and test needs to be reworked crbug.com/935900")
     public void testPushPermissionGranted() throws TimeoutException {
         // Notifications permission should initially be prompt.
         Assert.assertEquals("\"default\"", runScriptBlocking("Notification.permission"));
 
-        // PushManager.subscribePush() should show the notifications infobar.
-        Assert.assertEquals(0, mNotificationTestRule.getInfoBars().size());
+        // PushManager.subscribePush() should show the notifications permission prompt.
+        Assert.assertFalse("Permission prompt should not be shown",
+                PermissionDialogController.getInstance().isDialogShownForTest());
         runScript("subscribePush()");
-        InfoBar infoBar = getInfobarBlocking();
 
-        // Accepting the infobar should cause subscribe() to succeed.
-        Assert.assertTrue(InfoBarUtil.clickPrimaryButton(infoBar));
-        waitForInfobarToClose();
+        // Accepting the prompt should cause subscribe() to succeed.
+        PermissionTestRule.waitForDialog(mNotificationTestRule.getActivity());
+        PermissionTestRule.replyToDialog(true, mNotificationTestRule.getActivity());
         waitForTitle(mNotificationTestRule.getActivity().getActivityTab(), "subscribe ok");
 
         // This should have caused notifications permission to become granted.
@@ -330,26 +327,4 @@
             Assert.assertEquals(expectedTitle, tab.getTitle());
         }
     }
-
-    private InfoBar getInfobarBlocking() {
-        CriteriaHelper.pollUiThread(new Criteria() {
-            @Override
-            public boolean isSatisfied() {
-                return !mNotificationTestRule.getInfoBars().isEmpty();
-            }
-        });
-        List<InfoBar> infoBars = mNotificationTestRule.getInfoBars();
-        Assert.assertEquals(1, infoBars.size());
-        return infoBars.get(0);
-    }
-
-    private void waitForInfobarToClose() {
-        CriteriaHelper.pollUiThread(new Criteria() {
-            @Override
-            public boolean isSatisfied() {
-                return mNotificationTestRule.getInfoBars().isEmpty();
-            }
-        });
-        Assert.assertEquals(0, mNotificationTestRule.getInfoBars().size());
-    }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/searchwidget/SearchWidgetProviderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/searchwidget/SearchWidgetProviderTest.java
index 5051241..6394053 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/searchwidget/SearchWidgetProviderTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/searchwidget/SearchWidgetProviderTest.java
@@ -9,7 +9,6 @@
 import android.app.Instrumentation.ActivityMonitor;
 import android.content.Context;
 import android.content.Intent;
-import android.content.SharedPreferences;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.util.Pair;
@@ -27,11 +26,12 @@
 import org.chromium.base.IntentUtils;
 import org.chromium.base.test.util.AdvancedMockContext;
 import org.chromium.base.test.util.CommandLineFlags;
-import org.chromium.base.test.util.InMemorySharedPreferences;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.firstrun.FirstRunActivity;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.locale.LocaleManager;
+import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
+import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
 import org.chromium.chrome.browser.searchwidget.SearchActivity.SearchActivityDelegate;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.util.ApplicationTestUtils;
@@ -62,12 +62,10 @@
 
         public final List<Pair<Integer, RemoteViews>> mViews = new ArrayList<>();
         private Context mContext;
-        private SharedPreferences mPreferences;
 
         private TestDelegate(Context context) {
             super(context);
             mContext = context;
-            mPreferences = new InMemorySharedPreferences();
         }
 
         @Override
@@ -76,11 +74,6 @@
         }
 
         @Override
-        protected SharedPreferences getSharedPreferences() {
-            return mPreferences;
-        }
-
-        @Override
         protected int[] getAllSearchWidgetIds() {
             return ALL_IDS;
         }
@@ -198,10 +191,8 @@
         // SearchWidgetProvider should now believe that its widgets are displaying branding when it
         // isn't allowed to, then update them.
         mDelegate.mViews.clear();
-        mDelegate.getSharedPreferences()
-                .edit()
-                .putString(SearchWidgetProvider.PREF_SEARCH_ENGINE_SHORTNAME, TEXT_SEARCH_ENGINE)
-                .apply();
+        mDelegate.getSharedPreferencesManager().writeString(
+                ChromePreferenceKeys.SEARCH_WIDGET_SEARCH_ENGINE_SHORTNAME, TEXT_SEARCH_ENGINE);
         TestThreadUtils.runOnUiThreadBlocking(
                 () -> SearchWidgetProvider.updateCachedEngineName(TEXT_SEARCH_ENGINE));
         checkWidgetStates(TEXT_GENERIC, View.VISIBLE);
@@ -307,7 +298,7 @@
             }
         };
 
-        SharedPreferences prefs = mDelegate.getSharedPreferences();
+        SharedPreferencesManager prefs = mDelegate.getSharedPreferencesManager();
         Assert.assertEquals(0, SearchWidgetProvider.getNumConsecutiveCrashes(prefs));
 
         // The first few crashes should be silently absorbed.
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/site_settings/SiteSettingsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/site_settings/SiteSettingsTest.java
index 1d1c0c7e..a6fe0df 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/site_settings/SiteSettingsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/site_settings/SiteSettingsTest.java
@@ -27,20 +27,19 @@
 import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.Feature;
 import org.chromium.base.test.util.RetryOnFailure;
-import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
-import org.chromium.chrome.browser.infobar.InfoBarContainer;
 import org.chromium.chrome.browser.notifications.channels.ChromeChannelDefinitions;
 import org.chromium.chrome.browser.notifications.channels.SiteChannelsManager;
+import org.chromium.chrome.browser.permissions.PermissionTestRule;
+import org.chromium.chrome.browser.permissions.PermissionTestRule.PermissionUpdateWaiter;
 import org.chromium.chrome.browser.preferences.Pref;
 import org.chromium.chrome.browser.preferences.PrefServiceBridge;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.settings.SettingsActivity;
 import org.chromium.chrome.browser.settings.SettingsLauncher;
 import org.chromium.chrome.browser.settings.SettingsLauncherImpl;
-import org.chromium.chrome.test.ChromeActivityTestRule;
+import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
-import org.chromium.chrome.test.util.InfoBarTestAnimationListener;
 import org.chromium.chrome.test.util.browser.Features.DisableFeatures;
 import org.chromium.chrome.test.util.browser.Features.EnableFeatures;
 import org.chromium.chrome.test.util.browser.LocationSettingsTestUtil;
@@ -63,14 +62,13 @@
 import org.chromium.components.permissions.nfc.NfcSystemLevelSetting;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.content_public.common.ContentSwitches;
-import org.chromium.net.test.EmbeddedTestServer;
-import org.chromium.net.test.ServerCertificate;
+import org.chromium.device.geolocation.LocationProviderOverrider;
+import org.chromium.device.geolocation.MockLocationProvider;
 import org.chromium.policy.test.annotations.Policies;
 
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
-import java.util.concurrent.Callable;
 
 /**
  * Tests for everything under Settings > Site Settings.
@@ -82,21 +80,28 @@
 @EnableFeatures(ContentSettingsFeatureList.IMPROVED_COOKIE_CONTROLS)
 public class SiteSettingsTest {
     @Rule
-    public ChromeActivityTestRule<ChromeActivity> mActivityTestRule =
-            new ChromeActivityTestRule<>(ChromeActivity.class);
+    public PermissionTestRule mPermissionRule = new PermissionTestRule(true);
 
-    private EmbeddedTestServer mTestServer;
+    private PermissionUpdateWaiter mPermissionUpdateWaiter = null;
 
     @Before
     public void setUp() throws Exception {
-        mActivityTestRule.startMainActivityOnBlankPage();
-        mTestServer = EmbeddedTestServer.createAndStartHTTPSServer(
-                InstrumentationRegistry.getContext(), ServerCertificate.CERT_OK);
+        mPermissionRule.startMainActivityOnBlankPage();
     }
 
     @After
     public void tearDown() {
-        mTestServer.stopAndDestroyServer();
+        if (mPermissionUpdateWaiter != null) {
+            mPermissionRule.getActivity().getActivityTab().removeObserver(mPermissionUpdateWaiter);
+        }
+    }
+
+    private void initializeUpdateWaiter(final boolean expectGranted) {
+        Tab tab = mPermissionRule.getActivity().getActivityTab();
+
+        mPermissionUpdateWaiter = new PermissionUpdateWaiter(
+                expectGranted ? "Granted:" : "Denied:", mPermissionRule.getActivity());
+        tab.addObserver(mPermissionUpdateWaiter);
     }
 
     private BrowserContextHandle getBrowserContextHandle() {
@@ -105,6 +110,7 @@
 
     private void setAllowLocation(final boolean enabled) {
         LocationSettingsTestUtil.setSystemLocationSettingEnabled(true);
+        LocationProviderOverrider.setLocationProviderImpl(new MockLocationProvider());
         final SettingsActivity settingsActivity = SiteSettingsTestUtils.startSiteSettingsCategory(
                 SiteSettingsCategory.Type.DEVICE_LOCATION);
 
@@ -123,58 +129,35 @@
         });
     }
 
-    private InfoBarTestAnimationListener setInfoBarAnimationListener() {
-        return TestThreadUtils.runOnUiThreadBlockingNoException(
-                new Callable<InfoBarTestAnimationListener>() {
-                    @Override
-                    public InfoBarTestAnimationListener call() {
-                        InfoBarContainer container = mActivityTestRule.getInfoBarContainer();
-                        InfoBarTestAnimationListener listener = new InfoBarTestAnimationListener();
-                        container.addAnimationListener(listener);
-                        return listener;
-                    }
-                });
-    }
-
     /**
      * Sets Allow Location Enabled to be true and make sure it is set correctly.
-     *
-     * TODO(timloh): Update this test once modals are enabled everywhere.
      */
     @Test
     @SmallTest
     @Feature({"Preferences"})
-    @DisabledTest(message = "Modals are now enabled and test needs to be reworked crbug.com/935900")
     public void testSetAllowLocationEnabled() throws Exception {
         setAllowLocation(true);
-        InfoBarTestAnimationListener listener = setInfoBarAnimationListener();
 
-        // Launch a page that uses geolocation and make sure an infobar shows up.
-        mActivityTestRule.loadUrl(
-                mTestServer.getURL("/chrome/test/data/geolocation/geolocation_on_load.html"));
-        listener.addInfoBarAnimationFinished("InfoBar not added.");
+        initializeUpdateWaiter(true /* expectGranted */);
 
-        Assert.assertEquals("Wrong infobar count", 1, mActivityTestRule.getInfoBars().size());
+        // Launch a page that uses geolocation and make sure a permission prompt shows up.
+        mPermissionRule.runAllowTest(mPermissionUpdateWaiter,
+                "/chrome/test/data/geolocation/geolocation_on_load.html", "", 1, false, true);
     }
 
     /**
      * Sets Allow Location Enabled to be false and make sure it is set correctly.
-     *
-     * TODO(timloh): Update this test once modals are enabled everywhere.
      */
     @Test
     @SmallTest
     @Feature({"Preferences"})
-    @DisabledTest(message = "Modals are now enabled and test needs to be reworked crbug.com/935900")
-    public void testSetAllowLocationNotEnabled() {
+    public void testSetAllowLocationNotEnabled() throws Exception {
         setAllowLocation(false);
 
-        // Launch a page that uses geolocation.
-        mActivityTestRule.loadUrl(
-                mTestServer.getURL("/chrome/test/data/geolocation/geolocation_on_load.html"));
-
-        // No infobars are expected.
-        Assert.assertTrue(mActivityTestRule.getInfoBars().isEmpty());
+        // Launch a page that uses geolocation. No permission prompt is expected.
+        initializeUpdateWaiter(false /* expectGranted */);
+        mPermissionRule.runNoPromptTest(mPermissionUpdateWaiter,
+                "/chrome/test/data/geolocation/geolocation_on_load.html", "", 1, false, true);
     }
 
     private void setCookiesEnabled(final SettingsActivity settingsActivity, final boolean enabled) {
@@ -441,19 +424,19 @@
         setCookiesEnabled(settingsActivity, true);
         settingsActivity.finish();
 
-        final String url = mTestServer.getURL("/chrome/test/data/android/cookie.html");
+        final String url = mPermissionRule.getURL("/chrome/test/data/android/cookie.html");
 
         // Load the page and clear any set cookies.
-        mActivityTestRule.loadUrl(url + "#clear");
-        Assert.assertEquals("\"\"", mActivityTestRule.runJavaScriptCodeInCurrentTab("getCookie()"));
-        mActivityTestRule.runJavaScriptCodeInCurrentTab("setCookie()");
+        mPermissionRule.loadUrl(url + "#clear");
+        Assert.assertEquals("\"\"", mPermissionRule.runJavaScriptCodeInCurrentTab("getCookie()"));
+        mPermissionRule.runJavaScriptCodeInCurrentTab("setCookie()");
         Assert.assertEquals(
-                "\"Foo=Bar\"", mActivityTestRule.runJavaScriptCodeInCurrentTab("getCookie()"));
+                "\"Foo=Bar\"", mPermissionRule.runJavaScriptCodeInCurrentTab("getCookie()"));
 
         // Load the page again and ensure the cookie still is set.
-        mActivityTestRule.loadUrl(url);
+        mPermissionRule.loadUrl(url);
         Assert.assertEquals(
-                "\"Foo=Bar\"", mActivityTestRule.runJavaScriptCodeInCurrentTab("getCookie()"));
+                "\"Foo=Bar\"", mPermissionRule.runJavaScriptCodeInCurrentTab("getCookie()"));
     }
 
     /**
@@ -468,17 +451,17 @@
         setCookiesEnabled(settingsActivity, false);
         settingsActivity.finish();
 
-        final String url = mTestServer.getURL("/chrome/test/data/android/cookie.html");
+        final String url = mPermissionRule.getURL("/chrome/test/data/android/cookie.html");
 
         // Load the page and clear any set cookies.
-        mActivityTestRule.loadUrl(url + "#clear");
-        Assert.assertEquals("\"\"", mActivityTestRule.runJavaScriptCodeInCurrentTab("getCookie()"));
-        mActivityTestRule.runJavaScriptCodeInCurrentTab("setCookie()");
-        Assert.assertEquals("\"\"", mActivityTestRule.runJavaScriptCodeInCurrentTab("getCookie()"));
+        mPermissionRule.loadUrl(url + "#clear");
+        Assert.assertEquals("\"\"", mPermissionRule.runJavaScriptCodeInCurrentTab("getCookie()"));
+        mPermissionRule.runJavaScriptCodeInCurrentTab("setCookie()");
+        Assert.assertEquals("\"\"", mPermissionRule.runJavaScriptCodeInCurrentTab("getCookie()"));
 
         // Load the page again and ensure the cookie remains unset.
-        mActivityTestRule.loadUrl(url);
-        Assert.assertEquals("\"\"", mActivityTestRule.runJavaScriptCodeInCurrentTab("getCookie()"));
+        mPermissionRule.loadUrl(url);
+        Assert.assertEquals("\"\"", mPermissionRule.runJavaScriptCodeInCurrentTab("getCookie()"));
     }
 
     /**
@@ -496,29 +479,30 @@
         setCookiesEnabled(settingsActivity, true);
         settingsActivity.finish();
 
-        final String url = mTestServer.getURL("/chrome/test/data/android/cookie.html");
+        final String url = mPermissionRule.getURL("/chrome/test/data/android/cookie.html");
 
         // Load the page and clear any set cookies.
-        mActivityTestRule.loadUrl(url + "#clear");
-        Assert.assertEquals("\"\"", mActivityTestRule.runJavaScriptCodeInCurrentTab("getCookie()"));
+        mPermissionRule.loadUrl(url + "#clear");
+        Assert.assertEquals("\"\"", mPermissionRule.runJavaScriptCodeInCurrentTab("getCookie()"));
 
         // Check cookies can be set for this website when there is no rule.
-        mActivityTestRule.runJavaScriptCodeInCurrentTab("setCookie()");
+        mPermissionRule.runJavaScriptCodeInCurrentTab("setCookie()");
         Assert.assertEquals(
-                "\"Foo=Bar\"", mActivityTestRule.runJavaScriptCodeInCurrentTab("getCookie()"));
+                "\"Foo=Bar\"", mPermissionRule.runJavaScriptCodeInCurrentTab("getCookie()"));
 
         // Set specific rule to block site and ensure it cannot set cookies.
-        mActivityTestRule.loadUrl(url + "#clear");
+        mPermissionRule.loadUrl(url + "#clear");
         settingsActivity =
                 SiteSettingsTestUtils.startSiteSettingsCategory(SiteSettingsCategory.Type.COOKIES);
         setBlockCookiesSiteException(settingsActivity, url, false);
-        settingsActivity.finish();
-        mActivityTestRule.runJavaScriptCodeInCurrentTab("setCookie()");
-        Assert.assertEquals("\"\"", mActivityTestRule.runJavaScriptCodeInCurrentTab("getCookie()"));
+        mPermissionRule.runJavaScriptCodeInCurrentTab("setCookie()");
+        Assert.assertEquals("\"\"", mPermissionRule.runJavaScriptCodeInCurrentTab("getCookie()"));
 
         // Load the page again and ensure the cookie remains unset.
-        mActivityTestRule.loadUrl(url);
-        Assert.assertEquals("\"\"", mActivityTestRule.runJavaScriptCodeInCurrentTab("getCookie()"));
+        mPermissionRule.loadUrl(url);
+        Assert.assertEquals("\"\"", mPermissionRule.runJavaScriptCodeInCurrentTab("getCookie()"));
+
+        settingsActivity.finish();
     }
 
     /**
@@ -528,19 +512,19 @@
     @SmallTest
     @Feature({"Preferences"})
     public void testClearCookies() throws Exception {
-        final String url = mTestServer.getURL("/chrome/test/data/android/cookie.html");
+        final String url = mPermissionRule.getURL("/chrome/test/data/android/cookie.html");
 
-        mActivityTestRule.loadUrl(url);
-        Assert.assertEquals("\"\"", mActivityTestRule.runJavaScriptCodeInCurrentTab("getCookie()"));
-        mActivityTestRule.runJavaScriptCodeInCurrentTab("setCookie()");
+        mPermissionRule.loadUrl(url);
+        Assert.assertEquals("\"\"", mPermissionRule.runJavaScriptCodeInCurrentTab("getCookie()"));
+        mPermissionRule.runJavaScriptCodeInCurrentTab("setCookie()");
         Assert.assertEquals(
-                "\"Foo=Bar\"", mActivityTestRule.runJavaScriptCodeInCurrentTab("getCookie()"));
+                "\"Foo=Bar\"", mPermissionRule.runJavaScriptCodeInCurrentTab("getCookie()"));
 
         resetSite(WebsiteAddress.create(url));
 
         // Load the page again and ensure the cookie is gone.
-        mActivityTestRule.loadUrl(url);
-        Assert.assertEquals("\"\"", mActivityTestRule.runJavaScriptCodeInCurrentTab("getCookie()"));
+        mPermissionRule.loadUrl(url);
+        Assert.assertEquals("\"\"", mPermissionRule.runJavaScriptCodeInCurrentTab("getCookie()"));
     }
 
     /**
@@ -550,21 +534,21 @@
     @SmallTest
     @Feature({"Preferences"})
     public void testClearDomainCookies() throws Exception {
-        final String url = mTestServer.getURLWithHostName(
+        final String url = mPermissionRule.getURLWithHostName(
                 "test.example.com", "/chrome/test/data/android/cookie.html");
 
-        mActivityTestRule.loadUrl(url);
-        Assert.assertEquals("\"\"", mActivityTestRule.runJavaScriptCodeInCurrentTab("getCookie()"));
-        mActivityTestRule.runJavaScriptCodeInCurrentTab("setCookie(\".example.com\")");
-        mActivityTestRule.runJavaScriptCodeInCurrentTab("setCookie(\".test.example.com\")");
+        mPermissionRule.loadUrl(url);
+        Assert.assertEquals("\"\"", mPermissionRule.runJavaScriptCodeInCurrentTab("getCookie()"));
+        mPermissionRule.runJavaScriptCodeInCurrentTab("setCookie(\".example.com\")");
+        mPermissionRule.runJavaScriptCodeInCurrentTab("setCookie(\".test.example.com\")");
         Assert.assertEquals("\"Foo=Bar; Foo=Bar\"",
-                mActivityTestRule.runJavaScriptCodeInCurrentTab("getCookie()"));
+                mPermissionRule.runJavaScriptCodeInCurrentTab("getCookie()"));
 
         resetSite(WebsiteAddress.create("test.example.com"));
 
         // Load the page again and ensure the cookie is gone.
-        mActivityTestRule.loadUrl(url);
-        Assert.assertEquals("\"\"", mActivityTestRule.runJavaScriptCodeInCurrentTab("getCookie()"));
+        mPermissionRule.loadUrl(url);
+        Assert.assertEquals("\"\"", mPermissionRule.runJavaScriptCodeInCurrentTab("getCookie()"));
     }
 
     /**
@@ -741,7 +725,7 @@
         setEnablePopups(false);
 
         // Test that the popup doesn't open.
-        mActivityTestRule.loadUrl(mTestServer.getURL("/chrome/test/data/android/popup.html"));
+        mPermissionRule.setUpUrl("/chrome/test/data/android/popup.html");
         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
 
         Assert.assertEquals(1, getTabCount());
@@ -757,7 +741,7 @@
         setEnablePopups(true);
 
         // Test that a popup opens.
-        mActivityTestRule.loadUrl(mTestServer.getURL("/chrome/test/data/android/popup.html"));
+        mPermissionRule.setUpUrl("/chrome/test/data/android/popup.html");
         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
 
         Assert.assertEquals(2, getTabCount());
@@ -939,26 +923,21 @@
         setEnableCamera(false);
 
         // Test that the camera permission doesn't get requested.
-        mActivityTestRule.loadUrl(mTestServer.getURL("/content/test/data/media/getusermedia.html"));
-        mActivityTestRule.runJavaScriptCodeInCurrentTab(
-                "getUserMediaAndStop({video: true, audio: false});");
-
-        // No infobars are expected.
-        Assert.assertTrue(mActivityTestRule.getInfoBars().isEmpty());
+        initializeUpdateWaiter(false /* expectGranted */);
+        mPermissionRule.runNoPromptTest(mPermissionUpdateWaiter,
+                "/content/test/data/media/getusermedia.html",
+                "getUserMediaAndStop({video: true, audio: false});", 1, false, true);
     }
 
     /**
      * Sets Allow Mic Enabled to be false and make sure it is set correctly.
      *
-     * TODO(timloh): Update this test once modals are enabled everywhere.
-     *
      * @throws Exception
      */
     @Test
     @SmallTest
     @Feature({"Preferences"})
     @CommandLineFlags.Add({ContentSwitches.USE_FAKE_DEVICE_FOR_MEDIA_STREAM})
-    @DisabledTest(message = "Modals are now enabled and test needs to be reworked crbug.com/935900")
     public void testMicBlocked() throws Exception {
         setGlobalToggleForCategory(SiteSettingsCategory.Type.MICROPHONE, false);
 
@@ -969,64 +948,47 @@
         });
 
         // Test that the microphone permission doesn't get requested.
-        mActivityTestRule.loadUrl(mTestServer.getURL("/content/test/data/media/getusermedia.html"));
-        mActivityTestRule.runJavaScriptCodeInCurrentTab(
-                "getUserMediaAndStop({video: false, audio: true});");
-
-        // No infobars are expected.
-        Assert.assertTrue(mActivityTestRule.getInfoBars().isEmpty());
+        initializeUpdateWaiter(false /* expectGranted */);
+        mPermissionRule.runNoPromptTest(mPermissionUpdateWaiter,
+                "/content/test/data/media/getusermedia.html",
+                "getUserMediaAndStop({video: false, audio: true});", 1, true, true);
     }
 
     /**
      * Sets Allow Camera Enabled to be true and make sure it is set correctly.
      *
-     * TODO(timloh): Update this test once modals are enabled everywhere.
-     *
      * @throws Exception
      */
     @Test
     @SmallTest
     @Feature({"Preferences"})
     @CommandLineFlags.Add({ContentSwitches.USE_FAKE_DEVICE_FOR_MEDIA_STREAM})
-    @DisabledTest(message = "Modals are now enabled and test needs to be reworked crbug.com/935900")
     public void testCameraNotBlocked() throws Exception {
         setEnableCamera(true);
 
-        InfoBarTestAnimationListener listener = setInfoBarAnimationListener();
-
-        // Launch a page that uses camera and make sure an infobar shows up.
-        mActivityTestRule.loadUrl(mTestServer.getURL("/content/test/data/media/getusermedia.html"));
-        mActivityTestRule.runJavaScriptCodeInCurrentTab(
-                "getUserMediaAndStop({video: true, audio: false});");
-
-        listener.addInfoBarAnimationFinished("InfoBar not added.");
-        Assert.assertEquals("Wrong infobar count", 1, mActivityTestRule.getInfoBars().size());
+        initializeUpdateWaiter(true /* expectGranted */);
+        mPermissionRule.runAllowTest(mPermissionUpdateWaiter,
+                "/content/test/data/media/getusermedia.html",
+                "getUserMediaAndStop({video: true, audio: false});", 1, false, true);
     }
 
     /**
      * Sets Allow Mic Enabled to be true and make sure it is set correctly.
      *
-     * TODO(timloh): Update this test once modals are enabled everywhere.
-     *
      * @throws Exception
      */
     @Test
     @SmallTest
     @Feature({"Preferences"})
     @CommandLineFlags.Add({ContentSwitches.USE_FAKE_DEVICE_FOR_MEDIA_STREAM})
-    @DisabledTest(message = "Modals are now enabled and test needs to be reworked crbug.com/935900")
     public void testMicNotBlocked() throws Exception {
         setEnableCamera(true);
 
-        InfoBarTestAnimationListener listener = setInfoBarAnimationListener();
-
-        // Launch a page that uses the microphone and make sure an infobar shows up.
-        mActivityTestRule.loadUrl(mTestServer.getURL("/content/test/data/media/getusermedia.html"));
-        mActivityTestRule.runJavaScriptCodeInCurrentTab(
-                "getUserMediaAndStop({video: false, audio: true});");
-
-        listener.addInfoBarAnimationFinished("InfoBar not added.");
-        Assert.assertEquals("Wrong infobar count", 1, mActivityTestRule.getInfoBars().size());
+        // Launch a page that uses the microphone and make sure a permission prompt shows up.
+        initializeUpdateWaiter(true /* expectGranted */);
+        mPermissionRule.runAllowTest(mPermissionUpdateWaiter,
+                "/content/test/data/media/getusermedia.html",
+                "getUserMediaAndStop({video: false, audio: true});", 1, true, true);
     }
 
     /**
@@ -1234,7 +1196,7 @@
 
     private int getTabCount() {
         return TestThreadUtils.runOnUiThreadBlockingNoException(
-                () -> mActivityTestRule.getActivity().getTabModelSelector().getTotalTabCount());
+                () -> mPermissionRule.getActivity().getTabModelSelector().getTotalTabCount());
     }
 
     @Test
@@ -1242,14 +1204,14 @@
     @Feature({"Preferences"})
     public void testEmbargoedNotificationSiteSettings() throws Exception {
         if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return;
-        final String url = mTestServer.getURLWithHostName(
+        final String url = mPermissionRule.getURLWithHostName(
                 "example.com", "/chrome/test/data/notifications/notification_tester.html");
 
         // Ignore notification request 4 times to enter embargo. 5th one ensures that notifications
         // are blocked by actually causing a deny-by-embargo.
         for (int i = 0; i < 5; i++) {
-            mActivityTestRule.loadUrl(url);
-            mActivityTestRule.runJavaScriptCodeInCurrentTab("requestPermissionAndRespond()");
+            mPermissionRule.loadUrl(url);
+            mPermissionRule.runJavaScriptCodeInCurrentTab("requestPermissionAndRespond()");
         }
 
         SettingsLauncher settingsLauncher = new SettingsLauncherImpl();
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/media/ui/MediaNotificationManagerServiceActionsTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/media/ui/MediaNotificationManagerServiceActionsTest.java
index 6619dbf..95aa2e97 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/media/ui/MediaNotificationManagerServiceActionsTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/media/ui/MediaNotificationManagerServiceActionsTest.java
@@ -6,14 +6,11 @@
 
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
 
 import android.content.Intent;
 import android.media.AudioManager;
-import android.view.KeyEvent;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -24,7 +21,7 @@
 import org.chromium.media_session.mojom.MediaSessionAction;
 
 /**
- * JUnit tests for checking {@link MediaNotificationManager.ListenerService} handles intent actionss
+ * JUnit tests for checking {@link MediaNotificationManager.ListenerService} handles intent actions
  * correctly.
  */
 @RunWith(BaseRobolectricTestRunner.class)
@@ -49,110 +46,6 @@
     }
 
     @Test
-    public void testProcessMediaButton_Play() {
-        setUpService();
-
-        mService.processAction(
-                createMediaButtonActionIntent(KeyEvent.KEYCODE_MEDIA_PLAY), getManager());
-        verify(getManager()).onPlay(MediaNotificationListener.ACTION_SOURCE_MEDIA_SESSION);
-    }
-
-    @Test
-    public void testProcessMediaButton_Pause() {
-        setUpService();
-
-        mService.processAction(
-                createMediaButtonActionIntent(KeyEvent.KEYCODE_MEDIA_PAUSE), getManager());
-        verify(getManager()).onPause(MediaNotificationListener.ACTION_SOURCE_MEDIA_SESSION);
-    }
-
-    @Test
-    public void testProcessMediaButton_HeadsetHook() {
-        setUpService();
-
-        mMediaNotificationInfoBuilder.setPaused(false);
-        getManager().mMediaNotificationInfo = mMediaNotificationInfoBuilder.build();
-
-        mService.processAction(
-                createMediaButtonActionIntent(KeyEvent.KEYCODE_HEADSETHOOK), getManager());
-        verify(getManager()).onPause(MediaNotificationListener.ACTION_SOURCE_MEDIA_SESSION);
-    }
-
-    @Test
-    public void testProcessMediaButton_PlayPause() {
-        setUpService();
-
-        mService.processAction(
-                createMediaButtonActionIntent(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE), getManager());
-        verify(getManager()).onPause(MediaNotificationListener.ACTION_SOURCE_MEDIA_SESSION);
-    }
-
-    @Test
-    public void testProcessMediaButton_Previous() {
-        setUpService();
-
-        mService.processAction(
-                createMediaButtonActionIntent(KeyEvent.KEYCODE_MEDIA_PREVIOUS), getManager());
-        verify(getManager()).onMediaSessionAction(MediaSessionAction.PREVIOUS_TRACK);
-    }
-
-    @Test
-    public void testProcessMediaButton_Next() {
-        setUpService();
-
-        mService.processAction(
-                createMediaButtonActionIntent(KeyEvent.KEYCODE_MEDIA_NEXT), getManager());
-        verify(getManager()).onMediaSessionAction(MediaSessionAction.NEXT_TRACK);
-    }
-
-    @Test
-    public void testProcessMediaButton_Rewind() {
-        setUpService();
-
-        mService.processAction(
-                createMediaButtonActionIntent(KeyEvent.KEYCODE_MEDIA_FAST_FORWARD), getManager());
-        verify(getManager()).onMediaSessionAction(MediaSessionAction.SEEK_FORWARD);
-    }
-
-    @Test
-    public void testProcessMediaButton_Backward() {
-        setUpService();
-
-        mService.processAction(
-                createMediaButtonActionIntent(KeyEvent.KEYCODE_MEDIA_REWIND), getManager());
-        verify(getManager()).onMediaSessionAction(MediaSessionAction.SEEK_BACKWARD);
-    }
-
-    @Test
-    public void testProcessMediaButtonActionWithNoKeyEvent() {
-        setUpService();
-
-        clearInvocations(getManager());
-        mService.processAction(new Intent(Intent.ACTION_MEDIA_BUTTON), getManager());
-
-        verifyZeroInteractions(getManager());
-    }
-
-    @Test
-    public void testProcessMediaButtonActionWithWrongTypeKeyEvent() {
-        setUpService();
-
-        clearInvocations(getManager());
-        mService.processAction(
-                new Intent(Intent.ACTION_MEDIA_BUTTON)
-                        .putExtra(Intent.EXTRA_KEY_EVENT,
-                                new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MEDIA_PLAY)),
-                getManager());
-        mService.processAction(new Intent(Intent.ACTION_MEDIA_BUTTON)
-                                       .putExtra(Intent.EXTRA_KEY_EVENT,
-                                               new KeyEvent(KeyEvent.ACTION_MULTIPLE,
-                                                       KeyEvent.KEYCODE_MEDIA_PLAY)),
-                getManager());
-
-        verifyZeroInteractions(getManager());
-    }
-
-    @Test
     public void testProcessNotificationButtonAction_Stop() {
         setUpService();
 
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/media/ui/MediaNotificationManagerTestBase.java b/chrome/android/junit/src/org/chromium/chrome/browser/media/ui/MediaNotificationManagerTestBase.java
index c2c9f925..d5e62f3 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/media/ui/MediaNotificationManagerTestBase.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/media/ui/MediaNotificationManagerTestBase.java
@@ -21,7 +21,6 @@
 import android.graphics.drawable.Icon;
 import android.os.Build;
 import android.support.v4.media.session.MediaSessionCompat;
-import android.view.KeyEvent;
 
 import org.junit.After;
 import org.junit.Before;
@@ -83,13 +82,6 @@
         }
     }
 
-    static class MockMediaButtonReceiver extends MediaButtonReceiver {
-        @Override
-        public Class<?> getServiceClass() {
-            return MockListenerService.class;
-        }
-    }
-
     @Before
     public void setUp() {
         // For checking the notification presented to NotificationManager.
@@ -103,8 +95,8 @@
         mListener = mock(MediaNotificationListener.class);
 
         MediaNotificationManager.sMapNotificationIdToOptions.put(getNotificationId(),
-                new MediaNotificationManager.NotificationOptions(MockListenerService.class,
-                        MockMediaButtonReceiver.class, NOTIFICATION_GROUP_NAME));
+                new MediaNotificationManager.NotificationOptions(
+                        MockListenerService.class, NOTIFICATION_GROUP_NAME));
 
         mMockUmaTracker = mock(NotificationUmaTracker.class);
         MediaNotificationManager.setManagerForTesting(getNotificationId(),
@@ -197,14 +189,6 @@
                 .stopListenerService();
     }
 
-    Intent createMediaButtonActionIntent(int keyCode) {
-        Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON);
-        KeyEvent keyEvent = new KeyEvent(KeyEvent.ACTION_DOWN, keyCode);
-        intent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
-
-        return intent;
-    }
-
     Bitmap iconToBitmap(Icon icon) {
         if (icon == null) return null;
 
diff --git a/chrome/android/webapk/libs/client/BUILD.gn b/chrome/android/webapk/libs/client/BUILD.gn
index 84eac57..2d537d0 100644
--- a/chrome/android/webapk/libs/client/BUILD.gn
+++ b/chrome/android/webapk/libs/client/BUILD.gn
@@ -43,6 +43,7 @@
   data = [ "//chrome/test/data/webapks/" ]
   deps = [
     ":client_java",
+    "//base:base_java",
     "//base:base_junit_test_support",
     "//chrome/android/webapk/libs/common:common_java",
     "//chrome/android/webapk/libs/runtime_library:webapk_service_aidl_java",
diff --git a/chrome/android/webapk/shell_apk/BUILD.gn b/chrome/android/webapk/shell_apk/BUILD.gn
index 04a3ce9..1466558 100644
--- a/chrome/android/webapk/shell_apk/BUILD.gn
+++ b/chrome/android/webapk/shell_apk/BUILD.gn
@@ -359,6 +359,8 @@
     "junit/src/org/chromium/webapk/shell_apk/WebApkUtilsTest.java",
   ]
   deps = [
+    ":compiled_in_runtime_library_java",
+    ":webapk_generated_webapk_java",
     ":webapk_generated_webapk_with_service_java",
     "//chrome/android/webapk/libs/common:common_java",
     "//chrome/android/webapk/libs/runtime_library:runtime_library_for_tests_java",
@@ -375,6 +377,7 @@
   ]
   deps = [
     ":${h2o_junit_manifest_target_name}",
+    ":h2o_j_unit_webapk_generated_webapk_java",
     ":h2o_j_unit_webapk_generated_webapk_with_service_java",
     "//chrome/android/webapk/libs/common:common_java",
     "//chrome/android/webapk/test:junit_test_support",
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp
index 679132be..d979979 100644
--- a/chrome/app/chromeos_strings.grdp
+++ b/chrome/app/chromeos_strings.grdp
@@ -1963,41 +1963,6 @@
     No language
   </message>
 
-  <!-- OOBE supervised user Change picture UI -->
-  <message name="IDS_OPTIONS_CHANGE_PICTURE_TAKE_PHOTO" desc="The text on the button to take photo of the current user.">
-    Take photo
-  </message>
-  <message name="IDS_OPTIONS_CHANGE_PICTURE_CAPTURE_VIDEO" desc="The text on the button to capture video of the current user.">
-    Capture video
-  </message>
-  <message name="IDS_OPTIONS_CHANGE_PICTURE_PHOTO_FROM_CAMERA" desc="The accessible text on the icon in the user image grid for a camera photo, when a photo has been captured.">
-    Photo from internal camera
-  </message>
-  <message name="IDS_OPTIONS_CHANGE_PICTURE_DISCARD_PHOTO" desc="The text on the button to discard the captured photo or video of the current user.">
-    Discard photo or video
-  </message>
-  <message name="IDS_OPTIONS_PHOTO_CAPTURE_ACCESSIBLE_TEXT" desc="The accessible message to speak to announce that a photo was captured.">
-    Photo was captured
-  </message>
-  <message name="IDS_OPTIONS_PHOTO_DISCARD_ACCESSIBLE_TEXT" desc="The accessible message to speak to announce that a photo was discarded.">
-    Photo was discarded
-  </message>
-  <message name="IDS_OPTIONS_CHANGE_PICTURE_SWITCH_MODE_TO_CAMERA" desc="The text on the button to switch the mode of the camera to photo.">
-    Switch to camera mode
-  </message>
-  <message name="IDS_OPTIONS_CHANGE_PICTURE_SWITCH_MODE_TO_VIDEO" desc="The text on the button to switch the mode of the camera to video.">
-    Switch to video recorder
-  </message>
-  <message name="IDS_IMAGE_SCREEN_PROFILE_PHOTO" desc="The title of the Google profile photo of the user on image selection screen. Please keep in sync with IDS_OPTIONS_CHANGE_PICTURE_PROFILE_PHOTO.">
-    Google Profile photo
-  </message>
-  <message name="IDS_IMAGE_SCREEN_PROFILE_LOADING_PHOTO" desc="The title of the loading stub for Google profile image on image selection screen. Please keep in sync with IDS_OPTIONS_CHANGE_PICTURE_PROFILE_LOADING_PHOTO.">
-    Google Profile photo (loading)
-  </message>
-  <message name="IDS_IMAGE_SCREEN_SYNCING_PREFERENCES" desc="The message displayed on the image screen of the GAIA flow while user preferences are syncing.">
-    Syncing your preferences...
-  </message>
-
   <!-- Slow UI -->
   <message name="IDS_SLOW_DISABLE" desc="The text of the button that disables performance collection for feedback reports">
     Disable performance data collection
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 3bb47f4..61dfad01 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -4621,6 +4621,12 @@
       <message name="IDS_EXTENSION_NTP_OVERRIDDEN_DIALOG_BODY_GENERIC" desc="The body of the dialog informing the user that the new tab page has been overridden by an extension, indicating which extension now controls it.">
         This page was changed by the <ph name="EXTENSION_NAME">$1<ex>Google Arts and Culture</ex></ph> extension
       </message>
+      <message name="IDS_EXTENSION_SEARCH_OVERRIDDEN_DIALOG_TITLE_GENERIC" desc="The title of the dialog informing the user that the new tab page has been overridden by an extension.">
+        Did you mean to change your search provider?
+      </message>
+      <message name="IDS_EXTENSION_SEARCH_OVERRIDDEN_DIALOG_BODY_GENERIC" desc="The body of the dialog informing the user that the new tab page has been overridden by an extension, indicating which extension now controls it.">
+        The <ph name="EXTENSION_NAME">$2<ex>Some New Search</ex></ph> extension changed search to use <ph name="SEARCH_PROVIDER_DOMAIN">$1<ex>google.com</ex></ph>
+      </message>
       <!-- End Extension Settings Overridden Dialog strings. -->
 
       <!-- Extensions Menu bubble -->
diff --git a/chrome/app/generated_resources_grd/IDS_EXTENSION_SEARCH_OVERRIDDEN_DIALOG_BODY_GENERIC.png.sha1 b/chrome/app/generated_resources_grd/IDS_EXTENSION_SEARCH_OVERRIDDEN_DIALOG_BODY_GENERIC.png.sha1
new file mode 100644
index 0000000..f116e3a
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_EXTENSION_SEARCH_OVERRIDDEN_DIALOG_BODY_GENERIC.png.sha1
@@ -0,0 +1 @@
+8a6343ef70fa82abb74d059084f03dee82bb8dc6
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_EXTENSION_SEARCH_OVERRIDDEN_DIALOG_TITLE_GENERIC.png.sha1 b/chrome/app/generated_resources_grd/IDS_EXTENSION_SEARCH_OVERRIDDEN_DIALOG_TITLE_GENERIC.png.sha1
new file mode 100644
index 0000000..f116e3a
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_EXTENSION_SEARCH_OVERRIDDEN_DIALOG_TITLE_GENERIC.png.sha1
@@ -0,0 +1 @@
+8a6343ef70fa82abb74d059084f03dee82bb8dc6
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings.grdp b/chrome/app/os_settings_strings.grdp
index 8626eb2..18d2725 100644
--- a/chrome/app/os_settings_strings.grdp
+++ b/chrome/app/os_settings_strings.grdp
@@ -793,11 +793,12 @@
     Manage your signed-in accounts. Websites, apps, and extensions in Chrome and Google Play may use these accounts to customize your experience, depending on permissions. <ph name="LINK_BEGIN">&lt;a&gt;</ph>Learn more<ph name="LINK_END">&lt;/a&gt;</ph>
   </message>
   <message name="IDS_SETTINGS_ACCOUNT_MANAGER_CHILD_DESCRIPTION" desc="Description of the Account Manager Settings page for child users. Shown just below the title of the page.">
-    Add a school account or manage child accounts here. <ph name="LINK_BEGIN">&lt;a&gt;</ph>Learn more<ph name="LINK_END">&lt;/a&gt;</ph>
+    Manage your accounts here. <ph name="LINK_BEGIN">&lt;a&gt;</ph>Learn more<ph name="LINK_END">&lt;/a&gt;</ph>
   </message>
-  <message name="IDS_SETTINGS_ACCOUNT_MANAGER_CHILD_DESCRIPTION_2" desc="Description of the Account Manager Settings page for child users. Shown just below the title of the page.">
-    Manage your accounts here. <ph name="LINK_BEGIN">&lt;a&gt;</ph>Learn more<ph name="LINK_END">&lt;/a&gt;</ph>&lt;br&gt;&lt;br&gt;
-    Bookmarks, passwords, and other browser data are synced with the primary account.&lt;br&gt;&lt;br&gt;
+  <message name="IDS_SETTINGS_ACCOUNT_MANAGER_CHILD_FIRST_MESSAGE" desc="Description of the Account Manager Settings page for child users. Shown just below the link to learn more about account management.">
+     Bookmarks, passwords, and other browser data are synced with the primary account.
+  </message>
+  <message name="IDS_SETTINGS_ACCOUNT_MANAGER_CHILD_SECOND_MESSAGE" desc="Description of the Account Manager Settings page for child users. Shown just below the link to learn more about account management.">
     Adding a school account enables easy sign-in to websites and extensions as a student while still operating under parental controls.
   </message>
   <message name="IDS_SETTINGS_ACCOUNT_MANAGER_LIST_HEADER" desc="List header for Account List in Account Manager Settings page.">
@@ -1100,10 +1101,10 @@
     Ports
   </message>
   <message name="IDS_SETTINGS_CROSTINI_MIC_TITLE" desc="Title for sharing mic with Crostini.">
-    Allow Linux apps to access the microphone
+    Allow Linux to access your microphone
   </message>
   <message name="IDS_SETTINGS_CROSTINI_MIC_DIALOG_LABEL" desc="Dialog label for sharing mic with Crostini.">
-    The change made to the microphone access requires Linux to restart. Shut down Linux to proceed.
+    The change in microphone setting requires Linux to shut down. Shut down Linux to proceed.
   </message>
   <message name="IDS_SETTINGS_CROSTINI_MIC_DIALOG_SHUTDOWN_BUTTON" desc="Label for the shutdown button in the Crostini mic sharing dialog.">
     Shut down
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_MIC_DIALOG_LABEL.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_MIC_DIALOG_LABEL.png.sha1
index b911b03..56aea6c 100644
--- a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_MIC_DIALOG_LABEL.png.sha1
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_MIC_DIALOG_LABEL.png.sha1
@@ -1 +1 @@
-2256ba32a9633ed3aea27de9637f0fdcda261de0
\ No newline at end of file
+6c6941186b10a819f5e133f5a8fdc3991786f24a
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_MIC_TITLE.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_MIC_TITLE.png.sha1
index 9185bcb..b431b89b 100644
--- a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_MIC_TITLE.png.sha1
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_MIC_TITLE.png.sha1
@@ -1 +1 @@
-43cc7d849d3cc002110273bb5cc133ee0f18416c
\ No newline at end of file
+9de1f7b23f79a3608c60869725a7ea0558b7b52c
\ No newline at end of file
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index ab686ee..b321c9a 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -455,6 +455,14 @@
     Password deleted
   </message>
   <!-- TODO(crbug.com/1062344): Make it translateable and add translation screenshots once final mocks are available. -->
+  <message translateable="false" name="IDS_SETTINGS_PASSWORD_STORED_ON_DEVICE" desc="Message displayed in the edit dialog for a password stored on the device.">
+    Your password is stored &lt;b&gt;on this device&lt;/b&gt;.
+  </message>
+  <!-- TODO(crbug.com/1062344): Make it translateable and add translation screenshots once final mocks are available. -->
+  <message translateable="false" name="IDS_SETTINGS_PASSWORD_STORED_IN_ACCOUNT" desc="Message displayed in the edit dialog for a password stored in the account.">
+    Your password is stored &lt;b&gt;in your Google Account&lt;/b&gt;.
+  </message>
+  <!-- TODO(crbug.com/1062344): Make it translateable and add translation screenshots once final mocks are available. -->
   <message translateable="false" name="IDS_SETTINGS_PASSWORD_DELETED_PASSWORD_FROM_ACCOUNT" desc="Label for an undo tooltip following deletion of a password from the account.">
     Password deleted from your Google Account
   </message>
diff --git a/chrome/app/theme/theme_resources.grd b/chrome/app/theme/theme_resources.grd
index 6e1f0e4..573a0fe 100644
--- a/chrome/app/theme/theme_resources.grd
+++ b/chrome/app/theme/theme_resources.grd
@@ -250,6 +250,8 @@
       </if>
       <if expr="chromeos">
         <structure type="chrome_scaled_image" name="IDR_RESET_WARNING" file="cros/reset_warning.png" />
+        <structure type="chrome_scaled_image" name="IDR_ENABLE_DEBUGGING_FAILURE" file="cros/enable_debugging_failure.png" />
+        <structure type="chrome_scaled_image" name="IDR_ENABLE_DEBUGGING_SUCCESS" file="cros/enable_debugging_success.png" />
       </if>
       <structure type="chrome_scaled_image" name="IDR_RESTORE_BUTTON_MASK" file="common/restore_button_mask.png" />
       <if expr="not is_android">
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 3c8efd5f..a1acd043 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -3205,14 +3205,6 @@
      kOsAll,
      FEATURE_VALUE_TYPE(
          autofill::features::kAutofillCreditCardAblationExperiment)},
-    {"enable-autofill-credit-card-upload-editable-expiration-date",
-     flag_descriptions::
-         kEnableAutofillCreditCardUploadEditableExpirationDateName,
-     flag_descriptions::
-         kEnableAutofillCreditCardUploadEditableExpirationDateDescription,
-     kOsAll,
-     FEATURE_VALUE_TYPE(
-         autofill::features::kAutofillUpstreamEditableExpirationDate)},
 
 #if defined(OS_ANDROID)
     {"enable-autofill-manual-fallback",
@@ -4629,12 +4621,6 @@
      flag_descriptions::kStrictOriginIsolationDescription, kOsAll,
      FEATURE_VALUE_TYPE(features::kStrictOriginIsolation)},
 
-    {"autofill-no-local-save-on-unmask-success",
-     flag_descriptions::kAutofillNoLocalSaveOnUnmaskSuccessName,
-     flag_descriptions::kAutofillNoLocalSaveOnUnmaskSuccessDescription, kOsAll,
-     FEATURE_VALUE_TYPE(
-         autofill::features::kAutofillNoLocalSaveOnUnmaskSuccess)},
-
 #if defined(OS_ANDROID)
     {"enable-logging-js-console-messages",
      flag_descriptions::kLogJsConsoleMessagesName,
diff --git a/chrome/browser/android/explore_sites/explore_sites_fetcher.cc b/chrome/browser/android/explore_sites/explore_sites_fetcher.cc
index ced913b..90176e4 100644
--- a/chrome/browser/android/explore_sites/explore_sites_fetcher.cc
+++ b/chrome/browser/android/explore_sites/explore_sites_fetcher.cc
@@ -89,7 +89,7 @@
         -1,          // Don't discard entry even if unused.
         false        // Don't use initial delay unless the last was an error.
 };
-const int ExploreSitesFetcher::kMaxFailureCountForBackgroundFetch = 7;
+const int ExploreSitesFetcher::kMaxFailureCountForBackgroundFetch = 2;
 
 // static
 std::unique_ptr<ExploreSitesFetcher> ExploreSitesFetcher::CreateForGetCatalog(
diff --git a/chrome/browser/android/explore_sites/explore_sites_fetcher_unittest.cc b/chrome/browser/android/explore_sites/explore_sites_fetcher_unittest.cc
index b93b5c3..cd857a3 100644
--- a/chrome/browser/android/explore_sites/explore_sites_fetcher_unittest.cc
+++ b/chrome/browser/android/explore_sites/explore_sites_fetcher_unittest.cc
@@ -436,30 +436,6 @@
   EXPECT_EQ(kTestData, data);
 }
 
-TEST_F(ExploreSitesFetcherTest, TwoBackoffsForBackgroundFetch) {
-  std::string data;
-  int initial_delay_ms =
-      ExploreSitesFetcher::kBackgroundFetchBackoffPolicy.initial_delay_ms;
-  std::vector<base::TimeDelta> backoff_delays = {
-      base::TimeDelta::FromMilliseconds(initial_delay_ms),
-      base::TimeDelta::FromMilliseconds(initial_delay_ms * 2)};
-  std::vector<base::OnceCallback<void(void)>> respond_callbacks;
-  respond_callbacks.push_back(
-      base::BindOnce(&ExploreSitesFetcherTest::RespondWithNetError,
-                     base::Unretained(this), net::ERR_INTERNET_DISCONNECTED));
-  respond_callbacks.push_back(
-      base::BindOnce(&ExploreSitesFetcherTest::RespondWithHttpError,
-                     base::Unretained(this), net::HTTP_INTERNAL_SERVER_ERROR));
-  respond_callbacks.push_back(
-      base::BindOnce(&ExploreSitesFetcherTest::RespondWithData,
-                     base::Unretained(this), kTestData));
-  EXPECT_EQ(
-      ExploreSitesRequestStatus::kSuccess,
-      RunFetcherWithBackoffs(false /*is_immediate_fetch*/, 2u, backoff_delays,
-                             std::move(respond_callbacks), &data));
-  EXPECT_EQ(kTestData, data);
-}
-
 TEST_F(ExploreSitesFetcherTest, ExceedMaxBackoffsForImmediateFetch) {
   std::string data;
   int delay_ms =
diff --git a/chrome/browser/android/feed/v2/feed_stream_surface.cc b/chrome/browser/android/feed/v2/feed_stream_surface.cc
index 482f636..a5b5e15 100644
--- a/chrome/browser/android/feed/v2/feed_stream_surface.cc
+++ b/chrome/browser/android/feed/v2/feed_stream_surface.cc
@@ -190,4 +190,10 @@
   feed_stream_api_->ReportStreamScrolled(distance_dp);
 }
 
+void FeedStreamSurface::ReportStreamScrollStart(
+    JNIEnv* env,
+    const JavaParamRef<jobject>& obj) {
+  feed_stream_api_->ReportStreamScrollStart();
+}
+
 }  // namespace feed
diff --git a/chrome/browser/android/feed/v2/feed_stream_surface.h b/chrome/browser/android/feed/v2/feed_stream_surface.h
index abb8dfa..70dda94 100644
--- a/chrome/browser/android/feed/v2/feed_stream_surface.h
+++ b/chrome/browser/android/feed/v2/feed_stream_surface.h
@@ -99,6 +99,8 @@
   void ReportStreamScrolled(JNIEnv* env,
                             const base::android::JavaParamRef<jobject>& obj,
                             int distance_dp);
+  void ReportStreamScrollStart(JNIEnv* env,
+                               const base::android::JavaParamRef<jobject>& obj);
 
  private:
   base::android::ScopedJavaGlobalRef<jobject> java_ref_;
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd
index 056df0e..9a47490c 100644
--- a/chrome/browser/browser_resources.grd
+++ b/chrome/browser/browser_resources.grd
@@ -224,7 +224,6 @@
         <include name="IDR_EDU_LOGIN_EDU_LOGIN_BUTTON_JS" file="${root_gen_dir}\chrome\browser\resources\chromeos\edu_login\edu_login_button.js" use_base_dir="false" type="BINDATA" compress="gzip" />
         <include name="IDR_EDU_LOGIN_EDU_LOGIN_TEMPLATE_JS" file="${root_gen_dir}\chrome\browser\resources\chromeos\edu_login\edu_login_template.js" use_base_dir="false" type="BINDATA" compress="gzip" />
         <include name="IDR_EDU_LOGIN_EDU_LOGIN_CSS_JS" file="${root_gen_dir}\chrome\browser\resources\chromeos\edu_login\edu_login_css.js" use_base_dir="false" type ="BINDATA" />
-        <include name="IDR_EDU_LOGIN_EDU_LOGIN_WELCOME_JS" file="${root_gen_dir}\chrome\browser\resources\chromeos\edu_login\edu_login_welcome.js" use_base_dir="false" type ="BINDATA" compress="gzip" preprocess="true" />
         <include name="IDR_EDU_LOGIN_EDU_LOGIN_PARENTS_JS" file="${root_gen_dir}\chrome\browser\resources\chromeos\edu_login\edu_login_parents.js" use_base_dir="false" type ="BINDATA" compress="gzip" preprocess="true" />
         <include name="IDR_EDU_LOGIN_EDU_LOGIN_PARENT_SIGNIN_JS" file="${root_gen_dir}\chrome\browser\resources\chromeos\edu_login\edu_login_parent_signin.js" use_base_dir="false" type ="BINDATA" compress="gzip" preprocess="true" />
         <include name="IDR_EDU_LOGIN_EDU_LOGIN_PARENT_INFO_JS" file="${root_gen_dir}\chrome\browser\resources\chromeos\edu_login\edu_login_parent_info.js" use_base_dir="false" type ="BINDATA" compress="gzip" preprocess="true" />
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index af01f3e..f7ecc02 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -2230,10 +2230,6 @@
         command_line->AppendSwitch(switches::kAllowSyncXHRInPageDismissal);
       }
 
-      if (prefs->HasPrefPath(prefs::kUseLegacyFormControls) &&
-          prefs->GetBoolean(prefs::kUseLegacyFormControls)) {
-        command_line->AppendSwitch(switches::kUseLegacyFormControls);
-      }
 
       if (prefs->HasPrefPath(prefs::kScrollToTextFragmentEnabled) &&
           !prefs->GetBoolean(prefs::kScrollToTextFragmentEnabled)) {
diff --git a/chrome/browser/chromeos/cert_provisioning/cert_provisioning_scheduler_user_service.h b/chrome/browser/chromeos/cert_provisioning/cert_provisioning_scheduler_user_service.h
index 4d0be0d..6370ead 100644
--- a/chrome/browser/chromeos/cert_provisioning/cert_provisioning_scheduler_user_service.h
+++ b/chrome/browser/chromeos/cert_provisioning/cert_provisioning_scheduler_user_service.h
@@ -19,6 +19,8 @@
   explicit CertProvisioningSchedulerUserService(Profile* profile);
   ~CertProvisioningSchedulerUserService() override;
 
+  CertProvisioningScheduler* scheduler() { return scheduler_.get(); }
+
  private:
   std::unique_ptr<CertProvisioningScheduler> scheduler_;
 };
diff --git a/chrome/browser/chromeos/extensions/printing/printing_apitest.cc b/chrome/browser/chromeos/extensions/printing/printing_apitest.cc
index 04dcee1b..f6efa49 100644
--- a/chrome/browser/chromeos/extensions/printing/printing_apitest.cc
+++ b/chrome/browser/chromeos/extensions/printing/printing_apitest.cc
@@ -18,6 +18,7 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "chromeos/printing/printer_configuration.h"
+#include "content/public/test/browser_test.h"
 #include "extensions/test/result_catcher.h"
 #include "printing/backend/print_backend.h"
 #include "printing/backend/test_print_backend.h"
diff --git a/chrome/browser/chromeos/login/screens/welcome_screen.h b/chrome/browser/chromeos/login/screens/welcome_screen.h
index d970c19..18cf413 100644
--- a/chrome/browser/chromeos/login/screens/welcome_screen.h
+++ b/chrome/browser/chromeos/login/screens/welcome_screen.h
@@ -70,6 +70,8 @@
   void AddObserver(Observer* observer);
   void RemoveObserver(Observer* observer);
 
+  base::Value* GetConfigurationForTesting() { return GetConfiguration(); }
+
  protected:
   // Exposes exit callback to test overrides.
   base::RepeatingClosure* exit_callback() { return &exit_callback_; }
diff --git a/chrome/browser/chromeos/login/session/user_session_manager.cc b/chrome/browser/chromeos/login/session/user_session_manager.cc
index 2fd3632..dff1988 100644
--- a/chrome/browser/chromeos/login/session/user_session_manager.cc
+++ b/chrome/browser/chromeos/login/session/user_session_manager.cc
@@ -2314,8 +2314,10 @@
   if (!release_notes_notification_) {
     release_notes_notification_ =
         std::make_unique<ReleaseNotesNotification>(profile);
-    if (chrome::GetChannel() == version_info::Channel::STABLE)
+    if (chrome::GetChannel() == version_info::Channel::STABLE ||
+        chrome::GetChannel() == version_info::Channel::BETA) {
       release_notes_notification_->MaybeShowReleaseNotes();
+    }
   }
 }
 
diff --git a/chrome/browser/chromeos/login/test/oobe_base_test.cc b/chrome/browser/chromeos/login/test/oobe_base_test.cc
index 1475ee8..d948534 100644
--- a/chrome/browser/chromeos/login/test/oobe_base_test.cc
+++ b/chrome/browser/chromeos/login/test/oobe_base_test.cc
@@ -113,13 +113,16 @@
 }
 
 void OobeBaseTest::WaitForOobeUI() {
+  // Wait for notification first. Otherwise LoginDisplayHost might not be
+  // created yet.
+  MaybeWaitForLoginScreenLoad();
+
   // Wait for OobeUI to finish loading.
   base::RunLoop run_loop;
   if (!LoginDisplayHost::default_host()->GetOobeUI()->IsJSReady(
           run_loop.QuitClosure())) {
     run_loop.Run();
   }
-  MaybeWaitForLoginScreenLoad();
 }
 
 void OobeBaseTest::WaitForGaiaPageLoad() {
diff --git a/chrome/browser/chromeos/login/wizard_controller_browsertest.cc b/chrome/browser/chromeos/login/wizard_controller_browsertest.cc
index be52725..47d6a88 100644
--- a/chrome/browser/chromeos/login/wizard_controller_browsertest.cc
+++ b/chrome/browser/chromeos/login/wizard_controller_browsertest.cc
@@ -169,11 +169,6 @@
   ~PrefStoreStub() override {}
 };
 
-// Matches on non-empty DictionaryValue* object.
-MATCHER(NonEmptyConfiguration, "") {
-  return arg && !arg->DictEmpty();
-}
-
 // Used to set up a |FakeAutoEnrollmentClientFactory| for the duration of a
 // test.
 class ScopedFakeAutoEnrollmentClientFactory {
@@ -373,19 +368,6 @@
     return host->GetOobeWebContents();
   }
 
-  void WaitUntilJSIsReady() {
-    LoginDisplayHost* host = LoginDisplayHost::default_host();
-    if (!host)
-      return;
-    chromeos::OobeUI* oobe_ui = host->GetOobeUI();
-    if (!oobe_ui)
-      return;
-    base::RunLoop run_loop;
-    const bool oobe_ui_ready = oobe_ui->IsJSReady(run_loop.QuitClosure());
-    if (!oobe_ui_ready)
-      run_loop.Run();
-  }
-
   bool JSExecute(const std::string& script) {
     return content::ExecuteScript(GetWebContents(), script);
   }
@@ -693,8 +675,6 @@
   void TestControlFlowMain() {
     CheckCurrentScreen(WelcomeView::kScreenId);
 
-    WaitUntilJSIsReady();
-
     test_url_loader_factory_.SetInterceptor(base::BindLambdaForTesting(
         [&](const network::ResourceRequest& request) {
           if (base::StartsWith(
@@ -1345,7 +1325,6 @@
   EXPECT_EQ(AutoEnrollmentCheckScreenView::kScreenId.AsId(),
             GetErrorScreen()->GetParentScreen());
 
-  WaitUntilJSIsReady();
   constexpr char guest_session_link_display[] =
       "window.getComputedStyle($('error-guest-signin-fix-network')).display";
   if (IsFREExplicitlyRequired()) {
@@ -1448,7 +1427,6 @@
     EXPECT_EQ(AutoEnrollmentCheckScreenView::kScreenId.AsId(),
               GetErrorScreen()->GetParentScreen());
 
-    WaitUntilJSIsReady();
     constexpr char guest_session_link_display[] =
         "window.getComputedStyle($('error-guest-signin-fix-network'))."
         "display";
@@ -1663,7 +1641,6 @@
   EXPECT_EQ(AutoEnrollmentCheckScreenView::kScreenId.AsId(),
             GetErrorScreen()->GetParentScreen());
 
-  WaitUntilJSIsReady();
   constexpr char guest_session_link_display[] =
       "window.getComputedStyle($('error-guest-signin-fix-network'))."
       "display";
@@ -2110,7 +2087,6 @@
   ASSERT_EQ(NetworkError::UI_STATE_LOCAL_STATE_ERROR,
             GetErrorScreen()->GetUIState());
 
-  WaitUntilJSIsReady();
   OobeScreenWaiter(ErrorScreenView::kScreenId).Wait();
 
   // Checks visibility of the error message and powerwash button.
@@ -2305,7 +2281,6 @@
 IN_PROC_BROWSER_TEST_F(WizardControllerEnableAdbSideloadingTest,
                        ShowAndEnableSideloading) {
   CheckCurrentScreen(WelcomeView::kScreenId);
-  WaitUntilJSIsReady();
 
   EXPECT_CALL(*mock_welcome_screen_, HideImpl()).Times(1);
   EXPECT_CALL(*mock_welcome_screen_, SetConfiguration(IsNull())).Times(1);
@@ -2334,7 +2309,6 @@
 IN_PROC_BROWSER_TEST_F(WizardControllerEnableAdbSideloadingTest,
                        ShowAndDoNotEnableSideloading) {
   CheckCurrentScreen(WelcomeView::kScreenId);
-  WaitUntilJSIsReady();
 
   EXPECT_CALL(*mock_welcome_screen_, HideImpl()).Times(1);
   EXPECT_CALL(*mock_welcome_screen_, SetConfiguration(IsNull())).Times(1);
@@ -2377,7 +2351,6 @@
 IN_PROC_BROWSER_TEST_F(WizardControllerEnableDebuggingTest,
                        ShowAndCancelEnableDebugging) {
   CheckCurrentScreen(WelcomeView::kScreenId);
-  WaitUntilJSIsReady();
 
   EXPECT_CALL(*mock_welcome_screen_, HideImpl()).Times(1);
   EXPECT_CALL(*mock_welcome_screen_, SetConfiguration(IsNull())).Times(1);
@@ -2430,7 +2403,6 @@
                        OnlineDemoSetupFlowFinished) {
   CheckCurrentScreen(WelcomeView::kScreenId);
   EXPECT_FALSE(DemoSetupController::IsOobeDemoSetupFlowInProgress());
-  WaitUntilJSIsReady();
 
   EXPECT_CALL(*mock_welcome_screen_, HideImpl()).Times(1);
   EXPECT_CALL(*mock_welcome_screen_, SetConfiguration(IsNull())).Times(1);
@@ -2505,7 +2477,6 @@
                        OfflineDemoSetupFlowFinished) {
   CheckCurrentScreen(WelcomeView::kScreenId);
   EXPECT_FALSE(DemoSetupController::IsOobeDemoSetupFlowInProgress());
-  WaitUntilJSIsReady();
 
   EXPECT_CALL(*mock_welcome_screen_, HideImpl()).Times(1);
   EXPECT_CALL(*mock_welcome_screen_, SetConfiguration(IsNull())).Times(1);
@@ -2563,7 +2534,6 @@
 IN_PROC_BROWSER_TEST_F(WizardControllerDemoSetupTest, DemoSetupCanceled) {
   CheckCurrentScreen(WelcomeView::kScreenId);
   EXPECT_FALSE(DemoSetupController::IsOobeDemoSetupFlowInProgress());
-  WaitUntilJSIsReady();
 
   EXPECT_CALL(*mock_welcome_screen_, HideImpl()).Times(1);
   EXPECT_CALL(*mock_welcome_screen_, SetConfiguration(IsNull())).Times(1);
@@ -2641,7 +2611,6 @@
 IN_PROC_BROWSER_TEST_F(WizardControllerDemoSetupTest, DemoPreferencesCanceled) {
   CheckCurrentScreen(WelcomeView::kScreenId);
   EXPECT_FALSE(DemoSetupController::IsOobeDemoSetupFlowInProgress());
-  WaitUntilJSIsReady();
   SkipToScreen(DemoPreferencesScreenView::kScreenId,
                mock_demo_preferences_screen_);
 
@@ -2661,7 +2630,6 @@
 IN_PROC_BROWSER_TEST_F(WizardControllerDemoSetupTest, NetworkBackPressed) {
   CheckCurrentScreen(WelcomeView::kScreenId);
   EXPECT_FALSE(DemoSetupController::IsOobeDemoSetupFlowInProgress());
-  WaitUntilJSIsReady();
   SkipToScreen(NetworkScreenView::kScreenId, mock_network_screen_);
 
   CheckCurrentScreen(NetworkScreenView::kScreenId);
@@ -2679,7 +2647,6 @@
 IN_PROC_BROWSER_TEST_F(WizardControllerDemoSetupTest, EulaBackPressed) {
   CheckCurrentScreen(WelcomeView::kScreenId);
   EXPECT_FALSE(DemoSetupController::IsOobeDemoSetupFlowInProgress());
-  WaitUntilJSIsReady();
   SkipToScreen(EulaView::kScreenId, mock_eula_screen_);
 
   CheckCurrentScreen(EulaView::kScreenId);
@@ -2697,7 +2664,6 @@
 IN_PROC_BROWSER_TEST_F(WizardControllerDemoSetupTest, ArcTosBackPressed) {
   CheckCurrentScreen(WelcomeView::kScreenId);
   EXPECT_FALSE(DemoSetupController::IsOobeDemoSetupFlowInProgress());
-  WaitUntilJSIsReady();
 
   // User cannot go to ARC ToS screen without accepting eula - simulate that.
   StartupUtils::MarkEulaAccepted();
@@ -2737,7 +2703,6 @@
                        OnlineDemoSetup) {
   CheckCurrentScreen(WelcomeView::kScreenId);
   EXPECT_FALSE(DemoSetupController::IsOobeDemoSetupFlowInProgress());
-  WaitUntilJSIsReady();
 
   EXPECT_CALL(*mock_welcome_screen_, HideImpl()).Times(1);
   EXPECT_CALL(*mock_welcome_screen_, SetConfiguration(IsNull())).Times(1);
@@ -2924,7 +2889,7 @@
 
   // WizardControllerTest:
   void SetUpCommandLine(base::CommandLine* command_line) override {
-    WizardControllerTest ::SetUpCommandLine(command_line);
+    WizardControllerTest::SetUpCommandLine(command_line);
 
     base::FilePath configuration_file;
     ASSERT_TRUE(chromeos::test_utils::GetTestDataPath(
@@ -2937,49 +2902,22 @@
   // WizardControllerTest:
   void SetUpOnMainThread() override {
     WizardControllerTest::SetUpOnMainThread();
-
-    // Make sure that OOBE is run as an "official" build.
-    branded_build_override_ = WizardController::ForceBrandedBuildForTesting();
-
     // Clear portal list (as it is by default in OOBE).
     NetworkHandler::Get()->network_state_handler()->SetCheckPortalList("");
-
-    mock_welcome_view_ = std::make_unique<MockWelcomeView>();
-    mock_welcome_screen_ =
-        MockScreenExpectLifecycle(std::make_unique<MockWelcomeScreen>(
-            mock_welcome_view_.get(),
-            base::BindRepeating(
-                &WizardController::OnWelcomeScreenExit,
-                base::Unretained(WizardController::default_controller()))));
   }
 
-  void WaitForConfigurationLoaded() {
-    base::RunLoop run_loop;
-    OOBEConfigurationWaiter waiter;
-    const bool ready = waiter.IsConfigurationLoaded(run_loop.QuitClosure());
-    if (!ready)
-      run_loop.Run();
-  }
-
- protected:
-  std::unique_ptr<MockWelcomeView> mock_welcome_view_;
-  MockWelcomeScreen* mock_welcome_screen_ = nullptr;
-  std::unique_ptr<base::AutoReset<bool>> branded_build_override_;
-
  private:
   DISALLOW_COPY_AND_ASSIGN(WizardControllerOobeConfigurationTest);
 };
 
-// TODO: fix, test is flaky. https://crbug.com/904841.
 IN_PROC_BROWSER_TEST_F(WizardControllerOobeConfigurationTest,
-                       DISABLED_ConfigurationIsLoaded) {
-  WaitForConfigurationLoaded();
-  EXPECT_CALL(*mock_welcome_screen_, ShowImpl()).Times(1);
-  EXPECT_CALL(*mock_welcome_screen_, SetConfiguration(NonEmptyConfiguration()))
-      .Times(1);
-  WizardController::default_controller()->AdvanceToScreen(
-      WelcomeView::kScreenId);
-  CheckCurrentScreen(WelcomeView::kScreenId);
+                       ConfigurationIsLoaded) {
+  OobeScreenWaiter(WelcomeView::kScreenId).Wait();
+  WelcomeScreen* screen = WelcomeScreen::Get(
+      WizardController::default_controller()->screen_manager());
+  base::Value* configuration = screen->GetConfigurationForTesting();
+  ASSERT_NE(configuration, nullptr);
+  EXPECT_FALSE(configuration->DictEmpty());
 }
 
 // TODO(dzhioev): Add test emulating device with wrong HWID.
diff --git a/chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h b/chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h
index 6cf773f..f9c1cff 100644
--- a/chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h
+++ b/chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h
@@ -179,6 +179,13 @@
     return hostname_handler_.get();
   }
 
+  // Return a pointer to the device-wide client certificate provisioning
+  // scheduler. The callers do not take ownership of that pointer.
+  chromeos::cert_provisioning::CertProvisioningScheduler*
+  GetDeviceCertProvisioningScheduler() {
+    return device_cert_provisioning_scheduler_.get();
+  }
+
   // Returns device's market segment.
   MarketSegment GetEnterpriseMarketSegment() const;
 
diff --git a/chrome/browser/chromeos/policy/device_policy_decoder_chromeos.cc b/chrome/browser/chromeos/policy/device_policy_decoder_chromeos.cc
index 99c9d73..60f275f 100644
--- a/chrome/browser/chromeos/policy/device_policy_decoder_chromeos.cc
+++ b/chrome/browser/chromeos/policy/device_policy_decoder_chromeos.cc
@@ -1566,17 +1566,6 @@
     }
   }
 
-  if (policy.has_device_login_screen_isolate_origins()) {
-    const em::DeviceLoginScreenIsolateOriginsProto& container(
-        policy.device_login_screen_isolate_origins());
-    if (container.has_isolate_origins()) {
-      policies->Set(
-          key::kDeviceLoginScreenIsolateOrigins, POLICY_LEVEL_MANDATORY,
-          POLICY_SCOPE_MACHINE, POLICY_SOURCE_CLOUD,
-          std::make_unique<base::Value>(container.isolate_origins()), nullptr);
-    }
-  }
-
   if (policy.has_virtual_machines_allowed()) {
     const em::VirtualMachinesAllowedProto& container(
         policy.virtual_machines_allowed());
diff --git a/chrome/browser/download/android/BUILD.gn b/chrome/browser/download/android/BUILD.gn
index 38b102b..263c21f 100644
--- a/chrome/browser/download/android/BUILD.gn
+++ b/chrome/browser/download/android/BUILD.gn
@@ -55,6 +55,8 @@
     "//components/offline_items_collection/core:core_java",
     "//content/public/android:content_java",
     "//third_party/android_deps:androidx_core_core_java",
+    "//third_party/android_deps:androidx_fragment_fragment_java",
+    "//third_party/android_deps:androidx_preference_preference_java",
     "//third_party/android_deps:com_google_android_material_material_java",
     "//ui/android:ui_java",
   ]
@@ -94,6 +96,7 @@
     "//base:base_java",
     "//base:base_java_test_support",
     "//chrome/test/android:chrome_java_test_support",
+    "//components/browser_ui/util/android:java",
     "//components/offline_items_collection/core:core_java",
     "//third_party/android_support_test_runner:runner_java",
     "//third_party/junit",
diff --git a/chrome/browser/extensions/settings_api_bubble_delegate.cc b/chrome/browser/extensions/settings_api_bubble_delegate.cc
index 93f1870..265cdce 100644
--- a/chrome/browser/extensions/settings_api_bubble_delegate.cc
+++ b/chrome/browser/extensions/settings_api_bubble_delegate.cc
@@ -25,10 +25,6 @@
 
 namespace {
 
-// Whether the user has been notified about extension taking over some aspect of
-// the user's settings (homepage, startup pages, or search engine).
-const char kSettingsBubbleAcknowledged[] = "ack_settings_bubble";
-
 using ProfileSetMap = std::map<std::string, std::set<Profile*>>;
 base::LazyInstance<ProfileSetMap>::Leaky g_settings_api_shown =
     LAZY_INSTANCE_INITIALIZER;
@@ -41,9 +37,12 @@
     : ExtensionMessageBubbleController::Delegate(profile),
       type_(type),
       profile_(profile) {
-  set_acknowledged_flag_pref_name(kSettingsBubbleAcknowledged);
+  set_acknowledged_flag_pref_name(kAcknowledgedPreference);
 }
 
+const char SettingsApiBubbleDelegate::kAcknowledgedPreference[] =
+    "ack_settings_bubble";
+
 SettingsApiBubbleDelegate::~SettingsApiBubbleDelegate() {}
 
 bool SettingsApiBubbleDelegate::ShouldIncludeExtension(
diff --git a/chrome/browser/extensions/settings_api_bubble_delegate.h b/chrome/browser/extensions/settings_api_bubble_delegate.h
index e6749179..6b3da095 100644
--- a/chrome/browser/extensions/settings_api_bubble_delegate.h
+++ b/chrome/browser/extensions/settings_api_bubble_delegate.h
@@ -21,6 +21,13 @@
   SettingsApiBubbleDelegate(Profile* profile, SettingsApiOverrideType type);
   ~SettingsApiBubbleDelegate() override;
 
+  // The preference used to indicate if the user has acknowledged the extension
+  // taking over some aspect of the user's settings (homepage, startup pages,
+  // or search engine).
+  // TODO(devlin): We currently use one preference for all of these, but that's
+  // probably not desirable.
+  static const char kAcknowledgedPreference[];
+
   // ExtensionMessageBubbleController::Delegate methods.
   bool ShouldIncludeExtension(const Extension* extension) override;
   void AcknowledgeExtension(
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index c74190f..f9a2d29 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -2209,7 +2209,7 @@
   {
     "name": "enable-touch-drag-drop",
     "owners": [ "nzolghadr", "input-dev" ],
-    "expiry_milestone": 82
+    "expiry_milestone": 88
   },
   {
     "name": "enable-touchscreen-calibration",
@@ -3746,7 +3746,7 @@
   {
     "name": "safety-tips",
     "owners": [ "jdeblasio", "estark", "meacer" ],
-    "expiry_milestone": 84
+    "expiry_milestone": 87
   },
   {
     "name": "same-site-by-default-cookies",
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 0d18ab5..e84c17d 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -248,12 +248,6 @@
     "When enabled, autofill will generally require a form to have at least 3 "
     "fillable fields before uploading field-type votes for that form.";
 
-const char kAutofillNoLocalSaveOnUnmaskSuccessName[] =
-    "Remove the option to save local copies of unmasked server cards";
-const char kAutofillNoLocalSaveOnUnmaskSuccessDescription[] =
-    "When enabled, the server card unmask prompt will not include the checkbox "
-    "to also save the card locally on the current device upon success.";
-
 const char kAutofillOffNoServerDataName[] = "Autofill Off No Server Data";
 const char kAutofillOffNoServerDataDescription[] =
     "Disables Autofill for fields with autocomplete off that have no "
@@ -518,13 +512,6 @@
     "authenticator (if available) to verify card ownership when retrieving "
     "credit cards from Google Payments.";
 
-const char kEnableAutofillCreditCardUploadEditableExpirationDateName[] =
-    "Make expiration date editable in dialog during credit card upload";
-const char kEnableAutofillCreditCardUploadEditableExpirationDateDescription[] =
-    "If enabled, if a credit card's expiration date was not detected when "
-    "offering card upload to Google Payments, the offer-to-save dialog "
-    "displays an expiration date selector.";
-
 const char kEnableAutofillCreditCardUploadFeedbackName[] =
     "Enable feedback for credit card upload flow";
 const char kEnableAutofillCreditCardUploadFeedbackDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 004ea83c..a0fb8a8 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -150,9 +150,6 @@
 extern const char kAutofillEnforceMinRequiredFieldsForUploadName[];
 extern const char kAutofillEnforceMinRequiredFieldsForUploadDescription[];
 
-extern const char kAutofillNoLocalSaveOnUnmaskSuccessName[];
-extern const char kAutofillNoLocalSaveOnUnmaskSuccessDescription[];
-
 extern const char kAutofillOffNoServerDataName[];
 extern const char kAutofillOffNoServerDataDescription[];
 
@@ -319,10 +316,6 @@
 extern const char kEnableAutofillCreditCardAuthenticationName[];
 extern const char kEnableAutofillCreditCardAuthenticationDescription[];
 
-extern const char kEnableAutofillCreditCardUploadEditableExpirationDateName[];
-extern const char
-    kEnableAutofillCreditCardUploadEditableExpirationDateDescription[];
-
 extern const char kEnableAutofillCreditCardUploadFeedbackName[];
 extern const char kEnableAutofillCreditCardUploadFeedbackDescription[];
 
diff --git a/chrome/browser/flags/BUILD.gn b/chrome/browser/flags/BUILD.gn
index a8481265..d26786b 100644
--- a/chrome/browser/flags/BUILD.gn
+++ b/chrome/browser/flags/BUILD.gn
@@ -64,6 +64,7 @@
     "//base:base_junit_test_support",
     "//base/test:test_support_java",
     "//chrome/test/android:chrome_java_test_support",
+    "//third_party/junit",
   ]
 }
 
diff --git a/chrome/browser/media/router/providers/cast/cast_media_route_provider.cc b/chrome/browser/media/router/providers/cast/cast_media_route_provider.cc
index fedd73b..43d5111 100644
--- a/chrome/browser/media/router/providers/cast/cast_media_route_provider.cc
+++ b/chrome/browser/media/router/providers/cast/cast_media_route_provider.cc
@@ -9,7 +9,6 @@
 
 #include "base/bind.h"
 #include "base/stl_util.h"
-#include "base/strings/strcat.h"
 #include "base/task/post_task.h"
 #include "chrome/browser/media/router/providers/cast/cast_activity_manager.h"
 #include "chrome/browser/media/router/providers/cast/cast_internal_message_util.h"
@@ -23,6 +22,25 @@
 
 namespace media_router {
 
+namespace {
+
+// Returns a list of origins that are valid for |source_id|. An empty list
+// means all origins are valid.
+std::vector<url::Origin> GetOrigins(const MediaSource::Id& source_id) {
+  // Use of the mirroring app as a Cast URL is permitted for Slides as a
+  // temporary workaround only. The eventual goal is to support their usecase
+  // using generic Presentation API.
+  // See also cast_media_source.cc.
+  static const char kMirroringAppPrefix[] = "cast:0F5096E8";
+  return base::StartsWith(source_id, kMirroringAppPrefix,
+                          base::CompareCase::SENSITIVE)
+             ? std::vector<url::Origin>(
+                   {url::Origin::Create(GURL("https://docs.google.com"))})
+             : std::vector<url::Origin>();
+}
+
+}  // namespace
+
 CastMediaRouteProvider::CastMediaRouteProvider(
     mojo::PendingReceiver<mojom::MediaRouteProvider> receiver,
     mojo::PendingRemote<mojom::MediaRouter> media_router,
@@ -94,23 +112,16 @@
     return;
   }
 
-  const auto cast_source = CastMediaSource::FromMediaSourceId(source_id);
+  std::unique_ptr<CastMediaSource> cast_source =
+      CastMediaSource::FromMediaSourceId(source_id);
   if (!cast_source) {
     DVLOG(2) << "CreateRoute: invalid source";
     std::move(callback).Run(
-        base::nullopt, nullptr, base::StrCat({"Invalid source ", source_id}),
+        base::nullopt, nullptr, std::string("Invalid source"),
         RouteRequestResult::ResultCode::NO_SUPPORTED_PROVIDER);
     return;
   }
 
-  if (!cast_source->IsAllowedOrigin(origin)) {
-    std::move(callback).Run(
-        base::nullopt, nullptr,
-        base::StrCat({"Invalid origin ", origin.Serialize()}),
-        RouteRequestResult::ResultCode::INVALID_ORIGIN);
-    return;
-  }
-
   activity_manager_->LaunchSession(*cast_source, *sink, presentation_id, origin,
                                    tab_id, incognito, std::move(callback));
 }
@@ -122,10 +133,11 @@
                                        base::TimeDelta timeout,
                                        bool incognito,
                                        JoinRouteCallback callback) {
-  const auto cast_source = CastMediaSource::FromMediaSourceId(media_source);
+  std::unique_ptr<CastMediaSource> cast_source =
+      CastMediaSource::FromMediaSourceId(media_source);
   if (!cast_source) {
     std::move(callback).Run(
-        base::nullopt, nullptr, base::StrCat({"Invalid source ", media_source}),
+        base::nullopt, nullptr, std::string("Invalid source"),
         RouteRequestResult::ResultCode::NO_SUPPORTED_PROVIDER);
     return;
   }
@@ -175,7 +187,8 @@
   if (base::Contains(sink_queries_, media_source))
     return;
 
-  const auto cast_source = CastMediaSource::FromMediaSourceId(media_source);
+  std::unique_ptr<CastMediaSource> cast_source =
+      CastMediaSource::FromMediaSourceId(media_source);
   if (!cast_source)
     return;
 
@@ -278,11 +291,8 @@
     const std::vector<MediaSinkInternal>& sinks) {
   DVLOG(1) << __func__ << ", source_id: " << source_id
            << ", #sinks: " << sinks.size();
-  const auto cast_source = CastMediaSource::FromMediaSourceId(source_id);
-  if (cast_source) {
-    media_router_->OnSinksReceived(MediaRouteProviderId::CAST, source_id, sinks,
-                                   cast_source->GetAllowedOrigins());
-  }
+  media_router_->OnSinksReceived(MediaRouteProviderId::CAST, source_id, sinks,
+                                 GetOrigins(source_id));
 }
 
 void CastMediaRouteProvider::BroadcastMessageToSinks(
diff --git a/chrome/browser/media/router/providers/cast/cast_media_route_provider_unittest.cc b/chrome/browser/media/router/providers/cast/cast_media_route_provider_unittest.cc
index c22d340..cc3985d6 100644
--- a/chrome/browser/media/router/providers/cast/cast_media_route_provider_unittest.cc
+++ b/chrome/browser/media/router/providers/cast/cast_media_route_provider_unittest.cc
@@ -6,7 +6,6 @@
 
 #include "base/bind.h"
 #include "base/run_loop.h"
-#include "base/strings/strcat.h"
 #include "base/test/test_simple_task_runner.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "chrome/browser/media/router/providers/cast/cast_session_tracker.h"
@@ -90,17 +89,6 @@
     route_ = std::make_unique<MediaRoute>(*route);
   }
 
-  void ExpectCreateRouteSuccessWithoutConnections(
-      const base::Optional<MediaRoute>& route,
-      mojom::RoutePresentationConnectionPtr presentation_connections,
-      const base::Optional<std::string>& error,
-      RouteRequestResult::ResultCode result) {
-    EXPECT_TRUE(route);
-    EXPECT_FALSE(presentation_connections);
-    EXPECT_FALSE(error);
-    EXPECT_EQ(RouteRequestResult::ResultCode::OK, result);
-  }
-
   void ExpectCreateRouteFailure(
       RouteRequestResult::ResultCode expected_result,
       const base::Optional<MediaRoute>& route,
@@ -195,66 +183,6 @@
                      RouteRequestResult::ResultCode::NO_SUPPORTED_PROVIDER));
 }
 
-TEST_F(CastMediaRouteProviderTest, CreateRouteForStreamingFailsInvalidOrigin) {
-  MediaSinkInternal sink = CreateCastSink(1);
-  media_sink_service_.AddOrUpdateSink(sink);
-
-  const auto streaming_source = CastMediaSource::FromAppId(kCastStreamingAppId);
-
-  // origin_ == https://www.youtube.com is not allowed to launch streaming apps.
-  provider_->CreateRoute(
-      streaming_source->source_id(), sink.sink().id(), kPresentationId, origin_,
-      kTabId, kRouteTimeout, /* incognito */ false,
-      base::BindOnce(&CastMediaRouteProviderTest::ExpectCreateRouteFailure,
-                     base::Unretained(this),
-                     RouteRequestResult::ResultCode::INVALID_ORIGIN));
-}
-
-TEST_F(CastMediaRouteProviderTest,
-       CreateRouteForStreamingSucceedsForWhitelistedOrigin) {
-  MediaSinkInternal sink = CreateCastSink(1);
-  media_sink_service_.AddOrUpdateSink(sink);
-
-  const auto streaming_source = CastMediaSource::FromMediaSourceId(
-      base::StrCat({"cast:", kCastStreamingAppId, "?clientId=12345"}));
-
-  EXPECT_CALL(message_handler_, LaunchSession(sink.cast_data().cast_channel_id,
-                                              kCastStreamingAppId,
-                                              kDefaultLaunchTimeout, _, _, _));
-  // Whitelisted origins are allowed to launch streaming apps.
-  provider_->CreateRoute(
-      streaming_source->source_id(), sink.sink().id(), kPresentationId,
-      url::Origin::Create(GURL("https://meet.google.com")), kTabId,
-      kRouteTimeout, /* incognito */ false,
-      base::BindOnce(
-          &CastMediaRouteProviderTest::ExpectCreateRouteSuccessAndSetRoute,
-          base::Unretained(this)));
-}
-
-TEST_F(CastMediaRouteProviderTest,
-       CreateRouteForTabMirroringSucceedsForEmptyOrigin) {
-  MediaSinkInternal sink = CreateCastSink(1);
-  media_sink_service_.AddOrUpdateSink(sink);
-
-  const auto tab_mirroring_source =
-      CastMediaSource::FromMediaSource(MediaSource::ForTab(kTabId));
-
-  EXPECT_CALL(message_handler_, LaunchSession(sink.cast_data().cast_channel_id,
-                                              kCastStreamingAppId,
-                                              kDefaultLaunchTimeout, _, _, _));
-
-  // Empty origins, passed by the browser UI, are allowed to initiate tab
-  // mirroring.
-  // TODO(crbug.com/1047834): Check a specific origin for browser requested
-  // mirroring.
-  provider_->CreateRoute(
-      tab_mirroring_source->source_id(), sink.sink().id(), kPresentationId,
-      url::Origin::Create(GURL()), kTabId, kRouteTimeout, /* incognito */ false,
-      base::BindOnce(&CastMediaRouteProviderTest::
-                         ExpectCreateRouteSuccessWithoutConnections,
-                     base::Unretained(this)));
-}
-
 TEST_F(CastMediaRouteProviderTest, CreateRoute) {
   MediaSinkInternal sink = CreateCastSink(1);
   media_sink_service_.AddOrUpdateSink(sink);
diff --git a/chrome/browser/net/chrome_mojo_proxy_resolver_factory.cc b/chrome/browser/net/chrome_mojo_proxy_resolver_factory.cc
index 1347508..99dfc41 100644
--- a/chrome/browser/net/chrome_mojo_proxy_resolver_factory.cc
+++ b/chrome/browser/net/chrome_mojo_proxy_resolver_factory.cc
@@ -12,6 +12,7 @@
 #include "base/single_thread_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
+#include "chrome/browser/service_sandbox_type.h"
 #include "content/public/common/child_process_host.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "mojo/public/cpp/bindings/self_owned_receiver.h"
@@ -24,10 +25,6 @@
 #include "services/strings/grit/services_strings.h"
 #endif
 
-#if defined(OS_WIN)
-#include "services/service_manager/sandbox/sandbox_type.h"
-#endif
-
 namespace {
 
 proxy_resolver::mojom::ProxyResolverFactory* GetProxyResolverFactory() {
@@ -47,9 +44,6 @@
         remote->BindNewPipeAndPassReceiver(),
         content::ServiceProcessHost::Options()
             .WithDisplayName(IDS_PROXY_RESOLVER_DISPLAY_NAME)
-#if defined(OS_WIN)
-            .WithSandboxType(service_manager::SandboxType::kProxyResolver)
-#endif
             .Pass());
 
     // The service will report itself idle once there are no more bound
diff --git a/chrome/browser/offline_pages/android/BUILD.gn b/chrome/browser/offline_pages/android/BUILD.gn
index a5b1a67f..f455eb6 100644
--- a/chrome/browser/offline_pages/android/BUILD.gn
+++ b/chrome/browser/offline_pages/android/BUILD.gn
@@ -8,6 +8,7 @@
   sources = [ "java/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchConfiguration.java" ]
 
   deps = [
+    "//base:jni_java",
     "//chrome/browser/flags:java",
     "//chrome/browser/profiles/android:java",
   ]
diff --git a/chrome/browser/optimization_guide/android/BUILD.gn b/chrome/browser/optimization_guide/android/BUILD.gn
index 2fecfe2..ff706ea 100644
--- a/chrome/browser/optimization_guide/android/BUILD.gn
+++ b/chrome/browser/optimization_guide/android/BUILD.gn
@@ -60,6 +60,8 @@
     "//base:base_java_test_support",
     "//base:base_junit_test_support",
     "//chrome/android:chrome_test_util_java",
+    "//components/optimization_guide/proto:optimization_guide_proto_java",
+    "//content/public/android:content_java",
     "//third_party/junit",
     "//third_party/mockito:mockito_java",
   ]
diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
index 906653c..55dea91 100644
--- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc
+++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
@@ -345,9 +345,6 @@
   { key::kSendFilesForMalwareCheck,
     prefs::kSafeBrowsingSendFilesForMalwareCheck,
     base::Value::Type::INTEGER },
-  { key::kUseLegacyFormControls,
-    prefs::kUseLegacyFormControls,
-    base::Value::Type::BOOLEAN },
   { key::kAmbientAuthenticationInPrivateModesEnabled,
     prefs::kAmbientAuthenticationInPrivateModesEnabled,
     base::Value::Type::INTEGER },
diff --git a/chrome/browser/preferences/BUILD.gn b/chrome/browser/preferences/BUILD.gn
index 5491897..1a48601 100644
--- a/chrome/browser/preferences/BUILD.gn
+++ b/chrome/browser/preferences/BUILD.gn
@@ -51,5 +51,7 @@
     "//base:base_java_test_support",
     "//base:base_junit_test_support",
     "//base/test:test_support_java",
+    "//third_party/junit:junit",
+    "//third_party/mockito:mockito_java",
   ]
 }
diff --git a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java
index 92b6afd..7096280 100644
--- a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java
+++ b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java
@@ -529,6 +529,13 @@
     public static final String SEARCH_ENGINE_CHOICE_REQUESTED_TIMESTAMP =
             "search_engine_choice_requested_timestamp";
 
+    public static final String SEARCH_WIDGET_IS_VOICE_SEARCH_AVAILABLE =
+            "org.chromium.chrome.browser.searchwidget.IS_VOICE_SEARCH_AVAILABLE";
+    public static final String SEARCH_WIDGET_NUM_CONSECUTIVE_CRASHES =
+            "org.chromium.chrome.browser.searchwidget.NUM_CONSECUTIVE_CRASHES";
+    public static final String SEARCH_WIDGET_SEARCH_ENGINE_SHORTNAME =
+            "org.chromium.chrome.browser.searchwidget.SEARCH_ENGINE_SHORTNAME";
+
     // Tracks which GUIDs there is an active notification for.
     public static final String SEND_TAB_TO_SELF_ACTIVE_NOTIFICATIONS =
             "send_tab_to_self.notification.active";
diff --git a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/GrandfatheredChromePreferenceKeys.java b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/GrandfatheredChromePreferenceKeys.java
index ca5196f..8c7d547 100644
--- a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/GrandfatheredChromePreferenceKeys.java
+++ b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/GrandfatheredChromePreferenceKeys.java
@@ -145,6 +145,9 @@
                 ChromePreferenceKeys.SEARCH_ENGINE_CHOICE_DEFAULT_TYPE_BEFORE,
                 ChromePreferenceKeys.SEARCH_ENGINE_CHOICE_PRESENTED_VERSION,
                 ChromePreferenceKeys.SEARCH_ENGINE_CHOICE_REQUESTED_TIMESTAMP,
+                ChromePreferenceKeys.SEARCH_WIDGET_IS_VOICE_SEARCH_AVAILABLE,
+                ChromePreferenceKeys.SEARCH_WIDGET_NUM_CONSECUTIVE_CRASHES,
+                ChromePreferenceKeys.SEARCH_WIDGET_SEARCH_ENGINE_SHORTNAME,
                 ChromePreferenceKeys.SEND_TAB_TO_SELF_ACTIVE_NOTIFICATIONS,
                 ChromePreferenceKeys.SEND_TAB_TO_SELF_NEXT_NOTIFICATION_ID,
                 ChromePreferenceKeys.SETTINGS_DEVELOPER_ENABLED,
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index bf68b12..95485d7 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -89,7 +89,6 @@
 #include "chrome/browser/web_applications/web_app_provider.h"
 #include "chrome/browser/webauthn/chrome_authenticator_request_delegate.h"
 #include "chrome/common/buildflags.h"
-#include "chrome/common/chrome_ui_features_prefs.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/common/secure_origin_whitelist.h"
 #include "components/autofill/core/common/autofill_prefs.h"
@@ -840,7 +839,6 @@
   chrome_browser_net::NetErrorTabHelper::RegisterProfilePrefs(registry);
   chrome_browser_net::RegisterPredictionOptionsProfilePrefs(registry);
   chrome_prefs::RegisterProfilePrefs(registry);
-  chrome_ui_features_prefs::RegisterProfilePrefs(registry);
   DocumentProvider::RegisterProfilePrefs(registry);
   dom_distiller::DistilledPagePrefs::RegisterProfilePrefs(registry);
   dom_distiller::RegisterProfilePrefs(registry);
diff --git a/chrome/browser/printing/print_preview_message_handler.cc b/chrome/browser/printing/print_preview_message_handler.cc
index a32714a..c38a1cf 100644
--- a/chrome/browser/printing/print_preview_message_handler.cc
+++ b/chrome/browser/printing/print_preview_message_handler.cc
@@ -23,6 +23,7 @@
 #include "chrome/browser/printing/printer_query.h"
 #include "chrome/browser/ui/webui/print_preview/print_preview_ui.h"
 #include "components/printing/browser/print_composite_client.h"
+#include "components/printing/common/print.mojom.h"
 #include "components/printing/common/print_messages.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
@@ -104,7 +105,7 @@
 }
 
 void PrintPreviewMessageHandler::OnDidStartPreview(
-    const PrintHostMsg_DidStartPreview_Params& params,
+    const mojom::DidStartPreviewParams& params,
     const PrintHostMsg_PreviewIds& ids) {
   if (params.page_count <= 0 || params.pages_to_render.empty()) {
     NOTREACHED();
diff --git a/chrome/browser/printing/print_preview_message_handler.h b/chrome/browser/printing/print_preview_message_handler.h
index 6d8b0a3..3e8629e 100644
--- a/chrome/browser/printing/print_preview_message_handler.h
+++ b/chrome/browser/printing/print_preview_message_handler.h
@@ -9,13 +9,13 @@
 #include "base/memory/read_only_shared_memory_region.h"
 #include "base/memory/weak_ptr.h"
 #include "chrome/services/printing/public/mojom/pdf_nup_converter.mojom.h"
+#include "components/printing/common/print.mojom-forward.h"
 #include "components/services/print_compositor/public/mojom/print_compositor.mojom.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/browser/web_contents_user_data.h"
 
 struct PrintHostMsg_DidPreviewDocument_Params;
 struct PrintHostMsg_DidPreviewPage_Params;
-struct PrintHostMsg_DidStartPreview_Params;
 struct PrintHostMsg_PreviewIds;
 struct PrintHostMsg_RequestPrintPreview_Params;
 
@@ -69,7 +69,7 @@
                                  const gfx::Rect& printable_area_in_points,
                                  bool has_custom_page_size_style,
                                  const PrintHostMsg_PreviewIds& ids);
-  void OnDidStartPreview(const PrintHostMsg_DidStartPreview_Params& params,
+  void OnDidStartPreview(const mojom::DidStartPreviewParams& params,
                          const PrintHostMsg_PreviewIds& ids);
   void OnDidPreviewPage(content::RenderFrameHost* render_frame_host,
                         const PrintHostMsg_DidPreviewPage_Params& params,
diff --git a/chrome/browser/printing/print_preview_pdf_generated_browsertest.cc b/chrome/browser/printing/print_preview_pdf_generated_browsertest.cc
index 84ff527..a5ac123 100644
--- a/chrome/browser/printing/print_preview_pdf_generated_browsertest.cc
+++ b/chrome/browser/printing/print_preview_pdf_generated_browsertest.cc
@@ -39,6 +39,7 @@
 #include "chrome/common/chrome_paths.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
+#include "components/printing/common/print.mojom.h"
 #include "components/printing/common/print_messages.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_ui_message_handler.h"
@@ -269,7 +270,7 @@
 
   // Called when the observer gets the IPC message with the preview document's
   // properties.
-  void OnDidStartPreview(const PrintHostMsg_DidStartPreview_Params& params,
+  void OnDidStartPreview(const mojom::DidStartPreviewParams& params,
                          const PrintHostMsg_PreviewIds& ids) {
     WebContents* web_contents = GetDialog();
     ASSERT_TRUE(web_contents);
diff --git a/chrome/browser/printing/printing_service.cc b/chrome/browser/printing/printing_service.cc
index 1c9e085..2b73b11 100644
--- a/chrome/browser/printing/printing_service.cc
+++ b/chrome/browser/printing/printing_service.cc
@@ -5,7 +5,7 @@
 #include "chrome/browser/printing/printing_service.h"
 
 #include "base/no_destructor.h"
-#include "build/build_config.h"
+#include "chrome/browser/service_sandbox_type.h"
 #include "chrome/grit/generated_resources.h"
 #include "content/public/browser/service_process_host.h"
 
@@ -17,9 +17,6 @@
         remote->BindNewPipeAndPassReceiver(),
         content::ServiceProcessHost::Options()
             .WithDisplayName(IDS_UTILITY_PROCESS_PRINTING_SERVICE_NAME)
-#if defined(OS_WIN)
-            .WithSandboxType(service_manager::SandboxType::kPdfConversion)
-#endif
             .Pass());
 
     // Ensure that if the interface is ever disconnected (e.g. the service
diff --git a/chrome/browser/renderer_context_menu/quick_answers_menu_observer.cc b/chrome/browser/renderer_context_menu/quick_answers_menu_observer.cc
index c34333ab2..90a657c 100644
--- a/chrome/browser/renderer_context_menu/quick_answers_menu_observer.cc
+++ b/chrome/browser/renderer_context_menu/quick_answers_menu_observer.cc
@@ -133,7 +133,7 @@
   if (!IsRichUiEnabled())
     return;
 
-  if (!is_eligible_ || !quick_answers_controller_)
+  if (!quick_answers_controller_)
     return;
 
   if (params.input_field_type ==
@@ -157,7 +157,7 @@
   if (!IsRichUiEnabled())
     return;
 
-  if (!is_eligible_ || !quick_answers_controller_)
+  if (!quick_answers_controller_)
     return;
 
   quick_answers_controller_->DismissQuickAnswers(!is_other_command_executed_);
diff --git a/chrome/browser/resources/chromeos/edu_login/BUILD.gn b/chrome/browser/resources/chromeos/edu_login/BUILD.gn
index 7f88acd..1370a3a 100644
--- a/chrome/browser/resources/chromeos/edu_login/BUILD.gn
+++ b/chrome/browser/resources/chromeos/edu_login/BUILD.gn
@@ -22,7 +22,6 @@
     ":edu_login_signin",
     ":edu_login_template",
     ":edu_login_util",
-    ":edu_login_welcome",
   ]
 }
 
@@ -33,7 +32,6 @@
     ":edu_login_parents",
     ":edu_login_signin",
     ":edu_login_util",
-    ":edu_login_welcome",
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
     "//ui/webui/resources/cr_elements/cr_view_manager:cr_view_manager.m",
     "//ui/webui/resources/js:assert.m",
@@ -145,12 +143,6 @@
   html_type = "v3-ready"
 }
 
-polymer_modulizer("edu_login_welcome") {
-  js_file = "edu_login_welcome.js"
-  html_file = "edu_login_welcome.html"
-  html_type = "v3-ready"
-}
-
 polymer_modulizer("edu_login_parents") {
   js_file = "edu_login_parents.js"
   html_file = "edu_login_parents.html"
@@ -185,7 +177,6 @@
     ":edu_login_parents_module",
     ":edu_login_signin_module",
     ":edu_login_template_module",
-    ":edu_login_welcome_module",
     ":icons_module",
   ]
 }
diff --git a/chrome/browser/resources/chromeos/edu_login/app.html b/chrome/browser/resources/chromeos/edu_login/app.html
index b55c819..1a7547d 100644
--- a/chrome/browser/resources/chromeos/edu_login/app.html
+++ b/chrome/browser/resources/chromeos/edu_login/app.html
@@ -1,6 +1,4 @@
 <cr-view-manager id="viewManager">
-  <edu-login-welcome id="[[Steps.WELCOME]]" slot="view">
-  </edu-login-welcome>
   <edu-login-parents id="[[Steps.PARENTS]]" slot="view"
       selected-parent="{{selectedParent_}}">
   </edu-login-parents>
diff --git a/chrome/browser/resources/chromeos/edu_login/app.js b/chrome/browser/resources/chromeos/edu_login/app.js
index 7be7137..9955dc74 100644
--- a/chrome/browser/resources/chromeos/edu_login/app.js
+++ b/chrome/browser/resources/chromeos/edu_login/app.js
@@ -2,7 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import './edu_login_welcome.js';
 import './edu_login_parents.js';
 import './edu_login_parent_signin.js';
 import './edu_login_parent_info.js';
@@ -15,7 +14,6 @@
 
 /** @enum {string} */
 const Steps = {
-  WELCOME: 'welcome',
   PARENTS: 'parents',
   PARENT_SIGNIN: 'parent-signin',
   PARENT_INFO: 'parent-info',
diff --git a/chrome/browser/resources/chromeos/edu_login/edu_login_parents.html b/chrome/browser/resources/chromeos/edu_login/edu_login_parents.html
index 718f869..1a573a4 100644
--- a/chrome/browser/resources/chromeos/edu_login/edu_login_parents.html
+++ b/chrome/browser/resources/chromeos/edu_login/edu_login_parents.html
@@ -7,6 +7,10 @@
     transition: background 200ms;
   }
 
+  .main-padding {
+    margin-bottom: 16px;
+  }
+
   .main-padding.account-list-item {
     padding-bottom: 0;
     padding-top: 0;
@@ -31,20 +35,20 @@
     width: var(--profile-icon-size);
   }
 
-  #parentsListBody {
-    margin-bottom: 16px;
-  }
 </style>
 
 <edu-login-template>
   <span slot="main">
     <div class="main-padding">
       <if expr="_google_chrome">
-        <img class="google-full-logo"
-            src="chrome://theme/IDR_LOGO_GOOGLE_COLOR_90" alt="">
+        <img class="google-logo"
+            src="chrome://chrome-signin/googleg.svg" alt="">
       </if>
-      <h1>$i18n{parentsListTitle}</h1>
-      <p id="parentsListBody" class="secondary">$i18n{parentsListBody}</p>
+      <h1>[[getTitle_()]]</h1>
+      <p id="reauth-message" hidden="[[!reauthFlow_]]">
+        [[getReauthMessage_()]]
+      </p>
+      <p>[[getBody_()]]</p>
     </div>
     <div>
       <template is="dom-repeat" items="[[parents_]]">
@@ -66,7 +70,6 @@
     </div>
   </span>
   <span slot="buttons">
-    <edu-login-button button-type="back"></edu-login-button>
     <edu-login-button button-type="next"
         disabled="[[isNextDisabled_(selectedParent)]]">
     </edu-login-button>
diff --git a/chrome/browser/resources/chromeos/edu_login/edu_login_parents.js b/chrome/browser/resources/chromeos/edu_login/edu_login_parents.js
index 7bc049a..2d07e85 100644
--- a/chrome/browser/resources/chromeos/edu_login/edu_login_parents.js
+++ b/chrome/browser/resources/chromeos/edu_login/edu_login_parents.js
@@ -6,6 +6,7 @@
 import './edu_login_template.js';
 import './edu_login_button.js';
 
+import {I18nBehavior} from 'chrome://resources/js/i18n_behavior.m.js';
 import {getImage} from 'chrome://resources/js/icon.m.js';
 import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
@@ -17,6 +18,8 @@
 
   _template: html`{__html_template__}`,
 
+  behaviors: [I18nBehavior],
+
   properties: {
     /**
      * Selected parent account for approving EDU login flow.
@@ -38,6 +41,27 @@
         return [];
       },
     },
+
+    /**
+     * Whether current flow is a reauthentication.
+     * @private {boolean}
+     */
+    reauthFlow_: {
+      type: Boolean,
+      value: false,
+    },
+  },
+
+  /** @override */
+  created() {
+    // For the reauth flow the email is appended to query params in
+    // InlineLoginHandlerDialogChromeOS. It's used later in auth extension to
+    // pass the email value to Gaia.
+    let currentQueryParameters = new URLSearchParams(window.location.search);
+    if (currentQueryParameters.get('email')) {
+      this.reauthFlow_ = true;
+    }
+    document.title = this.getTitle_();
   },
 
   /** @override */
@@ -95,4 +119,28 @@
     this.selectedParent = e.model.item;
     this.fire('go-next');
   },
+
+  /**
+   * @return {string}
+   * @private
+   */
+  getTitle_() {
+    return this.i18n('parentsListTitle');
+  },
+
+  /**
+   * @return {string}
+   * @private
+   */
+  getBody_() {
+    return this.i18n('parentsListBody');
+  },
+
+  /**
+   * @return {string}
+   * @private
+   */
+  getReauthMessage_() {
+    return this.i18n('reauthBody');
+  }
 });
diff --git a/chrome/browser/resources/chromeos/edu_login/edu_login_welcome.html b/chrome/browser/resources/chromeos/edu_login/edu_login_welcome.html
deleted file mode 100644
index f6a36e0..0000000
--- a/chrome/browser/resources/chromeos/edu_login/edu_login_welcome.html
+++ /dev/null
@@ -1,39 +0,0 @@
-<style include="edu-login-css">
-  .main-padding {
-    display: flex;
-    flex-direction: column;
-    height: calc(100% - 90px);
-  }
-
-  .image-container {
-    align-items: center;
-    display: flex;
-    flex-grow: 1;
-    justify-content: center;
-    width: 100%;
-  }
-
-  .welcome-image {
-    height: 256px;
-    width: 256px;
-  }
-</style>
-<edu-login-template>
-  <span slot="main">
-    <div class="main-padding">
-      <if expr="_google_chrome">
-        <img class="google-logo"
-            src="chrome://chrome-signin/googleg.svg" alt="">
-      </if>
-      <h1>[[getTitle_()]]</h1>
-      <p>[[getBody_()]]</p>
-      <div class="image-container">
-        <img class="welcome-image" src="family_link_logo.svg" alt="">
-      </div>
-    </div>
-  </span>
-  <span slot="buttons">
-    <edu-login-button button-type="ok">
-    </edu-login-button>
-  </span>
-</edu-login-template>
diff --git a/chrome/browser/resources/chromeos/edu_login/edu_login_welcome.js b/chrome/browser/resources/chromeos/edu_login/edu_login_welcome.js
deleted file mode 100644
index ab4b295..0000000
--- a/chrome/browser/resources/chromeos/edu_login/edu_login_welcome.js
+++ /dev/null
@@ -1,54 +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.
-
-import './edu_login_css.js';
-import './edu_login_template.js';
-import './edu_login_button.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';
-
-Polymer({
-  is: 'edu-login-welcome',
-
-  _template: html`{__html_template__}`,
-
-  behaviors: [I18nBehavior],
-
-  /**
-   * Whether current flow is a reauthentication.
-   * @private {boolean}
-   */
-  reauthFlow_: false,
-
-  /** @override */
-  created() {
-    // For the reauth flow the email is appended to query params in
-    // InlineLoginHandlerDialogChromeOS. It's used later in auth extension to
-    // pass the email value to Gaia.
-    let currentQueryParameters = new URLSearchParams(window.location.search);
-    if (currentQueryParameters.get('email')) {
-      this.reauthFlow_ = true;
-    }
-    document.title = this.getTitle_();
-  },
-
-  /**
-   * @return {string}
-   * @private
-   */
-  getTitle_() {
-    return this.reauthFlow_ ? this.i18n('welcomeReauthTitle') :
-                              this.i18n('welcomeTitle');
-  },
-
-  /**
-   * @return {string}
-   * @private
-   */
-  getBody_() {
-    return this.reauthFlow_ ? this.i18n('welcomeReauthBody') :
-                              this.i18n('welcomeBody');
-  },
-});
diff --git a/chrome/browser/resources/chromeos/login/arc_terms_of_service.css b/chrome/browser/resources/chromeos/login/arc_terms_of_service.css
index 16f92fc..59378b0 100644
--- a/chrome/browser/resources/chromeos/login/arc_terms_of_service.css
+++ b/chrome/browser/resources/chromeos/login/arc_terms_of_service.css
@@ -14,6 +14,10 @@
   padding: 0;
 }
 
+cr-checkbox {
+  --cr-checkbox-label-padding-start: 16px;
+}
+
 .arc-tos-loaded .arc-tos-loading,
 .arc-tos-loaded .arc-tos-error,
 .arc-tos-loaded #arcTosRetryButton,
diff --git a/chrome/browser/resources/chromeos/login/arc_terms_of_service.html b/chrome/browser/resources/chromeos/login/arc_terms_of_service.html
index 1dd60dbb..39011805 100644
--- a/chrome/browser/resources/chromeos/login/arc_terms_of_service.html
+++ b/chrome/browser/resources/chromeos/login/arc_terms_of_service.html
@@ -45,6 +45,7 @@
             disabled="[[backupRestoreManaged]]"
             hidden="[[demoMode]]">
           <cr-checkbox id="arcEnableBackupRestore"
+              class="layout start"
               checked="{{backupRestore}}"
               disabled="[[backupRestoreManaged]]">
             <p>
@@ -62,6 +63,7 @@
               disabled="[[locationServiceManaged]]"
               hidden="[[demoMode]]">
             <cr-checkbox id="arcEnableLocationService"
+                class="layout start"
                 checked="{{locationService}}"
                 disabled="[[locationServiceManaged]]">
               <p>
@@ -90,6 +92,7 @@
             class="parameter-section arc-tos-content"
             hidden="[[demoMode]]">
             <cr-checkbox id="arcReviewSettingsCheckbox"
+                class="layout start"
                 checked="{{reviewSettings}}">
               <p>[[i18nDynamic(locale, 'arcTextReviewSettings')]]</p>
             </cr-checkbox>
diff --git a/chrome/browser/resources/chromeos/login/oobe_eula.css b/chrome/browser/resources/chromeos/login/oobe_eula.css
index c52e0dc..b00432f 100644
--- a/chrome/browser/resources/chromeos/login/oobe_eula.css
+++ b/chrome/browser/resources/chromeos/login/oobe_eula.css
@@ -37,14 +37,13 @@
   color: rgba(0, 0, 0, 0.54);
 }
 
-cr-toggle {
-  align-self: center;
+cr-checkbox {
+  --cr-checkbox-label-padding-start: 16px;
 }
 
 #usageStatsLabelContainer {
   color: var(--google-grey-refresh-700); /* #5F6368 */
   line-height: 20px;
-  margin-inline-start: 16px;
 }
 
 #usageStatsLabel {
diff --git a/chrome/browser/resources/chromeos/login/oobe_eula.html b/chrome/browser/resources/chromeos/login/oobe_eula.html
index 4b929d4..3bfe19f 100644
--- a/chrome/browser/resources/chromeos/login/oobe_eula.html
+++ b/chrome/browser/resources/chromeos/login/oobe_eula.html
@@ -4,7 +4,7 @@
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_style_css.html">
-<link rel="import" href="chrome://resources/cr_elements/cr_toggle/cr_toggle.html">
+<link rel="import" href="chrome://resources/cr_elements/cr_checkbox/cr_checkbox.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
 
 <dom-module id="oobe-eula-md">
@@ -46,18 +46,20 @@
             [[i18nDynamic(locale, 'eulaSystemInstallationSettings')]]
           </a>
           <div id="logging" class="layout horizontal">
-            <cr-toggle id="usageStats" checked="{{usageStatsChecked}}"
-                on-change="onUsageChanged_" aria-labelledby="usageStatsLabel">
-            </cr-toggle>
-            <div id="usageStatsLabelContainer">
-              <span id="usageStatsLabel" on-tap="usageStatsLabelClicked_">
-                [[i18nDynamic(locale, 'checkboxLogging')]]
-              </span>
-              <a id="learn-more" href="#" on-tap="onUsageStatsHelpLinkClicked_"
-                  class="oobe-local-link">
-                [[i18nDynamic(locale, 'learnMore')]]
-              </a>
-            </div>
+            <cr-checkbox id="usageStats" class="layout start self-center"
+                checked="{{usageStatsChecked}}" on-change="onUsageChanged_"
+                aria-labelledby="usageStatsLabel">
+              <div id="usageStatsLabelContainer">
+                <span id="usageStatsLabel" on-tap="usageStatsLabelClicked_">
+                  [[i18nDynamic(locale, 'checkboxLogging')]]
+                </span>
+                <a id="learn-more" href="#"
+                    on-tap="onUsageStatsHelpLinkClicked_"
+                    class="oobe-local-link">
+                  [[i18nDynamic(locale, 'learnMore')]]
+                </a>
+              </div>
+            </cr-checkbox>
           </div>
         </div>
       </div>
diff --git a/chrome/browser/resources/new_tab_page/app.js b/chrome/browser/resources/new_tab_page/app.js
index 74dbea6..d1086c2 100644
--- a/chrome/browser/resources/new_tab_page/app.js
+++ b/chrome/browser/resources/new_tab_page/app.js
@@ -608,10 +608,11 @@
       overlayRects.forEach(({x, y, width, height}) => {
         const rectElement =
             document.createElementNS('http://www.w3.org/2000/svg', 'rect');
-        rectElement.setAttribute('x', x);
-        rectElement.setAttribute('y', y);
-        rectElement.setAttribute('width', width);
-        rectElement.setAttribute('height', height);
+        // Add 8px around every rect to ensure shadows are not cutoff.
+        rectElement.setAttribute('x', x - 8);
+        rectElement.setAttribute('y', y - 8);
+        rectElement.setAttribute('width', width + 16);
+        rectElement.setAttribute('height', height + 16);
         this.$.oneGoogleBarClipPath.appendChild(rectElement);
       });
     }
diff --git a/chrome/browser/resources/print_preview/ui/link_container.html b/chrome/browser/resources/print_preview/ui/link_container.html
index 6ada90e..9c483864 100644
--- a/chrome/browser/resources/print_preview/ui/link_container.html
+++ b/chrome/browser/resources/print_preview/ui/link_container.html
@@ -42,7 +42,7 @@
   }
 
   .link:not([actionable]) .label {
-    @apply --print-preview-disabled-label;
+    opacity: var(--cr-disabled-opacity);
   }
 </style>
 <div class="link" id="systemDialogLink"
diff --git a/chrome/browser/resources/print_preview/ui/more_settings.html b/chrome/browser/resources/print_preview/ui/more_settings.html
index d63ce664..13c6d2c 100644
--- a/chrome/browser/resources/print_preview/ui/more_settings.html
+++ b/chrome/browser/resources/print_preview/ui/more_settings.html
@@ -33,7 +33,7 @@
   }
 
   :host([disabled]) #label {
-    @apply --print-preview-disabled-label;
+    opacity: var(--cr-disabled-opacity);
   }
 </style>
 <div on-click="toggleExpandButton_" actionable>
diff --git a/chrome/browser/resources/print_preview/ui/print_preview_vars_css.html b/chrome/browser/resources/print_preview/ui/print_preview_vars_css.html
index d476823e..2208774 100644
--- a/chrome/browser/resources/print_preview/ui/print_preview_vars_css.html
+++ b/chrome/browser/resources/print_preview/ui/print_preview_vars_css.html
@@ -8,10 +8,6 @@
 
     --print-preview-settings-border: 1px solid var(--google-grey-200);
     --print-preview-dialog-margin: 34px;
-    --print-preview-disabled-label: {
-      color: var(--paper-grey-600);
-      opacity: .65;
-    }
     --cr-form-field-label-height: initial;
     --cr-form-field-label-line-height: .75rem;
     --destination-item-height: 32px;
diff --git a/chrome/browser/resources/settings/autofill_page/password_edit_dialog.html b/chrome/browser/resources/settings/autofill_page/password_edit_dialog.html
index 0e06b58..ada16c2 100644
--- a/chrome/browser/resources/settings/autofill_page/password_edit_dialog.html
+++ b/chrome/browser/resources/settings/autofill_page/password_edit_dialog.html
@@ -20,6 +20,11 @@
         justify-content: initial;
       }
 
+      #storageDetails {
+        margin-inline-start: 2px;
+        margin-bottom: 16px;
+      }
+
       cr-icon-button {
         --cr-icon-button-icon-size: 24px;
         margin-inline-start: 2px;
@@ -28,6 +33,8 @@
     <cr-dialog id="dialog" close-text="$i18n{close}">
       <div slot="title">$i18n{passwordDetailsTitle}</div>
       <div slot="body">
+        <div hidden="[[!shouldShowStorageDetails]]" id="storageDetails"
+        inner-h-t-m-l="[[getStorageDetailsMessage_()]]"></div>
         <cr-input id="websiteInput" label="$i18n{editPasswordWebsiteLabel}"
             value="[[item.entry.urls.link]]" on-blur="onInputBlur_" readonly>
         </cr-input>
diff --git a/chrome/browser/resources/settings/autofill_page/password_edit_dialog.js b/chrome/browser/resources/settings/autofill_page/password_edit_dialog.js
index e41e1e9..e636d9c 100644
--- a/chrome/browser/resources/settings/autofill_page/password_edit_dialog.js
+++ b/chrome/browser/resources/settings/autofill_page/password_edit_dialog.js
@@ -17,6 +17,7 @@
 import '../settings_shared_css.m.js';
 import '../settings_vars_css.m.js';
 import './passwords_shared_css.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';
 
@@ -28,7 +29,11 @@
 
   _template: html`{__html_template__}`,
 
-  behaviors: [ShowPasswordBehavior],
+  behaviors: [ShowPasswordBehavior, I18nBehavior],
+
+  properties: {
+    shouldShowStorageDetails: {type: Boolean, value: false},
+  },
 
   /** @override */
   attached() {
@@ -52,4 +57,16 @@
   onInputBlur_() {
     this.shadowRoot.getSelection().removeAllRanges();
   },
+
+  /**
+   * Gets the HTML-formatted message to indicate in which locations the password
+   * is stored.
+   */
+  getStorageDetailsMessage_() {
+    // TODO(crbug.com/1049141): Add support and tests for the multi-store case
+    // when dedup is being done.
+    return this.item.entry.isPresentInAccount() ?
+        this.i18nAdvanced('passwordStoredInAccount', {tags: ['b']}) :
+        this.i18nAdvanced('passwordStoredOnDevice', {tags: ['b']});
+  }
 });
diff --git a/chrome/browser/resources/settings/autofill_page/passwords_section.html b/chrome/browser/resources/settings/autofill_page/passwords_section.html
index 8c16900..a23be0e 100644
--- a/chrome/browser/resources/settings/autofill_page/passwords_section.html
+++ b/chrome/browser/resources/settings/autofill_page/passwords_section.html
@@ -217,10 +217,13 @@
     </template>
     <template is="dom-if" if="[[showPasswordEditDialog_]]" restamp>
       <password-edit-dialog on-close="onPasswordEditDialogClosed_"
+          id="passwordEditDialog"
 <if expr="chromeos">
           token-request-manager="[[tokenRequestManager_]]"
 </if>
-          item="[[activePassword.item]]">
+          item="[[activePassword.item]]"
+          should-show-storage-details=
+            "[[shouldShowStorageDetailsInEditDialog_]]">
       </password-edit-dialog>
     </template>
 <if expr="chromeos">
diff --git a/chrome/browser/resources/settings/autofill_page/passwords_section.js b/chrome/browser/resources/settings/autofill_page/passwords_section.js
index 0c9721b..d607a889 100644
--- a/chrome/browser/resources/settings/autofill_page/passwords_section.js
+++ b/chrome/browser/resources/settings/autofill_page/passwords_section.js
@@ -186,6 +186,13 @@
           'signedIn_, hasNeverCheckedPasswords_, hasStoredPasswords_)',
     },
 
+    shouldShowStorageDetailsInEditDialog_: {
+      type: Boolean,
+      value: false,
+      computed: 'computeShouldShowStorageDetailsInEditDialog_('+
+      'eligibleForAccountStorage_, isOptedInForAccountStorage_)',
+    },
+
     /** @private */
     hasLeakedCredentials_: {
       type: Boolean,
@@ -501,6 +508,14 @@
    * @return {boolean}
    * @private
    */
+  computeShouldShowStorageDetailsInEditDialog_() {
+    return this.eligibleForAccountStorage_ && this.isOptedInForAccountStorage_;
+  },
+
+  /**
+   * @return {boolean}
+   * @private
+   */
   computeHidePasswordsLink_() {
     return !!this.syncStatus_ && !!this.syncStatus_.signedIn &&
         !!this.syncPrefs_ && !!this.syncPrefs_.encryptAllData;
diff --git a/chrome/browser/resources/settings/chromeos/localized_link/localized_link.js b/chrome/browser/resources/settings/chromeos/localized_link/localized_link.js
index 2c8967c..1ae4452 100644
--- a/chrome/browser/resources/settings/chromeos/localized_link/localized_link.js
+++ b/chrome/browser/resources/settings/chromeos/localized_link/localized_link.js
@@ -73,6 +73,7 @@
         ariaLabelledByIds.push(node.id);
         return;
       }
+
       // Only text and <a> nodes are allowed.
       assertNotReached('settings-localized-link has invalid node types');
     });
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/account_manager.html b/chrome/browser/resources/settings/chromeos/os_people_page/account_manager.html
index 2c3ef2f..58f176e 100644
--- a/chrome/browser/resources/settings/chromeos/os_people_page/account_manager.html
+++ b/chrome/browser/resources/settings/chromeos/os_people_page/account_manager.html
@@ -54,6 +54,15 @@
         align-items: flex-end;
       }
 
+      #account-description {
+        align-items: flex-start;
+        flex-direction: column;
+      }
+
+      #account-description > p {
+        margin-bottom: 0;
+      }
+
       #account-list-header > h2 {
         padding-bottom: 12px;
         padding-top: 12px;
@@ -111,12 +120,16 @@
       }
     </style>
 
-    <div class="settings-box first">
+    <div id="account-description" class="settings-box first">
       <settings-localized-link
           class="account-manager-description"
           localized-string="[[getAccountManagerDescription_()]]"
           link-url="$i18nRaw{accountManagerLearnMoreUrl}">
       </settings-localized-link>
+      <template is="dom-if" if="[[isChildUser_]]">
+        <p>$i18n{accountManagerChildFirstMessage}</p>
+        <p>$i18n{accountManagerChildSecondMessage}</p>
+      </template>
     </div>
 
     <div id="settings-box-user-message" class="settings-box first user-message"
diff --git a/chrome/browser/resources/settings/chromeos/os_settings_search_box/os_search_result_row.html b/chrome/browser/resources/settings/chromeos/os_settings_search_box/os_search_result_row.html
index b141b27..7968cfe 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings_search_box/os_search_result_row.html
+++ b/chrome/browser/resources/settings/chromeos/os_settings_search_box/os_search_result_row.html
@@ -26,6 +26,10 @@
         background-color: var(--cros-menu-button-bg-color-hover);
       }
 
+      :host-context([dir=rtl]) #actionTypeIcon {
+        transform: scaleX(-1);  /* Invert X: flip on the Y axis (aka mirror). */
+      }
+
       [focus-row-container] {
         width: inherit;
       }
diff --git a/chrome/browser/resources/tab_strip/tab.js b/chrome/browser/resources/tab_strip/tab.js
index db8fee4..32a2bab 100644
--- a/chrome/browser/resources/tab_strip/tab.js
+++ b/chrome/browser/resources/tab_strip/tab.js
@@ -11,7 +11,7 @@
 
 import {AlertIndicatorsElement} from './alert_indicators.js';
 import {CustomElement} from './custom_element.js';
-import {TabStripEmbedderProxy} from './tab_strip_embedder_proxy.js';
+import {TabStripEmbedderProxy, TabStripEmbedderProxyImpl} from './tab_strip_embedder_proxy.js';
 import {tabStripOptions} from './tab_strip_options.js';
 import {TabSwiper} from './tab_swiper.js';
 import {CloseTabAction, TabData, TabNetworkState, TabsApiProxy, TabsApiProxyImpl} from './tabs_api_proxy.js';
@@ -87,7 +87,7 @@
     this.tabsApi_ = TabsApiProxyImpl.getInstance();
 
     /** @private {!TabStripEmbedderProxy} */
-    this.embedderApi_ = TabStripEmbedderProxy.getInstance();
+    this.embedderApi_ = TabStripEmbedderProxyImpl.getInstance();
 
     /** @private {!HTMLElement} */
     this.titleTextEl_ = /** @type {!HTMLElement} */ (this.$('#titleText'));
diff --git a/chrome/browser/resources/tab_strip/tab_group.js b/chrome/browser/resources/tab_strip/tab_group.js
index e50abdb..46d6da5 100644
--- a/chrome/browser/resources/tab_strip/tab_group.js
+++ b/chrome/browser/resources/tab_strip/tab_group.js
@@ -5,7 +5,7 @@
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 
 import {CustomElement} from './custom_element.js';
-import {TabStripEmbedderProxy} from './tab_strip_embedder_proxy.js';
+import {TabStripEmbedderProxy, TabStripEmbedderProxyImpl} from './tab_strip_embedder_proxy.js';
 import {TabGroupVisualData} from './tabs_api_proxy.js';
 
 export class TabGroupElement extends CustomElement {
@@ -17,7 +17,7 @@
     super();
 
     /** @private @const {!TabStripEmbedderProxy} */
-    this.embedderApi_ = TabStripEmbedderProxy.getInstance();
+    this.embedderApi_ = TabStripEmbedderProxyImpl.getInstance();
 
     /** @private @const {!HTMLElement} */
     this.chip_ = /** @type {!HTMLElement} */ (this.$('#chip'));
diff --git a/chrome/browser/resources/tab_strip/tab_list.js b/chrome/browser/resources/tab_strip/tab_list.js
index dd587ef..3417d6b 100644
--- a/chrome/browser/resources/tab_strip/tab_list.js
+++ b/chrome/browser/resources/tab_strip/tab_list.js
@@ -17,7 +17,7 @@
 import {DragManager, DragManagerDelegate} from './drag_manager.js';
 import {TabElement} from './tab.js';
 import {isTabGroupElement, TabGroupElement} from './tab_group.js';
-import {TabStripEmbedderProxy} from './tab_strip_embedder_proxy.js';
+import {TabStripEmbedderProxy, TabStripEmbedderProxyImpl} from './tab_strip_embedder_proxy.js';
 import {tabStripOptions} from './tab_strip_options.js';
 import {TabData, TabGroupVisualData, TabsApiProxy, TabsApiProxyImpl} from './tabs_api_proxy.js';
 
@@ -122,7 +122,7 @@
     this.pinnedTabsElement_ = /** @type {!Element} */ (this.$('#pinnedTabs'));
 
     /** @private {!TabStripEmbedderProxy} */
-    this.tabStripEmbedderProxy_ = TabStripEmbedderProxy.getInstance();
+    this.tabStripEmbedderProxy_ = TabStripEmbedderProxyImpl.getInstance();
 
     /** @private {!TabsApiProxy} */
     this.tabsApi_ = TabsApiProxyImpl.getInstance();
diff --git a/chrome/browser/resources/tab_strip/tab_strip_embedder_proxy.js b/chrome/browser/resources/tab_strip/tab_strip_embedder_proxy.js
index 898858e..3bfcc59 100644
--- a/chrome/browser/resources/tab_strip/tab_strip_embedder_proxy.js
+++ b/chrome/browser/resources/tab_strip/tab_strip_embedder_proxy.js
@@ -4,31 +4,24 @@
 
 import {addSingletonGetter, addWebUIListener, sendWithPromise} from 'chrome://resources/js/cr.m.js';
 
+/** @interface */
 export class TabStripEmbedderProxy {
   /** @return {boolean} */
-  isVisible() {
-    return document.visibilityState === 'visible';
-  }
+  isVisible() {}
 
   /**
    * @return {!Promise<!Object<string, string>>} Object with CSS variables
    *     as keys and rgba strings as values
    */
-  getColors() {
-    return sendWithPromise('getThemeColors');
-  }
+  getColors() {}
 
   /**
    * @return {!Promise<!Object<string, string>>} Object with CSS variables
    *     as keys and pixel lengths as values
    */
-  getLayout() {
-    return sendWithPromise('getLayout');
-  }
+  getLayout() {}
 
-  observeThemeChanges() {
-    chrome.send('observeThemeChanges');
-  }
+  observeThemeChanges() {}
 
   /**
    * @param {string} groupId
@@ -37,53 +30,97 @@
    * @param {number} width
    * @param {number} height
    */
-  showEditDialogForGroup(groupId, locationX, locationY, width, height) {
-    chrome.send(
-        'showEditDialogForGroup',
-        [groupId, locationX, locationY, width, height]);
-  }
+  showEditDialogForGroup(groupId, locationX, locationY, width, height) {}
 
   /**
    * @param {number} tabId
    * @param {number} locationX
    * @param {number} locationY
    */
-  showTabContextMenu(tabId, locationX, locationY) {
-    chrome.send('showTabContextMenu', [tabId, locationX, locationY]);
-  }
+  showTabContextMenu(tabId, locationX, locationY) {}
 
   /**
    * @param {number} locationX
    * @param {number} locationY
    */
-  showBackgroundContextMenu(locationX, locationY) {
-    chrome.send('showBackgroundContextMenu', [locationX, locationY]);
-  }
+  showBackgroundContextMenu(locationX, locationY) {}
 
-  closeContainer() {
-    chrome.send('closeContainer');
-  }
+  closeContainer() {}
 
   /** @param {number} durationMs Activation duration time in ms. */
-  reportTabActivationDuration(durationMs) {
-    chrome.send('reportTabActivationDuration', [durationMs]);
-  }
+  reportTabActivationDuration(durationMs) {}
 
   /**
    * @param {number} tabCount Number of tabs.
    * @param {number} durationMs Activation duration time in ms.
    */
-  reportTabDataReceivedDuration(tabCount, durationMs) {
-    chrome.send('reportTabDataReceivedDuration', [tabCount, durationMs]);
-  }
+  reportTabDataReceivedDuration(tabCount, durationMs) {}
 
   /**
    * @param {number} tabCount Number of tabs.
    * @param {number} durationMs Creation duration time in ms.
    */
+  reportTabCreationDuration(tabCount, durationMs) {}
+}
+
+/** @implements {TabStripEmbedderProxy} */
+export class TabStripEmbedderProxyImpl {
+  /** @override */
+  isVisible() {
+    return document.visibilityState === 'visible';
+  }
+
+  /** @override */
+  getColors() {
+    return sendWithPromise('getThemeColors');
+  }
+
+  /** @override */
+  getLayout() {
+    return sendWithPromise('getLayout');
+  }
+
+  /** @override */
+  observeThemeChanges() {
+    chrome.send('observeThemeChanges');
+  }
+
+  /** @override */
+  showEditDialogForGroup(groupId, locationX, locationY, width, height) {
+    chrome.send(
+        'showEditDialogForGroup',
+        [groupId, locationX, locationY, width, height]);
+  }
+
+  /** @override */
+  showTabContextMenu(tabId, locationX, locationY) {
+    chrome.send('showTabContextMenu', [tabId, locationX, locationY]);
+  }
+
+  /** @override */
+  showBackgroundContextMenu(locationX, locationY) {
+    chrome.send('showBackgroundContextMenu', [locationX, locationY]);
+  }
+
+  /** @override */
+  closeContainer() {
+    chrome.send('closeContainer');
+  }
+
+  /** @override */
+  reportTabActivationDuration(durationMs) {
+    chrome.send('reportTabActivationDuration', [durationMs]);
+  }
+
+  /** @override */
+  reportTabDataReceivedDuration(tabCount, durationMs) {
+    chrome.send('reportTabDataReceivedDuration', [tabCount, durationMs]);
+  }
+
+  /** @override */
   reportTabCreationDuration(tabCount, durationMs) {
     chrome.send('reportTabCreationDuration', [tabCount, durationMs]);
   }
 }
 
-addSingletonGetter(TabStripEmbedderProxy);
+addSingletonGetter(TabStripEmbedderProxyImpl);
diff --git a/chrome/browser/resources/tools/rollup_plugin.js b/chrome/browser/resources/tools/rollup_plugin.js
index f9d68b5..d6c49bf 100644
--- a/chrome/browser/resources/tools/rollup_plugin.js
+++ b/chrome/browser/resources/tools/rollup_plugin.js
@@ -15,6 +15,7 @@
 const nonGeneratedFiles = [
   'action_link.js',
   'certificate_manager_types.js',
+  'certificate_provisioning_browser_proxy.js',
   'certificates_browser_proxy.js',
   'cr.m.js',
   'cr_splitter.js',
diff --git a/chrome/browser/service_sandbox_type.h b/chrome/browser/service_sandbox_type.h
index 1a66223..0339a19 100644
--- a/chrome/browser/service_sandbox_type.h
+++ b/chrome/browser/service_sandbox_type.h
@@ -55,4 +55,32 @@
   return content::SandboxType::kNoSandbox;
 }
 
+// printing::mojom::PrintingService
+#if defined(OS_WIN)
+namespace printing {
+namespace mojom {
+class PrintingService;
+}
+}  // namespace printing
+template <>
+inline content::SandboxType
+content::GetServiceSandboxType<printing::mojom::PrintingService>() {
+  return content::SandboxType::kPdfConversion;
+}
+#endif  // defined(OS_WIN)
+
+// proxy_resolver::mojom::ProxyResolverFactory
+#if defined(OS_WIN)
+namespace proxy_resolver {
+namespace mojom {
+class ProxyResolverFactory;
+}
+}  // namespace proxy_resolver
+template <>
+inline content::SandboxType
+content::GetServiceSandboxType<proxy_resolver::mojom::ProxyResolverFactory>() {
+  return content::SandboxType::kProxyResolver;
+}
+#endif  // defined(OS_WIN)
+
 #endif  // CHROME_BROWSER_SERVICE_SANDBOX_TYPE_H_
diff --git a/chrome/browser/settings/BUILD.gn b/chrome/browser/settings/BUILD.gn
index 3de774ae..2522aba 100644
--- a/chrome/browser/settings/BUILD.gn
+++ b/chrome/browser/settings/BUILD.gn
@@ -30,12 +30,14 @@
     ":test_support_java",
     "//chrome/android:chrome_java",
     "//chrome/test/android:chrome_java_test_support",
+    "//components/browser_ui/settings/android:java",
     "//content/public/test/android:content_java_test_support",
     "//third_party/android_deps:android_support_v7_appcompat_java",
     "//third_party/android_deps:androidx_preference_preference_java",
     "//third_party/android_support_test_runner:rules_java",
     "//third_party/android_support_test_runner:runner_java",
     "//third_party/espresso:espresso_all_java",
+    "//third_party/guava:guava_android_java",
     "//third_party/hamcrest:hamcrest_java",
     "//third_party/junit",
   ]
@@ -47,9 +49,13 @@
   sources = [ "android/java/src/org/chromium/chrome/browser/settings/SettingsActivityTestRule.java" ]
 
   deps = [
+    "//base:base_java",
     "//chrome/android:chrome_java",
+    "//chrome/browser/settings:java",
     "//content/public/test/android:content_java_test_support",
     "//third_party/android_deps:androidx_preference_preference_java",
     "//third_party/android_support_test_runner:rules_java",
+    "//third_party/android_support_test_runner:runner_java",
+    "//third_party/junit",
   ]
 }
diff --git a/chrome/browser/share/DEPS b/chrome/browser/share/DEPS
index 05083d6..b67d2d4 100644
--- a/chrome/browser/share/DEPS
+++ b/chrome/browser/share/DEPS
@@ -1,18 +1,12 @@
 include_rules = [
   # TODO(crbug/1022172): Remove this dependency when ShareActivity is moved to
   # chrome/browser/share.
-  "+chrome/android/java/src/org/chromium/chrome/browser/share",
   "+chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java",
   "+chrome/android/java/src/org/chromium/chrome/browser/IntentHandler.java",
   "+chrome/android/java/src/org/chromium/chrome/browser/ShortcutHelper.java",
   "+chrome/android/java/src/org/chromium/chrome/browser/document/ChromeLauncherActivity.java",
-  "+chrome/android/java/src/org/chromium/chrome/browser/modules/ModuleInstallUi.java",
-  "+chrome/android/java/src/org/chromium/chrome/browser/screenshot/EditorScreenshotTask.java",
-  "+chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java",
-  "+chrome/services/qrcode_generator",
-  "+content/public/android/java/src/org/chromium/content_public/browser/RenderWidgetHostView.java",
-  "+content/public/android/java/src/org/chromium/content_public/browser/WebContents.java",
   "+chrome/android/java/src/org/chromium/chrome/browser/media/MediaViewerUtils.java",
+  "+chrome/android/java/src/org/chromium/chrome/browser/modules/ModuleInstallUi.java",
   "+chrome/android/java/src/org/chromium/chrome/browser/notifications/ChromeNotificationBuilder.java",
   "+chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationBuilderFactory.java",
   "+chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationConstants.java",
@@ -20,6 +14,12 @@
   "+chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationUmaTracker.java",
   "+chrome/android/java/src/org/chromium/chrome/browser/notifications/PendingIntentProvider.java",
   "+chrome/android/java/src/org/chromium/chrome/browser/notifications/channels/ChromeChannelDefinitions.java",
+  "+chrome/android/java/src/org/chromium/chrome/browser/screenshot/EditorScreenshotTask.java",
+  "+chrome/android/java/src/org/chromium/chrome/browser/share",
+  "+chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java",
+  "+chrome/services/qrcode_generator",
   "+components/browser_ui/notifications/android",
   "+components/browser_ui/share/android",
+  "+content/public/android/java/src/org/chromium/content_public/browser/RenderWidgetHostView.java",
+  "+content/public/android/java/src/org/chromium/content_public/browser/WebContents.java",
 ]
diff --git a/chrome/browser/share/android/BUILD.gn b/chrome/browser/share/android/BUILD.gn
index a2f5d56..abd9483 100644
--- a/chrome/browser/share/android/BUILD.gn
+++ b/chrome/browser/share/android/BUILD.gn
@@ -7,12 +7,17 @@
 android_resources("java_resources") {
   sources = [
     "java/res/drawable/camera_img.xml",
+    "java/res/drawable/delete.xml",
+    "java/res/drawable/edit.xml",
     "java/res/drawable/qrcode_background.xml",
+    "java/res/drawable/save.xml",
+    "java/res/drawable/share.xml",
     "java/res/layout/qrcode_camera_error_layout.xml",
     "java/res/layout/qrcode_dialog.xml",
     "java/res/layout/qrcode_open_settings_layout.xml",
     "java/res/layout/qrcode_permission_layout.xml",
     "java/res/layout/qrcode_share_layout.xml",
+    "java/res/layout/screenshot_share_sheet.xml",
     "java/res/values-night/colors.xml",
     "java/res/values-sw600dp/dimens.xml",
     "java/res/values/colors.xml",
diff --git a/chrome/browser/share/android/java/res/drawable/delete.xml b/chrome/browser/share/android/java/res/drawable/delete.xml
new file mode 100644
index 0000000..d9b4434b
--- /dev/null
+++ b/chrome/browser/share/android/java/res/drawable/delete.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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. -->
+  <vector xmlns:android="http://schemas.android.com/apk/res/android"
+   android:width="24dp"
+   android:height="24dp"
+   android:viewportWidth="24"
+   android:viewportHeight="24">
+   <group>
+     <clip-path
+       android:pathData="m15,4v-1h-6v1h-5v2h1v13c0,1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2v-13h1v-2zM17,19h-10v-13h10zM9,8h2v9h-2zM13,8h2v9h-2z"/>
+     <path
+       android:pathData="m0,0h24v24h-24z"
+       android:fillColor="@color/modern_grey_800"
+       android:fillType="evenOdd"/>
+   </group>
+  </vector>
diff --git a/chrome/browser/share/android/java/res/drawable/edit.xml b/chrome/browser/share/android/java/res/drawable/edit.xml
new file mode 100644
index 0000000..8d46de29
--- /dev/null
+++ b/chrome/browser/share/android/java/res/drawable/edit.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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. -->
+  <vector xmlns:android="http://schemas.android.com/apk/res/android"
+   android:width="24dp"
+   android:height="24dp"
+   android:viewportWidth="24"
+   android:viewportHeight="24">
+  <group>
+       <clip-path android:pathData="m20.41,4.94 l-1.35,-1.35c-0.78,-0.78 -2.05,-0.78 -2.83,0l-2.83,2.82 -10.4,10.41v4.18h4.18l10.46,-10.46 2.77,-2.77c0.79,-0.78 0.79,-2.05 0,-2.83zM6.41,19.06 L5,19v-1.36l9.82,-9.82 1.41,1.41z"/>
+       <path
+         android:fillColor="@color/modern_grey_800"
+         android:fillType="evenOdd"
+         android:pathData="m0,0h24v24h-24z"/>
+   </group>
+  </vector>
diff --git a/chrome/browser/share/android/java/res/drawable/save.xml b/chrome/browser/share/android/java/res/drawable/save.xml
new file mode 100644
index 0000000..d27d366
--- /dev/null
+++ b/chrome/browser/share/android/java/res/drawable/save.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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. -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+   android:width="24dp"
+   android:height="24dp"
+   android:viewportWidth="24"
+   android:viewportHeight="24">
+ <group>
+   <clip-path
+       android:pathData="m8.41,9.09 l-1.41,1.41 5,5 5,-5 -1.41,-1.41 -2.59,2.58v-8.67h-2v8.67zM21,19v-14c0,-1.11 -0.9,-2 -2,-2h-4v2h4v14h-14v-14h4v-2h-4c-1.1,0 -2,0.89 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2z"/>
+   <path
+       android:pathData="m0,0h24v24h-24z"
+       android:fillColor="@color/modern_grey_800"
+       android:fillType="evenOdd"/>
+ </group>
+</vector>
diff --git a/chrome/browser/share/android/java/res/drawable/share.xml b/chrome/browser/share/android/java/res/drawable/share.xml
new file mode 100644
index 0000000..aeec4f3
--- /dev/null
+++ b/chrome/browser/share/android/java/res/drawable/share.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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. -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+  android:width="24dp"
+  android:height="24dp"
+  android:viewportWidth="24"
+  android:viewportHeight="24">
+  <path
+    android:pathData="m16.5,15.7333c-0.6333,0 -1.2,0.25 -1.6333,0.6417l-5.9417,-3.4583c0.0417,-0.1917 0.075,-0.3833 0.075,-0.5833s-0.0333,-0.3917 -0.075,-0.5833l5.875,-3.425c0.45,0.4167 1.0417,0.675 1.7,0.675 1.3833,0 2.5,-1.1167 2.5,-2.5s-1.1167,-2.5 -2.5,-2.5 -2.5,1.1167 -2.5,2.5c0,0.2 0.0333,0.3917 0.075,0.5833l-5.875,3.425c-0.45,-0.4167 -1.0417,-0.675 -1.7,-0.675 -1.3833,0 -2.5,1.1167 -2.5,2.5 0,1.3833 1.1167,2.5 2.5,2.5 0.6583,0 1.25,-0.2583 1.7,-0.675l5.9333,3.4667c-0.0417,0.175 -0.0667,0.3583 -0.0667,0.5417 0,1.3417 1.0917,2.4333 2.4333,2.4333s2.4333,-1.0917 2.4333,-2.4333c0,-1.3417 -1.0917,-2.4333 -2.4333,-2.4333z"
+    android:fillColor="@color/modern_grey_800"
+    android:fillType="evenOdd"/>
+</vector>
diff --git a/chrome/browser/share/android/java/res/layout/screenshot_share_sheet.xml b/chrome/browser/share/android/java/res/layout/screenshot_share_sheet.xml
new file mode 100644
index 0000000..bdfd64c
--- /dev/null
+++ b/chrome/browser/share/android/java/res/layout/screenshot_share_sheet.xml
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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. -->
+<org.chromium.chrome.browser.share.screenshot.ScreenshotShareSheetView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <RelativeLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+        <org.chromium.ui.widget.ChromeImageButton
+            android:id="@+id/close_button"
+            style="@style/ToolbarButton"
+            android:src="@drawable/btn_close"
+            android:contentDescription="@string/close"
+            app:tint="@color/default_icon_color_tint_list" />
+
+       <!-- TODO: Add a frame layout to hold the screenshot preview.  -->
+
+       <LinearLayout
+           android:layout_width="match_parent"
+           android:layout_height="56dp"
+           android:layout_marginStart="36dp"
+           android:layout_marginEnd="36dp"
+           android:weightSum="4"
+           android:orientation="horizontal"
+           android:layout_gravity="start|bottom">
+
+           <TextView
+               android:id="@+id/edit"
+               android:text="@string/screenshot_edit_title"
+               style="@style/SplitToolbarButton"
+               android:drawableTop="@drawable/edit"
+               android:layout_weight="1"
+               android:gravity="center"
+               android:layout_gravity="end|bottom"
+               android:background="?attr/selectableItemBackgroundBorderless"
+               app:tint="@color/default_icon_color_tint_list" />
+
+           <TextView
+               android:id="@+id/delete"
+               android:text="@string/screenshot_delete_title"
+               style="@style/SplitToolbarButton"
+               android:drawableTop="@drawable/delete"
+               android:layout_weight="1"
+               android:gravity="center"
+               android:layout_gravity="center|bottom"
+               android:background="?attr/selectableItemBackgroundBorderless"
+               app:tint="@color/default_icon_color_tint_list" />
+
+           <TextView
+               android:id="@+id/save"
+               android:text="@string/screenshot_save_title"
+               style="@style/SplitToolbarButton"
+               android:drawableTop="@drawable/save"
+               android:layout_weight="1"
+               android:gravity="center"
+               android:layout_gravity="center|bottom"
+               android:background="?attr/selectableItemBackgroundBorderless"
+               app:tint="@color/default_icon_color_tint_list" />
+
+           <TextView
+               android:id="@+id/share"
+               android:text="@string/screenshot_share_title"
+               style="@style/SplitToolbarButton"
+               android:drawableTop="@drawable/share"
+               android:layout_weight="1"
+               android:gravity="center"
+               android:layout_gravity="start|bottom"
+               android:background="?attr/selectableItemBackgroundBorderless"
+               app:tint="@color/default_icon_color_tint_list" />
+
+        </LinearLayout>
+
+
+    </RelativeLayout>
+
+</org.chromium.chrome.browser.share.screenshot.ScreenshotShareSheetView>
diff --git a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/screenshot/ScreenshotCoordinator.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/screenshot/ScreenshotCoordinator.java
index 16d0efd..1511fb9 100644
--- a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/screenshot/ScreenshotCoordinator.java
+++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/screenshot/ScreenshotCoordinator.java
@@ -78,15 +78,17 @@
      * Opens the screenshot sharesheet.
      */
     private void launchSharesheet() {
-        // TODO(crbug/1024586): Open screenshot sharesheet.
+        ScreenshotShareSheetCoordinator shareSheet = new ScreenshotShareSheetCoordinator(mActivity);
+        shareSheet.showShareSheet();
+        mScreenshot = null;
     }
 
     /**
-     * Installs the DFM and shows UI (i.e. toasts and a retry dialog) informing the user of the
-     * installation status.
+     * Installs the DFM and shows UI (i.e. toasts and a retry dialog) informing the
+     * user of the installation status.
      */
     private void installEditor() {
-        ModuleInstallUi ui = new ModuleInstallUi(
+        final ModuleInstallUi ui = new ModuleInstallUi(
                 mTab, R.string.image_editor_module_title, new ModuleInstallUi.FailureUiListener() {
                     @Override
                     public void onFailureUiResponse(boolean retry) {
@@ -94,6 +96,8 @@
                             // User initiated retries are not counted toward the maximum number
                             // of install attempts per session.
                             installEditor();
+                        } else {
+                            launchSharesheet();
                         }
                     }
                 });
@@ -104,7 +108,7 @@
                 ui.showInstallSuccessUi();
                 launchEditor();
             } else {
-                ui.showInstallFailureUi();
+                launchSharesheet();
             }
         });
     }
diff --git a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/screenshot/ScreenshotShareSheetCoordinator.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/screenshot/ScreenshotShareSheetCoordinator.java
new file mode 100644
index 0000000..75e14d92
--- /dev/null
+++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/screenshot/ScreenshotShareSheetCoordinator.java
@@ -0,0 +1,42 @@
+// 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.share.screenshot;
+
+import android.app.Activity;
+import android.app.FragmentManager;
+
+/**
+ * Coordinator for displaying the screenshot share sheet.
+ */
+public class ScreenshotShareSheetCoordinator {
+    private final ScreenshotShareSheetDialog mDialog;
+    private final FragmentManager mFragmentManager;
+
+    /**
+     * Constructs a new ShareSheetCoordinator.
+     *
+     * @param context The context to use for user permissions.
+     */
+    public ScreenshotShareSheetCoordinator(Activity activity) {
+        mDialog = new ScreenshotShareSheetDialog();
+
+        mFragmentManager = activity.getFragmentManager();
+        // TODO(crbug/1024586) Flesh out MVC for the upstream screenshot MVC.
+    }
+
+    /**
+     * Show the main share sheet dialog.
+     */
+    protected void showShareSheet() {
+        mDialog.show(mFragmentManager, null);
+    }
+
+    /**
+     * Dismiss the main dialog.
+     */
+    public void dismiss() {
+        mDialog.dismiss();
+    }
+}
diff --git a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/screenshot/ScreenshotShareSheetDialog.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/screenshot/ScreenshotShareSheetDialog.java
new file mode 100644
index 0000000..74d5457
--- /dev/null
+++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/screenshot/ScreenshotShareSheetDialog.java
@@ -0,0 +1,53 @@
+// 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.share.screenshot;
+
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.content.Context;
+import android.os.Bundle;
+import android.view.View;
+
+import androidx.appcompat.app.AlertDialog;
+
+import org.chromium.chrome.R;
+import org.chromium.ui.widget.ChromeImageButton;
+
+/**
+ * ScreenshotShareSheetDialog is the main view for sharing non edited screenshots.
+ */
+public class ScreenshotShareSheetDialog extends DialogFragment {
+    private Context mContext;
+
+    /**
+     * The ScreenshotShareSheetDialog constructor.
+     */
+    public ScreenshotShareSheetDialog() {}
+
+    @Override
+    public void onAttach(Context context) {
+        super.onAttach(context);
+        mContext = context;
+    }
+
+    @Override
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+        AlertDialog.Builder builder =
+                new AlertDialog.Builder(getActivity(), R.style.Theme_Chromium_Fullscreen);
+        builder.setView(getDialogView());
+        return builder.create();
+    }
+
+    private View getDialogView() {
+        ScreenshotShareSheetView dialogView =
+                (ScreenshotShareSheetView) getActivity().getLayoutInflater().inflate(
+                        org.chromium.chrome.browser.share.R.layout.screenshot_share_sheet, null);
+        ChromeImageButton closeButton =
+                (ChromeImageButton) dialogView.findViewById(R.id.close_button);
+        closeButton.setOnClickListener(v -> dismiss());
+
+        return dialogView;
+    }
+}
diff --git a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/screenshot/ScreenshotShareSheetMediator.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/screenshot/ScreenshotShareSheetMediator.java
new file mode 100644
index 0000000..11c6a3b
--- /dev/null
+++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/screenshot/ScreenshotShareSheetMediator.java
@@ -0,0 +1,28 @@
+// 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.share.screenshot;
+
+import android.content.Context;
+
+import org.chromium.ui.modelutil.PropertyModel;
+
+/**
+ * ScreenshotShareSheetMediator is in charge of calculating and setting values for
+ * ScreenshotShareSheetViewProperties.
+ */
+class ScreenshotShareSheetMediator implements ShareImageFileUtils.OnImageSaveListener {
+    private final Context mContext;
+    private final PropertyModel mPropertyModel;
+
+    /**
+     * The ScreenshotShareSheetMediator constructor.
+     * @param context The context to use.
+     * @param propertyModel The property modelto use to communicate with views.
+     */
+    ScreenshotShareSheetMediator(Context context, PropertyModel propertyModel) {
+        mContext = context;
+        mPropertyModel = propertyModel;
+    }
+}
diff --git a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/screenshot/ScreenshotShareSheetView.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/screenshot/ScreenshotShareSheetView.java
new file mode 100644
index 0000000..301b757
--- /dev/null
+++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/screenshot/ScreenshotShareSheetView.java
@@ -0,0 +1,19 @@
+// 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.share.screenshot;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.FrameLayout;
+
+/**
+ * Manages the Android View representing the Screenshot share panel.
+ */
+class ScreenshotShareSheetView extends FrameLayout {
+    /** Constructor for use from XML. */
+    public ScreenshotShareSheetView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+}
diff --git a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/screenshot/ScreenshotShareSheetViewBinder.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/screenshot/ScreenshotShareSheetViewBinder.java
new file mode 100644
index 0000000..1bdafae
--- /dev/null
+++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/screenshot/ScreenshotShareSheetViewBinder.java
@@ -0,0 +1,16 @@
+// 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.share.screenshot;
+
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.PropertyModelChangeProcessor.ViewBinder;
+
+/** The view binder for the Screenshot Share Sheet. */
+class ScreenshotShareSheetViewBinder
+        implements ViewBinder<PropertyModel, ScreenshotShareSheetView, PropertyKey> {
+    @Override
+    public void bind(PropertyModel model, ScreenshotShareSheetView view, PropertyKey propertyKey) {}
+}
diff --git a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/screenshot/ScreenshotShareSheetViewProperties.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/screenshot/ScreenshotShareSheetViewProperties.java
new file mode 100644
index 0000000..41407a88
--- /dev/null
+++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/screenshot/ScreenshotShareSheetViewProperties.java
@@ -0,0 +1,11 @@
+// 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.share.screenshot;
+
+import org.chromium.ui.modelutil.PropertyKey;
+
+class ScreenshotShareSheetViewProperties {
+    public static final PropertyKey[] ALL_KEYS = {};
+}
diff --git a/chrome/browser/share/android/java_sources.gni b/chrome/browser/share/android/java_sources.gni
index afc755de..0533fb5 100644
--- a/chrome/browser/share/android/java_sources.gni
+++ b/chrome/browser/share/android/java_sources.gni
@@ -27,4 +27,9 @@
   "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/qrcode/share_tab/QrCodeShareViewBinder.java",
   "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/qrcode/share_tab/QrCodeShareViewProperties.java",
   "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/screenshot/ScreenshotCoordinator.java",
+  "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/screenshot/ScreenshotShareSheetCoordinator.java",
+  "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/screenshot/ScreenshotShareSheetDialog.java",
+  "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/screenshot/ScreenshotShareSheetView.java",
+  "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/screenshot/ScreenshotShareSheetViewBinder.java",
+  "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/screenshot/ScreenshotShareSheetViewProperties.java",
 ]
diff --git a/chrome/browser/signin/chrome_signin_helper.cc b/chrome/browser/signin/chrome_signin_helper.cc
index 89ec38c6..8f17876 100644
--- a/chrome/browser/signin/chrome_signin_helper.cc
+++ b/chrome/browser/signin/chrome_signin_helper.cc
@@ -304,6 +304,8 @@
     signin_metrics::LogAccountReconcilorStateOnGaiaResponse(
         account_reconcilor->GetState());
     auto* window = web_contents->GetNativeView()->GetWindowAndroid();
+    if (!window)
+      return;
     SigninUtils::OpenAccountManagementScreen(window, service_type,
                                              manage_accounts_params.email);
   }
diff --git a/chrome/browser/thumbnail/generator/BUILD.gn b/chrome/browser/thumbnail/generator/BUILD.gn
index a4dd63a..361335f 100644
--- a/chrome/browser/thumbnail/generator/BUILD.gn
+++ b/chrome/browser/thumbnail/generator/BUILD.gn
@@ -114,6 +114,7 @@
       "//chrome/browser/flags:java",
       "//chrome/browser/util:java",
       "//chrome/test/android:chrome_java_test_support",
+      "//components/browser_ui/util/android:java",
       "//content/public/android:content_java",
       "//content/public/test/android:content_java_test_support",
       "//third_party/android_deps:android_support_v4_java",
diff --git a/chrome/browser/touch_to_fill/android/BUILD.gn b/chrome/browser/touch_to_fill/android/BUILD.gn
index de15117..44409ab6 100644
--- a/chrome/browser/touch_to_fill/android/BUILD.gn
+++ b/chrome/browser/touch_to_fill/android/BUILD.gn
@@ -47,13 +47,21 @@
   sources = [ "junit/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillControllerTest.java" ]
 
   deps = [
+    "//base:base_java",
     "//base:base_java_test_support",
     "//base:base_junit_test_support",
+    "//chrome/android:chrome_java",
     "//chrome/android:chrome_test_util_java",
+    "//chrome/browser/touch_to_fill/android:public_java",
     "//chrome/browser/touch_to_fill/android/internal:java",
+    "//chrome/browser/ui/android/favicon:java",
+    "//chrome/test/android:chrome_java_test_support",
     "//components/module_installer/android:module_installer_java",
+    "//components/url_formatter/android:url_formatter_java",
+    "//third_party/hamcrest:hamcrest_java",
     "//third_party/junit",
     "//third_party/mockito:mockito_java",
+    "//ui/android:ui_full_java",
   ]
 }
 
diff --git a/chrome/browser/touch_to_fill/android/internal/BUILD.gn b/chrome/browser/touch_to_fill/android/internal/BUILD.gn
index 28683e96..66dfa82 100644
--- a/chrome/browser/touch_to_fill/android/internal/BUILD.gn
+++ b/chrome/browser/touch_to_fill/android/internal/BUILD.gn
@@ -17,6 +17,7 @@
     "//chrome/browser/touch_to_fill/android:public_java",
     "//chrome/browser/ui/android/favicon:java",
     "//chrome/browser/util:java",
+    "//components/browser_ui/android/bottomsheet:java",
     "//components/embedder_support/android:util_java",
     "//components/url_formatter/android:url_formatter_java",
     "//third_party/android_deps:androidx_recyclerview_recyclerview_java",
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 1a2322cf..d4d9f737 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -1741,6 +1741,8 @@
       "views/touch_selection_menu_chromeos.h",
       "views/touch_selection_menu_runner_chromeos.cc",
       "views/touch_selection_menu_runner_chromeos.h",
+      "webui/certificate_provisioning_ui_handler.cc",
+      "webui/certificate_provisioning_ui_handler.h",
       "webui/chromeos/account_manager_error_ui.cc",
       "webui/chromeos/account_manager_error_ui.h",
       "webui/chromeos/account_manager_welcome_dialog.cc",
@@ -4494,6 +4496,12 @@
       "ash/test_session_controller.h",
       "ash/test_wallpaper_controller.cc",
       "ash/test_wallpaper_controller.h",
+      "webui/settings/chromeos/fake_hierarchy.cc",
+      "webui/settings/chromeos/fake_hierarchy.h",
+      "webui/settings/chromeos/fake_os_settings_section.cc",
+      "webui/settings/chromeos/fake_os_settings_section.h",
+      "webui/settings/chromeos/fake_os_settings_sections.cc",
+      "webui/settings/chromeos/fake_os_settings_sections.h",
     ]
     deps += [
       "//ash/public/cpp",
diff --git a/chrome/browser/ui/android/appmenu/internal/BUILD.gn b/chrome/browser/ui/android/appmenu/internal/BUILD.gn
index 81e5cda..01b9a75 100644
--- a/chrome/browser/ui/android/appmenu/internal/BUILD.gn
+++ b/chrome/browser/ui/android/appmenu/internal/BUILD.gn
@@ -30,6 +30,7 @@
     "//components/browser_ui/widget/android:java",
     "//third_party/android_deps:android_support_v7_appcompat_java",
     "//third_party/android_deps:androidx_annotation_annotation_java",
+    "//third_party/android_deps:androidx_appcompat_appcompat_resources_java",
     "//ui/android:ui_java",
   ]
 }
@@ -74,6 +75,7 @@
     "//components/browser_ui/widget/android:test_support_java",
     "//content/public/test/android:content_java_test_support",
     "//third_party/android_deps:android_support_v7_appcompat_java",
+    "//third_party/android_deps:androidx_appcompat_appcompat_resources_java",
     "//third_party/android_support_test_runner:rules_java",
     "//third_party/android_support_test_runner:runner_java",
     "//third_party/junit",
@@ -110,5 +112,8 @@
   deps = [
     ":java",
     "//base:base_junit_test_support",
+    "//third_party/junit",
+    "//third_party/mockito:mockito_java",
+    "//third_party/robolectric:robolectric_all_java",
   ]
 }
diff --git a/chrome/browser/ui/android/autofill/card_unmask_prompt_view_android.cc b/chrome/browser/ui/android/autofill/card_unmask_prompt_view_android.cc
index 2f2f2d1..8bb9f80 100644
--- a/chrome/browser/ui/android/autofill/card_unmask_prompt_view_android.cc
+++ b/chrome/browser/ui/android/autofill/card_unmask_prompt_view_android.cc
@@ -154,7 +154,6 @@
              confirm,
              ResourceMapper::MapToJavaDrawableId(controller_->GetCvcImageRid()),
              controller_->ShouldRequestExpirationDate(),
-             controller_->CanStoreLocally(),
              controller_->GetStoreLocallyStartState(),
              controller_->ShouldOfferWebauthn(),
              controller_->GetWebauthnOfferStartState(),
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd
index 186b225..245d952 100644
--- a/chrome/browser/ui/android/strings/android_chrome_strings.grd
+++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd
@@ -3666,6 +3666,24 @@
       <message name="IDS_QR_CODE_FAILED_DOWNLOAD_TEXT" desc="Notification text for failed QR code download.">
         Something went wrong
       </message>
+
+      <!-- Share Screenshot strings -->
+      <message name="IDS_SCREENSHOT_EDIT_TITLE" desc="The text shown on the share option for screenshots.">
+        Edit
+      </message>
+
+      <message name="IDS_SCREENSHOT_DELETE_TITLE" desc="The text shown on the delete option for screenshots.">
+        Delete
+      </message>
+
+      <message name="IDS_SCREENSHOT_SAVE_TITLE" desc="The text shown on the save option for screenshots.">
+        Save
+      </message>
+
+      <message name="IDS_SCREENSHOT_SHARE_TITLE" desc="The text shown on the share option for screenshots.">
+        Share
+      </message>
+
       <!-- Chime DFM module strings -->
       <message name="IDS_CHIME_MODULE_TITLE" desc="Text shown when the chime module is referenced in install start, success, failure UI (e.g. in IDS_MODULE_INSTALL_START_TEXT, which will expand to 'Installing Google Notifications Platform for Chrome…').">
         Google Notifications Platform
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_SCREENSHOT_DELETE_TITLE.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_SCREENSHOT_DELETE_TITLE.png.sha1
new file mode 100644
index 0000000..64d9d58
--- /dev/null
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_SCREENSHOT_DELETE_TITLE.png.sha1
@@ -0,0 +1 @@
+1a93040ac25614a480537ad983fb5f5f28c0d3df
\ No newline at end of file
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_SCREENSHOT_EDIT_TITLE.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_SCREENSHOT_EDIT_TITLE.png.sha1
new file mode 100644
index 0000000..64d9d58
--- /dev/null
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_SCREENSHOT_EDIT_TITLE.png.sha1
@@ -0,0 +1 @@
+1a93040ac25614a480537ad983fb5f5f28c0d3df
\ No newline at end of file
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_SCREENSHOT_SAVE_TITLE.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_SCREENSHOT_SAVE_TITLE.png.sha1
new file mode 100644
index 0000000..64d9d58
--- /dev/null
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_SCREENSHOT_SAVE_TITLE.png.sha1
@@ -0,0 +1 @@
+1a93040ac25614a480537ad983fb5f5f28c0d3df
\ No newline at end of file
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_SCREENSHOT_SHARE_TITLE.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_SCREENSHOT_SHARE_TITLE.png.sha1
new file mode 100644
index 0000000..64d9d58
--- /dev/null
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_SCREENSHOT_SHARE_TITLE.png.sha1
@@ -0,0 +1 @@
+1a93040ac25614a480537ad983fb5f5f28c0d3df
\ No newline at end of file
diff --git a/chrome/browser/ui/ash/OWNERS b/chrome/browser/ui/ash/OWNERS
index aee95f3..59d8dd0 100644
--- a/chrome/browser/ui/ash/OWNERS
+++ b/chrome/browser/ui/ash/OWNERS
@@ -4,6 +4,7 @@
 xiyuan@chromium.org
 
 per-file chrome_keyboard_ui*=yhanada@chromium.org
+per-file *login*=file://ash/login/OWNERS
 per-file keyboard_*=yhanada@chromium.org
 per-file *wallpaper*=file://ash/wallpaper/OWNERS
 
diff --git a/chrome/browser/ui/ash/assistant/assistant_browsertest.cc b/chrome/browser/ui/ash/assistant/assistant_browsertest.cc
index ee0520be..8edbf62 100644
--- a/chrome/browser/ui/ash/assistant/assistant_browsertest.cc
+++ b/chrome/browser/ui/ash/assistant/assistant_browsertest.cc
@@ -11,8 +11,6 @@
 #include "chromeos/dbus/power_manager/backlight.pb.h"
 #include "chromeos/services/assistant/public/cpp/features.h"
 #include "content/public/test/browser_test.h"
-#include "ui/message_center/message_center.h"
-#include "ui/message_center/public/cpp/notification.h"
 
 namespace chromeos {
 namespace assistant {
@@ -25,14 +23,6 @@
 
 constexpr int kStartBrightnessPercent = 50;
 
-// Ensures that |str_| starts with |prefix_|. If it doesn't, this will print a
-// nice error message.
-#define EXPECT_STARTS_WITH(str_, prefix_)                                      \
-  ({                                                                           \
-    EXPECT_TRUE(base::StartsWith(str_, prefix_, base::CompareCase::SENSITIVE)) \
-        << "Expected '" << str_ << "'' to start with '" << prefix_ << "'";     \
-  })
-
 // Ensures that |value_| is within the range {min_, max_}. If it isn't, this
 // will print a nice error message.
 #define EXPECT_WITHIN_RANGE(min_, value_, max_)                \
@@ -238,61 +228,5 @@
 // response processing v1 as well as response processing v2.
 INSTANTIATE_TEST_SUITE_P(All, AssistantBrowserTest, testing::Bool());
 
-// TODO(b/153485859): Move to assistant_timers_browsertest.cc.
-class AssistantTimersV2BrowserTest : public MixinBasedInProcessBrowserTest {
- public:
-  AssistantTimersV2BrowserTest() {
-    feature_list_.InitAndEnableFeature(features::kAssistantTimersV2);
-  }
-
-  AssistantTimersV2BrowserTest(const AssistantTimersV2BrowserTest&) = delete;
-  AssistantTimersV2BrowserTest& operator=(const AssistantTimersV2BrowserTest&) =
-      delete;
-
-  ~AssistantTimersV2BrowserTest() override = default;
-
-  void ShowAssistantUi() {
-    if (!tester()->IsVisible())
-      tester()->PressAssistantKey();
-  }
-
-  AssistantTestMixin* tester() { return &tester_; }
-
- private:
-  base::test::ScopedFeatureList feature_list_;
-  AssistantTestMixin tester_{&mixin_host_, this, embedded_test_server(), kMode,
-                             kVersion};
-};
-
-IN_PROC_BROWSER_TEST_F(AssistantTimersV2BrowserTest,
-                       ShouldDismissTimerNotificationsWhenDisablingAssistant) {
-  tester()->StartAssistantAndWaitForReady();
-
-  ShowAssistantUi();
-  EXPECT_TRUE(tester()->IsVisible());
-
-  // Confirm no Assistant notifications are currently being shown.
-  auto* message_center = message_center::MessageCenter::Get();
-  EXPECT_TRUE(message_center->FindNotificationsByAppId("assistant").empty());
-
-  // Start a timer for one minute.
-  tester()->SendTextQuery("Set a timer for 1 minute.");
-
-  // Check for a stable substring of the expected answers.
-  tester()->ExpectTextResponse("1 min.");
-
-  // Confirm that an Assistant timer notification is now showing.
-  auto notifications = message_center->FindNotificationsByAppId("assistant");
-  ASSERT_EQ(1u, notifications.size());
-  EXPECT_STARTS_WITH((*notifications.begin())->id(), "assistant/timer");
-
-  // Disable Assistant.
-  tester()->SetAssistantEnabled(false);
-  base::RunLoop().RunUntilIdle();
-
-  // Confirm that our Assistant timer notification has been dismissed.
-  EXPECT_TRUE(message_center->FindNotificationsByAppId("assistant").empty());
-}
-
 }  // namespace assistant
 }  // namespace chromeos
diff --git a/chrome/browser/ui/ash/assistant/assistant_timers_browsertest.cc b/chrome/browser/ui/ash/assistant/assistant_timers_browsertest.cc
index b764975..0ab8ebb 100644
--- a/chrome/browser/ui/ash/assistant/assistant_timers_browsertest.cc
+++ b/chrome/browser/ui/ash/assistant/assistant_timers_browsertest.cc
@@ -29,8 +29,9 @@
 
 namespace {
 
-// Please remember to set auth token when running in |kProxy| mode.
+// Please remember to set auth token when *not* running in |kReplay| mode.
 constexpr auto kMode = FakeS3Mode::kReplay;
+
 // Update this when you introduce breaking changes to existing tests.
 constexpr int kVersion = 1;
 
@@ -173,6 +174,34 @@
 
 // Tests -----------------------------------------------------------------------
 
+// Timer notifications should be dismissed when disabling Assistant in settings.
+IN_PROC_BROWSER_TEST_F(AssistantTimersBrowserTest,
+                       ShouldDismissTimerNotificationsWhenDisablingAssistant) {
+  tester()->StartAssistantAndWaitForReady();
+
+  ShowAssistantUi();
+  EXPECT_TRUE(tester()->IsVisible());
+
+  // Confirm no Assistant notifications are currently being shown.
+  EXPECT_TRUE(FindAssistantNotifications().empty());
+
+  // Start a timer for one minute.
+  tester()->SendTextQuery("Set a timer for 1 minute.");
+
+  // Check for a stable substring of the expected answers.
+  tester()->ExpectTextResponse("1 min.");
+
+  // Confirm that an Assistant timer notification is now showing.
+  ASSERT_EQ(1u, FindVisibleNotificationsByPrefixedId("assistant/timer").size());
+
+  // Disable Assistant.
+  tester()->SetAssistantEnabled(false);
+  base::RunLoop().RunUntilIdle();
+
+  // Confirm that our Assistant timer notification has been dismissed.
+  EXPECT_TRUE(FindAssistantNotifications().empty());
+}
+
 // Pressing the "STOP" action button in a timer notification should result in
 // the timer being removed.
 IN_PROC_BROWSER_TEST_F(AssistantTimersBrowserTest,
diff --git a/chrome/browser/ui/autofill/chrome_autofill_client.cc b/chrome/browser/ui/autofill/chrome_autofill_client.cc
index 76a8804..5a81625 100644
--- a/chrome/browser/ui/autofill/chrome_autofill_client.cc
+++ b/chrome/browser/ui/autofill/chrome_autofill_client.cc
@@ -641,9 +641,7 @@
           GetPersonalDataManager(),
           GetPersonalDataManager()->app_locale())),
       unmask_controller_(
-          user_prefs::UserPrefs::Get(web_contents->GetBrowserContext()),
-          Profile::FromBrowserContext(web_contents->GetBrowserContext())
-              ->IsOffTheRecord()) {
+          user_prefs::UserPrefs::Get(web_contents->GetBrowserContext())) {
   // TODO(crbug.com/928595): Replace the closure with a callback to the renderer
   // that indicates if log messages should be sent from the renderer.
   log_manager_ =
diff --git a/chrome/browser/ui/autofill/payments/card_unmask_prompt_view_browsertest.cc b/chrome/browser/ui/autofill/payments/card_unmask_prompt_view_browsertest.cc
index 99d7ca8..a5971db 100644
--- a/chrome/browser/ui/autofill/payments/card_unmask_prompt_view_browsertest.cc
+++ b/chrome/browser/ui/autofill/payments/card_unmask_prompt_view_browsertest.cc
@@ -70,8 +70,7 @@
       content::WebContents* contents,
       scoped_refptr<content::MessageLoopRunner> runner)
       : CardUnmaskPromptControllerImpl(
-            user_prefs::UserPrefs::Get(contents->GetBrowserContext()),
-            false),
+            user_prefs::UserPrefs::Get(contents->GetBrowserContext())),
         runner_(runner) {}
 
   // CardUnmaskPromptControllerImpl:.
diff --git a/chrome/browser/ui/extensions/settings_api_bubble_helpers.cc b/chrome/browser/ui/extensions/settings_api_bubble_helpers.cc
index 2412a85..75c7757 100644
--- a/chrome/browser/ui/extensions/settings_api_bubble_helpers.cc
+++ b/chrome/browser/ui/extensions/settings_api_bubble_helpers.cc
@@ -74,11 +74,31 @@
     content::WebContents* web_contents,
     AutocompleteMatch::Type match_type) {
 #if defined(OS_WIN) || defined(OS_MACOSX)
-  if (AutocompleteMatch::IsSearchType(match_type) &&
-      match_type != AutocompleteMatchType::SEARCH_OTHER_ENGINE) {
-    Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
-    if (browser)
-      ShowSettingsApiBubble(BUBBLE_TYPE_SEARCH_ENGINE, browser);
+  if (!AutocompleteMatch::IsSearchType(match_type) ||
+      match_type == AutocompleteMatchType::SEARCH_OTHER_ENGINE) {
+    return;
+  }
+
+  Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
+  if (!browser)
+    return;
+
+  if (base::FeatureList::IsEnabled(
+          features::kExtensionSettingsOverriddenDialogs)) {
+    base::Optional<ExtensionSettingsOverriddenDialog::Params> params =
+        settings_overridden_params::GetSearchOverriddenParams(
+            browser->profile());
+    if (!params)
+      return;
+
+    auto dialog = std::make_unique<ExtensionSettingsOverriddenDialog>(
+        std::move(*params), browser->profile());
+    if (!dialog->ShouldShow())
+      return;
+
+    chrome::ShowExtensionSettingsOverriddenDialog(std::move(dialog), browser);
+  } else {
+    ShowSettingsApiBubble(BUBBLE_TYPE_SEARCH_ENGINE, browser);
   }
 #endif
 }
diff --git a/chrome/browser/ui/extensions/settings_overridden_params_providers.cc b/chrome/browser/ui/extensions/settings_overridden_params_providers.cc
index a88c135..cb4b84a 100644
--- a/chrome/browser/ui/extensions/settings_overridden_params_providers.cc
+++ b/chrome/browser/ui/extensions/settings_overridden_params_providers.cc
@@ -7,9 +7,15 @@
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/extensions/extension_web_ui.h"
 #include "chrome/browser/extensions/ntp_overridden_bubble_delegate.h"
+#include "chrome/browser/extensions/settings_api_bubble_delegate.h"
+#include "chrome/browser/extensions/settings_api_helpers.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/search_engines/template_url_service_factory.h"
 #include "chrome/common/webui_url_constants.h"
 #include "chrome/grit/generated_resources.h"
+#include "components/search_engines/template_url.h"
+#include "components/search_engines/template_url_service.h"
+#include "components/url_formatter/url_formatter.h"
 #include "ui/base/l10n/l10n_util.h"
 
 namespace settings_overridden_params {
@@ -43,4 +49,57 @@
       std::move(dialog_message));
 }
 
+base::Optional<ExtensionSettingsOverriddenDialog::Params>
+GetSearchOverriddenParams(Profile* profile) {
+  const extensions::Extension* extension =
+      extensions::GetExtensionOverridingSearchEngine(profile);
+  if (!extension)
+    return base::nullopt;
+
+  // We deliberately re-use the same preference that the bubble UI uses. This
+  // way, users won't see the bubble or dialog UI if they've already
+  // acknowledged either version.
+  const char* preference_name =
+      extensions::SettingsApiBubbleDelegate::kAcknowledgedPreference;
+
+  constexpr char kHistogramName[] =
+      "Extensions.SettingsOverridden.GenericSearchOverriddenDialogResult";
+
+  // Find the active search engine (which is provided by the extension).
+  TemplateURLService* template_url_service =
+      TemplateURLServiceFactory::GetForProfile(profile);
+  DCHECK(template_url_service->IsExtensionControlledDefaultSearch());
+  const TemplateURL* default_search =
+      template_url_service->GetDefaultSearchProvider();
+  DCHECK(default_search);
+  DCHECK_EQ(TemplateURL::NORMAL_CONTROLLED_BY_EXTENSION,
+            default_search->type());
+
+  // NOTE: For most TemplateURLs, there's no guarantee that search_url is a
+  // valid URL (it could contain placeholders, etc). However, for extension-
+  // provided search engines, we require they be valid URLs.
+  GURL search_url(default_search->url());
+  DCHECK(search_url.is_valid()) << default_search->url();
+
+  // Format the URL for display.
+  const url_formatter::FormatUrlTypes kFormatRules =
+      url_formatter::kFormatUrlOmitTrivialSubdomains |
+      url_formatter::kFormatUrlTrimAfterHost |
+      url_formatter::kFormatUrlOmitHTTPS;
+  base::string16 formatted_search_url = url_formatter::FormatUrl(
+      search_url, kFormatRules, net::UnescapeRule::SPACES, nullptr, nullptr,
+      nullptr);
+
+  // TODO(devlin): Adjust these strings based on the previous search engine.
+  base::string16 dialog_title = l10n_util::GetStringUTF16(
+      IDS_EXTENSION_SEARCH_OVERRIDDEN_DIALOG_TITLE_GENERIC);
+  base::string16 dialog_message = l10n_util::GetStringFUTF16(
+      IDS_EXTENSION_SEARCH_OVERRIDDEN_DIALOG_BODY_GENERIC, formatted_search_url,
+      base::UTF8ToUTF16(extension->name().c_str()));
+
+  return ExtensionSettingsOverriddenDialog::Params(
+      extension->id(), preference_name, kHistogramName, std::move(dialog_title),
+      std::move(dialog_message));
+}
+
 }  // namespace settings_overridden_params
diff --git a/chrome/browser/ui/extensions/settings_overridden_params_providers.h b/chrome/browser/ui/extensions/settings_overridden_params_providers.h
index 94fd7c6..38ba84c 100644
--- a/chrome/browser/ui/extensions/settings_overridden_params_providers.h
+++ b/chrome/browser/ui/extensions/settings_overridden_params_providers.h
@@ -15,6 +15,12 @@
 base::Optional<ExtensionSettingsOverriddenDialog::Params>
 GetNtpOverriddenParams(Profile* profile);
 
+// Retrieves the params for displaying the dialog indicating that the default
+// search engine has been overridden, if there is a controlling extension.
+// Otherwise, returns an empty optional.
+base::Optional<ExtensionSettingsOverriddenDialog::Params>
+GetSearchOverriddenParams(Profile* profile);
+
 }  // namespace settings_overridden_params
 
 #endif  // CHROME_BROWSER_UI_EXTENSIONS_SETTINGS_OVERRIDDEN_PARAMS_PROVIDERS_H_
diff --git a/chrome/browser/ui/extensions/settings_overridden_params_providers_browsertest.cc b/chrome/browser/ui/extensions/settings_overridden_params_providers_browsertest.cc
new file mode 100644
index 0000000..1075cd8
--- /dev/null
+++ b/chrome/browser/ui/extensions/settings_overridden_params_providers_browsertest.cc
@@ -0,0 +1,69 @@
+// 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/extensions/settings_overridden_params_providers.h"
+
+#include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
+#include "chrome/browser/extensions/extension_browsertest.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/settings_api_helpers.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "content/public/test/browser_test.h"
+#include "extensions/browser/extension_system.h"
+#include "extensions/common/extension_builder.h"
+#include "extensions/common/value_builder.h"
+
+class SettingsOverriddenParamsProvidersBrowserTest
+    : public extensions::ExtensionBrowserTest {
+ public:
+  // Installs a new extension that controls the default search engine.
+  const extensions::Extension* AddExtensionControllingSearch() {
+    const extensions::Extension* extension =
+        InstallExtensionWithPermissionsGranted(
+            test_data_dir_.AppendASCII("search_provider_override"), 1);
+    EXPECT_EQ(extension,
+              extensions::GetExtensionOverridingSearchEngine(profile()));
+    return extension;
+  }
+};
+
+// The chrome_settings_overrides API that allows extensions to override the
+// default search provider is only available on Windows and Mac.
+#if defined(OS_WIN) || defined(OS_MACOSX)
+
+// NOTE: It's very unfortunate that this has to be a browsertest. Unfortunately,
+// a few bits here - the TemplateURLService in particular - don't play nicely
+// with a unittest environment.
+IN_PROC_BROWSER_TEST_F(SettingsOverriddenParamsProvidersBrowserTest,
+                       GetExtensionControllingSearch) {
+  // With no extensions installed, there should be no controlling extension.
+  EXPECT_EQ(base::nullopt,
+            settings_overridden_params::GetSearchOverriddenParams(profile()));
+
+  // Install an extension, but not one that overrides the default search engine.
+  // There should still be no controlling extension.
+  InstallExtensionWithPermissionsGranted(
+      test_data_dir_.AppendASCII("simple_with_icon"), 1);
+  EXPECT_EQ(base::nullopt,
+            settings_overridden_params::GetSearchOverriddenParams(profile()));
+
+  // Finally, install an extension that overrides the default search engine.
+  // It should be the controlling extension.
+  const extensions::Extension* search_extension =
+      AddExtensionControllingSearch();
+  base::Optional<ExtensionSettingsOverriddenDialog::Params> params =
+      settings_overridden_params::GetSearchOverriddenParams(profile());
+  ASSERT_TRUE(params);
+  EXPECT_EQ(search_extension->id(), params->controlling_extension_id);
+
+  // Validate the body message, since it has a bit of formatting applied.
+  EXPECT_EQ(
+      "The Search Override Extension extension changed search to use "
+      "example.com",
+      base::UTF16ToUTF8(params->dialog_message));
+}
+
+#endif  // defined(OS_WIN) || defined(OS_MACOSX)
diff --git a/chrome/browser/ui/messages/android/BUILD.gn b/chrome/browser/ui/messages/android/BUILD.gn
index 44070c1..cc63b83 100644
--- a/chrome/browser/ui/messages/android/BUILD.gn
+++ b/chrome/browser/ui/messages/android/BUILD.gn
@@ -65,6 +65,7 @@
     "//components/infobars/core:infobar_enums_java",
     "//third_party/android_deps:androidx_annotation_annotation_java",
     "//third_party/android_deps:androidx_appcompat_appcompat_java",
+    "//third_party/android_deps:androidx_appcompat_appcompat_resources_java",
     "//ui/android:ui_full_java",
     "//ui/android:ui_utils_java",
   ]
diff --git a/chrome/browser/ui/views/autofill/payments/card_unmask_prompt_views.cc b/chrome/browser/ui/views/autofill/payments/card_unmask_prompt_views.cc
index 25cb448..15f8243 100644
--- a/chrome/browser/ui/views/autofill/payments/card_unmask_prompt_views.cc
+++ b/chrome/browser/ui/views/autofill/payments/card_unmask_prompt_views.cc
@@ -64,19 +64,6 @@
   return overlay_layout;
 }
 
-std::unique_ptr<views::Checkbox> CreateSaveCheckbox(bool start_state) {
-  auto storage_checkbox =
-      std::make_unique<views::Checkbox>(l10n_util::GetStringUTF16(
-          IDS_AUTOFILL_CARD_UNMASK_PROMPT_STORAGE_CHECKBOX));
-  storage_checkbox->SetBorder(views::CreateEmptyBorder(gfx::Insets()));
-  storage_checkbox->SetChecked(start_state);
-  storage_checkbox->SetEnabledTextColors(views::style::GetColor(
-      *storage_checkbox.get(), ChromeTextContext::CONTEXT_BODY_TEXT_SMALL,
-      views::style::STYLE_SECONDARY));
-
-  return storage_checkbox;
-}
-
 }  // namespace
 
 CardUnmaskPromptViews::CardUnmaskPromptViews(
@@ -84,11 +71,6 @@
     content::WebContents* web_contents)
     : controller_(controller), web_contents_(web_contents) {
   chrome::RecordDialogCreation(chrome::DialogIdentifier::CARD_UNMASK);
-  if (controller_->CanStoreLocally()) {
-    storage_checkbox_ = SetFootnoteView(
-        CreateSaveCheckbox(controller_->GetStoreLocallyStartState()));
-  }
-
   UpdateButtons();
 }
 
diff --git a/chrome/browser/ui/views/autofill/payments/local_card_migration_browsertest.cc b/chrome/browser/ui/views/autofill/payments/local_card_migration_browsertest.cc
index 215650b..f72811b 100644
--- a/chrome/browser/ui/views/autofill/payments/local_card_migration_browsertest.cc
+++ b/chrome/browser/ui/views/autofill/payments/local_card_migration_browsertest.cc
@@ -385,7 +385,7 @@
 
   void ClickOnDialogViewAndWait(
       views::View* view,
-      views::DialogDelegateView* local_card_migration_view) {
+      views::BubbleDialogDelegateView* local_card_migration_view) {
     CHECK(local_card_migration_view);
     views::test::WidgetDestroyedWaiter destroyed_waiter(
         local_card_migration_view->GetWidget());
@@ -402,7 +402,7 @@
 
   views::View* FindViewInDialogById(
       DialogViewId view_id,
-      views::DialogDelegateView* local_card_migration_view) {
+      views::BubbleDialogDelegateView* local_card_migration_view) {
     CHECK(local_card_migration_view);
 
     views::View* specified_view =
@@ -417,14 +417,15 @@
     return specified_view;
   }
 
-  void ClickOnOkButton(views::DialogDelegateView* local_card_migration_view) {
+  void ClickOnOkButton(
+      views::BubbleDialogDelegateView* local_card_migration_view) {
     views::View* ok_button = local_card_migration_view->GetOkButton();
 
     ClickOnDialogViewAndWait(ok_button, local_card_migration_view);
   }
 
   void ClickOnCancelButton(
-      views::DialogDelegateView* local_card_migration_view) {
+      views::BubbleDialogDelegateView* local_card_migration_view) {
     views::View* cancel_button = local_card_migration_view->GetCancelButton();
     ClickOnDialogViewAndWait(cancel_button, local_card_migration_view);
   }
@@ -441,7 +442,7 @@
             ->local_card_migration_bubble_view());
   }
 
-  views::DialogDelegateView* GetLocalCardMigrationMainDialogView() {
+  views::BubbleDialogDelegateView* GetLocalCardMigrationMainDialogView() {
     LocalCardMigrationDialogControllerImpl*
         local_card_migration_dialog_controller_impl =
             LocalCardMigrationDialogControllerImpl::FromWebContents(
diff --git a/chrome/browser/ui/views/autofill/payments/save_card_bubble_views_browsertest.cc b/chrome/browser/ui/views/autofill/payments/save_card_bubble_views_browsertest.cc
index 14ba443..ae4eb3b 100644
--- a/chrome/browser/ui/views/autofill/payments/save_card_bubble_views_browsertest.cc
+++ b/chrome/browser/ui/views/autofill/payments/save_card_bubble_views_browsertest.cc
@@ -817,23 +817,6 @@
   DISALLOW_COPY_AND_ASSIGN(SaveCardBubbleViewsFullFormBrowserTest);
 };
 
-class SaveCardBubbleViewsFullFormBrowserTestWithEditableExpirationDate
-    : public SaveCardBubbleViewsFullFormBrowserTest {
- public:
-  SaveCardBubbleViewsFullFormBrowserTestWithEditableExpirationDate() {
-    // Enable the EditableExpirationDate experiment.
-    feature_list_.InitWithFeatures(
-        // Enabled
-        {features::kAutofillUpstreamEditableExpirationDate,
-         features::kAutofillUpstream},
-        // Disabled
-        {});
-  }
-
- private:
-  base::test::ScopedFeatureList feature_list_;
-};
-
 // TODO(crbug.com/932818): Remove this class after experiment flag is cleaned
 // up. Otherwise we need it because the toolbar is init-ed before each test is
 // set up. Thus need to enable the feature in the general browsertest SetUp().
@@ -1776,7 +1759,7 @@
 // Tests the upload save bubble. Ensures that the bubble surfaces a pair of
 // dropdowns requesting expiration date if expiration date is missing.
 IN_PROC_BROWSER_TEST_F(
-    SaveCardBubbleViewsFullFormBrowserTestWithEditableExpirationDate,
+    SaveCardBubbleViewsFullFormBrowserTestWithAutofillUpstream,
     Upload_SubmittingFormWithMissingExpirationDateRequestsExpirationDate) {
   SetUpForEditableExpirationDate();
   FillFormWithoutExpirationDate();
@@ -1787,7 +1770,7 @@
 // Tests the upload save bubble. Ensures that the bubble surfaces a pair of
 // dropdowns requesting expiration date if expiration date is expired.
 IN_PROC_BROWSER_TEST_F(
-    SaveCardBubbleViewsFullFormBrowserTestWithEditableExpirationDate,
+    SaveCardBubbleViewsFullFormBrowserTestWithAutofillUpstream,
     Upload_SubmittingFormWithExpiredExpirationDateRequestsExpirationDate) {
   SetUpForEditableExpirationDate();
   FillFormWithSpecificExpirationDate("08", "2000");
@@ -1795,51 +1778,10 @@
   VerifyExpirationDateDropdownsAreVisible();
 }
 
-class
-    SaveCardBubbleViewsFullFormBrowserTestWithAutofillUpstreamAndNoEditableExpirationDate
-    : public SaveCardBubbleViewsFullFormBrowserTest {
- public:
-  SaveCardBubbleViewsFullFormBrowserTestWithAutofillUpstreamAndNoEditableExpirationDate() {
-    // Disable the EditableExpirationDate experiment.
-    feature_list_.InitWithFeatures(
-        // Enabled
-        {features::kAutofillUpstream},
-        // Disabled
-        {features::kAutofillUpstreamEditableExpirationDate});
-  }
-
- private:
-  base::test::ScopedFeatureList feature_list_;
-};
-
-// Tests the upload save bubble. Ensures that the bubble is not shown when
-// expiration date is passed, but the flag is disabled.
-IN_PROC_BROWSER_TEST_F(
-    SaveCardBubbleViewsFullFormBrowserTestWithAutofillUpstreamAndNoEditableExpirationDate,
-    Logic_ShouldNotOfferToSaveIfSubmittingExpiredExpirationDateAndExpOff) {
-  // The credit card will not be imported if the expiration date is expired and
-  // experiment is off.
-  FillFormWithSpecificExpirationDate("08", "2000");
-  SubmitForm();
-  EXPECT_FALSE(GetSaveCardBubbleViews());
-}
-
-// Tests the upload save bubble. Ensures that the bubble is not shown when
-// expiration date is missing, but the flag is disabled.
-IN_PROC_BROWSER_TEST_F(
-    SaveCardBubbleViewsFullFormBrowserTestWithAutofillUpstreamAndNoEditableExpirationDate,
-    Logic_ShouldNotOfferToSaveIfMissingExpirationDateAndExpOff) {
-  // The credit card will not be imported if there is no expiration date and
-  // experiment is off.
-  FillFormWithoutExpirationDate();
-  SubmitForm();
-  EXPECT_FALSE(GetSaveCardBubbleViews());
-}
-
 // Tests the upload save bubble. Ensures that the bubble does not surface the
 // expiration date dropdowns if it is not needed.
 IN_PROC_BROWSER_TEST_F(
-    SaveCardBubbleViewsFullFormBrowserTestWithEditableExpirationDate,
+    SaveCardBubbleViewsFullFormBrowserTestWithAutofillUpstream,
     Upload_ShouldNotRequestExpirationDateInHappyPath) {
   SetUpForEditableExpirationDate();
   FillForm();
@@ -1858,7 +1800,7 @@
 // Tests the upload save bubble. Ensures that if the expiration date drop down
 // box is changing, [Save] button will change status correctly.
 IN_PROC_BROWSER_TEST_F(
-    SaveCardBubbleViewsFullFormBrowserTestWithEditableExpirationDate,
+    SaveCardBubbleViewsFullFormBrowserTestWithAutofillUpstream,
     Upload_SaveButtonStatusResetBetweenExpirationDateSelectionChanges) {
   SetUpForEditableExpirationDate();
   FillFormWithoutExpirationDate();
@@ -1890,7 +1832,7 @@
 // Tests the upload save bubble. Ensures that if the user is selecting an
 // expired expiration date, it is not allowed to click [Save].
 IN_PROC_BROWSER_TEST_F(
-    SaveCardBubbleViewsFullFormBrowserTestWithEditableExpirationDate,
+    SaveCardBubbleViewsFullFormBrowserTestWithAutofillUpstream,
     Upload_SaveButtonIsDisabledIfExpiredExpirationDateAndExpirationDateRequested) {
   SetUpForEditableExpirationDate();
   FillFormWithoutExpirationDate();
@@ -1916,7 +1858,7 @@
 // dropdowns requesting expiration date with year pre-populated if year is valid
 // but month is missing.
 IN_PROC_BROWSER_TEST_F(
-    SaveCardBubbleViewsFullFormBrowserTestWithEditableExpirationDate,
+    SaveCardBubbleViewsFullFormBrowserTestWithAutofillUpstream,
     Upload_SubmittingFormWithMissingExpirationDateMonthAndWithValidYear) {
   SetUpForEditableExpirationDate();
   // Submit the form with a year value, but not a month value.
@@ -1934,7 +1876,7 @@
 // dropdowns requesting expiration date with month pre-populated if month is
 // detected but year is missing.
 IN_PROC_BROWSER_TEST_F(
-    SaveCardBubbleViewsFullFormBrowserTestWithEditableExpirationDate,
+    SaveCardBubbleViewsFullFormBrowserTestWithAutofillUpstream,
     Upload_SubmittingFormWithMissingExpirationDateYearAndWithMonth) {
   SetUpForEditableExpirationDate();
   // Submit the form with a month value, but not a year value.
@@ -1952,7 +1894,7 @@
 // dropdowns requesting expiration date if month is missing and year is detected
 // but out of the range of dropdown.
 IN_PROC_BROWSER_TEST_F(
-    SaveCardBubbleViewsFullFormBrowserTestWithEditableExpirationDate,
+    SaveCardBubbleViewsFullFormBrowserTestWithAutofillUpstream,
     Upload_SubmittingFormWithExpirationDateMonthAndWithYearIsOutOfRange) {
   SetUpForEditableExpirationDate();
   // Fill form but with an expiration year ten years in the future which is out
@@ -1970,7 +1912,7 @@
 // dropdowns requesting expiration date if expiration date month is missing and
 // year is detected but passed.
 IN_PROC_BROWSER_TEST_F(
-    SaveCardBubbleViewsFullFormBrowserTestWithEditableExpirationDate,
+    SaveCardBubbleViewsFullFormBrowserTestWithAutofillUpstream,
     Upload_SubmittingFormWithExpirationDateMonthAndYearExpired) {
   SetUpForEditableExpirationDate();
   // Fill form with a valid month but a passed year.
@@ -1988,7 +1930,7 @@
 // dropdowns requesting expiration date if expiration date is expired but is
 // current year.
 IN_PROC_BROWSER_TEST_F(
-    SaveCardBubbleViewsFullFormBrowserTestWithEditableExpirationDate,
+    SaveCardBubbleViewsFullFormBrowserTestWithAutofillUpstream,
     Upload_SubmittingFormWithExpirationDateMonthAndCurrentYear) {
   SetUpForEditableExpirationDate();
   const base::Time kJune2017 = base::Time::FromDoubleT(1497552271);
diff --git a/chrome/browser/ui/views/extensions/expandable_container_view.cc b/chrome/browser/ui/views/extensions/expandable_container_view.cc
index 8cd08bf..824f24a 100644
--- a/chrome/browser/ui/views/extensions/expandable_container_view.cc
+++ b/chrome/browser/ui/views/extensions/expandable_container_view.cc
@@ -39,13 +39,9 @@
   }
 }
 
-gfx::Size ExpandableContainerView::DetailsView::CalculatePreferredSize() const {
-  return expanded_ ? views::View::CalculatePreferredSize() : gfx::Size();
-}
-
 void ExpandableContainerView::DetailsView::ToggleExpanded() {
   expanded_ = !expanded_;
-  PreferredSizeChanged();
+  SetVisible(expanded_);
 }
 
 // ExpandableContainerView -----------------------------------------------------
@@ -54,29 +50,17 @@
     const std::vector<base::string16>& details,
     int available_width) {
   DCHECK(!details.empty());
+  SetLayoutManager(std::make_unique<views::BoxLayout>(
+      views::BoxLayout::Orientation::kVertical));
 
-  views::GridLayout* layout =
-      SetLayoutManager(std::make_unique<views::GridLayout>());
-  constexpr int kColumnSetId = 0;
-  views::ColumnSet* column_set = layout->AddColumnSet(kColumnSetId);
-
-  // Even though we only have one column, using a GridLayout here will
-  // properly handle a 0 height row when |details_view_| is collapsed.
-  column_set->AddColumn(views::GridLayout::LEADING, views::GridLayout::LEADING,
-                        views::GridLayout::kFixedSize,
-                        views::GridLayout::ColumnSize::kFixed, available_width,
-                        0);
-
-  layout->StartRow(views::GridLayout::kFixedSize, kColumnSetId);
-  details_view_ = layout->AddView(std::make_unique<DetailsView>(details));
-
-  layout->StartRow(views::GridLayout::kFixedSize, kColumnSetId);
+  details_view_ = AddChildView(std::make_unique<DetailsView>(details));
+  details_view_->SetVisible(false);
   auto details_link = std::make_unique<views::Link>(
       l10n_util::GetStringUTF16(IDS_EXTENSIONS_SHOW_DETAILS));
   details_link->set_callback(base::BindRepeating(
       &ExpandableContainerView::ToggleDetailLevel, base::Unretained(this)));
   details_link->SetHorizontalAlignment(gfx::ALIGN_LEFT);
-  details_link_ = layout->AddView(std::move(details_link));
+  details_link_ = AddChildView(std::move(details_link));
 }
 
 ExpandableContainerView::~ExpandableContainerView() = default;
diff --git a/chrome/browser/ui/views/extensions/expandable_container_view.h b/chrome/browser/ui/views/extensions/expandable_container_view.h
index 682a161..f110005 100644
--- a/chrome/browser/ui/views/extensions/expandable_container_view.h
+++ b/chrome/browser/ui/views/extensions/expandable_container_view.h
@@ -28,6 +28,10 @@
   ExpandableContainerView(const ExpandableContainerView&) = delete;
   ExpandableContainerView& operator=(const ExpandableContainerView&) = delete;
 
+  // Accessors for testing.
+  View* details_view() { return details_view_; }
+  void ToggleDetailLevelForTest() { ToggleDetailLevel(); }
+
  private:
   // Helper class representing the list of details, that can hide itself.
   class DetailsView : public views::View {
@@ -35,9 +39,6 @@
     explicit DetailsView(const std::vector<base::string16>& details);
     ~DetailsView() override;
 
-    // views::View:
-    gfx::Size CalculatePreferredSize() const override;
-
     // Expands or collapses this view.
     void ToggleExpanded();
 
diff --git a/chrome/browser/ui/views/extensions/expandable_container_view_unittest.cc b/chrome/browser/ui/views/extensions/expandable_container_view_unittest.cc
new file mode 100644
index 0000000..e19e050
--- /dev/null
+++ b/chrome/browser/ui/views/extensions/expandable_container_view_unittest.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 "chrome/browser/ui/views/extensions/expandable_container_view.h"
+
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/test/views/chrome_views_test_base.h"
+
+using ExpandableContainerViewTest = ChromeViewsTestBase;
+
+TEST_F(ExpandableContainerViewTest, DetailLevelVisibility) {
+  std::vector<base::string16> details;
+  details.push_back(base::ASCIIToUTF16("Detail 1"));
+  details.push_back(base::ASCIIToUTF16("Detail 2"));
+  details.push_back(base::ASCIIToUTF16("Detail 2"));
+
+  int content_width = 100;
+  auto container =
+      std::make_unique<ExpandableContainerView>(details, content_width);
+
+  // Initially the details view should not be expanded or visible.
+  EXPECT_FALSE(container->details_view()->GetVisible());
+
+  // When the link is triggered, the details should get expanded and become
+  // visible.
+  container->ToggleDetailLevelForTest();
+  EXPECT_TRUE(container->details_view()->GetVisible());
+}
diff --git a/chrome/browser/ui/views/extensions/extension_install_dialog_view.h b/chrome/browser/ui/views/extensions/extension_install_dialog_view.h
index ce8f2b4..fe0cabd 100644
--- a/chrome/browser/ui/views/extensions/extension_install_dialog_view.h
+++ b/chrome/browser/ui/views/extensions/extension_install_dialog_view.h
@@ -53,7 +53,6 @@
   // Changes the widget size to accommodate the contents' preferred size.
   void ResizeWidget();
 
- private:
   // views::BubbleDialogDelegate:
   gfx::Size CalculatePreferredSize() const override;
   void VisibilityChanged(views::View* starting_from, bool is_visible) override;
@@ -61,6 +60,7 @@
   bool IsDialogButtonEnabled(ui::DialogButton button) const override;
   bool ShouldShowCloseButton() const override;
 
+ private:
   void CloseDialog();
 
   // extensions::ExtensionRegistryObserver:
diff --git a/chrome/browser/ui/views/extensions/extension_install_dialog_view_browsertest.cc b/chrome/browser/ui/views/extensions/extension_install_dialog_view_browsertest.cc
index 3e560b7e..9c41e7f 100644
--- a/chrome/browser/ui/views/extensions/extension_install_dialog_view_browsertest.cc
+++ b/chrome/browser/ui/views/extensions/extension_install_dialog_view_browsertest.cc
@@ -186,12 +186,12 @@
       const ExtensionInstallDialogViewTest&) = delete;
 
  protected:
-  views::DialogDelegateView* CreateAndShowPrompt(
+  ExtensionInstallDialogView* CreateAndShowPrompt(
       ExtensionInstallPromptTestHelper* helper) {
     auto dialog = std::make_unique<ExtensionInstallDialogView>(
         profile(), web_contents(), helper->GetCallback(),
         CreatePrompt(ExtensionInstallPrompt::INSTALL_PROMPT));
-    views::DialogDelegateView* delegate_view = dialog.get();
+    ExtensionInstallDialogView* delegate_view = dialog.get();
 
     views::Widget* modal_dialog = views::DialogDelegate::CreateDialogWidget(
         dialog.release(), nullptr,
@@ -207,14 +207,14 @@
   {
     // User presses install.
     ExtensionInstallPromptTestHelper helper;
-    views::DialogDelegateView* delegate_view = CreateAndShowPrompt(&helper);
+    ExtensionInstallDialogView* delegate_view = CreateAndShowPrompt(&helper);
     delegate_view->AcceptDialog();
     EXPECT_EQ(ExtensionInstallPrompt::Result::ACCEPTED, helper.result());
   }
   {
     // User presses cancel.
     ExtensionInstallPromptTestHelper helper;
-    views::DialogDelegateView* delegate_view = CreateAndShowPrompt(&helper);
+    ExtensionInstallDialogView* delegate_view = CreateAndShowPrompt(&helper);
     delegate_view->CancelDialog();
     EXPECT_EQ(ExtensionInstallPrompt::Result::USER_CANCELED, helper.result());
   }
@@ -222,7 +222,7 @@
     // Dialog is closed without the user explicitly choosing to proceed or
     // cancel.
     ExtensionInstallPromptTestHelper helper;
-    views::DialogDelegateView* delegate_view = CreateAndShowPrompt(&helper);
+    ExtensionInstallDialogView* delegate_view = CreateAndShowPrompt(&helper);
     CloseAndWait(delegate_view->GetWidget());
     // TODO(devlin): Should this be ABORTED?
     EXPECT_EQ(ExtensionInstallPrompt::Result::USER_CANCELED, helper.result());
@@ -234,7 +234,7 @@
 IN_PROC_BROWSER_TEST_F(ExtensionInstallDialogViewTest, InstallButtonDelay) {
   ExtensionInstallDialogView::SetInstallButtonDelayForTesting(0);
   ExtensionInstallPromptTestHelper helper;
-  views::DialogDelegateView* delegate_view = CreateAndShowPrompt(&helper);
+  ExtensionInstallDialogView* delegate_view = CreateAndShowPrompt(&helper);
 
   // Check that dialog is visible.
   EXPECT_TRUE(delegate_view->GetVisible());
@@ -617,7 +617,7 @@
   prompt->AddPermissionSet(permissions);
   auto dialog = std::make_unique<ExtensionInstallDialogView>(
       profile(), web_contents(), helper.GetCallback(), std::move(prompt));
-  views::DialogDelegateView* delegate_view = dialog.get();
+  views::BubbleDialogDelegateView* delegate_view = dialog.get();
 
   views::Widget* modal_dialog = views::DialogDelegate::CreateDialogWidget(
       dialog.release(), nullptr,
diff --git a/chrome/browser/ui/views/extensions/extension_install_dialog_view_supervised_browsertest.cc b/chrome/browser/ui/views/extensions/extension_install_dialog_view_supervised_browsertest.cc
index 81ad8e8..5867f1f9 100644
--- a/chrome/browser/ui/views/extensions/extension_install_dialog_view_supervised_browsertest.cc
+++ b/chrome/browser/ui/views/extensions/extension_install_dialog_view_supervised_browsertest.cc
@@ -45,7 +45,7 @@
   content::WebContents* web_contents() { return web_contents_; }
 
  protected:
-  views::DialogDelegateView* CreateAndShowPrompt(
+  ExtensionInstallDialogView* CreateAndShowPrompt(
       ExtensionInstallPromptTestHelper* helper,
       std::unique_ptr<ExtensionInstallPrompt::Prompt> prompt);
 
@@ -91,13 +91,13 @@
   return prompt;
 }
 
-views::DialogDelegateView*
+ExtensionInstallDialogView*
 ExtensionInstallDialogViewTestSupervised::CreateAndShowPrompt(
     ExtensionInstallPromptTestHelper* helper,
     std::unique_ptr<ExtensionInstallPrompt::Prompt> prompt) {
   auto dialog = std::make_unique<ExtensionInstallDialogView>(
       profile(), web_contents(), helper->GetCallback(), std::move(prompt));
-  views::DialogDelegateView* delegate_view = dialog.get();
+  ExtensionInstallDialogView* delegate_view = dialog.get();
 
   views::Widget* modal_dialog = views::DialogDelegate::CreateDialogWidget(
       dialog.release(), nullptr,
@@ -150,7 +150,7 @@
   task_runner->FastForwardBy(duration);
 
   // Supervised user presses "Ask a parent".
-  views::DialogDelegateView* delegate_view =
+  ExtensionInstallDialogView* delegate_view =
       CreateAndShowPrompt(&helper, install_prompt.GetPromptForTesting());
   delegate_view->AcceptDialog();
   EXPECT_EQ(ExtensionInstallPrompt::Result::ACCEPTED, helper.result());
@@ -222,7 +222,7 @@
   task_runner->FastForwardBy(duration);
 
   // Supervised user presses "Cancel".
-  views::DialogDelegateView* delegate_view =
+  ExtensionInstallDialogView* delegate_view =
       CreateAndShowPrompt(&helper, install_prompt.GetPromptForTesting());
   delegate_view->CancelDialog();
   EXPECT_EQ(ExtensionInstallPrompt::Result::USER_CANCELED, helper.result());
diff --git a/chrome/browser/ui/views/extensions/settings_overridden_dialog_view_browsertest.cc b/chrome/browser/ui/views/extensions/settings_overridden_dialog_view_browsertest.cc
index a65c342..bea07a3 100644
--- a/chrome/browser/ui/views/extensions/settings_overridden_dialog_view_browsertest.cc
+++ b/chrome/browser/ui/views/extensions/settings_overridden_dialog_view_browsertest.cc
@@ -7,18 +7,22 @@
 #include "base/path_service.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/scoped_feature_list.h"
+#include "base/time/time.h"
+#include "build/build_config.h"
 #include "chrome/browser/extensions/chrome_test_extension_loader.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/extensions/settings_api_bubble_helpers.h"
 #include "chrome/browser/ui/extensions/settings_overridden_dialog_controller.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/test/test_browser_dialog.h"
 #include "chrome/browser/ui/ui_features.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/webui_url_constants.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "content/public/test/browser_test.h"
+#include "content/public/test/browser_test_utils.h"
 
 namespace {
 
@@ -51,10 +55,13 @@
   ~SettingsOverriddenDialogViewBrowserTest() override = default;
 
   void ShowUi(const std::string& name) override {
+    test_name_ = name;
     if (name == "SimpleDialog")
       ShowSimpleDialog();
     else if (name == "NtpOverriddenDialog")
       ShowNtpOverriddenDialog();
+    else if (name == "SearchOverriddenDialog")
+      ShowSearchOverriddenDialog();
     else
       NOTREACHED() << name;
   }
@@ -88,7 +95,45 @@
         ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);
   }
 
+  void ShowSearchOverriddenDialog() {
+    base::FilePath test_root_path;
+    ASSERT_TRUE(base::PathService::Get(chrome::DIR_TEST_DATA, &test_root_path));
+
+    // Load up an extension that overrides search.
+    Profile* const profile = browser()->profile();
+    scoped_refptr<const extensions::Extension> extension =
+        extensions::ChromeTestExtensionLoader(profile).LoadExtension(
+            test_root_path.AppendASCII("extensions/search_provider_override"));
+    ASSERT_TRUE(extension);
+
+    // Perform a search via the omnibox to trigger the dialog.
+    ui_test_utils::SendToOmniboxAndSubmit(browser(), "Penguin",
+                                          base::TimeTicks::Now());
+    content::WaitForLoadStop(
+        browser()->tab_strip_model()->GetActiveWebContents());
+  }
+
+  bool VerifyUi() override {
+    if (!DialogBrowserTest::VerifyUi())
+      return false;
+
+    if (test_name_ == "SearchOverriddenDialog") {
+      // Note: Because this is a test, we don't actually expect this navigation
+      // to succeed. But we can still check that the user was sent to
+      // example.com (the new search engine).
+      EXPECT_EQ("www.example.com", browser()
+                                       ->tab_strip_model()
+                                       ->GetActiveWebContents()
+                                       ->GetLastCommittedURL()
+                                       .host_piece());
+    }
+
+    return true;
+  }
+
  private:
+  std::string test_name_;
+
   base::test::ScopedFeatureList scoped_feature_list_;
 };
 
@@ -105,3 +150,12 @@
   ShowAndVerifyUi();
   extensions::SetNtpPostInstallUiEnabledForTesting(false);
 }
+
+// The chrome_settings_overrides API that allows extensions to override the
+// default search provider is only available on Windows and Mac.
+#if defined(OS_WIN) || defined(OS_MACOSX)
+IN_PROC_BROWSER_TEST_F(SettingsOverriddenDialogViewBrowserTest,
+                       InvokeUi_SearchOverriddenDialog) {
+  ShowAndVerifyUi();
+}
+#endif  // defined(OS_WIN) || defined(OS_MACOSX)
diff --git a/chrome/browser/ui/views/passwords/password_dialog_view_browsertest.cc b/chrome/browser/ui/views/passwords/password_dialog_view_browsertest.cc
index e0a259e..4ebf33b 100644
--- a/chrome/browser/ui/views/passwords/password_dialog_view_browsertest.cc
+++ b/chrome/browser/ui/views/passwords/password_dialog_view_browsertest.cc
@@ -322,7 +322,8 @@
                          url::Origin::Create(origin));
 
   EXPECT_TRUE(controller()->current_account_chooser());
-  views::DialogDelegateView* dialog = controller()->current_account_chooser();
+  views::BubbleDialogDelegateView* dialog =
+      controller()->current_account_chooser();
   views::test::WidgetClosingObserver bubble_observer(dialog->GetWidget());
   EXPECT_CALL(*this, OnChooseCredential(testing::Pointee(form)));
   dialog->Accept();
diff --git a/chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc b/chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc
index 3d28409..fc37c54 100644
--- a/chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc
+++ b/chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc
@@ -349,9 +349,8 @@
 
 void BrowserTabStripController::ToggleTabGroupCollapsedState(
     const tab_groups::TabGroupId group) {
-  tab_groups::TabGroupVisualData new_data(GetGroupTitle(group),
-                                          GetGroupColorId(group),
-                                          !GetGroupCollapsedState(group));
+  tab_groups::TabGroupVisualData new_data(
+      GetGroupTitle(group), GetGroupColorId(group), !IsGroupCollapsed(group));
   model_->group_model()->GetTabGroup(group)->SetVisualData(new_data, true);
 }
 
@@ -474,7 +473,7 @@
   return model_->group_model()->GetTabGroup(group)->visual_data()->color();
 }
 
-bool BrowserTabStripController::GetGroupCollapsedState(
+bool BrowserTabStripController::IsGroupCollapsed(
     const tab_groups::TabGroupId& group) const {
   return model_->group_model()->ContainsTabGroup(group) &&
          model_->group_model()
diff --git a/chrome/browser/ui/views/tabs/browser_tab_strip_controller.h b/chrome/browser/ui/views/tabs/browser_tab_strip_controller.h
index 8a096b6..3658e42 100644
--- a/chrome/browser/ui/views/tabs/browser_tab_strip_controller.h
+++ b/chrome/browser/ui/views/tabs/browser_tab_strip_controller.h
@@ -94,8 +94,7 @@
       const tab_groups::TabGroupId& group_id) const override;
   tab_groups::TabGroupColorId GetGroupColorId(
       const tab_groups::TabGroupId& group_id) const override;
-  bool GetGroupCollapsedState(
-      const tab_groups::TabGroupId& group) const override;
+  bool IsGroupCollapsed(const tab_groups::TabGroupId& group) const override;
 
   void SetVisualDataForGroup(
       const tab_groups::TabGroupId& group,
diff --git a/chrome/browser/ui/views/tabs/fake_base_tab_strip_controller.cc b/chrome/browser/ui/views/tabs/fake_base_tab_strip_controller.cc
index e6fb6c6..bada6aa 100644
--- a/chrome/browser/ui/views/tabs/fake_base_tab_strip_controller.cc
+++ b/chrome/browser/ui/views/tabs/fake_base_tab_strip_controller.cc
@@ -89,7 +89,7 @@
   return fake_group_data_.color();
 }
 
-bool FakeBaseTabStripController::GetGroupCollapsedState(
+bool FakeBaseTabStripController::IsGroupCollapsed(
     const tab_groups::TabGroupId& group) const {
   return fake_group_data_.is_collapsed();
 }
diff --git a/chrome/browser/ui/views/tabs/fake_base_tab_strip_controller.h b/chrome/browser/ui/views/tabs/fake_base_tab_strip_controller.h
index 64be95e..78acda2 100644
--- a/chrome/browser/ui/views/tabs/fake_base_tab_strip_controller.h
+++ b/chrome/browser/ui/views/tabs/fake_base_tab_strip_controller.h
@@ -67,8 +67,7 @@
       const tab_groups::TabGroupId& group_id) const override;
   tab_groups::TabGroupColorId GetGroupColorId(
       const tab_groups::TabGroupId& group_id) const override;
-  bool GetGroupCollapsedState(
-      const tab_groups::TabGroupId& group) const override;
+  bool IsGroupCollapsed(const tab_groups::TabGroupId& group) const override;
   void SetVisualDataForGroup(
       const tab_groups::TabGroupId& group,
       const tab_groups::TabGroupVisualData& visual_data) override;
diff --git a/chrome/browser/ui/views/tabs/tab_group_views.cc b/chrome/browser/ui/views/tabs/tab_group_views.cc
index 8e16b45..1136ea1 100644
--- a/chrome/browser/ui/views/tabs/tab_group_views.cc
+++ b/chrome/browser/ui/views/tabs/tab_group_views.cc
@@ -46,7 +46,7 @@
 }
 
 gfx::Rect TabGroupViews::GetBounds() const {
-  if (tab_strip_->controller()->GetGroupCollapsedState(group_))
+  if (tab_strip_->controller()->IsGroupCollapsed(group_))
     return header_->bounds();
 
   const Tab* last_tab = GetLastTabInGroup();
diff --git a/chrome/browser/ui/views/tabs/tab_strip_controller.h b/chrome/browser/ui/views/tabs/tab_strip_controller.h
index 963b77d..9e5f1a9 100644
--- a/chrome/browser/ui/views/tabs/tab_strip_controller.h
+++ b/chrome/browser/ui/views/tabs/tab_strip_controller.h
@@ -152,8 +152,9 @@
   virtual tab_groups::TabGroupColorId GetGroupColorId(
       const tab_groups::TabGroupId& group) const = 0;
 
-  virtual bool GetGroupCollapsedState(
-      const tab_groups::TabGroupId& group) const = 0;
+  // Returns the |group| collapsed state. Returns false if the group does not
+  // exist or is not collapsed.
+  virtual bool IsGroupCollapsed(const tab_groups::TabGroupId& group) const = 0;
 
   // Sets the title and color ID of the given |group|.
   virtual void SetVisualDataForGroup(
diff --git a/chrome/browser/ui/views/tabs/tab_strip_layout_helper.cc b/chrome/browser/ui/views/tabs/tab_strip_layout_helper.cc
index 35b3158..87063e6 100644
--- a/chrome/browser/ui/views/tabs/tab_strip_layout_helper.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip_layout_helper.cc
@@ -344,7 +344,7 @@
     base::Optional<tab_groups::TabGroupId> id = slots_[i].view->group();
     bool slot_is_collapsed_tab =
         (slots_[i].type == ViewType::kTab && id.has_value())
-            ? controller_->GetGroupCollapsedState(id.value())
+            ? controller_->IsGroupCollapsed(id.value())
             : false;
 
     auto open = (slots_[i].animation->IsClosing() || slot_is_collapsed_tab)
diff --git a/chrome/browser/ui/webui/certificate_manager_localized_strings_provider.cc b/chrome/browser/ui/webui/certificate_manager_localized_strings_provider.cc
index 1fb5f60..d250a38 100644
--- a/chrome/browser/ui/webui/certificate_manager_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/certificate_manager_localized_strings_provider.cc
@@ -15,74 +15,92 @@
 
 void AddLocalizedStrings(content::WebUIDataSource* html_source) {
   static constexpr webui::LocalizedString kLocalizedStrings[] = {
-      {"certificateManagerExpandA11yLabel",
-       IDS_SETTINGS_CERTIFICATE_MANAGER_EXPAND_ACCESSIBILITY_LABEL},
-      {"certificateManagerNoCertificates",
-       IDS_SETTINGS_CERTIFICATE_MANAGER_NO_CERTIFICATES},
-      {"certificateManagerYourCertificates",
-       IDS_SETTINGS_CERTIFICATE_MANAGER_YOUR_CERTIFICATES},
-      {"certificateManagerYourCertificatesDescription",
-       IDS_SETTINGS_CERTIFICATE_MANAGER_YOUR_CERTIFICATES_DESCRIPTION},
-      {"certificateManagerServers", IDS_SETTINGS_CERTIFICATE_MANAGER_SERVERS},
-      {"certificateManagerServersDescription",
-       IDS_SETTINGS_CERTIFICATE_MANAGER_SERVERS_DESCRIPTION},
-      {"certificateManagerAuthorities",
-       IDS_SETTINGS_CERTIFICATE_MANAGER_AUTHORITIES},
-      {"certificateManagerAuthoritiesDescription",
-       IDS_SETTINGS_CERTIFICATE_MANAGER_AUTHORITIES_DESCRIPTION},
-      {"certificateManagerOthers", IDS_SETTINGS_CERTIFICATE_MANAGER_OTHERS},
-      {"certificateManagerOthersDescription",
-       IDS_SETTINGS_CERTIFICATE_MANAGER_OTHERS_DESCRIPTION},
-      {"certificateManagerView", IDS_SETTINGS_CERTIFICATE_MANAGER_VIEW},
-      {"certificateManagerImport", IDS_SETTINGS_CERTIFICATE_MANAGER_IMPORT},
-      {"certificateManagerImportAndBind",
-       IDS_SETTINGS_CERTIFICATE_MANAGER_IMPORT_AND_BIND},
-      {"certificateManagerExport", IDS_SETTINGS_CERTIFICATE_MANAGER_EXPORT},
-      {"certificateManagerDelete", IDS_SETTINGS_DELETE},
-      {"certificateManagerUntrusted",
-       IDS_SETTINGS_CERTIFICATE_MANAGER_UNTRUSTED},
-      // CA trust edit dialog.
-      {"certificateManagerCaTrustEditDialogTitle",
-       IDS_SETTINGS_CERTIFICATE_MANAGER_CA_TRUST_EDIT_DIALOG_TITLE},
-      {"certificateManagerCaTrustEditDialogDescription",
-       IDS_SETTINGS_CERTIFICATE_MANAGER_CA_TRUST_EDIT_DIALOG_DESCRIPTION},
-      {"certificateManagerCaTrustEditDialogExplanation",
-       IDS_SETTINGS_CERTIFICATE_MANAGER_CA_TRUST_EDIT_DIALOG_EXPLANATION},
-      {"certificateManagerCaTrustEditDialogSsl",
-       IDS_SETTINGS_CERTIFICATE_MANAGER_CA_TRUST_EDIT_DIALOG_SSL},
-      {"certificateManagerCaTrustEditDialogEmail",
-       IDS_SETTINGS_CERTIFICATE_MANAGER_CA_TRUST_EDIT_DIALOG_EMAIL},
-      {"certificateManagerCaTrustEditDialogObjSign",
-       IDS_SETTINGS_CERTIFICATE_MANAGER_CA_TRUST_EDIT_DIALOG_OBJ_SIGN},
-      // Certificate delete confirmation dialog.
-      {"certificateManagerDeleteUserTitle",
-       IDS_SETTINGS_CERTIFICATE_MANAGER_DELETE_USER_TITLE},
-      {"certificateManagerDeleteUserDescription",
-       IDS_SETTINGS_CERTIFICATE_MANAGER_DELETE_USER_DESCRIPTION},
-      {"certificateManagerDeleteServerTitle",
-       IDS_SETTINGS_CERTIFICATE_MANAGER_DELETE_SERVER_TITLE},
-      {"certificateManagerDeleteServerDescription",
-       IDS_SETTINGS_CERTIFICATE_MANAGER_DELETE_SERVER_DESCRIPTION},
-      {"certificateManagerDeleteCaTitle",
-       IDS_SETTINGS_CERTIFICATE_MANAGER_DELETE_CA_TITLE},
-      {"certificateManagerDeleteCaDescription",
-       IDS_SETTINGS_CERTIFICATE_MANAGER_DELETE_CA_DESCRIPTION},
-      {"certificateManagerDeleteOtherTitle",
-       IDS_SETTINGS_CERTIFICATE_MANAGER_DELETE_OTHER_TITLE},
-      // Encrypt/decrypt password dialogs.
-      {"certificateManagerEncryptPasswordTitle",
-       IDS_SETTINGS_CERTIFICATE_MANAGER_ENCRYPT_PASSWORD_TITLE},
-      {"certificateManagerDecryptPasswordTitle",
-       IDS_SETTINGS_CERTIFICATE_MANAGER_DECRYPT_PASSWORD_TITLE},
-      {"certificateManagerEncryptPasswordDescription",
-       IDS_SETTINGS_CERTIFICATE_MANAGER_ENCRYPT_PASSWORD_DESCRIPTION},
-      {"certificateManagerPassword", IDS_SETTINGS_CERTIFICATE_MANAGER_PASSWORD},
-      {"certificateManagerConfirmPassword",
-       IDS_SETTINGS_CERTIFICATE_MANAGER_CONFIRM_PASSWORD},
-      {"certificateImportErrorFormat",
-       IDS_SETTINGS_CERTIFICATE_MANAGER_IMPORT_ERROR_FORMAT},
-      // For A11y.
-      {"menu", IDS_MENU},
+    {"certificateManagerExpandA11yLabel",
+     IDS_SETTINGS_CERTIFICATE_MANAGER_EXPAND_ACCESSIBILITY_LABEL},
+    {"certificateManagerNoCertificates",
+     IDS_SETTINGS_CERTIFICATE_MANAGER_NO_CERTIFICATES},
+    {"certificateManagerYourCertificates",
+     IDS_SETTINGS_CERTIFICATE_MANAGER_YOUR_CERTIFICATES},
+    {"certificateManagerYourCertificatesDescription",
+     IDS_SETTINGS_CERTIFICATE_MANAGER_YOUR_CERTIFICATES_DESCRIPTION},
+    {"certificateManagerServers", IDS_SETTINGS_CERTIFICATE_MANAGER_SERVERS},
+    {"certificateManagerServersDescription",
+     IDS_SETTINGS_CERTIFICATE_MANAGER_SERVERS_DESCRIPTION},
+    {"certificateManagerAuthorities",
+     IDS_SETTINGS_CERTIFICATE_MANAGER_AUTHORITIES},
+    {"certificateManagerAuthoritiesDescription",
+     IDS_SETTINGS_CERTIFICATE_MANAGER_AUTHORITIES_DESCRIPTION},
+    {"certificateManagerOthers", IDS_SETTINGS_CERTIFICATE_MANAGER_OTHERS},
+    {"certificateManagerOthersDescription",
+     IDS_SETTINGS_CERTIFICATE_MANAGER_OTHERS_DESCRIPTION},
+    {"certificateManagerView", IDS_SETTINGS_CERTIFICATE_MANAGER_VIEW},
+    {"certificateManagerImport", IDS_SETTINGS_CERTIFICATE_MANAGER_IMPORT},
+    {"certificateManagerImportAndBind",
+     IDS_SETTINGS_CERTIFICATE_MANAGER_IMPORT_AND_BIND},
+    {"certificateManagerExport", IDS_SETTINGS_CERTIFICATE_MANAGER_EXPORT},
+    {"certificateManagerDelete", IDS_SETTINGS_DELETE},
+    {"certificateManagerUntrusted", IDS_SETTINGS_CERTIFICATE_MANAGER_UNTRUSTED},
+    // CA trust edit dialog.
+    {"certificateManagerCaTrustEditDialogTitle",
+     IDS_SETTINGS_CERTIFICATE_MANAGER_CA_TRUST_EDIT_DIALOG_TITLE},
+    {"certificateManagerCaTrustEditDialogDescription",
+     IDS_SETTINGS_CERTIFICATE_MANAGER_CA_TRUST_EDIT_DIALOG_DESCRIPTION},
+    {"certificateManagerCaTrustEditDialogExplanation",
+     IDS_SETTINGS_CERTIFICATE_MANAGER_CA_TRUST_EDIT_DIALOG_EXPLANATION},
+    {"certificateManagerCaTrustEditDialogSsl",
+     IDS_SETTINGS_CERTIFICATE_MANAGER_CA_TRUST_EDIT_DIALOG_SSL},
+    {"certificateManagerCaTrustEditDialogEmail",
+     IDS_SETTINGS_CERTIFICATE_MANAGER_CA_TRUST_EDIT_DIALOG_EMAIL},
+    {"certificateManagerCaTrustEditDialogObjSign",
+     IDS_SETTINGS_CERTIFICATE_MANAGER_CA_TRUST_EDIT_DIALOG_OBJ_SIGN},
+    // Certificate delete confirmation dialog.
+    {"certificateManagerDeleteUserTitle",
+     IDS_SETTINGS_CERTIFICATE_MANAGER_DELETE_USER_TITLE},
+    {"certificateManagerDeleteUserDescription",
+     IDS_SETTINGS_CERTIFICATE_MANAGER_DELETE_USER_DESCRIPTION},
+    {"certificateManagerDeleteServerTitle",
+     IDS_SETTINGS_CERTIFICATE_MANAGER_DELETE_SERVER_TITLE},
+    {"certificateManagerDeleteServerDescription",
+     IDS_SETTINGS_CERTIFICATE_MANAGER_DELETE_SERVER_DESCRIPTION},
+    {"certificateManagerDeleteCaTitle",
+     IDS_SETTINGS_CERTIFICATE_MANAGER_DELETE_CA_TITLE},
+    {"certificateManagerDeleteCaDescription",
+     IDS_SETTINGS_CERTIFICATE_MANAGER_DELETE_CA_DESCRIPTION},
+    {"certificateManagerDeleteOtherTitle",
+     IDS_SETTINGS_CERTIFICATE_MANAGER_DELETE_OTHER_TITLE},
+    // Encrypt/decrypt password dialogs.
+    {"certificateManagerEncryptPasswordTitle",
+     IDS_SETTINGS_CERTIFICATE_MANAGER_ENCRYPT_PASSWORD_TITLE},
+    {"certificateManagerDecryptPasswordTitle",
+     IDS_SETTINGS_CERTIFICATE_MANAGER_DECRYPT_PASSWORD_TITLE},
+    {"certificateManagerEncryptPasswordDescription",
+     IDS_SETTINGS_CERTIFICATE_MANAGER_ENCRYPT_PASSWORD_DESCRIPTION},
+    {"certificateManagerPassword", IDS_SETTINGS_CERTIFICATE_MANAGER_PASSWORD},
+    {"certificateManagerConfirmPassword",
+     IDS_SETTINGS_CERTIFICATE_MANAGER_CONFIRM_PASSWORD},
+    {"certificateImportErrorFormat",
+     IDS_SETTINGS_CERTIFICATE_MANAGER_IMPORT_ERROR_FORMAT},
+#if defined(OS_CHROMEOS)
+    {"certificateProvisioningListHeader",
+     IDS_SETTINGS_CERTIFICATE_MANAGER_PROVISIONING_LIST_HEADER},
+    {"certificateProvisioningRefresh",
+     IDS_SETTINGS_CERTIFICATE_MANAGER_PROVISIONING_REFRESH},
+    {"certificateProvisioningDetails",
+     IDS_SETTINGS_CERTIFICATE_MANAGER_PROVISIONING_DETAILS},
+    {"certificateProvisioningAdvancedSectionTitle",
+     IDS_SETTINGS_CERTIFICATE_MANAGER_PROVISIONING_ADVANCED},
+    {"certificateProvisioningProfile",
+     IDS_SETTINGS_CERTIFICATE_MANAGER_PROVISIONING_CERTIFICATE_PROFILE},
+    {"certificateProvisioningStatus",
+     IDS_SETTINGS_CERTIFICATE_MANAGER_PROVISIONING_STATUS},
+    {"certificateProvisioningStatusId",
+     IDS_SETTINGS_CERTIFICATE_MANAGER_PROVISIONING_STATUS_ID},
+    {"certificateProvisioningLastUpdate",
+     IDS_SETTINGS_CERTIFICATE_MANAGER_PROVISIONING_LAST_UPDATE},
+    {"certificateProvisioningPublicKey", IDS_CERT_DETAILS_SUBJECT_KEY},
+#endif  // defined(OS_CHROMEOS)
+    // For A11y.
+    {"menu", IDS_MENU},
   };
   AddLocalizedStringsBulk(html_source, kLocalizedStrings);
 }
diff --git a/chrome/browser/ui/webui/certificate_provisioning_ui_handler.cc b/chrome/browser/ui/webui/certificate_provisioning_ui_handler.cc
new file mode 100644
index 0000000..1542ce87
--- /dev/null
+++ b/chrome/browser/ui/webui/certificate_provisioning_ui_handler.cc
@@ -0,0 +1,247 @@
+// 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 <string>
+
+#include "chrome/browser/ui/webui/certificate_provisioning_ui_handler.h"
+
+#include "base/bind.h"
+#include "base/containers/span.h"
+#include "base/strings/string16.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/time/time.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/browser_process_platform_part.h"
+#include "chrome/browser/chromeos/cert_provisioning/cert_provisioning_common.h"
+#include "chrome/browser/chromeos/cert_provisioning/cert_provisioning_scheduler.h"
+#include "chrome/browser/chromeos/cert_provisioning/cert_provisioning_scheduler_user_service.h"
+#include "chrome/browser/chromeos/cert_provisioning/cert_provisioning_worker.h"
+#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
+#include "chrome/common/net/x509_certificate_model_nss.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/user_manager/user.h"
+#include "components/user_manager/user_manager.h"
+#include "content/public/browser/web_ui.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/l10n/time_format.h"
+
+namespace chromeos {
+namespace cert_provisioning {
+
+namespace {
+
+// Returns localized representation for the state of a certificate provisioning
+// process.
+base::string16 GetProvisioningProcessStatus(
+    chromeos::cert_provisioning::CertProvisioningWorkerState state) {
+  using CertProvisioningWorkerState =
+      chromeos::cert_provisioning::CertProvisioningWorkerState;
+  switch (state) {
+    case CertProvisioningWorkerState ::kInitState:
+      return l10n_util::GetStringUTF16(
+          IDS_SETTINGS_CERTIFICATE_MANAGER_PROVISIONING_STATUS_PREPARING_CSR);
+    case CertProvisioningWorkerState ::kKeypairGenerated:
+      return l10n_util::GetStringUTF16(
+          IDS_SETTINGS_CERTIFICATE_MANAGER_PROVISIONING_STATUS_PREPARING_CSR_WAITING);
+    case CertProvisioningWorkerState::kStartCsrResponseReceived:
+      // Intentional fall-through.
+    case CertProvisioningWorkerState::kVaChallengeFinished:
+      // Intentional fall-through.
+    case CertProvisioningWorkerState::kKeyRegistered:
+      // Intentional fall-through.
+    case CertProvisioningWorkerState::kKeypairMarked:
+      return l10n_util::GetStringUTF16(
+          IDS_SETTINGS_CERTIFICATE_MANAGER_PROVISIONING_STATUS_PREPARING_CSR);
+    case CertProvisioningWorkerState::kSignCsrFinished:
+      return l10n_util::GetStringUTF16(
+          IDS_SETTINGS_CERTIFICATE_MANAGER_PROVISIONING_STATUS_PREPARING_CSR_WAITING);
+    case CertProvisioningWorkerState::kFinishCsrResponseReceived:
+      return l10n_util::GetStringUTF16(
+          IDS_SETTINGS_CERTIFICATE_MANAGER_PROVISIONING_STATUS_WAITING_FOR_CA);
+    case CertProvisioningWorkerState::kSucceeded:
+      return l10n_util::GetStringUTF16(
+          IDS_SETTINGS_CERTIFICATE_MANAGER_PROVISIONING_STATUS_SUCCESS);
+    case CertProvisioningWorkerState::kFailed:
+      return l10n_util::GetStringUTF16(
+          IDS_SETTINGS_CERTIFICATE_MANAGER_PROVISIONING_STATUS_FAILURE);
+    case CertProvisioningWorkerState::kInconsistentDataError:
+      return l10n_util::GetStringUTF16(
+          IDS_SETTINGS_CERTIFICATE_MANAGER_PROVISIONING_STATUS_PREPARING_CSR_WAITING);
+    case CertProvisioningWorkerState::kCanceled:
+      return l10n_util::GetStringUTF16(
+          IDS_SETTINGS_CERTIFICATE_MANAGER_PROVISIONING_STATUS_CANCELED);
+  }
+  NOTREACHED();
+}
+
+// Returns a localized representation of the last update time as a delay (e.g.
+// "5 minutes ago".
+base::string16 GetTimeSinceLastUpdate(base::Time last_update_time) {
+  const base::Time now = base::Time::NowFromSystemTime();
+  if (last_update_time.is_null() || last_update_time > now)
+    return base::string16();
+  const base::TimeDelta elapsed_time = now - last_update_time;
+  return ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_ELAPSED,
+                                ui::TimeFormat::LENGTH_SHORT, elapsed_time);
+}
+
+base::Value CreateProvisioningProcessEntry(
+    const std::string& cert_profile_id,
+    bool is_device_wide,
+    chromeos::cert_provisioning::CertProvisioningWorkerState state,
+    base::Time time_since_last_update,
+    const std::string& public_key_spki_der) {
+  base::Value entry(base::Value::Type::DICTIONARY);
+  entry.SetStringKey("certProfileId", cert_profile_id);
+  entry.SetBoolKey("isDeviceWide", is_device_wide);
+  entry.SetStringKey("status", GetProvisioningProcessStatus(state));
+  entry.SetIntKey("stateId", static_cast<int>(state));
+  entry.SetStringKey("timeSinceLastUpdate",
+                     GetTimeSinceLastUpdate(time_since_last_update));
+
+  auto spki_der_bytes = base::as_bytes(base::make_span(public_key_spki_der));
+  entry.SetStringKey(
+      "publicKey",
+      x509_certificate_model::ProcessRawSubjectPublicKeyInfo(spki_der_bytes));
+
+  return entry;
+}
+
+// Collects information about certificate provisioning processes from
+// |cert_provisioning_scheduler| and appends them to |list_to_append_to|.
+void CollectProvisioningProcesses(
+    base::Value* list_to_append_to,
+    CertProvisioningScheduler* cert_provisioning_scheduler,
+    bool is_device_wide) {
+  for (const auto& worker_entry : cert_provisioning_scheduler->GetWorkers()) {
+    CertProvisioningWorker* worker = worker_entry.second.get();
+    list_to_append_to->Append(CreateProvisioningProcessEntry(
+        worker_entry.first, is_device_wide, worker->GetState(),
+        worker->GetLastUpdateTime(), worker->GetPublicKey()));
+  }
+  for (const auto& failed_worker_entry :
+       cert_provisioning_scheduler->GetFailedCertProfileIds()) {
+    const chromeos::cert_provisioning::FailedWorkerInfo& worker =
+        failed_worker_entry.second;
+    list_to_append_to->Append(CreateProvisioningProcessEntry(
+        failed_worker_entry.first, is_device_wide, worker.state,
+        worker.last_update_time, worker.public_key));
+  }
+}
+
+}  // namespace
+
+CertificateProvisioningUiHandler::CertificateProvisioningUiHandler() = default;
+CertificateProvisioningUiHandler::~CertificateProvisioningUiHandler() = default;
+
+void CertificateProvisioningUiHandler::RegisterMessages() {
+  // Passing base::Unretained(this) to web_ui()->RegisterMessageCallback is fine
+  // because in chrome Web UI, web_ui() has acquired ownership of |this| and
+  // maintains the life time of |this| accordingly.
+  web_ui()->RegisterMessageCallback(
+      "refreshCertificateProvisioningProcessses",
+      base::BindRepeating(&CertificateProvisioningUiHandler::
+                              HandleRefreshCertificateProvisioningProcesses,
+                          base::Unretained(this)));
+  web_ui()->RegisterMessageCallback(
+      "triggerCertificateProvisioningProcessUpdate",
+      base::BindRepeating(&CertificateProvisioningUiHandler::
+                              HandleTriggerCertificateProvisioningProcessUpdate,
+                          base::Unretained(this)));
+}
+
+CertProvisioningScheduler*
+CertificateProvisioningUiHandler::GetCertProvisioningSchedulerForUser(
+    Profile* user_profile) {
+  chromeos::cert_provisioning::CertProvisioningSchedulerUserService*
+      user_service = chromeos::cert_provisioning::
+          CertProvisioningSchedulerUserServiceFactory::GetForProfile(
+              user_profile);
+  if (!user_service)
+    return nullptr;
+  return user_service->scheduler();
+}
+
+CertProvisioningScheduler*
+CertificateProvisioningUiHandler::GetCertProvisioningSchedulerForDevice(
+    Profile* user_profile) {
+  const user_manager::User* user =
+      chromeos::ProfileHelper::Get()->GetUserByProfile(user_profile);
+  if (!user || !user->IsAffiliated())
+    return nullptr;
+
+  policy::BrowserPolicyConnectorChromeOS* connector =
+      g_browser_process->platform_part()->browser_policy_connector_chromeos();
+  return connector->GetDeviceCertProvisioningScheduler();
+}
+
+void CertificateProvisioningUiHandler::
+    HandleRefreshCertificateProvisioningProcesses(const base::ListValue* args) {
+  CHECK_EQ(0U, args->GetSize());
+  AllowJavascript();
+  RefreshCertificateProvisioningProcesses();
+}
+
+void CertificateProvisioningUiHandler::
+    HandleTriggerCertificateProvisioningProcessUpdate(
+        const base::ListValue* args) {
+  CHECK_EQ(2U, args->GetSize());
+  if (!args->is_list())
+    return;
+  const base::Value& cert_profile_id = args->GetList()[0];
+  if (!cert_profile_id.is_string())
+    return;
+  const base::Value& device_wide = args->GetList()[1];
+  if (!device_wide.is_bool())
+    return;
+
+  Profile* profile = Profile::FromWebUI(web_ui());
+  CertProvisioningScheduler* scheduler =
+      device_wide.GetBool() ? GetCertProvisioningSchedulerForDevice(profile)
+                            : GetCertProvisioningSchedulerForUser(profile);
+  if (!scheduler)
+    return;
+
+  scheduler->UpdateOneCert(cert_profile_id.GetString());
+
+  // Send an update to the UI immediately to reflect a possible status change.
+  RefreshCertificateProvisioningProcesses();
+
+  // Trigger a refresh in a few seconds, in case the state has triggered a
+  // refresh with the server.
+  // TODO(https://crbug.com/1045895): Use a real observer instead.
+  constexpr base::TimeDelta kTimeToWaitBeforeRefresh =
+      base::TimeDelta::FromSeconds(10);
+  base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+      FROM_HERE,
+      base::BindOnce(&CertificateProvisioningUiHandler::
+                         RefreshCertificateProvisioningProcesses,
+                     weak_ptr_factory_.GetWeakPtr()),
+      kTimeToWaitBeforeRefresh);
+}
+
+void CertificateProvisioningUiHandler::
+    RefreshCertificateProvisioningProcesses() {
+  Profile* profile = Profile::FromWebUI(web_ui());
+
+  base::ListValue all_processes;
+  CertProvisioningScheduler* scheduler_for_user =
+      GetCertProvisioningSchedulerForUser(profile);
+  if (scheduler_for_user)
+    CollectProvisioningProcesses(&all_processes, scheduler_for_user,
+                                 /*is_device_wide=*/false);
+
+  CertProvisioningScheduler* scheduler_for_device =
+      GetCertProvisioningSchedulerForDevice(profile);
+  if (scheduler_for_device)
+    CollectProvisioningProcesses(&all_processes, scheduler_for_device,
+                                 /*is_device_wide=*/true);
+
+  FireWebUIListener("certificate-provisioning-processes-changed",
+                    std::move(all_processes));
+}
+
+}  // namespace cert_provisioning
+}  // namespace chromeos
diff --git a/chrome/browser/ui/webui/certificate_provisioning_ui_handler.h b/chrome/browser/ui/webui/certificate_provisioning_ui_handler.h
new file mode 100644
index 0000000..16c2808
--- /dev/null
+++ b/chrome/browser/ui/webui/certificate_provisioning_ui_handler.h
@@ -0,0 +1,69 @@
+// 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_CERTIFICATE_PROVISIONING_UI_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_CERTIFICATE_PROVISIONING_UI_HANDLER_H_
+
+#include "base/memory/weak_ptr.h"
+#include "base/values.h"
+#include "content/public/browser/web_ui_message_handler.h"
+
+class Profile;
+
+namespace chromeos {
+namespace cert_provisioning {
+
+class CertProvisioningScheduler;
+
+class CertificateProvisioningUiHandler : public content::WebUIMessageHandler {
+ public:
+  CertificateProvisioningUiHandler();
+
+  CertificateProvisioningUiHandler(
+      const CertificateProvisioningUiHandler& other) = delete;
+  CertificateProvisioningUiHandler& operator=(
+      const CertificateProvisioningUiHandler& other) = delete;
+
+  ~CertificateProvisioningUiHandler() override;
+
+  // content::WebUIMessageHandler.
+  void RegisterMessages() override;
+
+ private:
+  // Returns the per-user CertProvisioningScheduler for |user_profile|, if it
+  // has any.
+  chromeos::cert_provisioning::CertProvisioningScheduler*
+  GetCertProvisioningSchedulerForUser(Profile* user_profile);
+
+  // Returns the per-device CertProvisioningScheduler, if |user_profile| is
+  // associated with a user that has access to device-wide client certificates.
+  chromeos::cert_provisioning::CertProvisioningScheduler*
+  GetCertProvisioningSchedulerForDevice(Profile* user_profile);
+
+  // Send the list of certificate provisioning processes to the UI, triggered by
+  // the UI when it loads.
+  // |args| is expected to be empty.
+  void HandleRefreshCertificateProvisioningProcesses(
+      const base::ListValue* args);
+
+  // Trigger an update / refresh on a certificate provisioning process.
+  // |args| is expected to contain two arguments:
+  // The argument at index 0 is a string specifying the certificate profile id
+  // of the process that an update should be triggered for. The argument at
+  // index 1 is a boolean specifying whether the process is a user-specific
+  // (false) or a device-wide (true) certificate provisioning process.
+  void HandleTriggerCertificateProvisioningProcessUpdate(
+      const base::ListValue* args);
+
+  // Send the list of certificate provisioning processes to the UI.
+  void RefreshCertificateProvisioningProcesses();
+
+  base::WeakPtrFactory<CertificateProvisioningUiHandler> weak_ptr_factory_{
+      this};
+};
+
+}  // namespace cert_provisioning
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_UI_WEBUI_CERTIFICATE_PROVISIONING_UI_HANDLER_H_
diff --git a/chrome/browser/ui/webui/chromeos/certificate_manager_dialog_ui.cc b/chrome/browser/ui/webui/chromeos/certificate_manager_dialog_ui.cc
index 4fbfb46..518c0a2 100644
--- a/chrome/browser/ui/webui/chromeos/certificate_manager_dialog_ui.cc
+++ b/chrome/browser/ui/webui/chromeos/certificate_manager_dialog_ui.cc
@@ -8,6 +8,7 @@
 
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/webui/certificate_manager_localized_strings_provider.h"
+#include "chrome/browser/ui/webui/certificate_provisioning_ui_handler.h"
 #include "chrome/browser/ui/webui/certificates_handler.h"
 #include "chrome/browser/ui/webui/chromeos/bluetooth_dialog_localized_strings_provider.h"
 #include "chrome/common/url_constants.h"
@@ -59,6 +60,9 @@
 
   web_ui->AddMessageHandler(
       std::make_unique<certificate_manager::CertificatesHandler>());
+  web_ui->AddMessageHandler(
+      std::make_unique<
+          chromeos::cert_provisioning::CertificateProvisioningUiHandler>());
 
   content::WebUIDataSource::Add(Profile::FromWebUI(web_ui), source);
 }
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_ui.cc b/chrome/browser/ui/webui/print_preview/print_preview_ui.cc
index f85f7e0..fe085d5 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_ui.cc
+++ b/chrome/browser/ui/webui/print_preview/print_preview_ui.cc
@@ -651,7 +651,7 @@
 }
 
 void PrintPreviewUI::OnDidStartPreview(
-    const PrintHostMsg_DidStartPreview_Params& params,
+    const mojom::DidStartPreviewParams& params,
     int request_id) {
   DCHECK_GT(params.page_count, 0);
   DCHECK(!params.pages_to_render.empty());
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_ui.h b/chrome/browser/ui/webui/print_preview/print_preview_ui.h
index 1bdafd9..d8611a2 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_ui.h
+++ b/chrome/browser/ui/webui/print_preview/print_preview_ui.h
@@ -26,7 +26,6 @@
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/size.h"
 
-struct PrintHostMsg_DidStartPreview_Params;
 struct PrintHostMsg_PreviewIds;
 struct PrintHostMsg_RequestPrintPreview_Params;
 
@@ -146,7 +145,7 @@
   virtual void OnPrintPreviewRequest(int request_id);
 
   // Notifies the Web UI about the properties of the request preview.
-  void OnDidStartPreview(const PrintHostMsg_DidStartPreview_Params& params,
+  void OnDidStartPreview(const mojom::DidStartPreviewParams& params,
                          int request_id);
 
   // Notifies the Web UI of the default page layout according to the currently
diff --git a/chrome/browser/ui/webui/settings/chromeos/fake_hierarchy.cc b/chrome/browser/ui/webui/settings/chromeos/fake_hierarchy.cc
new file mode 100644
index 0000000..dff558c
--- /dev/null
+++ b/chrome/browser/ui/webui/settings/chromeos/fake_hierarchy.cc
@@ -0,0 +1,33 @@
+// 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/fake_hierarchy.h"
+
+namespace chromeos {
+namespace settings {
+
+FakeHierarchy::FakeHierarchy() = default;
+
+FakeHierarchy::~FakeHierarchy() = default;
+
+void FakeHierarchy::AddSubpageMetadata(
+    mojom::Section section,
+    mojom::Subpage subpage,
+    base::Optional<mojom::Subpage> parent_subpage) {
+  auto pair = subpage_map_.emplace(subpage, section);
+  DCHECK(pair.second);
+  pair.first->second.parent_subpage = parent_subpage;
+}
+
+void FakeHierarchy::AddSettingMetadata(
+    mojom::Section section,
+    mojom::Setting setting,
+    base::Optional<mojom::Subpage> parent_subpage) {
+  auto pair = setting_map_.emplace(setting, section);
+  DCHECK(pair.second);
+  pair.first->second.primary.second = parent_subpage;
+}
+
+}  // namespace settings
+}  // namespace chromeos
diff --git a/chrome/browser/ui/webui/settings/chromeos/fake_hierarchy.h b/chrome/browser/ui/webui/settings/chromeos/fake_hierarchy.h
new file mode 100644
index 0000000..df08452
--- /dev/null
+++ b/chrome/browser/ui/webui/settings/chromeos/fake_hierarchy.h
@@ -0,0 +1,36 @@
+// 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_FAKE_HIERARCHY_H_
+#define CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_FAKE_HIERARCHY_H_
+
+#include "base/optional.h"
+#include "chrome/browser/ui/webui/settings/chromeos/hierarchy.h"
+
+namespace chromeos {
+namespace settings {
+
+// Fake Hierarchy implementation. Note that this class currently does not
+// provide "alternate settings location" functionality.
+class FakeHierarchy : public Hierarchy {
+ public:
+  FakeHierarchy();
+  FakeHierarchy(const FakeHierarchy& other) = delete;
+  FakeHierarchy& operator=(const FakeHierarchy& other) = delete;
+  ~FakeHierarchy() override;
+
+  void AddSubpageMetadata(
+      mojom::Section section,
+      mojom::Subpage subpage,
+      base::Optional<mojom::Subpage> parent_subpage = base::nullopt);
+  void AddSettingMetadata(
+      mojom::Section section,
+      mojom::Setting setting,
+      base::Optional<mojom::Subpage> parent_subpage = base::nullopt);
+};
+
+}  // namespace settings
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_FAKE_HIERARCHY_H_
diff --git a/chrome/browser/ui/webui/settings/chromeos/fake_os_settings_section.cc b/chrome/browser/ui/webui/settings/chromeos/fake_os_settings_section.cc
new file mode 100644
index 0000000..96eea8a
--- /dev/null
+++ b/chrome/browser/ui/webui/settings/chromeos/fake_os_settings_section.cc
@@ -0,0 +1,25 @@
+// 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/fake_os_settings_section.h"
+
+#include <sstream>
+
+namespace chromeos {
+namespace settings {
+
+FakeOsSettingsSection::FakeOsSettingsSection(mojom::Section section)
+    : section_(section) {}
+
+FakeOsSettingsSection::~FakeOsSettingsSection() = default;
+
+std::string FakeOsSettingsSection::ModifySearchResultUrl(
+    const SearchConcept& concept) const {
+  std::stringstream ss;
+  ss << section_ << "::" << concept.url_path_with_parameters;
+  return ss.str();
+}
+
+}  // namespace settings
+}  // namespace chromeos
diff --git a/chrome/browser/ui/webui/settings/chromeos/fake_os_settings_section.h b/chrome/browser/ui/webui/settings/chromeos/fake_os_settings_section.h
new file mode 100644
index 0000000..3154bbb9
--- /dev/null
+++ b/chrome/browser/ui/webui/settings/chromeos/fake_os_settings_section.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 CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_FAKE_OS_SETTINGS_SECTION_H_
+#define CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_FAKE_OS_SETTINGS_SECTION_H_
+
+#include "chrome/browser/ui/webui/settings/chromeos/os_settings_section.h"
+
+namespace chromeos {
+namespace settings {
+
+// Fake OsSettingsSection implementation.
+class FakeOsSettingsSection : public OsSettingsSection {
+ public:
+  explicit FakeOsSettingsSection(mojom::Section section);
+  ~FakeOsSettingsSection() override;
+
+  FakeOsSettingsSection(const FakeOsSettingsSection& other) = delete;
+  FakeOsSettingsSection& operator=(const FakeOsSettingsSection& other) = delete;
+
+  mojom::Section section() { return section_; }
+
+  // OsSettingsSection:
+  void AddLoadTimeData(content::WebUIDataSource* html_source) override {}
+  void RegisterHierarchy(HierarchyGenerator* generator) const override {}
+
+  // Prepends the section name and "::" to the URL in |concept|. For example, if
+  // the URL is "networkDetails" and the section is mojom::Section::kNetwork,
+  // the returned URL is "Section::kNetwork::networkDetails".
+  std::string ModifySearchResultUrl(
+      const SearchConcept& concept) const override;
+
+ private:
+  const mojom::Section section_;
+};
+
+}  // namespace settings
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_FAKE_OS_SETTINGS_SECTION_H_
diff --git a/chrome/browser/ui/webui/settings/chromeos/fake_os_settings_sections.cc b/chrome/browser/ui/webui/settings/chromeos/fake_os_settings_sections.cc
new file mode 100644
index 0000000..253d965
--- /dev/null
+++ b/chrome/browser/ui/webui/settings/chromeos/fake_os_settings_sections.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/fake_os_settings_sections.h"
+
+#include "chrome/browser/ui/webui/settings/chromeos/constants/constants_util.h"
+#include "chrome/browser/ui/webui/settings/chromeos/fake_os_settings_section.h"
+
+namespace chromeos {
+namespace settings {
+
+FakeOsSettingsSections::FakeOsSettingsSections() : OsSettingsSections() {
+  for (const auto& section : constants::AllSections()) {
+    auto fake_section = std::make_unique<FakeOsSettingsSection>(section);
+    sections_map_[section] = fake_section.get();
+    sections_.push_back(std::move(fake_section));
+  }
+}
+
+FakeOsSettingsSections::~FakeOsSettingsSections() = default;
+
+}  // namespace settings
+}  // namespace chromeos
diff --git a/chrome/browser/ui/webui/settings/chromeos/fake_os_settings_sections.h b/chrome/browser/ui/webui/settings/chromeos/fake_os_settings_sections.h
new file mode 100644
index 0000000..2c148a72
--- /dev/null
+++ b/chrome/browser/ui/webui/settings/chromeos/fake_os_settings_sections.h
@@ -0,0 +1,26 @@
+// 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_FAKE_OS_SETTINGS_SECTIONS_H_
+#define CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_FAKE_OS_SETTINGS_SECTIONS_H_
+
+#include "chrome/browser/ui/webui/settings/chromeos/os_settings_sections.h"
+
+namespace chromeos {
+namespace settings {
+
+// Collection of FakeOsSettingsSections.
+class FakeOsSettingsSections : public OsSettingsSections {
+ public:
+  FakeOsSettingsSections();
+  FakeOsSettingsSections(const FakeOsSettingsSections& other) = delete;
+  FakeOsSettingsSections& operator=(const FakeOsSettingsSections& other) =
+      delete;
+  ~FakeOsSettingsSections() override;
+};
+
+}  // namespace settings
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_FAKE_OS_SETTINGS_SECTIONS_H_
diff --git a/chrome/browser/ui/webui/settings/chromeos/hierarchy.cc b/chrome/browser/ui/webui/settings/chromeos/hierarchy.cc
index adb79f5..7fbfe69 100644
--- a/chrome/browser/ui/webui/settings/chromeos/hierarchy.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/hierarchy.cc
@@ -126,6 +126,8 @@
   }
 }
 
+Hierarchy::Hierarchy() = default;
+
 Hierarchy::~Hierarchy() = default;
 
 const Hierarchy::SubpageMetadata& Hierarchy::GetSubpageMetadata(
diff --git a/chrome/browser/ui/webui/settings/chromeos/hierarchy.h b/chrome/browser/ui/webui/settings/chromeos/hierarchy.h
index 7ff4283..43e283c08 100644
--- a/chrome/browser/ui/webui/settings/chromeos/hierarchy.h
+++ b/chrome/browser/ui/webui/settings/chromeos/hierarchy.h
@@ -36,7 +36,7 @@
   explicit Hierarchy(const OsSettingsSections& sections);
   Hierarchy(const Hierarchy& other) = delete;
   Hierarchy& operator=(const Hierarchy& other) = delete;
-  ~Hierarchy();
+  virtual ~Hierarchy();
 
   struct SubpageMetadata {
     explicit SubpageMetadata(mojom::Section section);
@@ -69,11 +69,15 @@
   const SubpageMetadata& GetSubpageMetadata(mojom::Subpage subpage) const;
   const SettingMetadata& GetSettingMetadata(mojom::Setting setting) const;
 
- private:
-  class PerSectionHierarchyGenerator;
+ protected:
+  // Used by tests.
+  Hierarchy();
 
   std::unordered_map<mojom::Subpage, SubpageMetadata> subpage_map_;
   std::unordered_map<mojom::Setting, SettingMetadata> setting_map_;
+
+ private:
+  class PerSectionHierarchyGenerator;
 };
 
 }  // namespace settings
diff --git a/chrome/browser/ui/webui/settings/chromeos/internet_section.cc b/chrome/browser/ui/webui/settings/chromeos/internet_section.cc
index 36e2cde..676e063 100644
--- a/chrome/browser/ui/webui/settings/chromeos/internet_section.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/internet_section.cc
@@ -8,6 +8,7 @@
 #include "ash/public/cpp/network_config_service.h"
 #include "base/bind.h"
 #include "base/no_destructor.h"
+#include "base/strings/stringprintf.h"
 #include "chrome/browser/ui/webui/chromeos/network_element_localized_strings_provider.h"
 #include "chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom.h"
 #include "chrome/browser/ui/webui/settings/chromeos/internet_handler.h"
@@ -374,6 +375,44 @@
   return *tags;
 }
 
+const std::vector<mojom::Setting>& GetEthernetDetailsSettings() {
+  static const base::NoDestructor<std::vector<mojom::Setting>> settings({
+      mojom::Setting::kConfigureEthernet,
+      mojom::Setting::kEthernetAutoConfigureIp,
+      mojom::Setting::kEthernetDns,
+      mojom::Setting::kEthernetProxy,
+  });
+  return *settings;
+}
+
+const std::vector<mojom::Setting>& GetWifiDetailsSettings() {
+  static const base::NoDestructor<std::vector<mojom::Setting>> settings({
+      mojom::Setting::kDisconnectWifiNetwork,
+      mojom::Setting::kPreferWifiNetwork,
+      mojom::Setting::kForgetWifiNetwork,
+      mojom::Setting::kConfigureWifi,
+      mojom::Setting::kWifiAutoConfigureIp,
+      mojom::Setting::kWifiDns,
+      mojom::Setting::kWifiProxy,
+      mojom::Setting::kWifiAutoConnectToNetwork,
+  });
+  return *settings;
+}
+
+const std::vector<mojom::Setting>& GetCellularDetailsSettings() {
+  static const base::NoDestructor<std::vector<mojom::Setting>> settings({
+      mojom::Setting::kCellularSimLock,
+      mojom::Setting::kCellularRoaming,
+      mojom::Setting::kCellularApn,
+      mojom::Setting::kDisconnectCellularNetwork,
+      mojom::Setting::kCellularAutoConfigureIp,
+      mojom::Setting::kCellularDns,
+      mojom::Setting::kCellularProxy,
+      mojom::Setting::kCellularAutoConnectToNetwork,
+  });
+  return *settings;
+}
+
 bool IsConnected(network_config::mojom::ConnectionStateType connection_state) {
   return connection_state ==
              network_config::mojom::ConnectionStateType::kOnline ||
@@ -381,6 +420,38 @@
              network_config::mojom::ConnectionStateType::kConnected;
 }
 
+bool IsPartOfDetailsSubpage(const SearchConcept& concept,
+                            mojom::Subpage details_subpage) {
+  switch (concept.type) {
+    case mojom::SearchResultType::kSection:
+      // Applies to a section, not a details subpage.
+      return false;
+
+    case mojom::SearchResultType::kSubpage:
+      return concept.id.subpage == details_subpage;
+
+    case mojom::SearchResultType::kSetting: {
+      const mojom::Setting& setting = concept.id.setting;
+      switch (details_subpage) {
+        case mojom::Subpage::kEthernetDetails:
+          return base::Contains(GetEthernetDetailsSettings(), setting);
+        case mojom::Subpage::kWifiDetails:
+          return base::Contains(GetWifiDetailsSettings(), setting);
+        case mojom::Subpage::kCellularDetails:
+          return base::Contains(GetCellularDetailsSettings(), setting);
+        default:
+          return false;
+      }
+    }
+  }
+}
+
+std::string GetDetailsSubpageUrl(const SearchConcept& concept,
+                                 const std::string& guid) {
+  return base::StringPrintf("%s?guid=%s", concept.url_path_with_parameters,
+                            guid.c_str());
+}
+
 }  // namespace
 
 InternetSection::InternetSection(Profile* profile,
@@ -563,14 +634,8 @@
 void InternetSection::RegisterHierarchy(HierarchyGenerator* generator) const {
   // Ethernet details.
   generator->RegisterTopLevelSubpage(mojom::Subpage::kEthernetDetails);
-  static constexpr mojom::Setting kEthernetDetailsSettings[] = {
-      mojom::Setting::kConfigureEthernet,
-      mojom::Setting::kEthernetAutoConfigureIp,
-      mojom::Setting::kEthernetDns,
-      mojom::Setting::kEthernetProxy,
-  };
   RegisterNestedSettingBulk(mojom::Subpage::kEthernetDetails,
-                            kEthernetDetailsSettings, generator);
+                            GetEthernetDetailsSettings(), generator);
 
   // Wi-Fi networks.
   generator->RegisterTopLevelSubpage(mojom::Subpage::kWifiNetworks);
@@ -581,18 +646,8 @@
   // Wi-Fi details.
   generator->RegisterNestedSubpage(mojom::Subpage::kWifiDetails,
                                    mojom::Subpage::kWifiNetworks);
-  static constexpr mojom::Setting kWifiDetailsSettings[] = {
-      mojom::Setting::kDisconnectWifiNetwork,
-      mojom::Setting::kPreferWifiNetwork,
-      mojom::Setting::kForgetWifiNetwork,
-      mojom::Setting::kConfigureWifi,
-      mojom::Setting::kWifiAutoConfigureIp,
-      mojom::Setting::kWifiDns,
-      mojom::Setting::kWifiProxy,
-      mojom::Setting::kWifiAutoConnectToNetwork,
-  };
-  RegisterNestedSettingBulk(mojom::Subpage::kWifiDetails, kWifiDetailsSettings,
-                            generator);
+  RegisterNestedSettingBulk(mojom::Subpage::kWifiDetails,
+                            GetWifiDetailsSettings(), generator);
 
   // Known networks.
   generator->RegisterNestedSubpage(mojom::Subpage::kKnownNetworks,
@@ -616,18 +671,8 @@
   // to the cellular details page and skips over the mobile data subpage.
   generator->RegisterNestedSubpage(mojom::Subpage::kCellularDetails,
                                    mojom::Subpage::kMobileDataNetworks);
-  static constexpr mojom::Setting kCellularDetailsSettings[] = {
-      mojom::Setting::kCellularSimLock,
-      mojom::Setting::kCellularRoaming,
-      mojom::Setting::kCellularApn,
-      mojom::Setting::kDisconnectCellularNetwork,
-      mojom::Setting::kCellularAutoConfigureIp,
-      mojom::Setting::kCellularDns,
-      mojom::Setting::kCellularProxy,
-      mojom::Setting::kCellularAutoConnectToNetwork,
-  };
   RegisterNestedSettingBulk(mojom::Subpage::kCellularDetails,
-                            kCellularDetailsSettings, generator);
+                            GetCellularDetailsSettings(), generator);
 
   // Instant Tethering. Although this is a multi-device feature, its UI resides
   // in the network section.
@@ -640,6 +685,27 @@
   generator->RegisterTopLevelSubpage(mojom::Subpage::kVpnDetails);
 }
 
+std::string InternetSection::ModifySearchResultUrl(
+    const SearchConcept& concept) const {
+  if (IsPartOfDetailsSubpage(concept, mojom::Subpage::kEthernetDetails))
+    return GetDetailsSubpageUrl(concept, *connected_ethernet_guid_);
+
+  if (IsPartOfDetailsSubpage(concept, mojom::Subpage::kWifiDetails))
+    return GetDetailsSubpageUrl(concept, *connected_wifi_guid_);
+
+  if (IsPartOfDetailsSubpage(concept, mojom::Subpage::kCellularDetails))
+    return GetDetailsSubpageUrl(concept, *connected_cellular_guid_);
+
+  if (IsPartOfDetailsSubpage(concept, mojom::Subpage::kTetherDetails))
+    return GetDetailsSubpageUrl(concept, *connected_tether_guid_);
+
+  if (IsPartOfDetailsSubpage(concept, mojom::Subpage::kVpnDetails))
+    return GetDetailsSubpageUrl(concept, *connected_vpn_guid_);
+
+  // URL does not need to be modified; use default implementation.
+  return OsSettingsSection::ModifySearchResultUrl(concept);
+}
+
 void InternetSection::OnDeviceStateListChanged() {
   FetchDeviceList();
 }
@@ -722,28 +788,39 @@
   registry()->RemoveSearchTags(GetInstantTetheringConnectedSearchConcepts());
   registry()->RemoveSearchTags(GetVpnConnectedSearchConcepts());
 
+  connected_ethernet_guid_.reset();
+  connected_wifi_guid_.reset();
+  connected_cellular_guid_.reset();
+  connected_tether_guid_.reset();
+  connected_vpn_guid_.reset();
+
   for (const auto& network : networks) {
     if (!IsConnected(network->connection_state))
       continue;
 
     switch (network->type) {
       case NetworkType::kEthernet:
+        connected_ethernet_guid_ = network->guid;
         registry()->AddSearchTags(GetEthernetConnectedSearchConcepts());
         break;
 
       case NetworkType::kWiFi:
+        connected_wifi_guid_ = network->guid;
         registry()->AddSearchTags(GetWifiConnectedSearchConcepts());
         break;
 
       case NetworkType::kCellular:
+        connected_cellular_guid_ = network->guid;
         registry()->AddSearchTags(GetCellularConnectedSearchConcepts());
         break;
 
       case NetworkType::kTether:
+        connected_tether_guid_ = network->guid;
         registry()->AddSearchTags(GetInstantTetheringConnectedSearchConcepts());
         break;
 
       case NetworkType::kVPN:
+        connected_vpn_guid_ = network->guid;
         registry()->AddSearchTags(GetVpnConnectedSearchConcepts());
         break;
 
diff --git a/chrome/browser/ui/webui/settings/chromeos/internet_section.h b/chrome/browser/ui/webui/settings/chromeos/internet_section.h
index d2a5903..d382969 100644
--- a/chrome/browser/ui/webui/settings/chromeos/internet_section.h
+++ b/chrome/browser/ui/webui/settings/chromeos/internet_section.h
@@ -5,8 +5,10 @@
 #ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_INTERNET_SECTION_H_
 #define CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_INTERNET_SECTION_H_
 
+#include <string>
 #include <vector>
 
+#include "base/optional.h"
 #include "chrome/browser/ui/webui/settings/chromeos/os_settings_section.h"
 #include "chromeos/services/network_config/public/mojom/cros_network_config.mojom.h"
 #include "mojo/public/cpp/bindings/receiver.h"
@@ -35,6 +37,8 @@
   void AddLoadTimeData(content::WebUIDataSource* html_source) override;
   void AddHandlers(content::WebUI* web_ui) override;
   void RegisterHierarchy(HierarchyGenerator* generator) const override;
+  std::string ModifySearchResultUrl(
+      const SearchConcept& concept) const override;
 
   // network_config::mojom::CrosNetworkConfigObserver:
   void OnActiveNetworksChanged(
@@ -56,6 +60,13 @@
   void OnActiveNetworks(
       std::vector<network_config::mojom::NetworkStatePropertiesPtr> networks);
 
+  // Note: If not connected, the below fields are null.
+  base::Optional<std::string> connected_ethernet_guid_;
+  base::Optional<std::string> connected_wifi_guid_;
+  base::Optional<std::string> connected_cellular_guid_;
+  base::Optional<std::string> connected_tether_guid_;
+  base::Optional<std::string> connected_vpn_guid_;
+
   mojo::Receiver<network_config::mojom::CrosNetworkConfigObserver> receiver_{
       this};
   mojo::Remote<network_config::mojom::CrosNetworkConfig> cros_network_config_;
diff --git a/chrome/browser/ui/webui/settings/chromeos/os_settings_manager.cc b/chrome/browser/ui/webui/settings/chromeos/os_settings_manager.cc
index 5af6db5..705605e 100644
--- a/chrome/browser/ui/webui/settings/chromeos/os_settings_manager.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/os_settings_manager.cc
@@ -45,7 +45,8 @@
       hierarchy_(std::make_unique<Hierarchy>(*sections_)) {
   if (base::FeatureList::IsEnabled(features::kNewOsSettingsSearch)) {
     search_handler_ = std::make_unique<SearchHandler>(
-        search_tag_registry_.get(), local_search_service);
+        search_tag_registry_.get(), sections_.get(), hierarchy_.get(),
+        local_search_service);
   }
 }
 
diff --git a/chrome/browser/ui/webui/settings/chromeos/os_settings_section.cc b/chrome/browser/ui/webui/settings/chromeos/os_settings_section.cc
index ef07d21..5f7227d 100644
--- a/chrome/browser/ui/webui/settings/chromeos/os_settings_section.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/os_settings_section.cc
@@ -7,6 +7,7 @@
 #include "base/check.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/system/sys_info.h"
+#include "chrome/browser/ui/webui/settings/chromeos/search/search_concept.h"
 
 namespace chromeos {
 namespace settings {
@@ -36,5 +37,13 @@
   DCHECK(search_tag_registry);
 }
 
+OsSettingsSection::OsSettingsSection() = default;
+
+std::string OsSettingsSection::ModifySearchResultUrl(
+    const SearchConcept& concept) const {
+  // Default case for static URLs which do not need to be modified.
+  return concept.url_path_with_parameters;
+}
+
 }  // namespace settings
 }  // namespace chromeos
diff --git a/chrome/browser/ui/webui/settings/chromeos/os_settings_section.h b/chrome/browser/ui/webui/settings/chromeos/os_settings_section.h
index 9c33240..3f14647 100644
--- a/chrome/browser/ui/webui/settings/chromeos/os_settings_section.h
+++ b/chrome/browser/ui/webui/settings/chromeos/os_settings_section.h
@@ -24,6 +24,7 @@
 namespace chromeos {
 namespace settings {
 
+struct SearchConcept;
 class SearchTagRegistry;
 
 // Represents one top-level section of the settings app (i.e., one item on the
@@ -98,6 +99,13 @@
   // Registers the subpages and/or settings which reside in this section.
   virtual void RegisterHierarchy(HierarchyGenerator* generator) const = 0;
 
+  // Modifies a URL to be used by settings search. Some URLs require dynamic
+  // content (e.g., network detail settings use the GUID of the network as a URL
+  // parameter to route to details for a specific network). By default, this
+  // function simply returns the URL contained in |concept|, which provides
+  // functionality for static URLs.
+  virtual std::string ModifySearchResultUrl(const SearchConcept& concept) const;
+
  protected:
   static base::string16 GetHelpUrlWithBoard(const std::string& original_url);
   static void RegisterNestedSettingBulk(
@@ -107,6 +115,9 @@
 
   OsSettingsSection(Profile* profile, SearchTagRegistry* search_tag_registry);
 
+  // Used by tests.
+  OsSettingsSection();
+
   Profile* profile() { return profile_; }
   SearchTagRegistry* registry() { return search_tag_registry_; }
 
diff --git a/chrome/browser/ui/webui/settings/chromeos/os_settings_sections.cc b/chrome/browser/ui/webui/settings/chromeos/os_settings_sections.cc
index a71ad20..13cb309 100644
--- a/chrome/browser/ui/webui/settings/chromeos/os_settings_sections.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/os_settings_sections.cc
@@ -131,6 +131,8 @@
   sections_.push_back(std::move(about_section));
 }
 
+OsSettingsSections::OsSettingsSections() = default;
+
 OsSettingsSections::~OsSettingsSections() = default;
 
 const OsSettingsSection* OsSettingsSections::GetSection(
diff --git a/chrome/browser/ui/webui/settings/chromeos/os_settings_sections.h b/chrome/browser/ui/webui/settings/chromeos/os_settings_sections.h
index cd67db83..d152648 100644
--- a/chrome/browser/ui/webui/settings/chromeos/os_settings_sections.h
+++ b/chrome/browser/ui/webui/settings/chromeos/os_settings_sections.h
@@ -54,7 +54,7 @@
       CupsPrintersManager* printers_manager);
   OsSettingsSections(const OsSettingsSections& other) = delete;
   OsSettingsSections& operator=(const OsSettingsSections& other) = delete;
-  ~OsSettingsSections();
+  virtual ~OsSettingsSections();
 
   const OsSettingsSection* GetSection(mojom::Section section) const;
 
@@ -62,7 +62,10 @@
     return sections_;
   }
 
- private:
+ protected:
+  // Used by tests.
+  OsSettingsSections();
+
   std::unordered_map<mojom::Section, OsSettingsSection*> sections_map_;
   std::vector<std::unique_ptr<OsSettingsSection>> sections_;
 };
diff --git a/chrome/browser/ui/webui/settings/chromeos/people_section.cc b/chrome/browser/ui/webui/settings/chromeos/people_section.cc
index 1866315..91c1cb9 100644
--- a/chrome/browser/ui/webui/settings/chromeos/people_section.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/people_section.cc
@@ -243,6 +243,10 @@
       {"accountManagerDescription", IDS_SETTINGS_ACCOUNT_MANAGER_DESCRIPTION},
       {"accountManagerChildDescription",
        IDS_SETTINGS_ACCOUNT_MANAGER_CHILD_DESCRIPTION},
+      {"accountManagerChildFirstMessage",
+       IDS_SETTINGS_ACCOUNT_MANAGER_CHILD_FIRST_MESSAGE},
+      {"accountManagerChildSecondMessage",
+       IDS_SETTINGS_ACCOUNT_MANAGER_CHILD_SECOND_MESSAGE},
       {"accountListHeader", IDS_SETTINGS_ACCOUNT_MANAGER_LIST_HEADER},
       {"accountManagerPrimaryAccountTooltip",
        IDS_SETTINGS_ACCOUNT_MANAGER_PRIMARY_ACCOUNT_TOOLTIP},
diff --git a/chrome/browser/ui/webui/settings/chromeos/search/search_handler.cc b/chrome/browser/ui/webui/settings/chromeos/search/search_handler.cc
index 47c0052..54219e2c 100644
--- a/chrome/browser/ui/webui/settings/chromeos/search/search_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/search/search_handler.cc
@@ -7,6 +7,8 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/chromeos/local_search_service/local_search_service.h"
+#include "chrome/browser/ui/webui/settings/chromeos/hierarchy.h"
+#include "chrome/browser/ui/webui/settings/chromeos/os_settings_sections.h"
 #include "chrome/browser/ui/webui/settings/chromeos/search/search_concept.h"
 #include "chrome/browser/ui/webui/settings/chromeos/search/search_result_icon.mojom.h"
 #include "chrome/browser/ui/webui/settings/chromeos/search/search_tag_registry.h"
@@ -35,8 +37,12 @@
 
 SearchHandler::SearchHandler(
     SearchTagRegistry* search_tag_registry,
+    OsSettingsSections* sections,
+    Hierarchy* hierarchy,
     local_search_service::LocalSearchService* local_search_service)
     : search_tag_registry_(search_tag_registry),
+      sections_(sections),
+      hierarchy_(hierarchy),
       index_(local_search_service->GetIndex(
           local_search_service::IndexId::kCrosSettings)) {}
 
@@ -102,30 +108,43 @@
   if (!concept)
     return nullptr;
 
+  std::string url;
   mojom::SearchResultIdentifierPtr result_id;
   switch (concept->type) {
-    case mojom::SearchResultType::kSection:
-      result_id =
-          mojom::SearchResultIdentifier::NewSection(concept->id.section);
+    case mojom::SearchResultType::kSection: {
+      mojom::Section section = concept->id.section;
+      url = GetModifiedUrl(*concept, section);
+      result_id = mojom::SearchResultIdentifier::NewSection(section);
       break;
-    case mojom::SearchResultType::kSubpage:
-      result_id =
-          mojom::SearchResultIdentifier::NewSubpage(concept->id.subpage);
+    }
+    case mojom::SearchResultType::kSubpage: {
+      mojom::Subpage subpage = concept->id.subpage;
+      url = GetModifiedUrl(*concept,
+                           hierarchy_->GetSubpageMetadata(subpage).section);
+      result_id = mojom::SearchResultIdentifier::NewSubpage(subpage);
       break;
-    case mojom::SearchResultType::kSetting:
-      result_id =
-          mojom::SearchResultIdentifier::NewSetting(concept->id.setting);
+    }
+    case mojom::SearchResultType::kSetting: {
+      mojom::Setting setting = concept->id.setting;
+      url = GetModifiedUrl(
+          *concept, hierarchy_->GetSettingMetadata(setting).primary.first);
+      result_id = mojom::SearchResultIdentifier::NewSetting(setting);
       break;
+    }
   }
 
   // TODO(https://crbug.com/1071700): Generate real hierarchy instead of using
   // GenerateDummySettingsHierarchy().
   return mojom::SearchResult::New(
-      l10n_util::GetStringUTF16(message_id), concept->url_path_with_parameters,
-      concept->icon, result.score,
+      l10n_util::GetStringUTF16(message_id), url, concept->icon, result.score,
       GenerateDummySettingsHierarchy(concept->url_path_with_parameters),
       concept->default_rank, concept->type, std::move(result_id));
 }
 
+std::string SearchHandler::GetModifiedUrl(const SearchConcept& concept,
+                                          mojom::Section section) const {
+  return sections_->GetSection(section)->ModifySearchResultUrl(concept);
+}
+
 }  // namespace settings
 }  // namespace chromeos
diff --git a/chrome/browser/ui/webui/settings/chromeos/search/search_handler.h b/chrome/browser/ui/webui/settings/chromeos/search/search_handler.h
index 8247ccc..12ba659 100644
--- a/chrome/browser/ui/webui/settings/chromeos/search/search_handler.h
+++ b/chrome/browser/ui/webui/settings/chromeos/search/search_handler.h
@@ -21,6 +21,9 @@
 namespace chromeos {
 namespace settings {
 
+class Hierarchy;
+class OsSettingsSections;
+struct SearchConcept;
 class SearchTagRegistry;
 
 // Handles search queries for Chrome OS settings. Search() is expected to be
@@ -37,6 +40,8 @@
   static const size_t kNumMaxResults;
 
   SearchHandler(SearchTagRegistry* search_tag_registry,
+                OsSettingsSections* sections,
+                Hierarchy* hierarchy,
                 local_search_service::LocalSearchService* local_search_service);
   ~SearchHandler() override;
 
@@ -58,8 +63,12 @@
           local_search_service_results);
   mojom::SearchResultPtr ResultToSearchResult(
       const local_search_service::Result& result);
+  std::string GetModifiedUrl(const SearchConcept& concept,
+                             mojom::Section section) const;
 
   SearchTagRegistry* search_tag_registry_;
+  OsSettingsSections* sections_;
+  Hierarchy* hierarchy_;
   local_search_service::Index* index_;
 
   // Note: Expected to have multiple clients, so a ReceiverSet is used.
diff --git a/chrome/browser/ui/webui/settings/chromeos/search/search_handler_unittest.cc b/chrome/browser/ui/webui/settings/chromeos/search/search_handler_unittest.cc
index f2c9d42..8e0d7b0 100644
--- a/chrome/browser/ui/webui/settings/chromeos/search/search_handler_unittest.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/search/search_handler_unittest.cc
@@ -10,6 +10,8 @@
 #include "base/test/task_environment.h"
 #include "chrome/browser/chromeos/local_search_service/local_search_service.h"
 #include "chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom.h"
+#include "chrome/browser/ui/webui/settings/chromeos/fake_hierarchy.h"
+#include "chrome/browser/ui/webui/settings/chromeos/fake_os_settings_sections.h"
 #include "chrome/browser/ui/webui/settings/chromeos/search/search.mojom-test-utils.h"
 #include "chrome/browser/ui/webui/settings/chromeos/search/search_tag_registry.h"
 #include "chrome/grit/generated_resources.h"
@@ -58,7 +60,10 @@
  protected:
   SearchHandlerTest()
       : search_tag_registry_(&local_search_service_),
-        handler_(&search_tag_registry_, &local_search_service_) {}
+        handler_(&search_tag_registry_,
+                 &fake_sections_,
+                 &fake_hierarchy_,
+                 &local_search_service_) {}
   ~SearchHandlerTest() override = default;
 
   // testing::Test:
@@ -66,12 +71,21 @@
     scoped_feature_list_.InitAndEnableFeature(
         chromeos::features::kNewOsSettingsSearch);
     handler_.BindInterface(handler_remote_.BindNewPipeAndPassReceiver());
+
+    fake_hierarchy_.AddSubpageMetadata(mojom::Section::kPrinting,
+                                       mojom::Subpage::kPrintingDetails);
+    fake_hierarchy_.AddSettingMetadata(mojom::Section::kPrinting,
+                                       mojom::Setting::kAddPrinter);
+    fake_hierarchy_.AddSettingMetadata(mojom::Section::kPrinting,
+                                       mojom::Setting::kSavedPrinters);
   }
 
   base::test::TaskEnvironment task_environment_;
   base::test::ScopedFeatureList scoped_feature_list_;
   local_search_service::LocalSearchService local_search_service_;
   SearchTagRegistry search_tag_registry_;
+  FakeOsSettingsSections fake_sections_;
+  FakeHierarchy fake_hierarchy_;
   SearchHandler handler_;
   mojo::Remote<mojom::SearchHandler> handler_remote_;
 };
@@ -99,5 +113,22 @@
   EXPECT_TRUE(search_results.empty());
 }
 
+TEST_F(SearchHandlerTest, UrlModification) {
+  // Add printing search tags to registry and search for "Saved".
+  search_tag_registry_.AddSearchTags(GetPrintingSearchConcepts());
+  std::vector<mojom::SearchResultPtr> search_results;
+  mojom::SearchHandlerAsyncWaiter(handler_remote_.get())
+      .Search(base::ASCIIToUTF16("Saved"), &search_results);
+
+  // Only the "saved printers" item should be returned.
+  EXPECT_EQ(search_results.size(), 1u);
+
+  // The URL should have bee modified according to the FakeOsSettingSection
+  // scheme.
+  EXPECT_EQ(
+      std::string("Section::kPrinting::") + mojom::kPrintingDetailsSubpagePath,
+      search_results[0]->url_path_with_parameters);
+}
+
 }  // namespace settings
 }  // namespace chromeos
diff --git a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
index b029747..1ffdbb2 100644
--- a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
@@ -882,6 +882,8 @@
       {"passwordDetailsTitle", IDS_SETTINGS_PASSWORDS_VIEW_DETAILS_TITLE},
       {"passwordViewDetails", IDS_SETTINGS_PASSWORD_DETAILS},
       {"copyPassword", IDS_SETTINGS_PASSWORD_COPY},
+      {"passwordStoredOnDevice", IDS_SETTINGS_PASSWORD_STORED_ON_DEVICE},
+      {"passwordStoredInAccount", IDS_SETTINGS_PASSWORD_STORED_IN_ACCOUNT},
       {"editPasswordWebsiteLabel", IDS_SETTINGS_PASSWORDS_WEBSITE},
       {"editPasswordUsernameLabel", IDS_SETTINGS_PASSWORDS_USERNAME},
       {"editPasswordPasswordLabel", IDS_SETTINGS_PASSWORDS_PASSWORD},
diff --git a/chrome/browser/ui/webui/settings/settings_ui.cc b/chrome/browser/ui/webui/settings/settings_ui.cc
index 3cace0b..9b6537e 100644
--- a/chrome/browser/ui/webui/settings/settings_ui.cc
+++ b/chrome/browser/ui/webui/settings/settings_ui.cc
@@ -103,6 +103,7 @@
 #include "chrome/browser/chromeos/android_sms/android_sms_service_factory.h"
 #include "chrome/browser/chromeos/multidevice_setup/multidevice_setup_client_factory.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
+#include "chrome/browser/ui/webui/certificate_provisioning_ui_handler.h"
 #include "chrome/browser/ui/webui/settings/chromeos/account_manager_handler.h"
 #include "chrome/browser/ui/webui/settings/chromeos/android_apps_handler.h"
 #include "chrome/browser/ui/webui/settings/chromeos/multidevice_handler.h"
@@ -170,6 +171,11 @@
 #elif defined(OS_WIN) || defined(OS_MACOSX)
   AddSettingsPageUIHandler(std::make_unique<NativeCertificatesHandler>());
 #endif  // defined(USE_NSS_CERTS)
+#if defined(OS_CHROMEOS)
+  AddSettingsPageUIHandler(
+      std::make_unique<
+          chromeos::cert_provisioning::CertificateProvisioningUiHandler>());
+#endif
 
   AddSettingsPageUIHandler(std::make_unique<AccessibilityMainHandler>());
   AddSettingsPageUIHandler(std::make_unique<BrowserLifetimeHandler>());
diff --git a/chrome/browser/ui/webui/signin/inline_login_ui.cc b/chrome/browser/ui/webui/signin/inline_login_ui.cc
index afc3305..3a2bbff 100644
--- a/chrome/browser/ui/webui/signin/inline_login_ui.cc
+++ b/chrome/browser/ui/webui/signin/inline_login_ui.cc
@@ -52,17 +52,9 @@
   source->AddLocalizedString("backButton", IDS_EDU_LOGIN_BACK);
   source->AddLocalizedString("nextButton", IDS_EDU_LOGIN_NEXT);
 
-  source->AddLocalizedString("welcomeTitle", IDS_EDU_LOGIN_WELCOME_TITLE);
-  source->AddLocalizedString("welcomeBody", IDS_EDU_LOGIN_WELCOME_BODY);
-  source->AddLocalizedString("welcomeReauthTitle",
-                             IDS_EDU_LOGIN_WELCOME_REAUTH_TITLE);
-  source->AddLocalizedString("welcomeReauthBody",
-                             IDS_EDU_LOGIN_WELCOME_REAUTH_BODY);
-  source->AddLocalizedString("parentsListTitle",
-                             IDS_EDU_LOGIN_PARENTS_LIST_TITLE);
-  source->AddLocalizedString("parentsListBody",
-                             IDS_EDU_LOGIN_PARENTS_LIST_BODY);
-
+  source->AddLocalizedString("parentsListTitle", IDS_EDU_LOGIN_WELCOME_TITLE_2);
+  source->AddLocalizedString("parentsListBody", IDS_EDU_LOGIN_WELCOME_BODY_2);
+  source->AddLocalizedString("reauthBody", IDS_EDU_LOGIN_WELCOME_REAUTH_BODY);
   source->AddLocalizedString("parentSigninTitle",
                              IDS_EDU_LOGIN_PARENT_SIGNIN_TITLE);
   source->AddString(
@@ -131,7 +123,6 @@
     {"icons.js", IDR_EDU_LOGIN_ICONS_JS},
     {"browser_proxy.js", IDR_EDU_LOGIN_BROWSER_PROXY_JS},
     {"edu_login_util.js", IDR_EDU_LOGIN_EDU_LOGIN_UTIL_JS},
-    {"edu_login_welcome.js", IDR_EDU_LOGIN_EDU_LOGIN_WELCOME_JS},
     {"edu_login_parents.js", IDR_EDU_LOGIN_EDU_LOGIN_PARENTS_JS},
     {"edu_login_parent_signin.js", IDR_EDU_LOGIN_EDU_LOGIN_PARENT_SIGNIN_JS},
     {"edu_login_parent_info.js", IDR_EDU_LOGIN_EDU_LOGIN_PARENT_INFO_JS},
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 2de6595..ebcb14f 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-master-1589874680-5ac33befa963a769a0374aa0b67592d28e114a8a.profdata
\ No newline at end of file
+chrome-mac-master-1589903980-9824f0da2e5a5b705292a405f6736e894a220581.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 31e99c01..4e53c09 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-master-1589874680-5bafae5c8594e160e392ef10b9fbea3d577d4fff.profdata
\ No newline at end of file
+chrome-win32-master-1589903980-88790d48e5518ac3f655ae4b6dd9841a6fd4af4d.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 3335164..f1960db 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-master-1589831941-f19e09eb6f138b832047fb84f08c61ae600030fb.profdata
\ No newline at end of file
+chrome-win64-master-1589903980-ca5fa5e99fa1c661edfdfa4ab3f1c52b9013445e.profdata
diff --git a/chrome/common/BUILD.gn b/chrome/common/BUILD.gn
index 7aec455..25f5f64 100644
--- a/chrome/common/BUILD.gn
+++ b/chrome/common/BUILD.gn
@@ -106,8 +106,6 @@
     "chrome_descriptors.h",
     "chrome_isolated_world_ids.h",
     "chrome_result_codes.h",
-    "chrome_ui_features_prefs.cc",
-    "chrome_ui_features_prefs.h",
     "common_message_generator.cc",
     "common_message_generator.h",
     "component_flash_hint_file_linux.cc",
diff --git a/chrome/common/chrome_ui_features_prefs.cc b/chrome/common/chrome_ui_features_prefs.cc
deleted file mode 100644
index 7e06f82..0000000
--- a/chrome/common/chrome_ui_features_prefs.cc
+++ /dev/null
@@ -1,17 +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 "chrome/common/chrome_ui_features_prefs.h"
-
-#include "chrome/common/pref_names.h"
-#include "components/prefs/pref_registry_simple.h"
-
-namespace chrome_ui_features_prefs {
-
-void RegisterProfilePrefs(PrefRegistrySimple* registry) {
-  registry->RegisterBooleanPref(prefs::kUseLegacyFormControls,
-                                /*default_value=*/false);
-}
-
-}  // namespace chrome_ui_features_prefs
diff --git a/chrome/common/chrome_ui_features_prefs.h b/chrome/common/chrome_ui_features_prefs.h
deleted file mode 100644
index 9f18b97..0000000
--- a/chrome/common/chrome_ui_features_prefs.h
+++ /dev/null
@@ -1,17 +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 CHROME_COMMON_CHROME_UI_FEATURES_PREFS_H_
-#define CHROME_COMMON_CHROME_UI_FEATURES_PREFS_H_
-
-class PrefRegistrySimple;
-
-namespace chrome_ui_features_prefs {
-
-// Register preferences for Chrome UI Features.
-void RegisterProfilePrefs(PrefRegistrySimple* registry);
-
-}  // namespace chrome_ui_features_prefs
-
-#endif  // CHROME_COMMON_CHROME_UI_FEATURES_PREFS_H_
diff --git a/chrome/common/media_router/media_source.h b/chrome/common/media_router/media_source.h
index 3c781cf..edf2111 100644
--- a/chrome/common/media_router/media_source.h
+++ b/chrome/common/media_router/media_source.h
@@ -11,9 +11,6 @@
 #include "base/hash/hash.h"
 #include "url/gurl.h"
 
-// TODO(takumif): Move all Chromecast-specific constants out of this file, since
-// they are not directly related to MediaSource.
-
 namespace media_router {
 
 // URL schemes used by Presentation URLs for Cast and DIAL.
@@ -31,6 +28,9 @@
 constexpr char kMirroringAppUri[] = "cast:0F5096E8";
 
 // Strings used in presentation IDs by the Cast SDK implementation.
+// TODO(takumif): Move them out of this file, since they are not directly
+// related to MediaSource.
+//
 // This value must be the same as |chrome.cast.AUTO_JOIN_PRESENTATION_ID| in the
 // component extension.
 constexpr char kAutoJoinPresentationId[] = "auto-join";
diff --git a/chrome/common/media_router/providers/cast/cast_media_source.cc b/chrome/common/media_router/providers/cast/cast_media_source.cc
index add2491..97002d0 100644
--- a/chrome/common/media_router/providers/cast/cast_media_source.cc
+++ b/chrome/common/media_router/providers/cast/cast_media_source.cc
@@ -23,9 +23,6 @@
 using cast_channel::CastDeviceCapability;
 using cast_channel::ReceiverAppType;
 
-constexpr char kGoogleDocsOrigin[] = "https://docs.google.com";
-constexpr char kGoogleMeetOrigin[] = "https://meet.google.com";
-
 namespace cast_util {
 
 using media_router::AutoJoinPolicy;
@@ -426,26 +423,6 @@
   return app_ids;
 }
 
-std::vector<url::Origin> CastMediaSource::GetAllowedOrigins() const {
-  // Initiation of tab mirroring via a cast: URL is permitted for Slides and
-  // Meet until web APIs can meet their needs.
-  return ContainsStreamingApp()
-             ? std::vector<url::Origin>(
-                   {url::Origin::Create(GURL(kGoogleDocsOrigin)),
-                    url::Origin::Create(GURL(kGoogleMeetOrigin))})
-             : std::vector<url::Origin>();
-}
-
-bool CastMediaSource::IsAllowedOrigin(const url::Origin& origin) const {
-  // TODO(crbug.com/1047834): Check a specific origin for browser requested
-  // streaming.
-  if (ContainsStreamingApp() && origin.scheme() == url::kHttpsScheme) {
-    return base::Contains(GetAllowedOrigins(), origin);
-  } else {
-    return true;
-  }
-}
-
 void CastMediaSource::set_supported_app_types(
     const std::vector<ReceiverAppType>& types) {
   DCHECK(!types.empty());
diff --git a/chrome/common/media_router/providers/cast/cast_media_source.h b/chrome/common/media_router/providers/cast/cast_media_source.h
index 1abbfd5..09c0449 100644
--- a/chrome/common/media_router/providers/cast/cast_media_source.h
+++ b/chrome/common/media_router/providers/cast/cast_media_source.h
@@ -16,7 +16,6 @@
 #include "chrome/common/media_router/media_source.h"
 #include "components/cast_channel/cast_message_util.h"
 #include "components/cast_channel/cast_socket.h"
-#include "url/origin.h"
 
 using cast_channel::ReceiverAppType;
 
@@ -147,14 +146,6 @@
   // Returns a list of App IDs in this CastMediaSource.
   std::vector<std::string> GetAppIds() const;
 
-  // Returns a list of origins that are allowed to use this source.  An empty
-  // list means any origin is allowed.  For now, non-empty origins are websites
-  // allowed to use this source via the Presentation API.
-  std::vector<url::Origin> GetAllowedOrigins() const;
-
-  // Returns true if |origin| is allowed according to GetAllowedOrigins().
-  bool IsAllowedOrigin(const url::Origin& origin) const;
-
   const MediaSource::Id& source_id() const { return source_id_; }
   const std::vector<CastAppInfo>& app_infos() const { return app_infos_; }
   const std::string& client_id() const { return client_id_; }
diff --git a/chrome/common/media_router/providers/cast/cast_media_source_unittest.cc b/chrome/common/media_router/providers/cast/cast_media_source_unittest.cc
index 9439ca0..42c5c66 100644
--- a/chrome/common/media_router/providers/cast/cast_media_source_unittest.cc
+++ b/chrome/common/media_router/providers/cast/cast_media_source_unittest.cc
@@ -161,30 +161,4 @@
       "https://google.com/cast#__castAppId__=/param=foo"));
 }
 
-TEST(CastMediaSourceTest, TestIsAllowedOrigin) {
-  const auto cast_app_source = CastMediaSource::FromAppId("ABCDEFAB");
-  ASSERT_TRUE(cast_app_source);
-  ASSERT_FALSE(cast_app_source->ContainsStreamingApp());
-
-  const auto cast_streaming_source =
-      CastMediaSource::FromAppId(kCastStreamingAppId);
-  ASSERT_TRUE(cast_streaming_source);
-  ASSERT_TRUE(cast_streaming_source->ContainsStreamingApp());
-
-  const url::Origin empty_origin = url::Origin::Create(GURL());
-  const url::Origin site_origin =
-      url::Origin::Create(GURL("https://www.example.com"));
-  const url::Origin docs_origin =
-      url::Origin::Create(GURL("https://docs.google.com"));
-
-  EXPECT_TRUE(cast_app_source->IsAllowedOrigin(empty_origin));
-  EXPECT_TRUE(cast_app_source->IsAllowedOrigin(site_origin));
-  EXPECT_TRUE(cast_app_source->IsAllowedOrigin(docs_origin));
-
-  // Cast streaming apps are only allowed on whitelisted origins.
-  EXPECT_FALSE(cast_streaming_source->IsAllowedOrigin(site_origin));
-  EXPECT_TRUE(cast_streaming_source->IsAllowedOrigin(empty_origin));
-  EXPECT_TRUE(cast_streaming_source->IsAllowedOrigin(docs_origin));
-}
-
 }  // namespace media_router
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc
index 87c8118..4f720ae 100644
--- a/chrome/common/pref_names.cc
+++ b/chrome/common/pref_names.cc
@@ -2972,11 +2972,6 @@
 const char kExternalProtocolDialogShowAlwaysOpenCheckbox[] =
     "external_protocol_dialog.show_always_open_checkbox";
 
-// This pref allows the FormControlsRefresh feature to be disabled temporarily
-// from M81 through M84.
-// TODO(1034611): Remove this after M84.
-const char kUseLegacyFormControls[] = "use_legacy_form_controls";
-
 // This pref enables the ScrollToTextFragment feature.
 const char kScrollToTextFragmentEnabled[] = "scroll_to_text_fragment_enabled";
 
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index 6e1e935..46acf1e0 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -1041,8 +1041,6 @@
 
 extern const char kExternalProtocolDialogShowAlwaysOpenCheckbox[];
 
-extern const char kUseLegacyFormControls[];
-
 extern const char kScrollToTextFragmentEnabled[];
 
 #if defined(OS_ANDROID)
diff --git a/chrome/common/profiler/thread_profiler.cc b/chrome/common/profiler/thread_profiler.cc
index f32a9ef3..06a489d 100644
--- a/chrome/common/profiler/thread_profiler.cc
+++ b/chrome/common/profiler/thread_profiler.cc
@@ -26,6 +26,10 @@
 #include "content/public/common/service_names.mojom.h"
 #include "services/service_manager/embedder/switches.h"
 
+#if defined(OS_ANDROID)
+#include "chrome/android/modules/stack_unwinder/public/module.h"
+#endif
+
 using CallStackProfileBuilder = metrics::CallStackProfileBuilder;
 using CallStackProfileParams = metrics::CallStackProfileParams;
 using StackSamplingProfiler = base::StackSamplingProfiler;
@@ -62,6 +66,46 @@
   return CallStackProfileParams::UNKNOWN_PROCESS;
 }
 
+const base::RepeatingCallback<std::unique_ptr<base::Unwinder>()>&
+GetNativeUnwinderFactory() {
+  const auto create_native_unwinder_factory = []() {
+#if defined(OS_ANDROID)
+    // The module is loadable if the profiler is enabled for the current
+    // process.
+    CHECK(StackSamplingConfiguration::Get()
+              ->IsProfilerEnabledForCurrentProcess());
+
+    struct UnwinderCreationState {
+      std::unique_ptr<stack_unwinder::Module> module;
+      std::unique_ptr<stack_unwinder::MemoryRegionsMap> memory_regions_map;
+    };
+    const auto create_native_unwinder =
+        [](UnwinderCreationState* creation_state) {
+          return creation_state->module->CreateNativeUnwinder(
+              creation_state->memory_regions_map.get());
+        };
+
+    std::unique_ptr<stack_unwinder::Module> module =
+        stack_unwinder::Module::Load();
+    std::unique_ptr<stack_unwinder::MemoryRegionsMap> memory_regions_map =
+        module->CreateMemoryRegionsMap();
+    return base::BindRepeating(
+        create_native_unwinder,
+        base::Owned(new UnwinderCreationState{std::move(module),
+                                              std::move(memory_regions_map)}));
+#else
+    return base::BindRepeating(
+        []() -> std::unique_ptr<base::Unwinder> { return nullptr; });
+#endif
+  };
+
+  static base::NoDestructor<
+      base::RepeatingCallback<std::unique_ptr<base::Unwinder>()>>
+      native_unwinder_factory(create_native_unwinder_factory());
+
+  return *native_unwinder_factory;
+}
+
 }  // namespace
 
 // The scheduler works by splitting execution time into repeated periods such
@@ -239,7 +283,8 @@
       std::make_unique<CallStackProfileBuilder>(
           CallStackProfileParams(GetProcess(), thread,
                                  CallStackProfileParams::PROCESS_STARTUP),
-          work_id_recorder_.get()));
+          work_id_recorder_.get()),
+      GetNativeUnwinderFactory().Run());
 
   startup_profiler_->Start();
 
@@ -303,7 +348,8 @@
           work_id_recorder_.get(),
           base::BindOnce(&ThreadProfiler::OnPeriodicCollectionCompleted,
                          owning_thread_task_runner_,
-                         weak_factory_.GetWeakPtr())));
+                         weak_factory_.GetWeakPtr())),
+      GetNativeUnwinderFactory().Run());
   if (aux_unwinder_factory_)
     periodic_profiler_->AddAuxUnwinder(aux_unwinder_factory_.Run());
 
diff --git a/chrome/common/profiler/thread_profiler.h b/chrome/common/profiler/thread_profiler.h
index 6302c75..6c2b4f8 100644
--- a/chrome/common/profiler/thread_profiler.h
+++ b/chrome/common/profiler/thread_profiler.h
@@ -7,6 +7,7 @@
 
 #include <memory>
 
+#include "base/callback.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/profiler/stack_sampling_profiler.h"
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 27f0064..6fb4442 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1270,6 +1270,7 @@
       "../browser/ui/extensions/extension_message_bubble_browsertest.cc",
       "../browser/ui/extensions/extension_message_bubble_browsertest.h",
       "../browser/ui/extensions/hosted_app_browsertest.cc",
+      "../browser/ui/extensions/settings_overridden_params_providers_browsertest.cc",
       "../browser/ui/find_bar/find_bar_controller_browsertest.cc",
       "../browser/ui/find_bar/find_bar_host_browsertest.cc",
       "../browser/ui/find_bar/find_bar_platform_helper_mac_browsertest.mm",
@@ -5398,6 +5399,7 @@
       "../browser/ui/views/download/download_in_progress_dialog_view_unittest.cc",
       "../browser/ui/views/download/download_item_view_unittest.cc",
       "../browser/ui/views/extensions/chooser_dialog_view_unittest.cc",
+      "../browser/ui/views/extensions/expandable_container_view_unittest.cc",
       "../browser/ui/views/extensions/extensions_menu_item_unittest.cc",
       "../browser/ui/views/extensions/extensions_menu_view_unittest.cc",
       "../browser/ui/views/extensions/media_galleries_dialog_views_unittest.cc",
diff --git a/chrome/test/android/BUILD.gn b/chrome/test/android/BUILD.gn
index b6aab09..71b8017 100644
--- a/chrome/test/android/BUILD.gn
+++ b/chrome/test/android/BUILD.gn
@@ -64,6 +64,8 @@
   ]
   deps = [
     ":chrome_java_test_pagecontroller",
+    "//base:base_java_test_support",
+    "//content/public/test/android:content_java_test_support",
     "//third_party/junit",
   ]
 
@@ -104,6 +106,11 @@
   sources = [ "javatests/src/org/chromium/chrome/test/pagecontroller/tests/webapk/MapsGoFirstRunTest.java" ]
   deps = [
     ":chrome_java_test_pagecontroller",
+    "//base:base_java",
+    "//chrome/android:chrome_java",
+    "//chrome/android/webapk/libs/client:client_java",
+    "//content/public/test/android:content_java_test_support",
+    "//third_party/android_support_test_runner:runner_java",
     "//third_party/junit",
   ]
 
diff --git a/chrome/test/data/android/url_overriding/navigation_to_cct_via_intent_uri_spoofing.html b/chrome/test/data/android/url_overriding/navigation_to_cct_via_intent_uri_spoofing.html
new file mode 100644
index 0000000..aa58863
--- /dev/null
+++ b/chrome/test/data/android/url_overriding/navigation_to_cct_via_intent_uri_spoofing.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+<!--
+Intent URI example taken from crbug.com/1056754 with package modification here.
+-->
+<head>
+    <meta name="viewport"
+          content="width=device-width, initial-scale=1.0, maximum-scale=1.0" />
+</head>
+
+<body>
+<a id="link" target='_blank' href='intent://about:blank#Intent;package=org.chromium.chrome.tests;action=android.intent.action.VIEW;scheme=http;S.android.support.customtabs.extra.SESSION=;i.org.chromium.chrome.browser.customtabs.EXTRA_UI_TYPE=2;end;'>
+    Click to open App!!
+</a>
+</body>
+
+</html>
\ No newline at end of file
diff --git a/chrome/test/data/extensions/search_provider_override/_locales/en/messages.json b/chrome/test/data/extensions/search_provider_override/_locales/en/messages.json
new file mode 100644
index 0000000..d3c54780
--- /dev/null
+++ b/chrome/test/data/extensions/search_provider_override/_locales/en/messages.json
@@ -0,0 +1,6 @@
+{
+  "url_domain": {
+    "message": "com",
+    "description": "TLD for the search provider"
+  }
+}
diff --git a/chrome/test/data/extensions/search_provider_override/manifest.json b/chrome/test/data/extensions/search_provider_override/manifest.json
new file mode 100644
index 0000000..b20d4e82
--- /dev/null
+++ b/chrome/test/data/extensions/search_provider_override/manifest.json
@@ -0,0 +1,17 @@
+{
+  "name": "Search Override Extension",
+  "manifest_version": 2,
+  "version": "0.1",
+  "description": "A simple extension that overrides the search engine",
+  "default_locale": "en",
+  "chrome_settings_overrides": {
+    "search_provider": {
+      "search_url": "https://www.example.com/?q={searchTerms}",
+      "name": "Example",
+      "keyword": "word",
+      "encoding": "UTF-8",
+      "is_default": true,
+      "favicon_url": "https://example.com/favicon.ico"
+    }
+  }
+}
diff --git a/chrome/test/data/geolocation/geolocation_on_load.html b/chrome/test/data/geolocation/geolocation_on_load.html
index 69b0bb5..d83a0f6 100644
--- a/chrome/test/data/geolocation/geolocation_on_load.html
+++ b/chrome/test/data/geolocation/geolocation_on_load.html
@@ -29,12 +29,14 @@
   var lng = position.coords.longitude;
   document.getElementById('lat').innerHTML = lat;
   document.getElementById('lng').innerHTML = lng;
+  window.document.title = "Granted:1";
 }
 
 function showError(positionError) {
   document.getElementById('lat').innerHTML =
-      positionError.message;
+      positionError.message;  
   document.getElementById('lng').innerHTML = '';
+  window.document.title = "Denied:1";
 }
 
     </script>
diff --git a/chrome/test/data/policy/policy_test_cases.json b/chrome/test/data/policy/policy_test_cases.json
index 0ba461d..f73409a 100644
--- a/chrome/test/data/policy/policy_test_cases.json
+++ b/chrome/test/data/policy/policy_test_cases.json
@@ -3466,7 +3466,7 @@
   },
 
   "UseLegacyFormControls": {
-    "note": "This policy has been removed. See https://bugs.chromium.org/p/chromium/issues/detail?id=1083085"
+    "note": "This policy has been removed, as of M85."
   },
 
   "ScrollToTextFragmentEnabled": {
@@ -5612,8 +5612,7 @@
   },
 
   "DeviceLoginScreenIsolateOrigins": {
-    "os": ["chromeos"],
-    "note": "Chrome OS device policy used by session_manager only"
+    "note": "This policy has been removed in Chrome 77, see https://crbug.com/964068 ."
   },
 
   "DeviceLoginScreenSitePerProcess": {
diff --git a/chrome/test/data/webui/BUILD.gn b/chrome/test/data/webui/BUILD.gn
index f3daa5d..038ed77 100644
--- a/chrome/test/data/webui/BUILD.gn
+++ b/chrome/test/data/webui/BUILD.gn
@@ -332,6 +332,7 @@
 group("closure_compile") {
   deps = [
     ":closure_compile_local",
+    "cr_components:closure_compile",
     "cr_elements:closure_compile",
     "print_preview:closure_compile",
     "settings:closure_compile",
diff --git a/chrome/test/data/webui/cr_components/BUILD.gn b/chrome/test/data/webui/cr_components/BUILD.gn
index cd15187..1646606e 100644
--- a/chrome/test/data/webui/cr_components/BUILD.gn
+++ b/chrome/test/data/webui/cr_components/BUILD.gn
@@ -2,6 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//third_party/closure_compiler/compile_js.gni")
 import("//ui/webui/resources/tools/js_modulizer.gni")
 import("../namespace_rewrites.gni")
 
@@ -9,3 +10,25 @@
   input_files = [ "managed_footnote_test.js" ]
   namespace_rewrites = test_namespace_rewrites
 }
+
+js_type_check("closure_compile") {
+  is_polymer3 = true
+  closure_flags = default_closure_args + [
+                    "browser_resolver_prefix_replacements=\"chrome://settings/=../../chrome/browser/resources/settings/\"",
+                    "js_module_root=../../chrome/test/data/webui/",
+                    "js_module_root=./gen/chrome/test/data/webui/",
+                  ]
+
+  deps = [ ":certificate_manager_provisioning_test" ]
+}
+
+js_library("certificate_manager_provisioning_test") {
+  deps = [
+    "..:chai_assert",
+    "..:test_browser_proxy.m",
+    "//ui/webui/resources/cr_components/certificate_manager:certificate_provisioning_details_dialog",
+    "//ui/webui/resources/cr_components/certificate_manager:certificate_provisioning_entry",
+    "//ui/webui/resources/cr_components/certificate_manager:certificate_provisioning_list",
+  ]
+  externs_list = [ "$externs_path/mocha-2.5.js" ]
+}
diff --git a/chrome/test/data/webui/cr_components/certificate_manager_provisioning_test.js b/chrome/test/data/webui/cr_components/certificate_manager_provisioning_test.js
new file mode 100644
index 0000000..fd4b0f9
--- /dev/null
+++ b/chrome/test/data/webui/cr_components/certificate_manager_provisioning_test.js
@@ -0,0 +1,220 @@
+// 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://settings/strings.m.js';
+import 'chrome://resources/cr_components/certificate_manager/certificate_provisioning_details_dialog.js';
+import 'chrome://resources/cr_components/certificate_manager/certificate_provisioning_entry.js';
+import 'chrome://resources/cr_components/certificate_manager/certificate_provisioning_list.js';
+
+import {CertificateProvisioningActionEventDetail, CertificateProvisioningViewDetailsActionEvent} from 'chrome://resources/cr_components/certificate_manager/certificate_manager_types.js';
+import {CertificateProvisioningBrowserProxy, CertificateProvisioningBrowserProxyImpl, CertificateProvisioningProcess} from 'chrome://resources/cr_components/certificate_manager/certificate_provisioning_browser_proxy.js';
+import {webUIListenerCallback} from 'chrome://resources/js/cr.m.js';
+import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {assertEquals, assertFalse, assertTrue} from '../chai_assert.js';
+import {TestBrowserProxy} from '../test_browser_proxy.m.js';
+import {eventToPromise} from '../test_util.m.js';
+
+
+/**
+ * A test version of CertificateProvisioningBrowserProxy.
+ * Provides helper methods for allowing tests to know when a method was called,
+ * as well as specifying mock responses.
+ *
+ * @implements {CertificateProvisioningBrowserProxy}
+ */
+class TestCertificateProvisioningBrowserProxy extends TestBrowserProxy {
+  constructor() {
+    super([
+      'refreshCertificateProvisioningProcesses',
+      'triggerCertificateProvisioningProcessUpdate',
+    ]);
+  }
+
+  /** override */
+  refreshCertificateProvisioningProcesses() {
+    this.methodCalled('refreshCertificateProvisioningProcesses');
+  }
+
+  /** override */
+  triggerCertificateProvisioningProcessUpdate(certProfileId, isDeviceWide) {
+    this.methodCalled(
+        'triggerCertificateProvisioningProcessUpdate',
+        {certProfileId, isDeviceWide});
+  }
+}
+
+/** @return {!CertificateProvisioningProcess} */
+function createSampleCertificateProvisioningProcess() {
+  return {
+    certProfileId: 'dummyProfileId',
+    isDeviceWide: true,
+    publicKey: 'dummyPublicKey',
+    stateId: 8,
+    status: 'dummyStateName',
+    timeSinceLastUpdate: 'dummyTimeSinceLastUpdate',
+  };
+}
+
+/**
+ * @param {?CertificateProvisioningListElement} certProvisioningList
+ * @return {!NodeList<!Element>}
+ */
+function getEntries(certProvisioningList) {
+  assertTrue(!!certProvisioningList);
+  return certProvisioningList.shadowRoot.querySelectorAll(
+      'certificate-provisioning-entry');
+}
+
+suite('CertificateProvisioningEntryTests', function() {
+  /** @type {?CertificateProvisioningEntryElement} */
+  let entry = null;
+
+  /** @type {?TestCertificateProvisioningBrowserProxy} */
+  let browserProxy = null;
+
+  /**
+   * @return {!Promise} A promise firing once
+   *     |CertificateProvisioningViewDetailsActionEvent| fires.
+   */
+  function actionEventToPromise() {
+    return eventToPromise(CertificateProvisioningViewDetailsActionEvent, entry);
+  }
+
+  setup(function() {
+    browserProxy = new TestCertificateProvisioningBrowserProxy();
+    CertificateProvisioningBrowserProxyImpl.instance_ = browserProxy;
+    entry = /** @type {!CertificateProvisioningEntryElement} */ (
+        document.createElement('certificate-provisioning-entry'));
+    entry.model = createSampleCertificateProvisioningProcess();
+    document.body.appendChild(entry);
+
+    // Bring up the popup menu for the following tests to use.
+    entry.$$('#dots').click();
+    flush();
+  });
+
+  teardown(function() {
+    entry.remove();
+  });
+
+  // Test case where 'Details' option is tapped.
+  test('MenuOptions_Details', function() {
+    const detailsButton = entry.$$('#details');
+    const waitForActionEvent = actionEventToPromise();
+    detailsButton.click();
+    return waitForActionEvent.then(function(event) {
+      const detail =
+          /** @type {!CertificateProvisioningActionEventDetail} */ (
+              event.detail);
+      assertEquals(entry.model, detail.model);
+    });
+  });
+});
+
+suite('CertificateManagerProvisioningTests', function() {
+  /** @type {?CertificateProvisioningListElement} */
+  let certProvisioningList = null;
+
+  /** @type {?TestCertificateProvisioningBrowserProxy} */
+  let browserProxy = null;
+
+  setup(function() {
+    browserProxy = new TestCertificateProvisioningBrowserProxy();
+    CertificateProvisioningBrowserProxyImpl.instance_ = browserProxy;
+    certProvisioningList =
+        /** @type {!CertificateProvisioningListElement} */ (
+            document.createElement('certificate-provisioning-list'));
+    document.body.appendChild(certProvisioningList);
+  });
+
+  teardown(function() {
+    certProvisioningList.remove();
+  });
+
+  /**
+   * Test that the certProvisioningList requests information about certificate
+   * provisioning processesfrom the browser on startup and that it gets
+   * populated accordingly.
+   */
+  test('Initialization', function() {
+    assertEquals(0, getEntries(certProvisioningList).length);
+
+    return browserProxy.whenCalled('refreshCertificateProvisioningProcesses')
+        .then(function() {
+          webUIListenerCallback(
+              'certificate-provisioning-processes-changed',
+              [createSampleCertificateProvisioningProcess()]);
+
+          flush();
+
+          assertEquals(1, getEntries(certProvisioningList).length);
+        });
+  });
+
+  test('OpensDialog_ViewDetails', function() {
+    const dialogId = 'certificate-provisioning-details-dialog';
+    const anchorForTest = document.createElement('a');
+    document.body.appendChild(anchorForTest);
+
+    assertFalse(!!certProvisioningList.$$(dialogId));
+    const whenDialogOpen =
+        eventToPromise('cr-dialog-open', certProvisioningList);
+    certProvisioningList.fire(
+        CertificateProvisioningViewDetailsActionEvent,
+        /** @type {!CertificateProvisioningActionEventDetail} */ ({
+          model: createSampleCertificateProvisioningProcess(),
+          anchor: anchorForTest
+        }));
+
+    return whenDialogOpen
+        .then(() => {
+          const dialog = certProvisioningList.$$(dialogId);
+          assertTrue(!!dialog);
+          const whenDialogClosed = eventToPromise('close', dialog);
+          dialog.$$('#dialog').$$('#close').click();
+          return whenDialogClosed;
+        })
+        .then(() => {
+          const dialog = certProvisioningList.$$(dialogId);
+          assertFalse(!!dialog);
+        });
+  });
+});
+
+suite('DetailsDialogTests', function() {
+  /** @type {?CertificateProvisioningDetailsDialogElement} */
+  let dialog = null;
+
+  /** @type {?TestCertificateProvisioningBrowserProxy} */
+  let browserProxy = null;
+
+  setup(async function() {
+    browserProxy = new TestCertificateProvisioningBrowserProxy();
+
+    CertificateProvisioningBrowserProxyImpl.instance_ = browserProxy;
+    dialog = /** @type {!CertificateProvisioningDetailsDialogElement} */ (
+        document.createElement('certificate-provisioning-details-dialog'));
+  });
+
+  teardown(function() {
+    dialog.remove();
+  });
+
+  test('RefreshProcess', function() {
+    dialog.model = createSampleCertificateProvisioningProcess();
+    document.body.appendChild(dialog);
+
+    // Simulate clicking 'Refresh'.
+    dialog.$.refresh.click();
+
+    browserProxy.whenCalled('triggerCertificateProvisioningProcessUpdate')
+        .then(function({certProfileId, isDeviceWide}) {
+          assertEquals(dialog.model.certProfileId, certProfileId);
+          assertEquals(dialog.model.isDeviceWide, isDeviceWide);
+          // Check that the dialog is still open.
+          assertTrue(dialog.$$('#dialog').open);
+        });
+  });
+});
diff --git a/chrome/test/data/webui/cr_components/cr_components_v3_browsertest.js b/chrome/test/data/webui/cr_components/cr_components_v3_browsertest.js
index 2c5a322..443cc6e9 100644
--- a/chrome/test/data/webui/cr_components/cr_components_v3_browsertest.js
+++ b/chrome/test/data/webui/cr_components/cr_components_v3_browsertest.js
@@ -74,3 +74,26 @@
 });
 
 GEN('#endif  // defined(USE_NSS_CERTS)');
+
+
+GEN('#if defined(USE_NSS_CERTS) && defined(OS_CHROMEOS)');
+
+/**
+ * ChromeOS specific test fixture for chrome://settings/certificates, testing
+ * the certificate provisioning UI. This tests the certificate-manager component
+ * in the context of the Settings privacy page.
+ */
+// eslint-disable-next-line no-var
+var CrComponentsCertificateManagerProvisioningV3Test =
+    class extends CrComponentsCertificateManagerV3Test {
+  /** @override */
+  get browsePreload() {
+    return 'chrome://settings/test_loader.html?module=cr_components/certificate_manager_provisioning_test.js';
+  }
+};
+
+TEST_F('CrComponentsCertificateManagerProvisioningV3Test', 'All', function() {
+  mocha.run();
+});
+
+GEN('#endif  // defined(USE_NSS_CERTS) && defined(OS_CHROMEOS)');
diff --git a/chrome/test/data/webui/settings/passwords_section_test.js b/chrome/test/data/webui/settings/passwords_section_test.js
index c328452..8e2f37b3 100644
--- a/chrome/test/data/webui/settings/passwords_section_test.js
+++ b/chrome/test/data/webui/settings/passwords_section_test.js
@@ -544,6 +544,46 @@
     assertTrue(passwordDialog.$.showPasswordButton.hidden);
   });
 
+  test('verifyStorageDetailsInEditDialogForAccountPassword', function() {
+    const accountPassword = createPasswordEntry('goo.gl', 'bart');
+    accountPassword.fromAccountStore = true;
+    const accountPasswordDialog =
+        elementFactory.createPasswordEditDialog(accountPassword);
+    flush();
+
+    // By default no message is displayed.
+    assertTrue(accountPasswordDialog.$.storageDetails.hidden);
+
+    // Display the message.
+    accountPasswordDialog.shouldShowStorageDetails = true;
+    flush();
+    assertFalse(accountPasswordDialog.$.storageDetails.hidden);
+    assertEquals(
+        accountPasswordDialog.i18nAdvanced(
+            'passwordStoredInAccount', {tags: ['b']}),
+        accountPasswordDialog.$.storageDetails.innerHTML);
+  });
+
+  test('verifyStorageDetailsInEditDialogForDevicePassword', function() {
+    const devicePassword = createPasswordEntry('goo.gl', 'bart');
+    devicePassword.fromAccountStore = false;
+    const devicePasswordDialog =
+        elementFactory.createPasswordEditDialog(devicePassword);
+    flush();
+
+    // By default no message is displayed.
+    assertTrue(devicePasswordDialog.$.storageDetails.hidden);
+
+    // Display the message.
+    devicePasswordDialog.shouldShowStorageDetails = true;
+    flush();
+    assertFalse(devicePasswordDialog.$.storageDetails.hidden);
+    assertEquals(
+        devicePasswordDialog.i18nAdvanced(
+            'passwordStoredOnDevice', {tags: ['b']}),
+        devicePasswordDialog.$.storageDetails.innerHTML);
+  });
+
   test('showSavedPasswordEditDialog', function() {
     const PASSWORD = 'bAn@n@5';
     const item = createPasswordEntry('goo.gl', 'bart');
diff --git a/chrome/test/data/webui/tab_strip/BUILD.gn b/chrome/test/data/webui/tab_strip/BUILD.gn
index 5488f66..7b07912 100644
--- a/chrome/test/data/webui/tab_strip/BUILD.gn
+++ b/chrome/test/data/webui/tab_strip/BUILD.gn
@@ -21,11 +21,18 @@
     #":tab_list_test",
     #":tab_swiper_test",
     #":tab_test",
-    #":test_tab_strip_embedder_proxy",
+    ":test_tab_strip_embedder_proxy",
     ":test_tabs_api_proxy",
   ]
 }
 
+js_library("test_tab_strip_embedder_proxy") {
+  deps = [
+    "..:test_browser_proxy.m",
+    "//chrome/browser/resources/tab_strip:tab_strip_embedder_proxy",
+  ]
+}
+
 js_library("test_tabs_api_proxy") {
   deps = [
     "..:test_browser_proxy.m",
diff --git a/chrome/test/data/webui/tab_strip/tab_group_test.js b/chrome/test/data/webui/tab_strip/tab_group_test.js
index 1c9def8..be281e9 100644
--- a/chrome/test/data/webui/tab_strip/tab_group_test.js
+++ b/chrome/test/data/webui/tab_strip/tab_group_test.js
@@ -5,7 +5,7 @@
 import 'chrome://tab-strip/tab.js';
 import 'chrome://tab-strip/tab_group.js';
 
-import {TabStripEmbedderProxy} from 'chrome://tab-strip/tab_strip_embedder_proxy.js';
+import {TabStripEmbedderProxyImpl} from 'chrome://tab-strip/tab_strip_embedder_proxy.js';
 import {TestTabStripEmbedderProxy} from './test_tab_strip_embedder_proxy.js';
 
 suite('TabGroup', () => {
@@ -16,7 +16,7 @@
 
   setup(() => {
     testTabStripEmbedderProxy = new TestTabStripEmbedderProxy();
-    TabStripEmbedderProxy.instance_ = testTabStripEmbedderProxy;
+    TabStripEmbedderProxyImpl.instance_ = testTabStripEmbedderProxy;
 
     document.body.innerHTML = '';
     tabGroupElement = document.createElement('tabstrip-tab-group');
diff --git a/chrome/test/data/webui/tab_strip/tab_list_test.js b/chrome/test/data/webui/tab_strip/tab_list_test.js
index fe5ff4f..c3a9e0a 100644
--- a/chrome/test/data/webui/tab_strip/tab_list_test.js
+++ b/chrome/test/data/webui/tab_strip/tab_list_test.js
@@ -6,7 +6,7 @@
 import {FocusOutlineManager} from 'chrome://resources/js/cr/ui/focus_outline_manager.m.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 import {setScrollAnimationEnabledForTesting} from 'chrome://tab-strip/tab_list.js';
-import {TabStripEmbedderProxy} from 'chrome://tab-strip/tab_strip_embedder_proxy.js';
+import {TabStripEmbedderProxyImpl} from 'chrome://tab-strip/tab_strip_embedder_proxy.js';
 import {TabsApiProxyImpl} from 'chrome://tab-strip/tabs_api_proxy.js';
 
 import {eventToPromise} from '../test_util.m.js';
@@ -91,7 +91,7 @@
       '--width': '150px',
     });
     testTabStripEmbedderProxy.setVisible(true);
-    TabStripEmbedderProxy.instance_ = testTabStripEmbedderProxy;
+    TabStripEmbedderProxyImpl.instance_ = testTabStripEmbedderProxy;
 
     setScrollAnimationEnabledForTesting(false);
 
diff --git a/chrome/test/data/webui/tab_strip/tab_test.js b/chrome/test/data/webui/tab_strip/tab_test.js
index 1c235e4..19558c7 100644
--- a/chrome/test/data/webui/tab_strip/tab_test.js
+++ b/chrome/test/data/webui/tab_strip/tab_test.js
@@ -6,7 +6,7 @@
 
 import {getFavicon} from 'chrome://resources/js/icon.m.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
-import {TabStripEmbedderProxy} from 'chrome://tab-strip/tab_strip_embedder_proxy.js';
+import {TabStripEmbedderProxy, TabStripEmbedderProxyImpl} from 'chrome://tab-strip/tab_strip_embedder_proxy.js';
 import {CloseTabAction, TabNetworkState, TabsApiProxyImpl} from 'chrome://tab-strip/tabs_api_proxy.js';
 
 import {TestTabStripEmbedderProxy} from './test_tab_strip_embedder_proxy.js';
@@ -44,7 +44,7 @@
     document.body.style.setProperty('--tabstrip-tab-spacing', '20px');
 
     testTabStripEmbedderProxy = new TestTabStripEmbedderProxy();
-    TabStripEmbedderProxy.instance_ = testTabStripEmbedderProxy;
+    TabStripEmbedderProxyImpl.instance_ = testTabStripEmbedderProxy;
 
     testTabsApiProxy = new TestTabsApiProxy();
     TabsApiProxyImpl.instance_ = testTabsApiProxy;
diff --git a/chrome/test/data/webui/tab_strip/test_tab_strip_embedder_proxy.js b/chrome/test/data/webui/tab_strip/test_tab_strip_embedder_proxy.js
index 1e8903c..424dd37 100644
--- a/chrome/test/data/webui/tab_strip/test_tab_strip_embedder_proxy.js
+++ b/chrome/test/data/webui/tab_strip/test_tab_strip_embedder_proxy.js
@@ -2,8 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {TestBrowserProxy} from 'chrome://test/test_browser_proxy.m.js';
+import {TabStripEmbedderProxy} from 'chrome://tab-strip/tab_strip_embedder_proxy.js';
 
+import {TestBrowserProxy} from '../test_browser_proxy.m.js';
+
+/** @implements {TabStripEmbedderProxy} */
 export class TestTabStripEmbedderProxy extends TestBrowserProxy {
   constructor() {
     super([
@@ -20,69 +23,87 @@
       'reportTabCreationDuration',
     ]);
 
+    /** @private {!Object<string, string>} */
     this.colors_ = {};
+
+    /** @private {!Object<string, string>} */
     this.layout_ = {};
+
+    /** @private {boolean} */
     this.visible_ = false;
   }
 
+  /** @override */
   getColors() {
     this.methodCalled('getColors');
     return Promise.resolve(this.colors_);
   }
 
+  /** @override */
   getLayout() {
     this.methodCalled('getLayout');
     return Promise.resolve(this.layout_);
   }
 
+  /** @override */
   isVisible() {
     this.methodCalled('isVisible');
     return this.visible_;
   }
 
+  /** @param {!Object<string, string>} colors */
   setColors(colors) {
     this.colors_ = colors;
   }
 
+  /** @param {!Object<string, string>} layout */
   setLayout(layout) {
     this.layout_ = layout;
   }
 
+  /** @param {boolean} visible */
   setVisible(visible) {
     this.visible_ = visible;
   }
 
+  /** @override */
   observeThemeChanges() {
     this.methodCalled('observeThemeChanges');
   }
 
+  /** @override */
   closeContainer() {
     this.methodCalled('closeContainer');
-    return Promise.resolve();
   }
 
+  /** @override */
   showBackgroundContextMenu(locationX, locationY) {
     this.methodCalled('showBackgroundContextMenu', [locationX, locationY]);
   }
 
+  /** @override */
   showEditDialogForGroup(groupId, locationX, locationY, width, height) {
     this.methodCalled(
         'showEditDialogForGroup',
         [groupId, locationX, locationY, width, height]);
   }
 
+  /** @override */
   showTabContextMenu(tabId, locationX, locationY) {
     this.methodCalled('showTabContextMenu', [tabId, locationX, locationY]);
   }
 
+  /** @override */
   reportTabActivationDuration(durationMs) {
     this.methodCalled('reportTabActivationDuration', [durationMs]);
   }
 
+  /** @override */
   reportTabDataReceivedDuration(tabCount, durationMs) {
     this.methodCalled('reportTabDataReceivedDuration', [tabCount, durationMs]);
   }
 
+  /** @override */
   reportTabCreationDuration(tabCount, durationMs) {
     this.methodCalled('reportTabCreationDuration', [tabCount, durationMs]);
   }
diff --git a/chromecast/ui/media_control_ui.cc b/chromecast/ui/media_control_ui.cc
index 7338bf8..8bc8564 100644
--- a/chromecast/ui/media_control_ui.cc
+++ b/chromecast/ui/media_control_ui.cc
@@ -66,52 +66,38 @@
 
   // |touch_view| will detect touch events and decide whether to show
   // or hide |view_|, which contains the media controls.
-  touch_view_ = std::make_unique<TouchView>(base::BindRepeating(
+  auto touch_view = std::make_unique<TouchView>(base::BindRepeating(
       &MediaControlUi::OnTapped, weak_factory_.GetWeakPtr()));
 
   // Main view.
-  view_ = std::make_unique<views::View>();
-  view_->set_owned_by_client();
-  view_->SetVisible(false);
-  view_->SetBackground(
+  auto view = std::make_unique<views::View>();
+  view->SetVisible(false);
+  view->SetBackground(
       views::CreateSolidBackground(SkColorSetA(SK_ColorBLACK, 0x80)));
-  view_->SetBoundsRect(
+  view->SetBoundsRect(
       window_manager_->GetRootWindow()->GetBoundsInRootWindow());
-  touch_view_->AddChildView(view_.get());
+  view_ = touch_view->AddChildView(std::move(view));
 
   // Buttons.
-  btn_previous_ =
-      CreateImageButton(vector_icons::kPreviousIcon, kButtonSmallHeight);
-  btn_previous_->set_owned_by_client();
-  view_->AddChildView(btn_previous_.get());
-  btn_play_pause_ =
-      CreateImageButton(vector_icons::kPlayIcon, kButtonBigHeight);
-  btn_play_pause_->set_owned_by_client();
-  view_->AddChildView(btn_play_pause_.get());
-  btn_next_ = CreateImageButton(vector_icons::kNextIcon, kButtonSmallHeight);
-  btn_next_->set_owned_by_client();
-  view_->AddChildView(btn_next_.get());
-  btn_replay30_ =
-      CreateImageButton(vector_icons::kBack30Icon, kButtonSmallHeight);
-  btn_replay30_->set_owned_by_client();
-  view_->AddChildView(btn_replay30_.get());
-  btn_forward30_ =
-      CreateImageButton(vector_icons::kForward30Icon, kButtonSmallHeight);
-  btn_forward30_->set_owned_by_client();
-  view_->AddChildView(btn_forward30_.get());
+  btn_previous_ = view_->AddChildView(
+      CreateImageButton(vector_icons::kPreviousIcon, kButtonSmallHeight));
+  btn_play_pause_ = view_->AddChildView(
+      CreateImageButton(vector_icons::kPlayIcon, kButtonBigHeight));
+  btn_next_ = view_->AddChildView(
+      CreateImageButton(vector_icons::kNextIcon, kButtonSmallHeight));
+  btn_replay30_ = view_->AddChildView(
+      CreateImageButton(vector_icons::kBack30Icon, kButtonSmallHeight));
+  btn_forward30_ = view_->AddChildView(
+      CreateImageButton(vector_icons::kForward30Icon, kButtonSmallHeight));
 
   // Labels.
-  lbl_title_ = std::make_unique<views::Label>(base::string16());
-  lbl_title_->set_owned_by_client();
-  view_->AddChildView(lbl_title_.get());
-  lbl_meta_ = std::make_unique<views::Label>(base::string16());
-  lbl_meta_->set_owned_by_client();
-  view_->AddChildView(lbl_meta_.get());
+  lbl_title_ =
+      view_->AddChildView(std::make_unique<views::Label>(base::string16()));
+  lbl_meta_ =
+      view_->AddChildView(std::make_unique<views::Label>(base::string16()));
 
   // Progress Bar.
-  progress_bar_ = std::make_unique<views::ProgressBar>();
-  progress_bar_->set_owned_by_client();
-  view_->AddChildView(progress_bar_.get());
+  progress_bar_ = view_->AddChildView(std::make_unique<views::ProgressBar>());
 
   LayoutElements();
 
@@ -123,7 +109,7 @@
   params.opacity = views::Widget::InitParams::WindowOpacity::kTranslucent;
   params.bounds = window_manager_->GetRootWindow()->GetBoundsInRootWindow();
   widget_->Init(std::move(params));
-  widget_->SetContentsView(touch_view_.release());  // Ownership passed.
+  widget_->SetContentsView(std::move(touch_view));
 
   window_manager_->SetZOrder(widget_->GetNativeView(),
                              mojom::ZOrder::MEDIA_INFO);
@@ -161,9 +147,8 @@
     if (is_paused_ && !visible()) {
       ShowMediaControls(true);
     }
-    SetButtonImage(btn_play_pause_.get(), is_paused_
-                                              ? vector_icons::kPlayIcon
-                                              : vector_icons::kPauseIcon);
+    SetButtonImage(btn_play_pause_, is_paused_ ? vector_icons::kPlayIcon
+                                               : vector_icons::kPauseIcon);
   }
 
   if (attributes->duration > 0.0) {
@@ -235,15 +220,15 @@
     return;
   }
 
-  if (sender == btn_previous_.get()) {
+  if (sender == btn_previous_) {
     client_->Execute(mojom::MediaCommand::PREVIOUS);
-  } else if (sender == btn_play_pause_.get()) {
+  } else if (sender == btn_play_pause_) {
     client_->Execute(mojom::MediaCommand::TOGGLE_PLAY_PAUSE);
-  } else if (sender == btn_next_.get()) {
+  } else if (sender == btn_next_) {
     client_->Execute(mojom::MediaCommand::NEXT);
-  } else if (sender == btn_replay30_.get()) {
+  } else if (sender == btn_replay30_) {
     client_->Execute(mojom::MediaCommand::REPLAY_30_SECONDS);
-  } else if (sender == btn_forward30_.get()) {
+  } else if (sender == btn_forward30_) {
     client_->Execute(mojom::MediaCommand::FORWARD_30_SECONDS);
   } else {
     NOTREACHED();
diff --git a/chromecast/ui/media_control_ui.h b/chromecast/ui/media_control_ui.h
index 5c29aa9d..0916c83 100644
--- a/chromecast/ui/media_control_ui.h
+++ b/chromecast/ui/media_control_ui.h
@@ -71,22 +71,21 @@
 
   // UI components
   std::unique_ptr<views::Widget> widget_;
-  std::unique_ptr<views::View> touch_view_;
-  std::unique_ptr<views::View> view_;
+  views::View* view_;
 
   // Controls
-  std::unique_ptr<views::ImageButton> btn_previous_;
-  std::unique_ptr<views::ImageButton> btn_play_pause_;
-  std::unique_ptr<views::ImageButton> btn_next_;
-  std::unique_ptr<views::ImageButton> btn_replay30_;
-  std::unique_ptr<views::ImageButton> btn_forward30_;
+  views::ImageButton* btn_previous_;
+  views::ImageButton* btn_play_pause_;
+  views::ImageButton* btn_next_;
+  views::ImageButton* btn_replay30_;
+  views::ImageButton* btn_forward30_;
 
   // Labels
-  std::unique_ptr<views::Label> lbl_meta_;
-  std::unique_ptr<views::Label> lbl_title_;
+  views::Label* lbl_meta_;
+  views::Label* lbl_title_;
 
   // Progress
-  std::unique_ptr<views::ProgressBar> progress_bar_;
+  views::ProgressBar* progress_bar_;
 
   bool is_paused_;
 
diff --git a/chromeos/services/assistant/assistant_manager_service_impl.cc b/chromeos/services/assistant/assistant_manager_service_impl.cc
index 76353f6..66627401 100644
--- a/chromeos/services/assistant/assistant_manager_service_impl.cc
+++ b/chromeos/services/assistant/assistant_manager_service_impl.cc
@@ -1054,7 +1054,7 @@
   // There can only be one |AssistantManager| instance at any given time.
   DCHECK(!assistant_manager_);
   new_display_connection_ = std::make_unique<CrosDisplayConnection>(
-      this, assistant::features::IsFeedbackUiEnabled(),
+      this, /*feedback_ui_enabled=*/true,
       assistant::features::IsMediaSessionIntegrationEnabled());
 
   new_assistant_manager_ = delegate_->CreateAssistantManager(
diff --git a/chromeos/services/assistant/public/cpp/features.cc b/chromeos/services/assistant/public/cpp/features.cc
index 82e5fc1..a76014c 100644
--- a/chromeos/services/assistant/public/cpp/features.cc
+++ b/chromeos/services/assistant/public/cpp/features.cc
@@ -13,12 +13,6 @@
 const base::Feature kAssistantAudioEraser{"AssistantAudioEraser",
                                           base::FEATURE_DISABLED_BY_DEFAULT};
 
-const base::Feature kAssistantFeedbackUi{"AssistantFeedbackUi",
-                                         base::FEATURE_ENABLED_BY_DEFAULT};
-
-const base::Feature kAssistantWarmerWelcomeFeature{
-    "AssistantWarmerWelcome", base::FEATURE_ENABLED_BY_DEFAULT};
-
 const base::Feature kAssistantAppSupport{"AssistantAppSupport",
                                          base::FEATURE_DISABLED_BY_DEFAULT};
 
@@ -149,10 +143,6 @@
   return base::FeatureList::IsEnabled(kEnableDspHotword);
 }
 
-bool IsFeedbackUiEnabled() {
-  return base::FeatureList::IsEnabled(kAssistantFeedbackUi);
-}
-
 bool IsLauncherChipIntegrationEnabled() {
   return base::FeatureList::IsEnabled(kAssistantLauncherChipIntegration);
 }
@@ -213,10 +203,6 @@
          (IsResponseProcessingV2Enabled() || IsRoutinesEnabled());
 }
 
-bool IsWarmerWelcomeEnabled() {
-  return base::FeatureList::IsEnabled(kAssistantWarmerWelcomeFeature);
-}
-
 }  // namespace features
 }  // namespace assistant
 }  // namespace chromeos
diff --git a/chromeos/services/assistant/public/cpp/features.h b/chromeos/services/assistant/public/cpp/features.h
index 342c71f..bba53c0f 100644
--- a/chromeos/services/assistant/public/cpp/features.h
+++ b/chromeos/services/assistant/public/cpp/features.h
@@ -20,14 +20,6 @@
 COMPONENT_EXPORT(ASSISTANT_SERVICE_PUBLIC)
 extern const base::Feature kAssistantAudioEraser;
 
-// Enable Assistant Feedback UI.
-COMPONENT_EXPORT(ASSISTANT_SERVICE_PUBLIC)
-extern const base::Feature kAssistantFeedbackUi;
-
-// Enables Assistant warmer welcome.
-COMPONENT_EXPORT(ASSISTANT_SERVICE_PUBLIC)
-extern const base::Feature kAssistantWarmerWelcomeFeature;
-
 // Enables Assistant app support.
 COMPONENT_EXPORT(ASSISTANT_SERVICE_PUBLIC)
 extern const base::Feature kAssistantAppSupport;
diff --git a/chromeos/services/secure_channel/ble_weave_client_connection.cc b/chromeos/services/secure_channel/ble_weave_client_connection.cc
index cab424d2..c530f76 100644
--- a/chromeos/services/secure_channel/ble_weave_client_connection.cc
+++ b/chromeos/services/secure_channel/ble_weave_client_connection.cc
@@ -692,8 +692,9 @@
   if (sub_status() == SubStatus::CONNECTED_AND_IDLE)
     SetSubStatus(SubStatus::CONNECTED_AND_SENDING_MESSAGE);
 
-  characteristic->DeprecatedWriteRemoteCharacteristic(
+  characteristic->WriteRemoteCharacteristic(
       pending_write_request_->value,
+      device::BluetoothRemoteGattCharacteristic::WriteType::kWithoutResponse,
       base::BindOnce(&BluetoothLowEnergyWeaveClientConnection::
                          OnRemoteCharacteristicWritten,
                      weak_ptr_factory_.GetWeakPtr()),
diff --git a/chromeos/services/secure_channel/ble_weave_client_connection_unittest.cc b/chromeos/services/secure_channel/ble_weave_client_connection_unittest.cc
index c7cd1a6..ff4fc9c 100644
--- a/chromeos/services/secure_channel/ble_weave_client_connection_unittest.cc
+++ b/chromeos/services/secure_channel/ble_weave_client_connection_unittest.cc
@@ -487,12 +487,11 @@
   // WAITING_CONNECTION_RESPONSE state.
   void NotifySessionStarted(
       TestBluetoothLowEnergyWeaveClientConnection* connection) {
-    EXPECT_CALL(*tx_characteristic_,
-                DeprecatedWriteRemoteCharacteristic_(_, _, _))
+    EXPECT_CALL(*tx_characteristic_, WriteRemoteCharacteristic_(_, _, _, _))
         .WillOnce(
             DoAll(SaveArg<0>(&last_value_written_on_tx_characteristic_),
-                  MoveArg<1>(&write_remote_characteristic_success_callback_),
-                  MoveArg<2>(&write_remote_characteristic_error_callback_)));
+                  MoveArg<2>(&write_remote_characteristic_success_callback_),
+                  MoveArg<3>(&write_remote_characteristic_error_callback_)));
     EXPECT_FALSE(notify_session_error_callback_.is_null());
     ASSERT_FALSE(notify_session_success_callback_.is_null());
 
@@ -548,12 +547,11 @@
   // state.
   void Disconnect(TestBluetoothLowEnergyWeaveClientConnection* connection) {
     if (connection->IsConnected()) {
-      EXPECT_CALL(*tx_characteristic_,
-                  DeprecatedWriteRemoteCharacteristic_(_, _, _))
+      EXPECT_CALL(*tx_characteristic_, WriteRemoteCharacteristic_(_, _, _, _))
           .WillOnce(
               DoAll(SaveArg<0>(&last_value_written_on_tx_characteristic_),
-                    MoveArg<1>(&write_remote_characteristic_success_callback_),
-                    MoveArg<2>(&write_remote_characteristic_error_callback_)));
+                    MoveArg<2>(&write_remote_characteristic_success_callback_),
+                    MoveArg<3>(&write_remote_characteristic_error_callback_)));
     }
 
     connection->Disconnect();
@@ -573,12 +571,11 @@
           connection) {
     bool was_connected = (*connection)->IsConnected();
     if (was_connected) {
-      EXPECT_CALL(*tx_characteristic_,
-                  DeprecatedWriteRemoteCharacteristic_(_, _, _))
+      EXPECT_CALL(*tx_characteristic_, WriteRemoteCharacteristic_(_, _, _, _))
           .WillOnce(
               DoAll(SaveArg<0>(&last_value_written_on_tx_characteristic_),
-                    MoveArg<1>(&write_remote_characteristic_success_callback_),
-                    MoveArg<2>(&write_remote_characteristic_error_callback_)));
+                    MoveArg<2>(&write_remote_characteristic_success_callback_),
+                    MoveArg<3>(&write_remote_characteristic_error_callback_)));
     }
 
     connection->reset();
@@ -791,12 +788,11 @@
   InitializeConnection(connection.get(), kDefaultMaxPacketSize);
   EXPECT_EQ(connection->sub_status(), SubStatus::CONNECTED_AND_IDLE);
 
-  EXPECT_CALL(*tx_characteristic_,
-              DeprecatedWriteRemoteCharacteristic_(_, _, _))
+  EXPECT_CALL(*tx_characteristic_, WriteRemoteCharacteristic_(_, _, _, _))
       .WillOnce(
           DoAll(SaveArg<0>(&last_value_written_on_tx_characteristic_),
-                MoveArg<1>(&write_remote_characteristic_success_callback_),
-                MoveArg<2>(&write_remote_characteristic_error_callback_)));
+                MoveArg<2>(&write_remote_characteristic_success_callback_),
+                MoveArg<3>(&write_remote_characteristic_error_callback_)));
 
   // Call Disconnect() twice; this should only result in one "close connection"
   // message (verified via WillOnce() above).
@@ -919,8 +915,7 @@
   ConnectGatt(connection.get());
   CharacteristicsFound(connection.get());
 
-  EXPECT_CALL(*tx_characteristic_,
-              DeprecatedWriteRemoteCharacteristic_(_, _, _))
+  EXPECT_CALL(*tx_characteristic_, WriteRemoteCharacteristic_(_, _, _, _))
       .Times(0);
   EXPECT_FALSE(notify_session_success_callback_.is_null());
   ASSERT_FALSE(notify_session_error_callback_.is_null());
@@ -948,16 +943,15 @@
 
   // |connection| will call WriteRemoteCharacteristics(_,_) to try to send the
   // message |kMaxNumberOfTries| times. There is already one EXPECT_CALL for
-  // DeprecatedWriteRemoteCharacteristic(_,_,_) in NotifySessionStated, that's
-  // why we use |kMaxNumberOfTries-1| in the EXPECT_CALL statement.
+  // WriteRemoteCharacteristic(_,_,_) in NotifySessionStated, that's why we use
+  // |kMaxNumberOfTries-1| in the EXPECT_CALL statement.
   EXPECT_EQ(0, connection_observer_->num_send_completed());
-  EXPECT_CALL(*tx_characteristic_,
-              DeprecatedWriteRemoteCharacteristic_(_, _, _))
+  EXPECT_CALL(*tx_characteristic_, WriteRemoteCharacteristic_(_, _, _, _))
       .Times(kMaxNumberOfTries - 1)
       .WillRepeatedly(
           DoAll(SaveArg<0>(&last_value_written_on_tx_characteristic_),
-                MoveArg<1>(&write_remote_characteristic_success_callback_),
-                MoveArg<2>(&write_remote_characteristic_error_callback_)));
+                MoveArg<2>(&write_remote_characteristic_success_callback_),
+                MoveArg<3>(&write_remote_characteristic_error_callback_)));
 
   for (int i = 0; i < kMaxNumberOfTries; i++) {
     EXPECT_EQ(last_value_written_on_tx_characteristic_, kConnectionRequest);
@@ -1030,14 +1024,13 @@
       CreateConnection(true /* should_set_low_connection_latency */));
   InitializeConnection(connection.get(), kDefaultMaxPacketSize);
 
-  // Expecting a first call of DeprecatedWriteRemoteCharacteristic, after
-  // SendMessage is called.
-  EXPECT_CALL(*tx_characteristic_,
-              DeprecatedWriteRemoteCharacteristic_(_, _, _))
+  // Expecting a first call of WriteRemoteCharacteristic, after SendMessage is
+  // called.
+  EXPECT_CALL(*tx_characteristic_, WriteRemoteCharacteristic_(_, _, _, _))
       .WillOnce(
           DoAll(SaveArg<0>(&last_value_written_on_tx_characteristic_),
-                MoveArg<1>(&write_remote_characteristic_success_callback_),
-                MoveArg<2>(&write_remote_characteristic_error_callback_)));
+                MoveArg<2>(&write_remote_characteristic_success_callback_),
+                MoveArg<3>(&write_remote_characteristic_error_callback_)));
 
   connection->SendMessage(
       std::make_unique<FakeWireMessage>(kSmallMessage, kTestFeature));
@@ -1064,14 +1057,13 @@
 
   InitializeConnection(connection.get(), kLargeMaxPacketSize);
 
-  // Expecting a first call of DeprecatedWriteRemoteCharacteristic, after
-  // SendMessage is called.
-  EXPECT_CALL(*tx_characteristic_,
-              DeprecatedWriteRemoteCharacteristic_(_, _, _))
+  // Expecting a first call of WriteRemoteCharacteristic, after SendMessage is
+  // called.
+  EXPECT_CALL(*tx_characteristic_, WriteRemoteCharacteristic_(_, _, _, _))
       .WillOnce(
           DoAll(SaveArg<0>(&last_value_written_on_tx_characteristic_),
-                MoveArg<1>(&write_remote_characteristic_success_callback_),
-                MoveArg<2>(&write_remote_characteristic_error_callback_)));
+                MoveArg<2>(&write_remote_characteristic_success_callback_),
+                MoveArg<3>(&write_remote_characteristic_error_callback_)));
 
   connection->SendMessage(
       std::make_unique<FakeWireMessage>(kLargeMessage, kTestFeature));
@@ -1081,12 +1073,11 @@
       last_value_written_on_tx_characteristic_.begin() + 1,
       last_value_written_on_tx_characteristic_.end());
 
-  EXPECT_CALL(*tx_characteristic_,
-              DeprecatedWriteRemoteCharacteristic_(_, _, _))
+  EXPECT_CALL(*tx_characteristic_, WriteRemoteCharacteristic_(_, _, _, _))
       .WillOnce(
           DoAll(SaveArg<0>(&last_value_written_on_tx_characteristic_),
-                MoveArg<1>(&write_remote_characteristic_success_callback_),
-                MoveArg<2>(&write_remote_characteristic_error_callback_)));
+                MoveArg<2>(&write_remote_characteristic_success_callback_),
+                MoveArg<3>(&write_remote_characteristic_error_callback_)));
 
   RunWriteCharacteristicSuccessCallback();
   VerifyGattWriteCharacteristicResult(true /* success */, 2 /* num_writes */);
@@ -1118,13 +1109,12 @@
       CreateConnection(true /* should_set_low_connection_latency */));
   InitializeConnection(connection.get(), kDefaultMaxPacketSize);
 
-  EXPECT_CALL(*tx_characteristic_,
-              DeprecatedWriteRemoteCharacteristic_(_, _, _))
+  EXPECT_CALL(*tx_characteristic_, WriteRemoteCharacteristic_(_, _, _, _))
       .Times(kMaxNumberOfTries)
       .WillRepeatedly(
           DoAll(SaveArg<0>(&last_value_written_on_tx_characteristic_),
-                MoveArg<1>(&write_remote_characteristic_success_callback_),
-                MoveArg<2>(&write_remote_characteristic_error_callback_)));
+                MoveArg<2>(&write_remote_characteristic_success_callback_),
+                MoveArg<3>(&write_remote_characteristic_error_callback_)));
 
   connection->SendMessage(
       std::make_unique<FakeWireMessage>(kSmallMessage, kTestFeature));
@@ -1181,12 +1171,11 @@
 
   InitializeConnection(connection.get(), kDefaultMaxPacketSize);
 
-  EXPECT_CALL(*tx_characteristic_,
-              DeprecatedWriteRemoteCharacteristic_(_, _, _))
+  EXPECT_CALL(*tx_characteristic_, WriteRemoteCharacteristic_(_, _, _, _))
       .WillOnce(
           DoAll(SaveArg<0>(&last_value_written_on_tx_characteristic_),
-                MoveArg<1>(&write_remote_characteristic_success_callback_),
-                MoveArg<2>(&write_remote_characteristic_error_callback_)));
+                MoveArg<2>(&write_remote_characteristic_success_callback_),
+                MoveArg<3>(&write_remote_characteristic_error_callback_)));
 
   connection->GattCharacteristicValueChanged(
       adapter_.get(), rx_characteristic_.get(), kErroneousPacket);
@@ -1212,12 +1201,11 @@
 
   InitializeConnection(connection.get(), kLargeMaxPacketSize);
 
-  EXPECT_CALL(*tx_characteristic_,
-              DeprecatedWriteRemoteCharacteristic_(_, _, _))
+  EXPECT_CALL(*tx_characteristic_, WriteRemoteCharacteristic_(_, _, _, _))
       .WillOnce(
           DoAll(SaveArg<0>(&last_value_written_on_tx_characteristic_),
-                MoveArg<1>(&write_remote_characteristic_success_callback_),
-                MoveArg<2>(&write_remote_characteristic_error_callback_)));
+                MoveArg<2>(&write_remote_characteristic_success_callback_),
+                MoveArg<3>(&write_remote_characteristic_error_callback_)));
 
   connection->SendMessage(
       std::make_unique<FakeWireMessage>(kLargeMessage, kTestFeature));
@@ -1227,12 +1215,11 @@
 
   EXPECT_EQ(last_value_written_on_tx_characteristic_, kLargePackets0);
 
-  EXPECT_CALL(*tx_characteristic_,
-              DeprecatedWriteRemoteCharacteristic_(_, _, _))
+  EXPECT_CALL(*tx_characteristic_, WriteRemoteCharacteristic_(_, _, _, _))
       .WillOnce(
           DoAll(SaveArg<0>(&last_value_written_on_tx_characteristic_),
-                MoveArg<1>(&write_remote_characteristic_success_callback_),
-                MoveArg<2>(&write_remote_characteristic_error_callback_)));
+                MoveArg<2>(&write_remote_characteristic_success_callback_),
+                MoveArg<3>(&write_remote_characteristic_error_callback_)));
 
   RunWriteCharacteristicSuccessCallback();
   VerifyGattWriteCharacteristicResult(true /* success */, 2 /* num_writes */);
@@ -1260,12 +1247,11 @@
 
   InitializeConnection(connection, kDefaultMaxPacketSize);
 
-  EXPECT_CALL(*tx_characteristic_,
-              DeprecatedWriteRemoteCharacteristic_(_, _, _))
+  EXPECT_CALL(*tx_characteristic_, WriteRemoteCharacteristic_(_, _, _, _))
       .WillOnce(
           DoAll(SaveArg<0>(&last_value_written_on_tx_characteristic_),
-                MoveArg<1>(&write_remote_characteristic_success_callback_),
-                MoveArg<2>(&write_remote_characteristic_error_callback_)));
+                MoveArg<2>(&write_remote_characteristic_success_callback_),
+                MoveArg<3>(&write_remote_characteristic_error_callback_)));
 
   connection->GattCharacteristicValueChanged(
       adapter_.get(), rx_characteristic_.get(), kErroneousPacket);
@@ -1294,13 +1280,12 @@
 
   InitializeConnection(connection, kDefaultMaxPacketSize);
 
-  EXPECT_CALL(*tx_characteristic_,
-              DeprecatedWriteRemoteCharacteristic_(_, _, _))
+  EXPECT_CALL(*tx_characteristic_, WriteRemoteCharacteristic_(_, _, _, _))
       .Times(2)
       .WillRepeatedly(
           DoAll(SaveArg<0>(&last_value_written_on_tx_characteristic_),
-                MoveArg<1>(&write_remote_characteristic_success_callback_),
-                MoveArg<2>(&write_remote_characteristic_error_callback_)));
+                MoveArg<2>(&write_remote_characteristic_success_callback_),
+                MoveArg<3>(&write_remote_characteristic_error_callback_)));
 
   connection->SendMessage(
       std::make_unique<FakeWireMessage>(kSmallMessage, kTestFeature));
@@ -1332,12 +1317,11 @@
   InitializeConnection(connection.get(), kDefaultMaxPacketSize);
   EXPECT_EQ(connection->sub_status(), SubStatus::CONNECTED_AND_IDLE);
 
-  EXPECT_CALL(*tx_characteristic_,
-              DeprecatedWriteRemoteCharacteristic_(_, _, _))
+  EXPECT_CALL(*tx_characteristic_, WriteRemoteCharacteristic_(_, _, _, _))
       .WillOnce(
           DoAll(SaveArg<0>(&last_value_written_on_tx_characteristic_),
-                MoveArg<1>(&write_remote_characteristic_success_callback_),
-                MoveArg<2>(&write_remote_characteristic_error_callback_)));
+                MoveArg<2>(&write_remote_characteristic_success_callback_),
+                MoveArg<3>(&write_remote_characteristic_error_callback_)));
   connection->Disconnect();
   EXPECT_EQ(connection->sub_status(), SubStatus::CONNECTED_AND_SENDING_MESSAGE);
 
@@ -1348,12 +1332,11 @@
     EXPECT_FALSE(write_remote_characteristic_success_callback_.is_null());
 
     if (i != kMaxNumberOfTries - 1) {
-      EXPECT_CALL(*tx_characteristic_,
-                  DeprecatedWriteRemoteCharacteristic_(_, _, _))
+      EXPECT_CALL(*tx_characteristic_, WriteRemoteCharacteristic_(_, _, _, _))
           .WillOnce(
               DoAll(SaveArg<0>(&last_value_written_on_tx_characteristic_),
-                    MoveArg<1>(&write_remote_characteristic_success_callback_),
-                    MoveArg<2>(&write_remote_characteristic_error_callback_)));
+                    MoveArg<2>(&write_remote_characteristic_success_callback_),
+                    MoveArg<3>(&write_remote_characteristic_error_callback_)));
     }
 
     std::move(write_remote_characteristic_error_callback_)
@@ -1632,8 +1615,7 @@
 
   InitializeConnection(connection.get(), kDefaultMaxPacketSize);
   EXPECT_EQ(connection->sub_status(), SubStatus::CONNECTED_AND_IDLE);
-  EXPECT_CALL(*tx_characteristic_,
-              DeprecatedWriteRemoteCharacteristic_(_, _, _));
+  EXPECT_CALL(*tx_characteristic_, WriteRemoteCharacteristic_(_, _, _, _));
 
   connection->SendMessage(
       std::make_unique<FakeWireMessage>(kSmallMessage, kTestFeature));
diff --git a/components/autofill/android/BUILD.gn b/components/autofill/android/BUILD.gn
index f246bae..42a9a9b 100644
--- a/components/autofill/android/BUILD.gn
+++ b/components/autofill/android/BUILD.gn
@@ -41,6 +41,7 @@
     "//base:base_java",
     "//content/public/android:content_java",
     "//third_party/android_deps:android_support_v7_appcompat_java",
+    "//third_party/android_deps:androidx_appcompat_appcompat_resources_java",
     "//ui/android:ui_java",
   ]
   sources = [
diff --git a/components/autofill/android/junit/BUILD.gn b/components/autofill/android/junit/BUILD.gn
index 0f58b8a..3e3e800 100644
--- a/components/autofill/android/junit/BUILD.gn
+++ b/components/autofill/android/junit/BUILD.gn
@@ -16,6 +16,9 @@
     "//base:base_junit_test_support",
     "//components/autofill/android:provider_java",
     "//content/public/android:content_java",
+    "//third_party/junit",
+    "//third_party/mockito:mockito_java",
+    "//third_party/robolectric:robolectric_all_java",
     "//ui/android:ui_java",
   ]
 }
diff --git a/components/autofill/core/browser/autofill_experiments.cc b/components/autofill/core/browser/autofill_experiments.cc
index ef21685..86f1fbc 100644
--- a/components/autofill/core/browser/autofill_experiments.cc
+++ b/components/autofill/core/browser/autofill_experiments.cc
@@ -225,34 +225,4 @@
   return group_name == "Disabled";
 }
 
-bool OfferStoreUnmaskedCards(bool is_off_the_record) {
-#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
-  // The checkbox can be forced on with a flag, but by default we don't store
-  // on Linux due to lack of system keychain integration. See crbug.com/162735
-  return base::CommandLine::ForCurrentProcess()->HasSwitch(
-      switches::kEnableOfferStoreUnmaskedWalletCards);
-#else
-  // Never offer to store unmasked cards when off the record.
-  if (is_off_the_record) {
-    return false;
-  }
-
-  // Query the field trial before checking command line flags to ensure UMA
-  // reports the correct group.
-  std::string group_name =
-      base::FieldTrialList::FindFullName("OfferStoreUnmaskedWalletCards");
-
-  // The checkbox can be forced on or off with flags.
-  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kEnableOfferStoreUnmaskedWalletCards))
-    return true;
-  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kDisableOfferStoreUnmaskedWalletCards))
-    return false;
-
-  // Otherwise use the field trial to show the checkbox or not.
-  return group_name != "Disabled";
-#endif
-}
-
 }  // namespace autofill
diff --git a/components/autofill/core/browser/autofill_experiments.h b/components/autofill/core/browser/autofill_experiments.h
index d291ce0..d5b92be5 100644
--- a/components/autofill/core/browser/autofill_experiments.h
+++ b/components/autofill/core/browser/autofill_experiments.h
@@ -44,11 +44,6 @@
 // disables providing suggestions.
 bool IsInAutofillSuggestionsDisabledExperiment();
 
-// Returns true if the user should be offered to locally store unmasked cards.
-// This controls whether the option is presented at all rather than the default
-// response of the option.
-bool OfferStoreUnmaskedCards(bool is_off_the_record);
-
 }  // namespace autofill
 
 #endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_EXPERIMENTS_H_
diff --git a/components/autofill/core/browser/form_data_importer.cc b/components/autofill/core/browser/form_data_importer.cc
index 8b0b407..e4147d4 100644
--- a/components/autofill/core/browser/form_data_importer.cc
+++ b/components/autofill/core/browser/form_data_importer.cc
@@ -679,17 +679,12 @@
     }
   }
 
-  // If editable expiration date experiment is enabled, the card with invalid
-  // expiration date can be uploaded. However, the card with invalid card number
-  // must be ignored.
+  // Cards with invalid expiration dates can be uploaded due to the existence of
+  // the expiration date fix flow. However, cards with invalid card numbers must
+  // still be ignored.
   if (!candidate_credit_card.HasValidCardNumber()) {
     return false;
   }
-  if (!candidate_credit_card.HasValidExpirationDate() &&
-      !base::FeatureList::IsEnabled(
-          features::kAutofillUpstreamEditableExpirationDate)) {
-    return false;
-  }
 
   // Can import one valid card per form. Start by treating it as NEW_CARD, but
   // overwrite this type if we discover it is already a local or server card.
diff --git a/components/autofill/core/browser/form_data_importer_unittest.cc b/components/autofill/core/browser/form_data_importer_unittest.cc
index ca166df..7eabf94 100644
--- a/components/autofill/core/browser/form_data_importer_unittest.cc
+++ b/components/autofill/core/browser/form_data_importer_unittest.cc
@@ -1507,39 +1507,10 @@
   ASSERT_EQ(0U, personal_data_manager_->GetCreditCards().size());
 }
 
-// Tests that an invalid credit card expiration is not extracted when the
-// expiration date fix flow experiment is disabled.
-TEST_F(FormDataImporterTest, ImportCreditCard_InvalidExpiryDate) {
-  scoped_feature_list_.InitAndDisableFeature(
-      features::kAutofillUpstreamEditableExpirationDate);
-  FormData form;
-  form.url = GURL("https://wwww.foo.com");
-
-  AddFullCreditCardForm(&form, "Smalls Biggie", "4111-1111-1111-1111", "0",
-                        "2999");
-
-  FormStructure form_structure(form);
-  form_structure.DetermineHeuristicTypes();
-  std::unique_ptr<CreditCard> imported_credit_card;
-  base::HistogramTester histogram_tester;
-  EXPECT_FALSE(ImportCreditCard(form_structure, false, &imported_credit_card));
-  ASSERT_FALSE(imported_credit_card);
-  histogram_tester.ExpectUniqueSample("Autofill.SubmittedCardState",
-                                      AutofillMetrics::HAS_CARD_NUMBER_ONLY, 1);
-
-  // Since no refresh is expected, reload the data from the database to make
-  // sure no changes were written out.
-  ResetPersonalDataManager(USER_MODE_NORMAL);
-
-  ASSERT_EQ(0U, personal_data_manager_->GetCreditCards().size());
-}
-
-// Tests that an empty credit card expiration is extracted when editable
-// expiration date experiment on.
+// Tests that a credit card with an empty expiration can be extracted due to the
+// expiration date fix flow.
 TEST_F(FormDataImporterTest,
        ImportCreditCard_InvalidExpiryDate_EditableExpirationExpOn) {
-  scoped_feature_list_.InitAndEnableFeature(
-      features::kAutofillUpstreamEditableExpirationDate);
   FormData form;
   form.url = GURL("https://wwww.foo.com");
 
@@ -1555,12 +1526,10 @@
                                       AutofillMetrics::HAS_CARD_NUMBER_ONLY, 1);
 }
 
-// Tests that an expired credit card is extracted when editable expiration date
-// experiment on.
+// Tests that an expired credit card can be extracted due to the expiration date
+// fix flow.
 TEST_F(FormDataImporterTest,
        ImportCreditCard_ExpiredExpiryDate_EditableExpirationExpOn) {
-  scoped_feature_list_.InitAndEnableFeature(
-      features::kAutofillUpstreamEditableExpirationDate);
   FormData form;
   form.url = GURL("https://wwww.foo.com");
 
@@ -2458,38 +2427,7 @@
 // Ensures that |imported_credit_card_record_type_| is set correctly.
 TEST_F(
     FormDataImporterTest,
-    ImportFormData_ImportCreditCardRecordType_NoCard_ExpiredCard_EditableExpDateOff) {
-  scoped_feature_list_.InitAndDisableFeature(
-      features::kAutofillUpstreamEditableExpirationDate);
-  // Simulate a form submission with an expired credit card.
-  FormData form;
-  form.url = GURL("https://wwww.foo.com");
-
-  AddFullCreditCardForm(&form, "Biggie Smalls", "4111 1111 1111 1111", "01",
-                        "1999");
-
-  FormStructure form_structure(form);
-  form_structure.DetermineHeuristicTypes();
-  std::unique_ptr<CreditCard> imported_credit_card;
-  base::Optional<std::string> imported_upi_id;
-  EXPECT_FALSE(form_data_importer_->ImportFormData(
-      form_structure, /*profile_autofill_enabled=*/true,
-      /*credit_card_autofill_enabled=*/true,
-      /*should_return_local_card=*/true, &imported_credit_card,
-      &imported_upi_id));
-  ASSERT_FALSE(imported_credit_card);
-  // |imported_credit_card_record_type_| should be NO_CARD because no valid card
-  // was successfully imported from the form.
-  ASSERT_TRUE(form_data_importer_->imported_credit_card_record_type_ ==
-              FormDataImporter::ImportedCreditCardRecordType::NO_CARD);
-}
-
-// Ensures that |imported_credit_card_record_type_| is set correctly.
-TEST_F(
-    FormDataImporterTest,
     ImportFormData_ImportCreditCardRecordType_NewCard_ExpiredCard_WithExpDateFixFlow) {
-  scoped_feature_list_.InitAndEnableFeature(
-      features::kAutofillUpstreamEditableExpirationDate);
   // Simulate a form submission with an expired credit card.
   FormData form;
   form.url = GURL("https://wwww.foo.com");
@@ -3110,8 +3048,6 @@
 // server card and user submitted an invalid expiration date month.
 TEST_F(FormDataImporterTest,
        Metrics_SubmittedServerCardExpirationStatus_EmptyExpirationMonth) {
-  scoped_feature_list_.InitAndEnableFeature(
-      features::kAutofillUpstreamEditableExpirationDate);
   EnableWalletCardImport();
 
   std::vector<CreditCard> server_cards;
@@ -3160,8 +3096,6 @@
 // server card and user submitted an invalid expiration date year.
 TEST_F(FormDataImporterTest,
        Metrics_SubmittedServerCardExpirationStatus_EmptyExpirationYear) {
-  scoped_feature_list_.InitAndEnableFeature(
-      features::kAutofillUpstreamEditableExpirationDate);
   EnableWalletCardImport();
 
   std::vector<CreditCard> server_cards;
@@ -3211,8 +3145,6 @@
 TEST_F(
     FormDataImporterTest,
     Metrics_SubmittedDifferentServerCardExpirationStatus_EmptyExpirationYear) {
-  scoped_feature_list_.InitAndEnableFeature(
-      features::kAutofillUpstreamEditableExpirationDate);
   EnableWalletCardImport();
 
   std::vector<CreditCard> server_cards;
diff --git a/components/autofill/core/browser/payments/credit_card_save_manager.cc b/components/autofill/core/browser/payments/credit_card_save_manager.cc
index 617c2f2..a42eab0f8 100644
--- a/components/autofill/core/browser/payments/credit_card_save_manager.cc
+++ b/components/autofill/core/browser/payments/credit_card_save_manager.cc
@@ -255,8 +255,6 @@
       (should_request_expiration_date_from_user_ &&
        personal_data_manager_->GetSyncSigninState() ==
            AutofillSyncSigninState::kSignedInAndWalletSyncTransportEnabled)) {
-    DCHECK(base::FeatureList::IsEnabled(
-        features::kAutofillUpstreamEditableExpirationDate));
     LogCardUploadDecisions(upload_decision_metrics_);
     pending_upload_request_origin_ = url::Origin();
     return;
@@ -731,43 +729,39 @@
     detected_values |= DetectedValue::HAS_GOOGLE_PAYMENTS_ACCOUNT;
   }
 
-  if (base::FeatureList::IsEnabled(
-          features::kAutofillUpstreamEditableExpirationDate)) {
-    // If expiration date month or expiration year are missing, signal that
-    // expiration date will be explicitly requested in the offer-to-save bubble.
-    if (!upload_request_.card
-             .GetInfo(AutofillType(CREDIT_CARD_EXP_MONTH), app_locale_)
-             .empty()) {
-      detected_values |= DetectedValue::CARD_EXPIRATION_MONTH;
-    }
-    if (!(upload_request_.card
-              .GetInfo(AutofillType(CREDIT_CARD_EXP_4_DIGIT_YEAR), app_locale_)
-              .empty())) {
-      detected_values |= DetectedValue::CARD_EXPIRATION_YEAR;
-    }
+  // If expiration date month or expiration year are missing, signal that
+  // expiration date will be explicitly requested in the offer-to-save bubble.
+  if (!upload_request_.card
+           .GetInfo(AutofillType(CREDIT_CARD_EXP_MONTH), app_locale_)
+           .empty()) {
+    detected_values |= DetectedValue::CARD_EXPIRATION_MONTH;
+  }
+  if (!(upload_request_.card
+            .GetInfo(AutofillType(CREDIT_CARD_EXP_4_DIGIT_YEAR), app_locale_)
+            .empty())) {
+    detected_values |= DetectedValue::CARD_EXPIRATION_YEAR;
+  }
 
-    // Set |USER_PROVIDED_EXPIRATION_DATE| if expiration date is detected as
-    // expired or missing.
-    if (detected_values & DetectedValue::CARD_EXPIRATION_MONTH &&
-        detected_values & DetectedValue::CARD_EXPIRATION_YEAR) {
-      int month_value = 0, year_value = 0;
-      bool parsable =
-          base::StringToInt(
-              upload_request_.card.GetInfo(AutofillType(CREDIT_CARD_EXP_MONTH),
-                                           app_locale_),
-              &month_value) &&
-          base::StringToInt(
-              upload_request_.card.GetInfo(
-                  AutofillType(CREDIT_CARD_EXP_4_DIGIT_YEAR), app_locale_),
-              &year_value);
-      DCHECK(parsable);
-      if (!IsValidCreditCardExpirationDate(year_value, month_value,
-                                           AutofillClock::Now())) {
-        detected_values |= DetectedValue::USER_PROVIDED_EXPIRATION_DATE;
-      }
-    } else {
+  // Set |USER_PROVIDED_EXPIRATION_DATE| if expiration date is detected as
+  // expired or missing.
+  if (detected_values & DetectedValue::CARD_EXPIRATION_MONTH &&
+      detected_values & DetectedValue::CARD_EXPIRATION_YEAR) {
+    int month_value = 0, year_value = 0;
+    bool parsable =
+        base::StringToInt(upload_request_.card.GetInfo(
+                              AutofillType(CREDIT_CARD_EXP_MONTH), app_locale_),
+                          &month_value) &&
+        base::StringToInt(
+            upload_request_.card.GetInfo(
+                AutofillType(CREDIT_CARD_EXP_4_DIGIT_YEAR), app_locale_),
+            &year_value);
+    DCHECK(parsable);
+    if (!IsValidCreditCardExpirationDate(year_value, month_value,
+                                         AutofillClock::Now())) {
       detected_values |= DetectedValue::USER_PROVIDED_EXPIRATION_DATE;
     }
+  } else {
+    detected_values |= DetectedValue::USER_PROVIDED_EXPIRATION_DATE;
   }
 
   // If cardholder name is conflicting/missing and the user does NOT have a
diff --git a/components/autofill/core/browser/payments/credit_card_save_manager_unittest.cc b/components/autofill/core/browser/payments/credit_card_save_manager_unittest.cc
index 184326b..8798b03 100644
--- a/components/autofill/core/browser/payments/credit_card_save_manager_unittest.cc
+++ b/components/autofill/core/browser/payments/credit_card_save_manager_unittest.cc
@@ -2619,9 +2619,6 @@
 TEST_F(
     CreditCardSaveManagerTest,
     UploadCreditCard_ShouldRequestExpirationDate_ResetBetweenConsecutiveSaves) {
-  scoped_feature_list_.InitAndEnableFeature(
-      features::kAutofillUpstreamEditableExpirationDate);
-
   // Create, fill and submit an address form in order to establish a recent
   // profile which can be selected for the upload request.
   FormData address_form;
@@ -2670,9 +2667,6 @@
 TEST_F(
     CreditCardSaveManagerTest,
     UploadCreditCard_WalletSyncTransportEnabled_ShouldNotRequestExpirationDate) {
-  scoped_feature_list_.InitAndEnableFeature(
-      features::kAutofillUpstreamEditableExpirationDate);
-
   // Wallet Sync Transport is enabled.
   personal_data_.SetSyncAndSignInState(
       AutofillSyncSigninState::kSignedInAndWalletSyncTransportEnabled);
@@ -2709,9 +2703,6 @@
 TEST_F(
     CreditCardSaveManagerTest,
     UploadCreditCard_WalletSyncTransportNotEnabled_ShouldRequestExpirationDate) {
-  scoped_feature_list_.InitAndEnableFeature(
-      features::kAutofillUpstreamEditableExpirationDate);
-
   // Wallet Sync Transport is not enabled.
   personal_data_.SetSyncAndSignInState(
       AutofillSyncSigninState::kSignedInAndSyncFeatureEnabled);
@@ -2750,9 +2741,6 @@
 TEST_F(
     CreditCardSaveManagerTest,
     UploadCreditCard_DoNotRequestExpirationDateIfMissingNameAndExpirationDate) {
-  scoped_feature_list_.InitAndEnableFeature(
-      features::kAutofillUpstreamEditableExpirationDate);
-
   // Create, fill and submit an address form in order to establish a recent
   // profile which can be selected for the upload request.
   FormData address_form;
@@ -2782,36 +2770,6 @@
 }
 
 TEST_F(CreditCardSaveManagerTest,
-       UploadCreditCard_DoNotRequestExpirationDate_EditableExpDateOff) {
-  scoped_feature_list_.InitAndDisableFeature(
-      features::kAutofillUpstreamEditableExpirationDate);
-  // Create, fill and submit an address form in order to establish a recent
-  // profile which can be selected for the upload request.
-  FormData address_form;
-  test::CreateTestAddressFormData(&address_form);
-  FormsSeen(std::vector<FormData>(1, address_form));
-  ManuallyFillAddressForm("John", "Smith", "77401", "US", &address_form);
-  FormSubmitted(address_form);
-
-  // Set up our credit card form data.
-  FormData credit_card_form;
-  CreateTestCreditCardFormData(&credit_card_form, CreditCardFormOptions());
-  FormsSeen(std::vector<FormData>(1, credit_card_form));
-
-  // Edit the data, and submit.
-  credit_card_form.fields[0].value = ASCIIToUTF16("John Smith");
-  credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
-  credit_card_form.fields[2].value = ASCIIToUTF16("");
-  credit_card_form.fields[3].value = ASCIIToUTF16("");
-  credit_card_form.fields[4].value = ASCIIToUTF16("123");
-
-  base::HistogramTester histogram_tester;
-  FormSubmitted(credit_card_form);
-  EXPECT_FALSE(autofill_client_.ConfirmSaveCardLocallyWasCalled());
-  EXPECT_FALSE(credit_card_save_manager_->CreditCardWasUploaded());
-}
-
-TEST_F(CreditCardSaveManagerTest,
        UploadCreditCard_RequestExpirationDateViaExpDateFixFlow) {
 #if defined(OS_IOS)
   // iOS should always provide a valid expiration date when attempting to
@@ -2824,8 +2782,6 @@
   }
 #endif
 
-  scoped_feature_list_.InitAndEnableFeature(
-      features::kAutofillUpstreamEditableExpirationDate);
   // Create, fill and submit an address form in order to establish a recent
   // profile which can be selected for the upload request.
   FormData address_form;
@@ -2878,8 +2834,6 @@
   }
 #endif
 
-  scoped_feature_list_.InitAndEnableFeature(
-      features::kAutofillUpstreamEditableExpirationDate);
   // Create, fill and submit an address form in order to establish a recent
   // profile which can be selected for the upload request.
   FormData address_form;
@@ -2932,8 +2886,6 @@
   }
 #endif
 
-  scoped_feature_list_.InitAndEnableFeature(
-      features::kAutofillUpstreamEditableExpirationDate);
   // Create, fill and submit an address form in order to establish a recent
   // profile which can be selected for the upload request.
   FormData address_form;
@@ -2986,8 +2938,6 @@
   }
 #endif
 
-  scoped_feature_list_.InitAndEnableFeature(
-      features::kAutofillUpstreamEditableExpirationDate);
   // Create, fill and submit an address form in order to establish a recent
   // profile which can be selected for the upload request.
   FormData address_form;
@@ -3041,8 +2991,6 @@
   }
 #endif
 
-  scoped_feature_list_.InitAndEnableFeature(
-      features::kAutofillUpstreamEditableExpirationDate);
   // Create, fill and submit an address form in order to establish a recent
   // profile which can be selected for the upload request.
   FormData address_form;
diff --git a/components/autofill/core/browser/personal_data_manager.cc b/components/autofill/core/browser/personal_data_manager.cc
index e45d579..c313b05 100644
--- a/components/autofill/core/browser/personal_data_manager.cc
+++ b/components/autofill/core/browser/personal_data_manager.cc
@@ -445,11 +445,6 @@
           ReceiveLoadedDbValues(h, result.get(),
                                 &pending_server_creditcards_query_,
                                 &server_credit_cards_);
-
-          // If the user has a saved unmasked server card and the experiment is
-          // disabled, force mask all cards back to the unsaved state.
-          if (!OfferStoreUnmaskedCards(is_off_the_record_))
-            ResetFullServerCards();
         }
         break;
       case AUTOFILL_CLOUDTOKEN_RESULT:
diff --git a/components/autofill/core/browser/personal_data_manager_unittest.cc b/components/autofill/core/browser/personal_data_manager_unittest.cc
index 7660449..5ac4782 100644
--- a/components/autofill/core/browser/personal_data_manager_unittest.cc
+++ b/components/autofill/core/browser/personal_data_manager_unittest.cc
@@ -1433,136 +1433,6 @@
   EXPECT_EQ(0, local_card.Compare(*personal_data_->GetCreditCards()[0]));
 }
 
-// Makes sure that full cards are re-masked when full PAN storage is off.
-TEST_F(PersonalDataManagerTest, RefuseToStoreFullCard) {
-// On Linux this should be disabled automatically. Elsewhere, only if the
-// flag is passed.
-#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
-  EXPECT_FALSE(base::CommandLine::ForCurrentProcess()->HasSwitch(
-      switches::kDisableOfferStoreUnmaskedWalletCards));
-#else
-  base::CommandLine::ForCurrentProcess()->AppendSwitch(
-      switches::kDisableOfferStoreUnmaskedWalletCards);
-#endif
-
-  std::vector<CreditCard> server_cards;
-  server_cards.push_back(CreditCard(CreditCard::FULL_SERVER_CARD, "c789"));
-  test::SetCreditCardInfo(&server_cards.back(), "Clyde Barrow",
-                          "378282246310005" /* American Express */, "04",
-                          "2999", "1");
-  SetServerCards(server_cards);
-  personal_data_->Refresh();
-
-  WaitForOnPersonalDataChanged();
-
-  ASSERT_EQ(1U, personal_data_->GetCreditCards().size());
-  EXPECT_EQ(CreditCard::MASKED_SERVER_CARD,
-            personal_data_->GetCreditCards()[0]->record_type());
-}
-
-// Makes sure that full cards are only added as masked card when full PAN
-// storage is disabled.
-TEST_F(PersonalDataManagerTest, AddFullCardAsMaskedCard) {
-// On Linux this should be disabled automatically. Elsewhere, only if the
-// flag is passed.
-#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
-  EXPECT_FALSE(base::CommandLine::ForCurrentProcess()->HasSwitch(
-      switches::kDisableOfferStoreUnmaskedWalletCards));
-#else
-  base::CommandLine::ForCurrentProcess()->AppendSwitch(
-      switches::kDisableOfferStoreUnmaskedWalletCards);
-#endif
-
-  CreditCard server_card(CreditCard::FULL_SERVER_CARD, "c789");
-  test::SetCreditCardInfo(&server_card, "Clyde Barrow",
-                          "378282246310005" /* American Express */, "04",
-                          "2999", "1");
-
-  personal_data_->AddFullServerCreditCard(server_card);
-
-  WaitForOnPersonalDataChanged();
-
-  ASSERT_EQ(1U, personal_data_->GetCreditCards().size());
-  EXPECT_EQ(CreditCard::MASKED_SERVER_CARD,
-            personal_data_->GetCreditCards()[0]->record_type());
-}
-
-TEST_F(PersonalDataManagerTest, OfferStoreUnmaskedCards) {
-#if defined(OS_CHROMEOS) || defined(OS_WIN) || defined(OS_MACOSX) || \
-    defined(OS_IOS) || defined(OS_ANDROID) || defined(OS_FUCHSIA)
-  bool should_offer = true;
-#elif defined(OS_LINUX)
-  bool should_offer = false;
-#endif
-  EXPECT_EQ(should_offer, OfferStoreUnmaskedCards(/*is_off_the_record=*/false));
-}
-
-// Tests that OfferStoreUnmaskedCards always returns false if the user is off
-// the record.
-TEST_F(PersonalDataManagerTest, OfferStoreUnmaskedCards_OffTheRecord) {
-  EXPECT_EQ(false, OfferStoreUnmaskedCards(/*is_off_the_record=*/true));
-}
-
-// Tests that UpdateServerCreditCard can be used to mask or unmask server cards.
-TEST_F(PersonalDataManagerTest, UpdateServerCreditCards) {
-  EnableWalletCardImport();
-
-  std::vector<CreditCard> server_cards;
-  server_cards.push_back(CreditCard(CreditCard::MASKED_SERVER_CARD, "a123"));
-  test::SetCreditCardInfo(&server_cards.back(), "John Dillinger",
-                          "3456" /* Visa */, "01", "2999", "1");
-  server_cards.back().SetNetworkForMaskedCard(kVisaCard);
-
-  server_cards.push_back(CreditCard(CreditCard::MASKED_SERVER_CARD, "b456"));
-  test::SetCreditCardInfo(&server_cards.back(), "Bonnie Parker",
-                          "5100" /* Mastercard */, "12", "2999", "1");
-  server_cards.back().SetNetworkForMaskedCard(kMasterCard);
-
-  server_cards.push_back(CreditCard(CreditCard::FULL_SERVER_CARD, "c789"));
-  test::SetCreditCardInfo(&server_cards.back(), "Clyde Barrow",
-                          "378282246310005" /* American Express */, "04",
-                          "2999", "1");
-
-  SetServerCards(server_cards);
-  personal_data_->Refresh();
-
-  WaitForOnPersonalDataChanged();
-
-  ASSERT_EQ(3U, personal_data_->GetCreditCards().size());
-  if (!OfferStoreUnmaskedCards(/*is_off_the_record=*/false)) {
-    for (CreditCard* card : personal_data_->GetCreditCards()) {
-      EXPECT_EQ(CreditCard::MASKED_SERVER_CARD, card->record_type());
-    }
-    // The rest of this test doesn't work if we're force-masking all unmasked
-    // cards.
-    return;
-  }
-
-  // The GUIDs will be different, so just compare the data.
-  for (size_t i = 0; i < 3; ++i)
-    EXPECT_EQ(0, server_cards[i].Compare(*personal_data_->GetCreditCards()[i]));
-
-  CreditCard* unmasked_card = &server_cards.front();
-  unmasked_card->set_record_type(CreditCard::FULL_SERVER_CARD);
-  unmasked_card->SetNumber(base::ASCIIToUTF16("4234567890123456"));
-  personal_data_->UpdateServerCreditCard(*unmasked_card);
-
-  WaitForOnPersonalDataChanged();
-
-  for (size_t i = 0; i < 3; ++i)
-    EXPECT_EQ(0, server_cards[i].Compare(*personal_data_->GetCreditCards()[i]));
-
-  CreditCard* remasked_card = &server_cards.back();
-  remasked_card->set_record_type(CreditCard::MASKED_SERVER_CARD);
-  remasked_card->SetNumber(base::ASCIIToUTF16("0005"));
-  personal_data_->UpdateServerCreditCard(*remasked_card);
-
-  WaitForOnPersonalDataChanged();
-
-  for (size_t i = 0; i < 3; ++i)
-    EXPECT_EQ(0, server_cards[i].Compare(*personal_data_->GetCreditCards()[i]));
-}
-
 TEST_F(PersonalDataManagerTest, AddProfilesAndCreditCards) {
   AutofillProfile profile0(base::GenerateGUID(), test::kEmptyOrigin);
   test::SetProfileInfo(&profile0, "Marion", "Mitchell", "Morrison",
@@ -4036,120 +3906,6 @@
   EXPECT_EQ(kArbitraryTime, added_card->modification_date());
 }
 
-TEST_F(PersonalDataManagerTest, UpdateServerCreditCardUsageStats) {
-  EnableWalletCardImport();
-
-  std::vector<CreditCard> server_cards;
-  server_cards.push_back(CreditCard(CreditCard::MASKED_SERVER_CARD, "a123"));
-  test::SetCreditCardInfo(&server_cards.back(), "John Dillinger",
-                          "3456" /* Visa */, "01", "2999", "1");
-  server_cards.back().SetNetworkForMaskedCard(kVisaCard);
-
-  server_cards.push_back(CreditCard(CreditCard::MASKED_SERVER_CARD, "b456"));
-  test::SetCreditCardInfo(&server_cards.back(), "Bonnie Parker",
-                          "4444" /* Mastercard */, "12", "2999", "1");
-  server_cards.back().SetNetworkForMaskedCard(kMasterCard);
-
-  server_cards.push_back(CreditCard(CreditCard::FULL_SERVER_CARD, "c789"));
-  test::SetCreditCardInfo(&server_cards.back(), "Clyde Barrow",
-                          "378282246310005" /* American Express */, "04",
-                          "2999", "1");
-
-  // Create the test clock and set the time to a specific value.
-  TestAutofillClock test_clock;
-  test_clock.SetNow(kArbitraryTime);
-
-  SetServerCards(server_cards);
-
-  // Make sure everything is set up correctly.
-  personal_data_->Refresh();
-  WaitForOnPersonalDataChanged();
-  EXPECT_EQ(3U, personal_data_->GetCreditCards().size());
-
-  if (!OfferStoreUnmaskedCards(/*is_off_the_record=*/false)) {
-    for (CreditCard* card : personal_data_->GetCreditCards()) {
-      EXPECT_EQ(CreditCard::MASKED_SERVER_CARD, card->record_type());
-    }
-    // The rest of this test doesn't work if we're force-masking all unmasked
-    // cards.
-    return;
-  }
-
-  // The GUIDs will be different, so just compare the data.
-  for (size_t i = 0; i < 3; ++i)
-    EXPECT_EQ(0, server_cards[i].Compare(*personal_data_->GetCreditCards()[i]));
-
-  CreditCard* unmasked_card = &server_cards.front();
-  unmasked_card->set_record_type(CreditCard::FULL_SERVER_CARD);
-  unmasked_card->SetNumber(base::ASCIIToUTF16("4234567890123456"));
-  personal_data_->UpdateServerCreditCard(*unmasked_card);
-
-  WaitForOnPersonalDataChanged();
-  ASSERT_EQ(3U, personal_data_->GetCreditCards().size());
-
-  for (size_t i = 0; i < 3; ++i)
-    EXPECT_EQ(0, server_cards[i].Compare(*personal_data_->GetCreditCards()[i]));
-
-  // For an unmasked card, usage data starts out as 2 because of the unmasking
-  // which is considered a use. The use date should now be the specified Now()
-  // time kArbitraryTime.
-  EXPECT_EQ(2U, personal_data_->GetCreditCards()[0]->use_count());
-  EXPECT_EQ(kArbitraryTime, personal_data_->GetCreditCards()[0]->use_date());
-
-  EXPECT_EQ(1U, personal_data_->GetCreditCards()[1]->use_count());
-  EXPECT_NE(kArbitraryTime, personal_data_->GetCreditCards()[1]->use_date());
-
-  // Having unmasked this card, usage stats should be 2 and
-  // kArbitraryTime.
-  EXPECT_EQ(2U, personal_data_->GetCreditCards()[2]->use_count());
-  EXPECT_EQ(kArbitraryTime, personal_data_->GetCreditCards()[2]->use_date());
-
-  // Change the Now() value for a second time.
-  test_clock.SetNow(kSomeLaterTime);
-
-  server_cards.back().set_guid(personal_data_->GetCreditCards()[2]->guid());
-  personal_data_->RecordUseOf(server_cards.back());
-
-  WaitForOnPersonalDataChanged();
-  ASSERT_EQ(3U, personal_data_->GetCreditCards().size());
-  EXPECT_EQ(2U, personal_data_->GetCreditCards()[0]->use_count());
-  EXPECT_EQ(kArbitraryTime, personal_data_->GetCreditCards()[0]->use_date());
-
-  EXPECT_EQ(1U, personal_data_->GetCreditCards()[1]->use_count());
-  EXPECT_NE(kArbitraryTime, personal_data_->GetCreditCards()[1]->use_date());
-
-  // The RecordUseOf call should have incremented the use_count to 3 and set the
-  // use_date to kSomeLaterTime.
-  EXPECT_EQ(3U, personal_data_->GetCreditCards()[2]->use_count());
-  EXPECT_EQ(kSomeLaterTime, personal_data_->GetCreditCards()[2]->use_date());
-
-  // Can record usage stats on masked cards.
-  server_cards[1].set_guid(personal_data_->GetCreditCards()[1]->guid());
-  personal_data_->RecordUseOf(server_cards[1]);
-
-  WaitForOnPersonalDataChanged();
-  ASSERT_EQ(3U, personal_data_->GetCreditCards().size());
-  EXPECT_EQ(2U, personal_data_->GetCreditCards()[1]->use_count());
-  EXPECT_EQ(kSomeLaterTime, personal_data_->GetCreditCards()[1]->use_date());
-
-  // Change Now()'s return value for a third time.
-  test_clock.SetNow(kMuchLaterTime);
-
-  // Upgrading to unmasked retains the usage stats (and increments them).
-  CreditCard* unmasked_card2 = &server_cards[1];
-  unmasked_card2->set_record_type(CreditCard::FULL_SERVER_CARD);
-  unmasked_card2->SetNumber(base::ASCIIToUTF16("5555555555554444"));
-  personal_data_->UpdateServerCreditCard(*unmasked_card2);
-
-  server_cards[1].set_guid(personal_data_->GetCreditCards()[1]->guid());
-  personal_data_->RecordUseOf(server_cards[1]);
-
-  WaitForOnPersonalDataChanged();
-  ASSERT_EQ(3U, personal_data_->GetCreditCards().size());
-  EXPECT_EQ(3U, personal_data_->GetCreditCards()[1]->use_count());
-  EXPECT_EQ(kMuchLaterTime, personal_data_->GetCreditCards()[1]->use_date());
-}
-<