diff --git a/DEPS b/DEPS
index 70cc7c04..0f099ba 100644
--- a/DEPS
+++ b/DEPS
@@ -109,7 +109,7 @@
   # 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': 'c3ce505d9ddfa8e8280a46060f46ef7b3befe3fb',
+  'v8_revision': '8dc93fc4fa1ec6d0501d982679ce93efc383d3b8',
   # 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.
@@ -117,7 +117,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': '5c8113d3c20f3bc089ea055af0ff02422b23e1fb',
+  'angle_revision': '89ef177f94753a91d7ec12aa58a5b06df2c12f0f',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling build tools
   # and whatever else without interference from each other.
@@ -129,7 +129,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': 'e7e454da8e382513b9e271bb3e0be3bd901bfbd9',
+  'pdfium_revision': 'b165ffb64e59998ec6d5f76c82bd2fe53734b3cd',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling openmax_dl
   # and whatever else without interference from each other.
@@ -165,7 +165,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '4c70b503747584fb55ad3cbfc8bebc976d5b1335',
+  'catapult_revision': '1af68170e543102499b0b9be0351a5ffa6e21da2',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -540,7 +540,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '0664f1210277d11b89af367f85fc363d0c9e73e7',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'a3bc29bcacf392cdba0021fee8bab5bd45665975',
       'condition': 'checkout_linux',
   },
 
@@ -913,7 +913,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' +  'dc44083eeb4031363cf46c87c78011d3f1476d0a',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' +  'f37ffaf9b1a14565b1b68a375beac233a170a457',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + 'ac0d98b5cee6c024b0cffeb4f8f45b6fc5ccdb78',
diff --git a/android_webview/browser/aw_autofill_client.cc b/android_webview/browser/aw_autofill_client.cc
index 6ed38bb..2c8b867 100644
--- a/android_webview/browser/aw_autofill_client.cc
+++ b/android_webview/browser/aw_autofill_client.cc
@@ -265,6 +265,14 @@
   NOTIMPLEMENTED();
 }
 
+void AwAutofillClient::ConfirmSaveAutofillProfile(
+    const autofill::AutofillProfile& profile,
+    base::OnceClosure callback) {
+  // Since there is no confirmation needed to save an Autofill Profile,
+  // running |callback| will proceed with saving |profile|.
+  std::move(callback).Run();
+}
+
 void AwAutofillClient::ConfirmSaveCreditCardLocally(
     const autofill::CreditCard& card,
     const base::Closure& callback) {
diff --git a/android_webview/browser/aw_autofill_client.h b/android_webview/browser/aw_autofill_client.h
index 5d687f23..3625e70 100644
--- a/android_webview/browser/aw_autofill_client.h
+++ b/android_webview/browser/aw_autofill_client.h
@@ -74,6 +74,8 @@
       UnmaskCardReason reason,
       base::WeakPtr<autofill::CardUnmaskDelegate> delegate) override;
   void OnUnmaskVerificationResult(PaymentsRpcResult result) override;
+  void ConfirmSaveAutofillProfile(const autofill::AutofillProfile& profile,
+                                  base::OnceClosure callback) override;
   void ConfirmSaveCreditCardLocally(const autofill::CreditCard& card,
                                     const base::Closure& callback) override;
   void ConfirmSaveCreditCardToCloud(
diff --git a/android_webview/browser/surfaces_instance.cc b/android_webview/browser/surfaces_instance.cc
index 84e75b5..cde8ba3 100644
--- a/android_webview/browser/surfaces_instance.cc
+++ b/android_webview/browser/surfaces_instance.cc
@@ -151,7 +151,7 @@
       viz::BeginFrameAck::CreateManualAckWithDamage();
   frame.render_pass_list.push_back(std::move(render_pass));
   frame.metadata.device_scale_factor = device_scale_factor;
-  frame.metadata.referenced_surfaces = child_ids_;
+  frame.metadata.referenced_surfaces = GetChildIdsRanges();
 
   if (!root_id_.is_valid() || viewport != surface_size_ ||
       device_scale_factor != device_scale_factor_) {
@@ -201,7 +201,7 @@
   // We draw synchronously, so acknowledge a manual BeginFrame.
   frame.metadata.begin_frame_ack =
       viz::BeginFrameAck::CreateManualAckWithDamage();
-  frame.metadata.referenced_surfaces = child_ids_;
+  frame.metadata.referenced_surfaces = GetChildIdsRanges();
   frame.metadata.device_scale_factor = device_scale_factor_;
   support_->SubmitCompositorFrame(root_id_, std::move(frame));
 }
@@ -211,6 +211,13 @@
   ReclaimResources(resources);
 }
 
+std::vector<viz::SurfaceRange> SurfacesInstance::GetChildIdsRanges() {
+  std::vector<viz::SurfaceRange> child_ranges;
+  for (const viz::SurfaceId& surface_id : child_ids_)
+    child_ranges.emplace_back(surface_id);
+  return child_ranges;
+}
+
 void SurfacesInstance::OnBeginFrame(const viz::BeginFrameArgs& args) {}
 
 void SurfacesInstance::ReclaimResources(
diff --git a/android_webview/browser/surfaces_instance.h b/android_webview/browser/surfaces_instance.h
index f3827d8..52d2a70 100644
--- a/android_webview/browser/surfaces_instance.h
+++ b/android_webview/browser/surfaces_instance.h
@@ -83,6 +83,8 @@
 
   void SetSolidColorRootFrame();
 
+  std::vector<viz::SurfaceRange> GetChildIdsRanges();
+
   viz::FrameSinkIdAllocator frame_sink_id_allocator_;
 
   viz::FrameSinkId frame_sink_id_;
diff --git a/android_webview/java/src/org/chromium/android_webview/AwContents.java b/android_webview/java/src/org/chromium/android_webview/AwContents.java
index b780333..778e2ee 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwContents.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwContents.java
@@ -1330,7 +1330,6 @@
 
             mWebContentsObserver.destroy();
             mWebContentsObserver = null;
-            mContentViewCore.destroy();
             mContentViewCore = null;
             mNativeAwContents = 0;
             mWebContents = null;
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 73f331e..dbf984a 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -104,6 +104,8 @@
     "assistant/assistant_interaction_controller.h",
     "assistant/assistant_notification_controller.cc",
     "assistant/assistant_notification_controller.h",
+    "assistant/assistant_screen_context_controller.cc",
+    "assistant/assistant_screen_context_controller.h",
     "assistant/assistant_ui_controller.cc",
     "assistant/assistant_ui_controller.h",
     "assistant/model/assistant_interaction_model.cc",
@@ -111,6 +113,9 @@
     "assistant/model/assistant_interaction_model_observer.h",
     "assistant/model/assistant_query.cc",
     "assistant/model/assistant_query.h",
+    "assistant/model/assistant_screen_context_model.cc",
+    "assistant/model/assistant_screen_context_model.h",
+    "assistant/model/assistant_screen_context_model_observer.h",
     "assistant/model/assistant_ui_element.h",
     "assistant/model/assistant_ui_model.cc",
     "assistant/model/assistant_ui_model.h",
@@ -1701,6 +1706,7 @@
     "app_menu/notification_menu_controller_unittest.cc",
     "app_menu/notification_menu_view_unittest.cc",
     "assistant/assistant_controller_unittest.cc",
+    "assistant/assistant_screen_context_controller_unittest.cc",
     "assistant/util/deep_link_util_unittest.cc",
     "autoclick/autoclick_unittest.cc",
     "cursor_unittest.cc",
diff --git a/ash/assistant/assistant_controller.cc b/ash/assistant/assistant_controller.cc
index dc35501..bf4380b 100644
--- a/ash/assistant/assistant_controller.cc
+++ b/ash/assistant/assistant_controller.cc
@@ -7,132 +7,49 @@
 #include "ash/assistant/assistant_controller_observer.h"
 #include "ash/assistant/assistant_interaction_controller.h"
 #include "ash/assistant/assistant_notification_controller.h"
+#include "ash/assistant/assistant_screen_context_controller.h"
 #include "ash/assistant/assistant_ui_controller.h"
 #include "ash/assistant/util/deep_link_util.h"
 #include "ash/new_window_controller.h"
-#include "ash/public/cpp/shell_window_ids.h"
-#include "ash/public/cpp/window_properties.h"
 #include "ash/session/session_controller.h"
 #include "ash/shell.h"
-#include "ash/wm/mru_window_tracker.h"
 #include "base/bind.h"
 #include "base/memory/scoped_refptr.h"
-#include "base/stl_util.h"
-#include "base/task_scheduler/post_task.h"
 #include "base/unguessable_token.h"
-#include "ui/aura/client/aura_constants.h"
-#include "ui/compositor/layer_tree_owner.h"
-#include "ui/gfx/codec/jpeg_codec.h"
-#include "ui/gfx/skbitmap_operations.h"
-#include "ui/snapshot/snapshot.h"
-#include "ui/snapshot/snapshot_aura.h"
-#include "ui/wm/core/window_util.h"
 
 namespace ash {
 
-namespace {
-
-// When screenshot's width or height is smaller than this size, we will stop
-// downsampling.
-constexpr int kScreenshotMaxWidth = 1366;
-constexpr int kScreenshotMaxHeight = 768;
-
-std::vector<uint8_t> DownsampleAndEncodeImage(gfx::Image image) {
-  std::vector<uint8_t> res;
-  // We'll downsample the screenshot to avoid exceeding max allowed size on
-  // assistant server side if we are taking screenshot from high-res screen.
-  gfx::JPEGCodec::Encode(
-      SkBitmapOperations::DownsampleByTwoUntilSize(
-          image.AsBitmap(), kScreenshotMaxWidth, kScreenshotMaxHeight),
-      100 /* quality */, &res);
-  return res;
-}
-
-void EncodeScreenshotAndRunCallback(
-    AssistantController::RequestScreenshotCallback callback,
-    std::unique_ptr<ui::LayerTreeOwner> layer_owner,
-    gfx::Image image) {
-  base::PostTaskWithTraitsAndReplyWithResult(
-      FROM_HERE,
-      base::TaskTraits{base::MayBlock(), base::TaskPriority::USER_BLOCKING},
-      base::BindOnce(&DownsampleAndEncodeImage, std::move(image)),
-      std::move(callback));
-}
-
-std::unique_ptr<ui::LayerTreeOwner> CreateLayerForAssistantSnapshot(
-    aura::Window* root_window) {
-  using LayerSet = base::flat_set<const ui::Layer*>;
-  LayerSet excluded_layers;
-  LayerSet blocked_layers;
-
-  aura::Window* overlay_container =
-      ash::Shell::GetContainer(root_window, kShellWindowId_OverlayContainer);
-  if (overlay_container)
-    excluded_layers.insert(overlay_container->layer());
-
-  MruWindowTracker::WindowList windows =
-      Shell::Get()->mru_window_tracker()->BuildMruWindowList();
-
-  for (aura::Window* window : windows) {
-    if (window->GetProperty(kBlockedForAssistantSnapshotKey))
-      blocked_layers.insert(window->layer());
-  }
-
-  return ::wm::RecreateLayersWithClosure(
-      root_window,
-      base::BindRepeating(
-          [](LayerSet blocked_layers, LayerSet excluded_layers,
-             ui::LayerOwner* owner) -> std::unique_ptr<ui::Layer> {
-            // Parent layer is excluded meaning that it's pointless to clone
-            // current child and all its descendants. This reduces the number
-            // of layers to create.
-            if (base::ContainsKey(blocked_layers, owner->layer()->parent()))
-              return nullptr;
-            if (base::ContainsKey(blocked_layers, owner->layer())) {
-              // Blocked layers are replaced with solid black layers so that
-              // they won't be included in the screenshot but still preserve
-              // the window stacking.
-              auto layer =
-                  std::make_unique<ui::Layer>(ui::LayerType::LAYER_SOLID_COLOR);
-              layer->SetBounds(owner->layer()->bounds());
-              layer->SetColor(SK_ColorBLACK);
-              return layer;
-            }
-            if (excluded_layers.count(owner->layer()))
-              return nullptr;
-            return owner->RecreateLayer();
-
-          },
-          std::move(blocked_layers), std::move(excluded_layers)));
-}
-
-}  // namespace
-
 AssistantController::AssistantController()
     : assistant_interaction_controller_(
           std::make_unique<AssistantInteractionController>(this)),
-      assistant_ui_controller_(std::make_unique<AssistantUiController>(this)),
       assistant_notification_controller_(
           std::make_unique<AssistantNotificationController>(this)),
+      assistant_screen_context_controller_(
+          std::make_unique<AssistantScreenContextController>()),
+      assistant_ui_controller_(std::make_unique<AssistantUiController>(this)),
       weak_factory_(this) {
   // Note that the sub-controllers have a circular dependency.
   // TODO(dmblack): Remove this circular dependency.
   assistant_interaction_controller_->SetAssistantUiController(
       assistant_ui_controller_.get());
+  assistant_screen_context_controller_->SetAssistantInteractionController(
+      assistant_interaction_controller_.get());
+  assistant_screen_context_controller_->SetAssistantUiController(
+      assistant_ui_controller_.get());
   assistant_ui_controller_->SetAssistantInteractionController(
       assistant_interaction_controller_.get());
-
-  assistant_ui_controller_->AddModelObserver(this);
-  Shell::Get()->highlighter_controller()->AddObserver(this);
+  assistant_ui_controller_->SetAssistantScreenContextController(
+      assistant_screen_context_controller_.get());
 }
 
 AssistantController::~AssistantController() {
-  Shell::Get()->highlighter_controller()->RemoveObserver(this);
-  assistant_ui_controller_->RemoveModelObserver(this);
-
   // Explicitly clean up the circular dependency in the sub-controllers.
   assistant_interaction_controller_->SetAssistantUiController(nullptr);
+  assistant_screen_context_controller_->SetAssistantInteractionController(
+      nullptr);
+  assistant_screen_context_controller_->SetAssistantUiController(nullptr);
   assistant_ui_controller_->SetAssistantInteractionController(nullptr);
+  assistant_ui_controller_->SetAssistantScreenContextController(nullptr);
 }
 
 void AssistantController::BindRequest(
@@ -155,8 +72,9 @@
 
   // Provide reference to sub-controllers.
   assistant_interaction_controller_->SetAssistant(assistant_.get());
-  assistant_ui_controller_->SetAssistant(assistant_.get());
   assistant_notification_controller_->SetAssistant(assistant_.get());
+  assistant_screen_context_controller_->SetAssistant(assistant_.get());
+  assistant_ui_controller_->SetAssistant(assistant_.get());
 }
 
 void AssistantController::SetAssistantImageDownloader(
@@ -177,33 +95,13 @@
   web_contents_manager_ = std::move(web_contents_manager);
 }
 
+// TODO(dmblack): Expose AssistantScreenContextController over mojo rather
+// than implementing RequestScreenshot here in AssistantController.
 void AssistantController::RequestScreenshot(
     const gfx::Rect& rect,
     RequestScreenshotCallback callback) {
-  // TODO(muyuanli): handle multi-display when assistant's behavior is defined.
-  aura::Window* root_window = Shell::GetPrimaryRootWindow();
-
-  std::unique_ptr<ui::LayerTreeOwner> layer_owner =
-      CreateLayerForAssistantSnapshot(root_window);
-
-  ui::Layer* root_layer = layer_owner->root();
-
-  gfx::Rect source_rect =
-      rect.IsEmpty() ? gfx::Rect(root_window->bounds().size()) : rect;
-
-  // The root layer might have a scaling transform applied (if the user has
-  // changed the UI scale via Ctrl-Shift-Plus/Minus).
-  // Clear the transform so that the snapshot is taken at 1:1 scale relative
-  // to screen pixels.
-  root_layer->SetTransform(gfx::Transform());
-  root_window->layer()->Add(root_layer);
-  root_window->layer()->StackAtBottom(root_layer);
-
-  ui::GrabLayerSnapshotAsync(
-      root_layer, source_rect,
-      base::BindRepeating(&EncodeScreenshotAndRunCallback,
-                          base::Passed(std::move(callback)),
-                          base::Passed(std::move(layer_owner))));
+  assistant_screen_context_controller_->RequestScreenshot(rect,
+                                                          std::move(callback));
 }
 
 void AssistantController::ManageWebContents(
@@ -262,29 +160,6 @@
   assistant_image_downloader_->Download(account_id, url, std::move(callback));
 }
 
-void AssistantController::OnUiVisibilityChanged(bool visible,
-                                                AssistantSource source) {
-  // We don't initiate a contextual query for caching if the UI is being hidden.
-  if (!visible)
-    return;
-
-  // We don't initiate a contextual query for caching if we are using stylus
-  // input modality because we will do so on metalayer session complete.
-  if (assistant_interaction_controller_->model()->input_modality() ==
-      InputModality::kStylus) {
-    return;
-  }
-
-  // TODO(dmblack): Activate the Assistant UI when the callback is run.
-  assistant_->RequestScreenContext(gfx::Rect(), base::DoNothing());
-}
-
-void AssistantController::OnHighlighterSelectionRecognized(
-    const gfx::Rect& rect) {
-  // TODO(dmblack): Activate the Assistant UI when the callback is run.
-  assistant_->RequestScreenContext(rect, base::DoNothing());
-}
-
 void AssistantController::OnOpenUrlFromTab(const GURL& url) {
   OpenUrl(url);
 }
@@ -306,10 +181,4 @@
   return weak_factory_.GetWeakPtr();
 }
 
-std::unique_ptr<ui::LayerTreeOwner>
-AssistantController::CreateLayerForAssistantSnapshotForTest() {
-  aura::Window* root_window = Shell::GetPrimaryRootWindow();
-  return CreateLayerForAssistantSnapshot(root_window);
-}
-
 }  // namespace ash
diff --git a/ash/assistant/assistant_controller.h b/ash/assistant/assistant_controller.h
index ff4fe29..c540337 100644
--- a/ash/assistant/assistant_controller.h
+++ b/ash/assistant/assistant_controller.h
@@ -10,8 +10,6 @@
 #include <vector>
 
 #include "ash/ash_export.h"
-#include "ash/assistant/model/assistant_ui_model_observer.h"
-#include "ash/highlighter/highlighter_controller.h"
 #include "ash/public/interfaces/assistant_controller.mojom.h"
 #include "ash/public/interfaces/assistant_image_downloader.mojom.h"
 #include "ash/public/interfaces/assistant_setup.mojom.h"
@@ -27,21 +25,16 @@
 class UnguessableToken;
 }  // namespace base
 
-namespace ui {
-class LayerTreeOwner;
-}  // namespace ui
-
 namespace ash {
 
 class AssistantControllerObserver;
 class AssistantInteractionController;
 class AssistantNotificationController;
+class AssistantScreenContextController;
 class AssistantUiController;
 
 class ASH_EXPORT AssistantController
     : public mojom::AssistantController,
-      public AssistantUiModelObserver,
-      public HighlighterController::Observer,
       public mojom::ManagedWebContentsOpenUrlDelegate {
  public:
   AssistantController();
@@ -79,6 +72,8 @@
 
   // mojom::AssistantController:
   // TODO(updowndota): Refactor Set() calls to use a factory pattern.
+  // TODO(dmblack): Expose RequestScreenshot(...) over mojo through
+  // AssistantScreenContextController.
   void SetAssistant(
       chromeos::assistant::mojom::AssistantPtr assistant) override;
   void SetAssistantImageDownloader(
@@ -89,12 +84,6 @@
   void RequestScreenshot(const gfx::Rect& rect,
                          RequestScreenshotCallback callback) override;
 
-  // AssistantUiModelObserver:
-  void OnUiVisibilityChanged(bool visible, AssistantSource source) override;
-
-  // HighlighterController::Observer:
-  void OnHighlighterSelectionRecognized(const gfx::Rect& rect) override;
-
   // mojom::ManagedWebContentsOpenUrlDelegate:
   void OnOpenUrlFromTab(const GURL& url) override;
 
@@ -107,6 +96,16 @@
     return assistant_interaction_controller_.get();
   }
 
+  AssistantNotificationController* notification_controller() {
+    DCHECK(assistant_notification_controller_);
+    return assistant_notification_controller_.get();
+  }
+
+  AssistantScreenContextController* screen_context_controller() {
+    DCHECK(assistant_screen_context_controller_);
+    return assistant_screen_context_controller_.get();
+  }
+
   AssistantUiController* ui_controller() {
     DCHECK(assistant_ui_controller_);
     return assistant_ui_controller_.get();
@@ -114,8 +113,6 @@
 
   base::WeakPtr<AssistantController> GetWeakPtr();
 
-  std::unique_ptr<ui::LayerTreeOwner> CreateLayerForAssistantSnapshotForTest();
-
  private:
   // The observer list should be initialized early so that sub-controllers may
   // register as observers during their construction.
@@ -137,11 +134,14 @@
   std::unique_ptr<AssistantInteractionController>
       assistant_interaction_controller_;
 
-  std::unique_ptr<AssistantUiController> assistant_ui_controller_;
-
   std::unique_ptr<AssistantNotificationController>
       assistant_notification_controller_;
 
+  std::unique_ptr<AssistantScreenContextController>
+      assistant_screen_context_controller_;
+
+  std::unique_ptr<AssistantUiController> assistant_ui_controller_;
+
   base::WeakPtrFactory<AssistantController> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(AssistantController);
diff --git a/ash/assistant/assistant_controller_unittest.cc b/ash/assistant/assistant_controller_unittest.cc
index e5e8e27..e97e785 100644
--- a/ash/assistant/assistant_controller_unittest.cc
+++ b/ash/assistant/assistant_controller_unittest.cc
@@ -7,8 +7,6 @@
 #include <memory>
 
 #include "ash/assistant/assistant_ui_controller.h"
-#include "ash/public/cpp/shell_window_ids.h"
-#include "ash/public/cpp/window_properties.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
 #include "base/macros.h"
@@ -16,10 +14,6 @@
 #include "chromeos/chromeos_switches.h"
 #include "chromeos/services/assistant/test_support/mock_assistant.h"
 #include "mojo/public/cpp/bindings/binding.h"
-#include "ui/aura/window.h"
-#include "ui/compositor/layer.h"
-#include "ui/compositor/layer_tree_owner.h"
-#include "ui/compositor/layer_type.h"
 
 namespace ash {
 
@@ -86,38 +80,4 @@
   DISALLOW_COPY_AND_ASSIGN(AssistantControllerTest);
 };
 
-// Verify that incognito windows are blocked in screenshot.
-TEST_F(AssistantControllerTest, Screenshot) {
-  std::unique_ptr<aura::Window> window1 = CreateToplevelTestWindow(
-      gfx::Rect(0, 0, 200, 200), kShellWindowId_DefaultContainer);
-  std::unique_ptr<aura::Window> window2 = CreateToplevelTestWindow(
-      gfx::Rect(30, 30, 100, 100), kShellWindowId_DefaultContainer);
-
-  ui::Layer* window1_layer = window1->layer();
-  ui::Layer* window2_layer = window2->layer();
-
-  window1->SetProperty(kBlockedForAssistantSnapshotKey, true);
-
-  std::unique_ptr<ui::LayerTreeOwner> layer_owner =
-      controller()->CreateLayerForAssistantSnapshotForTest();
-
-  // Test that windows marked as blocked for assistant snapshot is not included.
-  EXPECT_FALSE(FindLayerWithClosure(
-      layer_owner->root(),
-      base::BindRepeating(
-          [](ui::Layer* target, ui::Layer* layer) { return layer == target; },
-          window1_layer)));
-  EXPECT_TRUE(FindLayerWithClosure(
-      layer_owner->root(),
-      base::BindRepeating(
-          [](ui::Layer* target, ui::Layer* layer) { return layer == target; },
-          window2_layer)));
-
-  // Test that a solid black layer is inserted.
-  EXPECT_TRUE(FindLayerWithClosure(
-      layer_owner->root(), base::BindRepeating([](ui::Layer* layer) {
-        return layer->type() == ui::LayerType::LAYER_SOLID_COLOR;
-      })));
-}
-
 }  // namespace ash
diff --git a/ash/assistant/assistant_interaction_controller.h b/ash/assistant/assistant_interaction_controller.h
index 04348cf..f1d3517 100644
--- a/ash/assistant/assistant_interaction_controller.h
+++ b/ash/assistant/assistant_interaction_controller.h
@@ -104,7 +104,7 @@
   // Owned by AssistantController.
   chromeos::assistant::mojom::Assistant* assistant_ = nullptr;
 
-  // Owned by AssisantController.
+  // Owned by AssistantController.
   AssistantUiController* assistant_ui_controller_ = nullptr;
 
   mojo::Binding<chromeos::assistant::mojom::AssistantInteractionSubscriber>
diff --git a/ash/assistant/assistant_notification_controller.h b/ash/assistant/assistant_notification_controller.h
index 21d3a86..4f77613 100644
--- a/ash/assistant/assistant_notification_controller.h
+++ b/ash/assistant/assistant_notification_controller.h
@@ -35,7 +35,7 @@
   void OnRemoveNotification(const std::string& grouping_id) override;
 
  private:
-  AssistantController* assistant_controller_;
+  AssistantController* const assistant_controller_;  // Owned by Shell.
 
   mojo::Binding<chromeos::assistant::mojom::AssistantNotificationSubscriber>
       assistant_notification_subscriber_binding_;
diff --git a/ash/assistant/assistant_screen_context_controller.cc b/ash/assistant/assistant_screen_context_controller.cc
new file mode 100644
index 0000000..92a29c8
--- /dev/null
+++ b/ash/assistant/assistant_screen_context_controller.cc
@@ -0,0 +1,231 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/assistant/assistant_screen_context_controller.h"
+
+#include "ash/assistant/assistant_interaction_controller.h"
+#include "ash/assistant/assistant_ui_controller.h"
+#include "ash/public/cpp/shell_window_ids.h"
+#include "ash/public/cpp/window_properties.h"
+#include "ash/shell.h"
+#include "ash/wm/mru_window_tracker.h"
+#include "base/bind.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/stl_util.h"
+#include "base/task_scheduler/post_task.h"
+#include "ui/aura/client/aura_constants.h"
+#include "ui/compositor/layer_tree_owner.h"
+#include "ui/gfx/codec/jpeg_codec.h"
+#include "ui/gfx/skbitmap_operations.h"
+#include "ui/snapshot/snapshot.h"
+#include "ui/snapshot/snapshot_aura.h"
+#include "ui/wm/core/window_util.h"
+
+namespace ash {
+
+namespace {
+
+// When the screenshot's dimensions are smaller than this size, we will stop
+// downsampling.
+constexpr int kScreenshotMaxWidth = 1366;
+constexpr int kScreenshotMaxHeight = 768;
+
+std::vector<uint8_t> DownsampleAndEncodeImage(gfx::Image image) {
+  // We'll downsample the screenshot to avoid exceeding max allowed size on
+  // Assistant server side if we are taking screenshot from high-res screen.
+  std::vector<uint8_t> res;
+  gfx::JPEGCodec::Encode(
+      SkBitmapOperations::DownsampleByTwoUntilSize(
+          image.AsBitmap(), kScreenshotMaxWidth, kScreenshotMaxHeight),
+      /*quality=*/100, &res);
+  return res;
+}
+
+void EncodeScreenshotAndRunCallback(
+    mojom::AssistantController::RequestScreenshotCallback callback,
+    std::unique_ptr<ui::LayerTreeOwner> layer_owner,
+    gfx::Image image) {
+  base::PostTaskWithTraitsAndReplyWithResult(
+      FROM_HERE,
+      base::TaskTraits{base::MayBlock(), base::TaskPriority::USER_BLOCKING},
+      base::BindOnce(&DownsampleAndEncodeImage, std::move(image)),
+      std::move(callback));
+}
+
+std::unique_ptr<ui::LayerTreeOwner> CreateLayerForAssistantSnapshot(
+    aura::Window* root_window) {
+  using LayerSet = base::flat_set<const ui::Layer*>;
+  LayerSet excluded_layers;
+  LayerSet blocked_layers;
+
+  aura::Window* overlay_container =
+      ash::Shell::GetContainer(root_window, kShellWindowId_OverlayContainer);
+
+  if (overlay_container)
+    excluded_layers.insert(overlay_container->layer());
+
+  MruWindowTracker::WindowList windows =
+      Shell::Get()->mru_window_tracker()->BuildMruWindowList();
+
+  for (aura::Window* window : windows) {
+    if (window->GetProperty(kBlockedForAssistantSnapshotKey))
+      blocked_layers.insert(window->layer());
+  }
+
+  return ::wm::RecreateLayersWithClosure(
+      root_window,
+      base::BindRepeating(
+          [](LayerSet blocked_layers, LayerSet excluded_layers,
+             ui::LayerOwner* owner) -> std::unique_ptr<ui::Layer> {
+            // Parent layer is excluded meaning that it's pointless to clone
+            // current child and all its descendants. This reduces the number
+            // of layers to create.
+            if (base::ContainsKey(blocked_layers, owner->layer()->parent()))
+              return nullptr;
+
+            if (base::ContainsKey(blocked_layers, owner->layer())) {
+              // Blocked layers are replaced with solid black layers so that
+              // they won't be included in the screenshot but still preserve
+              // the window stacking.
+              auto layer =
+                  std::make_unique<ui::Layer>(ui::LayerType::LAYER_SOLID_COLOR);
+              layer->SetBounds(owner->layer()->bounds());
+              layer->SetColor(SK_ColorBLACK);
+              return layer;
+            }
+
+            if (excluded_layers.count(owner->layer()))
+              return nullptr;
+
+            return owner->RecreateLayer();
+          },
+          std::move(blocked_layers), std::move(excluded_layers)));
+}
+
+}  // namespace
+
+AssistantScreenContextController::AssistantScreenContextController()
+    : screen_context_request_factory_(this) {
+  Shell::Get()->highlighter_controller()->AddObserver(this);
+}
+
+AssistantScreenContextController::~AssistantScreenContextController() {
+  Shell::Get()->highlighter_controller()->RemoveObserver(this);
+}
+
+void AssistantScreenContextController::SetAssistant(
+    chromeos::assistant::mojom::Assistant* assistant) {
+  assistant_ = std::move(assistant);
+}
+
+void AssistantScreenContextController::SetAssistantInteractionController(
+    AssistantInteractionController* assistant_interaction_controller) {
+  assistant_interaction_controller_ = assistant_interaction_controller;
+}
+
+void AssistantScreenContextController::SetAssistantUiController(
+    AssistantUiController* assistant_ui_controller) {
+  if (assistant_ui_controller_)
+    assistant_ui_controller_->RemoveModelObserver(this);
+
+  assistant_ui_controller_ = assistant_ui_controller;
+
+  if (assistant_ui_controller_)
+    assistant_ui_controller_->AddModelObserver(this);
+}
+
+void AssistantScreenContextController::AddModelObserver(
+    AssistantScreenContextModelObserver* observer) {
+  assistant_screen_context_model_.AddObserver(observer);
+}
+
+void AssistantScreenContextController::RemoveModelObserver(
+    AssistantScreenContextModelObserver* observer) {
+  assistant_screen_context_model_.RemoveObserver(observer);
+}
+
+void AssistantScreenContextController::RequestScreenshot(
+    const gfx::Rect& rect,
+    mojom::AssistantController::RequestScreenshotCallback callback) {
+  // TODO(muyuanli): Handle multi-display when Assistant's behavior is defined.
+  aura::Window* root_window = Shell::GetPrimaryRootWindow();
+
+  std::unique_ptr<ui::LayerTreeOwner> layer_owner =
+      CreateLayerForAssistantSnapshot(root_window);
+
+  ui::Layer* root_layer = layer_owner->root();
+
+  gfx::Rect source_rect =
+      rect.IsEmpty() ? gfx::Rect(root_window->bounds().size()) : rect;
+
+  // The root layer might have a scaling transform applied (if the user has
+  // changed the UI scale via Ctrl-Shift-Plus/Minus). Clear the transform so
+  // that the snapshot is taken at 1:1 scale relative to screen pixels.
+  root_layer->SetTransform(gfx::Transform());
+  root_window->layer()->Add(root_layer);
+  root_window->layer()->StackAtBottom(root_layer);
+
+  ui::GrabLayerSnapshotAsync(
+      root_layer, source_rect,
+      base::BindRepeating(&EncodeScreenshotAndRunCallback,
+                          base::Passed(std::move(callback)),
+                          base::Passed(std::move(layer_owner))));
+}
+
+void AssistantScreenContextController::RequestScreenContext(
+    const gfx::Rect& rect) {
+  // Abort any request in progress and update request state.
+  screen_context_request_factory_.InvalidateWeakPtrs();
+  assistant_screen_context_model_.SetRequestState(
+      ScreenContextRequestState::kInProgress);
+
+  // Request screen context for the entire screen.
+  assistant_->RequestScreenContext(
+      gfx::Rect(),
+      base::BindOnce(
+          &AssistantScreenContextController::OnScreenContextRequestFinished,
+          screen_context_request_factory_.GetWeakPtr()));
+}
+
+void AssistantScreenContextController::OnUiVisibilityChanged(
+    bool visible,
+    AssistantSource source) {
+  // We don't initiate a contextual query for caching if the UI is being hidden.
+  // Instead, we abort any requests in progress and reset state.
+  if (!visible) {
+    screen_context_request_factory_.InvalidateWeakPtrs();
+    assistant_screen_context_model_.SetRequestState(
+        ScreenContextRequestState::kIdle);
+    return;
+  }
+
+  // We don't initiate a contextual query for caching if we are using stylus
+  // input modality because we will do so on metalayer session complete.
+  if (assistant_interaction_controller_->model()->input_modality() ==
+      InputModality::kStylus) {
+    return;
+  }
+
+  // Request screen context for the entire screen.
+  RequestScreenContext(gfx::Rect());
+}
+
+void AssistantScreenContextController::OnHighlighterSelectionRecognized(
+    const gfx::Rect& rect) {
+  // Request screen context for the selected region.
+  RequestScreenContext(rect);
+}
+
+void AssistantScreenContextController::OnScreenContextRequestFinished() {
+  assistant_screen_context_model_.SetRequestState(
+      ScreenContextRequestState::kIdle);
+}
+
+std::unique_ptr<ui::LayerTreeOwner>
+AssistantScreenContextController::CreateLayerForAssistantSnapshotForTest() {
+  aura::Window* root_window = Shell::GetPrimaryRootWindow();
+  return CreateLayerForAssistantSnapshot(root_window);
+}
+
+}  // namespace ash
diff --git a/ash/assistant/assistant_screen_context_controller.h b/ash/assistant/assistant_screen_context_controller.h
new file mode 100644
index 0000000..27852c8
--- /dev/null
+++ b/ash/assistant/assistant_screen_context_controller.h
@@ -0,0 +1,99 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_ASSISTANT_ASSISTANT_SCREEN_CONTEXT_CONTROLLER_H_
+#define ASH_ASSISTANT_ASSISTANT_SCREEN_CONTEXT_CONTROLLER_H_
+
+#include <memory>
+
+#include "ash/ash_export.h"
+#include "ash/assistant/model/assistant_screen_context_model.h"
+#include "ash/assistant/model/assistant_ui_model_observer.h"
+#include "ash/highlighter/highlighter_controller.h"
+#include "ash/public/interfaces/assistant_controller.mojom.h"
+#include "base/macros.h"
+#include "base/observer_list.h"
+#include "chromeos/services/assistant/public/mojom/assistant.mojom.h"
+#include "ui/gfx/geometry/rect.h"
+
+namespace ui {
+class LayerTreeOwner;
+}  // namespace ui
+
+namespace ash {
+
+class AssistantInteractionController;
+class AssistantScreenContextModelObserver;
+class AssistantUiController;
+
+class ASH_EXPORT AssistantScreenContextController
+    : public AssistantUiModelObserver,
+      public HighlighterController::Observer {
+ public:
+  AssistantScreenContextController();
+  ~AssistantScreenContextController() override;
+
+  // Provides a pointer to the |assistant| owned by AssistantController.
+  void SetAssistant(chromeos::assistant::mojom::Assistant* assistant);
+
+  // Provides a pointer to the |assistant_interaction_controller| owned by
+  // AssistantController.
+  void SetAssistantInteractionController(
+      AssistantInteractionController* assistant_interaction_controller);
+
+  // Provides a pointer to the |assistant_ui_controller| owned by
+  // AssistantController.
+  void SetAssistantUiController(AssistantUiController* assistant_ui_controller);
+
+  // Returns a reference to the underlying model.
+  const AssistantScreenContextModel* model() const {
+    return &assistant_screen_context_model_;
+  }
+
+  // Adds/removes the specified screen context model |observer|.
+  void AddModelObserver(AssistantScreenContextModelObserver* observer);
+  void RemoveModelObserver(AssistantScreenContextModelObserver* observer);
+
+  // Requests a screenshot for the region defined by |rect|. If an empty rect is
+  // supplied, the entire screen is captured. Upon screenshot completion, the
+  // specified |callback| is run.
+  void RequestScreenshot(
+      const gfx::Rect& rect,
+      mojom::AssistantController::RequestScreenshotCallback callback);
+
+  // AssistantUiModelObserver:
+  void OnUiVisibilityChanged(bool visible, AssistantSource source) override;
+
+  // HighlighterController::Observer:
+  void OnHighlighterSelectionRecognized(const gfx::Rect& rect) override;
+
+  // Invoked on screen context request finished event.
+  void OnScreenContextRequestFinished();
+
+  std::unique_ptr<ui::LayerTreeOwner> CreateLayerForAssistantSnapshotForTest();
+
+ private:
+  void RequestScreenContext(const gfx::Rect& rect);
+
+  // Owned by AssistantController.
+  chromeos::assistant::mojom::Assistant* assistant_ = nullptr;
+
+  // Owned by AssistantController.
+  AssistantInteractionController* assistant_interaction_controller_ = nullptr;
+
+  // Owned by AssistantController.
+  AssistantUiController* assistant_ui_controller_ = nullptr;
+
+  AssistantScreenContextModel assistant_screen_context_model_;
+
+  // Weak pointer factory used for screen context requests.
+  base::WeakPtrFactory<AssistantScreenContextController>
+      screen_context_request_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(AssistantScreenContextController);
+};
+
+}  // namespace ash
+
+#endif  // ASH_ASSISTANT_ASSISTANT_SCREEN_CONTEXT_CONTROLLER_H_
diff --git a/ash/assistant/assistant_screen_context_controller_unittest.cc b/ash/assistant/assistant_screen_context_controller_unittest.cc
new file mode 100644
index 0000000..3f5282e
--- /dev/null
+++ b/ash/assistant/assistant_screen_context_controller_unittest.cc
@@ -0,0 +1,102 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/assistant/assistant_screen_context_controller.h"
+
+#include <memory>
+
+#include "ash/assistant/assistant_controller.h"
+#include "ash/public/cpp/shell_window_ids.h"
+#include "ash/public/cpp/window_properties.h"
+#include "ash/shell.h"
+#include "ash/test/ash_test_base.h"
+#include "base/macros.h"
+#include "base/test/scoped_feature_list.h"
+#include "chromeos/chromeos_switches.h"
+#include "ui/aura/window.h"
+#include "ui/compositor/layer.h"
+#include "ui/compositor/layer_tree_owner.h"
+#include "ui/compositor/layer_type.h"
+
+namespace ash {
+
+namespace {
+
+ui::Layer* FindLayerWithClosure(
+    ui::Layer* root,
+    const base::RepeatingCallback<bool(ui::Layer*)>& callback) {
+  if (callback.Run(root))
+    return root;
+  for (ui::Layer* child : root->children()) {
+    ui::Layer* result = FindLayerWithClosure(child, callback);
+    if (result)
+      return result;
+  }
+  return nullptr;
+}
+
+}  // namespace
+
+class AssistantScreenContextControllerTest : public AshTestBase {
+ protected:
+  AssistantScreenContextControllerTest() = default;
+  ~AssistantScreenContextControllerTest() override = default;
+
+  void SetUp() override {
+    scoped_feature_list_.InitAndEnableFeature(
+        chromeos::switches::kAssistantFeature);
+    ASSERT_TRUE(chromeos::switches::IsAssistantEnabled());
+
+    AshTestBase::SetUp();
+
+    controller_ =
+        Shell::Get()->assistant_controller()->screen_context_controller();
+    DCHECK(controller_);
+  }
+
+  ash::AssistantScreenContextController* controller() { return controller_; }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+
+  AssistantScreenContextController* controller_ = nullptr;
+
+  DISALLOW_COPY_AND_ASSIGN(AssistantScreenContextControllerTest);
+};
+
+// Verify that incognito windows are blocked in screenshot.
+TEST_F(AssistantScreenContextControllerTest, Screenshot) {
+  std::unique_ptr<aura::Window> window1 = CreateToplevelTestWindow(
+      gfx::Rect(0, 0, 200, 200), kShellWindowId_DefaultContainer);
+  std::unique_ptr<aura::Window> window2 = CreateToplevelTestWindow(
+      gfx::Rect(30, 30, 100, 100), kShellWindowId_DefaultContainer);
+
+  ui::Layer* window1_layer = window1->layer();
+  ui::Layer* window2_layer = window2->layer();
+
+  window1->SetProperty(kBlockedForAssistantSnapshotKey, true);
+
+  std::unique_ptr<ui::LayerTreeOwner> layer_owner =
+      controller()->CreateLayerForAssistantSnapshotForTest();
+
+  // Test that windows marked as blocked for assistant snapshot is not included.
+  EXPECT_FALSE(FindLayerWithClosure(
+      layer_owner->root(),
+      base::BindRepeating(
+          [](ui::Layer* target, ui::Layer* layer) { return layer == target; },
+          window1_layer)));
+  EXPECT_TRUE(FindLayerWithClosure(
+      layer_owner->root(),
+      base::BindRepeating(
+          [](ui::Layer* target, ui::Layer* layer) { return layer == target; },
+          window2_layer)));
+
+  // Test that a solid black layer is inserted.
+  EXPECT_TRUE(FindLayerWithClosure(
+      layer_owner->root(), base::BindRepeating([](ui::Layer* layer) {
+        return layer->type() == ui::LayerType::LAYER_SOLID_COLOR;
+      })));
+}
+
+}  // namespace ash
diff --git a/ash/assistant/assistant_ui_controller.cc b/ash/assistant/assistant_ui_controller.cc
index 7813f0a..d9b94da 100644
--- a/ash/assistant/assistant_ui_controller.cc
+++ b/ash/assistant/assistant_ui_controller.cc
@@ -6,6 +6,7 @@
 
 #include "ash/assistant/assistant_controller.h"
 #include "ash/assistant/assistant_interaction_controller.h"
+#include "ash/assistant/assistant_screen_context_controller.h"
 #include "ash/assistant/ui/assistant_container_view.h"
 #include "ash/assistant/util/deep_link_util.h"
 #include "ash/public/interfaces/assistant_setup.mojom.h"
@@ -71,6 +72,17 @@
     assistant_interaction_controller_->AddModelObserver(this);
 }
 
+void AssistantUiController::SetAssistantScreenContextController(
+    AssistantScreenContextController* assistant_screen_context_controller) {
+  if (assistant_screen_context_controller_)
+    assistant_screen_context_controller_->RemoveModelObserver(this);
+
+  assistant_screen_context_controller_ = assistant_screen_context_controller;
+
+  if (assistant_screen_context_controller_)
+    assistant_screen_context_controller_->AddModelObserver(this);
+}
+
 void AssistantUiController::SetAssistantSetup(
     mojom::AssistantSetup* assistant_setup) {
   assistant_setup_ = assistant_setup;
@@ -127,6 +139,17 @@
   UpdateUiMode();
 }
 
+void AssistantUiController::OnScreenContextRequestStateChanged(
+    ScreenContextRequestState request_state) {
+  if (!assistant_ui_model_.visible())
+    return;
+
+  // Once screen context request state has become idle, it is safe to activate
+  // the Assistant widget without causing complications.
+  if (request_state == ScreenContextRequestState::kIdle)
+    container_view_->GetWidget()->Activate();
+}
+
 bool AssistantUiController::OnCaptionButtonPressed(CaptionButtonId id) {
   switch (id) {
     case CaptionButtonId::kMinimize:
@@ -211,9 +234,10 @@
     container_view_->GetWidget()->AddObserver(this);
   }
 
-  // TODO(dmblack): Initially show UI as inactive and only activate after the
-  // screen context has been sent to the server.
-  container_view_->GetWidget()->Show();
+  // Note that we initially show the Assistant widget as inactive. This is
+  // necessary due to limitations imposed by retrieving screen context. Once we
+  // have finished retrieving screen context, the Assistant widget is activated.
+  container_view_->GetWidget()->ShowInactive();
   assistant_ui_model_.SetVisible(true, source);
 }
 
diff --git a/ash/assistant/assistant_ui_controller.h b/ash/assistant/assistant_ui_controller.h
index d006f64..9b2d580e 100644
--- a/ash/assistant/assistant_ui_controller.h
+++ b/ash/assistant/assistant_ui_controller.h
@@ -8,6 +8,7 @@
 #include "ash/ash_export.h"
 #include "ash/assistant/assistant_controller_observer.h"
 #include "ash/assistant/model/assistant_interaction_model_observer.h"
+#include "ash/assistant/model/assistant_screen_context_model_observer.h"
 #include "ash/assistant/model/assistant_ui_model.h"
 #include "ash/assistant/model/assistant_ui_model_observer.h"
 #include "ash/assistant/ui/caption_bar.h"
@@ -33,6 +34,7 @@
 class AssistantContainerView;
 class AssistantController;
 class AssistantInteractionController;
+class AssistantScreenContextController;
 
 namespace mojom {
 class AssistantSetup;
@@ -42,6 +44,7 @@
     : public views::WidgetObserver,
       public AssistantControllerObserver,
       public AssistantInteractionModelObserver,
+      public AssistantScreenContextModelObserver,
       public AssistantUiModelObserver,
       public CaptionBarDelegate,
       public DialogPlateObserver,
@@ -58,6 +61,11 @@
   void SetAssistantInteractionController(
       AssistantInteractionController* assistant_interaction_controller);
 
+  // Provides a pointer to the |assistant_screen_context_controller| owned by
+  // AssistantController.
+  void SetAssistantScreenContextController(
+      AssistantScreenContextController* assistant_screen_context_controller);
+
   // Provides a pointer to the |assistant_setup| owned by AssistantController.
   void SetAssistantSetup(mojom::AssistantSetup* assistant_setup);
 
@@ -78,6 +86,10 @@
   void OnInteractionStateChanged(InteractionState interaction_state) override;
   void OnMicStateChanged(MicState mic_state) override;
 
+  // AssistantScreenContextModelObserver:
+  void OnScreenContextRequestStateChanged(
+      ScreenContextRequestState request_state) override;
+
   // CaptionBarDelegate:
   bool OnCaptionButtonPressed(CaptionButtonId id) override;
 
@@ -112,6 +124,10 @@
   AssistantInteractionController* assistant_interaction_controller_ = nullptr;
 
   // Owned by AssistantController.
+  AssistantScreenContextController* assistant_screen_context_controller_ =
+      nullptr;
+
+  // Owned by AssistantController.
   mojom::AssistantSetup* assistant_setup_ = nullptr;
 
   AssistantUiModel assistant_ui_model_;
diff --git a/ash/assistant/model/assistant_screen_context_model.cc b/ash/assistant/model/assistant_screen_context_model.cc
new file mode 100644
index 0000000..ee9daa7
--- /dev/null
+++ b/ash/assistant/model/assistant_screen_context_model.cc
@@ -0,0 +1,39 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/assistant/model/assistant_screen_context_model.h"
+
+#include "ash/assistant/model/assistant_screen_context_model_observer.h"
+
+namespace ash {
+
+AssistantScreenContextModel::AssistantScreenContextModel() = default;
+
+AssistantScreenContextModel::~AssistantScreenContextModel() = default;
+
+void AssistantScreenContextModel::AddObserver(
+    AssistantScreenContextModelObserver* observer) {
+  observers_.AddObserver(observer);
+}
+
+void AssistantScreenContextModel::RemoveObserver(
+    AssistantScreenContextModelObserver* observer) {
+  observers_.RemoveObserver(observer);
+}
+
+void AssistantScreenContextModel::SetRequestState(
+    ScreenContextRequestState request_state) {
+  if (request_state == request_state_)
+    return;
+
+  request_state_ = request_state;
+  NotifyRequestStateChanged();
+}
+
+void AssistantScreenContextModel::NotifyRequestStateChanged() {
+  for (AssistantScreenContextModelObserver& observer : observers_)
+    observer.OnScreenContextRequestStateChanged(request_state_);
+}
+
+}  // namespace ash
diff --git a/ash/assistant/model/assistant_screen_context_model.h b/ash/assistant/model/assistant_screen_context_model.h
new file mode 100644
index 0000000..93e27af
--- /dev/null
+++ b/ash/assistant/model/assistant_screen_context_model.h
@@ -0,0 +1,45 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_ASSISTANT_MODEL_ASSISTANT_SCREEN_CONTEXT_MODEL_H_
+#define ASH_ASSISTANT_MODEL_ASSISTANT_SCREEN_CONTEXT_MODEL_H_
+
+#include "base/macros.h"
+#include "base/observer_list.h"
+
+namespace ash {
+
+class AssistantScreenContextModelObserver;
+
+// Enumeration of screen context request states.
+enum class ScreenContextRequestState {
+  kIdle,
+  kInProgress,
+};
+
+class AssistantScreenContextModel {
+ public:
+  AssistantScreenContextModel();
+  ~AssistantScreenContextModel();
+
+  // Adds/removes the specified screen context model |observer|.
+  void AddObserver(AssistantScreenContextModelObserver* observer);
+  void RemoveObserver(AssistantScreenContextModelObserver* observer);
+
+  // Sets the screen context request state.
+  void SetRequestState(ScreenContextRequestState request_state);
+
+ private:
+  void NotifyRequestStateChanged();
+
+  ScreenContextRequestState request_state_ = ScreenContextRequestState::kIdle;
+
+  base::ObserverList<AssistantScreenContextModelObserver> observers_;
+
+  DISALLOW_COPY_AND_ASSIGN(AssistantScreenContextModel);
+};
+
+}  // namespace ash
+
+#endif  // ASH_ASSISTANT_MODEL_ASSISTANT_SCREEN_CONTEXT_MODEL_H_
diff --git a/ash/assistant/model/assistant_screen_context_model_observer.h b/ash/assistant/model/assistant_screen_context_model_observer.h
new file mode 100644
index 0000000..2b814b6
--- /dev/null
+++ b/ash/assistant/model/assistant_screen_context_model_observer.h
@@ -0,0 +1,29 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_ASSISTANT_MODEL_ASSISTANT_SCREEN_CONTEXT_MODEL_OBSERVER_H_
+#define ASH_ASSISTANT_MODEL_ASSISTANT_SCREEN_CONTEXT_MODEL_OBSERVER_H_
+
+#include "base/macros.h"
+
+namespace ash {
+
+enum class ScreenContextRequestState;
+
+class AssistantScreenContextModelObserver {
+ public:
+  // Invoked when the screen context request state is changed.
+  virtual void OnScreenContextRequestStateChanged(
+      ScreenContextRequestState request_state) {}
+
+ protected:
+  AssistantScreenContextModelObserver() = default;
+  virtual ~AssistantScreenContextModelObserver() = default;
+
+  DISALLOW_COPY_AND_ASSIGN(AssistantScreenContextModelObserver);
+};
+
+}  // namespace ash
+
+#endif  // ASH_ASSISTANT_MODEL_ASSISTANT_SCREEN_CONTEXT_MODEL_OBSERVER_H_
diff --git a/ash/drag_drop/drag_drop_controller_unittest.cc b/ash/drag_drop/drag_drop_controller_unittest.cc
index 1c888d1..a3176d4 100644
--- a/ash/drag_drop/drag_drop_controller_unittest.cc
+++ b/ash/drag_drop/drag_drop_controller_unittest.cc
@@ -6,7 +6,6 @@
 
 #include "ash/drag_drop/drag_drop_tracker.h"
 #include "ash/drag_drop/drag_image_view.h"
-#include "ash/public/cpp/config.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
 #include "base/command_line.h"
@@ -360,9 +359,6 @@
 };
 
 TEST_F(DragDropControllerTest, DragDropInSingleViewTest) {
-  if (Shell::GetAshConfig() == Config::MASH_DEPRECATED)
-    return;  // DragDropController not created in mash.
-
   std::unique_ptr<views::Widget> widget = CreateTestWidget();
   DragTestView* drag_view = new DragTestView;
   AddViewToWidgetAndResize(widget.get(), drag_view);
@@ -404,9 +400,6 @@
 }
 
 TEST_F(DragDropControllerTest, DragDropWithZeroDragUpdates) {
-  if (Shell::GetAshConfig() == Config::MASH_DEPRECATED)
-    return;  // DragDropController not created in mash.
-
   std::unique_ptr<views::Widget> widget = CreateTestWidget();
   DragTestView* drag_view = new DragTestView;
   AddViewToWidgetAndResize(widget.get(), drag_view);
@@ -445,9 +438,6 @@
 }
 
 TEST_F(DragDropControllerTest, DragDropInMultipleViewsSingleWidgetTest) {
-  if (Shell::GetAshConfig() == Config::MASH_DEPRECATED)
-    return;  // DragDropController not created in mash.
-
   std::unique_ptr<views::Widget> widget = CreateFramelessWidget();
   DragTestView* drag_view1 = new DragTestView;
   AddViewToWidgetAndResize(widget.get(), drag_view1);
@@ -503,9 +493,6 @@
 }
 
 TEST_F(DragDropControllerTest, DragDropInMultipleViewsMultipleWidgetsTest) {
-  if (Shell::GetAshConfig() == Config::MASH_DEPRECATED)
-    return;  // DragDropController not created in mash.
-
   std::unique_ptr<views::Widget> widget1 = CreateFramelessWidget();
   DragTestView* drag_view1 = new DragTestView;
   AddViewToWidgetAndResize(widget1.get(), drag_view1);
@@ -566,9 +553,6 @@
 }
 
 TEST_F(DragDropControllerTest, ViewRemovedWhileInDragDropTest) {
-  if (Shell::GetAshConfig() == Config::MASH_DEPRECATED)
-    return;  // DragDropController not created in mash.
-
   std::unique_ptr<views::Widget> widget = CreateTestWidget();
   std::unique_ptr<DragTestView> drag_view(new DragTestView);
   AddViewToWidgetAndResize(widget.get(), drag_view.get());
@@ -622,9 +606,6 @@
 }
 
 TEST_F(DragDropControllerTest, DragLeavesClipboardAloneTest) {
-  if (Shell::GetAshConfig() == Config::MASH_DEPRECATED)
-    return;  // DragDropController not created in mash.
-
   ui::Clipboard* cb = ui::Clipboard::GetForCurrentThread();
   std::string clip_str("I am on the clipboard");
   {
@@ -663,9 +644,6 @@
 }
 
 TEST_F(DragDropControllerTest, WindowDestroyedDuringDragDrop) {
-  if (Shell::GetAshConfig() == Config::MASH_DEPRECATED)
-    return;  // DragDropController not created in mash.
-
   std::unique_ptr<views::Widget> widget = CreateTestWidget();
   DragTestView* drag_view = new DragTestView;
   AddViewToWidgetAndResize(widget.get(), drag_view);
@@ -712,9 +690,6 @@
 }
 
 TEST_F(DragDropControllerTest, SyntheticEventsDuringDragDrop) {
-  if (Shell::GetAshConfig() == Config::MASH_DEPRECATED)
-    return;  // DragDropController not created in mash.
-
   std::unique_ptr<views::Widget> widget = CreateTestWidget();
   DragTestView* drag_view = new DragTestView;
   AddViewToWidgetAndResize(widget.get(), drag_view);
@@ -765,9 +740,6 @@
 }
 
 TEST_F(DragDropControllerTest, PressingEscapeCancelsDragDrop) {
-  if (Shell::GetAshConfig() == Config::MASH_DEPRECATED)
-    return;  // DragDropController not created in mash.
-
   std::unique_ptr<views::Widget> widget = CreateTestWidget();
   DragTestView* drag_view = new DragTestView;
   AddViewToWidgetAndResize(widget.get(), drag_view);
@@ -810,9 +782,6 @@
 }
 
 TEST_F(DragDropControllerTest, CaptureLostCancelsDragDrop) {
-  if (Shell::GetAshConfig() == Config::MASH_DEPRECATED)
-    return;  // DragDropController not created in mash.
-
   std::unique_ptr<views::Widget> widget = CreateTestWidget();
   DragTestView* drag_view = new DragTestView;
   AddViewToWidgetAndResize(widget.get(), drag_view);
@@ -861,9 +830,6 @@
 }
 
 TEST_F(DragDropControllerTest, TouchDragDropInMultipleWindows) {
-  if (Shell::GetAshConfig() == Config::MASH_DEPRECATED)
-    return;  // DragDropController not created in mash.
-
   base::CommandLine::ForCurrentProcess()->AppendSwitch(
       switches::kEnableTouchDragDrop);
   std::unique_ptr<views::Widget> widget1 = CreateFramelessWidget();
@@ -926,9 +892,6 @@
 }
 
 TEST_F(DragDropControllerTest, TouchDragDropCancelsOnLongTap) {
-  if (Shell::GetAshConfig() == Config::MASH_DEPRECATED)
-    return;  // DragDropController not created in mash.
-
   base::CommandLine::ForCurrentProcess()->AppendSwitch(
       switches::kEnableTouchDragDrop);
   std::unique_ptr<views::Widget> widget = CreateTestWidget();
@@ -955,9 +918,6 @@
 }
 
 TEST_F(DragDropControllerTest, TouchDragDropLongTapGestureIsForwarded) {
-  if (Shell::GetAshConfig() == Config::MASH_DEPRECATED)
-    return;  // DragDropController not created in mash.
-
   base::CommandLine::ForCurrentProcess()->AppendSwitch(
       switches::kEnableTouchDragDrop);
   std::unique_ptr<views::Widget> widget = CreateTestWidget();
@@ -1001,9 +961,6 @@
 // Verifies the drag image moves back to the position where drag is started
 // across displays when drag is cancelled.
 TEST_F(DragDropControllerTest, DragCancelAcrossDisplays) {
-  if (Shell::GetAshConfig() == Config::MASH_DEPRECATED)
-    return;  // DragDropController not created in mash.
-
   UpdateDisplay("400x400,400x400");
   aura::Window::Windows root_windows = Shell::Get()->GetAllRootWindows();
   for (aura::Window::Windows::iterator iter = root_windows.begin();
@@ -1081,9 +1038,6 @@
 
 // Verifies that a drag is aborted if a display is disconnected during the drag.
 TEST_F(DragDropControllerTest, DragCancelOnDisplayDisconnect) {
-  if (Shell::GetAshConfig() == Config::MASH_DEPRECATED)
-    return;  // DragDropController not created in mash.
-
   UpdateDisplay("400x400,400x400");
   for (aura::Window* root : Shell::Get()->GetAllRootWindows()) {
     aura::client::SetDragDropClient(root, drag_drop_controller_.get());
@@ -1121,9 +1075,6 @@
 }
 
 TEST_F(DragDropControllerTest, TouchDragDropCompletesOnFling) {
-  if (Shell::GetAshConfig() == Config::MASH_DEPRECATED)
-    return;  // DragDropController not created in mash.
-
   base::CommandLine::ForCurrentProcess()->AppendSwitch(
       switches::kEnableTouchDragDrop);
   ui::GestureConfiguration::GetInstance()
@@ -1179,9 +1130,6 @@
 }
 
 TEST_F(DragDropControllerTest, DragStartedAndEndedEvents) {
-  if (Shell::GetAshConfig() == Config::MASH_DEPRECATED)
-    return;  // DragDropController not created in mash.
-
   TestObserver observer;
   drag_drop_controller_->AddObserver(&observer);
 
@@ -1209,9 +1157,6 @@
 }
 
 TEST_F(DragDropControllerTest, EventTarget) {
-  if (Shell::GetAshConfig() == Config::MASH_DEPRECATED)
-    return;  // DragDropController not created in mash.
-
   std::unique_ptr<aura::Window> window(CreateTestWindowInShellWithDelegate(
       aura::test::TestWindowDelegate::CreateSelfDestroyingDelegate(), -1,
       gfx::Rect(0, 0, 100, 100)));
diff --git a/ash/public/interfaces/voice_interaction_controller.mojom b/ash/public/interfaces/voice_interaction_controller.mojom
index 78e1e7b8..5e90bd4 100644
--- a/ash/public/interfaces/voice_interaction_controller.mojom
+++ b/ash/public/interfaces/voice_interaction_controller.mojom
@@ -55,6 +55,9 @@
   // the "context" (text and graphic content that is currently on screen).
   OnVoiceInteractionContextEnabled(bool enabled);
 
+  // Called when hotword listening is enabled/disabled.
+  OnVoiceInteractionHotwordEnabled(bool enabled);
+
   // Called when voice interaction setup flow completed.
   OnVoiceInteractionSetupCompleted(bool completed);
 
@@ -76,6 +79,9 @@
   // interaction session.
   NotifyContextEnabled(bool enabled);
 
+  // Called when the hotword listening is enabled/disabled.
+  NotifyHotwordEnabled(bool enabled);
+
   // Called when the voice interaction setup complete status is changed.
   NotifySetupCompleted(bool completed);
 
@@ -89,6 +95,9 @@
   // Return the voice interaction setup complete status.
   IsSetupCompleted() => (bool completed);
 
+  // Return the voice interaction hotword listening status.
+  IsHotwordEnabled() => (bool enabled);
+
   // Add an observer.
   AddObserver(VoiceInteractionObserver observer);
 };
diff --git a/ash/shelf/app_list_button.h b/ash/shelf/app_list_button.h
index d3efb8d..7fd6ea6 100644
--- a/ash/shelf/app_list_button.h
+++ b/ash/shelf/app_list_button.h
@@ -74,6 +74,7 @@
       mojom::VoiceInteractionState state) override;
   void OnVoiceInteractionSettingsEnabled(bool enabled) override;
   void OnVoiceInteractionContextEnabled(bool enabled) override {}
+  void OnVoiceInteractionHotwordEnabled(bool enabled) override {}
   void OnVoiceInteractionSetupCompleted(bool completed) override;
   void OnAssistantFeatureAllowedChanged(
       mojom::AssistantAllowedState state) override {}
diff --git a/ash/shell.cc b/ash/shell.cc
index f1f8d0b..608bd946 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -1179,9 +1179,7 @@
   if (!::features::IsAshInBrowserProcess())
     client_image_registry_ = std::make_unique<ClientImageRegistry>();
 
-  // In mash drag and drop is handled by mus.
-  if (config != Config::MASH_DEPRECATED)
-    drag_drop_controller_ = std::make_unique<DragDropController>();
+  drag_drop_controller_ = std::make_unique<DragDropController>();
 
   // |screenshot_controller_| needs to be created (and prepended as a
   // pre-target handler) at this point, because |mouse_cursor_filter_| needs to
@@ -1408,12 +1406,7 @@
   ::wm::SetActivationClient(root_window, focus_controller_.get());
   root_window->AddPreTargetHandler(focus_controller_.get());
   aura::client::SetVisibilityClient(root_window, visibility_controller_.get());
-  if (drag_drop_controller_) {
-    DCHECK_NE(Config::MASH_DEPRECATED, GetAshConfig());
-    aura::client::SetDragDropClient(root_window, drag_drop_controller_.get());
-  } else {
-    DCHECK_EQ(Config::MASH_DEPRECATED, GetAshConfig());
-  }
+  aura::client::SetDragDropClient(root_window, drag_drop_controller_.get());
   aura::client::SetScreenPositionClient(root_window,
                                         screen_position_controller_.get());
   aura::client::SetCursorClient(root_window, cursor_manager_.get());
diff --git a/ash/system/network/network_list.cc b/ash/system/network/network_list.cc
index 669d35f..f1ec7ee 100644
--- a/ash/system/network/network_list.cc
+++ b/ash/system/network/network_list.cc
@@ -9,7 +9,6 @@
 
 #include "ash/metrics/user_metrics_recorder.h"
 #include "ash/public/cpp/ash_view_ids.h"
-#include "ash/public/cpp/config.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/session/session_controller.h"
 #include "ash/shell.h"
@@ -50,6 +49,7 @@
 #include "third_party/cros_system_api/dbus/service_constants.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/base/l10n/l10n_util.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/font.h"
 #include "ui/gfx/image/image_skia.h"
@@ -64,7 +64,6 @@
 #include "ui/views/controls/scroll_view.h"
 #include "ui/views/controls/separator.h"
 #include "ui/views/layout/fill_layout.h"
-
 #include "ui/views/view.h"
 
 using chromeos::ManagedNetworkConfigurationHandler;
@@ -646,7 +645,7 @@
   // TODO(jamescook): Create UIProxyConfigService under mash. This will require
   // the mojo pref service to work with prefs in Local State.
   // http://crbug.com/718072
-  if (Shell::GetAshConfig() != Config::MASH_DEPRECATED) {
+  if (::features::IsAshInBrowserProcess()) {
     using_proxy = NetworkHandler::Get()
                       ->ui_proxy_config_service()
                       ->HasDefaultNetworkProxyConfigured();
diff --git a/ash/system/palette/tools/metalayer_mode.h b/ash/system/palette/tools/metalayer_mode.h
index 8254bad..d9742aa 100644
--- a/ash/system/palette/tools/metalayer_mode.h
+++ b/ash/system/palette/tools/metalayer_mode.h
@@ -68,6 +68,7 @@
       mojom::VoiceInteractionState state) override;
   void OnVoiceInteractionSettingsEnabled(bool enabled) override;
   void OnVoiceInteractionContextEnabled(bool enabled) override;
+  void OnVoiceInteractionHotwordEnabled(bool enabled) override {}
   void OnVoiceInteractionSetupCompleted(bool completed) override {}
   void OnAssistantFeatureAllowedChanged(
       mojom::AssistantAllowedState state) override;
diff --git a/ash/voice_interaction/voice_interaction_controller.cc b/ash/voice_interaction/voice_interaction_controller.cc
index 7b6b834..6d3f6c1 100644
--- a/ash/voice_interaction/voice_interaction_controller.cc
+++ b/ash/voice_interaction/voice_interaction_controller.cc
@@ -38,6 +38,13 @@
   });
 }
 
+void VoiceInteractionController::NotifyHotwordEnabled(bool enabled) {
+  hotword_enabled_ = enabled;
+  observers_.ForAllPtrs([enabled](auto* observer) {
+    observer->OnVoiceInteractionHotwordEnabled(enabled);
+  });
+}
+
 void VoiceInteractionController::NotifySetupCompleted(bool completed) {
   setup_completed_ = completed;
   observers_.ForAllPtrs([completed](auto* observer) {
@@ -63,6 +70,11 @@
   std::move(callback).Run(setup_completed_);
 }
 
+void VoiceInteractionController::IsHotwordEnabled(
+    IsHotwordEnabledCallback callback) {
+  std::move(callback).Run(hotword_enabled_);
+}
+
 void VoiceInteractionController::AddObserver(
     mojom::VoiceInteractionObserverPtr observer) {
   observers_.AddPtr(std::move(observer));
diff --git a/ash/voice_interaction/voice_interaction_controller.h b/ash/voice_interaction/voice_interaction_controller.h
index 01deca6..a29e246c4 100644
--- a/ash/voice_interaction/voice_interaction_controller.h
+++ b/ash/voice_interaction/voice_interaction_controller.h
@@ -26,10 +26,12 @@
   void NotifyStatusChanged(mojom::VoiceInteractionState state) override;
   void NotifySettingsEnabled(bool enabled) override;
   void NotifyContextEnabled(bool enabled) override;
+  void NotifyHotwordEnabled(bool enabled) override;
   void NotifySetupCompleted(bool completed) override;
   void NotifyFeatureAllowed(mojom::AssistantAllowedState state) override;
   void IsSettingEnabled(IsSettingEnabledCallback callback) override;
   void IsSetupCompleted(IsSetupCompletedCallback callback) override;
+  void IsHotwordEnabled(IsHotwordEnabledCallback callback) override;
   void AddObserver(mojom::VoiceInteractionObserverPtr observer) override;
 
   mojom::VoiceInteractionState voice_interaction_state() const {
@@ -56,6 +58,9 @@
   // Whether voice intearction setup flow has completed.
   bool setup_completed_ = false;
 
+  // Whether hotword listening is enabled.
+  bool hotword_enabled_ = false;
+
   // Whether voice intearction feature is allowed or disallowed for what reason.
   mojom::AssistantAllowedState allowed_state_ =
       mojom::AssistantAllowedState::ALLOWED;
diff --git a/ash/voice_interaction/voice_interaction_controller_unittest.cc b/ash/voice_interaction/voice_interaction_controller_unittest.cc
index 088742d..df68ffa 100644
--- a/ash/voice_interaction/voice_interaction_controller_unittest.cc
+++ b/ash/voice_interaction/voice_interaction_controller_unittest.cc
@@ -31,6 +31,9 @@
   void OnVoiceInteractionContextEnabled(bool enabled) override {
     context_enabled_ = enabled;
   }
+  void OnVoiceInteractionHotwordEnabled(bool enabled) override {
+    hotword_enabled_ = enabled;
+  }
   void OnVoiceInteractionSetupCompleted(bool completed) override {
     setup_completed_ = completed;
   }
@@ -42,6 +45,7 @@
   }
   bool settings_enabled() const { return settings_enabled_; }
   bool context_enabled() const { return context_enabled_; }
+  bool hotword_enabled() const { return hotword_enabled_; }
   bool setup_completed() const { return setup_completed_; }
 
   void SetVoiceInteractionController(VoiceInteractionController* controller) {
@@ -54,6 +58,7 @@
   mojom::VoiceInteractionState state_ = mojom::VoiceInteractionState::STOPPED;
   bool settings_enabled_ = false;
   bool context_enabled_ = false;
+  bool hotword_enabled_ = false;
   bool setup_completed_ = false;
 
   mojo::Binding<mojom::VoiceInteractionObserver> voice_interaction_binding_;
@@ -122,6 +127,13 @@
   EXPECT_TRUE(observer()->context_enabled());
 }
 
+TEST_F(VoiceInteractionControllerTest, NotifyHotwordEnabled) {
+  controller()->NotifyHotwordEnabled(true);
+  controller()->FlushForTesting();
+  // The observers should be notified.
+  EXPECT_TRUE(observer()->hotword_enabled());
+}
+
 TEST_F(VoiceInteractionControllerTest, NotifySetupCompleted) {
   controller()->NotifySetupCompleted(true);
   controller()->FlushForTesting();
diff --git a/base/allocator/BUILD.gn b/base/allocator/BUILD.gn
index 82489ba..c931d0f 100644
--- a/base/allocator/BUILD.gn
+++ b/base/allocator/BUILD.gn
@@ -74,7 +74,7 @@
 if (use_allocator == "tcmalloc") {
   # tcmalloc currently won't compile on Android.
   source_set("tcmalloc") {
-    tcmalloc_dir = "//third_party/tcmalloc/chromium"
+    tcmalloc_dir = "//third_party/tcmalloc/gperftools-2.0/chromium"
 
     # Don't check tcmalloc's includes. These files include various files like
     # base/foo.h and they actually refer to tcmalloc's forked copy of base
diff --git a/base/allocator/allocator_extension.cc b/base/allocator/allocator_extension.cc
index 9a3d114..b6ddbaa 100644
--- a/base/allocator/allocator_extension.cc
+++ b/base/allocator/allocator_extension.cc
@@ -7,9 +7,9 @@
 #include "base/logging.h"
 
 #if defined(USE_TCMALLOC)
-#include "third_party/tcmalloc/chromium/src/gperftools/heap-profiler.h"
-#include "third_party/tcmalloc/chromium/src/gperftools/malloc_extension.h"
-#include "third_party/tcmalloc/chromium/src/gperftools/malloc_hook.h"
+#include "third_party/tcmalloc/gperftools-2.0/chromium/src/gperftools/heap-profiler.h"
+#include "third_party/tcmalloc/gperftools-2.0/chromium/src/gperftools/malloc_extension.h"
+#include "third_party/tcmalloc/gperftools-2.0/chromium/src/gperftools/malloc_hook.h"
 #endif
 
 namespace base {
diff --git a/base/allocator/allocator_shim_default_dispatch_to_tcmalloc.cc b/base/allocator/allocator_shim_default_dispatch_to_tcmalloc.cc
index 878e8a7..71e4979 100644
--- a/base/allocator/allocator_shim_default_dispatch_to_tcmalloc.cc
+++ b/base/allocator/allocator_shim_default_dispatch_to_tcmalloc.cc
@@ -4,8 +4,8 @@
 
 #include "base/allocator/allocator_shim.h"
 #include "base/allocator/allocator_shim_internals.h"
-#include "third_party/tcmalloc/chromium/src/config.h"
-#include "third_party/tcmalloc/chromium/src/gperftools/tcmalloc.h"
+#include "third_party/tcmalloc/gperftools-2.0/chromium/src/config.h"
+#include "third_party/tcmalloc/gperftools-2.0/chromium/src/gperftools/tcmalloc.h"
 
 namespace {
 
diff --git a/base/allocator/debugallocation_shim.cc b/base/allocator/debugallocation_shim.cc
index 479cfca..7eb4504 100644
--- a/base/allocator/debugallocation_shim.cc
+++ b/base/allocator/debugallocation_shim.cc
@@ -14,7 +14,7 @@
 #endif
 
 #if defined(TCMALLOC_FOR_DEBUGALLOCATION)
-#include "third_party/tcmalloc/chromium/src/debugallocation.cc"
+#include "third_party/tcmalloc/gperftools-2.0/chromium/src/debugallocation.cc"
 #else
-#include "third_party/tcmalloc/chromium/src/tcmalloc.cc"
+#include "third_party/tcmalloc/gperftools-2.0/chromium/src/tcmalloc.cc"
 #endif
diff --git a/base/metrics/histogram_base.cc b/base/metrics/histogram_base.cc
index da3ae93..990d9f5 100644
--- a/base/metrics/histogram_base.cc
+++ b/base/metrics/histogram_base.cc
@@ -18,6 +18,7 @@
 #include "base/metrics/histogram_samples.h"
 #include "base/metrics/sparse_histogram.h"
 #include "base/metrics/statistics_recorder.h"
+#include "base/numerics/safe_conversions.h"
 #include "base/pickle.h"
 #include "base/process/process_handle.h"
 #include "base/rand_util.h"
@@ -113,7 +114,7 @@
 }
 
 void HistogramBase::AddTimeMillisecondsGranularity(const TimeDelta& time) {
-  Add(static_cast<Sample>(time.InMilliseconds()));
+  Add(saturated_cast<Sample>(time.InMilliseconds()));
 }
 
 void HistogramBase::AddTimeMicrosecondsGranularity(const TimeDelta& time) {
@@ -121,7 +122,7 @@
   // clocks. High-resolution metrics cannot make use of low-resolution data and
   // reporting it merely adds noise to the metric. https://crbug.com/807615#c16
   if (TimeTicks::IsHighResolution())
-    Add(static_cast<Sample>(time.InMicroseconds()));
+    Add(saturated_cast<Sample>(time.InMicroseconds()));
 }
 
 void HistogramBase::AddBoolean(bool value) {
diff --git a/base/metrics/histogram_base_unittest.cc b/base/metrics/histogram_base_unittest.cc
index e539e5c..0314ef4 100644
--- a/base/metrics/histogram_base_unittest.cc
+++ b/base/metrics/histogram_base_unittest.cc
@@ -186,4 +186,90 @@
   EXPECT_GE(2, samples->GetCount(300));
 }
 
+TEST_F(HistogramBaseTest, AddTimeMillisecondsGranularityOverflow) {
+  const HistogramBase::Sample sample_max =
+      std::numeric_limits<HistogramBase::Sample>::max() / 2;
+  HistogramBase* histogram = LinearHistogram::FactoryGet(
+      "TestAddTimeMillisecondsGranularity1", 1, sample_max, 100, 0);
+  int64_t large_positive = std::numeric_limits<int64_t>::max();
+  // |add_count| is the number of large values that have been added to the
+  // histogram. We consider a number to be 'large' if it cannot be represented
+  // in a HistogramBase::Sample.
+  int add_count = 0;
+  while (large_positive > std::numeric_limits<HistogramBase::Sample>::max()) {
+    // Add the TimeDelta corresponding to |large_positive| milliseconds to the
+    // histogram.
+    histogram->AddTimeMillisecondsGranularity(
+        TimeDelta::FromMilliseconds(large_positive));
+    ++add_count;
+    // Reduce the value of |large_positive|. The choice of 7 here is
+    // arbitrary.
+    large_positive /= 7;
+  }
+  std::unique_ptr<HistogramSamples> samples = histogram->SnapshotSamples();
+  // All of the reported values must have gone into the max overflow bucket.
+  EXPECT_EQ(add_count, samples->GetCount(sample_max));
+
+  // We now perform the analoguous operations, now with negative values with a
+  // large absolute value.
+  histogram = LinearHistogram::FactoryGet("TestAddTimeMillisecondsGranularity2",
+                                          1, sample_max, 100, 0);
+  int64_t large_negative = std::numeric_limits<int64_t>::min();
+  add_count = 0;
+  while (large_negative < std::numeric_limits<HistogramBase::Sample>::min()) {
+    histogram->AddTimeMillisecondsGranularity(
+        TimeDelta::FromMilliseconds(large_negative));
+    ++add_count;
+    large_negative /= 7;
+  }
+  samples = histogram->SnapshotSamples();
+  // All of the reported values must have gone into the min overflow bucket.
+  EXPECT_EQ(add_count, samples->GetCount(0));
+}
+
+TEST_F(HistogramBaseTest, AddTimeMicrosecondsGranularityOverflow) {
+  // Nothing to test if we don't have a high resolution clock.
+  if (!TimeTicks::IsHighResolution())
+    return;
+
+  const HistogramBase::Sample sample_max =
+      std::numeric_limits<HistogramBase::Sample>::max() / 2;
+  HistogramBase* histogram = LinearHistogram::FactoryGet(
+      "TestAddTimeMicrosecondsGranularity1", 1, sample_max, 100, 0);
+  int64_t large_positive = std::numeric_limits<int64_t>::max();
+  // |add_count| is the number of large values that have been added to the
+  // histogram. We consider a number to be 'large' if it cannot be represented
+  // in a HistogramBase::Sample.
+  int add_count = 0;
+  while (large_positive > std::numeric_limits<HistogramBase::Sample>::max()) {
+    // Add the TimeDelta corresponding to |large_positive| microseconds to the
+    // histogram.
+    histogram->AddTimeMicrosecondsGranularity(
+        TimeDelta::FromMicroseconds(large_positive));
+    ++add_count;
+    // Reduce the value of |large_positive|. The choice of 7 here is
+    // arbitrary.
+    large_positive /= 7;
+  }
+  std::unique_ptr<HistogramSamples> samples = histogram->SnapshotSamples();
+  // All of the reported values must have gone into the max overflow bucket.
+  EXPECT_EQ(add_count, samples->GetCount(sample_max));
+
+  // We now perform the analoguous operations, now with negative values with a
+  // large absolute value.
+  histogram = LinearHistogram::FactoryGet("TestAddTimeMicrosecondsGranularity2",
+                                          1, sample_max, 100, 0);
+  int64_t large_negative = std::numeric_limits<int64_t>::min();
+  add_count = 0;
+  while (large_negative < std::numeric_limits<HistogramBase::Sample>::min()) {
+    histogram->AddTimeMicrosecondsGranularity(
+        TimeDelta::FromMicroseconds(large_negative));
+    ++add_count;
+    large_negative /= 7;
+  }
+  samples = histogram->SnapshotSamples();
+  // All of the reported values must have gone into the min overflow bucket.
+  EXPECT_EQ(add_count, samples->GetCount(0));
+}
+
 }  // namespace base
diff --git a/base/process/memory_linux.cc b/base/process/memory_linux.cc
index 21b2069..171753c 100644
--- a/base/process/memory_linux.cc
+++ b/base/process/memory_linux.cc
@@ -18,8 +18,8 @@
 #include "build/build_config.h"
 
 #if defined(USE_TCMALLOC)
-#include "third_party/tcmalloc/chromium/src/config.h"
-#include "third_party/tcmalloc/chromium/src/gperftools/tcmalloc.h"
+#include "third_party/tcmalloc/gperftools-2.0/chromium/src/config.h"
+#include "third_party/tcmalloc/gperftools-2.0/chromium/src/gperftools/tcmalloc.h"
 #endif
 
 namespace base {
diff --git a/base/sampling_heap_profiler/sampling_heap_profiler.cc b/base/sampling_heap_profiler/sampling_heap_profiler.cc
index accc758..04c1737 100644
--- a/base/sampling_heap_profiler/sampling_heap_profiler.cc
+++ b/base/sampling_heap_profiler/sampling_heap_profiler.cc
@@ -21,6 +21,11 @@
 #include "base/threading/thread_local_storage.h"
 #include "build/build_config.h"
 
+#if defined(OS_ANDROID) && BUILDFLAG(CAN_UNWIND_WITH_CFI_TABLE) && \
+    defined(OFFICIAL_BUILD)
+#include "base/trace_event/cfi_backtrace_android.h"
+#endif
+
 namespace base {
 
 using base::allocator::AllocatorDispatch;
@@ -231,6 +236,14 @@
 }
 
 uint32_t SamplingHeapProfiler::Start() {
+#if defined(OS_ANDROID) && BUILDFLAG(CAN_UNWIND_WITH_CFI_TABLE) && \
+    defined(OFFICIAL_BUILD)
+  if (!base::trace_event::CFIBacktraceAndroid::GetInitializedInstance()
+           ->can_unwind_stack_frames()) {
+    LOG(WARNING) << "Sampling heap profiler: Stack unwinding is not available.";
+    return 0;
+  }
+#endif
   InstallAllocatorHooksOnce();
   base::subtle::Barrier_AtomicIncrement(&g_running, 1);
   return last_sample_ordinal_;
@@ -309,16 +322,31 @@
 void SamplingHeapProfiler::RecordStackTrace(Sample* sample,
                                             uint32_t skip_frames) {
 #if !defined(OS_NACL)
-  // TODO(alph): Consider using debug::TraceStackFramePointers. It should be
-  // somewhat faster than base::debug::StackTrace.
-  base::debug::StackTrace trace;
-  size_t count;
-  void* const* addresses = const_cast<void* const*>(trace.Addresses(&count));
-  const uint32_t kSkipProfilerOwnFrames = 2;
+  constexpr uint32_t kMaxStackEntries = 256;
+  constexpr uint32_t kSkipProfilerOwnFrames = 2;
   skip_frames += kSkipProfilerOwnFrames;
+#if defined(OS_ANDROID) && BUILDFLAG(CAN_UNWIND_WITH_CFI_TABLE) && \
+    defined(OFFICIAL_BUILD)
+  const void* frames[kMaxStackEntries];
+  size_t frame_count =
+      base::trace_event::CFIBacktraceAndroid::GetInitializedInstance()->Unwind(
+          frames, kMaxStackEntries);
+#elif BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS)
+  const void* frames[kMaxStackEntries];
+  size_t frame_count = base::debug::TraceStackFramePointers(
+      frames, kMaxStackEntries, skip_frames);
+  skip_frames = 0;
+#else
+  // Fall-back to capturing the stack with base::debug::StackTrace,
+  // which is likely slower, but more reliable.
+  base::debug::StackTrace stack_trace(kMaxStackEntries);
+  size_t frame_count = 0;
+  const void* const* frames = stack_trace.Addresses(&frame_count);
+#endif
+
   sample->stack.insert(
-      sample->stack.end(), &addresses[skip_frames],
-      &addresses[std::max(count, static_cast<size_t>(skip_frames))]);
+      sample->stack.end(), const_cast<void**>(&frames[skip_frames]),
+      const_cast<void**>(&frames[std::max<size_t>(frame_count, skip_frames)]));
 #endif
 }
 
diff --git a/base/sampling_heap_profiler/sampling_heap_profiler.h b/base/sampling_heap_profiler/sampling_heap_profiler.h
index c1b8de9..ea50e67 100644
--- a/base/sampling_heap_profiler/sampling_heap_profiler.h
+++ b/base/sampling_heap_profiler/sampling_heap_profiler.h
@@ -105,7 +105,7 @@
   std::stack<std::unique_ptr<LockFreeAddressHashSet>> sampled_addresses_stack_;
   std::unordered_map<void*, Sample> samples_;
   std::vector<SamplesObserver*> observers_;
-  uint32_t last_sample_ordinal_ = 0;
+  uint32_t last_sample_ordinal_ = 1;
 
   static SamplingHeapProfiler* instance_;
 
diff --git a/build/android/gyp/apkbuilder.py b/build/android/gyp/apkbuilder.py
index 16c1d62..ed15584 100755
--- a/build/android/gyp/apkbuilder.py
+++ b/build/android/gyp/apkbuilder.py
@@ -404,8 +404,7 @@
                                  options.key_name)
       else:
         shutil.move(tmp_file, options.output_apk)
-
-      tmp_apk.delete = False
+        tmp_apk.delete = False
 
   build_utils.CallAndWriteDepfileIfStale(
       on_stale_md5,
diff --git a/build/android/gyp/gcc_preprocess.py b/build/android/gyp/gcc_preprocess.py
index 660fcee..8c5c404 100755
--- a/build/android/gyp/gcc_preprocess.py
+++ b/build/android/gyp/gcc_preprocess.py
@@ -40,7 +40,6 @@
   parser.add_option('--include-path', help='Include path for gcc.')
   parser.add_option('--template', help='Path to template.')
   parser.add_option('--output', help='Path for generated file.')
-  parser.add_option('--stamp', help='Path to touch on success.')
   parser.add_option('--defines', help='Pre-defines macros', action='append')
 
   options, _ = parser.parse_args(args)
@@ -50,9 +49,6 @@
   if options.depfile:
     build_utils.WriteDepfile(options.depfile, options.output)
 
-  if options.stamp:
-    build_utils.Touch(options.stamp)
-
 
 if __name__ == '__main__':
   sys.exit(main(sys.argv[1:]))
diff --git a/build/android/gyp/generate_v14_compatible_resources.py b/build/android/gyp/generate_v14_compatible_resources.py
index 7a4f255..f9e8a37 100755
--- a/build/android/gyp/generate_v14_compatible_resources.py
+++ b/build/android/gyp/generate_v14_compatible_resources.py
@@ -20,7 +20,6 @@
 """
 
 import codecs
-import optparse
 import os
 import re
 import shutil
@@ -236,32 +235,6 @@
     GenerateV14StyleResource(input_filename, output_v14_filename)
 
 
-def ParseArgs():
-  """Parses command line options.
-
-  Returns:
-    An options object as from optparse.OptionsParser.parse_args()
-  """
-  parser = optparse.OptionParser()
-  parser.add_option('--res-dir',
-                    help='directory containing resources '
-                         'used to generate v14 compatible resources')
-  parser.add_option('--res-v14-compatibility-dir',
-                    help='output directory into which '
-                         'v14 compatible resources will be generated')
-  parser.add_option('--stamp', help='File to touch on success')
-
-  options, args = parser.parse_args()
-
-  if args:
-    parser.error('No positional arguments should be given.')
-
-  # Check that required options have been provided.
-  required_options = ('res_dir', 'res_v14_compatibility_dir')
-  build_utils.CheckOptions(options, parser, required=required_options)
-  return options
-
-
 def GenerateV14Resources(res_dir, res_v14_dir):
   for name in os.listdir(res_dir):
     if not os.path.isdir(os.path.join(res_dir, name)):
@@ -305,23 +278,4 @@
                                                output_qualifiers))
         GenerateV14StyleResourcesInDir(input_dir, output_v14_dir)
       elif not api_level_qualifier:
-        ErrorIfStyleResourceExistsInDir(input_dir)
-
-
-def main():
-  options = ParseArgs()
-
-  res_v14_dir = options.res_v14_compatibility_dir
-
-  build_utils.DeleteDirectory(res_v14_dir)
-  build_utils.MakeDirectory(res_v14_dir)
-
-  GenerateV14Resources(options.res_dir, res_v14_dir)
-
-  if options.stamp:
-    build_utils.Touch(options.stamp)
-
-
-if __name__ == '__main__':
-  sys.exit(main())
-
+        ErrorIfStyleResourceExistsInDir(input_dir)
\ No newline at end of file
diff --git a/build/android/gyp/java_cpp_enum_tests.py b/build/android/gyp/java_cpp_enum_tests.py
index a3c889e..703293b 100755
--- a/build/android/gyp/java_cpp_enum_tests.py
+++ b/build/android/gyp/java_cpp_enum_tests.py
@@ -10,17 +10,12 @@
 
 import collections
 from datetime import date
-import optparse
-import os
-import sys
 import unittest
 
 import java_cpp_enum
 from java_cpp_enum import EnumDefinition, GenerateOutput, GetScriptName
 from java_cpp_enum import HeaderParser
 
-sys.path.append(os.path.join(os.path.dirname(__file__), "gyp"))
-from util import build_utils
 
 class TestPreprocess(unittest.TestCase):
   def testOutput(self):
@@ -746,16 +741,6 @@
       finally:
         java_cpp_enum.DoParseHeaderFile = original_do_parse
 
-def main(argv):
-  parser = optparse.OptionParser()
-  parser.add_option("--stamp", help="File to touch on success.")
-  options, _ = parser.parse_args(argv)
-
-  suite = unittest.TestLoader().loadTestsFromTestCase(TestPreprocess)
-  unittest.TextTestRunner(verbosity=0).run(suite)
-
-  if options.stamp:
-    build_utils.Touch(options.stamp)
 
 if __name__ == '__main__':
-  main(sys.argv[1:])
+  unittest.main()
diff --git a/build/android/gyp/java_google_api_keys_tests.py b/build/android/gyp/java_google_api_keys_tests.py
index eb24ea4..6529a53 100755
--- a/build/android/gyp/java_google_api_keys_tests.py
+++ b/build/android/gyp/java_google_api_keys_tests.py
@@ -9,17 +9,10 @@
 generator.
 """
 
-import collections
-import argparse
-import os
-import sys
 import unittest
 
 import java_google_api_keys
 
-sys.path.append(os.path.join(os.path.dirname(__file__), "gyp"))
-from util import build_utils
-
 
 class TestJavaGoogleAPIKeys(unittest.TestCase):
   def testOutput(self):
@@ -45,17 +38,5 @@
     self.assertEqual(expected % java_google_api_keys.GetScriptName(), output)
 
 
-def main(argv):
-  parser = argparse.ArgumentParser()
-  parser.add_argument("--stamp", help="File to touch on success.")
-  options = parser.parse_args(argv)
-
-  suite = unittest.TestLoader().loadTestsFromTestCase(TestJavaGoogleAPIKeys)
-  unittest.TextTestRunner(verbosity=0).run(suite)
-
-  if options.stamp:
-    build_utils.Touch(options.stamp)
-
 if __name__ == '__main__':
-  main(sys.argv[1:])
-
+  unittest.main()
diff --git a/build/android/gyp/lint.py b/build/android/gyp/lint.py
index 41c3d96..bc664eb 100755
--- a/build/android/gyp/lint.py
+++ b/build/android/gyp/lint.py
@@ -316,8 +316,6 @@
                       help='If set, script will not log anything.')
   parser.add_argument('--src-dirs',
                       help='Directories containing java files.')
-  parser.add_argument('--stamp',
-                      help='Path to touch on success.')
   parser.add_argument('--srcjars',
                       help='GN list of included srcjars.')
 
diff --git a/build/android/gyp/proguard.py b/build/android/gyp/proguard.py
index b16db99..5a61968 100755
--- a/build/android/gyp/proguard.py
+++ b/build/android/gyp/proguard.py
@@ -58,7 +58,6 @@
       'added.')
   parser.add_option('--classpath', action='append',
                     help='Classpath for proguard.')
-  parser.add_option('--stamp', help='Path to touch on success.')
   parser.add_option('--enable-dangerous-optimizations', action='store_true',
                     help='Enable optimizations which are known to have issues.')
   parser.add_option('--verbose', '-v', action='store_true',
diff --git a/build/android/gyp/util/build_utils.py b/build/android/gyp/util/build_utils.py
index 9692a42..4640d80 100644
--- a/build/android/gyp/util/build_utils.py
+++ b/build/android/gyp/util/build_utils.py
@@ -553,10 +553,10 @@
                                output_paths=None, force=False,
                                pass_changes=False, depfile_deps=None,
                                add_pydeps=True):
-  """Wraps md5_check.CallAndRecordIfStale() and also writes dep & stamp files.
+  """Wraps md5_check.CallAndRecordIfStale() and writes a depfile if applicable.
 
-  Depfiles and stamp files are automatically added to output_paths when present
-  in the |options| argument. They are then created after |function| is called.
+  Depfiles are automatically added to output_paths when present in the |options|
+  argument. They are then created after |function| is called.
 
   By default, only python dependencies are added to the depfile. If there are
   other input paths that are not captured by GN deps, then they should be listed
@@ -576,10 +576,6 @@
     input_paths += python_deps
     output_paths += [options.depfile]
 
-  stamp_file = hasattr(options, 'stamp') and options.stamp
-  if stamp_file:
-    output_paths += [stamp_file]
-
   def on_stale_md5(changes):
     args = (changes,) if pass_changes else ()
     function(*args)
@@ -589,8 +585,6 @@
         all_depfile_deps.extend(depfile_deps)
       WriteDepfile(options.depfile, output_paths[0], all_depfile_deps,
                    add_pydeps=False)
-    if stamp_file:
-      Touch(stamp_file)
 
   md5_check.CallAndRecordIfStale(
       on_stale_md5,
diff --git a/build/android/gyp/write_ordered_libraries.py b/build/android/gyp/write_ordered_libraries.py
index 70186c3..e3d87c1 100755
--- a/build/android/gyp/write_ordered_libraries.py
+++ b/build/android/gyp/write_ordered_libraries.py
@@ -72,7 +72,6 @@
   parser.add_option('--exclude-shared-libraries',
       help='List of shared libraries to exclude from the output.')
   parser.add_option('--output', help='Path to the generated .json file.')
-  parser.add_option('--stamp', help='Path to touch on success.')
 
   options, _ = parser.parse_args(build_utils.ExpandFileArgs(sys.argv[1:]))
 
@@ -109,9 +108,6 @@
       options.output,
       only_if_changed=True)
 
-  if options.stamp:
-    build_utils.Touch(options.stamp)
-
   if options.depfile:
     build_utils.WriteDepfile(options.depfile, options.output, libraries)
 
diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn
index 295dabf..6e38ad7 100644
--- a/build/config/compiler/BUILD.gn
+++ b/build/config/compiler/BUILD.gn
@@ -743,6 +743,7 @@
         ldflags += [ "--target=aarch64-linux-gnu" ]
       }
     } else if (current_cpu == "mipsel" && !is_nacl) {
+      ldflags += [ "-Wl,--hash-style=sysv" ]
       if (custom_toolchain == "") {
         if (is_clang) {
           if (is_android) {
@@ -827,6 +828,7 @@
 
       cflags += [ "-m${mips_float_abi}-float" ]
     } else if (current_cpu == "mips" && !is_nacl) {
+      ldflags += [ "-Wl,--hash-style=sysv" ]
       if (custom_toolchain == "") {
         if (is_clang) {
           cflags += [ "--target=mips-linux-gnu" ]
@@ -871,6 +873,7 @@
 
       cflags += [ "-m${mips_float_abi}-float" ]
     } else if (current_cpu == "mips64el") {
+      ldflags += [ "-Wl,--hash-style=sysv" ]
       if (custom_toolchain == "") {
         if (is_clang) {
           if (is_android) {
@@ -933,6 +936,7 @@
         ]
       }
     } else if (current_cpu == "mips64") {
+      ldflags += [ "-Wl,--hash-style=sysv" ]
       if (custom_toolchain == "") {
         if (is_clang) {
           cflags += [ "--target=mips64-linux-gnuabi64" ]
diff --git a/cc/animation/scroll_timeline.cc b/cc/animation/scroll_timeline.cc
index 306c127..ee02fb5 100644
--- a/cc/animation/scroll_timeline.cc
+++ b/cc/animation/scroll_timeline.cc
@@ -28,15 +28,20 @@
 
 double ScrollTimeline::CurrentTime(const ScrollTree& scroll_tree,
                                    bool is_active_tree) const {
-  // If the scroller isn't in the ScrollTree, the element either no longer
-  // exists or is not currently scrollable. By the spec, return an unresolved
-  // time value.
+  // We may be asked for the CurrentTime before the pending tree with our
+  // scroller has been activated, or after the scroller has been removed (e.g.
+  // if it is no longer composited). In these cases the best we can do is to
+  // return an unresolved time value.
   if ((is_active_tree && !active_id_) || (!is_active_tree && !pending_id_))
     return std::numeric_limits<double>::quiet_NaN();
 
   ElementId scroller_id =
       is_active_tree ? active_id_.value() : pending_id_.value();
-  DCHECK(scroll_tree.FindNodeFromElementId(scroller_id));
+
+  // The scroller may not be in the ScrollTree if it is not currently scrollable
+  // (e.g. has overflow: visible). By the spec, return an unresolved time value.
+  if (!scroll_tree.FindNodeFromElementId(scroller_id))
+    return std::numeric_limits<double>::quiet_NaN();
 
   gfx::ScrollOffset offset = scroll_tree.current_scroll_offset(scroller_id);
   DCHECK_GE(offset.x(), 0);
diff --git a/cc/input/input_handler.h b/cc/input/input_handler.h
index 78d1295..a353060 100644
--- a/cc/input/input_handler.h
+++ b/cc/input/input_handler.h
@@ -123,8 +123,7 @@
   // Binds a client to this handler to receive notifications. Only one client
   // can be bound to an InputHandler. The client must live at least until the
   // handler calls WillShutdown() on the client.
-  virtual void BindToClient(InputHandlerClient* client,
-                            bool wheel_scroll_latching_enabled) = 0;
+  virtual void BindToClient(InputHandlerClient* client) = 0;
 
   // Selects a layer to be scrolled using the |scroll_state| start position.
   // Returns SCROLL_STARTED if the layer at the coordinates can be scrolled,
diff --git a/cc/layers/surface_layer.cc b/cc/layers/surface_layer.cc
index e102f3a..5b099a3 100644
--- a/cc/layers/surface_layer.cc
+++ b/cc/layers/surface_layer.cc
@@ -37,7 +37,16 @@
       TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "step",
       "SetPrimarySurfaceId", "surface_id", surface_id.ToString());
 
+  const viz::SurfaceRange old_surface_range(GetSurfaceRange());
+  if (layer_tree_host() && old_surface_range.IsValid())
+    layer_tree_host()->RemoveSurfaceRange(old_surface_range);
+
   primary_surface_id_ = surface_id;
+
+  const viz::SurfaceRange new_surface_range(GetSurfaceRange());
+  if (layer_tree_host() && new_surface_range.IsValid())
+    layer_tree_host()->AddSurfaceRange(new_surface_range);
+
   // We should never block or set a deadline on an invalid
   // |primary_surface_id_|.
   if (!primary_surface_id_.is_valid()) {
@@ -60,13 +69,15 @@
       TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "step",
       "SetFallbackSurfaceId", "surface_id", surface_id.ToString());
 
-  if (layer_tree_host())
-    layer_tree_host()->RemoveSurfaceLayerId(fallback_surface_id_);
+  const viz::SurfaceRange old_surface_range(GetSurfaceRange());
+  if (layer_tree_host() && old_surface_range.IsValid())
+    layer_tree_host()->RemoveSurfaceRange(old_surface_range);
 
   fallback_surface_id_ = surface_id;
 
-  if (layer_tree_host() && fallback_surface_id_.is_valid())
-    layer_tree_host()->AddSurfaceLayerId(fallback_surface_id_);
+  const viz::SurfaceRange new_surface_range(GetSurfaceRange());
+  if (layer_tree_host() && new_surface_range.IsValid())
+    layer_tree_host()->AddSurfaceRange(new_surface_range);
 
   SetNeedsCommit();
 }
@@ -99,14 +110,15 @@
   if (layer_tree_host() == host) {
     return;
   }
-
-  if (layer_tree_host() && fallback_surface_id_.is_valid())
-    layer_tree_host()->RemoveSurfaceLayerId(fallback_surface_id_);
+  const viz::SurfaceRange old_surface_range(GetSurfaceRange());
+  if (layer_tree_host() && old_surface_range.IsValid())
+    layer_tree_host()->RemoveSurfaceRange(old_surface_range);
 
   Layer::SetLayerTreeHost(host);
 
-  if (layer_tree_host() && fallback_surface_id_.is_valid())
-    layer_tree_host()->AddSurfaceLayerId(fallback_surface_id_);
+  const viz::SurfaceRange new_surface_range(GetSurfaceRange());
+  if (layer_tree_host() && new_surface_range.IsValid())
+    layer_tree_host()->AddSurfaceRange(new_surface_range);
 }
 
 void SurfaceLayer::PushPropertiesTo(LayerImpl* layer) {
@@ -123,4 +135,13 @@
   layer_impl->SetSurfaceHitTestable(surface_hit_testable_);
 }
 
+viz::SurfaceRange SurfaceLayer::GetSurfaceRange() const {
+  return viz::SurfaceRange(
+      fallback_surface_id_.is_valid()
+          ? base::Optional<viz::SurfaceId>(fallback_surface_id_)
+          : base::nullopt,
+      primary_surface_id_.is_valid() ? primary_surface_id_
+                                     : fallback_surface_id_);
+}
+
 }  // namespace cc
diff --git a/cc/layers/surface_layer.h b/cc/layers/surface_layer.h
index 87c5a26..a1cec40 100644
--- a/cc/layers/surface_layer.h
+++ b/cc/layers/surface_layer.h
@@ -10,6 +10,7 @@
 #include "cc/layers/deadline_policy.h"
 #include "cc/layers/layer.h"
 #include "components/viz/common/surfaces/surface_info.h"
+#include "components/viz/common/surfaces/surface_range.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/gfx/geometry/size.h"
 
@@ -59,6 +60,9 @@
  private:
   ~SurfaceLayer() override;
 
+  // Returns a SurfaceRange corresponding the surface layer.
+  viz::SurfaceRange GetSurfaceRange() const;
+
   viz::SurfaceId primary_surface_id_;
   viz::SurfaceId fallback_surface_id_;
   base::Optional<uint32_t> deadline_in_frames_ = 0u;
diff --git a/cc/layers/surface_layer_unittest.cc b/cc/layers/surface_layer_unittest.cc
index 23ecc6a..c6e8f16 100644
--- a/cc/layers/surface_layer_unittest.cc
+++ b/cc/layers/surface_layer_unittest.cc
@@ -4,6 +4,7 @@
 
 #include <stdint.h>
 
+#include <iostream>
 #include <set>
 #include <vector>
 
@@ -46,7 +47,7 @@
   void SynchronizeTrees() {
     TreeSynchronizer::PushLayerProperties(layer_tree_host_.get(),
                                           host_impl_.pending_tree());
-    layer_tree_host_->PushSurfaceIdsTo(host_impl_.pending_tree());
+    layer_tree_host_->PushSurfaceRangesTo(host_impl_.pending_tree());
   }
 
  protected:
@@ -134,24 +135,24 @@
   layer->SetBackgroundColor(SK_ColorBLUE);
   layer->SetStretchContentToFillBounds(true);
 
-  EXPECT_TRUE(layer_tree_host_->needs_surface_ids_sync());
-  EXPECT_EQ(layer_tree_host_->SurfaceLayerIds().size(), 1u);
+  EXPECT_TRUE(layer_tree_host_->needs_surface_ranges_sync());
+  EXPECT_EQ(layer_tree_host_->SurfaceRanges().size(), 1u);
 
   // Verify that pending tree has no surface ids already.
-  EXPECT_FALSE(host_impl_.pending_tree()->needs_surface_ids_sync());
-  EXPECT_EQ(host_impl_.pending_tree()->SurfaceLayerIds().size(), 0u);
+  EXPECT_FALSE(host_impl_.pending_tree()->needs_surface_ranges_sync());
+  EXPECT_EQ(host_impl_.pending_tree()->SurfaceRanges().size(), 0u);
 
   std::unique_ptr<SurfaceLayerImpl> layer_impl =
       SurfaceLayerImpl::Create(host_impl_.pending_tree(), layer->id());
   SynchronizeTrees();
 
   // Verify that pending tree received the surface id and also has
-  // needs_surface_ids_sync set to true as it needs to sync with active tree.
-  EXPECT_TRUE(host_impl_.pending_tree()->needs_surface_ids_sync());
-  EXPECT_EQ(host_impl_.pending_tree()->SurfaceLayerIds().size(), 1u);
+  // needs_surface_ranges_sync set to true as it needs to sync with active tree.
+  EXPECT_TRUE(host_impl_.pending_tree()->needs_surface_ranges_sync());
+  EXPECT_EQ(host_impl_.pending_tree()->SurfaceRanges().size(), 1u);
 
   // Verify we have reset the state on layer tree host.
-  EXPECT_FALSE(layer_tree_host_->needs_surface_ids_sync());
+  EXPECT_FALSE(layer_tree_host_->needs_surface_ranges_sync());
 
   // Verify that the primary and fallback SurfaceIds are pushed through.
   EXPECT_EQ(primary_id, layer_impl->primary_surface_id());
@@ -164,21 +165,23 @@
       kArbitraryFrameSinkId,
       viz::LocalSurfaceId(2, base::UnguessableToken::Create()));
   layer->SetFallbackSurfaceId(fallback_id);
+  layer->SetPrimarySurfaceId(fallback_id,
+                             DeadlinePolicy::UseExistingDeadline());
   layer->SetBackgroundColor(SK_ColorGREEN);
   layer->SetStretchContentToFillBounds(false);
 
   // Verify that fallback surface id is not recorded on the layer tree host as
   // surface synchronization is not enabled.
-  EXPECT_TRUE(layer_tree_host_->needs_surface_ids_sync());
-  EXPECT_EQ(layer_tree_host_->SurfaceLayerIds().size(), 1u);
+  EXPECT_TRUE(layer_tree_host_->needs_surface_ranges_sync());
+  EXPECT_EQ(layer_tree_host_->SurfaceRanges().size(), 1u);
 
   SynchronizeTrees();
 
-  EXPECT_EQ(host_impl_.pending_tree()->SurfaceLayerIds().size(), 1u);
+  EXPECT_EQ(host_impl_.pending_tree()->SurfaceRanges().size(), 1u);
 
   // Verify that the primary viz::SurfaceId stays the same and the new
   // fallback viz::SurfaceId is pushed through.
-  EXPECT_EQ(primary_id, layer_impl->primary_surface_id());
+  EXPECT_EQ(fallback_id, layer_impl->primary_surface_id());
   EXPECT_EQ(fallback_id, layer_impl->fallback_surface_id());
   EXPECT_EQ(SK_ColorGREEN, layer_impl->background_color());
   // The deadline resets back to 0 (no deadline) after the first commit.
@@ -218,9 +221,10 @@
   SynchronizeTrees();
 
   // Verify that only |old_surface_id| is going to be referenced.
-  EXPECT_THAT(layer_tree_host_->SurfaceLayerIds(), ElementsAre(old_surface_id));
-  EXPECT_THAT(host_impl_.pending_tree()->SurfaceLayerIds(),
-              ElementsAre(old_surface_id));
+  EXPECT_THAT(layer_tree_host_->SurfaceRanges(),
+              ElementsAre(viz::SurfaceRange(old_surface_id)));
+  EXPECT_THAT(host_impl_.pending_tree()->SurfaceRanges(),
+              ElementsAre(viz::SurfaceRange(old_surface_id)));
 
   const viz::SurfaceId new_surface_id(
       kArbitraryFrameSinkId,
@@ -234,10 +238,12 @@
   SynchronizeTrees();
 
   // Verify that both surface ids are going to be referenced.
-  EXPECT_THAT(layer_tree_host_->SurfaceLayerIds(),
-              ElementsAre(old_surface_id, new_surface_id));
-  EXPECT_THAT(host_impl_.pending_tree()->SurfaceLayerIds(),
-              ElementsAre(old_surface_id, new_surface_id));
+  EXPECT_THAT(layer_tree_host_->SurfaceRanges(),
+              ElementsAre(viz::SurfaceRange(old_surface_id),
+                          viz::SurfaceRange(new_surface_id)));
+  EXPECT_THAT(host_impl_.pending_tree()->SurfaceRanges(),
+              ElementsAre(viz::SurfaceRange(old_surface_id),
+                          viz::SurfaceRange(new_surface_id)));
 
   // Unparent the old layer like it's being destroyed at the end of animation.
   layer1->SetLayerTreeHost(nullptr);
@@ -245,15 +251,16 @@
   SynchronizeTrees();
 
   // Verify that only |new_surface_id| is going to be referenced.
-  EXPECT_THAT(layer_tree_host_->SurfaceLayerIds(), ElementsAre(new_surface_id));
-  EXPECT_THAT(host_impl_.pending_tree()->SurfaceLayerIds(),
-              ElementsAre(new_surface_id));
+  EXPECT_THAT(layer_tree_host_->SurfaceRanges(),
+              ElementsAre(viz::SurfaceRange(new_surface_id)));
+  EXPECT_THAT(host_impl_.pending_tree()->SurfaceRanges(),
+              ElementsAre(viz::SurfaceRange(new_surface_id)));
 
   // Cleanup for destruction.
   layer2->SetLayerTreeHost(nullptr);
 }
 
-// This test verifies LayerTreeHost::needs_surface_ids_sync() is correct when
+// This test verifies LayerTreeHost::needs_surface_ranges_sync() is correct when
 // there are cloned surface layers.
 TEST_F(SurfaceLayerTest, CheckNeedsSurfaceIdsSyncForClonedLayers) {
   const viz::SurfaceId surface_id(
@@ -265,17 +272,17 @@
   layer1->SetPrimarySurfaceId(surface_id, DeadlinePolicy::UseDefaultDeadline());
   layer1->SetFallbackSurfaceId(surface_id);
 
-  // Verify the surface id is in SurfaceLayerIds() and needs_surface_ids_sync()
-  // is true.
-  EXPECT_TRUE(layer_tree_host_->needs_surface_ids_sync());
-  EXPECT_THAT(layer_tree_host_->SurfaceLayerIds(), SizeIs(1));
+  // Verify the surface id is in SurfaceLayerIds() and
+  // needs_surface_ranges_sync() is true.
+  EXPECT_TRUE(layer_tree_host_->needs_surface_ranges_sync());
+  EXPECT_THAT(layer_tree_host_->SurfaceRanges(), SizeIs(1));
 
   std::unique_ptr<SurfaceLayerImpl> layer_impl1 =
       SurfaceLayerImpl::Create(host_impl_.pending_tree(), layer1->id());
   SynchronizeTrees();
 
-  // After syncchronizing trees verify needs_surface_ids_sync() is false.
-  EXPECT_FALSE(layer_tree_host_->needs_surface_ids_sync());
+  // After syncchronizing trees verify needs_surface_ranges_sync() is false.
+  EXPECT_FALSE(layer_tree_host_->needs_surface_ranges_sync());
 
   // Create the second layer that is a clone of the first.
   scoped_refptr<SurfaceLayer> layer2 = SurfaceLayer::Create();
@@ -284,29 +291,30 @@
   layer2->SetFallbackSurfaceId(surface_id);
 
   // Verify that after creating the second layer with the same surface id that
-  // needs_surface_ids_sync() is still false.
-  EXPECT_FALSE(layer_tree_host_->needs_surface_ids_sync());
-  EXPECT_THAT(layer_tree_host_->SurfaceLayerIds(), SizeIs(1));
+  // needs_surface_ranges_sync() is still false.
+  EXPECT_TRUE(layer_tree_host_->needs_surface_ranges_sync());
+  EXPECT_THAT(layer_tree_host_->SurfaceRanges(), SizeIs(1));
 
   std::unique_ptr<SurfaceLayerImpl> layer_impl2 =
       SurfaceLayerImpl::Create(host_impl_.pending_tree(), layer2->id());
   SynchronizeTrees();
 
-  // Verify needs_surface_ids_sync() is still false after synchronizing trees.
-  EXPECT_FALSE(layer_tree_host_->needs_surface_ids_sync());
+  // Verify needs_surface_ranges_sync() is still false after synchronizing
+  // trees.
+  EXPECT_FALSE(layer_tree_host_->needs_surface_ranges_sync());
 
   // Destroy one of the layers, leaving one layer with the surface id.
   layer1->SetLayerTreeHost(nullptr);
 
-  // Verify needs_surface_ids_sync() is still false.
-  EXPECT_FALSE(layer_tree_host_->needs_surface_ids_sync());
+  // Verify needs_surface_ranges_sync() is still false.
+  EXPECT_FALSE(layer_tree_host_->needs_surface_ranges_sync());
 
   // Destroy the last layer, this should change the set of layer surface ids.
   layer2->SetLayerTreeHost(nullptr);
 
-  // Verify SurfaceLayerIds() is empty and needs_surface_ids_sync() is true.
-  EXPECT_TRUE(layer_tree_host_->needs_surface_ids_sync());
-  EXPECT_THAT(layer_tree_host_->SurfaceLayerIds(), SizeIs(0));
+  // Verify SurfaceLayerIds() is empty and needs_surface_ranges_sync() is true.
+  EXPECT_TRUE(layer_tree_host_->needs_surface_ranges_sync());
+  EXPECT_THAT(layer_tree_host_->SurfaceRanges(), SizeIs(0));
 }
 
 }  // namespace
diff --git a/cc/scheduler/compositor_timing_history.cc b/cc/scheduler/compositor_timing_history.cc
index cb6bcf5..bf58eb0 100644
--- a/cc/scheduler/compositor_timing_history.cc
+++ b/cc/scheduler/compositor_timing_history.cc
@@ -287,8 +287,6 @@
   void AddBeginMainFrameIntervalCritical(base::TimeDelta interval) override {}
 
   void AddBeginMainFrameIntervalNotCritical(base::TimeDelta interval) override {
-    UMA_HISTOGRAM_CUSTOM_TIMES_VSYNC_ALIGNED(
-        "Scheduling.Browser.BeginMainFrameIntervalNotCritical", interval);
   }
 
   // CommitInterval is not meaningful to measure on browser side because
@@ -325,10 +323,7 @@
   }
 
   void AddBeginMainFrameQueueDurationNotCriticalDuration(
-      base::TimeDelta duration) override {
-    UMA_HISTOGRAM_CUSTOM_TIMES_DURATION(
-        "Scheduling.Browser.BeginMainFrameQueueDurationNotCritical", duration);
-  }
+      base::TimeDelta duration) override {}
 
   void AddBeginMainFrameStartToCommitDuration(
       base::TimeDelta duration) override {
diff --git a/cc/trees/layer_tree_host.cc b/cc/trees/layer_tree_host.cc
index 10e5986..c42da9a 100644
--- a/cc/trees/layer_tree_host.cc
+++ b/cc/trees/layer_tree_host.cc
@@ -320,7 +320,7 @@
     PushPropertyTreesTo(sync_tree);
     sync_tree->lifecycle().AdvanceTo(LayerTreeLifecycle::kSyncedPropertyTrees);
 
-    PushSurfaceIdsTo(sync_tree);
+    PushSurfaceRangesTo(sync_tree);
     TreeSynchronizer::PushLayerProperties(this, sync_tree);
     sync_tree->lifecycle().AdvanceTo(
         LayerTreeLifecycle::kSyncedLayerProperties);
@@ -1286,27 +1286,27 @@
   return did_paint_content;
 }
 
-void LayerTreeHost::AddSurfaceLayerId(const viz::SurfaceId& surface_id) {
-  if (++surface_layer_ids_[surface_id] == 1)
-    needs_surface_ids_sync_ = true;
+void LayerTreeHost::AddSurfaceRange(const viz::SurfaceRange& surface_range) {
+  if (++surface_ranges_[surface_range] == 1)
+    needs_surface_ranges_sync_ = true;
 }
 
-void LayerTreeHost::RemoveSurfaceLayerId(const viz::SurfaceId& surface_id) {
-  auto iter = surface_layer_ids_.find(surface_id);
-  if (iter == surface_layer_ids_.end())
+void LayerTreeHost::RemoveSurfaceRange(const viz::SurfaceRange& surface_range) {
+  auto iter = surface_ranges_.find(surface_range);
+  if (iter == surface_ranges_.end())
     return;
 
   if (--iter->second <= 0) {
-    surface_layer_ids_.erase(iter);
-    needs_surface_ids_sync_ = true;
+    surface_ranges_.erase(iter);
+    needs_surface_ranges_sync_ = true;
   }
 }
 
-base::flat_set<viz::SurfaceId> LayerTreeHost::SurfaceLayerIds() const {
-  base::flat_set<viz::SurfaceId> ids;
-  for (auto& map_entry : surface_layer_ids_)
-    ids.insert(map_entry.first);
-  return ids;
+base::flat_set<viz::SurfaceRange> LayerTreeHost::SurfaceRanges() const {
+  base::flat_set<viz::SurfaceRange> ranges;
+  for (auto& map_entry : surface_ranges_)
+    ranges.insert(map_entry.first);
+  return ranges;
 }
 
 void LayerTreeHost::AddLayerShouldPushProperties(Layer* layer) {
@@ -1451,12 +1451,12 @@
   tree_impl->set_has_ever_been_drawn(false);
 }
 
-void LayerTreeHost::PushSurfaceIdsTo(LayerTreeImpl* tree_impl) {
-  if (needs_surface_ids_sync()) {
-    tree_impl->ClearSurfaceLayerIds();
-    tree_impl->SetSurfaceLayerIds(SurfaceLayerIds());
+void LayerTreeHost::PushSurfaceRangesTo(LayerTreeImpl* tree_impl) {
+  if (needs_surface_ranges_sync()) {
+    tree_impl->ClearSurfaceRanges();
+    tree_impl->SetSurfaceRanges(SurfaceRanges());
     // Reset for next update
-    set_needs_surface_ids_sync(false);
+    set_needs_surface_ranges_sync(false);
   }
 }
 
diff --git a/cc/trees/layer_tree_host.h b/cc/trees/layer_tree_host.h
index 63424d1..8cea9a0 100644
--- a/cc/trees/layer_tree_host.h
+++ b/cc/trees/layer_tree_host.h
@@ -391,9 +391,9 @@
   void SetHasCopyRequest(bool has_copy_request);
   bool has_copy_request() const { return has_copy_request_; }
 
-  void AddSurfaceLayerId(const viz::SurfaceId& surface_id);
-  void RemoveSurfaceLayerId(const viz::SurfaceId& surface_id);
-  base::flat_set<viz::SurfaceId> SurfaceLayerIds() const;
+  void AddSurfaceRange(const viz::SurfaceRange& surface_range);
+  void RemoveSurfaceRange(const viz::SurfaceRange& surface_range);
+  base::flat_set<viz::SurfaceRange> SurfaceRanges() const;
 
   void AddLayerShouldPushProperties(Layer* layer);
   void RemoveLayerShouldPushProperties(Layer* layer);
@@ -410,16 +410,16 @@
   virtual void SetNeedsFullTreeSync();
   bool needs_full_tree_sync() const { return needs_full_tree_sync_; }
 
-  bool needs_surface_ids_sync() const { return needs_surface_ids_sync_; }
-  void set_needs_surface_ids_sync(bool needs_surface_ids_sync) {
-    needs_surface_ids_sync_ = needs_surface_ids_sync;
+  bool needs_surface_ranges_sync() const { return needs_surface_ranges_sync_; }
+  void set_needs_surface_ranges_sync(bool needs_surface_ranges_sync) {
+    needs_surface_ranges_sync_ = needs_surface_ranges_sync;
   }
 
   void SetPropertyTreesNeedRebuild();
 
   void PushPropertyTreesTo(LayerTreeImpl* tree_impl);
   void PushLayerTreePropertiesTo(LayerTreeImpl* tree_impl);
-  void PushSurfaceIdsTo(LayerTreeImpl* tree_impl);
+  void PushSurfaceRangesTo(LayerTreeImpl* tree_impl);
   void PushLayerTreeHostPropertiesTo(LayerTreeHostImpl* host_impl);
 
   MutatorHost* mutator_host() const { return mutator_host_; }
@@ -682,14 +682,15 @@
 
   bool needs_full_tree_sync_ = true;
 
-  bool needs_surface_ids_sync_ = false;
+  bool needs_surface_ranges_sync_ = false;
 
   gfx::Vector2dF elastic_overscroll_;
 
   scoped_refptr<HeadsUpDisplayLayer> hud_layer_;
 
-  // The number of SurfaceLayers that have fallback set to viz::SurfaceId.
-  base::flat_map<viz::SurfaceId, int> surface_layer_ids_;
+  // The number of SurfaceRanges that have (fallback,primary) set to
+  // viz::SurfaceRange.
+  base::flat_map<viz::SurfaceRange, int> surface_ranges_;
 
   // Set of layers that need to push properties.
   std::unordered_set<Layer*> layers_that_should_push_properties_;
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index e99dceb..a329a48 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -949,7 +949,7 @@
 
   // If the set of referenced surfaces has changed then we must submit a new
   // CompositorFrame to update surface references.
-  if (last_draw_referenced_surfaces_ != active_tree()->SurfaceLayerIds())
+  if (last_draw_referenced_surfaces_ != active_tree()->SurfaceRanges())
     return true;
 
   // If we have a new LocalSurfaceId, we must always submit a CompositorFrame
@@ -1940,10 +1940,10 @@
         IsActivelyScrolling() || mutator_host_->NeedsTickAnimations();
   }
 
-  const base::flat_set<viz::SurfaceId>& referenced_surfaces =
-      active_tree_->SurfaceLayerIds();
-  for (auto& surface_id : referenced_surfaces)
-    metadata.referenced_surfaces.push_back(surface_id);
+  const base::flat_set<viz::SurfaceRange>& referenced_surfaces =
+      active_tree_->SurfaceRanges();
+  for (auto& surface_range : referenced_surfaces)
+    metadata.referenced_surfaces.push_back(surface_range);
 
   if (last_draw_referenced_surfaces_ != referenced_surfaces)
     last_draw_referenced_surfaces_ = referenced_surfaces;
@@ -3298,11 +3298,9 @@
   return active_tree_->CurrentBrowserControlsShownRatio();
 }
 
-void LayerTreeHostImpl::BindToClient(InputHandlerClient* client,
-                                     bool wheel_scroll_latching_enabled) {
+void LayerTreeHostImpl::BindToClient(InputHandlerClient* client) {
   DCHECK(input_handler_client_ == nullptr);
   input_handler_client_ = client;
-  touchpad_and_wheel_scroll_latching_enabled_ = wheel_scroll_latching_enabled;
 }
 
 InputHandler::ScrollStatus LayerTreeHostImpl::TryScroll(
@@ -3785,8 +3783,7 @@
 
       // For the rest of the current scroll sequence, latch to the first node
       // that scrolled while it still exists.
-      if (touchpad_and_wheel_scroll_latching_enabled_ &&
-          scroll_tree.FindNodeFromElementId(
+      if (scroll_tree.FindNodeFromElementId(
               scroll_animating_latched_element_id_) &&
           scroll_node->element_id != scroll_animating_latched_element_id_) {
         continue;
diff --git a/cc/trees/layer_tree_host_impl.h b/cc/trees/layer_tree_host_impl.h
index c63ef340..d2d9d750 100644
--- a/cc/trees/layer_tree_host_impl.h
+++ b/cc/trees/layer_tree_host_impl.h
@@ -53,6 +53,7 @@
 #include "components/viz/common/surfaces/child_local_surface_id_allocator.h"
 #include "components/viz/common/surfaces/local_surface_id.h"
 #include "components/viz/common/surfaces/surface_id.h"
+#include "components/viz/common/surfaces/surface_range.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/latency/frame_metrics.h"
 
@@ -235,8 +236,7 @@
   ~LayerTreeHostImpl() override;
 
   // InputHandler implementation
-  void BindToClient(InputHandlerClient* client,
-                    bool wheel_scroll_latching_enabled) override;
+  void BindToClient(InputHandlerClient* client) override;
   InputHandler::ScrollStatus ScrollBegin(
       ScrollState* scroll_state,
       InputHandler::ScrollInputType type) override;
@@ -1072,8 +1072,6 @@
   bool has_scrolled_by_wheel_ = false;
   bool has_scrolled_by_touch_ = false;
 
-  bool touchpad_and_wheel_scroll_latching_enabled_ = false;
-
   ImplThreadPhase impl_thread_phase_ = ImplThreadPhase::IDLE;
 
   ImageAnimationController image_animation_controller_;
@@ -1087,7 +1085,7 @@
   uint32_t next_frame_token_ = 1u;
 
   viz::LocalSurfaceId last_draw_local_surface_id_;
-  base::flat_set<viz::SurfaceId> last_draw_referenced_surfaces_;
+  base::flat_set<viz::SurfaceRange> last_draw_referenced_surfaces_;
   base::Optional<RenderFrameMetadata> last_draw_render_frame_metadata_;
   viz::ChildLocalSurfaceIdAllocator child_local_surface_id_allocator_;
 
diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc
index a3d3dc4..9cd7cf6 100644
--- a/cc/trees/layer_tree_host_impl_unittest.cc
+++ b/cc/trees/layer_tree_host_impl_unittest.cc
@@ -4410,12 +4410,15 @@
     root->test_properties()->AddChild(std::move(child));
   }
 
-  base::flat_set<viz::SurfaceId> fallback_surfaces_set;
-  for (size_t i = 0; i < fallback_surfaces.size(); ++i)
-    fallback_surfaces_set.insert(fallback_surfaces[i]);
+  base::flat_set<viz::SurfaceRange> surfaces_set;
+  // |fallback_surfaces| and |primary_surfaces| should have same size
+  for (size_t i = 0; i < fallback_surfaces.size(); ++i) {
+    surfaces_set.insert(
+        viz::SurfaceRange(fallback_surfaces[i], primary_surfaces[i]));
+  }
 
   host_impl_->active_tree()->BuildPropertyTreesForTesting();
-  host_impl_->active_tree()->SetSurfaceLayerIds(fallback_surfaces_set);
+  host_impl_->active_tree()->SetSurfaceRanges(std::move(surfaces_set));
   host_impl_->SetFullViewportDamage();
   DrawFrame();
 
@@ -4430,7 +4433,9 @@
     EXPECT_THAT(
         metadata.referenced_surfaces,
         testing::UnorderedElementsAre(
-            fallback_surfaces[0], fallback_surfaces[1], fallback_surfaces[2]));
+            viz::SurfaceRange(fallback_surfaces[0], primary_surfaces[0]),
+            viz::SurfaceRange(fallback_surfaces[1], primary_surfaces[1]),
+            viz::SurfaceRange(fallback_surfaces[2], primary_surfaces[2])));
     EXPECT_EQ(2u, metadata.deadline.deadline_in_frames());
     EXPECT_FALSE(metadata.deadline.use_default_lower_bound_deadline());
   }
@@ -4451,7 +4456,9 @@
     EXPECT_THAT(
         metadata.referenced_surfaces,
         testing::UnorderedElementsAre(
-            fallback_surfaces[0], fallback_surfaces[1], fallback_surfaces[2]));
+            viz::SurfaceRange(fallback_surfaces[0], primary_surfaces[0]),
+            viz::SurfaceRange(fallback_surfaces[1], primary_surfaces[1]),
+            viz::SurfaceRange(fallback_surfaces[2], primary_surfaces[2])));
     EXPECT_EQ(0u, metadata.deadline.deadline_in_frames());
     EXPECT_FALSE(metadata.deadline.use_default_lower_bound_deadline());
   }
@@ -4468,7 +4475,7 @@
 
   // Submit an initial CompositorFrame with an empty set of referenced surfaces.
   host_impl_->active_tree()->BuildPropertyTreesForTesting();
-  host_impl_->active_tree()->SetSurfaceLayerIds({});
+  host_impl_->active_tree()->SetSurfaceRanges({});
   host_impl_->SetFullViewportDamage();
   DrawFrame();
 
@@ -4484,14 +4491,14 @@
   // make any other changes that would cause damage. This mimics updating the
   // SurfaceLayer for an offscreen tab.
   host_impl_->active_tree()->BuildPropertyTreesForTesting();
-  host_impl_->active_tree()->SetSurfaceLayerIds({surface_id});
+  host_impl_->active_tree()->SetSurfaceRanges({viz::SurfaceRange(surface_id)});
   DrawFrame();
 
   {
     const viz::CompositorFrameMetadata& metadata =
         fake_layer_tree_frame_sink->last_sent_frame()->metadata;
     EXPECT_THAT(metadata.referenced_surfaces,
-                testing::UnorderedElementsAre(surface_id));
+                testing::UnorderedElementsAre(viz::SurfaceRange(surface_id)));
   }
 }
 
@@ -6674,10 +6681,6 @@
 }
 
 TEST_F(LayerTreeHostImplTimelinesTest, ScrollAnimatedLatchToChild) {
-  // Enable wheel scroll latching flag.
-  TestInputHandlerClient input_handler_client;
-  host_impl_->BindToClient(&input_handler_client, true);
-
   // Scroll a child layer beyond its maximum scroll range and make sure the
   // parent layer isn't scrolled.
   gfx::Size surface_size(100, 100);
@@ -7371,7 +7374,7 @@
   scroll_layer->SetScrollable(gfx::Size(10, 20));
   host_impl_->active_tree()->BuildPropertyTreesForTesting();
 
-  host_impl_->BindToClient(&scroll_watcher, false);
+  host_impl_->BindToClient(&scroll_watcher);
 
   gfx::Vector2dF initial_scroll_delta(10.f, 10.f);
   scroll_layer->layer_tree_impl()
diff --git a/cc/trees/layer_tree_host_unittest_scroll.cc b/cc/trees/layer_tree_host_unittest_scroll.cc
index 1eeb4ca..d5fa8c4 100644
--- a/cc/trees/layer_tree_host_unittest_scroll.cc
+++ b/cc/trees/layer_tree_host_unittest_scroll.cc
@@ -1851,7 +1851,7 @@
 
   void BindInputHandler(base::WeakPtr<InputHandler> input_handler) {
     DCHECK(task_runner_provider()->IsImplThread());
-    input_handler->BindToClient(&input_handler_client_, false);
+    input_handler->BindToClient(&input_handler_client_);
     scroll_elasticity_helper_ = input_handler->CreateScrollElasticityHelper();
     DCHECK(scroll_elasticity_helper_);
   }
diff --git a/cc/trees/layer_tree_impl.cc b/cc/trees/layer_tree_impl.cc
index c0dc888..d8c0cb3 100644
--- a/cc/trees/layer_tree_impl.cc
+++ b/cc/trees/layer_tree_impl.cc
@@ -90,7 +90,7 @@
       needs_update_draw_properties_(true),
       scrollbar_geometries_need_update_(false),
       needs_full_tree_sync_(true),
-      needs_surface_ids_sync_(false),
+      needs_surface_ranges_sync_(false),
       next_activation_forces_redraw_(false),
       has_ever_been_drawn_(false),
       handle_visibility_changed_(false),
@@ -425,12 +425,12 @@
   target_tree->SetCurrentlyScrollingNode(scrolling_node);
 }
 
-void LayerTreeImpl::PushSurfaceIdsTo(LayerTreeImpl* target_tree) {
-  if (needs_surface_ids_sync()) {
-    target_tree->ClearSurfaceLayerIds();
-    target_tree->SetSurfaceLayerIds(SurfaceLayerIds());
+void LayerTreeImpl::PushSurfaceRangesTo(LayerTreeImpl* target_tree) {
+  if (needs_surface_ranges_sync()) {
+    target_tree->ClearSurfaceRanges();
+    target_tree->SetSurfaceRanges(SurfaceRanges());
     // Reset for next update
-    set_needs_surface_ids_sync(false);
+    set_needs_surface_ranges_sync(false);
   }
 }
 
@@ -439,7 +439,7 @@
   // The request queue should have been processed and does not require a push.
   DCHECK_EQ(ui_resource_request_queue_.size(), 0u);
 
-  PushSurfaceIdsTo(target_tree);
+  PushSurfaceRangesTo(target_tree);
   target_tree->property_trees()->scroll_tree.PushScrollUpdatesFromPendingTree(
       &property_trees_, target_tree);
 
@@ -1328,20 +1328,20 @@
   return iter != layer_id_map_.end() ? iter->second : nullptr;
 }
 
-void LayerTreeImpl::SetSurfaceLayerIds(
-    const base::flat_set<viz::SurfaceId>& surface_layer_ids) {
-  DCHECK(surface_layer_ids_.empty());
-  surface_layer_ids_ = surface_layer_ids;
-  needs_surface_ids_sync_ = true;
+void LayerTreeImpl::SetSurfaceRanges(
+    const base::flat_set<viz::SurfaceRange> surface_ranges) {
+  DCHECK(surface_layer_ranges_.empty());
+  surface_layer_ranges_ = std::move(surface_ranges);
+  needs_surface_ranges_sync_ = true;
 }
 
-const base::flat_set<viz::SurfaceId>& LayerTreeImpl::SurfaceLayerIds() const {
-  return surface_layer_ids_;
+const base::flat_set<viz::SurfaceRange>& LayerTreeImpl::SurfaceRanges() const {
+  return surface_layer_ranges_;
 }
 
-void LayerTreeImpl::ClearSurfaceLayerIds() {
-  surface_layer_ids_.clear();
-  needs_surface_ids_sync_ = true;
+void LayerTreeImpl::ClearSurfaceRanges() {
+  surface_layer_ranges_.clear();
+  needs_surface_ranges_sync_ = true;
 }
 
 void LayerTreeImpl::AddLayerShouldPushProperties(LayerImpl* layer) {
diff --git a/cc/trees/layer_tree_impl.h b/cc/trees/layer_tree_impl.h
index 2b862f3..6f54954 100644
--- a/cc/trees/layer_tree_impl.h
+++ b/cc/trees/layer_tree_impl.h
@@ -180,7 +180,7 @@
 
   void PushPropertyTreesTo(LayerTreeImpl* tree_impl);
   void PushPropertiesTo(LayerTreeImpl* tree_impl);
-  void PushSurfaceIdsTo(LayerTreeImpl* tree_impl);
+  void PushSurfaceRangesTo(LayerTreeImpl* tree_impl);
 
   void MoveChangeTrackingToLayers();
 
@@ -375,9 +375,9 @@
   void set_needs_full_tree_sync(bool needs) { needs_full_tree_sync_ = needs; }
   bool needs_full_tree_sync() const { return needs_full_tree_sync_; }
 
-  bool needs_surface_ids_sync() const { return needs_surface_ids_sync_; }
-  void set_needs_surface_ids_sync(bool needs_surface_ids_sync) {
-    needs_surface_ids_sync_ = needs_surface_ids_sync;
+  bool needs_surface_ranges_sync() const { return needs_surface_ranges_sync_; }
+  void set_needs_surface_ranges_sync(bool needs_surface_ranges_sync) {
+    needs_surface_ranges_sync_ = needs_surface_ranges_sync;
   }
 
   void ForceRedrawNextActivation() { next_activation_forces_redraw_ = true; }
@@ -405,10 +405,9 @@
   void AddToElementLayerList(ElementId element_id);
   void RemoveFromElementLayerList(ElementId element_id);
 
-  void SetSurfaceLayerIds(
-      const base::flat_set<viz::SurfaceId>& surface_layer_ids);
-  const base::flat_set<viz::SurfaceId>& SurfaceLayerIds() const;
-  void ClearSurfaceLayerIds();
+  void SetSurfaceRanges(const base::flat_set<viz::SurfaceRange> surface_ranges);
+  const base::flat_set<viz::SurfaceRange>& SurfaceRanges() const;
+  void ClearSurfaceRanges();
 
   void AddLayerShouldPushProperties(LayerImpl* layer);
   void RemoveLayerShouldPushProperties(LayerImpl* layer);
@@ -666,7 +665,7 @@
 
   std::vector<PictureLayerImpl*> picture_layers_;
 
-  base::flat_set<viz::SurfaceId> surface_layer_ids_;
+  base::flat_set<viz::SurfaceRange> surface_layer_ranges_;
 
   // List of render surfaces for the most recently prepared frame.
   RenderSurfaceList render_surface_list_;
@@ -685,7 +684,7 @@
   // structural differences relative to the active tree.
   bool needs_full_tree_sync_;
 
-  bool needs_surface_ids_sync_;
+  bool needs_surface_ranges_sync_;
 
   bool next_activation_forces_redraw_;
 
diff --git a/chrome/android/java/AndroidManifest.xml b/chrome/android/java/AndroidManifest.xml
index ab23dfc..cf31d1a 100644
--- a/chrome/android/java/AndroidManifest.xml
+++ b/chrome/android/java/AndroidManifest.xml
@@ -434,10 +434,14 @@
 
             <intent-filter android:priority="-1">
                 <category android:name="com.google.intent.category.DAYDREAM" />
+                <!-- Trick Android into thinking this intent filter contains data authorities, and
+                     so cannot serve as the default browser. crbug.com/847921 -->
+                <data android:host="*" />
                 {{ self.common_view_intent_shared_filter_body() }}
             </intent-filter>
             <intent-filter android:priority="-1">
                 <category android:name="com.google.intent.category.DAYDREAM" />
+                <data android:host="*" />
                 {{ self.common_view_intent_shared_filter_with_mime_body() }}
             </intent-filter>
             <intent-filter android:priority="-1">
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
index 9735c90..b92ac85 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
@@ -96,7 +96,6 @@
 import org.chromium.chrome.browser.metrics.ActivityTabStartupMetricsTracker;
 import org.chromium.chrome.browser.metrics.LaunchMetrics;
 import org.chromium.chrome.browser.metrics.UmaSessionStats;
-import org.chromium.chrome.browser.metrics.WebApkUma;
 import org.chromium.chrome.browser.modaldialog.AppModalPresenter;
 import org.chromium.chrome.browser.modaldialog.ModalDialogManager;
 import org.chromium.chrome.browser.multiwindow.MultiWindowUtils;
@@ -2137,9 +2136,7 @@
             try {
                 context.startActivity(launchIntent);
                 RecordUserAction.record("MobileMenuOpenWebApk");
-                WebApkUma.recordWebApkOpenAttempt(WebApkUma.WEBAPK_OPEN_LAUNCH_SUCCESS);
             } catch (ActivityNotFoundException e) {
-                WebApkUma.recordWebApkOpenAttempt(WebApkUma.WEBAPK_OPEN_ACTIVITY_NOT_FOUND);
                 Toast.makeText(context, R.string.open_webapk_failed, Toast.LENGTH_SHORT).show();
             }
         } else if (id == R.id.request_desktop_site_id || id == R.id.request_desktop_site_check_id) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuAdapter.java
index d9fb1a9..93d9d92 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuAdapter.java
@@ -9,6 +9,7 @@
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.graphics.drawable.Drawable;
+import android.support.annotation.IntDef;
 import android.support.v7.content.res.AppCompatResources;
 import android.text.TextUtils;
 import android.view.LayoutInflater;
@@ -27,6 +28,8 @@
 import org.chromium.ui.base.LocalizationUtils;
 import org.chromium.ui.interpolators.BakedBezierInterpolator;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.List;
 
 /**
@@ -41,41 +44,41 @@
  * 3) Hope that the icon row still fits on small phones.
  */
 class AppMenuAdapter extends BaseAdapter {
-    /**
-     * Regular Android menu item that contains a title and an icon if icon is specified.
-     */
-    private static final int STANDARD_MENU_ITEM = 0;
-
-    /**
-     * Menu item that has two buttons, the first one is a title and the second one is an icon.
-     * It is different from the regular menu item because it contains two separate buttons.
-     */
-    private static final int TITLE_BUTTON_MENU_ITEM = 1;
-
-    /**
-     * Menu item that has three buttons. Every one of these buttons is displayed as an icon.
-     */
-    private static final int THREE_BUTTON_MENU_ITEM = 2;
-
-    /**
-     * Menu item that has four buttons. Every one of these buttons is displayed as an icon.
-     */
-    private static final int FOUR_BUTTON_MENU_ITEM = 3;
-
-    /**
-     * Menu item that has five buttons. Every one of these buttons is displayed as an icon.
-     */
-    private static final int FIVE_BUTTON_MENU_ITEM = 4;
-
-    /**
-     * Menu item for updating Chrome; uses a custom layout.
-     */
-    private static final int UPDATE_MENU_ITEM = 5;
-
-    /**
-     * The number of view types specified above.  If you add a view type you MUST increment this.
-     */
-    private static final int VIEW_TYPE_COUNT = 6;
+    @IntDef({MenuItemType.STANDARD, MenuItemType.TITLE_BUTTON, MenuItemType.THREE_BUTTON,
+            MenuItemType.FOUR_BUTTON, MenuItemType.FIVE_BUTTON, MenuItemType.UPDATE})
+    @Retention(RetentionPolicy.SOURCE)
+    private @interface MenuItemType {
+        /**
+         * Regular Android menu item that contains a title and an icon if icon is specified.
+         */
+        int STANDARD = 0;
+        /**
+         * Menu item for updating Chrome; uses a custom layout.
+         */
+        int UPDATE = 1;
+        /**
+         * Menu item that has two buttons, the first one is a title and the second one is an icon.
+         * It is different from the regular menu item because it contains two separate buttons.
+         */
+        int TITLE_BUTTON = 2;
+        /**
+         * Menu item that has three buttons. Every one of these buttons is displayed as an icon.
+         */
+        int THREE_BUTTON = 3;
+        /**
+         * Menu item that has four buttons. Every one of these buttons is displayed as an icon.
+         */
+        int FOUR_BUTTON = 4;
+        /**
+         * Menu item that has five buttons. Every one of these buttons is displayed as an icon.
+         */
+        int FIVE_BUTTON = 5;
+        /**
+         * The number of view types specified above.  If you add a view type you MUST increment
+         * this.
+         */
+        int NUM_ENTRIES = 6;
+    }
 
     /** IDs of all of the buttons in icon_row_menu_item.xml. */
     private static final int[] BUTTON_IDS = {
@@ -117,26 +120,26 @@
 
     @Override
     public int getViewTypeCount() {
-        return VIEW_TYPE_COUNT;
+        return MenuItemType.NUM_ENTRIES;
     }
 
     @Override
-    public int getItemViewType(int position) {
+    public @MenuItemType int getItemViewType(int position) {
         MenuItem item = getItem(position);
         int viewCount = item.hasSubMenu() ? item.getSubMenu().size() : 1;
 
         if (item.getItemId() == R.id.update_menu_id) {
-            return UPDATE_MENU_ITEM;
-        } else if (viewCount == 5) {
-            return FIVE_BUTTON_MENU_ITEM;
-        } else if (viewCount == 4) {
-            return FOUR_BUTTON_MENU_ITEM;
-        } else if (viewCount == 3) {
-            return THREE_BUTTON_MENU_ITEM;
+            return MenuItemType.UPDATE;
         } else if (viewCount == 2) {
-            return TITLE_BUTTON_MENU_ITEM;
+            return MenuItemType.TITLE_BUTTON;
+        } else if (viewCount == 3) {
+            return MenuItemType.THREE_BUTTON;
+        } else if (viewCount == 4) {
+            return MenuItemType.FOUR_BUTTON;
+        } else if (viewCount == 5) {
+            return MenuItemType.FIVE_BUTTON;
         }
-        return STANDARD_MENU_ITEM;
+        return MenuItemType.STANDARD;
     }
 
     @Override
@@ -156,7 +159,7 @@
     public View getView(int position, View convertView, ViewGroup parent) {
         final MenuItem item = getItem(position);
         switch (getItemViewType(position)) {
-            case STANDARD_MENU_ITEM: {
+            case MenuItemType.STANDARD: {
                 StandardMenuItemViewHolder holder = null;
                 if (convertView == null
                         || !(convertView.getTag() instanceof StandardMenuItemViewHolder)) {
@@ -172,11 +175,10 @@
                 } else {
                     holder = (StandardMenuItemViewHolder) convertView.getTag();
                 }
-
                 setupStandardMenuItemViewHolder(holder, convertView, item);
                 break;
             }
-            case UPDATE_MENU_ITEM: {
+            case MenuItemType.UPDATE: {
                 CustomMenuItemViewHolder holder = null;
                 if (convertView == null
                         || !(convertView.getTag() instanceof CustomMenuItemViewHolder)) {
@@ -193,7 +195,6 @@
                 } else {
                     holder = (CustomMenuItemViewHolder) convertView.getTag();
                 }
-
                 setupStandardMenuItemViewHolder(holder, convertView, item);
                 String summary = UpdateMenuItemHelper.getInstance().getMenuItemSummaryText(
                         mInflater.getContext());
@@ -202,22 +203,18 @@
                 } else {
                     holder.summary.setText(summary);
                 }
-
                 break;
             }
-            case THREE_BUTTON_MENU_ITEM: {
+            case MenuItemType.THREE_BUTTON:
                 convertView = createMenuItemRow(convertView, parent, item, 3);
                 break;
-            }
-            case FOUR_BUTTON_MENU_ITEM: {
+            case MenuItemType.FOUR_BUTTON:
                 convertView = createMenuItemRow(convertView, parent, item, 4);
                 break;
-            }
-            case FIVE_BUTTON_MENU_ITEM: {
+            case MenuItemType.FIVE_BUTTON:
                 convertView = createMenuItemRow(convertView, parent, item, 5);
                 break;
-            }
-            case TITLE_BUTTON_MENU_ITEM: {
+            case MenuItemType.TITLE_BUTTON: {
                 assert item.hasSubMenu();
                 final MenuItem titleItem = item.getSubMenu().getItem(0);
                 final MenuItem subItem = item.getSubMenu().getItem(1);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuDragHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuDragHelper.java
index dbb48f8..9188361 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuDragHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuDragHelper.java
@@ -9,6 +9,7 @@
 import android.app.Activity;
 import android.content.res.Resources;
 import android.graphics.Rect;
+import android.support.annotation.IntDef;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewConfiguration;
@@ -20,6 +21,8 @@
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.chrome.R;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.concurrent.TimeUnit;
 
@@ -35,9 +38,13 @@
     private final AppMenu mAppMenu;
 
     // Internally used action constants for dragging.
-    private static final int ITEM_ACTION_HIGHLIGHT = 0;
-    private static final int ITEM_ACTION_PERFORM = 1;
-    private static final int ITEM_ACTION_CLEAR_HIGHLIGHT_ALL = 2;
+    @IntDef({ItemAction.HIGHLIGHT, ItemAction.PERFORM, ItemAction.CLEAR_HIGHLIGHT_ALL})
+    @Retention(RetentionPolicy.SOURCE)
+    private @interface ItemAction {
+        int HIGHLIGHT = 0;
+        int PERFORM = 1;
+        int CLEAR_HIGHLIGHT_ALL = 2;
+    }
 
     private static final float AUTO_SCROLL_AREA_MAX_RATIO = 0.25f;
 
@@ -83,8 +90,8 @@
 
             // Force touch move event to highlight items correctly for the scrolled position.
             if (!Float.isNaN(mLastTouchX) && !Float.isNaN(mLastTouchY)) {
-                menuItemAction(Math.round(mLastTouchX), Math.round(mLastTouchY),
-                        ITEM_ACTION_HIGHLIGHT);
+                menuItemAction(
+                        Math.round(mLastTouchX), Math.round(mLastTouchY), ItemAction.HIGHLIGHT);
             }
         });
 
@@ -122,7 +129,7 @@
         // needed to by menuItemAction. Only clear highlighting if the menu is still showing.
         // See crbug.com/589805.
         if (mAppMenu.getPopup().isShowing()) {
-            menuItemAction(0, 0, ITEM_ACTION_CLEAR_HIGHLIGHT_ALL);
+            menuItemAction(0, 0, ItemAction.CLEAR_HIGHLIGHT_ALL);
         }
         mDragScrolling.cancel();
     }
@@ -175,14 +182,15 @@
         if (!mDragScrolling.isRunning()) return false;
 
         boolean didPerformClick = false;
-        int itemAction = ITEM_ACTION_CLEAR_HIGHLIGHT_ALL;
+        @ItemAction
+        int itemAction = ItemAction.CLEAR_HIGHLIGHT_ALL;
         switch (eventActionMasked) {
             case MotionEvent.ACTION_DOWN:
             case MotionEvent.ACTION_MOVE:
-                itemAction = ITEM_ACTION_HIGHLIGHT;
+                itemAction = ItemAction.HIGHLIGHT;
                 break;
             case MotionEvent.ACTION_UP:
-                itemAction = ITEM_ACTION_PERFORM;
+                itemAction = ItemAction.PERFORM;
                 break;
             default:
                 break;
@@ -231,7 +239,7 @@
      * @param action  Action type to perform, it should be one of ITEM_ACTION_* constants.
      * @return true whether or not a menu item is performed (executed).
      */
-    private boolean menuItemAction(int screenX, int screenY, int action) {
+    private boolean menuItemAction(int screenX, int screenY, @ItemAction int action) {
         ListView listView = mAppMenu.getListView();
 
         // Starting M, we have a popup menu animation that slides down. If we process dragging
@@ -268,17 +276,17 @@
                     && getScreenVisibleRect(itemView).contains(screenX, screenY);
 
             switch (action) {
-                case ITEM_ACTION_HIGHLIGHT:
+                case ItemAction.HIGHLIGHT:
                     itemView.setPressed(shouldPerform);
                     break;
-                case ITEM_ACTION_PERFORM:
+                case ItemAction.PERFORM:
                     if (shouldPerform) {
                         RecordUserAction.record("MobileUsingMenuBySwButtonDragging");
                         itemView.performClick();
                         didPerformClick = true;
                     }
                     break;
-                case ITEM_ACTION_CLEAR_HIGHLIGHT_ALL:
+                case ItemAction.CLEAR_HIGHLIGHT_ALL:
                     itemView.setPressed(false);
                     break;
                 default:
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelBase.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelBase.java
index b6f049f..4abdb98b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelBase.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelBase.java
@@ -717,9 +717,7 @@
         PanelState nextState = PanelState.values()[0];
         PanelState prevState = nextState;
         for (PanelState state : PanelState.values()) {
-            if (!isValidUiState(state)) {
-                continue;
-            }
+            if (!isValidUiState(state)) continue;
             prevState = nextState;
             nextState = state;
             // The values in PanelState are ascending, they should be kept that way in order for
@@ -830,9 +828,7 @@
         // Iterate over all states and find the largest one which is being
         // transitioned to/from.
         for (PanelState state : PanelState.values()) {
-            if (!isValidUiState(state)) {
-                continue;
-            }
+            if (!isValidUiState(state)) continue;
             if (panelHeight <= getPanelHeightFromState(state)) {
                 stateFound = state;
                 break;
@@ -1024,19 +1020,11 @@
     }
 
     private float getBarHeightExpanded() {
-        if (isFullWidthSizePanel()) {
-            return mBarHeightExpanded;
-        } else {
-            return mBarHeightPeeking;
-        }
+        return isFullWidthSizePanel() ? mBarHeightExpanded : mBarHeightPeeking;
     }
 
     private float getBarHeightMaximized() {
-        if (isFullWidthSizePanel()) {
-            return mBarHeightMaximized;
-        } else {
-            return mBarHeightPeeking;
-        }
+        return isFullWidthSizePanel() ? mBarHeightMaximized : mBarHeightPeeking;
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelContent.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelContent.java
index c30bf6a8..c79256b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelContent.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelContent.java
@@ -376,7 +376,6 @@
         if (mContentViewCore != null) {
             // Native destroy will call up to destroy the Java WebContents.
             nativeDestroyWebContents(mNativeOverlayPanelContentPtr);
-            mContentViewCore.destroy();
             mContentViewCore = null;
             mWebContents = null;
             if (mWebContentsObserver != null) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelTextViewInflater.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelTextViewInflater.java
index 97eb0e8..c7c7fd7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelTextViewInflater.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelTextViewInflater.java
@@ -6,7 +6,6 @@
 
 import android.annotation.SuppressLint;
 import android.content.Context;
-import android.util.LayoutDirection;
 import android.view.Gravity;
 import android.view.View;
 import android.view.View.OnLayoutChangeListener;
@@ -79,10 +78,7 @@
     private void adjustViewDirection(TextView textView) {
         float textWidth = textView.getPaint().measureText(textView.getText().toString());
         if (textWidth < SHORTNESS_FACTOR * textView.getWidth()) {
-            int layoutDirection =
-                    LocalizationUtils.isLayoutRtl() ? LayoutDirection.RTL : LayoutDirection.LTR;
-            if (layoutDirection == LayoutDirection.LTR) textView.setGravity(Gravity.LEFT);
-            if (layoutDirection == LayoutDirection.RTL) textView.setGravity(Gravity.RIGHT);
+            textView.setGravity(LocalizationUtils.isLayoutRtl() ? Gravity.RIGHT : Gravity.LEFT);
         }
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/externalnav/ExternalNavigationHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/externalnav/ExternalNavigationHandler.java
index 4c0c49a..bd2472b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/externalnav/ExternalNavigationHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/externalnav/ExternalNavigationHandler.java
@@ -777,7 +777,7 @@
             return false;
         }
         return ExternalNavigationDelegateImpl
-                       .getSpecializedHandlersWithFilter(handlers, appId, null)
+                       .getSpecializedHandlersWithFilter(handlers, appId, intent)
                        .size()
                 > 0;
     }
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
index 1c95ba66..d2a085a 100644
--- 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
@@ -7,6 +7,7 @@
 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.AppHooks;
@@ -26,11 +27,19 @@
     public void onReceive(Context context, Intent intent) {
         if (intent == null || !Intent.ACTION_MEDIA_BUTTON.equals(intent.getAction())
                 || !intent.hasExtra(Intent.EXTRA_KEY_EVENT)) {
-            Log.i(TAG, "Received unsupported intent: " + intent);
+            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());
-        Log.i(TAG, "Receive broadcast message, starting foreground service");
         AppHooks.get().startForegroundService(intent);
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/metrics/WebApkUma.java b/chrome/android/java/src/org/chromium/chrome/browser/metrics/WebApkUma.java
index dc38fb0..1d8f6f9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/metrics/WebApkUma.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/metrics/WebApkUma.java
@@ -16,16 +16,12 @@
 import org.chromium.base.AsyncTask;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.metrics.RecordHistogram;
-import org.chromium.chrome.browser.preferences.website.SiteSettingsCategory;
-import org.chromium.chrome.browser.preferences.website.Website;
-import org.chromium.chrome.browser.preferences.website.WebsitePermissionsFetcher;
 import org.chromium.chrome.browser.util.ConversionUtils;
 import org.chromium.webapk.lib.common.WebApkConstants;
 
 import java.io.File;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.util.Collection;
 import java.util.HashSet;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
@@ -164,16 +160,6 @@
                 HISTOGRAM_LAUNCH_TO_SPLASHSCREEN_HIDDEN, durationMs, TimeUnit.MILLISECONDS);
     }
 
-    /**
-     * When a user presses on the "Open WebAPK" menu item, this records whether the WebAPK was
-     * opened successfully.
-     * @param type Result of trying to open WebAPK.
-     */
-    public static void recordWebApkOpenAttempt(int type) {
-        assert type >= 0 && type < WEBAPK_OPEN_MAX;
-        RecordHistogram.recordEnumeratedHistogram("WebApk.OpenFromMenu", type, WEBAPK_OPEN_MAX);
-    }
-
     /** Records whether a WebAPK has permission to display notifications. */
     public static void recordNotificationPermissionStatus(boolean permissionEnabled) {
         RecordHistogram.recordBooleanHistogram(
@@ -212,12 +198,6 @@
                 "WebApk.Session.TotalDuration", duration, TimeUnit.MILLISECONDS);
     }
 
-    /** Records the amount of time that it takes to bind to the play install service. */
-    public static void recordGooglePlayBindDuration(long durationMs) {
-        RecordHistogram.recordTimesHistogram(
-                "WebApk.Install.GooglePlayBindDuration", durationMs, TimeUnit.MILLISECONDS);
-    }
-
     /** Records the current Shell APK version. */
     public static void recordShellApkVersion(int shellApkVersion, String packageName) {
         String name = packageName.startsWith(WebApkConstants.WEBAPK_PACKAGE_PREFIX)
@@ -312,20 +292,12 @@
 
             @Override
             protected void onPostExecute(Void result) {
-                logSpaceUsageUMA(mAvailableSpaceInByte, mCacheSizeInByte);
+                logSpaceUsageUMAOnDataAvailable(mAvailableSpaceInByte, mCacheSizeInByte);
             }
         }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
     }
 
-    private static void logSpaceUsageUMA(long availableSpaceInByte, long cacheSizeInByte) {
-        WebsitePermissionsFetcher fetcher = new WebsitePermissionsFetcher(
-                new UnimportantStorageSizeCalculator(availableSpaceInByte, cacheSizeInByte));
-        fetcher.fetchPreferencesForCategory(
-                SiteSettingsCategory.createFromType(SiteSettingsCategory.Type.USE_STORAGE));
-    }
-
-    private static void logSpaceUsageUMAOnDataAvailable(
-            long spaceSize, long cacheSize, long unimportantSiteSize) {
+    private static void logSpaceUsageUMAOnDataAvailable(long spaceSize, long cacheSize) {
         RecordHistogram.recordSparseSlowlyHistogram(
                 "WebApk.Install.AvailableSpace.Fail", roundByteToMb(spaceSize));
 
@@ -333,19 +305,8 @@
                 "WebApk.Install.ChromeCacheSize.Fail", roundByteToMb(cacheSize));
 
         RecordHistogram.recordSparseSlowlyHistogram(
-                "WebApk.Install.ChromeUnimportantStorage.Fail", roundByteToMb(unimportantSiteSize));
-
-        RecordHistogram.recordSparseSlowlyHistogram(
                 "WebApk.Install.AvailableSpaceAfterFreeUpCache.Fail",
                 roundByteToMb(spaceSize + cacheSize));
-
-        RecordHistogram.recordSparseSlowlyHistogram(
-                "WebApk.Install.AvailableSpaceAfterFreeUpUnimportantStorage.Fail",
-                roundByteToMb(spaceSize + unimportantSiteSize));
-
-        RecordHistogram.recordSparseSlowlyHistogram(
-                "WebApk.Install.AvailableSpaceAfterFreeUpAll.Fail",
-                roundByteToMb(spaceSize + cacheSize + unimportantSiteSize));
     }
 
     private static int roundByteToMb(long bytes) {
@@ -439,31 +400,4 @@
 
         return Math.min(minFreeBytes, minFreePercentInBytes);
     }
-
-    private static class UnimportantStorageSizeCalculator
-            implements WebsitePermissionsFetcher.WebsitePermissionsCallback {
-        private long mAvailableSpaceInByte;
-        private long mCacheSizeInByte;
-
-        UnimportantStorageSizeCalculator(long availableSpaceInByte, long cacheSizeInByte) {
-            mAvailableSpaceInByte = availableSpaceInByte;
-            mCacheSizeInByte = cacheSizeInByte;
-        }
-        @Override
-        public void onWebsitePermissionsAvailable(Collection<Website> sites) {
-            long siteStorageSize = 0;
-            long importantSiteStorageTotal = 0;
-            for (Website site : sites) {
-                siteStorageSize += site.getTotalUsage();
-                if (site.getLocalStorageInfo() != null
-                        && site.getLocalStorageInfo().isDomainImportant()) {
-                    importantSiteStorageTotal += site.getTotalUsage();
-                }
-            }
-
-            long unimportantSiteStorageTotal = siteStorageSize - importantSiteStorageTotal;
-            logSpaceUsageUMAOnDataAvailable(
-                    mAvailableSpaceInByte, mCacheSizeInByte, unimportantSiteStorageTotal);
-        }
-    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/NotificationsPreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/NotificationsPreferences.java
index 1141ad3..7a42cb1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/NotificationsPreferences.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/NotificationsPreferences.java
@@ -83,6 +83,7 @@
 
         mFromWebsitesPref.setSummary(ContentSettingsResources.getCategorySummary(
                 ContentSettingsType.CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
-                PrefServiceBridge.getInstance().isNotificationsEnabled()));
+                PrefServiceBridge.getInstance().isCategoryEnabled(
+                        ContentSettingsType.CONTENT_SETTINGS_TYPE_NOTIFICATIONS)));
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/PrefServiceBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/PrefServiceBridge.java
index 5e82b0db..f991975 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/PrefServiceBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/PrefServiceBridge.java
@@ -214,27 +214,16 @@
      */
     @CalledByNative
     public static String[] getAndroidPermissionsForContentSetting(int contentSettingType) {
-        if (contentSettingType == ContentSettingsType.CONTENT_SETTINGS_TYPE_GEOLOCATION) {
-            return Arrays.copyOf(LOCATION_PERMISSIONS, LOCATION_PERMISSIONS.length);
+        switch (contentSettingType) {
+            case ContentSettingsType.CONTENT_SETTINGS_TYPE_GEOLOCATION:
+                return Arrays.copyOf(LOCATION_PERMISSIONS, LOCATION_PERMISSIONS.length);
+            case ContentSettingsType.CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC:
+                return Arrays.copyOf(MICROPHONE_PERMISSIONS, MICROPHONE_PERMISSIONS.length);
+            case ContentSettingsType.CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA:
+                return Arrays.copyOf(CAMERA_PERMISSIONS, CAMERA_PERMISSIONS.length);
+            default:
+                return EMPTY_PERMISSIONS;
         }
-        if (contentSettingType == ContentSettingsType.CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC) {
-            return Arrays.copyOf(MICROPHONE_PERMISSIONS, MICROPHONE_PERMISSIONS.length);
-        }
-        if (contentSettingType == ContentSettingsType.CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA) {
-            return Arrays.copyOf(CAMERA_PERMISSIONS, CAMERA_PERMISSIONS.length);
-        }
-        return EMPTY_PERMISSIONS;
-    }
-
-    /**
-     * @return Whether autoplay is enabled.
-     */
-    public boolean isAutoplayEnabled() {
-        return nativeGetAutoplayEnabled();
-    }
-
-    public boolean isAcceptCookiesEnabled() {
-        return nativeGetAcceptCookiesEnabled();
     }
 
     /**
@@ -283,13 +272,6 @@
     }
 
     /**
-     * @return Whether notifications are enabled.
-     */
-    public boolean isNotificationsEnabled() {
-        return nativeGetNotificationsEnabled();
-    }
-
-    /**
      * @return Whether vibration is enabled for notifications.
      */
     public boolean isNotificationsVibrateEnabled() {
@@ -344,14 +326,6 @@
     }
 
     /**
-     * @return true if JavaScript is enabled. It may return the temporary value set by
-     * {@link #setJavaScriptEnabled}. The default is true.
-     */
-    public boolean javaScriptEnabled() {
-        return isContentSettingEnabled(ContentSettingsType.CONTENT_SETTINGS_TYPE_JAVASCRIPT);
-    }
-
-    /**
      * @return Whether JavaScript is managed by policy.
      */
     public boolean javaScriptManaged() {
@@ -366,48 +340,6 @@
     }
 
     /**
-     * @return true if background sync is enabled.
-     */
-    public boolean isBackgroundSyncAllowed() {
-        return nativeGetBackgroundSyncEnabled();
-    }
-
-    /**
-     * @return true if websites are allowed to read from the clipboard.
-     */
-    public boolean isClipboardEnabled() {
-        return isContentSettingEnabled(ContentSettingsType.CONTENT_SETTINGS_TYPE_CLIPBOARD_READ);
-    }
-
-    /**
-     * @return true if websites are allowed to access device's sensors.
-     */
-    public boolean areSensorsEnabled() {
-        return nativeGetSensorsEnabled();
-    }
-
-    /**
-     * @return true if websites are allowed to play sound.
-     */
-    public boolean isSoundEnabled() {
-        return nativeGetSoundEnabled();
-    }
-
-    /**
-     * @return true if websites are allowed to request permission to access USB devices.
-     */
-    public boolean isUsbEnabled() {
-        return isContentSettingEnabled(ContentSettingsType.CONTENT_SETTINGS_TYPE_USB_GUARD);
-    }
-
-    /**
-     * Sets the preference that controls protected media identifier.
-     */
-    public void setProtectedMediaIdentifierEnabled(boolean enabled) {
-        nativeSetProtectedMediaIdentifierEnabled(enabled);
-    }
-
-    /**
      * Sets the preference that controls translate
      */
     public void setTranslateEnabled(boolean enabled) {
@@ -429,20 +361,6 @@
     }
 
     /**
-     * Enable or disable JavaScript.
-     */
-    public void setJavaScriptEnabled(boolean enabled) {
-        setContentSettingEnabled(ContentSettingsType.CONTENT_SETTINGS_TYPE_JAVASCRIPT, enabled);
-    }
-
-    /**
-     * Enable or disable background sync.
-     */
-    public void setBackgroundSyncEnabled(boolean enabled) {
-        nativeSetBackgroundSyncEnabled(enabled);
-    }
-
-    /**
      * @return the last account id associated with sync.
      */
     public String getSyncLastAccountId() {
@@ -625,13 +543,6 @@
     }
 
     /**
-     * @return Whether or not the protected media identifier is enabled.
-     */
-    public boolean isProtectedMediaIdentifierEnabled() {
-        return nativeGetProtectedMediaIdentifierEnabled();
-    }
-
-    /**
      * @return true if translate is enabled, false otherwise.
      */
     public boolean isTranslateEnabled() {
@@ -699,8 +610,6 @@
         nativeSetBrowsingDataDeletionTimePeriod(clearBrowsingDataTab, timePeriod);
     }
 
-
-
     /**
      * @return The index of the tab last visited by the user in the CBD dialog.
      *         Index 0 is for the basic tab, 1 is the advanced tab.
@@ -717,22 +626,10 @@
         nativeSetLastClearBrowsingDataTab(tabIndex);
     }
 
-    public void setAllowCookiesEnabled(boolean allow) {
-        nativeSetAllowCookiesEnabled(allow);
-    }
-
-    public void setAutoplayEnabled(boolean allow) {
-        nativeSetAutoplayEnabled(allow);
-    }
-
     public void setBlockThirdPartyCookiesEnabled(boolean enabled) {
         nativeSetBlockThirdPartyCookiesEnabled(enabled);
     }
 
-    public void setClipboardEnabled(boolean allow) {
-        nativeSetClipboardEnabled(allow);
-    }
-
     public void setDoNotTrackEnabled(boolean enabled) {
         nativeSetDoNotTrackEnabled(enabled);
     }
@@ -745,41 +642,14 @@
         nativeSetPasswordManagerAutoSigninEnabled(enabled);
     }
 
-    public void setNotificationsEnabled(boolean allow) {
-        nativeSetNotificationsEnabled(allow);
-    }
-
     public void setNotificationsVibrateEnabled(boolean enabled) {
         nativeSetNotificationsVibrateEnabled(enabled);
     }
 
-    public void setAllowLocationEnabled(boolean allow) {
-        nativeSetAllowLocationEnabled(allow);
-    }
-
     public void setPasswordEchoEnabled(boolean enabled) {
         nativeSetPasswordEchoEnabled(enabled);
     }
 
-    public void setSensorsEnabled(boolean allow) {
-        nativeSetSensorsEnabled(allow);
-    }
-
-    public void setSoundEnabled(boolean allow) {
-        nativeSetSoundEnabled(allow);
-    }
-
-    public void setUsbEnabled(boolean allow) {
-        setContentSettingEnabled(ContentSettingsType.CONTENT_SETTINGS_TYPE_USB_GUARD, allow);
-    }
-
-    /**
-     * @return The setting if popups are enabled
-     */
-    public boolean popupsEnabled() {
-        return isContentSettingEnabled(ContentSettingsType.CONTENT_SETTINGS_TYPE_POPUPS);
-    }
-
     /**
      * @return Whether the setting to allow popups is configured by policy
      */
@@ -788,42 +658,87 @@
     }
 
     /**
-     * Sets the preferences on whether to enable/disable popups
-     *
-     * @param allow attribute to enable/disable popups
+     * Sets the preferences on whether to enable/disable given setting.
      */
-    public void setAllowPopupsEnabled(boolean allow) {
-        setContentSettingEnabled(ContentSettingsType.CONTENT_SETTINGS_TYPE_POPUPS, allow);
+    public void setCategoryEnabled(int contentSettingsType, boolean allow) {
+        switch (contentSettingsType) {
+            case ContentSettingsType.CONTENT_SETTINGS_TYPE_ADS:
+            case ContentSettingsType.CONTENT_SETTINGS_TYPE_JAVASCRIPT:
+            case ContentSettingsType.CONTENT_SETTINGS_TYPE_POPUPS:
+            case ContentSettingsType.CONTENT_SETTINGS_TYPE_USB_GUARD:
+                setContentSettingEnabled(contentSettingsType, allow);
+                break;
+            case ContentSettingsType.CONTENT_SETTINGS_TYPE_AUTOPLAY:
+                nativeSetAutoplayEnabled(allow);
+                break;
+            case ContentSettingsType.CONTENT_SETTINGS_TYPE_BACKGROUND_SYNC:
+                nativeSetBackgroundSyncEnabled(allow);
+                break;
+            case ContentSettingsType.CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA:
+                nativeSetCameraEnabled(allow);
+                break;
+            case ContentSettingsType.CONTENT_SETTINGS_TYPE_CLIPBOARD_READ:
+                nativeSetClipboardEnabled(allow);
+                break;
+            case ContentSettingsType.CONTENT_SETTINGS_TYPE_COOKIES:
+                nativeSetAllowCookiesEnabled(allow);
+                break;
+            case ContentSettingsType.CONTENT_SETTINGS_TYPE_GEOLOCATION:
+                nativeSetAllowLocationEnabled(allow);
+                break;
+            case ContentSettingsType.CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC:
+                nativeSetMicEnabled(allow);
+                break;
+            case ContentSettingsType.CONTENT_SETTINGS_TYPE_NOTIFICATIONS:
+                nativeSetNotificationsEnabled(allow);
+                break;
+            case ContentSettingsType.CONTENT_SETTINGS_TYPE_PROTECTED_MEDIA_IDENTIFIER:
+                nativeSetProtectedMediaIdentifierEnabled(allow);
+                break;
+            case ContentSettingsType.CONTENT_SETTINGS_TYPE_SENSORS:
+                nativeSetSensorsEnabled(allow);
+                break;
+            case ContentSettingsType.CONTENT_SETTINGS_TYPE_SOUND:
+                nativeSetSoundEnabled(allow);
+                break;
+            default:
+                assert false;
+        }
     }
 
-    /**
-     * @return Whether ads are enabled / allowed on sites that tend to show intrusive ads.
-     */
-    public boolean adsEnabled() {
-        return isContentSettingEnabled(ContentSettingsType.CONTENT_SETTINGS_TYPE_ADS);
-    }
-
-    /**
-     * Sets the preferences on whether to enable/disable ads.
-     *
-     * @param allow attribute to enable ads / block ads if the site tends to show intrusive ads.
-     */
-    public void setAllowAdsEnabled(boolean allow) {
-        setContentSettingEnabled(ContentSettingsType.CONTENT_SETTINGS_TYPE_ADS, allow);
-    }
-
-    /**
-     * @return Whether the camera permission is enabled.
-     */
-    public boolean isCameraEnabled() {
-        return nativeGetCameraEnabled();
-    }
-
-    /**
-     * Sets the preferences on whether to enable/disable camera.
-     */
-    public void setCameraEnabled(boolean enabled) {
-        nativeSetCameraEnabled(enabled);
+    public boolean isCategoryEnabled(int contentSettingsType) {
+        switch (contentSettingsType) {
+            case ContentSettingsType.CONTENT_SETTINGS_TYPE_ADS:
+            case ContentSettingsType.CONTENT_SETTINGS_TYPE_CLIPBOARD_READ:
+            // Returns true if JavaScript is enabled. It may return the temporary value set by
+            // {@link #setJavaScriptEnabled}. The default is true.
+            case ContentSettingsType.CONTENT_SETTINGS_TYPE_JAVASCRIPT:
+            case ContentSettingsType.CONTENT_SETTINGS_TYPE_POPUPS:
+            // Returns true if websites are allowed to request permission to access USB devices.
+            case ContentSettingsType.CONTENT_SETTINGS_TYPE_USB_GUARD:
+                return isContentSettingEnabled(contentSettingsType);
+            case ContentSettingsType.CONTENT_SETTINGS_TYPE_AUTOPLAY:
+                return nativeGetAutoplayEnabled();
+            case ContentSettingsType.CONTENT_SETTINGS_TYPE_BACKGROUND_SYNC:
+                return nativeGetBackgroundSyncEnabled();
+            case ContentSettingsType.CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA:
+                return nativeGetCameraEnabled();
+            case ContentSettingsType.CONTENT_SETTINGS_TYPE_COOKIES:
+                return nativeGetAcceptCookiesEnabled();
+            case ContentSettingsType.CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC:
+                return nativeGetMicEnabled();
+            case ContentSettingsType.CONTENT_SETTINGS_TYPE_NOTIFICATIONS:
+                return nativeGetNotificationsEnabled();
+            case ContentSettingsType.CONTENT_SETTINGS_TYPE_PROTECTED_MEDIA_IDENTIFIER:
+                return nativeGetProtectedMediaIdentifierEnabled();
+            case ContentSettingsType.CONTENT_SETTINGS_TYPE_SENSORS:
+                return nativeGetSensorsEnabled();
+            case ContentSettingsType.CONTENT_SETTINGS_TYPE_SOUND:
+                return nativeGetSoundEnabled();
+            default:
+                assert false;
+                return false;
+        }
     }
 
     /**
@@ -842,13 +757,6 @@
     }
 
     /**
-     * @return Whether the microphone permission is enabled.
-     */
-    public boolean isMicEnabled() {
-        return nativeGetMicEnabled();
-    }
-
-    /**
      * Sets the preferences on whether to enable/disable microphone.
      */
     public void setMicEnabled(boolean enabled) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/SearchEngineAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/SearchEngineAdapter.java
index 17b3c37..35d32ff 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/SearchEngineAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/SearchEngineAdapter.java
@@ -356,9 +356,7 @@
                             : R.layout.search_engine,
                     null);
         }
-        if (itemViewType == VIEW_TYPE_DIVIDER) {
-            return view;
-        }
+        if (itemViewType == VIEW_TYPE_DIVIDER) return view;
 
         view.setOnClickListener(this);
         view.setTag(position);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/PrivacyPreferencesManager.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/PrivacyPreferencesManager.java
index 109afe9..1189e5a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/PrivacyPreferencesManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/PrivacyPreferencesManager.java
@@ -195,20 +195,15 @@
                             prefBandwidth);
                     if (BandwidthType.NEVER_PRERENDER.equals(prefetchBandwidthTypePref)) {
                         newValue = false;
-                    } else if (BandwidthType.PRERENDER_ON_WIFI.equals(prefetchBandwidthTypePref)) {
-                        newValue = true;
-                    } else if (BandwidthType.ALWAYS_PRERENDER.equals(prefetchBandwidthTypePref)) {
+                    } else if (BandwidthType.PRERENDER_ON_WIFI.equals(prefetchBandwidthTypePref)
+                            || BandwidthType.ALWAYS_PRERENDER.equals(prefetchBandwidthTypePref)) {
                         newValue = true;
                     }
                 }
             // Observe PREF_BANDWIDTH_NO_CELLULAR on devices without mobile network.
             } else {
                 if (mSharedPreferences.contains(PREF_BANDWIDTH_NO_CELLULAR_OLD)) {
-                    if (prefBandwidthNoCellular) {
-                        newValue = true;
-                    } else {
-                        newValue = false;
-                    }
+                    newValue = prefBandwidthNoCellular;
                 }
             }
             // Save new value in Chrome PrefService.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SingleCategoryPreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SingleCategoryPreferences.java
index a339dc9..9a1a768 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SingleCategoryPreferences.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SingleCategoryPreferences.java
@@ -36,6 +36,7 @@
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ContentSettingsType;
 import org.chromium.chrome.browser.help.HelpAndFeedback;
 import org.chromium.chrome.browser.media.cdm.MediaDrmCredentialManager;
 import org.chromium.chrome.browser.media.cdm.MediaDrmCredentialManager.MediaDrmCredentialManagerCallback;
@@ -446,39 +447,23 @@
         if (READ_WRITE_TOGGLE_KEY.equals(preference.getKey())) {
             assert !mCategory.isManaged();
 
-            if (mCategory.showSites(SiteSettingsCategory.Type.ADS)) {
-                PrefServiceBridge.getInstance().setAllowAdsEnabled((boolean) newValue);
-            } else if (mCategory.showSites(SiteSettingsCategory.Type.AUTOPLAY)) {
-                PrefServiceBridge.getInstance().setAutoplayEnabled((boolean) newValue);
-            } else if (mCategory.showSites(SiteSettingsCategory.Type.BACKGROUND_SYNC)) {
-                PrefServiceBridge.getInstance().setBackgroundSyncEnabled((boolean) newValue);
-            } else if (mCategory.showSites(SiteSettingsCategory.Type.CAMERA)) {
-                PrefServiceBridge.getInstance().setCameraEnabled((boolean) newValue);
-            } else if (mCategory.showSites(SiteSettingsCategory.Type.CLIPBOARD)) {
-                PrefServiceBridge.getInstance().setClipboardEnabled((boolean) newValue);
-            } else if (mCategory.showSites(SiteSettingsCategory.Type.COOKIES)) {
-                PrefServiceBridge.getInstance().setAllowCookiesEnabled((boolean) newValue);
-                updateThirdPartyCookiesCheckBox();
-            } else if (mCategory.showSites(SiteSettingsCategory.Type.DEVICE_LOCATION)) {
-                PrefServiceBridge.getInstance().setAllowLocationEnabled((boolean) newValue);
-            } else if (mCategory.showSites(SiteSettingsCategory.Type.JAVASCRIPT)) {
-                PrefServiceBridge.getInstance().setJavaScriptEnabled((boolean) newValue);
-            } else if (mCategory.showSites(SiteSettingsCategory.Type.MICROPHONE)) {
-                PrefServiceBridge.getInstance().setMicEnabled((boolean) newValue);
-            } else if (mCategory.showSites(SiteSettingsCategory.Type.NOTIFICATIONS)) {
-                PrefServiceBridge.getInstance().setNotificationsEnabled((boolean) newValue);
-                updateNotificationsVibrateCheckBox();
-            } else if (mCategory.showSites(SiteSettingsCategory.Type.POPUPS)) {
-                PrefServiceBridge.getInstance().setAllowPopupsEnabled((boolean) newValue);
-            } else if (mCategory.showSites(SiteSettingsCategory.Type.PROTECTED_MEDIA)) {
-                PrefServiceBridge.getInstance().setProtectedMediaIdentifierEnabled(
-                        (boolean) newValue);
-            } else if (mCategory.showSites(SiteSettingsCategory.Type.SENSORS)) {
-                PrefServiceBridge.getInstance().setSensorsEnabled((boolean) newValue);
-            } else if (mCategory.showSites(SiteSettingsCategory.Type.SOUND)) {
-                PrefServiceBridge.getInstance().setSoundEnabled((boolean) newValue);
-            } else if (mCategory.showSites(SiteSettingsCategory.Type.USB)) {
-                PrefServiceBridge.getInstance().setUsbEnabled((boolean) newValue);
+            for (@SiteSettingsCategory.Type int type = 0;
+                    type < SiteSettingsCategory.Type.NUM_ENTRIES; type++) {
+                if (type == SiteSettingsCategory.Type.ALL_SITES
+                        || type == SiteSettingsCategory.Type.USE_STORAGE
+                        || !mCategory.showSites(type)) {
+                    continue;
+                }
+
+                PrefServiceBridge.getInstance().setCategoryEnabled(
+                        SiteSettingsCategory.contentSettingsType(type), (boolean) newValue);
+
+                if (type == SiteSettingsCategory.Type.COOKIES) {
+                    updateThirdPartyCookiesCheckBox();
+                } else if (type == SiteSettingsCategory.Type.NOTIFICATIONS) {
+                    updateNotificationsVibrateCheckBox();
+                }
+                break;
             }
 
             // Categories that support adding exceptions also manage the 'Add site' preference.
@@ -522,7 +507,8 @@
         } else if (mCategory.showSites(SiteSettingsCategory.Type.JAVASCRIPT)) {
             resource = R.string.website_settings_add_site_description_javascript;
         } else if (mCategory.showSites(SiteSettingsCategory.Type.SOUND)) {
-            resource = PrefServiceBridge.getInstance().isSoundEnabled()
+            resource = PrefServiceBridge.getInstance().isCategoryEnabled(
+                               ContentSettingsType.CONTENT_SETTINGS_TYPE_SOUND)
                     ? R.string.website_settings_add_site_description_sound_block
                     : R.string.website_settings_add_site_description_sound_allow;
         }
@@ -555,7 +541,8 @@
         // The Sound content setting has exception lists for both BLOCK and ALLOW (others just
         // have exceptions to ALLOW).
         int setting = (mCategory.showSites(SiteSettingsCategory.Type.SOUND)
-                              && PrefServiceBridge.getInstance().isSoundEnabled())
+                              && PrefServiceBridge.getInstance().isCategoryEnabled(
+                                         ContentSettingsType.CONTENT_SETTINGS_TYPE_SOUND))
                 ? ContentSetting.BLOCK.toInt()
                 : ContentSetting.ALLOW.toInt();
         PrefServiceBridge.getInstance().nativeSetContentSettingForPattern(
@@ -589,16 +576,25 @@
 
         configureGlobalToggles();
 
-        if ((mCategory.showSites(SiteSettingsCategory.Type.AUTOPLAY)
-                    && !PrefServiceBridge.getInstance().isAutoplayEnabled())
-                || (mCategory.showSites(SiteSettingsCategory.Type.JAVASCRIPT)
-                           && !PrefServiceBridge.getInstance().javaScriptEnabled())
-                || mCategory.showSites(SiteSettingsCategory.Type.SOUND)
-                || (mCategory.showSites(SiteSettingsCategory.Type.BACKGROUND_SYNC)
-                           && !PrefServiceBridge.getInstance().isBackgroundSyncAllowed())) {
-            getPreferenceScreen().addPreference(
-                    new AddExceptionPreference(getActivity(), ADD_EXCEPTION_KEY,
-                            getAddExceptionDialogMessage(), this));
+        boolean exception = false;
+        if (mCategory.showSites(SiteSettingsCategory.Type.SOUND)) {
+            exception = true;
+        } else if (mCategory.showSites(SiteSettingsCategory.Type.AUTOPLAY)
+                && !PrefServiceBridge.getInstance().isCategoryEnabled(
+                           ContentSettingsType.CONTENT_SETTINGS_TYPE_AUTOPLAY)) {
+            exception = true;
+        } else if (mCategory.showSites(SiteSettingsCategory.Type.JAVASCRIPT)
+                && !PrefServiceBridge.getInstance().isCategoryEnabled(
+                           ContentSettingsType.CONTENT_SETTINGS_TYPE_JAVASCRIPT)) {
+            exception = true;
+        } else if (mCategory.showSites(SiteSettingsCategory.Type.BACKGROUND_SYNC)
+                && !PrefServiceBridge.getInstance().isCategoryEnabled(
+                           ContentSettingsType.CONTENT_SETTINGS_TYPE_BACKGROUND_SYNC)) {
+            exception = true;
+        }
+        if (exception) {
+            getPreferenceScreen().addPreference(new AddExceptionPreference(
+                    getActivity(), ADD_EXCEPTION_KEY, getAddExceptionDialogMessage(), this));
         }
     }
 
@@ -666,13 +662,8 @@
                 mIsInitialRun = false;
             }
 
-            if (!mBlockListExpanded) {
-                blockedGroup.removeAll();
-            }
-
-            if (!mAllowListExpanded) {
-                allowedGroup.removeAll();
-            }
+            if (!mBlockListExpanded) blockedGroup.removeAll();
+            if (!mAllowListExpanded) allowedGroup.removeAll();
         }
 
         mWebsites = websites;
@@ -827,38 +818,23 @@
                 return mCategory.isManagedByCustodian();
             }
         });
-        if (mCategory.showSites(SiteSettingsCategory.Type.ADS))
-            globalToggle.setChecked(PrefServiceBridge.getInstance().adsEnabled());
-        else if (mCategory.showSites(SiteSettingsCategory.Type.AUTOPLAY))
-            globalToggle.setChecked(PrefServiceBridge.getInstance().isAutoplayEnabled());
-        else if (mCategory.showSites(SiteSettingsCategory.Type.BACKGROUND_SYNC))
-            globalToggle.setChecked(PrefServiceBridge.getInstance().isBackgroundSyncAllowed());
-        else if (mCategory.showSites(SiteSettingsCategory.Type.CAMERA))
-            globalToggle.setChecked(PrefServiceBridge.getInstance().isCameraEnabled());
-        else if (mCategory.showSites(SiteSettingsCategory.Type.CLIPBOARD))
-            globalToggle.setChecked(PrefServiceBridge.getInstance().isClipboardEnabled());
-        else if (mCategory.showSites(SiteSettingsCategory.Type.COOKIES))
-            globalToggle.setChecked(PrefServiceBridge.getInstance().isAcceptCookiesEnabled());
-        else if (mCategory.showSites(SiteSettingsCategory.Type.DEVICE_LOCATION))
-            globalToggle.setChecked(
-                    LocationSettings.getInstance().isChromeLocationSettingEnabled());
-        else if (mCategory.showSites(SiteSettingsCategory.Type.JAVASCRIPT))
-            globalToggle.setChecked(PrefServiceBridge.getInstance().javaScriptEnabled());
-        else if (mCategory.showSites(SiteSettingsCategory.Type.MICROPHONE))
-            globalToggle.setChecked(PrefServiceBridge.getInstance().isMicEnabled());
-        else if (mCategory.showSites(SiteSettingsCategory.Type.NOTIFICATIONS))
-            globalToggle.setChecked(PrefServiceBridge.getInstance().isNotificationsEnabled());
-        else if (mCategory.showSites(SiteSettingsCategory.Type.POPUPS))
-            globalToggle.setChecked(PrefServiceBridge.getInstance().popupsEnabled());
-        else if (mCategory.showSites(SiteSettingsCategory.Type.PROTECTED_MEDIA))
-            globalToggle.setChecked(
-                    PrefServiceBridge.getInstance().isProtectedMediaIdentifierEnabled());
-        else if (mCategory.showSites(SiteSettingsCategory.Type.SENSORS))
-            globalToggle.setChecked(PrefServiceBridge.getInstance().areSensorsEnabled());
-        else if (mCategory.showSites(SiteSettingsCategory.Type.SOUND))
-            globalToggle.setChecked(PrefServiceBridge.getInstance().isSoundEnabled());
-        else if (mCategory.showSites(SiteSettingsCategory.Type.USB))
-            globalToggle.setChecked(PrefServiceBridge.getInstance().isUsbEnabled());
+        for (@SiteSettingsCategory.Type int type = 0; type < SiteSettingsCategory.Type.NUM_ENTRIES;
+                type++) {
+            if (type == SiteSettingsCategory.Type.ALL_SITES
+                    || type == SiteSettingsCategory.Type.USE_STORAGE
+                    || !mCategory.showSites(type)) {
+                continue;
+            }
+
+            if (type == SiteSettingsCategory.Type.DEVICE_LOCATION) {
+                globalToggle.setChecked(
+                        LocationSettings.getInstance().isChromeLocationSettingEnabled());
+            } else {
+                globalToggle.setChecked(PrefServiceBridge.getInstance().isCategoryEnabled(
+                        SiteSettingsCategory.contentSettingsType(type)));
+            }
+            break;
+        }
     }
 
     private void updateThirdPartyCookiesCheckBox() {
@@ -866,7 +842,8 @@
                 getPreferenceScreen().findPreference(THIRD_PARTY_COOKIES_TOGGLE_KEY);
         thirdPartyCookiesPref.setChecked(
                 !PrefServiceBridge.getInstance().isBlockThirdPartyCookiesEnabled());
-        thirdPartyCookiesPref.setEnabled(PrefServiceBridge.getInstance().isAcceptCookiesEnabled());
+        thirdPartyCookiesPref.setEnabled(PrefServiceBridge.getInstance().isCategoryEnabled(
+                ContentSettingsType.CONTENT_SETTINGS_TYPE_COOKIES));
         thirdPartyCookiesPref.setManagedPreferenceDelegate(
                 preference -> PrefServiceBridge.getInstance().isBlockThirdPartyCookiesManaged());
     }
@@ -876,7 +853,8 @@
                 (ChromeBaseCheckBoxPreference) getPreferenceScreen().findPreference(
                         NOTIFICATIONS_VIBRATE_TOGGLE_KEY);
         if (preference != null) {
-            preference.setEnabled(PrefServiceBridge.getInstance().isNotificationsEnabled());
+            preference.setEnabled(PrefServiceBridge.getInstance().isCategoryEnabled(
+                    ContentSettingsType.CONTENT_SETTINGS_TYPE_NOTIFICATIONS));
         }
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SingleWebsitePreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SingleWebsitePreferences.java
index b84d2d6..5c4a192 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SingleWebsitePreferences.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SingleWebsitePreferences.java
@@ -530,9 +530,7 @@
             setting = mSite.getPermission(PermissionInfo.Type.MICROPHONE);
         }
 
-        if (setting == null) {
-            return false;
-        }
+        if (setting == null) return false;
 
         SiteSettingsCategory category = SiteSettingsCategory.createFromType(type);
         return category.showPermissionBlockedMessage(getActivity());
@@ -624,8 +622,10 @@
         // In order to always show the sound permission, set it up with the default value if it
         // doesn't have a current value.
         if (currentValue == null) {
-            currentValue = PrefServiceBridge.getInstance().isSoundEnabled() ? ContentSetting.ALLOW
-                                                                            : ContentSetting.BLOCK;
+            currentValue = PrefServiceBridge.getInstance().isCategoryEnabled(
+                                   ContentSettingsType.CONTENT_SETTINGS_TYPE_SOUND)
+                    ? ContentSetting.ALLOW
+                    : ContentSetting.BLOCK;
         }
         setUpListPreference(preference, currentValue);
     }
@@ -661,10 +661,10 @@
         // However, if the blocking is activated, we still want to show the permission, even if it
         // is in the default state.
         if (permission == null) {
-            ContentSetting defaultPermission = PrefServiceBridge.getInstance().adsEnabled()
+            permission = PrefServiceBridge.getInstance().isCategoryEnabled(
+                                 ContentSettingsType.CONTENT_SETTINGS_TYPE_ADS)
                     ? ContentSetting.ALLOW
                     : ContentSetting.BLOCK;
-            permission = defaultPermission;
         }
         setUpListPreference(preference, permission);
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SiteSettingsCategory.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SiteSettingsCategory.java
index 4cc3d74..be8beca 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SiteSettingsCategory.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SiteSettingsCategory.java
@@ -64,7 +64,7 @@
         int USE_STORAGE = 15;
         int USB = 16;
         /**
-         * Number of handled exceptions used for calculating array sizes.
+         * Number of handled categories used for calculating array sizes.
          */
         int NUM_ENTRIES = 17;
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SiteSettingsPreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SiteSettingsPreferences.java
index 7bc31d7..e2d0720 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SiteSettingsPreferences.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SiteSettingsPreferences.java
@@ -77,10 +77,6 @@
         return findPreference(SiteSettingsCategory.preferenceKey(type));
     }
 
-    private String preference(@SiteSettingsCategory.Type int type) {
-        return SiteSettingsCategory.preferenceKey(type);
-    }
-
     private void configurePreferences() {
         if (mMediaSubMenu) {
             // The Media sub-menu only contains Protected Content and Autoplay, so remove all other
@@ -140,97 +136,71 @@
         if (translatePref != null) setTranslateStateSummary(translatePref);
 
         // Preferences that navigate to Website Settings.
-        List<String> websitePrefs = new ArrayList<String>();
+        List<Integer> websitePrefs = new ArrayList<Integer>();
         if (mMediaSubMenu) {
-            websitePrefs.add(preference(SiteSettingsCategory.Type.PROTECTED_MEDIA));
-            websitePrefs.add(preference(SiteSettingsCategory.Type.AUTOPLAY));
+            websitePrefs.add(SiteSettingsCategory.Type.PROTECTED_MEDIA);
+            websitePrefs.add(SiteSettingsCategory.Type.AUTOPLAY);
         } else {
             if (SiteSettingsCategory.adsCategoryEnabled()) {
-                websitePrefs.add(preference(SiteSettingsCategory.Type.ADS));
+                websitePrefs.add(SiteSettingsCategory.Type.ADS);
             }
             // When showing the main menu, if Protected Content is not available, only Autoplay
             // will be visible.
             if (!mProtectedContentMenuAvailable) {
-                websitePrefs.add(preference(SiteSettingsCategory.Type.AUTOPLAY));
+                websitePrefs.add(SiteSettingsCategory.Type.AUTOPLAY);
             }
-            websitePrefs.add(preference(SiteSettingsCategory.Type.BACKGROUND_SYNC));
-            websitePrefs.add(preference(SiteSettingsCategory.Type.CAMERA));
+            websitePrefs.add(SiteSettingsCategory.Type.BACKGROUND_SYNC);
+            websitePrefs.add(SiteSettingsCategory.Type.CAMERA);
             if (ChromeFeatureList.isEnabled(ChromeFeatureList.CLIPBOARD_CONTENT_SETTING)) {
-                websitePrefs.add(preference(SiteSettingsCategory.Type.CLIPBOARD));
+                websitePrefs.add(SiteSettingsCategory.Type.CLIPBOARD);
             }
-            websitePrefs.add(preference(SiteSettingsCategory.Type.COOKIES));
-            websitePrefs.add(preference(SiteSettingsCategory.Type.JAVASCRIPT));
-            websitePrefs.add(preference(SiteSettingsCategory.Type.DEVICE_LOCATION));
-            websitePrefs.add(preference(SiteSettingsCategory.Type.MICROPHONE));
-            websitePrefs.add(preference(SiteSettingsCategory.Type.NOTIFICATIONS));
-            websitePrefs.add(preference(SiteSettingsCategory.Type.POPUPS));
+            websitePrefs.add(SiteSettingsCategory.Type.COOKIES);
+            websitePrefs.add(SiteSettingsCategory.Type.JAVASCRIPT);
+            websitePrefs.add(SiteSettingsCategory.Type.DEVICE_LOCATION);
+            websitePrefs.add(SiteSettingsCategory.Type.MICROPHONE);
+            websitePrefs.add(SiteSettingsCategory.Type.NOTIFICATIONS);
+            websitePrefs.add(SiteSettingsCategory.Type.POPUPS);
             if (ChromeFeatureList.isEnabled(ChromeFeatureList.GENERIC_SENSOR_EXTRA_CLASSES)) {
-                websitePrefs.add(preference(SiteSettingsCategory.Type.SENSORS));
+                websitePrefs.add(SiteSettingsCategory.Type.SENSORS);
             }
             if (ChromeFeatureList.isEnabled(ChromeFeatureList.SOUND_CONTENT_SETTING)) {
-                websitePrefs.add(preference(SiteSettingsCategory.Type.SOUND));
+                websitePrefs.add(SiteSettingsCategory.Type.SOUND);
             }
-            websitePrefs.add(preference(SiteSettingsCategory.Type.USB));
+            websitePrefs.add(SiteSettingsCategory.Type.USB);
         }
 
         // Initialize the summary and icon for all preferences that have an
         // associated content settings entry.
-        for (String prefName : websitePrefs) {
-            Preference p = findPreference(prefName);
-            boolean checked = false;
-            if (preference(SiteSettingsCategory.Type.ADS).equals(prefName)) {
-                checked = PrefServiceBridge.getInstance().adsEnabled();
-            } else if (preference(SiteSettingsCategory.Type.AUTOPLAY).equals(prefName)) {
-                checked = PrefServiceBridge.getInstance().isAutoplayEnabled();
-            } else if (preference(SiteSettingsCategory.Type.BACKGROUND_SYNC).equals(prefName)) {
-                checked = PrefServiceBridge.getInstance().isBackgroundSyncAllowed();
-            } else if (preference(SiteSettingsCategory.Type.CAMERA).equals(prefName)) {
-                checked = PrefServiceBridge.getInstance().isCameraEnabled();
-            } else if (preference(SiteSettingsCategory.Type.CLIPBOARD).equals(prefName)) {
-                checked = PrefServiceBridge.getInstance().isClipboardEnabled();
-            } else if (preference(SiteSettingsCategory.Type.COOKIES).equals(prefName)) {
-                checked = PrefServiceBridge.getInstance().isAcceptCookiesEnabled();
-            } else if (preference(SiteSettingsCategory.Type.JAVASCRIPT).equals(prefName)) {
-                checked = PrefServiceBridge.getInstance().javaScriptEnabled();
-            } else if (preference(SiteSettingsCategory.Type.DEVICE_LOCATION).equals(prefName)) {
+        for (@SiteSettingsCategory.Type int prefCategory : websitePrefs) {
+            Preference p = findPreference(prefCategory);
+            int contentType = SiteSettingsCategory.contentSettingsType(prefCategory);
+
+            boolean checked;
+            if (prefCategory == SiteSettingsCategory.Type.DEVICE_LOCATION) {
                 checked = LocationSettings.getInstance().areAllLocationSettingsEnabled();
-            } else if (preference(SiteSettingsCategory.Type.MICROPHONE).equals(prefName)) {
-                checked = PrefServiceBridge.getInstance().isMicEnabled();
-            } else if (preference(SiteSettingsCategory.Type.NOTIFICATIONS).equals(prefName)) {
-                checked = PrefServiceBridge.getInstance().isNotificationsEnabled();
-            } else if (preference(SiteSettingsCategory.Type.POPUPS).equals(prefName)) {
-                checked = PrefServiceBridge.getInstance().popupsEnabled();
-            } else if (preference(SiteSettingsCategory.Type.PROTECTED_MEDIA).equals(prefName)) {
-                checked = PrefServiceBridge.getInstance().isProtectedMediaIdentifierEnabled();
-            } else if (preference(SiteSettingsCategory.Type.SENSORS).equals(prefName)) {
-                checked = PrefServiceBridge.getInstance().areSensorsEnabled();
-            } else if (preference(SiteSettingsCategory.Type.SOUND).equals(prefName)) {
-                checked = PrefServiceBridge.getInstance().isSoundEnabled();
-            } else if (preference(SiteSettingsCategory.Type.USB).equals(prefName)) {
-                checked = PrefServiceBridge.getInstance().isUsbEnabled();
+            } else {
+                checked = PrefServiceBridge.getInstance().isCategoryEnabled(contentType);
             }
 
-            int contentType = SiteSettingsCategory.contentSettingsType(prefName);
             p.setTitle(ContentSettingsResources.getTitle(contentType));
             p.setOnPreferenceClickListener(this);
 
             // Disable autoplay preference if Data Saver is ON.
-            if (preference(SiteSettingsCategory.Type.AUTOPLAY).equals(prefName)
+            if (SiteSettingsCategory.Type.AUTOPLAY == prefCategory
                     && DataReductionProxySettings.getInstance().isDataReductionProxyEnabled()) {
                 p.setSummary(ContentSettingsResources.getAutoplayDisabledByDataSaverSummary());
                 p.setEnabled(false);
-            } else if (preference(SiteSettingsCategory.Type.COOKIES).equals(prefName) && checked
+            } else if (SiteSettingsCategory.Type.COOKIES == prefCategory && checked
                     && prefServiceBridge.isBlockThirdPartyCookiesEnabled()) {
                 p.setSummary(ContentSettingsResources.getCookieAllowedExceptThirdPartySummary());
-            } else if (preference(SiteSettingsCategory.Type.CLIPBOARD).equals(prefName)
-                    && !checked) {
+            } else if (SiteSettingsCategory.Type.CLIPBOARD == prefCategory && !checked) {
                 p.setSummary(ContentSettingsResources.getClipboardBlockedListSummary());
-            } else if (preference(SiteSettingsCategory.Type.DEVICE_LOCATION).equals(prefName)
-                    && checked && prefServiceBridge.isLocationAllowedByPolicy()) {
+            } else if (SiteSettingsCategory.Type.DEVICE_LOCATION == prefCategory && checked
+                    && prefServiceBridge.isLocationAllowedByPolicy()) {
                 p.setSummary(ContentSettingsResources.getGeolocationAllowedSummary());
-            } else if (preference(SiteSettingsCategory.Type.ADS).equals(prefName) && !checked) {
+            } else if (SiteSettingsCategory.Type.ADS == prefCategory && !checked) {
                 p.setSummary(ContentSettingsResources.getAdsBlockedListSummary());
-            } else if (preference(SiteSettingsCategory.Type.SOUND).equals(prefName) && !checked) {
+            } else if (SiteSettingsCategory.Type.SOUND == prefCategory && !checked) {
                 p.setSummary(ContentSettingsResources.getSoundBlockedListSummary());
             } else {
                 p.setSummary(ContentSettingsResources.getCategorySummary(contentType, checked));
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/Website.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/Website.java
index 55bd4a4..33e02d0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/Website.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/Website.java
@@ -212,9 +212,7 @@
         } else {
             clearedCallback.onStorageInfoCleared();
         }
-        for (StorageInfo info : mStorageInfo) {
-            info.clear(clearedCallback);
-        }
+        for (StorageInfo info : mStorageInfo) info.clear(clearedCallback);
         mStorageInfo.clear();
     }
 
@@ -225,12 +223,8 @@
 
     public long getTotalUsage() {
         long usage = 0;
-        if (mLocalStorageInfo != null) {
-            usage += mLocalStorageInfo.getSize();
-        }
-        for (StorageInfo info : mStorageInfo) {
-            usage += info.getSize();
-        }
+        if (mLocalStorageInfo != null) usage += mLocalStorageInfo.getSize();
+        for (StorageInfo info : mStorageInfo) usage += info.getSize();
         return usage;
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java
index 885bc2e..9b419d9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java
@@ -2448,7 +2448,6 @@
             manager.removeListener(mGestureStateListener);
         }
 
-        mContentViewCore.destroy();
         mContentViewCore = null;
         mWebContents = null;
         mWebContentsDelegate = null;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
index d1c0843..d1c8fff 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
@@ -1152,13 +1152,14 @@
     public void onUrlFocusChange(boolean hasFocus) {
         mToolbar.onUrlFocusChange(hasFocus);
 
+        if (hasFocus) mOmniboxStartupMetrics.onUrlBarFocused();
+
         if (mFindToolbarManager != null && hasFocus) mFindToolbarManager.hideToolbar();
 
         if (mControlsVisibilityDelegate == null) return;
         if (hasFocus) {
             mFullscreenFocusToken = mControlsVisibilityDelegate
                     .showControlsPersistentAndClearOldToken(mFullscreenFocusToken);
-            mOmniboxStartupMetrics.onUrlBarFocused();
         } else {
             mControlsVisibilityDelegate.hideControlsPersistent(mFullscreenFocusToken);
             mFullscreenFocusToken = FullscreenManager.INVALID_TOKEN;
@@ -1271,16 +1272,15 @@
         long elapsedTime = SystemClock.elapsedRealtime() - activityCreationTimeMs;
         if (elapsedTime < RECORD_UMA_PERFORMANCE_METRICS_DELAY_MS) {
             ThreadUtils.postOnUiThreadDelayed(() -> {
-                // Noop if mOmniboxStartupMetrics is null. ie. ToolbarManager is destroyed. See
-                // https://crbug.com/860449
-                if (mOmniboxStartupMetrics == null) return;
                 onDeferredStartup(activityCreationTimeMs, activityName);
             }, RECORD_UMA_PERFORMANCE_METRICS_DELAY_MS - elapsedTime);
             return;
         }
         RecordHistogram.recordTimesHistogram("MobileStartup.ToolbarFirstDrawTime2." + activityName,
                 mToolbar.getFirstDrawTime() - activityCreationTimeMs, TimeUnit.MILLISECONDS);
-        mOmniboxStartupMetrics.recordHistogram();
+        // mOmniboxStartupMetrics might be null. ie. ToolbarManager is destroyed. See
+        // https://crbug.com/860449
+        if (mOmniboxStartupMetrics != null) mOmniboxStartupMetrics.recordHistogram();
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappTabDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappTabDelegate.java
index 11dfd8d..7d3bbdf 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappTabDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappTabDelegate.java
@@ -83,7 +83,7 @@
             boolean foundSpecializedHandler = false;
 
             for (String result : ExternalNavigationDelegateImpl.getSpecializedHandlersWithFilter(
-                         handlers, null, null)) {
+                         handlers, null, intent)) {
                 if (result.equals(mApkPackageName)) {
                     // Current webapk matches, don't intercept so that we can launch a cct. See
                     // http://crbug.com/831806 for more context.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webauth/OWNERS b/chrome/android/java/src/org/chromium/chrome/browser/webauth/OWNERS
new file mode 100644
index 0000000..ef9d7b9
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webauth/OWNERS
@@ -0,0 +1,3 @@
+kpaulhamus@chromium.org
+
+# COMPONENT: Blink>WebAuthentication
\ No newline at end of file
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/input/SelectPopupOtherContentViewTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/input/SelectPopupOtherContentViewTest.java
index bdeb5f9..43b8ef7 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/input/SelectPopupOtherContentViewTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/input/SelectPopupOtherContentViewTest.java
@@ -97,19 +97,15 @@
                 WindowAndroid windowAndroid = new ActivityWindowAndroid(activity);
 
                 ContentView cv = ContentView.createContentView(activity, webContents);
-                ContentViewCore contentViewCore = ContentViewCore.create(activity, "", webContents,
+                ContentViewCore.create(activity, "", webContents,
                         ViewAndroidDelegate.createBasicDelegate(cv), cv, windowAndroid);
-                contentViewCore.destroy();
+                webContents.destroy();
             }
         });
 
         // Process some more events to give a chance to the dialog to hide if it were to.
         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
 
-        // The popup should still be shown.
-        ContentViewCore viewCore =
-                mActivityTestRule.getActivity().getActivityTab().getContentViewCore();
-
         Assert.assertTrue("The select popup got hidden by destroying of unrelated ContentViewCore.",
                 mActivityTestRule.getActivity()
                         .getActivityTab()
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/NotificationsPreferencesTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/NotificationsPreferencesTest.java
index 02c5242..b0bfee9 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/NotificationsPreferencesTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/NotificationsPreferencesTest.java
@@ -145,11 +145,13 @@
         ThreadUtils.runOnUiThreadBlocking(new Runnable() {
             @Override
             public void run() {
-                PrefServiceBridge.getInstance().setNotificationsEnabled(false);
+                PrefServiceBridge.getInstance().setCategoryEnabled(
+                        ContentSettingsType.CONTENT_SETTINGS_TYPE_NOTIFICATIONS, false);
                 fragment.onResume();
                 Assert.assertEquals(fromWebsites.getSummary(), getNotificationsSummary(false));
 
-                PrefServiceBridge.getInstance().setNotificationsEnabled(true);
+                PrefServiceBridge.getInstance().setCategoryEnabled(
+                        ContentSettingsType.CONTENT_SETTINGS_TYPE_NOTIFICATIONS, true);
                 fragment.onResume();
                 Assert.assertEquals(fromWebsites.getSummary(), getNotificationsSummary(true));
             }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/website/SiteSettingsPreferencesTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/website/SiteSettingsPreferencesTest.java
index 82fdcf6..394e87f 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/website/SiteSettingsPreferencesTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/website/SiteSettingsPreferencesTest.java
@@ -23,6 +23,7 @@
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.ChromeSwitches;
+import org.chromium.chrome.browser.ContentSettingsType;
 import org.chromium.chrome.browser.infobar.InfoBarContainer;
 import org.chromium.chrome.browser.preferences.ChromeBaseCheckBoxPreference;
 import org.chromium.chrome.browser.preferences.ChromeSwitchPreference;
@@ -194,7 +195,8 @@
             }
 
             private boolean doesAcceptCookies() {
-                return PrefServiceBridge.getInstance().isAcceptCookiesEnabled();
+                return PrefServiceBridge.getInstance().isCategoryEnabled(
+                        ContentSettingsType.CONTENT_SETTINGS_TYPE_COOKIES);
             }
         });
     }
@@ -243,7 +245,9 @@
             @Override
             public void run() {
                 Assert.assertEquals("Popups should be " + (enabled ? "allowed" : "blocked"),
-                        enabled, PrefServiceBridge.getInstance().popupsEnabled());
+                        enabled,
+                        PrefServiceBridge.getInstance().isCategoryEnabled(
+                                ContentSettingsType.CONTENT_SETTINGS_TYPE_POPUPS));
             }
         });
     }
@@ -255,7 +259,9 @@
             @Override
             public void run() {
                 Assert.assertEquals("Camera should be " + (enabled ? "allowed" : "blocked"),
-                        enabled, PrefServiceBridge.getInstance().isCameraEnabled());
+                        enabled,
+                        PrefServiceBridge.getInstance().isCategoryEnabled(
+                                ContentSettingsType.CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA));
             }
         });
     }
@@ -477,8 +483,9 @@
         ThreadUtils.runOnUiThreadBlocking(new Runnable() {
             @Override
             public void run() {
-                Assert.assertFalse(
-                        "Mic should be blocked", PrefServiceBridge.getInstance().isMicEnabled());
+                Assert.assertFalse("Mic should be blocked",
+                        PrefServiceBridge.getInstance().isCategoryEnabled(
+                                ContentSettingsType.CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC));
             }
         });
 
@@ -554,7 +561,9 @@
             public void run() {
                 Assert.assertEquals(
                         "Background Sync should be " + (enabled ? "enabled" : "disabled"),
-                        PrefServiceBridge.getInstance().isBackgroundSyncAllowed(), enabled);
+                        PrefServiceBridge.getInstance().isCategoryEnabled(
+                                ContentSettingsType.CONTENT_SETTINGS_TYPE_BACKGROUND_SYNC),
+                        enabled);
             }
         });
     }
@@ -583,7 +592,9 @@
             @Override
             public void run() {
                 Assert.assertEquals("USB should be " + (enabled ? "enabled" : "disabled"),
-                        PrefServiceBridge.getInstance().isUsbEnabled(), enabled);
+                        PrefServiceBridge.getInstance().isCategoryEnabled(
+                                ContentSettingsType.CONTENT_SETTINGS_TYPE_USB_GUARD),
+                        enabled);
             }
         });
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/util/NativeUiUtils.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/util/NativeUiUtils.java
index 13c64461..f9f897e 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/util/NativeUiUtils.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/util/NativeUiUtils.java
@@ -76,8 +76,12 @@
             throws InterruptedException {
         final TestVrShellDelegate instance = TestVrShellDelegate.getInstance();
         final CountDownLatch resultLatch = new CountDownLatch(1);
-        instance.setUiExpectingActivityForTesting(
-                DEFAULT_UI_QUIESCENCE_TIMEOUT_MS, () -> { resultLatch.countDown(); });
+        // Run on the UI thread to prevent issues with registering a new callback before
+        // reportUiActivityResultForTesting has finished.
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            instance.setUiExpectingActivityForTesting(
+                    DEFAULT_UI_QUIESCENCE_TIMEOUT_MS, () -> { resultLatch.countDown(); });
+        });
         action.run();
 
         // Wait for any outstanding animations to finish.
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/webauth/OWNERS b/chrome/android/javatests/src/org/chromium/chrome/browser/webauth/OWNERS
new file mode 100644
index 0000000..ef9d7b9
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/webauth/OWNERS
@@ -0,0 +1,3 @@
+kpaulhamus@chromium.org
+
+# COMPONENT: Blink>WebAuthentication
\ No newline at end of file
diff --git a/chrome/app/chrome_command_ids.h b/chrome/app/chrome_command_ids.h
index 46a3460f..b28768f 100644
--- a/chrome/app/chrome_command_ids.h
+++ b/chrome/app/chrome_command_ids.h
@@ -94,6 +94,7 @@
 #define IDC_ROUTE_MEDIA                 35011
 #define IDC_WINDOW_MUTE_SITE            35012
 #define IDC_WINDOW_PIN_TAB              35013
+#define IDC_MIGRATE_LOCAL_CREDIT_CARD_FOR_PAGE 35014
 
 // Clipboard commands
 #define IDC_CUT                         36000
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 8230c7a..91c0cc9 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -4871,6 +4871,9 @@
           Save Card
         </message>
        </if>
+      <message name="IDS_TOOLTIP_MIGRATE_LOCAL_CARD" desc="The tooltip for the icon that shows the local card migration bubble">
+        Have your cards in one place
+      </message>
       <message name="IDS_TOOLTIP_TRANSLATE" desc="The tooltip for translate">
         Translate this page
       </message>
diff --git a/chrome/app/nibs/MainMenu.xib b/chrome/app/nibs/MainMenu.xib
index 4ac904f..ba5febc 100644
--- a/chrome/app/nibs/MainMenu.xib
+++ b/chrome/app/nibs/MainMenu.xib
@@ -481,20 +481,14 @@
                                 </connections>
                             </menuItem>
                             <menuItem isSeparatorItem="YES" id="453"/>
-                            <menuItem title="^IDS_NEXT_TAB_MAC" tag="34016" id="455">
-                                <string key="keyEquivalent" base64-UTF8="YES">
-CQ
-</string>
-                                <modifierMask key="keyEquivalentModifierMask" control="YES"/>
+                            <menuItem title="^IDS_NEXT_TAB_MAC" tag="34016" keyEquivalent="" id="455">
+                                <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
                                 <connections>
                                     <action selector="commandDispatch:" target="-1" id="529"/>
                                 </connections>
                             </menuItem>
-                            <menuItem title="^IDS_PREV_TAB_MAC" tag="34017" id="454">
-                                <string key="keyEquivalent" base64-UTF8="YES">
-CQ
-</string>
-                                <modifierMask key="keyEquivalentModifierMask" shift="YES" control="YES"/>
+                            <menuItem title="^IDS_PREV_TAB_MAC" tag="34017" keyEquivalent="" id="454">
+                                <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
                                 <connections>
                                     <action selector="commandDispatch:" target="-1" id="530"/>
                                 </connections>
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index c40b99a..42bf63e 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -3582,11 +3582,6 @@
      FEATURE_VALUE_TYPE(media::kAv1Decoder)},
 #endif  // ENABLE_AV1_DECODER
 
-    {"enable-wheel-scroll-latching",
-     flag_descriptions::kEnableTouchpadAndWheelScrollLatchingName,
-     flag_descriptions::kEnableTouchpadAndWheelScrollLatchingDescription,
-     kOsAll, FEATURE_VALUE_TYPE(features::kTouchpadAndWheelScrollLatching)},
-
 #if defined(OS_ANDROID)
     {"grant-notifications-to-dse",
      flag_descriptions::kGrantNotificationsToDSEName,
diff --git a/chrome/browser/android/feed/feed_host_service_factory.cc b/chrome/browser/android/feed/feed_host_service_factory.cc
index 56a35d5..b4a1007 100644
--- a/chrome/browser/android/feed/feed_host_service_factory.cc
+++ b/chrome/browser/android/feed/feed_host_service_factory.cc
@@ -18,6 +18,7 @@
 #include "components/feed/core/feed_image_manager.h"
 #include "components/feed/core/feed_networking_host.h"
 #include "components/feed/core/feed_scheduler_host.h"
+#include "components/feed/core/feed_storage_database.h"
 #include "components/image_fetcher/core/image_fetcher_impl.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "components/version_info/version_info.h"
@@ -85,9 +86,11 @@
   auto scheduler_host = std::make_unique<FeedSchedulerHost>(
       profile->GetPrefs(), base::DefaultClock::GetInstance());
 
-  return new FeedHostService(std::move(image_manager),
-                             std::move(networking_host),
-                             std::move(scheduler_host));
+  auto storage_database = std::make_unique<FeedStorageDatabase>(feed_dir);
+
+  return new FeedHostService(
+      std::move(image_manager), std::move(networking_host),
+      std::move(scheduler_host), std::move(storage_database));
 }
 
 content::BrowserContext* FeedHostServiceFactory::GetBrowserContextToUse(
diff --git a/chrome/browser/apps/guest_view/web_view_browsertest.cc b/chrome/browser/apps/guest_view/web_view_browsertest.cc
index 79e2597..3662e32e 100644
--- a/chrome/browser/apps/guest_view/web_view_browsertest.cc
+++ b/chrome/browser/apps/guest_view/web_view_browsertest.cc
@@ -1898,6 +1898,7 @@
       interstitial_main_frame->GetRenderViewHost()->GetWidget();
 
   content::WaitForHitTestDataOrChildSurfaceReady(interstitial_main_frame);
+  content::RenderFrameSubmissionObserver frame_observer(guest_web_contents);
 
   EXPECT_NE(interstitial_widget,
             content::GetFocusedRenderWidgetHost(guest_web_contents));
@@ -1912,16 +1913,15 @@
   content::RouteMouseEvent(outer_web_contents, &event);
 
   // Wait a frame.
-  content::MainThreadFrameObserver observer(interstitial_widget);
-  observer.Wait();
+  frame_observer.WaitForAnyFrameSubmission();
 
   // Send mouse up.
+  content::InputEventAckWaiter waiter(interstitial_widget,
+                                      blink::WebInputEvent::kMouseUp);
   event.SetType(blink::WebInputEvent::kMouseUp);
   event.SetPositionInWidget(10, 10);
   content::RouteMouseEvent(outer_web_contents, &event);
-
-  // Wait another frame.
-  observer.Wait();
+  waiter.Wait();
 
   EXPECT_EQ(interstitial_widget,
             content::GetFocusedRenderWidgetHost(guest_web_contents));
@@ -3944,31 +3944,9 @@
   }
 }
 
-// Tests scroll latching behaviour with WebViews.
-// Only applicable with OOPIF-based guests when scroll latching is enabled.
-// We can move these tests to a more general fixture once the features
-// have landed (crbug.com/533069 and crbug.com/526463).
-class WebViewGuestScrollLatchingTest : public WebViewTest {
- protected:
-  WebViewGuestScrollLatchingTest() {}
-  ~WebViewGuestScrollLatchingTest() override {}
-
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    WebViewTest::SetUpCommandLine(command_line);
-    feature_list_.InitWithFeatures({features::kTouchpadAndWheelScrollLatching,
-                                    features::kAsyncWheelEvents},
-                                   {});
-  }
-
- private:
-  base::test::ScopedFeatureList feature_list_;
-
-  DISALLOW_COPY_AND_ASSIGN(WebViewGuestScrollLatchingTest);
-};
-
 // Test that when we bubble scroll from a guest, the guest does not also
 // consume the scroll.
-IN_PROC_BROWSER_TEST_F(WebViewGuestScrollLatchingTest,
+IN_PROC_BROWSER_TEST_P(WebViewGuestScrollTest,
                        ScrollLatchingPreservedInGuests) {
   LoadAppWithGuest("web_view/scrollable_embedder_and_guest");
 
diff --git a/chrome/browser/apps/guest_view/web_view_interactive_browsertest.cc b/chrome/browser/apps/guest_view/web_view_interactive_browsertest.cc
index 4921155..7e8f21a 100644
--- a/chrome/browser/apps/guest_view/web_view_interactive_browsertest.cc
+++ b/chrome/browser/apps/guest_view/web_view_interactive_browsertest.cc
@@ -1165,13 +1165,11 @@
   content::WebContents* embedder_web_contents = GetFirstAppWindowWebContents();
   content::WebContents* guest_web_contents =
       GetGuestViewManager()->WaitForSingleGuestCreated();
-
-  content::MainThreadFrameObserver embedder_observer(
-      embedder_web_contents->GetMainFrame()->GetView()->GetRenderWidgetHost());
-  content::MainThreadFrameObserver guest_observer(
-      guest_web_contents->GetMainFrame()->GetView()->GetRenderWidgetHost());
-  embedder_observer.Wait();
-  guest_observer.Wait();
+  content::RenderFrameSubmissionObserver embedder_observer(
+      embedder_web_contents);
+  content::RenderFrameSubmissionObserver guest_observer(guest_web_contents);
+  embedder_observer.WaitForMetadataChange();
+  guest_observer.WaitForMetadataChange();
 
   ExtensionTestMessageListener listener{"WebViewTest.WEBVIEW_LOADED", false};
   EXPECT_TRUE(ui_test_utils::ShowAndFocusNativeWindow(GetPlatformAppWindow()));
diff --git a/chrome/browser/browser_process_impl.cc b/chrome/browser/browser_process_impl.cc
index e6225ac..2e6464a 100644
--- a/chrome/browser/browser_process_impl.cc
+++ b/chrome/browser/browser_process_impl.cc
@@ -139,7 +139,6 @@
 #include "services/preferences/public/cpp/in_process_service_factory.h"
 #include "ui/base/idle/idle.h"
 #include "ui/base/l10n/l10n_util.h"
-#include "ui/base/pref_names.h"
 #include "ui/base/ui_base_features.h"
 
 #if defined(OS_WIN)
@@ -638,8 +637,8 @@
   DCHECK(io_thread_);
   if (!network_connection_tracker_) {
     network_connection_tracker_ =
-        std::make_unique<content::NetworkConnectionTracker>();
-    network_connection_tracker_->Initialize(content::GetNetworkService());
+        std::make_unique<content::NetworkConnectionTracker>(
+            base::BindRepeating(&content::GetNetworkService));
   }
   return network_connection_tracker_.get();
 }
@@ -877,7 +876,8 @@
   if (!tab_manager_) {
     tab_manager_ = std::make_unique<resource_coordinator::TabManager>();
     tab_lifecycle_unit_source_ =
-        std::make_unique<resource_coordinator::TabLifecycleUnitSource>();
+        std::make_unique<resource_coordinator::TabLifecycleUnitSource>(
+            tab_manager_->intervention_policy_database());
     tab_lifecycle_unit_source_->AddObserver(tab_manager_.get());
   }
   return tab_manager_.get();
diff --git a/chrome/browser/chrome_browser_main.cc b/chrome/browser/chrome_browser_main.cc
index 61d4ba1..513ce73 100644
--- a/chrome/browser/chrome_browser_main.cc
+++ b/chrome/browser/chrome_browser_main.cc
@@ -191,7 +191,6 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/layout.h"
 #include "ui/base/material_design/material_design_controller.h"
-#include "ui/base/pref_names.h"
 #include "ui/base/resource/resource_bundle.h"
 
 #if defined(OS_ANDROID)
diff --git a/chrome/browser/chromeos/accessibility/accessibility_manager.cc b/chrome/browser/chromeos/accessibility/accessibility_manager.cc
index 85c808d..69caf04 100644
--- a/chrome/browser/chromeos/accessibility/accessibility_manager.cc
+++ b/chrome/browser/chromeos/accessibility/accessibility_manager.cc
@@ -79,7 +79,6 @@
 #include "services/service_manager/public/cpp/connector.h"
 #include "ui/accessibility/ax_enum_util.h"
 #include "ui/base/ime/chromeos/extension_ime_util.h"
-#include "ui/base/pref_names.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/base/ui_base_features.h"
 #include "ui/views/widget/widget.h"
diff --git a/chrome/browser/chromeos/accessibility/dictation_chromeos.cc b/chrome/browser/chromeos/accessibility/dictation_chromeos.cc
index d59b691..bd19358 100644
--- a/chrome/browser/chromeos/accessibility/dictation_chromeos.cc
+++ b/chrome/browser/chromeos/accessibility/dictation_chromeos.cc
@@ -17,7 +17,6 @@
 #include "ui/base/ime/composition_text.h"
 #include "ui/base/ime/ime_bridge.h"
 #include "ui/base/ime/ime_input_context_handler_interface.h"
-#include "ui/base/pref_names.h"
 
 namespace {
 
diff --git a/chrome/browser/chromeos/arc/intent_helper/arc_settings_service.cc b/chrome/browser/chromeos/arc/intent_helper/arc_settings_service.cc
index 8924167..428655e 100644
--- a/chrome/browser/chromeos/arc/intent_helper/arc_settings_service.cc
+++ b/chrome/browser/chromeos/arc/intent_helper/arc_settings_service.cc
@@ -44,7 +44,6 @@
 #include "components/proxy_config/proxy_config_pref_names.h"
 #include "content/public/common/page_zoom.h"
 #include "net/proxy_resolution/proxy_config.h"
-#include "ui/base/pref_names.h"
 
 using ::chromeos::CrosSettings;
 using ::chromeos::system::TimezoneSettings;
diff --git a/chrome/browser/chromeos/arc/voice_interaction/fake_voice_interaction_controller.cc b/chrome/browser/chromeos/arc/voice_interaction/fake_voice_interaction_controller.cc
index c39b6e1..964d2dc 100644
--- a/chrome/browser/chromeos/arc/voice_interaction/fake_voice_interaction_controller.cc
+++ b/chrome/browser/chromeos/arc/voice_interaction/fake_voice_interaction_controller.cc
@@ -31,6 +31,10 @@
   voice_interaction_context_enabled_ = enabled;
 }
 
+void FakeVoiceInteractionController::NotifyHotwordEnabled(bool enabled) {
+  voice_interaction_hotword_enabled_ = enabled;
+}
+
 void FakeVoiceInteractionController::NotifySetupCompleted(bool completed) {
   voice_interaction_setup_completed_ = completed;
 }
@@ -50,4 +54,9 @@
   std::move(callback).Run(voice_interaction_setup_completed_);
 }
 
+void FakeVoiceInteractionController::IsHotwordEnabled(
+    IsHotwordEnabledCallback callback) {
+  std::move(callback).Run(voice_interaction_hotword_enabled_);
+}
+
 }  // namespace arc
diff --git a/chrome/browser/chromeos/arc/voice_interaction/fake_voice_interaction_controller.h b/chrome/browser/chromeos/arc/voice_interaction/fake_voice_interaction_controller.h
index b53b1ef9..95c0ead 100644
--- a/chrome/browser/chromeos/arc/voice_interaction/fake_voice_interaction_controller.h
+++ b/chrome/browser/chromeos/arc/voice_interaction/fake_voice_interaction_controller.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_ARC_VOICE_INTERACTION_TEST_VOICE_INTERACTION_CONTROLLER_H_
-#define CHROME_BROWSER_CHROMEOS_ARC_VOICE_INTERACTION_TEST_VOICE_INTERACTION_CONTROLLER_H_
+#ifndef CHROME_BROWSER_CHROMEOS_ARC_VOICE_INTERACTION_FAKE_VOICE_INTERACTION_CONTROLLER_H_
+#define CHROME_BROWSER_CHROMEOS_ARC_VOICE_INTERACTION_FAKE_VOICE_INTERACTION_CONTROLLER_H_
 
 #include "ash/public/interfaces/voice_interaction_controller.mojom.h"
 #include "mojo/public/cpp/bindings/binding.h"
@@ -22,10 +22,12 @@
   void NotifyStatusChanged(ash::mojom::VoiceInteractionState state) override;
   void NotifySettingsEnabled(bool enabled) override;
   void NotifyContextEnabled(bool enabled) override;
+  void NotifyHotwordEnabled(bool enabled) override;
   void NotifySetupCompleted(bool completed) override;
   void NotifyFeatureAllowed(ash::mojom::AssistantAllowedState state) override;
   void IsSettingEnabled(IsSettingEnabledCallback callback) override;
   void IsSetupCompleted(IsSetupCompletedCallback callback) override;
+  void IsHotwordEnabled(IsHotwordEnabledCallback callback) override;
   void AddObserver(ash::mojom::VoiceInteractionObserverPtr observer) override {}
 
   ash::mojom::VoiceInteractionState voice_interaction_state() const {
@@ -37,6 +39,9 @@
   bool voice_interaction_context_enabled() const {
     return voice_interaction_context_enabled_;
   }
+  bool voice_interaction_hotword_enabled() const {
+    return voice_interaction_hotword_enabled_;
+  }
   bool voice_interaction_setup_completed() const {
     return voice_interaction_setup_completed_;
   }
@@ -49,6 +54,7 @@
       ash::mojom::VoiceInteractionState::STOPPED;
   bool voice_interaction_settings_enabled_ = false;
   bool voice_interaction_context_enabled_ = false;
+  bool voice_interaction_hotword_enabled_ = false;
   bool voice_interaction_setup_completed_ = false;
   ash::mojom::AssistantAllowedState assistant_allowed_state_ =
       ash::mojom::AssistantAllowedState::DISALLOWED_BY_INCOGNITO;
@@ -60,4 +66,4 @@
 
 }  // namespace arc
 
-#endif  // CHROME_BROWSER_CHROMEOS_ARC_VOICE_INTERACTION_TEST_VOICE_INTERACTION_CONTROLLER_H_
+#endif  // CHROME_BROWSER_CHROMEOS_ARC_VOICE_INTERACTION_FAKE_VOICE_INTERACTION_CONTROLLER_H_
diff --git a/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client.cc b/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client.cc
index c4013b3..1c450ef 100644
--- a/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client.cc
+++ b/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client.cc
@@ -95,6 +95,15 @@
   voice_interaction_controller_->NotifyContextEnabled(enabled);
 }
 
+void VoiceInteractionControllerClient::NotifyHotwordEnabled() {
+  DCHECK(profile_);
+  PrefService* prefs = profile_->GetPrefs();
+  // Make sure voice interaction is enabled.
+  DCHECK(prefs->GetBoolean(prefs::kVoiceInteractionEnabled));
+  bool enabled = prefs->GetBoolean(prefs::kVoiceInteractionHotwordEnabled);
+  voice_interaction_controller_->NotifyHotwordEnabled(enabled);
+}
+
 void VoiceInteractionControllerClient::NotifySetupCompleted() {
   DCHECK(profile_);
   PrefService* prefs = profile_->GetPrefs();
@@ -153,10 +162,17 @@
       base::BindRepeating(
           &VoiceInteractionControllerClient::NotifyContextEnabled,
           base::Unretained(this)));
+  pref_change_registrar_->Add(
+      prefs::kVoiceInteractionHotwordEnabled,
+      base::BindRepeating(
+          &VoiceInteractionControllerClient::NotifyHotwordEnabled,
+          base::Unretained(this)));
 
   NotifySetupCompleted();
   NotifySettingsEnabled();
   NotifyContextEnabled();
+  if (prefs->GetBoolean(prefs::kVoiceInteractionEnabled))
+    NotifyHotwordEnabled();
   NotifyFeatureAllowed();
 }
 
diff --git a/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client.h b/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client.h
index 29874c0..6c8c0af 100644
--- a/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client.h
+++ b/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client.h
@@ -55,6 +55,7 @@
   // Notify the controller about state changes.
   void NotifySettingsEnabled();
   void NotifyContextEnabled();
+  void NotifyHotwordEnabled();
   void NotifySetupCompleted();
   void NotifyFeatureAllowed();
 
diff --git a/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client_unittest.cc b/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client_unittest.cc
index a1a7960..fe500e79 100644
--- a/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client_unittest.cc
+++ b/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client_unittest.cc
@@ -118,6 +118,14 @@
       true,
       voice_interaction_controller()->voice_interaction_context_enabled());
 
+  ASSERT_EQ(false, prefs->GetBoolean(prefs::kVoiceInteractionHotwordEnabled));
+  prefs->SetBoolean(prefs::kVoiceInteractionHotwordEnabled, true);
+  ASSERT_EQ(true, prefs->GetBoolean(prefs::kVoiceInteractionHotwordEnabled));
+  voice_interaction_controller_client()->FlushMojoForTesting();
+  EXPECT_EQ(
+      true,
+      voice_interaction_controller()->voice_interaction_hotword_enabled());
+
   ASSERT_EQ(false,
             prefs->GetBoolean(prefs::kArcVoiceInteractionValuePropAccepted));
   prefs->SetBoolean(prefs::kArcVoiceInteractionValuePropAccepted, true);
diff --git a/chrome/browser/chromeos/hats/hats_dialog.cc b/chrome/browser/chromeos/hats/hats_dialog.cc
index ac6c1b6..c999ac7 100644
--- a/chrome/browser/chromeos/hats/hats_dialog.cc
+++ b/chrome/browser/chromeos/hats/hats_dialog.cc
@@ -17,7 +17,6 @@
 #include "components/prefs/pref_service.h"
 #include "components/version_info/version_info.h"
 #include "ui/base/l10n/l10n_util.h"
-#include "ui/base/pref_names.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/gfx/geometry/size.h"
 
diff --git a/chrome/browser/chromeos/locale_change_guard.cc b/chrome/browser/chromeos/locale_change_guard.cc
index 56484e5..9ad41c7 100644
--- a/chrome/browser/chromeos/locale_change_guard.cc
+++ b/chrome/browser/chromeos/locale_change_guard.cc
@@ -28,7 +28,6 @@
 #include "content/public/common/service_manager_connection.h"
 #include "services/service_manager/public/cpp/connector.h"
 #include "ui/base/l10n/l10n_util.h"
-#include "ui/base/pref_names.h"
 
 using base::UserMetricsAction;
 using content::WebContents;
diff --git a/chrome/browser/chromeos/login/auth/cryptohome_authenticator_unittest.cc b/chrome/browser/chromeos/login/auth/cryptohome_authenticator_unittest.cc
index f4257ff..97b8de7 100644
--- a/chrome/browser/chromeos/login/auth/cryptohome_authenticator_unittest.cc
+++ b/chrome/browser/chromeos/login/auth/cryptohome_authenticator_unittest.cc
@@ -150,6 +150,10 @@
     migrate_key_should_succeed_ = should_succeed;
   }
 
+  void set_mount_guest_should_succeed(bool should_succeed) {
+    mount_guest_should_succeed_ = should_succeed;
+  }
+
   void MountEx(const cryptohome::Identification& cryptohome_id,
                const cryptohome::AuthorizationRequest& auth,
                const cryptohome::MountRequest& request,
@@ -198,11 +202,23 @@
         FROM_HERE, base::BindOnce(std::move(callback), reply));
   }
 
+  void MountGuestEx(
+      const cryptohome::MountGuestRequest& request,
+      DBusMethodCallback<cryptohome::BaseReply> callback) override {
+    cryptohome::BaseReply reply;
+    if (!mount_guest_should_succeed_)
+      reply.set_error(cryptohome::CRYPTOHOME_ERROR_MOUNT_FATAL);
+
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE, base::BindOnce(std::move(callback), reply));
+  }
+
  private:
   cryptohome::Identification expected_id_;
   std::string expected_authorization_secret_;
   bool is_create_attempt_expected_ = false;
   bool migrate_key_should_succeed_ = false;
+  bool mount_guest_should_succeed_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(TestCryptohomeClient);
 };
@@ -384,6 +400,10 @@
     fake_cryptohome_client_->set_migrate_key_should_succeed(should_succeed);
   }
 
+  void ExpectMountGuestExCall(bool should_succeed) {
+    fake_cryptohome_client_->set_mount_guest_should_succeed(should_succeed);
+  }
+
   void RunResolve(CryptohomeAuthenticator* auth) {
     auth->Resolve();
     base::RunLoop().RunUntilIdle();
@@ -610,11 +630,7 @@
 TEST_F(CryptohomeAuthenticatorTest, DriveGuestLogin) {
   ExpectGuestLoginSuccess();
   FailOnLoginFailure();
-
-  // Set up mock async method caller to respond as though a tmpfs mount
-  // attempt has occurred and succeeded.
-  mock_caller_->SetUp(true, cryptohome::MOUNT_ERROR_NONE);
-  EXPECT_CALL(*mock_caller_, AsyncMountGuest(_)).Times(1).RetiresOnSaturation();
+  ExpectMountGuestExCall(true /*should_succeeed*/);
 
   auth_->LoginOffTheRecord();
   run_loop_.Run();
@@ -623,11 +639,7 @@
 TEST_F(CryptohomeAuthenticatorTest, DriveGuestLoginButFail) {
   FailOnGuestLoginSuccess();
   ExpectLoginFailure(AuthFailure(AuthFailure::COULD_NOT_MOUNT_TMPFS));
-
-  // Set up mock async method caller to respond as though a tmpfs mount
-  // attempt has occurred and failed.
-  mock_caller_->SetUp(false, cryptohome::MOUNT_ERROR_FATAL);
-  EXPECT_CALL(*mock_caller_, AsyncMountGuest(_)).Times(1).RetiresOnSaturation();
+  ExpectMountGuestExCall(false /*should_succeed*/);
 
   auth_->LoginOffTheRecord();
   run_loop_.Run();
diff --git a/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service_regular.cc b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service_regular.cc
index aa91c69..eccef3ff 100644
--- a/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service_regular.cc
+++ b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service_regular.cc
@@ -304,7 +304,10 @@
                     serialized_beacon_seeds);
 
     // This differentiates the local device from the remote device.
-    dict->SetBoolean(key_names::kKeyUnlockKey, device.unlock_key());
+    bool unlock_key = device.GetSoftwareFeatureState(
+                          cryptauth::SoftwareFeature::EASY_UNLOCK_HOST) ==
+                      cryptauth::SoftwareFeatureState::kEnabled;
+    dict->SetBoolean(key_names::kKeyUnlockKey, unlock_key);
 
     device_list->Append(std::move(dict));
   }
@@ -847,7 +850,10 @@
 cryptauth::RemoteDeviceRefList EasyUnlockServiceRegular::GetUnlockKeys() {
   cryptauth::RemoteDeviceRefList unlock_keys;
   for (const auto& remote_device : device_sync_client_->GetSyncedDevices()) {
-    if (remote_device.unlock_key())
+    bool unlock_key = remote_device.GetSoftwareFeatureState(
+                          cryptauth::SoftwareFeature::EASY_UNLOCK_HOST) ==
+                      cryptauth::SoftwareFeatureState::kEnabled;
+    if (unlock_key)
       unlock_keys.push_back(remote_device);
   }
   return unlock_keys;
diff --git a/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service_signin_chromeos.cc b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service_signin_chromeos.cc
index fa696b8..c4095b7 100644
--- a/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service_signin_chromeos.cc
+++ b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service_signin_chromeos.cc
@@ -521,6 +521,12 @@
       continue;
     }
 
+    std::map<cryptauth::SoftwareFeature, cryptauth::SoftwareFeatureState>
+        software_features;
+    software_features[cryptauth::SoftwareFeature::EASY_UNLOCK_HOST] =
+        device.unlock_key ? cryptauth::SoftwareFeatureState::kEnabled
+                          : cryptauth::SoftwareFeatureState::kNotSupported;
+
     std::vector<cryptauth::BeaconSeed> beacon_seeds;
     if (!device.serialized_beacon_seeds.empty()) {
       PA_LOG(INFO) << "Deserializing BeaconSeeds: "
@@ -532,10 +538,8 @@
 
     cryptauth::RemoteDevice remote_device(
         account_id.GetUserEmail(), std::string() /* name */, decoded_public_key,
-        decoded_psk /* persistent_symmetric_key */, device.unlock_key,
-        false /* supports_mobile_hotspot */, 0L /* last_update_time_millis */,
-        std::map<cryptauth::SoftwareFeature, cryptauth::SoftwareFeatureState>(),
-        beacon_seeds);
+        decoded_psk /* persistent_symmetric_key */,
+        0L /* last_update_time_millis */, software_features, beacon_seeds);
 
     remote_devices.push_back(remote_device);
     PA_LOG(INFO) << "Loaded Remote Device:\n"
@@ -577,7 +581,11 @@
   std::string local_device_id;
 
   for (const auto& remote_device : remote_devices) {
-    if (remote_device.unlock_key) {
+    if (base::ContainsKey(remote_device.software_features,
+                          cryptauth::SoftwareFeature::EASY_UNLOCK_HOST) &&
+        remote_device.software_features.at(
+            cryptauth::SoftwareFeature::EASY_UNLOCK_HOST) ==
+            cryptauth::SoftwareFeatureState::kEnabled) {
       if (!unlock_key_id.empty()) {
         PA_LOG(ERROR) << "Only one of the devices should be an unlock key.";
         SetHardlockStateForUser(account_id,
diff --git a/chrome/browser/chromeos/login/oobe_localization_browsertest.cc b/chrome/browser/chromeos/login/oobe_localization_browsertest.cc
index 121f501..3338af6 100644
--- a/chrome/browser/chromeos/login/oobe_localization_browsertest.cc
+++ b/chrome/browser/chromeos/login/oobe_localization_browsertest.cc
@@ -33,7 +33,6 @@
 #include "ui/base/ime/chromeos/input_method_manager.h"
 #include "ui/base/ime/chromeos/input_method_util.h"
 #include "ui/base/ime/chromeos/input_method_whitelist.h"
-#include "ui/base/pref_names.h"
 
 namespace base {
 class TaskRunner;
diff --git a/chrome/browser/chromeos/login/screens/welcome_screen.cc b/chrome/browser/chromeos/login/screens/welcome_screen.cc
index 7d89b94..36f1b02 100644
--- a/chrome/browser/chromeos/login/screens/welcome_screen.cc
+++ b/chrome/browser/chromeos/login/screens/welcome_screen.cc
@@ -32,7 +32,6 @@
 #include "components/prefs/pref_service.h"
 #include "content/public/browser/browser_thread.h"
 #include "ui/base/l10n/l10n_util.h"
-#include "ui/base/pref_names.h"
 
 namespace {
 
diff --git a/chrome/browser/chromeos/login/session/user_session_manager.cc b/chrome/browser/chromeos/login/session/user_session_manager.cc
index 406c1bf..f483593 100644
--- a/chrome/browser/chromeos/login/session/user_session_manager.cc
+++ b/chrome/browser/chromeos/login/session/user_session_manager.cc
@@ -137,7 +137,6 @@
 #include "ui/base/ime/chromeos/input_method_descriptor.h"
 #include "ui/base/ime/chromeos/input_method_manager.h"
 #include "ui/base/ime/chromeos/input_method_util.h"
-#include "ui/base/pref_names.h"
 #include "url/gurl.h"
 
 #if BUILDFLAG(ENABLE_RLZ)
diff --git a/chrome/browser/chromeos/login/ui/login_display_host_webui.cc b/chrome/browser/chromeos/login/ui/login_display_host_webui.cc
index e16ccd9..da86cbd 100644
--- a/chrome/browser/chromeos/login/ui/login_display_host_webui.cc
+++ b/chrome/browser/chromeos/login/ui/login_display_host_webui.cc
@@ -88,7 +88,6 @@
 #include "ui/base/ime/chromeos/extension_ime_util.h"
 #include "ui/base/ime/chromeos/input_method_manager.h"
 #include "ui/base/ime/chromeos/input_method_util.h"
-#include "ui/base/pref_names.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/base/ui_base_features.h"
 #include "ui/compositor/compositor_observer.h"
diff --git a/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos_unittest.cc b/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos_unittest.cc
index b48535a..25cfb82 100644
--- a/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos_unittest.cc
+++ b/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos_unittest.cc
@@ -62,6 +62,7 @@
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
 #include "services/network/test/test_url_loader_factory.h"
+#include "services/network/test/test_utils.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -258,20 +259,6 @@
     return fetcher;
   }
 
-  void SimulateOAuth2TokenResponse(const std::string& content) {
-    GURL url = GaiaUrls::GetInstance()->oauth2_token_url();
-    ASSERT_TRUE(test_url_loader_factory_.IsPending(url.spec()));
-    ASSERT_EQ(url,
-              (*test_url_loader_factory_.pending_requests())[0].request.url);
-
-    network::TestURLLoaderFactory::PendingRequest request =
-        std::move((*test_url_loader_factory_.pending_requests())[0]);
-    test_url_loader_factory_.pending_requests()->erase(
-        test_url_loader_factory_.pending_requests()->begin());
-    network::TestURLLoaderFactory::SimulateResponse(std::move(request),
-                                                    content);
-  }
-
   // Issues the OAuth2 tokens and returns the device management register job
   // if the flow succeeded.
   MockDeviceManagementJob* IssueOAuthToken(bool has_request_token) {
@@ -310,7 +297,11 @@
 
       // Issue the access token. This uses OAuth2AccessTokenFetcher which uses
       // the network service.
-      SimulateOAuth2TokenResponse(kOAuth2AccessTokenData);
+      test_url_loader_factory_.SimulateResponseForPendingRequest(
+          GaiaUrls::GetInstance()->oauth2_token_url(),
+          network::URLLoaderCompletionStatus(net::OK),
+          network::CreateResourceResponseHead(net::HTTP_OK),
+          kOAuth2AccessTokenData);
     } else {
       // Since the refresh token is available, OAuth2TokenService was used
       // to request the access token and not UserCloudPolicyTokenForwarder.
diff --git a/chrome/browser/extensions/api/settings_private/prefs_util.cc b/chrome/browser/extensions/api/settings_private/prefs_util.cc
index 66d5312..8a87a3e 100644
--- a/chrome/browser/extensions/api/settings_private/prefs_util.cc
+++ b/chrome/browser/extensions/api/settings_private/prefs_util.cc
@@ -37,7 +37,6 @@
 #include "extensions/browser/extension_system.h"
 #include "extensions/browser/management_policy.h"
 #include "extensions/common/extension.h"
-#include "ui/base/pref_names.h"
 
 #if defined(OS_CHROMEOS)
 #include "ash/public/cpp/ash_pref_names.h"  // nogncheck
diff --git a/chrome/browser/extensions/chrome_extension_web_contents_observer.cc b/chrome/browser/extensions/chrome_extension_web_contents_observer.cc
index 6ba265a..65421f9 100644
--- a/chrome/browser/extensions/chrome_extension_web_contents_observer.cc
+++ b/chrome/browser/extensions/chrome_extension_web_contents_observer.cc
@@ -22,6 +22,8 @@
 #include "content/public/common/content_switches.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/extension_system.h"
+#include "extensions/browser/extensions_browser_client.h"
+#include "extensions/browser/kiosk/kiosk_delegate.h"
 #include "extensions/common/constants.h"
 #include "extensions/common/extension_messages.h"
 #include "extensions/common/extension_urls.h"
@@ -177,13 +179,20 @@
   const ExtensionRegistry* registry = ExtensionRegistry::Get(
       navigation_handle->GetWebContents()->GetBrowserContext());
 
+  const Extension* extension =
+      GetExtensionFromFrame(web_contents()->GetMainFrame(), false);
+  DCHECK(ExtensionsBrowserClient::Get()->GetKioskDelegate());
+  bool is_kiosk = extension && ExtensionsBrowserClient::Get()
+                                   ->GetKioskDelegate()
+                                   ->IsAutoLaunchedKioskApp(extension->id());
+
   // If the top most frame is an extension, packaged app, hosted app, etc. then
   // the main frame and all iframes should be able to autoplay without
   // restriction. <webview> should still have autoplay blocked though.
   GURL url = navigation_handle->IsInMainFrame()
                  ? navigation_handle->GetURL()
                  : navigation_handle->GetWebContents()->GetLastCommittedURL();
-  if (registry->enabled_extensions().GetExtensionOrAppByURL(url)) {
+  if (is_kiosk || registry->enabled_extensions().GetExtensionOrAppByURL(url)) {
     blink::mojom::AutoplayConfigurationClientAssociatedPtr client;
     navigation_handle->GetRenderFrameHost()
         ->GetRemoteAssociatedInterfaces()
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index f6eb16c..1fc44e3 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -706,12 +706,6 @@
 const char kEnableTokenBindingName[] = "Token Binding.";
 const char kEnableTokenBindingDescription[] = "Enable Token Binding support.";
 
-extern const char kEnableTouchpadAndWheelScrollLatchingName[] =
-    "Wheel Scroll Latching.";
-extern const char kEnableTouchpadAndWheelScrollLatchingDescription[] =
-    "Wheel scroll latching enforces latching to a single element for the "
-    "duration of a scroll sequence.";
-
 const char kEnableUseZoomForDsfName[] =
     "Use Blink's zoom for device scale factor.";
 const char kEnableUseZoomForDsfDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 3a326c5..67e26c8 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -447,9 +447,6 @@
 extern const char kEnableTokenBindingName[];
 extern const char kEnableTokenBindingDescription[];
 
-extern const char kEnableTouchpadAndWheelScrollLatchingName[];
-extern const char kEnableTouchpadAndWheelScrollLatchingDescription[];
-
 extern const char kEnableUseZoomForDsfName[];
 extern const char kEnableUseZoomForDsfDescription[];
 extern const char kEnableUseZoomForDsfChoiceDefault[];
diff --git a/chrome/browser/global_keyboard_shortcuts_mac.mm b/chrome/browser/global_keyboard_shortcuts_mac.mm
index 903444f..c6d5d03 100644
--- a/chrome/browser/global_keyboard_shortcuts_mac.mm
+++ b/chrome/browser/global_keyboard_shortcuts_mac.mm
@@ -123,9 +123,9 @@
     {true,  true,  false, false, kVK_ANSI_RightBracket, IDC_SELECT_NEXT_TAB},
     {true,  true,  false, false, kVK_ANSI_LeftBracket,  IDC_SELECT_PREVIOUS_TAB},
     {false, false, true,  false, kVK_PageDown,          IDC_SELECT_NEXT_TAB},
+    {false, false, true,  false, kVK_Tab,               IDC_SELECT_NEXT_TAB},
     {false, false, true,  false, kVK_PageUp,            IDC_SELECT_PREVIOUS_TAB},
-    {true,  false, false, true,  kVK_RightArrow,        IDC_SELECT_NEXT_TAB},
-    {true,  false, false, true,  kVK_LeftArrow,         IDC_SELECT_PREVIOUS_TAB},
+    {false, true,  true,  false, kVK_Tab,               IDC_SELECT_PREVIOUS_TAB},
 
     // Cmd-0..8 select the nth tab, with cmd-9 being "last tab".
     {true, false, false, false, kVK_ANSI_1,             IDC_SELECT_TAB_0},
diff --git a/chrome/browser/lifetime/application_lifetime.cc b/chrome/browser/lifetime/application_lifetime.cc
index a1016eb..1ee9454 100644
--- a/chrome/browser/lifetime/application_lifetime.cc
+++ b/chrome/browser/lifetime/application_lifetime.cc
@@ -36,7 +36,6 @@
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/navigation_details.h"
 #include "content/public/browser/notification_service.h"
-#include "ui/base/pref_names.h"
 
 #if !defined(OS_ANDROID)
 #include "chrome/browser/lifetime/termination_notification.h"
diff --git a/chrome/browser/net/network_connection_tracker_browsertest.cc b/chrome/browser/net/network_connection_tracker_browsertest.cc
index 38937f6..7e28b03 100644
--- a/chrome/browser/net/network_connection_tracker_browsertest.cc
+++ b/chrome/browser/net/network_connection_tracker_browsertest.cc
@@ -3,14 +3,20 @@
 // found in the LICENSE file.
 
 #include "base/callback_forward.h"
+#include "base/feature_list.h"
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/run_loop.h"
-#include "base/test/scoped_feature_list.h"
+#include "base/sequence_checker.h"
+#include "base/test/bind_test_util.h"
 #include "build/build_config.h"
 #include "chrome/browser/browser_process_impl.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
 #include "chrome/test/base/in_process_browser_test.h"
+#include "content/public/browser/browser_context.h"
 #include "content/public/browser/network_connection_tracker.h"
+#include "content/public/browser/storage_partition.h"
 #include "content/public/common/service_manager_connection.h"
 #include "content/public/common/service_names.mojom.h"
 #include "content/public/test/browser_test.h"
@@ -30,7 +36,9 @@
   explicit TestNetworkConnectionObserver(NetworkConnectionTracker* tracker)
       : num_notifications_(0),
         tracker_(tracker),
+        run_loop_(std::make_unique<base::RunLoop>()),
         connection_type_(network::mojom::ConnectionType::CONNECTION_UNKNOWN) {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
     tracker_->AddNetworkConnectionObserver(this);
   }
 
@@ -40,6 +48,7 @@
 
   // NetworkConnectionObserver implementation:
   void OnConnectionChanged(network::mojom::ConnectionType type) override {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
     network::mojom::ConnectionType queried_type;
     bool sync = tracker_->GetConnectionType(
         &queried_type,
@@ -49,10 +58,13 @@
 
     num_notifications_++;
     connection_type_ = type;
-    run_loop_.Quit();
+    run_loop_->Quit();
   }
 
-  void WaitForNotification() { run_loop_.Run(); }
+  void WaitForNotification() {
+    run_loop_->Run();
+    run_loop_ = std::make_unique<base::RunLoop>();
+  }
 
   size_t num_notifications() const { return num_notifications_; }
   network::mojom::ConnectionType connection_type() const {
@@ -62,27 +74,21 @@
  private:
   size_t num_notifications_;
   NetworkConnectionTracker* tracker_;
-  base::RunLoop run_loop_;
+  std::unique_ptr<base::RunLoop> run_loop_;
   network::mojom::ConnectionType connection_type_;
 
+  SEQUENCE_CHECKER(sequence_checker_);
+
   DISALLOW_COPY_AND_ASSIGN(TestNetworkConnectionObserver);
 };
 
 }  // namespace
 
-class NetworkConnectionTrackerBrowserTest
-    : public InProcessBrowserTest,
-      public testing::WithParamInterface<bool> {
+class NetworkConnectionTrackerBrowserTest : public InProcessBrowserTest {
  public:
-  NetworkConnectionTrackerBrowserTest() : network_service_enabled_(GetParam()) {
-    if (network_service_enabled_) {
-      scoped_feature_list_.InitAndEnableFeature(
-          network::features::kNetworkService);
-    } else {
-      scoped_feature_list_.InitAndDisableFeature(
-          network::features::kNetworkService);
-    }
-  }
+  NetworkConnectionTrackerBrowserTest()
+      : network_service_enabled_(
+            base::FeatureList::IsEnabled(network::features::kNetworkService)) {}
   ~NetworkConnectionTrackerBrowserTest() override {}
 
   // Simulates a network connection change.
@@ -106,12 +112,11 @@
   bool network_service_enabled() const { return network_service_enabled_; }
 
  private:
-  base::test::ScopedFeatureList scoped_feature_list_;
   const bool network_service_enabled_;
 };
 
 // Basic test to make sure NetworkConnectionTracker is set up.
-IN_PROC_BROWSER_TEST_P(NetworkConnectionTrackerBrowserTest,
+IN_PROC_BROWSER_TEST_F(NetworkConnectionTrackerBrowserTest,
                        NetworkConnectionTracker) {
 #if defined(OS_CHROMEOS) || defined(OS_MACOSX)
   // NetworkService on ChromeOS doesn't yet have a NetworkChangeManager
@@ -146,8 +151,56 @@
   EXPECT_EQ(1u, network_connection_observer.num_notifications());
 }
 
-INSTANTIATE_TEST_CASE_P(/* no prefix */,
-                        NetworkConnectionTrackerBrowserTest,
-                        testing::Bool());
+// Simulates a network service crash, and ensures that network change manager
+// binds to the restarted network service.
+IN_PROC_BROWSER_TEST_F(NetworkConnectionTrackerBrowserTest,
+                       SimulateNetworkServiceCrash) {
+  // Out-of-process network service is not enabled, so network service's crash
+  // and restart aren't applicable.
+  if (!network_service_enabled())
+    return;
+
+  NetworkConnectionTracker* tracker =
+      g_browser_process->network_connection_tracker();
+  EXPECT_NE(nullptr, tracker);
+
+  // Issue a GetConnectionType() request to make sure NetworkService has been
+  // started up. This way, NetworkService will receive the broadcast when
+  // SimulateNetworkChange() is called.
+  base::RunLoop run_loop;
+  network::mojom::ConnectionType ignored_type;
+  bool sync = tracker->GetConnectionType(
+      &ignored_type,
+      base::BindOnce(
+          [](base::RunLoop* run_loop, network::mojom::ConnectionType type) {
+            run_loop->Quit();
+          },
+          base::Unretained(&run_loop)));
+  if (!sync)
+    run_loop.Run();
+
+  TestNetworkConnectionObserver network_connection_observer(tracker);
+  SimulateNetworkChange(network::mojom::ConnectionType::CONNECTION_3G);
+
+  network_connection_observer.WaitForNotification();
+  EXPECT_EQ(network::mojom::ConnectionType::CONNECTION_3G,
+            network_connection_observer.connection_type());
+  // Wait a bit longer to make sure only 1 notification is received and that
+  // there is no duplicate notification.
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(1u, network_connection_observer.num_notifications());
+
+  SimulateNetworkServiceCrash();
+
+  SimulateNetworkChange(network::mojom::ConnectionType::CONNECTION_2G);
+  network_connection_observer.WaitForNotification();
+  EXPECT_EQ(network::mojom::ConnectionType::CONNECTION_2G,
+            network_connection_observer.connection_type());
+
+  // Wait a bit longer to make sure only 2 notifications are received.
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(2u, network_connection_observer.num_notifications());
+}
 
 }  // namespace content
diff --git a/chrome/browser/net/network_quality_tracker_browsertest.cc b/chrome/browser/net/network_quality_tracker_browsertest.cc
index 94004ef..2f143ba 100644
--- a/chrome/browser/net/network_quality_tracker_browsertest.cc
+++ b/chrome/browser/net/network_quality_tracker_browsertest.cc
@@ -113,13 +113,6 @@
   NetworkQualityTrackerBrowserTest()
       : network_service_enabled_(
             base::FeatureList::IsEnabled(network::features::kNetworkService)) {
-    if (network_service_enabled_) {
-      scoped_feature_list_.InitAndEnableFeature(
-          network::features::kNetworkService);
-    } else {
-      scoped_feature_list_.InitAndDisableFeature(
-          network::features::kNetworkService);
-    }
   }
   ~NetworkQualityTrackerBrowserTest() override {}
 
@@ -154,7 +147,6 @@
   bool network_service_enabled() const { return network_service_enabled_; }
 
  private:
-  base::test::ScopedFeatureList scoped_feature_list_;
   const bool network_service_enabled_;
 };
 
diff --git a/chrome/browser/net/variations_http_headers_browsertest.cc b/chrome/browser/net/variations_http_headers_browsertest.cc
index 02ae4a8a..859cc89 100644
--- a/chrome/browser/net/variations_http_headers_browsertest.cc
+++ b/chrome/browser/net/variations_http_headers_browsertest.cc
@@ -241,29 +241,6 @@
   EXPECT_FALSE(HasReceivedHeader(GetExampleUrl(), "X-Client-Data"));
 }
 
-// Verify in an integration that that the variations header (X-Client-Data) is
-// correctly attached and stripped from network requests that are triggered via
-// the network service.
-IN_PROC_BROWSER_TEST_F(VariationsHttpHeadersBrowserTest,
-                       TestStrippingHeadersFromNetworkService) {
-  content::StoragePartition* partition =
-      content::BrowserContext::GetDefaultStoragePartition(browser()->profile());
-  network::mojom::NetworkContext* network_context =
-      partition->GetNetworkContext();
-  EXPECT_EQ(net::OK, content::LoadBasicRequest(network_context,
-                                               GetGoogleRedirectUrl1()));
-
-  // TODO(crbug.com/794644) Once the network service stack starts injecting
-  // X-Client-Data headers, the following expectations should be used.
-  EXPECT_FALSE(HasReceivedHeader(GetGoogleRedirectUrl1(), "X-Client-Data"));
-  /*
-  EXPECT_TRUE(HasReceivedHeader(GetGoogleRedirectUrl1(), "X-Client-Data"));
-  EXPECT_TRUE(HasReceivedHeader(GetGoogleRedirectUrl2(), "X-Client-Data"));
-  EXPECT_TRUE(HasReceivedHeader(GetExampleUrl(), "Host"));
-  EXPECT_FALSE(HasReceivedHeader(GetExampleUrl(), "X-Client-Data"));
-  */
-}
-
 IN_PROC_BROWSER_TEST_F(VariationsHttpHeadersBrowserTest,
                        TestStrippingHeadersFromSubresourceRequest) {
   GURL url = server()->GetURL("/simple_page.html");
diff --git a/chrome/browser/policy/cloud/test_request_interceptor.cc b/chrome/browser/policy/cloud/test_request_interceptor.cc
index ae4a200..62dee70 100644
--- a/chrome/browser/policy/cloud/test_request_interceptor.cc
+++ b/chrome/browser/policy/cloud/test_request_interceptor.cc
@@ -319,7 +319,7 @@
   status.decoded_body_length = content.size();
 
   network::ResourceResponseHead head =
-      network::TestURLLoaderFactory::CreateResourceResponseHead(net::HTTP_OK);
+      network::CreateResourceResponseHead(net::HTTP_OK);
   head.mime_type = "application/protobuf";
 
   factory->AddResponse(request.url, head, content, status);
diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
index 2f10786..17afce8 100644
--- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc
+++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
@@ -63,7 +63,6 @@
 #include "extensions/buildflags/buildflags.h"
 #include "media/media_buildflags.h"
 #include "ppapi/buildflags/buildflags.h"
-#include "ui/base/pref_names.h"
 
 #if defined(OS_ANDROID)
 #include "chrome/browser/android/contextual_suggestions/contextual_suggestions_prefs.h"
diff --git a/chrome/browser/policy/policy_browsertest.cc b/chrome/browser/policy/policy_browsertest.cc
index 630e574..7092b1a2 100644
--- a/chrome/browser/policy/policy_browsertest.cc
+++ b/chrome/browser/policy/policy_browsertest.cc
@@ -234,7 +234,6 @@
 #include "third_party/blink/public/platform/web_input_event.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/page_transition_types.h"
-#include "ui/base/pref_names.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/display/manager/display_manager.h"
 #include "url/gurl.h"
diff --git a/chrome/browser/prefs/chrome_command_line_pref_store.cc b/chrome/browser/prefs/chrome_command_line_pref_store.cc
index 139f9b0..239130a 100644
--- a/chrome/browser/prefs/chrome_command_line_pref_store.cc
+++ b/chrome/browser/prefs/chrome_command_line_pref_store.cc
@@ -27,7 +27,6 @@
 #include "components/sync/base/pref_names.h"
 #include "content/public/common/content_switches.h"
 #include "services/network/public/cpp/network_switches.h"
-#include "ui/base/pref_names.h"
 #include "ui/base/ui_base_switches.h"
 #include "ui/display/display_switches.h"
 
diff --git a/chrome/browser/prefs/chrome_command_line_pref_store_unittest.cc b/chrome/browser/prefs/chrome_command_line_pref_store_unittest.cc
index 434c13a..5194a110 100644
--- a/chrome/browser/prefs/chrome_command_line_pref_store_unittest.cc
+++ b/chrome/browser/prefs/chrome_command_line_pref_store_unittest.cc
@@ -16,7 +16,6 @@
 #include "components/proxy_config/proxy_config_dictionary.h"
 #include "components/proxy_config/proxy_config_pref_names.h"
 #include "content/public/common/content_switches.h"
-#include "ui/base/pref_names.h"
 #include "ui/base/ui_base_switches.h"
 
 namespace {
diff --git a/chrome/browser/profiles/profile.cc b/chrome/browser/profiles/profile.cc
index 641a1b32..b2e4553 100644
--- a/chrome/browser/profiles/profile.cc
+++ b/chrome/browser/profiles/profile.cc
@@ -29,7 +29,6 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_ui.h"
 #include "extensions/buildflags/buildflags.h"
-#include "ui/base/pref_names.h"
 
 #if defined(OS_CHROMEOS)
 #include "base/command_line.h"
diff --git a/chrome/browser/profiles/profile_browsertest.cc b/chrome/browser/profiles/profile_browsertest.cc
index 69270d03..577f3c8 100644
--- a/chrome/browser/profiles/profile_browsertest.cc
+++ b/chrome/browser/profiles/profile_browsertest.cc
@@ -9,6 +9,8 @@
 #include <memory>
 
 #include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/files/file_path_watcher.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/json/json_reader.h"
@@ -35,6 +37,7 @@
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/common/chrome_constants.h"
 #include "chrome/common/chrome_features.h"
+#include "chrome/common/chrome_paths.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
@@ -846,3 +849,109 @@
       net::LOAD_ONLY_FROM_CACHE);
   url_fetcher_delegate3.WaitForCompletion();
 }
+
+// Create a media cache file, and make sure it's deleted by the time the next
+// test runs.
+IN_PROC_BROWSER_TEST_F(ProfileWithoutMediaCacheBrowserTest,
+                       PRE_DeleteMediaCache) {
+  base::FilePath media_cache_path =
+      browser()->profile()->GetPath().Append(chrome::kMediaCacheDirname);
+
+  base::ScopedAllowBlockingForTesting allow_blocking;
+  EXPECT_TRUE(base::CreateDirectory(media_cache_path));
+  std::string data = "foo";
+  base::WriteFile(media_cache_path.AppendASCII("foo"), data.c_str(),
+                  data.size());
+}
+
+IN_PROC_BROWSER_TEST_F(ProfileWithoutMediaCacheBrowserTest, DeleteMediaCache) {
+  base::FilePath media_cache_path =
+      browser()->profile()->GetPath().Append(chrome::kMediaCacheDirname);
+
+  base::ScopedAllowBlockingForTesting allow_blocking;
+
+  // Wait until the file is deleted. Unfortunately, there's no way to wait until
+  // the deletion itself has run directly, so there are several possible orders
+  // of events that this code must handle:
+  //
+  // * In PRE_DeleteMediaCache, the media cache was deleted before the test
+  // completed, by the task posted on Profile creation.
+  //
+  // * In DeleteMediaCache, the media cache was deleted by the delete media
+  // cache task before starting the FilePathWatcher.
+  //
+  // * In DeleteMediaCache, the media cache was deleted by the delete media
+  // cache task after starting the FilePathWatcher.
+  //
+  // It also may be possible to get a notification of the media cache being
+  // created from the PRE_DeleteMediaCache test, so allow multiple watch events
+  // to happen.
+  while (true) {
+    base::RunLoop run_loop;
+    base::FilePathWatcher watcher;
+    EXPECT_TRUE(watcher.Watch(media_cache_path, false /* recursive */,
+                              base::BindRepeating(
+                                  [](base::RunLoop* run_loop,
+                                     const base::FilePath& path, bool error) {
+                                    EXPECT_FALSE(error);
+                                    run_loop->Quit();
+                                  },
+                                  &run_loop)));
+    if (!base::PathExists(media_cache_path))
+      return;
+    run_loop.Run();
+  }
+}
+
+// Create a media cache file, and make sure it's deleted by initializing an
+// extension browser context.
+IN_PROC_BROWSER_TEST_F(ProfileWithoutMediaCacheBrowserTest,
+                       PRE_DeleteIsolatedAppMediaCache) {
+  scoped_refptr<const extensions::Extension> app =
+      BuildTestApp(browser()->profile());
+  content::StoragePartition* extension_partition =
+      content::BrowserContext::GetStoragePartitionForSite(
+          browser()->profile(),
+          extensions::Extension::GetBaseURLFromExtensionId(app->id()));
+
+  base::FilePath extension_media_cache_path =
+      extension_partition->GetPath().Append(chrome::kMediaCacheDirname);
+
+  base::ScopedAllowBlockingForTesting allow_blocking;
+  EXPECT_TRUE(base::CreateDirectory(extension_media_cache_path));
+  std::string data = "foo";
+  base::WriteFile(extension_media_cache_path.AppendASCII("foo"), data.c_str(),
+                  data.size());
+}
+
+IN_PROC_BROWSER_TEST_F(ProfileWithoutMediaCacheBrowserTest,
+                       DeleteIsolatedAppMediaCache) {
+  scoped_refptr<const extensions::Extension> app =
+      BuildTestApp(browser()->profile());
+  content::StoragePartition* extension_partition =
+      content::BrowserContext::GetStoragePartitionForSite(
+          browser()->profile(),
+          extensions::Extension::GetBaseURLFromExtensionId(app->id()));
+
+  base::FilePath extension_media_cache_path =
+      extension_partition->GetPath().Append(chrome::kMediaCacheDirname);
+
+  // As with the DeleteMediaCache test, have to allow for a lot of different
+  // deletion orderings here.
+  base::ScopedAllowBlockingForTesting allow_blocking;
+  while (true) {
+    base::RunLoop run_loop;
+    base::FilePathWatcher watcher;
+    EXPECT_TRUE(watcher.Watch(extension_media_cache_path, false /* recursive */,
+                              base::BindRepeating(
+                                  [](base::RunLoop* run_loop,
+                                     const base::FilePath& path, bool error) {
+                                    EXPECT_FALSE(error);
+                                    run_loop->Quit();
+                                  },
+                                  &run_loop)));
+    if (!base::PathExists(extension_media_cache_path))
+      return;
+    run_loop.Run();
+  }
+}
diff --git a/chrome/browser/profiles/profile_impl.cc b/chrome/browser/profiles/profile_impl.cc
index 51293df..bd76e87 100644
--- a/chrome/browser/profiles/profile_impl.cc
+++ b/chrome/browser/profiles/profile_impl.cc
@@ -137,7 +137,6 @@
 #include "services/preferences/public/mojom/tracked_preference_validation_delegate.mojom.h"
 #include "services/service_manager/public/cpp/service.h"
 #include "ui/base/l10n/l10n_util.h"
-#include "ui/base/pref_names.h"
 
 #if defined(OS_CHROMEOS)
 #include "chrome/browser/chromeos/arc/arc_service_launcher.h"
diff --git a/chrome/browser/profiles/profile_impl_io_data.cc b/chrome/browser/profiles/profile_impl_io_data.cc
index 04b0193..6e6d35c 100644
--- a/chrome/browser/profiles/profile_impl_io_data.cc
+++ b/chrome/browser/profiles/profile_impl_io_data.cc
@@ -12,6 +12,8 @@
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/feature_list.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/metrics/field_trial.h"
@@ -19,6 +21,7 @@
 #include "base/stl_util.h"
 #include "base/strings/string_util.h"
 #include "base/task_scheduler/post_task.h"
+#include "base/task_scheduler/task_traits.h"
 #include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chrome_notification_types.h"
@@ -112,6 +115,19 @@
   return net::CACHE_BACKEND_DEFAULT;
 }
 
+void MaybeDeleteMediaCache(const base::FilePath& media_cache_path) {
+  if (!base::FeatureList::IsEnabled(features::kUseSameCacheForMedia) ||
+      media_cache_path.empty()) {
+    return;
+  }
+  base::PostTaskWithTraits(
+      FROM_HERE,
+      {base::TaskPriority::BACKGROUND, base::MayBlock(),
+       base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
+      base::BindOnce(base::IgnoreResult(&base::DeleteFile), media_cache_path,
+                     true /* recursive */));
+}
+
 }  // namespace
 
 using content::BrowserThread;
@@ -521,6 +537,8 @@
   InitializeExtensionsRequestContext(profile_params);
 #endif
 
+  MaybeDeleteMediaCache(lazy_params_->media_cache_path);
+
   // Create a media request context based on the main context, but using a
   // media cache.  It shares the same job factory as the main context.
   StoragePartitionDescriptor details(profile_path_, false);
@@ -554,6 +572,11 @@
         protocol_handler_interceptor,
     content::ProtocolHandlerMap* protocol_handlers,
     content::URLRequestInterceptorScopedVector request_interceptors) const {
+  if (!partition_descriptor.in_memory) {
+    MaybeDeleteMediaCache(
+        partition_descriptor.path.Append(chrome::kMediaCacheDirname));
+  }
+
   // Copy most state from the main context.
   AppRequestContext* context = new AppRequestContext();
   context->CopyFrom(main_context);
diff --git a/chrome/browser/resource_coordinator/intervention_policy_database.cc b/chrome/browser/resource_coordinator/intervention_policy_database.cc
index 3e43def..6210a00 100644
--- a/chrome/browser/resource_coordinator/intervention_policy_database.cc
+++ b/chrome/browser/resource_coordinator/intervention_policy_database.cc
@@ -54,6 +54,13 @@
                      weak_factory_.GetWeakPtr()));
 }
 
+void InterventionPolicyDatabase::AddOriginPoliciesForTesting(
+    const url::Origin& origin,
+    OriginInterventionPolicies policies) {
+  database_.emplace(SerializeOriginIntoDatabaseKey(origin),
+                    std::move(policies));
+}
+
 // static
 InterventionPolicyDatabase::InterventionsMap
 InterventionPolicyDatabase::ReadDatabaseFromProtoFileOnSequence(
diff --git a/chrome/browser/resource_coordinator/intervention_policy_database.h b/chrome/browser/resource_coordinator/intervention_policy_database.h
index 5558fde1..95463f5f 100644
--- a/chrome/browser/resource_coordinator/intervention_policy_database.h
+++ b/chrome/browser/resource_coordinator/intervention_policy_database.h
@@ -50,6 +50,9 @@
       const base::Version& version,
       std::unique_ptr<base::DictionaryValue> manifest);
 
+  void AddOriginPoliciesForTesting(const url::Origin& origin,
+                                   OriginInterventionPolicies policies);
+
  protected:
   // Map that associates the MD5 hash of an origin to its polices.
   using InterventionsMap =
diff --git a/chrome/browser/resource_coordinator/tab_lifecycle_unit.cc b/chrome/browser/resource_coordinator/tab_lifecycle_unit.cc
index 5482e3d..e29168f 100644
--- a/chrome/browser/resource_coordinator/tab_lifecycle_unit.cc
+++ b/chrome/browser/resource_coordinator/tab_lifecycle_unit.cc
@@ -16,11 +16,13 @@
 #include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h"
 #include "chrome/browser/media/webrtc/media_stream_capture_indicator.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/resource_coordinator/intervention_policy_database.h"
 #include "chrome/browser/resource_coordinator/lifecycle_unit_state.mojom.h"
 #include "chrome/browser/resource_coordinator/local_site_characteristics_data_store_factory.h"
 #include "chrome/browser/resource_coordinator/tab_activity_watcher.h"
 #include "chrome/browser/resource_coordinator/tab_helper.h"
 #include "chrome/browser/resource_coordinator/tab_lifecycle_observer.h"
+#include "chrome/browser/resource_coordinator/tab_lifecycle_unit_source.h"
 #include "chrome/browser/resource_coordinator/tab_load_tracker.h"
 #include "chrome/browser/resource_coordinator/tab_manager_features.h"
 #include "chrome/browser/resource_coordinator/time.h"
@@ -219,6 +221,10 @@
                     DecisionFailureReason::HEURISTIC_TITLE);
 }
 
+InterventionPolicyDatabase* GetInterventionPolicyDatabase() {
+  return TabLifecycleUnitSource::GetInstance()->intervention_policy_database();
+}
+
 }  // namespace
 
 TabLifecycleUnitSource::TabLifecycleUnit::TabLifecycleUnit(
@@ -438,11 +444,24 @@
     return false;
   }
 
+  auto intervention_policy = GetInterventionPolicyDatabase()->GetFreezingPolicy(
+      url::Origin::Create(GetWebContents()->GetLastCommittedURL()));
+
+  switch (intervention_policy) {
+    case OriginInterventions::OPT_IN:
+      decision_details->AddReason(DecisionSuccessReason::GLOBAL_WHITELIST);
+      break;
+    case OriginInterventions::OPT_OUT:
+      decision_details->AddReason(DecisionFailureReason::GLOBAL_BLACKLIST);
+      break;
+    case OriginInterventions::DEFAULT:
+      break;
+  }
+
   if (GetWebContents()->GetVisibility() == content::Visibility::VISIBLE)
     decision_details->AddReason(DecisionFailureReason::LIVE_STATE_VISIBLE);
 
-  CheckIfTabIsUsedInBackground(decision_details,
-                               false /* urgent_intervention */);
+  CheckIfTabIsUsedInBackground(decision_details, InterventionType::kProactive);
 
   if (decision_details->reasons().empty()) {
     decision_details->AddReason(
@@ -504,9 +523,26 @@
 #endif  // defined(OS_CHROMEOS)
   }
 
-// We deliberately run through all of the logic without early termination.
-// This ensures that the decision details lists all possible reasons that the
-// transition can be denied.
+  // We deliberately run through all of the logic without early termination.
+  // This ensures that the decision details lists all possible reasons that the
+  // transition can be denied.
+
+  if (reason == DiscardReason::kProactive) {
+    auto intervention_policy =
+        GetInterventionPolicyDatabase()->GetDiscardingPolicy(
+            url::Origin::Create(GetWebContents()->GetLastCommittedURL()));
+
+    switch (intervention_policy) {
+      case OriginInterventions::OPT_IN:
+        decision_details->AddReason(DecisionSuccessReason::GLOBAL_WHITELIST);
+        break;
+      case OriginInterventions::OPT_OUT:
+        decision_details->AddReason(DecisionFailureReason::GLOBAL_BLACKLIST);
+        break;
+      case OriginInterventions::DEFAULT:
+        break;
+    }
+  }
 
 #if defined(OS_CHROMEOS)
   if (GetWebContents()->GetVisibility() == content::Visibility::VISIBLE)
@@ -535,7 +571,9 @@
   }
 
   CheckIfTabIsUsedInBackground(decision_details,
-                               reason == DiscardReason::kUrgent);
+                               reason == DiscardReason::kProactive
+                                   ? InterventionType::kProactive
+                                   : InterventionType::kExternalOrUrgent);
 
   if (decision_details->reasons().empty()) {
     decision_details->AddReason(
@@ -818,7 +856,7 @@
 
 void TabLifecycleUnitSource::TabLifecycleUnit::CheckIfTabIsUsedInBackground(
     DecisionDetails* decision_details,
-    bool urgent_intervention) const {
+    InterventionType intervention_type) const {
   DCHECK(decision_details);
 
   // We deliberately run through all of the logic without early termination.
@@ -830,9 +868,9 @@
 
   // Consult the local database to see if this tab could try to communicate with
   // the user while in background (don't check for the visibility here as
-  // there's already a check for that above). Skip this test if this is an
-  // urgent intervention.
-  if (!urgent_intervention) {
+  // there's already a check for that above). Only do this for proactive
+  // interventions.
+  if (intervention_type == InterventionType::kProactive) {
     CheckIfTabCanCommunicateWithUserWhileInBackground(GetWebContents(),
                                                       decision_details);
   }
diff --git a/chrome/browser/resource_coordinator/tab_lifecycle_unit.h b/chrome/browser/resource_coordinator/tab_lifecycle_unit.h
index 01aca6c..33516c1 100644
--- a/chrome/browser/resource_coordinator/tab_lifecycle_unit.h
+++ b/chrome/browser/resource_coordinator/tab_lifecycle_unit.h
@@ -113,6 +113,12 @@
   friend class TabLifecycleUnitSource;
 
  private:
+  // Indicates if an intervention (freezing or discarding) is proactive or not.
+  enum class InterventionType {
+    kProactive,
+    kExternalOrUrgent,
+  };
+
   // Determines if the tab is a media tab, and populates an optional
   // |decision_details| with full details.
   bool IsMediaTabImpl(DecisionDetails* decision_details) const;
@@ -146,11 +152,11 @@
 
   // Indicates if freezing or discarding this tab would be noticeable by the
   // user even if it isn't brought back to the foreground. Populates
-  // |decision_details| with full details. |urgent_intervention| indicates if
-  // this is for an urgent intervention, in which case some heuristics will be
-  // skipped.
+  // |decision_details| with full details. If |intervention_type| indicates that
+  // this is a proactive intervention then more heuristics will be
+  // applied.
   void CheckIfTabIsUsedInBackground(DecisionDetails* decision_details,
-                                    bool urgent_intervention) const;
+                                    InterventionType intervention_type) const;
 
   // List of observers to notify when the discarded state or the auto-
   // discardable state of this tab changes.
diff --git a/chrome/browser/resource_coordinator/tab_lifecycle_unit_source.cc b/chrome/browser/resource_coordinator/tab_lifecycle_unit_source.cc
index 5830d42..2d30144 100644
--- a/chrome/browser/resource_coordinator/tab_lifecycle_unit_source.cc
+++ b/chrome/browser/resource_coordinator/tab_lifecycle_unit_source.cc
@@ -47,13 +47,16 @@
   DISALLOW_COPY_AND_ASSIGN(TabLifecycleUnitHolder);
 };
 
-TabLifecycleUnitSource::TabLifecycleUnitSource()
-    : browser_tab_strip_tracker_(this, nullptr, this) {
+TabLifecycleUnitSource::TabLifecycleUnitSource(
+    InterventionPolicyDatabase* intervention_policy_database)
+    : browser_tab_strip_tracker_(this, nullptr, this),
+      intervention_policy_database_(intervention_policy_database) {
   DCHECK(!instance_);
 
   // In unit tests, tabs might already exist when TabLifecycleUnitSource is
   // instantiated. No TabLifecycleUnit is created for these tabs.
 
+  DCHECK(intervention_policy_database_);
   browser_tab_strip_tracker_.Init();
   instance_ = this;
   // TODO(chrisha): Create a ScopedPageSignalObserver helper class to clean up
diff --git a/chrome/browser/resource_coordinator/tab_lifecycle_unit_source.h b/chrome/browser/resource_coordinator/tab_lifecycle_unit_source.h
index 06b65fd..25ac393 100644
--- a/chrome/browser/resource_coordinator/tab_lifecycle_unit_source.h
+++ b/chrome/browser/resource_coordinator/tab_lifecycle_unit_source.h
@@ -23,6 +23,7 @@
 
 namespace resource_coordinator {
 
+class InterventionPolicyDatabase;
 class TabLifecycleObserver;
 class TabLifecycleUnitExternal;
 
@@ -32,7 +33,8 @@
                                public PageSignalObserver,
                                public TabStripModelObserver {
  public:
-  TabLifecycleUnitSource();
+  explicit TabLifecycleUnitSource(
+      InterventionPolicyDatabase* intervention_policy_database);
   ~TabLifecycleUnitSource() override;
 
   static TabLifecycleUnitSource* GetInstance();
@@ -50,6 +52,10 @@
   // Pretend that |tab_strip| is the TabStripModel of the focused window.
   void SetFocusedTabStripModelForTesting(TabStripModel* tab_strip);
 
+  InterventionPolicyDatabase* intervention_policy_database() const {
+    return intervention_policy_database_;
+  }
+
   class TabLifecycleUnitHolder;
 
  private:
@@ -129,6 +135,10 @@
   // changes.
   base::ObserverList<TabLifecycleObserver> tab_lifecycle_observers_;
 
+  // The intervention policy database used to assist freezing/discarding
+  // decisions.
+  InterventionPolicyDatabase* intervention_policy_database_;
+
   DISALLOW_COPY_AND_ASSIGN(TabLifecycleUnitSource);
 };
 
diff --git a/chrome/browser/resource_coordinator/tab_lifecycle_unit_unittest.cc b/chrome/browser/resource_coordinator/tab_lifecycle_unit_unittest.cc
index 8ecd2df..23c9853 100644
--- a/chrome/browser/resource_coordinator/tab_lifecycle_unit_unittest.cc
+++ b/chrome/browser/resource_coordinator/tab_lifecycle_unit_unittest.cc
@@ -10,6 +10,8 @@
 #include "base/observer_list.h"
 #include "base/test/simple_test_tick_clock.h"
 #include "build/build_config.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/resource_coordinator/intervention_policy_database.h"
 #include "chrome/browser/resource_coordinator/lifecycle_unit_observer.h"
 #include "chrome/browser/resource_coordinator/local_site_characteristics_data_unittest_utils.h"
 #include "chrome/browser/resource_coordinator/local_site_characteristics_webcontents_observer.h"
@@ -79,6 +81,9 @@
   void SetUp() override {
     ChromeTestHarnessWithLocalDB::SetUp();
 
+    // Force TabManager/TabLifecycleUnitSource creation.
+    g_browser_process->GetTabManager();
+
     std::unique_ptr<content::WebContents> test_web_contents =
         CreateTestWebContents();
     web_contents_ = test_web_contents.get();
@@ -168,14 +173,14 @@
     EXPECT_FALSE(decision_details.IsPositive());
     EXPECT_EQ(failure_reason, decision_details.FailureReason());
   }
+
+  // Heuristics shouldn't be considered for urgent or external tab discarding.
   {
     DecisionDetails decision_details;
-    EXPECT_FALSE(tab_lifecycle_unit.CanDiscard(DiscardReason::kExternal,
-                                               &decision_details));
-    EXPECT_FALSE(decision_details.IsPositive());
-    EXPECT_EQ(failure_reason, decision_details.FailureReason());
+    EXPECT_TRUE(tab_lifecycle_unit.CanDiscard(DiscardReason::kExternal,
+                                              &decision_details));
+    EXPECT_TRUE(decision_details.IsPositive());
   }
-  // Heuristics shouldn't be considered for urgent tab discarding.
   {
     DecisionDetails decision_details;
     EXPECT_TRUE(tab_lifecycle_unit.CanDiscard(DiscardReason::kUrgent,
@@ -390,6 +395,104 @@
       DecisionFailureReason::HEURISTIC_INSUFFICIENT_OBSERVATION, nullptr);
 }
 
+TEST_F(TabLifecycleUnitTest, CannotProactivelyDiscardTabIfOriginOptedOut) {
+  InterventionPolicyDatabase* policy_db =
+      TabLifecycleUnitSource::GetInstance()->intervention_policy_database();
+  policy_db->AddOriginPoliciesForTesting(
+      url::Origin::Create(web_contents_->GetLastCommittedURL()),
+      {OriginInterventions::OPT_OUT, OriginInterventions::DEFAULT});
+
+  TabLifecycleUnit tab_lifecycle_unit(&observers_, web_contents_,
+                                      tab_strip_model_.get());
+  // Proactive discarding shouldn't be possible, urgent and external discarding
+  // should still be possible.
+  {
+    DecisionDetails decision_details;
+    EXPECT_FALSE(tab_lifecycle_unit.CanDiscard(DiscardReason::kProactive,
+                                               &decision_details));
+    EXPECT_FALSE(decision_details.IsPositive());
+    EXPECT_EQ(DecisionFailureReason::GLOBAL_BLACKLIST,
+              decision_details.FailureReason());
+    EXPECT_EQ(1U, decision_details.reasons().size());
+  }
+  {
+    DecisionDetails decision_details;
+    EXPECT_TRUE(tab_lifecycle_unit.CanDiscard(DiscardReason::kUrgent,
+                                              &decision_details));
+    EXPECT_TRUE(decision_details.IsPositive());
+  }
+  {
+    DecisionDetails decision_details;
+    EXPECT_TRUE(tab_lifecycle_unit.CanDiscard(DiscardReason::kExternal,
+                                              &decision_details));
+    EXPECT_TRUE(decision_details.IsPositive());
+  }
+}
+
+TEST_F(TabLifecycleUnitTest, CannotFreezeTabIfOriginOptedOut) {
+  auto* policy_db =
+      TabLifecycleUnitSource::GetInstance()->intervention_policy_database();
+  policy_db->AddOriginPoliciesForTesting(
+      url::Origin::Create(web_contents_->GetLastCommittedURL()),
+      InterventionPolicyDatabase::OriginInterventionPolicies(
+          OriginInterventions::DEFAULT, OriginInterventions::OPT_OUT));
+
+  TabLifecycleUnit tab_lifecycle_unit(&observers_, web_contents_,
+                                      tab_strip_model_.get());
+  TabLoadTracker::Get()->TransitionStateForTesting(web_contents_,
+                                                   LoadingState::LOADED);
+  DecisionDetails decision_details;
+  EXPECT_FALSE(tab_lifecycle_unit.CanFreeze(&decision_details));
+  EXPECT_FALSE(decision_details.IsPositive());
+  EXPECT_EQ(DecisionFailureReason::GLOBAL_BLACKLIST,
+            decision_details.FailureReason());
+}
+
+TEST_F(TabLifecycleUnitTest, OptInTabsGetsDiscarded) {
+  auto* policy_db =
+      TabLifecycleUnitSource::GetInstance()->intervention_policy_database();
+  policy_db->AddOriginPoliciesForTesting(
+      url::Origin::Create(web_contents_->GetLastCommittedURL()),
+      InterventionPolicyDatabase::OriginInterventionPolicies(
+          OriginInterventions::OPT_IN, OriginInterventions::DEFAULT));
+
+  TabLifecycleUnit tab_lifecycle_unit(&observers_, web_contents_,
+                                      tab_strip_model_.get());
+
+  // Mark the tab as recently audible, this should protect it from being
+  // discarded.
+  tab_lifecycle_unit.SetRecentlyAudible(true);
+
+  DecisionDetails decision_details;
+  EXPECT_TRUE(tab_lifecycle_unit.CanDiscard(DiscardReason::kProactive,
+                                            &decision_details));
+  EXPECT_TRUE(decision_details.IsPositive());
+  EXPECT_EQ(DecisionSuccessReason::GLOBAL_WHITELIST,
+            decision_details.SuccessReason());
+}
+
+TEST_F(TabLifecycleUnitTest, CanFreezeOptedInTabs) {
+  auto* policy_db =
+      TabLifecycleUnitSource::GetInstance()->intervention_policy_database();
+  policy_db->AddOriginPoliciesForTesting(
+      url::Origin::Create(web_contents_->GetLastCommittedURL()),
+      {OriginInterventions::DEFAULT, OriginInterventions::OPT_IN});
+
+  TabLifecycleUnit tab_lifecycle_unit(&observers_, web_contents_,
+                                      tab_strip_model_.get());
+  TabLoadTracker::Get()->TransitionStateForTesting(web_contents_,
+                                                   LoadingState::LOADED);
+
+  // Mark the tab as recently audible, this should protect it from being frozen.
+  tab_lifecycle_unit.SetRecentlyAudible(true);
+
+  DecisionDetails decision_details;
+  EXPECT_TRUE(tab_lifecycle_unit.CanFreeze(&decision_details));
+  EXPECT_TRUE(decision_details.IsPositive());
+  EXPECT_EQ(DecisionSuccessReason::GLOBAL_WHITELIST,
+            decision_details.SuccessReason());
+}
+
 TEST_F(TabLifecycleUnitTest, CannotFreezeAFrozenTab) {
   TabLifecycleUnit tab_lifecycle_unit(&observers_, web_contents_,
                                       tab_strip_model_.get());
diff --git a/chrome/browser/resource_coordinator/usage_clock.cc b/chrome/browser/resource_coordinator/usage_clock.cc
index b6d74b0..3ff310a 100644
--- a/chrome/browser/resource_coordinator/usage_clock.cc
+++ b/chrome/browser/resource_coordinator/usage_clock.cc
@@ -9,21 +9,17 @@
 namespace resource_coordinator {
 
 UsageClock::UsageClock() : current_usage_session_start_time_(NowTicks()) {
-#if !defined(OS_CHROMEOS)
   if (metrics::DesktopSessionDurationTracker::IsInitialized()) {
     auto* tracker = metrics::DesktopSessionDurationTracker::Get();
     tracker->AddObserver(this);
     if (!tracker->in_session())
       current_usage_session_start_time_ = base::TimeTicks();
   }
-#endif  // defined(OS_CHROMEOS)
 }
 
 UsageClock::~UsageClock() {
-#if !defined(OS_CHROMEOS)
   if (metrics::DesktopSessionDurationTracker::IsInitialized())
     metrics::DesktopSessionDurationTracker::Get()->RemoveObserver(this);
-#endif
 }
 
 base::TimeDelta UsageClock::GetTotalUsageTime() const {
@@ -37,7 +33,6 @@
   return !current_usage_session_start_time_.is_null();
 }
 
-#if !defined(OS_CHROMEOS)
 void UsageClock::OnSessionStarted(base::TimeTicks session_start) {
   // Ignore |session_start| because it doesn't come from the resource
   // coordinator clock.
@@ -53,6 +48,5 @@
       NowTicks() - current_usage_session_start_time_;
   current_usage_session_start_time_ = base::TimeTicks();
 }
-#endif  // !defined(OS_CHROMEOS)
 
 }  // namespace resource_coordinator
diff --git a/chrome/browser/resource_coordinator/usage_clock.h b/chrome/browser/resource_coordinator/usage_clock.h
index bc4c210..6b7bbd9 100644
--- a/chrome/browser/resource_coordinator/usage_clock.h
+++ b/chrome/browser/resource_coordinator/usage_clock.h
@@ -7,11 +7,7 @@
 
 #include "base/macros.h"
 #include "base/time/time.h"
-#include "build/build_config.h"
-
-#if !defined(OS_CHROMEOS)
 #include "chrome/browser/metrics/desktop_session_duration/desktop_session_duration_tracker.h"
-#endif
 
 namespace resource_coordinator {
 
@@ -23,17 +19,11 @@
 // forcing all tests that indirectly depend on this to initialize
 // metrics::DesktopSessionDurationTracker.
 class UsageClock
-#if !defined(OS_CHROMEOS)
     : public metrics::DesktopSessionDurationTracker::Observer
-#endif
 {
  public:
   UsageClock();
-#if defined(OS_CHROMEOS)
-  ~UsageClock();
-#else
   ~UsageClock() override;
-#endif
 
   // Returns the amount of Chrome usage time since this was instantiated.
   base::TimeDelta GetTotalUsageTime() const;
@@ -42,10 +32,8 @@
   bool IsInUse() const;
 
  private:
-#if !defined(OS_CHROMEOS)
   void OnSessionStarted(base::TimeTicks session_start) override;
   void OnSessionEnded(base::TimeDelta session_length) override;
-#endif
 
   // The total time elapsed in completed usage sessions. The duration of the
   // current usage session, if any, must be added to this to get the total usage
diff --git a/chrome/browser/resource_coordinator/usage_clock_unittest.cc b/chrome/browser/resource_coordinator/usage_clock_unittest.cc
index 03c64270..e664d59 100644
--- a/chrome/browser/resource_coordinator/usage_clock_unittest.cc
+++ b/chrome/browser/resource_coordinator/usage_clock_unittest.cc
@@ -6,13 +6,9 @@
 
 #include "base/test/scoped_task_environment.h"
 #include "base/test/simple_test_tick_clock.h"
-#include "build/build_config.h"
 #include "chrome/browser/resource_coordinator/time.h"
 #include "testing/gtest/include/gtest/gtest.h"
-
-#if !defined(OS_CHROMEOS)
 #include "chrome/browser/metrics/desktop_session_duration/desktop_session_duration_tracker.h"
-#endif
 
 namespace resource_coordinator {
 
@@ -25,18 +21,6 @@
     clock.Advance(base::TimeDelta::FromMinutes(42));
     ScopedSetTickClockForTesting scoped_set_tick_clock_for_testing(&clock);
 
-#if defined(OS_CHROMEOS)
-    // On ChromeOS, UsageClock behaves like a normal clock.
-    UsageClock usage_clock;
-    EXPECT_EQ(usage_clock.GetTotalUsageTime(), base::TimeDelta());
-
-    clock.Advance(base::TimeDelta::FromMinutes(1));
-    EXPECT_EQ(usage_clock.GetTotalUsageTime(), base::TimeDelta::FromMinutes(1));
-    clock.Advance(base::TimeDelta::FromMinutes(1));
-    EXPECT_EQ(usage_clock.GetTotalUsageTime(), base::TimeDelta::FromMinutes(2));
-#else
-    // On non-ChromeOS, UsageClock advances when
-    // DesktopSessionDurationTracker::in_session() is true.
     metrics::DesktopSessionDurationTracker::Initialize();
     auto* tracker = metrics::DesktopSessionDurationTracker::Get();
     tracker->SetInactivityTimeoutForTesting(0);
@@ -72,13 +56,10 @@
     EXPECT_TRUE(usage_clock.IsInUse());
     clock.Advance(base::TimeDelta::FromMinutes(1));
     EXPECT_EQ(usage_clock.GetTotalUsageTime(), base::TimeDelta::FromMinutes(4));
-#endif  // defined(OS_CHROMEOS)
   }
 
-#if !defined(OS_CHROMEOS)
   // Must be after UsageClock destruction.
   metrics::DesktopSessionDurationTracker::CleanupForTesting();
-#endif
 }
 
 }  // namespace resource_coordinator
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/braille_command_handler.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/braille_command_handler.js
index 35ce91d..a707125 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/braille_command_handler.js
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/braille_command_handler.js
@@ -161,6 +161,9 @@
 
   var isMultiline = AutomationPredicate.multiline(current.start.node);
   switch (command) {
+    case 'forceClickOnCurrentItem':
+      BackgroundKeyboardHandler.sendKeyPress(13);
+      break;
     case 'previousCharacter':
       BackgroundKeyboardHandler.sendKeyPress(37);
       break;
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/desktop_automation_handler.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/desktop_automation_handler.js
index 9dc6941..cae9224 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/desktop_automation_handler.js
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/desktop_automation_handler.js
@@ -310,6 +310,13 @@
   onChildrenChanged: function(evt) {
     var curRange = ChromeVoxState.instance.currentRange;
 
+    // views::TextField blinks by making its cursor view alternate between
+    // visible and not visible. This results in a children changed event on its
+    // parent (the text field itself). In general, text field feedback should be
+    // given within text field specific events.
+    if (evt.target.role == RoleType.TEXT_FIELD)
+      return;
+
     // Always refresh the braille contents.
     if (curRange && curRange.equals(cursors.Range.fromNode(evt.target))) {
       new Output()
diff --git a/chrome/browser/resources/local_ntp/custom_backgrounds.css b/chrome/browser/resources/local_ntp/custom_backgrounds.css
index 7e963f4..e622360 100644
--- a/chrome/browser/resources/local_ntp/custom_backgrounds.css
+++ b/chrome/browser/resources/local_ntp/custom_backgrounds.css
@@ -52,7 +52,6 @@
   background: #fff;
   border: none;
   border-radius: 8px;
-  border-style: outset;
   border-width: thin;
   bottom: 44px;
   box-shadow: 0 1px 3px 0 rgba(60, 64, 67, 0.3), 0 4px 8px 3px rgba(60, 64, 67, 0.15);
diff --git a/chrome/browser/resources/md_downloads/manager.html b/chrome/browser/resources/md_downloads/manager.html
index 3662b4d..cf973f4a 100644
--- a/chrome/browser/resources/md_downloads/manager.html
+++ b/chrome/browser/resources/md_downloads/manager.html
@@ -27,15 +27,7 @@
         z-index: 0;
       }
 
-      @media screen and (max-width: 1024px) {
-        :host {
-          flex-basis: calc(
-              var(--downloads-card-width) + 2 * var(--downloads-card-margin));
-        }
-      }
-
       #toolbar {
-        position: relative;
         z-index: 1;
       }
 
@@ -48,6 +40,8 @@
       }
 
       #downloads-list {
+        min-width: calc(
+          var(--downloads-card-width) + 2 * var(--downloads-card-margin));
         /* TODO(dbeam): we're not setting scrollTarget explicitly, yet
          * style="overflow: auto" is still being set by <iron-list>'s JS. Weird.
          */
@@ -84,22 +78,30 @@
         height: 144px;  /* Matches natural image height. */
         margin-bottom: 32px;
       }
+
+      #mainContainer {
+        display: flex;
+        flex: 1;
+        overflow: auto;
+      }
     </style>
 
     <downloads-toolbar id="toolbar" spinner-active="{{spinnerActive_}}"
         role="none">
     </downloads-toolbar>
     <div id="drop-shadow"></div>
-    <iron-list id="downloads-list" items="[[items_]]"
-        hidden="[[!hasDownloads_]]">
-      <template>
-        <downloads-item data="[[item]]"></downloads-item>
-      </template>
-    </iron-list>
-    <div id="no-downloads" hidden="[[hasDownloads_]]">
-      <div>
-        <div class="illustration"></div>
-        <span>[[noDownloadsText_(inSearchMode_)]]</span>
+    <div id="mainContainer">
+      <iron-list id="downloads-list" items="[[items_]]"
+          hidden="[[!hasDownloads_]]">
+        <template>
+          <downloads-item data="[[item]]"></downloads-item>
+        </template>
+      </iron-list>
+      <div id="no-downloads" hidden="[[hasDownloads_]]">
+        <div>
+          <div class="illustration"></div>
+          <span>[[noDownloadsText_(inSearchMode_)]]</span>
+        </div>
       </div>
     </div>
   </template>
diff --git a/chrome/browser/resources/md_user_manager/shared_styles.html b/chrome/browser/resources/md_user_manager/shared_styles.html
index bd8feb6..49d7889 100644
--- a/chrome/browser/resources/md_user_manager/shared_styles.html
+++ b/chrome/browser/resources/md_user_manager/shared_styles.html
@@ -21,10 +21,6 @@
         text-decoration: none;
       }
 
-      paper-button {
-        background-color: white;
-      }
-
       .product-logo {
         content: -webkit-image-set(
             url(../../../app/theme/default_100_percent/%DISTRIBUTION%/product_logo_name_22.png) 1x,
diff --git a/chrome/browser/resources/md_user_manager/user_manager.html b/chrome/browser/resources/md_user_manager/user_manager.html
index 9b375fb..f5bc375 100644
--- a/chrome/browser/resources/md_user_manager/user_manager.html
+++ b/chrome/browser/resources/md_user_manager/user_manager.html
@@ -33,10 +33,6 @@
   <!-- Make sure paper-button is imported to use in user_pod_template.html -->
   <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
   <style is="custom-style" include="shared-styles">
-    body {
-      background-color: var(--md-background-color);
-    }
-
     user-manager-pages,
     #login-header-bar {
       bottom: 0;
diff --git a/chrome/browser/resources/settings/google_assistant_page/google_assistant_browser_proxy.js b/chrome/browser/resources/settings/google_assistant_page/google_assistant_browser_proxy.js
index 5772dd6..27aa284 100644
--- a/chrome/browser/resources/settings/google_assistant_page/google_assistant_browser_proxy.js
+++ b/chrome/browser/resources/settings/google_assistant_page/google_assistant_browser_proxy.js
@@ -22,12 +22,6 @@
      */
     setGoogleAssistantContextEnabled(enabled) {}
 
-    /**
-     * Enables or disables hotword detection for the Google Assistant.
-     * @param {boolean} enabled
-     */
-    setGoogleAssistantHotwordEnabled(enabled) {}
-
     /** Launches into the Google Assistant app settings. */
     launchGoogleAssistantSettings() {}
   }
@@ -45,11 +39,6 @@
     }
 
     /** @override */
-    setGoogleAssistantHotwordEnabled(enabled) {
-      chrome.send('setGoogleAssistantHotwordEnabled', [enabled]);
-    }
-
-    /** @override */
     showGoogleAssistantSettings() {
       chrome.send('showGoogleAssistantSettings');
     }
diff --git a/chrome/browser/resources/settings/google_assistant_page/google_assistant_page.html b/chrome/browser/resources/settings/google_assistant_page/google_assistant_page.html
index 1c2a581..fdfd106 100644
--- a/chrome/browser/resources/settings/google_assistant_page/google_assistant_page.html
+++ b/chrome/browser/resources/settings/google_assistant_page/google_assistant_page.html
@@ -34,8 +34,7 @@
             class="continuation indented"
             pref="{{prefs.settings.voice_interaction.hotword.enabled}}"
             label="$i18n{googleAssistantEnableHotword}"
-            sub-label="$i18n{googleAssistantEnableHotwordDescription}"
-            on-change="onGoogleAssistantHotwordEnableChange_">
+            sub-label="$i18n{googleAssistantEnableHotwordDescription}">
         </settings-toggle-button>
       </template>
       <div id="googleAssistantSettings" class="settings-box"
diff --git a/chrome/browser/resources/settings/google_assistant_page/google_assistant_page.js b/chrome/browser/resources/settings/google_assistant_page/google_assistant_page.js
index 8c28bf0..307b161 100644
--- a/chrome/browser/resources/settings/google_assistant_page/google_assistant_page.js
+++ b/chrome/browser/resources/settings/google_assistant_page/google_assistant_page.js
@@ -51,12 +51,6 @@
   },
 
   /** @private */
-  onGoogleAssistantHotwordEnableChange_: function() {
-    this.browserProxy_.setGoogleAssistantHotwordEnabled(
-        !!this.getPref('settings.voice_interaction.hotword.enabled.value'));
-  },
-
-  /** @private */
   onGoogleAssistantSettingsTapped_: function() {
     this.browserProxy_.showGoogleAssistantSettings();
   },
diff --git a/chrome/browser/resources/settings/internet_page/BUILD.gn b/chrome/browser/resources/settings/internet_page/BUILD.gn
index 67475fd..19d20ec0 100644
--- a/chrome/browser/resources/settings/internet_page/BUILD.gn
+++ b/chrome/browser/resources/settings/internet_page/BUILD.gn
@@ -12,6 +12,7 @@
     ":internet_page",
     ":internet_page_browser_proxy",
     ":internet_subpage",
+    ":network_listener_behavior",
     ":network_proxy_section",
     ":network_summary",
     ":network_summary_item",
@@ -23,6 +24,7 @@
   deps = [
     ":internet_config",
     ":internet_page_browser_proxy",
+    ":network_listener_behavior",
     "..:route",
     "../settings_page:settings_animated_pages",
     "//ui/webui/resources/cr_elements/chromeos/network:cr_onc_types",
@@ -99,6 +101,14 @@
   extra_sources = [ "$interfaces_path/networking_private_interface.js" ]
 }
 
+js_library("network_listener_behavior") {
+  deps = [
+    "//ui/webui/resources/js:assert",
+  ]
+  externs_list = [ "$externs_path/networking_private.js" ]
+  extra_sources = [ "$interfaces_path/networking_private_interface.js" ]
+}
+
 js_library("network_proxy_section") {
   deps = [
     "..:route",
diff --git a/chrome/browser/resources/settings/internet_page/internet_page.html b/chrome/browser/resources/settings/internet_page/internet_page.html
index 1d70bd6..41701b4 100644
--- a/chrome/browser/resources/settings/internet_page/internet_page.html
+++ b/chrome/browser/resources/settings/internet_page/internet_page.html
@@ -17,6 +17,7 @@
 <link rel="import" href="internet_detail_page.html">
 <link rel="import" href="internet_known_networks_page.html">
 <link rel="import" href="internet_subpage.html">
+<link rel="import" href="network_listener_behavior.html">
 <link rel="import" href="network_summary.html">
 
 <dom-module id="settings-internet-page">
diff --git a/chrome/browser/resources/settings/internet_page/internet_page.js b/chrome/browser/resources/settings/internet_page/internet_page.js
index c9b28fe..3f01b7f 100644
--- a/chrome/browser/resources/settings/internet_page/internet_page.js
+++ b/chrome/browser/resources/settings/internet_page/internet_page.js
@@ -10,8 +10,10 @@
 Polymer({
   is: 'settings-internet-page',
 
-  behaviors:
-      [I18nBehavior, settings.RouteObserverBehavior, WebUIListenerBehavior],
+  behaviors: [
+    I18nBehavior, settings.RouteObserverBehavior, WebUIListenerBehavior,
+    NetworkListenerBehavior
+  ],
 
   properties: {
     /**
@@ -78,6 +80,27 @@
     /** @private {!chrome.networkingPrivate.GlobalPolicy|undefined} */
     globalPolicy_: Object,
 
+    /** Overridden from NetworkListenerBehavior. */
+    networkListChangeSubscriberSelectors_: {
+      type: Array,
+      value: function() {
+        return [
+          'network-summary',
+          'settings-internet-detail-page',
+          'settings-internet-known-networks-page',
+          'settings-internet-subpage',
+        ];
+      }
+    },
+
+    /** Overridden from NetworkListenerBehavior. */
+    networksChangeSubscriberSelectors_: {
+      type: Array,
+      value: function() {
+        return ['network-summary', 'settings-internet-detail-page'];
+      }
+    },
+
     /**
      * List of third party VPN providers.
      * @type {!Array<!chrome.networkingPrivate.ThirdPartyVPNProperties>}
@@ -124,13 +147,6 @@
     'show-networks': 'onShowNetworks_',
   },
 
-  // chrome.networkingPrivate listeners
-  /** @private {?function(!Array<string>)} */
-  networkListChangedListener_: null,
-
-  /** @private {?function(!Array<string>)} */
-  networksChangedListener_: null,
-
   // chrome.management listeners
   /** @private {Function} */
   onExtensionAddedListener_: null,
@@ -158,16 +174,6 @@
 
   /** @override */
   attached: function() {
-    this.networkListChangedListener_ = this.networkListChangedListener_ ||
-        this.onNetworkListChanged_.bind(this);
-    this.networkingPrivate.onNetworkListChanged.addListener(
-        this.networkListChangedListener_);
-
-    this.networksChangedListener_ =
-        this.networksChangedListener_ || this.onNetworksChanged_.bind(this);
-    this.networkingPrivate.onNetworksChanged.addListener(
-        this.networksChangedListener_);
-
     this.onExtensionAddedListener_ =
         this.onExtensionAddedListener_ || this.onExtensionAdded_.bind(this);
     chrome.management.onInstalled.addListener(this.onExtensionAddedListener_);
@@ -191,11 +197,6 @@
 
   /** @override */
   detached: function() {
-    this.networkingPrivate.onNetworkListChanged.removeListener(
-        assert(this.networkListChangedListener_));
-    this.networkingPrivate.onNetworksChanged.removeListener(
-        assert(this.networksChangedListener_));
-
     chrome.management.onInstalled.removeListener(
         assert(this.onExtensionAddedListener_));
     chrome.management.onEnabled.removeListener(
@@ -450,45 +451,6 @@
   },
 
   /**
-   * This event is triggered when the list of networks changes.
-   * |networkIds| contains the ids for all visible or configured networks.
-   * networkingPrivate.onNetworkListChanged event callback.
-   * @param {!Array<string>} networkIds
-   * @private
-   */
-  onNetworkListChanged_: function(networkIds) {
-    const event = new CustomEvent('network-list-changed', {detail: networkIds});
-    this.maybeDispatchEvent_('network-summary', event);
-    this.maybeDispatchEvent_('settings-internet-detail-page', event);
-    this.maybeDispatchEvent_('settings-internet-known-networks-page', event);
-    this.maybeDispatchEvent_('settings-internet-subpage', event);
-  },
-
-  /**
-   * This event is triggered when interesting properties of a network change.
-   * |networkIds| contains the ids for networks whose properties have changed.
-   * networkingPrivate.onNetworksChanged event callback.
-   * @param {!Array<string>} networkIds
-   * @private
-   */
-  onNetworksChanged_: function(networkIds) {
-    const event = new CustomEvent('networks-changed', {detail: networkIds});
-    this.maybeDispatchEvent_('network-summary', event);
-    this.maybeDispatchEvent_('settings-internet-detail-page', event);
-  },
-
-  /**
-   * @param {!Event} event
-   * @private
-   */
-  maybeDispatchEvent_: function(identifier, event) {
-    const element = this.$$(identifier);
-    if (!element)
-      return;
-    element.dispatchEvent(event);
-  },
-
-  /**
    * chrome.management.onInstalled or onEnabled event.
    * @param {!chrome.management.ExtensionInfo} extension
    * @private
diff --git a/chrome/browser/resources/settings/internet_page/network_listener_behavior.html b/chrome/browser/resources/settings/internet_page/network_listener_behavior.html
new file mode 100644
index 0000000..f564a74
--- /dev/null
+++ b/chrome/browser/resources/settings/internet_page/network_listener_behavior.html
@@ -0,0 +1 @@
+<script src="network_listener_behavior.js"></script>
diff --git a/chrome/browser/resources/settings/internet_page/network_listener_behavior.js b/chrome/browser/resources/settings/internet_page/network_listener_behavior.js
new file mode 100644
index 0000000..665cc540
--- /dev/null
+++ b/chrome/browser/resources/settings/internet_page/network_listener_behavior.js
@@ -0,0 +1,94 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Polymer behavior for alerting specified child elements of
+ * changes to the devices network data.
+ */
+
+/** @polymerBehavior */
+const NetworkListenerBehavior = {
+  properties: {
+    /**
+     * Array of selectors specifying all children to alert of changes to the
+     * network list.
+     * @private {!Array<string>}
+     */
+    networkListChangeSubscriberSelectors_: Array,
+
+    /**
+     * Array of selectors specifying all children to alert of important changes
+     * to the specific networks.
+     * @private {!Array<string>}
+     */
+    networksChangeSubscriberSelectors_: Array,
+
+    /** @type {!NetworkingPrivate} */
+    networkingPrivate: Object,
+  },
+
+  /** @private {?function(!Array<string>)} */
+  networkListChangedListener_: null,
+
+  /** @private {?function(!Array<string>)} */
+  networksChangedListener_: null,
+
+  /** @override */
+  attached: function() {
+    this.networkListChangedListener_ = this.networkListChangedListener_ ||
+        this.onNetworkListChanged_.bind(this);
+    this.networkingPrivate.onNetworkListChanged.addListener(
+        this.networkListChangedListener_);
+
+    this.networksChangedListener_ =
+        this.networksChangedListener_ || this.onNetworksChanged_.bind(this);
+    this.networkingPrivate.onNetworksChanged.addListener(
+        this.networksChangedListener_);
+  },
+
+  /** @override */
+  detached: function() {
+    this.networkingPrivate.onNetworkListChanged.removeListener(
+        assert(this.networkListChangedListener_));
+    this.networkingPrivate.onNetworksChanged.removeListener(
+        assert(this.networksChangedListener_));
+  },
+
+  /**
+   * This event is triggered when the list of networks changes.
+   * |networkIds| contains the ids for all visible or configured networks.
+   * networkingPrivate.onNetworkListChanged event callback.
+   * @param {!Array<string>} networkIds
+   * @private
+   */
+  onNetworkListChanged_: function(networkIds) {
+    const event = new CustomEvent('network-list-changed', {detail: networkIds});
+    this.networkListChangeSubscriberSelectors_.forEach(
+        selector => this.maybeDispatchEvent_(selector, event));
+  },
+
+  /**
+   * This event is triggered when interesting properties of a network change.
+   * |networkIds| contains the ids for networks whose properties have changed.
+   * networkingPrivate.onNetworksChanged event callback.
+   * @param {!Array<string>} networkIds
+   * @private
+   */
+  onNetworksChanged_: function(networkIds) {
+    const event = new CustomEvent('networks-changed', {detail: networkIds});
+    this.networksChangeSubscriberSelectors_.forEach(
+        selector => this.maybeDispatchEvent_(selector, event));
+  },
+
+  /**
+   * @param {!Event} event
+   * @private
+   */
+  maybeDispatchEvent_: function(selectors, event) {
+    const element = this.$$(selectors);
+    if (!element)
+      return;
+    element.dispatchEvent(event);
+  },
+};
diff --git a/chrome/browser/resources/settings/search_settings.js b/chrome/browser/resources/settings/search_settings.js
index 38ef1cf..19c3a1e 100644
--- a/chrome/browser/resources/settings/search_settings.js
+++ b/chrome/browser/resources/settings/search_settings.js
@@ -65,14 +65,19 @@
     const domIfTag = Polymer.DomIf ? 'DOM-IF' : 'TEMPLATE';
 
     function doSearch(node) {
-      // TODO(dpapad): Note that for subpage wrappers
-      // <template route-path="..."> a noSearch member variable *should* be
-      // used, instead of the "no-search" CSS attribute, which does not work
-      // (throws an error) with the automatic Polymer 2 conversion to
-      // <dom-if><template...> syntax. Clean this up once Polymer 2 migration
-      // has finished.
+      // NOTE: For subpage wrappers <template route-path="..."> when |no-search|
+      // participates in a data binding:
+      //
+      //  - Always use noSearch Polymer property, for example
+      //    no-search="[[foo]]"
+      //  - *Don't* use a no-search CSS attribute like no-search$="[[foo]]"
+      //
+      // The latter throws an error during the automatic Polymer 2 conversion to
+      // <dom-if><template...></dom-if> syntax.
+      // TODO(dpapad):Clean this up once Polymer 2 migration has finished.
       if (node.nodeName == domIfTag && node.hasAttribute('route-path') &&
-          !node.if && !node['noSearch']) {
+          !node.if && !node['noSearch'] &&
+          !node.hasAttribute(SKIP_SEARCH_CSS_ATTRIBUTE)) {
         request.queue_.addRenderTask(new RenderTask(request, node));
         return;
       }
diff --git a/chrome/browser/resources/settings/settings_resources.grd b/chrome/browser/resources/settings/settings_resources.grd
index 19384a7..3e10295 100644
--- a/chrome/browser/resources/settings/settings_resources.grd
+++ b/chrome/browser/resources/settings/settings_resources.grd
@@ -1336,6 +1336,12 @@
         <structure name="IDR_SETTINGS_NETWORK_PROXY_SECTION_JS"
                    file="internet_page/network_proxy_section.js"
                    type="chrome_html" />
+        <structure name="IDR_SETTINGS_NETWORK_LISTENER_BEHAVIOR_HTML"
+                   file="internet_page/network_listener_behavior.html"
+                   type="chrome_html" />
+        <structure name="IDR_SETTINGS_NETWORK_LISTENER_BEHAVIOR_JS"
+                   file="internet_page/network_listener_behavior.js"
+                   type="chrome_html" />
         <structure name="IDR_SETTINGS_NETWORK_SUMMARY_HTML"
                    file="internet_page/network_summary.html"
                    type="chrome_html" />
diff --git a/chrome/browser/safe_browsing/chrome_password_protection_service.cc b/chrome/browser/safe_browsing/chrome_password_protection_service.cc
index dc1dba6..d0b528d 100644
--- a/chrome/browser/safe_browsing/chrome_password_protection_service.cc
+++ b/chrome/browser/safe_browsing/chrome_password_protection_service.cc
@@ -1213,4 +1213,21 @@
       ->ScheduleEnterprisePasswordURLUpdate();
 }
 
+bool ChromePasswordProtectionService::CanShowInterstitial(
+    RequestOutcome reason,
+    ReusedPasswordType password_type,
+    const GURL& main_frame_url) {
+  // If it's not password alert mode, no need to log any metric.
+  if (reason != PASSWORD_ALERT_MODE ||
+      (password_type != PasswordReuseEvent::SIGN_IN_PASSWORD &&
+       password_type != PasswordReuseEvent::ENTERPRISE_PASSWORD)) {
+    return false;
+  }
+
+  if (!IsURLWhitelistedForPasswordEntry(main_frame_url, &reason))
+    reason = PasswordProtectionService::SUCCEEDED;
+  LogPasswordAlertModeOutcome(reason, password_type);
+  return reason == PasswordProtectionService::SUCCEEDED;
+}
+
 }  // namespace safe_browsing
diff --git a/chrome/browser/safe_browsing/chrome_password_protection_service.h b/chrome/browser/safe_browsing/chrome_password_protection_service.h
index 4bdc05f..2f8d495 100644
--- a/chrome/browser/safe_browsing/chrome_password_protection_service.h
+++ b/chrome/browser/safe_browsing/chrome_password_protection_service.h
@@ -257,6 +257,13 @@
     sync_password_hash_ = new_password_hash;
   }
 
+  // Determines if we should show chrome://reset-password interstitial based on
+  // previous request outcome, the reused |password_type| and the
+  // |main_frame_url|.
+  bool CanShowInterstitial(RequestOutcome reason,
+                           ReusedPasswordType password_type,
+                           const GURL& main_frame_url) override;
+
   FRIEND_TEST_ALL_PREFIXES(ChromePasswordProtectionServiceTest,
                            VerifyUserPopulationForPasswordOnFocusPing);
   FRIEND_TEST_ALL_PREFIXES(ChromePasswordProtectionServiceTest,
@@ -279,6 +286,8 @@
   FRIEND_TEST_ALL_PREFIXES(
       ChromePasswordProtectionServiceTest,
       VerifyUnhandledSyncPasswordReuseUponClearHistoryDeletion);
+  FRIEND_TEST_ALL_PREFIXES(ChromePasswordProtectionServiceTest,
+                           VerifyCanShowInterstitial);
   FRIEND_TEST_ALL_PREFIXES(ChromePasswordProtectionServiceBrowserTest,
                            VerifyCheckGaiaPasswordChange);
   FRIEND_TEST_ALL_PREFIXES(ChromePasswordProtectionServiceBrowserTest,
diff --git a/chrome/browser/safe_browsing/chrome_password_protection_service_unittest.cc b/chrome/browser/safe_browsing/chrome_password_protection_service_unittest.cc
index 706438e..4ae7de4 100644
--- a/chrome/browser/safe_browsing/chrome_password_protection_service_unittest.cc
+++ b/chrome/browser/safe_browsing/chrome_password_protection_service_unittest.cc
@@ -947,4 +947,52 @@
       service_->GetWarningDetailText(PasswordReuseEvent::ENTERPRISE_PASSWORD));
 }
 
+TEST_F(ChromePasswordProtectionServiceTest, VerifyCanShowInterstitial) {
+  ASSERT_FALSE(
+      profile()->GetPrefs()->HasPrefPath(prefs::kSafeBrowsingWhitelistDomains));
+  GURL trigger_url = GURL(kPhishingURL);
+  EXPECT_FALSE(service_->CanShowInterstitial(
+      PasswordProtectionService::TURNED_OFF_BY_ADMIN,
+      PasswordReuseEvent::SAVED_PASSWORD, trigger_url));
+  EXPECT_FALSE(service_->CanShowInterstitial(
+      PasswordProtectionService::TURNED_OFF_BY_ADMIN,
+      PasswordReuseEvent::SIGN_IN_PASSWORD, trigger_url));
+  EXPECT_FALSE(service_->CanShowInterstitial(
+      PasswordProtectionService::TURNED_OFF_BY_ADMIN,
+      PasswordReuseEvent::OTHER_GAIA_PASSWORD, trigger_url));
+  EXPECT_FALSE(service_->CanShowInterstitial(
+      PasswordProtectionService::TURNED_OFF_BY_ADMIN,
+      PasswordReuseEvent::ENTERPRISE_PASSWORD, trigger_url));
+  EXPECT_FALSE(service_->CanShowInterstitial(
+      PasswordProtectionService::PASSWORD_ALERT_MODE,
+      PasswordReuseEvent::SAVED_PASSWORD, trigger_url));
+  EXPECT_TRUE(service_->CanShowInterstitial(
+      PasswordProtectionService::PASSWORD_ALERT_MODE,
+      PasswordReuseEvent::SIGN_IN_PASSWORD, trigger_url));
+  EXPECT_FALSE(service_->CanShowInterstitial(
+      PasswordProtectionService::PASSWORD_ALERT_MODE,
+      PasswordReuseEvent::OTHER_GAIA_PASSWORD, trigger_url));
+  EXPECT_TRUE(service_->CanShowInterstitial(
+      PasswordProtectionService::PASSWORD_ALERT_MODE,
+      PasswordReuseEvent::ENTERPRISE_PASSWORD, trigger_url));
+
+  // Add |trigger_url| to enterprise whitelist.
+  base::ListValue whitelisted_domains;
+  whitelisted_domains.AppendString(trigger_url.host());
+  profile()->GetPrefs()->Set(prefs::kSafeBrowsingWhitelistDomains,
+                             whitelisted_domains);
+  EXPECT_FALSE(service_->CanShowInterstitial(
+      PasswordProtectionService::PASSWORD_ALERT_MODE,
+      PasswordReuseEvent::SAVED_PASSWORD, trigger_url));
+  EXPECT_FALSE(service_->CanShowInterstitial(
+      PasswordProtectionService::PASSWORD_ALERT_MODE,
+      PasswordReuseEvent::SIGN_IN_PASSWORD, trigger_url));
+  EXPECT_FALSE(service_->CanShowInterstitial(
+      PasswordProtectionService::PASSWORD_ALERT_MODE,
+      PasswordReuseEvent::OTHER_GAIA_PASSWORD, trigger_url));
+  EXPECT_FALSE(service_->CanShowInterstitial(
+      PasswordProtectionService::PASSWORD_ALERT_MODE,
+      PasswordReuseEvent::ENTERPRISE_PASSWORD, trigger_url));
+}
+
 }  // namespace safe_browsing
diff --git a/chrome/browser/safe_browsing/incident_reporting/last_download_finder.cc b/chrome/browser/safe_browsing/incident_reporting/last_download_finder.cc
index 1e4bdfa..79096fe 100644
--- a/chrome/browser/safe_browsing/incident_reporting/last_download_finder.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/last_download_finder.cc
@@ -35,7 +35,6 @@
 #include "content/public/browser/notification_source.h"
 #include "crypto/sha2.h"
 #include "extensions/buildflags/buildflags.h"
-#include "ui/base/pref_names.h"
 
 namespace safe_browsing {
 
diff --git a/chrome/browser/search/one_google_bar/one_google_bar_loader_impl_unittest.cc b/chrome/browser/search/one_google_bar/one_google_bar_loader_impl_unittest.cc
index d8ed047..0fe8719 100644
--- a/chrome/browser/search/one_google_bar/one_google_bar_loader_impl_unittest.cc
+++ b/chrome/browser/search/one_google_bar/one_google_bar_loader_impl_unittest.cc
@@ -26,7 +26,6 @@
 #include "services/network/test/test_url_loader_factory.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "ui/base/pref_names.h"
 
 using testing::_;
 using testing::Eq;
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 15b0181..cf7e83e 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -2785,6 +2785,9 @@
 
   if (toolkit_views) {
     sources += [
+      "autofill/local_card_migration_bubble.h",
+      "autofill/local_card_migration_bubble_controller_impl.cc",
+      "autofill/local_card_migration_bubble_controller_impl.h",
       "autofill/save_card_bubble_controller_impl.cc",
       "autofill/save_card_bubble_controller_impl.h",
       "autofill/save_card_bubble_view.h",
@@ -2820,6 +2823,8 @@
       "views/autofill/autofill_popup_view_views.h",
       "views/autofill/card_unmask_prompt_views.cc",
       "views/autofill/card_unmask_prompt_views.h",
+      "views/autofill/local_card_migration_bubble_views.cc",
+      "views/autofill/local_card_migration_bubble_views.h",
       "views/autofill/save_card_bubble_views.cc",
       "views/autofill/save_card_bubble_views.h",
       "views/autofill/view_util.cc",
@@ -3073,6 +3078,8 @@
       sources += [
         "views/accessibility/invert_bubble_view.cc",
         "views/accessibility/invert_bubble_view.h",
+        "views/autofill/local_card_migration_icon_view.cc",
+        "views/autofill/local_card_migration_icon_view.h",
         "views/autofill/save_card_icon_view.cc",
         "views/autofill/save_card_icon_view.h",
         "views/bookmarks/bookmark_bar_instructions_view.cc",
diff --git a/chrome/browser/ui/app_list/arc/arc_app_utils.cc b/chrome/browser/ui/app_list/arc/arc_app_utils.cc
index f6042fb..6d37765 100644
--- a/chrome/browser/ui/app_list/arc/arc_app_utils.cc
+++ b/chrome/browser/ui/app_list/arc/arc_app_utils.cc
@@ -34,7 +34,6 @@
 #include "components/arc/intent_helper/arc_intent_helper_bridge.h"
 #include "components/prefs/pref_service.h"
 #include "ui/aura/window.h"
-#include "ui/base/pref_names.h"
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
 #include "ui/events/event_constants.h"
diff --git a/chrome/browser/ui/autofill/chrome_autofill_client.cc b/chrome/browser/ui/autofill/chrome_autofill_client.cc
index c6073b0..bb13e5b 100644
--- a/chrome/browser/ui/autofill/chrome_autofill_client.cc
+++ b/chrome/browser/ui/autofill/chrome_autofill_client.cc
@@ -190,6 +190,14 @@
   unmask_controller_.OnVerificationResult(result);
 }
 
+void ChromeAutofillClient::ConfirmSaveAutofillProfile(
+    const AutofillProfile& profile,
+    base::OnceClosure callback) {
+  // Since there is no confirmation needed to save an Autofill Profile,
+  // running |callback| will proceed with saving |profile|.
+  std::move(callback).Run();
+}
+
 void ChromeAutofillClient::ConfirmSaveCreditCardLocally(
     const CreditCard& card,
     const base::Closure& callback) {
diff --git a/chrome/browser/ui/autofill/chrome_autofill_client.h b/chrome/browser/ui/autofill/chrome_autofill_client.h
index 63e2971..4fdec0d 100644
--- a/chrome/browser/ui/autofill/chrome_autofill_client.h
+++ b/chrome/browser/ui/autofill/chrome_autofill_client.h
@@ -61,6 +61,8 @@
                         UnmaskCardReason reason,
                         base::WeakPtr<CardUnmaskDelegate> delegate) override;
   void OnUnmaskVerificationResult(PaymentsRpcResult result) override;
+  void ConfirmSaveAutofillProfile(const AutofillProfile& profile,
+                                  base::OnceClosure callback) override;
   void ConfirmSaveCreditCardLocally(const CreditCard& card,
                                     const base::Closure& callback) override;
   void ConfirmSaveCreditCardToCloud(
diff --git a/chrome/browser/ui/autofill/local_card_migration_bubble.h b/chrome/browser/ui/autofill/local_card_migration_bubble.h
new file mode 100644
index 0000000..7b146dc
--- /dev/null
+++ b/chrome/browser/ui/autofill/local_card_migration_bubble.h
@@ -0,0 +1,21 @@
+// Copyright 2018 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_AUTOFILL_LOCAL_CARD_MIGRATION_BUBBLE_H_
+#define CHROME_BROWSER_UI_AUTOFILL_LOCAL_CARD_MIGRATION_BUBBLE_H_
+
+namespace autofill {
+
+// The cross-platform interface which displays the intermediate bubble
+// for browser-stored local card migration flow.
+class LocalCardMigrationBubble {
+ public:
+  // Called from controller to shut down the bubble and prevent any further
+  // action.
+  virtual void Hide() = 0;
+};
+
+}  // namespace autofill
+
+#endif  // CHROME_BROWSER_UI_AUTOFILL_LOCAL_CARD_MIGRATION_BUBBLE_H_
\ No newline at end of file
diff --git a/chrome/browser/ui/autofill/local_card_migration_bubble_controller_impl.cc b/chrome/browser/ui/autofill/local_card_migration_bubble_controller_impl.cc
new file mode 100644
index 0000000..240919bb
--- /dev/null
+++ b/chrome/browser/ui/autofill/local_card_migration_bubble_controller_impl.cc
@@ -0,0 +1,171 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/autofill/local_card_migration_bubble_controller_impl.h"
+
+#include <stddef.h>
+
+#include "chrome/browser/ui/autofill/local_card_migration_bubble.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/location_bar/location_bar.h"
+#include "components/autofill/core/browser/autofill_metrics.h"
+#include "components/strings/grit/components_strings.h"
+#include "content/public/browser/navigation_handle.h"
+#include "ui/base/l10n/l10n_util.h"
+
+DEFINE_WEB_CONTENTS_USER_DATA_KEY(
+    autofill::LocalCardMigrationBubbleControllerImpl);
+
+namespace autofill {
+
+namespace {
+// Number of seconds the bubble and icon will survive navigations, starting
+// from when the bubble is shown.
+// TODO(crbug.com/862397): Share with ManagePasswordsUIController.
+const int kSurviveNavigationSeconds = 5;
+}  // namespace
+
+// TODO(crbug.com/862405): Build a base class for this
+// and SaveCardBubbleControllerImpl.
+LocalCardMigrationBubbleControllerImpl::LocalCardMigrationBubbleControllerImpl(
+    content::WebContents* web_contents)
+    : content::WebContentsObserver(web_contents),
+      local_card_migration_bubble_(nullptr) {}
+
+LocalCardMigrationBubbleControllerImpl::
+    ~LocalCardMigrationBubbleControllerImpl() {
+  if (local_card_migration_bubble_)
+    HideBubble();
+}
+
+void LocalCardMigrationBubbleControllerImpl::ShowBubble(
+    base::OnceClosure local_card_migration_bubble_closure) {
+  // Don't show the bubble if it's already visible.
+  if (local_card_migration_bubble_)
+    return;
+
+  is_reshow_ = false;
+  local_card_migration_bubble_closure_ =
+      std::move(local_card_migration_bubble_closure);
+
+  ShowBubbleImplementation();
+}
+
+void LocalCardMigrationBubbleControllerImpl::HideBubble() {
+  if (local_card_migration_bubble_) {
+    local_card_migration_bubble_->Hide();
+    local_card_migration_bubble_ = nullptr;
+  }
+}
+
+void LocalCardMigrationBubbleControllerImpl::ReshowBubble() {
+  if (local_card_migration_bubble_)
+    return;
+
+  ShowBubbleImplementation();
+}
+
+bool LocalCardMigrationBubbleControllerImpl::IsIconVisible() const {
+  return !local_card_migration_bubble_closure_.is_null();
+}
+
+LocalCardMigrationBubble*
+LocalCardMigrationBubbleControllerImpl::local_card_migration_bubble_view()
+    const {
+  return local_card_migration_bubble_;
+}
+
+base::string16 LocalCardMigrationBubbleControllerImpl::GetWindowTitle() const {
+  // TODO(crbug.com/859254): Update string once mock is finalized.
+  return l10n_util::GetStringUTF16(
+      IDS_AUTOFILL_LOCAL_CARD_MIGRATION_BUBBLE_TITLE);
+}
+
+void LocalCardMigrationBubbleControllerImpl::OnConfirmButtonClicked() {
+  DCHECK(local_card_migration_bubble_closure_);
+  std::move(local_card_migration_bubble_closure_).Run();
+}
+
+void LocalCardMigrationBubbleControllerImpl::OnCancelButtonClicked() {
+  local_card_migration_bubble_closure_.Reset();
+}
+
+void LocalCardMigrationBubbleControllerImpl::OnBubbleClosed() {
+  local_card_migration_bubble_ = nullptr;
+  UpdateIcon();
+}
+
+base::TimeDelta LocalCardMigrationBubbleControllerImpl::Elapsed() const {
+  return timer_->Elapsed();
+}
+
+void LocalCardMigrationBubbleControllerImpl::DidFinishNavigation(
+    content::NavigationHandle* navigation_handle) {
+  if (!navigation_handle->IsInMainFrame() || !navigation_handle->HasCommitted())
+    return;
+
+  // Nothing to do if there's no bubble available.
+  if (!local_card_migration_bubble_closure_)
+    return;
+
+  // Don't react to same-document (fragment) navigations.
+  if (navigation_handle->IsSameDocument())
+    return;
+
+  // Don't do anything if a navigation occurs before a user could reasonably
+  // interact with the bubble.
+  if (Elapsed() < base::TimeDelta::FromSeconds(kSurviveNavigationSeconds))
+    return;
+
+  // Otherwise, get rid of the bubble and icon.
+  local_card_migration_bubble_closure_.Reset();
+  bool bubble_was_visible = local_card_migration_bubble_;
+  if (bubble_was_visible) {
+    local_card_migration_bubble_->Hide();
+    OnBubbleClosed();
+  } else {
+    UpdateIcon();
+  }
+}
+
+void LocalCardMigrationBubbleControllerImpl::OnVisibilityChanged(
+    content::Visibility visibility) {
+  if (visibility == content::Visibility::HIDDEN)
+    HideBubble();
+}
+
+void LocalCardMigrationBubbleControllerImpl::WebContentsDestroyed() {
+  HideBubble();
+}
+
+void LocalCardMigrationBubbleControllerImpl::ShowBubbleImplementation() {
+  DCHECK(local_card_migration_bubble_closure_);
+  DCHECK(!local_card_migration_bubble_);
+
+  // Need to create location bar icon before bubble, otherwise bubble will be
+  // unanchored.
+  UpdateIcon();
+
+  Browser* browser = chrome::FindBrowserWithWebContents(web_contents());
+  local_card_migration_bubble_ =
+      browser->window()->ShowLocalCardMigrationBubble(web_contents(), this,
+                                                      true);
+  DCHECK(local_card_migration_bubble_);
+  UpdateIcon();
+  timer_.reset(new base::ElapsedTimer());
+}
+
+void LocalCardMigrationBubbleControllerImpl::UpdateIcon() {
+  Browser* browser = chrome::FindBrowserWithWebContents(web_contents());
+  if (!browser)
+    return;
+  LocationBar* location_bar = browser->window()->GetLocationBar();
+  if (!location_bar)
+    return;
+  location_bar->UpdateLocalCardMigrationIcon();
+}
+
+}  // namespace autofill
\ No newline at end of file
diff --git a/chrome/browser/ui/autofill/local_card_migration_bubble_controller_impl.h b/chrome/browser/ui/autofill/local_card_migration_bubble_controller_impl.h
new file mode 100644
index 0000000..36a6c4f
--- /dev/null
+++ b/chrome/browser/ui/autofill/local_card_migration_bubble_controller_impl.h
@@ -0,0 +1,90 @@
+// Copyright 2018 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_AUTOFILL_LOCAL_CARD_MIGRATION_BUBBLE_CONTROLLER_IMPL_H_
+#define CHROME_BROWSER_UI_AUTOFILL_LOCAL_CARD_MIGRATION_BUBBLE_CONTROLLER_IMPL_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/timer/elapsed_timer.h"
+#include "components/autofill/core/browser/ui/local_card_migration_bubble_controller.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "content/public/browser/web_contents_user_data.h"
+
+namespace autofill {
+
+// Implementation of per-tab class to control the local card migration bubble
+// and Omnibox icon.
+class LocalCardMigrationBubbleControllerImpl
+    : public LocalCardMigrationBubbleController,
+      public content::WebContentsObserver,
+      public content::WebContentsUserData<
+          LocalCardMigrationBubbleControllerImpl> {
+ public:
+  ~LocalCardMigrationBubbleControllerImpl() override;
+
+  // Shows the prompt that offers local credit card migration.
+  // |local_card_migration_bubble_closure| is run upon acceptance.
+  void ShowBubble(base::OnceClosure local_card_migration_bubble_closure);
+
+  // Remove the |local_card_migration_bubble_| and hide the bubble.
+  void HideBubble();
+
+  // Invoked when local card migration icon is clicked.
+  void ReshowBubble();
+
+  // Returns true if Omnibox save credit card icon should be visible.
+  bool IsIconVisible() const;
+
+  // Returns nullptr if no bubble is currently shown.
+  LocalCardMigrationBubble* local_card_migration_bubble_view() const;
+
+  // LocalCardBubbleController:
+  base::string16 GetWindowTitle() const override;
+  void OnConfirmButtonClicked() override;
+  void OnCancelButtonClicked() override;
+  void OnBubbleClosed() override;
+
+ protected:
+  explicit LocalCardMigrationBubbleControllerImpl(
+      content::WebContents* web_contents);
+
+  // Returns the time elapsed since |timer_| was initialized.
+  // Exists for testing.
+  virtual base::TimeDelta Elapsed() const;
+
+  // content::WebContentsObserver:
+  void DidFinishNavigation(
+      content::NavigationHandle* navigation_handle) override;
+  void OnVisibilityChanged(content::Visibility visibility) override;
+  void WebContentsDestroyed() override;
+
+ private:
+  friend class content::WebContentsUserData<
+      LocalCardMigrationBubbleControllerImpl>;
+
+  void ShowBubbleImplementation();
+
+  // Update the visibility and toggled state of the Omnibox save card icon.
+  void UpdateIcon();
+
+  // Weak reference. Will be nullptr if no bubble is currently shown.
+  LocalCardMigrationBubble* local_card_migration_bubble_ = nullptr;
+
+  // Callback to run if user presses Save button in the offer-to-migrate bubble.
+  base::OnceClosure local_card_migration_bubble_closure_;
+
+  // Timer used to track the amount of time on this page.
+  std::unique_ptr<base::ElapsedTimer> timer_;
+
+  // Boolean to determine if bubble is called from ReshowBubble().
+  bool is_reshow_ = false;
+
+  DISALLOW_COPY_AND_ASSIGN(LocalCardMigrationBubbleControllerImpl);
+};
+
+}  // namespace autofill
+
+#endif  // CHROME_BROWSER_UI_AUTOFILL_LOCAL_CARD_MIGRATION_BUBBLE_CONTROLLER_IMPL_H_
\ No newline at end of file
diff --git a/chrome/browser/ui/browser_window.h b/chrome/browser/ui/browser_window.h
index 39fd1a7..eaa2e78 100644
--- a/chrome/browser/ui/browser_window.h
+++ b/chrome/browser/ui/browser_window.h
@@ -41,6 +41,8 @@
 class ToolbarActionsBar;
 
 namespace autofill {
+class LocalCardMigrationBubbleController;
+class LocalCardMigrationBubble;
 class SaveCardBubbleController;
 class SaveCardBubbleView;
 }
@@ -259,6 +261,12 @@
       autofill::SaveCardBubbleController* controller,
       bool is_user_gesture) = 0;
 
+  // Shows the local card migration bubble.
+  virtual autofill::LocalCardMigrationBubble* ShowLocalCardMigrationBubble(
+      content::WebContents* contents,
+      autofill::LocalCardMigrationBubbleController* controller,
+      bool is_user_gesture) = 0;
+
   // Shows the translate bubble.
   //
   // |is_user_gesture| is true when the bubble is shown on the user's deliberate
diff --git a/chrome/browser/ui/cocoa/accelerators_cocoa.mm b/chrome/browser/ui/cocoa/accelerators_cocoa.mm
index ab4ebf2..0f10ac1 100644
--- a/chrome/browser/ui/cocoa/accelerators_cocoa.mm
+++ b/chrome/browser/ui/cocoa/accelerators_cocoa.mm
@@ -42,66 +42,67 @@
   NSUInteger modifiers;  // The Cocoa modifiers.
   ui::KeyboardCode key_code;  // The key used for cross-platform compatibility.
 } kAcceleratorMap[] = {
-    // Accelerators used in the toolbar menu.
-    {IDC_CLEAR_BROWSING_DATA, NSCommandKeyMask | NSShiftKeyMask, ui::VKEY_BACK},
-    {IDC_COPY, NSCommandKeyMask, ui::VKEY_C},
-    {IDC_CUT, NSCommandKeyMask, ui::VKEY_X},
-    {IDC_DEV_TOOLS, NSCommandKeyMask | NSAlternateKeyMask, ui::VKEY_I},
-    {IDC_DEV_TOOLS_CONSOLE, NSCommandKeyMask | NSAlternateKeyMask, ui::VKEY_J},
-    {IDC_FIND, NSCommandKeyMask, ui::VKEY_F},
-    {IDC_FULLSCREEN, NSCommandKeyMask | NSControlKeyMask, ui::VKEY_F},
-    {IDC_NEW_INCOGNITO_WINDOW, NSCommandKeyMask | NSShiftKeyMask, ui::VKEY_N},
-    {IDC_NEW_TAB, NSCommandKeyMask, ui::VKEY_T},
-    {IDC_NEW_WINDOW, NSCommandKeyMask, ui::VKEY_N},
-    {IDC_PASTE, NSCommandKeyMask, ui::VKEY_V},
-    {IDC_PRINT, NSCommandKeyMask, ui::VKEY_P},
-    {IDC_RESTORE_TAB, NSCommandKeyMask | NSShiftKeyMask, ui::VKEY_T},
-    {IDC_SAVE_PAGE, NSCommandKeyMask, ui::VKEY_S},
-    {IDC_SHOW_BOOKMARK_BAR, NSCommandKeyMask | NSShiftKeyMask, ui::VKEY_B},
-    {IDC_SHOW_BOOKMARK_MANAGER, NSCommandKeyMask | NSAlternateKeyMask,
-     ui::VKEY_B},
-    {IDC_BOOKMARK_PAGE, NSCommandKeyMask, ui::VKEY_D},
-    {IDC_SHOW_DOWNLOADS, NSCommandKeyMask | NSShiftKeyMask, ui::VKEY_J},
-    {IDC_SHOW_HISTORY, NSCommandKeyMask, ui::VKEY_Y},
-    {IDC_VIEW_SOURCE, NSCommandKeyMask | NSAlternateKeyMask, ui::VKEY_U},
-    {IDC_ZOOM_MINUS, NSCommandKeyMask, ui::VKEY_OEM_MINUS},
-    {IDC_ZOOM_PLUS, NSCommandKeyMask | NSShiftKeyMask, ui::VKEY_OEM_PLUS},
+  // Accelerators used in the toolbar menu.
+  {IDC_CLEAR_BROWSING_DATA, NSCommandKeyMask | NSShiftKeyMask, ui::VKEY_BACK},
+  {IDC_COPY, NSCommandKeyMask, ui::VKEY_C},
+  {IDC_CUT, NSCommandKeyMask, ui::VKEY_X},
+  {IDC_DEV_TOOLS, NSCommandKeyMask | NSAlternateKeyMask, ui::VKEY_I},
+  {IDC_DEV_TOOLS_CONSOLE, NSCommandKeyMask | NSAlternateKeyMask, ui::VKEY_J},
+  {IDC_FIND, NSCommandKeyMask, ui::VKEY_F},
+  {IDC_FULLSCREEN, NSCommandKeyMask | NSControlKeyMask, ui::VKEY_F},
+  {IDC_NEW_INCOGNITO_WINDOW, NSCommandKeyMask | NSShiftKeyMask, ui::VKEY_N},
+  {IDC_NEW_TAB, NSCommandKeyMask, ui::VKEY_T},
+  {IDC_NEW_WINDOW, NSCommandKeyMask, ui::VKEY_N},
+  {IDC_PASTE, NSCommandKeyMask, ui::VKEY_V},
+  {IDC_PRINT, NSCommandKeyMask, ui::VKEY_P},
+  {IDC_RESTORE_TAB, NSCommandKeyMask | NSShiftKeyMask, ui::VKEY_T},
+  {IDC_SAVE_PAGE, NSCommandKeyMask, ui::VKEY_S},
+  {IDC_SHOW_BOOKMARK_BAR, NSCommandKeyMask | NSShiftKeyMask, ui::VKEY_B},
+  {IDC_SHOW_BOOKMARK_MANAGER, NSCommandKeyMask | NSAlternateKeyMask,
+   ui::VKEY_B},
+  {IDC_BOOKMARK_PAGE, NSCommandKeyMask, ui::VKEY_D},
+  {IDC_SHOW_DOWNLOADS, NSCommandKeyMask | NSShiftKeyMask, ui::VKEY_J},
+  {IDC_SHOW_HISTORY, NSCommandKeyMask, ui::VKEY_Y},
+  {IDC_VIEW_SOURCE, NSCommandKeyMask | NSAlternateKeyMask, ui::VKEY_U},
+  {IDC_ZOOM_MINUS, NSCommandKeyMask, ui::VKEY_OEM_MINUS},
+  {IDC_ZOOM_PLUS, NSCommandKeyMask | NSShiftKeyMask, ui::VKEY_OEM_PLUS},
 
-    // Accelerators used in MainMenu.xib, but not the toolbar menu.
-    {IDC_HIDE_APP, NSCommandKeyMask, ui::VKEY_H},
-    {IDC_EXIT, NSCommandKeyMask, ui::VKEY_Q},
-    {IDC_OPEN_FILE, NSCommandKeyMask, ui::VKEY_O},
-    {IDC_FOCUS_LOCATION, NSCommandKeyMask, ui::VKEY_L},
-    {IDC_CLOSE_WINDOW, NSCommandKeyMask, ui::VKEY_W},
-    {IDC_EMAIL_PAGE_LOCATION, NSCommandKeyMask | NSShiftKeyMask, ui::VKEY_I},
+  // Accelerators used in MainMenu.xib, but not the toolbar menu.
+  {IDC_HIDE_APP, NSCommandKeyMask, ui::VKEY_H},
+  {IDC_EXIT, NSCommandKeyMask, ui::VKEY_Q},
+  {IDC_OPEN_FILE, NSCommandKeyMask, ui::VKEY_O},
+  {IDC_FOCUS_LOCATION, NSCommandKeyMask, ui::VKEY_L},
+  {IDC_CLOSE_WINDOW, NSCommandKeyMask, ui::VKEY_W},
+  {IDC_EMAIL_PAGE_LOCATION, NSCommandKeyMask | NSShiftKeyMask, ui::VKEY_I},
 #if BUILDFLAG(ENABLE_PRINTING)
-    {IDC_BASIC_PRINT, NSCommandKeyMask | NSAlternateKeyMask, ui::VKEY_P},
+  {IDC_BASIC_PRINT, NSCommandKeyMask | NSAlternateKeyMask, ui::VKEY_P},
 #endif  // ENABLE_PRINTING
-    {IDC_CONTENT_CONTEXT_UNDO, NSCommandKeyMask, ui::VKEY_Z},
-    {IDC_CONTENT_CONTEXT_REDO, NSCommandKeyMask | NSShiftKeyMask, ui::VKEY_Z},
-    {IDC_CONTENT_CONTEXT_CUT, NSCommandKeyMask, ui::VKEY_X},
-    {IDC_CONTENT_CONTEXT_COPY, NSCommandKeyMask, ui::VKEY_C},
-    {IDC_CONTENT_CONTEXT_PASTE, NSCommandKeyMask, ui::VKEY_V},
-    {IDC_CONTENT_CONTEXT_PASTE_AND_MATCH_STYLE,
-     NSCommandKeyMask | NSShiftKeyMask, ui::VKEY_V},
-    {IDC_CONTENT_CONTEXT_SELECTALL, NSCommandKeyMask, ui::VKEY_A},
-    {IDC_FOCUS_SEARCH, NSCommandKeyMask | NSAlternateKeyMask, ui::VKEY_F},
-    {IDC_FIND_NEXT, NSCommandKeyMask, ui::VKEY_G},
-    {IDC_FIND_PREVIOUS, NSCommandKeyMask | NSShiftKeyMask, ui::VKEY_G},
-    {IDC_ZOOM_PLUS, NSCommandKeyMask, ui::VKEY_OEM_PLUS},
-    {IDC_ZOOM_MINUS, NSCommandKeyMask, ui::VKEY_OEM_MINUS},
-    {IDC_STOP, NSCommandKeyMask, ui::VKEY_OEM_PERIOD},
-    {IDC_RELOAD, NSCommandKeyMask, ui::VKEY_R},
-    {IDC_RELOAD_BYPASSING_CACHE, NSCommandKeyMask | NSShiftKeyMask, ui::VKEY_R},
-    {IDC_ZOOM_NORMAL, NSCommandKeyMask, ui::VKEY_0},
-    {IDC_HOME, NSCommandKeyMask | NSShiftKeyMask, ui::VKEY_H},
-    {IDC_BACK, NSCommandKeyMask, ui::VKEY_OEM_4},
-    {IDC_FORWARD, NSCommandKeyMask, ui::VKEY_OEM_6},
-    {IDC_BOOKMARK_ALL_TABS, NSCommandKeyMask | NSShiftKeyMask, ui::VKEY_D},
-    {IDC_MINIMIZE_WINDOW, NSCommandKeyMask, ui::VKEY_M},
-    {IDC_SELECT_NEXT_TAB, NSControlKeyMask, ui::VKEY_TAB},
-    {IDC_SELECT_PREVIOUS_TAB, NSControlKeyMask | NSShiftKeyMask, ui::VKEY_TAB},
-    {IDC_HELP_PAGE_VIA_MENU, NSCommandKeyMask | NSShiftKeyMask, ui::VKEY_OEM_2},
+  {IDC_CONTENT_CONTEXT_UNDO, NSCommandKeyMask, ui::VKEY_Z},
+  {IDC_CONTENT_CONTEXT_REDO, NSCommandKeyMask | NSShiftKeyMask, ui::VKEY_Z},
+  {IDC_CONTENT_CONTEXT_CUT, NSCommandKeyMask, ui::VKEY_X},
+  {IDC_CONTENT_CONTEXT_COPY, NSCommandKeyMask, ui::VKEY_C},
+  {IDC_CONTENT_CONTEXT_PASTE, NSCommandKeyMask, ui::VKEY_V},
+  {IDC_CONTENT_CONTEXT_PASTE_AND_MATCH_STYLE,
+   NSCommandKeyMask | NSShiftKeyMask, ui::VKEY_V},
+  {IDC_CONTENT_CONTEXT_SELECTALL, NSCommandKeyMask, ui::VKEY_A},
+  {IDC_FOCUS_SEARCH, NSCommandKeyMask | NSAlternateKeyMask, ui::VKEY_F},
+  {IDC_FIND_NEXT, NSCommandKeyMask, ui::VKEY_G},
+  {IDC_FIND_PREVIOUS, NSCommandKeyMask | NSShiftKeyMask, ui::VKEY_G},
+  {IDC_ZOOM_PLUS, NSCommandKeyMask, ui::VKEY_OEM_PLUS},
+  {IDC_ZOOM_MINUS, NSCommandKeyMask, ui::VKEY_OEM_MINUS},
+  {IDC_STOP, NSCommandKeyMask, ui::VKEY_OEM_PERIOD},
+  {IDC_RELOAD, NSCommandKeyMask, ui::VKEY_R},
+  {IDC_RELOAD_BYPASSING_CACHE, NSCommandKeyMask | NSShiftKeyMask, ui::VKEY_R},
+  {IDC_ZOOM_NORMAL, NSCommandKeyMask, ui::VKEY_0},
+  {IDC_HOME, NSCommandKeyMask | NSShiftKeyMask, ui::VKEY_H},
+  {IDC_BACK, NSCommandKeyMask, ui::VKEY_OEM_4},
+  {IDC_FORWARD, NSCommandKeyMask, ui::VKEY_OEM_6},
+  {IDC_BOOKMARK_ALL_TABS, NSCommandKeyMask | NSShiftKeyMask, ui::VKEY_D},
+  {IDC_MINIMIZE_WINDOW, NSCommandKeyMask, ui::VKEY_M},
+  {IDC_SELECT_NEXT_TAB, NSCommandKeyMask | NSAlternateKeyMask, ui::VKEY_RIGHT},
+  {IDC_SELECT_PREVIOUS_TAB, NSCommandKeyMask | NSAlternateKeyMask,
+   ui::VKEY_LEFT},
+  {IDC_HELP_PAGE_VIA_MENU, NSCommandKeyMask | NSShiftKeyMask, ui::VKEY_OEM_2},
 };
 
 // Create a Cocoa platform accelerator given a cross platform |key_code| and
diff --git a/chrome/browser/ui/cocoa/browser_window_cocoa.h b/chrome/browser/ui/cocoa/browser_window_cocoa.h
index c4e9b34..fcd90de1 100644
--- a/chrome/browser/ui/cocoa/browser_window_cocoa.h
+++ b/chrome/browser/ui/cocoa/browser_window_cocoa.h
@@ -106,6 +106,10 @@
       content::WebContents* contents,
       autofill::SaveCardBubbleController* controller,
       bool user_gesture) override;
+  autofill::LocalCardMigrationBubble* ShowLocalCardMigrationBubble(
+      content::WebContents* contents,
+      autofill::LocalCardMigrationBubbleController* controller,
+      bool user_gesture) override;
   ShowTranslateBubbleResult ShowTranslateBubble(
       content::WebContents* contents,
       translate::TranslateStep step,
diff --git a/chrome/browser/ui/cocoa/browser_window_cocoa.mm b/chrome/browser/ui/cocoa/browser_window_cocoa.mm
index b08b078..96f88bf 100644
--- a/chrome/browser/ui/cocoa/browser_window_cocoa.mm
+++ b/chrome/browser/ui/cocoa/browser_window_cocoa.mm
@@ -519,6 +519,16 @@
                                             controller_, user_gesture);
 }
 
+autofill::LocalCardMigrationBubble*
+BrowserWindowCocoa::ShowLocalCardMigrationBubble(
+    content::WebContents* web_contents,
+    autofill::LocalCardMigrationBubbleController* controller,
+    bool user_gesture) {
+  // TODO(crbug.com/859652): Implement on Mac.
+  NOTIMPLEMENTED();
+  return nullptr;
+}
+
 ShowTranslateBubbleResult BrowserWindowCocoa::ShowTranslateBubble(
     content::WebContents* contents,
     translate::TranslateStep step,
diff --git a/chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h b/chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h
index bdceec4..0ea1f6e 100644
--- a/chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h
+++ b/chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h
@@ -77,6 +77,7 @@
   void UpdateContentSettingsIcons() override;
   void UpdateManagePasswordsIconAndBubble() override;
   void UpdateSaveCreditCardIcon() override;
+  void UpdateLocalCardMigrationIcon() override;
   void UpdateFindBarIconVisibility() override;
   void UpdateBookmarkStarVisibility() override;
   void UpdateLocationBarVisibility(bool visible, bool animate) override;
diff --git a/chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.mm b/chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.mm
index e7e6a5f..f3ee0f7 100644
--- a/chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.mm
+++ b/chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.mm
@@ -201,6 +201,11 @@
   OnDecorationsChanged();
 }
 
+void LocationBarViewMac::UpdateLocalCardMigrationIcon() {
+  // TODO(crbug.com/859652): Implement for mac.
+  NOTIMPLEMENTED();
+}
+
 void LocationBarViewMac::UpdateFindBarIconVisibility() {
   // TODO(crbug/651643): Implement for mac.
   NOTIMPLEMENTED();
diff --git a/chrome/browser/ui/cocoa/nsmenuitem_additions.mm b/chrome/browser/ui/cocoa/nsmenuitem_additions.mm
index 9cd13b7..2dc62b7 100644
--- a/chrome/browser/ui/cocoa/nsmenuitem_additions.mm
+++ b/chrome/browser/ui/cocoa/nsmenuitem_additions.mm
@@ -147,20 +147,10 @@
     }
   }
 
-  // [ctr + shift + tab] generates the "End of Medium" keyEquivalent rather than
-  // "Horizontal Tab". We still use "Horizontal Tab" in the main menu to match
-  // the behavior of Safari and Terminal. Thus, we need to explicitly check for
-  // this case.
-  if ((eventModifiers & NSShiftKeyMask) &&
-      [eventString isEqualToString:@"\x19"]) {
-    eventString = @"\x9";
-  } else {
-    // Clear shift key for printable characters, excluding tab.
-    if ((eventModifiers & (NSNumericPadKeyMask | NSFunctionKeyMask)) == 0 &&
-        [[self keyEquivalent] characterAtIndex:0] != '\r') {
-      eventModifiers &= ~NSShiftKeyMask;
-    }
-  }
+  // Clear shift key for printable characters.
+  if ((eventModifiers & (NSNumericPadKeyMask | NSFunctionKeyMask)) == 0 &&
+      [[self keyEquivalent] characterAtIndex:0] != '\r')
+    eventModifiers &= ~NSShiftKeyMask;
 
   // Clear all non-interesting modifiers
   eventModifiers &= NSCommandKeyMask |
diff --git a/chrome/browser/ui/cocoa/nsmenuitem_additions_unittest.mm b/chrome/browser/ui/cocoa/nsmenuitem_additions_unittest.mm
index 4182386..c37c000 100644
--- a/chrome/browser/ui/cocoa/nsmenuitem_additions_unittest.mm
+++ b/chrome/browser/ui/cocoa/nsmenuitem_additions_unittest.mm
@@ -291,13 +291,6 @@
   key = KeyEvent(0x100108, @"s", @"\u0441", 1);
   ExpectKeyFiresItem(key, MenuItem(@"s", 0x100000), false);
   ExpectKeyDoesntFireItem(key, MenuItem(@"c", 0x100000));
-
-  // ctr + shift + tab produces the "End of Medium" keyEquivalent, even though
-  // it should produce the "Horizontal Tab" keyEquivalent. Check to make sure
-  // it matches anyways.
-  key = KeyEvent(0x60103, @"\x19", @"\x19", 1);
-  ExpectKeyFiresItem(key, MenuItem(@"\x9", NSShiftKeyMask | NSControlKeyMask),
-                     false);
 }
 
 NSString* keyCodeToCharacter(NSUInteger keyCode,
diff --git a/chrome/browser/ui/layout_constants.cc b/chrome/browser/ui/layout_constants.cc
index 40d7ca2..259ca5b 100644
--- a/chrome/browser/ui/layout_constants.cc
+++ b/chrome/browser/ui/layout_constants.cc
@@ -75,12 +75,14 @@
       return touch_optimized_material ? 12 : 16;
     case TAB_HEIGHT: {
       constexpr int kTabHeight[] = {29, 33, 41, 36, 41};
-      return kTabHeight[mode];
+      return kTabHeight[mode] + GetLayoutConstant(TABSTRIP_TOOLBAR_OVERLAP);
     }
     case TAB_PRE_TITLE_PADDING:
       return newer_material ? 8 : 6;
     case TAB_STACK_DISTANCE:
       return touch_optimized_material ? 4 : 6;
+    case TABSTRIP_TOOLBAR_OVERLAP:
+      return ui::MaterialDesignController::IsRefreshUi() ? 1 : 0;
     case TOOLBAR_ELEMENT_PADDING: {
       constexpr int kPadding[] = {0, 8, 0, 4, 0};
       return kPadding[mode];
diff --git a/chrome/browser/ui/layout_constants.h b/chrome/browser/ui/layout_constants.h
index e0f2545..a0fa58c 100644
--- a/chrome/browser/ui/layout_constants.h
+++ b/chrome/browser/ui/layout_constants.h
@@ -86,6 +86,13 @@
   // subsequent tab when tabs are stacked.
   TAB_STACK_DISTANCE,
 
+  // In refresh, tabs are drawn with an extension into the toolbar's
+  // space to prevent a gap from appearing between the toolbar and the
+  // bottom of tabs on some non-integral scales.
+  // TODO(tbergquist): Remove this after pixel canvas or any deeper fix to
+  // non-pixel-aligned drawing goes in.  See https://crbug.com/765723.
+  TABSTRIP_TOOLBAR_OVERLAP,
+
   // Additional horizontal padding between the elements in the toolbar.
   TOOLBAR_ELEMENT_PADDING,
 
diff --git a/chrome/browser/ui/location_bar/location_bar.h b/chrome/browser/ui/location_bar/location_bar.h
index adeebd45..ea956e2 100644
--- a/chrome/browser/ui/location_bar/location_bar.h
+++ b/chrome/browser/ui/location_bar/location_bar.h
@@ -52,6 +52,9 @@
   // Updates the visibility and toggled state of the save credit card icon.
   virtual void UpdateSaveCreditCardIcon() = 0;
 
+  // Updates the visibility and toggled state of the local card migration icon.
+  virtual void UpdateLocalCardMigrationIcon() = 0;
+
   // Updates the visibility of the find bar image icon.
   virtual void UpdateFindBarIconVisibility() = 0;
 
diff --git a/chrome/browser/ui/page_info/page_info.cc b/chrome/browser/ui/page_info/page_info.cc
index 66dd2f9..9681d8e 100644
--- a/chrome/browser/ui/page_info/page_info.cc
+++ b/chrome/browser/ui/page_info/page_info.cc
@@ -16,6 +16,7 @@
 #include "base/i18n/time_formatting.h"
 #include "base/macros.h"
 #include "base/metrics/field_trial.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/user_metrics.h"
 #include "base/strings/string_number_conversions.h"
@@ -302,6 +303,37 @@
      IDS_PAGE_INFO_DELETE_USB_DEVICE, "name"},
 };
 
+// Time open histogram prefixes.
+const char kPageInfoTimePrefix[] = "Security.PageInfo.TimeOpen";
+const char kPageInfoTimeActionPrefix[] = "Security.PageInfo.TimeOpen.Action";
+const char kPageInfoTimeNoActionPrefix[] =
+    "Security.PageInfo.TimeOpen.NoAction";
+
+std::string GetHistogramSuffixForSecurityLevel(
+    security_state::SecurityLevel level) {
+  switch (level) {
+    case security_state::EV_SECURE:
+      return "EV_SECURE";
+    case security_state::SECURE:
+      return "SECURE";
+    case security_state::NONE:
+      return "NONE";
+    case security_state::HTTP_SHOW_WARNING:
+      return "HTTP_SHOW_WARNING";
+    case security_state::SECURE_WITH_POLICY_INSTALLED_CERT:
+      return "SECURE_WITH_POLICY_INSTALLED_CERT";
+    case security_state::DANGEROUS:
+      return "DANGEROUS";
+    default:
+      return "OTHER";
+  }
+}
+
+std::string GetHistogramName(const char* prefix,
+                             security_state::SecurityLevel level) {
+  return std::string(prefix) + "." + GetHistogramSuffixForSecurityLevel(level);
+}
+
 }  // namespace
 
 PageInfo::PageInfo(PageInfoUI* ui,
@@ -330,7 +362,8 @@
           safe_browsing::ChromePasswordProtectionService::
               GetPasswordProtectionService(profile_)),
 #endif
-      show_change_password_buttons_(false) {
+      show_change_password_buttons_(false),
+      did_perform_action_(false) {
   Init(url, security_info);
 
   PresentSitePermissions();
@@ -340,6 +373,10 @@
   // Every time the Page Info UI is opened a |PageInfo| object is
   // created. So this counts how ofter the Page Info UI is opened.
   RecordPageInfoAction(PAGE_INFO_OPENED);
+
+  // Record the time when the Page Info UI is opened so the total time it is
+  // open can be measured.
+  start_time_ = base::TimeTicks::Now();
 }
 
 PageInfo::~PageInfo() {
@@ -353,9 +390,32 @@
                               user_decision,
                               END_OF_SSL_CERTIFICATE_DECISIONS_DID_REVOKE_ENUM);
   }
+
+  // Record the total time the Page Info UI was open for all opens as well as
+  // split between whether any action was taken.
+  base::UmaHistogramCustomTimes(
+      GetHistogramName(kPageInfoTimePrefix, security_level_),
+      base::TimeTicks::Now() - start_time_,
+      base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromHours(1), 100);
+  if (did_perform_action_) {
+    base::UmaHistogramCustomTimes(
+        GetHistogramName(kPageInfoTimeActionPrefix, security_level_),
+        base::TimeTicks::Now() - start_time_,
+        base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromHours(1),
+        100);
+  } else {
+    base::UmaHistogramCustomTimes(
+        GetHistogramName(kPageInfoTimeNoActionPrefix, security_level_),
+        base::TimeTicks::Now() - start_time_,
+        base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromHours(1),
+        100);
+  }
 }
 
 void PageInfo::RecordPageInfoAction(PageInfoAction action) {
+  if (action != PAGE_INFO_OPENED)
+    did_perform_action_ = true;
+
   UMA_HISTOGRAM_ENUMERATION("WebsiteSettings.Action", action, PAGE_INFO_COUNT);
 
   std::string histogram_name;
diff --git a/chrome/browser/ui/page_info/page_info.h b/chrome/browser/ui/page_info/page_info.h
index 4674757..6d90185 100644
--- a/chrome/browser/ui/page_info/page_info.h
+++ b/chrome/browser/ui/page_info/page_info.h
@@ -289,6 +289,9 @@
   // whitelist current site.
   bool show_change_password_buttons_;
 
+  base::TimeTicks start_time_;
+  bool did_perform_action_;
+
   DISALLOW_COPY_AND_ASSIGN(PageInfo);
 };
 
diff --git a/chrome/browser/ui/page_info/page_info_unittest.cc b/chrome/browser/ui/page_info/page_info_unittest.cc
index 4bcdd9f..059b221 100644
--- a/chrome/browser/ui/page_info/page_info_unittest.cc
+++ b/chrome/browser/ui/page_info/page_info_unittest.cc
@@ -983,6 +983,71 @@
   }
 }
 
+// Tests that the duration of time the PageInfo is open is recorded for pages
+// with various security levels.
+TEST_F(PageInfoTest, TimeOpenMetrics) {
+  struct TestCase {
+    const std::string url;
+    const security_state::SecurityLevel security_level;
+    const std::string security_level_name;
+    const PageInfo::PageInfoAction action;
+  };
+
+  const std::string kHistogramPrefix("Security.PageInfo.TimeOpen.");
+
+  const TestCase kTestCases[] = {
+      // PAGE_INFO_COUNT used as shorthand for "take no action".
+      {"https://example.test", security_state::SECURE, "SECURE",
+       PageInfo::PAGE_INFO_COUNT},
+      {"https://example.test", security_state::EV_SECURE, "EV_SECURE",
+       PageInfo::PAGE_INFO_COUNT},
+      {"http://example.test", security_state::NONE, "NONE",
+       PageInfo::PAGE_INFO_COUNT},
+      {"https://example.test", security_state::SECURE, "SECURE",
+       PageInfo::PAGE_INFO_SITE_SETTINGS_OPENED},
+      {"https://example.test", security_state::EV_SECURE, "EV_SECURE",
+       PageInfo::PAGE_INFO_SITE_SETTINGS_OPENED},
+      {"http://example.test", security_state::NONE, "NONE",
+       PageInfo::PAGE_INFO_SITE_SETTINGS_OPENED},
+  };
+
+  for (const auto& test : kTestCases) {
+    base::HistogramTester histograms;
+    SetURL(test.url);
+    security_info_.security_level = test.security_level;
+    ResetMockUI();
+    ClearPageInfo();
+    SetDefaultUIExpectations(mock_ui());
+
+    histograms.ExpectTotalCount(kHistogramPrefix + test.security_level_name, 0);
+    histograms.ExpectTotalCount(
+        kHistogramPrefix + "Action." + test.security_level_name, 0);
+    histograms.ExpectTotalCount(
+        kHistogramPrefix + "NoAction." + test.security_level_name, 0);
+
+    PageInfo* test_page_info = page_info();
+    if (test.action != PageInfo::PAGE_INFO_COUNT) {
+      test_page_info->RecordPageInfoAction(test.action);
+    }
+    ClearPageInfo();
+
+    histograms.ExpectTotalCount(kHistogramPrefix + test.security_level_name, 1);
+
+    if (test.action != PageInfo::PAGE_INFO_COUNT) {
+      histograms.ExpectTotalCount(
+          kHistogramPrefix + "Action." + test.security_level_name, 1);
+    } else {
+      histograms.ExpectTotalCount(
+          kHistogramPrefix + "NoAction." + test.security_level_name, 1);
+    }
+  }
+
+  // PageInfoTest expects a valid PageInfo instance to exist at end of test.
+  ResetMockUI();
+  SetDefaultUIExpectations(mock_ui());
+  page_info();
+}
+
 // Tests that the SubresourceFilter setting is omitted correctly.
 TEST_F(PageInfoTest, SubresourceFilterSetting_MatchesActivation) {
   auto showing_setting = [](const PermissionInfoList& permissions) {
diff --git a/chrome/browser/ui/search/local_ntp_test_utils.cc b/chrome/browser/ui/search/local_ntp_test_utils.cc
index 0cc250a..23b8268 100644
--- a/chrome/browser/ui/search/local_ntp_test_utils.cc
+++ b/chrome/browser/ui/search/local_ntp_test_utils.cc
@@ -24,7 +24,6 @@
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_test_utils.h"
-#include "ui/base/pref_names.h"
 #include "ui/base/resource/resource_bundle.h"
 
 namespace local_ntp_test_utils {
diff --git a/chrome/browser/ui/view_ids.h b/chrome/browser/ui/view_ids.h
index 90753bb..ae98cca 100644
--- a/chrome/browser/ui/view_ids.h
+++ b/chrome/browser/ui/view_ids.h
@@ -60,6 +60,7 @@
   VIEW_ID_OMNIBOX,
   VIEW_ID_SCRIPT_BUBBLE,
   VIEW_ID_SAVE_CREDIT_CARD_BUTTON,
+  VIEW_ID_MIGRATE_LOCAL_CREDIT_CARD_BUTTON,
   VIEW_ID_TRANSLATE_BUTTON,
 
   // The Bookmark Bar.
diff --git a/chrome/browser/ui/views/autofill/local_card_migration_bubble_views.cc b/chrome/browser/ui/views/autofill/local_card_migration_bubble_views.cc
new file mode 100644
index 0000000..ca8ee7d
--- /dev/null
+++ b/chrome/browser/ui/views/autofill/local_card_migration_bubble_views.cc
@@ -0,0 +1,127 @@
+// Copyright 2018 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/autofill/local_card_migration_bubble_views.h"
+
+#include <stddef.h>
+#include <memory>
+
+#include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
+#include "chrome/app/vector_icons/vector_icons.h"
+#include "chrome/browser/ui/browser_dialogs.h"
+#include "chrome/browser/ui/views/autofill/dialog_view_ids.h"
+#include "chrome/browser/ui/views/harmony/chrome_layout_provider.h"
+#include "chrome/browser/ui/views/harmony/chrome_typography.h"
+#include "components/autofill/core/browser/ui/local_card_migration_bubble_controller.h"
+#include "components/strings/grit/components_strings.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/material_design/material_design_controller.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/gfx/color_palette.h"
+#include "ui/gfx/geometry/insets.h"
+#include "ui/gfx/paint_vector_icon.h"
+#include "ui/views/border.h"
+#include "ui/views/bubble/bubble_frame_view.h"
+#include "ui/views/controls/button/blue_button.h"
+#include "ui/views/controls/button/label_button.h"
+#include "ui/views/controls/label.h"
+#include "ui/views/layout/box_layout.h"
+#include "ui/views/style/typography.h"
+
+namespace autofill {
+
+LocalCardMigrationBubbleViews::LocalCardMigrationBubbleViews(
+    views::View* anchor_view,
+    const gfx::Point& anchor_point,
+    content::WebContents* web_contents,
+    LocalCardMigrationBubbleController* controller)
+    : LocationBarBubbleDelegateView(anchor_view, anchor_point, web_contents),
+      controller_(controller) {
+  DCHECK(controller);
+}
+
+void LocalCardMigrationBubbleViews::Show(DisplayReason reason) {
+  ShowForReason(reason);
+}
+
+void LocalCardMigrationBubbleViews::Hide() {
+  // If |controller_| is null, WindowClosing() won't invoke OnBubbleClosed(), so
+  // do that here. This will clear out |controller_|'s reference to |this|. Note
+  // that WindowClosing() happens only after the _asynchronous_ Close() task
+  // posted in CloseBubble() completes, but we need to fix references sooner.
+  if (controller_)
+    controller_->OnBubbleClosed();
+  controller_ = nullptr;
+  CloseBubble();
+}
+
+bool LocalCardMigrationBubbleViews::Accept() {
+  if (controller_)
+    controller_->OnConfirmButtonClicked();
+  return true;
+}
+
+bool LocalCardMigrationBubbleViews::Cancel() {
+  if (controller_)
+    controller_->OnCancelButtonClicked();
+  return true;
+}
+
+bool LocalCardMigrationBubbleViews::Close() {
+  return true;
+}
+
+int LocalCardMigrationBubbleViews::GetDialogButtons() const {
+  return ui::MaterialDesignController::IsSecondaryUiMaterial()
+             ? ui::DIALOG_BUTTON_OK
+             : ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL;
+}
+
+base::string16 LocalCardMigrationBubbleViews::GetDialogButtonLabel(
+    ui::DialogButton button) const {
+  // TODO(crbug.com/859254): Update OK button label once mock is finalized.
+  return l10n_util::GetStringUTF16(
+      button == ui::DIALOG_BUTTON_OK
+          ? IDS_AUTOFILL_LOCAL_CARD_MIGRATION_BUBBLE_BUTTON_LABEL
+          : IDS_NO_THANKS);
+}
+
+gfx::Size LocalCardMigrationBubbleViews::CalculatePreferredSize() const {
+  const int width = ChromeLayoutProvider::Get()->GetDistanceMetric(
+                        DISTANCE_BUBBLE_PREFERRED_WIDTH) -
+                    margins().width();
+  return gfx::Size(width, GetHeightForWidth(width));
+}
+
+bool LocalCardMigrationBubbleViews::ShouldShowCloseButton() const {
+  return ui::MaterialDesignController::IsSecondaryUiMaterial();
+}
+
+base::string16 LocalCardMigrationBubbleViews::GetWindowTitle() const {
+  return controller_->GetWindowTitle();
+}
+
+void LocalCardMigrationBubbleViews::WindowClosing() {
+  if (controller_) {
+    controller_->OnBubbleClosed();
+    controller_ = nullptr;
+  }
+}
+
+LocalCardMigrationBubbleViews::~LocalCardMigrationBubbleViews() {}
+
+std::unique_ptr<views::View>
+LocalCardMigrationBubbleViews::CreateMainContentView() {
+  std::unique_ptr<views::View> view = std::make_unique<views::View>();
+  return view;
+}
+
+void LocalCardMigrationBubbleViews::Init() {
+  SetLayoutManager(
+      std::make_unique<views::BoxLayout>(views::BoxLayout::kVertical));
+  AddChildView(CreateMainContentView().release());
+}
+
+}  // namespace autofill
\ No newline at end of file
diff --git a/chrome/browser/ui/views/autofill/local_card_migration_bubble_views.h b/chrome/browser/ui/views/autofill/local_card_migration_bubble_views.h
new file mode 100644
index 0000000..3338ff0
--- /dev/null
+++ b/chrome/browser/ui/views/autofill/local_card_migration_bubble_views.h
@@ -0,0 +1,63 @@
+// Copyright 2018 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_VIEWS_AUTOFILL_LOCAL_CARD_MIGRATION_BUBBLE_VIEWS_H_
+#define CHROME_BROWSER_UI_VIEWS_AUTOFILL_LOCAL_CARD_MIGRATION_BUBBLE_VIEWS_H_
+
+#include "base/macros.h"
+#include "chrome/browser/ui/autofill/local_card_migration_bubble.h"
+#include "chrome/browser/ui/views/location_bar/location_bar_bubble_delegate_view.h"
+#include "components/autofill/core/browser/ui/local_card_migration_bubble_controller.h"
+
+namespace content {
+class WebContents;
+}
+
+namespace autofill {
+
+// Class responsible for showing the local card migration bubble which is
+// the entry point of the entire migration flow.
+class LocalCardMigrationBubbleViews : public LocalCardMigrationBubble,
+                                      public LocationBarBubbleDelegateView {
+ public:
+  // The |controller| is lazily initialized in ChromeAutofillClient and there
+  // should be only one controller per tab after the initialization. It should
+  // live even when bubble is gone.
+  LocalCardMigrationBubbleViews(views::View* anchor_view,
+                                const gfx::Point& anchor_point,
+                                content::WebContents* web_contents,
+                                LocalCardMigrationBubbleController* controller);
+
+  void Show(DisplayReason reason);
+
+  // LocalCardMigrationBubble:
+  void Hide() override;
+
+  // LocationBarBubbleDelegateView:
+  bool Accept() override;
+  bool Cancel() override;
+  bool Close() override;
+  int GetDialogButtons() const override;
+  base::string16 GetDialogButtonLabel(ui::DialogButton button) const override;
+  gfx::Size CalculatePreferredSize() const override;
+  bool ShouldShowCloseButton() const override;
+  base::string16 GetWindowTitle() const override;
+  void WindowClosing() override;
+
+ private:
+  ~LocalCardMigrationBubbleViews() override;
+
+  std::unique_ptr<views::View> CreateMainContentView();
+
+  // views::BubbleDialogDelegateView:
+  void Init() override;
+
+  LocalCardMigrationBubbleController* controller_;
+
+  DISALLOW_COPY_AND_ASSIGN(LocalCardMigrationBubbleViews);
+};
+
+}  // namespace autofill
+
+#endif  // CHROME_BROWSER_UI_VIEWS_AUTOFILL_LOCAL_CARD_MIGRATION_BUBBLE_VIEWS_H_
\ No newline at end of file
diff --git a/chrome/browser/ui/views/autofill/local_card_migration_icon_view.cc b/chrome/browser/ui/views/autofill/local_card_migration_icon_view.cc
new file mode 100644
index 0000000..2afbb86
--- /dev/null
+++ b/chrome/browser/ui/views/autofill/local_card_migration_icon_view.cc
@@ -0,0 +1,81 @@
+// Copyright 2018 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/autofill/local_card_migration_icon_view.h"
+
+#include "chrome/app/chrome_command_ids.h"
+#include "chrome/app/vector_icons/vector_icons.h"
+#include "chrome/browser/ui/autofill/local_card_migration_bubble_controller_impl.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_command_controller.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/view_ids.h"
+#include "chrome/browser/ui/views/autofill/local_card_migration_bubble_views.h"
+#include "chrome/grit/generated_resources.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace autofill {
+
+LocalCardMigrationIconView::LocalCardMigrationIconView(
+    CommandUpdater* command_updater,
+    Browser* browser,
+    PageActionIconView::Delegate* delegate)
+    : PageActionIconView(command_updater,
+                         IDC_MIGRATE_LOCAL_CREDIT_CARD_FOR_PAGE,
+                         delegate),
+      browser_(browser) {
+  DCHECK(delegate);
+  set_id(VIEW_ID_MIGRATE_LOCAL_CREDIT_CARD_BUTTON);
+}
+
+LocalCardMigrationIconView::~LocalCardMigrationIconView() {}
+
+views::BubbleDialogDelegateView* LocalCardMigrationIconView::GetBubble() const {
+  LocalCardMigrationBubbleControllerImpl* controller = GetController();
+  if (!controller)
+    return nullptr;
+
+  return static_cast<autofill::LocalCardMigrationBubbleViews*>(
+      controller->local_card_migration_bubble_view());
+}
+
+bool LocalCardMigrationIconView::Update() {
+  if (!GetWebContents())
+    return false;
+
+  const bool was_visible = visible();
+
+  // |controller| may be nullptr due to lazy initialization.
+  LocalCardMigrationBubbleControllerImpl* controller = GetController();
+  bool enabled = controller && controller->IsIconVisible();
+  enabled &= SetCommandEnabled(enabled);
+  SetVisible(enabled);
+  return was_visible != visible();
+}
+
+void LocalCardMigrationIconView::OnExecuting(
+    PageActionIconView::ExecuteSource execute_source) {}
+
+const gfx::VectorIcon& LocalCardMigrationIconView::GetVectorIcon() const {
+  return kCreditCardIcon;
+}
+
+base::string16 LocalCardMigrationIconView::GetTextForTooltipAndAccessibleName()
+    const {
+  return l10n_util::GetStringUTF16(IDS_TOOLTIP_MIGRATE_LOCAL_CARD);
+}
+
+LocalCardMigrationBubbleControllerImpl*
+LocalCardMigrationIconView::GetController() const {
+  if (!browser_)
+    return nullptr;
+
+  content::WebContents* web_contents = GetWebContents();
+  if (!web_contents)
+    return nullptr;
+
+  return autofill::LocalCardMigrationBubbleControllerImpl::FromWebContents(
+      web_contents);
+}
+
+}  // namespace autofill
diff --git a/chrome/browser/ui/views/autofill/local_card_migration_icon_view.h b/chrome/browser/ui/views/autofill/local_card_migration_icon_view.h
new file mode 100644
index 0000000..f87a170
--- /dev/null
+++ b/chrome/browser/ui/views/autofill/local_card_migration_icon_view.h
@@ -0,0 +1,48 @@
+// Copyright 2018 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_VIEWS_AUTOFILL_LOCAL_CARD_MIGRATION_ICON_VIEW_H_
+#define CHROME_BROWSER_UI_VIEWS_AUTOFILL_LOCAL_CARD_MIGRATION_ICON_VIEW_H_
+
+#include "base/macros.h"
+#include "chrome/browser/ui/views/page_action/page_action_icon_view.h"
+
+class Browser;
+class CommandUpdater;
+
+namespace autofill {
+
+class LocalCardMigrationBubbleControllerImpl;
+
+// The icon shown in location bar for the intermediate local card migration
+// bubble.
+class LocalCardMigrationIconView : public PageActionIconView {
+ public:
+  LocalCardMigrationIconView(CommandUpdater* command_updater,
+                             Browser* browser,
+                             PageActionIconView::Delegate* delegate);
+  ~LocalCardMigrationIconView() override;
+
+  // PageActionIconView:
+  views::BubbleDialogDelegateView* GetBubble() const override;
+  bool Update() override;
+  base::string16 GetTextForTooltipAndAccessibleName() const override;
+
+ protected:
+  // PageActionIconView:
+  void OnExecuting(PageActionIconView::ExecuteSource execute_source) override;
+  const gfx::VectorIcon& GetVectorIcon() const override;
+
+ private:
+  LocalCardMigrationBubbleControllerImpl* GetController() const;
+
+  // Used to do nullptr check when getting the controller.
+  Browser* const browser_;
+
+  DISALLOW_COPY_AND_ASSIGN(LocalCardMigrationIconView);
+};
+
+}  // namespace autofill
+
+#endif  // CHROME_BROWSER_UI_VIEWS_AUTOFILL_LOCAL_CARD_MIGRATION_ICON_VIEW_H_
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_unittest.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_unittest.cc
index 7714f62..62ecea9 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_unittest.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_unittest.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/ui/views/frame/browser_non_client_frame_view.h"
 
 #include "base/command_line.h"
+#include "chrome/browser/ui/layout_constants.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/frame/test_with_browser_view.h"
 #include "chrome/browser/ui/views/tabs/tab_strip.h"
@@ -73,9 +74,15 @@
 
   // Hits tab strip but not client area.
   EXPECT_TRUE(frame_view_->HitTestRect(
-      gfx::Rect(tabstrip_bounds.x() + 1, tabstrip_bounds.bottom() - 1, 1, 1)));
+      gfx::Rect(tabstrip_bounds.x() + 1,
+                tabstrip_bounds.bottom() -
+                    GetLayoutConstant(TABSTRIP_TOOLBAR_OVERLAP) - 1,
+                1, 1)));
 
   // Hits tab strip and client area.
-  EXPECT_TRUE(frame_view_->HitTestRect(gfx::Rect(
-      tabstrip_bounds.x() + 1, tabstrip_bounds.bottom() - 1, 100, 100)));
+  EXPECT_TRUE(frame_view_->HitTestRect(
+      gfx::Rect(tabstrip_bounds.x() + 1,
+                tabstrip_bounds.bottom() -
+                    GetLayoutConstant(TABSTRIP_TOOLBAR_OVERLAP) - 1,
+                100, 100)));
 }
diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc
index 3a6674d..498191f 100644
--- a/chrome/browser/ui/views/frame/browser_view.cc
+++ b/chrome/browser/ui/views/frame/browser_view.cc
@@ -42,6 +42,7 @@
 #include "chrome/browser/themes/theme_properties.h"
 #include "chrome/browser/themes/theme_service.h"
 #include "chrome/browser/translate/chrome_translate_client.h"
+#include "chrome/browser/ui/autofill/local_card_migration_bubble.h"
 #include "chrome/browser/ui/autofill/save_card_bubble_view.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_command_controller.h"
@@ -60,6 +61,8 @@
 #include "chrome/browser/ui/view_ids.h"
 #include "chrome/browser/ui/views/accelerator_table.h"
 #include "chrome/browser/ui/views/accessibility/invert_bubble_view.h"
+#include "chrome/browser/ui/views/autofill/local_card_migration_bubble_views.h"
+#include "chrome/browser/ui/views/autofill/local_card_migration_icon_view.h"
 #include "chrome/browser/ui/views/autofill/save_card_bubble_views.h"
 #include "chrome/browser/ui/views/autofill/save_card_icon_view.h"
 #include "chrome/browser/ui/views/bookmarks/bookmark_bar_view.h"
@@ -1234,6 +1237,7 @@
       anchor_view, gfx::Point(), web_contents, controller);
   views::Widget* bubble_widget =
       views::BubbleDialogDelegateView::CreateBubble(bubble);
+
   if (card_view)
     card_view->OnBubbleWidgetCreated(bubble_widget);
   bubble->Show(user_gesture ? autofill::SaveCardBubbleViews::USER_GESTURE
@@ -1241,6 +1245,35 @@
   return bubble;
 }
 
+autofill::LocalCardMigrationBubble* BrowserView::ShowLocalCardMigrationBubble(
+    content::WebContents* web_contents,
+    autofill::LocalCardMigrationBubbleController* controller,
+    bool user_gesture) {
+  LocationBarView* location_bar = GetLocationBarView();
+  PageActionIconView* card_view =
+      location_bar->local_card_migration_icon_view();
+
+  views::View* anchor_view = location_bar;
+  if (!ui::MaterialDesignController::IsSecondaryUiMaterial()) {
+    if (card_view && card_view->visible())
+      anchor_view = card_view;
+    else
+      anchor_view = toolbar_button_provider()->GetAppMenuButton();
+  }
+
+  autofill::LocalCardMigrationBubbleViews* bubble =
+      new autofill::LocalCardMigrationBubbleViews(anchor_view, gfx::Point(),
+                                                  web_contents, controller);
+  views::Widget* bubble_widget =
+      views::BubbleDialogDelegateView::CreateBubble(bubble);
+  if (card_view)
+    card_view->OnBubbleWidgetCreated(bubble_widget);
+  bubble->Show(user_gesture
+                   ? autofill::LocalCardMigrationBubbleViews::USER_GESTURE
+                   : autofill::LocalCardMigrationBubbleViews::AUTOMATIC);
+  return bubble;
+}
+
 ShowTranslateBubbleResult BrowserView::ShowTranslateBubble(
     content::WebContents* web_contents,
     translate::TranslateStep step,
diff --git a/chrome/browser/ui/views/frame/browser_view.h b/chrome/browser/ui/views/frame/browser_view.h
index 1dfa9f3..753e3e5 100644
--- a/chrome/browser/ui/views/frame/browser_view.h
+++ b/chrome/browser/ui/views/frame/browser_view.h
@@ -347,6 +347,10 @@
       content::WebContents* contents,
       autofill::SaveCardBubbleController* controller,
       bool is_user_gesture) override;
+  autofill::LocalCardMigrationBubble* ShowLocalCardMigrationBubble(
+      content::WebContents* contents,
+      autofill::LocalCardMigrationBubbleController* controller,
+      bool is_user_gesture) override;
   ShowTranslateBubbleResult ShowTranslateBubble(
       content::WebContents* contents,
       translate::TranslateStep step,
diff --git a/chrome/browser/ui/views/frame/browser_view_layout.cc b/chrome/browser/ui/views/frame/browser_view_layout.cc
index 47362ca..66ce192 100644
--- a/chrome/browser/ui/views/frame/browser_view_layout.cc
+++ b/chrome/browser/ui/views/frame/browser_view_layout.cc
@@ -12,6 +12,7 @@
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/find_bar/find_bar.h"
 #include "chrome/browser/ui/find_bar/find_bar_controller.h"
+#include "chrome/browser/ui/layout_constants.h"
 #include "chrome/browser/ui/views/bookmarks/bookmark_bar_view.h"
 #include "chrome/browser/ui/views/download/download_shelf_view.h"
 #include "chrome/browser/ui/views/exclusive_access_bubble_views.h"
@@ -403,7 +404,7 @@
   tab_strip_->SetVisible(true);
   tab_strip_->SetBoundsRect(tabstrip_bounds);
 
-  return tabstrip_bounds.bottom();
+  return tabstrip_bounds.bottom() - GetLayoutConstant(TABSTRIP_TOOLBAR_OVERLAP);
 }
 
 int BrowserViewLayout::LayoutToolbar(int top) {
diff --git a/chrome/browser/ui/views/frame/browser_view_unittest.cc b/chrome/browser/ui/views/frame/browser_view_unittest.cc
index 669c6f0..d3240b8 100644
--- a/chrome/browser/ui/views/frame/browser_view_unittest.cc
+++ b/chrome/browser/ui/views/frame/browser_view_unittest.cc
@@ -114,7 +114,9 @@
   EXPECT_EQ(expected_tabstrip_origin.x(), tabstrip->x());
   EXPECT_EQ(expected_tabstrip_origin.y(), tabstrip->y());
   EXPECT_EQ(0, toolbar->x());
-  EXPECT_EQ(tabstrip->bounds().bottom(), toolbar->y());
+  EXPECT_EQ(
+      tabstrip->bounds().bottom() - GetLayoutConstant(TABSTRIP_TOOLBAR_OVERLAP),
+      toolbar->y());
   EXPECT_EQ(0, contents_container->x());
   EXPECT_EQ(toolbar->bounds().bottom(), contents_container->y());
   EXPECT_EQ(top_container->bounds().bottom(), contents_container->y());
@@ -158,7 +160,9 @@
 
   // Bookmark bar layout on NTP.
   EXPECT_EQ(0, bookmark_bar->x());
-  EXPECT_EQ(tabstrip->bounds().bottom() + toolbar->height(), bookmark_bar->y());
+  EXPECT_EQ(tabstrip->bounds().bottom() + toolbar->height() -
+                GetLayoutConstant(TABSTRIP_TOOLBAR_OVERLAP),
+            bookmark_bar->y());
   EXPECT_EQ(toolbar->bounds().bottom(), contents_container->y());
   EXPECT_EQ(bookmark_bar->height(), devtools_web_view->y());
   EXPECT_EQ(bookmark_bar->height(), contents_web_view->y());
diff --git a/chrome/browser/ui/views/location_bar/location_bar_view.cc b/chrome/browser/ui/views/location_bar/location_bar_view.cc
index 23a2dedb..061f0b0 100644
--- a/chrome/browser/ui/views/location_bar/location_bar_view.cc
+++ b/chrome/browser/ui/views/location_bar/location_bar_view.cc
@@ -28,6 +28,7 @@
 #include "chrome/browser/themes/theme_service_factory.h"
 #include "chrome/browser/translate/chrome_translate_client.h"
 #include "chrome/browser/translate/translate_service.h"
+#include "chrome/browser/ui/autofill/local_card_migration_bubble_controller_impl.h"
 #include "chrome/browser/ui/autofill/save_card_bubble_controller_impl.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_finder.h"
@@ -41,6 +42,7 @@
 #include "chrome/browser/ui/omnibox/omnibox_theme.h"
 #include "chrome/browser/ui/passwords/manage_passwords_ui_controller.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/views/autofill/local_card_migration_icon_view.h"
 #include "chrome/browser/ui/views/autofill/save_card_icon_view.h"
 #include "chrome/browser/ui/views/chrome_platform_style.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
@@ -233,6 +235,11 @@
   }
   translate_icon_view_ = new TranslateIconView(command_updater(), this);
   page_action_icons_.push_back(translate_icon_view_);
+  if (browser_) {
+    local_card_migration_icon_view_ = new autofill::LocalCardMigrationIconView(
+        command_updater(), browser_, this);
+    page_action_icons_.push_back(local_card_migration_icon_view_);
+  }
 
 #if defined(OS_CHROMEOS)
   if (browser_)
@@ -434,6 +441,8 @@
   trailing_width += IncrementalMinimumWidth(translate_icon_view_);
   if (save_credit_card_icon_view_)
     trailing_width += IncrementalMinimumWidth(save_credit_card_icon_view_);
+  if (local_card_migration_icon_view_)
+    trailing_width += IncrementalMinimumWidth(local_card_migration_icon_view_);
   trailing_width += IncrementalMinimumWidth(manage_passwords_icon_view_) +
                     IncrementalMinimumWidth(page_action_icon_container_view_);
 #if defined(OS_CHROMEOS)
@@ -535,6 +544,8 @@
   add_trailing_decoration(translate_icon_view_);
   if (save_credit_card_icon_view_)
     add_trailing_decoration(save_credit_card_icon_view_);
+  if (local_card_migration_icon_view_)
+    add_trailing_decoration(local_card_migration_icon_view_);
   add_trailing_decoration(manage_passwords_icon_view_);
   for (ContentSettingViews::const_reverse_iterator i(
            content_setting_views_.rbegin());
@@ -1047,6 +1058,13 @@
   }
 }
 
+void LocationBarView::UpdateLocalCardMigrationIcon() {
+  if (local_card_migration_icon_view_->Update()) {
+    Layout();
+    SchedulePaint();
+  }
+}
+
 void LocationBarView::UpdateFindBarIconVisibility() {
   const bool visibility_changed = RefreshFindBarIcon();
   if (visibility_changed) {
diff --git a/chrome/browser/ui/views/location_bar/location_bar_view.h b/chrome/browser/ui/views/location_bar/location_bar_view.h
index 1455efc..88a5512 100644
--- a/chrome/browser/ui/views/location_bar/location_bar_view.h
+++ b/chrome/browser/ui/views/location_bar/location_bar_view.h
@@ -53,6 +53,7 @@
 class TranslateIconView;
 
 namespace autofill {
+class LocalCardMigrationIconView;
 class SaveCardIconView;
 }
 
@@ -164,6 +165,10 @@
     return save_credit_card_icon_view_;
   }
 
+  autofill::LocalCardMigrationIconView* local_card_migration_icon_view() {
+    return local_card_migration_icon_view_;
+  }
+
   // The translate icon. It may not be visible.
   TranslateIconView* translate_icon_view() { return translate_icon_view_; }
 
@@ -337,6 +342,7 @@
   void UpdateContentSettingsIcons() override;
   void UpdateManagePasswordsIconAndBubble() override;
   void UpdateSaveCreditCardIcon() override;
+  void UpdateLocalCardMigrationIcon() override;
   void UpdateFindBarIconVisibility() override;
   void UpdateBookmarkStarVisibility() override;
   void UpdateLocationBarVisibility(bool visible, bool animation) override;
@@ -431,6 +437,10 @@
   // The save credit card icon.  It will be null when |browser_| is null.
   autofill::SaveCardIconView* save_credit_card_icon_view_ = nullptr;
 
+  // The icon for the local card migration prompt.
+  autofill::LocalCardMigrationIconView* local_card_migration_icon_view_ =
+      nullptr;
+
   // The icon for Translate.
   TranslateIconView* translate_icon_view_ = nullptr;
 
diff --git a/chrome/browser/ui/views/tabs/new_tab_button.cc b/chrome/browser/ui/views/tabs/new_tab_button.cc
index 958a62b..0b832ef 100644
--- a/chrome/browser/ui/views/tabs/new_tab_button.cc
+++ b/chrome/browser/ui/views/tabs/new_tab_button.cc
@@ -118,6 +118,7 @@
   // on incognito or not, so we used `is_incognito=false`.
   const int extra_vertical_space =
       GetLayoutConstant(TAB_HEIGHT) -
+      GetLayoutConstant(TABSTRIP_TOOLBAR_OVERLAP) -
       GetLayoutSize(NEW_TAB_BUTTON, false /* is_incognito */).height();
 
   // In newer material UI, the button is placed vertically exactly in the
diff --git a/chrome/browser/ui/views/tabs/tab.cc b/chrome/browser/ui/views/tabs/tab.cc
index 61d5141..91746ea 100644
--- a/chrome/browser/ui/views/tabs/tab.cc
+++ b/chrome/browser/ui/views/tabs/tab.cc
@@ -219,7 +219,10 @@
   const float left = aligned_bounds.x();
   const float top = aligned_bounds.y() + Tab::GetStrokeHeight();
   const float right = aligned_bounds.right();
-  const float bottom = aligned_bounds.bottom();
+  const float extended_bottom = aligned_bounds.bottom();
+  const float bottom_extension =
+      GetLayoutConstant(TABSTRIP_TOOLBAR_OVERLAP) * scale;
+  const float bottom = extended_bottom - bottom_extension;
 
   // Construct the interior path by intersecting paths representing the left
   // and right halves of the tab.  Compared to computing the full path at once,
@@ -228,8 +231,8 @@
 
   // Bottom right.
   gfx::Path right_path;
-  right_path.moveTo(right, bottom);
-  right_path.rLineTo(-corner_gap, 0);
+  right_path.moveTo(right - corner_gap, extended_bottom);
+  right_path.rLineTo(0, -bottom_extension);
   right_path.arcTo(radius, radius, 0, SkPath::kSmall_ArcSize,
                    SkPath::kCW_Direction, right - extension, bottom - radius);
 
@@ -242,7 +245,7 @@
 
   // Top/bottom edges of right side.
   right_path.lineTo(left, top);
-  right_path.lineTo(left, bottom);
+  right_path.lineTo(left, extended_bottom);
   right_path.close();
 
   // Top left.
@@ -257,9 +260,10 @@
   // Bottom left.
   left_path.arcTo(radius, radius, 0, SkPath::kSmall_ArcSize,
                   SkPath::kCW_Direction, left + corner_gap, bottom);
+  left_path.rLineTo(0, bottom_extension);
 
   // Bottom/top edges of left side.
-  left_path.lineTo(right, bottom);
+  left_path.lineTo(right, extended_bottom);
   left_path.lineTo(right, top);
   left_path.close();
 
@@ -340,11 +344,15 @@
   const float left = aligned_bounds.x();
   const float top = aligned_bounds.y();
   const float right = aligned_bounds.right();
-  const float bottom = aligned_bounds.bottom();
+  const float extended_bottom = aligned_bounds.bottom();
+  const float bottom_extension =
+      GetLayoutConstant(TABSTRIP_TOOLBAR_OVERLAP) * scale;
+  const float bottom = extended_bottom - bottom_extension;
 
   // Bottom left.
   gfx::Path path;
-  path.moveTo(left, bottom);
+  path.moveTo(left, extended_bottom);
+  path.rLineTo(0, -bottom_extension);
   path.rLineTo(0, -stroke_thickness);
   path.rLineTo(corner_gap, 0);
   path.arcTo(outer_radius, outer_radius, 0, SkPath::kSmall_ArcSize,
@@ -382,6 +390,7 @@
              bottom - stroke_thickness);
   path.rLineTo(corner_gap, 0);
   path.rLineTo(0, stroke_thickness);
+  path.rLineTo(0, bottom_extension);
 
   // Bottom edge.
   path.close();
diff --git a/chrome/browser/ui/webui/chromeos/login/terms_of_service_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/terms_of_service_screen_handler.cc
index d2aa937..a1afa85 100644
--- a/chrome/browser/ui/webui/chromeos/login/terms_of_service_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/terms_of_service_screen_handler.cc
@@ -28,7 +28,6 @@
 #include "components/user_manager/user_manager.h"
 #include "content/public/browser/web_ui.h"
 #include "ui/base/ime/chromeos/input_method_manager.h"
-#include "ui/base/pref_names.h"
 
 namespace {
 
diff --git a/chrome/browser/ui/webui/settings/chromeos/google_assistant_handler.cc b/chrome/browser/ui/webui/settings/chromeos/google_assistant_handler.cc
index b543b61..8b3341f9 100644
--- a/chrome/browser/ui/webui/settings/chromeos/google_assistant_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/google_assistant_handler.cc
@@ -37,13 +37,6 @@
       base::BindRepeating(
           &GoogleAssistantHandler::HandleSetGoogleAssistantContextEnabled,
           base::Unretained(this)));
-  if (chromeos::switches::IsAssistantEnabled()) {
-    web_ui()->RegisterMessageCallback(
-        "setGoogleAssistantHotwordEnabled",
-        base::BindRepeating(
-            &GoogleAssistantHandler::HandleSetGoogleAssistantHotwordEnabled,
-            base::Unretained(this)));
-  }
   web_ui()->RegisterMessageCallback(
       "showGoogleAssistantSettings",
       base::BindRepeating(
@@ -79,17 +72,6 @@
     service->SetVoiceInteractionContextEnabled(enabled);
 }
 
-void GoogleAssistantHandler::HandleSetGoogleAssistantHotwordEnabled(
-    const base::ListValue* args) {
-  CHECK(chromeos::switches::IsAssistantEnabled());
-
-  CHECK_EQ(1U, args->GetSize());
-  bool enabled;
-  CHECK(args->GetBoolean(0, &enabled));
-
-  // TODO(b/110219351) Handle toggle hotword.
-}
-
 void GoogleAssistantHandler::HandleShowGoogleAssistantSettings(
     const base::ListValue* args) {
   auto* service =
diff --git a/chrome/browser/ui/webui/settings/chromeos/google_assistant_handler.h b/chrome/browser/ui/webui/settings/chromeos/google_assistant_handler.h
index 75bc27f..8ae9aab 100644
--- a/chrome/browser/ui/webui/settings/chromeos/google_assistant_handler.h
+++ b/chrome/browser/ui/webui/settings/chromeos/google_assistant_handler.h
@@ -27,8 +27,6 @@
   void HandleSetGoogleAssistantEnabled(const base::ListValue* args);
   // WebUI call to enable context for the Google Assistant.
   void HandleSetGoogleAssistantContextEnabled(const base::ListValue* args);
-  // WebUI call to enable hotword detection for the Google Assistant.
-  void HandleSetGoogleAssistantHotwordEnabled(const base::ListValue* args);
   // WebUI call to launch into the Google Assistant app settings.
   void HandleShowGoogleAssistantSettings(const base::ListValue* args);
   // WebUI call to launch assistant runtime flow.
diff --git a/chrome/browser/ui/webui/settings/languages_handler.cc b/chrome/browser/ui/webui/settings/languages_handler.cc
index 7ae56f1..5bd4cba 100644
--- a/chrome/browser/ui/webui/settings/languages_handler.cc
+++ b/chrome/browser/ui/webui/settings/languages_handler.cc
@@ -12,7 +12,6 @@
 #include "chrome/common/pref_names.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/browser/web_ui.h"
-#include "ui/base/pref_names.h"
 
 #if defined(OS_CHROMEOS)
 #include "chrome/browser/chromeos/base/locale_util.h"
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc
index b8ae525..c81dc1d 100644
--- a/chrome/common/pref_names.cc
+++ b/chrome/common/pref_names.cc
@@ -194,6 +194,8 @@
 const char kRlzPingDelaySeconds[] = "rlz_ping_delay";
 #endif  // BUILDFLAG(ENABLE_RLZ)
 
+// Important: Refer to header file for how to use this.
+const char kApplicationLocale[] = "intl.app_locale";
 #if defined(OS_CHROMEOS)
 // Locale preference of device' owner.  ChromeOS device appears in this locale
 // after startup/wakeup/signout.
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index 006b62a2..75538d8 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -62,6 +62,11 @@
 extern const char kRlzPingDelaySeconds[];
 #endif  // BUILDFLAG(ENABLE_RLZ)
 
+// The application locale.
+// DO NOT USE this locale directly: use language::ConverToActualLocale() after
+// reading it to get the system locale.
+// This pref stores the locale that the user selected, if applicable.
+extern const char kApplicationLocale[];
 // For OS_CHROMEOS we maintain the kApplicationLocale property in both local
 // state and the user's profile.  The global property determines the locale of
 // the login screen, while the user's profile determines their personal locale
diff --git a/chrome/service/service_process.cc b/chrome/service/service_process.cc
index 2ddde90..124f13c 100644
--- a/chrome/service/service_process.cc
+++ b/chrome/service/service_process.cc
@@ -51,7 +51,6 @@
 #include "net/url_request/url_fetcher.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/material_design/material_design_controller.h"
-#include "ui/base/pref_names.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/base/ui_base_switches.h"
 
diff --git a/chrome/test/base/ash_test_environment_chrome.cc b/chrome/test/base/ash_test_environment_chrome.cc
index 5ae2681..1635f68 100644
--- a/chrome/test/base/ash_test_environment_chrome.cc
+++ b/chrome/test/base/ash_test_environment_chrome.cc
@@ -5,6 +5,7 @@
 #include "chrome/test/base/ash_test_environment_chrome.h"
 
 #include "ash/test/ash_test_views_delegate.h"
+#include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h"
 #include "ui/views/views_delegate.h"
 
 AshTestEnvironmentChrome::AshTestEnvironmentChrome() {}
@@ -15,3 +16,12 @@
 AshTestEnvironmentChrome::CreateViewsDelegate() {
   return std::make_unique<ash::AshTestViewsDelegate>();
 }
+
+void AshTestEnvironmentChrome::TearDown() {
+  // If initialized, the KioskAppManager will register an observer to
+  // CrosSettings and will need to be destroyed before it. Having it destroyed
+  // as part of the teardown will avoid unexpected test failures.
+  chromeos::KioskAppManager::Shutdown();
+
+  ash::AshTestEnvironment::TearDown();
+}
diff --git a/chrome/test/base/ash_test_environment_chrome.h b/chrome/test/base/ash_test_environment_chrome.h
index 3f7391f..d95de04 100644
--- a/chrome/test/base/ash_test_environment_chrome.h
+++ b/chrome/test/base/ash_test_environment_chrome.h
@@ -15,6 +15,7 @@
 
   // AshTestEnvironment:
   std::unique_ptr<ash::AshTestViewsDelegate> CreateViewsDelegate() override;
+  void TearDown() override;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(AshTestEnvironmentChrome);
diff --git a/chrome/test/base/test_browser_window.cc b/chrome/test/base/test_browser_window.cc
index 5f72198..96b0db3 100644
--- a/chrome/test/base/test_browser_window.cc
+++ b/chrome/test/base/test_browser_window.cc
@@ -164,6 +164,14 @@
   return nullptr;
 }
 
+autofill::LocalCardMigrationBubble*
+TestBrowserWindow::ShowLocalCardMigrationBubble(
+    content::WebContents* contents,
+    autofill::LocalCardMigrationBubbleController* controller,
+    bool user_gesture) {
+  return nullptr;
+}
+
 bool TestBrowserWindow::IsDownloadShelfVisible() const {
   return false;
 }
diff --git a/chrome/test/base/test_browser_window.h b/chrome/test/base/test_browser_window.h
index 6d015ce7..c78fa1c 100644
--- a/chrome/test/base/test_browser_window.h
+++ b/chrome/test/base/test_browser_window.h
@@ -113,6 +113,10 @@
       content::WebContents* contents,
       autofill::SaveCardBubbleController* controller,
       bool user_gesture) override;
+  autofill::LocalCardMigrationBubble* ShowLocalCardMigrationBubble(
+      content::WebContents* contents,
+      autofill::LocalCardMigrationBubbleController* controller,
+      bool user_gesture) override;
   ShowTranslateBubbleResult ShowTranslateBubble(
       content::WebContents* contents,
       translate::TranslateStep step,
@@ -172,6 +176,7 @@
     void UpdateContentSettingsIcons() override {}
     void UpdateManagePasswordsIconAndBubble() override {}
     void UpdateSaveCreditCardIcon() override {}
+    void UpdateLocalCardMigrationIcon() override {}
     void UpdateFindBarIconVisibility() override {}
     void UpdateBookmarkStarVisibility() override {}
     void UpdateLocationBarVisibility(bool visible, bool animate) override {}
diff --git a/chrome/test/base/testing_browser_process.cc b/chrome/test/base/testing_browser_process.cc
index 1a946f8..1df7b45 100644
--- a/chrome/test/base/testing_browser_process.cc
+++ b/chrome/test/base/testing_browser_process.cc
@@ -432,7 +432,8 @@
   if (!tab_manager_) {
     tab_manager_ = std::make_unique<resource_coordinator::TabManager>();
     tab_lifecycle_unit_source_ =
-        std::make_unique<resource_coordinator::TabLifecycleUnitSource>();
+        std::make_unique<resource_coordinator::TabLifecycleUnitSource>(
+            tab_manager_->intervention_policy_database());
     tab_lifecycle_unit_source_->AddObserver(tab_manager_.get());
   }
   return tab_manager_.get();
diff --git a/chrome/test/data/webui/md_history/md_history_browsertest.js b/chrome/test/data/webui/md_history/md_history_browsertest.js
index 9a2857f..ab8cc3b 100644
--- a/chrome/test/data/webui/md_history/md_history_browsertest.js
+++ b/chrome/test/data/webui/md_history/md_history_browsertest.js
@@ -35,18 +35,6 @@
       // Wait for the top-level app element to be upgraded.
       return waitForAppUpgrade()
           .then(function() {
-            // <iron-list>#_maxPages controls the default number of "pages" of
-            // "physical" (i.e. DOM) elements to render. Some of these tests
-            // rely on rendering up to 3 "pages" of items, which was previously
-            // the default, changeed to 2 for performance reasons. TODO(dbeam):
-            // maybe trim down the number of items created in the tests? Or
-            // don't touch <iron-list>'s physical items as much?
-            Array.from(document.querySelectorAll('* /deep/ iron-list'))
-                .forEach(function(ironList) {
-                  ironList._maxPages = 3;
-                });
-          })
-          .then(function() {
             return md_history.ensureLazyLoaded();
           })
           .then(function() {
diff --git a/chrome/test/data/webui/settings/search_settings_test.js b/chrome/test/data/webui/settings/search_settings_test.js
index e46c03a..3d557ff 100644
--- a/chrome/test/data/webui/settings/search_settings_test.js
+++ b/chrome/test/data/webui/settings/search_settings_test.js
@@ -152,12 +152,20 @@
           <template>
             <button></button>
             <settings-section hidden-by-search>
-              <template is="dom-if" route-path="/myPath"
+              <!-- Test case were no-search is part of a data binding. -->
+              <template is="dom-if" route-path="/myPath0"
                   no-search="[[noSearch]]">
                 <settings-subpage associated-control="[[$$('button')]]">
                   ${text}
                 </settings-subpage>
               </template>
+
+              <!-- Test case were no-search is not part of any data binding.-->
+              <template is="dom-if" route-path="/myPath1" no-search>
+                <settings-subpage associated-control="[[$$('button')]]">
+                  ${text}
+                </settings-subpage>
+              </template>
             </settings-section>
           </template>
         </dom-module>
diff --git a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsView.java b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsView.java
index f86216c..36aee34 100644
--- a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsView.java
+++ b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsView.java
@@ -48,7 +48,6 @@
             return () -> {
                 layout.removeView(contentView);
                 layout.removeView(contentViewRenderView);
-                contentViewCore.destroy();
                 contentViewRenderView.destroy();
                 window.destroy();
             };
diff --git a/chromecast/browser/cast_content_window.h b/chromecast/browser/cast_content_window.h
index 2281d81..15e0ce1e 100644
--- a/chromecast/browser/cast_content_window.h
+++ b/chromecast/browser/cast_content_window.h
@@ -64,7 +64,7 @@
   HIDDEN = 5,
 };
 
-enum class GestureType { NO_GESTURE = 0, GO_BACK = 1, TAP = 2 };
+enum class GestureType { NO_GESTURE = 0, GO_BACK = 1, TAP = 2, TAP_DOWN = 3 };
 
 // Class that represents the "window" a WebContents is displayed in cast_shell.
 // For Linux, this represents an Aura window. For Android, this is a Activity.
diff --git a/chromecast/browser/cast_content_window_aura.cc b/chromecast/browser/cast_content_window_aura.cc
index f7a7ac0..3f21201 100644
--- a/chromecast/browser/cast_content_window_aura.cc
+++ b/chromecast/browser/cast_content_window_aura.cc
@@ -144,15 +144,20 @@
   gesture_dispatcher_->HandleSideSwipeContinue(swipe_origin, touch_location);
 }
 
-void CastContentWindowAura::HandleTapGesture(const gfx::Point& touch_location) {
-  gesture_dispatcher_->HandleTapGesture(touch_location);
-}
-
 void CastContentWindowAura::HandleSideSwipeEnd(
     CastSideSwipeOrigin swipe_origin,
     const gfx::Point& touch_location) {
   gesture_dispatcher_->HandleSideSwipeEnd(swipe_origin, touch_location);
 }
 
+void CastContentWindowAura::HandleTapDownGesture(
+    const gfx::Point& touch_location) {
+  gesture_dispatcher_->HandleTapDownGesture(touch_location);
+}
+
+void CastContentWindowAura::HandleTapGesture(const gfx::Point& touch_location) {
+  gesture_dispatcher_->HandleTapGesture(touch_location);
+}
+
 }  // namespace shell
 }  // namespace chromecast
diff --git a/chromecast/browser/cast_content_window_aura.h b/chromecast/browser/cast_content_window_aura.h
index f3e0b2c..9364cc9 100644
--- a/chromecast/browser/cast_content_window_aura.h
+++ b/chromecast/browser/cast_content_window_aura.h
@@ -44,6 +44,7 @@
                                const gfx::Point& touch_location) override;
   void HandleSideSwipeEnd(CastSideSwipeOrigin swipe_origin,
                           const gfx::Point& touch_location) override;
+  void HandleTapDownGesture(const gfx::Point& touch_location) override;
   void HandleTapGesture(const gfx::Point& touch_location) override;
 
  private:
diff --git a/chromecast/browser/cast_gesture_dispatcher.cc b/chromecast/browser/cast_gesture_dispatcher.cc
index e0a0cbf..2805679 100644
--- a/chromecast/browser/cast_gesture_dispatcher.cc
+++ b/chromecast/browser/cast_gesture_dispatcher.cc
@@ -77,6 +77,14 @@
   }
 }
 
+void CastGestureDispatcher::HandleTapDownGesture(
+    const gfx::Point& touch_location) {
+  if (!delegate_->CanHandleGesture(GestureType::TAP_DOWN)) {
+    return;
+  }
+  delegate_->ConsumeGesture(GestureType::TAP_DOWN);
+}
+
 void CastGestureDispatcher::HandleTapGesture(const gfx::Point& touch_location) {
   if (!delegate_->CanHandleGesture(GestureType::TAP)) {
     return;
diff --git a/chromecast/browser/cast_gesture_dispatcher.h b/chromecast/browser/cast_gesture_dispatcher.h
index 16a82f3..50b1ae13 100644
--- a/chromecast/browser/cast_gesture_dispatcher.h
+++ b/chromecast/browser/cast_gesture_dispatcher.h
@@ -28,6 +28,7 @@
                                const gfx::Point& touch_location) override;
   void HandleSideSwipeEnd(CastSideSwipeOrigin swipe_origin,
                           const gfx::Point& touch_location) override;
+  void HandleTapDownGesture(const gfx::Point& touch_location) override;
   void HandleTapGesture(const gfx::Point& touch_location) override;
 
  private:
diff --git a/chromecast/graphics/cast_gesture_handler.h b/chromecast/graphics/cast_gesture_handler.h
index 158d522..4b23f92 100644
--- a/chromecast/graphics/cast_gesture_handler.h
+++ b/chromecast/graphics/cast_gesture_handler.h
@@ -37,7 +37,12 @@
   virtual void HandleSideSwipeEnd(CastSideSwipeOrigin swipe_origin,
                                   const gfx::Point& touch_location) {}
 
-  // Triggered on the completion of a tap event.
+  // Triggered on the completion of a tap down event, fired when the
+  // finger is pressed.
+  virtual void HandleTapDownGesture(const gfx::Point& touch_location) {}
+
+  // Triggered on the completion of a tap event, fire after a press
+  // followed by a release, within the tap timeout window
   virtual void HandleTapGesture(const gfx::Point& touch_location) {}
 
  private:
diff --git a/chromecast/graphics/cast_system_gesture_event_handler.cc b/chromecast/graphics/cast_system_gesture_event_handler.cc
index bbdd323..adaa628 100644
--- a/chromecast/graphics/cast_system_gesture_event_handler.cc
+++ b/chromecast/graphics/cast_system_gesture_event_handler.cc
@@ -149,8 +149,7 @@
 
 void CastSystemGestureEventHandler::OnGestureEvent(ui::GestureEvent* event) {
   if (event->type() == ui::ET_GESTURE_TAP ||
-      event->type() == ui::ET_GESTURE_TAP_DOWN ||
-      event->type() == ui::ET_GESTURE_LONG_PRESS) {
+      event->type() == ui::ET_GESTURE_TAP_DOWN) {
     ProcessPressedEvent(event);
   }
 }
@@ -161,9 +160,21 @@
     return;
   }
   gfx::Point touch_location(event->location());
-  for (auto* gesture_handler : gesture_handlers_) {
-    // Let the subscriber know about the gesture begin.
-    gesture_handler->HandleTapGesture(touch_location);
+  // Let the subscriber know about the gesture begin.
+  switch (event->type()) {
+    case ui::ET_GESTURE_TAP_DOWN: {
+      for (auto* gesture_handler : gesture_handlers_) {
+        gesture_handler->HandleTapDownGesture(touch_location);
+      }
+      break;
+    }
+    case ui::ET_GESTURE_TAP: {
+      for (auto* gesture_handler : gesture_handlers_) {
+        gesture_handler->HandleTapGesture(touch_location);
+      }
+      break;
+    }
+    default: { return; }
   }
 }
 
diff --git a/chromeos/components/proximity_auth/bluetooth_low_energy_connection_finder_unittest.cc b/chromeos/components/proximity_auth/bluetooth_low_energy_connection_finder_unittest.cc
index 187a9fc..1c3dd7b 100644
--- a/chromeos/components/proximity_auth/bluetooth_low_energy_connection_finder_unittest.cc
+++ b/chromeos/components/proximity_auth/bluetooth_low_energy_connection_finder_unittest.cc
@@ -206,9 +206,10 @@
     uuid_list.push_back(advertisement_uuid);
     device::BluetoothDevice::ServiceDataMap service_data_map;
     service_data_map[advertisement_uuid] = eid_vector;
-    device_->UpdateAdvertisementData(
-        kRssi, uuid_list, service_data_map, {} /* manufacturer_data */,
-        nullptr /* tx_power */, nullptr /* flags */);
+    device_->UpdateAdvertisementData(kRssi, base::nullopt /* flags */,
+                                     uuid_list, base::nullopt /* tx_power */,
+                                     service_data_map,
+                                     {} /* manufacturer_data */);
   }
 
   scoped_refptr<device::MockBluetoothAdapter> adapter_;
diff --git a/chromeos/components/proximity_auth/webui/proximity_auth_webui_handler.cc b/chromeos/components/proximity_auth/webui/proximity_auth_webui_handler.cc
index 3daab58..e16b6c2 100644
--- a/chromeos/components/proximity_auth/webui/proximity_auth_webui_handler.cc
+++ b/chromeos/components/proximity_auth/webui/proximity_auth_webui_handler.cc
@@ -616,9 +616,14 @@
                         device_info.friendly_device_name());
   dictionary->SetString(kExternalDeviceBluetoothAddress,
                         device_info.bluetooth_address());
-  dictionary->SetBoolean(kExternalDeviceUnlockKey, device_info.unlock_key());
-  dictionary->SetBoolean(kExternalDeviceMobileHotspot,
-                         device_info.mobile_hotspot_supported());
+  dictionary->SetBoolean(
+      kExternalDeviceUnlockKey,
+      base::ContainsValue(device_info.enabled_software_features(),
+                          cryptauth::SoftwareFeature::EASY_UNLOCK_HOST));
+  dictionary->SetBoolean(
+      kExternalDeviceMobileHotspot,
+      base::ContainsValue(device_info.supported_software_features(),
+                          cryptauth::SoftwareFeature::MAGIC_TETHER_HOST));
   dictionary->SetBoolean(kExternalDeviceIsArcPlusPlusEnrollment,
                          device_info.arc_plus_plus());
   dictionary->SetBoolean(kExternalDeviceIsPixelPhone,
@@ -691,9 +696,14 @@
   dictionary->SetString(kExternalDevicePublicKeyTruncated,
                         remote_device.GetTruncatedDeviceIdForLogs());
   dictionary->SetString(kExternalDeviceFriendlyName, remote_device.name());
-  dictionary->SetBoolean(kExternalDeviceUnlockKey, remote_device.unlock_key());
+  dictionary->SetBoolean(kExternalDeviceUnlockKey,
+                         remote_device.GetSoftwareFeatureState(
+                             cryptauth::SoftwareFeature::EASY_UNLOCK_HOST) ==
+                             cryptauth::SoftwareFeatureState::kEnabled);
   dictionary->SetBoolean(kExternalDeviceMobileHotspot,
-                         remote_device.supports_mobile_hotspot());
+                         remote_device.GetSoftwareFeatureState(
+                             cryptauth::SoftwareFeature::MAGIC_TETHER_HOST) ==
+                             cryptauth::SoftwareFeatureState::kSupported);
   dictionary->SetString(kExternalDeviceConnectionStatus,
                         kExternalDeviceDisconnected);
 
diff --git a/chromeos/components/tether/tether_host_fetcher_impl.cc b/chromeos/components/tether/tether_host_fetcher_impl.cc
index 7ac1910..e34c900 100644
--- a/chromeos/components/tether/tether_host_fetcher_impl.cc
+++ b/chromeos/components/tether/tether_host_fetcher_impl.cc
@@ -92,13 +92,19 @@
 
   if (base::FeatureList::IsEnabled(chromeos::features::kMultiDeviceApi)) {
     for (const auto& remote_device : device_sync_client_->GetSyncedDevices()) {
-      if (remote_device.supports_mobile_hotspot())
+      if (remote_device.GetSoftwareFeatureState(
+              cryptauth::SoftwareFeature::MAGIC_TETHER_HOST) ==
+          cryptauth::SoftwareFeatureState::kSupported)
         updated_list.push_back(remote_device);
     }
   } else {
     for (const auto& remote_device :
          remote_device_provider_->GetSyncedDevices()) {
-      if (remote_device.supports_mobile_hotspot) {
+      if (base::ContainsKey(remote_device.software_features,
+                            cryptauth::SoftwareFeature::MAGIC_TETHER_HOST) &&
+          remote_device.software_features.at(
+              cryptauth::SoftwareFeature::MAGIC_TETHER_HOST) ==
+              cryptauth::SoftwareFeatureState::kSupported) {
         updated_list.push_back(cryptauth::RemoteDeviceRef(
             std::make_shared<cryptauth::RemoteDevice>(remote_device)));
       }
diff --git a/chromeos/components/tether/tether_host_fetcher_impl_unittest.cc b/chromeos/components/tether/tether_host_fetcher_impl_unittest.cc
index ad2ca6b..1b4a078 100644
--- a/chromeos/components/tether/tether_host_fetcher_impl_unittest.cc
+++ b/chromeos/components/tether/tether_host_fetcher_impl_unittest.cc
@@ -116,8 +116,10 @@
   cryptauth::RemoteDeviceList CreateTestRemoteDeviceList() {
     cryptauth::RemoteDeviceList list =
         cryptauth::CreateRemoteDeviceListForTest(kNumTestDevices);
-    for (auto& device : list)
-      device.supports_mobile_hotspot = true;
+    for (auto& device : list) {
+      device.software_features[cryptauth::SoftwareFeature::MAGIC_TETHER_HOST] =
+          cryptauth::SoftwareFeatureState::kSupported;
+    }
 
     return list;
   }
@@ -195,7 +197,9 @@
     // Now, set another device as the only device, but remove its mobile data
     // support. It should not be returned.
     cryptauth::RemoteDevice remote_device = cryptauth::RemoteDevice();
-    remote_device.supports_mobile_hotspot = false;
+    remote_device
+        .software_features[cryptauth::SoftwareFeature::MAGIC_TETHER_HOST] =
+        cryptauth::SoftwareFeatureState::kNotSupported;
 
     SetSyncedDevices(cryptauth::RemoteDeviceList{remote_device});
     NotifyNewDevicesSynced();
@@ -214,9 +218,12 @@
 
     // Create a list of test devices, only some of which are valid tether hosts.
     // Ensure that only that subset is fetched.
-
-    test_remote_device_list_[3].supports_mobile_hotspot = false;
-    test_remote_device_list_[4].supports_mobile_hotspot = false;
+    test_remote_device_list_[3]
+        .software_features[cryptauth::SoftwareFeature::MAGIC_TETHER_HOST] =
+        cryptauth::SoftwareFeatureState::kNotSupported;
+    test_remote_device_list_[4]
+        .software_features[cryptauth::SoftwareFeature::MAGIC_TETHER_HOST] =
+        cryptauth::SoftwareFeatureState::kNotSupported;
 
     cryptauth::RemoteDeviceRefList host_device_list(
         CreateTestRemoteDeviceRefList({test_remote_device_list_[0],
diff --git a/chromeos/cryptohome/async_method_caller.cc b/chromeos/cryptohome/async_method_caller.cc
index 4d6acc1..c9956d8 100644
--- a/chromeos/cryptohome/async_method_caller.cc
+++ b/chromeos/cryptohome/async_method_caller.cc
@@ -35,13 +35,6 @@
     DBusThreadManager::Get()->GetCryptohomeClient()->RemoveObserver(this);
   }
 
-  void AsyncMountGuest(Callback callback) override {
-    DBusThreadManager::Get()->GetCryptohomeClient()->AsyncMountGuest(
-        base::BindOnce(&AsyncMethodCallerImpl::RegisterAsyncCallback,
-                       weak_ptr_factory_.GetWeakPtr(), callback,
-                       "Couldn't initiate async mount of cryptohome."));
-  }
-
   void AsyncRemove(const Identification& cryptohome_id,
                    Callback callback) override {
     DBusThreadManager::Get()->GetCryptohomeClient()->AsyncRemove(
@@ -154,19 +147,6 @@
                 "Couldn't initiate async attestation simple challenge."));
   }
 
-  void AsyncGetSanitizedUsername(const Identification& cryptohome_id,
-                                 const DataCallback& callback) override {
-    DBusThreadManager::Get()->GetCryptohomeClient()->GetSanitizedUsername(
-        cryptohome_id,
-        base::BindOnce(&AsyncMethodCallerImpl::GetSanitizedUsernameCallback,
-                       weak_ptr_factory_.GetWeakPtr(), callback));
-  }
-
-  void GetSanitizedUsernameCallback(const DataCallback& callback,
-                                    base::Optional<std::string> result) {
-    callback.Run(true, result.value_or(std::string()));
-  }
-
  private:
   struct CallbackElement {
     CallbackElement() = default;
diff --git a/chromeos/cryptohome/async_method_caller.h b/chromeos/cryptohome/async_method_caller.h
index 2e1c065..9a4558e 100644
--- a/chromeos/cryptohome/async_method_caller.h
+++ b/chromeos/cryptohome/async_method_caller.h
@@ -33,10 +33,6 @@
 
   virtual ~AsyncMethodCaller() {}
 
-  // Asks cryptohomed to asynchronously to mount a tmpfs for guest mode.
-  // |callback| will be called with status info on completion.
-  virtual void AsyncMountGuest(Callback callback) = 0;
-
   // Asks cryptohomed to asynchronously try to find the cryptohome for
   // |user_id| and then nuke it.
   virtual void AsyncRemove(const Identification& user_id,
@@ -126,12 +122,6 @@
       const std::string& challenge,
       const DataCallback& callback) = 0;
 
-  // Asks cryptohome to asynchronously retrieve a string associated with given
-  // |user_id| that would be used in mount path instead of |user_id|.
-  // On success the data is sent to |callback|.
-  virtual void AsyncGetSanitizedUsername(const Identification& user_id,
-                                         const DataCallback& callback) = 0;
-
   // Creates the global AsyncMethodCaller instance.
   static void Initialize();
 
diff --git a/chromeos/cryptohome/mock_async_method_caller.cc b/chromeos/cryptohome/mock_async_method_caller.cc
index 3729d63..69fc7c8 100644
--- a/chromeos/cryptohome/mock_async_method_caller.cc
+++ b/chromeos/cryptohome/mock_async_method_caller.cc
@@ -26,9 +26,6 @@
 void MockAsyncMethodCaller::SetUp(bool success, MountError return_code) {
   success_ = success;
   return_code_ = return_code;
-  ON_CALL(*this, AsyncMountGuest(_))
-      .WillByDefault(
-          WithArgs<0>(Invoke(this, &MockAsyncMethodCaller::DoCallback)));
   ON_CALL(*this, AsyncRemove(_, _))
       .WillByDefault(
           WithArgs<1>(Invoke(this, &MockAsyncMethodCaller::DoCallback)));
@@ -47,11 +44,6 @@
       .WillByDefault(
           WithArgs<4>(Invoke(this,
                              &MockAsyncMethodCaller::FakeFinishCertRequest)));
-  ON_CALL(*this, AsyncGetSanitizedUsername(_, _))
-      .WillByDefault(
-          WithArgs<1>(Invoke(this,
-                             &MockAsyncMethodCaller::
-                                 FakeGetSanitizedUsername)));
   ON_CALL(*this, TpmAttestationSignEnterpriseChallenge(_, _, _, _, _, _, _, _))
       .WillByDefault(
           WithArgs<7>(Invoke(this,
diff --git a/chromeos/cryptohome/mock_async_method_caller.h b/chromeos/cryptohome/mock_async_method_caller.h
index 8783b84..4df7c15 100644
--- a/chromeos/cryptohome/mock_async_method_caller.h
+++ b/chromeos/cryptohome/mock_async_method_caller.h
@@ -28,7 +28,6 @@
 
   void SetUp(bool success, MountError return_code);
 
-  MOCK_METHOD1(AsyncMountGuest, void(Callback callback));
   MOCK_METHOD2(AsyncRemove,
                void(const Identification& user_id, Callback callback));
   MOCK_METHOD2(AsyncTpmAttestationCreateEnrollRequest,
@@ -71,10 +70,6 @@
                     const std::string& key_name,
                     const std::string& challenge,
                     const DataCallback& callback));
-  MOCK_METHOD2(AsyncGetSanitizedUsername,
-               void(const Identification& user_id,
-                    const DataCallback& callback));
-
  private:
   bool success_;
   MountError return_code_;
diff --git a/chromeos/dbus/cryptohome_client.cc b/chromeos/dbus/cryptohome_client.cc
index e997a8a..f7dac99 100644
--- a/chromeos/dbus/cryptohome_client.cc
+++ b/chromeos/dbus/cryptohome_client.cc
@@ -217,12 +217,17 @@
   }
 
   // CryptohomeClient override.
-  void AsyncMountGuest(AsyncMethodCallback callback) override {
+  void MountGuestEx(
+      const cryptohome::MountGuestRequest& request,
+      DBusMethodCallback<cryptohome::BaseReply> callback) override {
     dbus::MethodCall method_call(cryptohome::kCryptohomeInterface,
-                                 cryptohome::kCryptohomeAsyncMountGuest);
+                                 cryptohome::kCryptohomeMountGuestEx);
+    dbus::MessageWriter writer(&method_call);
+    writer.AppendProtoAsArrayOfBytes(request);
+
     proxy_->CallMethod(
         &method_call, kTpmDBusTimeoutMs,
-        base::BindOnce(&CryptohomeClientImpl::OnAsyncMethodCall,
+        base::BindOnce(&CryptohomeClientImpl::OnBaseReplyMethod,
                        weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
   }
 
diff --git a/chromeos/dbus/cryptohome_client.h b/chromeos/dbus/cryptohome_client.h
index 366b714..705cddd 100644
--- a/chromeos/dbus/cryptohome_client.h
+++ b/chromeos/dbus/cryptohome_client.h
@@ -31,6 +31,7 @@
 class GetSupportedKeyPoliciesRequest;
 class MigrateKeyRequest;
 class MigrateToDircryptoRequest;
+class MountGuestRequest;
 class MountRequest;
 class RemoveFirmwareManagementParametersRequest;
 class RemoveKeyRequest;
@@ -195,9 +196,11 @@
   virtual std::string BlockingGetSanitizedUsername(
       const cryptohome::Identification& cryptohome_id) = 0;
 
-  // Calls AsyncMountGuest method.  |callback| is called after the method call
+  // Calls MountGuestEx method. |callback| is called after the method call
   // succeeds.
-  virtual void AsyncMountGuest(AsyncMethodCallback callback) = 0;
+  virtual void MountGuestEx(
+      const cryptohome::MountGuestRequest& request,
+      DBusMethodCallback<cryptohome::BaseReply> callback) = 0;
 
   // Calls TpmIsReady method.
   virtual void TpmIsReady(DBusMethodCallback<bool> callback) = 0;
diff --git a/chromeos/dbus/fake_cryptohome_client.cc b/chromeos/dbus/fake_cryptohome_client.cc
index 707083f..5e9e898 100644
--- a/chromeos/dbus/fake_cryptohome_client.cc
+++ b/chromeos/dbus/fake_cryptohome_client.cc
@@ -146,8 +146,10 @@
                                : std::string();
 }
 
-void FakeCryptohomeClient::AsyncMountGuest(AsyncMethodCallback callback) {
-  ReturnAsyncMethodResult(std::move(callback));
+void FakeCryptohomeClient::MountGuestEx(
+    const cryptohome::MountGuestRequest& request,
+    DBusMethodCallback<cryptohome::BaseReply> callback) {
+  ReturnProtobufMethodCallback(cryptohome::BaseReply(), std::move(callback));
 }
 
 void FakeCryptohomeClient::TpmIsReady(DBusMethodCallback<bool> callback) {
diff --git a/chromeos/dbus/fake_cryptohome_client.h b/chromeos/dbus/fake_cryptohome_client.h
index e17bdc2..9ab6b6f 100644
--- a/chromeos/dbus/fake_cryptohome_client.h
+++ b/chromeos/dbus/fake_cryptohome_client.h
@@ -57,7 +57,9 @@
                             DBusMethodCallback<std::string> callback) override;
   std::string BlockingGetSanitizedUsername(
       const cryptohome::Identification& cryptohome_id) override;
-  void AsyncMountGuest(AsyncMethodCallback callback) override;
+  void MountGuestEx(
+      const cryptohome::MountGuestRequest& request,
+      DBusMethodCallback<cryptohome::BaseReply> callback) override;
   void TpmIsReady(DBusMethodCallback<bool> callback) override;
   void TpmIsEnabled(DBusMethodCallback<bool> callback) override;
   bool CallTpmIsEnabledAndBlock(bool* enabled) override;
diff --git a/chromeos/login/auth/cryptohome_authenticator.cc b/chromeos/login/auth/cryptohome_authenticator.cc
index 1f28af3..f9eddf9 100644
--- a/chromeos/login/auth/cryptohome_authenticator.cc
+++ b/chromeos/login/auth/cryptohome_authenticator.cc
@@ -121,12 +121,21 @@
   resolver->Resolve();
 }
 
-// Callback invoked when cryptohome's MigrateKeyEx() method has finished.
-void OnMigrate(const base::WeakPtr<AuthAttemptState>& attempt,
-               scoped_refptr<CryptohomeAuthenticator> resolver,
-               base::Optional<cryptohome::BaseReply> reply) {
-  chromeos::LoginEventRecorder::Get()->AddLoginTimeMarker(
-      "CryptohomeMigrate-End", false);
+// Callback invoked when cryptohome's GetSantiziedUsername() method has
+// finished.
+void OnGetSanitizedUsername(
+    base::OnceCallback<void(bool, const std::string&)> callback,
+    base::Optional<std::string> result) {
+  std::move(callback).Run(result.has_value(), result.value_or(std::string()));
+}
+
+// Callback invoked when a crypotyhome *Ex method, which only returns a
+// base::Reply, finishes.
+void OnBaseReplyMethod(const base::WeakPtr<AuthAttemptState>& attempt,
+                       scoped_refptr<CryptohomeAuthenticator> resolver,
+                       const std::string& time_marker,
+                       base::Optional<cryptohome::BaseReply> reply) {
+  chromeos::LoginEventRecorder::Get()->AddLoginTimeMarker(time_marker, false);
   attempt->RecordCryptohomeStatus(BaseReplyToMountError(reply));
   resolver->Resolve();
 }
@@ -412,15 +421,19 @@
 // cryptohome.
 void MountGuestAndGetHash(const base::WeakPtr<AuthAttemptState>& attempt,
                           scoped_refptr<CryptohomeAuthenticator> resolver) {
+  chromeos::LoginEventRecorder::Get()->AddLoginTimeMarker(
+      "CryptohomeMountGuest-Start", false);
   attempt->UsernameHashRequested();
-  cryptohome::AsyncMethodCaller::GetInstance()->AsyncMountGuest(
-      base::Bind(&TriggerResolveWithLoginTimeMarker,
-                 "CryptohomeMount-End",
-                 attempt,
-                 resolver));
-  cryptohome::AsyncMethodCaller::GetInstance()->AsyncGetSanitizedUsername(
+
+  DBusThreadManager::Get()->GetCryptohomeClient()->MountGuestEx(
+      cryptohome::MountGuestRequest(),
+      base::BindOnce(&OnBaseReplyMethod, attempt, resolver,
+                     "CryptohomeMountGuest-End"));
+
+  DBusThreadManager::Get()->GetCryptohomeClient()->GetSanitizedUsername(
       cryptohome::Identification(attempt->user_context.GetAccountId()),
-      base::Bind(&TriggerResolveHash, attempt, resolver));
+      base::BindOnce(&OnGetSanitizedUsername,
+                     base::BindOnce(&TriggerResolveHash, attempt, resolver)));
 }
 
 // Calls cryptohome's MountEx method with the public_mount option.
@@ -479,7 +492,8 @@
 
   DBusThreadManager::Get()->GetCryptohomeClient()->MigrateKeyEx(
       account_id, auth_request, migrate_request,
-      base::BindOnce(&OnMigrate, attempt, resolver));
+      base::BindOnce(&OnBaseReplyMethod, attempt, resolver,
+                     "CryptohomeMigrate-End"));
 }
 
 // Calls cryptohome's remove method.
diff --git a/chromeos/services/assistant/assistant_manager_service_impl.cc b/chromeos/services/assistant/assistant_manager_service_impl.cc
index 8152598..70a1b8b 100644
--- a/chromeos/services/assistant/assistant_manager_service_impl.cc
+++ b/chromeos/services/assistant/assistant_manager_service_impl.cc
@@ -42,13 +42,13 @@
 AssistantManagerServiceImpl::AssistantManagerServiceImpl(
     service_manager::Connector* connector,
     device::mojom::BatteryMonitorPtr battery_monitor,
-    Service* service)
-    : platform_api_(CreateLibAssistantConfig(),
-                    connector,
-                    std::move(battery_monitor)),
+    Service* service,
+    const std::string& config_str)
+    : platform_api_(config_str, connector, std::move(battery_monitor)),
+      config_str_(config_str),
       action_module_(std::make_unique<action::CrosActionModule>(this)),
-      display_connection_(std::make_unique<CrosDisplayConnection>(this)),
       main_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()),
+      display_connection_(std::make_unique<CrosDisplayConnection>(this)),
       voice_interaction_observer_binding_(this),
       service_(service),
       background_thread_("background thread"),
@@ -394,8 +394,8 @@
     const std::string& arc_version) {
   DCHECK(background_thread_.task_runner()->BelongsToCurrentThread());
 
-  assistant_manager_.reset(assistant_client::AssistantManager::Create(
-      &platform_api_, CreateLibAssistantConfig()));
+  assistant_manager_.reset(
+      assistant_client::AssistantManager::Create(&platform_api_, config_str_));
   assistant_manager_internal_ =
       UnwrapAssistantManagerInternal(assistant_manager_.get());
 
diff --git a/chromeos/services/assistant/assistant_manager_service_impl.h b/chromeos/services/assistant/assistant_manager_service_impl.h
index 3b4507b..c8fda9d 100644
--- a/chromeos/services/assistant/assistant_manager_service_impl.h
+++ b/chromeos/services/assistant/assistant_manager_service_impl.h
@@ -62,7 +62,8 @@
   // |service| owns this class and must outlive this class.
   AssistantManagerServiceImpl(service_manager::Connector* connector,
                               device::mojom::BatteryMonitorPtr battery_monitor,
-                              Service* service);
+                              Service* service,
+                              const std::string& config_str);
 
   ~AssistantManagerServiceImpl() override;
 
@@ -124,6 +125,7 @@
       ash::mojom::VoiceInteractionState state) override {}
   void OnVoiceInteractionSettingsEnabled(bool enabled) override;
   void OnVoiceInteractionContextEnabled(bool enabled) override;
+  void OnVoiceInteractionHotwordEnabled(bool enabled) override {}
   void OnVoiceInteractionSetupCompleted(bool completed) override;
   void OnAssistantFeatureAllowedChanged(
       ash::mojom::AssistantAllowedState state) override {}
@@ -186,6 +188,7 @@
 
   State state_ = State::STOPPED;
   PlatformApiImpl platform_api_;
+  const std::string config_str_;
   std::unique_ptr<action::CrosActionModule> action_module_;
   scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_;
   std::unique_ptr<assistant_client::AssistantManager> assistant_manager_;
diff --git a/chromeos/services/assistant/service.cc b/chromeos/services/assistant/service.cc
index 426d228..4b2d62c 100644
--- a/chromeos/services/assistant/service.cc
+++ b/chromeos/services/assistant/service.cc
@@ -25,9 +25,11 @@
 #include "services/service_manager/public/cpp/service_context.h"
 
 #if BUILDFLAG(ENABLE_CROS_LIBASSISTANT)
+#include "ash/public/interfaces/constants.mojom.h"
 #include "chromeos/assistant/internal/internal_constants.h"
 #include "chromeos/services/assistant/assistant_manager_service_impl.h"
 #include "chromeos/services/assistant/assistant_settings_manager_impl.h"
+#include "chromeos/services/assistant/utils.h"
 #include "services/device/public/mojom/battery_monitor.mojom.h"
 #include "services/device/public/mojom/constants.mojom.h"
 #else
@@ -57,6 +59,7 @@
       token_refresh_timer_(std::make_unique<base::OneShotTimer>()),
       main_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()),
       power_manager_observer_(this),
+      voice_interaction_observer_binding_(this),
       weak_ptr_factory_(this) {
   registry_.AddInterface<mojom::AssistantPlatform>(base::BindRepeating(
       &Service::BindAssistantPlatformConnection, base::Unretained(this)));
@@ -128,6 +131,15 @@
   UpdateListeningState();
 }
 
+void Service::OnVoiceInteractionHotwordEnabled(bool enabled) {
+  if (assistant_manager_service_->GetState() !=
+      AssistantManagerService::State::RUNNING) {
+    return;
+  }
+  CreateAssistantManagerService(enabled);
+  RequestAccessToken();
+}
+
 void Service::BindAssistantSettingsManager(
     mojom::AssistantSettingsManagerRequest request) {
   DCHECK(assistant_settings_manager_);
@@ -164,11 +176,13 @@
   client_ = std::move(client);
   device_actions_ = std::move(device_actions);
 #if BUILDFLAG(ENABLE_CROS_LIBASSISTANT)
-  device::mojom::BatteryMonitorPtr battery_monitor;
-  context()->connector()->BindInterface(device::mojom::kServiceName,
-                                        mojo::MakeRequest(&battery_monitor));
-  assistant_manager_service_ = std::make_unique<AssistantManagerServiceImpl>(
-      context()->connector(), std::move(battery_monitor), this);
+  context()->connector()->BindInterface(ash::mojom::kServiceName,
+                                        &voice_interaction_controller_);
+  ash::mojom::VoiceInteractionObserverPtr ptr;
+  voice_interaction_observer_binding_.Bind(mojo::MakeRequest(&ptr));
+  voice_interaction_controller_->IsHotwordEnabled(base::BindOnce(
+      &Service::CreateAssistantManagerService, weak_ptr_factory_.GetWeakPtr()));
+  voice_interaction_controller_->AddObserver(std::move(ptr));
 #else
   assistant_manager_service_ =
       std::make_unique<FakeAssistantManagerServiceImpl>();
@@ -229,6 +243,18 @@
                               this, &Service::RequestAccessToken);
 }
 
+void Service::CreateAssistantManagerService(bool enable_hotword) {
+#if BUILDFLAG(ENABLE_CROS_LIBASSISTANT)
+  // TODO(updowndota): Check settings enabled pref when start Assistant.
+  device::mojom::BatteryMonitorPtr battery_monitor;
+  context()->connector()->BindInterface(device::mojom::kServiceName,
+                                        mojo::MakeRequest(&battery_monitor));
+  assistant_manager_service_ = std::make_unique<AssistantManagerServiceImpl>(
+      context()->connector(), std::move(battery_monitor), this,
+      CreateLibAssistantConfig(!enable_hotword));
+#endif
+}
+
 void Service::FinalizeAssistantManagerService() {
   DCHECK(assistant_manager_service_->GetState() ==
          AssistantManagerService::State::RUNNING);
diff --git a/chromeos/services/assistant/service.h b/chromeos/services/assistant/service.h
index 6a49183..5470683 100644
--- a/chromeos/services/assistant/service.h
+++ b/chromeos/services/assistant/service.h
@@ -10,6 +10,7 @@
 
 #include "ash/public/interfaces/assistant_controller.mojom.h"
 #include "ash/public/interfaces/session_controller.mojom.h"
+#include "ash/public/interfaces/voice_interaction_controller.mojom.h"
 #include "base/callback.h"
 #include "base/memory/weak_ptr.h"
 #include "base/optional.h"
@@ -41,7 +42,8 @@
 class Service : public service_manager::Service,
                 public chromeos::PowerManagerClient::Observer,
                 public ash::mojom::SessionActivationObserver,
-                public mojom::AssistantPlatform {
+                public mojom::AssistantPlatform,
+                public ash::mojom::VoiceInteractionObserver {
  public:
   Service();
   ~Service() override;
@@ -81,6 +83,16 @@
   void OnSessionActivated(bool activated) override;
   void OnLockStateChanged(bool locked) override;
 
+  // ash::mojom::VoiceInteractionObserver:
+  void OnVoiceInteractionStatusChanged(
+      ash::mojom::VoiceInteractionState state) override {}
+  void OnVoiceInteractionSettingsEnabled(bool enabled) override {}
+  void OnVoiceInteractionContextEnabled(bool enabled) override {}
+  void OnVoiceInteractionHotwordEnabled(bool enabled) override;
+  void OnVoiceInteractionSetupCompleted(bool completed) override {}
+  void OnAssistantFeatureAllowedChanged(
+      ash::mojom::AssistantAllowedState state) override {}
+
   void BindAssistantSettingsManager(
       mojom::AssistantSettingsManagerRequest request);
 
@@ -100,6 +112,8 @@
 
   void UpdateListeningState();
 
+  void CreateAssistantManagerService(bool enable_hotword);
+
   void FinalizeAssistantManagerService();
 
   void RetryRefreshToken();
@@ -131,6 +145,9 @@
   bool locked_ = false;
 
   ash::mojom::AssistantControllerPtr assistant_controller_;
+  ash::mojom::VoiceInteractionControllerPtr voice_interaction_controller_;
+  mojo::Binding<ash::mojom::VoiceInteractionObserver>
+      voice_interaction_observer_binding_;
 
   base::WeakPtrFactory<Service> weak_ptr_factory_;
 
diff --git a/chromeos/services/assistant/utils.cc b/chromeos/services/assistant/utils.cc
index e1a058d..993a466 100644
--- a/chromeos/services/assistant/utils.cc
+++ b/chromeos/services/assistant/utils.cc
@@ -14,7 +14,7 @@
 namespace chromeos {
 namespace assistant {
 
-std::string CreateLibAssistantConfig() {
+std::string CreateLibAssistantConfig(bool disable_hotword) {
   using Value = base::Value;
   using Type = base::Value::Type;
 
@@ -38,6 +38,14 @@
   internal.SetKey("disable_log_files", Value(true));
   config.SetKey("internal", std::move(internal));
 
+  Value audio_input(Type::DICTIONARY);
+  Value sources(Type::LIST);
+  Value dict(Type::DICTIONARY);
+  dict.SetKey("disable_hotword", Value(disable_hotword));
+  sources.GetList().push_back(std::move(dict));
+  audio_input.SetKey("sources", std::move(sources));
+  config.SetKey("audio_input", std::move(audio_input));
+
   std::string json;
   base::JSONWriter::Write(config, &json);
   return json;
diff --git a/chromeos/services/assistant/utils.h b/chromeos/services/assistant/utils.h
index 781cc26..379e54d 100644
--- a/chromeos/services/assistant/utils.h
+++ b/chromeos/services/assistant/utils.h
@@ -12,7 +12,7 @@
 namespace chromeos {
 namespace assistant {
 
-std::string CreateLibAssistantConfig();
+std::string CreateLibAssistantConfig(bool disable_hotword);
 
 }  // namespace assistant
 }  // namespace chromeos
diff --git a/chromeos/services/device_sync/public/mojom/device_sync.mojom b/chromeos/services/device_sync/public/mojom/device_sync.mojom
index 4ae4374..1430ec3 100644
--- a/chromeos/services/device_sync/public/mojom/device_sync.mojom
+++ b/chromeos/services/device_sync/public/mojom/device_sync.mojom
@@ -71,15 +71,6 @@
   // Encryption key used for communication with this device.
   string persistent_symmetric_key;
 
-  // True if this device has the capability of unlocking another device via
-  // EasyUnlock.
-  bool unlock_key;
-
-  // True if this device can enable a Wi-Fi hotspot to support Instant
-  // Tethering (i.e., the device supports mobile data and its model supports the
-  // feature).
-  bool supports_mobile_hotspot;
-
   // The time at which this device's metadata was last updated on the CryptAuth
   // back-end.
   mojo_base.mojom.Time last_update_time;
diff --git a/chromeos/services/device_sync/public/mojom/device_sync_mojom_traits.cc b/chromeos/services/device_sync/public/mojom/device_sync_mojom_traits.cc
index 389ab32..167b754 100644
--- a/chromeos/services/device_sync/public/mojom/device_sync_mojom_traits.cc
+++ b/chromeos/services/device_sync/public/mojom/device_sync_mojom_traits.cc
@@ -76,18 +76,6 @@
   return remote_device.persistent_symmetric_key;
 }
 
-bool StructTraits<chromeos::device_sync::mojom::RemoteDeviceDataView,
-                  cryptauth::RemoteDevice>::
-    unlock_key(const cryptauth::RemoteDevice& remote_device) {
-  return remote_device.unlock_key;
-}
-
-bool StructTraits<chromeos::device_sync::mojom::RemoteDeviceDataView,
-                  cryptauth::RemoteDevice>::
-    supports_mobile_hotspot(const cryptauth::RemoteDevice& remote_device) {
-  return remote_device.supports_mobile_hotspot;
-}
-
 base::Time StructTraits<chromeos::device_sync::mojom::RemoteDeviceDataView,
                         cryptauth::RemoteDevice>::
     last_update_time(const cryptauth::RemoteDevice& remote_device) {
@@ -123,8 +111,6 @@
     return false;
   }
 
-  out->unlock_key = in.unlock_key();
-  out->supports_mobile_hotspot = in.supports_mobile_hotspot();
   out->last_update_time_millis = last_update_time.ToJavaTime();
 
   return true;
diff --git a/chromeos/services/device_sync/public/mojom/device_sync_mojom_traits.h b/chromeos/services/device_sync/public/mojom/device_sync_mojom_traits.h
index 42bca0e..d65e1c6 100644
--- a/chromeos/services/device_sync/public/mojom/device_sync_mojom_traits.h
+++ b/chromeos/services/device_sync/public/mojom/device_sync_mojom_traits.h
@@ -41,9 +41,6 @@
       const cryptauth::RemoteDevice& remote_device);
   static const std::string& persistent_symmetric_key(
       const cryptauth::RemoteDevice& remote_device);
-  static bool unlock_key(const cryptauth::RemoteDevice& remote_device);
-  static bool supports_mobile_hotspot(
-      const cryptauth::RemoteDevice& remote_device);
   static base::Time last_update_time(
       const cryptauth::RemoteDevice& remote_device);
   static const std::map<cryptauth::SoftwareFeature,
diff --git a/chromeos/services/device_sync/public/mojom/device_sync_mojom_traits_unittest.cc b/chromeos/services/device_sync/public/mojom/device_sync_mojom_traits_unittest.cc
index ff09c69..36bef48 100644
--- a/chromeos/services/device_sync/public/mojom/device_sync_mojom_traits_unittest.cc
+++ b/chromeos/services/device_sync/public/mojom/device_sync_mojom_traits_unittest.cc
@@ -56,8 +56,6 @@
   input.name = "name";
   input.public_key = "publicKey";
   input.persistent_symmetric_key = "persistentSymmetricKey";
-  input.unlock_key = true;
-  input.supports_mobile_hotspot = true;
   input.last_update_time_millis = 3L;
   input.software_features = software_features;
   input.beacon_seeds = {CreateTestBeaconSeed()};
@@ -70,8 +68,6 @@
   EXPECT_EQ("name", output.name);
   EXPECT_EQ("publicKey", output.public_key);
   EXPECT_EQ("persistentSymmetricKey", output.persistent_symmetric_key);
-  EXPECT_TRUE(output.unlock_key);
-  EXPECT_TRUE(output.supports_mobile_hotspot);
   EXPECT_EQ(3L, output.last_update_time_millis);
   EXPECT_EQ(software_features, output.software_features);
   ASSERT_EQ(1u, output.beacon_seeds.size());
diff --git a/components/autofill/core/browser/autofill_client.h b/components/autofill/core/browser/autofill_client.h
index c8d2477..7c45201 100644
--- a/components/autofill/core/browser/autofill_client.h
+++ b/components/autofill/core/browser/autofill_client.h
@@ -46,6 +46,7 @@
 
 class AddressNormalizer;
 class AutofillPopupDelegate;
+class AutofillProfile;
 class AutofillWebDataService;
 class CardUnmaskDelegate;
 class CreditCard;
@@ -125,6 +126,10 @@
   // Causes the Autofill settings UI to be shown.
   virtual void ShowAutofillSettings() = 0;
 
+  // Runs |callback| if the |profile| should be imported as personal data.
+  virtual void ConfirmSaveAutofillProfile(const AutofillProfile& profile,
+                                          base::OnceClosure callback) = 0;
+
   // A user has attempted to use a masked card. Prompt them for further
   // information to proceed.
   virtual void ShowUnmaskPrompt(const CreditCard& card,
diff --git a/components/autofill/core/browser/autofill_download_manager.cc b/components/autofill/core/browser/autofill_download_manager.cc
index c8fa738b..9b39799 100644
--- a/components/autofill/core/browser/autofill_download_manager.cc
+++ b/components/autofill/core/browser/autofill_download_manager.cc
@@ -69,12 +69,6 @@
     false,
 };
 
-#if defined(GOOGLE_CHROME_BUILD)
-const char kClientName[] = "Google+Chrome";
-#else
-const char kClientName[] = "Chromium";
-#endif  // defined(GOOGLE_CHROME_BUILD)
-
 const char kDefaultAutofillServerURL[] =
     "https://clients1.google.com/tbproxy/af/";
 
@@ -372,7 +366,7 @@
 AutofillDownloadManager::GetRequestURLAndMethod(
     const FormRequestData& request_data) const {
   net::URLFetcher::RequestType method = net::URLFetcher::POST;
-  std::string query_str(base::StrCat({"client=", kClientName}));
+  std::string query_str;
 
   if (request_data.request_type == AutofillDownloadManager::REQUEST_QUERY) {
     if (request_data.payload.length() <= kMaxQueryGetSize &&
@@ -382,19 +376,18 @@
       base::Base64UrlEncode(request_data.payload,
                             base::Base64UrlEncodePolicy::INCLUDE_PADDING,
                             &base64_payload);
-      base::StrAppend(&query_str, {"&q=", base64_payload});
+      base::StrAppend(&query_str, {"q=", base64_payload});
     }
     UMA_HISTOGRAM_BOOLEAN("Autofill.Query.Method",
                           (method == net::URLFetcher::GET) ? 0 : 1);
   }
 
   GURL::Replacements replacements;
-  replacements.SetQueryStr(std::move(query_str));
+  replacements.SetQueryStr(query_str);
 
   GURL url = autofill_server_url_
                  .Resolve(RequestTypeToString(request_data.request_type))
                  .ReplaceComponents(replacements);
-
   return std::make_tuple(std::move(url), method);
 }
 
diff --git a/components/autofill/core/browser/autofill_merge_unittest.cc b/components/autofill/core/browser/autofill_merge_unittest.cc
index c8d4796..0695357 100644
--- a/components/autofill/core/browser/autofill_merge_unittest.cc
+++ b/components/autofill/core/browser/autofill_merge_unittest.cc
@@ -23,6 +23,7 @@
 #include "components/autofill/core/browser/form_data_importer.h"
 #include "components/autofill/core/browser/form_structure.h"
 #include "components/autofill/core/browser/personal_data_manager.h"
+#include "components/autofill/core/browser/test_autofill_client.h"
 #include "components/autofill/core/common/form_data.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
@@ -181,6 +182,7 @@
   // Deserializes |str| into a field type.
   ServerFieldType StringToFieldType(const std::string& str);
 
+  TestAutofillClient autofill_client_;
   PersonalDataManagerMock personal_data_;
   std::unique_ptr<FormDataImporter> form_data_importer_;
 
@@ -204,7 +206,7 @@
 void AutofillMergeTest::SetUp() {
   test::DisableSystemServices(nullptr);
   form_data_importer_ = std::make_unique<FormDataImporter>(
-      /*AutofillClient=*/nullptr,
+      &autofill_client_,
       /*payments::PaymentsClient=*/nullptr, &personal_data_, "en");
 }
 
diff --git a/components/autofill/core/browser/credit_card_save_manager.cc b/components/autofill/core/browser/credit_card_save_manager.cc
index 4cd6d5e..6103a33 100644
--- a/components/autofill/core/browser/credit_card_save_manager.cc
+++ b/components/autofill/core/browser/credit_card_save_manager.cc
@@ -34,6 +34,7 @@
 #include "components/autofill/core/browser/personal_data_manager.h"
 #include "components/autofill/core/browser/validation.h"
 #include "components/autofill/core/common/autofill_clock.h"
+#include "components/autofill/core/common/autofill_features.h"
 #include "components/autofill/core/common/autofill_util.h"
 #include "components/prefs/pref_service.h"
 #include "services/identity/public/cpp/identity_manager.h"
@@ -107,7 +108,8 @@
   // In an ideal scenario, when uploading a card, we would have:
   //  1) Card number and expiration
   //  2) CVC
-  //  3) 1+ recently-used or modified addresses that meet validation rules
+  //  3) 1+ recently-used or modified addresses that meet validation rules (or
+  //     only the address countries if the relevant feature is enabled).
   //  4) Cardholder name or names on the address profiles
   // At a minimum, only #1 (card number and expiration) is absolutely required
   // in order to save a card to Google Payments. We perform all checks before
@@ -367,14 +369,30 @@
   if (verified_zip.empty() && !candidate_profiles.empty())
     upload_decision_metrics_ |= AutofillMetrics::UPLOAD_NOT_OFFERED_NO_ZIP_CODE;
 
+  // If the relevant feature is enabled, only send the country of the
+  // recently-used addresses.
+  if (base::FeatureList::IsEnabled(
+          features::kAutofillSendOnlyCountryInGetUploadDetails)) {
+    for (size_t i = 0; i < candidate_profiles.size(); i++) {
+      AutofillProfile country_only;
+      country_only.SetInfo(
+          ADDRESS_HOME_COUNTRY,
+          candidate_profiles[i].GetInfo(ADDRESS_HOME_COUNTRY, app_locale_),
+          app_locale_);
+      candidate_profiles[i] = std::move(country_only);
+    }
+  }
+
   // Set up |upload_request->profiles|.
   upload_request->profiles.assign(candidate_profiles.begin(),
                                   candidate_profiles.end());
-  if (!has_modified_profile)
-    for (const AutofillProfile& profile : candidate_profiles)
+  if (!has_modified_profile) {
+    for (const AutofillProfile& profile : candidate_profiles) {
       UMA_HISTOGRAM_COUNTS_1000(
           "Autofill.DaysSincePreviousUseAtSubmission.Profile",
           (profile.use_date() - profile.previous_use_date()).InDays());
+    }
+  }
 }
 
 int CreditCardSaveManager::GetDetectedValues() const {
diff --git a/components/autofill/core/browser/credit_card_save_manager.h b/components/autofill/core/browser/credit_card_save_manager.h
index 605f16c..ca84bac 100644
--- a/components/autofill/core/browser/credit_card_save_manager.h
+++ b/components/autofill/core/browser/credit_card_save_manager.h
@@ -119,7 +119,9 @@
   // to |upload_request.profiles|. If any problems are found when determining
   // the candidate set of profiles, sets |upload_decision_metrics_| with the
   // failure reasons. Appends any experiments that were triggered to
-  // |upload_request.active_experiments|.
+  // |upload_request.active_experiments|. Note that if the relevant feature is
+  // enabled, the addresses being assigned to |upload_request.profiles| may only
+  // contain countries.
   void SetProfilesForCreditCardUpload(
       const CreditCard& card,
       payments::PaymentsClient::UploadRequestDetails* upload_request);
diff --git a/components/autofill/core/browser/credit_card_save_manager_unittest.cc b/components/autofill/core/browser/credit_card_save_manager_unittest.cc
index 9a20694..429564b 100644
--- a/components/autofill/core/browser/credit_card_save_manager_unittest.cc
+++ b/components/autofill/core/browser/credit_card_save_manager_unittest.cc
@@ -39,6 +39,7 @@
 #include "components/autofill/core/browser/validation.h"
 #include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
 #include "components/autofill/core/common/autofill_clock.h"
+#include "components/autofill/core/common/autofill_features.h"
 #include "components/autofill/core/common/form_data.h"
 #include "components/autofill/core/common/form_field_data.h"
 #include "components/prefs/pref_service.h"
@@ -442,7 +443,9 @@
   histogram_tester.ExpectTotalCount("Autofill.CardUploadDecisionMetric", 0);
 }
 
-TEST_F(CreditCardSaveManagerTest, UploadCreditCard) {
+TEST_F(CreditCardSaveManagerTest, UploadCreditCard_FullAddresses) {
+  scoped_feature_list_.InitAndDisableFeature(
+      features::kAutofillSendOnlyCountryInGetUploadDetails);
   personal_data_.ClearCreditCards();
   personal_data_.ClearProfiles();
   credit_card_save_manager_->SetCreditCardUploadEnabled(true);
@@ -476,11 +479,88 @@
   FormSubmitted(credit_card_form);
   EXPECT_TRUE(credit_card_save_manager_->CreditCardWasUploaded());
   EXPECT_THAT(
-      payments_client_->GetActiveExperimentsSetInRequest(),
+      payments_client_->active_experiments_in_request(),
       UnorderedElementsAre(kAutofillUpstreamUpdatePromptExplanation.name));
 
+  // Verify that one profile was saved, and it was included in the upload
+  // details request to payments.
+  EXPECT_EQ(1U, personal_data_.GetProfiles().size());
+  EXPECT_THAT(
+      payments_client_->addresses_in_upload_details(),
+      testing::UnorderedElementsAreArray({*personal_data_.GetProfiles()[0]}));
+
   // Server did not send a server_id, expect copy of card is not stored.
   EXPECT_TRUE(personal_data_.GetCreditCards().empty());
+
+  // Verify that the correct histogram entry (and only that) was logged.
+  ExpectUniqueCardUploadDecision(histogram_tester,
+                                 AutofillMetrics::UPLOAD_OFFERED);
+  // Verify that the correct UKM was logged.
+  ExpectCardUploadDecisionUkm(AutofillMetrics::UPLOAD_OFFERED);
+  // Verify the histogram entry for recent profile modification.
+  histogram_tester.ExpectUniqueSample(
+      "Autofill.HasModifiedProfile.CreditCardFormSubmission", true, 1);
+  // Verify that UMA for "DaysSincePreviousUse" was not logged because we
+  // modified the profile.
+  histogram_tester.ExpectTotalCount(
+      "Autofill.DaysSincePreviousUseAtSubmission.Profile", 0);
+}
+
+TEST_F(CreditCardSaveManagerTest, UploadCreditCard_OnlyCountryInAddresses) {
+  // When this feature is enabled, the addresses being sent in the upload
+  // details request will only contain the country.
+  scoped_feature_list_.InitAndEnableFeature(
+      features::kAutofillSendOnlyCountryInGetUploadDetails);
+  personal_data_.ClearCreditCards();
+  personal_data_.ClearProfiles();
+  credit_card_save_manager_->SetCreditCardUploadEnabled(true);
+
+  // 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));
+  ExpectUniqueFillableFormParsedUkm();
+
+  ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form);
+  FormSubmitted(address_form);
+
+  // Set up our credit card form data.
+  FormData credit_card_form;
+  CreateTestCreditCardFormData(&credit_card_form, true, false);
+  FormsSeen(std::vector<FormData>(1, credit_card_form));
+  ExpectFillableFormParsedUkm(2 /* num_fillable_forms_parsed */);
+
+  // Edit the data, and submit.
+  credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master");
+  credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111");
+  credit_card_form.fields[2].value = ASCIIToUTF16(NextMonth());
+  credit_card_form.fields[3].value = ASCIIToUTF16(NextYear());
+  credit_card_form.fields[4].value = ASCIIToUTF16("123");
+
+  base::HistogramTester histogram_tester;
+
+  EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)).Times(0);
+  FormSubmitted(credit_card_form);
+  EXPECT_TRUE(credit_card_save_manager_->CreditCardWasUploaded());
+  EXPECT_THAT(
+      payments_client_->active_experiments_in_request(),
+      UnorderedElementsAre(kAutofillUpstreamUpdatePromptExplanation.name));
+
+  // Verify that even though the full address profile was saved, only the
+  // country was included in the upload details request to payments.
+  EXPECT_EQ(1U, personal_data_.GetProfiles().size());
+  AutofillProfile only_country;
+  only_country.SetInfo(ADDRESS_HOME_COUNTRY, ASCIIToUTF16("US"), "en-US");
+  EXPECT_EQ(1U, payments_client_->addresses_in_upload_details().size());
+  // AutofillProfile::Compare will ignore the difference in guid between our
+  // actual profile being sent and the expected one constructed here.
+  EXPECT_EQ(0, payments_client_->addresses_in_upload_details()[0].Compare(
+                   only_country));
+
+  // Server did not send a server_id, expect copy of card is not stored.
+  EXPECT_TRUE(personal_data_.GetCreditCards().empty());
+
   // Verify that the correct histogram entry (and only that) was logged.
   ExpectUniqueCardUploadDecision(histogram_tester,
                                  AutofillMetrics::UPLOAD_OFFERED);
@@ -533,7 +613,7 @@
   FormSubmitted(credit_card_form);
   EXPECT_TRUE(credit_card_save_manager_->CreditCardWasUploaded());
   EXPECT_THAT(
-      payments_client_->GetActiveExperimentsSetInRequest(),
+      payments_client_->active_experiments_in_request(),
       UnorderedElementsAre(kAutofillUpstreamUpdatePromptExplanation.name));
 
   // Server did not send a server_id, expect copy of card is not stored.
@@ -619,7 +699,7 @@
   FormSubmitted(credit_card_form);
   EXPECT_TRUE(credit_card_save_manager_->CreditCardWasUploaded());
   EXPECT_THAT(
-      payments_client_->GetActiveExperimentsSetInRequest(),
+      payments_client_->active_experiments_in_request(),
       UnorderedElementsAre(kAutofillUpstreamUpdatePromptExplanation.name));
 
   // Server did not send a server_id, expect copy of card is not stored.
@@ -1843,7 +1923,7 @@
   ExpectCardUploadDecision(
       histogram_tester,
       AutofillMetrics::USER_REQUESTED_TO_PROVIDE_CARDHOLDER_NAME);
-  EXPECT_TRUE(payments_client_->GetDetectedValuesSetInRequest() &
+  EXPECT_TRUE(payments_client_->detected_values_in_upload_details() &
               CreditCardSaveManager::DetectedValue::USER_PROVIDED_NAME);
 }
 
@@ -1888,7 +1968,7 @@
   ExpectCardUploadDecision(
       histogram_tester,
       AutofillMetrics::USER_REQUESTED_TO_PROVIDE_CARDHOLDER_NAME);
-  EXPECT_TRUE(payments_client_->GetDetectedValuesSetInRequest() &
+  EXPECT_TRUE(payments_client_->detected_values_in_upload_details() &
               CreditCardSaveManager::DetectedValue::USER_PROVIDED_NAME);
 }
 
@@ -1931,7 +2011,7 @@
   ExpectNoCardUploadDecision(
       histogram_tester,
       AutofillMetrics::USER_REQUESTED_TO_PROVIDE_CARDHOLDER_NAME);
-  EXPECT_FALSE(payments_client_->GetDetectedValuesSetInRequest() &
+  EXPECT_FALSE(payments_client_->detected_values_in_upload_details() &
                CreditCardSaveManager::DetectedValue::USER_PROVIDED_NAME);
 }
 
@@ -1981,7 +2061,7 @@
   ExpectNoCardUploadDecision(
       histogram_tester,
       AutofillMetrics::USER_REQUESTED_TO_PROVIDE_CARDHOLDER_NAME);
-  EXPECT_FALSE(payments_client_->GetDetectedValuesSetInRequest() &
+  EXPECT_FALSE(payments_client_->detected_values_in_upload_details() &
                CreditCardSaveManager::DetectedValue::USER_PROVIDED_NAME);
 }
 
@@ -2031,7 +2111,7 @@
   ExpectNoCardUploadDecision(
       histogram_tester,
       AutofillMetrics::USER_REQUESTED_TO_PROVIDE_CARDHOLDER_NAME);
-  EXPECT_FALSE(payments_client_->GetDetectedValuesSetInRequest() &
+  EXPECT_FALSE(payments_client_->detected_values_in_upload_details() &
                CreditCardSaveManager::DetectedValue::USER_PROVIDED_NAME);
 }
 
@@ -2076,7 +2156,7 @@
   ExpectNoCardUploadDecision(
       histogram_tester,
       AutofillMetrics::USER_REQUESTED_TO_PROVIDE_CARDHOLDER_NAME);
-  EXPECT_FALSE(payments_client_->GetDetectedValuesSetInRequest() &
+  EXPECT_FALSE(payments_client_->detected_values_in_upload_details() &
                CreditCardSaveManager::DetectedValue::USER_PROVIDED_NAME);
 }
 
@@ -2121,7 +2201,7 @@
   ExpectNoCardUploadDecision(
       histogram_tester,
       AutofillMetrics::USER_REQUESTED_TO_PROVIDE_CARDHOLDER_NAME);
-  EXPECT_FALSE(payments_client_->GetDetectedValuesSetInRequest() &
+  EXPECT_FALSE(payments_client_->detected_values_in_upload_details() &
                CreditCardSaveManager::DetectedValue::USER_PROVIDED_NAME);
 }
 
@@ -2165,7 +2245,7 @@
   ExpectCardUploadDecision(
       histogram_tester,
       AutofillMetrics::USER_REQUESTED_TO_PROVIDE_CARDHOLDER_NAME);
-  EXPECT_TRUE(payments_client_->GetDetectedValuesSetInRequest() &
+  EXPECT_TRUE(payments_client_->detected_values_in_upload_details() &
               CreditCardSaveManager::DetectedValue::USER_PROVIDED_NAME);
 }
 #endif
@@ -2320,7 +2400,7 @@
 
   // Submit the form and check what detected_values for an upload save would be.
   FormSubmitted(credit_card_form);
-  EXPECT_EQ(payments_client_->GetDetectedValuesSetInRequest(), 0);
+  EXPECT_EQ(payments_client_->detected_values_in_upload_details(), 0);
 }
 
 TEST_F(CreditCardSaveManagerTest, GetDetectedValues_DetectCvc) {
@@ -2341,7 +2421,7 @@
 
   // Submit the form and check what detected_values for an upload save would be.
   FormSubmitted(credit_card_form);
-  EXPECT_EQ(payments_client_->GetDetectedValuesSetInRequest(),
+  EXPECT_EQ(payments_client_->detected_values_in_upload_details(),
             CreditCardSaveManager::DetectedValue::CVC);
 }
 
@@ -2363,7 +2443,7 @@
 
   // Submit the form and check what detected_values for an upload save would be.
   FormSubmitted(credit_card_form);
-  EXPECT_EQ(payments_client_->GetDetectedValuesSetInRequest(),
+  EXPECT_EQ(payments_client_->detected_values_in_upload_details(),
             CreditCardSaveManager::DetectedValue::CARDHOLDER_NAME);
 }
 
@@ -2391,7 +2471,7 @@
 
   // Submit the form and check what detected_values for an upload save would be.
   FormSubmitted(credit_card_form);
-  EXPECT_EQ(payments_client_->GetDetectedValuesSetInRequest(),
+  EXPECT_EQ(payments_client_->detected_values_in_upload_details(),
             CreditCardSaveManager::DetectedValue::ADDRESS_NAME);
 }
 
@@ -2420,7 +2500,7 @@
 
   // Submit the form and check what detected_values for an upload save would be.
   FormSubmitted(credit_card_form);
-  EXPECT_EQ(payments_client_->GetDetectedValuesSetInRequest(),
+  EXPECT_EQ(payments_client_->detected_values_in_upload_details(),
             CreditCardSaveManager::DetectedValue::CARDHOLDER_NAME |
                 CreditCardSaveManager::DetectedValue::ADDRESS_NAME);
 }
@@ -2450,7 +2530,7 @@
 
   // Submit the form and check what detected_values for an upload save would be.
   FormSubmitted(credit_card_form);
-  EXPECT_EQ(payments_client_->GetDetectedValuesSetInRequest(), 0);
+  EXPECT_EQ(payments_client_->detected_values_in_upload_details(), 0);
 }
 
 TEST_F(CreditCardSaveManagerTest, GetDetectedValues_DetectPostalCode) {
@@ -2477,7 +2557,7 @@
 
   // Submit the form and check what detected_values for an upload save would be.
   FormSubmitted(credit_card_form);
-  EXPECT_EQ(payments_client_->GetDetectedValuesSetInRequest(),
+  EXPECT_EQ(payments_client_->detected_values_in_upload_details(),
             CreditCardSaveManager::DetectedValue::POSTAL_CODE);
 }
 
@@ -2510,7 +2590,7 @@
 
   // Submit the form and check what detected_values for an upload save would be.
   FormSubmitted(credit_card_form);
-  EXPECT_EQ(payments_client_->GetDetectedValuesSetInRequest(), 0);
+  EXPECT_EQ(payments_client_->detected_values_in_upload_details(), 0);
 }
 
 TEST_F(CreditCardSaveManagerTest, GetDetectedValues_DetectAddressLine) {
@@ -2537,7 +2617,7 @@
 
   // Submit the form and check what detected_values for an upload save would be.
   FormSubmitted(credit_card_form);
-  EXPECT_EQ(payments_client_->GetDetectedValuesSetInRequest(),
+  EXPECT_EQ(payments_client_->detected_values_in_upload_details(),
             CreditCardSaveManager::DetectedValue::ADDRESS_LINE);
 }
 
@@ -2565,7 +2645,7 @@
 
   // Submit the form and check what detected_values for an upload save would be.
   FormSubmitted(credit_card_form);
-  EXPECT_EQ(payments_client_->GetDetectedValuesSetInRequest(),
+  EXPECT_EQ(payments_client_->detected_values_in_upload_details(),
             CreditCardSaveManager::DetectedValue::LOCALITY);
 }
 
@@ -2593,7 +2673,7 @@
 
   // Submit the form and check what detected_values for an upload save would be.
   FormSubmitted(credit_card_form);
-  EXPECT_EQ(payments_client_->GetDetectedValuesSetInRequest(),
+  EXPECT_EQ(payments_client_->detected_values_in_upload_details(),
             CreditCardSaveManager::DetectedValue::ADMINISTRATIVE_AREA);
 }
 
@@ -2621,7 +2701,7 @@
 
   // Submit the form and check what detected_values for an upload save would be.
   FormSubmitted(credit_card_form);
-  EXPECT_EQ(payments_client_->GetDetectedValuesSetInRequest(),
+  EXPECT_EQ(payments_client_->detected_values_in_upload_details(),
             CreditCardSaveManager::DetectedValue::COUNTRY_CODE);
 }
 
@@ -2649,7 +2729,7 @@
 
   // Submit the form and check what detected_values for an upload save would be.
   FormSubmitted(credit_card_form);
-  EXPECT_EQ(payments_client_->GetDetectedValuesSetInRequest(),
+  EXPECT_EQ(payments_client_->detected_values_in_upload_details(),
             CreditCardSaveManager::DetectedValue::HAS_GOOGLE_PAYMENTS_ACCOUNT);
 }
 
@@ -2682,7 +2762,7 @@
 
   // Submit the form and check what detected_values for an upload save would be.
   FormSubmitted(credit_card_form);
-  EXPECT_EQ(payments_client_->GetDetectedValuesSetInRequest(),
+  EXPECT_EQ(payments_client_->detected_values_in_upload_details(),
             CreditCardSaveManager::DetectedValue::CVC |
                 CreditCardSaveManager::DetectedValue::CARDHOLDER_NAME |
                 CreditCardSaveManager::DetectedValue::ADDRESS_NAME |
@@ -2721,7 +2801,7 @@
 
   // Submit the form and check what detected_values for an upload save would be.
   FormSubmitted(credit_card_form);
-  EXPECT_EQ(payments_client_->GetDetectedValuesSetInRequest(),
+  EXPECT_EQ(payments_client_->detected_values_in_upload_details(),
             CreditCardSaveManager::DetectedValue::CVC |
                 CreditCardSaveManager::DetectedValue::LOCALITY |
                 CreditCardSaveManager::DetectedValue::POSTAL_CODE |
@@ -2769,7 +2849,7 @@
 
   // Submit the form and check what detected_values for an upload save would be.
   FormSubmitted(credit_card_form);
-  EXPECT_EQ(payments_client_->GetDetectedValuesSetInRequest(),
+  EXPECT_EQ(payments_client_->detected_values_in_upload_details(),
             CreditCardSaveManager::DetectedValue::ADDRESS_LINE |
                 CreditCardSaveManager::DetectedValue::LOCALITY |
                 CreditCardSaveManager::DetectedValue::ADMINISTRATIVE_AREA |
@@ -3292,7 +3372,7 @@
   FormSubmitted(credit_card_form);
   EXPECT_TRUE(credit_card_save_manager_->CreditCardWasUploaded());
   EXPECT_THAT(
-      payments_client_->GetActiveExperimentsSetInRequest(),
+      payments_client_->active_experiments_in_request(),
       UnorderedElementsAre(kAutofillUpstreamUpdatePromptExplanation.name));
 }
 
@@ -3327,7 +3407,7 @@
   EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)).Times(0);
   FormSubmitted(credit_card_form);
   EXPECT_TRUE(credit_card_save_manager_->CreditCardWasUploaded());
-  EXPECT_TRUE(payments_client_->GetActiveExperimentsSetInRequest().empty());
+  EXPECT_TRUE(payments_client_->active_experiments_in_request().empty());
 }
 
 TEST_F(CreditCardSaveManagerTest, UploadCreditCard_AddPanFirstSixToRequest) {
@@ -3360,11 +3440,11 @@
   EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)).Times(0);
   FormSubmitted(credit_card_form);
   EXPECT_TRUE(credit_card_save_manager_->CreditCardWasUploaded());
-  EXPECT_EQ(payments_client_->GetPanFirstSixSetInRequest(), "444433");
+  EXPECT_EQ(payments_client_->pan_first_six_in_upload_details(), "444433");
   // Confirm that the "send pan first six" experiment flag and enabled
   // UpdatePromptExplanation experiment flag state was sent in the request.
   EXPECT_THAT(
-      payments_client_->GetActiveExperimentsSetInRequest(),
+      payments_client_->active_experiments_in_request(),
       UnorderedElementsAre(kAutofillUpstreamSendPanFirstSix.name,
                            kAutofillUpstreamUpdatePromptExplanation.name));
 }
diff --git a/components/autofill/core/browser/form_data_importer.cc b/components/autofill/core/browser/form_data_importer.cc
index 52e7c9e..fa2e83b 100644
--- a/components/autofill/core/browser/form_data_importer.cc
+++ b/components/autofill/core/browser/form_data_importer.cc
@@ -96,7 +96,8 @@
                                    payments::PaymentsClient* payments_client,
                                    PersonalDataManager* personal_data_manager,
                                    const std::string& app_locale)
-    : credit_card_save_manager_(
+    : client_(client),
+      credit_card_save_manager_(
           std::make_unique<CreditCardSaveManager>(client,
                                                   payments_client,
                                                   app_locale,
@@ -296,7 +297,14 @@
   if (!IsValidLearnableProfile(candidate_profile, app_locale_))
     return false;
 
-  personal_data_manager_->SaveImportedProfile(candidate_profile);
+  // Delaying |SaveImportedProfile| is safe here because PersonalDataManager
+  // outlives this class.
+  client_->ConfirmSaveAutofillProfile(
+      candidate_profile,
+      base::BindOnce(
+          base::IgnoreResult(&PersonalDataManager::SaveImportedProfile),
+          base::Unretained(personal_data_manager_), candidate_profile));
+
   return true;
 }
 
diff --git a/components/autofill/core/browser/form_data_importer.h b/components/autofill/core/browser/form_data_importer.h
index 2374e9d..0009ffb 100644
--- a/components/autofill/core/browser/form_data_importer.h
+++ b/components/autofill/core/browser/form_data_importer.h
@@ -97,6 +97,9 @@
   CreditCard ExtractCreditCardFromForm(const FormStructure& form,
                                        bool* hasDuplicateFieldType);
 
+  // The associated autofill client. Weak reference.
+  AutofillClient* client_;
+
   // Responsible for managing credit card save flows (local or upload).
   std::unique_ptr<CreditCardSaveManager> credit_card_save_manager_;
 
diff --git a/components/autofill/core/browser/form_data_importer_unittest.cc b/components/autofill/core/browser/form_data_importer_unittest.cc
index 2178708..206db16 100644
--- a/components/autofill/core/browser/form_data_importer_unittest.cc
+++ b/components/autofill/core/browser/form_data_importer_unittest.cc
@@ -34,6 +34,7 @@
 #include "components/autofill/core/browser/form_structure.h"
 #include "components/autofill/core/browser/personal_data_manager.h"
 #include "components/autofill/core/browser/personal_data_manager_observer.h"
+#include "components/autofill/core/browser/test_autofill_client.h"
 #include "components/autofill/core/browser/webdata/autofill_table.h"
 #include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
 #include "components/autofill/core/common/autofill_constants.h"
@@ -220,6 +221,7 @@
   scoped_refptr<WebDatabaseService> web_database_;
   AutofillTable* autofill_table_;  // weak ref
   PersonalDataLoadedObserverMock personal_data_observer_;
+  std::unique_ptr<TestAutofillClient> autofill_client_;
   std::unique_ptr<PersonalDataManager> personal_data_manager_;
   std::unique_ptr<FormDataImporter> form_data_importer_;
 };
@@ -245,11 +247,13 @@
         WebDataServiceBase::ProfileErrorCallback());
     autofill_database_service_->Init();
 
+    autofill_client_ = std::make_unique<TestAutofillClient>();
+
     test::DisableSystemServices(prefs_.get());
     ResetPersonalDataManager(USER_MODE_NORMAL);
 
     form_data_importer_.reset(
-        new FormDataImporter(/*AutofillClient=*/nullptr,
+        new FormDataImporter(autofill_client_.get(),
                              /*payments::PaymentsClient=*/nullptr,
                              personal_data_manager_.get(), "en"));
 
diff --git a/components/autofill/core/browser/payments/test_payments_client.cc b/components/autofill/core/browser/payments/test_payments_client.cc
index c85d84c..1048649 100644
--- a/components/autofill/core/browser/payments/test_payments_client.cc
+++ b/components/autofill/core/browser/payments/test_payments_client.cc
@@ -31,6 +31,7 @@
     const std::string& pan_first_six,
     const std::vector<const char*>& active_experiments,
     const std::string& app_locale) {
+  upload_details_addresses_ = addresses;
   detected_values_ = detected_values;
   pan_first_six_ = pan_first_six;
   active_experiments_ = active_experiments;
@@ -57,18 +58,5 @@
   server_id_ = server_id;
 }
 
-int TestPaymentsClient::GetDetectedValuesSetInRequest() const {
-  return detected_values_;
-}
-
-std::string TestPaymentsClient::GetPanFirstSixSetInRequest() const {
-  return pan_first_six_;
-}
-
-std::vector<const char*> TestPaymentsClient::GetActiveExperimentsSetInRequest()
-    const {
-  return active_experiments_;
-}
-
 }  // namespace payments
 }  // namespace autofill
diff --git a/components/autofill/core/browser/payments/test_payments_client.h b/components/autofill/core/browser/payments/test_payments_client.h
index 2c35cbd..13d24e0 100644
--- a/components/autofill/core/browser/payments/test_payments_client.h
+++ b/components/autofill/core/browser/payments/test_payments_client.h
@@ -42,13 +42,19 @@
 
   void SetServerIdForCardUpload(std::string);
 
-  int GetDetectedValuesSetInRequest() const;
-  std::string GetPanFirstSixSetInRequest() const;
-  std::vector<const char*> GetActiveExperimentsSetInRequest() const;
+  int detected_values_in_upload_details() const { return detected_values_; }
+  const std::vector<AutofillProfile>& addresses_in_upload_details() const {
+    return upload_details_addresses_;
+  }
+  std::string pan_first_six_in_upload_details() const { return pan_first_six_; }
+  const std::vector<const char*>& active_experiments_in_request() const {
+    return active_experiments_;
+  }
 
  private:
   payments::PaymentsClientSaveDelegate* save_delegate_;
   std::string server_id_;
+  std::vector<AutofillProfile> upload_details_addresses_;
   int detected_values_;
   std::string pan_first_six_;
   std::vector<const char*> active_experiments_;
diff --git a/components/autofill/core/browser/test_autofill_client.cc b/components/autofill/core/browser/test_autofill_client.cc
index 38a52ed..7c8e23f 100644
--- a/components/autofill/core/browser/test_autofill_client.cc
+++ b/components/autofill/core/browser/test_autofill_client.cc
@@ -73,6 +73,14 @@
 void TestAutofillClient::OnUnmaskVerificationResult(PaymentsRpcResult result) {
 }
 
+void TestAutofillClient::ConfirmSaveAutofillProfile(
+    const AutofillProfile& profile,
+    base::OnceClosure callback) {
+  // Since there is no confirmation needed to save an Autofill Profile,
+  // running |callback| will proceed with saving |profile|.
+  std::move(callback).Run();
+}
+
 void TestAutofillClient::ConfirmSaveCreditCardLocally(
     const CreditCard& card,
     const base::Closure& callback) {
diff --git a/components/autofill/core/browser/test_autofill_client.h b/components/autofill/core/browser/test_autofill_client.h
index 9c971dd..b9bb101 100644
--- a/components/autofill/core/browser/test_autofill_client.h
+++ b/components/autofill/core/browser/test_autofill_client.h
@@ -41,6 +41,8 @@
                         UnmaskCardReason reason,
                         base::WeakPtr<CardUnmaskDelegate> delegate) override;
   void OnUnmaskVerificationResult(PaymentsRpcResult result) override;
+  void ConfirmSaveAutofillProfile(const AutofillProfile& profile,
+                                  base::OnceClosure callback) override;
   void ConfirmSaveCreditCardLocally(const CreditCard& card,
                                     const base::Closure& callback) override;
   void ConfirmSaveCreditCardToCloud(
diff --git a/components/autofill/core/browser/ui/local_card_migration_bubble_controller.h b/components/autofill/core/browser/ui/local_card_migration_bubble_controller.h
new file mode 100644
index 0000000..a2dc620
--- /dev/null
+++ b/components/autofill/core/browser/ui/local_card_migration_bubble_controller.h
@@ -0,0 +1,37 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_UI_LOCAL_CARD_MIGRATION_BUBBLE_CONTROLLER_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_UI_LOCAL_CARD_MIGRATION_BUBBLE_CONTROLLER_H_
+
+#include "base/macros.h"
+#include "base/strings/string16.h"
+
+namespace autofill {
+
+class LocalCardMigrationBubble;
+
+// Interface that exposes controller functionality to
+// LocalCardMigrationBubble. The bubble is shown to offer user an option
+// to upload credit cards stored in browser to Google Payments.
+class LocalCardMigrationBubbleController {
+ public:
+  LocalCardMigrationBubbleController() {}
+  virtual ~LocalCardMigrationBubbleController() {}
+
+  // Returns the title that should be displayed in the bubble.
+  virtual base::string16 GetWindowTitle() const = 0;
+
+  // Interaction.
+  virtual void OnConfirmButtonClicked() = 0;
+  virtual void OnCancelButtonClicked() = 0;
+  virtual void OnBubbleClosed() = 0;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(LocalCardMigrationBubbleController);
+};
+
+}  // namespace autofill
+
+#endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_UI_LOCAL_CARD_MIGRATION_BUBBLE_CONTROLLER_H_
diff --git a/components/autofill/core/common/autofill_features.cc b/components/autofill/core/common/autofill_features.cc
index 9ab52ed..f7e020f 100644
--- a/components/autofill/core/common/autofill_features.cc
+++ b/components/autofill/core/common/autofill_features.cc
@@ -95,6 +95,13 @@
     "AutofillSendExperimentIdsInPaymentsRPCs",
     base::FEATURE_ENABLED_BY_DEFAULT};
 
+// If enabled, only countries of recently-used addresses are sent in the
+// GetUploadDetails call to Payments. If disabled, whole recently-used addresses
+// are sent.
+const base::Feature kAutofillSendOnlyCountryInGetUploadDetails{
+    "AutofillSendOnlyCountryInGetUploadDetails",
+    base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Controls whether we show warnings in the Dev console for misused autocomplete
 // types.
 const base::Feature kAutofillShowAutocompleteConsoleWarnings{
diff --git a/components/autofill/core/common/autofill_features.h b/components/autofill/core/common/autofill_features.h
index ad3bb22..6a139f2 100644
--- a/components/autofill/core/common/autofill_features.h
+++ b/components/autofill/core/common/autofill_features.h
@@ -25,6 +25,7 @@
 extern const base::Feature kAutofillResetFullServerCardsOnAuthError;
 extern const base::Feature kAutofillRestrictUnownedFieldsToFormlessCheckout;
 extern const base::Feature kAutofillSendExperimentIdsInPaymentsRPCs;
+extern const base::Feature kAutofillSendOnlyCountryInGetUploadDetails;
 extern const base::Feature kAutofillShowAllSuggestionsOnPrefilledForms;
 extern const base::Feature kAutofillShowAutocompleteConsoleWarnings;
 extern const base::Feature kAutofillShowTypePredictions;
diff --git a/components/autofill_strings.grdp b/components/autofill_strings.grdp
index 7139c7ab..0ec5a9f 100644
--- a/components/autofill_strings.grdp
+++ b/components/autofill_strings.grdp
@@ -244,6 +244,16 @@
     This name will also be used when creating your Google payments account.
   </message>
 
+  <!-- Autofill Local card migration bubble or prompt -->
+  <if expr="not is_ios and not is_android">
+    <message name="IDS_AUTOFILL_LOCAL_CARD_MIGRATION_BUBBLE_TITLE" desc="Text to show in the Autofill local card migration intermediate bubble.">
+      Do you want to have all your cards in one place?
+    </message>
+    <message name="IDS_AUTOFILL_LOCAL_CARD_MIGRATION_BUBBLE_BUTTON_LABEL" desc="Text to show in the Autofill local card migration intermediate bubble button.">
+      Continue
+    </message>
+  </if>
+
   <!-- Autofill credit card suggestion popup -->
   <message name="IDS_AUTOFILL_CREDIT_CARD_EXPIRATION_DATE_ABBR" desc="Abbreviated label for credit card expiration date. [CHAR-LIMIT=32]">
     Exp: <ph name="EXPIRATION_MONTH">$1<ex>06</ex></ph>/<ph name="EXPIRATION_YEAR">$2<ex>17</ex></ph>
diff --git a/components/browser_sync/abstract_profile_sync_service_test.cc b/components/browser_sync/abstract_profile_sync_service_test.cc
index 8c0b263..f335b83 100644
--- a/components/browser_sync/abstract_profile_sync_service_test.cc
+++ b/components/browser_sync/abstract_profile_sync_service_test.cc
@@ -11,7 +11,7 @@
 #include "base/files/file_path.h"
 #include "base/location.h"
 #include "base/run_loop.h"
-#include "base/threading/thread_task_runner_handle.h"
+#include "base/threading/sequenced_task_runner_handle.h"
 #include "components/browser_sync/test_http_bridge_factory.h"
 #include "components/browser_sync/test_profile_sync_service.h"
 #include "components/sync/driver/glue/sync_backend_host_core.h"
@@ -105,7 +105,7 @@
   // send back the list of newly configured types instead and hope it doesn't
   // break anything.
   // Posted to avoid re-entrancy issues.
-  base::ThreadTaskRunnerHandle::Get()->PostTask(
+  base::SequencedTaskRunnerHandle::Get()->PostTask(
       FROM_HERE,
       base::Bind(
           &SyncEngineForProfileSyncTest::FinishConfigureDataTypesOnFrontendLoop,
diff --git a/components/browser_sync/profile_sync_test_util.cc b/components/browser_sync/profile_sync_test_util.cc
index f01e508..427723ce 100644
--- a/components/browser_sync/profile_sync_test_util.cc
+++ b/components/browser_sync/profile_sync_test_util.cc
@@ -8,7 +8,9 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "base/sequenced_task_runner.h"
 #include "base/single_thread_task_runner.h"
+#include "base/threading/sequenced_task_runner_handle.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "components/bookmarks/browser/bookmark_model.h"
 #include "components/history/core/browser/history_model_worker.h"
@@ -53,8 +55,8 @@
                        get_sync_service_callback,
                    const base::Callback<bookmarks::BookmarkModel*(void)>&
                        get_bookmark_model_callback,
-                   scoped_refptr<base::SingleThreadTaskRunner> db_thread,
-                   scoped_refptr<base::SingleThreadTaskRunner> file_thread,
+                   scoped_refptr<base::SequencedTaskRunner> db_thread,
+                   scoped_refptr<base::SequencedTaskRunner> file_thread,
                    history::HistoryService* history_service);
 
   ~BundleSyncClient() override;
@@ -81,8 +83,8 @@
   const base::Callback<bookmarks::BookmarkModel*(void)>
       get_bookmark_model_callback_;
   // These task runners, if not null, are used in CreateModelWorkerForGroup.
-  const scoped_refptr<base::SingleThreadTaskRunner> db_thread_;
-  const scoped_refptr<base::SingleThreadTaskRunner> file_thread_;
+  const scoped_refptr<base::SequencedTaskRunner> db_thread_;
+  const scoped_refptr<base::SequencedTaskRunner> file_thread_;
   history::HistoryService* history_service_;
 };
 
@@ -96,8 +98,8 @@
     const base::Callback<syncer::SyncService*(void)>& get_sync_service_callback,
     const base::Callback<bookmarks::BookmarkModel*(void)>&
         get_bookmark_model_callback,
-    scoped_refptr<base::SingleThreadTaskRunner> db_thread,
-    scoped_refptr<base::SingleThreadTaskRunner> file_thread,
+    scoped_refptr<base::SequencedTaskRunner> db_thread,
+    scoped_refptr<base::SequencedTaskRunner> file_thread,
     history::HistoryService* history_service)
     : syncer::FakeSyncClient(factory),
       pref_service_(pref_service),
@@ -232,12 +234,13 @@
       get_syncable_service_callback_, get_sync_service_callback_,
       get_bookmark_model_callback_,
       activate_model_creation_ ? bundle_->db_thread() : nullptr,
-      activate_model_creation_ ? base::ThreadTaskRunnerHandle::Get() : nullptr,
+      activate_model_creation_ ? base::SequencedTaskRunnerHandle::Get()
+                               : nullptr,
       history_service_);
 }
 
 ProfileSyncServiceBundle::ProfileSyncServiceBundle()
-    : db_thread_(base::ThreadTaskRunnerHandle::Get()),
+    : db_thread_(base::SequencedTaskRunnerHandle::Get()),
       signin_client_(&pref_service_),
 #if defined(OS_CHROMEOS)
       signin_manager_(&signin_client_, &account_tracker_),
diff --git a/components/browser_sync/profile_sync_test_util.h b/components/browser_sync/profile_sync_test_util.h
index fef9316..c2091d2 100644
--- a/components/browser_sync/profile_sync_test_util.h
+++ b/components/browser_sync/profile_sync_test_util.h
@@ -158,15 +158,15 @@
     return &fake_invalidation_service_;
   }
 
-  base::SingleThreadTaskRunner* db_thread() { return db_thread_.get(); }
+  base::SequencedTaskRunner* db_thread() { return db_thread_.get(); }
 
   void set_db_thread(
-      const scoped_refptr<base::SingleThreadTaskRunner>& db_thread) {
+      const scoped_refptr<base::SequencedTaskRunner>& db_thread) {
     db_thread_ = db_thread;
   }
 
  private:
-  scoped_refptr<base::SingleThreadTaskRunner> db_thread_;
+  scoped_refptr<base::SequencedTaskRunner> db_thread_;
   sync_preferences::TestingPrefServiceSyncable pref_service_;
   TestSigninClient signin_client_;
   AccountTrackerService account_tracker_;
diff --git a/components/browser_sync/signin_confirmation_helper.cc b/components/browser_sync/signin_confirmation_helper.cc
index 1270e0a..f7f9f30 100644
--- a/components/browser_sync/signin_confirmation_helper.cc
+++ b/components/browser_sync/signin_confirmation_helper.cc
@@ -5,10 +5,13 @@
 #include "components/browser_sync/signin_confirmation_helper.h"
 
 #include <memory>
+#include <utility>
 
-#include "base/single_thread_task_runner.h"
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/sequenced_task_runner.h"
 #include "base/strings/string16.h"
-#include "base/threading/thread_task_runner_handle.h"
+#include "base/threading/sequenced_task_runner_handle.h"
 #include "components/history/core/browser/history_backend.h"
 #include "components/history/core/browser/history_db_task.h"
 #include "components/history/core/browser/history_service.h"
@@ -21,8 +24,9 @@
 // Determines whether there are any typed URLs in a history backend.
 class HasTypedURLsTask : public history::HistoryDBTask {
  public:
-  explicit HasTypedURLsTask(const base::Callback<void(bool)>& cb)
-      : has_typed_urls_(false), cb_(cb) {}
+  explicit HasTypedURLsTask(base::OnceCallback<void(bool)> cb)
+      : has_typed_urls_(false), cb_(std::move(cb)) {}
+  ~HasTypedURLsTask() override {}
 
   bool RunOnDBThread(history::HistoryBackend* backend,
                      history::HistoryDatabase* db) override {
@@ -36,27 +40,25 @@
     return true;
   }
 
-  void DoneRunOnMainThread() override { cb_.Run(has_typed_urls_); }
+  void DoneRunOnMainThread() override { std::move(cb_).Run(has_typed_urls_); }
 
  private:
-  ~HasTypedURLsTask() override {}
-
   bool has_typed_urls_;
-  base::Callback<void(bool)> cb_;
+  base::OnceCallback<void(bool)> cb_;
 };
 
 }  // namespace
 
 SigninConfirmationHelper::SigninConfirmationHelper(
     history::HistoryService* history_service,
-    const base::Callback<void(bool)>& return_result)
-    : origin_thread_(base::ThreadTaskRunnerHandle::Get()),
+    base::OnceCallback<void(bool)> return_result)
+    : origin_sequence_(base::SequencedTaskRunnerHandle::Get()),
       history_service_(history_service),
       pending_requests_(0),
-      return_result_(return_result) {}
+      return_result_(std::move(return_result)) {}
 
 SigninConfirmationHelper::~SigninConfirmationHelper() {
-  DCHECK(origin_thread_->BelongsToCurrentThread());
+  DCHECK(origin_sequence_->RunsTasksInCurrentSequence());
 }
 
 void SigninConfirmationHelper::OnHistoryQueryResults(
@@ -95,23 +97,23 @@
   }
   history_service_->ScheduleDBTask(
       FROM_HERE,
-      std::unique_ptr<history::HistoryDBTask>(new HasTypedURLsTask(base::Bind(
-          &SigninConfirmationHelper::ReturnResult, base::Unretained(this)))),
+      std::make_unique<HasTypedURLsTask>(base::BindOnce(
+          &SigninConfirmationHelper::ReturnResult, base::Unretained(this))),
       &task_tracker_);
 }
 
 void SigninConfirmationHelper::PostResult(bool result) {
-  origin_thread_->PostTask(FROM_HERE,
-                           base::Bind(&SigninConfirmationHelper::ReturnResult,
-                                      base::Unretained(this), result));
+  origin_sequence_->PostTask(
+      FROM_HERE, base::BindOnce(&SigninConfirmationHelper::ReturnResult,
+                                base::Unretained(this), result));
 }
 
 void SigninConfirmationHelper::ReturnResult(bool result) {
-  DCHECK(origin_thread_->BelongsToCurrentThread());
+  DCHECK(origin_sequence_->RunsTasksInCurrentSequence());
   // Pass |true| into the callback as soon as one of the tasks passes a
   // result of |true|, otherwise pass the last returned result.
   if (--pending_requests_ == 0 || result) {
-    return_result_.Run(result);
+    std::move(return_result_).Run(result);
 
     // This leaks at shutdown if the HistoryService is destroyed, but
     // the process is going to die anyway.
diff --git a/components/browser_sync/signin_confirmation_helper.h b/components/browser_sync/signin_confirmation_helper.h
index c006653..4260aa9 100644
--- a/components/browser_sync/signin_confirmation_helper.h
+++ b/components/browser_sync/signin_confirmation_helper.h
@@ -12,7 +12,7 @@
 #include "base/task/cancelable_task_tracker.h"
 
 namespace base {
-class SingleThreadTaskRunner;
+class SequencedTaskRunner;
 }
 
 namespace history {
@@ -28,7 +28,7 @@
 class SigninConfirmationHelper {
  public:
   SigninConfirmationHelper(history::HistoryService* history_service,
-                           const base::Callback<void(bool)>& return_result);
+                           base::OnceCallback<void(bool)> return_result);
 
   // This helper checks if there are history entries in the history service.
   void CheckHasHistory(int max_entries);
@@ -44,15 +44,15 @@
   void OnHistoryQueryResults(size_t max_entries,
                              history::QueryResults* results);
 
-  // Posts the given result to the origin thread.
+  // Posts the given result to the origin sequence.
   void PostResult(bool result);
 
   // Calls |return_result_| if |result| == true or if it's the result of the
   // last pending check.
   void ReturnResult(bool result);
 
-  // The task runner for the thread this object was constructed on.
-  const scoped_refptr<base::SingleThreadTaskRunner> origin_thread_;
+  // The task runner for the sequence this object was constructed on.
+  const scoped_refptr<base::SequencedTaskRunner> origin_sequence_;
 
   // Pointer to the history service.
   history::HistoryService* history_service_;
@@ -64,7 +64,7 @@
   int pending_requests_;
 
   // Callback to pass the result back to the caller.
-  const base::Callback<void(bool)> return_result_;
+  base::OnceCallback<void(bool)> return_result_;
 
   DISALLOW_COPY_AND_ASSIGN(SigninConfirmationHelper);
 };
diff --git a/components/consent_auditor/consent_sync_bridge_impl_unittest.cc b/components/consent_auditor/consent_sync_bridge_impl_unittest.cc
index b810efb..f446f3c 100644
--- a/components/consent_auditor/consent_sync_bridge_impl_unittest.cc
+++ b/components/consent_auditor/consent_sync_bridge_impl_unittest.cc
@@ -236,7 +236,12 @@
   // initilization is in progress.
   EXPECT_CALL(*processor(), Put(_, _, _)).Times(0);
   bridge()->RecordConsent(SpecificsUniquePtr(/*client_consent_time_usec=*/1u));
-  EXPECT_THAT(GetAllData(), IsEmpty());
+
+  // When store is fully initialized, the consent should be reported to the
+  // processor.
+  ON_CALL(*processor(), IsTrackingMetadata()).WillByDefault(Return(true));
+  EXPECT_CALL(*processor(), Put(_, _, _));
+  base::RunLoop().RunUntilIdle();
 }
 
 // User consents should be buffered if the store and processor is not fully
diff --git a/components/cryptauth/cryptauth_device_manager_impl.cc b/components/cryptauth/cryptauth_device_manager_impl.cc
index cc07d6f..6a26f0a 100644
--- a/components/cryptauth/cryptauth_device_manager_impl.cc
+++ b/components/cryptauth/cryptauth_device_manager_impl.cc
@@ -110,7 +110,9 @@
 std::unique_ptr<base::DictionaryValue>
 SupportedAndEnabledSoftwareFeaturesToDictionaryValue(
     const google::protobuf::RepeatedField<int>& supported_software_features,
-    const google::protobuf::RepeatedField<int>& enabled_software_features) {
+    const google::protobuf::RepeatedField<int>& enabled_software_features,
+    bool legacy_unlock_key,
+    bool legacy_mobile_hotspot_supported) {
   std::unique_ptr<base::DictionaryValue> dictionary =
       std::make_unique<base::DictionaryValue>();
 
@@ -140,6 +142,25 @@
                            static_cast<int>(SoftwareFeatureState::kEnabled));
   }
 
+  // If software features for EASY_UNLOCK_HOST or MAGIC_TETHER_HOST have not
+  // been set, check to see if the deprecated corresponding booleans are
+  // enabled. This can happen if the CryptAuth server is not yet serving
+  // software features, and only serving the deprecated booleans.
+  int software_feature_state;
+  std::string software_feature_key;
+  software_feature_key = std::to_string(SoftwareFeature::EASY_UNLOCK_HOST);
+  if (legacy_unlock_key &&
+      !dictionary->GetInteger(software_feature_key, &software_feature_state)) {
+    dictionary->SetInteger(software_feature_key,
+                           static_cast<int>(SoftwareFeatureState::kEnabled));
+  }
+  software_feature_key = std::to_string(SoftwareFeature::MAGIC_TETHER_HOST);
+  if (legacy_mobile_hotspot_supported &&
+      !dictionary->GetInteger(software_feature_key, &software_feature_state)) {
+    dictionary->SetInteger(software_feature_key,
+                           static_cast<int>(SoftwareFeatureState::kSupported));
+  }
+
   return dictionary;
 }
 
@@ -180,10 +201,6 @@
                           bluetooth_address_b64);
   }
 
-  if (device.has_unlock_key()) {
-    dictionary->SetBoolean(kExternalDeviceKeyUnlockKey, device.unlock_key());
-  }
-
   if (device.has_unlockable()) {
     dictionary->SetBoolean(kExternalDeviceKeyUnlockable, device.unlockable());
   }
@@ -193,11 +210,6 @@
                           std::to_string(device.last_update_time_millis()));
   }
 
-  if (device.has_mobile_hotspot_supported()) {
-    dictionary->SetBoolean(kExternalDeviceKeyMobileHotspotSupported,
-                           device.mobile_hotspot_supported());
-  }
-
   if (device.has_device_type() && DeviceType_IsValid(device.device_type())) {
     dictionary->SetInteger(kExternalDeviceKeyDeviceType, device.device_type());
   }
@@ -214,10 +226,19 @@
     dictionary->SetBoolean(kExternalDeviceKeyPixelPhone, device.pixel_phone());
   }
 
+  // In the case that the CryptAuth server is not yet serving SoftwareFeatures,
+  // but only the deprecated booleans, |unlock_key| and
+  // |mobile_hotspot_supported|, pass in the legacy values in order to correctly
+  // populate the SoftwareFeatures.
+  bool legacy_unlock_key = device.has_unlock_key() && device.unlock_key();
+  bool legacy_mobile_hotspot_supported =
+      device.has_mobile_hotspot_supported() &&
+      device.mobile_hotspot_supported();
   dictionary->Set(kDictionaryKeySoftwareFeatures,
                   SupportedAndEnabledSoftwareFeaturesToDictionaryValue(
                       device.supported_software_features(),
-                      device.enabled_software_features()));
+                      device.enabled_software_features(), legacy_unlock_key,
+                      legacy_mobile_hotspot_supported));
 
   return dictionary;
 }
@@ -270,7 +291,9 @@
 
 void AddSoftwareFeaturesToExternalDevice(
     const base::DictionaryValue& software_features_dictionary,
-    ExternalDeviceInfo* external_device) {
+    ExternalDeviceInfo* external_device,
+    bool old_unlock_key_value_from_prefs,
+    bool old_mobile_hotspot_supported_from_prefs) {
   for (const auto& it : software_features_dictionary.DictItems()) {
     int software_feature_state;
     if (!it.second.GetAsInteger(&software_feature_state)) {
@@ -291,6 +314,31 @@
         break;
     }
   }
+
+  // ExternalDeviceInfos's |unlock_key| and |mobile_hotspot_supported| fields
+  // are deprecated, but it may be the case that after an update to Chrome, the
+  // prefs reflect the old style of using these deprecated fields, instead of
+  // software features. To work around this, these pref values are migrated to
+  // software features locally.
+  if (old_unlock_key_value_from_prefs) {
+    if (!base::ContainsValue(external_device->supported_software_features(),
+                             SoftwareFeature::EASY_UNLOCK_HOST)) {
+      external_device->add_supported_software_features(
+          SoftwareFeature::EASY_UNLOCK_HOST);
+    }
+    if (!base::ContainsValue(external_device->enabled_software_features(),
+                             SoftwareFeature::EASY_UNLOCK_HOST)) {
+      external_device->add_enabled_software_features(
+          SoftwareFeature::EASY_UNLOCK_HOST);
+    }
+  }
+  if (old_mobile_hotspot_supported_from_prefs) {
+    if (!base::ContainsValue(external_device->supported_software_features(),
+                             SoftwareFeature::MAGIC_TETHER_HOST)) {
+      external_device->add_supported_software_features(
+          SoftwareFeature::MAGIC_TETHER_HOST);
+    }
+  }
 }
 
 // Converts an unlock key dictionary stored in user prefs to an
@@ -336,10 +384,8 @@
     }
   }
 
-  bool unlock_key;
-  if (dictionary.GetBoolean(kExternalDeviceKeyUnlockKey, &unlock_key))
-    external_device->set_unlock_key(unlock_key);
-
+  // TODO(crbug.com/848477): Migrate |unlockable| into
+  // |supported_software_features|.
   bool unlockable;
   if (dictionary.GetBoolean(kExternalDeviceKeyUnlockable, &unlockable))
     external_device->set_unlockable(unlockable);
@@ -357,12 +403,6 @@
     }
   }
 
-  bool mobile_hotspot_supported;
-  if (dictionary.GetBoolean(kExternalDeviceKeyMobileHotspotSupported,
-                            &mobile_hotspot_supported)) {
-    external_device->set_mobile_hotspot_supported(mobile_hotspot_supported);
-  }
-
   int device_type;
   if (dictionary.GetInteger(kExternalDeviceKeyDeviceType, &device_type) &&
       DeviceType_IsValid(device_type)) {
@@ -381,11 +421,18 @@
   if (dictionary.GetBoolean(kExternalDeviceKeyPixelPhone, &pixel_phone))
     external_device->set_pixel_phone(pixel_phone);
 
+  bool unlock_key = false;
+  dictionary.GetBoolean(kExternalDeviceKeyUnlockKey, &unlock_key);
+  bool mobile_hotspot_supported = false;
+  dictionary.GetBoolean(kExternalDeviceKeyMobileHotspotSupported,
+                        &mobile_hotspot_supported);
+
   const base::DictionaryValue* software_features_dictionary;
   if (dictionary.GetDictionary(kDictionaryKeySoftwareFeatures,
                                &software_features_dictionary)) {
     AddSoftwareFeaturesToExternalDevice(*software_features_dictionary,
-                                        external_device);
+                                        external_device, unlock_key,
+                                        mobile_hotspot_supported);
   }
 
   return true;
@@ -514,8 +561,10 @@
     const {
   std::vector<ExternalDeviceInfo> unlock_keys;
   for (const auto& device : synced_devices_) {
-    if (device.unlock_key())
+    if (base::ContainsValue(device.enabled_software_features(),
+                            SoftwareFeature::EASY_UNLOCK_HOST)) {
       unlock_keys.push_back(device);
+    }
   }
   return unlock_keys;
 }
@@ -524,8 +573,11 @@
     const {
   std::vector<ExternalDeviceInfo> unlock_keys;
   for (const auto& device : synced_devices_) {
-    if (device.unlock_key() && device.pixel_phone())
+    if (base::ContainsValue(device.enabled_software_features(),
+                            SoftwareFeature::EASY_UNLOCK_HOST) &&
+        device.pixel_phone()) {
       unlock_keys.push_back(device);
+    }
   }
   return unlock_keys;
 }
@@ -534,8 +586,10 @@
     const {
   std::vector<ExternalDeviceInfo> tether_hosts;
   for (const auto& device : synced_devices_) {
-    if (device.mobile_hotspot_supported())
+    if (base::ContainsValue(device.supported_software_features(),
+                            SoftwareFeature::MAGIC_TETHER_HOST)) {
       tether_hosts.push_back(device);
+    }
   }
   return tether_hosts;
 }
@@ -544,7 +598,9 @@
 CryptAuthDeviceManagerImpl::GetPixelTetherHosts() const {
   std::vector<ExternalDeviceInfo> tether_hosts;
   for (const auto& device : synced_devices_) {
-    if (device.mobile_hotspot_supported() && device.pixel_phone())
+    if (base::ContainsValue(device.supported_software_features(),
+                            SoftwareFeature::MAGIC_TETHER_HOST) &&
+        device.pixel_phone())
       tether_hosts.push_back(device);
   }
   return tether_hosts;
diff --git a/components/cryptauth/cryptauth_device_manager_impl_unittest.cc b/components/cryptauth/cryptauth_device_manager_impl_unittest.cc
index 432a24e..d5c96a7 100644
--- a/components/cryptauth/cryptauth_device_manager_impl_unittest.cc
+++ b/components/cryptauth/cryptauth_device_manager_impl_unittest.cc
@@ -46,20 +46,16 @@
 const double kLastSyncTimeSeconds = kInitialTimeNowSeconds - (60 * 60 * 5);
 
 // Unlock key fields originally stored in the user prefs.
-const char kStoredPublicKey[] = "AAPL";
-const char kStoredDeviceName[] = "iPhone 6";
+const char kStoredPublicKey[] = "storedPublicKey";
+const char kStoredDeviceName[] = "Pixel 2";
 const char kStoredBluetoothAddress[] = "12:34:56:78:90:AB";
-const bool kStoredUnlockKey = true;
 const bool kStoredUnlockable = false;
-const bool kStoredMobileHotspotSupported = true;
 
 // ExternalDeviceInfo fields for the synced unlock key.
 const char kPublicKey1[] = "GOOG";
 const char kDeviceName1[] = "Pixel XL";
 const char kBluetoothAddress1[] = "aa:bb:cc:ee:dd:ff";
-const bool kUnlockKey1 = true;
 const bool kUnlockable1 = false;
-const bool kMobileHotspotSupported1 = true;
 const char kBeaconSeed1Data[] = "beaconSeed1Data";
 const int64_t kBeaconSeed1StartTime = 123456;
 const int64_t kBeaconSeed1EndTime = 123457;
@@ -70,11 +66,9 @@
 const bool kPixelPhone1 = true;
 
 // ExternalDeviceInfo fields for a non-synced unlockable device.
-const char kPublicKey2[] = "MSFT";
-const char kDeviceName2[] = "Surface Pro 3";
-const bool kUnlockKey2 = false;
+const char kPublicKey2[] = "CROS";
+const char kDeviceName2[] = "Pixelbook";
 const bool kUnlockable2 = true;
-const bool kMobileHotspotSupported2 = false;
 const char kBeaconSeed3Data[] = "beaconSeed3Data";
 const int64_t kBeaconSeed3StartTime = 123456;
 const int64_t kBeaconSeed3EndTime = 123457;
@@ -149,19 +143,20 @@
     EXPECT_EQ(expected_device.has_pixel_phone(), device.has_pixel_phone());
     EXPECT_EQ(expected_device.pixel_phone(), device.pixel_phone());
 
-    ASSERT_EQ(expected_device.supported_software_features_size(),
+    EXPECT_EQ(expected_device.supported_software_features_size(),
               device.supported_software_features_size());
-    for (int i = 0; i < expected_device.supported_software_features_size();
-         i++) {
-      EXPECT_EQ(expected_device.supported_software_features(i),
-                device.supported_software_features(i));
+    for (const auto& software_feature :
+         expected_device.supported_software_features()) {
+      EXPECT_TRUE(base::ContainsValue(device.supported_software_features(),
+                                      software_feature));
     }
 
-    ASSERT_EQ(expected_device.enabled_software_features_size(),
+    EXPECT_EQ(expected_device.enabled_software_features_size(),
               device.enabled_software_features_size());
-    for (int i = 0; i < expected_device.enabled_software_features_size(); i++) {
-      EXPECT_EQ(expected_device.enabled_software_features(i),
-                device.enabled_software_features(i));
+    for (const auto& software_feature :
+         expected_device.enabled_software_features()) {
+      EXPECT_TRUE(base::ContainsValue(device.enabled_software_features(),
+                                      software_feature));
     }
   }
 }
@@ -410,9 +405,7 @@
     unlock_key.set_public_key(kPublicKey1);
     unlock_key.set_friendly_device_name(kDeviceName1);
     unlock_key.set_bluetooth_address(kBluetoothAddress1);
-    unlock_key.set_unlock_key(kUnlockKey1);
     unlock_key.set_unlockable(kUnlockable1);
-    unlock_key.set_mobile_hotspot_supported(kMobileHotspotSupported1);
     BeaconSeed* seed1 = unlock_key.add_beacon_seeds();
     seed1->set_data(kBeaconSeed1Data);
     seed1->set_start_time_millis(kBeaconSeed1StartTime);
@@ -424,6 +417,9 @@
     unlock_key.set_arc_plus_plus(kArcPlusPlus1);
     unlock_key.set_pixel_phone(kPixelPhone1);
     unlock_key.add_supported_software_features(
+        SoftwareFeature::EASY_UNLOCK_HOST);
+    unlock_key.add_enabled_software_features(SoftwareFeature::EASY_UNLOCK_HOST);
+    unlock_key.add_supported_software_features(
         SoftwareFeature::BETTER_TOGETHER_HOST);
     unlock_key.add_supported_software_features(
         SoftwareFeature::BETTER_TOGETHER_CLIENT);
@@ -434,9 +430,7 @@
     ExternalDeviceInfo unlockable_device;
     unlockable_device.set_public_key(kPublicKey2);
     unlockable_device.set_friendly_device_name(kDeviceName2);
-    unlockable_device.set_unlock_key(kUnlockKey2);
     unlockable_device.set_unlockable(kUnlockable2);
-    unlockable_device.set_mobile_hotspot_supported(kMobileHotspotSupported2);
     BeaconSeed* seed3 = unlockable_device.add_beacon_seeds();
     seed3->set_data(kBeaconSeed3Data);
     seed3->set_start_time_millis(kBeaconSeed3StartTime);
@@ -447,11 +441,11 @@
     seed4->set_end_time_millis(kBeaconSeed4EndTime);
     unlockable_device.set_arc_plus_plus(kArcPlusPlus2);
     unlockable_device.set_pixel_phone(kPixelPhone2);
-    unlock_key.add_supported_software_features(
+    unlockable_device.add_supported_software_features(
         SoftwareFeature::MAGIC_TETHER_HOST);
-    unlock_key.add_supported_software_features(
+    unlockable_device.add_supported_software_features(
         SoftwareFeature::MAGIC_TETHER_CLIENT);
-    unlock_key.add_enabled_software_features(
+    unlockable_device.add_enabled_software_features(
         SoftwareFeature::MAGIC_TETHER_HOST);
     devices_in_response_.push_back(unlockable_device);
   }
@@ -490,13 +484,11 @@
     device_dictionary->SetString("public_key", public_key_b64);
     device_dictionary->SetString("device_name", device_name_b64);
     device_dictionary->SetString("bluetooth_address", bluetooth_address_b64);
-    device_dictionary->SetBoolean("unlock_key", kStoredUnlockKey);
     device_dictionary->SetBoolean("unlockable", kStoredUnlockable);
-    device_dictionary->SetBoolean("mobile_hotspot_supported",
-                                  kStoredMobileHotspotSupported);
     device_dictionary->Set("beacon_seeds", std::make_unique<base::ListValue>());
     device_dictionary->Set("software_features",
                            std::make_unique<base::DictionaryValue>());
+
     {
       ListPrefUpdate update(&pref_service_,
                             prefs::kCryptAuthDeviceSyncUnlockKeys);
@@ -657,10 +649,60 @@
   EXPECT_EQ(kStoredPublicKey, synced_devices[0].public_key());
   EXPECT_EQ(kStoredDeviceName, synced_devices[0].friendly_device_name());
   EXPECT_EQ(kStoredBluetoothAddress, synced_devices[0].bluetooth_address());
-  EXPECT_EQ(kStoredUnlockKey, synced_devices[0].unlock_key());
   EXPECT_EQ(kStoredUnlockable, synced_devices[0].unlockable());
 }
 
+// ExternalDeviceInfos's |unlock_key| and |mobile_hotspot_supported| fields
+// are deprecated, but it may be the case that after an update to Chrome, the
+// prefs reflect the old style of using these deprecated fields, instead of
+// software features. This test ensures the CryptAuthDeviceManager considers
+// these deprecated booleans, and populates the correct software features.
+TEST_F(
+    CryptAuthDeviceManagerImplTest,
+    InitWithExistingPrefs_MigrateDeprecateBooleansFromPrefsToSoftwareFeature) {
+  ListPrefUpdate update_clear(&pref_service_,
+                              prefs::kCryptAuthDeviceSyncUnlockKeys);
+  update_clear.Get()->Clear();
+
+  // Simulate a deprecated device being persisted to prefs.
+  auto device_dictionary = std::make_unique<base::DictionaryValue>();
+  std::string public_key_b64;
+  base::Base64UrlEncode(kStoredPublicKey,
+                        base::Base64UrlEncodePolicy::INCLUDE_PADDING,
+                        &public_key_b64);
+  device_dictionary->SetString("public_key", public_key_b64);
+  device_dictionary->SetBoolean("unlock_key", true);
+  device_dictionary->SetBoolean("mobile_hotspot_supported", true);
+  device_dictionary->Set("software_features",
+                         std::make_unique<base::DictionaryValue>());
+
+  ListPrefUpdate update(&pref_service_, prefs::kCryptAuthDeviceSyncUnlockKeys);
+  update.Get()->Append(std::move(device_dictionary));
+
+  device_manager_.reset(new TestCryptAuthDeviceManager(
+      &clock_, client_factory_.get(), &gcm_manager_, &pref_service_));
+  device_manager_->Start();
+
+  // Ensure that the deprecated booleans are not exposed in the final
+  // ExternalDeviceInfo, but rather in the correct software features.
+  auto synced_devices = device_manager_->GetSyncedDevices();
+  ASSERT_EQ(1u, synced_devices.size());
+  EXPECT_EQ(kStoredPublicKey, synced_devices[0].public_key());
+  EXPECT_FALSE(synced_devices[0].unlock_key());
+  EXPECT_FALSE(synced_devices[0].mobile_hotspot_supported());
+
+  EXPECT_EQ(2, synced_devices[0].supported_software_features().size());
+  EXPECT_TRUE(
+      base::ContainsValue(synced_devices[0].supported_software_features(),
+                          SoftwareFeature::EASY_UNLOCK_HOST));
+  EXPECT_TRUE(
+      base::ContainsValue(synced_devices[0].supported_software_features(),
+                          SoftwareFeature::MAGIC_TETHER_HOST));
+  EXPECT_EQ(1, synced_devices[0].enabled_software_features().size());
+  EXPECT_TRUE(base::ContainsValue(synced_devices[0].enabled_software_features(),
+                                  SoftwareFeature::EASY_UNLOCK_HOST));
+}
+
 TEST_F(CryptAuthDeviceManagerImplTest, SyncSucceedsForFirstTime) {
   pref_service_.ClearPref(prefs::kCryptAuthDeviceSyncLastSyncTimeSeconds);
   device_manager_->Start();
@@ -798,9 +840,7 @@
   synced_device.set_public_key(kStoredPublicKey);
   synced_device.set_friendly_device_name(kStoredDeviceName);
   synced_device.set_bluetooth_address(kStoredBluetoothAddress);
-  synced_device.set_unlock_key(kStoredUnlockKey);
   synced_device.set_unlockable(kStoredUnlockable);
-  synced_device.set_mobile_hotspot_supported(kStoredMobileHotspotSupported);
   GetMyDevicesResponse get_my_devices_response;
   get_my_devices_response.add_devices()->CopyFrom(synced_device);
   success_callback_.Run(get_my_devices_response);
@@ -834,7 +874,11 @@
   synced_device2.set_public_key("new public key");
   synced_device2.set_friendly_device_name("new device name");
   synced_device2.set_bluetooth_address("aa:bb:cc:dd:ee:ff");
-  synced_device2.set_unlock_key(true);
+  synced_device2.add_supported_software_features(
+      SoftwareFeature::EASY_UNLOCK_HOST);
+  synced_device2.add_enabled_software_features(
+      SoftwareFeature::EASY_UNLOCK_HOST);
+
   response.add_devices()->CopyFrom(synced_device2);
 
   std::vector<ExternalDeviceInfo> expected_devices;
@@ -899,11 +943,10 @@
   // all fields filled out.
   ExternalDeviceInfo device_with_only_public_key;
   device_with_only_public_key.set_public_key("publicKey1");
-  // Currently, CryptAuthDeviceManager only stores devices which are unlock
-  // keys, so set_unlock_key(true) must be called here for storage to work.
-  // TODO(khorimoto): Remove this when support for storing all types of devices
-  // is added.
-  device_with_only_public_key.set_unlock_key(true);
+  device_with_only_public_key.add_supported_software_features(
+      SoftwareFeature::EASY_UNLOCK_HOST);
+  device_with_only_public_key.add_enabled_software_features(
+      SoftwareFeature::EASY_UNLOCK_HOST);
 
   // Second, use a device with all fields filled out. This ensures that all
   // device details are properly saved.
@@ -911,10 +954,8 @@
   device_with_all_fields.set_public_key("publicKey2");
   device_with_all_fields.set_friendly_device_name("deviceName");
   device_with_all_fields.set_bluetooth_address("aa:bb:cc:dd:ee:ff");
-  device_with_all_fields.set_unlock_key(true);
   device_with_all_fields.set_unlockable(true);
   device_with_all_fields.set_last_update_time_millis(123456789L);
-  device_with_all_fields.set_mobile_hotspot_supported(true);
   device_with_all_fields.set_device_type(DeviceType::ANDROIDOS);
 
   BeaconSeed seed1;
@@ -938,7 +979,7 @@
       SoftwareFeature::MAGIC_TETHER_HOST);
 
   device_with_all_fields.add_enabled_software_features(
-      SoftwareFeature::MAGIC_TETHER_HOST);
+      SoftwareFeature::EASY_UNLOCK_HOST);
 
   std::vector<ExternalDeviceInfo> expected_devices;
   expected_devices.push_back(device_with_only_public_key);
@@ -981,8 +1022,82 @@
 
   // Only tether hosts.
   ExpectSyncedDevicesAreEqual(
-      std::vector<ExternalDeviceInfo>(1, devices_in_response_[0]),
+      std::vector<ExternalDeviceInfo>(1, devices_in_response_[1]),
       device_manager_->GetTetherHosts());
 }
 
+TEST_F(CryptAuthDeviceManagerImplTest,
+       TestDeprecatedBooleansArePersistedOnlyAsSoftwareFeatures) {
+  device_manager_->Start();
+
+  ExternalDeviceInfo device;
+  device.set_public_key("public key");
+  device.set_friendly_device_name("deprecated device");
+  device.set_unlock_key(true);
+  device.set_mobile_hotspot_supported(true);
+
+  devices_in_response_.push_back(device);
+  get_my_devices_response_.add_devices()->CopyFrom(device);
+
+  FireSchedulerForSync(INVOCATION_REASON_PERIODIC);
+  ASSERT_FALSE(success_callback_.is_null());
+  EXPECT_CALL(*this, OnSyncFinishedProxy(
+                         CryptAuthDeviceManager::SyncResult::SUCCESS,
+                         CryptAuthDeviceManager::DeviceChangeResult::CHANGED));
+  success_callback_.Run(get_my_devices_response_);
+
+  ExternalDeviceInfo synced_device = device_manager_->GetSyncedDevices()[2];
+
+  EXPECT_FALSE(synced_device.unlock_key());
+  EXPECT_FALSE(synced_device.mobile_hotspot_supported());
+
+  EXPECT_TRUE(base::ContainsValue(synced_device.supported_software_features(),
+                                  SoftwareFeature::EASY_UNLOCK_HOST));
+  EXPECT_TRUE(base::ContainsValue(synced_device.enabled_software_features(),
+                                  SoftwareFeature::EASY_UNLOCK_HOST));
+  EXPECT_TRUE(base::ContainsValue(synced_device.supported_software_features(),
+                                  SoftwareFeature::MAGIC_TETHER_HOST));
+  EXPECT_FALSE(base::ContainsValue(synced_device.enabled_software_features(),
+                                   SoftwareFeature::MAGIC_TETHER_HOST));
+}
+
+TEST_F(CryptAuthDeviceManagerImplTest,
+       TestIgnoreDeprecatedBooleansIfSoftwareFeaturesArePresent) {
+  device_manager_->Start();
+
+  ExternalDeviceInfo device;
+  device.set_public_key("public key");
+  device.set_friendly_device_name("deprecated device");
+  device.set_unlock_key(false);
+  device.set_mobile_hotspot_supported(false);
+
+  device.add_supported_software_features(SoftwareFeature::EASY_UNLOCK_HOST);
+  device.add_enabled_software_features(SoftwareFeature::EASY_UNLOCK_HOST);
+  device.add_supported_software_features(SoftwareFeature::MAGIC_TETHER_HOST);
+
+  devices_in_response_.push_back(device);
+  get_my_devices_response_.add_devices()->CopyFrom(device);
+
+  FireSchedulerForSync(INVOCATION_REASON_PERIODIC);
+  ASSERT_FALSE(success_callback_.is_null());
+  EXPECT_CALL(*this, OnSyncFinishedProxy(
+                         CryptAuthDeviceManager::SyncResult::SUCCESS,
+                         CryptAuthDeviceManager::DeviceChangeResult::CHANGED));
+  success_callback_.Run(get_my_devices_response_);
+
+  ExternalDeviceInfo synced_device = device_manager_->GetSyncedDevices()[2];
+
+  EXPECT_FALSE(synced_device.unlock_key());
+  EXPECT_FALSE(synced_device.mobile_hotspot_supported());
+
+  EXPECT_TRUE(base::ContainsValue(synced_device.supported_software_features(),
+                                  SoftwareFeature::EASY_UNLOCK_HOST));
+  EXPECT_TRUE(base::ContainsValue(synced_device.enabled_software_features(),
+                                  SoftwareFeature::EASY_UNLOCK_HOST));
+  EXPECT_TRUE(base::ContainsValue(synced_device.supported_software_features(),
+                                  SoftwareFeature::MAGIC_TETHER_HOST));
+  EXPECT_FALSE(base::ContainsValue(synced_device.enabled_software_features(),
+                                   SoftwareFeature::MAGIC_TETHER_HOST));
+}
+
 }  // namespace cryptauth
diff --git a/components/cryptauth/remote_device.cc b/components/cryptauth/remote_device.cc
index d248c5e..c1fb898 100644
--- a/components/cryptauth/remote_device.cc
+++ b/components/cryptauth/remote_device.cc
@@ -39,18 +39,13 @@
   return device_id;
 }
 
-RemoteDevice::RemoteDevice()
-    : unlock_key(false),
-      supports_mobile_hotspot(false),
-      last_update_time_millis(0L) {}
+RemoteDevice::RemoteDevice() : last_update_time_millis(0L) {}
 
 RemoteDevice::RemoteDevice(
     const std::string& user_id,
     const std::string& name,
     const std::string& public_key,
     const std::string& persistent_symmetric_key,
-    bool unlock_key,
-    bool supports_mobile_hotspot,
     int64_t last_update_time_millis,
     const std::map<SoftwareFeature, SoftwareFeatureState>& software_features,
     const std::vector<BeaconSeed>& beacon_seeds)
@@ -58,8 +53,6 @@
       name(name),
       public_key(public_key),
       persistent_symmetric_key(persistent_symmetric_key),
-      unlock_key(unlock_key),
-      supports_mobile_hotspot(supports_mobile_hotspot),
       last_update_time_millis(last_update_time_millis),
       software_features(software_features),
       beacon_seeds(beacon_seeds) {}
@@ -76,8 +69,6 @@
   return user_id == other.user_id && name == other.name &&
          public_key == other.public_key &&
          persistent_symmetric_key == other.persistent_symmetric_key &&
-         unlock_key == other.unlock_key &&
-         supports_mobile_hotspot == other.supports_mobile_hotspot &&
          last_update_time_millis == other.last_update_time_millis &&
          software_features == other.software_features &&
          AreBeaconSeedsEqual(beacon_seeds, other.beacon_seeds);
diff --git a/components/cryptauth/remote_device.h b/components/cryptauth/remote_device.h
index 22a83bd..157b3816 100644
--- a/components/cryptauth/remote_device.h
+++ b/components/cryptauth/remote_device.h
@@ -23,8 +23,6 @@
   std::string name;
   std::string public_key;
   std::string persistent_symmetric_key;
-  bool unlock_key;
-  bool supports_mobile_hotspot;
   int64_t last_update_time_millis;
   std::map<SoftwareFeature, SoftwareFeatureState> software_features;
   std::vector<BeaconSeed> beacon_seeds;
@@ -35,8 +33,6 @@
       const std::string& name,
       const std::string& public_key,
       const std::string& persistent_symmetric_key,
-      bool unlock_key,
-      bool supports_mobile_hotspot,
       int64_t last_update_time_millis,
       const std::map<SoftwareFeature, SoftwareFeatureState>& software_features,
       const std::vector<BeaconSeed>& beacon_seeds);
diff --git a/components/cryptauth/remote_device_loader.cc b/components/cryptauth/remote_device_loader.cc
index 1f3c53e..87fbe38 100644
--- a/components/cryptauth/remote_device_loader.cc
+++ b/components/cryptauth/remote_device_loader.cc
@@ -129,7 +129,6 @@
 
   RemoteDevice remote_device(
       user_id_, device.friendly_device_name(), device.public_key(), psk,
-      device.unlock_key(), device.mobile_hotspot_supported(),
       device.last_update_time_millis(), GetSoftwareFeatureToStateMap(device),
       beacon_seeds);
 
diff --git a/components/cryptauth/remote_device_loader_unittest.cc b/components/cryptauth/remote_device_loader_unittest.cc
index 0428e51..5750456 100644
--- a/components/cryptauth/remote_device_loader_unittest.cc
+++ b/components/cryptauth/remote_device_loader_unittest.cc
@@ -117,36 +117,6 @@
   EXPECT_EQ(kBeaconSeedEndTimeMs, beacon_seed.end_time_millis());
 }
 
-TEST_F(CryptAuthRemoteDeviceLoaderTest, BooleanAttributes) {
-  cryptauth::ExternalDeviceInfo first = CreateDeviceInfo("0");
-  first.set_unlock_key(true);
-  first.set_mobile_hotspot_supported(true);
-
-  cryptauth::ExternalDeviceInfo second = CreateDeviceInfo("1");
-  second.set_unlock_key(false);
-  second.set_mobile_hotspot_supported(false);
-
-  std::vector<cryptauth::ExternalDeviceInfo> device_infos{first, second};
-
-  RemoteDeviceLoader loader(device_infos, user_private_key_, kUserId,
-                            std::move(secure_message_delegate_));
-
-  EXPECT_CALL(*this, LoadCompleted());
-  loader.Load(
-      base::Bind(&CryptAuthRemoteDeviceLoaderTest::OnRemoteDevicesLoaded,
-                 base::Unretained(this)));
-
-  EXPECT_EQ(2u, remote_devices_.size());
-
-  EXPECT_FALSE(remote_devices_[0].persistent_symmetric_key.empty());
-  EXPECT_TRUE(remote_devices_[0].unlock_key);
-  EXPECT_TRUE(remote_devices_[0].supports_mobile_hotspot);
-
-  EXPECT_FALSE(remote_devices_[1].persistent_symmetric_key.empty());
-  EXPECT_FALSE(remote_devices_[1].unlock_key);
-  EXPECT_FALSE(remote_devices_[1].supports_mobile_hotspot);
-}
-
 TEST_F(CryptAuthRemoteDeviceLoaderTest, LastUpdateTimeMillis) {
   cryptauth::ExternalDeviceInfo first = CreateDeviceInfo("0");
   first.set_last_update_time_millis(1000);
diff --git a/components/cryptauth/remote_device_ref.h b/components/cryptauth/remote_device_ref.h
index 2aa31e8..65b485e 100644
--- a/components/cryptauth/remote_device_ref.h
+++ b/components/cryptauth/remote_device_ref.h
@@ -63,10 +63,6 @@
   const std::string& persistent_symmetric_key() const {
     return remote_device_->persistent_symmetric_key;
   }
-  bool unlock_key() const { return remote_device_->unlock_key; }
-  bool supports_mobile_hotspot() const {
-    return remote_device_->supports_mobile_hotspot;
-  }
   int64_t last_update_time_millis() const {
     return remote_device_->last_update_time_millis;
   }
diff --git a/components/cryptauth/remote_device_ref_unittest.cc b/components/cryptauth/remote_device_ref_unittest.cc
index e9937cb..0485e86 100644
--- a/components/cryptauth/remote_device_ref_unittest.cc
+++ b/components/cryptauth/remote_device_ref_unittest.cc
@@ -31,7 +31,6 @@
 
     remote_device_ = std::make_shared<RemoteDevice>(
         "user_id", "name", "public_key", "persistent_symmetric_key",
-        true /* unlock_key */, true /* supports_mobile_hotspot */,
         42000 /* last_update_time_millis */,
         software_feature_to_state_map /* software_features */,
         beacon_seeds /* beacon_seeds */);
@@ -50,9 +49,6 @@
   EXPECT_EQ(remote_device_->public_key, remote_device_ref.public_key());
   EXPECT_EQ(remote_device_->persistent_symmetric_key,
             remote_device_ref.persistent_symmetric_key());
-  EXPECT_EQ(remote_device_->unlock_key, remote_device_ref.unlock_key());
-  EXPECT_EQ(remote_device_->supports_mobile_hotspot,
-            remote_device_ref.supports_mobile_hotspot());
   EXPECT_EQ(remote_device_->last_update_time_millis,
             remote_device_ref.last_update_time_millis());
   EXPECT_EQ(&remote_device_->beacon_seeds, &remote_device_ref.beacon_seeds());
diff --git a/components/cryptauth/remote_device_test_util.cc b/components/cryptauth/remote_device_test_util.cc
index da344c2..859a3930 100644
--- a/components/cryptauth/remote_device_test_util.cc
+++ b/components/cryptauth/remote_device_test_util.cc
@@ -16,8 +16,6 @@
 const char kTestRemoteDeviceName[] = "remote device";
 const char kTestRemoteDevicePublicKey[] = "public key";
 const char kTestRemoteDevicePSK[] = "remote device psk";
-const bool kTestRemoteDeviceUnlockKey = true;
-const bool kTestRemoteDeviceSupportsMobileHotspot = true;
 const int64_t kTestRemoteDeviceLastUpdateTimeMillis = 0L;
 
 RemoteDeviceRefBuilder::RemoteDeviceRefBuilder() {
@@ -46,7 +44,10 @@
 
 RemoteDeviceRefBuilder& RemoteDeviceRefBuilder::SetSupportsMobileHotspot(
     bool supports_mobile_hotspot) {
-  remote_device_->supports_mobile_hotspot = supports_mobile_hotspot;
+  remote_device_
+      ->software_features[cryptauth::SoftwareFeature::MAGIC_TETHER_HOST] =
+      supports_mobile_hotspot ? cryptauth::SoftwareFeatureState::kSupported
+                              : cryptauth::SoftwareFeatureState::kNotSupported;
   return *this;
 }
 
@@ -74,12 +75,17 @@
 }
 
 RemoteDevice CreateRemoteDeviceForTest() {
-  return RemoteDevice(
-      kTestRemoteDeviceUserId, kTestRemoteDeviceName,
-      kTestRemoteDevicePublicKey, kTestRemoteDevicePSK,
-      kTestRemoteDeviceUnlockKey, kTestRemoteDeviceSupportsMobileHotspot,
-      kTestRemoteDeviceLastUpdateTimeMillis,
-      std::map<SoftwareFeature, SoftwareFeatureState>(), {} /* beacon_seeds */);
+  std::map<cryptauth::SoftwareFeature, cryptauth::SoftwareFeatureState>
+      software_features;
+  software_features[cryptauth::SoftwareFeature::EASY_UNLOCK_HOST] =
+      cryptauth::SoftwareFeatureState::kEnabled;
+  software_features[cryptauth::SoftwareFeature::MAGIC_TETHER_HOST] =
+      cryptauth::SoftwareFeatureState::kSupported;
+
+  return RemoteDevice(kTestRemoteDeviceUserId, kTestRemoteDeviceName,
+                      kTestRemoteDevicePublicKey, kTestRemoteDevicePSK,
+                      kTestRemoteDeviceLastUpdateTimeMillis, software_features,
+                      {} /* beacon_seeds */);
 }
 
 RemoteDeviceRef CreateRemoteDeviceRefForTest() {
diff --git a/components/embedder_support/android/java/src/org/chromium/components/embedder_support/view/ContentView.java b/components/embedder_support/android/java/src/org/chromium/components/embedder_support/view/ContentView.java
index c3334d9..947f8fa 100644
--- a/components/embedder_support/android/java/src/org/chromium/components/embedder_support/view/ContentView.java
+++ b/components/embedder_support/android/java/src/org/chromium/components/embedder_support/view/ContentView.java
@@ -21,7 +21,6 @@
 import android.widget.FrameLayout;
 
 import org.chromium.base.TraceEvent;
-import org.chromium.content_public.browser.ContentViewCore;
 import org.chromium.content_public.browser.ImeAdapter;
 import org.chromium.content_public.browser.RenderCoordinates;
 import org.chromium.content_public.browser.SmartClipProvider;
@@ -137,14 +136,14 @@
 
     @Override
     public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
-        if (getContentViewCore() == null) return null;
+        // Calls may come while/after WebContents is destroyed. See https://crbug.com/821750#c8.
+        if (mWebContents.isDestroyed()) return null;
         return ImeAdapter.fromWebContents(mWebContents).onCreateInputConnection(outAttrs);
     }
 
     @Override
     public boolean onCheckIsTextEditor() {
-        if (getContentViewCore() == null) return false;
-
+        if (mWebContents.isDestroyed()) return false;
         return ImeAdapter.fromWebContents(mWebContents).onCheckIsTextEditor();
     }
 
@@ -205,11 +204,6 @@
         return getEventForwarder().onGenericMotionEvent(event);
     }
 
-    private ContentViewCore getContentViewCore() {
-        if (mWebContents.isDestroyed()) return null;
-        return ContentViewCore.fromWebContents(mWebContents);
-    }
-
     private EventForwarder getEventForwarder() {
         if (mEventForwarder == null) {
             mEventForwarder = mWebContents.getEventForwarder();
diff --git a/components/feed/core/feed_host_service.cc b/components/feed/core/feed_host_service.cc
index e06bb3f..fe202d6 100644
--- a/components/feed/core/feed_host_service.cc
+++ b/components/feed/core/feed_host_service.cc
@@ -11,10 +11,12 @@
 FeedHostService::FeedHostService(
     std::unique_ptr<FeedImageManager> image_manager,
     std::unique_ptr<FeedNetworkingHost> networking_host,
-    std::unique_ptr<FeedSchedulerHost> scheduler_host)
+    std::unique_ptr<FeedSchedulerHost> scheduler_host,
+    std::unique_ptr<FeedStorageDatabase> storage_database)
     : image_manager_(std::move(image_manager)),
       networking_host_(std::move(networking_host)),
-      scheduler_host_(std::move(scheduler_host)) {}
+      scheduler_host_(std::move(scheduler_host)),
+      storage_database_(std::move(storage_database)) {}
 
 FeedHostService::~FeedHostService() = default;
 
@@ -30,4 +32,8 @@
   return scheduler_host_.get();
 }
 
+FeedStorageDatabase* FeedHostService::GetStorageDatabase() {
+  return storage_database_.get();
+}
+
 }  // namespace feed
diff --git a/components/feed/core/feed_host_service.h b/components/feed/core/feed_host_service.h
index 88d12de..223f668 100644
--- a/components/feed/core/feed_host_service.h
+++ b/components/feed/core/feed_host_service.h
@@ -11,6 +11,7 @@
 #include "components/feed/core/feed_image_manager.h"
 #include "components/feed/core/feed_networking_host.h"
 #include "components/feed/core/feed_scheduler_host.h"
+#include "components/feed/core/feed_storage_database.h"
 #include "components/keyed_service/core/keyed_service.h"
 
 namespace feed {
@@ -24,17 +25,20 @@
  public:
   FeedHostService(std::unique_ptr<FeedImageManager> image_manager,
                   std::unique_ptr<FeedNetworkingHost> networking_host,
-                  std::unique_ptr<FeedSchedulerHost> scheduler_host);
+                  std::unique_ptr<FeedSchedulerHost> scheduler_host,
+                  std::unique_ptr<FeedStorageDatabase> storage_database);
   ~FeedHostService() override;
 
   FeedImageManager* GetImageManager();
   FeedNetworkingHost* GetNetworkingHost();
   FeedSchedulerHost* GetSchedulerHost();
+  FeedStorageDatabase* GetStorageDatabase();
 
  private:
   std::unique_ptr<FeedImageManager> image_manager_;
   std::unique_ptr<FeedNetworkingHost> networking_host_;
   std::unique_ptr<FeedSchedulerHost> scheduler_host_;
+  std::unique_ptr<FeedStorageDatabase> storage_database_;
 
   DISALLOW_COPY_AND_ASSIGN(FeedHostService);
 };
diff --git a/components/feed/core/feed_storage_database.cc b/components/feed/core/feed_storage_database.cc
index fe54160..f440efd 100644
--- a/components/feed/core/feed_storage_database.cc
+++ b/components/feed/core/feed_storage_database.cc
@@ -75,13 +75,13 @@
 
 }  // namespace
 
-FeedStorageDatabase::FeedStorageDatabase(
-    const base::FilePath& database_folder,
-    const scoped_refptr<base::SequencedTaskRunner>& task_runner)
+FeedStorageDatabase::FeedStorageDatabase(const base::FilePath& database_folder)
     : FeedStorageDatabase(
           database_folder,
           std::make_unique<leveldb_proto::ProtoDatabaseImpl<FeedStorageProto>>(
-              task_runner)) {}
+              base::CreateSequencedTaskRunnerWithTraits(
+                  {base::MayBlock(), base::TaskPriority::BACKGROUND,
+                   base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}))) {}
 
 FeedStorageDatabase::FeedStorageDatabase(
     const base::FilePath& database_folder,
diff --git a/components/feed/core/feed_storage_database.h b/components/feed/core/feed_storage_database.h
index 64f931c..dc657c6 100644
--- a/components/feed/core/feed_storage_database.h
+++ b/components/feed/core/feed_storage_database.h
@@ -49,9 +49,7 @@
   using ConfirmationCallback = base::OnceCallback<void(bool)>;
 
   // Initializes the database with |database_folder|.
-  FeedStorageDatabase(
-      const base::FilePath& database_folder,
-      const scoped_refptr<base::SequencedTaskRunner>& task_runner);
+  explicit FeedStorageDatabase(const base::FilePath& database_folder);
 
   // Initializes the database with |database_folder|. Creates storage using the
   // given |storage_database| for local storage. Useful for testing.
diff --git a/components/omnibox/browser/omnibox_field_trial.cc b/components/omnibox/browser/omnibox_field_trial.cc
index 9462adb..5df1f24 100644
--- a/components/omnibox/browser/omnibox_field_trial.cc
+++ b/components/omnibox/browser/omnibox_field_trial.cc
@@ -649,9 +649,9 @@
     return EMPHASIZE_WHEN_NONEMPTY;
   }
 
-  // Touch-optimized UI and MD Refresh also always swap title and URL.
+  // Touch-optimized UI always swaps title and URL.
   if (ui::MaterialDesignController::is_mode_initialized() &&
-      ui::MaterialDesignController::IsNewerMaterialUi()) {
+      ui::MaterialDesignController::IsTouchOptimizedUiEnabled()) {
     return EMPHASIZE_WHEN_NONEMPTY;
   }
 
diff --git a/components/optimization_guide/proto/hints.proto b/components/optimization_guide/proto/hints.proto
index 906342a..fbb7d0f 100644
--- a/components/optimization_guide/proto/hints.proto
+++ b/components/optimization_guide/proto/hints.proto
@@ -61,6 +61,17 @@
   // Only the first ResourceLoadingHint record that matches a target resource
   // URL will be applied to that resource.
   repeated ResourceLoadingHint resource_loading_hints = 3;
+  // If this optimization is experimental, this field will contain the
+  // experiment name that activates the optimization. If a non-empty name is
+  // configured, the optimization will be disabled unless a previews experiment
+  // of that same name is running. Previews experiments are enabled and
+  // configured by passing an experiment_name parameter to the
+  // OptimizationHintsExperiments feature. For example, if experiment_name is
+  // my_exp, it could be enabled with the following command-line flags:
+  //   --enable-features=OptimizationHintsExperiments<MyFieldTrial
+  //   --force-fieldtrial-params="MyFieldTrial.Enabled:experiment_name/my_exp"
+  //   --force-fieldtrials=MyFieldTrial/Enabled/
+  optional string experiment_name = 4;
 }
 
 // The possible effective connection type values.
diff --git a/components/previews/content/previews_hints.cc b/components/previews/content/previews_hints.cc
index de991e8..2a1773d 100644
--- a/components/previews/content/previews_hints.cc
+++ b/components/previews/content/previews_hints.cc
@@ -9,9 +9,11 @@
 
 #include "base/files/file.h"
 #include "base/files/file_util.h"
+#include "base/metrics/field_trial_params.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/optional.h"
 #include "components/optimization_guide/optimization_guide_service_observer.h"
+#include "components/previews/core/previews_features.h"
 
 namespace previews {
 
@@ -168,6 +170,23 @@
     // whitelisted for the host suffix.
     std::set<std::pair<PreviewsType, int>> whitelisted_optimizations;
     for (const auto optimization : hint.whitelisted_optimizations()) {
+      // If this optimization has been marked with an experiment name, skip it
+      // unless an experiment with that name is running. Experiment names are
+      // configured with the experiment_name parameter to the
+      // kOptimizationHintsExperiments feature.
+      //
+      // If kOptimizationHintsExperiments is disabled, getting the param value
+      // returns an empty string. Since experiment names are not allowed to be
+      // empty strings, all experiments will be disabled if the feature is
+      // disabled.
+      if (optimization.has_experiment_name() &&
+          !optimization.experiment_name().empty() &&
+          optimization.experiment_name() !=
+              base::GetFieldTrialParamValueByFeature(
+                  features::kOptimizationHintsExperiments,
+                  features::kOptimizationHintsExperimentNameParam)) {
+        continue;
+      }
       base::Optional<PreviewsType> previews_type =
           ConvertProtoOptimizationTypeToPreviewsOptimizationType(
               optimization.optimization_type());
@@ -242,4 +261,4 @@
   return activation_list_->IsHostWhitelistedAtNavigation(url, type);
 }
 
-}  // namespace previews
\ No newline at end of file
+}  // namespace previews
diff --git a/components/previews/content/previews_optimization_guide_unittest.cc b/components/previews/content/previews_optimization_guide_unittest.cc
index 9ed76e3..7ad2a7b 100644
--- a/components/previews/content/previews_optimization_guide_unittest.cc
+++ b/components/previews/content/previews_optimization_guide_unittest.cc
@@ -10,13 +10,16 @@
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/macros.h"
+#include "base/optional.h"
 #include "base/run_loop.h"
 #include "base/test/gtest_util.h"
 #include "base/test/metrics/histogram_tester.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/test/scoped_task_environment.h"
 #include "components/optimization_guide/optimization_guide_service.h"
 #include "components/optimization_guide/optimization_guide_service_observer.h"
 #include "components/optimization_guide/proto/hints.pb.h"
+#include "components/previews/core/previews_features.h"
 #include "components/previews/core/previews_user_data.h"
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "net/url_request/url_request.h"
@@ -96,6 +99,9 @@
     base::RunLoop().RunUntilIdle();
   }
 
+  void DoExperimentFlagTest(base::Optional<std::string> experiment_name,
+                            bool expect_enabled);
+
  private:
   base::test::ScopedTaskEnvironment scoped_task_environment_;
   base::ScopedTempDir temp_dir_;
@@ -266,6 +272,128 @@
       GURL("https://google.com"), PreviewsType::RESOURCE_LOADING_HINTS));
 }
 
+// This is a helper function for testing the experiment flags on the config for
+// the optimization guide. It creates a test config with a hint containing
+// multiple optimizations. The optimization under test will be marked with an
+// experiment name if one is provided in |experiment_name|. It will then be
+// tested to see if it's enabled, the expectation found in |expect_enabled|.
+void PreviewsOptimizationGuideTest::DoExperimentFlagTest(
+    base::Optional<std::string> experiment_name,
+    bool expect_enabled) {
+  optimization_guide::proto::Configuration config;
+
+  // Create a hint with two optimizations. One may be marked experimental
+  // depending on test configuration. The other is never marked experimental.
+  optimization_guide::proto::Hint* hint1 = config.add_hints();
+  hint1->set_key("facebook.com");
+  hint1->set_key_representation(optimization_guide::proto::HOST_SUFFIX);
+  optimization_guide::proto::Optimization* optimization1 =
+      hint1->add_whitelisted_optimizations();
+  // NOSCRIPT is the optimization under test and may be marked experimental.
+  if (experiment_name.has_value()) {
+    optimization1->set_experiment_name(experiment_name.value());
+  }
+  optimization1->set_optimization_type(optimization_guide::proto::NOSCRIPT);
+  // RESOURCE_LOADING is never marked experimental.
+  optimization_guide::proto::Optimization* optimization2 =
+      hint1->add_whitelisted_optimizations();
+  optimization2->set_optimization_type(
+      optimization_guide::proto::RESOURCE_LOADING);
+
+  // Add a second, non-experimental hint.
+  optimization_guide::proto::Hint* hint2 = config.add_hints();
+  hint2->set_key("twitter.com");
+  hint2->set_key_representation(optimization_guide::proto::HOST_SUFFIX);
+  optimization_guide::proto::Optimization* optimization3 =
+      hint2->add_whitelisted_optimizations();
+  optimization3->set_optimization_type(optimization_guide::proto::NOSCRIPT);
+  ProcessHints(config, "2.0.0");
+
+  RunUntilIdle();
+
+  // Check to ensure the optimization under test (facebook noscript) is either
+  // enabled or disabled, depending on what the caller told us to expect.
+  EXPECT_EQ(expect_enabled,
+            guide()->IsWhitelisted(
+                *CreateRequestWithURL(GURL("https://m.facebook.com")),
+                PreviewsType::NOSCRIPT));
+
+  // RESOURCE_LOADING_HINTS for facebook should always be enabled.
+  EXPECT_TRUE(guide()->IsWhitelisted(
+      *CreateRequestWithURL(GURL("https://m.facebook.com")),
+      PreviewsType::RESOURCE_LOADING_HINTS));
+  // Twitter's NOSCRIPT should always be enabled; RESOURCE_LOADING_HINTS is not
+  // configured and should be disabled.
+  EXPECT_TRUE(guide()->IsWhitelisted(
+      *CreateRequestWithURL(GURL("https://m.twitter.com/example")),
+      PreviewsType::NOSCRIPT));
+  EXPECT_FALSE(guide()->IsHostWhitelistedAtNavigation(
+      GURL("https://m.twitter.com/example"),
+      PreviewsType::RESOURCE_LOADING_HINTS));
+  // Google (which is not configured at all) should always have both NOSCRIPT
+  // and RESOURCE_LOADING_HINTS disabled.
+  EXPECT_FALSE(
+      guide()->IsWhitelisted(*CreateRequestWithURL(GURL("https://google.com")),
+                             PreviewsType::NOSCRIPT));
+  EXPECT_FALSE(guide()->IsHostWhitelistedAtNavigation(
+      GURL("https://google.com"), PreviewsType::RESOURCE_LOADING_HINTS));
+}
+
+TEST_F(PreviewsOptimizationGuideTest,
+       HandlesExperimentalFlagWithNoExperimentFlaggedOrEnabled) {
+  // With the optimization NOT flagged as experimental and no experiment
+  // enabled, the optimization should be enabled.
+  base::test::ScopedFeatureList scoped_list;
+  scoped_list.InitAndDisableFeature(features::kOptimizationHintsExperiments);
+  DoExperimentFlagTest(base::nullopt, true);
+}
+
+TEST_F(PreviewsOptimizationGuideTest,
+       HandlesExperimentalFlagWithEmptyExperimentName) {
+  // Empty experiment names should be equivalent to no experiment flag set.
+  base::test::ScopedFeatureList scoped_list;
+  scoped_list.InitAndDisableFeature(features::kOptimizationHintsExperiments);
+  DoExperimentFlagTest("", true);
+}
+
+TEST_F(PreviewsOptimizationGuideTest,
+       HandlesExperimentalFlagWithExperimentConfiguredAndNotRunning) {
+  // With the optimization flagged as experimental and no experiment
+  // enabled, the optimization should be disabled.
+  base::test::ScopedFeatureList scoped_list;
+  scoped_list.InitAndDisableFeature(features::kOptimizationHintsExperiments);
+  DoExperimentFlagTest("foo_experiment", false);
+}
+
+TEST_F(PreviewsOptimizationGuideTest,
+       HandlesExperimentalFlagWithExperimentConfiguredAndSameOneRunning) {
+  // With the optimization flagged as experimental and an experiment with that
+  // name running, the optimization should be enabled.
+  base::test::ScopedFeatureList scoped_list;
+  scoped_list.InitAndEnableFeatureWithParameters(
+      features::kOptimizationHintsExperiments,
+      {{"experiment_name", "foo_experiment"}});
+  DoExperimentFlagTest("foo_experiment", true);
+}
+
+TEST_F(PreviewsOptimizationGuideTest,
+       HandlesExperimentalFlagWithExperimentConfiguredAndDifferentOneRunning) {
+  // With the optimization flagged as experimental and a *different* experiment
+  // enabled, the optimization should be disabled.
+  base::test::ScopedFeatureList scoped_list;
+  scoped_list.InitAndEnableFeatureWithParameters(
+      features::kOptimizationHintsExperiments,
+      {{"experiment_name", "bar_experiment"}});
+  DoExperimentFlagTest("foo_experiment", false);
+}
+
+TEST_F(PreviewsOptimizationGuideTest, EnsureExperimentsDisabledByDefault) {
+  // Mark an optimization as experiment, and ensure it's disabled even though we
+  // don't explicitly enable or disable the feature as part of the test. This
+  // ensures the experiments feature is disabled by default.
+  DoExperimentFlagTest("foo_experiment", false);
+}
+
 TEST_F(PreviewsOptimizationGuideTest, ProcessHintsUnsupportedKeyRepIsIgnored) {
   optimization_guide::proto::Configuration config;
   optimization_guide::proto::Hint* hint = config.add_hints();
diff --git a/components/previews/core/previews_features.cc b/components/previews/core/previews_features.cc
index 6168218..937ee62 100644
--- a/components/previews/core/previews_features.cc
+++ b/components/previews/core/previews_features.cc
@@ -49,6 +49,18 @@
 const base::Feature kOptimizationHints{"OptimizationHints",
                                        base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Enables Optimization Hints that are marked as experimental. Optimizations are
+// marked experimental by setting an experiment name in the "experiment_name"
+// field of the Optimization proto. This allows experiments at the granularity
+// of a single PreviewType for a single host (or host suffix). The intent is
+// that optimizations that may not work properly for certain sites can be tried
+// at a small scale via Finch experiments. Experimental optimizations can be
+// activated by enabling this feature and passing an experiment name as a
+// parameter called "experiment_name" that matches the experiment name in the
+// Optimization proto.
+const base::Feature kOptimizationHintsExperiments{
+    "OptimizationHintsExperiments", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Enables the application of the resource loading hints when loading resources.
 const base::Feature kResourceLoadingHints{"ResourceLoadingHints",
                                           base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/components/previews/core/previews_features.h b/components/previews/core/previews_features.h
index b798738..fd0a1a8 100644
--- a/components/previews/core/previews_features.h
+++ b/components/previews/core/previews_features.h
@@ -16,6 +16,8 @@
 extern const base::Feature kNoScriptPreviews;
 extern const base::Feature kStalePreviewsTimestamp;
 extern const base::Feature kOptimizationHints;
+extern const base::Feature kOptimizationHintsExperiments;
+constexpr char kOptimizationHintsExperimentNameParam[] = "experiment_name";
 extern const base::Feature kResourceLoadingHints;
 
 }  // namespace features
diff --git a/components/safe_browsing/password_protection/mock_password_protection_service.h b/components/safe_browsing/password_protection/mock_password_protection_service.h
index 4b1a792..aafdf42 100644
--- a/components/safe_browsing/password_protection/mock_password_protection_service.h
+++ b/components/safe_browsing/password_protection/mock_password_protection_service.h
@@ -62,6 +62,8 @@
                void(content::WebContents*,
                     PasswordProtectionService::RequestOutcome,
                     const safe_browsing::LoginReputationClientResponse*));
+  MOCK_METHOD3(CanShowInterstitial,
+               bool(RequestOutcome, ReusedPasswordType, const GURL&));
   MOCK_METHOD4(
       MaybeStartPasswordFieldOnFocusRequest,
       void(content::WebContents*, const GURL&, const GURL&, const GURL&));
diff --git a/components/safe_browsing/password_protection/password_protection_service.cc b/components/safe_browsing/password_protection/password_protection_service.cc
index d266ae1..93df501 100644
--- a/components/safe_browsing/password_protection/password_protection_service.cc
+++ b/components/safe_browsing/password_protection/password_protection_service.cc
@@ -496,9 +496,8 @@
                  password_field_exists);
   } else {
     MaybeLogPasswordReuseLookupEvent(web_contents, reason, nullptr);
-    if (reason == PASSWORD_ALERT_MODE) {
+    if (CanShowInterstitial(reason, reused_password_type, main_frame_url))
       ShowInterstitial(web_contents, reused_password_type);
-    }
   }
 }
 
@@ -1050,4 +1049,21 @@
       verdicts_migrated);
 }
 
+void PasswordProtectionService::LogPasswordAlertModeOutcome(
+    RequestOutcome reason,
+    ReusedPasswordType password_type) {
+  DCHECK(password_type == PasswordReuseEvent::SIGN_IN_PASSWORD ||
+         password_type == PasswordReuseEvent::ENTERPRISE_PASSWORD);
+  if (password_type == PasswordReuseEvent::SIGN_IN_PASSWORD) {
+    base::UmaHistogramEnumeration(
+        "PasswordProtection.PasswordAlertModeOutcome.GSuiteSyncPasswordEntry",
+        reason, MAX_OUTCOME);
+  } else {
+    base::UmaHistogramEnumeration(
+        "PasswordProtection.PasswordAlertModeOutcome."
+        "NonGaiaEnterprisePasswordEntry",
+        reason, MAX_OUTCOME);
+  }
+}
+
 }  // namespace safe_browsing
diff --git a/components/safe_browsing/password_protection/password_protection_service.h b/components/safe_browsing/password_protection/password_protection_service.h
index 6d46274..ef81808 100644
--- a/components/safe_browsing/password_protection/password_protection_service.h
+++ b/components/safe_browsing/password_protection/password_protection_service.h
@@ -297,11 +297,11 @@
  protected:
   friend class PasswordProtectionRequest;
 
-  // Chrome can send password protection ping if it is allowed by Finch config
-  // and if Safe Browsing can compute reputation of |main_frame_url| (e.g.
-  // Safe Browsing is not able to compute reputation of a private IP or
-  // a local host). Update |reason| if sending ping is not allowed.
-  // |password_type| is used for UMA metric recording.
+  // Chrome can send password protection ping if it is allowed by for the
+  // |trigger_type| and if Safe Browsing can compute reputation of
+  // |main_frame_url| (e.g. Safe Browsing is not able to compute reputation of a
+  // private IP or a local host). Update |reason| if sending ping is not
+  // allowed. |password_type| is used for UMA metric recording.
   bool CanSendPing(LoginReputationClientRequest::TriggerType trigger_type,
                    const GURL& main_frame_url,
                    ReusedPasswordType password_type,
@@ -379,6 +379,13 @@
 
   bool IsModalWarningShowingInWebContents(content::WebContents* web_contents);
 
+  virtual bool CanShowInterstitial(RequestOutcome reason,
+                                   ReusedPasswordType password_type,
+                                   const GURL& main_frame_url) = 0;
+
+  void LogPasswordAlertModeOutcome(RequestOutcome reason,
+                                   ReusedPasswordType password_type);
+
  private:
   friend class PasswordProtectionServiceTest;
   friend class TestPasswordProtectionService;
diff --git a/components/sync/BUILD.gn b/components/sync/BUILD.gn
index a82c681..c30c9c8 100644
--- a/components/sync/BUILD.gn
+++ b/components/sync/BUILD.gn
@@ -382,6 +382,7 @@
     "js/js_event_handler.h",
     "js/sync_js_controller.cc",
     "js/sync_js_controller.h",
+    "model/blocking_model_type_store.h",
     "model/change_processor.cc",
     "model/change_processor.h",
     "model/conflict_resolution.cc",
@@ -406,6 +407,8 @@
     "model/model_type_change_processor.h",
     "model/model_type_store.cc",
     "model/model_type_store.h",
+    "model/model_type_store_base.cc",
+    "model/model_type_store_base.h",
     "model/model_type_sync_bridge.cc",
     "model/model_type_sync_bridge.h",
     "model/mutable_data_batch.cc",
@@ -427,6 +430,8 @@
     "model/syncable_service.cc",
     "model/syncable_service.h",
     "model/time.h",
+    "model_impl/blocking_model_type_store_impl.cc",
+    "model_impl/blocking_model_type_store_impl.h",
     "model_impl/client_tag_based_model_type_processor.cc",
     "model_impl/client_tag_based_model_type_processor.h",
     "model_impl/in_memory_metadata_change_list.cc",
diff --git a/components/sync/driver/backend_migrator.cc b/components/sync/driver/backend_migrator.cc
index f0e8633d..a65f15e 100644
--- a/components/sync/driver/backend_migrator.cc
+++ b/components/sync/driver/backend_migrator.cc
@@ -5,9 +5,9 @@
 #include "components/sync/driver/backend_migrator.h"
 
 #include "base/location.h"
-#include "base/single_thread_task_runner.h"
+#include "base/sequenced_task_runner.h"
 #include "base/strings/string_number_conversions.h"
-#include "base/threading/thread_task_runner_handle.h"
+#include "base/threading/sequenced_task_runner_handle.h"
 #include "components/sync/driver/sync_service.h"
 #include "components/sync/engine/configure_reason.h"
 #include "components/sync/protocol/sync.pb.h"
@@ -105,7 +105,7 @@
   // |manager_|'s methods aren't re-entrant, and we're notified from
   // them, so post a task to avoid problems.
   SDVLOG(1) << "Posting OnConfigureDoneImpl";
-  base::ThreadTaskRunnerHandle::Get()->PostTask(
+  base::SequencedTaskRunnerHandle::Get()->PostTask(
       FROM_HERE, base::Bind(&BackendMigrator::OnConfigureDoneImpl,
                             weak_ptr_factory_.GetWeakPtr(), result));
 }
diff --git a/components/sync/driver/data_type_controller.h b/components/sync/driver/data_type_controller.h
index 30121b6..61e6bf5 100644
--- a/components/sync/driver/data_type_controller.h
+++ b/components/sync/driver/data_type_controller.h
@@ -178,7 +178,7 @@
   explicit DataTypeController(ModelType type);
 
   // Allows subclasses to DCHECK that they're on the correct sequence.
-  // TODO(treib): Rename this to CalledOnValidSequence.
+  // TODO(crbug.com/846238): Rename this to CalledOnValidSequence.
   bool CalledOnValidThread() const;
 
  private:
diff --git a/components/sync/driver/data_type_manager_impl.cc b/components/sync/driver/data_type_manager_impl.cc
index 180f77b..338bd4e 100644
--- a/components/sync/driver/data_type_manager_impl.cc
+++ b/components/sync/driver/data_type_manager_impl.cc
@@ -14,8 +14,9 @@
 #include "base/containers/queue.h"
 #include "base/logging.h"
 #include "base/metrics/histogram_macros.h"
+#include "base/sequenced_task_runner.h"
 #include "base/strings/stringprintf.h"
-#include "base/threading/thread_task_runner_handle.h"
+#include "base/threading/sequenced_task_runner_handle.h"
 #include "base/trace_event/trace_event.h"
 #include "components/sync/driver/data_type_encryption_handler.h"
 #include "components/sync/driver/data_type_manager_observer.h"
@@ -671,7 +672,7 @@
       // Do this asynchronously so the ModelAssociationManager has a chance to
       // finish stopping this type, otherwise DeactivateDataType() and Stop()
       // end up getting called twice on the controller.
-      base::ThreadTaskRunnerHandle::Get()->PostTask(
+      base::SequencedTaskRunnerHandle::Get()->PostTask(
           FROM_HERE, base::Bind(&DataTypeManagerImpl::ProcessReconfigure,
                                 weak_ptr_factory_.GetWeakPtr()));
     }
diff --git a/components/sync/driver/fake_data_type_controller.cc b/components/sync/driver/fake_data_type_controller.cc
index 135b373..ebc871c 100644
--- a/components/sync/driver/fake_data_type_controller.cc
+++ b/components/sync/driver/fake_data_type_controller.cc
@@ -5,7 +5,7 @@
 #include "components/sync/driver/fake_data_type_controller.h"
 
 #include "base/bind.h"
-#include "base/threading/thread_task_runner_handle.h"
+#include "base/threading/sequenced_task_runner_handle.h"
 #include "components/sync/model/data_type_error_handler_impl.h"
 #include "components/sync/model/sync_merge_result.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -156,7 +156,7 @@
 FakeDataTypeController::CreateErrorHandler() {
   DCHECK(CalledOnValidThread());
   return std::make_unique<DataTypeErrorHandlerImpl>(
-      base::ThreadTaskRunnerHandle::Get(), base::Closure(),
+      base::SequencedTaskRunnerHandle::Get(), base::Closure(),
       base::Bind(model_load_callback_, type()));
 }