diff --git a/DEPS b/DEPS
index 74d7453..d94be74 100644
--- a/DEPS
+++ b/DEPS
@@ -133,11 +133,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': 'aeb71ce166c755a09ba4a66f5e6a5d7a22149b12',
+  'skia_revision': 'b8e77698ad39494d1de58d97ea7b2fb8579d2490',
   # 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': '7afc9ca45927216f43bc869135b33134af3a64de',
+  'v8_revision': '60263cb67e90fea34c6754f4cb9242351a2cb676',
   # 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.
@@ -145,7 +145,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': '08146a27b7bc07d6a2426591de7f1ec1a06bed42',
+  'angle_revision': 'e8247a574956349b7f0a9beaa63c8a7db90e2403',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -153,7 +153,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': 'b6220d071585a765b9ae8a626a5975e4d6fb4ab7',
+  'pdfium_revision': '0958e507daa3774ac8996b6e7ee260b9154f8ee5',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling BoringSSL
   # and whatever else without interference from each other.
@@ -184,7 +184,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling freetype
   # and whatever else without interference from each other.
-  'freetype_revision': '2f4b740ce435bc1ad5ef8570bb91ab7cd5682720',
+  'freetype_revision': 'fbbcf50367403a6316a013b51690071198962920',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling HarfBuzz
   # and whatever else without interference from each other.
@@ -268,7 +268,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': '2bc3169f0d64a07469b73c318a2e4c887f8eb17a',
+  'dawn_revision': '2a7b631482d8cb167d5651be130b08549b4dedc8',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -802,7 +802,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'f0a719c3731cdc76fb6efd270801145b7afe1243',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '1ffff0a0da2bcb60cd2a94c15081ff491c4f72c0',
       'condition': 'checkout_linux',
   },
 
@@ -827,7 +827,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'ad39f9d8f8d117ef63fc7d50b207a14017f05c08',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '5716400ae256163c8c91b489ce694e7c561e0c92',
 
   'src/third_party/devtools-node-modules':
     Var('chromium_git') + '/external/github.com/ChromeDevTools/devtools-node-modules' + '@' + Var('devtools_node_modules_revision'),
@@ -1177,7 +1177,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' +  '513ef55bd2588a21ecdbd0d743c5aede17d02a9b',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' +  'c9cac42e24593ab65c8c828f7e1b428a7de35df7',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + 'ac0d98b5cee6c024b0cffeb4f8f45b6fc5ccdb78',
@@ -1345,7 +1345,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '6f0b34abee8dba611c253738d955c59f703c147a',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '1ff16c87aa63d4f9b63a238fc0664b5e625e9884',
+    Var('webrtc_git') + '/src.git' + '@' + '762076b88663ac0225175593cb216040471701e5',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
@@ -1386,7 +1386,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@b28d01ad3a2a4984e35b311e010151d1a51bf881',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@e2260af0009653969ed497da3ae8fd0536338756',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index 35de071..7a685ad 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -563,33 +563,6 @@
       (),
     ),
     (
-      r'/\bbase::Bind\(',
-      (
-          'Please consider using base::Bind{Once,Repeating} instead',
-          'of base::Bind. (crbug.com/714018)',
-      ),
-      False,
-      (),
-    ),
-    (
-      r'/\bbase::Callback<',
-      (
-          'Please consider using base::{Once,Repeating}Callback instead',
-          'of base::Callback. (crbug.com/714018)',
-      ),
-      False,
-      (),
-    ),
-    (
-      r'/\bbase::Closure\b',
-      (
-          'Please consider using base::{Once,Repeating}Closure instead',
-          'of base::Closure. (crbug.com/714018)',
-      ),
-      False,
-      (),
-    ),
-    (
       r'/base::SharedMemory(|Handle)',
       (
           'base::SharedMemory is deprecated. Please use',
diff --git a/WATCHLISTS b/WATCHLISTS
index 869f743..ca45689 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -118,9 +118,6 @@
     'arc_common': {
       'filepath': 'components/arc/common/',
     },
-    'arc_fileapi': {
-      'filepath': 'chrome/browser/chromeos/arc/fileapi'
-    },
     'arc_ime': {
       'filepath': 'chrome/browser/chromeos/arc/input_method_manager/'\
                   '|components/arc/ime/'
@@ -1922,7 +1919,6 @@
             'arc-reviews+chromium@google.com'],
     'arc_auth': ['khmel+watch@chromium.org'],
     'arc_common': ['hashimoto+watch@chromium.org'],
-    'arc_fileapi': ['nya+watch@chromium.org'],
     'arc_ime': ['yhanada+watch@chromium.org'],
     'arc_kiosk': ['poromov+watch@chromium.org'],
     'arc_net': ['abhishekbh@chromium.org',
diff --git a/ash/accelerators/accelerator_controller_impl.cc b/ash/accelerators/accelerator_controller_impl.cc
index cffe64e..90a6450 100644
--- a/ash/accelerators/accelerator_controller_impl.cc
+++ b/ash/accelerators/accelerator_controller_impl.cc
@@ -208,7 +208,7 @@
 
 void RecordImeSwitchByAccelerator() {
   UMA_HISTOGRAM_ENUMERATION("InputMethod.ImeSwitch",
-                            ImeSwitchType::kAccelerator, ImeSwitchType::kCount);
+                            ImeSwitchType::kAccelerator);
 }
 
 void HandleCycleBackwardMRU(const ui::Accelerator& accelerator) {
diff --git a/ash/app_list/app_list_controller_impl.cc b/ash/app_list/app_list_controller_impl.cc
index 6f29011..483a7b6 100644
--- a/ash/app_list/app_list_controller_impl.cc
+++ b/ash/app_list/app_list_controller_impl.cc
@@ -182,7 +182,8 @@
   return &search_model_;
 }
 
-void AppListControllerImpl::AddItem(AppListItemMetadataPtr item_data) {
+void AppListControllerImpl::AddItem(
+    std::unique_ptr<ash::AppListItemMetadata> item_data) {
   const std::string folder_id = item_data->folder_id;
   if (folder_id.empty())
     model_->AddItem(CreateAppListItem(std::move(item_data)));
@@ -190,8 +191,9 @@
     AddItemToFolder(std::move(item_data), folder_id);
 }
 
-void AppListControllerImpl::AddItemToFolder(AppListItemMetadataPtr item_data,
-                                            const std::string& folder_id) {
+void AppListControllerImpl::AddItemToFolder(
+    std::unique_ptr<ash::AppListItemMetadata> item_data,
+    const std::string& folder_id) {
   // When we're setting a whole model of a profile, each item may have its
   // folder id set properly. However, |AppListModel::AddItemToFolder| requires
   // the item to add is not in the target folder yet, and sets its folder id
@@ -259,8 +261,9 @@
   search_model_.PublishResults(std::move(new_results));
 }
 
-void AppListControllerImpl::SetItemMetadata(const std::string& id,
-                                            AppListItemMetadataPtr data) {
+void AppListControllerImpl::SetItemMetadata(
+    const std::string& id,
+    std::unique_ptr<ash::AppListItemMetadata> data) {
   app_list::AppListItem* item = model_->FindItem(id);
   if (!item)
     return;
@@ -314,7 +317,7 @@
 
 void AppListControllerImpl::SetModelData(
     int profile_id,
-    std::vector<AppListItemMetadataPtr> apps,
+    std::vector<std::unique_ptr<ash::AppListItemMetadata>> apps,
     bool is_search_engine_google) {
   // Clear old model data.
   model_->DeleteAllItems();
@@ -394,7 +397,7 @@
     model_->SetItemPosition(oem_folder, oem_position);
   }
   model_->SetItemName(oem_folder, oem_folder_name);
-  std::move(callback).Run(oem_folder->CloneMetadata());
+  std::move(callback).Run();
 }
 
 void AppListControllerImpl::ResolveOemFolderPosition(
@@ -402,7 +405,7 @@
     ResolveOemFolderPositionCallback callback) {
   // In ash:
   app_list::AppListFolderItem* ash_oem_folder = FindFolderItem(kOemFolderId);
-  ash::mojom::AppListItemMetadataPtr metadata = nullptr;
+  std::unique_ptr<ash::AppListItemMetadata> metadata;
   if (ash_oem_folder) {
     const syncer::StringOrdinal& oem_folder_pos =
         preferred_oem_position.IsValid() ? preferred_oem_position
@@ -1270,7 +1273,7 @@
 }
 
 std::unique_ptr<app_list::AppListItem> AppListControllerImpl::CreateAppListItem(
-    AppListItemMetadataPtr metadata) {
+    std::unique_ptr<ash::AppListItemMetadata> metadata) {
   std::unique_ptr<app_list::AppListItem> app_list_item =
       metadata->is_folder
           ? std::make_unique<app_list::AppListFolderItem>(metadata->id)
diff --git a/ash/app_list/app_list_controller_impl.h b/ash/app_list/app_list_controller_impl.h
index 49ba387..c289b26 100644
--- a/ash/app_list/app_list_controller_impl.h
+++ b/ash/app_list/app_list_controller_impl.h
@@ -77,8 +77,8 @@
 
   // app_list::AppListController:
   void SetClient(app_list::AppListClient* client) override;
-  void AddItem(AppListItemMetadataPtr app_item) override;
-  void AddItemToFolder(AppListItemMetadataPtr app_item,
+  void AddItem(std::unique_ptr<ash::AppListItemMetadata> app_item) override;
+  void AddItemToFolder(std::unique_ptr<ash::AppListItemMetadata> app_item,
                        const std::string& folder_id) override;
   void RemoveItem(const std::string& id) override;
   void RemoveUninstalledItem(const std::string& id) override;
@@ -97,13 +97,13 @@
   void PublishSearchResults(
       std::vector<SearchResultMetadataPtr> results) override;
   void SetItemMetadata(const std::string& id,
-                       AppListItemMetadataPtr data) override;
+                       std::unique_ptr<ash::AppListItemMetadata> data) override;
   void SetItemIcon(const std::string& id, const gfx::ImageSkia& icon) override;
   void SetItemIsInstalling(const std::string& id, bool is_installing) override;
   void SetItemPercentDownloaded(const std::string& id,
                                 int32_t percent_downloaded) override;
   void SetModelData(int profile_id,
-                    std::vector<AppListItemMetadataPtr> apps,
+                    std::vector<std::unique_ptr<ash::AppListItemMetadata>> apps,
                     bool is_search_engine_google) override;
 
   void SetSearchResultMetadata(SearchResultMetadataPtr metadata) override;
@@ -313,7 +313,7 @@
 
   syncer::StringOrdinal GetOemFolderPos();
   std::unique_ptr<app_list::AppListItem> CreateAppListItem(
-      AppListItemMetadataPtr metadata);
+      std::unique_ptr<ash::AppListItemMetadata> metadata);
   app_list::AppListFolderItem* FindFolderItem(const std::string& folder_id);
 
   // Update the visibility of Assistant functionality.
diff --git a/ash/app_list/model/app_list_item.cc b/ash/app_list/model/app_list_item.cc
index 11d1bb2..bd9ecd7 100644
--- a/ash/app_list/model/app_list_item.cc
+++ b/ash/app_list/model/app_list_item.cc
@@ -10,18 +10,11 @@
 namespace app_list {
 
 AppListItem::AppListItem(const std::string& id)
-    : metadata_(ash::mojom::AppListItemMetadata::New(
-          id,
-          std::string() /* name */,
-          std::string() /* short_name */,
-          std::string() /* folder_id */,
-          syncer::StringOrdinal() /* position */,
-          false /* is_folder */,
-          false /* is_persistent */,
-          gfx::ImageSkia() /* icon */,
-          false /* is_page_break */)),
+    : metadata_(std::make_unique<ash::AppListItemMetadata>()),
       is_installing_(false),
-      percent_downloaded_(-1) {}
+      percent_downloaded_(-1) {
+  metadata_->id = id;
+}
 
 AppListItem::~AppListItem() {
   for (auto& observer : observers_)
diff --git a/ash/app_list/model/app_list_item.h b/ash/app_list/model/app_list_item.h
index 04512b4..c1a55a8 100644
--- a/ash/app_list/model/app_list_item.h
+++ b/ash/app_list/model/app_list_item.h
@@ -7,10 +7,12 @@
 
 #include <stddef.h>
 
+#include <memory>
 #include <string>
 #include <utility>
 
 #include "ash/app_list/model/app_list_model_export.h"
+#include "ash/public/cpp/app_list/app_list_types.h"
 #include "ash/public/interfaces/app_list.mojom.h"
 #include "base/macros.h"
 #include "base/observer_list.h"
@@ -34,6 +36,8 @@
 // and action to be executed when the AppListItemView is activated.
 class APP_LIST_MODEL_EXPORT AppListItem {
  public:
+  using AppListItemMetadata = ash::AppListItemMetadata;
+
   explicit AppListItem(const std::string& id);
   virtual ~AppListItem();
 
@@ -60,14 +64,12 @@
   const std::string& folder_id() const { return metadata_->folder_id; }
   const syncer::StringOrdinal& position() const { return metadata_->position; }
 
-  void SetMetadata(ash::mojom::AppListItemMetadataPtr metadata) {
+  void SetMetadata(std::unique_ptr<AppListItemMetadata> metadata) {
     metadata_ = std::move(metadata);
   }
-  const ash::mojom::AppListItemMetadata* GetMetadata() const {
-    return metadata_.get();
-  }
-  ash::mojom::AppListItemMetadataPtr CloneMetadata() const {
-    return metadata_.Clone();
+  const AppListItemMetadata* GetMetadata() const { return metadata_.get(); }
+  std::unique_ptr<AppListItemMetadata> CloneMetadata() const {
+    return std::make_unique<AppListItemMetadata>(*metadata_);
   }
 
   void AddObserver(AppListItemObserver* observer);
@@ -97,7 +99,7 @@
 
  protected:
   // Subclasses also have mutable access to the metadata ptr.
-  ash::mojom::AppListItemMetadata* metadata() { return metadata_.get(); }
+  AppListItemMetadata* metadata() { return metadata_.get(); }
 
   friend class ::FastShowPickler;
   friend class ash::AppListControllerImpl;
@@ -130,7 +132,7 @@
  private:
   friend class AppListModelTest;
 
-  ash::mojom::AppListItemMetadataPtr metadata_;
+  std::unique_ptr<AppListItemMetadata> metadata_;
 
   // A shortened name for the item, used for display.
   std::string short_name_;
diff --git a/ash/app_list/test/test_app_list_client.h b/ash/app_list/test/test_app_list_client.h
index dff1181..edd843e 100644
--- a/ash/app_list/test/test_app_list_client.h
+++ b/ash/app_list/test/test_app_list_client.h
@@ -44,11 +44,11 @@
   void OnAppListTargetVisibilityChanged(bool visible) override {}
   void OnAppListVisibilityChanged(bool visible) override {}
   void OnFolderCreated(int profile_id,
-                       mojom::AppListItemMetadataPtr item) override {}
+                       std::unique_ptr<AppListItemMetadata> item) override {}
   void OnFolderDeleted(int profile_id,
-                       mojom::AppListItemMetadataPtr item) override {}
+                       std::unique_ptr<AppListItemMetadata> item) override {}
   void OnItemUpdated(int profile_id,
-                     mojom::AppListItemMetadataPtr item) override {}
+                     std::unique_ptr<AppListItemMetadata> item) override {}
   void OnPageBreakItemAdded(int profile_id,
                             const std::string& id,
                             const syncer::StringOrdinal& position) override {}
diff --git a/ash/app_list/views/top_icon_animation_view.h b/ash/app_list/views/top_icon_animation_view.h
index ff71d93..aaed713 100644
--- a/ash/app_list/views/top_icon_animation_view.h
+++ b/ash/app_list/views/top_icon_animation_view.h
@@ -59,7 +59,7 @@
   // location to the small icon inside the folder icon.
   void TransformView();
 
-  // views::View
+  // views::View:
   const char* GetClassName() const override;
 
  private:
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd
index d17d579..abbf7f8 100644
--- a/ash/ash_strings.grd
+++ b/ash/ash_strings.grd
@@ -887,7 +887,7 @@
         Show network list. <ph name="STATE_TEXT">$1<ex>Connected to public wifi</ex></ph>
       </message>
 
-      <!-- Status Tray Network strings -->
+      <!-- Status Tray Bluetooth strings -->
       <message name="IDS_ASH_STATUS_TRAY_BLUETOOTH_DISPLAY_PINCODE" desc="Bluetooth pairing message typically shown on a request from a 2.0 device that has a keyboard.">
         Bluetooth device "<ph name="DEVICE_NAME">$1<ex>Nexus S</ex></ph>" would like permission to pair. Please enter this PIN code on that device: <ph name="PINCODE">$2<ex>123456</ex></ph>
       </message>
@@ -1230,18 +1230,12 @@
       <message name="IDS_ASH_STATUS_TRAY_NETWORK_CONNECTING" desc="Message for the network tray tooltip and network list when connecting to a network.">
         Connecting to <ph name="NAME">$1<ex>GoogleGuest</ex></ph>
       </message>
-      <message name="IDS_ASH_STATUS_TRAY_NETWORK_RECONNECTING" desc="Message for the network tray tooltip and network list when reconnecting to a network.">
-        Reconnecting to <ph name="NAME">$1<ex>Company VPN</ex></ph>
-      </message>
       <message name="IDS_ASH_STATUS_TRAY_NETWORK_LIST_ACTIVATE" desc="Message for the network list to activate the network.">
         Activate <ph name="NETWORKSERVICE">$1<ex>YBH Cellular</ex></ph>
       </message>
       <message name="IDS_ASH_STATUS_TRAY_NETWORK_LIST_ACTIVATING" desc="Message for the network list when activating a network.">
         <ph name="NAME">$1<ex>YBH Cellular</ex></ph>: Activating...
       </message>
-      <message name="IDS_ASH_STATUS_TRAY_NETWORK_LIST_CONNECTING" desc="Message for the network list when connecting to a network.">
-        <ph name="NAME">$1<ex>GoogleGuest</ex></ph>: Connecting...
-      </message>
       <message name="IDS_ASH_STATUS_TRAY_NETWORK_NOT_CONNECTED" desc="Description in status area or network list when no network is connected.">
         No network
       </message>
diff --git a/ash/ime/ime_controller.cc b/ash/ime/ime_controller.cc
index 0e57214..3c906d7 100644
--- a/ash/ime/ime_controller.cc
+++ b/ash/ime/ime_controller.cc
@@ -244,8 +244,7 @@
     UMA_HISTOGRAM_ENUMERATION("InputMethod.ModeChangeKeyAction",
                               ModeChangeKeyAction::kSwitchIme);
     UMA_HISTOGRAM_ENUMERATION("InputMethod.ImeSwitch",
-                              ImeSwitchType::kModeChangeKey,
-                              ImeSwitchType::kCount);
+                              ImeSwitchType::kModeChangeKey);
   } else {
     client_->ShowModeIndicator();
 
diff --git a/ash/ime/ime_switch_type.h b/ash/ime/ime_switch_type.h
index f43af4d..6058092 100644
--- a/ash/ime/ime_switch_type.h
+++ b/ash/ime/ime_switch_type.h
@@ -14,7 +14,7 @@
   kTray = 0,
   kAccelerator = 1,
   kModeChangeKey = 2,
-  kCount = 3,
+  kMaxValue = kModeChangeKey,
 };
 
 }  // namespace ash
diff --git a/ash/keyboard/ui/keyboard_controller.cc b/ash/keyboard/ui/keyboard_controller.cc
index c747292..baefcce 100644
--- a/ash/keyboard/ui/keyboard_controller.cc
+++ b/ash/keyboard/ui/keyboard_controller.cc
@@ -219,7 +219,7 @@
 
   show_on_keyboard_window_load_ = false;
   keyboard_locked_ = false;
-  DCHECK_EQ(model_.state(), KeyboardControllerState::kInitial);
+  DCHECK_EQ(model_.state(), KeyboardUIState::kInitial);
   ui_->SetController(this);
   SetContainerBehaviorInternal(mojom::ContainerType::kFullWidth);
   visual_bounds_in_root_ = gfx::Rect();
@@ -251,8 +251,8 @@
 
   // Return to the INITIAL state to ensure that transitions entering a state
   // is equal to transitions leaving the state.
-  if (model_.state() != KeyboardControllerState::kInitial)
-    ChangeState(KeyboardControllerState::kInitial);
+  if (model_.state() != KeyboardUIState::kInitial)
+    ChangeState(KeyboardUIState::kInitial);
 
   // TODO(https://crbug.com/731537): Move KeyboardController members into a
   // subobject so we can just put this code into the subobject destructor.
@@ -360,8 +360,8 @@
 
 void KeyboardController::NotifyKeyboardWindowLoaded() {
   const bool should_show = show_on_keyboard_window_load_;
-  if (model_.state() == KeyboardControllerState::kLoadingExtension)
-    ChangeState(KeyboardControllerState::kHidden);
+  if (model_.state() == KeyboardUIState::kLoading)
+    ChangeState(KeyboardUIState::kHidden);
   if (should_show) {
     // The window height is set to 0 initially or before switch to an IME in a
     // different extension. Virtual keyboard window may wait for this bounds
@@ -530,16 +530,16 @@
   TRACE_EVENT0("vk", "HideKeyboard");
 
   switch (model_.state()) {
-    case KeyboardControllerState::kUnknown:
-    case KeyboardControllerState::kInitial:
-    case KeyboardControllerState::kHidden:
+    case KeyboardUIState::kUnknown:
+    case KeyboardUIState::kInitial:
+    case KeyboardUIState::kHidden:
       return;
-    case KeyboardControllerState::kLoadingExtension:
+    case KeyboardUIState::kLoading:
       show_on_keyboard_window_load_ = false;
       return;
 
-    case KeyboardControllerState::kWillHide:
-    case KeyboardControllerState::kShown: {
+    case KeyboardUIState::kWillHide:
+    case KeyboardUIState::kShown: {
       SetTouchEventLogging(true /* enable */);
 
       // Log whether this was a user or system (automatic) action.
@@ -591,7 +591,7 @@
       }
 
       ui_->HideKeyboardWindow();
-      ChangeState(KeyboardControllerState::kHidden);
+      ChangeState(KeyboardUIState::kHidden);
 
       for (KeyboardControllerObserver& observer : observer_list_)
         observer.OnKeyboardHidden(reason == HIDE_REASON_SYSTEM_TEMPORARY);
@@ -619,10 +619,10 @@
 }
 
 void KeyboardController::HideKeyboardImplicitlyBySystem() {
-  if (model_.state() != KeyboardControllerState::kShown || keyboard_locked_)
+  if (model_.state() != KeyboardUIState::kShown || keyboard_locked_)
     return;
 
-  ChangeState(KeyboardControllerState::kWillHide);
+  ChangeState(KeyboardUIState::kWillHide);
 
   base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
       FROM_HERE,
@@ -634,7 +634,7 @@
 
 // private
 void KeyboardController::HideAnimationFinished() {
-  if (model_.state() == KeyboardControllerState::kHidden) {
+  if (model_.state() == KeyboardUIState::kHidden) {
     if (queued_container_type_) {
       SetContainerBehaviorInternal(queued_container_type_->container_type());
       // The position of the container window will be adjusted shortly in
@@ -701,7 +701,7 @@
 }
 
 void KeyboardController::LoadKeyboardWindowInBackground() {
-  DCHECK_EQ(model_.state(), KeyboardControllerState::kInitial);
+  DCHECK_EQ(model_.state(), KeyboardUIState::kInitial);
 
   TRACE_EVENT0("vk", "LoadKeyboardWindowInBackground");
 
@@ -716,7 +716,7 @@
   keyboard_window->AddObserver(this);
   parent_container_->AddChild(keyboard_window);
 
-  ChangeState(KeyboardControllerState::kLoadingExtension);
+  ChangeState(KeyboardUIState::kLoading);
 }
 
 ui::InputMethod* KeyboardController::GetInputMethodForTest() {
@@ -796,10 +796,10 @@
 
   if (should_hide) {
     switch (model_.state()) {
-      case KeyboardControllerState::kLoadingExtension:
+      case KeyboardUIState::kLoading:
         show_on_keyboard_window_load_ = false;
         return;
-      case KeyboardControllerState::kShown:
+      case KeyboardUIState::kShown:
         HideKeyboardImplicitlyBySystem();
         return;
       default:
@@ -807,11 +807,11 @@
     }
   } else {
     switch (model_.state()) {
-      case KeyboardControllerState::kWillHide:
+      case KeyboardUIState::kWillHide:
         // Abort a pending keyboard hide.
-        ChangeState(KeyboardControllerState::kShown);
+        ChangeState(KeyboardUIState::kShown);
         return;
-      case KeyboardControllerState::kHidden:
+      case KeyboardUIState::kHidden:
         if (focused && is_web)
           ShowKeyboardIfWithinTransientBlurThreshold();
         return;
@@ -846,7 +846,7 @@
 
 void KeyboardController::PopulateKeyboardContent(
     aura::Window* target_container) {
-  DCHECK_NE(model_.state(), KeyboardControllerState::kInitial);
+  DCHECK_NE(model_.state(), KeyboardUIState::kInitial);
 
   DVLOG(1) << "PopulateKeyboardContent: " << StateToStr(model_.state());
   TRACE_EVENT0("vk", "PopulateKeyboardContent");
@@ -858,9 +858,9 @@
   DCHECK_EQ(parent_container_, keyboard_window->parent());
 
   switch (model_.state()) {
-    case KeyboardControllerState::kShown:
+    case KeyboardUIState::kShown:
       return;
-    case KeyboardControllerState::kLoadingExtension:
+    case KeyboardUIState::kLoading:
       show_on_keyboard_window_load_ = true;
       return;
     default:
@@ -872,14 +872,14 @@
   SetTouchEventLogging(false /* enable */);
 
   switch (model_.state()) {
-    case KeyboardControllerState::kWillHide:
-      ChangeState(KeyboardControllerState::kShown);
+    case KeyboardUIState::kWillHide:
+      ChangeState(KeyboardUIState::kShown);
       return;
     default:
       break;
   }
 
-  DCHECK_EQ(model_.state(), KeyboardControllerState::kHidden);
+  DCHECK_EQ(model_.state(), KeyboardUIState::kHidden);
 
   // If the container is not animating, makes sure the position and opacity
   // are at begin states for animation.
@@ -907,7 +907,7 @@
   // gets destroyed.
   queued_container_type_ = nullptr;
 
-  ChangeState(KeyboardControllerState::kShown);
+  ChangeState(KeyboardUIState::kShown);
 
   UMA_HISTOGRAM_ENUMERATION("InputMethod.VirtualKeyboard.ContainerBehavior",
                             GetActiveContainerType());
@@ -915,7 +915,7 @@
 
 bool KeyboardController::WillHideKeyboard() const {
   bool res = weak_factory_will_hide_.HasWeakPtrs();
-  DCHECK_EQ(res, model_.state() == KeyboardControllerState::kWillHide);
+  DCHECK_EQ(res, model_.state() == KeyboardUIState::kWillHide);
   return res;
 }
 
@@ -924,18 +924,18 @@
     observer.OnKeyboardConfigChanged();
 }
 
-void KeyboardController::ChangeState(KeyboardControllerState state) {
+void KeyboardController::ChangeState(KeyboardUIState state) {
   model_.ChangeState(state);
 
-  if (state != KeyboardControllerState::kWillHide)
+  if (state != KeyboardUIState::kWillHide)
     weak_factory_will_hide_.InvalidateWeakPtrs();
-  if (state != KeyboardControllerState::kLoadingExtension)
+  if (state != KeyboardUIState::kLoading)
     show_on_keyboard_window_load_ = false;
 
   weak_factory_report_lingering_state_.InvalidateWeakPtrs();
   switch (model_.state()) {
-    case KeyboardControllerState::kLoadingExtension:
-    case KeyboardControllerState::kWillHide:
+    case KeyboardUIState::kLoading:
+    case KeyboardUIState::kWillHide:
       base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
           FROM_HERE,
           base::BindOnce(&KeyboardController::ReportLingeringState,
@@ -1022,7 +1022,7 @@
     return;
   }
 
-  if (model_.state() == KeyboardControllerState::kShown) {
+  if (model_.state() == KeyboardUIState::kShown) {
     // Keyboard is already shown. Hiding the keyboard at first then switching
     // container type.
     queued_container_type_ = std::make_unique<QueuedContainerType>(
@@ -1054,7 +1054,7 @@
 }
 
 bool KeyboardController::IsKeyboardVisible() {
-  if (model_.state() == KeyboardControllerState::kShown) {
+  if (model_.state() == KeyboardUIState::kShown) {
     DCHECK(IsEnabled());
     return true;
   }
diff --git a/ash/keyboard/ui/keyboard_controller.h b/ash/keyboard/ui/keyboard_controller.h
index 4d99e54..a18377b 100644
--- a/ash/keyboard/ui/keyboard_controller.h
+++ b/ash/keyboard/ui/keyboard_controller.h
@@ -232,7 +232,7 @@
       std::unique_ptr<ContainerBehavior> container_behavior) {
     container_behavior_ = std::move(container_behavior);
   }
-  KeyboardControllerState GetStateForTest() const { return model_.state(); }
+  KeyboardUIState GetStateForTest() const { return model_.state(); }
   ui::InputMethod* GetInputMethodForTest();
   void EnsureCaretInWorkAreaForTest(const gfx::Rect& occluded_bounds_in_root);
 
@@ -354,11 +354,10 @@
   void NotifyKeyboardWindowLoaded();
 
   // Validates the state transition. Called from ChangeState.
-  void CheckStateTransition(KeyboardControllerState prev,
-                            KeyboardControllerState next);
+  void CheckStateTransition(KeyboardUIState prev, KeyboardUIState next);
 
   // Changes the current state and validates the transition.
-  void ChangeState(KeyboardControllerState state);
+  void ChangeState(KeyboardUIState state);
 
   // Reports error histogram in case lingering in an intermediate state.
   void ReportLingeringState();
diff --git a/ash/keyboard/ui/keyboard_ui_model.cc b/ash/keyboard/ui/keyboard_ui_model.cc
index b65d002..054a259 100644
--- a/ash/keyboard/ui/keyboard_ui_model.cc
+++ b/ash/keyboard/ui/keyboard_ui_model.cc
@@ -14,43 +14,37 @@
 
 // Returns whether a given state transition is valid.
 // See the design document linked in https://crbug.com/71990.
-bool IsAllowedStateTransition(KeyboardControllerState from,
-                              KeyboardControllerState to) {
+bool IsAllowedStateTransition(KeyboardUIState from, KeyboardUIState to) {
   static const base::NoDestructor<
-      std::set<std::pair<KeyboardControllerState, KeyboardControllerState>>>
+      std::set<std::pair<KeyboardUIState, KeyboardUIState>>>
       kAllowedStateTransition({
           // The initial ShowKeyboard scenario
           // INITIAL -> LOADING_EXTENSION -> HIDDEN -> SHOWN.
-          {KeyboardControllerState::kInitial,
-           KeyboardControllerState::kLoadingExtension},
-          {KeyboardControllerState::kLoadingExtension,
-           KeyboardControllerState::kHidden},
-          {KeyboardControllerState::kHidden, KeyboardControllerState::kShown},
+          {KeyboardUIState::kInitial, KeyboardUIState::kLoading},
+          {KeyboardUIState::kLoading, KeyboardUIState::kHidden},
+          {KeyboardUIState::kHidden, KeyboardUIState::kShown},
 
           // Hide scenario
           // SHOWN -> WILL_HIDE -> HIDDEN.
-          {KeyboardControllerState::kShown, KeyboardControllerState::kWillHide},
-          {KeyboardControllerState::kWillHide,
-           KeyboardControllerState::kHidden},
+          {KeyboardUIState::kShown, KeyboardUIState::kWillHide},
+          {KeyboardUIState::kWillHide, KeyboardUIState::kHidden},
 
           // Focus transition scenario
           // SHOWN -> WILL_HIDE -> SHOWN.
-          {KeyboardControllerState::kWillHide, KeyboardControllerState::kShown},
+          {KeyboardUIState::kWillHide, KeyboardUIState::kShown},
 
           // HideKeyboard can be called at anytime for example on shutdown.
-          {KeyboardControllerState::kShown, KeyboardControllerState::kHidden},
+          {KeyboardUIState::kShown, KeyboardUIState::kHidden},
 
           // Return to INITIAL when keyboard is disabled.
-          {KeyboardControllerState::kLoadingExtension,
-           KeyboardControllerState::kInitial},
-          {KeyboardControllerState::kHidden, KeyboardControllerState::kInitial},
+          {KeyboardUIState::kLoading, KeyboardUIState::kInitial},
+          {KeyboardUIState::kHidden, KeyboardUIState::kInitial},
       });
   return kAllowedStateTransition->count(std::make_pair(from, to)) == 1;
 }
 
 // Records a state transition for metrics.
-void RecordStateTransition(KeyboardControllerState prev,
-                           KeyboardControllerState next) {
+void RecordStateTransition(KeyboardUIState prev, KeyboardUIState next) {
   const bool valid_transition = IsAllowedStateTransition(prev, next);
 
   // Emit UMA
@@ -68,26 +62,26 @@
 
 }  // namespace
 
-std::string StateToStr(KeyboardControllerState state) {
+std::string StateToStr(KeyboardUIState state) {
   switch (state) {
-    case KeyboardControllerState::kUnknown:
+    case KeyboardUIState::kUnknown:
       return "UNKNOWN";
-    case KeyboardControllerState::kInitial:
+    case KeyboardUIState::kInitial:
       return "INITIAL";
-    case KeyboardControllerState::kLoadingExtension:
-      return "LOADING_EXTENSION";
-    case KeyboardControllerState::kShown:
+    case KeyboardUIState::kLoading:
+      return "LOADING";
+    case KeyboardUIState::kShown:
       return "SHOWN";
-    case KeyboardControllerState::kWillHide:
+    case KeyboardUIState::kWillHide:
       return "WILL_HIDE";
-    case KeyboardControllerState::kHidden:
+    case KeyboardUIState::kHidden:
       return "HIDDEN";
   }
 }
 
 KeyboardUIModel::KeyboardUIModel() = default;
 
-void KeyboardUIModel::ChangeState(KeyboardControllerState new_state) {
+void KeyboardUIModel::ChangeState(KeyboardUIState new_state) {
   RecordStateTransition(state_, new_state);
 
   if (new_state == state_)
diff --git a/ash/keyboard/ui/keyboard_ui_model.h b/ash/keyboard/ui/keyboard_ui_model.h
index 94a593f3..7a5497b 100644
--- a/ash/keyboard/ui/keyboard_ui_model.h
+++ b/ash/keyboard/ui/keyboard_ui_model.h
@@ -13,21 +13,20 @@
 
 namespace keyboard {
 
-// TODO(https://crbug.com/964191): Change this to be part of the model.
-// Represents the current state of the keyboard managed by the controller.
+// Represents the current state of the keyboard UI.
 // These values are persisted to logs. Entries should not be renumbered and
 // numeric values should never be reused.
-enum class KeyboardControllerState {
+enum class KeyboardUIState {
   kUnknown = 0,
   // Keyboard has never been shown.
   kInitial = 1,
   // Waiting for an extension to be loaded. Will move to HIDDEN if this is
   // loading pre-emptively, otherwise will move to SHOWN.
-  kLoadingExtension = 2,
+  kLoading = 2,
   // kShowing = 3,  // no longer used
   // Keyboard is shown.
   kShown = 4,
-  // Keyboard is still shown, but will move to HIDING in a short period, or if
+  // Keyboard is still shown, but will move to HIDDEN in a short period, or if
   // an input element gets focused again, will move to SHOWN.
   kWillHide = 5,
   // kHiding = 6,  // no longer used
@@ -36,8 +35,8 @@
   kMaxValue = kHidden
 };
 
-// Convert a state into a string.
-std::string StateToStr(KeyboardControllerState state);
+// Returns the string representation of a keyboard UI state.
+std::string StateToStr(KeyboardUIState state);
 
 // Model for the virtual keyboard UI.
 class KEYBOARD_EXPORT KeyboardUIModel {
@@ -45,14 +44,14 @@
   KeyboardUIModel();
 
   // Get the current state of the keyboard UI.
-  KeyboardControllerState state() const { return state_; }
+  KeyboardUIState state() const { return state_; }
 
   // Changes the current state to another. Only accepts valid state transitions.
-  void ChangeState(KeyboardControllerState new_state);
+  void ChangeState(KeyboardUIState new_state);
 
  private:
   // Current state of the keyboard UI.
-  KeyboardControllerState state_ = KeyboardControllerState::kInitial;
+  KeyboardUIState state_ = KeyboardUIState::kInitial;
 
   DISALLOW_COPY_AND_ASSIGN(KeyboardUIModel);
 };
diff --git a/ash/keyboard/ui/test/keyboard_test_util.cc b/ash/keyboard/ui/test/keyboard_test_util.cc
index a93958a..8b1e48d 100644
--- a/ash/keyboard/ui/test/keyboard_test_util.cc
+++ b/ash/keyboard/ui/test/keyboard_test_util.cc
@@ -55,7 +55,7 @@
   // single RunUntilIdle call.
   base::RunLoop run_loop;
   while (KeyboardController::Get()->GetStateForTest() ==
-         KeyboardControllerState::kLoadingExtension) {
+         KeyboardUIState::kLoading) {
     run_loop.RunUntilIdle();
   }
   return true;
@@ -82,18 +82,15 @@
   DCHECK(keyboard_controller->IsEnabled());
 
   // KeyboardController sets its state to SHOWN when it is about to show.
-  return keyboard_controller->GetStateForTest() ==
-         KeyboardControllerState::kShown;
+  return keyboard_controller->GetStateForTest() == KeyboardUIState::kShown;
 }
 
 bool IsKeyboardHiding() {
   auto* keyboard_controller = KeyboardController::Get();
   DCHECK(keyboard_controller->IsEnabled());
 
-  return keyboard_controller->GetStateForTest() ==
-             KeyboardControllerState::kWillHide ||
-         keyboard_controller->GetStateForTest() ==
-             KeyboardControllerState::kHidden;
+  return keyboard_controller->GetStateForTest() == KeyboardUIState::kWillHide ||
+         keyboard_controller->GetStateForTest() == KeyboardUIState::kHidden;
 }
 
 gfx::Rect KeyboardBoundsFromRootBounds(const gfx::Rect& root_bounds,
diff --git a/ash/public/cpp/BUILD.gn b/ash/public/cpp/BUILD.gn
index 6d60872..3edda99 100644
--- a/ash/public/cpp/BUILD.gn
+++ b/ash/public/cpp/BUILD.gn
@@ -148,7 +148,6 @@
     "//chromeos/constants",
     "//chromeos/dbus/power:power_manager_proto",
     "//components/prefs",
-    "//components/sync:rest_of_sync",
     "//mojo/public/cpp/bindings",
     "//services/service_manager/public/cpp",
     "//services/ws/public/mojom",
@@ -192,6 +191,7 @@
   defines = [ "ASH_PUBLIC_IMPLEMENTATION" ]
 
   public_deps = [
+    "//components/sync:rest_of_sync",
     "//ui/gfx",
   ]
 }
diff --git a/ash/public/cpp/app_list/app_list_client.h b/ash/public/cpp/app_list/app_list_client.h
index 9319db30..739c746 100644
--- a/ash/public/cpp/app_list/app_list_client.h
+++ b/ash/public/cpp/app_list/app_list_client.h
@@ -99,14 +99,17 @@
                                    const std::string& id,
                                    GetContextMenuModelCallback callback) = 0;
   // Invoked when a folder is created in Ash (e.g. merge items into a folder).
-  virtual void OnFolderCreated(int profile_id,
-                               ash::mojom::AppListItemMetadataPtr folder) = 0;
+  virtual void OnFolderCreated(
+      int profile_id,
+      std::unique_ptr<ash::AppListItemMetadata> folder) = 0;
   // Invoked when a folder has only one item left and so gets removed.
-  virtual void OnFolderDeleted(int profile_id,
-                               ash::mojom::AppListItemMetadataPtr folder) = 0;
+  virtual void OnFolderDeleted(
+      int profile_id,
+      std::unique_ptr<ash::AppListItemMetadata> folder) = 0;
   // Invoked when user changes a folder's name or an item's position.
-  virtual void OnItemUpdated(int profile_id,
-                             ash::mojom::AppListItemMetadataPtr folder) = 0;
+  virtual void OnItemUpdated(
+      int profile_id,
+      std::unique_ptr<ash::AppListItemMetadata> folder) = 0;
   // Invoked when a "page break" item is added with |id| and |position|.
   virtual void OnPageBreakItemAdded(int profile_id,
                                     const std::string& id,
diff --git a/ash/public/cpp/app_list/app_list_controller.h b/ash/public/cpp/app_list/app_list_controller.h
index e9cbe3f..ede9397 100644
--- a/ash/public/cpp/app_list/app_list_controller.h
+++ b/ash/public/cpp/app_list/app_list_controller.h
@@ -5,6 +5,8 @@
 #ifndef ASH_PUBLIC_CPP_APP_LIST_APP_LIST_CONTROLLER_H_
 #define ASH_PUBLIC_CPP_APP_LIST_APP_LIST_CONTROLLER_H_
 
+#include <memory>
+
 #include "ash/public/cpp/ash_public_export.h"
 // TODO(crbug.com/958134): Remove.
 #include "ash/public/interfaces/app_list.mojom.h"
@@ -25,7 +27,6 @@
 //   happen while installing/uninstalling apps and the app list gets toggled.
 class ASH_PUBLIC_EXPORT AppListController {
  public:
-  using AppListItemMetadataPtr = ash::mojom::AppListItemMetadataPtr;
   using SearchResultMetadataPtr = ash::mojom::SearchResultMetadataPtr;
 
   // Gets the instance.
@@ -35,11 +36,12 @@
   virtual void SetClient(AppListClient* client) = 0;
 
   // Adds an item to AppListModel.
-  virtual void AddItem(AppListItemMetadataPtr app_item) = 0;
+  virtual void AddItem(std::unique_ptr<ash::AppListItemMetadata> app_item) = 0;
 
   // Adds an item into a certain folder in AppListModel.
-  virtual void AddItemToFolder(AppListItemMetadataPtr app_item,
-                               const std::string& folder_id) = 0;
+  virtual void AddItemToFolder(
+      std::unique_ptr<ash::AppListItemMetadata> app_item,
+      const std::string& folder_id) = 0;
 
   // Removes an item by its id from AppListModel.
   virtual void RemoveItem(const std::string& id) = 0;
@@ -85,8 +87,9 @@
       std::vector<SearchResultMetadataPtr> results) = 0;
 
   // Updates an item's metadata (e.g. name, position, etc).
-  virtual void SetItemMetadata(const std::string& id,
-                               AppListItemMetadataPtr data) = 0;
+  virtual void SetItemMetadata(
+      const std::string& id,
+      std::unique_ptr<ash::AppListItemMetadata> data) = 0;
 
   // Updates an item's icon.
   virtual void SetItemIcon(const std::string& id,
@@ -101,9 +104,10 @@
                                         int32_t percent_downloaded) = 0;
 
   // Update the whole model, usually when profile changes happen in Chrome.
-  virtual void SetModelData(int profile_id,
-                            std::vector<AppListItemMetadataPtr> apps,
-                            bool is_search_engine_google) = 0;
+  virtual void SetModelData(
+      int profile_id,
+      std::vector<std::unique_ptr<ash::AppListItemMetadata>> apps,
+      bool is_search_engine_google) = 0;
 
   // Updates a search rresult's metadata.
   virtual void SetSearchResultMetadata(SearchResultMetadataPtr metadata) = 0;
@@ -131,8 +135,7 @@
   //                           creating; if it's invalid then the final position
   //                           is determined in Ash.
   // |oem_folder|: the meta data of the existing/created OEM folder.
-  using FindOrCreateOemFolderCallback =
-      base::OnceCallback<void(AppListItemMetadataPtr)>;
+  using FindOrCreateOemFolderCallback = base::OnceClosure;
   virtual void FindOrCreateOemFolder(
       const std::string& oem_folder_name,
       const syncer::StringOrdinal& preferred_oem_position,
@@ -144,7 +147,7 @@
   //                           Ash.
   // |oem_folder|: the meta data of the OEM folder, or null if it doesn't exist.
   using ResolveOemFolderPositionCallback =
-      base::OnceCallback<void(AppListItemMetadataPtr)>;
+      base::OnceCallback<void(std::unique_ptr<ash::AppListItemMetadata>)>;
   virtual void ResolveOemFolderPosition(
       const syncer::StringOrdinal& preferred_oem_position,
       ResolveOemFolderPositionCallback callback) = 0;
diff --git a/ash/public/cpp/app_list/app_list_types.cc b/ash/public/cpp/app_list/app_list_types.cc
index a16d517..b873b59 100644
--- a/ash/public/cpp/app_list/app_list_types.cc
+++ b/ash/public/cpp/app_list/app_list_types.cc
@@ -8,6 +8,14 @@
 
 const char kOemFolderId[] = "ddb1da55-d478-4243-8642-56d3041f0263";
 
+////////////////////////////////////////////////////////////////////////////////
+// AppListItemMetadata:
+
+AppListItemMetadata::AppListItemMetadata() = default;
+AppListItemMetadata::AppListItemMetadata(const AppListItemMetadata& rhs) =
+    default;
+AppListItemMetadata::~AppListItemMetadata() = default;
+
 OmniBoxZeroStateAction GetOmniBoxZeroStateAction(int button_index) {
   if (button_index < 0 ||
       button_index >=
diff --git a/ash/public/cpp/app_list/app_list_types.h b/ash/public/cpp/app_list/app_list_types.h
index 16ec50f..65e576a 100644
--- a/ash/public/cpp/app_list/app_list_types.h
+++ b/ash/public/cpp/app_list/app_list_types.h
@@ -5,9 +5,11 @@
 #ifndef ASH_PUBLIC_CPP_APP_LIST_APP_LIST_TYPES_H_
 #define ASH_PUBLIC_CPP_APP_LIST_APP_LIST_TYPES_H_
 
+#include <string>
 #include <vector>
 
 #include "ash/public/cpp/ash_public_export.h"
+#include "components/sync/model/string_ordinal.h"
 #include "ui/gfx/image/image_skia.h"
 #include "ui/gfx/range/range.h"
 
@@ -23,6 +25,26 @@
 // Id of OEM folder in app list.
 ASH_PUBLIC_EXPORT extern const char kOemFolderId[];
 
+// A structure holding the common information which is sent between ash and,
+// chrome representing an app list item.
+struct ASH_PUBLIC_EXPORT AppListItemMetadata {
+  AppListItemMetadata();
+  AppListItemMetadata(const AppListItemMetadata& rhs);
+  ~AppListItemMetadata();
+
+  std::string id;          // Id of the app list item.
+  std::string name;        // Corresponding app/folder's name of the item.
+  std::string short_name;  // Corresponding app's short name of the item. Empty
+                           // if the app doesn't have one or it's a folder.
+  std::string folder_id;   // Id of folder where the item resides.
+  syncer::StringOrdinal position;  // Position of the item.
+  bool is_folder = false;          // Whether this item is a folder.
+  bool is_persistent = false;  // Whether this folder is allowed to contain only
+                               // 1 item.
+  gfx::ImageSkia icon;         // The icon of this item.
+  bool is_page_break = false;  // Whether this item is a "page break" item.
+};
+
 // All possible states of the app list.
 // Note: Do not change the order of these as they are used for metrics.
 enum class AppListState {
diff --git a/ash/public/interfaces/app_list.mojom b/ash/public/interfaces/app_list.mojom
index 5527921..9f5fb1b 100644
--- a/ash/public/interfaces/app_list.mojom
+++ b/ash/public/interfaces/app_list.mojom
@@ -13,23 +13,6 @@
 import "ui/gfx/range/mojo/range.mojom";
 import "url/mojom/url.mojom";
 
-// A structure holding the common information which is sent between ash and,
-// chrome representing an app list item.
-// This structure should be kept as small as possible so that minimum data
-// is sent via mojo calls when an item is moved or reparented.
-struct AppListItemMetadata {
-  string id;          // The id of the app list item.
-  string name;        // The corresponding app or folder's name of the item.
-  string short_name;  // The corresponding app's short name of the item. It's
-                      // empty if the app doesn't have one or it's a folder.
-  string folder_id;   // The id of the item's folder.
-  syncer.mojom.StringOrdinal position; // The position of the item.
-  bool is_folder;     // Whether this item is a folder.
-  bool is_persistent; // Whether this folder is allowed to contain 1 item.
-  gfx.mojom.ImageSkia? icon;  // The icon of this item.
-  bool is_page_break; // Whether this item is a "page break" item.
-};
-
 // A structure holding the common information which is sent from chrome to ash,
 // representing a search result.
 struct SearchResultMetadata {
diff --git a/ash/shelf/shelf_layout_manager_unittest.cc b/ash/shelf/shelf_layout_manager_unittest.cc
index 869c940..73ebd5b 100644
--- a/ash/shelf/shelf_layout_manager_unittest.cc
+++ b/ash/shelf/shelf_layout_manager_unittest.cc
@@ -1344,7 +1344,7 @@
   EXPECT_EQ(SHELF_AUTO_HIDE, shelf->GetVisibilityState());
   EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->GetAutoHideState());
   EXPECT_EQ(display.bounds().bottom() - kHiddenShelfInScreenPortion,
-            GetShelfWidget()->GetWindowBoundsInScreen().y());
+            GetShelfWidget()->GetNativeWindow()->GetTargetBounds().y());
 }
 
 // Assertions around SetAutoHideBehavior.
diff --git a/ash/system/accessibility/accessibility_feature_disable_dialog.cc b/ash/system/accessibility/accessibility_feature_disable_dialog.cc
index 90315799..6030ffc 100644
--- a/ash/system/accessibility/accessibility_feature_disable_dialog.cc
+++ b/ash/system/accessibility/accessibility_feature_disable_dialog.cc
@@ -89,4 +89,8 @@
   return weak_ptr_factory_.GetWeakPtr();
 }
 
+const char* AccessibilityFeatureDisableDialog::GetClassName() const {
+  return "AccessibilityFeatureDisableDialog";
+}
+
 }  // namespace ash
\ No newline at end of file
diff --git a/ash/system/accessibility/accessibility_feature_disable_dialog.h b/ash/system/accessibility/accessibility_feature_disable_dialog.h
index 60a8cc9..a33079a0 100644
--- a/ash/system/accessibility/accessibility_feature_disable_dialog.h
+++ b/ash/system/accessibility/accessibility_feature_disable_dialog.h
@@ -34,6 +34,9 @@
 
   base::WeakPtr<AccessibilityFeatureDisableDialog> GetWeakPtr();
 
+  // views::View:
+  const char* GetClassName() const override;
+
  private:
   const base::string16 window_title_;
   base::OnceClosure on_accept_callback_;
diff --git a/ash/system/accessibility/autoclick_menu_view.cc b/ash/system/accessibility/autoclick_menu_view.cc
index 23651c9..668178f 100644
--- a/ash/system/accessibility/autoclick_menu_view.cc
+++ b/ash/system/accessibility/autoclick_menu_view.cc
@@ -56,6 +56,9 @@
 
   ~AutoclickMenuButton() override = default;
 
+  // views::Button:
+  const char* GetClassName() const override { return "AutoclickMenuButton"; }
+
   // Set the vector icon shown in a circle.
   void SetVectorIcon(const gfx::VectorIcon& icon) {
     icon_ = &icon;
@@ -137,6 +140,10 @@
   SetAnchorRect(rect);
 }
 
+const char* AutoclickMenuBubbleView::GetClassName() const {
+  return "AutoclickMenuBubbleView";
+}
+
 // ------ AutoclickMenuView  ------ //
 
 AutoclickMenuView::AutoclickMenuView(mojom::AutoclickEventType type,
@@ -304,4 +311,8 @@
                             type);
 }
 
+const char* AutoclickMenuView::GetClassName() const {
+  return "AutoclickMenuView";
+}
+
 }  // namespace ash
diff --git a/ash/system/accessibility/autoclick_menu_view.h b/ash/system/accessibility/autoclick_menu_view.h
index 789ddec..5262bcb 100644
--- a/ash/system/accessibility/autoclick_menu_view.h
+++ b/ash/system/accessibility/autoclick_menu_view.h
@@ -25,6 +25,9 @@
 
   void MoveToPosition(const gfx::Rect& rect);
 
+  // views::View:
+  const char* GetClassName() const override;
+
  private:
   DISALLOW_COPY_AND_ASSIGN(AutoclickMenuBubbleView);
 };
@@ -53,6 +56,9 @@
   // views::ButtonListener:
   void ButtonPressed(views::Button* sender, const ui::Event& event) override;
 
+  // views::View:
+  const char* GetClassName() const override;
+
  private:
   // Unowned. Owned by views hierarchy.
   AutoclickMenuButton* left_click_button_;
diff --git a/ash/system/accessibility/dictation_button_tray.cc b/ash/system/accessibility/dictation_button_tray.cc
index d5bdc3c..b99fd70 100644
--- a/ash/system/accessibility/dictation_button_tray.cc
+++ b/ash/system/accessibility/dictation_button_tray.cc
@@ -78,6 +78,10 @@
   // This class has no bubbles to hide.
 }
 
+const char* DictationButtonTray::GetClassName() const {
+  return "DictationButtonTray";
+}
+
 void DictationButtonTray::UpdateIcon(bool dictation_active) {
   if (dictation_active) {
     icon_->SetImage(on_image_);
diff --git a/ash/system/accessibility/dictation_button_tray.h b/ash/system/accessibility/dictation_button_tray.h
index 9568d16..bf646c6 100644
--- a/ash/system/accessibility/dictation_button_tray.h
+++ b/ash/system/accessibility/dictation_button_tray.h
@@ -46,6 +46,9 @@
   base::string16 GetAccessibleNameForTray() override;
   void HideBubbleWithView(const TrayBubbleView* bubble_view) override;
 
+  // views::View:
+  const char* GetClassName() const override;
+
  private:
   friend class DictationButtonTrayTest;
 
diff --git a/ash/system/accessibility/tray_accessibility.cc b/ash/system/accessibility/tray_accessibility.cc
index 713891f..ba303d3f 100644
--- a/ash/system/accessibility/tray_accessibility.cc
+++ b/ash/system/accessibility/tray_accessibility.cc
@@ -137,6 +137,10 @@
                                             sticky_keys_enabled_);
 }
 
+const char* AccessibilityDetailedView::GetClassName() const {
+  return "AccessibilityDetailedView";
+}
+
 void AccessibilityDetailedView::AppendAccessibilityList() {
   CreateScrollableList();
 
diff --git a/ash/system/accessibility/tray_accessibility.h b/ash/system/accessibility/tray_accessibility.h
index 03e26ee..1eb765b 100644
--- a/ash/system/accessibility/tray_accessibility.h
+++ b/ash/system/accessibility/tray_accessibility.h
@@ -42,6 +42,9 @@
 
   void OnAccessibilityStatusChanged();
 
+  // views::View
+  const char* GetClassName() const override;
+
  private:
   friend class ::ash::TrayAccessibilityLoginScreenTest;
   friend class ::ash::TrayAccessibilityTest;
diff --git a/ash/system/audio/audio_detailed_view.cc b/ash/system/audio/audio_detailed_view.cc
index 63ee464..9aab6993 100644
--- a/ash/system/audio/audio_detailed_view.cc
+++ b/ash/system/audio/audio_detailed_view.cc
@@ -70,6 +70,10 @@
   Layout();
 }
 
+const char* AudioDetailedView::GetClassName() const {
+  return "AudioDetailedView";
+}
+
 void AudioDetailedView::AddAudioSubHeader(const gfx::VectorIcon& icon,
                                           int text_id) {
   TriView* header = AddScrollListSubHeader(icon, text_id);
diff --git a/ash/system/audio/audio_detailed_view.h b/ash/system/audio/audio_detailed_view.h
index 67b240c1..e920332 100644
--- a/ash/system/audio/audio_detailed_view.h
+++ b/ash/system/audio/audio_detailed_view.h
@@ -26,6 +26,9 @@
 
   void Update();
 
+  // views::View:
+  const char* GetClassName() const override;
+
  private:
   // Helper function to add non-clickable header rows within the scrollable
   // list.
diff --git a/ash/system/audio/unified_volume_view.cc b/ash/system/audio/unified_volume_view.cc
index 739618d..d4f97e7 100644
--- a/ash/system/audio/unified_volume_view.cc
+++ b/ash/system/audio/unified_volume_view.cc
@@ -120,6 +120,8 @@
                                                          kTrayItemSize / 2);
   }
 
+  const char* GetClassName() const override { return "MoreButton"; }
+
  private:
   DISALLOW_COPY_AND_ASSIGN(MoreButton);
 };
@@ -140,6 +142,10 @@
   CrasAudioHandler::Get()->RemoveAudioObserver(this);
 }
 
+const char* UnifiedVolumeView::GetClassName() const {
+  return "UnifiedVolumeView";
+}
+
 void UnifiedVolumeView::Update(bool by_user) {
   bool is_muted = CrasAudioHandler::Get()->IsOutputMuted();
   float level = CrasAudioHandler::Get()->GetOutputVolumePercent() / 100.f;
diff --git a/ash/system/audio/unified_volume_view.h b/ash/system/audio/unified_volume_view.h
index 69e2fba..dadbfd2 100644
--- a/ash/system/audio/unified_volume_view.h
+++ b/ash/system/audio/unified_volume_view.h
@@ -21,6 +21,9 @@
 
   views::Button* more_button() { return more_button_; }
 
+  // views::View:
+  const char* GetClassName() const override;
+
  private:
   void Update(bool by_user);
 
diff --git a/ash/system/bluetooth/bluetooth_detailed_view.cc b/ash/system/bluetooth/bluetooth_detailed_view.cc
index 4271f36..eb2a182 100644
--- a/ash/system/bluetooth/bluetooth_detailed_view.cc
+++ b/ash/system/bluetooth/bluetooth_detailed_view.cc
@@ -218,6 +218,10 @@
     toggle_->AnimateIsOn(is_on);
 }
 
+const char* BluetoothDetailedView::GetClassName() const {
+  return "BluetoothDetailedView";
+}
+
 void BluetoothDetailedView::CreateItems() {
   CreateScrollableList();
   CreateTitleRow(IDS_ASH_STATUS_TRAY_BLUETOOTH);
diff --git a/ash/system/bluetooth/bluetooth_detailed_view.h b/ash/system/bluetooth/bluetooth_detailed_view.h
index 7176981..2001148 100644
--- a/ash/system/bluetooth/bluetooth_detailed_view.h
+++ b/ash/system/bluetooth/bluetooth_detailed_view.h
@@ -47,6 +47,9 @@
   // Sets the state of the toggle in the header.
   void SetToggleIsOn(bool is_on);
 
+  // views::View:
+  const char* GetClassName() const override;
+
  private:
   void CreateItems();
 
diff --git a/ash/system/brightness/unified_brightness_view.cc b/ash/system/brightness/unified_brightness_view.cc
index fc60c2e..3215fc3 100644
--- a/ash/system/brightness/unified_brightness_view.cc
+++ b/ash/system/brightness/unified_brightness_view.cc
@@ -32,4 +32,8 @@
   SetSliderValue(model_->display_brightness(), by_user);
 }
 
+const char* UnifiedBrightnessView::GetClassName() const {
+  return "UnifiedBrightnessView";
+}
+
 }  // namespace ash
diff --git a/ash/system/brightness/unified_brightness_view.h b/ash/system/brightness/unified_brightness_view.h
index 596574b..8dcea58 100644
--- a/ash/system/brightness/unified_brightness_view.h
+++ b/ash/system/brightness/unified_brightness_view.h
@@ -24,6 +24,9 @@
   // UnifiedSystemTrayModel::Observer:
   void OnDisplayBrightnessChanged(bool by_user) override;
 
+  // views::View:
+  const char* GetClassName() const override;
+
  private:
   UnifiedSystemTrayModel* const model_;
 
diff --git a/ash/system/cast/tray_cast.cc b/ash/system/cast/tray_cast.cc
index 07e3c7e..e5aa7ac 100644
--- a/ash/system/cast/tray_cast.cc
+++ b/ash/system/cast/tray_cast.cc
@@ -112,6 +112,10 @@
   Layout();
 }
 
+const char* CastDetailedView::GetClassName() const {
+  return "CastDetailedView";
+}
+
 void CastDetailedView::UpdateReceiverListFromCachedData() {
   // Remove all of the existing views.
   view_to_sink_map_.clear();
diff --git a/ash/system/cast/tray_cast.h b/ash/system/cast/tray_cast.h
index 0c1d717..85435554 100644
--- a/ash/system/cast/tray_cast.h
+++ b/ash/system/cast/tray_cast.h
@@ -27,6 +27,9 @@
   // CastConfigController::Observer:
   void OnDevicesUpdated(const std::vector<SinkAndRoute>& devices) override;
 
+  // views::View:
+  const char* GetClassName() const override;
+
  private:
   void CreateItems();
 
diff --git a/ash/system/ime_menu/ime_list_view.cc b/ash/system/ime_menu/ime_list_view.cc
index 5bf5267..d621640 100644
--- a/ash/system/ime_menu/ime_list_view.cc
+++ b/ash/system/ime_menu/ime_list_view.cc
@@ -306,8 +306,7 @@
     std::string ime_id = ime->second;
     last_selected_item_id_ = ime_id;
     ime_controller->SwitchImeById(ime_id, false /* show_message */);
-    UMA_HISTOGRAM_ENUMERATION("InputMethod.ImeSwitch", ImeSwitchType::kTray,
-                              ImeSwitchType::kCount);
+    UMA_HISTOGRAM_ENUMERATION("InputMethod.ImeSwitch", ImeSwitchType::kTray);
 
   } else {
     std::map<views::View*, std::string>::const_iterator property =
diff --git a/ash/system/locale/locale_detailed_view.cc b/ash/system/locale/locale_detailed_view.cc
index 72df6a8..dbc14de 100644
--- a/ash/system/locale/locale_detailed_view.cc
+++ b/ash/system/locale/locale_detailed_view.cc
@@ -91,6 +91,8 @@
     ScrollViewToVisible();
   }
 
+  const char* GetClassName() const override { return "LocaleItem"; }
+
   void GetAccessibleNodeData(ui::AXNodeData* node_data) override {
     ActionableView::GetAccessibleNodeData(node_data);
     node_data->role = ax::mojom::Role::kCheckBox;
@@ -148,5 +150,9 @@
   }
 }
 
+const char* LocaleDetailedView::GetClassName() const {
+  return "LocaleDetailedView";
+}
+
 }  // namespace tray
 }  // namespace ash
\ No newline at end of file
diff --git a/ash/system/locale/locale_detailed_view.h b/ash/system/locale/locale_detailed_view.h
index 49fb775..21c6bbf 100644
--- a/ash/system/locale/locale_detailed_view.h
+++ b/ash/system/locale/locale_detailed_view.h
@@ -23,6 +23,9 @@
   // TrayDetailedView:
   void HandleViewClicked(views::View* view) override;
 
+  // views::View:
+  const char* GetClassName() const override;
+
  private:
   void CreateItems();
 
diff --git a/ash/system/network/active_network_icon.cc b/ash/system/network/active_network_icon.cc
index 1c4a604..42fc426 100644
--- a/ash/system/network/active_network_icon.cc
+++ b/ash/system/network/active_network_icon.cc
@@ -86,16 +86,6 @@
                      base::Unretained(this), connector));
 }
 
-base::string16 ActiveNetworkIcon::GetDefaultLabel(
-    network_icon::IconType icon_type) {
-  if (!default_network_) {
-    if (cellular_uninitialized_msg_ != 0)
-      return l10n_util::GetStringUTF16(cellular_uninitialized_msg_);
-    return l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_NETWORK_NOT_CONNECTED);
-  }
-  return network_icon::GetLabelForNetwork(*default_network_, icon_type);
-}
-
 gfx::ImageSkia ActiveNetworkIcon::GetSingleImage(
     network_icon::IconType icon_type,
     bool* animating) {
diff --git a/ash/system/network/active_network_icon.h b/ash/system/network/active_network_icon.h
index c9e3429..70904b5 100644
--- a/ash/system/network/active_network_icon.h
+++ b/ash/system/network/active_network_icon.h
@@ -37,8 +37,8 @@
 //    a technology badge is used to represent the network.
 // ** Cellular (enabled devices only): The state of the Cellular connection if
 //    available regardless of whether it is the active network.
-// NOTE : GetSingleDefaultImage and GetDefaultLabel are partially tested in
-// network_icon_unittest.cc, and partially in active_network_icon_unittest.cc.
+// NOTE : GetSingleDefaultImage is partially tested in network_icon_unittest.cc,
+// and partially in active_network_icon_unittest.cc.
 // TODO(stevenjb): Move all test coverage to active_network_icon_unittest.cc and
 // test Dual icon methods.
 // This class is also responsible for periodically purging the icon cache.
@@ -48,9 +48,6 @@
   explicit ActiveNetworkIcon(service_manager::Connector* connector);
   ~ActiveNetworkIcon() override;
 
-  // Returns the label for the primary active network..
-  base::string16 GetDefaultLabel(network_icon::IconType icon_type);
-
   // Single image mode. Returns a network icon (which may be empty) and sets
   // |animating| if provided.
   gfx::ImageSkia GetSingleImage(network_icon::IconType icon_type,
diff --git a/ash/system/network/active_network_icon_unittest.cc b/ash/system/network/active_network_icon_unittest.cc
index 52cb2e5..a354f41 100644
--- a/ash/system/network/active_network_icon_unittest.cc
+++ b/ash/system/network/active_network_icon_unittest.cc
@@ -166,24 +166,6 @@
   DISALLOW_COPY_AND_ASSIGN(ActiveNetworkIconTest);
 };
 
-TEST_F(ActiveNetworkIconTest, GetDefaultLabel) {
-  SetupCellular(shill::kStateOnline);
-  base::string16 label = active_network_icon()->GetDefaultLabel(icon_type());
-  // Note: The guid is used for the name in ConfigureService.
-  EXPECT_EQ(l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_NETWORK_CONNECTED,
-                                       base::UTF8ToUTF16("cellular_guid")),
-            label);
-
-  SetupWiFi(shill::kStateIdle);
-  network_state_handler()->SetNetworkConnectRequested(wifi_path(), true);
-  base::RunLoop().RunUntilIdle();
-  label = active_network_icon()->GetDefaultLabel(icon_type());
-  // Note: The guid is used for the name in ConfigureService.
-  EXPECT_EQ(l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_NETWORK_CONNECTING,
-                                       base::UTF8ToUTF16("wifi_guid")),
-            label);
-}
-
 TEST_F(ActiveNetworkIconTest, GetSingleImage) {
   // Cellular only = Cellular icon
   SetupCellular(shill::kStateOnline);
@@ -234,11 +216,6 @@
 TEST_F(ActiveNetworkIconTest, CellularUninitialized) {
   SetCellularUninitialized(false /* scanning */);
 
-  base::string16 label = active_network_icon()->GetDefaultLabel(icon_type());
-  EXPECT_EQ(
-      l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_INITIALIZING_CELLULAR),
-      label);
-
   bool animating;
   gfx::ImageSkia image =
       active_network_icon()->GetSingleImage(icon_type(), &animating);
@@ -254,10 +231,6 @@
   ASSERT_TRUE(network_state_handler()->GetScanningByType(
       chromeos::NetworkTypePattern::Cellular()));
 
-  base::string16 label = active_network_icon()->GetDefaultLabel(icon_type());
-  EXPECT_EQ(l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_MOBILE_SCANNING),
-            label);
-
   bool animating;
   gfx::ImageSkia image =
       active_network_icon()->GetSingleImage(icon_type(), &animating);
diff --git a/ash/system/network/network_feature_pod_button.cc b/ash/system/network/network_feature_pod_button.cc
index 9cc0c12a..03bac40 100644
--- a/ash/system/network/network_feature_pod_button.cc
+++ b/ash/system/network/network_feature_pod_button.cc
@@ -187,6 +187,10 @@
   Update();
 }
 
+const char* NetworkFeaturePodButton::GetClassName() const {
+  return "NetworkFeaturePodButton";
+}
+
 void NetworkFeaturePodButton::Update() {
   bool animating = false;
   gfx::ImageSkia image =
diff --git a/ash/system/network/network_feature_pod_button.h b/ash/system/network/network_feature_pod_button.h
index 49acdcb..52a5325 100644
--- a/ash/system/network/network_feature_pod_button.h
+++ b/ash/system/network/network_feature_pod_button.h
@@ -26,6 +26,9 @@
   // TrayNetworkStateObserver::Observer:
   void ActiveNetworkStateChanged() override;
 
+  // views::Button:
+  const char* GetClassName() const override;
+
  private:
   void Update();
   void SetTooltipState(const base::string16& tooltip_state);
diff --git a/ash/system/network/network_icon.cc b/ash/system/network/network_icon.cc
index 4d4ac1c..564908e 100644
--- a/ash/system/network/network_icon.cc
+++ b/ash/system/network/network_icon.cc
@@ -581,42 +581,17 @@
   return CreateNetworkIconImage(icon, badges);
 }
 
-base::string16 GetLabelForNetwork(const NetworkIconState& network,
-                                  IconType icon_type) {
+base::string16 GetLabelForNetworkList(const NetworkIconState& network) {
   ActivationStateType activation_state = network.activation_state;
-  if (icon_type == ICON_TYPE_LIST || icon_type == ICON_TYPE_MENU_LIST) {
-    // Show "<network>: [Connecting|Activating]..."
-    if (icon_type != ICON_TYPE_MENU_LIST && IsConnecting(network)) {
-      return l10n_util::GetStringFUTF16(
-          IDS_ASH_STATUS_TRAY_NETWORK_LIST_CONNECTING,
-          base::UTF8ToUTF16(network.name));
-    }
-    if (activation_state == ActivationStateType::kActivating) {
-      return l10n_util::GetStringFUTF16(
-          IDS_ASH_STATUS_TRAY_NETWORK_LIST_ACTIVATING,
-          base::UTF8ToUTF16(network.name));
-    }
-    // Show "Activate <network>" in list view only.
-    if (activation_state == ActivationStateType::kNotActivated ||
-        activation_state == ActivationStateType::kPartiallyActivated) {
-      return l10n_util::GetStringFUTF16(
-          IDS_ASH_STATUS_TRAY_NETWORK_LIST_ACTIVATE,
-          base::UTF8ToUTF16(network.name));
-    }
-  } else {
-    // Show "[Connected to|Connecting to|Activating] <network>" (non-list view).
-    if (IsConnected(network)) {
-      return l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_NETWORK_CONNECTED,
-                                        base::UTF8ToUTF16(network.name));
-    }
-    if (IsConnecting(network)) {
-      return l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_NETWORK_CONNECTING,
-                                        base::UTF8ToUTF16(network.name));
-    }
-    if (activation_state == ActivationStateType::kActivating) {
-      return l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_NETWORK_ACTIVATING,
-                                        base::UTF8ToUTF16(network.name));
-    }
+  if (activation_state == ActivationStateType::kActivating) {
+    return l10n_util::GetStringFUTF16(
+        IDS_ASH_STATUS_TRAY_NETWORK_LIST_ACTIVATING,
+        base::UTF8ToUTF16(network.name));
+  }
+  if (activation_state == ActivationStateType::kNotActivated ||
+      activation_state == ActivationStateType::kPartiallyActivated) {
+    return l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_NETWORK_LIST_ACTIVATE,
+                                      base::UTF8ToUTF16(network.name));
   }
 
   // Otherwise just show the network name or 'Ethernet'.
diff --git a/ash/system/network/network_icon.h b/ash/system/network/network_icon.h
index 4ff01b3..caca2ed 100644
--- a/ash/system/network/network_icon.h
+++ b/ash/system/network/network_icon.h
@@ -107,10 +107,9 @@
 ASH_EXPORT gfx::ImageSkia GetImageForNewWifiNetwork(SkColor icon_color,
                                                     SkColor badge_color);
 
-// Returns the label for |network| based on |icon_type|. |network| cannot be
-// nullptr.
-ASH_EXPORT base::string16 GetLabelForNetwork(const NetworkIconState&,
-                                             IconType icon_type);
+// Returns the label for |network| when displayed in a list.
+ASH_EXPORT base::string16 GetLabelForNetworkList(
+    const NetworkIconState& network);
 
 // Called periodically with the current list of network guids. Removes cached
 // entries that are no longer in the list.
diff --git a/ash/system/network/network_icon_unittest.cc b/ash/system/network/network_icon_unittest.cc
index 01f69d6c..c57f397 100644
--- a/ash/system/network/network_icon_unittest.cc
+++ b/ash/system/network/network_icon_unittest.cc
@@ -99,12 +99,8 @@
     return GetImageForNonVirtualNetwork(network, false /* show_vpn_badge */);
   }
 
-  void GetDefaultNetworkImageAndLabel(IconType icon_type,
-                                      gfx::ImageSkia* image,
-                                      base::string16* label,
-                                      bool* animating) {
-    *image = active_network_icon_->GetSingleImage(icon_type, animating);
-    *label = active_network_icon_->GetDefaultLabel(icon_type);
+  gfx::ImageSkia GetDefaultNetworkImage(IconType icon_type, bool* animating) {
+    return active_network_icon_->GetSingleImage(icon_type, animating);
   }
 
   // The icon for a Tether network should be the same as one for a cellular
@@ -236,18 +232,15 @@
   EXPECT_EQ(SignalStrength::STRONG, GetSignalStrength(100));
 }
 
-TEST_F(NetworkIconTest, DefaultImageAndLabelWifiConnected) {
+TEST_F(NetworkIconTest, DefaultImageWifiConnected) {
   // Set the Wifi service as connected.
   SetServiceProperty(wifi1_path(), shill::kSignalStrengthProperty,
                      base::Value(45));
   SetServiceProperty(wifi1_path(), shill::kStateProperty,
                      base::Value(shill::kStateOnline));
 
-  gfx::ImageSkia default_image;
-  base::string16 label;
   bool animating = false;
-  GetDefaultNetworkImageAndLabel(icon_type_, &default_image, &label,
-                                 &animating);
+  gfx::ImageSkia default_image = GetDefaultNetworkImage(icon_type_, &animating);
   ASSERT_FALSE(default_image.isNull());
   EXPECT_FALSE(animating);
 
@@ -258,18 +251,15 @@
       gfx::Image(default_image), ImageForNetwork(reference_network.get())));
 }
 
-TEST_F(NetworkIconTest, DefaultImageAndLabelWifiConnecting) {
+TEST_F(NetworkIconTest, DefaultImageWifiConnecting) {
   // Set the Wifi service as connected.
   SetServiceProperty(wifi1_path(), shill::kSignalStrengthProperty,
                      base::Value(45));
   SetServiceProperty(wifi1_path(), shill::kStateProperty,
                      base::Value(shill::kStateAssociation));
 
-  gfx::ImageSkia default_image;
-  base::string16 label;
   bool animating = false;
-  GetDefaultNetworkImageAndLabel(icon_type_, &default_image, &label,
-                                 &animating);
+  gfx::ImageSkia default_image = GetDefaultNetworkImage(icon_type_, &animating);
   ASSERT_FALSE(default_image.isNull());
   EXPECT_TRUE(animating);
 
@@ -286,7 +276,7 @@
 // connected, but that is not always the case. For example, if the connected
 // wifi service has no Internet connectivity, cellular service will be selected
 // as default.
-TEST_F(NetworkIconTest, DefaultImageAndLabelCellularDefaultWithWifiConnected) {
+TEST_F(NetworkIconTest, DefaultImageCellularDefaultWithWifiConnected) {
   // Set both wifi and cellular networks in a connected state, but with wifi not
   // online - this should prompt fake shill manager implementation to prefer
   // cellular network over wifi.
@@ -300,11 +290,8 @@
   SetServiceProperty(cellular_path(), shill::kStateProperty,
                      base::Value(shill::kStateOnline));
 
-  gfx::ImageSkia default_image;
-  base::string16 label;
   bool animating = false;
-  GetDefaultNetworkImageAndLabel(icon_type_, &default_image, &label,
-                                 &animating);
+  gfx::ImageSkia default_image = GetDefaultNetworkImage(icon_type_, &animating);
   ASSERT_FALSE(default_image.isNull());
   EXPECT_FALSE(animating);
 
@@ -334,14 +321,11 @@
   SetServiceProperty(wifi1_path(), shill::kStateProperty,
                      base::Value(shill::kStateAssociation));
 
-  gfx::ImageSkia default_image;
-  base::string16 label;
-  bool animating = false;
   // Verify that the default network is connecting icon for the initial default
   // network (even though the default network as reported by shill actually
   // changed).
-  GetDefaultNetworkImageAndLabel(icon_type_, &default_image, &label,
-                                 &animating);
+  bool animating = false;
+  gfx::ImageSkia default_image = GetDefaultNetworkImage(icon_type_, &animating);
   ASSERT_FALSE(default_image.isNull());
   EXPECT_TRUE(animating);
 
@@ -364,8 +348,7 @@
   NetworkStatePropertiesPtr reference_network_2 =
       CreateStandaloneNetworkProperties("reference2", NetworkType::kCellular,
                                         ConnectionStateType::kOnline, 65);
-  GetDefaultNetworkImageAndLabel(icon_type_, &default_image, &label,
-                                 &animating);
+  default_image = GetDefaultNetworkImage(icon_type_, &animating);
   ASSERT_FALSE(default_image.isNull());
   EXPECT_FALSE(animating);
 
@@ -380,8 +363,7 @@
   NetworkStatePropertiesPtr reference_network_3 =
       CreateStandaloneNetworkProperties("reference3", NetworkType::kWiFi,
                                         ConnectionStateType::kOnline, 45);
-  GetDefaultNetworkImageAndLabel(icon_type_, &default_image, &label,
-                                 &animating);
+  default_image = GetDefaultNetworkImage(icon_type_, &animating);
   ASSERT_FALSE(default_image.isNull());
   EXPECT_FALSE(animating);
 
@@ -405,11 +387,8 @@
   SetServiceProperty(wifi1_path(), shill::kStateProperty,
                      base::Value(shill::kStateIdle));
 
-  gfx::ImageSkia default_image;
-  base::string16 label;
   bool animating = false;
-  GetDefaultNetworkImageAndLabel(icon_type_, &default_image, &label,
-                                 &animating);
+  gfx::ImageSkia default_image = GetDefaultNetworkImage(icon_type_, &animating);
   ASSERT_FALSE(default_image.isNull());
   EXPECT_FALSE(animating);
 
@@ -438,15 +417,12 @@
   SetServiceProperty(cellular_path(), shill::kStateProperty,
                      base::Value(shill::kStateAssociation));
 
-  gfx::ImageSkia default_image;
-  base::string16 label;
-  bool animating = false;
   // Currently, a connecting icon is used as default network icon even if
   // another network connected and used as default.
   // TODO(tbarzic): Consider changing network icon logic to use a connected
   //     network icon if a network is connected while a network is reconnecting.
-  GetDefaultNetworkImageAndLabel(icon_type_, &default_image, &label,
-                                 &animating);
+  bool animating = false;
+  gfx::ImageSkia default_image = GetDefaultNetworkImage(icon_type_, &animating);
   ASSERT_FALSE(default_image.isNull());
   EXPECT_TRUE(animating);
 
@@ -461,8 +437,7 @@
   SetServiceProperty(cellular_path(), shill::kStateProperty,
                      base::Value(shill::kStateReady));
 
-  GetDefaultNetworkImageAndLabel(icon_type_, &default_image, &label,
-                                 &animating);
+  default_image = GetDefaultNetworkImage(icon_type_, &animating);
   ASSERT_FALSE(default_image.isNull());
   EXPECT_FALSE(animating);
   NetworkStatePropertiesPtr reference_network_2 =
@@ -476,8 +451,7 @@
   SetServiceProperty(cellular_path(), shill::kStateProperty,
                      base::Value(shill::kStateOnline));
 
-  GetDefaultNetworkImageAndLabel(icon_type_, &default_image, &label,
-                                 &animating);
+  default_image = GetDefaultNetworkImage(icon_type_, &animating);
   ASSERT_FALSE(default_image.isNull());
   EXPECT_FALSE(animating);
   EXPECT_TRUE(gfx::test::AreImagesEqual(
@@ -498,11 +472,8 @@
   SetServiceProperty(cellular_path(), shill::kStateProperty,
                      base::Value(shill::kStateOnline));
 
-  gfx::ImageSkia default_image;
-  base::string16 label;
   bool animating = false;
-  GetDefaultNetworkImageAndLabel(icon_type_, &default_image, &label,
-                                 &animating);
+  gfx::ImageSkia default_image = GetDefaultNetworkImage(icon_type_, &animating);
   ASSERT_FALSE(default_image.isNull());
   EXPECT_FALSE(animating);
 
@@ -521,11 +492,8 @@
   SetServiceProperty(cellular_path(), shill::kActivationStateProperty,
                      base::Value(shill::kActivationStateActivating));
 
-  gfx::ImageSkia default_image;
-  base::string16 label;
   bool animating = false;
-  GetDefaultNetworkImageAndLabel(icon_type_, &default_image, &label,
-                                 &animating);
+  gfx::ImageSkia default_image = GetDefaultNetworkImage(icon_type_, &animating);
   ASSERT_FALSE(default_image.isNull());
   EXPECT_FALSE(animating);
 
@@ -550,11 +518,8 @@
   SetServiceProperty(cellular_path(), shill::kActivationStateProperty,
                      base::Value(shill::kActivationStateActivating));
 
-  gfx::ImageSkia default_image;
-  base::string16 label;
   bool animating = false;
-  GetDefaultNetworkImageAndLabel(icon_type_, &default_image, &label,
-                                 &animating);
+  gfx::ImageSkia default_image = GetDefaultNetworkImage(icon_type_, &animating);
   ASSERT_FALSE(default_image.isNull());
   EXPECT_FALSE(animating);
 
@@ -567,10 +532,6 @@
 
 // Tests VPN badging for the default network.
 TEST_F(NetworkIconTest, DefaultNetworkVpnBadge) {
-  gfx::ImageSkia default_image;
-  base::string16 label;
-  bool animating = false;
-
   // Set up initial state with Ethernet and WiFi connected.
   std::string ethernet_path = ConfigureService(
       R"({"GUID": "ethernet_guid", "Type": "ethernet", "State": "online"})");
@@ -582,8 +543,8 @@
                      base::Value(45));
 
   // With Ethernet and WiFi connected, the default icon should be empty.
-  GetDefaultNetworkImageAndLabel(icon_type_, &default_image, &label,
-                                 &animating);
+  bool animating = false;
+  gfx::ImageSkia default_image = GetDefaultNetworkImage(icon_type_, &animating);
   ASSERT_TRUE(default_image.isNull());
   EXPECT_FALSE(animating);
 
@@ -593,8 +554,7 @@
   ASSERT_FALSE(vpn_path.empty());
 
   // When a VPN is connected, the default icon should be Ethernet with a badge.
-  GetDefaultNetworkImageAndLabel(icon_type_, &default_image, &label,
-                                 &animating);
+  default_image = GetDefaultNetworkImage(icon_type_, &animating);
   ASSERT_FALSE(default_image.isNull());
   EXPECT_FALSE(animating);
 
@@ -613,8 +573,7 @@
   // Disconnect Ethernet. The default icon should become WiFi with a badge.
   SetServiceProperty(ethernet_path, shill::kStateProperty,
                      base::Value(shill::kStateIdle));
-  GetDefaultNetworkImageAndLabel(icon_type_, &default_image, &label,
-                                 &animating);
+  default_image = GetDefaultNetworkImage(icon_type_, &animating);
   ASSERT_FALSE(default_image.isNull());
   EXPECT_FALSE(animating);
 
@@ -628,8 +587,7 @@
   // Set the VPN to connecting; the default icon should be animating.
   SetServiceProperty(vpn_path, shill::kStateProperty,
                      base::Value(shill::kStateAssociation));
-  GetDefaultNetworkImageAndLabel(icon_type_, &default_image, &label,
-                                 &animating);
+  default_image = GetDefaultNetworkImage(icon_type_, &animating);
   ASSERT_FALSE(default_image.isNull());
   EXPECT_TRUE(animating);
 }
@@ -645,11 +603,8 @@
       R"({"GUID": "vpn_guid", "Type": "vpn", "State": "online"})");
   ASSERT_FALSE(vpn_path.empty());
 
-  gfx::ImageSkia default_image;
-  base::string16 label;
   bool animating = false;
-  GetDefaultNetworkImageAndLabel(icon_type_, &default_image, &label,
-                                 &animating);
+  gfx::ImageSkia default_image = GetDefaultNetworkImage(icon_type_, &animating);
   ASSERT_FALSE(default_image.isNull());
   EXPECT_TRUE(animating);
 
@@ -672,11 +627,8 @@
       R"({"GUID": "vpn_guid", "Type": "vpn", "State": "online"})");
   ASSERT_FALSE(vpn_path.empty());
 
-  gfx::ImageSkia default_image;
-  base::string16 label;
   bool animating = false;
-  GetDefaultNetworkImageAndLabel(icon_type_, &default_image, &label,
-                                 &animating);
+  gfx::ImageSkia default_image = GetDefaultNetworkImage(icon_type_, &animating);
   ASSERT_FALSE(default_image.isNull());
   EXPECT_TRUE(animating);
 
diff --git a/ash/system/network/network_list.cc b/ash/system/network/network_list.cc
index a523665..0c520d2 100644
--- a/ash/system/network/network_list.cc
+++ b/ash/system/network/network_list.cc
@@ -98,6 +98,10 @@
   return true;
 }
 
+const char* NetworkListView::GetClassName() const {
+  return "NetworkListView";
+}
+
 void NetworkListView::BindCrosNetworkConfig() {
   // Ensure binding is reset in case this is called after a failure.
   cros_network_config_ptr_.reset();
@@ -160,8 +164,7 @@
     auto info = std::make_unique<NetworkInfo>(network->guid);
 
     network_icon::NetworkIconState network_icon_state(network.get());
-    info->label = network_icon::GetLabelForNetwork(
-        network_icon_state, network_icon::ICON_TYPE_MENU_LIST);
+    info->label = network_icon::GetLabelForNetworkList(network_icon_state);
     // |network_list_| only contains non virtual networks.
     info->image = network_icon::GetImageForNonVirtualNetwork(
         network_icon_state, network_icon::ICON_TYPE_LIST,
diff --git a/ash/system/network/network_list.h b/ash/system/network/network_list.h
index da69afd..3943745 100644
--- a/ash/system/network/network_list.h
+++ b/ash/system/network/network_list.h
@@ -45,6 +45,9 @@
   void UpdateNetworkList() override;
   bool IsNetworkEntry(views::View* view, std::string* guid) const override;
 
+  // views::View:
+  const char* GetClassName() const override;
+
  private:
   void BindCrosNetworkConfig();
   void OnGetDeviceStateList(
diff --git a/ash/system/network/network_row_title_view.cc b/ash/system/network/network_row_title_view.cc
index caf5089..4a8d6f8 100644
--- a/ash/system/network/network_row_title_view.cc
+++ b/ash/system/network/network_row_title_view.cc
@@ -33,4 +33,9 @@
 }
 
 NetworkRowTitleView::~NetworkRowTitleView() = default;
+
+const char* NetworkRowTitleView::GetClassName() const {
+  return "NetworkRowTitleView";
+}
+
 }  // namespace ash
diff --git a/ash/system/network/network_row_title_view.h b/ash/system/network/network_row_title_view.h
index 21e6457..7ce2480 100644
--- a/ash/system/network/network_row_title_view.h
+++ b/ash/system/network/network_row_title_view.h
@@ -17,6 +17,9 @@
   explicit NetworkRowTitleView(int title_message_id);
   ~NetworkRowTitleView() override;
 
+  // views::View:
+  const char* GetClassName() const override;
+
  private:
   views::Label* const title_;
 
diff --git a/ash/system/network/network_section_header_view.cc b/ash/system/network/network_section_header_view.cc
index a4ffc7f..9acddbf 100644
--- a/ash/system/network/network_section_header_view.cc
+++ b/ash/system/network/network_section_header_view.cc
@@ -86,6 +86,10 @@
   toggle_->AnimateIsOn(is_on);
 }
 
+const char* NetworkSectionHeaderView::GetClassName() const {
+  return "NetworkSectionHeaderView";
+}
+
 int NetworkSectionHeaderView::GetHeightForWidth(int width) const {
   // Make row height fixed avoiding layout manager adjustments.
   return GetPreferredSize().height();
diff --git a/ash/system/network/network_section_header_view.h b/ash/system/network/network_section_header_view.h
index a385003..36367db 100644
--- a/ash/system/network/network_section_header_view.h
+++ b/ash/system/network/network_section_header_view.h
@@ -33,6 +33,9 @@
   // Modify enabled/disabled and on/off state of toggle.
   virtual void SetToggleState(bool toggle_enabled, bool is_on);
 
+  // views::View:
+  const char* GetClassName() const override;
+
  protected:
   void Init(bool enabled);
 
diff --git a/ash/system/network/network_state_list_detailed_view.cc b/ash/system/network/network_state_list_detailed_view.cc
index ec69d3b..348fab7 100644
--- a/ash/system/network/network_state_list_detailed_view.cc
+++ b/ash/system/network/network_state_list_detailed_view.cc
@@ -203,6 +203,10 @@
   ToggleInfoBubble();
 }
 
+const char* NetworkStateListDetailedView::GetClassName() const {
+  return "NetworkStateListDetailedView";
+}
+
 void NetworkStateListDetailedView::Init() {
   CreateScrollableList();
   CreateTitleRow(list_type_ == ListType::LIST_TYPE_NETWORK
diff --git a/ash/system/network/network_state_list_detailed_view.h b/ash/system/network/network_state_list_detailed_view.h
index 961683d..aee47d2 100644
--- a/ash/system/network/network_state_list_detailed_view.h
+++ b/ash/system/network/network_state_list_detailed_view.h
@@ -36,6 +36,9 @@
 
   void ToggleInfoBubbleForTesting();
 
+  // views::View:
+  const char* GetClassName() const override;
+
  protected:
   enum ListType { LIST_TYPE_NETWORK, LIST_TYPE_VPN };
 
diff --git a/ash/system/network/vpn_list_view.cc b/ash/system/network/vpn_list_view.cc
index f6ec4b4..bb6a0fb 100644
--- a/ash/system/network/vpn_list_view.cc
+++ b/ash/system/network/vpn_list_view.cc
@@ -130,6 +130,9 @@
     tri_view->AddView(TriView::Container::END, add_vpn_button);
   }
 
+  // views::View:
+  const char* GetClassName() const override { return "VPNListProviderEntry"; }
+
  protected:
   // views::ButtonListener:
   void ButtonPressed(views::Button* sender, const ui::Event& event) override {
@@ -175,6 +178,9 @@
   // views::ButtonListener:
   void ButtonPressed(Button* sender, const ui::Event& event) override;
 
+  // views::View:
+  const char* GetClassName() const override { return "VPNListNetworkEntry"; }
+
  private:
   void OnGetNetworkState(NetworkStatePropertiesPtr result);
   void UpdateFromNetworkState(const NetworkStateProperties* network);
@@ -240,8 +246,7 @@
   network_icon::NetworkIconState vpn_icon_state(vpn);
   gfx::ImageSkia image = network_icon::GetImageForVPN(
       vpn_icon_state, network_icon::ICON_TYPE_LIST);
-  base::string16 label = network_icon::GetLabelForNetwork(
-      vpn_icon_state, network_icon::ICON_TYPE_MENU_LIST);
+  base::string16 label = network_icon::GetLabelForNetworkList(vpn_icon_state);
   AddIconAndLabel(image, label);
   if (chromeos::network_config::StateIsConnected(vpn->connection_state)) {
     owner_->SetupConnectedScrollListItem(this);
@@ -363,6 +368,10 @@
                                 PrefRegistry::PUBLIC);
 }
 
+const char* VPNListView::GetClassName() const {
+  return "VPNListView";
+}
+
 void VPNListView::BindCrosNetworkConfig() {
   // Ensure binding is reset in case this is called after a failure.
   cros_network_config_ptr_.reset();
diff --git a/ash/system/network/vpn_list_view.h b/ash/system/network/vpn_list_view.h
index 4ff980da..55bee4b 100644
--- a/ash/system/network/vpn_list_view.h
+++ b/ash/system/network/vpn_list_view.h
@@ -59,6 +59,9 @@
   // See Shell::RegisterProfilePrefs().
   static void RegisterProfilePrefs(PrefRegistrySimple* registry);
 
+  // views::View:
+  const char* GetClassName() const override;
+
  private:
   using NetworkStateList =
       std::vector<chromeos::network_config::mojom::NetworkStatePropertiesPtr>;
diff --git a/ash/system/night_light/night_light_toggle_button.cc b/ash/system/night_light/night_light_toggle_button.cc
index 3bdcdcb..cc3974a0 100644
--- a/ash/system/night_light/night_light_toggle_button.cc
+++ b/ash/system/night_light/night_light_toggle_button.cc
@@ -60,6 +60,10 @@
   NotifyAccessibilityEvent(ax::mojom::Event::kAriaAttributeChanged, true);
 }
 
+const char* NightLightToggleButton::GetClassName() const {
+  return "NightLightToggleButton";
+}
+
 void NightLightToggleButton::Update() {
   const bool night_light_enabled =
       Shell::Get()->night_light_controller()->GetEnabled();
diff --git a/ash/system/night_light/night_light_toggle_button.h b/ash/system/night_light/night_light_toggle_button.h
index 5f9bdcd..696e40c 100644
--- a/ash/system/night_light/night_light_toggle_button.h
+++ b/ash/system/night_light/night_light_toggle_button.h
@@ -19,6 +19,9 @@
   // Toggles the status of NightLight.
   void Toggle();
 
+  // views::View:
+  const char* GetClassName() const override;
+
  private:
   // Updates the icon and its style based on the status of NightLight.
   void Update();
diff --git a/ash/system/overview/overview_button_tray.cc b/ash/system/overview/overview_button_tray.cc
index 134c511..639bed0 100644
--- a/ash/system/overview/overview_button_tray.cc
+++ b/ash/system/overview/overview_button_tray.cc
@@ -168,6 +168,10 @@
   // This class has no bubbles to hide.
 }
 
+const char* OverviewButtonTray::GetClassName() const {
+  return "OverviewButtonTray";
+}
+
 void OverviewButtonTray::UpdateIconVisibility() {
   // The visibility of the OverviewButtonTray has diverged from
   // OverviewController::CanSelect. The visibility of the button should
diff --git a/ash/system/overview/overview_button_tray.h b/ash/system/overview/overview_button_tray.h
index bf6419e..7669961 100644
--- a/ash/system/overview/overview_button_tray.h
+++ b/ash/system/overview/overview_button_tray.h
@@ -69,6 +69,9 @@
   base::string16 GetAccessibleNameForTray() override;
   void HideBubbleWithView(const TrayBubbleView* bubble_view) override;
 
+  // views::View:
+  const char* GetClassName() const override;
+
  private:
   friend class OverviewButtonTrayTest;
 
diff --git a/ash/system/power/power_button_menu_item_view.cc b/ash/system/power/power_button_menu_item_view.cc
index a514363..3c94426 100644
--- a/ash/system/power/power_button_menu_item_view.cc
+++ b/ash/system/power/power_button_menu_item_view.cc
@@ -72,6 +72,10 @@
 
 PowerButtonMenuItemView::~PowerButtonMenuItemView() = default;
 
+const char* PowerButtonMenuItemView::GetClassName() const {
+  return "PowerButtonMenuItemView";
+}
+
 void PowerButtonMenuItemView::Layout() {
   const gfx::Rect rect(GetContentsBounds());
 
diff --git a/ash/system/power/power_button_menu_item_view.h b/ash/system/power/power_button_menu_item_view.h
index 22e9277..4464bd7 100644
--- a/ash/system/power/power_button_menu_item_view.h
+++ b/ash/system/power/power_button_menu_item_view.h
@@ -38,6 +38,9 @@
                           const base::string16& title_text);
   ~PowerButtonMenuItemView() override;
 
+  // views::View:
+  const char* GetClassName() const override;
+
  private:
   // views::View:
   void Layout() override;
diff --git a/ash/system/power/power_button_menu_screen_view.cc b/ash/system/power/power_button_menu_screen_view.cc
index 38354c1..0d7cd8c 100644
--- a/ash/system/power/power_button_menu_screen_view.cc
+++ b/ash/system/power/power_button_menu_screen_view.cc
@@ -96,6 +96,11 @@
     layer()->SetOpacity(show ? kPowerButtonMenuOpacity : 0.f);
   }
 
+  // views::View:
+  const char* GetClassName() const override {
+    return "PowerButtonMenuScreenView";
+  }
+
  private:
   // A callback for when the animation that shows the power menu has finished.
   base::RepeatingClosure show_animation_done_;
@@ -132,6 +137,10 @@
   power_button_menu_view_->ScheduleShowHideAnimation(show);
 }
 
+const char* PowerButtonMenuScreenView::GetClassName() const {
+  return "PowerButtonMenuScreenView";
+}
+
 void PowerButtonMenuScreenView::Layout() {
   power_button_screen_background_shield_->SetBoundsRect(GetContentsBounds());
 
diff --git a/ash/system/power/power_button_menu_screen_view.h b/ash/system/power/power_button_menu_screen_view.h
index 9f1a872..0c2fe78 100644
--- a/ash/system/power/power_button_menu_screen_view.h
+++ b/ash/system/power/power_button_menu_screen_view.h
@@ -38,6 +38,9 @@
   // Schedules an animation to show or hide the view.
   void ScheduleShowHideAnimation(bool show);
 
+  // views::View:
+  const char* GetClassName() const override;
+
  private:
   class PowerButtonMenuBackgroundView;
 
diff --git a/ash/system/power/power_button_menu_view.cc b/ash/system/power/power_button_menu_view.cc
index 246f530..683ef05 100644
--- a/ash/system/power/power_button_menu_view.cc
+++ b/ash/system/power/power_button_menu_view.cc
@@ -137,6 +137,10 @@
   return transform_displacement;
 }
 
+const char* PowerButtonMenuView::GetClassName() const {
+  return "PowerButtonMenuView";
+}
+
 void PowerButtonMenuView::CreateItems() {
   power_off_item_ = new PowerButtonMenuItemView(
       this, kSystemPowerButtonMenuPowerOffIcon,
diff --git a/ash/system/power/power_button_menu_view.h b/ash/system/power/power_button_menu_view.h
index 8e0a645..5c94e3b 100644
--- a/ash/system/power/power_button_menu_view.h
+++ b/ash/system/power/power_button_menu_view.h
@@ -70,6 +70,9 @@
   // Gets the transform displacement, which contains direction and distance.
   TransformDisplacement GetTransformDisplacement() const;
 
+  // views::View:
+  const char* GetClassName() const override;
+
  private:
   // Creates the items that in the menu.
   void CreateItems();
diff --git a/ash/system/power/power_status_view.cc b/ash/system/power/power_status_view.cc
index f579a54..98dc68b 100644
--- a/ash/system/power/power_status_view.cc
+++ b/ash/system/power/power_status_view.cc
@@ -94,4 +94,8 @@
   node_data->SetName(accessible_name_);
 }
 
+const char* PowerStatusView::GetClassName() const {
+  return "PowerStatusView";
+}
+
 }  // namespace ash
diff --git a/ash/system/power/power_status_view.h b/ash/system/power/power_status_view.h
index e7ee96c..c603576 100644
--- a/ash/system/power/power_status_view.h
+++ b/ash/system/power/power_status_view.h
@@ -26,6 +26,7 @@
   // views::View:
   void Layout() override;
   void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
+  const char* GetClassName() const override;
 
   // PowerStatus::Observer:
   void OnPowerStatusChanged() override;
diff --git a/ash/system/power/tray_power.cc b/ash/system/power/tray_power.cc
index ce909be..ed90bf1 100644
--- a/ash/system/power/tray_power.cc
+++ b/ash/system/power/tray_power.cc
@@ -73,6 +73,10 @@
   return tooltip_;
 }
 
+const char* PowerTrayView::GetClassName() const {
+  return "PowerTrayView";
+}
+
 void PowerTrayView::OnPowerStatusChanged() {
   UpdateStatus();
 }
diff --git a/ash/system/power/tray_power.h b/ash/system/power/tray_power.h
index 2b199652..dd614e6 100644
--- a/ash/system/power/tray_power.h
+++ b/ash/system/power/tray_power.h
@@ -29,6 +29,7 @@
   void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
   views::View* GetTooltipHandlerForPoint(const gfx::Point& point) override;
   base::string16 GetTooltipText(const gfx::Point& p) const override;
+  const char* GetClassName() const override;
 
   // PowerStatus::Observer:
   void OnPowerStatusChanged() override;
diff --git a/ash/system/session/logout_button_tray.cc b/ash/system/session/logout_button_tray.cc
index 291dc3a..25faeea 100644
--- a/ash/system/session/logout_button_tray.cc
+++ b/ash/system/session/logout_button_tray.cc
@@ -108,6 +108,10 @@
   node_data->SetName(button_->GetText());
 }
 
+const char* LogoutButtonTray::GetClassName() const {
+  return "LogoutButtonTray";
+}
+
 void LogoutButtonTray::UpdateShowLogoutButtonInTray() {
   show_logout_button_in_tray_ = pref_change_registrar_->prefs()->GetBoolean(
       prefs::kShowLogoutButtonInTray);
diff --git a/ash/system/session/logout_button_tray.h b/ash/system/session/logout_button_tray.h
index 7601a50..be52a41 100644
--- a/ash/system/session/logout_button_tray.h
+++ b/ash/system/session/logout_button_tray.h
@@ -41,6 +41,7 @@
 
   // views::View:
   void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
+  const char* GetClassName() const override;
 
   // views::ButtonListener:
   void ButtonPressed(views::Button* sender, const ui::Event& event) override;
diff --git a/ash/system/session/logout_confirmation_dialog.cc b/ash/system/session/logout_confirmation_dialog.cc
index 643025a..e1a5e87 100644
--- a/ash/system/session/logout_confirmation_dialog.cc
+++ b/ash/system/session/logout_confirmation_dialog.cc
@@ -112,6 +112,10 @@
       GetLayoutManager()->GetPreferredHeightForWidth(this, kDefaultWidth));
 }
 
+const char* LogoutConfirmationDialog::GetClassName() const {
+  return "LogoutConfirmationDialog";
+}
+
 void LogoutConfirmationDialog::UpdateLabel() {
   const base::TimeDelta time_remaining =
       logout_time_ - controller_->clock()->NowTicks();
diff --git a/ash/system/session/logout_confirmation_dialog.h b/ash/system/session/logout_confirmation_dialog.h
index 74e9832..c395f3f 100644
--- a/ash/system/session/logout_confirmation_dialog.h
+++ b/ash/system/session/logout_confirmation_dialog.h
@@ -44,6 +44,7 @@
 
   // views::View:
   gfx::Size CalculatePreferredSize() const override;
+  const char* GetClassName() const override;
 
  private:
   void UpdateLabel();
diff --git a/ash/system/time/time_tray_item_view.cc b/ash/system/time/time_tray_item_view.cc
index 6ec8e4c..2480063 100644
--- a/ash/system/time/time_tray_item_view.cc
+++ b/ash/system/time/time_tray_item_view.cc
@@ -40,5 +40,9 @@
   time_view_->SetTextColorBasedOnSession(state);
 }
 
+const char* TimeTrayItemView::GetClassName() const {
+  return "TimeTrayItemView";
+}
+
 }  // namespace tray
 }  // namespace ash
diff --git a/ash/system/time/time_tray_item_view.h b/ash/system/time/time_tray_item_view.h
index 4e203ab..53a1b9e 100644
--- a/ash/system/time/time_tray_item_view.h
+++ b/ash/system/time/time_tray_item_view.h
@@ -28,6 +28,9 @@
   // SessionObserver:
   void OnSessionStateChanged(session_manager::SessionState state) override;
 
+  // views::View:
+  const char* GetClassName() const override;
+
  private:
   TimeView* time_view_ = nullptr;
   ScopedSessionObserver session_observer_;
diff --git a/ash/system/time/time_view.cc b/ash/system/time/time_view.cc
index cbc5196..767a0b3 100644
--- a/ash/system/time/time_view.cc
+++ b/ash/system/time/time_view.cc
@@ -126,6 +126,10 @@
   return model_->hour_clock_type();
 }
 
+const char* TimeView::GetClassName() const {
+  return "TimeView";
+}
+
 bool TimeView::PerformAction(const ui::Event& event) {
   return false;
 }
diff --git a/ash/system/time/time_view.h b/ash/system/time/time_view.h
index 01ccad2..c211b7b 100644
--- a/ash/system/time/time_view.h
+++ b/ash/system/time/time_view.h
@@ -56,6 +56,9 @@
 
   base::HourClockType GetHourTypeForTesting() const;
 
+  // views::View:
+  const char* GetClassName() const override;
+
  private:
   friend class TimeViewTest;
 
diff --git a/ash/system/tray/hover_highlight_view.cc b/ash/system/tray/hover_highlight_view.cc
index a083c9e..a25f0f1 100644
--- a/ash/system/tray/hover_highlight_view.cc
+++ b/ash/system/tray/hover_highlight_view.cc
@@ -229,6 +229,10 @@
   node_data->SetCheckedState(checked_state);
 }
 
+const char* HoverHighlightView::GetClassName() const {
+  return "HoverHighlightView";
+}
+
 gfx::Size HoverHighlightView::CalculatePreferredSize() const {
   gfx::Size size = ActionableView::CalculatePreferredSize();
 
diff --git a/ash/system/tray/hover_highlight_view.h b/ash/system/tray/hover_highlight_view.h
index d24a032..1269161 100644
--- a/ash/system/tray/hover_highlight_view.h
+++ b/ash/system/tray/hover_highlight_view.h
@@ -105,6 +105,7 @@
 
   // views::View:
   void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
+  const char* GetClassName() const override;
 
   TriView* tri_view() { return tri_view_; }
 
diff --git a/ash/system/tray/label_tray_view.cc b/ash/system/tray/label_tray_view.cc
index f944891..bdadf25 100644
--- a/ash/system/tray/label_tray_view.cc
+++ b/ash/system/tray/label_tray_view.cc
@@ -39,6 +39,10 @@
   }
 }
 
+const char* LabelTrayView::GetClassName() const {
+  return "LabelTrayView";
+}
+
 views::View* LabelTrayView::CreateChildView(
     const base::string16& message) const {
   HoverHighlightView* child = new HoverHighlightView(click_listener_);
diff --git a/ash/system/tray/label_tray_view.h b/ash/system/tray/label_tray_view.h
index cb8346e..a97f834f 100644
--- a/ash/system/tray/label_tray_view.h
+++ b/ash/system/tray/label_tray_view.h
@@ -29,6 +29,9 @@
 
   void SetMessage(const base::string16& message);
 
+  // views::View:
+  const char* GetClassName() const override;
+
  private:
   views::View* CreateChildView(const base::string16& message) const;
 
diff --git a/ash/system/tray/system_menu_button.cc b/ash/system/tray/system_menu_button.cc
index eb86c61..0bc09dc 100644
--- a/ash/system/tray/system_menu_button.cc
+++ b/ash/system/tray/system_menu_button.cc
@@ -91,4 +91,8 @@
       ink_drop_color_.value_or(kTrayPopupInkDropBaseColor));
 }
 
+const char* SystemMenuButton::GetClassName() const {
+  return "SystemMenuButton";
+}
+
 }  // namespace ash
diff --git a/ash/system/tray/system_menu_button.h b/ash/system/tray/system_menu_button.h
index 2eccad4..0d2880f 100644
--- a/ash/system/tray/system_menu_button.h
+++ b/ash/system/tray/system_menu_button.h
@@ -50,6 +50,7 @@
   std::unique_ptr<views::InkDropRipple> CreateInkDropRipple() const override;
   std::unique_ptr<views::InkDropHighlight> CreateInkDropHighlight()
       const override;
+  const char* GetClassName() const override;
 
  private:
   // Returns the size that the ink drop should be constructed with.
diff --git a/ash/system/tray/tray_bubble_view.cc b/ash/system/tray/tray_bubble_view.cc
index 4e982f1..3263c17 100644
--- a/ash/system/tray/tray_bubble_view.cc
+++ b/ash/system/tray/tray_bubble_view.cc
@@ -468,6 +468,10 @@
   }
 }
 
+const char* TrayBubbleView::GetClassName() const {
+  return "TrayBubbleView";
+}
+
 void TrayBubbleView::MouseMovedOutOfHost() {
   // The user moved the mouse that was over the bubble when it was first shown.
   if (delegate_)
diff --git a/ash/system/tray/tray_bubble_view.h b/ash/system/tray/tray_bubble_view.h
index cffe486..122f8f5 100644
--- a/ash/system/tray/tray_bubble_view.h
+++ b/ash/system/tray/tray_bubble_view.h
@@ -172,6 +172,7 @@
   void OnMouseEntered(const ui::MouseEvent& event) override;
   void OnMouseExited(const ui::MouseEvent& event) override;
   void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
+  const char* GetClassName() const override;
 
   // Overridden from MouseWatcherListener
   void MouseMovedOutOfHost() override;
diff --git a/ash/system/tray/tray_container.cc b/ash/system/tray/tray_container.cc
index 1cf11a4..137231b 100644
--- a/ash/system/tray/tray_container.cc
+++ b/ash/system/tray/tray_container.cc
@@ -60,6 +60,10 @@
   return GetBoundsInScreen();
 }
 
+const char* TrayContainer::GetClassName() const {
+  return "TrayContainer";
+}
+
 void TrayContainer::UpdateLayout() {
   const bool is_horizontal = shelf_->IsHorizontalAlignment();
 
diff --git a/ash/system/tray/tray_container.h b/ash/system/tray/tray_container.h
index 1758e72..4495bbb 100644
--- a/ash/system/tray/tray_container.h
+++ b/ash/system/tray/tray_container.h
@@ -29,6 +29,7 @@
   void ViewHierarchyChanged(
       const views::ViewHierarchyChangedDetails& details) override;
   gfx::Rect GetAnchorBoundsInScreen() const override;
+  const char* GetClassName() const override;
 
  private:
   void UpdateLayout();
diff --git a/ash/system/tray/tray_detailed_view.cc b/ash/system/tray/tray_detailed_view.cc
index 89e2e58..6a840da 100644
--- a/ash/system/tray/tray_detailed_view.cc
+++ b/ash/system/tray/tray_detailed_view.cc
@@ -123,6 +123,8 @@
     PositionHeaderRows();
   }
 
+  const char* GetClassName() const override { return "ScrollContentsView"; }
+
   View::Views GetChildrenInZOrder() override {
     // Place sticky headers last in the child order so that they wind up on top
     // in Z order.
@@ -459,4 +461,8 @@
   return height();
 }
 
+const char* TrayDetailedView::GetClassName() const {
+  return "TrayDetailedView";
+}
+
 }  // namespace ash
diff --git a/ash/system/tray/tray_detailed_view.h b/ash/system/tray/tray_detailed_view.h
index f0ec573b..523581d 100644
--- a/ash/system/tray/tray_detailed_view.h
+++ b/ash/system/tray/tray_detailed_view.h
@@ -52,6 +52,7 @@
   // views::View:
   void Layout() override;
   int GetHeightForWidth(int width) const override;
+  const char* GetClassName() const override;
 
   // Exposes the layout manager of this view to give control to subclasses.
   views::BoxLayout* box_layout() { return box_layout_; }
diff --git a/ash/system/tray/tray_info_label.cc b/ash/system/tray/tray_info_label.cc
index c1c75cf..534fed4 100644
--- a/ash/system/tray/tray_info_label.cc
+++ b/ash/system/tray/tray_info_label.cc
@@ -75,6 +75,10 @@
     node_data->role = ax::mojom::Role::kLabelText;
 }
 
+const char* TrayInfoLabel::GetClassName() const {
+  return "TrayInfoLabel";
+}
+
 bool TrayInfoLabel::IsClickable() {
   if (delegate_)
     return delegate_->IsLabelClickable(message_id_);
diff --git a/ash/system/tray/tray_info_label.h b/ash/system/tray/tray_info_label.h
index 5e7e631..f93d284 100644
--- a/ash/system/tray/tray_info_label.h
+++ b/ash/system/tray/tray_info_label.h
@@ -40,6 +40,9 @@
   bool PerformAction(const ui::Event& event) override;
   void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
 
+  // views::View:
+  const char* GetClassName() const override;
+
  private:
   friend class TrayInfoLabelTest;
 
diff --git a/ash/system/tray/tray_item_view.cc b/ash/system/tray/tray_item_view.cc
index 6338d58e..f16a046 100644
--- a/ash/system/tray/tray_item_view.cc
+++ b/ash/system/tray/tray_item_view.cc
@@ -110,6 +110,10 @@
   return GetPreferredSize().height();
 }
 
+const char* TrayItemView::GetClassName() const {
+  return "TrayItemView";
+}
+
 void TrayItemView::ChildPreferredSizeChanged(views::View* child) {
   PreferredSizeChanged();
 }
diff --git a/ash/system/tray/tray_item_view.h b/ash/system/tray/tray_item_view.h
index 596c76f..4973ef1 100644
--- a/ash/system/tray/tray_item_view.h
+++ b/ash/system/tray/tray_item_view.h
@@ -62,6 +62,7 @@
   void SetVisible(bool visible) override;
   gfx::Size CalculatePreferredSize() const override;
   int GetHeightForWidth(int width) const override;
+  const char* GetClassName() const override;
 
  protected:
   // The default animation duration is 200ms. But each view can customize this.
diff --git a/ash/system/virtual_keyboard/virtual_keyboard_tray.cc b/ash/system/virtual_keyboard/virtual_keyboard_tray.cc
index 36ff6cc..fc0f311 100644
--- a/ash/system/virtual_keyboard/virtual_keyboard_tray.cc
+++ b/ash/system/virtual_keyboard/virtual_keyboard_tray.cc
@@ -106,6 +106,10 @@
   UpdateIcon();
 }
 
+const char* VirtualKeyboardTray::GetClassName() const {
+  return "VirtualKeyboardTray";
+}
+
 void VirtualKeyboardTray::UpdateIcon() {
   const gfx::VectorIcon& icon = kShelfKeyboardNewuiIcon;
   gfx::ImageSkia image = gfx::CreateVectorIcon(
diff --git a/ash/system/virtual_keyboard/virtual_keyboard_tray.h b/ash/system/virtual_keyboard/virtual_keyboard_tray.h
index b97aefa..3ebfc15 100644
--- a/ash/system/virtual_keyboard/virtual_keyboard_tray.h
+++ b/ash/system/virtual_keyboard/virtual_keyboard_tray.h
@@ -43,6 +43,9 @@
   // SessionObserver:
   void OnSessionStateChanged(session_manager::SessionState state) override;
 
+  // views::View:
+  const char* GetClassName() const override;
+
  private:
   // Updates the icon UI.
   void UpdateIcon();
diff --git a/base/mac/foundation_util.h b/base/mac/foundation_util.h
index d7f709d..7ddecaa 100644
--- a/base/mac/foundation_util.h
+++ b/base/mac/foundation_util.h
@@ -402,6 +402,13 @@
 #if defined(__OBJC__)
 BASE_EXPORT extern std::ostream& operator<<(std::ostream& o, id);
 BASE_EXPORT extern std::ostream& operator<<(std::ostream& o, NSRange);
+BASE_EXPORT extern std::ostream& operator<<(std::ostream& o, SEL);
+
+#if !defined(OS_IOS)
+BASE_EXPORT extern std::ostream& operator<<(std::ostream& o, NSPoint);
+BASE_EXPORT extern std::ostream& operator<<(std::ostream& o, NSRect);
+BASE_EXPORT extern std::ostream& operator<<(std::ostream& o, NSSize);
+#endif
 #endif
 
 #endif  // BASE_MAC_FOUNDATION_UTIL_H_
diff --git a/base/mac/foundation_util.mm b/base/mac/foundation_util.mm
index b5003fbf..8b20ebc 100644
--- a/base/mac/foundation_util.mm
+++ b/base/mac/foundation_util.mm
@@ -510,3 +510,19 @@
 std::ostream& operator<<(std::ostream& o, NSRange range) {
   return o << NSStringFromRange(range);
 }
+
+std::ostream& operator<<(std::ostream& o, SEL selector) {
+  return o << NSStringFromSelector(selector);
+}
+
+#if !defined(OS_IOS)
+std::ostream& operator<<(std::ostream& o, NSPoint point) {
+  return o << NSStringFromPoint(point);
+}
+std::ostream& operator<<(std::ostream& o, NSRect rect) {
+  return o << NSStringFromRect(rect);
+}
+std::ostream& operator<<(std::ostream& o, NSSize size) {
+  return o << NSStringFromSize(size);
+}
+#endif
diff --git a/base/win/scoped_variant.cc b/base/win/scoped_variant.cc
index dc2374c1..61f645a 100644
--- a/base/win/scoped_variant.cc
+++ b/base/win/scoped_variant.cc
@@ -95,24 +95,18 @@
   ULONG flags = ignore_case ? NORM_IGNORECASE : 0;
   HRESULT hr = ::VarCmp(const_cast<VARIANT*>(&var_), const_cast<VARIANT*>(&var),
                         LOCALE_USER_DEFAULT, flags);
-  int ret = 0;
+  DCHECK(SUCCEEDED(hr) && hr != VARCMP_NULL)
+      << "unsupported variant comparison: " << var_.vt << " and " << var.vt;
 
   switch (hr) {
     case VARCMP_LT:
-      ret = -1;
-      break;
-
+      return -1;
     case VARCMP_GT:
     case VARCMP_NULL:
-      ret = 1;
-      break;
-
+      return 1;
     default:
-      // Equal.
-      break;
+      return 0;
   }
-
-  return ret;
 }
 
 void ScopedVariant::Set(const wchar_t* str) {
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index d48a285..cadf905 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-8912934291927162032
\ No newline at end of file
+8912909845839913648
\ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index 06cf694..4ccb055 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-8912948016361816848
\ No newline at end of file
+8912928491539290416
\ No newline at end of file
diff --git a/chrome/BUILD.gn b/chrome/BUILD.gn
index 0ccf359..ebb49824 100644
--- a/chrome/BUILD.gn
+++ b/chrome/BUILD.gn
@@ -856,11 +856,12 @@
       _stamp_file,
     ]
 
+    _old_versions_dir =
+        "$root_out_dir/$chrome_product_full_name.app/Contents/Versions"
     if (new_mac_bundle_structure) {
       _versions_dir = "$root_out_dir/$chrome_product_full_name.app/Contents/Frameworks/$chrome_framework_name.framework/Versions"
     } else {
-      _versions_dir =
-          "$root_out_dir/$chrome_product_full_name.app/Contents/Versions"
+      _versions_dir = _old_versions_dir
     }
 
     args = [
@@ -876,6 +877,8 @@
       args += [
         "--keep",
         "Current",
+        "--delete",
+        rebase_path(_old_versions_dir, root_build_dir),
       ]
     }
   }
diff --git a/chrome/VERSION b/chrome/VERSION
index 6cb0cf7..4945942 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=76
 MINOR=0
-BUILD=3801
+BUILD=3802
 PATCH=0
diff --git a/chrome/app/onboarding_welcome_strings.grdp b/chrome/app/onboarding_welcome_strings.grdp
index eb5f4cc..cbabb3c 100644
--- a/chrome/app/onboarding_welcome_strings.grdp
+++ b/chrome/app/onboarding_welcome_strings.grdp
@@ -30,6 +30,11 @@
   <message name="IDS_ONBOARDING_WELCOME_NUX_GOOGLE_APPS_DESCRIPTION" desc="Description of what this section in the onboarding workflow does.">
     Add bookmarks to your favorite Google Apps
   </message>
+  <if expr="_google_chrome">
+    <message name="IDS_ONBOARDING_WELCOME_NUX_GOOGLE_SEARCH" desc="Label for a button that creates a bookmark to google.com, this should be the name of the brand.">
+      Google
+    </message>
+  </if>
   <message name="IDS_ONBOARDING_WELCOME_NUX_GOOGLE_GMAIL" desc="Label for a button that creates a bookmark to gmail.com, this should be the name of the brand.">
     Gmail
   </message>
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index 92e3ff7b..8d12ea9 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -1509,6 +1509,9 @@
     <message name="IDS_SETTINGS_PRINTING_CUPS_MANUFACTURER_MODEL_ADDITIONAL_INFORMATION" desc="Informational text displayed to the user when the the user is doing advanced manual printer setup.">
       <ph name="PRINTER_NAME">$1<ex>Printer</ex></ph> could not be configured automatically. Please specify advanced printer details.
     </message>
+    <message name="IDS_SETTINGS_PRINTING_CUPS_EULA_NOTICE" desc="The message shown to users if a printer has a EULA agreement attached to it.">
+      End User License Agreement
+    </message>
   </if>
   <if expr="not chromeos">
     <message name="IDS_SETTINGS_PRINTING_LOCAL_PRINTERS_TITLE" desc="In Printing Settings, the title of local printers setting section on OS other than Chrome OS.">
@@ -1626,18 +1629,6 @@
     <message name="IDS_SETTINGS_KERBEROS_ACCOUNTS_REAUTHENTICATION_LABEL" desc="Label of the re-authentication button on Kerberos Accounts Settings page.">
       Sign in
     </message>
-    <message name="IDS_SETTINGS_ADD_KERBEROS_ACCOUNT" desc="In Add Kerberos Accounts dialog, the title of the dialog.">
-      Add Kerberos Account
-    </message>
-    <message name="IDS_SETTINGS_KERBEROS_USERNAME" desc="Title for the input that lets users specify their username for a Kerberos account.">
-      Username
-    </message>
-    <message name="IDS_SETTINGS_KERBEROS_PASSWORD" desc="Title for the input that lets users specify their password for a Kerberos account.">
-      Password
-    </message>
-    <message name="IDS_SETTINGS_KERBEROS_GENERAL_ERROR_MESSAGE" desc="Fallback error message displayed in the Add Kerberos.">
-      Oops! Something went wrong (error code <ph name="ERROR_CODE">$1<ex>123</ex></ph>).
-    </message>
   </if>
 
   <!-- Date/Time Page -->
diff --git a/chrome/browser/browser_process_platform_part_chromeos.cc b/chrome/browser/browser_process_platform_part_chromeos.cc
index 65334bee..366a587 100644
--- a/chrome/browser/browser_process_platform_part_chromeos.cc
+++ b/chrome/browser/browser_process_platform_part_chromeos.cc
@@ -44,8 +44,6 @@
 #include "services/service_manager/public/cpp/service.h"
 #include "services/ws/public/cpp/input_devices/input_device_controller.h"
 #include "services/ws/public/cpp/input_devices/input_device_controller_client.h"
-#include "services/ws/public/mojom/constants.mojom.h"
-#include "ui/base/ui_base_features.h"
 
 BrowserProcessPlatformPart::BrowserProcessPlatformPart()
     : created_profile_helper_(false),
@@ -201,13 +199,10 @@
 ws::InputDeviceControllerClient*
 BrowserProcessPlatformPart::GetInputDeviceControllerClient() {
   if (!input_device_controller_client_) {
-    const std::string service_name = !features::IsMultiProcessMash()
-                                         ? chromeos::kChromeServiceName
-                                         : ws::mojom::kServiceName;
     input_device_controller_client_ =
         std::make_unique<ws::InputDeviceControllerClient>(
             content::ServiceManagerConnection::GetForProcess()->GetConnector(),
-            service_name);
+            chromeos::kChromeServiceName);
   }
   return input_device_controller_client_.get();
 }
diff --git a/chrome/browser/chromeos/accessibility/select_to_speak_browsertest.cc b/chrome/browser/chromeos/accessibility/select_to_speak_browsertest.cc
index be85c3b..0b8f9dd 100644
--- a/chrome/browser/chromeos/accessibility/select_to_speak_browsertest.cc
+++ b/chrome/browser/chromeos/accessibility/select_to_speak_browsertest.cc
@@ -3,7 +3,6 @@
 // found in the LICENSE file.
 
 #include <memory>
-#include <utility>
 #include <vector>
 
 #include "ash/accessibility/accessibility_focus_ring_controller.h"
@@ -314,15 +313,15 @@
       "<span lang='en-US'>The first paragraph</span>"
       "<span lang='fr-FR'>la deuxième paragraphe</span></div>");
 
-  std::pair<std::string, std::string> result1 =
+  SpeechMonitorUtterance result1 =
       speech_monitor_.GetNextUtteranceWithLanguage();
-  EXPECT_TRUE(base::MatchPattern(result1.first, "The first paragraph*"));
-  EXPECT_EQ("en-US", result1.second);
+  EXPECT_TRUE(base::MatchPattern(result1.text, "The first paragraph*"));
+  EXPECT_EQ("en-US", result1.lang);
 
-  std::pair<std::string, std::string> result2 =
+  SpeechMonitorUtterance result2 =
       speech_monitor_.GetNextUtteranceWithLanguage();
-  EXPECT_TRUE(base::MatchPattern(result2.first, "la deuxième paragraphe*"));
-  EXPECT_EQ("fr-FR", result2.second);
+  EXPECT_TRUE(base::MatchPattern(result2.text, "la deuxième paragraphe*"));
+  EXPECT_EQ("fr-FR", result2.lang);
 }
 
 // Flaky test. https://crbug.com/950049
diff --git a/chrome/browser/chromeos/accessibility/speech_monitor.cc b/chrome/browser/chromeos/accessibility/speech_monitor.cc
index 0ae533d..3ea5d4f 100644
--- a/chrome/browser/chromeos/accessibility/speech_monitor.cc
+++ b/chrome/browser/chromeos/accessibility/speech_monitor.cc
@@ -25,17 +25,16 @@
 }
 
 std::string SpeechMonitor::GetNextUtterance() {
-  return GetNextUtteranceWithLanguage().first;
+  return GetNextUtteranceWithLanguage().text;
 }
 
-std::pair<std::string, std::string>
-SpeechMonitor::GetNextUtteranceWithLanguage() {
+SpeechMonitorUtterance SpeechMonitor::GetNextUtteranceWithLanguage() {
   if (utterance_queue_.empty()) {
     loop_runner_ = new content::MessageLoopRunner();
     loop_runner_->Run();
     loop_runner_ = NULL;
   }
-  std::pair<std::string, std::string> result = utterance_queue_.front();
+  SpeechMonitorUtterance result = utterance_queue_.front();
   utterance_queue_.pop_front();
   return result;
 }
@@ -63,9 +62,9 @@
       loop_runner_->Run();
       loop_runner_ = NULL;
     }
-    std::pair<std::string, std::string> result = utterance_queue_.front();
+    SpeechMonitorUtterance result = utterance_queue_.front();
     utterance_queue_.pop_front();
-    if (result.first == message)
+    if (result.text == message)
       return true;
   }
   return false;
@@ -117,8 +116,7 @@
     return;
 
   VLOG(0) << "Speaking " << utterance->GetText();
-  utterance_queue_.push_back(
-      std::make_pair(utterance->GetText(), utterance->GetLang()));
+  utterance_queue_.emplace_back(utterance->GetText(), utterance->GetLang());
   if (loop_runner_.get())
     loop_runner_->Quit();
 }
diff --git a/chrome/browser/chromeos/accessibility/speech_monitor.h b/chrome/browser/chromeos/accessibility/speech_monitor.h
index 9c28ec7..4ad7abe 100644
--- a/chrome/browser/chromeos/accessibility/speech_monitor.h
+++ b/chrome/browser/chromeos/accessibility/speech_monitor.h
@@ -5,8 +5,6 @@
 #ifndef CHROME_BROWSER_CHROMEOS_ACCESSIBILITY_SPEECH_MONITOR_H_
 #define CHROME_BROWSER_CHROMEOS_ACCESSIBILITY_SPEECH_MONITOR_H_
 
-#include <utility>
-
 #include "base/containers/circular_deque.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
@@ -17,6 +15,13 @@
 
 namespace chromeos {
 
+struct SpeechMonitorUtterance {
+  SpeechMonitorUtterance(std::string text_, std::string lang_)
+      : text(text_), lang(lang_) {}
+  std::string text;
+  std::string lang;
+};
+
 // For testing purpose installs itself as the platform speech synthesis engine,
 // allowing it to intercept all speech calls, and then provides a method to
 // block until the next utterance is spoken.
@@ -28,7 +33,7 @@
   // Blocks until the next utterance is spoken, and returns its text.
   std::string GetNextUtterance();
   // Blocks until the next utterance is spoken, and returns its text.
-  std::pair<std::string, std::string> GetNextUtteranceWithLanguage();
+  SpeechMonitorUtterance GetNextUtteranceWithLanguage();
 
   // Wait for next utterance and return true if next utterance is ChromeVox
   // enabled message.
@@ -65,7 +70,7 @@
 
   scoped_refptr<content::MessageLoopRunner> loop_runner_;
   // Our list of utterances and specified language.
-  base::circular_deque<std::pair<std::string, std::string>> utterance_queue_;
+  base::circular_deque<SpeechMonitorUtterance> utterance_queue_;
   bool did_stop_ = false;
   std::string error_;
 
diff --git a/chrome/browser/chromeos/arc/fileapi/arc_file_system_bridge.cc b/chrome/browser/chromeos/arc/fileapi/arc_file_system_bridge.cc
index bc7e681..cc73ebb 100644
--- a/chrome/browser/chromeos/arc/fileapi/arc_file_system_bridge.cc
+++ b/chrome/browser/chromeos/arc/fileapi/arc_file_system_bridge.cc
@@ -76,11 +76,15 @@
 // Converts the given URL to a FileSystemURL.
 storage::FileSystemURL GetFileSystemURL(
     scoped_refptr<storage::FileSystemContext> context,
-    const GURL& url) {
+    const GURL& url,
+    storage::IsolatedContext::ScopedFSHandle* file_system) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  return file_manager::util::CreateIsolatedURLFromVirtualPath(
-      *context, /* empty origin */ GURL(),
-      chromeos::ExternalFileURLToVirtualPath(url));
+  storage::FileSystemURL result;
+  std::tie(result, *file_system) =
+      file_manager::util::CreateIsolatedURLFromVirtualPath(
+          *context, /* empty origin */ GURL(),
+          chromeos::ExternalFileURLToVirtualPath(url));
+  return result;
 }
 
 // Retrieves the file size on the IO thread, and runs the callback on the UI
@@ -244,11 +248,18 @@
   }
   scoped_refptr<storage::FileSystemContext> context =
       GetFileSystemContext(profile_, url_decoded);
+  storage::IsolatedContext::ScopedFSHandle isolated_file_system;
   base::PostTaskWithTraits(
       FROM_HERE, {content::BrowserThread::IO},
-      base::BindOnce(&GetFileSizeOnIOThread, context,
-                     GetFileSystemURL(context, url_decoded),
-                     std::move(callback)));
+      base::BindOnce(
+          &GetFileSizeOnIOThread, context,
+          GetFileSystemURL(context, url_decoded, &isolated_file_system),
+          std::move(callback)));
+  // TODO(https://crbug.com/963027): This is currently leaking the isolated
+  // file system, the file system should somehow be revoked when the url
+  // returned by GetFileSystemURL is no longer needed.
+  storage::IsolatedContext::GetInstance()->AddReference(
+      isolated_file_system.id());
 }
 
 void ArcFileSystemBridge::GetFileType(const std::string& url,
@@ -262,8 +273,9 @@
   }
   scoped_refptr<storage::FileSystemContext> context =
       GetFileSystemContext(profile_, url_decoded);
+  storage::IsolatedContext::ScopedFSHandle file_system;
   storage::FileSystemURL file_system_url =
-      GetFileSystemURL(context, url_decoded);
+      GetFileSystemURL(context, url_decoded, &file_system);
   extensions::app_file_handler_util::GetMimeTypeForLocalPath(
       profile_, file_system_url.path(),
       base::Bind(
@@ -405,11 +417,16 @@
   const GURL& url = it_url->second;
   scoped_refptr<storage::FileSystemContext> context =
       GetFileSystemContext(profile_, url);
+  storage::IsolatedContext::ScopedFSHandle file_system;
   *it_forwarder = FileStreamForwarderPtr(new FileStreamForwarder(
-      context, GetFileSystemURL(context, url), offset, size,
+      context, GetFileSystemURL(context, url, &file_system), offset, size,
       std::move(pipe_write_end),
       base::BindOnce(&ArcFileSystemBridge::OnReadRequestCompleted,
                      weak_ptr_factory_.GetWeakPtr(), id, it_forwarder)));
+  // TODO(https://crbug.com/963027): This is currently leaking the isolated
+  // file system, the file system should somehow be revoked when the url
+  // returned by GetFileSystemURL is no longer needed.
+  storage::IsolatedContext::GetInstance()->AddReference(file_system.id());
   return true;
 }
 
diff --git a/chrome/browser/chromeos/arc/video/gpu_arc_video_service_host.cc b/chrome/browser/chromeos/arc/video/gpu_arc_video_service_host.cc
index eef36d9..f796d9b 100644
--- a/chrome/browser/chromeos/arc/video/gpu_arc_video_service_host.cc
+++ b/chrome/browser/chromeos/arc/video/gpu_arc_video_service_host.cc
@@ -30,8 +30,6 @@
 #include "mojo/public/cpp/system/platform_handle.h"
 #include "services/service_manager/public/cpp/connector.h"
 #include "services/ws/public/mojom/arc_gpu.mojom.h"
-#include "services/ws/public/mojom/constants.mojom.h"
-#include "ui/base/ui_base_features.h"
 
 namespace arc {
 
@@ -58,7 +56,7 @@
 
 class VideoAcceleratorFactoryService : public mojom::VideoAcceleratorFactory {
  public:
-  VideoAcceleratorFactoryService() { DCHECK(!features::IsMultiProcessMash()); }
+  VideoAcceleratorFactoryService() = default;
 
   ~VideoAcceleratorFactoryService() override = default;
 
@@ -93,54 +91,6 @@
   DISALLOW_COPY_AND_ASSIGN(VideoAcceleratorFactoryService);
 };
 
-class VideoAcceleratorFactoryServiceViz
-    : public mojom::VideoAcceleratorFactory {
- public:
-  VideoAcceleratorFactoryServiceViz() {
-    DCHECK(features::IsMultiProcessMash());
-    DETACH_FROM_THREAD(thread_checker_);
-    auto* connector =
-        content::ServiceManagerConnection::GetForProcess()->GetConnector();
-    connector->BindInterface(ws::mojom::kServiceName, &arc_gpu_);
-  }
-
-  ~VideoAcceleratorFactoryServiceViz() override {
-    DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  }
-
-  void CreateDecodeAccelerator(
-      mojom::VideoDecodeAcceleratorRequest request) override {
-    DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-    arc_gpu_->CreateVideoDecodeAccelerator(std::move(request));
-  }
-
-  void CreateEncodeAccelerator(
-      mojom::VideoEncodeAcceleratorRequest request) override {
-    DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-    arc_gpu_->CreateVideoEncodeAccelerator(std::move(request));
-  }
-
-  void CreateProtectedBufferAllocator(
-      mojom::VideoProtectedBufferAllocatorRequest request) override {
-    DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-    arc_gpu_->CreateVideoProtectedBufferAllocator(std::move(request));
-  }
-
- private:
-  THREAD_CHECKER(thread_checker_);
-
-  ws::mojom::ArcGpuPtr arc_gpu_;
-
-  DISALLOW_COPY_AND_ASSIGN(VideoAcceleratorFactoryServiceViz);
-};
-
-std::unique_ptr<mojom::VideoAcceleratorFactory>
-CreateVideoAcceleratorFactory() {
-  if (features::IsMultiProcessMash())
-    return std::make_unique<VideoAcceleratorFactoryServiceViz>();
-  return std::make_unique<VideoAcceleratorFactoryService>();
-}
-
 }  // namespace
 
 // static
@@ -152,7 +102,8 @@
 GpuArcVideoServiceHost::GpuArcVideoServiceHost(content::BrowserContext* context,
                                                ArcBridgeService* bridge_service)
     : arc_bridge_service_(bridge_service),
-      video_accelerator_factory_(CreateVideoAcceleratorFactory()) {
+      video_accelerator_factory_(
+          std::make_unique<VideoAcceleratorFactoryService>()) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   arc_bridge_service_->video()->SetHost(this);
 }
diff --git a/chrome/browser/chromeos/drive/file_system_util_unittest.cc b/chrome/browser/chromeos/drive/file_system_util_unittest.cc
index 6a49941..8291470 100644
--- a/chrome/browser/chromeos/drive/file_system_util_unittest.cc
+++ b/chrome/browser/chromeos/drive/file_system_util_unittest.cc
@@ -166,7 +166,7 @@
 
   // Type:"isolated" + virtual_path:"isolated_id/name" mapped on a Drive path.
   std::string isolated_name;
-  std::string isolated_id =
+  storage::IsolatedContext::ScopedFSHandle isolated_fs =
       storage::IsolatedContext::GetInstance()->RegisterFileSystemForPath(
           storage::kFileSystemTypeNativeForPlatformApp, std::string(),
           GetDriveMountPointPath(&profile).AppendASCII("bar/buz"),
@@ -174,7 +174,7 @@
   EXPECT_EQ(base::FilePath::FromUTF8Unsafe("drive/bar/buz"),
             ExtractDrivePathFromFileSystemUrl(context->CrackURL(
                 GURL("filesystem:chrome-extension://dummy-id/isolated/" +
-                     isolated_id + "/" + isolated_name))));
+                     isolated_fs.id() + "/" + isolated_name))));
 }
 
 TEST_F(ProfileRelatedFileSystemUtilTest, GetCacheRootPath) {
diff --git a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
index d4b0d14..97b8797 100644
--- a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
+++ b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
@@ -79,7 +79,6 @@
 #include "mojo/public/cpp/bindings/associated_binding.h"
 #include "net/base/filename_util.h"
 #include "services/service_manager/public/cpp/connector.h"
-#include "services/ws/public/mojom/constants.mojom.h"
 #include "ui/base/ime/ime_bridge.h"
 #include "ui/base/ui_base_features.h"
 #include "ui/display/display.h"
@@ -1542,63 +1541,6 @@
 }
 
 ///////////////////////////////////////////////////////////////////////////////
-// AutotestPrivateEnsureWindowServiceClientHasDrawnWindowFunction
-///////////////////////////////////////////////////////////////////////////////
-
-AutotestPrivateEnsureWindowServiceClientHasDrawnWindowFunction::
-    AutotestPrivateEnsureWindowServiceClientHasDrawnWindowFunction() = default;
-AutotestPrivateEnsureWindowServiceClientHasDrawnWindowFunction::
-    ~AutotestPrivateEnsureWindowServiceClientHasDrawnWindowFunction() = default;
-
-ExtensionFunction::ResponseAction
-AutotestPrivateEnsureWindowServiceClientHasDrawnWindowFunction::Run() {
-  auto params = api::autotest_private::EnsureWindowServiceClientHasDrawnWindow::
-      Params::Create(*args_);
-  EXTENSION_FUNCTION_VALIDATE(params);
-
-  service_manager::Connector* connector =
-      content::ServiceManagerConnection::GetForProcess()->GetConnector();
-  connector->BindInterface(
-      service_manager::ServiceFilter::ByName(ws::mojom::kServiceName),
-      mojo::MakeRequest(&window_server_test_ptr_));
-  window_server_test_ptr_->EnsureClientHasDrawnWindow(
-      params->client_name,
-      base::BindOnce(
-          &AutotestPrivateEnsureWindowServiceClientHasDrawnWindowFunction::
-              OnEnsureClientHasDrawnWindowCallback,
-          this));
-
-  timeout_timer_.Start(
-      FROM_HERE, base::TimeDelta::FromMilliseconds(params->timeout_ms),
-      base::BindOnce(
-          &AutotestPrivateEnsureWindowServiceClientHasDrawnWindowFunction::
-              OnTimeout,
-          this));
-
-  return RespondLater();
-}
-
-void AutotestPrivateEnsureWindowServiceClientHasDrawnWindowFunction::
-    OnEnsureClientHasDrawnWindowCallback(bool success) {
-  if (did_respond()) {
-    LOG(ERROR) << "EnsureClientHasDrawnWindow returned after timeout: "
-               << success;
-    return;
-  }
-
-  Respond(OneArgument(std::make_unique<base::Value>(success)));
-  timeout_timer_.AbandonAndStop();
-}
-
-void AutotestPrivateEnsureWindowServiceClientHasDrawnWindowFunction::
-    OnTimeout() {
-  if (did_respond())
-    return;
-
-  Respond(Error("EnsureWindowServiceClientHasDrawnWindowFunction timeout."));
-}
-
-///////////////////////////////////////////////////////////////////////////////
 // AutotestPrivateGetPrimaryDisplayScaleFactorFunction
 ///////////////////////////////////////////////////////////////////////////////
 
diff --git a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.h b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.h
index f95a886..49badeb 100644
--- a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.h
+++ b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.h
@@ -22,7 +22,6 @@
 #include "chromeos/services/machine_learning/public/mojom/machine_learning_service.mojom.h"
 #include "chromeos/services/machine_learning/public/mojom/model.mojom.h"
 #include "extensions/browser/browser_context_keyed_api_factory.h"
-#include "services/ws/public/mojom/window_server_test.mojom.h"
 #include "ui/message_center/public/cpp/notification_types.h"
 #include "ui/snapshot/screenshot_grabber.h"
 
@@ -567,26 +566,6 @@
   ResponseAction Run() override;
 };
 
-// Ensure a Window Service client has drawn windows with a timeout.
-class AutotestPrivateEnsureWindowServiceClientHasDrawnWindowFunction
-    : public UIThreadExtensionFunction {
- public:
-  AutotestPrivateEnsureWindowServiceClientHasDrawnWindowFunction();
-  DECLARE_EXTENSION_FUNCTION(
-      "autotestPrivate.ensureWindowServiceClientHasDrawnWindow",
-      AUTOTESTPRIVATE_ENSUREWINDOWSERVICECLIENTHASDRAWNWINDOW)
-
- private:
-  ~AutotestPrivateEnsureWindowServiceClientHasDrawnWindowFunction() override;
-  ResponseAction Run() override;
-
-  void OnEnsureClientHasDrawnWindowCallback(bool success);
-  void OnTimeout();
-
-  ws::mojom::WindowServerTestPtr window_server_test_ptr_;
-  base::OneShotTimer timeout_timer_;
-};
-
 // The profile-keyed service that manages the autotestPrivate extension API.
 class AutotestPrivateAPI : public BrowserContextKeyedAPI {
  public:
diff --git a/chrome/browser/chromeos/extensions/input_method_api.cc b/chrome/browser/chromeos/extensions/input_method_api.cc
index a26aa86..866c259 100644
--- a/chrome/browser/chromeos/extensions/input_method_api.cc
+++ b/chrome/browser/chromeos/extensions/input_method_api.cc
@@ -42,6 +42,7 @@
 #include "ui/base/ime/chromeos/input_method_util.h"
 #include "ui/base/ime/ime_bridge.h"
 
+namespace input_method_private = extensions::api::input_method_private;
 namespace AddWordToDictionary =
     extensions::api::input_method_private::AddWordToDictionary;
 namespace SetCurrentInputMethod =
@@ -64,9 +65,13 @@
     extensions::api::input_method_private::GetSurroundingText;
 namespace GetSetting = extensions::api::input_method_private::GetSetting;
 namespace SetSetting = extensions::api::input_method_private::SetSetting;
+namespace SetCompositionRange =
+    extensions::api::input_method_private::SetCompositionRange;
 namespace OnSettingsChanged =
     extensions::api::input_method_private::OnSettingsChanged;
 
+using input_method::InputMethodEngineBase;
+
 namespace {
 
 // Prefix, which is used by XKB.
@@ -386,6 +391,51 @@
 #endif
 }
 
+ExtensionFunction::ResponseAction
+InputMethodPrivateSetCompositionRangeFunction::Run() {
+  InputImeEventRouter* event_router =
+      GetInputImeEventRouter(Profile::FromBrowserContext(browser_context()));
+  InputMethodEngineBase* engine =
+      event_router ? event_router->GetActiveEngine(extension_id()) : nullptr;
+  if (engine) {
+    const auto parent_params = SetCompositionRange::Params::Create(*args_);
+    const auto& params = parent_params->parameters;
+    std::vector<InputMethodEngineBase::SegmentInfo> segments;
+    if (params.segments) {
+      for (const auto& segments_arg : *params.segments) {
+        InputMethodEngineBase::SegmentInfo segment_info;
+        segment_info.start = segments_arg.start;
+        segment_info.end = segments_arg.end;
+        switch (segments_arg.style) {
+          case input_method_private::UNDERLINE_STYLE_UNDERLINE:
+            segment_info.style = InputMethodEngineBase::SEGMENT_STYLE_UNDERLINE;
+            break;
+          case input_method_private::UNDERLINE_STYLE_NONE:
+            EXTENSION_FUNCTION_VALIDATE(false);
+            break;
+        }
+        segments.push_back(segment_info);
+      }
+    } else {
+      // Default to a single segment that spans the entire range.
+      InputMethodEngineBase::SegmentInfo segment_info;
+      segment_info.start = 0;
+      segment_info.end = params.selection_before + params.selection_after;
+      segment_info.style = InputMethodEngineBase::SEGMENT_STYLE_UNDERLINE;
+      segments.push_back(segment_info);
+    }
+    std::string error;
+    if (!engine->SetCompositionRange(params.context_id, params.selection_before,
+                                     params.selection_after, segments,
+                                     &error)) {
+      auto results = std::make_unique<base::ListValue>();
+      results->Append(std::make_unique<base::Value>(false));
+      return RespondNow(ErrorWithArguments(std::move(results), error));
+    }
+  }
+  return RespondNow(OneArgument(std::make_unique<base::Value>(true)));
+}
+
 InputMethodAPI::InputMethodAPI(content::BrowserContext* context)
     : context_(context) {
   EventRouter::Get(context_)->RegisterObserver(this, OnChanged::kEventName);
diff --git a/chrome/browser/chromeos/extensions/input_method_api.h b/chrome/browser/chromeos/extensions/input_method_api.h
index b720e08..ac44931 100644
--- a/chrome/browser/chromeos/extensions/input_method_api.h
+++ b/chrome/browser/chromeos/extensions/input_method_api.h
@@ -240,6 +240,19 @@
   DISALLOW_COPY_AND_ASSIGN(InputMethodPrivateSetSettingFunction);
 };
 
+class InputMethodPrivateSetCompositionRangeFunction
+    : public UIThreadExtensionFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("inputMethodPrivate.setCompositionRange",
+                             INPUTMETHODPRIVATE_SETCOMPOSITIONRANGE)
+
+ protected:
+  ~InputMethodPrivateSetCompositionRangeFunction() override {}
+
+  // UIThreadExtensionFunction:
+  ResponseAction Run() override;
+};
+
 class InputMethodAPI : public BrowserContextKeyedAPI,
                        public extensions::EventRouter::Observer {
  public:
diff --git a/chrome/browser/chromeos/extensions/input_method_apitest_chromeos.cc b/chrome/browser/chromeos/extensions/input_method_apitest_chromeos.cc
index 9178c633..22251a3 100644
--- a/chrome/browser/chromeos/extensions/input_method_apitest_chromeos.cc
+++ b/chrome/browser/chromeos/extensions/input_method_apitest_chromeos.cc
@@ -12,6 +12,7 @@
 #include "chrome/browser/chromeos/extensions/input_method_event_router.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/pref_names.h"
+#include "chrome/test/base/ui_test_utils.h"
 #include "chromeos/constants/chromeos_switches.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/browser/notification_observer.h"
@@ -103,6 +104,21 @@
   ASSERT_TRUE(RunExtensionTest("input_method/basic")) << message_;
 }
 
+IN_PROC_BROWSER_TEST_F(ExtensionInputMethodApiTest, Typing) {
+  // Enable the test IME from the test extension.
+  std::vector<std::string> extension_ime_ids = {
+      "_ext_ime_ilanclmaeigfpnmdlgelmhkpkegdioiptest"};
+  InputMethodManager::Get()->GetActiveIMEState()->SetEnabledExtensionImes(
+      &extension_ime_ids);
+
+  GURL test_url = ui_test_utils::GetTestUrl(
+      base::FilePath("extensions/api_test/input_method/typing/"),
+      base::FilePath("test_page.html"));
+  ui_test_utils::NavigateToURL(browser(), test_url);
+
+  ASSERT_TRUE(RunExtensionTest("input_method/typing")) << message_;
+}
+
 IN_PROC_BROWSER_TEST_F(ExtensionInputMethodApiTest, ImeMenuActivation) {
   // Listener for IME menu initial state ready.
   ExtensionTestMessageListener config_listener("config_ready", false);
diff --git a/chrome/browser/chromeos/file_manager/fileapi_util.cc b/chrome/browser/chromeos/file_manager/fileapi_util.cc
index fad604fe..b96934d 100644
--- a/chrome/browser/chromeos/file_manager/fileapi_util.cc
+++ b/chrome/browser/chromeos/file_manager/fileapi_util.cc
@@ -304,13 +304,21 @@
         return;
       }
 
-      const GURL url = CreateIsolatedURLFromVirtualPath(
-                           *context_, origin, virtual_path).ToGURL();
+      storage::FileSystemURL file_system_url;
+      storage::IsolatedContext::ScopedFSHandle file_system;
+      std::tie(file_system_url, file_system) =
+          CreateIsolatedURLFromVirtualPath(*context_, origin, virtual_path);
+      const GURL url = file_system_url.ToGURL();
       if (!url.is_valid()) {
         NotifyError(std::move(lifetime));
         return;
       }
 
+      // Increase ref count of file system to keep it alive after |file_system|
+      // goes out of scope. Our destructor will eventually revoke the file
+      // system.
+      storage::IsolatedContext::GetInstance()->AddReference(file_system.id());
+
       auto fs_info = FileSystemFileInfo::New();
       fs_info->url = url;
       chooser_info_list_.push_back(
@@ -627,27 +635,23 @@
                      google_apis::CreateRelayCallback(std::move(callback))));
 }
 
-storage::FileSystemURL CreateIsolatedURLFromVirtualPath(
-    const storage::FileSystemContext& context,
-    const GURL& origin,
-    const base::FilePath& virtual_path) {
+std::pair<storage::FileSystemURL, storage::IsolatedContext::ScopedFSHandle>
+CreateIsolatedURLFromVirtualPath(const storage::FileSystemContext& context,
+                                 const GURL& origin,
+                                 const base::FilePath& virtual_path) {
   const storage::FileSystemURL original_url =
       context.CreateCrackedFileSystemURL(
           origin, storage::kFileSystemTypeExternal, virtual_path);
 
   std::string register_name;
-  const std::string isolated_file_system_id =
+  storage::IsolatedContext::ScopedFSHandle file_system =
       storage::IsolatedContext::GetInstance()->RegisterFileSystemForPath(
-          original_url.type(),
-          original_url.filesystem_id(),
-          original_url.path(),
-          &register_name);
-  const storage::FileSystemURL isolated_url =
-      context.CreateCrackedFileSystemURL(
-          origin,
-          storage::kFileSystemTypeIsolated,
-          base::FilePath(isolated_file_system_id).Append(register_name));
-  return isolated_url;
+          original_url.type(), original_url.filesystem_id(),
+          original_url.path(), &register_name);
+  storage::FileSystemURL isolated_url = context.CreateCrackedFileSystemURL(
+      origin, storage::kFileSystemTypeIsolated,
+      base::FilePath(file_system.id()).Append(register_name));
+  return {isolated_url, file_system};
 }
 
 }  // namespace util
diff --git a/chrome/browser/chromeos/file_manager/fileapi_util.h b/chrome/browser/chromeos/file_manager/fileapi_util.h
index 1bc9df2..ff297be 100644
--- a/chrome/browser/chromeos/file_manager/fileapi_util.h
+++ b/chrome/browser/chromeos/file_manager/fileapi_util.h
@@ -15,6 +15,7 @@
 #include "base/files/file.h"
 #include "base/files/file_path.h"
 #include "storage/browser/fileapi/file_system_operation_runner.h"
+#include "storage/browser/fileapi/isolated_context.h"
 #include "third_party/blink/public/mojom/choosers/file_chooser.mojom.h"
 #include "url/gurl.h"
 
@@ -172,10 +173,10 @@
 
 // Obtains isolated file system URL from |virtual_path| pointing a file in the
 // external file system.
-storage::FileSystemURL CreateIsolatedURLFromVirtualPath(
-    const storage::FileSystemContext& context,
-    const GURL& origin,
-    const base::FilePath& virtual_path);
+std::pair<storage::FileSystemURL, storage::IsolatedContext::ScopedFSHandle>
+CreateIsolatedURLFromVirtualPath(const storage::FileSystemContext& context,
+                                 const GURL& origin,
+                                 const base::FilePath& virtual_path);
 
 }  // namespace util
 }  // namespace file_manager
diff --git a/chrome/browser/chromeos/file_system_provider/mount_path_util_unittest.cc b/chrome/browser/chromeos/file_system_provider/mount_path_util_unittest.cc
index bf4b57e..0ca8123 100644
--- a/chrome/browser/chromeos/file_system_provider/mount_path_util_unittest.cc
+++ b/chrome/browser/chromeos/file_system_provider/mount_path_util_unittest.cc
@@ -211,15 +211,13 @@
   // Create an isolated URL for the original one.
   storage::IsolatedContext* const isolated_context =
       storage::IsolatedContext::GetInstance();
-  const std::string isolated_file_system_id =
+  const storage::IsolatedContext::ScopedFSHandle isolated_file_system =
       isolated_context->RegisterFileSystemForPath(
-          storage::kFileSystemTypeProvided,
-          url.filesystem_id(),
-          url.path(),
+          storage::kFileSystemTypeProvided, url.filesystem_id(), url.path(),
           NULL);
 
   const base::FilePath isolated_virtual_path =
-      isolated_context->CreateVirtualRootPath(isolated_file_system_id)
+      isolated_context->CreateVirtualRootPath(isolated_file_system.id())
           .Append(kFilePath.BaseName().value());
 
   const storage::FileSystemURL isolated_url =
diff --git a/chrome/browser/chromeos/fileapi/external_file_resolver.cc b/chrome/browser/chromeos/fileapi/external_file_resolver.cc
index b91c3c4..166638c 100644
--- a/chrome/browser/chromeos/fileapi/external_file_resolver.cc
+++ b/chrome/browser/chromeos/fileapi/external_file_resolver.cc
@@ -39,7 +39,7 @@
   using HelperCallback = base::OnceCallback<void(
       net::Error,
       const scoped_refptr<storage::FileSystemContext>& file_system_context,
-      std::unique_ptr<IsolatedFileSystemScope> isolated_file_system_scope,
+      storage::IsolatedContext::ScopedFSHandle isolated_file_system_scope,
       const storage::FileSystemURL& file_system_url,
       const std::string& mime_type)>;
 
@@ -73,8 +73,9 @@
     const base::FilePath virtual_path = ExternalFileURLToVirtualPath(url);
 
     // Obtain the file system URL.
-    file_system_url_ = file_manager::util::CreateIsolatedURLFromVirtualPath(
-        *context, /* empty origin */ GURL(), virtual_path);
+    std::tie(file_system_url_, isolated_file_system_scope_) =
+        file_manager::util::CreateIsolatedURLFromVirtualPath(
+            *context, /* empty origin */ GURL(), virtual_path);
 
     // Check if the obtained path providing external file URL or not.
     if (!file_system_url_.is_valid()) {
@@ -82,9 +83,6 @@
       return;
     }
 
-    isolated_file_system_scope_.reset(
-        new IsolatedFileSystemScope(file_system_url_.filesystem_id()));
-
     if (!IsExternalFileURLType(file_system_url_.type())) {
       ReplyResult(net::ERR_FAILED);
       return;
@@ -114,13 +112,13 @@
     base::PostTaskWithTraits(
         FROM_HERE, {content::BrowserThread::IO},
         base::BindOnce(std::move(callback_), error, file_system_context_,
-                       base::Passed(&isolated_file_system_scope_),
-                       file_system_url_, mime_type_));
+                       std::move(isolated_file_system_scope_), file_system_url_,
+                       mime_type_));
   }
 
   HelperCallback callback_;
   scoped_refptr<storage::FileSystemContext> file_system_context_;
-  std::unique_ptr<IsolatedFileSystemScope> isolated_file_system_scope_;
+  storage::IsolatedContext::ScopedFSHandle isolated_file_system_scope_;
   storage::FileSystemURL file_system_url_;
   std::string mime_type_;
 
@@ -129,14 +127,6 @@
 
 }  // namespace
 
-IsolatedFileSystemScope::IsolatedFileSystemScope(
-    const std::string& file_system_id)
-    : file_system_id_(file_system_id) {}
-
-IsolatedFileSystemScope::~IsolatedFileSystemScope() {
-  storage::IsolatedContext::GetInstance()->RevokeFileSystem(file_system_id_);
-}
-
 ExternalFileResolver::ExternalFileResolver(void* profile_id)
     : profile_id_(profile_id),
       range_parse_result_(net::OK),
@@ -202,7 +192,7 @@
 void ExternalFileResolver::OnHelperResultObtained(
     net::Error error,
     const scoped_refptr<storage::FileSystemContext>& file_system_context,
-    std::unique_ptr<IsolatedFileSystemScope> isolated_file_system_scope,
+    storage::IsolatedContext::ScopedFSHandle isolated_file_system_scope,
     const storage::FileSystemURL& file_system_url,
     const std::string& mime_type) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
diff --git a/chrome/browser/chromeos/fileapi/external_file_resolver.h b/chrome/browser/chromeos/fileapi/external_file_resolver.h
index 2901c0e..3305758 100644
--- a/chrome/browser/chromeos/fileapi/external_file_resolver.h
+++ b/chrome/browser/chromeos/fileapi/external_file_resolver.h
@@ -15,6 +15,7 @@
 #include "net/base/net_errors.h"
 #include "net/http/http_byte_range.h"
 #include "storage/browser/fileapi/file_system_url.h"
+#include "storage/browser/fileapi/isolated_context.h"
 #include "url/gurl.h"
 
 namespace net {
@@ -28,17 +29,6 @@
 
 namespace chromeos {
 
-// Scope of isolated file system.
-class IsolatedFileSystemScope {
- public:
-  explicit IsolatedFileSystemScope(const std::string& file_system_id);
-  ~IsolatedFileSystemScope();
-
- private:
-  std::string file_system_id_;
-  DISALLOW_COPY_AND_ASSIGN(IsolatedFileSystemScope);
-};
-
 // Resolves an externalfile URL to a redirect or a FileStreamReader.
 class ExternalFileResolver {
  public:
@@ -47,7 +37,7 @@
                                                    const GURL& redirect_url)>;
   using StreamCallback = base::OnceCallback<void(
       const std::string& mime_type,
-      std::unique_ptr<IsolatedFileSystemScope> isolated_file_system_scope,
+      storage::IsolatedContext::ScopedFSHandle isolated_file_system_scope,
       std::unique_ptr<storage::FileStreamReader> stream_reader,
       int64_t size)>;
 
@@ -70,7 +60,7 @@
   void OnHelperResultObtained(
       net::Error error,
       const scoped_refptr<storage::FileSystemContext>& file_system_context,
-      std::unique_ptr<IsolatedFileSystemScope> isolated_file_system_scope,
+      storage::IsolatedContext::ScopedFSHandle isolated_file_system_scope,
       const storage::FileSystemURL& file_system_url,
       const std::string& mime_type);
 
@@ -88,7 +78,7 @@
   StreamCallback stream_callback_;
 
   scoped_refptr<storage::FileSystemContext> file_system_context_;
-  std::unique_ptr<IsolatedFileSystemScope> isolated_file_system_scope_;
+  storage::IsolatedContext::ScopedFSHandle isolated_file_system_scope_;
   storage::FileSystemURL file_system_url_;
   std::string mime_type_;
   base::WeakPtrFactory<ExternalFileResolver> weak_ptr_factory_;
diff --git a/chrome/browser/chromeos/fileapi/external_file_url_loader_factory.cc b/chrome/browser/chromeos/fileapi/external_file_url_loader_factory.cc
index 5ecaf34..f03c16c 100644
--- a/chrome/browser/chromeos/fileapi/external_file_url_loader_factory.cc
+++ b/chrome/browser/chromeos/fileapi/external_file_url_loader_factory.cc
@@ -264,7 +264,7 @@
 
   void OnStreamObtained(
       const std::string& mime_type,
-      std::unique_ptr<IsolatedFileSystemScope> isolated_file_system_scope,
+      storage::IsolatedContext::ScopedFSHandle isolated_file_system_scope,
       std::unique_ptr<storage::FileStreamReader> stream_reader,
       int64_t size) {
     head_.mime_type = mime_type;
@@ -326,7 +326,7 @@
 
   std::unique_ptr<ExternalFileResolver> resolver_;
   network::ResourceResponseHead head_;
-  std::unique_ptr<IsolatedFileSystemScope> isolated_file_system_scope_;
+  storage::IsolatedContext::ScopedFSHandle isolated_file_system_scope_;
   std::unique_ptr<FileSystemReaderDataPipeProducer> data_producer_;
 
   base::WeakPtrFactory<ExternalFileURLLoader> weak_ptr_factory_;
diff --git a/chrome/browser/chromeos/fileapi/external_file_url_request_job.cc b/chrome/browser/chromeos/fileapi/external_file_url_request_job.cc
index 9eff600..d38a438 100644
--- a/chrome/browser/chromeos/fileapi/external_file_url_request_job.cc
+++ b/chrome/browser/chromeos/fileapi/external_file_url_request_job.cc
@@ -62,7 +62,7 @@
 
 void ExternalFileURLRequestJob::OnStreamObtained(
     const std::string& mime_type,
-    std::unique_ptr<IsolatedFileSystemScope> isolated_file_system_scope,
+    storage::IsolatedContext::ScopedFSHandle isolated_file_system_scope,
     std::unique_ptr<storage::FileStreamReader> stream_reader,
     int64_t size) {
   mime_type_ = mime_type;
@@ -82,7 +82,7 @@
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   resolver_.reset();
   stream_reader_.reset();
-  isolated_file_system_scope_.reset();
+  isolated_file_system_scope_ = {};
   net::URLRequestJob::Kill();
   weak_ptr_factory_.InvalidateWeakPtrs();
 }
diff --git a/chrome/browser/chromeos/fileapi/external_file_url_request_job.h b/chrome/browser/chromeos/fileapi/external_file_url_request_job.h
index e4d7dab..670e317 100644
--- a/chrome/browser/chromeos/fileapi/external_file_url_request_job.h
+++ b/chrome/browser/chromeos/fileapi/external_file_url_request_job.h
@@ -63,7 +63,7 @@
 
   void OnStreamObtained(
       const std::string& mime_type,
-      std::unique_ptr<IsolatedFileSystemScope> isolated_file_system_scope,
+      storage::IsolatedContext::ScopedFSHandle isolated_file_system_scope,
       std::unique_ptr<storage::FileStreamReader> stream_reader,
       int64_t size);
 
@@ -75,7 +75,7 @@
   std::unique_ptr<ExternalFileResolver> resolver_;
 
   std::string mime_type_;
-  std::unique_ptr<IsolatedFileSystemScope> isolated_file_system_scope_;
+  storage::IsolatedContext::ScopedFSHandle isolated_file_system_scope_;
   std::unique_ptr<storage::FileStreamReader> stream_reader_;
   int64_t remaining_bytes_;
   GURL redirect_url_;
diff --git a/chrome/browser/chromeos/input_method/input_method_engine.cc b/chrome/browser/chromeos/input_method/input_method_engine.cc
index cc84314..cca2bc7 100644
--- a/chrome/browser/chromeos/input_method/input_method_engine.cc
+++ b/chrome/browser/chromeos/input_method/input_method_engine.cc
@@ -411,6 +411,22 @@
   }
 }
 
+bool InputMethodEngine::SetCompositionRange(
+    uint32_t before,
+    uint32_t after,
+    const std::vector<ui::ImeTextSpan>& text_spans) {
+  if (mojo_helper_->IsConnected()) {
+    // TODO(https://crbug.com/952757): Implement this in Mojo.
+    return false;
+  } else {
+    ui::IMEInputContextHandlerInterface* input_context =
+        ui::IMEBridge::Get()->GetInputContextHandler();
+    if (!input_context)
+      return false;
+    return input_context->SetCompositionRange(before, after, text_spans);
+  }
+}
+
 void InputMethodEngine::CommitTextToInputContext(int context_id,
                                                  const std::string& text) {
   bool committed = false;
diff --git a/chrome/browser/chromeos/input_method/input_method_engine.h b/chrome/browser/chromeos/input_method/input_method_engine.h
index 5a8d0e2..ea00479 100644
--- a/chrome/browser/chromeos/input_method/input_method_engine.h
+++ b/chrome/browser/chromeos/input_method/input_method_engine.h
@@ -145,6 +145,10 @@
   void UpdateComposition(const ui::CompositionText& composition_text,
                          uint32_t cursor_pos,
                          bool is_visible) override;
+  bool SetCompositionRange(
+      uint32_t before,
+      uint32_t after,
+      const std::vector<ui::ImeTextSpan>& text_spans) override;
   void CommitTextToInputContext(int context_id,
                                 const std::string& text) override;
   void DeleteSurroundingTextToInputContext(int offset,
diff --git a/chrome/browser/chromeos/login/enrollment/enrollment_local_policy_server_browsertest.cc b/chrome/browser/chromeos/login/enrollment/enrollment_local_policy_server_browsertest.cc
index 6ee4121..9c85611 100644
--- a/chrome/browser/chromeos/login/enrollment/enrollment_local_policy_server_browsertest.cc
+++ b/chrome/browser/chromeos/login/enrollment/enrollment_local_policy_server_browsertest.cc
@@ -457,8 +457,15 @@
   OobeScreenWaiter(GaiaView::kScreenId).Wait();
 }
 
+// https://crbug.com/965367
+#if defined(OS_CHROMEOS)
+#define MAYBE_ReenrollmentNone DISABLED_ReenrollmentNone
+#else
+#define MAYBE_ReenrollmentNone ReenrollmentNone
+#endif
 // State keys are present but restore mode is not requested.
-IN_PROC_BROWSER_TEST_F(AutoEnrollmentLocalPolicyServer, ReenrollmentNone) {
+IN_PROC_BROWSER_TEST_F(AutoEnrollmentLocalPolicyServer,
+                       MAYBE_ReenrollmentNone) {
   EXPECT_TRUE(policy_server_.SetDeviceStateRetrievalResponse(
       state_keys_broker(),
       enterprise_management::DeviceStateRetrievalResponse::RESTORE_MODE_NONE,
diff --git a/chrome/browser/chromeos/printing/automatic_usb_printer_configurer.cc b/chrome/browser/chromeos/printing/automatic_usb_printer_configurer.cc
index 637f826..f0e6526 100644
--- a/chrome/browser/chromeos/printing/automatic_usb_printer_configurer.cc
+++ b/chrome/browser/chromeos/printing/automatic_usb_printer_configurer.cc
@@ -13,10 +13,10 @@
 namespace chromeos {
 namespace {
 
-bool IsInAutomaticList(const std::string& printer_id,
-                       const std::vector<Printer>& automatic_printers) {
-  for (const auto& automatic_printer : automatic_printers) {
-    if (automatic_printer.id() == printer_id) {
+bool IsPrinterIdInList(const std::string& printer_id,
+                       const std::vector<Printer>& printer_list) {
+  for (const auto& printer : printer_list) {
+    if (printer.id() == printer_id) {
       return true;
     }
   }
@@ -47,16 +47,33 @@
     return;
   }
 
-  if (printer_class != PrinterClass::kAutomatic) {
+  if (printer_class == PrinterClass::kAutomatic) {
+    // Remove any notifications for printers that are no longer in the automatic
+    // class and setup any USB printers we haven't seen yet.
+    PruneRemovedAutomaticPrinters(printers);
+    for (const Printer& printer : printers) {
+      if (!configured_printers_.contains(printer.id()) &&
+          printer.IsUsbProtocol()) {
+        SetupPrinter(printer);
+      }
+    }
     return;
   }
 
-  PruneRemovedPrinters(printers);
-
-  for (const Printer& printer : printers) {
-    if (!printers_.contains(printer.id()) && printer.IsUsbProtocol()) {
-      SetupPrinter(printer);
+  if (printer_class == PrinterClass::kDiscovered) {
+    // Remove any notifications for printers that are no longer in the
+    // discovered class and show a configuration notification for printers we
+    // haven't seen yet
+    PruneRemovedDiscoveredPrinters(printers);
+    for (const Printer& printer : printers) {
+      if (!unconfigured_printers_.contains(printer.id()) &&
+          printer.IsUsbProtocol()) {
+        notification_controller_->ShowConfigurationNotification(printer);
+        DCHECK(!configured_printers_.contains(printer.id()));
+        unconfigured_printers_.insert(printer.id());
+      }
     }
+    return;
   }
 }
 
@@ -89,15 +106,29 @@
   VLOG(1) << "Auto USB Printer setup successful for " << printer.id();
 
   notification_controller_->ShowEphemeralNotification(printer);
-  printers_.insert(printer.id());
+  DCHECK(!unconfigured_printers_.contains(printer.id()));
+  configured_printers_.insert(printer.id());
+}
+
+void AutomaticUsbPrinterConfigurer::PruneRemovedAutomaticPrinters(
+    const std::vector<Printer>& automatic_printers) {
+  PruneRemovedPrinters(automatic_printers, /*use_configured_printers=*/true);
+}
+
+void AutomaticUsbPrinterConfigurer::PruneRemovedDiscoveredPrinters(
+    const std::vector<Printer>& discovered_printers) {
+  PruneRemovedPrinters(discovered_printers, /*use_configured_printers=*/false);
 }
 
 void AutomaticUsbPrinterConfigurer::PruneRemovedPrinters(
-    const std::vector<Printer>& automatic_printers) {
-  for (auto it = printers_.begin(); it != printers_.end();) {
-    if (!IsInAutomaticList(*it, automatic_printers)) {
+    const std::vector<Printer>& current_printers,
+    bool use_configured_printers) {
+  auto& printers =
+      use_configured_printers ? configured_printers_ : unconfigured_printers_;
+  for (auto it = printers.begin(); it != printers.end();) {
+    if (!IsPrinterIdInList(*it, current_printers)) {
       notification_controller_->RemoveNotification(*it);
-      it = printers_.erase(it);
+      it = printers.erase(it);
     } else {
       ++it;
     }
diff --git a/chrome/browser/chromeos/printing/automatic_usb_printer_configurer.h b/chrome/browser/chromeos/printing/automatic_usb_printer_configurer.h
index 4ffd917..2f1bd13 100644
--- a/chrome/browser/chromeos/printing/automatic_usb_printer_configurer.h
+++ b/chrome/browser/chromeos/printing/automatic_usb_printer_configurer.h
@@ -37,8 +37,6 @@
  private:
   FRIEND_TEST_ALL_PREFIXES(AutomaticUsbPrinterConfigurerTest,
                            UsbPrinterAddedToSet);
-  FRIEND_TEST_ALL_PREFIXES(AutomaticUsbPrinterConfigurerTest,
-                           UsbPrinterRemovedFromSet);
 
   // Uses |printer_configurer_| to setup |printer| if it is not yet setup.
   void SetupPrinter(const Printer& printer);
@@ -46,19 +44,33 @@
   // Callback for PrinterConfiguer::SetUpPrinter().
   void OnSetupComplete(const Printer& printer, PrinterSetupResult result);
 
-  // Completes the configuration for |printer|. Saves printer in |printers_|.
+  // Completes the configuration for |printer|. Saves printer in
+  // |configured_printers_|.
   void CompleteConfiguration(const Printer& printer);
 
-  // Removes any printers from |printers_| that are no longer in
+  // Removes any printers from |configured_printers_| that are no longer in
   // |automatic_printers|.
-  void PruneRemovedPrinters(const std::vector<Printer>& automatic_printers);
+  void PruneRemovedAutomaticPrinters(
+      const std::vector<Printer>& automatic_printers);
+
+  // Removes any printers from |unconfigured_printers_| that are no longer in
+  // |discovered_printers|.
+  void PruneRemovedDiscoveredPrinters(
+      const std::vector<Printer>& discovered_printers);
+
+  // Helper function that removes printers that are no longer in
+  // |current_printers|. If |use_configured_printers|, |configured_printers_| is
+  // pruned. Otherwise, |unconfigured_printers_| is pruned.
+  void PruneRemovedPrinters(const std::vector<Printer>& current_printers,
+                            bool use_configured_printers);
 
   SEQUENCE_CHECKER(sequence_);
 
   std::unique_ptr<PrinterConfigurer> printer_configurer_;
   PrinterInstallationManager* installation_manager_;  // Not owned.
   UsbPrinterNotificationController* notification_controller_;  // Not owned.
-  base::flat_set<std::string> printers_;
+  base::flat_set<std::string> configured_printers_;
+  base::flat_set<std::string> unconfigured_printers_;
 
   base::WeakPtrFactory<AutomaticUsbPrinterConfigurer> weak_factory_;
 
diff --git a/chrome/browser/chromeos/printing/automatic_usb_printer_configurer_unittest.cc b/chrome/browser/chromeos/printing/automatic_usb_printer_configurer_unittest.cc
index 4e8dd7a..d34538d 100644
--- a/chrome/browser/chromeos/printing/automatic_usb_printer_configurer_unittest.cc
+++ b/chrome/browser/chromeos/printing/automatic_usb_printer_configurer_unittest.cc
@@ -65,6 +65,10 @@
     RemovePrinter(PrinterClass::kAutomatic, printer_id);
   }
 
+  void RemoveDiscoveredPrinter(const std::string& printer_id) {
+    RemovePrinter(PrinterClass::kDiscovered, printer_id);
+  }
+
  private:
   // Add |printer| to the corresponding list in |printers_| bases on the given
   // |printer_class|.
@@ -76,7 +80,7 @@
   // Remove |printer_id| from |printer_class|.
   void RemovePrinter(PrinterClass printer_class,
                      const std::string& printer_id) {
-    printers_.Remove(PrinterClass::kAutomatic, printer_id);
+    printers_.Remove(printer_class, printer_id);
     observer_->OnPrintersChanged(printer_class, printers_.Get(printer_class));
   }
 
@@ -117,15 +121,15 @@
 
   // Manipulation functions
   bool IsConfigured(const std::string& printer_id) const {
-    return configured_.contains(printer_id);
+    return configured_printers_.contains(printer_id);
   }
 
   void MarkConfigured(const std::string& printer_id) {
-    configured_.insert(printer_id);
+    configured_printers_.insert(printer_id);
   }
 
  private:
-  base::flat_set<std::string> configured_;
+  base::flat_set<std::string> configured_printers_;
 };
 
 class FakeUsbPrinterNotificationController
@@ -143,7 +147,7 @@
   }
 
   void ShowConfigurationNotification(const Printer& printer) override {
-    NOTIMPLEMENTED();
+    open_notifications_.insert(printer.id());
   }
 
   void RemoveNotification(const std::string& printer_id) override {
@@ -237,8 +241,8 @@
   const std::string printer_id = "id";
   const Printer printer = CreateUsbPrinter(printer_id);
 
-  // Adding an automatic USB printer should result in the printer becoming
-  // configured and getting marked as installed.
+  // Adding a discovered USB printer should not result in the printer getting
+  // installed.
   fake_observable_printers_manager_.AddNearbyDiscoveredPrinter(printer);
 
   EXPECT_FALSE(fake_installation_manager_->IsPrinterInstalled(printer));
@@ -260,30 +264,44 @@
 }
 
 TEST_F(AutomaticUsbPrinterConfigurerTest, UsbPrinterAddedToSet) {
-  const std::string printer_id = "id";
-  const Printer printer = CreateUsbPrinter(printer_id);
+  const std::string automatic_printer_id = "auto_id";
+  const Printer automatic_printer = CreateUsbPrinter(automatic_printer_id);
+
+  const std::string discovered_printer_id = "disco_id";
+  const Printer discovered_printer = CreateUsbPrinter(discovered_printer_id);
 
   // Adding an automatic USB printer should result in the printer getting added
-  // to |auto_usb_printer_configurer_::printers_|.
-  fake_observable_printers_manager_.AddNearbyAutomaticPrinter(printer);
+  // to |auto_usb_printer_configurer_::configured_printers_|.
+  fake_observable_printers_manager_.AddNearbyAutomaticPrinter(
+      automatic_printer);
 
-  EXPECT_EQ(1u, auto_usb_printer_configurer_->printers_.size());
-  EXPECT_TRUE(auto_usb_printer_configurer_->printers_.contains(printer_id));
-}
+  EXPECT_TRUE(auto_usb_printer_configurer_->configured_printers_.contains(
+      automatic_printer_id));
 
-TEST_F(AutomaticUsbPrinterConfigurerTest, UsbPrinterRemovedFromSet) {
-  const std::string printer_id = "id";
-  const Printer printer = CreateUsbPrinter(printer_id);
+  // Adding a discovered USB printer should result in the printer getting added
+  // to |auto_usb_printer_configurer_::unconfigured_printers_|.
+  fake_observable_printers_manager_.AddNearbyDiscoveredPrinter(
+      discovered_printer);
 
-  fake_observable_printers_manager_.AddNearbyAutomaticPrinter(printer);
+  EXPECT_TRUE(auto_usb_printer_configurer_->unconfigured_printers_.contains(
+      discovered_printer_id));
 
-  EXPECT_EQ(1u, auto_usb_printer_configurer_->printers_.size());
+  EXPECT_EQ(1u, auto_usb_printer_configurer_->configured_printers_.size());
+  EXPECT_EQ(1u, auto_usb_printer_configurer_->unconfigured_printers_.size());
 
   // Removing the automatic printer should result in the printer getting
-  // removed from |auto_usb_printer_configurer_::printers_|.
-  fake_observable_printers_manager_.RemoveAutomaticPrinter(printer_id);
+  // removed from |auto_usb_printer_configurer_::unconfigured_printers_|.
+  fake_observable_printers_manager_.RemoveDiscoveredPrinter(
+      discovered_printer_id);
 
-  EXPECT_EQ(0u, auto_usb_printer_configurer_->printers_.size());
+  EXPECT_EQ(0u, auto_usb_printer_configurer_->unconfigured_printers_.size());
+
+  // Removing the automatic printer should result in the printer getting
+  // removed from |auto_usb_printer_configurer_::configured_printers_|.
+  fake_observable_printers_manager_.RemoveAutomaticPrinter(
+      automatic_printer_id);
+
+  EXPECT_EQ(0u, auto_usb_printer_configurer_->configured_printers_.size());
 }
 
 TEST_F(AutomaticUsbPrinterConfigurerTest, NotificationOpenedForNewAutomatic) {
@@ -323,4 +341,13 @@
   EXPECT_FALSE(fake_notification_controller_->IsNotification(printer_id));
 }
 
+TEST_F(AutomaticUsbPrinterConfigurerTest, NotificationOpenedForNewDiscovered) {
+  const std::string printer_id = "id";
+  const Printer printer = CreateUsbPrinter(printer_id);
+
+  fake_observable_printers_manager_.AddNearbyAutomaticPrinter(printer);
+
+  EXPECT_TRUE(fake_notification_controller_->IsNotification(printer_id));
+}
+
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/printing/cups_printers_manager_unittest.cc b/chrome/browser/chromeos/printing/cups_printers_manager_unittest.cc
index 6175e63..4b65ae9b 100644
--- a/chrome/browser/chromeos/printing/cups_printers_manager_unittest.cc
+++ b/chrome/browser/chromeos/printing/cups_printers_manager_unittest.cc
@@ -307,20 +307,31 @@
     NOTIMPLEMENTED();
   }
   void ShowConfigurationNotification(const Printer& printer) override {
-    NOTIMPLEMENTED();
+    configuration_notifications_.insert(printer.id());
   }
   void ShowSavedNotification(const Printer& printer) override {
-    open_notifications_.insert(printer.id());
+    saved_notifications_.insert(printer.id());
   }
   void RemoveNotification(const std::string& printer_id) override {
-    open_notifications_.erase(printer_id);
+    saved_notifications_.erase(printer_id);
+    configuration_notifications_.erase(printer_id);
   }
   bool IsNotification(const std::string& printer_id) const override {
-    return open_notifications_.contains(printer_id);
+    return configuration_notifications_.contains(printer_id) ||
+           saved_notifications_.contains(printer_id);
+  }
+
+  bool IsSavedNotification(const std::string& printer_id) const {
+    return saved_notifications_.contains(printer_id);
+  }
+
+  bool IsConfigurationNotification(const std::string& printer_id) const {
+    return configuration_notifications_.contains(printer_id);
   }
 
  private:
-  base::flat_set<std::string> open_notifications_;
+  base::flat_set<std::string> saved_notifications_;
+  base::flat_set<std::string> configuration_notifications_;
 };
 
 class CupsPrintersManagerTest : public testing::Test,
@@ -835,5 +846,32 @@
   EXPECT_FALSE(printer_configurer_->IsConfigured("Automatic"));
 }
 
+TEST_F(CupsPrintersManagerTest, DetectedUsbPrinterConfigurationNotification) {
+  auto discovered_printer = MakeDiscoveredPrinter("Discovered");
+  discovered_printer.printer.set_uri("usb:");
+
+  usb_detector_->AddDetections({discovered_printer});
+  scoped_task_environment_.RunUntilIdle();
+
+  EXPECT_TRUE(usb_notif_controller_->IsConfigurationNotification("Discovered"));
+
+  usb_detector_->RemoveDetections({"Discovered"});
+
+  EXPECT_FALSE(
+      usb_notif_controller_->IsConfigurationNotification("Discovered"));
+}
+
+TEST_F(CupsPrintersManagerTest,
+       DetectedZeroconfDiscoveredPrinterNoNotification) {
+  auto discovered_printer = MakeDiscoveredPrinter("Discovered");
+  discovered_printer.printer.set_uri("ipp:");
+
+  zeroconf_detector_->AddDetections({discovered_printer});
+  scoped_task_environment_.RunUntilIdle();
+
+  EXPECT_FALSE(
+      usb_notif_controller_->IsConfigurationNotification("Discovered"));
+}
+
 }  // namespace
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/printing/doc/cups_printers_settings_page.md b/chrome/browser/chromeos/printing/doc/cups_printers_settings_page.md
index 5ce556b..78100ce 100644
--- a/chrome/browser/chromeos/printing/doc/cups_printers_settings_page.md
+++ b/chrome/browser/chromeos/printing/doc/cups_printers_settings_page.md
@@ -100,6 +100,7 @@
 `getPrinterPpdManufacturerAndModel` | `HandleGetPrinterPpdManufacturerAndModel`
 `addDiscoveredPrinter`              | `HandleAddDiscoveredPrinter`
 `cancelPrinterSetup`                | `HandleSetUpCancel`
+`getEulaUrl`                        | `HandleGetEulaUrl`
 
 ## Javascript Listeners
 
diff --git a/chrome/browser/devtools/devtools_file_helper.cc b/chrome/browser/devtools/devtools_file_helper.cc
index 7497a31..b9a627c 100644
--- a/chrome/browser/devtools/devtools_file_helper.cc
+++ b/chrome/browser/devtools/devtools_file_helper.cc
@@ -145,24 +145,25 @@
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   CHECK(web_contents->GetURL().SchemeIs(content::kChromeDevToolsScheme));
   std::string root_name(kRootName);
-  std::string file_system_id = isolated_context()->RegisterFileSystemForPath(
-      storage::kFileSystemTypeNativeLocal, std::string(), path, &root_name);
+  storage::IsolatedContext::ScopedFSHandle file_system =
+      isolated_context()->RegisterFileSystemForPath(
+          storage::kFileSystemTypeNativeLocal, std::string(), path, &root_name);
 
   content::ChildProcessSecurityPolicy* policy =
       content::ChildProcessSecurityPolicy::GetInstance();
   RenderViewHost* render_view_host = web_contents->GetRenderViewHost();
   int renderer_id = render_view_host->GetProcess()->GetID();
-  policy->GrantReadFileSystem(renderer_id, file_system_id);
-  policy->GrantWriteFileSystem(renderer_id, file_system_id);
-  policy->GrantCreateFileForFileSystem(renderer_id, file_system_id);
-  policy->GrantDeleteFromFileSystem(renderer_id, file_system_id);
+  policy->GrantReadFileSystem(renderer_id, file_system.id());
+  policy->GrantWriteFileSystem(renderer_id, file_system.id());
+  policy->GrantCreateFileForFileSystem(renderer_id, file_system.id());
+  policy->GrantDeleteFromFileSystem(renderer_id, file_system.id());
 
   // We only need file level access for reading FileEntries. Saving FileEntries
   // just needs the file system to have read/write access, which is granted
   // above if required.
   if (!policy->CanReadFile(renderer_id, path))
     policy->GrantReadFile(renderer_id, path);
-  return file_system_id;
+  return file_system.id();
 }
 
 DevToolsFileHelper::FileSystem CreateFileSystemStruct(
diff --git a/chrome/browser/extensions/api/file_system/chrome_file_system_delegate.cc b/chrome/browser/extensions/api/file_system/chrome_file_system_delegate.cc
index 8f148a77..b440c80 100644
--- a/chrome/browser/extensions/api/file_system/chrome_file_system_delegate.cc
+++ b/chrome/browser/extensions/api/file_system/chrome_file_system_delegate.cc
@@ -167,12 +167,12 @@
   // Set a fixed register name, as the automatic one would leak the mount point
   // directory.
   std::string register_name = "fs";
-  const std::string file_system_id =
+  const storage::IsolatedContext::ScopedFSHandle file_system =
       isolated_context->RegisterFileSystemForPath(
           storage::kFileSystemTypeNativeForPlatformApp,
           std::string() /* file_system_id */, original_url.path(),
           &register_name);
-  if (file_system_id.empty()) {
+  if (!file_system.is_valid()) {
     error_callback.Run(kSecurityError);
     return;
   }
@@ -187,18 +187,18 @@
   const auto process_id = requester->source_process_id();
   // Read-only permisisons.
   policy->GrantReadFile(process_id, volume->mount_path());
-  policy->GrantReadFileSystem(process_id, file_system_id);
+  policy->GrantReadFileSystem(process_id, file_system.id());
 
   // Additional write permissions.
   if (writable) {
     policy->GrantCreateReadWriteFile(process_id, volume->mount_path());
     policy->GrantCopyInto(process_id, volume->mount_path());
-    policy->GrantWriteFileSystem(process_id, file_system_id);
-    policy->GrantDeleteFromFileSystem(process_id, file_system_id);
-    policy->GrantCreateFileForFileSystem(process_id, file_system_id);
+    policy->GrantWriteFileSystem(process_id, file_system.id());
+    policy->GrantDeleteFromFileSystem(process_id, file_system.id());
+    policy->GrantCreateFileForFileSystem(process_id, file_system.id());
   }
 
-  success_callback.Run(file_system_id, register_name);
+  success_callback.Run(file_system.id(), register_name);
 }
 
 }  // namespace
diff --git a/chrome/browser/media/webrtc/webrtc_logging_handler_host.cc b/chrome/browser/media/webrtc/webrtc_logging_handler_host.cc
index d0bfe10..9788a4a 100644
--- a/chrome/browser/media/webrtc/webrtc_logging_handler_host.cc
+++ b/chrome/browser/media/webrtc/webrtc_logging_handler_host.cc
@@ -312,20 +312,21 @@
   DCHECK(isolated_context);
 
   std::string registered_name;
-  std::string filesystem_id = isolated_context->RegisterFileSystemForPath(
-      storage::kFileSystemTypeNativeLocal, std::string(), logs_path,
-      &registered_name);
+  storage::IsolatedContext::ScopedFSHandle file_system =
+      isolated_context->RegisterFileSystemForPath(
+          storage::kFileSystemTypeNativeLocal, std::string(), logs_path,
+          &registered_name);
 
   // Only granting read and delete access to reduce contention with
   // webrtcLogging APIs that modify files in that folder.
   content::ChildProcessSecurityPolicy* policy =
       content::ChildProcessSecurityPolicy::GetInstance();
-  policy->GrantReadFileSystem(render_process_id_, filesystem_id);
+  policy->GrantReadFileSystem(render_process_id_, file_system.id());
   // Delete is needed to prevent accumulation of files.
-  policy->GrantDeleteFromFileSystem(render_process_id_, filesystem_id);
+  policy->GrantDeleteFromFileSystem(render_process_id_, file_system.id());
   base::PostTaskWithTraits(
       FROM_HERE, {BrowserThread::UI},
-      base::BindOnce(callback, filesystem_id, registered_name));
+      base::BindOnce(callback, file_system.id(), registered_name));
 }
 #endif  // defined(OS_LINUX) || defined(OS_CHROMEOS)
 
diff --git a/chrome/browser/media_galleries/fileapi/media_file_validator_browsertest.cc b/chrome/browser/media_galleries/fileapi/media_file_validator_browsertest.cc
index f1e7f00..dd6919e 100644
--- a/chrome/browser/media_galleries/fileapi/media_file_validator_browsertest.cc
+++ b/chrome/browser/media_galleries/fileapi/media_file_validator_browsertest.cc
@@ -153,18 +153,16 @@
 
     base::FilePath dest_path = base.AppendASCII("dest_fs");
     ASSERT_TRUE(base::CreateDirectory(dest_path));
-    std::string dest_fsid =
+    dest_fs_ =
         storage::IsolatedContext::GetInstance()->RegisterFileSystemForPath(
-            storage::kFileSystemTypeNativeMedia,
-            std::string(),
-            dest_path,
+            storage::kFileSystemTypeNativeMedia, std::string(), dest_path,
             NULL);
 
     size_t extension_index = filename.find_last_of(".");
     ASSERT_NE(std::string::npos, extension_index);
     std::string extension = filename.substr(extension_index);
     std::string dest_root_fs_url = storage::GetIsolatedFileSystemRootURIString(
-        GURL(kOrigin), dest_fsid, "dest_fs/");
+        GURL(kOrigin), dest_fs_.id(), "dest_fs/");
     move_dest_ = file_system_context_->CrackURL(GURL(
           dest_root_fs_url + "move_dest" + extension));
 
@@ -262,6 +260,7 @@
 
   storage::FileSystemURL move_src_;
   storage::FileSystemURL move_dest_;
+  storage::IsolatedContext::ScopedFSHandle dest_fs_;
 
   base::OnceClosure quit_closure_;
   scoped_refptr<base::SequencedTaskRunner> file_system_runner_;
diff --git a/chrome/browser/media_galleries/fileapi/native_media_file_util_unittest.cc b/chrome/browser/media_galleries/fileapi/native_media_file_util_unittest.cc
index 751cadb8..dd621fd 100644
--- a/chrome/browser/media_galleries/fileapi/native_media_file_util_unittest.cc
+++ b/chrome/browser/media_galleries/fileapi/native_media_file_util_unittest.cc
@@ -54,23 +54,23 @@
 };
 
 const FilteringTestCase kFilteringTestCases[] = {
-  // Directory should always be visible.
-  { FPL("hoge"), true, true, false, NULL },
-  { FPL("fuga.jpg"), true, true, false, NULL },
-  { FPL("piyo.txt"), true, true, false, NULL },
-  { FPL("moga.cod"), true, true, false, NULL },
+    // Directory should always be visible.
+    {FPL("hoge"), true, true, false, NULL},
+    {FPL("fuga.jpg"), true, true, false, NULL},
+    {FPL("piyo.txt"), true, true, false, NULL},
+    {FPL("moga.cod"), true, true, false, NULL},
 
-  // File should be visible if it's a supported media file.
-  // File without extension.
-  { FPL("foo"), false, false, false, "abc" },
-  // Supported media file.
-  { FPL("bar.jpg"), false, true, true, "\xFF\xD8\xFF" },
-  // Unsupported masquerading file.
-  { FPL("sna.jpg"), false, true, false, "abc" },
-  // Non-media file.
-  { FPL("baz.txt"), false, false, false, "abc" },
-  // Unsupported media file.
-  { FPL("foobar.cod"), false, false, false, "abc" },
+    // File should be visible if it's a supported media file.
+    // File without extension.
+    {FPL("foo"), false, false, false, "abc"},
+    // Supported media file.
+    {FPL("bar.jpg"), false, true, true, "\xFF\xD8\xFF"},
+    // Unsupported masquerading file.
+    {FPL("sna.jpg"), false, true, false, "abc"},
+    // Non-media file.
+    {FPL("baz.txt"), false, false, false, "abc"},
+    // Unsupported media file.
+    {FPL("foobar.cod"), false, false, false, "abc"},
 };
 
 void ExpectEqHelper(const std::string& test_name,
@@ -143,14 +143,12 @@
         std::vector<storage::URLRequestAutoMountHandler>(), data_dir_.GetPath(),
         content::CreateAllowFileAccessOptions());
 
-    filesystem_id_ = isolated_context()->RegisterFileSystemForPath(
+    filesystem_ = isolated_context()->RegisterFileSystemForPath(
         storage::kFileSystemTypeNativeMedia, std::string(), root_path(), NULL);
-
-    isolated_context()->AddReference(filesystem_id_);
+    filesystem_id_ = filesystem_.id();
   }
 
   void TearDown() override {
-    isolated_context()->RemoveReference(filesystem_id_);
     file_system_context_ = NULL;
   }
 
@@ -161,8 +159,7 @@
 
   FileSystemURL CreateURL(const base::FilePath::CharType* test_case_path) {
     return file_system_context_->CreateCrackedFileSystemURL(
-        origin(),
-        storage::kFileSystemTypeIsolated,
+        origin(), storage::kFileSystemTypeIsolated,
         GetVirtualPath(test_case_path));
   }
 
@@ -176,14 +173,12 @@
 
   base::FilePath GetVirtualPath(
       const base::FilePath::CharType* test_case_path) {
-    return base::FilePath::FromUTF8Unsafe(filesystem_id_).
-               Append(FPL("Media Directory")).
-               Append(base::FilePath(test_case_path));
+    return base::FilePath::FromUTF8Unsafe(filesystem_id_)
+        .Append(FPL("Media Directory"))
+        .Append(base::FilePath(test_case_path));
   }
 
-  GURL origin() {
-    return GURL("http://example.com");
-  }
+  GURL origin() { return GURL("http://example.com"); }
 
   storage::FileSystemType type() { return storage::kFileSystemTypeNativeMedia; }
 
@@ -198,6 +193,7 @@
   scoped_refptr<storage::FileSystemContext> file_system_context_;
 
   std::string filesystem_id_;
+  storage::IsolatedContext::ScopedFSHandle filesystem_;
 
   DISALLOW_COPY_AND_ASSIGN(NativeMediaFileUtilTest);
 };
@@ -209,10 +205,9 @@
   for (size_t i = 0; i < base::size(kFilteringTestCases); ++i) {
     FileSystemURL url = CreateURL(kFilteringTestCases[i].path);
 
-    base::File::Error expectation =
-        kFilteringTestCases[i].visible ?
-        base::File::FILE_OK :
-        base::File::FILE_ERROR_NOT_FOUND;
+    base::File::Error expectation = kFilteringTestCases[i].visible
+                                        ? base::File::FILE_OK
+                                        : base::File::FILE_ERROR_NOT_FOUND;
 
     std::string test_name =
         base::StringPrintf("DirectoryExistsAndFileExistsFiltering %" PRIuS, i);
@@ -260,10 +255,9 @@
         std::string test_name = base::StringPrintf(
             "CreateFileAndCreateDirectoryFiltering run %d, test %" PRIuS,
             loop_count, i);
-        base::File::Error expectation =
-            kFilteringTestCases[i].visible ?
-            base::File::FILE_OK :
-            base::File::FILE_ERROR_SECURITY;
+        base::File::Error expectation = kFilteringTestCases[i].visible
+                                            ? base::File::FILE_OK
+                                            : base::File::FILE_ERROR_SECURITY;
         operation_runner()->CreateDirectory(
             url, false, false,
             base::Bind(&ExpectEqHelper, test_name, expectation));
@@ -349,10 +343,9 @@
         // handled above.
         // If the destination path does not exist and is not visible, then
         // creating it would be a security violation.
-        expectation =
-            kFilteringTestCases[i].visible ?
-            base::File::FILE_OK :
-            base::File::FILE_ERROR_SECURITY;
+        expectation = kFilteringTestCases[i].visible
+                          ? base::File::FILE_OK
+                          : base::File::FILE_ERROR_SECURITY;
       } else {
         if (!kFilteringTestCases[i].visible) {
           // If the destination path exist and is not visible, then to the copy
@@ -407,9 +400,7 @@
         expectation = base::File::FILE_ERROR_INVALID_OPERATION;
       }
       operation_runner()->Move(
-          url,
-          dest_url,
-          storage::FileSystemOperation::OPTION_NONE,
+          url, dest_url, storage::FileSystemOperation::OPTION_NONE,
           base::Bind(&ExpectEqHelper, test_name, expectation));
       content::RunAllTasksUntilIdle();
     }
@@ -454,10 +445,9 @@
         // handled above.
         // If the destination path does not exist and is not visible, then
         // creating it would be a security violation.
-        expectation =
-            kFilteringTestCases[i].visible ?
-            base::File::FILE_OK :
-            base::File::FILE_ERROR_SECURITY;
+        expectation = kFilteringTestCases[i].visible
+                          ? base::File::FILE_OK
+                          : base::File::FILE_ERROR_SECURITY;
       } else {
         if (!kFilteringTestCases[i].visible) {
           // If the destination path exist and is not visible, then to the move
@@ -473,9 +463,7 @@
         }
       }
       operation_runner()->Move(
-          src_url,
-          url,
-          storage::FileSystemOperation::OPTION_NONE,
+          src_url, url, storage::FileSystemOperation::OPTION_NONE,
           base::Bind(&ExpectEqHelper, test_name, expectation));
       content::RunAllTasksUntilIdle();
     }
@@ -560,8 +548,8 @@
     else
       expected_error = base::File::FILE_ERROR_SECURITY;
     error = base::File::FILE_ERROR_FAILED;
-    operation_runner()->CreateSnapshotFile(url,
-        base::Bind(CreateSnapshotCallback, &error));
+    operation_runner()->CreateSnapshotFile(
+        url, base::Bind(CreateSnapshotCallback, &error));
     content::RunAllTasksUntilIdle();
     ASSERT_EQ(expected_error, error);
   }
diff --git a/chrome/browser/notifications/scheduler/notification_scheduler.cc b/chrome/browser/notifications/scheduler/notification_scheduler.cc
index 8985f92..f1375a7 100644
--- a/chrome/browser/notifications/scheduler/notification_scheduler.cc
+++ b/chrome/browser/notifications/scheduler/notification_scheduler.cc
@@ -37,16 +37,19 @@
   // Initializes subsystems in notification scheduler, |callback| will be
   // invoked if all initializations finished or anyone of them failed. The
   // object should be destroyed along with the |callback|.
-  void Init(NotificationSchedulerContext* context, InitCallback callback) {
+  void Init(NotificationSchedulerContext* context,
+            ScheduledNotificationManager::Delegate* delegate,
+            InitCallback callback) {
     callback_ = std::move(callback);
     context->icon_store()->Init(base::BindOnce(
         &InitHelper::OnIconStoreInitialized, weak_ptr_factory_.GetWeakPtr()));
     context->impression_tracker()->Init(
         base::BindOnce(&InitHelper::OnImpressionTrackerInitialized,
                        weak_ptr_factory_.GetWeakPtr()));
+
     context->notification_manager()->Init(
-        base::BindOnce(&InitHelper::OnNotificationManagerInitialized,
-                       weak_ptr_factory_.GetWeakPtr()));
+        delegate, base::BindOnce(&InitHelper::OnNotificationManagerInitialized,
+                                 weak_ptr_factory_.GetWeakPtr()));
   }
 
  private:
@@ -106,7 +109,7 @@
     auto helper = std::make_unique<InitHelper>();
     auto* helper_ptr = helper.get();
     helper_ptr->Init(
-        context_.get(),
+        context_.get(), this,
         base::BindOnce(&NotificationSchedulerImpl::OnInitialized,
                        weak_ptr_factory_.GetWeakPtr(), std::move(helper),
                        std::move(init_callback)));
diff --git a/chrome/browser/notifications/scheduler/schedule_service_factory_helper.cc b/chrome/browser/notifications/scheduler/schedule_service_factory_helper.cc
index af11260..abe5f4b 100644
--- a/chrome/browser/notifications/scheduler/schedule_service_factory_helper.cc
+++ b/chrome/browser/notifications/scheduler/schedule_service_factory_helper.cc
@@ -16,6 +16,7 @@
 #include "chrome/browser/notifications/scheduler/notification_background_task_scheduler.h"
 #include "chrome/browser/notifications/scheduler/notification_schedule_service_impl.h"
 #include "chrome/browser/notifications/scheduler/notification_scheduler_context.h"
+#include "chrome/browser/notifications/scheduler/notification_store.h"
 #include "chrome/browser/notifications/scheduler/scheduled_notification_manager.h"
 #include "chrome/browser/notifications/scheduler/scheduler_config.h"
 #include "components/leveldb_proto/public/proto_database_provider.h"
@@ -26,6 +27,8 @@
 const base::FilePath::CharType kImpressionDBName[] =
     FILE_PATH_LITERAL("ImpressionDB");
 const base::FilePath::CharType kIconDBName[] = FILE_PATH_LITERAL("IconDB");
+const base::FilePath::CharType kNotificationDBName[] =
+    FILE_PATH_LITERAL("NotificationDB");
 }  // namespace
 
 KeyedService* CreateNotificationScheduleService(
@@ -54,13 +57,24 @@
   auto impression_tracker = std::make_unique<ImpressionHistoryTrackerImpl>(
       *config.get(), std::move(impression_store));
 
-  // TODO(xingliu): Build notification store.
+  // Build notification store.
+  base::FilePath notification_store_dir =
+      storage_dir.Append(kNotificationDBName);
+  auto notification_db = db_provider->GetDB<proto::NotificationEntry,
+                                            notifications::NotificationEntry>(
+      leveldb_proto::ProtoDbType::NOTIFICATION_SCHEDULER_NOTIFICATION_STORE,
+      notification_store_dir, task_runner);
+  auto notification_store =
+      std::make_unique<NotificationStore>(std::move(notification_db));
+
   std::unique_ptr<ScheduledNotificationManager> notification_manager;
+  notification_manager->Create(std::move(notification_store));
 
   auto context = std::make_unique<NotificationSchedulerContext>(
       std::move(background_task_scheduler), std::move(icon_store),
       std::move(impression_tracker), std::move(notification_manager),
       DisplayDecider::Create(), std::move(config));
+
   auto scheduler = NotificationScheduler::Create(std::move(context));
   auto init_aware_scheduler =
       std::make_unique<InitAwareNotificationScheduler>(std::move(scheduler));
diff --git a/chrome/browser/notifications/scheduler/scheduled_notification_manager.cc b/chrome/browser/notifications/scheduler/scheduled_notification_manager.cc
index 3b1badb..cd3bdf0 100644
--- a/chrome/browser/notifications/scheduler/scheduled_notification_manager.cc
+++ b/chrome/browser/notifications/scheduler/scheduled_notification_manager.cc
@@ -27,13 +27,13 @@
  public:
   using Store = std::unique_ptr<CollectionStore<NotificationEntry>>;
 
-  ScheduledNotificationManagerImpl(Store store, Delegate* delegate)
-      : store_(std::move(store)),
-        delegate_(delegate),
-        weak_ptr_factory_(this) {}
+  ScheduledNotificationManagerImpl(Store store)
+      : store_(std::move(store)), delegate_(nullptr), weak_ptr_factory_(this) {}
 
  private:
-  void Init(InitCallback callback) override {
+  void Init(Delegate* delegate, InitCallback callback) override {
+    DCHECK(!delegate_);
+    delegate_ = delegate;
     store_->InitAndLoad(
         base::BindOnce(&ScheduledNotificationManagerImpl::OnStoreInitialized,
                        weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
@@ -127,10 +127,8 @@
 // static
 std::unique_ptr<ScheduledNotificationManager>
 ScheduledNotificationManager::Create(
-    std::unique_ptr<CollectionStore<NotificationEntry>> store,
-    Delegate* delegate) {
-  return std::make_unique<ScheduledNotificationManagerImpl>(std::move(store),
-                                                            delegate);
+    std::unique_ptr<CollectionStore<NotificationEntry>> store) {
+  return std::make_unique<ScheduledNotificationManagerImpl>(std::move(store));
 }
 
 ScheduledNotificationManager::ScheduledNotificationManager() = default;
diff --git a/chrome/browser/notifications/scheduler/scheduled_notification_manager.h b/chrome/browser/notifications/scheduler/scheduled_notification_manager.h
index 01293a5..00d3716 100644
--- a/chrome/browser/notifications/scheduler/scheduled_notification_manager.h
+++ b/chrome/browser/notifications/scheduler/scheduled_notification_manager.h
@@ -42,11 +42,10 @@
 
   // Creates the instance.
   static std::unique_ptr<ScheduledNotificationManager> Create(
-      std::unique_ptr<CollectionStore<NotificationEntry>> store,
-      Delegate* delegate);
+      std::unique_ptr<CollectionStore<NotificationEntry>> store);
 
   // Initializes the notification store.
-  virtual void Init(InitCallback callback) = 0;
+  virtual void Init(Delegate* delegate, InitCallback callback) = 0;
 
   // Adds a new notification.
   virtual void ScheduleNotification(
diff --git a/chrome/browser/notifications/scheduler/scheduled_notification_manager_unittest.cc b/chrome/browser/notifications/scheduler/scheduled_notification_manager_unittest.cc
index 43d350a..0a6de34 100644
--- a/chrome/browser/notifications/scheduler/scheduled_notification_manager_unittest.cc
+++ b/chrome/browser/notifications/scheduler/scheduled_notification_manager_unittest.cc
@@ -67,8 +67,7 @@
     delegate_ = std::make_unique<MockDelegate>();
     auto store = std::make_unique<MockNotificationStore>();
     store_ = store.get();
-    manager_ =
-        ScheduledNotificationManager::Create(std::move(store), delegate_.get());
+    manager_ = ScheduledNotificationManager::Create(std::move(store));
   }
 
  protected:
@@ -93,12 +92,13 @@
               std::move(cb).Run(true, std::move(entries));
             }));
     base::RunLoop loop;
-    manager()->Init(base::BindOnce(
-        [](base::RepeatingClosure closure, bool success) {
-          EXPECT_TRUE(success);
-          std::move(closure).Run();
-        },
-        loop.QuitClosure()));
+    manager()->Init(delegate(),
+                    base::BindOnce(
+                        [](base::RepeatingClosure closure, bool success) {
+                          EXPECT_TRUE(success);
+                          std::move(closure).Run();
+                        },
+                        loop.QuitClosure()));
     loop.Run();
   }
 
@@ -118,13 +118,14 @@
       }));
 
   base::RunLoop loop;
-  manager()->Init(base::BindOnce(
-      [](base::RepeatingClosure closure, bool success) {
-        // Expected to receive error.
-        EXPECT_FALSE(success);
-        std::move(closure).Run();
-      },
-      loop.QuitClosure()));
+  manager()->Init(delegate(),
+                  base::BindOnce(
+                      [](base::RepeatingClosure closure, bool success) {
+                        // Expected to receive error.
+                        EXPECT_FALSE(success);
+                        std::move(closure).Run();
+                      },
+                      loop.QuitClosure()));
   loop.Run();
 }
 
diff --git a/chrome/browser/renderer_host/pepper/pepper_isolated_file_system_message_filter.cc b/chrome/browser/renderer_host/pepper/pepper_isolated_file_system_message_filter.cc
index 5e468b2..0d083bb 100644
--- a/chrome/browser/renderer_host/pepper/pepper_isolated_file_system_message_filter.cc
+++ b/chrome/browser/renderer_host/pepper/pepper_isolated_file_system_message_filter.cc
@@ -102,14 +102,14 @@
   return profile_manager->GetProfile(profile_directory_);
 }
 
-std::string PepperIsolatedFileSystemMessageFilter::CreateCrxFileSystem(
-    Profile* profile) {
+storage::IsolatedContext::ScopedFSHandle
+PepperIsolatedFileSystemMessageFilter::CreateCrxFileSystem(Profile* profile) {
 #if BUILDFLAG(ENABLE_EXTENSIONS)
   const extensions::Extension* extension =
       extensions::ExtensionRegistry::Get(profile)->enabled_extensions().GetByID(
           document_url_.host());
   if (!extension)
-    return std::string();
+    return storage::IsolatedContext::ScopedFSHandle();
 
   // First level directory for isolated filesystem to lookup.
   std::string kFirstLevelDirectory("crxfs");
@@ -119,7 +119,7 @@
       extension->path(),
       &kFirstLevelDirectory);
 #else
-  return std::string();
+  return storage::IsolatedContext::ScopedFSHandle();
 #endif
 }
 
@@ -160,8 +160,9 @@
   // TODO(raymes): When we remove FileSystem from the renderer, we should create
   // a pending PepperFileSystemBrowserHost here with the fsid and send the
   // pending host ID back to the plugin.
-  const std::string fsid = CreateCrxFileSystem(profile);
-  if (fsid.empty()) {
+  const storage::IsolatedContext::ScopedFSHandle fs =
+      CreateCrxFileSystem(profile);
+  if (!fs.is_valid()) {
     context->reply_msg =
         PpapiPluginMsg_IsolatedFileSystem_BrowserOpenReply(std::string());
     return PP_ERROR_NOTSUPPORTED;
@@ -170,9 +171,10 @@
   // Grant readonly access of isolated filesystem to renderer process.
   content::ChildProcessSecurityPolicy* policy =
       content::ChildProcessSecurityPolicy::GetInstance();
-  policy->GrantReadFileSystem(render_process_id_, fsid);
+  policy->GrantReadFileSystem(render_process_id_, fs.id());
 
-  context->reply_msg = PpapiPluginMsg_IsolatedFileSystem_BrowserOpenReply(fsid);
+  context->reply_msg =
+      PpapiPluginMsg_IsolatedFileSystem_BrowserOpenReply(fs.id());
   return PP_OK;
 #else
   return PP_ERROR_NOTSUPPORTED;
diff --git a/chrome/browser/renderer_host/pepper/pepper_isolated_file_system_message_filter.h b/chrome/browser/renderer_host/pepper/pepper_isolated_file_system_message_filter.h
index ca7f87b..1d5ff1f 100644
--- a/chrome/browser/renderer_host/pepper/pepper_isolated_file_system_message_filter.h
+++ b/chrome/browser/renderer_host/pepper/pepper_isolated_file_system_message_filter.h
@@ -17,6 +17,7 @@
 #include "ppapi/c/private/ppb_isolated_file_system_private.h"
 #include "ppapi/host/resource_host.h"
 #include "ppapi/host/resource_message_filter.h"
+#include "storage/browser/fileapi/isolated_context.h"
 #include "url/gurl.h"
 
 class Profile;
@@ -58,7 +59,8 @@
   // Returns filesystem id of isolated filesystem if valid, or empty string
   // otherwise.  This must run on the UI thread because ProfileManager only
   // allows access on that thread.
-  std::string CreateCrxFileSystem(Profile* profile);
+  storage::IsolatedContext::ScopedFSHandle CreateCrxFileSystem(
+      Profile* profile);
 
   int32_t OnOpenFileSystem(ppapi::host::HostMessageContext* context,
                            PP_IsolatedFileSystemType_Private type);
diff --git a/chrome/browser/resources/chromeos/camera/src/js/views/settings.js b/chrome/browser/resources/chromeos/camera/src/js/views/settings.js
index 641fad3..1c1c916 100644
--- a/chrome/browser/resources/chromeos/camera/src/js/views/settings.js
+++ b/chrome/browser/resources/chromeos/camera/src/js/views/settings.js
@@ -307,13 +307,33 @@
 
   // Update external camera settings
   this.externalSettings_ = externalSettings;
-  this.resMenu_.querySelectorAll('.menu-item.external-camera')
-      .forEach((element) => element.parentNode.removeChild(element));
 
-  externalSettings.forEach(([deviceId, photoRs, videoRs]) => {
+  // To prevent losing focus on item already exist before update, locate
+  // focused item in both previous and current list, pop out all items in
+  // previous list except those having same deviceId as focused one and
+  // recreate all other items from current list.
+  const prevFocus =
+      this.resMenu_.querySelector('.menu-item.external-camera:focus');
+  const prevFId = prevFocus && prevFocus.dataset.deviceId;
+  const focusIdx = externalSettings.findIndex(([id]) => id === prevFId);
+  const fTitle =
+      this.resMenu_.querySelector(`.menu-item[data-device-id="${prevFId}"]`);
+  const focusedId = focusIdx === -1 ? null : prevFId;
+
+  this.resMenu_.querySelectorAll('.menu-item.external-camera')
+      .forEach(
+          (element) => element.dataset.deviceId !== focusedId &&
+              element.parentNode.removeChild(element));
+
+  externalSettings.forEach(([deviceId, photoRs, videoRs], index) => {
+    if (deviceId === focusedId) {
+      return;
+    }
     const extItem = document.importNode(this.extcamItemTempl_.content, true);
-    const [photoItem, videoItem] = extItem.querySelectorAll('.resol-item');
-    photoItem.dataset.deviceId = videoItem.dataset.deviceId = deviceId;
+    const [titleItem, photoItem, videoItem] =
+        extItem.querySelectorAll('.menu-item');
+    titleItem.dataset.deviceId = photoItem.dataset.deviceId =
+        videoItem.dataset.deviceId = deviceId;
     checkMulti(photoItem, photoRs, this.photoOptTextTempl_);
     checkMulti(videoItem, videoRs, this.videoOptTextTempl_);
 
@@ -327,7 +347,11 @@
         this.openVideoResSettings_(deviceId, videoRs, videoItem);
       }
     });
-    this.resMenu_.appendChild(extItem);
+    if (index < focusIdx) {
+      this.resMenu_.insertBefore(extItem, fTitle);
+    } else {
+      this.resMenu_.appendChild(extItem);
+    }
   });
 };
 
diff --git a/chrome/browser/resources/print_preview/data/destination_store.js b/chrome/browser/resources/print_preview/data/destination_store.js
index 097a0cb8..8f72f8cd 100644
--- a/chrome/browser/resources/print_preview/data/destination_store.js
+++ b/chrome/browser/resources/print_preview/data/destination_store.js
@@ -916,27 +916,27 @@
         return true;
       }
 
-      return this.fetchPreselectedDestination_(
-          assert(recentDestinations.find(d => {
-            return print_preview.createRecentDestinationKey(d) === key;
-          })));
-    }
+      const recent = recentDestinations.find(d => {
+        return print_preview.createRecentDestinationKey(d) === key;
+      });
+      if (recent) {
+        return this.fetchPreselectedDestination_(recent);
+      }
 
-    /** Pre-fetches the Google Drive destination for the current active user. */
-    startLoadDriveDestination() {
+      // Should be fetching the Google Drive destination.
       const driveKey = print_preview.createDestinationKey(
           print_preview.Destination.GooglePromotedId.DOCS,
           print_preview.DestinationOrigin.COOKIES, this.activeUser_);
-      if (!this.cloudPrintInterface_ || this.destinationMap_.get(driveKey) ||
-          this.inFlightCloudPrintRequests_.has(driveKey)) {
-        // Already loaded or loading, or no cloud print interface.
-        return;
-      }
-
-      this.inFlightCloudPrintRequests_.add(driveKey);
-      this.cloudPrintInterface_.printer(
-          print_preview.Destination.GooglePromotedId.DOCS,
-          print_preview.DestinationOrigin.COOKIES, this.activeUser_);
+      assert(key === driveKey);
+      return this.fetchPreselectedDestination_({
+        id: print_preview.Destination.GooglePromotedId.DOCS,
+        origin: print_preview.DestinationOrigin.COOKIES,
+        account: this.activeUser_,
+        capabilities: null,
+        displayName: '',
+        extensionId: '',
+        extensionName: '',
+      });
     }
 
     // <if expr="chromeos">
diff --git a/chrome/browser/resources/print_preview/data/user_info.js b/chrome/browser/resources/print_preview/data/user_info.js
index f084409..de61c96 100644
--- a/chrome/browser/resources/print_preview/data/user_info.js
+++ b/chrome/browser/resources/print_preview/data/user_info.js
@@ -59,16 +59,7 @@
     this.tracker_.add(
         cloudPrintInterface.getEventTarget(),
         cloudprint.CloudPrintInterfaceEventType.UPDATE_USERS,
-        this.updateUsers_.bind(this));
-  },
-
-  /**
-   * @param {string} user The new active user.
-   * @private
-   */
-  setActiveUser_: function(user) {
-    this.destinationStore.setActiveUser(user);
-    this.activeUser = user;
+        this.onCloudPrintUpdateUsers_.bind(this));
   },
 
   /**
@@ -76,16 +67,30 @@
    *     active user and users.
    * @private
    */
-  updateUsers_: function(e) {
-    this.setActiveUser_(e.detail.activeUser);
+  onCloudPrintUpdateUsers_: function(e) {
+    this.updateActiveUser(e.detail.activeUser, false);
     if (e.detail.users) {
-      this.users = e.detail.users;
+      this.updateUsers(e.detail.users);
     }
   },
 
-  /** @param {string} user The new active user. */
-  updateActiveUser: function(user) {
-    this.setActiveUser_(user);
+  /** @param {!Array<string>} users The full list of signed in users. */
+  updateUsers: function(users) {
+    this.users = users;
+  },
+
+  /**
+   * @param {string} user The new active user.
+   * @param {boolean} reloadCookies Whether to reload cookie based destinations
+   *    and invitations.
+   */
+  updateActiveUser: function(user, reloadCookies) {
+    this.destinationStore.setActiveUser(user);
+    this.activeUser = user;
+    if (!reloadCookies) {
+      return;
+    }
+
     this.destinationStore.reloadUserCookieBasedDestinations(user);
     this.invitationStore.startLoadingInvitations(user);
   },
diff --git a/chrome/browser/resources/print_preview/ui/advanced_settings_item.html b/chrome/browser/resources/print_preview/ui/advanced_settings_item.html
index 1f6fe09..ee686cd 100644
--- a/chrome/browser/resources/print_preview/ui/advanced_settings_item.html
+++ b/chrome/browser/resources/print_preview/ui/advanced_settings_item.html
@@ -24,7 +24,6 @@
         overflow: hidden;
         text-overflow: ellipsis;
         vertical-align: middle;
-        white-space: nowrap;
       }
 
       .label,
@@ -32,18 +31,20 @@
         align-self: center;
         color: var(--cr-primary-text-color);
         overflow: hidden;
-        text-overflow: ellipsis;
       }
 
       .label {
         flex: 1;
         margin-inline-end: 10px;
+        min-width: 96px;
         opacity: 0.87;
       }
 
       .value {
         flex: 0;
         min-width: 239px;
+        text-overflow: ellipsis;
+        white-space: nowrap;
       }
 
       cr-input {
diff --git a/chrome/browser/resources/print_preview/ui/advanced_settings_item.js b/chrome/browser/resources/print_preview/ui/advanced_settings_item.js
index 9011b96..4b01e0e 100644
--- a/chrome/browser/resources/print_preview/ui/advanced_settings_item.js
+++ b/chrome/browser/resources/print_preview/ui/advanced_settings_item.js
@@ -11,11 +11,8 @@
     /** @type {!print_preview.VendorCapability} */
     capability: Object,
 
-    /** @private {(number | string | boolean)} */
-    currentValue_: {
-      type: Object,
-      value: null,
-    },
+    /** @private {string} */
+    currentValue_: String,
   },
 
   observers: [
@@ -76,9 +73,9 @@
    * @private
    */
   isOptionSelected_: function(option) {
-    return (this.currentValue_ !== null &&
-            option.value === this.currentValue_) ||
-        (this.currentValue_ == null && !!option.is_default);
+    return this.currentValue_ === undefined ?
+        !!option.is_default :
+        option.value === this.currentValue_;
   },
 
   /**
@@ -140,15 +137,16 @@
   },
 
   /**
-   * @return {(number | string | boolean)} The current value of the setting.
+   * @return {string} The current value of the setting, or the empty string if
+   *     it is not set.
    */
   getCurrentValue: function() {
-    return this.currentValue_;
+    return this.currentValue_ || '';
   },
 
   /**
    * Only used in tests.
-   * @param {(number | string | boolean)} value A value to set the setting to.
+   * @param {string} value A value to set the setting to.
    */
   setCurrentValueForTest: function(value) {
     this.currentValue_ = value;
diff --git a/chrome/browser/resources/print_preview/ui/app.js b/chrome/browser/resources/print_preview/ui/app.js
index 6758986..7c15940 100644
--- a/chrome/browser/resources/print_preview/ui/app.js
+++ b/chrome/browser/resources/print_preview/ui/app.js
@@ -276,7 +276,8 @@
       this.setSetting('selectionOnly', settings.shouldPrintSelectionOnly);
       this.$.sidebar.init(
           settings.isInAppKioskMode, settings.printerName,
-          settings.serializedDefaultDestinationSelectionRulesStr);
+          settings.serializedDefaultDestinationSelectionRulesStr,
+          settings.userAccounts || null);
       this.isInKioskAutoPrintMode_ = settings.isInKioskAutoPrintMode;
 
       // This is only visible in the task manager.
diff --git a/chrome/browser/resources/print_preview/ui/destination_settings.js b/chrome/browser/resources/print_preview/ui/destination_settings.js
index 9434344..b7df87b 100644
--- a/chrome/browser/resources/print_preview/ui/destination_settings.js
+++ b/chrome/browser/resources/print_preview/ui/destination_settings.js
@@ -182,9 +182,17 @@
    * @param {string} defaultPrinter The system default printer ID.
    * @param {string} serializedDefaultDestinationRulesStr String with rules for
    *     selecting a default destination.
+   * @param {?Array<string>} userAccounts The signed in user accounts.
    */
-  initDestinationStore: function(
-      defaultPrinter, serializedDefaultDestinationRulesStr) {
+  init: function(
+      defaultPrinter, serializedDefaultDestinationRulesStr, userAccounts) {
+    if (!!userAccounts && userAccounts.length > 0) {
+      this.$.userInfo.updateActiveUser(userAccounts[0], false);
+      this.$.userInfo.updateUsers(userAccounts);
+    } else if (userAccounts) {
+      this.cloudPrintState_ = print_preview.CloudPrintState.NOT_SIGNED_IN;
+    }
+
     this.destinationStore_.init(
         this.appKioskMode, defaultPrinter, serializedDefaultDestinationRulesStr,
         /** @type {!Array<print_preview.RecentDestination>} */
@@ -200,10 +208,6 @@
     assert(this.cloudPrintState_ !== print_preview.CloudPrintState.DISABLED);
     this.cloudPrintState_ = print_preview.CloudPrintState.SIGNED_IN;
 
-    // Load docs, in case the user was not signed in previously and signed in
-    // from the destinations dialog.
-    this.destinationStore_.startLoadDriveDestination();
-
     if (this.destinationState === print_preview.DestinationState.SELECTED &&
         this.destination.origin === print_preview.DestinationOrigin.COOKIES) {
       // Adjust states if the destination is now ready to be printed to.
@@ -231,12 +235,6 @@
     // destinationState.
     this.destination = destination;
     this.updateRecentDestinations_();
-    if (this.cloudPrintState_ === print_preview.CloudPrintState.ENABLED) {
-      // Try to load the docs destination. This will trigger a response from
-      // the cloud print server that will tell Print Preview whether the user is
-      // signed in.
-      this.destinationStore_.startLoadDriveDestination();
-    }
   },
 
   /** @private */
@@ -439,7 +437,7 @@
    * @private
    */
   onAccountChange_: function(e) {
-    this.$.userInfo.updateActiveUser(e.detail);
+    this.$.userInfo.updateActiveUser(e.detail, true);
   },
 
   /** @private */
diff --git a/chrome/browser/resources/print_preview/ui/pages_settings.js b/chrome/browser/resources/print_preview/ui/pages_settings.js
index aa6193f..64f6d0a 100644
--- a/chrome/browser/resources/print_preview/ui/pages_settings.js
+++ b/chrome/browser/resources/print_preview/ui/pages_settings.js
@@ -273,7 +273,7 @@
   computeRangesToPrint_: function() {
     if (!this.pagesToPrint_ || this.pagesToPrint_.length == 0 ||
         this.pagesToPrint_[0] == -1 ||
-        this.pagesToPrint_ == this.allPagesArray_) {
+        this.pagesToPrint_.length == this.allPagesArray_.length) {
       return [];
     }
 
diff --git a/chrome/browser/resources/print_preview/ui/sidebar.js b/chrome/browser/resources/print_preview/ui/sidebar.js
index 3a323be..8ac6348 100644
--- a/chrome/browser/resources/print_preview/ui/sidebar.js
+++ b/chrome/browser/resources/print_preview/ui/sidebar.js
@@ -110,12 +110,14 @@
    * @param {string} defaultPrinter The system default printer ID.
    * @param {string} serializedDestinationSelectionRulesStr String with rules
    *     for selecting the default destination.
+   * @param {?Array<string>} userAccounts The signed in user accounts.
    */
   init: function(
-      appKioskMode, defaultPrinter, serializedDestinationSelectionRulesStr) {
+      appKioskMode, defaultPrinter, serializedDestinationSelectionRulesStr,
+      userAccounts) {
     this.isInAppKioskMode_ = appKioskMode;
-    this.$.destinationSettings.initDestinationStore(
-        defaultPrinter, serializedDestinationSelectionRulesStr);
+    this.$.destinationSettings.init(
+        defaultPrinter, serializedDestinationSelectionRulesStr, userAccounts);
   },
 
   /**
diff --git a/chrome/browser/resources/settings/os_settings_resources.grd b/chrome/browser/resources/settings/os_settings_resources.grd
index eebda2c..d8021a7 100644
--- a/chrome/browser/resources/settings/os_settings_resources.grd
+++ b/chrome/browser/resources/settings/os_settings_resources.grd
@@ -1232,13 +1232,8 @@
                  type="chrome_html"/>
       <structure name="IDR_OS_SETTINGS_PEOPLE_PAGE_KERBEROS_ACCOUNTS_JS"
                  file="people_page/kerberos_accounts.js"
-                 type="chrome_html"/>
-      <structure name="IDR_OS_SETTINGS_PEOPLE_PAGE_KERBEROS_ADD_ACCOUNT_DIALOG_HTML"
-                 file="people_page/kerberos_add_account_dialog.html"
-                 type="chrome_html"/>
-      <structure name="IDR_OS_SETTINGS_PEOPLE_PAGE_KERBEROS_ADD_ACCOUNT_DIALOG_JS"
-                 file="people_page/kerberos_add_account_dialog.js"
-                 type="chrome_html"/>
+                 type="chrome_html"
+                 preprocess="true" />
       <structure name="IDR_OS_SETTINGS_PEOPLE_PAGE_KEREROS_ACCOUNTS_BROWSER_PROXY_HTML"
                  file="people_page/kerberos_accounts_browser_proxy.html"
                  type="chrome_html"/>
diff --git a/chrome/browser/resources/settings/people_page/BUILD.gn b/chrome/browser/resources/settings/people_page/BUILD.gn
index 68f6969..6750eb3 100644
--- a/chrome/browser/resources/settings/people_page/BUILD.gn
+++ b/chrome/browser/resources/settings/people_page/BUILD.gn
@@ -33,7 +33,6 @@
       ":fingerprint_list",
       ":kerberos_accounts",
       ":kerberos_accounts_browser_proxy",
-      ":kerberos_add_account_dialog",
       ":lock_screen",
       ":lock_screen_password_prompt_dialog",
       ":lock_state_behavior",
@@ -126,15 +125,6 @@
   ]
 }
 
-js_library("kerberos_add_account_dialog") {
-  deps = [
-    ":kerberos_accounts_browser_proxy",
-    "//ui/webui/resources/cr_elements/cr_input:cr_input",
-    "//ui/webui/resources/js:cr",
-    "//ui/webui/resources/js:i18n_behavior",
-    "//ui/webui/resources/js:web_ui_listener_behavior",
-  ]
-}
 js_library("lock_screen") {
   deps = [
     ":fingerprint_browser_proxy",
diff --git a/chrome/browser/resources/settings/people_page/kerberos_accounts.html b/chrome/browser/resources/settings/people_page/kerberos_accounts.html
index b7b295f..b977bbc 100644
--- a/chrome/browser/resources/settings/people_page/kerberos_accounts.html
+++ b/chrome/browser/resources/settings/people_page/kerberos_accounts.html
@@ -11,7 +11,6 @@
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
 <link rel="import" href="../settings_shared_css.html">
 <link rel="import" href="kerberos_accounts_browser_proxy.html">
-<link rel="import" href="kerberos_add_account_dialog.html">
 
 <dom-module id="settings-kerberos-accounts">
   <template>
@@ -64,7 +63,7 @@
       <div id="account-list-header" class="flex">
         <h2>$i18n{kerberosAccountsListHeader}</h2>
       </div>
-      <paper-button id="add-account-button" on-click="onAddAccountClick_">
+      <paper-button id="add-account-button" on-click="addAccount_">
         <div id="add-account-icon"></div>
         $i18n{kerberosAccountsAddAccountLabel}
       </paper-button>
@@ -113,12 +112,6 @@
         </button>
       </cr-action-menu>
     </div>
-
-    <template is="dom-if" if="[[showAddAccountDialog_]]" restamp>
-      <kerberos-add-account-dialog username="[[addAccountPresetUsername_]]"
-          on-close="onAddAccountDialogClosed_">
-      </kerberos-add-account-dialog>
-    </template>
   </template>
   <script src="kerberos_accounts.js"></script>
 </dom-module>
\ No newline at end of file
diff --git a/chrome/browser/resources/settings/people_page/kerberos_accounts.js b/chrome/browser/resources/settings/people_page/kerberos_accounts.js
index 219fe4f..cf85a05 100644
--- a/chrome/browser/resources/settings/people_page/kerberos_accounts.js
+++ b/chrome/browser/resources/settings/people_page/kerberos_accounts.js
@@ -32,15 +32,6 @@
      * @private {?settings.KerberosAccount}
      */
     actionMenuAccount_: Object,
-
-    /** @private */
-    addAccountPresetUsername_: {
-      type: String,
-      value: '',
-    },
-
-    /** @private */
-    showAddAccountDialog_: Boolean,
   },
 
   /** @private {?settings.KerberosAccountsBrowserProxy} */
@@ -72,9 +63,8 @@
    * @param {!Event} event
    * @private
    */
-  onAddAccountClick_: function(event) {
-    this.addAccountPresetUsername_ = '';
-    this.showAddAccountDialog_ = true;
+  addAccount_: function(event) {
+    this.browserProxy_.addAccount();
   },
 
   /**
@@ -82,13 +72,7 @@
    * @private
    */
   onReauthenticationClick_: function(event) {
-    this.addAccountPresetUsername_ = event.model.item.principalName;
-    this.showAddAccountDialog_ = true;
-  },
-
-  /** @private */
-  onAddAccountDialogClosed_: function() {
-    this.showAddAccountDialog_ = false;
+    this.browserProxy_.reauthenticateAccount(event.model.item.principalName);
   },
 
   /**
diff --git a/chrome/browser/resources/settings/people_page/kerberos_accounts_browser_proxy.html b/chrome/browser/resources/settings/people_page/kerberos_accounts_browser_proxy.html
index c74b570..7ca5511 100644
--- a/chrome/browser/resources/settings/people_page/kerberos_accounts_browser_proxy.html
+++ b/chrome/browser/resources/settings/people_page/kerberos_accounts_browser_proxy.html
@@ -1,2 +1 @@
-<link rel="import" href="chrome://resources/html/cr.html">
 <script src="kerberos_accounts_browser_proxy.js"></script>
\ No newline at end of file
diff --git a/chrome/browser/resources/settings/people_page/kerberos_accounts_browser_proxy.js b/chrome/browser/resources/settings/people_page/kerberos_accounts_browser_proxy.js
index 25ec4f1..a9731c7 100644
--- a/chrome/browser/resources/settings/people_page/kerberos_accounts_browser_proxy.js
+++ b/chrome/browser/resources/settings/people_page/kerberos_accounts_browser_proxy.js
@@ -20,33 +20,6 @@
 settings.KerberosAccount;
 
 cr.define('settings', function() {
-  /**
-   *  @enum {number}
-   *  These values must be kept in sync with the ErrorType enum in
-   *  third_party/cros_system_api/dbus/kerberos/kerberos_service.proto.
-   */
-  const KerberosErrorType = {
-    kNone: 0,
-    kUnknown: 1,
-    kDBusFailure: 2,
-    kNetworkProblem: 3,
-    kUnknownKrb5Error: 4,
-    kBadPrincipal: 5,
-    kBadPassword: 6,
-    kPasswordExpired: 7,
-    kPasswordRejected: 8,
-    kNoCredentialsCacheFound: 9,
-    kKerberosTicketExpired: 10,
-    kKdcDoesNotSupportEncryptionType: 11,
-    kContactingKdcFailed: 12,
-    kParseRequestFailed: 13,
-    kLocalIo: 14,
-    kUnknownPrincipalName: 15,
-    kDuplicatePrincipalName: 16,
-    kInProgress: 17,
-    kParsePrincipalFailed: 18,
-  };
-
   /** @interface */
   class KerberosAccountsBrowserProxy {
     /**
@@ -57,12 +30,16 @@
     getAccounts() {}
 
     /**
-     * Attempts to add a new (or update an existing) Kerberos account.
-     * @param {string} principalName Kerberos principal (user@realm.com).
-     * @param {string} password Account password.
-     * @return {!Promise<!settings.KerberosErrorType>}
+     * Triggers the 'Add account' flow.
      */
-    addAccount(principalName, password) {}
+    addAccount() {}
+
+    /**
+     * Triggers the re-authentication flow for the account pointed to by
+     * |principalName|.
+     * @param {!string} principalName
+     */
+    reauthenticateAccount(principalName) {}
 
     /**
      * Removes |account| from the set of Kerberos accounts.
@@ -81,8 +58,13 @@
     }
 
     /** @override */
-    addAccount(principalName, password) {
-      return cr.sendWithPromise('addKerberosAccount', principalName, password);
+    addAccount() {
+      chrome.send('addKerberosAccount');
+    }
+
+    /** @override */
+    reauthenticateAccount(principalName) {
+      chrome.send('reauthenticateKerberosAccount', [principalName]);
     }
 
     /** @override */
@@ -94,7 +76,6 @@
   cr.addSingletonGetter(KerberosAccountsBrowserProxyImpl);
 
   return {
-    KerberosErrorType: KerberosErrorType,
     KerberosAccountsBrowserProxy: KerberosAccountsBrowserProxy,
     KerberosAccountsBrowserProxyImpl: KerberosAccountsBrowserProxyImpl,
   };
diff --git a/chrome/browser/resources/settings/people_page/kerberos_add_account_dialog.html b/chrome/browser/resources/settings/people_page/kerberos_add_account_dialog.html
deleted file mode 100644
index c6a1553..0000000
--- a/chrome/browser/resources/settings/people_page/kerberos_add_account_dialog.html
+++ /dev/null
@@ -1,63 +0,0 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
-
-<link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
-<link rel="import" href="chrome://resources/cr_elements/cr_input/cr_input.html">
-<link rel="import" href="chrome://resources/cr_elements/icons.html">
-<link rel="import" href="chrome://resources/cr_elements/paper_button_style_css.html">
-<link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
-<link rel="import" href="chrome://resources/html/i18n_behavior.html">
-<link rel="import" href="chrome://resources/html/load_time_data.html">
-<link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
-<link rel="import" href="kerberos_accounts_browser_proxy.html">
-
-<dom-module id="kerberos-add-account-dialog">
-  <template>
-    <style include="paper-button-style">
-      .label {
-        @apply --cr-form-field-label;
-      }
-
-      #credentials > cr-input:not(:last-child) {
-        margin-bottom: var(--cr-form-field-bottom-spacing);
-      }
-
-      #general-error-container {
-        height: 48px;
-      }
-    </style>
-    <cr-dialog id="dialog">
-      <div slot="title">$i18n{addKerberosAccount}</div>
-      <div slot="body" spellcheck="false">
-        <div id="general-error-container">
-          <div hidden="[[!showError_(generalErrorText_)]]">
-            <iron-icon id="general-error-icon" icon="cr:warning"></iron-icon>
-            <div id="general-error-message">[[generalErrorText_]]</div>
-          </div>
-        </div>
-        <div id="credentials">
-          <cr-input id="username" label="$i18n{kerberosUsername}"
-              value="{{username}}" invalid="[[showError_(usernameErrorText_)]]"
-              error-message="[[usernameErrorText_]]">
-          </cr-input>
-          <cr-input id="password" type="password"
-              label="$i18n{kerberosPassword}" value="{{password_}}"
-              invalid="[[showError_(passwordErrorText_)]]"
-              error-message="[[passwordErrorText_]]">
-          </cr-input>
-        </div>
-      </div>
-      <div slot="button-container">
-        <paper-button class="cancel-button" on-click="onCancel_" id="cancel">
-          $i18n{cancel}
-        </paper-button>
-        <paper-button class="action-button" on-click="onAdd_"
-            disabled="[[inProgress_]]">
-          $i18n{add}
-        </paper-button>
-      </div>
-    </cr-dialog>
-  </template>
-  <script src="kerberos_add_account_dialog.js"></script>
-</dom-module>
\ No newline at end of file
diff --git a/chrome/browser/resources/settings/people_page/kerberos_add_account_dialog.js b/chrome/browser/resources/settings/people_page/kerberos_add_account_dialog.js
deleted file mode 100644
index 2b139099..0000000
--- a/chrome/browser/resources/settings/people_page/kerberos_add_account_dialog.js
+++ /dev/null
@@ -1,151 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-/**
- * @fileoverview
- * 'kerberos-add-account-dialog' is an element to add Kerberos accounts.
- */
-
-Polymer({
-  is: 'kerberos-add-account-dialog',
-
-  behaviors: [I18nBehavior],
-
-  properties: {
-    username: {
-      type: String,
-      value: '',
-    },
-
-    /** @private */
-    password_: {
-      type: String,
-      value: '',
-    },
-
-    /** @private */
-    generalErrorText_: {
-      type: String,
-      value: '',
-    },
-
-    /** @private */
-    usernameErrorText_: {
-      type: String,
-      value: '',
-    },
-
-    /** @private */
-    passwordErrorText_: {
-      type: String,
-      value: '',
-    },
-
-    /** @private */
-    inProgress_: {
-      type: Boolean,
-      value: false,
-    },
-  },
-
-  /** @private {?settings.KerberosAccountsBrowserProxy} */
-  browserProxy_: null,
-
-  /** @private {!settings.KerberosErrorType} */
-  lastError_: settings.KerberosErrorType.kNone,
-
-  /** @override */
-  attached: function() {
-    this.$.dialog.showModal();
-
-    // If a non-empty username is preset, make the UI read-only.
-    // Note: At least the focus() part needs to be after showModal.
-    if (this.username) {
-      this.$.username.disabled = true;
-      this.$.password.focus();
-    }
-  },
-
-  /** @private */
-  onCancel_: function() {
-    this.$.dialog.cancel();
-  },
-
-  /** @private */
-  onAdd_: function() {
-    this.inProgress_ = true;
-    this.updateErrorMessages_(settings.KerberosErrorType.kNone);
-
-    settings.KerberosAccountsBrowserProxyImpl.getInstance()
-        .addAccount(this.username, this.password_)
-        .then(error => {
-          this.inProgress_ = false;
-
-          // Success case. Close dialog.
-          if (error == settings.KerberosErrorType.kNone) {
-            this.$.dialog.close();
-            return;
-          }
-
-          // Triggers the UI to update error messages.
-          this.updateErrorMessages_(error);
-        });
-  },
-
-  /**
-   * @param {!settings.KerberosErrorType} error Current error enum
-   * @private
-   */
-  updateErrorMessages_: function(error) {
-    this.lastError_ = error;
-
-    this.generalErrorText_ = '';
-    this.usernameErrorText_ = '';
-    this.passwordErrorText_ = '';
-
-    switch (error) {
-      // TODO(ljusten): Proper errors
-      case settings.KerberosErrorType.kNone:
-        break;
-
-      case settings.KerberosErrorType.kNetworkProblem:
-        this.generalErrorText_ = 'Network problem or bad realm';
-        break;
-      case settings.KerberosErrorType.kParsePrincipalFailed:
-        this.usernameErrorText_ = 'Username invalid (should be user@realm.com)';
-        break;
-      case settings.KerberosErrorType.kBadPrincipal:
-        this.usernameErrorText_ = 'Username not known to server';
-        break;
-      case settings.KerberosErrorType.kContactingKdcFailed:
-        this.usernameErrorText_ = 'Contacting server for realm failed';
-        break;
-
-      case settings.KerberosErrorType.kBadPassword:
-        this.passwordErrorText_ = 'Password invalid';
-        break;
-      case settings.KerberosErrorType.kPasswordExpired:
-        this.passwordErrorText_ = 'Password expired';
-        break;
-
-      case settings.KerberosErrorType.kKdcDoesNotSupportEncryptionType:
-        this.generalErrorText_ = 'KDC does not support encryption type';
-        break;
-      default:
-        this.generalErrorText_ = this.i18nDynamic(
-            this.locale, 'kerberosGeneralErrorMessage', String(error));
-    }
-  },
-
-  /**
-   * Whether an error element should be shown.
-   * Note that !! is not supported in Polymer bindings.
-   * @param {?string} errorText Error text to be displayed. Empty if no error.
-   * @return {boolean} True iff errorText is not empty.
-   * @private
-   */
-  showError_: function(errorText) {
-    return !!errorText;
-  }
-});
\ No newline at end of file
diff --git a/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.html b/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.html
index 1f20da2..af3d2d2f 100644
--- a/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.html
+++ b/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.html
@@ -187,13 +187,15 @@
           </a>
         </div>
         <div class="settings-box two-line">
-          <cr-searchable-drop-down items="[[manufacturerList]]"
+          <cr-searchable-drop-down id="manufacturerDropdown"
+              items="[[manufacturerList]]"
               label="$i18n{printerManufacturer}"
               value="{{activePrinter.ppdManufacturer}}">
           </cr-searchable-drop-down>
         </div>
         <div class="settings-box two-line">
-          <cr-searchable-drop-down items="[[modelList]]"
+          <cr-searchable-drop-down id="modelDropdown"
+              items="[[modelList]]"
               label="$i18n{printerModel}"
               value="{{activePrinter.ppdModel}}">
           </cr-searchable-drop-down>
@@ -207,6 +209,9 @@
             $i18n{selectDriverButtonText}
           </paper-button>
         </div>
+        <div class="eula" id="eulaUrl" hidden="[[!eulaUrl_]]">
+          <a href="[[eulaUrl_]]" target="_blank">$i18n{printerEulaNotice}</a>
+        </div>
       </div>
       <div slot="dialog-buttons">
         <paper-button class="cancel-button secondary-button"
diff --git a/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.js b/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.js
index 80ca448..37fe0cc 100644
--- a/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.js
+++ b/chrome/browser/resources/settings/printing_page/cups_add_printer_dialog.js
@@ -235,10 +235,20 @@
      * @private
      */
     newUserPPD_: String,
+
+    /**
+     * The URL to a printer's EULA.
+     * @private
+     */
+    eulaUrl_: {
+      type: String,
+      value: '',
+    },
   },
 
   observers: [
     'selectedManufacturerChanged_(activePrinter.ppdManufacturer)',
+    'selectedModelChanged_(activePrinter.ppdModel)',
   ],
 
   /** @override */
@@ -280,6 +290,32 @@
   },
 
   /**
+   * Attempts to get the EULA Url if the selected printer has one.
+   * @private
+   */
+  selectedModelChanged_: function() {
+    if (!this.activePrinter.ppdManufacturer || !this.activePrinter.ppdModel) {
+      // Do not check for an EULA unless both |ppdManufacturer| and |ppdModel|
+      // are set. Set |eulaUrl_| to be empty in this case.
+      this.onGetEulaUrlCompleted_('' /* eulaUrl */);
+      return;
+    }
+
+    settings.CupsPrintersBrowserProxyImpl.getInstance()
+        .getEulaUrl(
+            this.activePrinter.ppdManufacturer, this.activePrinter.ppdModel)
+        .then(this.onGetEulaUrlCompleted_.bind(this));
+  },
+
+  /**
+   * @param {string} eulaUrl The URL for the printer's EULA.
+   * @private
+   */
+  onGetEulaUrlCompleted_: function(eulaUrl) {
+    this.eulaUrl_ = eulaUrl;
+  },
+
+  /**
    * @param {!ManufacturersInfo} manufacturersInfo
    * @private
    */
@@ -521,7 +557,7 @@
           this.newPrinter.printerId);
     } else if (this.previousDialog_ == AddPrinterDialogs.MANUFACTURER) {
       this.configuringDialogTitle =
-          loadTimeData.getString('selectManufacturerAndModelTitle');
+          loadTimeData.getString('manufacturerAndModelDialogTitle');
       this.addPrinter_();
     } else if (this.previousDialog_ == AddPrinterDialogs.MANUALLY) {
       this.configuringDialogTitle =
diff --git a/chrome/browser/resources/settings/printing_page/cups_edit_printer_dialog.html b/chrome/browser/resources/settings/printing_page/cups_edit_printer_dialog.html
index e1e85f9..7acadc0c 100644
--- a/chrome/browser/resources/settings/printing_page/cups_edit_printer_dialog.html
+++ b/chrome/browser/resources/settings/printing_page/cups_edit_printer_dialog.html
@@ -4,6 +4,7 @@
 <link rel="import" href="chrome://resources/cr_elements/cr_scrollable_behavior.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_searchable_drop_down/cr_searchable_drop_down.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
+<link rel="import" href="../i18n_setup.html">
 <link rel="import" href="cups_add_printer_dialog_elements.html">
 <link rel="import" href="cups_printer_dialog_util.html">
 <link rel="import" href="cups_printer_shared_css.html">
@@ -106,6 +107,9 @@
             $i18n{selectDriverButtonText}
           </paper-button>
         </div>
+        <div class="eula" id="eulaUrl" hidden="[[!eulaUrl_]]">
+          <a href="[[eulaUrl_]]" target="_blank">$i18n{printerEulaNotice}</a>
+        </div>
       </div>
       <div slot="dialog-buttons">
         <paper-button class="cancel-button secondary-button"
diff --git a/chrome/browser/resources/settings/printing_page/cups_edit_printer_dialog.js b/chrome/browser/resources/settings/printing_page/cups_edit_printer_dialog.js
index cb0d4a2..2e5404f 100644
--- a/chrome/browser/resources/settings/printing_page/cups_edit_printer_dialog.js
+++ b/chrome/browser/resources/settings/printing_page/cups_edit_printer_dialog.js
@@ -92,6 +92,15 @@
      * @private
      */
     newUserPPD_: String,
+
+    /**
+     * The URL to a printer's EULA.
+     * @private
+     */
+    eulaUrl_: {
+      type: String,
+      value: '',
+    },
   },
 
   observers: [
@@ -235,13 +244,35 @@
   },
 
   /**
-   * Sets printerInfoChanged_ to true to show that the model has changed.
+   * Sets printerInfoChanged_ to true to show that the model has changed. Also
+   * attempts to get the EULA Url if the selected printer has one.
    * @private
    */
   onModelChanged_: function() {
     if (this.arePrinterFieldsInitialized_) {
       this.printerInfoChanged_ = true;
     }
+
+    if (!this.pendingPrinter_.ppdManufacturer ||
+        !this.pendingPrinter_.ppdModel) {
+      // Do not check for an EULA unless both |ppdManufacturer| and |ppdModel|
+      // are set. Set |eulaUrl_| to be empty in this case.
+      this.onGetEulaUrlCompleted_('' /* eulaUrl */);
+      return;
+    }
+
+    settings.CupsPrintersBrowserProxyImpl.getInstance()
+        .getEulaUrl(
+            this.pendingPrinter_.ppdManufacturer, this.pendingPrinter_.ppdModel)
+        .then(this.onGetEulaUrlCompleted_.bind(this));
+  },
+
+  /**
+   * @param {string} eulaUrl The URL for the printer's EULA.
+   * @private
+   */
+  onGetEulaUrlCompleted_: function(eulaUrl) {
+    this.eulaUrl_ = eulaUrl;
   },
 
   /** @private */
diff --git a/chrome/browser/resources/settings/printing_page/cups_printer_shared_css.html b/chrome/browser/resources/settings/printing_page/cups_printer_shared_css.html
index 5a21b6c..a8040ee 100644
--- a/chrome/browser/resources/settings/printing_page/cups_printer_shared_css.html
+++ b/chrome/browser/resources/settings/printing_page/cups_printer_shared_css.html
@@ -62,6 +62,10 @@
         width: 100%;
       }
 
+      [slot='dialog-body'] .eula {
+        padding-inline-start: 20px;
+      }
+
       [slot='dialog-buttons'] {
         display: flex;
         justify-content: space-between;
diff --git a/chrome/browser/resources/settings/printing_page/cups_printers_browser_proxy.js b/chrome/browser/resources/settings/printing_page/cups_printers_browser_proxy.js
index d083e82..b309666 100644
--- a/chrome/browser/resources/settings/printing_page/cups_printers_browser_proxy.js
+++ b/chrome/browser/resources/settings/printing_page/cups_printers_browser_proxy.js
@@ -180,6 +180,14 @@
      * @param {!CupsPrinterInfo} newPrinter
      */
     cancelPrinterSetUp(newPrinter) {}
+
+    /**
+     * @param {string} ppdManufacturer
+     * @param {string} ppdModel
+     * @return {!Promise<string>} Returns the EULA URL of the printer. Returns
+     * an empty string if no EULA is required.
+     */
+    getEulaUrl(ppdManufacturer, ppdModel) {}
   }
 
   /**
@@ -255,6 +263,11 @@
     cancelPrinterSetUp(newPrinter) {
       chrome.send('cancelPrinterSetUp', [newPrinter]);
     }
+
+    /** @override */
+    getEulaUrl(ppdManufacturer, ppdModel) {
+      return cr.sendWithPromise('getEulaUrl', ppdManufacturer, ppdModel);
+    }
   }
 
   cr.addSingletonGetter(CupsPrintersBrowserProxyImpl);
diff --git a/chrome/browser/resources/settings/settings_resources.grd b/chrome/browser/resources/settings/settings_resources.grd
index 4b220cb..50e2709 100644
--- a/chrome/browser/resources/settings/settings_resources.grd
+++ b/chrome/browser/resources/settings/settings_resources.grd
@@ -1519,12 +1519,6 @@
         <structure name="IDR_SETTINGS_PEOPLE_PAGE_KERBEROS_ACCOUNTS_JS"
                    file="people_page/kerberos_accounts.js"
                    type="chrome_html"/>
-        <structure name="IDR_OS_SETTINGS_PEOPLE_PAGE_KERBEROS_ADD_ACCOUNT_DIALOG_HTML"
-                   file="people_page/kerberos_add_account_dialog.html"
-                   type="chrome_html"/>
-        <structure name="IDR_OS_SETTINGS_PEOPLE_PAGE_KERBEROS_ADD_ACCOUNT_DIALOG_JS"
-                   file="people_page/kerberos_add_account_dialog.js"
-                   type="chrome_html"/>
         <structure name="IDR_SETTINGS_PEOPLE_PAGE_KERBEROS_ACCOUNTS_BROWSER_PROXY_HTML"
                    file="people_page/kerberos_accounts_browser_proxy.html"
                    type="chrome_html"/>
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/google_apps/nux_google_apps.html b/chrome/browser/resources/welcome/onboarding_welcome/google_apps/nux_google_apps.html
index dba91a4..6e53f1b 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/google_apps/nux_google_apps.html
+++ b/chrome/browser/resources/welcome/onboarding_welcome/google_apps/nux_google_apps.html
@@ -170,6 +170,12 @@
             url(chrome://welcome/images/news_2x.png) 2x);
       }
 
+      .search {
+        content: -webkit-image-set(
+            url(chrome://theme/IDS_ONBOARDING_WELCOME_SEARCH@1x) 1x,
+            url(chrome://theme/IDS_ONBOARDING_WELCOME_SEARCH@2x) 2x);
+      }
+
       .web-store {
         content: -webkit-image-set(
             url(chrome://welcome/images/chrome_store_1x.png) 1x,
diff --git a/chrome/browser/search/background/ntp_background_service.cc b/chrome/browser/search/background/ntp_background_service.cc
index 6cefbba..8124da2 100644
--- a/chrome/browser/search/background/ntp_background_service.cc
+++ b/chrome/browser/search/background/ntp_background_service.cc
@@ -267,6 +267,23 @@
   collection_images_.push_back(image);
 }
 
+void NtpBackgroundService::AddValidBackdropUrlWithThumbnailForTesting(
+    const GURL& url,
+    const GURL& thumbnail_url) {
+  CollectionImage image;
+  image.image_url = url;
+  image.thumbnail_image_url = thumbnail_url;
+  collection_images_.push_back(image);
+}
+
+const GURL& NtpBackgroundService::GetThumbnailUrl(const GURL& image_url) {
+  for (auto& image : collection_images_) {
+    if (image.image_url == image_url)
+      return image.thumbnail_image_url;
+  }
+  return GURL::EmptyGURL();
+}
+
 void NtpBackgroundService::NotifyObservers(FetchComplete fetch_complete) {
   for (auto& observer : observers_) {
     switch (fetch_complete) {
diff --git a/chrome/browser/search/background/ntp_background_service.h b/chrome/browser/search/background/ntp_background_service.h
index de41780..64a7163 100644
--- a/chrome/browser/search/background/ntp_background_service.h
+++ b/chrome/browser/search/background/ntp_background_service.h
@@ -55,6 +55,13 @@
 
   void AddValidBackdropUrlForTesting(const GURL& url);
 
+  void AddValidBackdropUrlWithThumbnailForTesting(const GURL& url,
+                                                  const GURL& thumbnail_url);
+
+  // Returns thumbnail url for the given image url if its valid. Otherwise,
+  // returns empty url.
+  const GURL& GetThumbnailUrl(const GURL& image_url);
+
   // Returns the currently cached CollectionInfo, if any.
   const std::vector<CollectionInfo>& collection_info() const {
     return collection_info_;
diff --git a/chrome/browser/search/background/ntp_background_service_unittest.cc b/chrome/browser/search/background/ntp_background_service_unittest.cc
index e7d5a73..5d04323 100644
--- a/chrome/browser/search/background/ntp_background_service_unittest.cc
+++ b/chrome/browser/search/background/ntp_background_service_unittest.cc
@@ -261,3 +261,14 @@
   EXPECT_FALSE(service()->IsValidBackdropUrl(
       GURL("https://wallpapers.co/another_image")));
 }
+
+TEST_F(NtpBackgroundServiceTest, GetThumbnailUrl) {
+  const GURL kInvalidUrl("foo");
+  const GURL kValidUrl("https://www.foo.com");
+  const GURL kValidThumbnailUrl("https://www.foo.com/thumbnail");
+  service()->AddValidBackdropUrlWithThumbnailForTesting(kValidUrl,
+                                                        kValidThumbnailUrl);
+
+  EXPECT_EQ(kValidThumbnailUrl, service()->GetThumbnailUrl(kValidUrl));
+  EXPECT_EQ(GURL::EmptyGURL(), service()->GetThumbnailUrl(kInvalidUrl));
+}
diff --git a/chrome/browser/search/instant_service.cc b/chrome/browser/search/instant_service.cc
index 449a5bb..12f07f9 100644
--- a/chrome/browser/search/instant_service.cc
+++ b/chrome/browser/search/instant_service.cc
@@ -17,6 +17,7 @@
 #include "base/task/post_task.h"
 #include "build/build_config.h"
 #include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/image_fetcher/image_decoder_impl.h"
 #include "chrome/browser/ntp_tiles/chrome_most_visited_sites_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/search/background/ntp_background_service.h"
@@ -51,7 +52,9 @@
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/notification_types.h"
 #include "content/public/browser/render_process_host.h"
+#include "content/public/browser/storage_partition.h"
 #include "content/public/browser/url_data_source.h"
+#include "ui/gfx/color_analysis.h"
 #include "ui/gfx/color_utils.h"
 #include "ui/native_theme/dark_mode_observer.h"
 
@@ -63,6 +66,8 @@
 const char kNtpCustomBackgroundAttributionActionURL[] =
     "attribution_action_url";
 
+const char kImageFetcherUmaClientName[] = "NtpCustomBackgrounds";
+
 base::DictionaryValue GetBackgroundInfoAsDict(
     const GURL& background_url,
     const std::string& attribution_line_1,
@@ -81,6 +86,33 @@
   return background_info;
 }
 
+// |GetBackgroundInfoWithColor| has to return new object so that updated version
+// gets synced.
+base::DictionaryValue GetBackgroundInfoWithColor(
+    const base::DictionaryValue* background_info,
+    const SkColor color) {
+  base::DictionaryValue new_background_info;
+  auto url = const_cast<base::Value&&>(
+      *background_info->FindKey(kNtpCustomBackgroundURL));
+  auto attribution_line_1 = const_cast<base::Value&&>(
+      *background_info->FindKey(kNtpCustomBackgroundAttributionLine1));
+  auto attribution_line_2 = const_cast<base::Value&&>(
+      *background_info->FindKey(kNtpCustomBackgroundAttributionLine2));
+  auto action_url = const_cast<base::Value&&>(
+      *background_info->FindKey(kNtpCustomBackgroundAttributionActionURL));
+
+  new_background_info.SetKey(kNtpCustomBackgroundURL, url.Clone());
+  new_background_info.SetKey(kNtpCustomBackgroundAttributionLine1,
+                             attribution_line_1.Clone());
+  new_background_info.SetKey(kNtpCustomBackgroundAttributionLine2,
+                             attribution_line_2.Clone());
+  new_background_info.SetKey(kNtpCustomBackgroundAttributionActionURL,
+                             action_url.Clone());
+  new_background_info.SetKey(kNtpCustomBackgroundMainColor,
+                             base::Value((int)color));
+  return new_background_info;
+}
+
 base::Value NtpCustomBackgroundDefaults() {
   base::Value defaults(base::Value::Type::DICTIONARY);
   defaults.SetKey(kNtpCustomBackgroundURL,
@@ -113,8 +145,17 @@
     std::move(*callback).Run(result);
 }
 
+// |GetBitmapMainColor| just wraps |CalculateKMeanColorOfBitmap|.
+// As |CalculateKMeanColorOfBitmap| is overloaded, it cannot be bind for async
+// call.
+SkColor GetBitmapMainColor(const SkBitmap& bitmap) {
+  return color_utils::CalculateKMeanColorOfBitmap(bitmap);
+}
+
 }  // namespace
 
+const char kNtpCustomBackgroundMainColor[] = "background_main_color";
+
 // Keeps track of any changes in search engine provider and notifies
 // InstantService if a third-party search provider (i.e. a third-party NTP) is
 // being used.
@@ -231,6 +272,11 @@
       prefs::kNtpCustomBackgroundDict,
       base::BindRepeating(&InstantService::UpdateBackgroundFromSync,
                           weak_ptr_factory_.GetWeakPtr()));
+
+  image_fetcher_ = std::make_unique<image_fetcher::ImageFetcherImpl>(
+      std::make_unique<ImageDecoderImpl>(),
+      content::BrowserContext::GetDefaultStoragePartition(profile_)
+          ->GetURLLoaderFactoryForBrowserProcess());
 }
 
 InstantService::~InstantService() = default;
@@ -383,6 +429,12 @@
   RemoveLocalBackgroundImageCopy();
 
   if (background_url.is_valid() && is_backdrop_url) {
+    const GURL& thumbnail_url =
+        background_service_->GetThumbnailUrl(background_url);
+    FetchCustomBackground(background_url, thumbnail_url.is_valid()
+                                              ? thumbnail_url
+                                              : background_url);
+
     base::DictionaryValue background_info = GetBackgroundInfoAsDict(
         background_url, attribution_line_1, attribution_line_2, action_url);
     pref_service_->Set(prefs::kNtpCustomBackgroundDict, background_info);
@@ -723,6 +775,52 @@
   ResetCustomBackgroundThemeInfo();
 }
 
+void InstantService::UpdateCustomBackgroundColorAsync(
+    const GURL& image_url,
+    const gfx::Image& fetched_image,
+    const image_fetcher::RequestMetadata& metadata) {
+  // Calculate the bitmap color asynchronously as it is slow (1-2 seconds for
+  // the thumbnail). However, prefs should be updated on the main thread.
+  base::PostTaskWithTraitsAndReplyWithResult(
+      FROM_HERE, {base::TaskPriority::BEST_EFFORT},
+      base::BindOnce(&GetBitmapMainColor, *fetched_image.ToSkBitmap()),
+      base::BindOnce(&InstantService::UpdateCustomBackgroundPrefsWithColor,
+                     weak_ptr_factory_.GetWeakPtr(), image_url));
+}
+
+void InstantService::FetchCustomBackground(const GURL& image_url,
+                                           const GURL& fetch_url) {
+  DCHECK(!fetch_url.is_empty());
+
+  net::NetworkTrafficAnnotationTag traffic_annotation =
+      net::DefineNetworkTrafficAnnotation("ntp_custom_background",
+                                          R"(
+    semantics {
+      sender: "Desktop Chrome background fetcher"
+      description:
+        "Fetch New Tab Page custom background for color calculation."
+      trigger:
+        "User selects new background on the New Tab Page."
+      data: "The only data sent is the path to an image"
+      destination: GOOGLE_OWNED_SERVICE
+    }
+    policy {
+      cookies_allowed: NO
+      setting:
+        "Users cannot disable this feature. The feature is enabled by "
+        "default."
+      policy_exception_justification: "Not implemented."
+    })");
+
+  image_fetcher::ImageFetcherParams params(traffic_annotation,
+                                           kImageFetcherUmaClientName);
+  image_fetcher_->FetchImage(
+      image_url,
+      base::BindOnce(&InstantService::UpdateCustomBackgroundColorAsync,
+                     weak_ptr_factory_.GetWeakPtr(), image_url),
+      std::move(params));
+}
+
 bool InstantService::IsCustomBackgroundPrefValid(GURL& custom_background_url) {
   const base::DictionaryValue* background_info =
       profile_->GetPrefs()->GetDictionary(prefs::kNtpCustomBackgroundDict);
@@ -777,3 +875,24 @@
                                 false);
   registry->RegisterBooleanPref(prefs::kNtpUseMostVisitedTiles, false);
 }
+
+void InstantService::UpdateCustomBackgroundPrefsWithColor(const GURL& image_url,
+                                                          SkColor color) {
+  // Update background color only if the selected background is still the same.
+  const base::DictionaryValue* background_info =
+      pref_service_->GetDictionary(prefs::kNtpCustomBackgroundDict);
+  if (!background_info)
+    return;
+
+  GURL current_bg_url(
+      background_info->FindKey(kNtpCustomBackgroundURL)->GetString());
+  if (current_bg_url == image_url) {
+    pref_service_->Set(prefs::kNtpCustomBackgroundDict,
+                       GetBackgroundInfoWithColor(background_info, color));
+  }
+}
+
+void InstantService::SetImageFetcherForTesting(
+    image_fetcher::ImageFetcher* image_fetcher) {
+  image_fetcher_ = base::WrapUnique(image_fetcher);
+}
diff --git a/chrome/browser/search/instant_service.h b/chrome/browser/search/instant_service.h
index 319bab8..c40e247e 100644
--- a/chrome/browser/search/instant_service.h
+++ b/chrome/browser/search/instant_service.h
@@ -18,6 +18,7 @@
 #include "base/optional.h"
 #include "build/build_config.h"
 #include "components/history/core/browser/history_types.h"
+#include "components/image_fetcher/core/image_fetcher_impl.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/ntp_tiles/most_visited_sites.h"
 #include "components/ntp_tiles/ntp_tile.h"
@@ -47,6 +48,8 @@
 class DarkModeObserver;
 }  // namespace ui
 
+extern const char kNtpCustomBackgroundMainColor[];
+
 // Tracks render process host IDs that are associated with Instant, i.e.
 // processes that are used to render an NTP. Also responsible for keeping
 // necessary information (most visited tiles and theme info) updated in those
@@ -154,11 +157,22 @@
   // tests.
   virtual void ResetToDefault();
 
+  // Calculates the most frequent color of the image and stores it in prefs.
+  void UpdateCustomBackgroundColorAsync(
+      const GURL& image_url,
+      const gfx::Image& fetched_image,
+      const image_fetcher::RequestMetadata& metadata);
+
+  // Fetches the image for the given |fetch_url|.
+  void FetchCustomBackground(const GURL& image_url, const GURL& fetch_url);
+
  private:
   class SearchProviderObserver;
 
   friend class InstantExtendedTest;
   friend class InstantUnitTestBase;
+  friend class LocalNTPBackgroundsAndDarkModeTest;
+  friend class TestInstantService;
 
   FRIEND_TEST_ALL_PREFIXES(InstantExtendedTest, ProcessIsolation);
   FRIEND_TEST_ALL_PREFIXES(InstantServiceTest, DeleteThumbnailDataIfExists);
@@ -229,6 +243,12 @@
 
   void CreateDarkModeObserver(ui::NativeTheme* theme);
 
+  // Updates custom background prefs with color for the given |image_url|.
+  void UpdateCustomBackgroundPrefsWithColor(const GURL& image_url,
+                                            SkColor color);
+
+  void SetImageFetcherForTesting(image_fetcher::ImageFetcher* image_fetcher);
+
   Profile* const profile_;
 
   // The process ids associated with Instant processes.
@@ -261,6 +281,8 @@
 
   NtpBackgroundService* background_service_;
 
+  std::unique_ptr<image_fetcher::ImageFetcher> image_fetcher_;
+
   base::WeakPtrFactory<InstantService> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(InstantService);
diff --git a/chrome/browser/search/instant_service_unittest.cc b/chrome/browser/search/instant_service_unittest.cc
index 2e09ed5..b2dfc78 100644
--- a/chrome/browser/search/instant_service_unittest.cc
+++ b/chrome/browser/search/instant_service_unittest.cc
@@ -55,6 +55,18 @@
   MOCK_METHOD0(ResetCustomBackgroundThemeInfo, void());
 };
 
+bool CheckBackgroundColor(SkColor color,
+                          const base::DictionaryValue* background_info) {
+  if (!background_info)
+    return false;
+
+  const base::Value* background_color =
+      background_info->FindKey(kNtpCustomBackgroundMainColor);
+  if (!background_color)
+    return false;
+
+  return color == static_cast<uint32_t>(background_color->GetInt());
+}
 }  // namespace
 
 using InstantServiceTest = InstantUnitTestBase;
@@ -576,3 +588,48 @@
   EXPECT_EQ(std::string(), theme_info->custom_background_attribution_line_2);
   EXPECT_EQ(GURL(), theme_info->custom_background_attribution_action_url);
 }
+
+TEST_F(InstantServiceTest, TestUpdateCustomBackgroundColor) {
+  SkBitmap bitmap;
+  bitmap.allocN32Pixels(32, 32);
+  bitmap.eraseColor(SK_ColorRED);
+  gfx::Image image = gfx::Image::CreateFrom1xBitmap(bitmap);
+  sync_preferences::TestingPrefServiceSyncable* pref_service =
+      profile()->GetTestingPrefService();
+
+  ASSERT_FALSE(instant_service_->IsCustomBackgroundSet());
+
+  // Background color will not update if no background is set.
+  instant_service_->UpdateCustomBackgroundColorAsync(
+      GURL(), image, image_fetcher::RequestMetadata());
+  thread_bundle()->RunUntilIdle();
+  EXPECT_FALSE(CheckBackgroundColor(
+      SK_ColorRED,
+      pref_service->GetDictionary(prefs::kNtpCustomBackgroundDict)));
+
+  const GURL kUrl("https://www.foo.com");
+  const std::string kAttributionLine1 = "foo";
+  const std::string kAttributionLine2 = "bar";
+  const GURL kActionUrl("https://www.bar.com");
+
+  SetUserSelectedDefaultSearchProvider("{google:baseURL}");
+  instant_service_->AddValidBackdropUrlForTesting(kUrl);
+  instant_service_->SetCustomBackgroundURLWithAttributions(
+      kUrl, kAttributionLine1, kAttributionLine2, kActionUrl);
+
+  // Background color will not update if current background url changed.
+  instant_service_->UpdateCustomBackgroundColorAsync(
+      GURL("different_url"), image, image_fetcher::RequestMetadata());
+  thread_bundle()->RunUntilIdle();
+  EXPECT_FALSE(CheckBackgroundColor(
+      SK_ColorRED,
+      pref_service->GetDictionary(prefs::kNtpCustomBackgroundDict)));
+
+  // Background color should update.
+  instant_service_->UpdateCustomBackgroundColorAsync(
+      kUrl, image, image_fetcher::RequestMetadata());
+  thread_bundle()->RunUntilIdle();
+  EXPECT_TRUE(CheckBackgroundColor(
+      SK_ColorRED,
+      pref_service->GetDictionary(prefs::kNtpCustomBackgroundDict)));
+}
diff --git a/chrome/browser/search/instant_unittest_base.cc b/chrome/browser/search/instant_unittest_base.cc
index 9c2df76..69f1e02 100644
--- a/chrome/browser/search/instant_unittest_base.cc
+++ b/chrome/browser/search/instant_unittest_base.cc
@@ -20,6 +20,7 @@
 #include "chrome/test/base/search_test_utils.h"
 #include "components/google/core/browser/google_pref_names.h"
 #include "components/google/core/browser/google_url_tracker.h"
+#include "components/image_fetcher/core/mock_image_fetcher.h"
 #include "components/search/search.h"
 #include "components/search_engines/template_url.h"
 #include "components/search_engines/template_url_service.h"
@@ -42,6 +43,9 @@
   UIThreadSearchTermsData::SetGoogleBaseURL("https://www.google.com/");
   SetUserSelectedDefaultSearchProvider("{google:baseURL}");
   instant_service_ = InstantServiceFactory::GetForProfile(profile());
+
+  instant_service_->SetImageFetcherForTesting(
+      new testing::NiceMock<image_fetcher::MockImageFetcher>());
 }
 
 void InstantUnitTestBase::TearDown() {
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 9c7f737..78a5018 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -1525,8 +1525,6 @@
       "views/frame/native_browser_frame_factory_chromeos.cc",
       "views/frame/top_controls_slide_controller_chromeos.cc",
       "views/frame/top_controls_slide_controller_chromeos.h",
-      "views/ime_driver/input_method_bridge_chromeos.cc",
-      "views/ime_driver/input_method_bridge_chromeos.h",
       "views/platform_keys_certificate_selector_chromeos.cc",
       "views/platform_keys_certificate_selector_chromeos.h",
       "views/profiles/profile_indicator_icon.cc",
@@ -3073,17 +3071,6 @@
       ]
     }
 
-    if (enable_mus) {
-      sources += [
-        "views/ime_driver/ime_driver_mus.cc",
-        "views/ime_driver/ime_driver_mus.h",
-        "views/ime_driver/remote_text_input_client.cc",
-        "views/ime_driver/remote_text_input_client.h",
-        "views/ime_driver/simple_input_method.cc",
-        "views/ime_driver/simple_input_method.h",
-      ]
-    }
-
     if (is_win) {
       sources += [ "views/chrome_views_delegate_win.cc" ]
     }
diff --git a/chrome/browser/ui/app_list/app_list_client_impl.cc b/chrome/browser/ui/app_list/app_list_client_impl.cc
index ebd5a31..5afe27a 100644
--- a/chrome/browser/ui/app_list/app_list_client_impl.cc
+++ b/chrome/browser/ui/app_list/app_list_client_impl.cc
@@ -225,7 +225,7 @@
 
 void AppListClientImpl::OnFolderCreated(
     int profile_id,
-    ash::mojom::AppListItemMetadataPtr item) {
+    std::unique_ptr<ash::AppListItemMetadata> item) {
   auto* requested_model_updater = profile_model_mappings_[profile_id];
   if (!requested_model_updater)
     return;
@@ -235,7 +235,7 @@
 
 void AppListClientImpl::OnFolderDeleted(
     int profile_id,
-    ash::mojom::AppListItemMetadataPtr item) {
+    std::unique_ptr<ash::AppListItemMetadata> item) {
   auto* requested_model_updater = profile_model_mappings_[profile_id];
   if (!requested_model_updater)
     return;
@@ -243,8 +243,9 @@
   requested_model_updater->OnFolderDeleted(std::move(item));
 }
 
-void AppListClientImpl::OnItemUpdated(int profile_id,
-                                      ash::mojom::AppListItemMetadataPtr item) {
+void AppListClientImpl::OnItemUpdated(
+    int profile_id,
+    std::unique_ptr<ash::AppListItemMetadata> item) {
   auto* requested_model_updater = profile_model_mappings_[profile_id];
   if (!requested_model_updater)
     return;
diff --git a/chrome/browser/ui/app_list/app_list_client_impl.h b/chrome/browser/ui/app_list/app_list_client_impl.h
index ceff688..2796499 100644
--- a/chrome/browser/ui/app_list/app_list_client_impl.h
+++ b/chrome/browser/ui/app_list/app_list_client_impl.h
@@ -70,11 +70,11 @@
   void OnAppListTargetVisibilityChanged(bool visible) override;
   void OnAppListVisibilityChanged(bool visible) override;
   void OnFolderCreated(int profile_id,
-                       ash::mojom::AppListItemMetadataPtr item) override;
+                       std::unique_ptr<ash::AppListItemMetadata> item) override;
   void OnFolderDeleted(int profile_id,
-                       ash::mojom::AppListItemMetadataPtr item) override;
+                       std::unique_ptr<ash::AppListItemMetadata> item) override;
   void OnItemUpdated(int profile_id,
-                     ash::mojom::AppListItemMetadataPtr item) override;
+                     std::unique_ptr<ash::AppListItemMetadata> item) override;
   void OnPageBreakItemAdded(int profile_id,
                             const std::string& id,
                             const syncer::StringOrdinal& position) override;
diff --git a/chrome/browser/ui/app_list/app_list_model_updater.h b/chrome/browser/ui/app_list/app_list_model_updater.h
index aa462f4..52476fa 100644
--- a/chrome/browser/ui/app_list/app_list_model_updater.h
+++ b/chrome/browser/ui/app_list/app_list_model_updater.h
@@ -138,9 +138,12 @@
   virtual bool SearchEngineIsGoogle() = 0;
 
   // Methods for handle model updates in ash:
-  virtual void OnFolderCreated(ash::mojom::AppListItemMetadataPtr item) = 0;
-  virtual void OnFolderDeleted(ash::mojom::AppListItemMetadataPtr item) = 0;
-  virtual void OnItemUpdated(ash::mojom::AppListItemMetadataPtr item) = 0;
+  virtual void OnFolderCreated(
+      std::unique_ptr<ash::AppListItemMetadata> item) = 0;
+  virtual void OnFolderDeleted(
+      std::unique_ptr<ash::AppListItemMetadata> item) = 0;
+  virtual void OnItemUpdated(
+      std::unique_ptr<ash::AppListItemMetadata> item) = 0;
   virtual void OnPageBreakItemAdded(const std::string& id,
                                     const syncer::StringOrdinal& position) = 0;
   virtual void OnPageBreakItemDeleted(const std::string& id) = 0;
diff --git a/chrome/browser/ui/app_list/app_list_syncable_service.h b/chrome/browser/ui/app_list/app_list_syncable_service.h
index f62c45b..b04cbd5 100644
--- a/chrome/browser/ui/app_list/app_list_syncable_service.h
+++ b/chrome/browser/ui/app_list/app_list_syncable_service.h
@@ -11,7 +11,6 @@
 #include <memory>
 #include <string>
 
-#include "ash/public/interfaces/app_list.mojom.h"
 #include "base/callback.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
diff --git a/chrome/browser/ui/app_list/chrome_app_list_item.cc b/chrome/browser/ui/app_list/chrome_app_list_item.cc
index 28c223f..25708f2 100644
--- a/chrome/browser/ui/app_list/chrome_app_list_item.cc
+++ b/chrome/browser/ui/app_list/chrome_app_list_item.cc
@@ -24,13 +24,11 @@
 
 AppListControllerDelegate* g_controller_for_test = nullptr;
 
-ash::mojom::AppListItemMetadataPtr CreateDefaultMetadata(
+std::unique_ptr<ash::AppListItemMetadata> CreateDefaultMetadata(
     const std::string& app_id) {
-  return ash::mojom::AppListItemMetadata::New(
-      app_id, std::string() /* name */, std::string() /* short_name */,
-      std::string() /* folder_id */, syncer::StringOrdinal(),
-      false /* is_folder */, false /* is_persistent */,
-      gfx::ImageSkia() /* icon */, false /* is_page_break */);
+  auto metadata = std::make_unique<ash::AppListItemMetadata>();
+  metadata->id = app_id;
+  return metadata;
 }
 
 }  // namespace
@@ -87,12 +85,13 @@
 }
 
 void ChromeAppListItem::SetMetadata(
-    ash::mojom::AppListItemMetadataPtr metadata) {
+    std::unique_ptr<ash::AppListItemMetadata> metadata) {
   metadata_ = std::move(metadata);
 }
 
-ash::mojom::AppListItemMetadataPtr ChromeAppListItem::CloneMetadata() const {
-  return metadata_.Clone();
+std::unique_ptr<ash::AppListItemMetadata> ChromeAppListItem::CloneMetadata()
+    const {
+  return std::make_unique<ash::AppListItemMetadata>(*metadata_);
 }
 
 void ChromeAppListItem::PerformActivate(int event_flags) {
diff --git a/chrome/browser/ui/app_list/chrome_app_list_item.h b/chrome/browser/ui/app_list/chrome_app_list_item.h
index 4b9a617..3806c06 100644
--- a/chrome/browser/ui/app_list/chrome_app_list_item.h
+++ b/chrome/browser/ui/app_list/chrome_app_list_item.h
@@ -9,6 +9,7 @@
 #include <string>
 #include <utility>
 
+#include "ash/public/cpp/app_list/app_list_types.h"
 #include "base/macros.h"
 #include "chrome/browser/ui/app_list/app_context_menu.h"
 #include "chrome/browser/ui/app_list/app_list_syncable_service.h"
@@ -65,8 +66,8 @@
   void SetIsInstalling(bool is_installing);
   void SetPercentDownloaded(int32_t percent_downloaded);
 
-  void SetMetadata(ash::mojom::AppListItemMetadataPtr metadata);
-  ash::mojom::AppListItemMetadataPtr CloneMetadata() const;
+  void SetMetadata(std::unique_ptr<ash::AppListItemMetadata> metadata);
+  std::unique_ptr<ash::AppListItemMetadata> CloneMetadata() const;
 
   // The following methods set Chrome side data here, and call model updater
   // interfaces that talk to ash directly.
@@ -140,7 +141,7 @@
   void MaybeDismissAppList();
 
  private:
-  ash::mojom::AppListItemMetadataPtr metadata_;
+  std::unique_ptr<ash::AppListItemMetadata> metadata_;
   Profile* profile_;
   AppListModelUpdater* model_updater_ = nullptr;
 
diff --git a/chrome/browser/ui/app_list/chrome_app_list_model_updater.cc b/chrome/browser/ui/app_list/chrome_app_list_model_updater.cc
index 6dbde0e..4c91cff 100644
--- a/chrome/browser/ui/app_list/chrome_app_list_model_updater.cc
+++ b/chrome/browser/ui/app_list/chrome_app_list_model_updater.cc
@@ -35,7 +35,7 @@
     return;
 
   // Activating this model updater should sync the cached model to Ash.
-  std::vector<ash::mojom::AppListItemMetadataPtr> items_to_sync;
+  std::vector<std::unique_ptr<ash::AppListItemMetadata>> items_to_sync;
   for (auto const& item : items_)
     items_to_sync.push_back(item.second->CloneMetadata());
 
@@ -46,7 +46,8 @@
 
 void ChromeAppListModelUpdater::AddItem(
     std::unique_ptr<ChromeAppListItem> app_item) {
-  ash::mojom::AppListItemMetadataPtr item_data = app_item->CloneMetadata();
+  std::unique_ptr<ash::AppListItemMetadata> item_data =
+      app_item->CloneMetadata();
   // Add to Chrome first leave all updates to observer methods.
   AddChromeItem(std::move(app_item));
   if (app_list_controller_)
@@ -56,7 +57,8 @@
 void ChromeAppListModelUpdater::AddItemToFolder(
     std::unique_ptr<ChromeAppListItem> app_item,
     const std::string& folder_id) {
-  ash::mojom::AppListItemMetadataPtr item_data = app_item->CloneMetadata();
+  std::unique_ptr<ash::AppListItemMetadata> item_data =
+      app_item->CloneMetadata();
   // Add to Chrome first leave all updates to observer methods.
   ChromeAppListItem* item_added = AddChromeItem(std::move(app_item));
   item_added->SetChromeFolderId(folder_id);
@@ -197,7 +199,7 @@
   ChromeAppListItem* item = FindItem(id);
   if (!item)
     return;
-  ash::mojom::AppListItemMetadataPtr data = item->CloneMetadata();
+  std::unique_ptr<ash::AppListItemMetadata> data = item->CloneMetadata();
   data->name = name;
   app_list_controller_->SetItemMetadata(id, std::move(data));
 }
@@ -211,7 +213,7 @@
   ChromeAppListItem* item = FindItem(id);
   if (!item)
     return;
-  ash::mojom::AppListItemMetadataPtr data = item->CloneMetadata();
+  std::unique_ptr<ash::AppListItemMetadata> data = item->CloneMetadata();
   data->name = name;
   data->short_name = short_name;
   app_list_controller_->SetItemMetadata(id, std::move(data));
@@ -225,7 +227,7 @@
   ChromeAppListItem* item = FindItem(id);
   if (!item)
     return;
-  ash::mojom::AppListItemMetadataPtr data = item->CloneMetadata();
+  std::unique_ptr<ash::AppListItemMetadata> data = item->CloneMetadata();
   data->position = new_position;
   app_list_controller_->SetItemMetadata(id, std::move(data));
 }
@@ -237,7 +239,7 @@
   ChromeAppListItem* item = FindItem(id);
   if (!item)
     return;
-  ash::mojom::AppListItemMetadataPtr data = item->CloneMetadata();
+  std::unique_ptr<ash::AppListItemMetadata> data = item->CloneMetadata();
   data->is_persistent = is_persistent;
   app_list_controller_->SetItemMetadata(id, std::move(data));
 }
@@ -249,7 +251,7 @@
   ChromeAppListItem* item = FindItem(id);
   if (!item)
     return;
-  ash::mojom::AppListItemMetadataPtr data = item->CloneMetadata();
+  std::unique_ptr<ash::AppListItemMetadata> data = item->CloneMetadata();
   data->folder_id = folder_id;
   app_list_controller_->SetItemMetadata(id, std::move(data));
 }
@@ -407,7 +409,7 @@
       base::BindOnce(
           [](base::WeakPtr<ChromeAppListModelUpdater> self,
              ResolveOemFolderPositionCallback callback,
-             ash::mojom::AppListItemMetadataPtr folder_data) {
+             std::unique_ptr<ash::AppListItemMetadata> folder_data) {
             if (!self)
               return;
             ChromeAppListItem* chrome_oem_folder = nullptr;
@@ -435,8 +437,7 @@
         oem_folder_name, position_to_try,
         base::BindOnce(
             [](base::WeakPtr<ChromeAppListModelUpdater> self,
-               std::unique_ptr<ChromeAppListItem> item,
-               ash::mojom::AppListItemMetadataPtr /* oem_folder */) {
+               std::unique_ptr<ChromeAppListItem> item) {
               if (!self)
                 return;
               self->AddItemToFolder(std::move(item), ash::kOemFolderId);
@@ -502,7 +503,7 @@
 // Methods called from Ash:
 
 void ChromeAppListModelUpdater::OnFolderCreated(
-    ash::mojom::AppListItemMetadataPtr item) {
+    std::unique_ptr<ash::AppListItemMetadata> item) {
   DCHECK(item->is_folder);
   ChromeAppListItem* chrome_item = FindItem(item->id);
   // If the item already exists, we should have set its information properly.
@@ -520,7 +521,7 @@
 }
 
 void ChromeAppListModelUpdater::OnFolderDeleted(
-    ash::mojom::AppListItemMetadataPtr item) {
+    std::unique_ptr<ash::AppListItemMetadata> item) {
   DCHECK(item->is_folder);
 
   ChromeAppListItem* chrome_item = FindItem(item->id);
@@ -534,7 +535,7 @@
 }
 
 void ChromeAppListModelUpdater::OnItemUpdated(
-    ash::mojom::AppListItemMetadataPtr item) {
+    std::unique_ptr<ash::AppListItemMetadata> item) {
   ChromeAppListItem* chrome_item = FindItem(item->id);
 
   // Ignore the item if it does not exist. This happens when a race occurs
diff --git a/chrome/browser/ui/app_list/chrome_app_list_model_updater.h b/chrome/browser/ui/app_list/chrome_app_list_model_updater.h
index a84b6b2..f172275 100644
--- a/chrome/browser/ui/app_list/chrome_app_list_model_updater.h
+++ b/chrome/browser/ui/app_list/chrome_app_list_model_updater.h
@@ -108,9 +108,9 @@
       bool update_folder) override;
 
   // Methods to handle model update from ash:
-  void OnFolderCreated(ash::mojom::AppListItemMetadataPtr item) override;
-  void OnFolderDeleted(ash::mojom::AppListItemMetadataPtr item) override;
-  void OnItemUpdated(ash::mojom::AppListItemMetadataPtr item) override;
+  void OnFolderCreated(std::unique_ptr<ash::AppListItemMetadata> item) override;
+  void OnFolderDeleted(std::unique_ptr<ash::AppListItemMetadata> item) override;
+  void OnItemUpdated(std::unique_ptr<ash::AppListItemMetadata> item) override;
   void OnPageBreakItemAdded(const std::string& id,
                             const syncer::StringOrdinal& position) override;
   void OnPageBreakItemDeleted(const std::string& id) override;
diff --git a/chrome/browser/ui/app_list/crostini/crostini_app_model_builder_unittest.cc b/chrome/browser/ui/app_list/crostini/crostini_app_model_builder_unittest.cc
index 2ff8198..2bf52ee 100644
--- a/chrome/browser/ui/app_list/crostini/crostini_app_model_builder_unittest.cc
+++ b/chrome/browser/ui/app_list/crostini/crostini_app_model_builder_unittest.cc
@@ -315,8 +315,7 @@
 
   // We simulate ash creating the crostini folder and calling back into chrome
   // (rather than use a full browser test).
-  ash::mojom::AppListItemMetadataPtr metadata =
-      ash::mojom::AppListItemMetadata::New();
+  auto metadata = std::make_unique<ash::AppListItemMetadata>();
   metadata->id = crostini::kCrostiniFolderId;
   GetModelUpdater()->OnFolderCreated(std::move(metadata));
 
diff --git a/chrome/browser/ui/app_list/test/chrome_app_list_test_support.cc b/chrome/browser/ui/app_list/test/chrome_app_list_test_support.cc
index 4dfcc4c2..b4c2de4 100644
--- a/chrome/browser/ui/app_list/test/chrome_app_list_test_support.cc
+++ b/chrome/browser/ui/app_list/test/chrome_app_list_test_support.cc
@@ -4,9 +4,11 @@
 
 #include "chrome/browser/ui/app_list/test/chrome_app_list_test_support.h"
 
+#include <memory>
 #include <string>
 
 #include "ash/public/cpp/app_list/app_list_config.h"
+#include "ash/public/cpp/app_list/app_list_types.h"
 #include "ash/public/interfaces/app_list_view.mojom.h"
 #include "base/bind.h"
 #include "base/macros.h"
@@ -97,11 +99,12 @@
     const std::string app_id = crx_file::id_util::GenerateId(app_name);
     auto item =
         std::make_unique<ChromeAppListItem>(profile, app_id, model_updater);
-    gfx::ImageSkia icon = CreateImageSkia(i);
-    item->SetMetadata(ash::mojom::AppListItemMetadata::New(
-        app_id, app_name, /*short_name=*/app_name, /*folder_id=*/"",
-        syncer::StringOrdinal(app_id), /*is_folder=*/false,
-        /*is_persistent=*/false, icon, /*is_page_break=*/false));
+    auto metadata = std::make_unique<ash::AppListItemMetadata>();
+    metadata->id = app_id;
+    metadata->name = app_name;
+    metadata->short_name = app_name;
+    metadata->icon = CreateImageSkia(i);
+    item->SetMetadata(std::move(metadata));
     model_updater->AddItem(std::move(item));
   }
   // Wait for the AddItem mojo calls to be handled by Ash. Note that
diff --git a/chrome/browser/ui/app_list/test/fake_app_list_model_updater.cc b/chrome/browser/ui/app_list/test/fake_app_list_model_updater.cc
index 6fded37..4ce990f 100644
--- a/chrome/browser/ui/app_list/test/fake_app_list_model_updater.cc
+++ b/chrome/browser/ui/app_list/test/fake_app_list_model_updater.cc
@@ -144,13 +144,12 @@
   search_results_ = results;
 }
 
-ash::mojom::AppListItemMetadataPtr
-FakeAppListModelUpdater::FindOrCreateOemFolder(
+void FakeAppListModelUpdater::FindOrCreateOemFolder(
     const std::string& oem_folder_name,
     const syncer::StringOrdinal& preferred_oem_position) {
   ChromeAppListItem* oem_folder = FindFolderItem(ash::kOemFolderId);
   if (oem_folder) {
-    ash::mojom::AppListItemMetadataPtr folder_data =
+    std::unique_ptr<ash::AppListItemMetadata> folder_data =
         oem_folder->CloneMetadata();
     folder_data->name = oem_folder_name;
     oem_folder->SetMetadata(std::move(folder_data));
@@ -159,7 +158,7 @@
         std::make_unique<ChromeAppListItem>(nullptr, ash::kOemFolderId,
                                             nullptr);
     oem_folder = new_folder.get();
-    ash::mojom::AppListItemMetadataPtr folder_data =
+    std::unique_ptr<ash::AppListItemMetadata> folder_data =
         oem_folder->CloneMetadata();
     folder_data->position = preferred_oem_position.IsValid()
                                 ? preferred_oem_position
@@ -168,7 +167,6 @@
     oem_folder->SetMetadata(std::move(folder_data));
     AddItem(std::move(new_folder));
   }
-  return oem_folder->CloneMetadata();
 }
 
 syncer::StringOrdinal FakeAppListModelUpdater::GetOemFolderPos() {
@@ -216,7 +214,7 @@
 }
 
 void FakeAppListModelUpdater::OnFolderCreated(
-    ash::mojom::AppListItemMetadataPtr folder) {
+    std::unique_ptr<ash::AppListItemMetadata> folder) {
   std::unique_ptr<ChromeAppListItem> stub_folder =
       std::make_unique<ChromeAppListItem>(profile_, folder->id, this);
 
diff --git a/chrome/browser/ui/app_list/test/fake_app_list_model_updater.h b/chrome/browser/ui/app_list/test/fake_app_list_model_updater.h
index 97f4620..50ad427 100644
--- a/chrome/browser/ui/app_list/test/fake_app_list_model_updater.h
+++ b/chrome/browser/ui/app_list/test/fake_app_list_model_updater.h
@@ -62,9 +62,11 @@
     return search_results_;
   }
 
-  void OnFolderCreated(ash::mojom::AppListItemMetadataPtr folder) override;
-  void OnFolderDeleted(ash::mojom::AppListItemMetadataPtr item) override {}
-  void OnItemUpdated(ash::mojom::AppListItemMetadataPtr item) override {}
+  void OnFolderCreated(
+      std::unique_ptr<ash::AppListItemMetadata> folder) override;
+  void OnFolderDeleted(
+      std::unique_ptr<ash::AppListItemMetadata> item) override {}
+  void OnItemUpdated(std::unique_ptr<ash::AppListItemMetadata> item) override {}
   void OnPageBreakItemAdded(const std::string& id,
                             const syncer::StringOrdinal& position) override {}
   void OnPageBreakItemDeleted(const std::string& id) override {}
@@ -79,7 +81,7 @@
   base::ObserverList<AppListModelUpdaterObserver> observers_;
   Profile* profile_;
 
-  ash::mojom::AppListItemMetadataPtr FindOrCreateOemFolder(
+  void FindOrCreateOemFolder(
       const std::string& oem_folder_name,
       const syncer::StringOrdinal& preferred_oem_position);
   syncer::StringOrdinal GetOemFolderPos();
diff --git a/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc b/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc
index efb656b..fc67f4c 100644
--- a/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc
+++ b/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc
@@ -43,7 +43,6 @@
 #include "chrome/browser/ui/ash/tablet_mode_client.h"
 #include "chrome/browser/ui/ash/vpn_list_forwarder.h"
 #include "chrome/browser/ui/ash/wallpaper_controller_client.h"
-#include "chrome/browser/ui/views/ime_driver/ime_driver_mus.h"
 #include "chrome/browser/ui/views/select_file_dialog_extension.h"
 #include "chrome/browser/ui/views/select_file_dialog_extension_factory.h"
 #include "chromeos/network/network_connect.h"
@@ -62,7 +61,6 @@
 #include "content/public/common/service_manager_connection.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "services/service_manager/public/cpp/connector.h"
-#include "services/ws/public/mojom/user_activity_monitor.mojom.h"
 #include "ui/base/ime/chromeos/input_method_manager.h"
 #include "ui/base/ui_base_features.h"
 
@@ -120,9 +118,6 @@
 }
 
 void ChromeBrowserMainExtraPartsAsh::PreProfileInit() {
-  // IME driver must be available at login screen, so initialize before profile.
-  IMEDriverMus::Register();
-
   // NetworkConnect handles the network connection state machine for the UI.
   network_connect_delegate_ =
       std::make_unique<NetworkConnectDelegateChromeOS>();
diff --git a/chrome/browser/ui/ash/keyboard/keyboard_controller_browsertest.cc b/chrome/browser/ui/ash/keyboard/keyboard_controller_browsertest.cc
index 4451059..6e6de86f 100644
--- a/chrome/browser/ui/ash/keyboard/keyboard_controller_browsertest.cc
+++ b/chrome/browser/ui/ash/keyboard/keyboard_controller_browsertest.cc
@@ -378,11 +378,9 @@
     return;
 
   auto* controller = keyboard::KeyboardController::Get();
-  EXPECT_EQ(controller->GetStateForTest(),
-            keyboard::KeyboardControllerState::kLoadingExtension);
+  EXPECT_EQ(controller->GetStateForTest(), keyboard::KeyboardUIState::kLoading);
   KeyboardLoadedWaiter().Wait();
-  EXPECT_EQ(controller->GetStateForTest(),
-            keyboard::KeyboardControllerState::kHidden);
+  EXPECT_EQ(controller->GetStateForTest(), keyboard::KeyboardUIState::kHidden);
 }
 
 IN_PROC_BROWSER_TEST_F(KeyboardControllerStateTest,
@@ -393,18 +391,15 @@
   auto* controller = keyboard::KeyboardController::Get();
   controller->ShowKeyboard(false);
   // Need to wait the extension to be loaded. Hence LOADING_EXTENSION.
-  EXPECT_EQ(controller->GetStateForTest(),
-            keyboard::KeyboardControllerState::kLoadingExtension);
+  EXPECT_EQ(controller->GetStateForTest(), keyboard::KeyboardUIState::kLoading);
   KeyboardVisibleWaiter(true).Wait();
 
   controller->HideKeyboardExplicitlyBySystem();
-  EXPECT_EQ(controller->GetStateForTest(),
-            keyboard::KeyboardControllerState::kHidden);
+  EXPECT_EQ(controller->GetStateForTest(), keyboard::KeyboardUIState::kHidden);
 
   controller->ShowKeyboard(false);
   // The extension already has been loaded. Hence SHOWING.
-  EXPECT_EQ(controller->GetStateForTest(),
-            keyboard::KeyboardControllerState::kShown);
+  EXPECT_EQ(controller->GetStateForTest(), keyboard::KeyboardUIState::kShown);
 }
 
 // See crbug.com/755354.
@@ -415,10 +410,8 @@
 
   auto* controller = keyboard::KeyboardController::Get();
 
-  EXPECT_EQ(controller->GetStateForTest(),
-            keyboard::KeyboardControllerState::kLoadingExtension);
+  EXPECT_EQ(controller->GetStateForTest(), keyboard::KeyboardUIState::kLoading);
 
   controller->Shutdown();
-  EXPECT_EQ(controller->GetStateForTest(),
-            keyboard::KeyboardControllerState::kInitial);
+  EXPECT_EQ(controller->GetStateForTest(), keyboard::KeyboardUIState::kInitial);
 }
diff --git a/chrome/browser/ui/input_method/input_method_engine.cc b/chrome/browser/ui/input_method/input_method_engine.cc
index cc13e14..5ab21b2d 100644
--- a/chrome/browser/ui/input_method/input_method_engine.cc
+++ b/chrome/browser/ui/input_method/input_method_engine.cc
@@ -89,6 +89,14 @@
   }
 }
 
+bool InputMethodEngine::SetCompositionRange(
+    uint32_t before,
+    uint32_t after,
+    const std::vector<ui::ImeTextSpan>& text_spans) {
+  // Not supported on non-Chrome OS platforms.
+  return false;
+}
+
 void InputMethodEngine::CommitTextToInputContext(int context_id,
                                                  const std::string& text) {
   ui::IMEInputContextHandlerInterface* input_context =
diff --git a/chrome/browser/ui/input_method/input_method_engine.h b/chrome/browser/ui/input_method/input_method_engine.h
index 29b3433..9293e0d 100644
--- a/chrome/browser/ui/input_method/input_method_engine.h
+++ b/chrome/browser/ui/input_method/input_method_engine.h
@@ -31,6 +31,10 @@
   void UpdateComposition(const ui::CompositionText& composition_text,
                          uint32_t cursor_pos,
                          bool is_visible) override;
+  bool SetCompositionRange(
+      uint32_t before,
+      uint32_t after,
+      const std::vector<ui::ImeTextSpan>& text_spans) override;
   void CommitTextToInputContext(int context_id,
                                 const std::string& text) override;
   void DeleteSurroundingTextToInputContext(int offset,
diff --git a/chrome/browser/ui/input_method/input_method_engine_base.cc b/chrome/browser/ui/input_method/input_method_engine_base.cc
index 84d09486..8df1347 100644
--- a/chrome/browser/ui/input_method/input_method_engine_base.cc
+++ b/chrome/browser/ui/input_method/input_method_engine_base.cc
@@ -404,6 +404,50 @@
   return true;
 }
 
+bool InputMethodEngineBase::SetCompositionRange(
+    int context_id,
+    int selection_before,
+    int selection_after,
+    const std::vector<SegmentInfo>& segments,
+    std::string* error) {
+  if (!IsActive()) {
+    *error = kErrorNotActive;
+    return false;
+  }
+  if (context_id != context_id_ || context_id_ == -1) {
+    *error = kErrorWrongContext;
+    return false;
+  }
+
+  // Can't change composition range when there's pending composition text.
+  if (!composition_text_->text.empty())
+    return false;
+
+  std::vector<ui::ImeTextSpan> text_spans;
+  for (const auto& segment : segments) {
+    ui::ImeTextSpan text_span;
+
+    text_span.underline_color = SK_ColorTRANSPARENT;
+    switch (segment.style) {
+      case SEGMENT_STYLE_UNDERLINE:
+        text_span.thickness = ui::ImeTextSpan::Thickness::kThin;
+        break;
+      case SEGMENT_STYLE_DOUBLE_UNDERLINE:
+        text_span.thickness = ui::ImeTextSpan::Thickness::kThick;
+        break;
+      case SEGMENT_STYLE_NO_UNDERLINE:
+        text_span.thickness = ui::ImeTextSpan::Thickness::kNone;
+        break;
+    }
+
+    text_span.start_offset = segment.start;
+    text_span.end_offset = segment.end;
+    text_spans.push_back(text_span);
+  }
+
+  return SetCompositionRange(selection_before, selection_after, text_spans);
+}
+
 void InputMethodEngineBase::KeyEventHandled(const std::string& extension_id,
                                             const std::string& request_id,
                                             bool handled) {
diff --git a/chrome/browser/ui/input_method/input_method_engine_base.h b/chrome/browser/ui/input_method/input_method_engine_base.h
index 9855ad31..fb8757d 100644
--- a/chrome/browser/ui/input_method/input_method_engine_base.h
+++ b/chrome/browser/ui/input_method/input_method_engine_base.h
@@ -183,6 +183,14 @@
                       int cursor,
                       const std::vector<SegmentInfo>& segments,
                       std::string* error);
+
+  // Set the current composition range.
+  bool SetCompositionRange(int context_id,
+                           int selection_before,
+                           int selection_after,
+                           const std::vector<SegmentInfo>& segments,
+                           std::string* error);
+
   // Called when a key event is handled.
   void KeyEventHandled(const std::string& extension_id,
                        const std::string& request_id,
@@ -208,6 +216,11 @@
   virtual void UpdateComposition(const ui::CompositionText& composition_text,
                                  uint32_t cursor_pos,
                                  bool is_visible) = 0;
+  // Notifies InputContextHandler to change the composition range.
+  virtual bool SetCompositionRange(
+      uint32_t before,
+      uint32_t after,
+      const std::vector<ui::ImeTextSpan>& text_spans) = 0;
   // Notifies InputContextHanlder to commit |text|.
   virtual void CommitTextToInputContext(int context_id,
                                         const std::string& text) = 0;
diff --git a/chrome/browser/ui/search/local_ntp_backgrounds_browsertest.cc b/chrome/browser/ui/search/local_ntp_backgrounds_browsertest.cc
index 4fdcc11..b35c9ed 100644
--- a/chrome/browser/ui/search/local_ntp_backgrounds_browsertest.cc
+++ b/chrome/browser/ui/search/local_ntp_backgrounds_browsertest.cc
@@ -21,15 +21,27 @@
 #include "chrome/common/search/instant_types.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/test/base/in_process_browser_test.h"
+#include "components/image_fetcher/core/mock_image_fetcher.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/test_utils.h"
 #include "extensions/browser/extension_registry.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-namespace {
-
 using LocalNTPCustomBackgroundsTest = InProcessBrowserTest;
 
+class TestInstantService {
+ public:
+  explicit TestInstantService(Profile* profile) {
+    instant_service = InstantServiceFactory::GetForProfile(profile);
+    instant_service->SetImageFetcherForTesting(
+        new testing::NiceMock<image_fetcher::MockImageFetcher>());
+  }
+  InstantService* get_instant_service() { return instant_service; }
+
+ private:
+  InstantService* instant_service;
+};
+
 IN_PROC_BROWSER_TEST_F(LocalNTPCustomBackgroundsTest,
                        EmbeddedSearchAPIEndToEnd) {
   content::WebContents* active_tab =
@@ -41,9 +53,9 @@
   local_ntp_test_utils::NavigateToNTPAndWaitUntilLoaded(browser());
 
   // Check that a URL with no attributions can be set.
-  InstantService* instant_service =
-      InstantServiceFactory::GetForProfile(browser()->profile());
-  instant_service->AddValidBackdropUrlForTesting(GURL("https://www.test.com/"));
+  TestInstantService test_instant_service(browser()->profile());
+  test_instant_service.get_instant_service()->AddValidBackdropUrlForTesting(
+      GURL("https://www.test.com/"));
   EXPECT_TRUE(content::ExecuteScript(active_tab,
                                      "window.chrome.embeddedSearch.newTabPage."
                                      "setBackgroundURL('https://www.test.com/"
@@ -76,9 +88,9 @@
   local_ntp_test_utils::NavigateToNTPAndWaitUntilLoaded(browser());
 
   // Set a custom background attribution via the EmbeddedSearch API.
-  InstantService* instant_service =
-      InstantServiceFactory::GetForProfile(browser()->profile());
-  instant_service->AddValidBackdropUrlForTesting(GURL("https://www.test.com/"));
+  TestInstantService test_instant_service(browser()->profile());
+  test_instant_service.get_instant_service()->AddValidBackdropUrlForTesting(
+      GURL("https://www.test.com/"));
   EXPECT_TRUE(content::ExecuteScript(active_tab,
                                      "window.chrome.embeddedSearch.newTabPage."
                                      "setBackgroundURLWithAttributions('https:/"
@@ -120,9 +132,8 @@
   local_ntp_test_utils::NavigateToNTPAndWaitUntilLoaded(browser());
 
   // Set a custom background image via the EmbeddedSearch API.
-  InstantService* instant_service =
-      InstantServiceFactory::GetForProfile(browser()->profile());
-  instant_service->AddValidBackdropUrlForTesting(
+  TestInstantService test_instant_service(browser()->profile());
+  test_instant_service.get_instant_service()->AddValidBackdropUrlForTesting(
       GURL("chrome-search://local-ntp/background1.jpg"));
   EXPECT_TRUE(content::ExecuteScript(
       active_tab,
@@ -166,9 +177,8 @@
   local_ntp_test_utils::NavigateToNTPAndWaitUntilLoaded(browser());
 
   // Set a custom background image via the EmbeddedSearch API.
-  InstantService* instant_service =
-      InstantServiceFactory::GetForProfile(browser()->profile());
-  instant_service->AddValidBackdropUrlForTesting(
+  TestInstantService test_instant_service(browser()->profile());
+  test_instant_service.get_instant_service()->AddValidBackdropUrlForTesting(
       GURL("chrome-search://local-ntp/background1.jpg"));
   ASSERT_TRUE(content::ExecuteScript(
       active_tab,
@@ -268,9 +278,9 @@
   local_ntp_test_utils::NavigateToNTPAndWaitUntilLoaded(browser());
 
   // Set a custom background attribution via the EmbeddedSearch API.
-  InstantService* instant_service =
-      InstantServiceFactory::GetForProfile(profile());
-  instant_service->AddValidBackdropUrlForTesting(GURL("https://www.test.com/"));
+  TestInstantService test_instant_service(browser()->profile());
+  test_instant_service.get_instant_service()->AddValidBackdropUrlForTesting(
+      GURL("https://www.test.com/"));
   ASSERT_TRUE(content::ExecuteScript(active_tab,
                                      "window.chrome.embeddedSearch.newTabPage."
                                      "setBackgroundURLWithAttributions('https:/"
@@ -327,9 +337,8 @@
   local_ntp_test_utils::NavigateToNTPAndWaitUntilLoaded(browser());
 
   // Set a custom background image via the EmbeddedSearch API.
-  InstantService* instant_service =
-      InstantServiceFactory::GetForProfile(profile());
-  instant_service->AddValidBackdropUrlForTesting(
+  TestInstantService test_instant_service(browser()->profile());
+  test_instant_service.get_instant_service()->AddValidBackdropUrlForTesting(
       GURL("chrome-search://local-ntp/background1.jpg"));
   ASSERT_TRUE(content::ExecuteScript(
       active_tab,
@@ -392,9 +401,8 @@
   EXPECT_TRUE(result);
 
   // Set a custom background image via the EmbeddedSearch API.
-  InstantService* instant_service =
-      InstantServiceFactory::GetForProfile(profile());
-  instant_service->AddValidBackdropUrlForTesting(
+  TestInstantService test_instant_service(browser()->profile());
+  test_instant_service.get_instant_service()->AddValidBackdropUrlForTesting(
       GURL("chrome-search://local-ntp/background1.jpg"));
   ASSERT_TRUE(content::ExecuteScript(
       active_tab,
@@ -440,6 +448,8 @@
         InstantServiceFactory::GetForProfile(browser()->profile());
     theme()->SetDarkMode(true);
     instant_service->SetDarkModeThemeForTesting(theme());
+    instant_service->SetImageFetcherForTesting(
+        new testing::NiceMock<image_fetcher::MockImageFetcher>());
   }
 
   InstantService* instant_service;
@@ -534,5 +544,3 @@
   EXPECT_TRUE(GetIsDarkModeApplied(active_tab));
   EXPECT_TRUE(GetIsLightChipsApplied(active_tab));
 }
-
-}  // namespace
diff --git a/chrome/browser/ui/views/chrome_browser_main_extra_parts_views.cc b/chrome/browser/ui/views/chrome_browser_main_extra_parts_views.cc
index d762118..79581831 100644
--- a/chrome/browser/ui/views/chrome_browser_main_extra_parts_views.cc
+++ b/chrome/browser/ui/views/chrome_browser_main_extra_parts_views.cc
@@ -27,7 +27,6 @@
 #include "content/public/common/service_manager_connection.h"
 #include "services/service_manager/public/cpp/connector.h"
 #include "services/ws/public/cpp/gpu/gpu.h"  // nogncheck
-#include "services/ws/public/mojom/constants.mojom.h"
 #include "ui/display/screen.h"
 #include "ui/views/widget/desktop_aura/desktop_screen.h"
 #include "ui/wm/core/wm_state.h"
diff --git a/chrome/browser/ui/views/extensions/extensions_toolbar_container.cc b/chrome/browser/ui/views/extensions/extensions_toolbar_container.cc
index 7a1e16e2..3a42b08 100644
--- a/chrome/browser/ui/views/extensions/extensions_toolbar_container.cc
+++ b/chrome/browser/ui/views/extensions/extensions_toolbar_container.cc
@@ -11,7 +11,8 @@
 #include "chrome/browser/ui/views/extensions/extensions_toolbar_button.h"
 
 ExtensionsToolbarContainer::ExtensionsToolbarContainer(Browser* browser)
-    : browser_(browser),
+    : ToolbarIconContainerView(/*uses_highlight=*/true),
+      browser_(browser),
       model_(ToolbarActionsModel::Get(browser_->profile())),
       model_observer_(this),
       extensions_button_(new ExtensionsToolbarButton(browser_, this)) {
diff --git a/chrome/browser/ui/views/ime_driver/OWNERS b/chrome/browser/ui/views/ime_driver/OWNERS
deleted file mode 100644
index 1d9f31f..0000000
--- a/chrome/browser/ui/views/ime_driver/OWNERS
+++ /dev/null
@@ -1,5 +0,0 @@
-# For mash / mustash.
-xiyuan@chromium.org
-
-# For general IME.
-file://ui/base/ime/OWNERS
diff --git a/chrome/browser/ui/views/ime_driver/ime_driver_mus.cc b/chrome/browser/ui/views/ime_driver/ime_driver_mus.cc
deleted file mode 100644
index a7e2d97..0000000
--- a/chrome/browser/ui/views/ime_driver/ime_driver_mus.cc
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/views/ime_driver/ime_driver_mus.h"
-
-#include <memory>
-#include <utility>
-
-#include "chrome/browser/ui/views/ime_driver/remote_text_input_client.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/common/service_manager_connection.h"
-#include "mojo/public/cpp/bindings/strong_binding.h"
-#include "services/service_manager/public/cpp/connector.h"
-#include "services/ws/public/mojom/constants.mojom.h"
-#include "services/ws/public/mojom/ime/ime.mojom.h"
-#include "ui/base/ime/ime_bridge.h"
-
-#if defined(OS_CHROMEOS)
-#include "chrome/browser/ui/views/ime_driver/input_method_bridge_chromeos.h"
-#else
-#include "chrome/browser/ui/views/ime_driver/simple_input_method.h"
-#endif  // defined(OS_CHROMEOS)
-
-IMEDriverMus::IMEDriverMus() {
-  ui::IMEBridge::Initialize();
-}
-
-IMEDriverMus::~IMEDriverMus() {}
-
-// static
-void IMEDriverMus::Register() {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  ws::mojom::IMEDriverPtr ime_driver_ptr;
-  mojo::MakeStrongBinding(std::make_unique<IMEDriverMus>(),
-                          MakeRequest(&ime_driver_ptr));
-  ws::mojom::IMERegistrarPtr ime_registrar;
-  content::ServiceManagerConnection::GetForProcess()
-      ->GetConnector()
-      ->BindInterface(ws::mojom::kServiceName, &ime_registrar);
-  ime_registrar->RegisterDriver(std::move(ime_driver_ptr));
-}
-
-void IMEDriverMus::StartSession(
-    ws::mojom::InputMethodRequest input_method_request,
-    ws::mojom::TextInputClientPtr client,
-    ws::mojom::SessionDetailsPtr details) {
-#if defined(OS_CHROMEOS)
-  std::unique_ptr<RemoteTextInputClient> remote_client =
-      std::make_unique<RemoteTextInputClient>(std::move(client),
-                                              std::move(details));
-  mojo::MakeStrongBinding(
-      std::make_unique<InputMethodBridge>(std::move(remote_client)),
-      std::move(input_method_request));
-#else
-  mojo::MakeStrongBinding(std::make_unique<SimpleInputMethod>(),
-                          std::move(input_method_request));
-#endif
-}
diff --git a/chrome/browser/ui/views/ime_driver/ime_driver_mus.h b/chrome/browser/ui/views/ime_driver/ime_driver_mus.h
deleted file mode 100644
index 53ed4ba..0000000
--- a/chrome/browser/ui/views/ime_driver/ime_driver_mus.h
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_VIEWS_IME_DRIVER_IME_DRIVER_MUS_H_
-#define CHROME_BROWSER_UI_VIEWS_IME_DRIVER_IME_DRIVER_MUS_H_
-
-#include "base/macros.h"
-#include "services/ws/public/mojom/ime/ime.mojom.h"
-
-// Creates an InputMethodBridge when an IME session is started via mojo.
-class IMEDriverMus : public ws::mojom::IMEDriver {
- public:
-  IMEDriverMus();
-  ~IMEDriverMus() override;
-
-  // Instantiate the IME driver and register it to the UI service.
-  static void Register();
-
- private:
-  // ws::mojom::IMEDriver:
-  void StartSession(ws::mojom::InputMethodRequest input_method_request,
-                    ws::mojom::TextInputClientPtr client,
-                    ws::mojom::SessionDetailsPtr details) override;
-
-  DISALLOW_COPY_AND_ASSIGN(IMEDriverMus);
-};
-
-#endif  // CHROME_BROWSER_UI_VIEWS_IME_DRIVER_IME_DRIVER_MUS_H_
diff --git a/chrome/browser/ui/views/ime_driver/input_method_bridge_chromeos.cc b/chrome/browser/ui/views/ime_driver/input_method_bridge_chromeos.cc
deleted file mode 100644
index 18470f0..0000000
--- a/chrome/browser/ui/views/ime_driver/input_method_bridge_chromeos.cc
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/views/ime_driver/input_method_bridge_chromeos.h"
-
-#include <memory>
-#include <utility>
-
-#include "chrome/browser/chromeos/accessibility/accessibility_input_method_observer.h"
-#include "chrome/browser/ui/views/ime_driver/remote_text_input_client.h"
-#include "ui/base/ime/chromeos/input_method_chromeos.h"
-#include "ui/base/ime/ime_bridge.h"
-
-namespace {
-
-bool IsActiveInputContextHandler(ui::InputMethodChromeOS* input_method) {
-  ui::IMEBridge* bridge = ui::IMEBridge::Get();
-  return bridge && bridge->GetInputContextHandler() == input_method;
-}
-
-}  // namespace
-
-InputMethodBridge::InputMethodBridge(
-    std::unique_ptr<RemoteTextInputClient> client)
-    : client_(std::move(client)),
-      input_method_chromeos_(
-          std::make_unique<ui::InputMethodChromeOS>(client_.get())),
-      accessibility_input_method_observer_(
-          std::make_unique<AccessibilityInputMethodObserver>(
-              input_method_chromeos_.get())) {
-  input_method_chromeos_->OnFocus();
-  input_method_chromeos_->SetFocusedTextInputClient(client_.get());
-}
-
-InputMethodBridge::~InputMethodBridge() {
-  // IME session is ending.
-  if (IsActiveInputContextHandler(input_method_chromeos_.get()))
-    accessibility_input_method_observer_->ResetCaretBounds();
-}
-
-void InputMethodBridge::OnTextInputStateChanged(
-    ws::mojom::TextInputStatePtr text_input_state) {
-  client_->SetTextInputState(std::move(text_input_state));
-
-  if (IsActiveInputContextHandler(input_method_chromeos_.get()))
-    input_method_chromeos_->OnTextInputTypeChanged(client_.get());
-}
-
-void InputMethodBridge::OnCaretBoundsChanged(const gfx::Rect& caret_bounds) {
-  client_->SetCaretBounds(caret_bounds);
-
-  if (IsActiveInputContextHandler(input_method_chromeos_.get()))
-    input_method_chromeos_->OnCaretBoundsChanged(client_.get());
-}
-
-void InputMethodBridge::OnTextInputClientDataChanged(
-    ws::mojom::TextInputClientDataPtr data) {
-  client_->SetTextInputClientData(std::move(data));
-}
-
-void InputMethodBridge::ProcessKeyEvent(std::unique_ptr<ui::Event> event,
-                                        ProcessKeyEventCallback callback) {
-  DCHECK(event->IsKeyEvent());
-  ui::KeyEvent* key_event = event->AsKeyEvent();
-  if (IsActiveInputContextHandler(input_method_chromeos_.get()) &&
-      !key_event->is_char()) {
-    input_method_chromeos_->DispatchKeyEventAsync(key_event,
-                                                  std::move(callback));
-  } else {
-    const bool handled = false;
-    std::move(callback).Run(handled);
-  }
-}
-
-void InputMethodBridge::CancelComposition() {
-  if (IsActiveInputContextHandler(input_method_chromeos_.get()))
-    input_method_chromeos_->CancelComposition(client_.get());
-}
-
-void InputMethodBridge::ShowVirtualKeyboardIfEnabled() {
-  if (IsActiveInputContextHandler(input_method_chromeos_.get()))
-    input_method_chromeos_->ShowVirtualKeyboardIfEnabled();
-}
diff --git a/chrome/browser/ui/views/ime_driver/input_method_bridge_chromeos.h b/chrome/browser/ui/views/ime_driver/input_method_bridge_chromeos.h
deleted file mode 100644
index b98d214..0000000
--- a/chrome/browser/ui/views/ime_driver/input_method_bridge_chromeos.h
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_VIEWS_IME_DRIVER_INPUT_METHOD_BRIDGE_CHROMEOS_H_
-#define CHROME_BROWSER_UI_VIEWS_IME_DRIVER_INPUT_METHOD_BRIDGE_CHROMEOS_H_
-
-#include "chrome/browser/ui/views/ime_driver/remote_text_input_client.h"
-#include "services/ws/public/mojom/ime/ime.mojom.h"
-
-class AccessibilityInputMethodObserver;
-
-namespace ui {
-class InputMethodChromeOS;
-}
-
-// This bridges between mojo InputMethod API and ui::InputMethodChromeOS. It
-// forwards the received events to an instance of ui::InputMethodChromeOS.
-// Under mash this object is created and destroyed as top-level windows gain
-// focus and start IME sessions.
-// NOTE: There may be multiple instances of InputMethodChromeOS. In classic ash
-// there is one instance shared by ash and browser, plus one per remote app
-// (e.g. shortcut viewer).
-class InputMethodBridge : public ws::mojom::InputMethod {
- public:
-  explicit InputMethodBridge(std::unique_ptr<RemoteTextInputClient> client);
-  ~InputMethodBridge() override;
-
-  // ws::mojom::InputMethod:
-  void OnTextInputStateChanged(
-      ws::mojom::TextInputStatePtr text_input_state) override;
-  void OnCaretBoundsChanged(const gfx::Rect& caret_bounds) override;
-  void OnTextInputClientDataChanged(
-      ws::mojom::TextInputClientDataPtr data) override;
-  void ProcessKeyEvent(std::unique_ptr<ui::Event> key_event,
-                       ProcessKeyEventCallback callback) override;
-  void CancelComposition() override;
-  void ShowVirtualKeyboardIfEnabled() override;
-
- private:
-  std::unique_ptr<RemoteTextInputClient> client_;
-  std::unique_ptr<ui::InputMethodChromeOS> input_method_chromeos_;
-  // Must be destroyed before |input_method_chromeos_|.
-  std::unique_ptr<AccessibilityInputMethodObserver>
-      accessibility_input_method_observer_;
-
-  DISALLOW_COPY_AND_ASSIGN(InputMethodBridge);
-};
-
-#endif  // CHROME_BROWSER_UI_VIEWS_IME_DRIVER_INPUT_METHOD_BRIDGE_CHROMEOS_H_
diff --git a/chrome/browser/ui/views/ime_driver/input_method_bridge_chromeos_unittest.cc b/chrome/browser/ui/views/ime_driver/input_method_bridge_chromeos_unittest.cc
deleted file mode 100644
index e44aa19bb..0000000
--- a/chrome/browser/ui/views/ime_driver/input_method_bridge_chromeos_unittest.cc
+++ /dev/null
@@ -1,275 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <stdint.h>
-
-#include <memory>
-
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/run_loop.h"
-#include "base/stl_util.h"
-#include "base/strings/utf_string_conversions.h"
-#include "chrome/browser/ui/views/ime_driver/input_method_bridge_chromeos.h"
-#include "content/public/test/test_browser_thread_bundle.h"
-#include "mojo/public/cpp/bindings/binding.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/base/ime/ime_bridge.h"
-#include "ui/events/event.h"
-#include "ui/events/event_constants.h"
-#include "ui/events/event_utils.h"
-#include "ui/events/keycodes/dom/dom_code.h"
-#include "ui/events/keycodes/dom/dom_key.h"
-#include "ui/events/keycodes/keyboard_code_conversion.h"
-#include "ui/events/keycodes/keyboard_codes.h"
-
-enum class CompositionEventType {
-  SET,
-  CONFIRM,
-  CLEAR,
-  INSERT_TEXT,
-  INSERT_CHAR
-};
-
-struct CompositionEvent {
-  CompositionEventType type;
-  base::string16 text_data;
-  base::char16 char_data;
-};
-
-class TestTextInputClient : public ws::mojom::TextInputClient {
- public:
-  explicit TestTextInputClient(ws::mojom::TextInputClientRequest request)
-      : binding_(this, std::move(request)) {}
-
-  CompositionEvent WaitUntilCompositionEvent() {
-    if (!receieved_event_.has_value()) {
-      run_loop_ = std::make_unique<base::RunLoop>();
-      run_loop_->Run();
-      run_loop_.reset();
-    }
-    CompositionEvent result = receieved_event_.value();
-    receieved_event_.reset();
-    return result;
-  }
-
-  void set_manual_ack_dispatch_key_event_post_ime_callback(bool manual) {
-    manual_ack_dispatch_key_event_post_ime_callback_ = manual;
-  }
-
-  void AckDispatchKeyEventPostIMECallback() {
-    DCHECK(manual_ack_dispatch_key_event_post_ime_callback_);
-
-    if (!pending_dispatch_key_event_post_ime_callback_) {
-      pending_dispatch_key_event_post_ime_callback_wait_loop_ =
-          std::make_unique<base::RunLoop>();
-      pending_dispatch_key_event_post_ime_callback_wait_loop_->Run();
-      pending_dispatch_key_event_post_ime_callback_wait_loop_.reset();
-    }
-
-    std::move(pending_dispatch_key_event_post_ime_callback_).Run(false, false);
-  }
-
- private:
-  void SetCompositionText(const ui::CompositionText& composition) override {
-    CompositionEvent ev = {CompositionEventType::SET, composition.text, 0};
-    receieved_event_ = ev;
-    if (run_loop_)
-      run_loop_->Quit();
-  }
-  void ConfirmCompositionText() override {
-    CompositionEvent ev = {CompositionEventType::CONFIRM, base::string16(), 0};
-    receieved_event_ = ev;
-    if (run_loop_)
-      run_loop_->Quit();
-  }
-  void ClearCompositionText() override {
-    CompositionEvent ev = {CompositionEventType::CLEAR, base::string16(), 0};
-    receieved_event_ = ev;
-    if (run_loop_)
-      run_loop_->Quit();
-  }
-  void InsertText(const base::string16& text) override {
-    CompositionEvent ev = {CompositionEventType::INSERT_TEXT, text, 0};
-    receieved_event_ = ev;
-    if (run_loop_)
-      run_loop_->Quit();
-  }
-  void InsertChar(std::unique_ptr<ui::Event> event) override {
-    ASSERT_TRUE(event->IsKeyEvent());
-    CompositionEvent ev = {CompositionEventType::INSERT_CHAR, base::string16(),
-                           event->AsKeyEvent()->GetCharacter()};
-    receieved_event_ = ev;
-    if (run_loop_)
-      run_loop_->Quit();
-  }
-  void DispatchKeyEventPostIME(
-      std::unique_ptr<ui::Event> event,
-      DispatchKeyEventPostIMECallback callback) override {
-    if (manual_ack_dispatch_key_event_post_ime_callback_) {
-      // Only one pending callback is expected.
-      EXPECT_FALSE(pending_dispatch_key_event_post_ime_callback_);
-      pending_dispatch_key_event_post_ime_callback_ = std::move(callback);
-      if (pending_dispatch_key_event_post_ime_callback_wait_loop_)
-        pending_dispatch_key_event_post_ime_callback_wait_loop_->Quit();
-      return;
-    }
-
-    std::move(callback).Run(false, false);
-  }
-  void EnsureCaretNotInRect(const gfx::Rect& rect) override {}
-  void SetEditableSelectionRange(const gfx::Range& range) override {}
-  void DeleteRange(const gfx::Range& range) override {}
-  void OnInputMethodChanged() override {}
-  void ChangeTextDirectionAndLayoutAlignment(
-      base::i18n::TextDirection direction) override {}
-  void ExtendSelectionAndDelete(uint32_t before, uint32_t after) override {}
-
-  mojo::Binding<ws::mojom::TextInputClient> binding_;
-  std::unique_ptr<base::RunLoop> run_loop_;
-  base::Optional<CompositionEvent> receieved_event_;
-
-  bool manual_ack_dispatch_key_event_post_ime_callback_ = false;
-  DispatchKeyEventPostIMECallback pending_dispatch_key_event_post_ime_callback_;
-  std::unique_ptr<base::RunLoop>
-      pending_dispatch_key_event_post_ime_callback_wait_loop_;
-
-  DISALLOW_COPY_AND_ASSIGN(TestTextInputClient);
-};
-
-class InputMethodBridgeChromeOSTest : public testing::Test {
- public:
-  InputMethodBridgeChromeOSTest()
-      : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP) {}
-  ~InputMethodBridgeChromeOSTest() override {}
-
-  void SetUp() override {
-    ui::IMEBridge::Initialize();
-
-    ws::mojom::TextInputClientPtr client_ptr;
-    client_ = std::make_unique<TestTextInputClient>(MakeRequest(&client_ptr));
-    ws::mojom::SessionDetailsPtr details = ws::mojom::SessionDetails::New();
-    details->state = ws::mojom::TextInputState::New(
-        ui::TEXT_INPUT_TYPE_TEXT, ui::TEXT_INPUT_MODE_DEFAULT,
-        base::i18n::LEFT_TO_RIGHT, 0);
-    details->data = ws::mojom::TextInputClientData::New();
-    input_method_ = std::make_unique<InputMethodBridge>(
-        std::make_unique<RemoteTextInputClient>(std::move(client_ptr),
-                                                std::move(details)));
-  }
-
-  bool ProcessKeyEvent(std::unique_ptr<ui::Event> event) {
-    handled_.reset();
-
-    input_method_->ProcessKeyEvent(
-        std::move(event),
-        base::Bind(&InputMethodBridgeChromeOSTest::ProcessKeyEventCallback,
-                   base::Unretained(this)));
-
-    if (!handled_.has_value()) {
-      run_loop_ = std::make_unique<base::RunLoop>();
-      run_loop_->Run();
-      run_loop_.reset();
-    }
-
-    return handled_.value();
-  }
-
-  std::unique_ptr<ui::Event> UnicodeKeyPress(ui::KeyboardCode vkey,
-                                             ui::DomCode code,
-                                             int flags,
-                                             base::char16 character) const {
-    return std::make_unique<ui::KeyEvent>(ui::ET_KEY_PRESSED, vkey, code, flags,
-                                          ui::DomKey::FromCharacter(character),
-                                          ui::EventTimeForNow());
-  }
-
- protected:
-  void ProcessKeyEventCallback(bool handled) {
-    handled_ = handled;
-    if (run_loop_)
-      run_loop_->Quit();
-  }
-
-  content::TestBrowserThreadBundle thread_bundle_;
-  std::unique_ptr<TestTextInputClient> client_;
-  std::unique_ptr<InputMethodBridge> input_method_;
-  std::unique_ptr<base::RunLoop> run_loop_;
-  base::Optional<bool> handled_;
-
-  DISALLOW_COPY_AND_ASSIGN(InputMethodBridgeChromeOSTest);
-};
-
-// Tests if hexadecimal composition provided by ui::CharacterComposer works
-// correctly. ui::CharacterComposer is tried if no input method extensions
-// have been registered yet.
-TEST_F(InputMethodBridgeChromeOSTest, HexadecimalComposition) {
-  struct {
-    ui::KeyboardCode vkey;
-    ui::DomCode code;
-    int flags;
-    base::char16 character;
-    std::string composition_text;
-  } kTestSequence[] = {
-      {ui::VKEY_U, ui::DomCode::US_U, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN,
-       'U', "u"},
-      {ui::VKEY_3, ui::DomCode::DIGIT3, 0, '3', "u3"},
-      {ui::VKEY_0, ui::DomCode::DIGIT0, 0, '0', "u30"},
-      {ui::VKEY_4, ui::DomCode::DIGIT4, 0, '4', "u304"},
-      {ui::VKEY_2, ui::DomCode::DIGIT2, 0, '2', "u3042"},
-  };
-
-  // Send the Ctrl-Shift-U,3,4,0,2 sequence.
-  for (size_t i = 0; i < base::size(kTestSequence); i++) {
-    EXPECT_TRUE(ProcessKeyEvent(
-        UnicodeKeyPress(kTestSequence[i].vkey, kTestSequence[i].code,
-                        kTestSequence[i].flags, kTestSequence[i].character)));
-    CompositionEvent ev = client_->WaitUntilCompositionEvent();
-    EXPECT_EQ(CompositionEventType::SET, ev.type);
-    EXPECT_EQ(base::UTF8ToUTF16(kTestSequence[i].composition_text),
-              ev.text_data);
-  }
-
-  // Press the return key and verify that the composition text was converted
-  // to the desired text.
-  EXPECT_TRUE(ProcessKeyEvent(
-      UnicodeKeyPress(ui::VKEY_RETURN, ui::DomCode::ENTER, 0, '\r')));
-  CompositionEvent ev = client_->WaitUntilCompositionEvent();
-  EXPECT_EQ(CompositionEventType::INSERT_TEXT, ev.type);
-  EXPECT_EQ(base::string16(1, 0x3042), ev.text_data);
-}
-
-// Test that Ctrl-C, Ctrl-X, and Ctrl-V are not handled.
-TEST_F(InputMethodBridgeChromeOSTest, ClipboardAccelerators) {
-  EXPECT_FALSE(ProcessKeyEvent(UnicodeKeyPress(ui::VKEY_C, ui::DomCode::US_C,
-                                               ui::EF_CONTROL_DOWN, 'C')));
-  EXPECT_FALSE(ProcessKeyEvent(UnicodeKeyPress(ui::VKEY_X, ui::DomCode::US_X,
-                                               ui::EF_CONTROL_DOWN, 'X')));
-  EXPECT_FALSE(ProcessKeyEvent(UnicodeKeyPress(ui::VKEY_V, ui::DomCode::US_V,
-                                               ui::EF_CONTROL_DOWN, 'V')));
-}
-
-// Test that multiple DispatchKeyEventPostIME calls are handled serially.
-TEST_F(InputMethodBridgeChromeOSTest, SerialDispatchKeyEventPostIME) {
-  client_->set_manual_ack_dispatch_key_event_post_ime_callback(true);
-
-  // Send multiple key events.
-  input_method_->ProcessKeyEvent(
-      std::make_unique<ui::KeyEvent>(ui::ET_KEY_PRESSED, ui::VKEY_A,
-                                     ui::EF_NONE),
-      base::DoNothing());
-  input_method_->ProcessKeyEvent(
-      std::make_unique<ui::KeyEvent>(ui::ET_KEY_RELEASED, ui::VKEY_A,
-                                     ui::EF_NONE),
-      base::DoNothing());
-
-  // TestTextInputClient::DispatchKeyEventPostIME is called via mojo.
-  // RemoteTextInputClient should make the calls serially. Spin message loop to
-  // see whether |client_| complains about more than one call at a time.
-  base::RunLoop().RunUntilIdle();
-
-  // Ack the pending key events.
-  client_->AckDispatchKeyEventPostIMECallback();
-  client_->AckDispatchKeyEventPostIMECallback();
-}
diff --git a/chrome/browser/ui/views/ime_driver/remote_text_input_client.cc b/chrome/browser/ui/views/ime_driver/remote_text_input_client.cc
deleted file mode 100644
index 6ef3e42..0000000
--- a/chrome/browser/ui/views/ime_driver/remote_text_input_client.cc
+++ /dev/null
@@ -1,251 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/views/ime_driver/remote_text_input_client.h"
-
-#include <memory>
-#include <utility>
-
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "ui/events/event_dispatcher.h"
-
-struct RemoteTextInputClient::QueuedEvent {
-  QueuedEvent(std::unique_ptr<ui::Event> event,
-              DispatchKeyEventPostIMECallback callback)
-      : event(std::move(event)), callback(std::move(callback)) {}
-
-  std::unique_ptr<ui::Event> event;
-  DispatchKeyEventPostIMECallback callback;
-};
-
-RemoteTextInputClient::RemoteTextInputClient(
-    ws::mojom::TextInputClientPtr client,
-    ws::mojom::SessionDetailsPtr details)
-    : remote_client_(std::move(client)), details_(std::move(details)) {}
-
-RemoteTextInputClient::~RemoteTextInputClient() {
-  while (!queued_events_.empty()) {
-    RunNextPendingCallback(/* handled */ false,
-                           /* stopped_propagation */ false);
-  }
-}
-
-void RemoteTextInputClient::SetTextInputState(
-    ws::mojom::TextInputStatePtr text_input_state) {
-  details_->state = std::move(text_input_state);
-}
-
-void RemoteTextInputClient::SetCaretBounds(const gfx::Rect& caret_bounds) {
-  details_->caret_bounds = caret_bounds;
-}
-
-void RemoteTextInputClient::SetTextInputClientData(
-    ws::mojom::TextInputClientDataPtr data) {
-  details_->data = std::move(data);
-}
-
-void RemoteTextInputClient::OnDispatchKeyEventPostIMECompleted(
-    bool handled,
-    bool stopped_propagation) {
-  RunNextPendingCallback(handled, stopped_propagation);
-  DispatchQueuedEvent();
-}
-
-void RemoteTextInputClient::SetCompositionText(
-    const ui::CompositionText& composition) {
-  remote_client_->SetCompositionText(composition);
-}
-
-void RemoteTextInputClient::ConfirmCompositionText() {
-  remote_client_->ConfirmCompositionText();
-}
-
-void RemoteTextInputClient::ClearCompositionText() {
-  remote_client_->ClearCompositionText();
-}
-
-void RemoteTextInputClient::InsertText(const base::string16& text) {
-  remote_client_->InsertText(text);
-}
-
-void RemoteTextInputClient::InsertChar(const ui::KeyEvent& event) {
-  remote_client_->InsertChar(ui::Event::Clone(event));
-}
-
-ui::TextInputType RemoteTextInputClient::GetTextInputType() const {
-  return details_->state->text_input_type;
-}
-
-ui::TextInputMode RemoteTextInputClient::GetTextInputMode() const {
-  return details_->state->text_input_mode;
-}
-
-base::i18n::TextDirection RemoteTextInputClient::GetTextDirection() const {
-  return details_->state->text_direction;
-}
-
-int RemoteTextInputClient::GetTextInputFlags() const {
-  return details_->state->text_input_flags;
-}
-
-bool RemoteTextInputClient::CanComposeInline() const {
-  // If we return false here, ui::InputMethodChromeOS will try to create a
-  // composition window. But here we are at IMEDriver, and composition
-  // window shouldn't be created by IMEDriver.
-  return true;
-}
-
-gfx::Rect RemoteTextInputClient::GetCaretBounds() const {
-  return details_->caret_bounds;
-}
-
-bool RemoteTextInputClient::GetCompositionCharacterBounds(
-    uint32_t index,
-    gfx::Rect* rect) const {
-  // TODO(moshayedi): crbug.com/631527.
-  NOTIMPLEMENTED_LOG_ONCE();
-  return false;
-}
-
-bool RemoteTextInputClient::HasCompositionText() const {
-  return details_->data->has_composition_text;
-}
-
-ui::TextInputClient::FocusReason RemoteTextInputClient::GetFocusReason() const {
-  return details_->focus_reason;
-}
-
-bool RemoteTextInputClient::GetTextRange(gfx::Range* range) const {
-  if (!details_->data->text_range.has_value())
-    return false;
-
-  *range = details_->data->text_range.value();
-  return true;
-}
-
-bool RemoteTextInputClient::GetCompositionTextRange(gfx::Range* range) const {
-  if (!details_->data->composition_text_range.has_value())
-    return false;
-
-  *range = details_->data->composition_text_range.value();
-  return true;
-}
-
-bool RemoteTextInputClient::GetEditableSelectionRange(gfx::Range* range) const {
-  if (!details_->data->editable_selection_range.has_value())
-    return false;
-
-  *range = details_->data->editable_selection_range.value();
-  return true;
-}
-
-bool RemoteTextInputClient::SetEditableSelectionRange(const gfx::Range& range) {
-  remote_client_->SetEditableSelectionRange(range);
-  // Note that we assume the client side always succeeds.
-  return true;
-}
-
-bool RemoteTextInputClient::DeleteRange(const gfx::Range& range) {
-  remote_client_->DeleteRange(range);
-  // Note that we assume the client side always succeeds.
-  return true;
-}
-
-bool RemoteTextInputClient::GetTextFromRange(const gfx::Range& range,
-                                             base::string16* text) const {
-  if (!details_->data->text.has_value() ||
-      !details_->data->text_range.has_value() ||
-      !details_->data->text_range->Contains(range)) {
-    return false;
-  }
-
-  *text = details_->data->text->substr(range.GetMin(), range.length());
-  return true;
-}
-
-void RemoteTextInputClient::OnInputMethodChanged() {
-  remote_client_->OnInputMethodChanged();
-}
-
-bool RemoteTextInputClient::ChangeTextDirectionAndLayoutAlignment(
-    base::i18n::TextDirection direction) {
-  remote_client_->ChangeTextDirectionAndLayoutAlignment(direction);
-  // Note that we assume the client side always succeeds.
-  return true;
-}
-
-void RemoteTextInputClient::ExtendSelectionAndDelete(size_t before,
-                                                     size_t after) {
-  remote_client_->ExtendSelectionAndDelete(before, after);
-}
-
-void RemoteTextInputClient::EnsureCaretNotInRect(const gfx::Rect& rect) {
-  remote_client_->EnsureCaretNotInRect(rect);
-}
-
-bool RemoteTextInputClient::IsTextEditCommandEnabled(
-    ui::TextEditCommand command) const {
-  if (!details_->data->edit_command_enabled.has_value())
-    return false;
-
-  const size_t index = static_cast<size_t>(command);
-  if (index >= details_->data->edit_command_enabled->size())
-    return false;
-
-  return details_->data->edit_command_enabled->at(index);
-}
-
-void RemoteTextInputClient::SetTextEditCommandForNextKeyEvent(
-    ui::TextEditCommand command) {
-  // TODO(moshayedi): crbug.com/631527.
-  NOTIMPLEMENTED_LOG_ONCE();
-}
-
-ukm::SourceId RemoteTextInputClient::GetClientSourceForMetrics() const {
-  return details_->client_source_for_metrics;
-}
-
-bool RemoteTextInputClient::ShouldDoLearning() {
-  return details_->should_do_learning;
-}
-
-bool RemoteTextInputClient::SetCompositionFromExistingText(
-    const gfx::Range& range,
-    const std::vector<ui::ImeTextSpan>& ui_ime_text_spans) {
-  // TODO(https://crbug.com/952757): Implement this method.
-  NOTIMPLEMENTED_LOG_ONCE();
-  return false;
-}
-
-ui::EventDispatchDetails RemoteTextInputClient::DispatchKeyEventPostIME(
-    ui::KeyEvent* event,
-    DispatchKeyEventPostIMECallback callback) {
-  const bool is_first_event = queued_events_.empty();
-  queued_events_.emplace(ui::Event::Clone(*event), std::move(callback));
-  if (is_first_event)
-    DispatchQueuedEvent();
-  return ui::EventDispatchDetails();
-}
-
-void RemoteTextInputClient::DispatchQueuedEvent() {
-  if (queued_events_.empty())
-    return;
-
-  DCHECK(queued_events_.front().event);
-  remote_client_->DispatchKeyEventPostIME(
-      std::move(queued_events_.front().event),
-      base::BindOnce(&RemoteTextInputClient::OnDispatchKeyEventPostIMECompleted,
-                     weak_ptr_factory_.GetWeakPtr()));
-}
-
-void RemoteTextInputClient::RunNextPendingCallback(bool handled,
-                                                   bool stopped_propagation) {
-  DCHECK(!queued_events_.empty());
-  DispatchKeyEventPostIMECallback callback =
-      std::move(queued_events_.front().callback);
-  queued_events_.pop();
-  if (callback)
-    std::move(callback).Run(handled, stopped_propagation);
-}
diff --git a/chrome/browser/ui/views/ime_driver/remote_text_input_client.h b/chrome/browser/ui/views/ime_driver/remote_text_input_client.h
deleted file mode 100644
index c920125..0000000
--- a/chrome/browser/ui/views/ime_driver/remote_text_input_client.h
+++ /dev/null
@@ -1,103 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_VIEWS_IME_DRIVER_REMOTE_TEXT_INPUT_CLIENT_H_
-#define CHROME_BROWSER_UI_VIEWS_IME_DRIVER_REMOTE_TEXT_INPUT_CLIENT_H_
-
-#include "base/containers/queue.h"
-#include "base/memory/weak_ptr.h"
-#include "services/ws/public/mojom/ime/ime.mojom.h"
-#include "ui/base/ime/input_method_delegate.h"
-#include "ui/base/ime/text_input_client.h"
-
-// This implementation of ui::TextInputClient sends all updates via mojo IPC to
-// a remote client. This is intended to be passed to the overrides of
-// ui::InputMethod::SetFocusedTextInputClient().
-// NOTE: Under SingleProcessMash this is used by ash code, for example by the
-// virtual keyboard controller in //ash/keyboard/ui.
-class RemoteTextInputClient : public ui::TextInputClient,
-                              public ui::internal::InputMethodDelegate {
- public:
-  RemoteTextInputClient(ws::mojom::TextInputClientPtr client,
-                        ws::mojom::SessionDetailsPtr details);
-  ~RemoteTextInputClient() override;
-
-  void SetTextInputState(ws::mojom::TextInputStatePtr text_input_state);
-  void SetCaretBounds(const gfx::Rect& caret_bounds);
-  void SetTextInputClientData(ws::mojom::TextInputClientDataPtr data);
-
- private:
-  struct QueuedEvent;
-
-  // See |pending_callbacks_| for details.
-  void OnDispatchKeyEventPostIMECompleted(bool handled,
-                                          bool stopped_propagation);
-
-  // ui::TextInputClient:
-  void SetCompositionText(const ui::CompositionText& composition) override;
-  void ConfirmCompositionText() override;
-  void ClearCompositionText() override;
-  void InsertText(const base::string16& text) override;
-  void InsertChar(const ui::KeyEvent& event) override;
-  ui::TextInputType GetTextInputType() const override;
-  ui::TextInputMode GetTextInputMode() const override;
-  base::i18n::TextDirection GetTextDirection() const override;
-  int GetTextInputFlags() const override;
-  bool CanComposeInline() const override;
-  gfx::Rect GetCaretBounds() const override;
-  bool GetCompositionCharacterBounds(uint32_t index,
-                                     gfx::Rect* rect) const override;
-  bool HasCompositionText() const override;
-  FocusReason GetFocusReason() const override;
-  bool GetTextRange(gfx::Range* range) const override;
-  bool GetCompositionTextRange(gfx::Range* range) const override;
-  bool GetEditableSelectionRange(gfx::Range* range) const override;
-  bool SetEditableSelectionRange(const gfx::Range& range) override;
-  bool DeleteRange(const gfx::Range& range) override;
-  bool GetTextFromRange(const gfx::Range& range,
-                        base::string16* text) const override;
-  void OnInputMethodChanged() override;
-  bool ChangeTextDirectionAndLayoutAlignment(
-      base::i18n::TextDirection direction) override;
-  void ExtendSelectionAndDelete(size_t before, size_t after) override;
-  void EnsureCaretNotInRect(const gfx::Rect& rect) override;
-  bool IsTextEditCommandEnabled(ui::TextEditCommand command) const override;
-  void SetTextEditCommandForNextKeyEvent(ui::TextEditCommand command) override;
-  ukm::SourceId GetClientSourceForMetrics() const override;
-  bool ShouldDoLearning() override;
-  bool SetCompositionFromExistingText(
-      const gfx::Range& range,
-      const std::vector<ui::ImeTextSpan>& ui_ime_text_spans) override;
-
-  // ui::internal::InputMethodDelegate:
-  ui::EventDispatchDetails DispatchKeyEventPostIME(
-      ui::KeyEvent* event,
-      DispatchKeyEventPostIMECallback callback) override;
-
-  // Dispatches the first queued event.
-  void DispatchQueuedEvent();
-
-  // Removes the queue event at the front of |queued_events_| and runs its
-  // callback with |handled| and |stopped_propagation| as arguments.
-  void RunNextPendingCallback(bool handled, bool stopped_propagation);
-
-  ws::mojom::TextInputClientPtr remote_client_;
-  ws::mojom::SessionDetailsPtr details_;
-
-  // Events to be dispatched with DispatchKeyEventPostIME(). Only one event is
-  // dispatched at a time and others are queued here until the current one
-  // finished processing, i.e. the response from the remote side is received
-  // (OnDispatchKeyEventPostIMECompleted()), the dispatched event is removed
-  // from the queue and its callback is invoked. This is done to avoid
-  // overlapping of key events processing (https://crbug.com/938808).
-  // Note that when we are destroyed all the callbacks needs to run. This is
-  // necessary as the callbacks may have originated from a remote client.
-  base::queue<QueuedEvent> queued_events_;
-
-  base::WeakPtrFactory<RemoteTextInputClient> weak_ptr_factory_{this};
-
-  DISALLOW_COPY_AND_ASSIGN(RemoteTextInputClient);
-};
-
-#endif  // CHROME_BROWSER_UI_VIEWS_IME_DRIVER_REMOTE_TEXT_INPUT_CLIENT_H_
diff --git a/chrome/browser/ui/views/ime_driver/simple_input_method.cc b/chrome/browser/ui/views/ime_driver/simple_input_method.cc
deleted file mode 100644
index 2849773a..0000000
--- a/chrome/browser/ui/views/ime_driver/simple_input_method.cc
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/views/ime_driver/simple_input_method.h"
-
-#include <utility>
-
-SimpleInputMethod::SimpleInputMethod() {}
-
-SimpleInputMethod::~SimpleInputMethod() {}
-
-void SimpleInputMethod::OnTextInputStateChanged(
-    ws::mojom::TextInputStatePtr text_input_state) {
-  NOTIMPLEMENTED_LOG_ONCE();
-}
-
-void SimpleInputMethod::OnCaretBoundsChanged(const gfx::Rect& caret_bounds) {
-  NOTIMPLEMENTED_LOG_ONCE();
-}
-
-void SimpleInputMethod::OnTextInputClientDataChanged(
-    ws::mojom::TextInputClientDataPtr data) {
-  NOTIMPLEMENTED_LOG_ONCE();
-}
-
-void SimpleInputMethod::ProcessKeyEvent(std::unique_ptr<ui::Event> key_event,
-                                        ProcessKeyEventCallback callback) {
-  NOTIMPLEMENTED_LOG_ONCE();
-  std::move(callback).Run(false);
-}
-
-void SimpleInputMethod::CancelComposition() {
-  NOTIMPLEMENTED_LOG_ONCE();
-}
-
-void SimpleInputMethod::ShowVirtualKeyboardIfEnabled() {
-  NOTIMPLEMENTED_LOG_ONCE();
-}
diff --git a/chrome/browser/ui/views/ime_driver/simple_input_method.h b/chrome/browser/ui/views/ime_driver/simple_input_method.h
deleted file mode 100644
index 62c9abb..0000000
--- a/chrome/browser/ui/views/ime_driver/simple_input_method.h
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_VIEWS_IME_DRIVER_SIMPLE_INPUT_METHOD_H_
-#define CHROME_BROWSER_UI_VIEWS_IME_DRIVER_SIMPLE_INPUT_METHOD_H_
-
-#include "services/ws/public/mojom/ime/ime.mojom.h"
-
-// This is to be used on platforms where a proper implementation of
-// ws::mojom::InputMethod is missing. It doesn't handle any events and calls
-// the callback with false, which will result in client code handling events
-// locally.
-class SimpleInputMethod : public ws::mojom::InputMethod {
- public:
-  SimpleInputMethod();
-  ~SimpleInputMethod() override;
-
-  // ws::mojom::InputMethod:
-  void OnTextInputStateChanged(
-      ws::mojom::TextInputStatePtr text_input_state) override;
-  void OnCaretBoundsChanged(const gfx::Rect& caret_bounds) override;
-  void OnTextInputClientDataChanged(
-      ws::mojom::TextInputClientDataPtr data) override;
-  void ProcessKeyEvent(std::unique_ptr<ui::Event> key_event,
-                       ProcessKeyEventCallback callback) override;
-  void CancelComposition() override;
-  void ShowVirtualKeyboardIfEnabled() override;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(SimpleInputMethod);
-};
-
-#endif  // CHROME_BROWSER_UI_VIEWS_IME_DRIVER_SIMPLE_INPUT_METHOD_H_
diff --git a/chrome/browser/ui/views/toolbar/toolbar_icon_container_view.cc b/chrome/browser/ui/views/toolbar/toolbar_icon_container_view.cc
index be97e7d..0bf9af2 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_icon_container_view.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_icon_container_view.cc
@@ -14,7 +14,8 @@
 #include "ui/views/layout/flex_layout.h"
 #include "ui/views/view_class_properties.h"
 
-ToolbarIconContainerView::ToolbarIconContainerView() {
+ToolbarIconContainerView::ToolbarIconContainerView(bool uses_highlight)
+    : uses_highlight_(uses_highlight) {
   auto layout_manager = std::make_unique<views::FlexLayout>();
   layout_manager->SetCollapseMargins(true).SetDefaultChildMargins(
       gfx::Insets(0, 0, 0, GetLayoutConstant(TOOLBAR_ELEMENT_PADDING)));
@@ -70,6 +71,9 @@
 }
 
 void ToolbarIconContainerView::UpdateHighlight(bool highlighted) {
+  if (!uses_highlight_)
+    return;
+
   SetBackground(highlighted
                     ? views::CreateRoundedRectBackground(
                           SkColorSetA(GetToolbarInkDropBaseColor(this),
diff --git a/chrome/browser/ui/views/toolbar/toolbar_icon_container_view.h b/chrome/browser/ui/views/toolbar/toolbar_icon_container_view.h
index 2f840ef2..cb9a8b0 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_icon_container_view.h
+++ b/chrome/browser/ui/views/toolbar/toolbar_icon_container_view.h
@@ -15,7 +15,7 @@
                                  public views::ButtonObserver,
                                  public views::ViewObserver {
  public:
-  ToolbarIconContainerView();
+  explicit ToolbarIconContainerView(bool uses_highlight);
   ~ToolbarIconContainerView() override;
 
   // Update all the icons it contains. Override by subclass.
@@ -34,7 +34,11 @@
   void OnViewFocused(views::View* observed_view) override;
   void OnViewBlurred(views::View* observed_view) override;
 
+  bool uses_highlight() { return uses_highlight_; }
+
  private:
+  friend class ToolbarPageActionIconContainerViewBrowserTest;
+
   // views::View:
   void ChildPreferredSizeChanged(views::View* child) override;
   void ChildVisibilityChanged(views::View* child) override;
@@ -48,6 +52,9 @@
   // Points to the child view that is currently highlighted.
   views::View* highlighted_view_ = nullptr;
 
+  // Determine whether the container shows its highlight background.
+  const bool uses_highlight_;
+
   DISALLOW_COPY_AND_ASSIGN(ToolbarIconContainerView);
 };
 
diff --git a/chrome/browser/ui/views/toolbar/toolbar_page_action_icon_container_view.cc b/chrome/browser/ui/views/toolbar/toolbar_page_action_icon_container_view.cc
index 641b8e9..f84d9cd 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_page_action_icon_container_view.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_page_action_icon_container_view.cc
@@ -4,8 +4,10 @@
 
 #include "chrome/browser/ui/views/toolbar/toolbar_page_action_icon_container_view.h"
 
+#include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/themes/theme_properties.h"
 #include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_command_controller.h"
 #include "chrome/browser/ui/views/autofill/payments/local_card_migration_icon_view.h"
 #include "chrome/browser/ui/views/autofill/payments/save_card_icon_view.h"
 #include "chrome/browser/ui/views/chrome_layout_provider.h"
@@ -17,15 +19,16 @@
 #include "ui/views/widget/widget.h"
 
 ToolbarPageActionIconContainerView::ToolbarPageActionIconContainerView(
-    CommandUpdater* command_updater,
     Browser* browser)
-    : ToolbarIconContainerView(), browser_(browser) {
+    : ToolbarIconContainerView(
+          /*uses_highlight=*/!browser->profile()->IsIncognito()),
+      browser_(browser) {
   manage_passwords_icon_views_ =
-      new ManagePasswordsIconViews(command_updater, this);
+      new ManagePasswordsIconViews(browser->command_controller(), this);
   page_action_icons_.push_back(manage_passwords_icon_views_);
 
   local_card_migration_icon_view_ = new autofill::LocalCardMigrationIconView(
-      command_updater, browser, this,
+      browser->command_controller(), browser, this,
       // TODO(crbug.com/932818): The font list and the icon color may not be
       // what we want here. Put placeholders for now.
       views::style::GetFont(CONTEXT_TOOLBAR_BUTTON,
@@ -33,7 +36,7 @@
   page_action_icons_.push_back(local_card_migration_icon_view_);
 
   save_card_icon_view_ = new autofill::SaveCardIconView(
-      command_updater, browser, this,
+      browser->command_controller(), browser, this,
       // TODO(crbug.com/932818): The font list and the icon color may not be
       // what we want here. Put placeholders for now.
       views::style::GetFont(CONTEXT_TOOLBAR_BUTTON,
diff --git a/chrome/browser/ui/views/toolbar/toolbar_page_action_icon_container_view.h b/chrome/browser/ui/views/toolbar/toolbar_page_action_icon_container_view.h
index 18a0432..0558126 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_page_action_icon_container_view.h
+++ b/chrome/browser/ui/views/toolbar/toolbar_page_action_icon_container_view.h
@@ -12,7 +12,6 @@
 
 class AvatarToolbarButton;
 class Browser;
-class CommandUpdater;
 class ManagePasswordsIconViews;
 
 namespace autofill {
@@ -26,8 +25,7 @@
                                            public PageActionIconContainer,
                                            public PageActionIconView::Delegate {
  public:
-  ToolbarPageActionIconContainerView(CommandUpdater* command_updater,
-                                     Browser* browser);
+  explicit ToolbarPageActionIconContainerView(Browser* browser);
   ~ToolbarPageActionIconContainerView() override;
 
   PageActionIconView* GetIconView(PageActionIconType icon_type);
diff --git a/chrome/browser/ui/views/toolbar/toolbar_page_action_icon_container_view_browsertest.cc b/chrome/browser/ui/views/toolbar/toolbar_page_action_icon_container_view_browsertest.cc
new file mode 100644
index 0000000..74d1547
--- /dev/null
+++ b/chrome/browser/ui/views/toolbar/toolbar_page_action_icon_container_view_browsertest.cc
@@ -0,0 +1,101 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/macros.h"
+#include "base/test/scoped_feature_list.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/views/frame/browser_view.h"
+#include "chrome/browser/ui/views/toolbar/toolbar_page_action_icon_container_view.h"
+#include "chrome/browser/ui/views/toolbar/toolbar_view.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/testing_profile.h"
+#include "components/autofill/core/common/autofill_payments_features.h"
+
+// TODO(crbug.com/932818): Clean this and the same code in ukm_browsertest.
+// Maybe move them to InProcessBrowserTest.
+namespace {
+
+void UnblockOnProfileCreation(base::RunLoop* run_loop,
+                              Profile* profile,
+                              Profile::CreateStatus status) {
+  if (status == Profile::CREATE_STATUS_INITIALIZED)
+    run_loop->Quit();
+}
+
+Profile* CreateGuestProfile() {
+  ProfileManager* profile_manager = g_browser_process->profile_manager();
+  base::FilePath new_path = profile_manager->GetGuestProfilePath();
+  base::RunLoop run_loop;
+  profile_manager->CreateProfileAsync(
+      new_path, base::BindRepeating(&UnblockOnProfileCreation, &run_loop),
+      base::string16(), std::string());
+  run_loop.Run();
+  return profile_manager->GetProfileByPath(new_path);
+}
+
+}  // namespace
+
+// The param is whether to use the highlight in the container.
+class ToolbarPageActionIconContainerViewBrowserTest
+    : public InProcessBrowserTest {
+ public:
+  ToolbarPageActionIconContainerViewBrowserTest() {}
+  ~ToolbarPageActionIconContainerViewBrowserTest() override = default;
+
+  void SetUp() override {
+    scoped_feature_list_.InitAndEnableFeature(
+        autofill::features::kAutofillEnableToolbarStatusChip);
+    InProcessBrowserTest::SetUp();
+  }
+
+  void TestUsesHighlight(ToolbarPageActionIconContainerView* view,
+                         bool expect_highlight) {
+    DCHECK(view);
+    EXPECT_EQ(view->uses_highlight(), expect_highlight);
+    EXPECT_EQ(view->background(), nullptr);
+
+    view->UpdateHighlight(true);
+    EXPECT_EQ(view->background() != nullptr, expect_highlight);
+
+    view->UpdateHighlight(false);
+    EXPECT_EQ(view->background(), nullptr);
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+
+  DISALLOW_COPY_AND_ASSIGN(ToolbarPageActionIconContainerViewBrowserTest);
+};
+
+IN_PROC_BROWSER_TEST_F(ToolbarPageActionIconContainerViewBrowserTest,
+                       ShouldUpdateHighlightInNormalWindow) {
+  ToolbarPageActionIconContainerView* container_view =
+      BrowserView::GetBrowserViewForBrowser(browser())
+          ->toolbar()
+          ->toolbar_page_action_container();
+  TestUsesHighlight(container_view, /*expect_highlight=*/true);
+}
+
+IN_PROC_BROWSER_TEST_F(ToolbarPageActionIconContainerViewBrowserTest,
+                       ShouldUpdateHighlightInGuestWindow) {
+  Profile* guest_profile = CreateGuestProfile();
+  Browser* guest_browser = CreateIncognitoBrowser(guest_profile);
+  ASSERT_TRUE(guest_browser->profile()->IsGuestSession());
+  ToolbarPageActionIconContainerView* container_view =
+      BrowserView::GetBrowserViewForBrowser(guest_browser)
+          ->toolbar()
+          ->toolbar_page_action_container();
+  TestUsesHighlight(container_view, /*expect_highlight=*/true);
+}
+
+IN_PROC_BROWSER_TEST_F(ToolbarPageActionIconContainerViewBrowserTest,
+                       ShouldNotUpdateHighlightInIncognitoWindow) {
+  Browser* incognito_browser = CreateIncognitoBrowser();
+  ToolbarPageActionIconContainerView* container_view =
+      BrowserView::GetBrowserViewForBrowser(incognito_browser)
+          ->toolbar()
+          ->toolbar_page_action_container();
+  TestUsesHighlight(container_view, /*expect_highlight=*/false);
+}
diff --git a/chrome/browser/ui/views/toolbar/toolbar_view.cc b/chrome/browser/ui/views/toolbar/toolbar_view.cc
index d69fb5b..e198219 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_view.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_view.cc
@@ -244,8 +244,7 @@
     // should not be created twice.
     show_avatar_toolbar_button = false;
     toolbar_page_action_container =
-        std::make_unique<ToolbarPageActionIconContainerView>(
-            browser_->command_controller(), browser_);
+        std::make_unique<ToolbarPageActionIconContainerView>(browser_);
   } else {
 #if defined(OS_CHROMEOS)
     // ChromeOS only badges Incognito and Guest icons in the browser window.
diff --git a/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.cc b/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.cc
index dac4a1c..218e5bb7 100644
--- a/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.cc
@@ -353,6 +353,9 @@
       "cancelPrinterSetUp",
       base::BindRepeating(&CupsPrintersHandler::HandleSetUpCancel,
                           base::Unretained(this)));
+  web_ui()->RegisterMessageCallback(
+      "getEulaUrl", base::BindRepeating(&CupsPrintersHandler::HandleGetEulaUrl,
+                                        base::Unretained(this)));
 }
 
 void CupsPrintersHandler::OnJavascriptAllowed() {
@@ -1095,6 +1098,21 @@
   ResolveJavascriptCallback(base::Value(callback_id), info);
 }
 
+void CupsPrintersHandler::HandleGetEulaUrl(const base::ListValue* args) {
+  std::string callback_id;
+  std::string ppdManufacturer;
+  std::string ppdModel;
+  CHECK_EQ(3U, args->GetSize());
+  CHECK(args->GetString(0, &callback_id));
+  CHECK(args->GetString(1, &ppdManufacturer));
+  CHECK(args->GetString(2, &ppdModel));
+
+  // TODO(crbug/958272): Implement this function to check if a |ppdModel| has an
+  // EULA.
+  ResolveJavascriptCallback(base::Value(callback_id),
+                            base::Value("" /* eulaUrl */));
+}
+
 void CupsPrintersHandler::FireManuallyAddDiscoveredPrinter(
     const Printer& printer) {
   FireWebUIListener("on-manually-add-discovered-printer",
diff --git a/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.h b/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.h
index 429e9a0..f790bfc 100644
--- a/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.h
+++ b/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.h
@@ -176,6 +176,9 @@
   void OnPrintersChanged(PrinterClass printer_class,
                          const std::vector<Printer>& printers) override;
 
+  // Handles getting the EULA URL if available.
+  void HandleGetEulaUrl(const base::ListValue* args);
+
   // ui::SelectFileDialog::Listener override:
   void FileSelected(const base::FilePath& path,
                     int index,
diff --git a/chrome/browser/ui/webui/settings/chromeos/kerberos_accounts_handler.cc b/chrome/browser/ui/webui/settings/chromeos/kerberos_accounts_handler.cc
index 16074ee..de13b59 100644
--- a/chrome/browser/ui/webui/settings/chromeos/kerberos_accounts_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/kerberos_accounts_handler.cc
@@ -44,6 +44,11 @@
       base::BindRepeating(&KerberosAccountsHandler::HandleAddKerberosAccount,
                           weak_factory_.GetWeakPtr()));
   web_ui()->RegisterMessageCallback(
+      "reauthenticateKerberosAccount",
+      base::BindRepeating(
+          &KerberosAccountsHandler::HandleReauthenticateKerberosAccount,
+          weak_factory_.GetWeakPtr()));
+  web_ui()->RegisterMessageCallback(
       "removeKerberosAccount",
       base::BindRepeating(&KerberosAccountsHandler::HandleRemoveKerberosAccount,
                           weak_factory_.GetWeakPtr()));
@@ -95,28 +100,23 @@
   //   - Prevent account changes when Kerberos is disabled.
   //   - Remove all accounts when Kerberos is disabled.
 
-  CHECK_EQ(3U, args->GetSize());
+  // TODO(ljusten): Call KerberosAddAccountDialog::Show() instead when that's
+  // implemented.
 
-  std::string callback_id;
-  CHECK(args->GetString(0, &callback_id));
-
-  std::string principal_name;
-  CHECK(args->GetString(1, &principal_name));
-
-  std::string password;
-  CHECK(args->GetString(2, &password));
-
+  static int count = 0;
   KerberosCredentialsManager::Get().AddAccountAndAuthenticate(
-      std::move(principal_name), password,
-      base::BindOnce(&KerberosAccountsHandler::OnAddAccountAndAuthenticate,
-                     weak_factory_.GetWeakPtr(), callback_id));
+      base::StringPrintf("user%i@realm", ++count), "password",
+      EmptyResultCallback());
 }
 
-void KerberosAccountsHandler::OnAddAccountAndAuthenticate(
-    const std::string& callback_id,
-    kerberos::ErrorType error) {
-  ResolveJavascriptCallback(base::Value(callback_id),
-                            base::Value(static_cast<int>(error)));
+void KerberosAccountsHandler::HandleReauthenticateKerberosAccount(
+    const base::ListValue* args) {
+  AllowJavascript();
+
+  CHECK(!args->GetList().empty());
+
+  // TODO(ljusten): Add KerberosAddAccountDialog::Show(principal_name) when
+  // that's implemented.
 }
 
 void KerberosAccountsHandler::HandleRemoveKerberosAccount(
diff --git a/chrome/browser/ui/webui/settings/chromeos/kerberos_accounts_handler.h b/chrome/browser/ui/webui/settings/chromeos/kerberos_accounts_handler.h
index c15457f..16316ac 100644
--- a/chrome/browser/ui/webui/settings/chromeos/kerberos_accounts_handler.h
+++ b/chrome/browser/ui/webui/settings/chromeos/kerberos_accounts_handler.h
@@ -44,14 +44,13 @@
   // WebUI "addKerberosAccount" message callback.
   void HandleAddKerberosAccount(const base::ListValue* args);
 
-  // Callback for the credential manager's AddAccountAndAuthenticate method.
-  void OnAddAccountAndAuthenticate(const std::string& callback_id,
-                                   kerberos::ErrorType error);
+  // WebUI "reauthenticateKerberosAccount" message callback.
+  void HandleReauthenticateKerberosAccount(const base::ListValue* args);
 
   // WebUI "removeKerberosAccount" message callback.
   void HandleRemoveKerberosAccount(const base::ListValue* args);
 
-  // Callback for the credential manager's ListAccounts method.
+  // Callback for the Kerberos daemon's ListAccounts D-Bus method.
   void OnListAccounts(base::Value callback_id,
                       const kerberos::ListAccountsResponse& response);
 
diff --git a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
index 51c3984..387fac0 100644
--- a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
@@ -1699,11 +1699,6 @@
     {"kerberosAccountsSignedOut", IDS_SETTINGS_KERBEROS_ACCOUNTS_SIGNED_OUT},
     {"kerberosAccountsReauthenticationLabel",
      IDS_SETTINGS_KERBEROS_ACCOUNTS_REAUTHENTICATION_LABEL},
-    {"addKerberosAccount", IDS_SETTINGS_ADD_KERBEROS_ACCOUNT},
-    {"kerberosUsername", IDS_SETTINGS_KERBEROS_USERNAME},
-    {"kerberosPassword", IDS_SETTINGS_KERBEROS_PASSWORD},
-    {"kerberosGeneralErrorMessage",
-     IDS_SETTINGS_KERBEROS_GENERAL_ERROR_MESSAGE},
     {"lockScreenAddFingerprint",
      IDS_SETTINGS_PEOPLE_LOCK_SCREEN_ADD_FINGERPRINT_BUTTON},
     {"lockScreenChangePinButton",
@@ -2083,6 +2078,7 @@
     {"editPrinterButtonText", IDS_SETTINGS_PRINTING_CUPS_EDIT_PRINTER_BUTTON},
     {"currentPpdMessage",
      IDS_SETTINGS_PRINTING_CUPS_EDIT_PRINTER_CURRENT_PPD_MESSAGE},
+    {"printerEulaNotice", IDS_SETTINGS_PRINTING_CUPS_EULA_NOTICE},
 #else
     {"localPrintersTitle", IDS_SETTINGS_PRINTING_LOCAL_PRINTERS_TITLE},
 #endif
diff --git a/chrome/browser/ui/webui/welcome/nux/constants.cc b/chrome/browser/ui/webui/welcome/nux/constants.cc
index 73a31c2..ac31489 100644
--- a/chrome/browser/ui/webui/welcome/nux/constants.cc
+++ b/chrome/browser/ui/webui/welcome/nux/constants.cc
@@ -24,4 +24,7 @@
     &kNuxOnboardingFeature, "returning-user-modules",
     kDefaultReturningUserModules};
 
+const base::FeatureParam<bool> kNuxOnboardingShowGoogleApp{
+    &kNuxOnboardingFeature, "app-variation-enabled", false};
+
 }  // namespace nux
diff --git a/chrome/browser/ui/webui/welcome/nux/constants.h b/chrome/browser/ui/webui/welcome/nux/constants.h
index b57b0dd..de397b2 100644
--- a/chrome/browser/ui/webui/welcome/nux/constants.h
+++ b/chrome/browser/ui/webui/welcome/nux/constants.h
@@ -21,6 +21,7 @@
 
 extern const base::FeatureParam<std::string> kNuxOnboardingNewUserModules;
 extern const base::FeatureParam<std::string> kNuxOnboardingReturningUserModules;
+extern const base::FeatureParam<bool> kNuxOnboardingShowGoogleApp;
 
 }  // namespace nux
 
diff --git a/chrome/browser/ui/webui/welcome/nux/google_apps_handler.cc b/chrome/browser/ui/webui/welcome/nux/google_apps_handler.cc
index e41da8b..3669d98 100644
--- a/chrome/browser/ui/webui/welcome/nux/google_apps_handler.cc
+++ b/chrome/browser/ui/webui/welcome/nux/google_apps_handler.cc
@@ -11,6 +11,8 @@
 #include "chrome/browser/favicon/favicon_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/webui/welcome/nux/bookmark_item.h"
+#include "chrome/browser/ui/webui/welcome/nux_helper.h"
+#include "chrome/grit/chrome_unscaled_resources.h"
 #include "chrome/grit/generated_resources.h"
 #include "chrome/grit/onboarding_welcome_resources.h"
 #include "components/favicon/core/favicon_service.h"
@@ -33,6 +35,7 @@
   kTranslate = 3,
   kNews = 4,
   kChromeWebStoreDoNotUse = 5,  // Deprecated.
+  kSearch = 6,
   kCount,
 };
 
@@ -41,32 +44,52 @@
 
 constexpr const int kGoogleAppIconSize = 48;  // Pixels.
 
-GoogleAppsHandler::GoogleAppsHandler()
-    :  // Do not translate icon name as it is not human visible and needs to
-       // match CSS.
-      google_apps_{{
-          {static_cast<int>(GoogleApps::kGmail),
-           l10n_util::GetStringUTF8(IDS_ONBOARDING_WELCOME_NUX_GOOGLE_GMAIL),
-           "gmail", "https://accounts.google.com/b/0/AddMailService",
-           IDR_NUX_GOOGLE_APPS_GMAIL_1X},
-          {static_cast<int>(GoogleApps::kYouTube),
-           l10n_util::GetStringUTF8(
-               IDS_ONBOARDING_WELCOME_NUX_GOOGLE_APPS_YOUTUBE),
-           "youtube", "https://youtube.com", IDR_NUX_GOOGLE_APPS_YOUTUBE_1X},
-          {static_cast<int>(GoogleApps::kMaps),
-           l10n_util::GetStringUTF8(
-               IDS_ONBOARDING_WELCOME_NUX_GOOGLE_APPS_MAPS),
-           "maps", "https://maps.google.com", IDR_NUX_GOOGLE_APPS_MAPS_1X},
-          {static_cast<int>(GoogleApps::kNews),
-           l10n_util::GetStringUTF8(
-               IDS_ONBOARDING_WELCOME_NUX_GOOGLE_APPS_NEWS),
-           "news", "https://news.google.com", IDR_NUX_GOOGLE_APPS_NEWS_1X},
-          {static_cast<int>(GoogleApps::kTranslate),
-           l10n_util::GetStringUTF8(
-               IDS_ONBOARDING_WELCOME_NUX_GOOGLE_APPS_TRANSLATE),
-           "translate", "https://translate.google.com",
-           IDR_NUX_GOOGLE_APPS_TRANSLATE_1X},
-      }} {}
+GoogleAppsHandler::GoogleAppsHandler() {
+  // Do not translate icon name as it is not human visible and needs to
+  // match CSS.
+
+  BookmarkItem gmail = {
+      static_cast<int>(GoogleApps::kGmail),
+      l10n_util::GetStringUTF8(IDS_ONBOARDING_WELCOME_NUX_GOOGLE_GMAIL),
+      "gmail", "https://accounts.google.com/b/0/AddMailService",
+      IDR_NUX_GOOGLE_APPS_GMAIL_1X};
+
+  if (IsAppVariationEnabled()) {
+#if defined(GOOGLE_CHROME_BUILD)
+    google_apps_.push_back(
+        {static_cast<int>(GoogleApps::kSearch),
+         l10n_util::GetStringUTF8(IDS_ONBOARDING_WELCOME_NUX_GOOGLE_SEARCH),
+         "search", "https://google.com", IDS_ONBOARDING_WELCOME_SEARCH});
+#endif  // GOOGLE_CHROME_BUILD
+  } else {
+    google_apps_.push_back(gmail);
+  }
+
+  google_apps_.push_back(
+      {static_cast<int>(GoogleApps::kYouTube),
+       l10n_util::GetStringUTF8(IDS_ONBOARDING_WELCOME_NUX_GOOGLE_APPS_YOUTUBE),
+       "youtube", "https://youtube.com", IDR_NUX_GOOGLE_APPS_YOUTUBE_1X});
+
+  google_apps_.push_back(
+      {static_cast<int>(GoogleApps::kMaps),
+       l10n_util::GetStringUTF8(IDS_ONBOARDING_WELCOME_NUX_GOOGLE_APPS_MAPS),
+       "maps", "https://maps.google.com", IDR_NUX_GOOGLE_APPS_MAPS_1X});
+
+  if (IsAppVariationEnabled()) {
+    google_apps_.push_back(gmail);
+  } else {
+    google_apps_.push_back(
+        {static_cast<int>(GoogleApps::kNews),
+         l10n_util::GetStringUTF8(IDS_ONBOARDING_WELCOME_NUX_GOOGLE_APPS_NEWS),
+         "news", "https://news.google.com", IDR_NUX_GOOGLE_APPS_NEWS_1X});
+  }
+
+  google_apps_.push_back({static_cast<int>(GoogleApps::kTranslate),
+                          l10n_util::GetStringUTF8(
+                              IDS_ONBOARDING_WELCOME_NUX_GOOGLE_APPS_TRANSLATE),
+                          "translate", "https://translate.google.com",
+                          IDR_NUX_GOOGLE_APPS_TRANSLATE_1X});
+}
 
 GoogleAppsHandler::~GoogleAppsHandler() {}
 
@@ -87,9 +110,9 @@
   args->GetInteger(0, &appId);
 
   const BookmarkItem* selectedApp = NULL;
-  for (size_t i = 0; i < kGoogleAppCount; i++) {
-    if (google_apps_[i].id == appId) {
-      selectedApp = &google_apps_[i];
+  for (const auto& google_app : google_apps_) {
+    if (google_app.id == appId) {
+      selectedApp = &google_app;
       break;
     }
   }
@@ -115,7 +138,7 @@
   CHECK(args->Get(0, &callback_id));
   ResolveJavascriptCallback(
       *callback_id,
-      BookmarkItemsToListValue(google_apps_.data(), kGoogleAppCount));
+      BookmarkItemsToListValue(google_apps_.data(), google_apps_.size()));
 }
 
 }  // namespace nux
diff --git a/chrome/browser/ui/webui/welcome/nux/google_apps_handler.h b/chrome/browser/ui/webui/welcome/nux/google_apps_handler.h
index a3db532..f35e22d 100644
--- a/chrome/browser/ui/webui/welcome/nux/google_apps_handler.h
+++ b/chrome/browser/ui/webui/welcome/nux/google_apps_handler.h
@@ -5,7 +5,7 @@
 #ifndef CHROME_BROWSER_UI_WEBUI_WELCOME_NUX_GOOGLE_APPS_HANDLER_H_
 #define CHROME_BROWSER_UI_WEBUI_WELCOME_NUX_GOOGLE_APPS_HANDLER_H_
 
-#include <array>
+#include <vector>
 
 #include "base/macros.h"
 #include "base/values.h"
@@ -25,8 +25,6 @@
   kCount,
 };
 
-const size_t kGoogleAppCount = 5;
-
 class GoogleAppsHandler : public content::WebUIMessageHandler {
  public:
   GoogleAppsHandler();
@@ -40,7 +38,7 @@
   void HandleGetGoogleAppsList(const base::ListValue* args);
 
  private:
-  std::array<BookmarkItem, kGoogleAppCount> google_apps_;
+  std::vector<BookmarkItem> google_apps_;
 
   DISALLOW_COPY_AND_ASSIGN(GoogleAppsHandler);
 };
diff --git a/chrome/browser/ui/webui/welcome/nux_helper.cc b/chrome/browser/ui/webui/welcome/nux_helper.cc
index bc2be7f..b900cc5 100644
--- a/chrome/browser/ui/webui/welcome/nux_helper.cc
+++ b/chrome/browser/ui/webui/welcome/nux_helper.cc
@@ -114,6 +114,8 @@
     kNuxOnboardingForceEnabledReturningUserModules = {
         &kNuxOnboardingForceEnabled, "returning-user-modules",
         "nux-set-as-default"};
+const base::FeatureParam<bool> kNuxOnboardingForceEnabledShowGoogleApp = {
+    &kNuxOnboardingForceEnabled, "app-variation-enabled", false};
 
 // Onboarding experiments depend on Google being the default search provider.
 bool CanExperimentWithVariations(Profile* profile) {
@@ -183,6 +185,11 @@
   return false;
 }
 
+bool IsAppVariationEnabled() {
+  return kNuxOnboardingForceEnabledShowGoogleApp.Get() ||
+         kNuxOnboardingShowGoogleApp.Get();
+}
+
 const policy::PolicyMap& GetPoliciesFromProfile(Profile* profile) {
   policy::ProfilePolicyConnector* profile_connector =
       profile->GetProfilePolicyConnector();
diff --git a/chrome/browser/ui/webui/welcome/nux_helper.h b/chrome/browser/ui/webui/welcome/nux_helper.h
index 0acfe76..719d11e 100644
--- a/chrome/browser/ui/webui/welcome/nux_helper.h
+++ b/chrome/browser/ui/webui/welcome/nux_helper.h
@@ -40,6 +40,8 @@
 
 bool IsNuxOnboardingEnabled(Profile* profile);
 
+bool IsAppVariationEnabled();
+
 bool DoesOnboardingHaveModulesToShow(Profile* profile);
 
 base::DictionaryValue GetNuxOnboardingModules(Profile* profile);
diff --git a/chrome/common/extensions/api/autotest_private.idl b/chrome/common/extensions/api/autotest_private.idl
index 856f660..69c6a85 100644
--- a/chrome/common/extensions/api/autotest_private.idl
+++ b/chrome/common/extensions/api/autotest_private.idl
@@ -159,9 +159,6 @@
 
   callback TakeScreenshotCallback = void (DOMString base64Png);
 
-  callback EnsureWindowServiceClientHasDrawnWindowCallback =
-      void(boolean success);
-
   callback GetPrimaryDisplayScaleFactorCallback = void (double scaleFactor);
 
   callback IsTabletModeEnabledCallback = void (boolean enabled);
@@ -338,15 +335,6 @@
     static void setCrostiniAppScaled(DOMString appId, boolean scaled,
                                      VoidCallback callback);
 
-    // Ensure that the Window Service client identified by |clientName| has
-    // drawn any window. |callback| is invoked with true if the client has drawn
-    // anything or when it does so before the time out. Otherwise, an error
-    // is raised when timeout happens.
-    static void ensureWindowServiceClientHasDrawnWindow(
-        DOMString clientName,
-        long timeout_ms,
-        EnsureWindowServiceClientHasDrawnWindowCallback callback);
-
     // Get the primary display scale factor.
     // |callback| is invoked with the scale factor.
     static void getPrimaryDisplayScaleFactor(
diff --git a/chrome/common/extensions/api/input_method_private.json b/chrome/common/extensions/api/input_method_private.json
index b960ff1..4d4cc4a 100644
--- a/chrome/common/extensions/api/input_method_private.json
+++ b/chrome/common/extensions/api/input_method_private.json
@@ -34,6 +34,12 @@
         }
       },
       {
+        "id": "UnderlineStyle",
+        "type": "string",
+        "description": "The type of the underline to modify a composition segment.",
+        "enum": ["underline"]
+      },
+      {
         "id": "FocusReason",
         "type": "string",
         "description": "Describes how the text field was focused",
@@ -393,6 +399,64 @@
             "parameters": []
           }
         ]
+      }, {
+        "name": "setCompositionRange",
+        "type": "function",
+        "description": "Set the composition range. If this extension does not own the active IME, this fails.",
+        "parameters": [
+          {
+            "name": "parameters",
+            "type": "object",
+            "properties": {
+              "contextID": {
+                "description": "ID of the context where the composition text will be set",
+                "type": "integer"
+              },
+              "selectionBefore": {
+                "description": "How much before the current selection to set as composition.",
+                "type": "integer"
+              },
+              "selectionAfter": {
+                "description": "How much after the current selection to set as composition.",
+                "type": "integer"
+              },
+              "segments": {
+                "description": "List of segments and their associated types.",
+                "type": "array",
+                "optional": true,
+                "items": {
+                  "type": "object",
+                  "properties": {
+                    "start": {
+                      "description": "Index of the character to start this segment at",
+                      "type": "integer"
+                    },
+                    "end": {
+                      "description": "Index of the character to end this segment after.",
+                      "type": "integer"
+                    },
+                    "style": {
+                      "$ref": "UnderlineStyle",
+                      "description": "The type of the underline to modify this segment."
+                    }
+                  }
+                }
+              }
+            }
+          },
+          {
+            "type": "function",
+            "name": "callback",
+            "optional": true,
+            "description": "Called when the operation completes with a boolean indicating if the text was accepted or not. On failure, chrome.runtime.lastError is set.",
+            "parameters": [
+              {
+                "name": "success",
+                "type": "boolean"
+              }
+            ]
+          }
+        ]
       }
     ],
     "events": [
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 986fc8e..c0f35e61 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1732,6 +1732,7 @@
           "../browser/ui/views/bookmarks/bookmark_bubble_sign_in_delegate_browsertest.cc",
           "../browser/ui/views/relaunch_notification/relaunch_recommended_bubble_view_browsertest.cc",
           "../browser/ui/views/relaunch_notification/relaunch_required_dialog_view_browsertest.cc",
+          "../browser/ui/views/toolbar/toolbar_page_action_icon_container_view_browsertest.cc",
         ]
       }
       deps += [
@@ -4706,9 +4707,6 @@
     if (is_mac) {
       sources += [ "../browser/ui/views/frame/browser_non_client_frame_view_mac_unittest.mm" ]
     }
-    if (is_chromeos) {
-      sources += [ "../browser/ui/views/ime_driver/input_method_bridge_chromeos_unittest.cc" ]
-    }
     if (!is_chromeos) {
       sources += [
         "../browser/ui/views/frame/opaque_browser_frame_view_layout_unittest.cc",
diff --git a/chrome/test/data/extensions/api_test/input_method/typing/background.js b/chrome/test/data/extensions/api_test/input_method/typing/background.js
new file mode 100644
index 0000000..24ba7db
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/input_method/typing/background.js
@@ -0,0 +1,139 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+class TestEnv {
+  constructor() {
+    this.inputContext = null;
+
+    chrome.input.ime.onFocus.addListener((context) => {
+      this.inputContext = context;
+    });
+
+    chrome.input.ime.onBlur.addListener(() => {
+      this.inputContext = null;
+    });
+  }
+
+  getContextID() {
+    return this.inputContext.contextID;
+  }
+
+  onSurroundingTextChanged() {
+    return new Promise((resolve) => {
+      chrome.input.ime.onSurroundingTextChanged.addListener(
+          function listener(_, surroundingInfo) {
+            chrome.input.ime.onSurroundingTextChanged.removeListener(listener);
+            resolve(surroundingInfo.text);
+          });
+    });
+  }
+
+  onCompositionBoundsChanged() {
+    return new Promise((resolve) => {
+      chrome.inputMethodPrivate.onCompositionBoundsChanged.addListener(
+          function listener(_, boundsList) {
+            chrome.inputMethodPrivate.onCompositionBoundsChanged.removeListener(
+                listener);
+            resolve(boundsList);
+          });
+    });
+  }
+};
+
+const testEnv = new TestEnv();
+
+// Wrap inputMethodPrivate in a promise-based API to simplify test code.
+function wrapAsync(apiFunction) {
+  return (...args) => {
+    return new Promise((resolve, reject) => {
+      apiFunction(...args, (...result) => {
+        if (!!chrome.runtime.lastError) {
+          console.log(chrome.runtime.lastError.message);
+          reject(Error(chrome.runtime.lastError));
+        } else {
+          resolve(...result);
+        }
+      });
+    });
+  }
+}
+
+const asyncInputIme = {
+  commitText: wrapAsync(chrome.input.ime.commitText),
+  setComposition: wrapAsync(chrome.input.ime.setComposition),
+}
+
+const asyncInputMethodPrivate = {
+  setCurrentInputMethod:
+      wrapAsync(chrome.inputMethodPrivate.setCurrentInputMethod),
+  setCompositionRange:
+      wrapAsync(chrome.inputMethodPrivate.setCompositionRange)
+};
+
+chrome.test.runTests([
+  async function setUp() {
+    await asyncInputMethodPrivate.setCurrentInputMethod(
+        '_ext_ime_ilanclmaeigfpnmdlgelmhkpkegdioiptest');
+
+    chrome.test.succeed();
+  },
+
+  async function setCompositionRangeTest() {
+    await asyncInputIme.commitText({
+      contextID: testEnv.getContextID(),
+      text: 'hello world'
+    });
+
+    chrome.test.assertEq('hello world',
+        await testEnv.onSurroundingTextChanged());
+
+    // Cursor is at the end of the string.
+    await asyncInputMethodPrivate.setCompositionRange({
+      contextID: testEnv.getContextID(),
+      selectionBefore: 5,
+      selectionAfter: 0,
+      segments: [
+        { start: 0, end: 2, style: "underline" },
+        { start: 2, end: 5, style: "underline" }
+      ]
+    });
+
+    // Should underline "world".
+    chrome.test.assertEq(5,
+      (await testEnv.onCompositionBoundsChanged()).length);
+
+    await asyncInputIme.setComposition({
+      contextID: testEnv.getContextID(),
+      text: "foo",
+      cursor: 0
+    });
+
+    // Composition should change to "foo".
+    chrome.test.assertEq(3,
+      (await testEnv.onCompositionBoundsChanged()).length);
+
+    // Should replace composition with "again".
+    await asyncInputIme.commitText({
+      contextID: testEnv.getContextID(),
+      text: 'again'
+    });
+
+    chrome.test.assertEq('hello again',
+        await testEnv.onSurroundingTextChanged());
+
+    // Cursor is at end of the string.
+    // Call setCompositionRange with no segments.
+    await asyncInputMethodPrivate.setCompositionRange({
+      contextID: testEnv.getContextID(),
+      selectionBefore: 5,
+      selectionAfter: 0
+    });
+
+    // Composition should be "again".
+    chrome.test.assertEq(5,
+      (await testEnv.onCompositionBoundsChanged()).length);
+
+    chrome.test.succeed();
+  }
+]);
diff --git a/chrome/test/data/extensions/api_test/input_method/typing/manifest.json b/chrome/test/data/extensions/api_test/input_method/typing/manifest.json
new file mode 100644
index 0000000..8fdea462
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/input_method/typing/manifest.json
@@ -0,0 +1,20 @@
+{
+  "key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC9fDu8apG3Dz72XTT3Ym1SfGt06tdowTlYQ+3lGlCbVpfnMOmewgRgYxzUtUPso9aQERZcmI2+7UtbWjtk6/usl9Hr7a1JBQwfaUoUygEe56ajUeZhe/ErkH5CXT84U0pokfPr5vMvc7RVPduU+UBiF0DnGb/hSpzz/1UhJ5H9AwIDAQAB",
+  "name": "inputMethodPrivate tests",
+  "version": "0.1",
+  "manifest_version": 2,
+  "description": "Input method API tests.",
+  "background": {
+    "scripts": ["background.js"]
+  },
+  "permissions": [ "input", "inputMethodPrivate" ],
+  "input_components": [{
+    "name": "Test IME",
+    "type": "ime",
+    "id": "test",
+    "description": "Test",
+    "language": "en",
+    "layouts": ["us::eng"]
+  }]
+}
+
diff --git a/chrome/test/data/extensions/api_test/input_method/typing/test_page.html b/chrome/test/data/extensions/api_test/input_method/typing/test_page.html
new file mode 100644
index 0000000..cc27846
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/input_method/typing/test_page.html
@@ -0,0 +1,6 @@
+<!--
+ * Copyright 2019 The Chromium Authors. All rights reserved.  Use of this
+ * source code is governed by a BSD-style license that can be found in the
+ * LICENSE file.
+-->
+<input id="target" type="text" autofocus/>
diff --git a/chrome/test/data/webui/print_preview/destination_select_test.js b/chrome/test/data/webui/print_preview/destination_select_test.js
index ad8090d..9f056d8 100644
--- a/chrome/test/data/webui/print_preview/destination_select_test.js
+++ b/chrome/test/data/webui/print_preview/destination_select_test.js
@@ -91,9 +91,10 @@
           JSON.parse(initialSettings.serializedAppStateStr).recentDestinations :
           [];
       destinationSettings.setSetting('recentDestinations', recentDestinations);
-      destinationSettings.initDestinationStore(
+      destinationSettings.init(
           initialSettings.printerName,
-          initialSettings.serializedDefaultDestinationSelectionRulesStr);
+          initialSettings.serializedDefaultDestinationSelectionRulesStr,
+          initialSettings.userAccounts);
       destinationSettings.disabled = false;
       return opt_expectPrinterFailure ? Promise.resolve() : Promise.race([
         nativeLayer.whenCalled('getPrinterCapabilities'), whenCapabilitiesReady
@@ -337,6 +338,7 @@
         version: 2,
         recentDestinations: [recentDestination],
       });
+      initialSettings.userAccounts = ['foo@chromium.org'];
 
       return setInitialSettings().then(function(args) {
         assertEquals('FooDevice', args.destinationId);
@@ -400,6 +402,7 @@
         version: 2,
         recentDestinations: recentDestinations,
       });
+      initialSettings.userAccounts = [account1, account2];
 
       return setInitialSettings()
           .then(() => {
diff --git a/chrome/test/data/webui/print_preview/destination_settings_test.js b/chrome/test/data/webui/print_preview/destination_settings_test.js
index d46ae79..1229f78 100644
--- a/chrome/test/data/webui/print_preview/destination_settings_test.js
+++ b/chrome/test/data/webui/print_preview/destination_settings_test.js
@@ -35,6 +35,9 @@
     /** @type {!Array<!print_preview.Destination>} */
     let destinations = [];
 
+    /** @type {!Array<string>} */
+    let initialAccounts = [];
+
     /** @type {string} */
     const defaultUser = 'foo@chromium.org';
 
@@ -78,9 +81,10 @@
 
       // Set up the destination store, but no destination yet. Dropdown is still
       // hidden.
-      destinationSettings.initDestinationStore(
+      destinationSettings.init(
           'FooDevice' /* printerName */,
-          '' /* serializedDefaultDestinationSelectionRulesStr */);
+          '' /* serializedDefaultDestinationSelectionRulesStr */,
+          [] /* userAccounts */);
       assertTrue(dropdown.hidden);
 
       return test_util
@@ -161,15 +165,18 @@
       destinationSettings.cloudPrintInterface = cloudPrintInterface;
       destinationSettings.setSetting('recentDestinations', recentDestinations);
       destinationSettings.appKioskMode = false;
-      destinationSettings.initDestinationStore(
+      destinationSettings.init(
           '' /* printerName */,
-          '' /* serializedDefaultDestinationSelectionRulesStr */);
+          '' /* serializedDefaultDestinationSelectionRulesStr */,
+          initialAccounts);
       destinationSettings.state = print_preview.State.READY;
       destinationSettings.disabled = false;
     }
 
     /** Simulates a user signing in to Chrome. */
     function signIn() {
+      destinationSettings.$.userInfo.updateActiveUser(defaultUser, false);
+      destinationSettings.$.userInfo.updateUsers([defaultUser]);
       cloudPrintInterface.setPrinter(
           print_preview_test_utils.getGoogleDriveDestination(defaultUser));
       cr.webUIListenerCallback('reload-printer-list');
@@ -533,6 +540,7 @@
         cloudPrinterUser1, cloudPrinterUser2, destinations[0]
       ].map(destination => print_preview.makeRecentDestination(destination));
 
+      initialAccounts = [defaultUser, account2];
       initialize();
       Polymer.dom.flush();
 
diff --git a/chrome/test/data/webui/print_preview/invalid_settings_browsertest.js b/chrome/test/data/webui/print_preview/invalid_settings_browsertest.js
index 4a4a6c7..8d12aea 100644
--- a/chrome/test/data/webui/print_preview/invalid_settings_browsertest.js
+++ b/chrome/test/data/webui/print_preview/invalid_settings_browsertest.js
@@ -97,6 +97,7 @@
         ],
       });
       initialSettings.cloudPrintURL = 'cloudprint URL';
+      initialSettings.userAccounts = [printers[0].account];
       localDestinationInfos = [];
 
       loadTimeData.overrideValues({isEnterpriseManaged: false});
diff --git a/chrome/test/data/webui/print_preview/pages_settings_test.js b/chrome/test/data/webui/print_preview/pages_settings_test.js
index de86239..8d3770e 100644
--- a/chrome/test/data/webui/print_preview/pages_settings_test.js
+++ b/chrome/test/data/webui/print_preview/pages_settings_test.js
@@ -108,6 +108,11 @@
           pagesSection, pagesSection.pagesValueEnum_.CUSTOM.toString());
       assertTrue(customInputCollapse.opened);
       validateState([1, 2], [{from: 1, to: 2}], '', false);
+
+      // Set a selection equal to the full page range. This should set ranges to
+      // empty, so that reselecting "all" does not regenerate the preview.
+      await setCustomInput('1-3');
+      validateState([1, 2, 3], [], '', false);
     });
 
     // Tests that the page ranges set are valid for different user inputs.
@@ -133,7 +138,7 @@
       validateState(tenToHundred, [{from: 10, to: 100}], '', false);
 
       await setCustomInput('-');
-      validateState(oneToHundred, [{from: 1, to: 100}], '', false);
+      validateState(oneToHundred, [], '', false);
 
       // https://crbug.com/806165
       await setCustomInput('1\u30012\u30013\u30011\u300156');
@@ -246,18 +251,18 @@
       await whenBlurred;
       // Blurring a blank field sets the full page range.
       assertEquals(customValue, select.value);
-      validateState([1, 2, 3], [{from: 1, to: 3}], '', false);
+      validateState([1, 2, 3], [], '', false);
       assertEquals('1-3', input.value);
       input.focus();
 
       await setCustomInput('5');
       assertEquals(customValue, select.value);
       // Invalid input doesn't change the preview.
-      validateState([1, 2, 3], [{from: 1, to: 3}], limitError + '3', true);
+      validateState([1, 2, 3], [], limitError + '3', true);
 
       await setCustomInput('');
       assertEquals(customValue, select.value);
-      validateState([1, 2, 3], [{from: 1, to: 3}], '', false);
+      validateState([1, 2, 3], [], '', false);
       whenBlurred = test_util.eventToPromise('blur', input);
       input.blur();
 
@@ -265,7 +270,7 @@
       // value to all pages.
       await whenBlurred;
       assertEquals(customValue, select.value);
-      validateState([1, 2, 3], [{from: 1, to: 3}], '', false);
+      validateState([1, 2, 3], [], '', false);
       assertEquals('1-3', input.value);
 
       // Re-focus and clear the input and then select "All" in the
diff --git a/chrome/test/data/webui/settings/cr_settings_browsertest.js b/chrome/test/data/webui/settings/cr_settings_browsertest.js
index 74b8c44..1e02026 100644
--- a/chrome/test/data/webui/settings/cr_settings_browsertest.js
+++ b/chrome/test/data/webui/settings/cr_settings_browsertest.js
@@ -590,7 +590,8 @@
   ]),
 };
 
-TEST_F('CrSettingsPeoplePageKerberosAccountsTest', 'All', function() {
+// Test is consistently failing. http://crbug.com/960837
+TEST_F('CrSettingsPeoplePageKerberosAccountsTest', 'DISABLED_All', function() {
   mocha.run();
 });
 
diff --git a/chrome/test/data/webui/settings/cups_printer_page_tests.js b/chrome/test/data/webui/settings/cups_printer_page_tests.js
index f937df8..2c4aca4 100644
--- a/chrome/test/data/webui/settings/cups_printer_page_tests.js
+++ b/chrome/test/data/webui/settings/cups_printer_page_tests.js
@@ -18,6 +18,7 @@
       'cancelPrinterSetUp',
       'updateCupsPrinter',
       'reconfigureCupsPrinter',
+      'getEulaUrl',
     ]);
 
     this.printerList = [];
@@ -25,6 +26,13 @@
     this.models = [];
     this.printerInfo = {};
     this.printerPpdMakeModel = {};
+
+    /**
+     * |eulaUrl_| in conjunction with |setEulaUrl| mimics setting the EULA url
+     * for a printer.
+     * @private {string}
+     */
+    this.eulaUrl_ = '';
   }
 
   /** @override */
@@ -78,7 +86,7 @@
 
   /** @override */
   updateCupsPrinter(printerId, printerName) {
-    this.methodCalled('updateCupsPrinter', printerId, printerName);
+    this.methodCalled('updateCupsPrinter', [printerId, printerName]);
   }
 
   /** @override */
@@ -91,6 +99,44 @@
   reconfigureCupsPrinter(printer) {
     this.methodCalled('reconfigureCupsPrinter', printer);
   }
+
+  /** @override */
+  getEulaUrl(ppdManufacturer, ppdModel) {
+    this.methodCalled('getEulaUrl', [ppdManufacturer, ppdModel]);
+    return Promise.resolve(this.eulaUrl_);
+  }
+
+  /** @param {string} eulaUrl */
+  setEulaUrl(eulaUrl) {
+    this.eulaUrl_ = eulaUrl;
+  }
+}
+
+/*
+ * Helper function that waits for |getEulaUrl| to get called and then verifies
+ * its arguments.
+ * @param {!TestCupsPrintersBrowserProxy} cupsPrintersBrowserProxy
+ * @param {string} expectedManufacturer
+ * @param {string} expectedModel
+ * @return {!Promise}
+ */
+function verifyGetEulaUrlWasCalled(
+    cupsPrintersBrowserProxy, expectedManufacturer, expectedModel) {
+  return cupsPrintersBrowserProxy.whenCalled('getEulaUrl').then(function(args) {
+    assertEquals(expectedManufacturer, args[0]);  // ppdManufacturer
+    assertEquals(expectedModel, args[1]);         // ppdModel
+  });
+}
+
+/*
+ * Helper function that resets the resolver for |getEulaUrl| and sets the new
+ * EULA URL.
+ * @param {!TestCupsPrintersBrowserProxy} cupsPrintersBrowserProxy
+ * @param {string} eulaUrl
+ */
+function resetGetEulaUrl(cupsPrintersBrowserProxy, eulaUrl) {
+  cupsPrintersBrowserProxy.resetResolver('getEulaUrl');
+  cupsPrintersBrowserProxy.setEulaUrl(eulaUrl);
 }
 
 suite('CupsAddPrinterDialogTests', function() {
@@ -481,6 +527,78 @@
       assertFalse(dialog.showConfiguringDialog_);
     });
   });
+
+  /**
+   * Test that we are checking if a printer model has an EULA upon a model
+   * change.
+   */
+  test('getEulaUrlGetsCalledOnModelChange', function() {
+    const discoveryDialog = dialog.$$('add-printer-discovery-dialog');
+    assertTrue(!!discoveryDialog);
+    discoveryDialog.$$('.secondary-button').click();
+    Polymer.dom.flush();
+
+    const addDialog = dialog.$$('add-printer-manually-dialog');
+    assertTrue(!!addDialog);
+    fillAddManuallyDialog(addDialog);
+
+    addDialog.$$('.action-button').click();
+    Polymer.dom.flush();
+
+    const eulaLink = 'google.com';
+    const expectedManufacturer = 'Google';
+    const expectedModel = 'printer';
+    const expectedModel2 = 'newPrinter';
+    const expectedModel3 = 'newPrinter2';
+
+    let modelDialog = null;
+    let urlElement = null;
+    let modelDropdown = null;
+
+    return cupsPrintersBrowserProxy
+        .whenCalled('getCupsPrinterManufacturersList')
+        .then(function() {
+          modelDialog = dialog.$$('add-printer-manufacturer-model-dialog');
+          assertTrue(!!modelDialog);
+
+          urlElement = modelDialog.$$('#eulaUrl');
+          // Check that the EULA text is not shown.
+          assertTrue(urlElement.hidden);
+
+          cupsPrintersBrowserProxy.setEulaUrl(eulaLink);
+
+          modelDialog.$$('#manufacturerDropdown').value = expectedManufacturer;
+          modelDropdown = modelDialog.$$('#modelDropdown');
+          modelDropdown.value = expectedModel;
+          return verifyGetEulaUrlWasCalled(
+              cupsPrintersBrowserProxy, expectedManufacturer, expectedModel);
+        })
+        .then(function(args) {
+          // Check that the EULA text is shown.
+          assertFalse(urlElement.hidden);
+
+          resetGetEulaUrl(cupsPrintersBrowserProxy, '' /* eulaUrl */);
+
+          // Change ppdModel and expect |getEulaUrl| to be called again.
+          modelDropdown.value = expectedModel2;
+          return verifyGetEulaUrlWasCalled(
+              cupsPrintersBrowserProxy, expectedManufacturer, expectedModel2);
+        })
+        .then(function(args) {
+          // Check that the EULA text is hidden.
+          assertTrue(urlElement.hidden);
+
+          resetGetEulaUrl(cupsPrintersBrowserProxy, eulaLink);
+
+          // Change ppdModel and expect |getEulaUrl| to be called again.
+          modelDropdown.value = expectedModel3;
+          return verifyGetEulaUrlWasCalled(
+              cupsPrintersBrowserProxy, expectedManufacturer, expectedModel3);
+        })
+        .then(function(args) {
+          assertFalse(urlElement.hidden);
+        });
+  });
 });
 
 suite('EditPrinterDialog', function() {
@@ -981,4 +1099,61 @@
     Polymer.dom.flush();
     assertTrue(!saveButton.disabled);
   });
+
+  /**
+   * Test that we are checking if a printer model has an EULA upon a model
+   * change.
+   */
+  test('getEulaUrlGetsCalledOnModelChange', function() {
+    const eulaLink = 'google.com';
+    const expectedManufacturer = 'Google';
+    const expectedModel = 'model';
+    const expectedModel2 = 'newModel';
+    const expectedModel3 = 'newModel2';
+
+    let modelDropdown = null;
+    let urlElement = null;
+
+    return PolymerTest.flushTasks()
+        .then(function() {
+          urlElement = dialog.$$('#eulaUrl');
+          // Check that the EULA text is hidden.
+          assertTrue(urlElement.hidden);
+
+          cupsPrintersBrowserProxy.setEulaUrl(eulaLink);
+
+          dialog.$$('#printerPPDManufacturer').value = expectedManufacturer;
+          modelDropdown = dialog.$$('#printerPPDModel');
+          modelDropdown.value = expectedModel;
+
+          return verifyGetEulaUrlWasCalled(
+              cupsPrintersBrowserProxy, expectedManufacturer, expectedModel);
+        })
+        .then(function() {
+          // Check that the EULA text is shown.
+          assertFalse(urlElement.hidden);
+
+          resetGetEulaUrl(cupsPrintersBrowserProxy, '' /* eulaUrl */);
+
+          // Change ppdModel and expect |getEulaUrl| to be called again.
+          modelDropdown.value = expectedModel2;
+          return verifyGetEulaUrlWasCalled(
+              cupsPrintersBrowserProxy, expectedManufacturer, expectedModel2);
+        })
+        .then(function() {
+          // Check that the EULA text is hidden.
+          assertTrue(urlElement.hidden);
+
+          resetGetEulaUrl(cupsPrintersBrowserProxy, eulaLink);
+
+          // Change ppdModel and expect |getEulaUrl| to be called again.
+          modelDropdown.value = expectedModel3;
+          return verifyGetEulaUrlWasCalled(
+              cupsPrintersBrowserProxy, expectedManufacturer, expectedModel3);
+        })
+        .then(function() {
+          // Check that the EULA text is shown again.
+          assertFalse(urlElement.hidden);
+        });
+  });
 });
diff --git a/chrome/test/data/webui/settings/people_page_kerberos_accounts_test.js b/chrome/test/data/webui/settings/people_page_kerberos_accounts_test.js
index 3b06cf9..855e0a4 100644
--- a/chrome/test/data/webui/settings/people_page_kerberos_accounts_test.js
+++ b/chrome/test/data/webui/settings/people_page_kerberos_accounts_test.js
@@ -9,11 +9,9 @@
       super([
         'getAccounts',
         'addAccount',
+        'reauthenticateAccount',
         'removeAccount',
       ]);
-
-      // Simulated error from a addKerberosAccount call.
-      this.addAccountError = ErrorType.kNone;
     }
 
     /** @override */
@@ -35,9 +33,13 @@
     }
 
     /** @override */
-    addAccount(principalName, password) {
-      this.methodCalled('addAccount', [principalName, password]);
-      return Promise.resolve(this.addAccountError);
+    addAccount() {
+      this.methodCalled('addAccount');
+    }
+
+    /** @override */
+    reauthenticateAccount(principalName) {
+      this.methodCalled('reauthenticateAccount', principalName);
     }
 
     /** @override */
@@ -46,7 +48,6 @@
     }
   }
 
-  // Tests for the Kerberos Accounts settings page.
   suite('KerberosAccountsTests', function() {
     let browserProxy = null;
     let kerberosAccounts = null;
@@ -78,34 +79,22 @@
     });
 
     test('AddAccount', function() {
-      assertTrue(!kerberosAccounts.$$('kerberos-add-account-dialog'));
       assertFalse(kerberosAccounts.$$('#add-account-button').disabled);
       kerberosAccounts.$$('#add-account-button').click();
-      Polymer.dom.flush();
-      addDialog = kerberosAccounts.$$('kerberos-add-account-dialog');
-      assertTrue(!!addDialog);
-      assertEquals('', addDialog.username);
+      assertEquals(1, browserProxy.getCallCount('addAccount'));
     });
 
     test('ReauthenticateAccount', function() {
-      // Wait until accounts are loaded.
       return browserProxy.whenCalled('getAccounts').then(() => {
         Polymer.dom.flush();
-
-        // The kerberos-add-account-dialog shouldn't be open yet.
-        assertTrue(!kerberosAccounts.$$('kerberos-add-account-dialog'));
-
-        // Click "Sign-In" on an existing account.
         // Note that both accounts have a reauth button, but [0] is hidden, so
         // click [1] (clicking a hidden button works, but it feels weird).
         kerberosAccounts.root.querySelectorAll('.reauth-button')[1].click();
-        Polymer.dom.flush();
-
-        // Now the kerberos-add-account-dialog should be open with preset
-        // username.
-        addDialog = kerberosAccounts.$$('kerberos-add-account-dialog');
-        assertTrue(!!addDialog);
-        assertEquals('user2@REALM2', addDialog.username);
+        assertEquals(1, browserProxy.getCallCount('reauthenticateAccount'));
+        return browserProxy.whenCalled('reauthenticateAccount')
+            .then((principalName) => {
+              assertEquals('user2@REALM2', principalName);
+            });
       });
     });
 
@@ -129,141 +118,4 @@
       assertEquals(2, browserProxy.getCallCount('getAccounts'));
     });
   });
-
-  // Tests for the kerberos-add-account-dialog element.
-  suite('KerberosAddAccountTests', function() {
-    let browserProxy = null;
-    let dialog = null;
-    let username = null;
-    let password = null;
-    let addButton = null;
-    let generalError = null;
-
-    setup(function() {
-      browserProxy = new TestKerberosAccountsBrowserProxy();
-      settings.KerberosAccountsBrowserProxyImpl.instance_ = browserProxy;
-      PolymerTest.clearBody();
-
-      dialog = document.createElement('kerberos-add-account-dialog');
-      document.body.appendChild(dialog);
-
-      username = dialog.$.username;
-      assertTrue(!!username);
-
-      password = dialog.$.password;
-      assertTrue(!!password);
-
-      addButton = dialog.$$('.action-button');
-      assertTrue(!!addButton);
-
-      generalError = dialog.$['general-error-message'];
-      assertTrue(!!generalError);
-    });
-
-    teardown(function() {
-      dialog.remove();
-    });
-
-    // Sets |error| as error result for addAccount(), simulates a click on the
-    // addAccount button and checks that |errorElement| has an non-empty
-    // innerText value afterwards.
-    function checkAddAccountError(error, errorElement) {
-      Polymer.dom.flush();
-      assertEquals(0, errorElement.innerText.length);
-      browserProxy.addAccountError = error;
-      addButton.click();
-      return browserProxy.whenCalled('addAccount').then(function() {
-        Polymer.dom.flush();
-        assertNotEquals(0, errorElement.innerText.length);
-      });
-    }
-
-    // The username input field is not disabled by default.
-    test('UsernameFieldNotDisabledByDefault', function() {
-      assertFalse(username.disabled);
-    });
-
-    // The username input field is disabled if a username is preset before the
-    // dialog is appended to the document.
-    test('UsernameFieldDisabledIfPreset', function() {
-      const newDialog = document.createElement('kerberos-add-account-dialog');
-      newDialog.username = 'user';
-      document.body.appendChild(newDialog);
-      assertTrue(newDialog.$.username.disabled);
-    });
-
-    // By clicking the "Add account", the username and password values are
-    // passed to the 'addAccount' browser proxy method.
-    test('AddButtonPassesCredentials', function() {
-      const EXPECTED_USER = 'testuser';
-      const EXPECTED_PASS = 'testpass';
-      username.value = EXPECTED_USER;
-      password.value = EXPECTED_PASS;
-      assertFalse(addButton.disabled);
-      addButton.click();
-      return browserProxy.whenCalled('addAccount').then(function(args) {
-        assertEquals(EXPECTED_USER, args[0]);
-        assertEquals(EXPECTED_PASS, args[1]);
-      });
-    });
-
-    // While an account is being added, the "Add account" is disabled.
-    test('AddButtonDisableWhileInProgress', function() {
-      assertFalse(addButton.disabled);
-      addButton.click();
-      assertTrue(addButton.disabled);
-      return browserProxy.whenCalled('addAccount').then(function(args) {
-        assertFalse(addButton.disabled);
-      });
-    });
-
-    // addAccount: ErrorType.kNetworkProblem spawns a general error.
-    test('AddAccountError_NetworkProblem', function() {
-      checkAddAccountError(
-          settings.KerberosErrorType.kNetworkProblem, generalError);
-    });
-
-    // addAccount: ErrorType.kParsePrincipalFailed spawns a username error.
-    test('AddAccountError_ParsePrincipalFailed', function() {
-      checkAddAccountError(
-          settings.KerberosErrorType.kParsePrincipalFailed, username.$.error);
-    });
-
-    // addAccount: ErrorType.kBadPrincipal spawns a username error.
-    test('AddAccountError_BadPrincipal', function() {
-      checkAddAccountError(
-          settings.KerberosErrorType.kBadPrincipal, username.$.error);
-    });
-
-    // addAccount: ErrorType.kContactingKdcFailed spawns a username error.
-    test('AddAccountError_ContactingKdcFailed', function() {
-      checkAddAccountError(
-          settings.KerberosErrorType.kContactingKdcFailed, username.$.error);
-    });
-
-    // addAccount: ErrorType.kBadPassword spawns a password error.
-    test('AddAccountError_BadPassword', function() {
-      checkAddAccountError(
-          settings.KerberosErrorType.kBadPassword, password.$.error);
-    });
-
-    // addAccount: ErrorType.kPasswordExpired spawns a password error.
-    test('AddAccountError_PasswordExpired', function() {
-      checkAddAccountError(
-          settings.KerberosErrorType.kPasswordExpired, password.$.error);
-    });
-
-    // addAccount: ErrorType.kKdcDoesNotSupportEncryptionType spawns a general
-    // error.
-    test('AddAccountError_KdcDoesNotSupportEncryptionType', function() {
-      checkAddAccountError(
-          settings.KerberosErrorType.kKdcDoesNotSupportEncryptionType,
-          generalError);
-    });
-
-    // addAccount: ErrorType.kUnknown spawns a general error.
-    test('AddAccountError_Unknown', function() {
-      checkAddAccountError(settings.KerberosErrorType.kUnknown, generalError);
-    });
-  });
-});
\ No newline at end of file
+});
diff --git a/chrome/test/delayload/delayloads_unittest.cc b/chrome/test/delayload/delayloads_unittest.cc
index 2d09341..ff4bfd9 100644
--- a/chrome/test/delayload/delayloads_unittest.cc
+++ b/chrome/test/delayload/delayloads_unittest.cc
@@ -114,7 +114,7 @@
   }
 }
 
-TEST_F(DelayloadsTest, ChromeDllLoadSanityTest) {
+TEST_F(DelayloadsTest, DISABLED_ChromeDllLoadSanityTest) {
   // As a precaution to avoid affecting other tests, we need to ensure this is
   // executed in its own test process. This "test" will re-launch with custom
   // parameters to accomplish that.
@@ -159,7 +159,7 @@
   EXPECT_TRUE(!!::FreeLibrary(chrome_module_handle));
 }
 
-TEST_F(DelayloadsTest, ChromeChildDllDelayloadsCheck) {
+TEST_F(DelayloadsTest, DISABLED_ChromeChildDllDelayloadsCheck) {
   base::FilePath dll;
   ASSERT_TRUE(base::PathService::Get(base::DIR_EXE, &dll));
   dll = dll.Append(L"chrome_child.dll");
diff --git a/chrome/test/ppapi/ppapi_browsertest.cc b/chrome/test/ppapi/ppapi_browsertest.cc
index c40c561..2794eb6 100644
--- a/chrome/test/ppapi/ppapi_browsertest.cc
+++ b/chrome/test/ppapi/ppapi_browsertest.cc
@@ -2146,7 +2146,9 @@
 // aura: http://crbug.com/104384
 // cros: http://crbug.com/396502
 // windows: http://crbug.com/899893
-#if defined(OS_MACOSX) || defined(OS_CHROMEOS) || defined(OS_WIN)
+// linux: http://crbug.com/899893
+#if defined(OS_MACOSX) || defined(OS_CHROMEOS) || defined(OS_WIN) || \
+    defined(OS_LINUX)
 #define MAYBE_FlashFullscreen DISABLED_FlashFullscreen
 #else
 #define MAYBE_FlashFullscreen FlashFullscreen
diff --git a/chrome/tools/build/mac/clean_up_old_versions.py b/chrome/tools/build/mac/clean_up_old_versions.py
index 7c34560..c8216dc 100644
--- a/chrome/tools/build/mac/clean_up_old_versions.py
+++ b/chrome/tools/build/mac/clean_up_old_versions.py
@@ -19,6 +19,10 @@
         else:
           os.unlink(path)
 
+  for path in args.delete:
+    if os.path.exists(path):
+      shutil.rmtree(path)
+
   open(args.stamp, 'w').close()
   os.utime(args.stamp, None)
 
@@ -32,9 +36,16 @@
   parser.add_argument(
       '--keep',
       action='append',
+      default=[],
       help=('The names of items to keep in the `--versions-dir`. '
             'Can be specified multiple times.'))
   parser.add_argument(
+      '--delete',
+      action='append',
+      default=[],
+      help=('Unconditionally deletes the tree at this path. Can be '
+            'specified multiple times.'))
+  parser.add_argument(
       '--stamp', required=True, help='Path to write the stamp file.')
   args = parser.parse_args()
 
diff --git a/chromecast/browser/BUILD.gn b/chromecast/browser/BUILD.gn
index 8e49eb1..0444371c 100644
--- a/chromecast/browser/BUILD.gn
+++ b/chromecast/browser/BUILD.gn
@@ -139,6 +139,7 @@
     ":prefs",
     ":public",
     "//chromecast/browser/metrics",
+    "//chromecast/metrics",
   ]
 
   deps = [
diff --git a/chromecast/browser/DEPS b/chromecast/browser/DEPS
index ddc5fd9..73d6d406 100644
--- a/chromecast/browser/DEPS
+++ b/chromecast/browser/DEPS
@@ -9,6 +9,7 @@
   "+chromecast/external_mojo",
   "+chromecast/internal/shell/browser",
   "+chromecast/media",
+  "+chromecast/metrics",
   "+chromecast/net",
   "+chromecast/service",
   "+components/cdm/browser",
diff --git a/chromecast/browser/cast_browser_main_parts.cc b/chromecast/browser/cast_browser_main_parts.cc
index ffce448..fc0b42b 100644
--- a/chromecast/browser/cast_browser_main_parts.cc
+++ b/chromecast/browser/cast_browser_main_parts.cc
@@ -40,7 +40,7 @@
 #include "chromecast/browser/cast_net_log.h"
 #include "chromecast/browser/devtools/remote_debugging_server.h"
 #include "chromecast/browser/media/media_caps_impl.h"
-#include "chromecast/browser/metrics/cast_metrics_service_client.h"
+#include "chromecast/browser/metrics/cast_browser_metrics.h"
 #include "chromecast/browser/tts/tts_controller_impl.h"
 #include "chromecast/browser/tts/tts_platform_stub.h"
 #include "chromecast/browser/url_request_context_factory.h"
@@ -50,6 +50,7 @@
 #include "chromecast/media/base/media_resource_tracker.h"
 #include "chromecast/media/base/video_plane_controller.h"
 #include "chromecast/media/cma/backend/media_pipeline_backend_manager.h"
+#include "chromecast/metrics/cast_metrics_service_client.h"
 #include "chromecast/net/connectivity_checker.h"
 #include "chromecast/public/cast_media_shlib.h"
 #include "chromecast/service/cast_service.h"
@@ -613,7 +614,7 @@
   // service is initialized because CastMetricsServiceClient,
   // CastURLLoaderThrottle and CastNetworkDelegate may use components
   // initialized by cast service.
-  cast_browser_process_->metrics_service_client()->Initialize();
+  cast_browser_process_->cast_browser_metrics()->Initialize();
   cast_content_browser_client_->InitializeURLLoaderThrottleDelegate();
   url_request_context_factory_->InitializeNetworkDelegates();
 
@@ -700,7 +701,7 @@
   window_manager_.reset();
 
   cast_browser_process_->cast_service()->Finalize();
-  cast_browser_process_->metrics_service_client()->Finalize();
+  cast_browser_process_->cast_browser_metrics()->Finalize();
   cast_browser_process_.reset();
 
 #if !defined(OS_FUCHSIA)
diff --git a/chromecast/browser/cast_browser_process.cc b/chromecast/browser/cast_browser_process.cc
index 1f127f5..541a761 100644
--- a/chromecast/browser/cast_browser_process.cc
+++ b/chromecast/browser/cast_browser_process.cc
@@ -12,8 +12,9 @@
 #include "chromecast/browser/cast_content_browser_client.h"
 #include "chromecast/browser/cast_network_contexts.h"
 #include "chromecast/browser/devtools/remote_debugging_server.h"
-#include "chromecast/browser/metrics/cast_metrics_service_client.h"
+#include "chromecast/browser/metrics/cast_browser_metrics.h"
 #include "chromecast/browser/tts/tts_controller.h"
+#include "chromecast/metrics/cast_metrics_service_client.h"
 #include "chromecast/net/connectivity_checker.h"
 #include "chromecast/service/cast_service.h"
 #include "components/prefs/pref_service.h"
@@ -112,8 +113,9 @@
 
 void CastBrowserProcess::SetMetricsServiceClient(
     std::unique_ptr<metrics::CastMetricsServiceClient> metrics_service_client) {
-  DCHECK(!metrics_service_client_);
-  metrics_service_client_.swap(metrics_service_client);
+  DCHECK(!cast_browser_metrics_);
+  cast_browser_metrics_ = std::make_unique<metrics::CastBrowserMetrics>(
+      std::move(metrics_service_client));
 }
 
 void CastBrowserProcess::SetPrefService(
diff --git a/chromecast/browser/cast_browser_process.h b/chromecast/browser/cast_browser_process.h
index 8f0dac0..3220ec6 100644
--- a/chromecast/browser/cast_browser_process.h
+++ b/chromecast/browser/cast_browser_process.h
@@ -27,6 +27,7 @@
 
 namespace metrics {
 class CastMetricsServiceClient;
+class CastBrowserMetrics;
 }  // namespace metrics
 
 namespace shell {
@@ -96,8 +97,8 @@
 #endif  //  BUILDFLAG(ENABLE_CHROMECAST_EXTENSIONS)
 
 #endif  // defined(USE_AURA)
-  metrics::CastMetricsServiceClient* metrics_service_client() const {
-    return metrics_service_client_.get();
+  metrics::CastBrowserMetrics* cast_browser_metrics() const {
+    return cast_browser_metrics_.get();
   }
   PrefService* pref_service() const { return pref_service_.get(); }
   ConnectivityChecker* connectivity_checker() const {
@@ -125,7 +126,7 @@
   std::unique_ptr<PrefService> pref_service_;
   scoped_refptr<ConnectivityChecker> connectivity_checker_;
   std::unique_ptr<CastBrowserContext> browser_context_;
-  std::unique_ptr<metrics::CastMetricsServiceClient> metrics_service_client_;
+  std::unique_ptr<metrics::CastBrowserMetrics> cast_browser_metrics_;
   std::unique_ptr<RemoteDebuggingServer> remote_debugging_server_;
 
   CastWebViewFactory* web_view_factory_ = nullptr;
diff --git a/chromecast/browser/cast_content_browser_client.cc b/chromecast/browser/cast_content_browser_client.cc
index 6a0363f..e3d96c06 100644
--- a/chromecast/browser/cast_content_browser_client.cc
+++ b/chromecast/browser/cast_content_browser_client.cc
@@ -233,11 +233,15 @@
 #endif
   });
 
-  // TODO(mdellaquila): This feature has to be disabled because it causes
-  // significantly higher power consumption while flinging media files.
-  // Remove this after fixing the bug: b/111363899
-  cast_feature_list_creator_->SetExtraDisableFeatures(
-      {::media::kUseModernMediaControls});
+  cast_feature_list_creator_->SetExtraDisableFeatures({
+    // TODO(mdellaquila): This feature has to be disabled because it causes
+    // significantly higher power consumption while flinging media files.
+    // Remove this after fixing the bug: b/111363899
+    ::media::kUseModernMediaControls,
+#if defined(OS_ANDROID)
+        ::media::kAudioFocusLossSuspendMediaSession,
+#endif
+  });
 }
 
 CastContentBrowserClient::~CastContentBrowserClient() {
diff --git a/chromecast/browser/cast_content_browser_client.h b/chromecast/browser/cast_content_browser_client.h
index bd3e2f6..244b4a47 100644
--- a/chromecast/browser/cast_content_browser_client.h
+++ b/chromecast/browser/cast_content_browser_client.h
@@ -15,8 +15,8 @@
 #include "base/threading/thread.h"
 #include "build/build_config.h"
 #include "build/buildflag.h"
-#include "chromecast/browser/metrics/cast_metrics_service_client.h"
 #include "chromecast/chromecast_buildflags.h"
+#include "chromecast/metrics/cast_metrics_service_client.h"
 #include "content/public/browser/certificate_request_result_type.h"
 #include "content/public/browser/content_browser_client.h"
 #include "services/service_manager/public/cpp/binder_registry.h"
diff --git a/chromecast/browser/metrics/BUILD.gn b/chromecast/browser/metrics/BUILD.gn
index 12afe05..c029745 100644
--- a/chromecast/browser/metrics/BUILD.gn
+++ b/chromecast/browser/metrics/BUILD.gn
@@ -6,10 +6,10 @@
 
 cast_source_set("metrics") {
   sources = [
+    "cast_browser_metrics.cc",
+    "cast_browser_metrics.h",
     "cast_metrics_prefs.cc",
     "cast_metrics_prefs.h",
-    "cast_metrics_service_client.cc",
-    "cast_metrics_service_client.h",
     "cast_stability_metrics_provider.cc",
     "cast_stability_metrics_provider.h",
   ]
@@ -20,6 +20,7 @@
     "//chromecast/base",
     "//chromecast/base:cast_sys_info_util",
     "//chromecast/base:cast_version",
+    "//chromecast/metrics",
     "//components/metrics",
     "//components/metrics:gpu",
     "//components/metrics:net",
diff --git a/chromecast/browser/metrics/DEPS b/chromecast/browser/metrics/DEPS
index ac999449..f0a2c8d 100644
--- a/chromecast/browser/metrics/DEPS
+++ b/chromecast/browser/metrics/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
+  "+chromecast/metrics",
   "+components/metrics",
   "+third_party/metrics_proto",
 ]
diff --git a/chromecast/browser/metrics/cast_browser_metrics.cc b/chromecast/browser/metrics/cast_browser_metrics.cc
new file mode 100644
index 0000000..22b4740
--- /dev/null
+++ b/chromecast/browser/metrics/cast_browser_metrics.cc
@@ -0,0 +1,139 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromecast/browser/metrics/cast_browser_metrics.h"
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "chromecast/base/chromecast_switches.h"
+#include "chromecast/base/path_utils.h"
+#include "chromecast/browser/metrics/cast_stability_metrics_provider.h"
+#include "components/metrics/gpu/gpu_metrics_provider.h"
+#include "components/metrics/metrics_service.h"
+#include "components/metrics/net/network_metrics_provider.h"
+#include "components/metrics/ui/screen_info_metrics_provider.h"
+#include "content/public/browser/histogram_fetcher.h"
+#include "content/public/browser/network_service_instance.h"
+#include "content/public/common/content_switches.h"
+
+#if defined(OS_LINUX)
+#include "chromecast/browser/metrics/external_metrics.h"
+#endif  // defined(OS_LINUX)
+
+#if defined(OS_ANDROID)
+#include "chromecast/base/android/dumpstate_writer.h"
+#endif
+
+namespace chromecast {
+namespace metrics {
+
+const int kMetricsFetchTimeoutSeconds = 60;
+
+#if defined(OS_LINUX)
+const char kExternalUmaEventsRelativePath[] = "metrics/uma-events";
+const char kPlatformUmaEventsPath[] = "/data/share/chrome/metrics/uma-events";
+#endif  // defined(OS_LINUX)
+
+CastBrowserMetrics::CastBrowserMetrics(
+    std::unique_ptr<CastMetricsServiceClient> metrics_service_client) {
+  metrics_service_client_ = std::move(metrics_service_client);
+  metrics_service_client_->SetCallbacks(
+      base::BindRepeating(&CastBrowserMetrics::CollectFinalMetricsForLog,
+                          base::Unretained(this)),
+      base::BindRepeating(&CastBrowserMetrics::ProcessExternalEvents,
+                          base::Unretained(this)));
+}
+
+CastBrowserMetrics::~CastBrowserMetrics() {
+#if defined(OS_LINUX)
+  DCHECK(!external_metrics_);
+  DCHECK(!platform_metrics_);
+#endif  // defined(OS_LINUX)
+}
+
+void CastBrowserMetrics::Initialize() {
+  metrics_service_client_->InitializeMetricsService();
+
+  auto* metrics_service = metrics_service_client_->GetMetricsService();
+  auto stability_provider_unique_ptr =
+      std::make_unique<CastStabilityMetricsProvider>(
+          metrics_service, metrics_service_client_->pref_service());
+#if defined(OS_LINUX)
+  auto* stability_provider = stability_provider_unique_ptr.get();
+#endif  // defined(OS_LINUX)
+  metrics_service->RegisterMetricsProvider(
+      std::move(stability_provider_unique_ptr));
+
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+  if (!command_line->HasSwitch(switches::kDisableGpu)) {
+    metrics_service->RegisterMetricsProvider(
+        std::make_unique<::metrics::GPUMetricsProvider>());
+
+    // TODO(gfhuang): Does ChromeCast actually need metrics about screen info?
+    // crbug.com/541577
+    metrics_service->RegisterMetricsProvider(
+        std::make_unique<::metrics::ScreenInfoMetricsProvider>());
+  }
+
+  metrics_service->RegisterMetricsProvider(
+      std::make_unique<::metrics::NetworkMetricsProvider>(
+          content::CreateNetworkConnectionTrackerAsyncGetter()));
+
+  metrics_service_client_->StartMetricsService();
+
+#if defined(OS_LINUX)
+  // Start external metrics collection, which feeds data from external
+  // processes into the main external metrics.
+  external_metrics_ = new ExternalMetrics(
+      stability_provider,
+      GetHomePathASCII(kExternalUmaEventsRelativePath).value());
+  external_metrics_->Start();
+  platform_metrics_ =
+      new ExternalMetrics(stability_provider, kPlatformUmaEventsPath);
+  platform_metrics_->Start();
+#endif  // defined(OS_LINUX)
+}
+
+void CastBrowserMetrics::Finalize() {
+#if !defined(OS_ANDROID)
+  // Set clean_shutdown bit.
+  metrics_service_client_->GetMetricsService()->RecordCompletedSessionEnd();
+#endif  // !defined(OS_ANDROID)
+
+#if defined(OS_LINUX)
+  // Stop metrics service cleanly before destructing CastMetricsServiceClient.
+  // The pointer will be deleted in StopAndDestroy().
+  external_metrics_->StopAndDestroy();
+  external_metrics_ = nullptr;
+  platform_metrics_->StopAndDestroy();
+  platform_metrics_ = nullptr;
+#endif  // defined(OS_LINUX)
+
+  metrics_service_client_->Finalize();
+}
+
+void CastBrowserMetrics::CollectFinalMetricsForLog(
+    const base::Closure& done_callback) {
+  // Asynchronously fetch metrics data from child processes. Since this method
+  // is called on log upload, metrics that occur between log upload and child
+  // process termination will not be uploaded.
+  content::FetchHistogramsAsynchronously(
+      base::ThreadTaskRunnerHandle::Get(), done_callback,
+      base::TimeDelta::FromSeconds(kMetricsFetchTimeoutSeconds));
+}
+
+void CastBrowserMetrics::ProcessExternalEvents(const base::Closure& cb) {
+#if defined(OS_LINUX)
+  external_metrics_->ProcessExternalEvents(
+      base::Bind(&ExternalMetrics::ProcessExternalEvents,
+                 base::Unretained(platform_metrics_), cb));
+#else
+  cb.Run();
+#endif  // defined(OS_LINUX)
+}
+
+}  // namespace metrics
+}  // namespace chromecast
diff --git a/chromecast/browser/metrics/cast_browser_metrics.h b/chromecast/browser/metrics/cast_browser_metrics.h
new file mode 100644
index 0000000..f3c2fb5
--- /dev/null
+++ b/chromecast/browser/metrics/cast_browser_metrics.h
@@ -0,0 +1,52 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMECAST_BROWSER_METRICS_CAST_BROWSER_METRICS_H_
+#define CHROMECAST_BROWSER_METRICS_CAST_BROWSER_METRICS_H_
+
+#include <memory>
+
+#include "base/callback_forward.h"
+#include "base/macros.h"
+#include "build/build_config.h"
+#include "chromecast/metrics/cast_metrics_service_client.h"
+
+namespace chromecast {
+namespace metrics {
+
+class ExternalMetrics;
+
+class CastBrowserMetrics {
+ public:
+  explicit CastBrowserMetrics(
+      std::unique_ptr<CastMetricsServiceClient> metrics_service_client);
+  ~CastBrowserMetrics();
+  void Initialize();
+  void Finalize();
+
+  // Processes all events from shared file. This should be used to consume all
+  // events in the file before shutdown. This function is safe to call from any
+  // thread.
+  void ProcessExternalEvents(const base::Closure& cb);
+  void CollectFinalMetricsForLog(const base::Closure& done_callback);
+
+  metrics::CastMetricsServiceClient* metrics_service_client() const {
+    return metrics_service_client_.get();
+  }
+
+ private:
+  std::unique_ptr<CastMetricsServiceClient> metrics_service_client_;
+
+#if defined(OS_LINUX)
+  ExternalMetrics* external_metrics_ = nullptr;
+  ExternalMetrics* platform_metrics_ = nullptr;
+#endif  // defined(OS_LINUX)
+
+  DISALLOW_COPY_AND_ASSIGN(CastBrowserMetrics);
+};
+
+}  // namespace metrics
+}  // namespace chromecast
+
+#endif  // CHROMECAST_BROWSER_METRICS_CAST_BROWSER_METRICS_H_
diff --git a/chromecast/browser/metrics/cast_metrics_prefs.cc b/chromecast/browser/metrics/cast_metrics_prefs.cc
index 2755cca..03e4d6c 100644
--- a/chromecast/browser/metrics/cast_metrics_prefs.cc
+++ b/chromecast/browser/metrics/cast_metrics_prefs.cc
@@ -4,8 +4,8 @@
 
 #include "chromecast/browser/metrics/cast_metrics_prefs.h"
 
-#include "chromecast/browser/metrics/cast_metrics_service_client.h"
 #include "chromecast/browser/metrics/cast_stability_metrics_provider.h"
+#include "chromecast/metrics/cast_metrics_service_client.h"
 #include "components/metrics/metrics_service.h"
 
 namespace chromecast {
diff --git a/chromecast/browser/metrics/cast_stability_metrics_provider.cc b/chromecast/browser/metrics/cast_stability_metrics_provider.cc
index e22c89f..b6666b6 100644
--- a/chromecast/browser/metrics/cast_stability_metrics_provider.cc
+++ b/chromecast/browser/metrics/cast_stability_metrics_provider.cc
@@ -10,7 +10,7 @@
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
 #include "chromecast/base/pref_names.h"
-#include "chromecast/browser/metrics/cast_metrics_service_client.h"
+#include "chromecast/metrics/cast_metrics_service_client.h"
 #include "components/metrics/metrics_service.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
diff --git a/chromecast/metrics/BUILD.gn b/chromecast/metrics/BUILD.gn
new file mode 100644
index 0000000..15e7dc4
--- /dev/null
+++ b/chromecast/metrics/BUILD.gn
@@ -0,0 +1,27 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//chromecast/chromecast.gni")
+
+cast_source_set("metrics") {
+  sources = [
+    "cast_metrics_service_client.cc",
+    "cast_metrics_service_client.h",
+  ]
+
+  deps = [
+    "//base",
+    "//base:i18n",
+    "//chromecast/base",
+    "//chromecast/base:cast_sys_info_util",
+    "//chromecast/base:cast_version",
+    "//components/metrics",
+    "//components/metrics:gpu",
+    "//components/metrics:net",
+    "//components/metrics:ui",
+    "//components/prefs",
+    "//services/network/public/cpp",
+    "//third_party/metrics_proto",
+  ]
+}
diff --git a/chromecast/metrics/DEPS b/chromecast/metrics/DEPS
new file mode 100644
index 0000000..cb0a439
--- /dev/null
+++ b/chromecast/metrics/DEPS
@@ -0,0 +1,6 @@
+include_rules = [
+  "+components/metrics",
+  "+components/prefs",
+  "+services/network/public",
+  "+third_party/metrics_proto",
+]
diff --git a/chromecast/browser/metrics/cast_metrics_service_client.cc b/chromecast/metrics/cast_metrics_service_client.cc
similarity index 69%
rename from chromecast/browser/metrics/cast_metrics_service_client.cc
rename to chromecast/metrics/cast_metrics_service_client.cc
index 645835f..aa6df00 100644
--- a/chromecast/browser/metrics/cast_metrics_service_client.cc
+++ b/chromecast/metrics/cast_metrics_service_client.cc
@@ -2,9 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromecast/browser/metrics/cast_metrics_service_client.h"
+#include "chromecast/metrics/cast_metrics_service_client.h"
 
 #include "base/bind.h"
+#include "base/callback.h"
 #include "base/command_line.h"
 #include "base/guid.h"
 #include "base/i18n/rtl.h"
@@ -18,7 +19,6 @@
 #include "chromecast/base/path_utils.h"
 #include "chromecast/base/pref_names.h"
 #include "chromecast/base/version.h"
-#include "chromecast/browser/metrics/cast_stability_metrics_provider.h"
 #include "chromecast/public/cast_sys_info.h"
 #include "components/metrics/client_info.h"
 #include "components/metrics/enabled_state_provider.h"
@@ -28,20 +28,12 @@
 #include "components/metrics/metrics_service.h"
 #include "components/metrics/metrics_state_manager.h"
 #include "components/metrics/net/net_metrics_log_uploader.h"
-#include "components/metrics/net/network_metrics_provider.h"
 #include "components/metrics/ui/screen_info_metrics_provider.h"
 #include "components/metrics/url_constants.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
-#include "content/public/browser/histogram_fetcher.h"
-#include "content/public/browser/network_service_instance.h"
-#include "content/public/common/content_switches.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 
-#if defined(OS_LINUX)
-#include "chromecast/browser/metrics/external_metrics.h"
-#endif  // defined(OS_LINUX)
-
 #if defined(OS_ANDROID)
 #include "chromecast/base/android/dumpstate_writer.h"
 #endif
@@ -52,7 +44,6 @@
 namespace {
 
 const int kStandardUploadIntervalMinutes = 5;
-const int kMetricsFetchTimeoutSeconds = 60;
 
 const char kMetricsOldClientID[] = "user_experience_metrics.client_id";
 
@@ -60,25 +51,20 @@
 const char kClientIdName[] = "Client ID";
 #else
 
-#if defined(OS_LINUX)
-const char kExternalUmaEventsRelativePath[] = "metrics/uma-events";
-const char kPlatformUmaEventsPath[] = "/data/share/chrome/metrics/uma-events";
-#endif  // defined(OS_LINUX)
-
 const struct ChannelMap {
   const char* chromecast_channel;
   const ::metrics::SystemProfileProto::Channel chrome_channel;
 } kMetricsChannelMap[] = {
-  { "canary-channel", ::metrics::SystemProfileProto::CHANNEL_CANARY },
-  { "dev-channel", ::metrics::SystemProfileProto::CHANNEL_DEV },
-  { "developer-channel", ::metrics::SystemProfileProto::CHANNEL_DEV },
-  { "beta-channel", ::metrics::SystemProfileProto::CHANNEL_BETA },
-  { "dogfood-channel", ::metrics::SystemProfileProto::CHANNEL_BETA },
-  { "stable-channel", ::metrics::SystemProfileProto::CHANNEL_STABLE },
+    {"canary-channel", ::metrics::SystemProfileProto::CHANNEL_CANARY},
+    {"dev-channel", ::metrics::SystemProfileProto::CHANNEL_DEV},
+    {"developer-channel", ::metrics::SystemProfileProto::CHANNEL_DEV},
+    {"beta-channel", ::metrics::SystemProfileProto::CHANNEL_BETA},
+    {"dogfood-channel", ::metrics::SystemProfileProto::CHANNEL_BETA},
+    {"stable-channel", ::metrics::SystemProfileProto::CHANNEL_STABLE},
 };
 
-::metrics::SystemProfileProto::Channel
-GetReleaseChannelFromUpdateChannelName(const std::string& channel_name) {
+::metrics::SystemProfileProto::Channel GetReleaseChannelFromUpdateChannelName(
+    const std::string& channel_name) {
   if (channel_name.empty())
     return ::metrics::SystemProfileProto::CHANNEL_UNKNOWN;
 
@@ -152,7 +138,7 @@
     LOG(ERROR) << "Invalid client id from platform: " << force_client_id_
                << " from platform.";
   }
-  return std::unique_ptr<::metrics::ClientInfo>();
+  return nullptr;
 }
 
 int32_t CastMetricsServiceClient::GetProduct() {
@@ -206,8 +192,7 @@
   CHECK(!CAST_IS_DEBUG_BUILD() ||
         channel != ::metrics::SystemProfileProto::CHANNEL_STABLE);
   const bool is_official_build =
-      build_number > 0 &&
-      !CAST_IS_DEBUG_BUILD() &&
+      build_number > 0 && !CAST_IS_DEBUG_BUILD() &&
       channel != ::metrics::SystemProfileProto::CHANNEL_UNKNOWN;
   if (!is_official_build)
     version_string.append("-devel");
@@ -217,12 +202,16 @@
 
 void CastMetricsServiceClient::CollectFinalMetricsForLog(
     const base::Closure& done_callback) {
-  // Asynchronously fetch metrics data from child processes. Since this method
-  // is called on log upload, metrics that occur between log upload and child
-  // process termination will not be uploaded.
-  content::FetchHistogramsAsynchronously(
-      task_runner_, done_callback,
-      base::TimeDelta::FromSeconds(kMetricsFetchTimeoutSeconds));
+  if (collect_final_metrics_cb_)
+    collect_final_metrics_cb_.Run(done_callback);
+}
+
+void CastMetricsServiceClient::SetCallbacks(
+    base::RepeatingCallback<void(const base::Closure&)>
+        collect_final_metrics_cb,
+    base::RepeatingCallback<void(const base::Closure&)> external_events_cb) {
+  collect_final_metrics_cb_ = collect_final_metrics_cb;
+  external_events_cb_ = external_events_cb;
 }
 
 GURL CastMetricsServiceClient::GetMetricsServerUrl() {
@@ -243,10 +232,9 @@
     base::StringPiece mime_type,
     ::metrics::MetricsLogUploader::MetricServiceType service_type,
     const ::metrics::MetricsLogUploader::UploadCallback& on_upload_complete) {
-  return std::unique_ptr<::metrics::MetricsLogUploader>(
-      new ::metrics::NetMetricsLogUploader(url_loader_factory_, server_url,
-                                           insecure_server_url, mime_type,
-                                           service_type, on_upload_complete));
+  return std::make_unique<::metrics::NetMetricsLogUploader>(
+      url_loader_factory_, server_url, insecure_server_url, mime_type,
+      service_type, on_upload_complete);
 }
 
 base::TimeDelta CastMetricsServiceClient::GetStandardUploadInterval() {
@@ -280,44 +268,23 @@
     : delegate_(delegate),
       pref_service_(pref_service),
       client_info_loaded_(false),
-#if defined(OS_LINUX)
-      external_metrics_(nullptr),
-      platform_metrics_(nullptr),
-#endif  // defined(OS_LINUX)
       task_runner_(base::ThreadTaskRunnerHandle::Get()),
-      url_loader_factory_(url_loader_factory) {
-}
+      url_loader_factory_(url_loader_factory) {}
 
-CastMetricsServiceClient::~CastMetricsServiceClient() {
-#if defined(OS_LINUX)
-  DCHECK(!external_metrics_);
-  DCHECK(!platform_metrics_);
-#endif  // defined(OS_LINUX)
-}
+CastMetricsServiceClient::~CastMetricsServiceClient() = default;
 
 void CastMetricsServiceClient::OnApplicationNotIdle() {
   metrics_service_->OnApplicationNotIdle();
 }
 
-void CastMetricsServiceClient::ProcessExternalEvents(const base::Closure& cb) {
-#if defined(OS_LINUX)
-  external_metrics_->ProcessExternalEvents(
-      base::Bind(&ExternalMetrics::ProcessExternalEvents,
-                 base::Unretained(platform_metrics_), cb));
-#else
-  cb.Run();
-#endif  // defined(OS_LINUX)
-}
-
-void CastMetricsServiceClient::SetForceClientId(
-    const std::string& client_id) {
+void CastMetricsServiceClient::SetForceClientId(const std::string& client_id) {
   DCHECK(force_client_id_.empty());
   DCHECK(!client_info_loaded_)
       << "Force client ID must be set before client info is loaded.";
   force_client_id_ = client_id;
 }
 
-void CastMetricsServiceClient::Initialize() {
+void CastMetricsServiceClient::InitializeMetricsService() {
   DCHECK(!metrics_state_manager_);
   metrics_state_manager_ = ::metrics::MetricsStateManager::Create(
       pref_service_, this, base::string16(),
@@ -338,27 +305,9 @@
   metrics_state_manager_->ForceClientIdCreation();
   // Populate |client_id| to other component parts.
   SetMetricsClientId(metrics_state_manager_->client_id());
+}
 
-  CastStabilityMetricsProvider* stability_provider =
-      new CastStabilityMetricsProvider(metrics_service_.get(), pref_service_);
-  metrics_service_->RegisterMetricsProvider(
-      std::unique_ptr<::metrics::MetricsProvider>(stability_provider));
-
-  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
-  if (!command_line->HasSwitch(switches::kDisableGpu)) {
-    metrics_service_->RegisterMetricsProvider(
-        std::unique_ptr<::metrics::MetricsProvider>(
-            new ::metrics::GPUMetricsProvider));
-
-    // TODO(gfhuang): Does ChromeCast actually need metrics about screen info?
-    // crbug.com/541577
-    metrics_service_->RegisterMetricsProvider(
-        std::unique_ptr<::metrics::MetricsProvider>(
-            new ::metrics::ScreenInfoMetricsProvider));
-  }
-  metrics_service_->RegisterMetricsProvider(
-      std::make_unique<::metrics::NetworkMetricsProvider>(
-          content::CreateNetworkConnectionTrackerAsyncGetter()));
+void CastMetricsServiceClient::StartMetricsService() {
   if (delegate_)
     delegate_->RegisterMetricsProviders(metrics_service_.get());
 
@@ -370,36 +319,16 @@
 
   if (IsReportingEnabled())
     metrics_service_->Start();
-
-#if defined(OS_LINUX)
-  // Start external metrics collection, which feeds data from external
-  // processes into the main external metrics.
-  external_metrics_ = new ExternalMetrics(
-      stability_provider,
-      GetHomePathASCII(kExternalUmaEventsRelativePath).value());
-  external_metrics_->Start();
-  platform_metrics_ =
-      new ExternalMetrics(stability_provider, kPlatformUmaEventsPath);
-  platform_metrics_->Start();
-#endif  // defined(OS_LINUX)
 }
 
 void CastMetricsServiceClient::Finalize() {
-#if !defined(OS_ANDROID)
-  // Set clean_shutdown bit.
-  metrics_service_->RecordCompletedSessionEnd();
-#endif  // !defined(OS_ANDROID)
-
-#if defined(OS_LINUX)
-  // Stop metrics service cleanly before destructing CastMetricsServiceClient.
-  // The pointer will be deleted in StopAndDestroy().
-  external_metrics_->StopAndDestroy();
-  external_metrics_ = nullptr;
-  platform_metrics_->StopAndDestroy();
-  platform_metrics_ = nullptr;
-#endif  // defined(OS_LINUX)
   metrics_service_->Stop();
 }
 
+void CastMetricsServiceClient::ProcessExternalEvents(const base::Closure& cb) {
+  if (external_events_cb_)
+    external_events_cb_.Run(cb);
+}
+
 }  // namespace metrics
 }  // namespace chromecast
diff --git a/chromecast/browser/metrics/cast_metrics_service_client.h b/chromecast/metrics/cast_metrics_service_client.h
similarity index 85%
rename from chromecast/browser/metrics/cast_metrics_service_client.h
rename to chromecast/metrics/cast_metrics_service_client.h
index 0f056d3..2162346 100644
--- a/chromecast/browser/metrics/cast_metrics_service_client.h
+++ b/chromecast/metrics/cast_metrics_service_client.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 CHROMECAST_BROWSER_METRICS_CAST_METRICS_SERVICE_CLIENT_H_
-#define CHROMECAST_BROWSER_METRICS_CAST_METRICS_SERVICE_CLIENT_H_
+#ifndef CHROMECAST_METRICS_CAST_METRICS_SERVICE_CLIENT_H_
+#define CHROMECAST_METRICS_CAST_METRICS_SERVICE_CLIENT_H_
 
 #include <stdint.h>
 
@@ -48,8 +48,6 @@
   virtual ~CastMetricsServiceDelegate() = default;
 };
 
-class ExternalMetrics;
-
 class CastMetricsServiceClient : public ::metrics::MetricsServiceClient,
                                  public ::metrics::EnabledStateProvider {
  public:
@@ -71,7 +69,8 @@
   // thread.
   void ProcessExternalEvents(const base::Closure& cb);
 
-  void Initialize();
+  void InitializeMetricsService();
+  void StartMetricsService();
   void Finalize();
 
   // ::metrics::MetricsServiceClient:
@@ -101,6 +100,12 @@
 
   std::string client_id() const { return client_id_; }
 
+  PrefService* pref_service() const { return pref_service_; }
+  void SetCallbacks(
+      base::RepeatingCallback<void(const base::Closure&)>
+          collect_final_metrics_cb,
+      base::RepeatingCallback<void(const base::Closure&)> external_events_cb);
+
  private:
   std::unique_ptr<::metrics::ClientInfo> LoadClientInfo();
   void StoreClientInfo(const ::metrics::ClientInfo& client_info);
@@ -111,15 +116,13 @@
   std::string force_client_id_;
   bool client_info_loaded_;
 
-#if defined(OS_LINUX)
-  ExternalMetrics* external_metrics_;
-  ExternalMetrics* platform_metrics_;
-#endif  // defined(OS_LINUX)
   const scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
   std::unique_ptr<::metrics::MetricsStateManager> metrics_state_manager_;
   std::unique_ptr<::metrics::MetricsService> metrics_service_;
   std::unique_ptr<::metrics::EnabledStateProvider> enabled_state_provider_;
   scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
+  base::RepeatingCallback<void(const base::Closure&)> collect_final_metrics_cb_;
+  base::RepeatingCallback<void(const base::Closure&)> external_events_cb_;
 
   DISALLOW_COPY_AND_ASSIGN(CastMetricsServiceClient);
 };
@@ -127,4 +130,4 @@
 }  // namespace metrics
 }  // namespace chromecast
 
-#endif  // CHROMECAST_BROWSER_METRICS_CAST_METRICS_SERVICE_CLIENT_H_
+#endif  // CHROMECAST_METRICS_CAST_METRICS_SERVICE_CLIENT_H_
diff --git a/components/gwp_asan/client/guarded_page_allocator.cc b/components/gwp_asan/client/guarded_page_allocator.cc
index 9bd94c7..9e7ae79 100644
--- a/components/gwp_asan/client/guarded_page_allocator.cc
+++ b/components/gwp_asan/client/guarded_page_allocator.cc
@@ -6,6 +6,7 @@
 
 #include <algorithm>
 #include <memory>
+#include <utility>
 
 #include "base/bits.h"
 #include "base/debug/stack_trace.h"
@@ -41,16 +42,49 @@
 #endif
 }
 
+template <typename T>
+T RandomEviction(std::vector<T>* list) {
+  DCHECK(!list->empty());
+  size_t rand = base::RandGenerator(list->size());
+  T out = (*list)[rand];
+  (*list)[rand] = list->back();
+  list->pop_back();
+  return out;
+}
+
 }  // namespace
 
 // TODO: Delete out-of-line constexpr defininitons once C++17 is in use.
+constexpr size_t GuardedPageAllocator::kOutOfMemoryCount;
 constexpr size_t GuardedPageAllocator::kGpaAllocAlignment;
 
+template <typename T>
+void GuardedPageAllocator::SimpleFreeList<T>::Initialize(T max_entries) {
+  max_entries_ = max_entries;
+  free_list_.reserve(max_entries);
+}
+
+template <typename T>
+T GuardedPageAllocator::SimpleFreeList<T>::Allocate() {
+  if (num_used_entries_ < max_entries_)
+    return num_used_entries_++;
+
+  DCHECK_LE(free_list_.size(), max_entries_);
+  return RandomEviction(&free_list_);
+}
+
+template <typename T>
+void GuardedPageAllocator::SimpleFreeList<T>::Free(T entry) {
+  DCHECK_LT(free_list_.size(), max_entries_);
+  free_list_.push_back(entry);
+}
+
 GuardedPageAllocator::GuardedPageAllocator() {}
 
 void GuardedPageAllocator::Init(size_t max_alloced_pages,
                                 size_t num_metadata,
-                                size_t total_pages) {
+                                size_t total_pages,
+                                OutOfMemoryCallback oom_callback) {
   CHECK_GT(max_alloced_pages, 0U);
   CHECK_LE(max_alloced_pages, num_metadata);
   CHECK_LE(num_metadata, AllocatorState::kMaxMetadata);
@@ -59,6 +93,7 @@
   max_alloced_pages_ = max_alloced_pages;
   state_.num_metadata = num_metadata;
   state_.total_pages = total_pages;
+  oom_callback_ = std::move(oom_callback);
 
   state_.page_size = base::GetPageSize();
 
@@ -74,8 +109,8 @@
     // Obtain this lock exclusively to satisfy the thread-safety annotations,
     // there should be no risk of a race here.
     base::AutoLock lock(lock_);
-    free_metadata_.reserve(num_metadata);
-    free_slots_.reserve(total_pages);
+    free_metadata_.Initialize(num_metadata);
+    free_slots_.Initialize(total_pages);
   }
 
   slot_to_metadata_idx_.resize(total_pages);
@@ -199,21 +234,20 @@
     AllocatorState::SlotIdx* slot,
     AllocatorState::MetadataIdx* metadata_idx) {
   base::AutoLock lock(lock_);
-  if (num_alloced_pages_ == max_alloced_pages_)
+  if (num_alloced_pages_ == max_alloced_pages_) {
+    if (++consecutive_failed_allocations_ == kOutOfMemoryCount) {
+      if (!oom_hit_) {
+        oom_hit_ = true;
+        base::AutoUnlock unlock(lock_);
+        std::move(oom_callback_).Run(total_allocations_ - kOutOfMemoryCount);
+      }
+    }
     return false;
-  num_alloced_pages_++;
+  }
 
-  if (num_used_metadata_ < state_.num_metadata) {
-    *metadata_idx = num_used_metadata_++;
-  } else {
-    DCHECK(!free_metadata_.empty());
-    DCHECK_LE(free_metadata_.size(), state_.num_metadata);
-    size_t rand = base::RandGenerator(free_metadata_.size());
-    *metadata_idx = free_metadata_[rand];
-    free_metadata_[rand] = free_metadata_.back();
-    free_metadata_.pop_back();
-    DCHECK_EQ(free_metadata_.size(), state_.num_metadata - num_alloced_pages_);
-
+  *slot = free_slots_.Allocate();
+  *metadata_idx = free_metadata_.Allocate();
+  if (metadata_[*metadata_idx].alloc_ptr) {
     // Overwrite the outdated slot_to_metadata_idx mapping from the previous use
     // of this metadata if it's still valid.
     DCHECK(state_.PointerIsMine(metadata_[*metadata_idx].alloc_ptr));
@@ -222,18 +256,9 @@
       slot_to_metadata_idx_[old_slot] = AllocatorState::kInvalidMetadataIdx;
   }
 
-  if (num_used_slots_ < state_.total_pages) {
-    *slot = num_used_slots_++;
-  } else {
-    DCHECK(!free_slots_.empty());
-    DCHECK_LE(free_slots_.size(), state_.total_pages);
-    size_t rand = base::RandGenerator(free_slots_.size());
-    *slot = free_slots_[rand];
-    free_slots_[rand] = free_slots_.back();
-    free_slots_.pop_back();
-    DCHECK_EQ(free_slots_.size(), state_.total_pages - num_alloced_pages_);
-  }
-
+  num_alloced_pages_++;
+  total_allocations_++;
+  consecutive_failed_allocations_ = 0;
   return true;
 }
 
@@ -244,17 +269,11 @@
   DCHECK_LT(metadata_idx, state_.num_metadata);
 
   base::AutoLock lock(lock_);
-  DCHECK_LT(free_slots_.size(), state_.total_pages);
-  free_slots_.push_back(slot);
-
-  DCHECK_LT(free_metadata_.size(), state_.num_metadata);
-  free_metadata_.push_back(metadata_idx);
+  free_slots_.Free(slot);
+  free_metadata_.Free(metadata_idx);
 
   DCHECK_GT(num_alloced_pages_, 0U);
   num_alloced_pages_--;
-
-  DCHECK_EQ(free_metadata_.size(), num_used_metadata_ - num_alloced_pages_);
-  DCHECK_EQ(free_slots_.size(), num_used_slots_ - num_alloced_pages_);
 }
 
 void GuardedPageAllocator::RecordAllocationMetadata(
diff --git a/components/gwp_asan/client/guarded_page_allocator.h b/components/gwp_asan/client/guarded_page_allocator.h
index 3a032907..502b1b7 100644
--- a/components/gwp_asan/client/guarded_page_allocator.h
+++ b/components/gwp_asan/client/guarded_page_allocator.h
@@ -10,6 +10,7 @@
 #include <string>
 #include <vector>
 
+#include "base/callback.h"
 #include "base/compiler_specific.h"
 #include "base/gtest_prod_util.h"
 #include "base/no_destructor.h"
@@ -29,9 +30,16 @@
 // initialization-time so there is no risk of malloc reentrancy.
 class GWP_ASAN_EXPORT GuardedPageAllocator {
  public:
+  // Number of consecutive allocations that fail due to lack of available pages
+  // before we call the OOM callback.
+  static constexpr size_t kOutOfMemoryCount = 100;
   // Default maximum alignment for all returned allocations.
   static constexpr size_t kGpaAllocAlignment = 16;
 
+  // Callback used to report the allocator running out of memory, reports the
+  // number of successful allocations before running out of memory.
+  using OutOfMemoryCallback = base::OnceCallback<void(size_t)>;
+
   // Does not allocate any memory for the allocator, to finish initializing call
   // Init().
   GuardedPageAllocator();
@@ -41,7 +49,13 @@
   // total_pages pages, where:
   //   1 <= max_alloced_pages <= num_metadata <= kMaxMetadata
   //   num_metadata <= total_pages <= kMaxSlots
-  void Init(size_t max_alloced_pages, size_t num_metadata, size_t total_pages);
+  //
+  // The OOM callback is called the first time the allocator fails to allocate
+  // kOutOfMemoryCount allocations consecutively due to lack of memory.
+  void Init(size_t max_alloced_pages,
+            size_t num_metadata,
+            size_t total_pages,
+            OutOfMemoryCallback oom_callback);
 
   // On success, returns a pointer to size bytes of page-guarded memory. On
   // failure, returns nullptr. The allocation is not guaranteed to be
@@ -73,6 +87,27 @@
   }
 
  private:
+  // Manages a free list of slot or metadata indices in the range
+  // [0, max_entries). Access to SimpleFreeList objects must be synchronized.
+  //
+  // SimpleFreeList is specifically designed to pre-allocate data in Initialize
+  // so that it never recurses into malloc/free during Allocate/Free.
+  template <typename T>
+  class SimpleFreeList {
+   public:
+    void Initialize(T max_entries);
+    T Allocate();
+    void Free(T entry);
+
+   private:
+    std::vector<T> free_list_;
+
+    // Number of used entries. This counter ensures all free entries are used
+    // before starting to use random eviction.
+    T num_used_entries_ = 0;
+    T max_entries_ = 0;
+  };
+
   // Unmaps memory allocated by this class, if Init was called.
   ~GuardedPageAllocator();
 
@@ -116,15 +151,8 @@
   // Lock that synchronizes allocating/freeing slots between threads.
   base::Lock lock_;
 
-  // Array used to store all free slot indices.
-  std::vector<AllocatorState::SlotIdx> free_slots_ GUARDED_BY(lock_);
-  // Array used to store all free metadata indices.
-  std::vector<AllocatorState::MetadataIdx> free_metadata_ GUARDED_BY(lock_);
-
-  // Number of used slots/metadata. These counters are to make sure we use all
-  // free metadata/slots before starting to use random eviction.
-  size_t num_used_slots_ GUARDED_BY(lock_) = 0;
-  size_t num_used_metadata_ GUARDED_BY(lock_) = 0;
+  SimpleFreeList<AllocatorState::SlotIdx> free_slots_ GUARDED_BY(lock_);
+  SimpleFreeList<AllocatorState::MetadataIdx> free_metadata_ GUARDED_BY(lock_);
 
   // Number of currently-allocated pages.
   size_t num_alloced_pages_ GUARDED_BY(lock_) = 0;
@@ -139,6 +167,13 @@
   // mapping exists.)
   std::vector<AllocatorState::MetadataIdx> slot_to_metadata_idx_;
 
+  // Maintain a count of total allocations and consecutive failed allocations
+  // to report allocator OOM.
+  size_t total_allocations_ GUARDED_BY(lock_) = 0;
+  size_t consecutive_failed_allocations_ GUARDED_BY(lock_) = 0;
+  bool oom_hit_ GUARDED_BY(lock_) = false;
+  OutOfMemoryCallback oom_callback_;
+
   // Required for a singleton to access the constructor.
   friend base::NoDestructor<GuardedPageAllocator>;
 
diff --git a/components/gwp_asan/client/guarded_page_allocator_unittest.cc b/components/gwp_asan/client/guarded_page_allocator_unittest.cc
index b7a6268..399b62c 100644
--- a/components/gwp_asan/client/guarded_page_allocator_unittest.cc
+++ b/components/gwp_asan/client/guarded_page_allocator_unittest.cc
@@ -11,6 +11,7 @@
 
 #include "base/bits.h"
 #include "base/process/process_metrics.h"
+#include "base/test/bind_test_util.h"
 #include "base/test/gtest_util.h"
 #include "base/threading/simple_thread.h"
 #include "build/build_config.h"
@@ -25,7 +26,9 @@
 class GuardedPageAllocatorTest : public testing::Test {
  protected:
   explicit GuardedPageAllocatorTest(size_t max_allocated_pages = kMaxMetadata) {
-    gpa_.Init(max_allocated_pages, kMaxMetadata, kMaxSlots);
+    gpa_.Init(max_allocated_pages, kMaxMetadata, kMaxSlots,
+              base::BindLambdaForTesting(
+                  [&](size_t allocations) { allocator_oom_ = true; }));
   }
 
   // Get a left- or right- aligned allocation (or nullptr on error.)
@@ -60,6 +63,7 @@
   }
 
   GuardedPageAllocator gpa_;
+  bool allocator_oom_ = false;
 };
 
 TEST_F(GuardedPageAllocatorTest, SingleAllocDealloc) {
@@ -140,6 +144,17 @@
   EXPECT_EQ(GetAlignedAllocation(false, 5, page_size * 2), nullptr);
 }
 
+TEST_F(GuardedPageAllocatorTest, OutOfMemoryCallback) {
+  for (size_t i = 0; i < kMaxMetadata; i++)
+    EXPECT_NE(gpa_.Allocate(1), nullptr);
+
+  for (size_t i = 0; i < GuardedPageAllocator::kOutOfMemoryCount - 1; i++)
+    EXPECT_EQ(gpa_.Allocate(1), nullptr);
+  EXPECT_FALSE(allocator_oom_);
+  EXPECT_EQ(gpa_.Allocate(1), nullptr);
+  EXPECT_TRUE(allocator_oom_);
+}
+
 class GuardedPageAllocatorParamTest
     : public GuardedPageAllocatorTest,
       public testing::WithParamInterface<size_t> {
diff --git a/components/gwp_asan/client/sampling_malloc_shims.cc b/components/gwp_asan/client/sampling_malloc_shims.cc
index 4e23af21..c5f381e 100644
--- a/components/gwp_asan/client/sampling_malloc_shims.cc
+++ b/components/gwp_asan/client/sampling_malloc_shims.cc
@@ -7,6 +7,7 @@
 #include <algorithm>
 
 #include "base/allocator/allocator_shim.h"
+#include "base/bind_helpers.h"
 #include "base/compiler_specific.h"
 #include "base/logging.h"
 #include "base/no_destructor.h"
@@ -254,7 +255,7 @@
                         size_t sampling_frequency) {
   static crash_reporter::CrashKeyString<24> malloc_crash_key(kMallocCrashKey);
   gpa = new GuardedPageAllocator();
-  gpa->Init(max_allocated_pages, num_metadata, total_pages);
+  gpa->Init(max_allocated_pages, num_metadata, total_pages, base::DoNothing());
   malloc_crash_key.Set(gpa->GetCrashKey());
   sampling_state.Init(sampling_frequency);
   base::allocator::InsertAllocatorDispatch(&g_allocator_dispatch);
diff --git a/components/gwp_asan/client/sampling_partitionalloc_shims.cc b/components/gwp_asan/client/sampling_partitionalloc_shims.cc
index 200cdb3..544f3a2 100644
--- a/components/gwp_asan/client/sampling_partitionalloc_shims.cc
+++ b/components/gwp_asan/client/sampling_partitionalloc_shims.cc
@@ -7,6 +7,7 @@
 #include <algorithm>
 
 #include "base/allocator/partition_allocator/partition_alloc.h"
+#include "base/bind_helpers.h"
 #include "base/logging.h"
 #include "base/partition_alloc_buildflags.h"
 #include "components/crash/core/common/crash_key.h"
@@ -85,7 +86,7 @@
   static crash_reporter::CrashKeyString<24> pa_crash_key(
       kPartitionAllocCrashKey);
   gpa = new GuardedPageAllocator();
-  gpa->Init(max_allocated_pages, num_metadata, total_pages);
+  gpa->Init(max_allocated_pages, num_metadata, total_pages, base::DoNothing());
   pa_crash_key.Set(gpa->GetCrashKey());
   sampling_state.Init(sampling_frequency);
   total_allocations = total_pages;
diff --git a/components/gwp_asan/crash_handler/crash_analyzer_unittest.cc b/components/gwp_asan/crash_handler/crash_analyzer_unittest.cc
index 54061ae..01d526d 100644
--- a/components/gwp_asan/crash_handler/crash_analyzer_unittest.cc
+++ b/components/gwp_asan/crash_handler/crash_analyzer_unittest.cc
@@ -9,6 +9,7 @@
 #include <utility>
 #include <vector>
 
+#include "base/bind_helpers.h"
 #include "base/debug/stack_trace.h"
 #include "base/test/gtest_util.h"
 #include "base/test/metrics/histogram_tester.h"
@@ -43,7 +44,7 @@
 class CrashAnalyzerTest : public testing::Test {
  protected:
   void SetUp() final {
-    gpa_.Init(1, 1, 1);
+    gpa_.Init(1, 1, 1, base::DoNothing());
     InitializeSnapshot();
   }
 
diff --git a/components/gwp_asan/crash_handler/crash_handler_unittest.cc b/components/gwp_asan/crash_handler/crash_handler_unittest.cc
index 80e86a2..e2d685a8 100644
--- a/components/gwp_asan/crash_handler/crash_handler_unittest.cc
+++ b/components/gwp_asan/crash_handler/crash_handler_unittest.cc
@@ -9,6 +9,7 @@
 #include <string>
 #include <vector>
 
+#include "base/bind_helpers.h"
 #include "base/command_line.h"
 #include "base/files/file_path.h"
 #include "base/files/scoped_temp_dir.h"
@@ -77,7 +78,7 @@
 MULTIPROCESS_TEST_MAIN(CrashingProcess) {
   base::NoDestructor<GuardedPageAllocator> gpa;
   gpa->Init(AllocatorState::kMaxMetadata, AllocatorState::kMaxMetadata,
-            kTotalPages);
+            kTotalPages, base::DoNothing());
 
   base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
   base::FilePath directory = cmd_line->GetSwitchValuePath("directory");
diff --git a/components/network_session_configurator/browser/network_session_configurator_unittest.cc b/components/network_session_configurator/browser/network_session_configurator_unittest.cc
index fae07a3..2ba9af7 100644
--- a/components/network_session_configurator/browser/network_session_configurator_unittest.cc
+++ b/components/network_session_configurator/browser/network_session_configurator_unittest.cc
@@ -127,8 +127,7 @@
   EXPECT_EQ(0, params_.quic_initial_rtt_for_handshake_milliseconds);
   EXPECT_FALSE(params_.quic_allow_server_migration);
   EXPECT_TRUE(params_.quic_host_whitelist.empty());
-  EXPECT_EQ(net::kDefaultRetransmittableOnWireTimeoutMillisecs,
-            params_.quic_retransmittable_on_wire_timeout_milliseconds);
+  EXPECT_EQ(0, params_.quic_retransmittable_on_wire_timeout_milliseconds);
 
   net::HttpNetworkSession::Params default_params;
   EXPECT_EQ(default_params.quic_supported_versions,
diff --git a/components/resources/OWNERS b/components/resources/OWNERS
index 456bcda..704da9f 100644
--- a/components/resources/OWNERS
+++ b/components/resources/OWNERS
@@ -18,6 +18,7 @@
 per-file neterror*=mmenke@chromium.org
 per-file neterror*=file://net/OWNERS
 per-file ntp_tiles_resources.grdp=file://components/ntp_tiles/OWNERS
+per-file onboarding_welcome_scaled_resources.grdp=file://ui/webui/PLATFORM_OWNERS
 per-file offline_pages_resources.grdp=file://components/offline_pages/OWNERS
 per-file proximity_auth*=tengs@chromium.org
 per-file printing_resources.grdp=file://printing/OWNERS
diff --git a/components/resources/components_scaled_resources.grd b/components/resources/components_scaled_resources.grd
index 2403267..379fb2f9 100644
--- a/components/resources/components_scaled_resources.grd
+++ b/components/resources/components_scaled_resources.grd
@@ -16,6 +16,7 @@
       <part file="crash_scaled_resources.grdp" />
       <part file="flags_ui_scaled_resources.grdp" />
       <part file="neterror_scaled_resources.grdp" />
+      <part file="onboarding_welcome_scaled_resources.grdp" />
       <part file="version_ui_scaled_resources.grdp" />
 
       <!-- Generic resources -->
diff --git a/components/resources/onboarding_welcome_scaled_resources.grdp b/components/resources/onboarding_welcome_scaled_resources.grdp
new file mode 100644
index 0000000..d083b94
--- /dev/null
+++ b/components/resources/onboarding_welcome_scaled_resources.grdp
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<grit-part>
+  <if expr="not is_android and not is_ios and _google_chrome">
+    <structure type="chrome_scaled_image" name="IDS_ONBOARDING_WELCOME_SEARCH" file="google_chrome/welcome/search.png" />
+  </if>
+</grit-part>
diff --git a/components/services/pdf_compositor/DEPS b/components/services/pdf_compositor/DEPS
index 0c41bb8..1f84ab9 100644
--- a/components/services/pdf_compositor/DEPS
+++ b/components/services/pdf_compositor/DEPS
@@ -9,7 +9,6 @@
   "+printing/common",
   "+services/service_manager/public/cpp",
   "+services/service_manager/public/mojom",
-  "+services/ws/public/mojom/constants.mojom.h",   # UI service name.
   "+skia",
   "+third_party/skia",
   "+third_party/blink/public/platform", # Test web sandbox support.
diff --git a/components/services/pdf_compositor/pdf_compositor_service.cc b/components/services/pdf_compositor/pdf_compositor_service.cc
index 517ed84..cc751c3 100644
--- a/components/services/pdf_compositor/pdf_compositor_service.cc
+++ b/components/services/pdf_compositor/pdf_compositor_service.cc
@@ -18,7 +18,6 @@
 #include "content/public/utility/utility_thread.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
 #include "services/service_manager/public/cpp/connector.h"
-#include "services/ws/public/mojom/constants.mojom.h"
 
 #if defined(OS_WIN)
 #include "content/public/child/dwrite_font_proxy_init_win.h"
diff --git a/components/viz/common/BUILD.gn b/components/viz/common/BUILD.gn
index 270277f..789d2a1 100644
--- a/components/viz/common/BUILD.gn
+++ b/components/viz/common/BUILD.gn
@@ -47,6 +47,8 @@
     defines = [ "VIZ_METAL_CONTEXT_PROVIDER_IMPLEMENTATION" ]
 
     sources = [
+      "gpu/metal_api_proxy.h",
+      "gpu/metal_api_proxy.mm",
       "gpu/metal_context_provider.h",
       "gpu/metal_context_provider.mm",
       "viz_metal_context_provider_export.h",
@@ -59,6 +61,7 @@
     deps = [
       "//base",
       "//ui/gfx",
+      "//ui/gl",
     ]
 
     libs = [
diff --git a/components/viz/common/gpu/DEPS b/components/viz/common/gpu/DEPS
index dcd56b3..cd1304f 100644
--- a/components/viz/common/gpu/DEPS
+++ b/components/viz/common/gpu/DEPS
@@ -10,3 +10,9 @@
   "+third_party/skia/include/gpu",
   "+third_party/vulkan/include",
 ]
+
+specific_include_rules = {
+  "metal_api_proxy.mm": [
+    "+ui/gl",
+  ]
+}
diff --git a/components/viz/common/gpu/metal_api_proxy.h b/components/viz/common/gpu/metal_api_proxy.h
new file mode 100644
index 0000000..8db4dac
--- /dev/null
+++ b/components/viz/common/gpu/metal_api_proxy.h
@@ -0,0 +1,33 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_VIZ_COMMON_GPU_METAL_API_PROXY_H_
+#define COMPONENTS_VIZ_COMMON_GPU_METAL_API_PROXY_H_
+
+#import <Metal/Metal.h>
+
+#include "base/mac/availability.h"
+#include "base/mac/scoped_nsobject.h"
+
+namespace gl {
+class ProgressReporter;
+}  // namespace gl
+
+// The MTLDeviceProxy wraps all calls to an MTLDevice. It reports progress
+// to the GPU watchdog to prevent the watchdog from killing the GPU process
+// when progress is being made.
+API_AVAILABLE(macos(10.11))
+@interface MTLDeviceProxy : NSObject <MTLDevice> {
+  base::scoped_nsprotocol<id<MTLDevice>> device_;
+
+  // Weak pointer to the progress reporter used to avoid watchdog timeouts.
+  // This must be re-set to nullptr when it is no longer known to be valid.
+  gl::ProgressReporter* progressReporter_;
+}
+
+- (id)initWithDevice:(id<MTLDevice>)device;
+- (void)setProgressReporter:(gl::ProgressReporter*)progressReporter;
+@end
+
+#endif  // COMPONENTS_VIZ_COMMON_GPU_METAL_API_PROXY_H_
diff --git a/components/viz/common/gpu/metal_api_proxy.mm b/components/viz/common/gpu/metal_api_proxy.mm
new file mode 100644
index 0000000..5cd5333
--- /dev/null
+++ b/components/viz/common/gpu/metal_api_proxy.mm
@@ -0,0 +1,256 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/viz/common/gpu/metal_api_proxy.h"
+
+#include "ui/gl/progress_reporter.h"
+
+@implementation MTLDeviceProxy
+- (id)initWithDevice:(id<MTLDevice>)device {
+  if (self = [super init])
+    device_.reset(device, base::scoped_policy::RETAIN);
+  return self;
+}
+
+- (void)setProgressReporter:(gl::ProgressReporter*)progressReporter {
+  progressReporter_ = progressReporter;
+}
+
+// Wrappers that add a gl::ScopedProgressReporter around calls to the true
+// MTLDevice. For a given method, the method name is fn, return type is R, the
+// argument types are A0,A1,A2,A3, and the argument names are a0,a1,a2,a3.
+#define PROXY_METHOD0(R, fn) \
+  -(R)fn {                   \
+    return [device_ fn];     \
+  }
+#define PROXY_METHOD1(R, fn, A0) \
+  -(R)fn : (A0)a0 {              \
+    return [device_ fn:a0];      \
+  }
+#define PROXY_METHOD2(R, fn, A0, a1, A1) \
+  -(R)fn : (A0)a0 a1 : (A1)a1 {          \
+    return [device_ fn:a0 a1:a1];        \
+  }
+#define PROXY_METHOD0_SLOW(R, fn)                                  \
+  -(R)fn {                                                         \
+    gl::ScopedProgressReporter scoped_reporter(progressReporter_); \
+    return [device_ fn];                                           \
+  }
+#define PROXY_METHOD1_SLOW(R, fn, A0)                              \
+  -(R)fn : (A0)a0 {                                                \
+    gl::ScopedProgressReporter scoped_reporter(progressReporter_); \
+    return [device_ fn:a0];                                        \
+  }
+#define PROXY_METHOD2_SLOW(R, fn, A0, a1, A1)                      \
+  -(R)fn : (A0)a0 a1 : (A1)a1 {                                    \
+    gl::ScopedProgressReporter scoped_reporter(progressReporter_); \
+    return [device_ fn:a0 a1:a1];                                  \
+  }
+#define PROXY_METHOD3_SLOW(R, fn, A0, a1, A1, a2, A2)              \
+  -(R)fn : (A0)a0 a1 : (A1)a1 a2 : (A2)a2 {                        \
+    gl::ScopedProgressReporter scoped_reporter(progressReporter_); \
+    return [device_ fn:a0 a1:a1 a2:a2];                            \
+  }
+#define PROXY_METHOD4_SLOW(R, fn, A0, a1, A1, a2, A2, a3, A3)      \
+  -(R)fn : (A0)a0 a1 : (A1)a1 a2 : (A2)a2 a3 : (A3)a3 {            \
+    gl::ScopedProgressReporter scoped_reporter(progressReporter_); \
+    return [device_ fn:a0 a1:a1 a2:a2 a3:a3];                      \
+  }
+
+// Disable availability warnings for the calls to |device_| in the macros. (The
+// relevant availability guards are already present in the MTLDevice protocol
+// for |self|).
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunguarded-availability"
+
+// Wrapped implementation of the MTLDevice protocol in which some methods
+// have a gl::ScopedProgressReporter. The methods implemented using macros
+// with the _SLOW suffix are the ones that create a gl::ScopedProgressReporter.
+// The rule of thumb is that methods that could potentially do a GPU allocation
+// or a shader compilation are marked as SLOW.
+PROXY_METHOD0(NSString*, name)
+PROXY_METHOD0(uint64_t, registryID)
+PROXY_METHOD0(MTLSize, maxThreadsPerThreadgroup)
+PROXY_METHOD0(BOOL, isLowPower)
+PROXY_METHOD0(BOOL, isHeadless)
+PROXY_METHOD0(BOOL, isRemovable)
+PROXY_METHOD0(uint64_t, recommendedMaxWorkingSetSize)
+PROXY_METHOD0(BOOL, isDepth24Stencil8PixelFormatSupported)
+PROXY_METHOD0(MTLReadWriteTextureTier, readWriteTextureSupport)
+PROXY_METHOD0(MTLArgumentBuffersTier, argumentBuffersSupport)
+PROXY_METHOD0(BOOL, areRasterOrderGroupsSupported)
+PROXY_METHOD0(NSUInteger, currentAllocatedSize)
+PROXY_METHOD0(NSUInteger, maxThreadgroupMemoryLength)
+PROXY_METHOD0(BOOL, areProgrammableSamplePositionsSupported)
+PROXY_METHOD0_SLOW(nullable id<MTLCommandQueue>, newCommandQueue)
+PROXY_METHOD1_SLOW(nullable id<MTLCommandQueue>,
+                   newCommandQueueWithMaxCommandBufferCount,
+                   NSUInteger)
+PROXY_METHOD1(MTLSizeAndAlign,
+              heapTextureSizeAndAlignWithDescriptor,
+              MTLTextureDescriptor*)
+PROXY_METHOD2(MTLSizeAndAlign,
+              heapBufferSizeAndAlignWithLength,
+              NSUInteger,
+              options,
+              MTLResourceOptions)
+PROXY_METHOD1_SLOW(nullable id<MTLHeap>,
+                   newHeapWithDescriptor,
+                   MTLHeapDescriptor*)
+PROXY_METHOD2_SLOW(nullable id<MTLBuffer>,
+                   newBufferWithLength,
+                   NSUInteger,
+                   options,
+                   MTLResourceOptions)
+PROXY_METHOD3_SLOW(nullable id<MTLBuffer>,
+                   newBufferWithBytes,
+                   const void*,
+                   length,
+                   NSUInteger,
+                   options,
+                   MTLResourceOptions)
+PROXY_METHOD4_SLOW(nullable id<MTLBuffer>,
+                   newBufferWithBytesNoCopy,
+                   void*,
+                   length,
+                   NSUInteger,
+                   options,
+                   MTLResourceOptions,
+                   deallocator,
+                   void (^__nullable)(void* pointer, NSUInteger length))
+PROXY_METHOD1(nullable id<MTLDepthStencilState>,
+              newDepthStencilStateWithDescriptor,
+              MTLDepthStencilDescriptor*)
+PROXY_METHOD1_SLOW(nullable id<MTLTexture>,
+                   newTextureWithDescriptor,
+                   MTLTextureDescriptor*)
+PROXY_METHOD3_SLOW(nullable id<MTLTexture>,
+                   newTextureWithDescriptor,
+                   MTLTextureDescriptor*,
+                   iosurface,
+                   IOSurfaceRef,
+                   plane,
+                   NSUInteger)
+PROXY_METHOD1_SLOW(nullable id<MTLSamplerState>,
+                   newSamplerStateWithDescriptor,
+                   MTLSamplerDescriptor*)
+PROXY_METHOD0_SLOW(nullable id<MTLLibrary>, newDefaultLibrary)
+PROXY_METHOD2_SLOW(nullable id<MTLLibrary>,
+                   newDefaultLibraryWithBundle,
+                   NSBundle*,
+                   error,
+                   __autoreleasing NSError**)
+PROXY_METHOD2_SLOW(nullable id<MTLLibrary>,
+                   newLibraryWithFile,
+                   NSString*,
+                   error,
+                   __autoreleasing NSError**)
+PROXY_METHOD2_SLOW(nullable id<MTLLibrary>,
+                   newLibraryWithURL,
+                   NSURL*,
+                   error,
+                   __autoreleasing NSError**)
+PROXY_METHOD2_SLOW(nullable id<MTLLibrary>,
+                   newLibraryWithData,
+                   dispatch_data_t,
+                   error,
+                   __autoreleasing NSError**)
+PROXY_METHOD3_SLOW(nullable id<MTLLibrary>,
+                   newLibraryWithSource,
+                   NSString*,
+                   options,
+                   nullable MTLCompileOptions*,
+                   error,
+                   __autoreleasing NSError**)
+PROXY_METHOD3_SLOW(void,
+                   newLibraryWithSource,
+                   NSString*,
+                   options,
+                   nullable MTLCompileOptions*,
+                   completionHandler,
+                   MTLNewLibraryCompletionHandler)
+PROXY_METHOD2_SLOW(nullable id<MTLRenderPipelineState>,
+                   newRenderPipelineStateWithDescriptor,
+                   MTLRenderPipelineDescriptor*,
+                   error,
+                   __autoreleasing NSError**)
+PROXY_METHOD4_SLOW(nullable id<MTLRenderPipelineState>,
+                   newRenderPipelineStateWithDescriptor,
+                   MTLRenderPipelineDescriptor*,
+                   options,
+                   MTLPipelineOption,
+                   reflection,
+                   MTLAutoreleasedRenderPipelineReflection* __nullable,
+                   error,
+                   __autoreleasing NSError**)
+PROXY_METHOD2_SLOW(void,
+                   newRenderPipelineStateWithDescriptor,
+                   MTLRenderPipelineDescriptor*,
+                   completionHandler,
+                   MTLNewRenderPipelineStateCompletionHandler)
+PROXY_METHOD3_SLOW(void,
+                   newRenderPipelineStateWithDescriptor,
+                   MTLRenderPipelineDescriptor*,
+                   options,
+                   MTLPipelineOption,
+                   completionHandler,
+                   MTLNewRenderPipelineStateWithReflectionCompletionHandler)
+PROXY_METHOD2_SLOW(nullable id<MTLComputePipelineState>,
+                   newComputePipelineStateWithFunction,
+                   id<MTLFunction>,
+                   error,
+                   __autoreleasing NSError**)
+PROXY_METHOD4_SLOW(nullable id<MTLComputePipelineState>,
+                   newComputePipelineStateWithFunction,
+                   id<MTLFunction>,
+                   options,
+                   MTLPipelineOption,
+                   reflection,
+                   MTLAutoreleasedComputePipelineReflection* __nullable,
+                   error,
+                   __autoreleasing NSError**)
+PROXY_METHOD2_SLOW(void,
+                   newComputePipelineStateWithFunction,
+                   id<MTLFunction>,
+                   completionHandler,
+                   MTLNewComputePipelineStateCompletionHandler)
+PROXY_METHOD3_SLOW(void,
+                   newComputePipelineStateWithFunction,
+                   id<MTLFunction>,
+                   options,
+                   MTLPipelineOption,
+                   completionHandler,
+                   MTLNewComputePipelineStateWithReflectionCompletionHandler)
+PROXY_METHOD4_SLOW(nullable id<MTLComputePipelineState>,
+                   newComputePipelineStateWithDescriptor,
+                   MTLComputePipelineDescriptor*,
+                   options,
+                   MTLPipelineOption,
+                   reflection,
+                   MTLAutoreleasedComputePipelineReflection* __nullable,
+                   error,
+                   __autoreleasing NSError**)
+PROXY_METHOD3_SLOW(void,
+                   newComputePipelineStateWithDescriptor,
+                   MTLComputePipelineDescriptor*,
+                   options,
+                   MTLPipelineOption,
+                   completionHandler,
+                   MTLNewComputePipelineStateWithReflectionCompletionHandler)
+PROXY_METHOD0_SLOW(nullable id<MTLFence>, newFence)
+PROXY_METHOD1(BOOL, supportsFeatureSet, MTLFeatureSet)
+PROXY_METHOD1(BOOL, supportsTextureSampleCount, NSUInteger)
+PROXY_METHOD1(NSUInteger,
+              minimumLinearTextureAlignmentForPixelFormat,
+              MTLPixelFormat)
+PROXY_METHOD2(void,
+              getDefaultSamplePositions,
+              MTLSamplePosition*,
+              count,
+              NSUInteger)
+PROXY_METHOD1_SLOW(nullable id<MTLArgumentEncoder>,
+                   newArgumentEncoderWithArguments,
+                   NSArray<MTLArgumentDescriptor*>*)
+#pragma clang diagnostic pop
+@end
diff --git a/components/viz/common/gpu/metal_context_provider.h b/components/viz/common/gpu/metal_context_provider.h
index 71ad85f..f69c33a 100644
--- a/components/viz/common/gpu/metal_context_provider.h
+++ b/components/viz/common/gpu/metal_context_provider.h
@@ -18,6 +18,10 @@
 using MTLDevicePtr = MTLDeviceProtocol*;
 #endif
 
+namespace gl {
+class ProgressReporter;
+}  // namespace gl
+
 namespace viz {
 
 // The MetalContextProvider provides a Metal-backed GrContext.
@@ -30,6 +34,11 @@
 
   virtual GrContext* GetGrContext() = 0;
   virtual MTLDevicePtr GetMTLDevice() = 0;
+
+  // Set the progress reported used to prevent watchdog timeouts during longer
+  // sequences of Metal API calls. It is guaranteed that no further calls to
+  // |progress_reporter| will be made after |this| is destroyed.
+  virtual void SetProgressReporter(gl::ProgressReporter* progress_reporter) = 0;
 };
 
 }  // namespace viz
diff --git a/components/viz/common/gpu/metal_context_provider.mm b/components/viz/common/gpu/metal_context_provider.mm
index 07af97e..6d7d026 100644
--- a/components/viz/common/gpu/metal_context_provider.mm
+++ b/components/viz/common/gpu/metal_context_provider.mm
@@ -5,6 +5,7 @@
 #include "components/viz/common/gpu/metal_context_provider.h"
 
 #include "base/mac/scoped_nsobject.h"
+#include "components/viz/common/gpu/metal_api_proxy.h"
 #include "third_party/skia/include/gpu/GrContext.h"
 
 #import <Metal/Metal.h>
@@ -16,9 +17,8 @@
 struct API_AVAILABLE(macos(10.11)) MetalContextProviderImpl
     : public MetalContextProvider {
  public:
-  explicit MetalContextProviderImpl(
-      base::scoped_nsprotocol<id<MTLDevice>> device)
-      : device_(device) {
+  explicit MetalContextProviderImpl(id<MTLDevice> device) {
+    device_.reset([[MTLDeviceProxy alloc] initWithDevice:device]);
     command_queue_.reset([device_ newCommandQueue]);
     // GrContext::MakeMetal will take ownership of the objects passed in. Retain
     // the objects before passing them to MakeMetal so that the objects in
@@ -27,12 +27,19 @@
         GrContext::MakeMetal([device_ retain], [command_queue_ retain]);
     DCHECK(gr_context_);
   }
-  ~MetalContextProviderImpl() override {}
+  ~MetalContextProviderImpl() override {
+    // Because there are no guarantees that |device_| will not outlive |this|,
+    // un-set the progress reporter on |device_|.
+    [device_ setProgressReporter:nullptr];
+  }
+  void SetProgressReporter(gl::ProgressReporter* progress_reporter) override {
+    [device_ setProgressReporter:progress_reporter];
+  }
   GrContext* GetGrContext() override { return gr_context_.get(); }
   MTLDevicePtr GetMTLDevice() override { return device_.get(); }
 
  private:
-  base::scoped_nsprotocol<id<MTLDevice>> device_;
+  base::scoped_nsobject<MTLDeviceProxy> device_;
   base::scoped_nsprotocol<id<MTLCommandQueue>> command_queue_;
   sk_sp<GrContext> gr_context_;
 
@@ -57,7 +64,7 @@
     if (!device_to_use)
       device_to_use.reset(MTLCreateSystemDefaultDevice());
     if (device_to_use)
-      return std::make_unique<MetalContextProviderImpl>(device_to_use);
+      return std::make_unique<MetalContextProviderImpl>(device_to_use.get());
     DLOG(ERROR) << "Failed to create MTLDevice.";
   }
   // If no device was found, or if the macOS version is too old for Metal,
diff --git a/content/browser/devtools/devtools_background_services_context_impl.cc b/content/browser/devtools/devtools_background_services_context_impl.cc
index 1c1331c..21ba92e 100644
--- a/content/browser/devtools/devtools_background_services_context_impl.cc
+++ b/content/browser/devtools/devtools_background_services_context_impl.cc
@@ -60,7 +60,8 @@
     BrowserContext* browser_context,
     scoped_refptr<ServiceWorkerContextWrapper> service_worker_context)
     : browser_context_(browser_context),
-      service_worker_context_(std::move(service_worker_context)) {
+      service_worker_context_(std::move(service_worker_context)),
+      weak_ptr_factory_(this) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   auto expiration_times =
@@ -147,7 +148,7 @@
       FROM_HERE, {BrowserThread::IO},
       base::BindOnce(&DevToolsBackgroundServicesContextImpl::
                          GetLoggedBackgroundServiceEventsOnIO,
-                     weak_ptr_factory_io_.GetWeakPtr(), service,
+                     weak_ptr_factory_.GetWeakPtr(), service,
                      std::move(callback)));
 }
 
@@ -160,7 +161,7 @@
   service_worker_context_->GetUserDataForAllRegistrationsByKeyPrefix(
       CreateEntryKeyPrefix(service),
       base::BindOnce(&DevToolsBackgroundServicesContextImpl::DidGetUserData,
-                     weak_ptr_factory_io_.GetWeakPtr(), std::move(callback)));
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
 }
 
 void DevToolsBackgroundServicesContextImpl::DidGetUserData(
@@ -208,7 +209,7 @@
       FROM_HERE, {BrowserThread::IO},
       base::BindOnce(&DevToolsBackgroundServicesContextImpl::
                          ClearLoggedBackgroundServiceEventsOnIO,
-                     weak_ptr_factory_io_.GetWeakPtr(), service));
+                     weak_ptr_factory_.GetWeakPtr(), service));
 }
 
 void DevToolsBackgroundServicesContextImpl::
@@ -233,7 +234,7 @@
       FROM_HERE, {BrowserThread::IO},
       base::BindOnce(
           &DevToolsBackgroundServicesContextImpl::LogBackgroundServiceEventOnIO,
-          weak_ptr_factory_io_.GetWeakPtr(), service_worker_registration_id,
+          weak_ptr_factory_.GetWeakPtr(), service_worker_registration_id,
           origin, service, event_name, instance_id, event_metadata));
 }
 
@@ -256,7 +257,7 @@
         FROM_HERE, {BrowserThread::UI},
         base::BindOnce(
             &DevToolsBackgroundServicesContextImpl::OnRecordingTimeExpired,
-            weak_ptr_factory_ui_.GetWeakPtr(), ServiceToProtoEnum(service)));
+            weak_ptr_factory_.GetWeakPtr(), ServiceToProtoEnum(service)));
     return;
   }
 
@@ -280,7 +281,7 @@
       FROM_HERE, {BrowserThread::UI},
       base::BindOnce(
           &DevToolsBackgroundServicesContextImpl::NotifyEventObservers,
-          weak_ptr_factory_ui_.GetWeakPtr(), std::move(event)));
+          weak_ptr_factory_.GetWeakPtr(), std::move(event)));
 }
 
 void DevToolsBackgroundServicesContextImpl::NotifyEventObservers(
diff --git a/content/browser/devtools/devtools_background_services_context_impl.h b/content/browser/devtools/devtools_background_services_context_impl.h
index c378fb5..2a5b560 100644
--- a/content/browser/devtools/devtools_background_services_context_impl.h
+++ b/content/browser/devtools/devtools_background_services_context_impl.h
@@ -136,10 +136,7 @@
 
   base::ObserverList<EventObserver> observers_;
 
-  base::WeakPtrFactory<DevToolsBackgroundServicesContextImpl>
-      weak_ptr_factory_ui_{this};
-  base::WeakPtrFactory<DevToolsBackgroundServicesContextImpl>
-      weak_ptr_factory_io_{this};
+  base::WeakPtrFactory<DevToolsBackgroundServicesContextImpl> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(DevToolsBackgroundServicesContextImpl);
 };
diff --git a/content/browser/devtools/protocol/background_service_handler.cc b/content/browser/devtools/protocol/background_service_handler.cc
index d5a174a..17b91e8 100644
--- a/content/browser/devtools/protocol/background_service_handler.cc
+++ b/content/browser/devtools/protocol/background_service_handler.cc
@@ -22,12 +22,6 @@
   } else if (service_name ==
              BackgroundService::ServiceNameEnum::BackgroundSync) {
     return devtools::proto::BackgroundService::BACKGROUND_SYNC;
-  } else if (service_name ==
-             BackgroundService::ServiceNameEnum::PushMessaging) {
-    return devtools::proto::BackgroundService::PUSH_MESSAGING;
-  } else if (service_name ==
-             BackgroundService::ServiceNameEnum::Notifications) {
-    return devtools::proto::BackgroundService::NOTIFICATIONS;
   }
   return devtools::proto::BackgroundService::UNKNOWN;
 }
@@ -38,10 +32,6 @@
       return BackgroundService::ServiceNameEnum::BackgroundFetch;
     case devtools::proto::BackgroundService::BACKGROUND_SYNC:
       return BackgroundService::ServiceNameEnum::BackgroundSync;
-    case devtools::proto::BackgroundService::PUSH_MESSAGING:
-      return BackgroundService::ServiceNameEnum::PushMessaging;
-    case devtools::proto::BackgroundService::NOTIFICATIONS:
-      return BackgroundService::ServiceNameEnum::Notifications;
     default:
       NOTREACHED();
   }
diff --git a/content/browser/devtools/protocol/system_info_handler.cc b/content/browser/devtools/protocol/system_info_handler.cc
index 83b9de3c..66730f3 100644
--- a/content/browser/devtools/protocol/system_info_handler.cc
+++ b/content/browser/devtools/protocol/system_info_handler.cc
@@ -117,6 +117,8 @@
                             .SetDeviceId(device.device_id)
                             .SetVendorString(device.vendor_string)
                             .SetDeviceString(device.device_string)
+                            .SetDriverVendor(device.driver_vendor)
+                            .SetDriverVersion(device.driver_version)
                             .Build();
 }
 
diff --git a/content/browser/fileapi/browser_file_system_helper.cc b/content/browser/fileapi/browser_file_system_helper.cc
index e278fff..7caf1a9 100644
--- a/content/browser/fileapi/browser_file_system_helper.cc
+++ b/content/browser/fileapi/browser_file_system_helper.cc
@@ -251,22 +251,24 @@
         file_system_context->CrackURL(file_system_file.url);
 
     std::string register_name;
-    std::string filesystem_id = isolated_context->RegisterFileSystemForPath(
-        file_system_url.type(), file_system_url.filesystem_id(),
-        file_system_url.path(), &register_name);
+    storage::IsolatedContext::ScopedFSHandle filesystem =
+        isolated_context->RegisterFileSystemForPath(
+            file_system_url.type(), file_system_url.filesystem_id(),
+            file_system_url.path(), &register_name);
 
-    if (!filesystem_id.empty()) {
-      // Grant the permission iff the ID is valid.
-      security_policy->GrantReadFileSystem(child_id, filesystem_id);
+    if (filesystem.is_valid()) {
+      // Grant the permission iff the ID is valid. This will also keep the FS
+      // alive after |filesystem| goes out of scope.
+      security_policy->GrantReadFileSystem(child_id, filesystem.id());
     }
 
     // Note: We are using the origin URL provided by the sender here. It may be
     // different from the receiver's.
     file_system_file.url = GURL(
         storage::GetIsolatedFileSystemRootURIString(
-            file_system_url.origin().GetURL(), filesystem_id, std::string())
+            file_system_url.origin().GetURL(), filesystem.id(), std::string())
             .append(register_name));
-    file_system_file.filesystem_id = filesystem_id;
+    file_system_file.filesystem_id = filesystem.id();
   }
 }
 
diff --git a/content/browser/media/session/audio_focus_delegate_android.cc b/content/browser/media/session/audio_focus_delegate_android.cc
index bb4e9cc..f835e2b 100644
--- a/content/browser/media/session/audio_focus_delegate_android.cc
+++ b/content/browser/media/session/audio_focus_delegate_android.cc
@@ -7,6 +7,7 @@
 #include "base/android/jni_android.h"
 #include "content/browser/media/session/media_session_impl.h"
 #include "jni/AudioFocusDelegate_jni.h"
+#include "media/base/media_switches.h"
 #include "services/media_session/public/mojom/audio_focus.mojom.h"
 
 using base::android::JavaParamRef;
@@ -61,7 +62,8 @@
 
 void AudioFocusDelegateAndroid::OnSuspend(JNIEnv*,
                                           const JavaParamRef<jobject>&) {
-  if (!media_session_->IsActive())
+  if (!media_session_->IsActive() ||
+      !base::FeatureList::IsEnabled(media::kAudioFocusLossSuspendMediaSession))
     return;
 
   media_session_->Suspend(MediaSession::SuspendType::kSystem);
diff --git a/content/browser/media/session/audio_focus_delegate_android.h b/content/browser/media/session/audio_focus_delegate_android.h
index f4a3b13..e8f4436 100644
--- a/content/browser/media/session/audio_focus_delegate_android.h
+++ b/content/browser/media/session/audio_focus_delegate_android.h
@@ -62,7 +62,6 @@
   // Weak pointer because |this| is owned by |media_session_|.
   MediaSessionImpl* media_session_;
   base::android::ScopedJavaGlobalRef<jobject> j_media_session_delegate_;
-
   DISALLOW_COPY_AND_ASSIGN(AudioFocusDelegateAndroid);
 };
 
diff --git a/content/browser/native_file_system/file_system_chooser.cc b/content/browser/native_file_system/file_system_chooser.cc
index f2413e5..31ad1c9 100644
--- a/content/browser/native_file_system/file_system_chooser.cc
+++ b/content/browser/native_file_system/file_system_chooser.cc
@@ -157,13 +157,19 @@
   result.reserve(files.size());
   for (const auto& path : files) {
     std::string base_name;
-    std::string file_system_id = isolated_context->RegisterFileSystemForPath(
-        storage::kFileSystemTypeNativeLocal, std::string(), path, &base_name);
+    storage::IsolatedContext::ScopedFSHandle file_system =
+        isolated_context->RegisterFileSystemForPath(
+            storage::kFileSystemTypeNativeLocal, std::string(), path,
+            &base_name);
+
+    // TODO(https://crbug.com/955185): Properly refcount file system in handle
+    // implementations, rather than just leaking them like this.
+    storage::IsolatedContext::GetInstance()->AddReference(file_system.id());
 
     base::FilePath root_path =
-        isolated_context->CreateVirtualRootPath(file_system_id);
+        isolated_context->CreateVirtualRootPath(file_system.id());
     base::FilePath isolated_path = root_path.AppendASCII(base_name);
-    result.push_back({file_system_id, isolated_path});
+    result.push_back({file_system.id(), isolated_path});
   }
 
   if (type_ == blink::mojom::ChooseFileSystemEntryType::kSaveFile) {
diff --git a/content/browser/service_worker/service_worker_installed_scripts_sender.cc b/content/browser/service_worker/service_worker_installed_scripts_sender.cc
index 7e35d7a..f5cab8fad 100644
--- a/content/browser/service_worker/service_worker_installed_scripts_sender.cc
+++ b/content/browser/service_worker/service_worker_installed_scripts_sender.cc
@@ -168,12 +168,20 @@
         kResponseReaderError:
       owner_->SetStartWorkerStatusCode(
           blink::ServiceWorkerStatusCode::kErrorDiskCache);
-      // Abort the worker by deleting from the registration since the data was
-      // corrupted.
+
+      // Break the Mojo connection with the renderer so the service worker knows
+      // to stop waiting for the script data to arrive and terminate. Note that
+      // DeleteVersion() below sends the Stop IPC, but without breaking the
+      // connection here, the service worker would be blocked waiting for the
+      // script data and won't respond to Stop.
+      manager_.reset();
+      binding_.Close();
+
+      // Delete the registration data since the data was corrupted.
       if (owner_->context()) {
         ServiceWorkerRegistration* registration =
             owner_->context()->GetLiveRegistration(owner_->registration_id());
-        // This ends up with destructing |this|.
+        // This can destruct |this|.
         registration->DeleteVersion(owner_);
       }
       return;
@@ -182,9 +190,9 @@
     case ServiceWorkerInstalledScriptReader::FinishedReason::kConnectionError:
     case ServiceWorkerInstalledScriptReader::FinishedReason::
         kMetaDataSenderError:
-      // Notify the renderer that a connection failure happened. Usually the
-      // failure means the renderer gets killed, and the error handler of
-      // EmbeddedWorkerInstance is invoked soon.
+      // Break the Mojo connection with the renderer. This usually causes the
+      // service worker to stop, and the error handler of EmbeddedWorkerInstance
+      // is invoked soon.
       manager_.reset();
       binding_.Close();
       return;
diff --git a/content/browser/webauth/authenticator_impl_unittest.cc b/content/browser/webauth/authenticator_impl_unittest.cc
index 4942a43..ce7224a 100644
--- a/content/browser/webauth/authenticator_impl_unittest.cc
+++ b/content/browser/webauth/authenticator_impl_unittest.cc
@@ -3744,6 +3744,33 @@
   EXPECT_TRUE(HasUV(callback_receiver));
 }
 
+TEST_F(ResidentKeyAuthenticatorImplTest, WithAppIDExtension) {
+  // Setting an AppID value for a resident-key request should be ignored.
+  device::VirtualCtap2Device::Config config;
+  config.u2f_support = true;
+  config.pin_support = true;
+  config.resident_key_support = true;
+  config.cred_protect_support = true;
+  virtual_device_.SetCtap2Config(config);
+  ASSERT_TRUE(virtual_device_.mutable_state()->InjectResidentKey(
+      /*credential_id=*/{{4, 3, 2, 1}}, kTestRelyingPartyId,
+      /*user_id=*/{{1, 2, 3, 4}}, "test@example.com", "Test User"));
+
+  TestServiceManagerContext smc;
+  AuthenticatorPtr authenticator = ConnectToAuthenticator();
+  TestGetAssertionCallback callback_receiver;
+  // |SelectAccount| should not be called when there's only a single response.
+  test_client_.expected_accounts = "<invalid>";
+
+  PublicKeyCredentialRequestOptionsPtr options = get_credential_options();
+  options->appid = kTestOrigin1;
+
+  authenticator->GetAssertion(std::move(options), callback_receiver.callback());
+  callback_receiver.WaitForCallback();
+  EXPECT_EQ(AuthenticatorStatus::SUCCESS, callback_receiver.status());
+  EXPECT_TRUE(HasUV(callback_receiver));
+}
+
 #if defined(OS_WIN)
 // Requests with a credProtect extension that have |enforce_protection_policy|
 // set should be rejected if the Windows WebAuthn API doesn't support
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
index 217b1c9..48498ec 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
@@ -49,12 +49,6 @@
 crbug.com/874620 [ linux nvidia ] conformance2/glsl3/const-struct-from-array-as-function-parameter.html [ Failure ]
 crbug.com/874620 [ opengl win nvidia ] conformance2/glsl3/const-struct-from-array-as-function-parameter.html [ Failure ]
 
-# TODO(shrekshao): Remove the following two Fail expectations
-# (draw-buffers and fs-color-type-mismatch-color-buffer-type)
-# after applying the new draw buffers validation
-crbug.com/927908 conformance2/rendering/draw-buffers.html [ Failure ]
-crbug.com/927908 conformance2/rendering/fs-color-type-mismatch-color-buffer-type.html [ Failure ]
-
 # Unsuppress after both
 # https://chromium-review.googlesource.com/1558678 and
 # https://github.com/KhronosGroup/WebGL/pull/2878 land and are rolled
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
index 257bf61..e2b9ad3 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
@@ -60,11 +60,6 @@
 # ========================
 # Fails on all platforms
 
-# TODO(shrekshao): Remove this after applying the new draw buffer
-# validation. And then uncomment the failure expectation for
-# angle bug 1523 (L160)
-crbug.com/927908 conformance/extensions/webgl-draw-buffers.html [ Failure ]
-
 # Need to implement new error semantics
 # https://github.com/KhronosGroup/WebGL/pull/2607
 crbug.com/849572 conformance/extensions/angle-instanced-arrays-out-of-bounds.html [ Failure ]
@@ -91,7 +86,7 @@
 crbug.com/735483 conformance/rendering/texture-switch-performance.html [ Skip ]
 crbug.com/951628 [ passthrough ] conformance/rendering/blending.html [ Failure ]
 
-# TODO(shrekhao): Restore failure expectation for
+# TODO(shrekshao): Restore failure expectation for
 # ['win', 'nvidia', 'passthrough', 'd3d11'], bug=737016
 # as Flaky after 953120 is fixed.
 # And restore ['linux', 'intel'], bug=928530 as Fail
@@ -105,7 +100,7 @@
 crbug.com/665521 [ intel opengl passthrough ] conformance/glsl/constructors/glsl-construct-mat2.html [ Failure ]
 
 # Passthrough command decoder / OpenGL / AMD
-# crbug.com/1523 [ amd opengl linux pass through ] conformance/extensions/webgl-draw-buffers.html [ Failure ]
+crbug.com/1523 [ amd opengl linux passthrough ] conformance/extensions/webgl-draw-buffers.html [ Failure ]
 
 crbug.com/665521 [ amd opengl passthrough ] conformance/glsl/constructors/glsl-construct-mat2.html [ Failure ]
 crbug.com/665521 [ amd opengl passthrough ] conformance/glsl/constructors/glsl-construct-vec-mat-corner-cases.html [ Failure ]
@@ -276,6 +271,7 @@
 crbug.com/angleproject/2914 [ win vulkan passthrough ] conformance/textures/misc/texture-copying-feedback-loops.html [ Failure ]
 crbug.com/angleproject/2913 [ win vulkan passthrough ] conformance/textures/misc/texture-hd-dpi.html [ Failure ]
 crbug.com/angleproject/2722 [ win vulkan passthrough ] conformance/textures/misc/texture-mips.html [ Failure ]
+crbug.com/angleproject/3469 [ win vulkan passthrough ] conformance/extensions/webgl-draw-buffers.html [ Failure ]
 
 # Note: the following test crashes so it's skipped.  http://anglebug.com/3352
 crbug.com/angleproject/2921 [ win vulkan passthrough ] conformance/uniforms/out-of-bounds-uniform-array-access.html [ Skip ]
diff --git a/device/fido/get_assertion_task.cc b/device/fido/get_assertion_task.cc
index e0824c5..97d7690 100644
--- a/device/fido/get_assertion_task.cc
+++ b/device/fido/get_assertion_task.cc
@@ -23,7 +23,8 @@
   bool ctap2_device_supports_u2f =
       device.device_info() &&
       base::ContainsKey(device.device_info()->versions, ProtocolVersion::kU2f);
-  return request.alternative_application_parameter && ctap2_device_supports_u2f;
+  return request.alternative_application_parameter &&
+         ctap2_device_supports_u2f && !request.allow_list.empty();
 }
 
 }  // namespace
diff --git a/docs/gwp_asan.md b/docs/gwp_asan.md
index 55d2153..90cdc7a 100644
--- a/docs/gwp_asan.md
+++ b/docs/gwp_asan.md
@@ -27,8 +27,6 @@
 allocation. That way though we may not be able to report the metadata for an old
 allocation, we will not report incorrect stack traces.
 
-Allocations are sampled to the GuardedPageAllocator using an [allocator shim.](/base/allocator/README.md)
-
 ## Crash handler
 
 The allocator is designed so that memory errors with GWP-ASan allocations
@@ -49,10 +47,10 @@
 
 ## Status
 
-GWP-ASan is currently only implemented for the system allocator (e.g. not
-PartitionAlloc/Oilpan/v8) on Windows and macOS. It is currently enabled by
-default. The allocator parameters can be manually modified by using the
-following invocation:
+GWP-ASan is implemented for malloc and PartitionAlloc, but not for Oilpan or v8,
+on Windows and macOS. It is currently enabled by default for malloc. The
+allocator parameters can be manually modified by using an invocation like the
+following:
 
 ```shell
 chrome --enable-features="GwpAsanMalloc<Study" \
@@ -60,14 +58,30 @@
        --force-fieldtrial-params=Study.Group1:MaxAllocations/128/MaxMetadata/255/TotalPages/4096/AllocationSamplingFrequency/1000/ProcessSamplingProbability/1.0
 ```
 
-GWP-ASan is tuned more aggressively in canary/dev (to increase the likelihood we
-catch newly introduced bugs) and for the browser process (because of its
-importance and sheer number of allocations.)
+GWP-ASan is tuned more aggressively in canary/dev, to increase the likelihood we
+catch newly introduced bugs, and for specific processes depending on the
+particular allocator.
 
 A [hotlist of bugs discovered by by GWP-ASan](https://bugs.chromium.org/p/chromium/issues/list?can=1&q=Hotlist%3DGWP-ASan)
 exists, though GWP-ASan crashes are filed without external visibility by
 default.
 
+## Limitations
+
+- GWP-ASan is configured with a small fixed-size amount of memory, so
+  long-lived allocations can quickly deplete the page pool and lead the
+  allocator to run out of memory. Depending on the sampling frequency and
+  distribution of allocation lifetimes this may lead to only allocations early
+  in the process lifetime being sampled.
+- Allocations over a page in size are not sampled.
+- The allocator skips zero-size allocations. Zero-size allocations on some
+  platforms return valid pointers and may be subject to lifetime and bounds
+  issues.
+- GWP-ASan does not intercept allocations for Oilpan or the v8 GC.
+- GWP-ASan does not hook PDFium's fork of PartitionAlloc.
+- Right-aligned allocations to catch overflows are not perfectly right-aligned,
+  so small out-of-bounds accesses may be missed.
+
 ## Testing
 
 There is [not yet](https://crbug.com/910751) a way to intentionally trigger a
diff --git a/extensions/browser/api/file_handlers/app_file_handler_util.cc b/extensions/browser/api/file_handlers/app_file_handler_util.cc
index ca3bc53..96fd9b3 100644
--- a/extensions/browser/api/file_handlers/app_file_handler_util.cc
+++ b/extensions/browser/api/file_handlers/app_file_handler_util.cc
@@ -285,9 +285,11 @@
       storage::IsolatedContext::GetInstance();
   DCHECK(isolated_context);
 
-  result.filesystem_id = isolated_context->RegisterFileSystemForPath(
-      storage::kFileSystemTypeNativeForPlatformApp, std::string(), path,
-      &result.registered_name);
+  storage::IsolatedContext::ScopedFSHandle filesystem =
+      isolated_context->RegisterFileSystemForPath(
+          storage::kFileSystemTypeNativeForPlatformApp, std::string(), path,
+          &result.registered_name);
+  result.filesystem_id = filesystem.id();
 
   content::ChildProcessSecurityPolicy* policy =
       content::ChildProcessSecurityPolicy::GetInstance();
diff --git a/extensions/browser/api/runtime/runtime_api.cc b/extensions/browser/api/runtime/runtime_api.cc
index 24732a0..c88b8dd5 100644
--- a/extensions/browser/api/runtime/runtime_api.cc
+++ b/extensions/browser/api/runtime/runtime_api.cc
@@ -736,14 +736,16 @@
 
   std::string relative_path = kPackageDirectoryPath;
   base::FilePath path = extension_->path();
-  std::string filesystem_id = isolated_context->RegisterFileSystemForPath(
-      storage::kFileSystemTypeNativeLocal, std::string(), path, &relative_path);
+  storage::IsolatedContext::ScopedFSHandle filesystem =
+      isolated_context->RegisterFileSystemForPath(
+          storage::kFileSystemTypeNativeLocal, std::string(), path,
+          &relative_path);
 
   content::ChildProcessSecurityPolicy* policy =
       content::ChildProcessSecurityPolicy::GetInstance();
-  policy->GrantReadFileSystem(source_process_id(), filesystem_id);
+  policy->GrantReadFileSystem(source_process_id(), filesystem.id());
   std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  dict->SetString("fileSystemId", filesystem_id);
+  dict->SetString("fileSystemId", filesystem.id());
   dict->SetString("baseName", relative_path);
   return RespondNow(OneArgument(std::move(dict)));
 }
diff --git a/extensions/browser/extension_function_histogram_value.h b/extensions/browser/extension_function_histogram_value.h
index ee96fba..0596bb4 100644
--- a/extensions/browser/extension_function_histogram_value.h
+++ b/extensions/browser/extension_function_histogram_value.h
@@ -1399,6 +1399,7 @@
   AUTOTESTPRIVATE_SETSHELFALIGNMENT = 1336,
   BLUETOOTH_RECORDPAIRING = 1337,
   FILEMANAGERPRIVATE_SETARCSTORAGETOASTSHOWNFLAG = 1338,
+  INPUTMETHODPRIVATE_SETCOMPOSITIONRANGE = 1339,
   // Last entry: Add new entries above, then run:
   // python tools/metrics/histograms/update_extension_histograms.py
   ENUM_BOUNDARY
diff --git a/gpu/command_buffer/client/gles2_implementation.cc b/gpu/command_buffer/client/gles2_implementation.cc
index d95fb380..211bfd8 100644
--- a/gpu/command_buffer/client/gles2_implementation.cc
+++ b/gpu/command_buffer/client/gles2_implementation.cc
@@ -6447,6 +6447,7 @@
   const Mailbox& mailbox = *reinterpret_cast<const Mailbox*>(mailbox_data);
   DCHECK(mailbox.Verify()) << "CreateAndTexStorage2DSharedImageCHROMIUM was "
                               "passed an invalid mailbox.";
+  DCHECK(mailbox.IsSharedImage());
   GLuint client_id;
   GetIdHandler(SharedIdNamespaces::kTextures)->MakeIds(this, 0, 1, &client_id);
   helper_->CreateAndTexStorage2DSharedImageINTERNALImmediate(client_id, GL_NONE,
@@ -6471,6 +6472,7 @@
   DCHECK(mailbox.Verify())
       << "CreateAndTexStorage2DSharedImageWithInternalFormatCHROMIUM was "
          "passed an invalid mailbox.";
+  DCHECK(mailbox.IsSharedImage());
   GLuint client_id;
   GetIdHandler(SharedIdNamespaces::kTextures)->MakeIds(this, 0, 1, &client_id);
   helper_->CreateAndTexStorage2DSharedImageINTERNALImmediate(
diff --git a/gpu/command_buffer/client/gles2_implementation_unittest.cc b/gpu/command_buffer/client/gles2_implementation_unittest.cc
index 77861ec..173c599 100644
--- a/gpu/command_buffer/client/gles2_implementation_unittest.cc
+++ b/gpu/command_buffer/client/gles2_implementation_unittest.cc
@@ -3586,7 +3586,7 @@
     GLbyte data[GL_MAILBOX_SIZE_CHROMIUM];
   };
 
-  Mailbox mailbox = Mailbox::Generate();
+  Mailbox mailbox = Mailbox::GenerateForSharedImage();
   Cmds expected;
   expected.cmd.Init(kTexturesStartId, GL_NONE, mailbox.name);
   GLuint id = gl_->CreateAndTexStorage2DSharedImageCHROMIUM(mailbox.name);
@@ -3601,7 +3601,7 @@
     GLbyte data[GL_MAILBOX_SIZE_CHROMIUM];
   };
 
-  Mailbox mailbox = Mailbox::Generate();
+  Mailbox mailbox = Mailbox::GenerateForSharedImage();
   const GLenum kFormat = GL_RGBA;
   Cmds expected;
   expected.cmd.Init(kTexturesStartId, kFormat, mailbox.name);
diff --git a/gpu/command_buffer/service/shared_context_state.cc b/gpu/command_buffer/service/shared_context_state.cc
index 2721214..a6befd9 100644
--- a/gpu/command_buffer/service/shared_context_state.cc
+++ b/gpu/command_buffer/service/shared_context_state.cc
@@ -90,6 +90,11 @@
     gl::ProgressReporter* progress_reporter) {
   progress_reporter_ = progress_reporter;
 
+#if defined(OS_MACOSX)
+  if (metal_context_provider_)
+    metal_context_provider_->SetProgressReporter(progress_reporter);
+#endif
+
   if (GrContextIsGL()) {
     DCHECK(context_->IsCurrent(nullptr));
     sk_sp<GrGLInterface> interface(gl::init::CreateGrGLInterface(
diff --git a/headless/app/headless_shell.cc b/headless/app/headless_shell.cc
index a993a1d8..57eb874c 100644
--- a/headless/app/headless_shell.cc
+++ b/headless/app/headless_shell.cc
@@ -372,7 +372,11 @@
 
 void HeadlessShell::OnReadyState(
     std::unique_ptr<runtime::EvaluateResult> result) {
-  if (result->GetResult()->GetValue()->is_string()) {
+  // result->GetResult() can be nullptr if
+  // HeadlessDevToolsClientImpl::DispatchMessageReply sees an error.
+  // It shouldn't, because the result is actually non-optional according to
+  // js_protocol.pdl; but there is no good way to graft that into here. Sigh.
+  if (result->GetResult() && result->GetResult()->GetValue()->is_string()) {
     std::stringstream stream(result->GetResult()->GetValue()->GetString());
     std::string ready_state;
     std::string url;
diff --git a/media/base/media_switches.cc b/media/base/media_switches.cc
index 2e364d0..16fa56b 100644
--- a/media/base/media_switches.cc
+++ b/media/base/media_switches.cc
@@ -500,6 +500,13 @@
 #endif
 };
 
+// Only affects Android. Suspends a media session when audio focus is lost; when
+// this setting is disabled, an Android media session will not be suspended when
+// Audio focus is lost. This is used by Cast which sometimes needs to drive
+// multiple media sessions.
+const base::Feature kAudioFocusLossSuspendMediaSession{
+    "AudioFocusMediaSession", base::FEATURE_ENABLED_BY_DEFAULT};
+
 // Enables the internal Media Session logic without enabling the Media Session
 // service.
 const base::Feature kInternalMediaSession {
diff --git a/media/base/media_switches.h b/media/base/media_switches.h
index 3bd59ea..0f530b2 100644
--- a/media/base/media_switches.h
+++ b/media/base/media_switches.h
@@ -94,6 +94,7 @@
 // alongside the definition of their values in the .cc file.
 
 MEDIA_EXPORT extern const base::Feature kAudioFocusDuckFlash;
+MEDIA_EXPORT extern const base::Feature kAudioFocusLossSuspendMediaSession;
 MEDIA_EXPORT extern const base::Feature kAutoplayIgnoreWebAudio;
 MEDIA_EXPORT extern const base::Feature kAutoplayDisableSettings;
 MEDIA_EXPORT extern const base::Feature kAutoplayWhitelistSettings;
diff --git a/media/filters/pipeline_controller.cc b/media/filters/pipeline_controller.cc
index bd05ebd..5d42944 100644
--- a/media/filters/pipeline_controller.cc
+++ b/media/filters/pipeline_controller.cc
@@ -177,6 +177,11 @@
   if (state_ == State::PLAYING_OR_SUSPENDED) {
     waiting_for_seek_ = false;
     state_ = pipeline_->IsSuspended() ? State::SUSPENDED : State::PLAYING;
+
+    // It's possible for a Suspend() call to come in during startup. If we've
+    // completed a suspended startup, we should clear that now.
+    if (state_ == State::SUSPENDED)
+      pending_suspend_ = false;
   }
 
   if (state_ == State::PLAYING) {
@@ -188,12 +193,15 @@
     // properly fixed.
     if (old_state == State::RESUMING) {
       DCHECK(!pipeline_->IsSuspended());
+      DCHECK(!pending_resume_);
+
       resumed_cb_.Run();
     }
   }
 
   if (state_ == State::SUSPENDED) {
     DCHECK(pipeline_->IsSuspended());
+    DCHECK(!pending_suspend_);
 
     // Warning: possibly reentrant. The state may change inside this callback.
     // It must be safe to call Dispatch() twice in a row here.
@@ -219,7 +227,11 @@
     return;
   }
 
-  if (pending_resume_ && state_ == State::SUSPENDED) {
+  // In additional to the standard |pending_resume_| case, if we completed a
+  // suspended startup, but a Seek() came in, we need to resume the pipeline to
+  // complete the seek before calling |seeked_cb_|.
+  if ((pending_resume_ || (pending_startup_ && pending_seek_)) &&
+      state_ == State::SUSPENDED) {
     // If there is a pending seek, resume to that time instead...
     if (pending_seek_) {
       seek_time_ = pending_seek_time_;
diff --git a/media/filters/pipeline_controller_unittest.cc b/media/filters/pipeline_controller_unittest.cc
index 56f09a8..56ba583 100644
--- a/media/filters/pipeline_controller_unittest.cc
+++ b/media/filters/pipeline_controller_unittest.cc
@@ -184,6 +184,47 @@
   EXPECT_TRUE(pipeline_controller_.IsStable());
 }
 
+TEST_F(PipelineControllerTest, StartSuspendedSeekAndResume) {
+  EXPECT_FALSE(pipeline_controller_.IsStable());
+  PipelineStatusCB start_cb;
+  EXPECT_CALL(*pipeline_, Start(_, _, _, _, _)).WillOnce(SaveArg<4>(&start_cb));
+  pipeline_controller_.Start(Pipeline::StartType::kSuspendAfterMetadata,
+                             &demuxer_, this, false, true);
+  Mock::VerifyAndClear(pipeline_);
+
+  // Initiate a seek before the pipeline completes suspended startup.
+  base::TimeDelta seek_time = base::TimeDelta::FromSeconds(5);
+  EXPECT_CALL(demuxer_, StartWaitingForSeek(seek_time));
+  pipeline_controller_.Seek(seek_time, true);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_FALSE(was_seeked_);
+
+  PipelineStatusCB resume_cb;
+  EXPECT_CALL(*pipeline_, Resume(_, _, _))
+      .WillOnce(DoAll(SaveArg<2>(&resume_cb)));
+  EXPECT_CALL(*pipeline_, GetMediaTime())
+      .WillRepeatedly(Return(base::TimeDelta()));
+
+  EXPECT_CALL(*pipeline_, IsSuspended()).WillRepeatedly(Return(true));
+  EXPECT_FALSE(pipeline_controller_.IsStable());
+  Complete(start_cb);
+
+  EXPECT_FALSE(pipeline_controller_.IsStable());
+  EXPECT_FALSE(pipeline_controller_.IsPipelineSuspended());
+  EXPECT_FALSE(pipeline_controller_.IsSuspended());
+  Mock::VerifyAndClear(pipeline_);
+
+  EXPECT_CALL(*pipeline_, IsSuspended()).WillRepeatedly(Return(false));
+  Complete(resume_cb);
+  EXPECT_TRUE(was_seeked_);
+  was_seeked_ = false;
+
+  EXPECT_TRUE(pipeline_controller_.IsStable());
+  EXPECT_FALSE(pipeline_controller_.IsPipelineSuspended());
+  EXPECT_FALSE(pipeline_controller_.IsSuspended());
+  Mock::VerifyAndClear(pipeline_);
+}
+
 TEST_F(PipelineControllerTest, StartSuspendedAndResume) {
   EXPECT_FALSE(pipeline_controller_.IsStable());
   PipelineStatusCB start_cb;
diff --git a/mojo/public/tools/bindings/chromium_bindings_configuration.gni b/mojo/public/tools/bindings/chromium_bindings_configuration.gni
index ecbb510..d70eb62 100644
--- a/mojo/public/tools/bindings/chromium_bindings_configuration.gni
+++ b/mojo/public/tools/bindings/chromium_bindings_configuration.gni
@@ -41,7 +41,6 @@
   "//services/resource_coordinator/public/cpp/typemaps.gni",
   "//services/service_manager/public/cpp/typemaps.gni",
   "//services/tracing/public/mojom/typemaps.gni",
-  "//services/ws/public/mojom/ime/typemaps.gni",
   "//services/viz/privileged/cpp/typemaps.gni",
   "//services/viz/privileged/interfaces/compositing/typemaps.gni",
   "//services/viz/public/cpp/compositing/typemaps.gni",
diff --git a/net/http/http_network_session.cc b/net/http/http_network_session.cc
index 3bff221..d3ed27d 100644
--- a/net/http/http_network_session.cc
+++ b/net/http/http_network_session.cc
@@ -108,8 +108,7 @@
       quic_goaway_sessions_on_ip_change(false),
       quic_idle_connection_timeout_seconds(kIdleConnectionTimeoutSeconds),
       quic_reduced_ping_timeout_seconds(quic::kPingTimeoutSecs),
-      quic_retransmittable_on_wire_timeout_milliseconds(
-          kDefaultRetransmittableOnWireTimeoutMillisecs),
+      quic_retransmittable_on_wire_timeout_milliseconds(0),
       quic_max_time_before_crypto_handshake_seconds(
           quic::kMaxTimeForCryptoHandshakeSecs),
       quic_max_idle_time_before_crypto_handshake_seconds(
diff --git a/net/http/http_network_session.h b/net/http/http_network_session.h
index 9de62fa..d6caef9 100644
--- a/net/http/http_network_session.h
+++ b/net/http/http_network_session.h
@@ -179,7 +179,8 @@
     // a connection was timed out with open streams.
     int quic_reduced_ping_timeout_seconds;
     // Maximum time that a session can have no retransmittable packets on the
-    // wire.
+    // wire. Set to zero if not specified and no retransmittable PING will be
+    // sent to peer when the wire has no retransmittable packets.
     int quic_retransmittable_on_wire_timeout_milliseconds;
     // Maximum time the session can be alive before crypto handshake is
     // finished.
diff --git a/net/quic/quic_chromium_client_session.cc b/net/quic/quic_chromium_client_session.cc
index 251b304..579e769e 100644
--- a/net/quic/quic_chromium_client_session.cc
+++ b/net/quic/quic_chromium_client_session.cc
@@ -819,7 +819,7 @@
   }
   connect_timing_.dns_start = dns_resolution_start_time;
   connect_timing_.dns_end = dns_resolution_end_time;
-  if (migrate_session_early_v2_) {
+  if (!retransmittable_on_wire_timeout.IsZero()) {
     connection->set_retransmittable_on_wire_timeout(
         retransmittable_on_wire_timeout);
   }
diff --git a/net/quic/quic_network_transaction_unittest.cc b/net/quic/quic_network_transaction_unittest.cc
index 78f9bf3..fa3dbbf 100644
--- a/net/quic/quic_network_transaction_unittest.cc
+++ b/net/quic/quic_network_transaction_unittest.cc
@@ -1277,6 +1277,8 @@
   CreateSession();
 
   SendRequestAndExpectQuicResponse("hello!");
+  EXPECT_TRUE(mock_quic_data.AllReadDataConsumed());
+  EXPECT_TRUE(mock_quic_data.AllWriteDataConsumed());
 }
 
 TEST_P(QuicNetworkTransactionTest, TooLargeResponseHeaders) {
diff --git a/net/quic/quic_stream_factory.cc b/net/quic/quic_stream_factory.cc
index 40bd5be..7a73796 100644
--- a/net/quic/quic_stream_factory.cc
+++ b/net/quic/quic_stream_factory.cc
@@ -1137,6 +1137,12 @@
   if (migrate_sessions_early_v2 || retry_on_alternate_network_before_handshake)
     DCHECK(migrate_sessions_on_network_change_v2);
 
+  if (retransmittable_on_wire_timeout_milliseconds == 0 &&
+      migrate_sessions_early_v2) {
+    retransmittable_on_wire_timeout_ = quic::QuicTime::Delta::FromMilliseconds(
+        kDefaultRetransmittableOnWireTimeoutMillisecs);
+  }
+
   // goaway_sessions_on_ip_change and close_sessions_on_ip_change should never
   // be simultaneously set to true.
   DCHECK(!(close_sessions_on_ip_change_ && goaway_sessions_on_ip_change_));
diff --git a/net/quic/quic_stream_factory.h b/net/quic/quic_stream_factory.h
index b7a988f6..bae47ec 100644
--- a/net/quic/quic_stream_factory.h
+++ b/net/quic/quic_stream_factory.h
@@ -83,8 +83,8 @@
 // Sessions can migrate if they have been idle for less than this period.
 const int kDefaultIdleSessionMigrationPeriodSeconds = 30;
 
-// The maximum time allowed to have no retransmittable packets on the wire
-// (after sending the first retransmittable packet) if
+// The default maximum time allowed to have no retransmittable packets on the
+// wire (after sending the first retransmittable packet) if
 // |migrate_session_early_v2_| is true. PING frames will be sent as needed to
 // enforce this.
 const int64_t kDefaultRetransmittableOnWireTimeoutMillisecs = 100;
diff --git a/net/quic/quic_stream_factory_test.cc b/net/quic/quic_stream_factory_test.cc
index 73b3e28..2a26bc8 100644
--- a/net/quic/quic_stream_factory_test.cc
+++ b/net/quic/quic_stream_factory_test.cc
@@ -804,6 +804,27 @@
         version_.transport_version, n);
   }
 
+  std::string ConstructDataHeader(size_t body_len) {
+    if (version_.transport_version != quic::QUIC_VERSION_99) {
+      return "";
+    }
+    quic::HttpEncoder encoder;
+    std::unique_ptr<char[]> buffer;
+    auto header_length = encoder.SerializeDataFrameHeader(body_len, &buffer);
+    return std::string(buffer.get(), header_length);
+  }
+
+  std::unique_ptr<quic::QuicEncryptedPacket> ConstructServerDataPacket(
+      uint64_t packet_number,
+      quic::QuicStreamId stream_id,
+      bool should_include_version,
+      bool fin,
+      quic::QuicStreamOffset offset,
+      quic::QuicStringPiece data) {
+    return server_maker_.MakeDataPacket(
+        packet_number, stream_id, should_include_version, fin, offset, data);
+  }
+
   quic::QuicStreamId GetNthServerInitiatedUnidirectionalStreamId(int n) {
     return quic::test::GetNthServerInitiatedUnidirectionalStreamId(
         version_.transport_version, n);
@@ -7207,12 +7228,880 @@
   EXPECT_EQ(200, response.headers->response_code());
 
   stream.reset();
+  EXPECT_TRUE(socket_data.AllWriteDataConsumed());
+  EXPECT_TRUE(socket_data1.AllReadDataConsumed());
+  EXPECT_TRUE(socket_data1.AllWriteDataConsumed());
+}
+
+// This test verifies that when connection migration on path degrading is
+// enabled, and no custom retransmittable on wire timeout is specified, the
+// default value is used.
+TEST_P(QuicStreamFactoryTest, DefaultRetransmittableOnWireTimeoutForMigration) {
+  InitializeConnectionMigrationV2Test(
+      {kDefaultNetworkForTests, kNewNetworkForTests});
+  ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
+  crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
+  crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
+
+  // Using a testing task runner.
+  auto task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>();
+  QuicStreamFactoryPeer::SetTaskRunner(factory_.get(), task_runner.get());
+  QuicStreamFactoryPeer::SetAlarmFactory(
+      factory_.get(),
+      std::make_unique<QuicChromiumAlarmFactory>(task_runner.get(), &clock_));
+
+  MockQuicData socket_data;
+  quic::QuicStreamOffset header_stream_offset = 0;
+  socket_data.AddWrite(
+      SYNCHRONOUS, ConstructInitialSettingsPacket(1, &header_stream_offset));
+  socket_data.AddRead(ASYNC, ERR_IO_PENDING);  // Pause
+  socket_data.AddRead(ASYNC, ERR_ADDRESS_UNREACHABLE);
+  socket_data.AddSocketDataToFactory(socket_factory_.get());
+
+  // Set up second socket data provider that is used after
+  // migration. The request is written to this new socket, and the
+  // response to the request is read on this new socket.
+  MockQuicData socket_data1;
+  // The PING packet sent post migration.
+  socket_data1.AddWrite(SYNCHRONOUS, client_maker_.MakePingPacket(2, true));
+  socket_data1.AddWrite(
+      SYNCHRONOUS, ConstructGetRequestPacket(
+                       3, GetNthClientInitiatedBidirectionalStreamId(0), true,
+                       true, &header_stream_offset));
+  socket_data1.AddRead(ASYNC, ERR_IO_PENDING);  // Pause.
+  // Read two packets so that client will send ACK immedaitely.
+  spdy::SpdyHeaderBlock response_headers =
+      server_maker_.GetResponseHeaders("200 OK");
+  response_headers["key1"] = std::string(2000, 'A');
+  spdy::SpdyHeadersIR headers_frame(
+      GetNthClientInitiatedBidirectionalStreamId(0),
+      std::move(response_headers));
+  spdy::SpdyFramer response_framer(spdy::SpdyFramer::ENABLE_COMPRESSION);
+  spdy::SpdySerializedFrame spdy_frame =
+      response_framer.SerializeFrame(headers_frame);
+  size_t chunk_size = 1200;
+  unsigned int packet_number = 1;
+  for (size_t offset = 0; offset < spdy_frame.size(); offset += chunk_size) {
+    size_t len = std::min(chunk_size, spdy_frame.size() - offset);
+    socket_data1.AddRead(
+        ASYNC,
+        server_maker_.MakeDataPacket(
+            packet_number++,
+            quic::QuicUtils::GetHeadersStreamId(version_.transport_version),
+            false, false, offset,
+            base::StringPiece(spdy_frame.data() + offset, len)));
+  }
+  // Read an ACK from server which acks all client data.
+  socket_data1.AddRead(SYNCHRONOUS,
+                       server_maker_.MakeAckPacket(3, 3, 1, 1, false));
+  socket_data1.AddWrite(ASYNC, client_maker_.MakeAckPacket(4, 2, 1, 1, false));
+  // The PING packet sent for retransmittable on wire.
+  socket_data1.AddWrite(SYNCHRONOUS, client_maker_.MakePingPacket(5, false));
+  socket_data1.AddRead(ASYNC, ERR_IO_PENDING);  // Pause.
+  std::string header = ConstructDataHeader(6);
+  socket_data1.AddRead(
+      ASYNC, ConstructServerDataPacket(
+                 3, GetNthClientInitiatedBidirectionalStreamId(0), false, true,
+                 0, header + "hello!"));
+  socket_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING);  // No more data to read.
+  socket_data1.AddWrite(
+      SYNCHRONOUS, client_maker_.MakeRstPacket(
+                       6, false, GetNthClientInitiatedBidirectionalStreamId(0),
+                       quic::QUIC_STREAM_CANCELLED));
+  socket_data1.AddSocketDataToFactory(socket_factory_.get());
+
+  // Create request and QuicHttpStream.
+  QuicStreamRequest request(factory_.get());
+  EXPECT_EQ(ERR_IO_PENDING,
+            request.Request(
+                host_port_pair_, version_.transport_version, privacy_mode_,
+                DEFAULT_PRIORITY, SocketTag(),
+                /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
+                failed_on_default_network_callback_, callback_.callback()));
+  EXPECT_EQ(OK, callback_.WaitForResult());
+  std::unique_ptr<HttpStream> stream = CreateStream(&request);
+  EXPECT_TRUE(stream.get());
+
+  // Cause QUIC stream to be created.
+  HttpRequestInfo request_info;
+  request_info.method = "GET";
+  request_info.url = GURL("https://www.example.org/");
+  request_info.traffic_annotation =
+      MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
+  EXPECT_EQ(OK, stream->InitializeStream(&request_info, true, DEFAULT_PRIORITY,
+                                         net_log_, CompletionOnceCallback()));
+
+  // Ensure that session is alive and active.
+  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+
+  // Now notify network is disconnected, cause the migration to complete
+  // immediately.
+  scoped_mock_network_change_notifier_->mock_network_change_notifier()
+      ->NotifyNetworkDisconnected(kDefaultNetworkForTests);
+
+  // Complete migration.
+  task_runner->RunUntilIdle();
+  EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
+  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_EQ(1u, session->GetNumActiveStreams());
+
+  // Send GET request on stream.
+  HttpResponseInfo response;
+  HttpRequestHeaders request_headers;
+  EXPECT_EQ(OK, stream->SendRequest(request_headers, &response,
+                                    callback_.callback()));
+  socket_data1.Resume();
+  // Spin up the message loop to read incoming data from server till the ACK.
+  base::RunLoop().RunUntilIdle();
+
+  // Ack delay time.
+  int delay = task_runner->NextPendingTaskDelay().InMilliseconds();
+  EXPECT_GT(kDefaultRetransmittableOnWireTimeoutMillisecs, delay);
+  // Fire the ack alarm, since ack has been sent, no ack will be sent.
+  clock_.AdvanceTime(quic::QuicTime::Delta::FromMilliseconds(delay));
+  task_runner->FastForwardBy(task_runner->NextPendingTaskDelay());
+
+  // Fire the ping alarm with retransmittable-on-wire timeout, send PING.
+  delay = kDefaultRetransmittableOnWireTimeoutMillisecs - delay;
+  EXPECT_EQ(base::TimeDelta::FromMilliseconds(delay),
+            task_runner->NextPendingTaskDelay());
+  clock_.AdvanceTime(quic::QuicTime::Delta::FromMilliseconds(delay));
+  task_runner->FastForwardBy(task_runner->NextPendingTaskDelay());
+
+  socket_data1.Resume();
+
+  // Verify that response headers on the migrated socket were delivered to the
+  // stream.
+  EXPECT_EQ(OK, stream->ReadResponseHeaders(callback_.callback()));
+  EXPECT_EQ(200, response.headers->response_code());
+
+  // Resume the old socket data, a read error will be delivered to the old
+  // packet reader. Verify that the session is not affected.
+  socket_data.Resume();
+  EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
+  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_EQ(1u, session->GetNumActiveStreams());
+
+  stream.reset();
   EXPECT_TRUE(socket_data.AllReadDataConsumed());
   EXPECT_TRUE(socket_data.AllWriteDataConsumed());
   EXPECT_TRUE(socket_data1.AllReadDataConsumed());
   EXPECT_TRUE(socket_data1.AllWriteDataConsumed());
 }
 
+// This test verifies that when connection migration on path degrading is
+// enabled, and a custom retransmittable on wire timeout is specified, the
+// custom value is used.
+TEST_P(QuicStreamFactoryTest, CustomRetransmittableOnWireTimeoutForMigration) {
+  int custom_timeout_value = 200;
+  test_params_.quic_retransmittable_on_wire_timeout_milliseconds =
+      custom_timeout_value;
+  InitializeConnectionMigrationV2Test(
+      {kDefaultNetworkForTests, kNewNetworkForTests});
+  ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
+  crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
+  crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
+
+  // Using a testing task runner.
+  auto task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>();
+  QuicStreamFactoryPeer::SetTaskRunner(factory_.get(), task_runner.get());
+  QuicStreamFactoryPeer::SetAlarmFactory(
+      factory_.get(),
+      std::make_unique<QuicChromiumAlarmFactory>(task_runner.get(), &clock_));
+
+  MockQuicData socket_data;
+  quic::QuicStreamOffset header_stream_offset = 0;
+  socket_data.AddWrite(
+      SYNCHRONOUS, ConstructInitialSettingsPacket(1, &header_stream_offset));
+  socket_data.AddRead(ASYNC, ERR_IO_PENDING);  // Pause
+  socket_data.AddRead(ASYNC, ERR_ADDRESS_UNREACHABLE);
+  socket_data.AddSocketDataToFactory(socket_factory_.get());
+
+  // Set up second socket data provider that is used after
+  // migration. The request is written to this new socket, and the
+  // response to the request is read on this new socket.
+  MockQuicData socket_data1;
+  // The PING packet sent post migration.
+  socket_data1.AddWrite(SYNCHRONOUS, client_maker_.MakePingPacket(2, true));
+  socket_data1.AddWrite(
+      SYNCHRONOUS, ConstructGetRequestPacket(
+                       3, GetNthClientInitiatedBidirectionalStreamId(0), true,
+                       true, &header_stream_offset));
+  socket_data1.AddRead(ASYNC, ERR_IO_PENDING);  // Pause.
+  // Read two packets so that client will send ACK immedaitely.
+  spdy::SpdyHeaderBlock response_headers =
+      server_maker_.GetResponseHeaders("200 OK");
+  response_headers["key1"] = std::string(2000, 'A');
+  spdy::SpdyHeadersIR headers_frame(
+      GetNthClientInitiatedBidirectionalStreamId(0),
+      std::move(response_headers));
+  spdy::SpdyFramer response_framer(spdy::SpdyFramer::ENABLE_COMPRESSION);
+  spdy::SpdySerializedFrame spdy_frame =
+      response_framer.SerializeFrame(headers_frame);
+  size_t chunk_size = 1200;
+  unsigned int packet_number = 1;
+  for (size_t offset = 0; offset < spdy_frame.size(); offset += chunk_size) {
+    size_t len = std::min(chunk_size, spdy_frame.size() - offset);
+    socket_data1.AddRead(
+        ASYNC,
+        server_maker_.MakeDataPacket(
+            packet_number++,
+            quic::QuicUtils::GetHeadersStreamId(version_.transport_version),
+            false, false, offset,
+            base::StringPiece(spdy_frame.data() + offset, len)));
+  }
+  // Read an ACK from server which acks all client data.
+  socket_data1.AddRead(SYNCHRONOUS,
+                       server_maker_.MakeAckPacket(3, 3, 1, 1, false));
+  socket_data1.AddWrite(ASYNC, client_maker_.MakeAckPacket(4, 2, 1, 1, false));
+  // The PING packet sent for retransmittable on wire.
+  socket_data1.AddWrite(SYNCHRONOUS, client_maker_.MakePingPacket(5, false));
+  socket_data1.AddRead(ASYNC, ERR_IO_PENDING);  // Pause.
+  std::string header = ConstructDataHeader(6);
+  socket_data1.AddRead(
+      ASYNC, ConstructServerDataPacket(
+                 3, GetNthClientInitiatedBidirectionalStreamId(0), false, true,
+                 0, header + "hello!"));
+  socket_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING);  // No more data to read.
+  socket_data1.AddWrite(
+      SYNCHRONOUS, client_maker_.MakeRstPacket(
+                       6, false, GetNthClientInitiatedBidirectionalStreamId(0),
+                       quic::QUIC_STREAM_CANCELLED));
+  socket_data1.AddSocketDataToFactory(socket_factory_.get());
+
+  // Create request and QuicHttpStream.
+  QuicStreamRequest request(factory_.get());
+  EXPECT_EQ(ERR_IO_PENDING,
+            request.Request(
+                host_port_pair_, version_.transport_version, privacy_mode_,
+                DEFAULT_PRIORITY, SocketTag(),
+                /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
+                failed_on_default_network_callback_, callback_.callback()));
+  EXPECT_EQ(OK, callback_.WaitForResult());
+  std::unique_ptr<HttpStream> stream = CreateStream(&request);
+  EXPECT_TRUE(stream.get());
+
+  // Cause QUIC stream to be created.
+  HttpRequestInfo request_info;
+  request_info.method = "GET";
+  request_info.url = GURL("https://www.example.org/");
+  request_info.traffic_annotation =
+      MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
+  EXPECT_EQ(OK, stream->InitializeStream(&request_info, true, DEFAULT_PRIORITY,
+                                         net_log_, CompletionOnceCallback()));
+
+  // Ensure that session is alive and active.
+  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+
+  // Now notify network is disconnected, cause the migration to complete
+  // immediately.
+  scoped_mock_network_change_notifier_->mock_network_change_notifier()
+      ->NotifyNetworkDisconnected(kDefaultNetworkForTests);
+
+  // Complete migration.
+  task_runner->RunUntilIdle();
+  EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
+  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_EQ(1u, session->GetNumActiveStreams());
+
+  // Send GET request on stream.
+  HttpResponseInfo response;
+  HttpRequestHeaders request_headers;
+  EXPECT_EQ(OK, stream->SendRequest(request_headers, &response,
+                                    callback_.callback()));
+  socket_data1.Resume();
+  // Spin up the message loop to read incoming data from server till the ACK.
+  base::RunLoop().RunUntilIdle();
+
+  // Ack delay time.
+  int delay = task_runner->NextPendingTaskDelay().InMilliseconds();
+  EXPECT_GT(custom_timeout_value, delay);
+  // Fire the ack alarm, since ack has been sent, no ack will be sent.
+  clock_.AdvanceTime(quic::QuicTime::Delta::FromMilliseconds(delay));
+  task_runner->FastForwardBy(task_runner->NextPendingTaskDelay());
+
+  // Fire the ping alarm with retransmittable-on-wire timeout, send PING.
+  delay = custom_timeout_value - delay;
+  EXPECT_EQ(base::TimeDelta::FromMilliseconds(delay),
+            task_runner->NextPendingTaskDelay());
+  clock_.AdvanceTime(quic::QuicTime::Delta::FromMilliseconds(delay));
+  task_runner->FastForwardBy(task_runner->NextPendingTaskDelay());
+
+  socket_data1.Resume();
+
+  // Verify that response headers on the migrated socket were delivered to the
+  // stream.
+  EXPECT_EQ(OK, stream->ReadResponseHeaders(callback_.callback()));
+  EXPECT_EQ(200, response.headers->response_code());
+
+  // Resume the old socket data, a read error will be delivered to the old
+  // packet reader. Verify that the session is not affected.
+  socket_data.Resume();
+  EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
+  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_EQ(1u, session->GetNumActiveStreams());
+
+  stream.reset();
+  EXPECT_TRUE(socket_data.AllReadDataConsumed());
+  EXPECT_TRUE(socket_data.AllWriteDataConsumed());
+  EXPECT_TRUE(socket_data1.AllReadDataConsumed());
+  EXPECT_TRUE(socket_data1.AllWriteDataConsumed());
+}
+
+// This test verifies that when no migration is enabled, but a custom value for
+// retransmittable-on-wire timeout is specified, the ping alarm is set up to
+// send retransmittable pings with the custom value.
+TEST_P(QuicStreamFactoryTest, CustomRetransmittableOnWireTimeout) {
+  int custom_timeout_value = 200;
+  test_params_.quic_retransmittable_on_wire_timeout_milliseconds =
+      custom_timeout_value;
+  Initialize();
+  ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
+  crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
+  crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
+
+  // Using a testing task runner.
+  auto task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>();
+  QuicStreamFactoryPeer::SetTaskRunner(factory_.get(), task_runner.get());
+  QuicStreamFactoryPeer::SetAlarmFactory(
+      factory_.get(),
+      std::make_unique<QuicChromiumAlarmFactory>(task_runner.get(), &clock_));
+
+  MockQuicData socket_data1;
+  quic::QuicStreamOffset header_stream_offset = 0;
+  socket_data1.AddWrite(
+      SYNCHRONOUS, ConstructInitialSettingsPacket(1, &header_stream_offset));
+  socket_data1.AddWrite(
+      SYNCHRONOUS, ConstructGetRequestPacket(
+                       2, GetNthClientInitiatedBidirectionalStreamId(0), true,
+                       true, &header_stream_offset));
+  socket_data1.AddRead(ASYNC, ERR_IO_PENDING);  // Pause.
+  // Read two packets so that client will send ACK immedaitely.
+  spdy::SpdyHeaderBlock response_headers =
+      server_maker_.GetResponseHeaders("200 OK");
+  response_headers["key1"] = std::string(2000, 'A');
+  spdy::SpdyHeadersIR headers_frame(
+      GetNthClientInitiatedBidirectionalStreamId(0),
+      std::move(response_headers));
+  spdy::SpdyFramer response_framer(spdy::SpdyFramer::ENABLE_COMPRESSION);
+  spdy::SpdySerializedFrame spdy_frame =
+      response_framer.SerializeFrame(headers_frame);
+  size_t chunk_size = 1200;
+  unsigned int packet_number = 1;
+  for (size_t offset = 0; offset < spdy_frame.size(); offset += chunk_size) {
+    size_t len = std::min(chunk_size, spdy_frame.size() - offset);
+    socket_data1.AddRead(
+        ASYNC,
+        server_maker_.MakeDataPacket(
+            packet_number++,
+            quic::QuicUtils::GetHeadersStreamId(version_.transport_version),
+            false, false, offset,
+            base::StringPiece(spdy_frame.data() + offset, len)));
+  }
+  // Read an ACK from server which acks all client data.
+  socket_data1.AddRead(SYNCHRONOUS,
+                       server_maker_.MakeAckPacket(3, 2, 1, 1, false));
+  socket_data1.AddWrite(ASYNC, client_maker_.MakeAckPacket(3, 2, 1, 1, false));
+  // The PING packet sent for retransmittable on wire.
+  socket_data1.AddWrite(SYNCHRONOUS, client_maker_.MakePingPacket(4, false));
+  socket_data1.AddRead(ASYNC, ERR_IO_PENDING);  // Pause.
+  std::string header = ConstructDataHeader(6);
+  socket_data1.AddRead(
+      ASYNC, ConstructServerDataPacket(
+                 3, GetNthClientInitiatedBidirectionalStreamId(0), false, true,
+                 0, header + "hello!"));
+  socket_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING);  // No more data to read.
+  socket_data1.AddWrite(
+      SYNCHRONOUS, client_maker_.MakeRstPacket(
+                       5, false, GetNthClientInitiatedBidirectionalStreamId(0),
+                       quic::QUIC_STREAM_CANCELLED));
+  socket_data1.AddSocketDataToFactory(socket_factory_.get());
+
+  // Create request and QuicHttpStream.
+  QuicStreamRequest request(factory_.get());
+  EXPECT_EQ(ERR_IO_PENDING,
+            request.Request(
+                host_port_pair_, version_.transport_version, privacy_mode_,
+                DEFAULT_PRIORITY, SocketTag(),
+                /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
+                failed_on_default_network_callback_, callback_.callback()));
+  EXPECT_EQ(OK, callback_.WaitForResult());
+  std::unique_ptr<HttpStream> stream = CreateStream(&request);
+  EXPECT_TRUE(stream.get());
+
+  // Cause QUIC stream to be created.
+  HttpRequestInfo request_info;
+  request_info.method = "GET";
+  request_info.url = GURL("https://www.example.org/");
+  request_info.traffic_annotation =
+      MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
+  EXPECT_EQ(OK, stream->InitializeStream(&request_info, true, DEFAULT_PRIORITY,
+                                         net_log_, CompletionOnceCallback()));
+
+  // Ensure that session is alive and active.
+  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+
+  // Complete migration.
+  task_runner->RunUntilIdle();
+  EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
+  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_EQ(1u, session->GetNumActiveStreams());
+
+  // Send GET request on stream.
+  HttpResponseInfo response;
+  HttpRequestHeaders request_headers;
+  EXPECT_EQ(OK, stream->SendRequest(request_headers, &response,
+                                    callback_.callback()));
+  socket_data1.Resume();
+  // Spin up the message loop to read incoming data from server till the ACK.
+  base::RunLoop().RunUntilIdle();
+
+  // Ack delay time.
+  int delay = task_runner->NextPendingTaskDelay().InMilliseconds();
+  EXPECT_GT(custom_timeout_value, delay);
+  // Fire the ack alarm, since ack has been sent, no ack will be sent.
+  clock_.AdvanceTime(quic::QuicTime::Delta::FromMilliseconds(delay));
+  task_runner->FastForwardBy(task_runner->NextPendingTaskDelay());
+
+  // Fire the ping alarm with retransmittable-on-wire timeout, send PING.
+  delay = custom_timeout_value - delay;
+  EXPECT_EQ(base::TimeDelta::FromMilliseconds(delay),
+            task_runner->NextPendingTaskDelay());
+  clock_.AdvanceTime(quic::QuicTime::Delta::FromMilliseconds(delay));
+  task_runner->FastForwardBy(task_runner->NextPendingTaskDelay());
+
+  socket_data1.Resume();
+
+  // Verify that response headers on the migrated socket were delivered to the
+  // stream.
+  EXPECT_EQ(OK, stream->ReadResponseHeaders(callback_.callback()));
+  EXPECT_EQ(200, response.headers->response_code());
+
+  // Resume the old socket data, a read error will be delivered to the old
+  // packet reader. Verify that the session is not affected.
+  EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
+  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_EQ(1u, session->GetNumActiveStreams());
+
+  stream.reset();
+  EXPECT_TRUE(socket_data1.AllReadDataConsumed());
+  EXPECT_TRUE(socket_data1.AllWriteDataConsumed());
+}
+
+// This test verifies that when no migration is enabled, and no custom value
+// for retransmittable-on-wire timeout is specified, the ping alarm will not
+// send any retransmittable pings.
+TEST_P(QuicStreamFactoryTest, NoRetransmittableOnWireTimeout) {
+  Initialize();
+  ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
+  crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
+  crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
+
+  // Using a testing task runner.
+  auto task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>();
+  QuicStreamFactoryPeer::SetTaskRunner(factory_.get(), task_runner.get());
+  QuicStreamFactoryPeer::SetAlarmFactory(
+      factory_.get(),
+      std::make_unique<QuicChromiumAlarmFactory>(task_runner.get(), &clock_));
+
+  MockQuicData socket_data1;
+  quic::QuicStreamOffset header_stream_offset = 0;
+  socket_data1.AddWrite(
+      SYNCHRONOUS, ConstructInitialSettingsPacket(1, &header_stream_offset));
+  socket_data1.AddWrite(
+      SYNCHRONOUS, ConstructGetRequestPacket(
+                       2, GetNthClientInitiatedBidirectionalStreamId(0), true,
+                       true, &header_stream_offset));
+  socket_data1.AddRead(ASYNC, ERR_IO_PENDING);  // Pause.
+  // Read two packets so that client will send ACK immedaitely.
+  spdy::SpdyHeaderBlock response_headers =
+      server_maker_.GetResponseHeaders("200 OK");
+  response_headers["key1"] = std::string(2000, 'A');
+  spdy::SpdyHeadersIR headers_frame(
+      GetNthClientInitiatedBidirectionalStreamId(0),
+      std::move(response_headers));
+  spdy::SpdyFramer response_framer(spdy::SpdyFramer::ENABLE_COMPRESSION);
+  spdy::SpdySerializedFrame spdy_frame =
+      response_framer.SerializeFrame(headers_frame);
+  size_t chunk_size = 1200;
+  unsigned int packet_number = 1;
+  for (size_t offset = 0; offset < spdy_frame.size(); offset += chunk_size) {
+    size_t len = std::min(chunk_size, spdy_frame.size() - offset);
+    socket_data1.AddRead(
+        ASYNC,
+        server_maker_.MakeDataPacket(
+            packet_number++,
+            quic::QuicUtils::GetHeadersStreamId(version_.transport_version),
+            false, false, offset,
+            base::StringPiece(spdy_frame.data() + offset, len)));
+  }
+  // Read an ACK from server which acks all client data.
+  socket_data1.AddRead(SYNCHRONOUS,
+                       server_maker_.MakeAckPacket(3, 2, 1, 1, false));
+  socket_data1.AddWrite(ASYNC, client_maker_.MakeAckPacket(3, 2, 1, 1, false));
+  std::string header = ConstructDataHeader(6);
+  socket_data1.AddRead(
+      ASYNC, ConstructServerDataPacket(
+                 3, GetNthClientInitiatedBidirectionalStreamId(0), false, true,
+                 0, header + "hello!"));
+  socket_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING);  // No more data to read.
+  socket_data1.AddWrite(
+      SYNCHRONOUS, client_maker_.MakeRstPacket(
+                       4, false, GetNthClientInitiatedBidirectionalStreamId(0),
+                       quic::QUIC_STREAM_CANCELLED));
+  socket_data1.AddSocketDataToFactory(socket_factory_.get());
+
+  // Create request and QuicHttpStream.
+  QuicStreamRequest request(factory_.get());
+  EXPECT_EQ(ERR_IO_PENDING,
+            request.Request(
+                host_port_pair_, version_.transport_version, privacy_mode_,
+                DEFAULT_PRIORITY, SocketTag(),
+                /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
+                failed_on_default_network_callback_, callback_.callback()));
+  EXPECT_EQ(OK, callback_.WaitForResult());
+  std::unique_ptr<HttpStream> stream = CreateStream(&request);
+  EXPECT_TRUE(stream.get());
+
+  // Cause QUIC stream to be created.
+  HttpRequestInfo request_info;
+  request_info.method = "GET";
+  request_info.url = GURL("https://www.example.org/");
+  request_info.traffic_annotation =
+      MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
+  EXPECT_EQ(OK, stream->InitializeStream(&request_info, true, DEFAULT_PRIORITY,
+                                         net_log_, CompletionOnceCallback()));
+
+  // Ensure that session is alive and active.
+  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+
+  // Complete migration.
+  task_runner->RunUntilIdle();
+  EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
+  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_EQ(1u, session->GetNumActiveStreams());
+
+  // Send GET request on stream.
+  HttpResponseInfo response;
+  HttpRequestHeaders request_headers;
+  EXPECT_EQ(OK, stream->SendRequest(request_headers, &response,
+                                    callback_.callback()));
+  socket_data1.Resume();
+  // Spin up the message loop to read incoming data from server till the ACK.
+  base::RunLoop().RunUntilIdle();
+
+  // Ack delay time.
+  int delay = task_runner->NextPendingTaskDelay().InMilliseconds();
+  EXPECT_GT(kDefaultRetransmittableOnWireTimeoutMillisecs, delay);
+  // Fire the ack alarm, since ack has been sent, no ack will be sent.
+  clock_.AdvanceTime(quic::QuicTime::Delta::FromMilliseconds(delay));
+  task_runner->FastForwardBy(task_runner->NextPendingTaskDelay());
+
+  // Verify that the ping alarm is not set with any default value.
+  int wrong_delay = kDefaultRetransmittableOnWireTimeoutMillisecs - delay;
+  delay = task_runner->NextPendingTaskDelay().InMilliseconds();
+  EXPECT_NE(wrong_delay, delay);
+  clock_.AdvanceTime(quic::QuicTime::Delta::FromMilliseconds(delay));
+  task_runner->FastForwardBy(task_runner->NextPendingTaskDelay());
+
+  // Verify that response headers on the migrated socket were delivered to the
+  // stream.
+  EXPECT_EQ(OK, stream->ReadResponseHeaders(callback_.callback()));
+  EXPECT_EQ(200, response.headers->response_code());
+
+  // Resume the old socket data, a read error will be delivered to the old
+  // packet reader. Verify that the session is not affected.
+  EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
+  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+  EXPECT_EQ(1u, session->GetNumActiveStreams());
+
+  stream.reset();
+  EXPECT_TRUE(socket_data1.AllReadDataConsumed());
+  EXPECT_TRUE(socket_data1.AllWriteDataConsumed());
+}
+
+// This test verifies that when only migration on network change is enabled, and
+// a custom value for retransmittable-on-wire is specified, the ping alarm will
+// send retransmittable pings to the peer with custom value.
+TEST_P(QuicStreamFactoryTest,
+       CustomeRetransmittableOnWireTimeoutWithMigrationOnNetworkChangeOnly) {
+  int custom_timeout_value = 200;
+  test_params_.quic_retransmittable_on_wire_timeout_milliseconds =
+      custom_timeout_value;
+  test_params_.quic_migrate_sessions_on_network_change_v2 = true;
+  Initialize();
+  ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
+  crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
+  crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
+
+  // Using a testing task runner.
+  auto task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>();
+  QuicStreamFactoryPeer::SetTaskRunner(factory_.get(), task_runner.get());
+  QuicStreamFactoryPeer::SetAlarmFactory(
+      factory_.get(),
+      std::make_unique<QuicChromiumAlarmFactory>(task_runner.get(), &clock_));
+
+  MockQuicData socket_data1;
+  quic::QuicStreamOffset header_stream_offset = 0;
+  socket_data1.AddWrite(
+      SYNCHRONOUS, ConstructInitialSettingsPacket(1, &header_stream_offset));
+  socket_data1.AddWrite(
+      SYNCHRONOUS, ConstructGetRequestPacket(
+                       2, GetNthClientInitiatedBidirectionalStreamId(0), true,
+                       true, &header_stream_offset));
+  socket_data1.AddRead(ASYNC, ERR_IO_PENDING);  // Pause.
+  // Read two packets so that client will send ACK immedaitely.
+  spdy::SpdyHeaderBlock response_headers =
+      server_maker_.GetResponseHeaders("200 OK");
+  response_headers["key1"] = std::string(2000, 'A');
+  spdy::SpdyHeadersIR headers_frame(
+      GetNthClientInitiatedBidirectionalStreamId(0),
+      std::move(response_headers));
+  spdy::SpdyFramer response_framer(spdy::SpdyFramer::ENABLE_COMPRESSION);
+  spdy::SpdySerializedFrame spdy_frame =
+      response_framer.SerializeFrame(headers_frame);
+  size_t chunk_size = 1200;
+  unsigned int packet_number = 1;
+  for (size_t offset = 0; offset < spdy_frame.size(); offset += chunk_size) {
+    size_t len = std::min(chunk_size, spdy_frame.size() - offset);
+    socket_data1.AddRead(