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..90a6450d 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 11d1bb27..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 04512b4d..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 dff11810..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 ff71d936..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..6058092a 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 b65d0029..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..ede93977 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..b99fd70c 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 03e26ee2..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..e9203327 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 739618df..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..dadbfd2f 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 71769817c..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 fc60c2e3..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 596574b0..8dcea58d 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 07e3c7ec..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 49fb7751..21c6bbfd 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..42fc4263 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..caca2edd 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 a52366599..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 21e64571..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 a4ffc7fa..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..348fab7b 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..aee47d28 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 f6ec4b40..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..3c944267c 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 9f1a8721..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..5c94e3bd 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 e7ee96cb..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..ed90bf1f 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..25faeea3 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..be52a416 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 6ec8e4cf..24800637 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 4e203ab0..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..c211b7bf 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 d24a0329..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 cb8346e92..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 eb86c61c..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 2eccad49..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..137231b9 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 1758e722..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..523581d1 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..4973ef14 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 b97aefa5..3ebfc15f 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..8b20ebc6 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 d48a2856..cadf9057 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 0ccf3592..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 eb5f4cc8..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..8d12ea95 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..366a5872 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 be85c3b8e..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..3ea5d4f1 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..cc73ebbe 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..97b8797e 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..866c2598 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..ff297bed 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 bf4b57e44..0ca812335 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 b91c3c49..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 2901c0e6..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 5ecaf341..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 e4d7dab0e..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 cc843142..cca2bc70 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..9c85611e 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..2f1bd132 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..d34538da 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 6175e63c..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..78100cef 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 d0bfe101..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..abe5f4b5 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 43d350a9..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 ca7f87b9..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 641fad31..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 1f6fe09f..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 94343446..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..8ac63482 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 68f69699..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..7ca5511cc 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 c6a15532..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 1f20da28..af3d2d2f9 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 cb0d4a22..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 319bab81..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 2e09ed57..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 9c2df763..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..78a50181 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 ebd5a317..5afe27a4f 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 aa462f47..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 f62c45b4..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..2bf52eed 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..fc67f4c41 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..fb8757de 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 6ef3e425..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 c9201251..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 62c9abb5..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 be97e7d5..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 16074ee8..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..3669d984 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..f35e22d4 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 856f6600..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 b960ff1e..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 986fc8ef7..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 4a4a6c75..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 74b8c44a..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..c8216dc3 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 ffce4483..fc0b42bc 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 6a0363fd..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..f3c2fb57
--- /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..03e4d6c0 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 0f056d3a..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 b7a6268f..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 fae07a36..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..704da9fb 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..d083b940
--- /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 0c41bb8f..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 517ed845..cc751c39 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..5cd5333a
--- /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..f69c33ac 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..6d7d0263 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..21ba92eb 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 c378fb54..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..7caf1a9a 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..31ad1c93 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 4942a43f..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 257bf61c..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 55d2153a..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..211bfd81 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 77861ecf..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 2e364d0c..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 3bd59ea6..0f530b26 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 bd05ebdc..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 56f09a80..56ba5839 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..d70eb62d 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 9de62fae..d6caef9c 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..fa3dbbf6 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(
+        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 only migration on network change is enabled, and
+// no custom value for retransmittable-on-wire is specified, the ping alarm will
+// NOT send retransmittable pings to the peer with custom value.
+TEST_P(QuicStreamFactoryTest,
+       NoRetransmittableOnWireTimeoutWithMigrationOnNetworkChangeOnly) {
+  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(
+        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 ping alarm is not set with 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 after migration on write error is posted, packet
 // read error on the old reader will be ignored and will not close the
 // connection.
diff --git a/printing/backend/cups_helper.cc b/printing/backend/cups_helper.cc
index 9fff0c5b..e522d6c 100644
--- a/printing/backend/cups_helper.cc
+++ b/printing/backend/cups_helper.cc
@@ -66,9 +66,8 @@
   const size_t kDestLen = sizeof(kDest) - 1;
   const size_t kDefaultLen = sizeof(kDefault) - 1;
 
-  for (base::StringPiece line :
-       base::SplitStringPiece(content, "\n", base::KEEP_WHITESPACE,
-                              base::SPLIT_WANT_NONEMPTY)) {
+  for (base::StringPiece line : base::SplitStringPiece(
+           content, "\n", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) {
     if (base::StartsWith(line, base::StringPiece(kDefault, kDefaultLen),
                          base::CompareCase::INSENSITIVE_ASCII) &&
         isspace(line[kDefaultLen])) {
@@ -235,8 +234,8 @@
   // value.
   ppd_choice_t* printout_mode_choice = ppdFindMarkedChoice(ppd, kPrintoutMode);
   if (!printout_mode_choice) {
-      printout_mode_choice = ppdFindChoice(printout_mode,
-                                           printout_mode->defchoice);
+    printout_mode_choice =
+        ppdFindChoice(printout_mode, printout_mode->defchoice);
   }
   if (printout_mode_choice) {
     if (EqualsCaseInsensitiveASCII(printout_mode_choice->choice, kNormalGray) ||
@@ -270,8 +269,8 @@
 
   ppd_choice_t* mode_choice = ppdFindMarkedChoice(ppd, kColorMode);
   if (!mode_choice) {
-    mode_choice = ppdFindChoice(color_mode_option,
-                                color_mode_option->defchoice);
+    mode_choice =
+        ppdFindChoice(color_mode_option, color_mode_option->defchoice);
   }
 
   if (mode_choice) {
@@ -333,8 +332,8 @@
 
   ppd_choice_t* mode_choice = ppdFindMarkedChoice(ppd, kColorMode);
   if (!mode_choice) {
-    mode_choice = ppdFindChoice(color_mode_option,
-                                color_mode_option->defchoice);
+    mode_choice =
+        ppdFindChoice(color_mode_option, color_mode_option->defchoice);
   }
   if (mode_choice) {
     *color_is_default = EqualsCaseInsensitiveASCII(mode_choice->choice, kColor);
@@ -347,7 +346,7 @@
                                   ColorModel* color_model_for_color,
                                   bool* color_is_default) {
   // Canon printers use "ProcessColorModel" attribute in their PPDs.
-  ppd_option_t* color_mode_option =  ppdFindOption(ppd, kProcessColorModel);
+  ppd_option_t* color_mode_option = ppdFindOption(ppd, kProcessColorModel);
   if (!color_mode_option)
     return false;
 
@@ -361,8 +360,8 @@
 
   ppd_choice_t* mode_choice = ppdFindMarkedChoice(ppd, kProcessColorModel);
   if (!mode_choice) {
-    mode_choice = ppdFindChoice(color_mode_option,
-                                color_mode_option->defchoice);
+    mode_choice =
+        ppdFindChoice(color_mode_option, color_mode_option->defchoice);
   }
 
   if (mode_choice) {
@@ -422,7 +421,7 @@
 }
 
 void HttpConnectionCUPS::SetBlocking(bool blocking) {
-  httpBlocking(http_, blocking ?  1 : 0);
+  httpBlocking(http_, blocking ? 1 : 0);
 }
 
 http_t* HttpConnectionCUPS::http() {
@@ -437,10 +436,8 @@
     return false;
 
   int data_size = printer_capabilities.length();
-  if (data_size != base::WriteFile(
-                       ppd_file_path,
-                       printer_capabilities.data(),
-                       data_size)) {
+  if (data_size !=
+      base::WriteFile(ppd_file_path, printer_capabilities.data(), data_size)) {
     base::DeleteFile(ppd_file_path, false);
     return false;
   }
@@ -469,9 +466,9 @@
     VLOG(1) << "Unknown printer color model";
   }
 
-  caps.color_changeable = ((cm_color != UNKNOWN_COLOR_MODEL) &&
-                           (cm_black != UNKNOWN_COLOR_MODEL) &&
-                           (cm_color != cm_black));
+  caps.color_changeable =
+      ((cm_color != UNKNOWN_COLOR_MODEL) && (cm_black != UNKNOWN_COLOR_MODEL) &&
+       (cm_color != cm_black));
   caps.color_default = is_color;
   caps.color_model = cm_color;
   caps.bw_model = cm_black;
diff --git a/printing/backend/cups_ipp_util.cc b/printing/backend/cups_ipp_util.cc
index f75318b9..7c5aab2 100644
--- a/printing/backend/cups_ipp_util.cc
+++ b/printing/backend/cups_ipp_util.cc
@@ -29,8 +29,8 @@
 constexpr char kIppColor[] = CUPS_PRINT_COLOR_MODE;
 constexpr char kIppMedia[] = CUPS_MEDIA;
 constexpr char kIppDuplex[] = CUPS_SIDES;
-constexpr char kIppResolution[] = "printer-resolution";  // RFC 2911
-constexpr char kIppDocumentName[] = "document-name";     // RFC 8011
+constexpr char kIppResolution[] = "printer-resolution";            // RFC 2911
+constexpr char kIppDocumentName[] = "document-name";               // RFC 8011
 constexpr char kIppRequestingUserName[] = "requesting-user-name";  // RFC 8011
 constexpr char kIppPin[] = "job-password";                       // PWG 5100.11
 constexpr char kIppPinEncryption[] = "job-password-encryption";  // PWG 5100.11
diff --git a/printing/backend/cups_jobs.cc b/printing/backend/cups_jobs.cc
index 31f6d75..80d4763 100644
--- a/printing/backend/cups_jobs.cc
+++ b/printing/backend/cups_jobs.cc
@@ -435,7 +435,6 @@
                     const std::string& resource,
                     bool encrypted,
                     PrinterInfo* printer_info) {
-
   ScopedHttpPtr http = ScopedHttpPtr(httpConnect2(
       address.c_str(), port, nullptr, AF_INET,
       encrypted ? HTTP_ENCRYPTION_ALWAYS : HTTP_ENCRYPTION_IF_REQUESTED, 0,
@@ -470,7 +469,6 @@
 bool GetPrinterStatus(http_t* http,
                       const std::string& printer_id,
                       PrinterStatus* printer_status) {
-
   ipp_status_t status;
   const std::string printer_uri = PrinterUriFromName(printer_id);
 
diff --git a/printing/backend/cups_printer.cc b/printing/backend/cups_printer.cc
index 07b91fff..2fe9da8 100644
--- a/printing/backend/cups_printer.cc
+++ b/printing/backend/cups_printer.cc
@@ -108,8 +108,8 @@
   if (state)
     base::StringToInt(state, &printer_info->printer_status);
 
-  const char* drv_info = cupsGetOption(kDriverNameTagName,
-                                       printer->num_options, printer->options);
+  const char* drv_info =
+      cupsGetOption(kDriverNameTagName, printer->num_options, printer->options);
   if (drv_info)
     printer_info->options[kDriverInfoTagName] = *drv_info;
 
@@ -127,9 +127,8 @@
 }
 
 std::string CupsPrinter::GetMakeAndModel() const {
-  const char* make_and_model =
-      cupsGetOption(kDriverNameTagName, destination_->num_options,
-                    destination_->options);
+  const char* make_and_model = cupsGetOption(
+      kDriverNameTagName, destination_->num_options, destination_->options);
 
   return make_and_model ? std::string(make_and_model) : std::string();
 }
diff --git a/printing/backend/print_backend.h b/printing/backend/print_backend.h
index f729a7b..a01e8bd 100644
--- a/printing/backend/print_backend.h
+++ b/printing/backend/print_backend.h
@@ -121,8 +121,7 @@
 #endif  // !defined(OS_CHROMEOS)
 
   // Gets the information about driver for a specific printer.
-  virtual std::string GetPrinterDriverInfo(
-      const std::string& printer_name) = 0;
+  virtual std::string GetPrinterDriverInfo(const std::string& printer_name) = 0;
 
   // Returns true if printer_name points to a valid printer.
   virtual bool IsValidPrinter(const std::string& printer_name) = 0;
diff --git a/printing/backend/print_backend_consts.cc b/printing/backend/print_backend_consts.cc
index 34b6a7e..f843ac0 100644
--- a/printing/backend/print_backend_consts.cc
+++ b/printing/backend/print_backend_consts.cc
@@ -12,6 +12,6 @@
 const char kCUPSPrintServerURL[] = "print_server_url";
 const char kDriverInfoTagName[] = "system_driverinfo";
 const char kDriverNameTagName[] = "printer-make-and-model";  // Match CUPS.
-const char kLocationTagName[] = "printer-location";  // Match CUPS.
+const char kLocationTagName[] = "printer-location";          // Match CUPS.
 const char kValueFalse[] = "false";
 const char kValueTrue[] = "true";
diff --git a/printing/backend/print_backend_cups.cc b/printing/backend/print_backend_cups.cc
index 4f7df1e0..8016c15 100644
--- a/printing/backend/print_backend_cups.cc
+++ b/printing/backend/print_backend_cups.cc
@@ -55,8 +55,8 @@
   if (state)
     base::StringToInt(state, &printer_info->printer_status);
 
-  const char* drv_info = cupsGetOption(kDriverNameTagName,
-                                       printer.num_options, printer.options);
+  const char* drv_info =
+      cupsGetOption(kDriverNameTagName, printer.num_options, printer.options);
   if (drv_info)
     printer_info->options[kDriverInfoTagName] = *drv_info;
 
@@ -75,8 +75,7 @@
                                    bool blocking)
     : print_server_url_(print_server_url),
       cups_encryption_(encryption),
-      blocking_(blocking) {
-}
+      blocking_(blocking) {}
 
 bool PrintBackendCUPS::EnumeratePrinters(PrinterList* printer_list) {
   DCHECK(printer_list);
@@ -132,11 +131,11 @@
     const std::string& printer_name,
     PrinterSemanticCapsAndDefaults* printer_info) {
   PrinterCapsAndDefaults info;
-  if (!GetPrinterCapsAndDefaults(printer_name, &info) )
+  if (!GetPrinterCapsAndDefaults(printer_name, &info))
     return false;
 
-  return ParsePpdCapabilities(
-      printer_name, info.printer_capabilities, printer_info);
+  return ParsePpdCapabilities(printer_name, info.printer_capabilities,
+                              printer_info);
 }
 
 bool PrintBackendCUPS::GetPrinterCapsAndDefaults(
@@ -204,8 +203,7 @@
     print_backend_settings->GetString(kCUPSPrintServerURL,
                                       &print_server_url_str);
 
-    print_backend_settings->GetString(kCUPSBlocking,
-                                      &cups_blocking);
+    print_backend_settings->GetString(kCUPSBlocking, &cups_blocking);
 
     print_backend_settings->GetInteger(kCUPSEncryption, &encryption);
   }
diff --git a/printing/backend/print_backend_dummy.cc b/printing/backend/print_backend_dummy.cc
index 727bdfa..ee1f4c4 100644
--- a/printing/backend/print_backend_dummy.cc
+++ b/printing/backend/print_backend_dummy.cc
@@ -16,16 +16,11 @@
 
 class DummyPrintBackend : public PrintBackend {
  public:
-  DummyPrintBackend() {
-  }
+  DummyPrintBackend() {}
 
-  bool EnumeratePrinters(PrinterList* printer_list) override {
-    return false;
-  }
+  bool EnumeratePrinters(PrinterList* printer_list) override { return false; }
 
-  std::string GetDefaultPrinterName() override {
-    return std::string();
-  }
+  std::string GetDefaultPrinterName() override { return std::string(); }
 
   bool GetPrinterBasicInfo(const std::string& printer_name,
                            PrinterBasicInfo* printer_info) override {
@@ -44,8 +39,7 @@
     return false;
   }
 
-  std::string GetPrinterDriverInfo(
-      const std::string& printer_name) override {
+  std::string GetPrinterDriverInfo(const std::string& printer_name) override {
     return std::string();
   }
 
diff --git a/printing/backend/print_backend_win.cc b/printing/backend/print_backend_win.cc
index 3b74abd8..ff497ad 100644
--- a/printing/backend/print_backend_win.cc
+++ b/printing/backend/print_backend_win.cc
@@ -170,11 +170,9 @@
   bool GetPrinterSemanticCapsAndDefaults(
       const std::string& printer_name,
       PrinterSemanticCapsAndDefaults* printer_info) override;
-  bool GetPrinterCapsAndDefaults(
-      const std::string& printer_name,
-      PrinterCapsAndDefaults* printer_info) override;
-  std::string GetPrinterDriverInfo(
-      const std::string& printer_name) override;
+  bool GetPrinterCapsAndDefaults(const std::string& printer_name,
+                                 PrinterCapsAndDefaults* printer_info) override;
+  std::string GetPrinterDriverInfo(const std::string& printer_name) override;
   bool IsValidPrinter(const std::string& printer_name) override;
 
  protected:
@@ -262,17 +260,17 @@
 
     if (user_settings->dmFields & DM_DUPLEX) {
       switch (user_settings->dmDuplex) {
-      case DMDUP_SIMPLEX:
-        caps.duplex_default = SIMPLEX;
-        break;
-      case DMDUP_VERTICAL:
-        caps.duplex_default = LONG_EDGE;
-        break;
-      case DMDUP_HORIZONTAL:
-        caps.duplex_default = SHORT_EDGE;
-        break;
-      default:
-        NOTREACHED();
+        case DMDUP_SIMPLEX:
+          caps.duplex_default = SIMPLEX;
+          break;
+        case DMDUP_VERTICAL:
+          caps.duplex_default = LONG_EDGE;
+          break;
+        case DMDUP_HORIZONTAL:
+          caps.duplex_default = SHORT_EDGE;
+          break;
+        default:
+          NOTREACHED();
       }
     }
 
diff --git a/printing/backend/printing_info_win.cc b/printing/backend/printing_info_win.cc
index ab878c0..6dd01ef 100644
--- a/printing/backend/printing_info_win.cc
+++ b/printing/backend/printing_info_win.cc
@@ -30,15 +30,15 @@
   DWORD size = 0;
   ::GetPrinter(printer, level, NULL, 0, &size);
   if (size == 0) {
-    LOG(WARNING) << "Failed to get size of PRINTER_INFO_" << level <<
-                    ", error = " << GetLastError();
+    LOG(WARNING) << "Failed to get size of PRINTER_INFO_" << level
+                 << ", error = " << GetLastError();
     return NULL;
   }
   std::unique_ptr<uint8_t[]> buffer(new uint8_t[size]);
   memset(buffer.get(), 0, size);
   if (!::GetPrinter(printer, level, buffer.get(), size, &size)) {
-    LOG(WARNING) << "Failed to get PRINTER_INFO_" << level <<
-                    ", error = " << GetLastError();
+    LOG(WARNING) << "Failed to get PRINTER_INFO_" << level
+                 << ", error = " << GetLastError();
     return NULL;
   }
   return buffer.release();
diff --git a/printing/backend/win_helper.cc b/printing/backend/win_helper.cc
index 57951dc..a8c10f8 100644
--- a/printing/backend/win_helper.cc
+++ b/printing/backend/win_helper.cc
@@ -27,23 +27,23 @@
 
 namespace {
 
-typedef HRESULT (WINAPI* PTOpenProviderProc)(PCWSTR printer_name,
-                                             DWORD version,
-                                             HPTPROVIDER* provider);
+typedef HRESULT(WINAPI* PTOpenProviderProc)(PCWSTR printer_name,
+                                            DWORD version,
+                                            HPTPROVIDER* provider);
 
-typedef HRESULT (WINAPI* PTGetPrintCapabilitiesProc)(HPTPROVIDER provider,
-                                                     IStream* print_ticket,
-                                                     IStream* capabilities,
-                                                     BSTR* error_message);
+typedef HRESULT(WINAPI* PTGetPrintCapabilitiesProc)(HPTPROVIDER provider,
+                                                    IStream* print_ticket,
+                                                    IStream* capabilities,
+                                                    BSTR* error_message);
 
-typedef HRESULT (WINAPI* PTConvertDevModeToPrintTicketProc)(
+typedef HRESULT(WINAPI* PTConvertDevModeToPrintTicketProc)(
     HPTPROVIDER provider,
     ULONG devmode_size_in_bytes,
     PDEVMODE devmode,
     EPrintTicketScope scope,
     IStream* print_ticket);
 
-typedef HRESULT (WINAPI* PTConvertPrintTicketToDevModeProc)(
+typedef HRESULT(WINAPI* PTConvertPrintTicketToDevModeProc)(
     HPTPROVIDER provider,
     IStream* print_ticket,
     EDefaultDevmodeType base_devmode_type,
@@ -52,7 +52,7 @@
     PDEVMODE* devmode,
     BSTR* error_message);
 
-typedef HRESULT (WINAPI* PTMergeAndValidatePrintTicketProc)(
+typedef HRESULT(WINAPI* PTMergeAndValidatePrintTicketProc)(
     HPTPROVIDER provider,
     IStream* base_ticket,
     IStream* delta_ticket,
@@ -60,11 +60,11 @@
     IStream* result_ticket,
     BSTR* error_message);
 
-typedef HRESULT (WINAPI* PTReleaseMemoryProc)(PVOID buffer);
+typedef HRESULT(WINAPI* PTReleaseMemoryProc)(PVOID buffer);
 
-typedef HRESULT (WINAPI* PTCloseProviderProc)(HPTPROVIDER provider);
+typedef HRESULT(WINAPI* PTCloseProviderProc)(HPTPROVIDER provider);
 
-typedef HRESULT (WINAPI* StartXpsPrintJobProc)(
+typedef HRESULT(WINAPI* StartXpsPrintJobProc)(
     const LPCWSTR printer_name,
     const LPCWSTR job_name,
     const LPCWSTR output_file_name,
@@ -107,26 +107,26 @@
 }
 
 const char kXpsTicketTemplate[] =
-  "<?xml version='1.0' encoding='UTF-8'?>"
-  "<psf:PrintTicket "
-  "xmlns:psf='"
-  "http://schemas.microsoft.com/windows/2003/08/printing/printschemaframework' "
-  "xmlns:psk="
-  "'http://schemas.microsoft.com/windows/2003/08/printing/printschemakeywords' "
-  "version='1'>"
-  "<psf:Feature name='psk:PageOutputColor'>"
-  "<psf:Option name='psk:%s'>"
-  "</psf:Option>"
-  "</psf:Feature>"
-  "</psf:PrintTicket>";
+    "<?xml version='1.0' encoding='UTF-8'?>"
+    "<psf:PrintTicket "
+    "xmlns:psf='"
+    "http://schemas.microsoft.com/windows/2003/08/printing/"
+    "printschemaframework' "
+    "xmlns:psk="
+    "'http://schemas.microsoft.com/windows/2003/08/printing/"
+    "printschemakeywords' "
+    "version='1'>"
+    "<psf:Feature name='psk:PageOutputColor'>"
+    "<psf:Option name='psk:%s'>"
+    "</psf:Option>"
+    "</psf:Feature>"
+    "</psf:PrintTicket>";
 
 const char kXpsTicketColor[] = "Color";
 const char kXpsTicketMonochrome[] = "Monochrome";
 
-
 }  // namespace
 
-
 namespace printing {
 
 bool XPSModule::Init() {
@@ -171,16 +171,14 @@
     NOTREACHED();
     return false;
   }
-  g_release_memory_proc =
-      reinterpret_cast<PTReleaseMemoryProc>(
-          GetProcAddress(prntvpt_module, "PTReleaseMemory"));
+  g_release_memory_proc = reinterpret_cast<PTReleaseMemoryProc>(
+      GetProcAddress(prntvpt_module, "PTReleaseMemory"));
   if (!g_release_memory_proc) {
     NOTREACHED();
     return false;
   }
-  g_close_provider_proc =
-      reinterpret_cast<PTCloseProviderProc>(
-          GetProcAddress(prntvpt_module, "PTCloseProvider"));
+  g_close_provider_proc = reinterpret_cast<PTCloseProviderProc>(
+      GetProcAddress(prntvpt_module, "PTCloseProvider"));
   if (!g_close_provider_proc) {
     NOTREACHED();
     return false;
@@ -198,9 +196,7 @@
                                         IStream* print_ticket,
                                         IStream* capabilities,
                                         BSTR* error_message) {
-  return g_get_print_capabilities_proc(provider,
-                                       print_ticket,
-                                       capabilities,
+  return g_get_print_capabilities_proc(provider, print_ticket, capabilities,
                                        error_message);
 }
 
@@ -209,11 +205,8 @@
                                                PDEVMODE devmode,
                                                EPrintTicketScope scope,
                                                IStream* print_ticket) {
-  return g_convert_devmode_to_print_ticket_proc(provider,
-                                                devmode_size_in_bytes,
-                                                devmode,
-                                                scope,
-                                                print_ticket);
+  return g_convert_devmode_to_print_ticket_proc(provider, devmode_size_in_bytes,
+                                                devmode, scope, print_ticket);
 }
 
 HRESULT XPSModule::ConvertPrintTicketToDevMode(
@@ -224,13 +217,9 @@
     ULONG* devmode_byte_count,
     PDEVMODE* devmode,
     BSTR* error_message) {
-  return g_convert_print_ticket_to_devmode_proc(provider,
-                                                print_ticket,
-                                                base_devmode_type,
-                                                scope,
-                                                devmode_byte_count,
-                                                devmode,
-                                                error_message);
+  return g_convert_print_ticket_to_devmode_proc(
+      provider, print_ticket, base_devmode_type, scope, devmode_byte_count,
+      devmode, error_message);
 }
 
 HRESULT XPSModule::MergeAndValidatePrintTicket(HPTPROVIDER provider,
@@ -239,12 +228,8 @@
                                                EPrintTicketScope scope,
                                                IStream* result_ticket,
                                                BSTR* error_message) {
-  return g_merge_and_validate_print_ticket_proc(provider,
-                                                base_ticket,
-                                                delta_ticket,
-                                                scope,
-                                                result_ticket,
-                                                error_message);
+  return g_merge_and_validate_print_ticket_proc(
+      provider, base_ticket, delta_ticket, scope, result_ticket, error_message);
 }
 
 HRESULT XPSModule::ReleaseMemory(PVOID buffer) {
@@ -316,16 +301,10 @@
     IXpsPrintJob** xps_print_job,
     IXpsPrintJobStream** document_stream,
     IXpsPrintJobStream** print_ticket_stream) {
-  return g_start_xps_print_job_proc(printer_name,
-                                    job_name,
-                                    output_file_name,
-                                    progress_event,
-                                    completion_event,
-                                    printable_pages_on,
-                                    printable_pages_on_count,
-                                    xps_print_job,
-                                    document_stream,
-                                    print_ticket_stream);
+  return g_start_xps_print_job_proc(
+      printer_name, job_name, output_file_name, progress_event,
+      completion_event, printable_pages_on, printable_pages_on_count,
+      xps_print_job, document_stream, print_ticket_stream);
 }
 
 bool InitBasicPrinterInfo(HANDLE printer, PrinterBasicInfo* printer_info) {
diff --git a/printing/backend/win_helper.h b/printing/backend/win_helper.h
index f31dc67d..c9f1b95 100644
--- a/printing/backend/win_helper.h
+++ b/printing/backend/win_helper.h
@@ -33,19 +33,15 @@
 
 class PrinterHandleTraits {
  public:
-  typedef HANDLE Handle;
+  using Handle = HANDLE;
 
   static bool CloseHandle(HANDLE handle) {
     return ::ClosePrinter(handle) != FALSE;
   }
 
-  static bool IsHandleValid(HANDLE handle) {
-    return handle != NULL;
-  }
+  static bool IsHandleValid(HANDLE handle) { return handle != NULL; }
 
-  static HANDLE NullHandle() {
-    return NULL;
-  }
+  static HANDLE NullHandle() { return NULL; }
 
  private:
   DISALLOW_IMPLICIT_CONSTRUCTORS(PrinterHandleTraits);
@@ -65,34 +61,30 @@
   }
 
  private:
-  typedef base::win::GenericScopedHandle<PrinterHandleTraits,
-                                         base::win::DummyVerifierTraits> Base;
+  using Base = base::win::GenericScopedHandle<PrinterHandleTraits,
+                                              base::win::DummyVerifierTraits>;
 };
 
 class PrinterChangeHandleTraits {
  public:
-  typedef HANDLE Handle;
+  using Handle = HANDLE;
 
   static bool CloseHandle(HANDLE handle) {
     ::FindClosePrinterChangeNotification(handle);
     return true;
   }
 
-  static bool IsHandleValid(HANDLE handle) {
-    return handle != NULL;
-  }
+  static bool IsHandleValid(HANDLE handle) { return handle != NULL; }
 
-  static HANDLE NullHandle() {
-    return NULL;
-  }
+  static HANDLE NullHandle() { return NULL; }
 
  private:
   DISALLOW_IMPLICIT_CONSTRUCTORS(PrinterChangeHandleTraits);
 };
 
-typedef base::win::GenericScopedHandle<PrinterChangeHandleTraits,
-                                       base::win::DummyVerifierTraits>
-    ScopedPrinterChangeHandle;
+using ScopedPrinterChangeHandle =
+    base::win::GenericScopedHandle<PrinterChangeHandleTraits,
+                                   base::win::DummyVerifierTraits>;
 
 // Wrapper class to wrap the XPS APIs (PTxxx APIs). This is needed because these
 // APIs are not available by default on XP. We could delayload prntvpt.dll but
@@ -134,7 +126,7 @@
   static HRESULT CloseProvider(HPTPROVIDER provider);
 
  private:
-  XPSModule() { }
+  XPSModule() {}
   static bool InitImpl();
 };
 
@@ -160,19 +152,19 @@
   // All the other methods can ONLY be called after a successful call to Init.
   // Init can be called many times and by multiple threads.
   static bool Init();
-  static HRESULT StartXpsPrintJob(
-      const LPCWSTR printer_name,
-      const LPCWSTR job_name,
-      const LPCWSTR output_file_name,
-      HANDLE progress_event,
-      HANDLE completion_event,
-      UINT8* printable_pages_on,
-      UINT32 printable_pages_on_count,
-      IXpsPrintJob **xps_print_job,
-      IXpsPrintJobStream **document_stream,
-      IXpsPrintJobStream **print_ticket_stream);
+  static HRESULT StartXpsPrintJob(const LPCWSTR printer_name,
+                                  const LPCWSTR job_name,
+                                  const LPCWSTR output_file_name,
+                                  HANDLE progress_event,
+                                  HANDLE completion_event,
+                                  UINT8* printable_pages_on,
+                                  UINT32 printable_pages_on_count,
+                                  IXpsPrintJob** xps_print_job,
+                                  IXpsPrintJobStream** document_stream,
+                                  IXpsPrintJobStream** print_ticket_stream);
+
  private:
-  XPSPrintModule() { }
+  XPSPrintModule() {}
   static bool InitImpl();
 };
 
diff --git a/printing/emf_win.cc b/printing/emf_win.cc
index edf6d3c..92b032e 100644
--- a/printing/emf_win.cc
+++ b/printing/emf_win.cc
@@ -25,7 +25,9 @@
 
 namespace {
 
-bool DIBFormatNativelySupported(HDC dc, uint32_t escape, const BYTE* bits,
+bool DIBFormatNativelySupported(HDC dc,
+                                uint32_t escape,
+                                const BYTE* bits,
                                 int size) {
   BOOL supported = FALSE;
   if (ExtEscape(dc, QUERYESCSUPPORT, sizeof(escape),
@@ -114,9 +116,7 @@
   gfx::Rect bound = GetPageBounds(1);
   RECT rect = bound.ToRECT();
   return bound.IsEmpty() ||
-         EnumEnhMetaFile(context,
-                         emf_,
-                         &Emf::SafePlaybackProc,
+         EnumEnhMetaFile(context, emf_, &Emf::SafePlaybackProc,
                          reinterpret_cast<void*>(&playback_context),
                          &rect) != 0;
 }
@@ -131,8 +131,7 @@
   }
   // Add 1 to right and bottom because it's inclusive rectangle.
   // See ENHMETAHEADER.
-  return gfx::Rect(header.rclBounds.left,
-                   header.rclBounds.top,
+  return gfx::Rect(header.rclBounds.left, header.rclBounds.top,
                    header.rclBounds.right - header.rclBounds.left + 1,
                    header.rclBounds.bottom - header.rclBounds.top + 1);
 }
@@ -179,16 +178,13 @@
   memset(this, 0, sizeof(*this));
 }
 
-Emf::Record::Record(const ENHMETARECORD* record)
-    : record_(record) {
+Emf::Record::Record(const ENHMETARECORD* record) : record_(record) {
   DCHECK(record_);
 }
 
 bool Emf::Record::Play(Emf::EnumerationContext* context) const {
-  return 0 != PlayEnhMetaFileRecord(context->hdc,
-                                    context->handle_table,
-                                    record_,
-                                    context->objects_count);
+  return 0 != PlayEnhMetaFileRecord(context->hdc, context->handle_table,
+                                    record_, context->objects_count);
 }
 
 bool Emf::Record::SafePlayback(Emf::EnumerationContext* context) const {
@@ -241,7 +237,7 @@
     case EMR_STRETCHDIBITS: {
       const EMRSTRETCHDIBITS* sdib_record =
           reinterpret_cast<const EMRSTRETCHDIBITS*>(record());
-      const BYTE* record_start = reinterpret_cast<const BYTE *>(record());
+      const BYTE* record_start = reinterpret_cast<const BYTE*>(record());
       const BITMAPINFOHEADER* bmih = reinterpret_cast<const BITMAPINFOHEADER*>(
           record_start + sdib_record->offBmiSrc);
       const BYTE* bits = record_start + sdib_record->offBitsSrc;
@@ -277,15 +273,13 @@
           }
           BITMAPINFOHEADER bmi = {0};
           skia::CreateBitmapHeader(bitmap->width(), bitmap->height(), &bmi);
-          res = (0 != StretchDIBits(hdc, sdib_record->xDest, sdib_record->yDest,
-                                    sdib_record->cxDest,
-                                    sdib_record->cyDest, sdib_record->xSrc,
-                                    sdib_record->ySrc,
-                                    sdib_record->cxSrc, sdib_record->cySrc,
-                                    pixels,
-                                    reinterpret_cast<const BITMAPINFO *>(&bmi),
-                                    sdib_record->iUsageSrc,
-                                    sdib_record->dwRop));
+          res = (0 !=
+                 StretchDIBits(hdc, sdib_record->xDest, sdib_record->yDest,
+                               sdib_record->cxDest, sdib_record->cyDest,
+                               sdib_record->xSrc, sdib_record->ySrc,
+                               sdib_record->cxSrc, sdib_record->cySrc, pixels,
+                               reinterpret_cast<const BITMAPINFO*>(&bmi),
+                               sdib_record->iUsageSrc, sdib_record->dwRop));
         }
       }
       break;
@@ -296,7 +290,7 @@
       HDC hdc = context->hdc;
       if (base_matrix) {
         res = 0 != SetWorldTransform(hdc, base_matrix) &&
-                   ModifyWorldTransform(hdc, xform, MWT_LEFTMULTIPLY);
+              ModifyWorldTransform(hdc, xform, MWT_LEFTMULTIPLY);
       } else {
         res = 0 != SetWorldTransform(hdc, xform);
       }
@@ -323,7 +317,7 @@
         case 4:  // MWT_SET
           if (base_matrix) {
             res = 0 != SetWorldTransform(hdc, base_matrix) &&
-                       ModifyWorldTransform(hdc, xform, MWT_LEFTMULTIPLY);
+                  ModifyWorldTransform(hdc, xform, MWT_LEFTMULTIPLY);
           } else {
             res = 0 != SetWorldTransform(hdc, xform);
           }
@@ -348,8 +342,7 @@
 
 void Emf::StartPage(const gfx::Size& /*page_size*/,
                     const gfx::Rect& /*content_area*/,
-                    const float& /*scale_factor*/) {
-}
+                    const float& /*scale_factor*/) {}
 
 bool Emf::FinishPage() {
   return true;
@@ -357,19 +350,15 @@
 
 Emf::Enumerator::Enumerator(const Emf& emf, HDC context, const RECT* rect) {
   items_.clear();
-  if (!EnumEnhMetaFile(context,
-                       emf.emf(),
-                       &Emf::Enumerator::EnhMetaFileProc,
-                       reinterpret_cast<void*>(this),
-                       rect)) {
+  if (!EnumEnhMetaFile(context, emf.emf(), &Emf::Enumerator::EnhMetaFileProc,
+                       reinterpret_cast<void*>(this), rect)) {
     NOTREACHED();
     items_.clear();
   }
   DCHECK_EQ(context_.hdc, context);
 }
 
-Emf::Enumerator::~Enumerator() {
-}
+Emf::Enumerator::~Enumerator() {}
 
 Emf::Enumerator::const_iterator Emf::Enumerator::begin() const {
   return items_.begin();
diff --git a/printing/emf_win.h b/printing/emf_win.h
index 9bb4232..5245b4d 100644
--- a/printing/emf_win.h
+++ b/printing/emf_win.h
@@ -24,7 +24,7 @@
 namespace gfx {
 class Rect;
 class Size;
-}
+}  // namespace gfx
 
 namespace printing {
 
diff --git a/printing/emf_win_unittest.cc b/printing/emf_win_unittest.cc
index 9372af1..d7395f5 100644
--- a/printing/emf_win_unittest.cc
+++ b/printing/emf_win_unittest.cc
@@ -94,9 +94,9 @@
   base::FilePath emf_file;
   EXPECT_TRUE(base::PathService::Get(base::DIR_SOURCE_ROOT, &emf_file));
   emf_file = emf_file.Append(FILE_PATH_LITERAL("printing"))
-                     .Append(FILE_PATH_LITERAL("test"))
-                     .Append(FILE_PATH_LITERAL("data"))
-                     .Append(FILE_PATH_LITERAL("test4.emf"));
+                 .Append(FILE_PATH_LITERAL("test"))
+                 .Append(FILE_PATH_LITERAL("data"))
+                 .Append(FILE_PATH_LITERAL("test4.emf"));
 
   // Load any EMF with an image.
   std::string emf_data;
@@ -116,15 +116,14 @@
   RECT page_bounds = emf.GetPageBounds(1).ToRECT();
   Emf::Enumerator emf_enum(emf, context.context(), &page_bounds);
   for (Emf::Enumerator::const_iterator itr = emf_enum.begin();
-       itr != emf_enum.end();
-       ++itr) {
+       itr != emf_enum.end(); ++itr) {
     // To help debugging.
     ptrdiff_t index = itr - emf_enum.begin();
     // If you get this assert, you need to lookup iType in wingdi.h. It starts
     // with EMR_HEADER.
     EMR_HEADER;
-    EXPECT_TRUE(itr->SafePlayback(&emf_enum.context_)) <<
-        " index: " << index << " type: " << itr->record()->iType;
+    EXPECT_TRUE(itr->SafePlayback(&emf_enum.context_))
+        << " index: " << index << " type: " << itr->record()->iType;
   }
   context.PageDone();
   context.DocumentDone();
diff --git a/printing/image.cc b/printing/image.cc
index 267346d..d5667fd 100644
--- a/printing/image.cc
+++ b/printing/image.cc
@@ -18,9 +18,7 @@
 
 namespace printing {
 
-Image::Image(const Metafile& metafile)
-    : row_length_(0),
-      ignore_alpha_(true) {
+Image::Image(const Metafile& metafile) : row_length_(0), ignore_alpha_(true) {
   LoadMetafile(metafile);
 }
 
@@ -37,19 +35,14 @@
 bool Image::SaveToPng(const base::FilePath& filepath) const {
   DCHECK(!data_.empty());
   std::vector<unsigned char> compressed;
-  bool success = gfx::PNGCodec::Encode(&*data_.begin(),
-                                       gfx::PNGCodec::FORMAT_BGRA,
-                                       size_,
-                                       row_length_,
-                                       true,
-                                       std::vector<gfx::PNGCodec::Comment>(),
-                                       &compressed);
+  bool success = gfx::PNGCodec::Encode(
+      &*data_.begin(), gfx::PNGCodec::FORMAT_BGRA, size_, row_length_, true,
+      std::vector<gfx::PNGCodec::Comment>(), &compressed);
   DCHECK(success && compressed.size());
   if (success) {
-    int write_bytes = base::WriteFile(
-        filepath,
-        reinterpret_cast<char*>(&*compressed.begin()),
-        base::checked_cast<int>(compressed.size()));
+    int write_bytes =
+        base::WriteFile(filepath, reinterpret_cast<char*>(&*compressed.begin()),
+                        base::checked_cast<int>(compressed.size()));
     success = (write_bytes == static_cast<int>(compressed.size()));
     DCHECK(success);
   }
@@ -57,8 +50,8 @@
 }
 
 double Image::PercentageDifferent(const Image& rhs) const {
-  if (size_.width() == 0 || size_.height() == 0 ||
-    rhs.size_.width() == 0 || rhs.size_.height() == 0)
+  if (size_.width() == 0 || size_.height() == 0 || rhs.size_.width() == 0 ||
+      rhs.size_.height() == 0)
     return 100.;
 
   int width = std::min(size_.width(), rhs.size_.width());
@@ -108,8 +101,8 @@
 
   // Like the WebKit ImageDiff tool, we define percentage different in terms
   // of the size of the 'actual' bitmap.
-  double total_pixels = static_cast<double>(size_.width()) *
-      static_cast<double>(height);
+  double total_pixels =
+      static_cast<double>(size_.width()) * static_cast<double>(height);
   return static_cast<double>(pixels_different) / total_pixels * 100.;
 }
 
diff --git a/printing/image.h b/printing/image.h
index 13ac4d5..86ef53be 100644
--- a/printing/image.h
+++ b/printing/image.h
@@ -35,9 +35,7 @@
 
   ~Image();
 
-  const gfx::Size& size() const {
-    return size_;
-  }
+  const gfx::Size& size() const { return size_; }
 
   // Return a checksum of the image (MD5 over the internal data structure).
   std::string checksum() const;
diff --git a/printing/image_mac.cc b/printing/image_mac.cc
index 1e1b683..11fc4ca 100644
--- a/printing/image_mac.cc
+++ b/printing/image_mac.cc
@@ -29,14 +29,9 @@
   data_.resize(bytes);
   base::ScopedCFTypeRef<CGColorSpaceRef> color_space(
       CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB));
-  base::ScopedCFTypeRef<CGContextRef> bitmap_context(
-      CGBitmapContextCreate(&*data_.begin(),
-                            size_.width(),
-                            size_.height(),
-                            8,
-                            row_length_,
-                            color_space,
-                            kCGImageAlphaPremultipliedLast));
+  base::ScopedCFTypeRef<CGContextRef> bitmap_context(CGBitmapContextCreate(
+      &*data_.begin(), size_.width(), size_.height(), 8, row_length_,
+      color_space, kCGImageAlphaPremultipliedLast));
   DCHECK(bitmap_context.get());
 
   struct Metafile::MacRenderPageParams params;
diff --git a/printing/metafile.h b/printing/metafile.h
index 01be9ab..a5fe9a9 100644
--- a/printing/metafile.h
+++ b/printing/metafile.h
@@ -56,8 +56,7 @@
           stretch_to_fit(false),
           center_horizontally(false),
           center_vertically(false),
-          autorotate(false) {
-    }
+          autorotate(false) {}
 
     bool shrink_to_fit;
     bool stretch_to_fit;
@@ -113,8 +112,7 @@
   // Initializes the metafile with the data in |src_buffer|. Returns true
   // on success.
   // Note: It should only be called from within the browser process.
-  virtual bool InitFromData(const void* src_buffer,
-                            size_t src_buffer_size) = 0;
+  virtual bool InitFromData(const void* src_buffer, size_t src_buffer_size) = 0;
 
   // Prepares a context for rendering a new page with the given |page_size|,
   // |content_area| and a |scale_factor| to use for the drawing. The units are
diff --git a/printing/native_drawing_context.h b/printing/native_drawing_context.h
index b2243f73..e1b288c 100644
--- a/printing/native_drawing_context.h
+++ b/printing/native_drawing_context.h
@@ -23,6 +23,6 @@
 typedef void* NativeDrawingContext;
 #endif
 
-}  // namespace skia
+}  // namespace printing
 
 #endif  // PRINTING_NATIVE_DRAWING_CONTEXT_H_
diff --git a/printing/page_number.cc b/printing/page_number.cc
index 19f025ff..4ec1b21 100644
--- a/printing/page_number.cc
+++ b/printing/page_number.cc
@@ -21,8 +21,7 @@
     : ranges_(NULL),
       page_number_(-1),
       page_range_index_(-1),
-      document_page_count_(0) {
-}
+      document_page_count_(0) {}
 
 void PageNumber::operator=(const PageNumber& other) {
   ranges_ = other.ranges_;
@@ -60,8 +59,8 @@
     ++page_number_;
     // Page ranges are inclusive.
     if (page_number_ > (*ranges_)[page_range_index_].to) {
-      DCHECK(ranges_->size() <= static_cast<size_t>(
-          std::numeric_limits<int>::max()));
+      DCHECK(ranges_->size() <=
+             static_cast<size_t>(std::numeric_limits<int>::max()));
       if (++page_range_index_ == static_cast<int>(ranges_->size())) {
         // Finished.
         *this = npos();
diff --git a/printing/page_number.h b/printing/page_number.h
index a51ad1e..9f566ae 100644
--- a/printing/page_number.h
+++ b/printing/page_number.h
@@ -29,17 +29,13 @@
   void Init(const PrintSettings& settings, int document_page_count);
 
   // Converts to a page numbers.
-  int ToInt() const {
-    return page_number_;
-  }
+  int ToInt() const { return page_number_; }
 
   // Calculates the next page in the serie.
   int operator++();
 
   // Returns an instance that represents the end of a serie.
-  static const PageNumber npos() {
-    return PageNumber();
-  }
+  static const PageNumber npos() { return PageNumber(); }
 
   // Equality operator. Only the current page number is verified so that
   // "page != PageNumber::npos()" works.
@@ -62,9 +58,10 @@
 };
 
 // Debug output support.
-template<class E, class T>
-inline typename std::basic_ostream<E,T>& operator<<(
-    typename std::basic_ostream<E,T>& ss, const PageNumber& page) {
+template <class E, class T>
+inline typename std::basic_ostream<E, T>& operator<<(
+    typename std::basic_ostream<E, T>& ss,
+    const PageNumber& page) {
   return ss << page.ToInt();
 }
 
diff --git a/printing/page_setup.cc b/printing/page_setup.cc
index 3a856545..605cebf 100644
--- a/printing/page_setup.cc
+++ b/printing/page_setup.cc
@@ -34,13 +34,7 @@
 }  // namespace
 
 PageMargins::PageMargins()
-    : header(0),
-      footer(0),
-      left(0),
-      right(0),
-      top(0),
-      bottom(0) {
-}
+    : header(0), footer(0), left(0), right(0), top(0), bottom(0) {}
 
 void PageMargins::Clear() {
   header = 0;
@@ -52,12 +46,8 @@
 }
 
 bool PageMargins::Equals(const PageMargins& rhs) const {
-  return header == rhs.header &&
-      footer == rhs.footer &&
-      left == rhs.left &&
-      top == rhs.top &&
-      right == rhs.right &&
-      bottom == rhs.bottom;
+  return header == rhs.header && footer == rhs.footer && left == rhs.left &&
+         top == rhs.top && right == rhs.right && bottom == rhs.bottom;
 }
 
 PageSetup::PageSetup() {
@@ -100,12 +90,12 @@
 
 bool PageSetup::Equals(const PageSetup& rhs) const {
   return physical_size_ == rhs.physical_size_ &&
-      printable_area_ == rhs.printable_area_ &&
-      overlay_area_ == rhs.overlay_area_ &&
-      content_area_ == rhs.content_area_ &&
-      effective_margins_.Equals(rhs.effective_margins_) &&
-      requested_margins_.Equals(rhs.requested_margins_) &&
-      text_height_ == rhs.text_height_;
+         printable_area_ == rhs.printable_area_ &&
+         overlay_area_ == rhs.overlay_area_ &&
+         content_area_ == rhs.content_area_ &&
+         effective_margins_.Equals(rhs.effective_margins_) &&
+         requested_margins_.Equals(rhs.requested_margins_) &&
+         text_height_ == rhs.text_height_;
 }
 
 void PageSetup::Init(const gfx::Size& physical_size,
@@ -140,8 +130,7 @@
     gfx::Size new_size(physical_size_.height(), physical_size_.width());
     int new_y = physical_size_.width() -
                 (printable_area_.width() + printable_area_.x());
-    gfx::Rect new_printable_area(printable_area_.y(),
-                                 new_y,
+    gfx::Rect new_printable_area(printable_area_.y(), new_y,
                                  printable_area_.height(),
                                  printable_area_.width());
     Init(new_size, new_printable_area, text_height_);
@@ -162,19 +151,15 @@
 void PageSetup::CalculateSizesWithinRect(const gfx::Rect& bounds,
                                          int text_height) {
   // Calculate the effective margins. The tricky part.
-  effective_margins_.header = std::max(requested_margins_.header,
-                                       bounds.y());
-  effective_margins_.footer = std::max(requested_margins_.footer,
-                                       physical_size_.height() -
-                                           bounds.bottom());
-  effective_margins_.left = std::max(requested_margins_.left,
-                                     bounds.x());
-  effective_margins_.top = std::max(std::max(requested_margins_.top,
-                                             bounds.y()),
-                                    effective_margins_.header + text_height);
+  effective_margins_.header = std::max(requested_margins_.header, bounds.y());
+  effective_margins_.footer = std::max(
+      requested_margins_.footer, physical_size_.height() - bounds.bottom());
+  effective_margins_.left = std::max(requested_margins_.left, bounds.x());
+  effective_margins_.top =
+      std::max(std::max(requested_margins_.top, bounds.y()),
+               effective_margins_.header + text_height);
   effective_margins_.right = std::max(requested_margins_.right,
-                                      physical_size_.width() -
-                                          bounds.right());
+                                      physical_size_.width() - bounds.right());
   effective_margins_.bottom =
       std::max(std::max(requested_margins_.bottom,
                         physical_size_.height() - bounds.bottom()),
@@ -184,27 +169,23 @@
   // size will be (0, 0).
   overlay_area_.set_x(effective_margins_.left);
   overlay_area_.set_y(effective_margins_.header);
-  overlay_area_.set_width(std::max(0,
-                                   physical_size_.width() -
-                                       effective_margins_.right -
-                                       overlay_area_.x()));
-  overlay_area_.set_height(std::max(0,
-                                    physical_size_.height() -
-                                        effective_margins_.footer -
-                                        overlay_area_.y()));
+  overlay_area_.set_width(std::max(
+      0,
+      physical_size_.width() - effective_margins_.right - overlay_area_.x()));
+  overlay_area_.set_height(std::max(
+      0,
+      physical_size_.height() - effective_margins_.footer - overlay_area_.y()));
 
   // Calculate the content area. If the margins are excessive, the content_area
   // size will be (0, 0).
   content_area_.set_x(effective_margins_.left);
   content_area_.set_y(effective_margins_.top);
-  content_area_.set_width(std::max(0,
-                                   physical_size_.width() -
-                                       effective_margins_.right -
-                                       content_area_.x()));
-  content_area_.set_height(std::max(0,
-                                    physical_size_.height() -
-                                        effective_margins_.bottom -
-                                        content_area_.y()));
+  content_area_.set_width(std::max(
+      0,
+      physical_size_.width() - effective_margins_.right - content_area_.x()));
+  content_area_.set_height(std::max(
+      0,
+      physical_size_.height() - effective_margins_.bottom - content_area_.y()));
 }
 
 }  // namespace printing
diff --git a/printing/page_setup.h b/printing/page_setup.h
index f02ee056..4c3fa7ec 100644
--- a/printing/page_setup.h
+++ b/printing/page_setup.h
@@ -48,7 +48,8 @@
   // Equality operator.
   bool Equals(const PageSetup& rhs) const;
 
-  void Init(const gfx::Size& physical_size, const gfx::Rect& printable_area,
+  void Init(const gfx::Size& physical_size,
+            const gfx::Rect& printable_area,
             int text_height);
 
   // Use |requested_margins| as long as they fall inside the printable area.
@@ -64,9 +65,7 @@
   const gfx::Rect& overlay_area() const { return overlay_area_; }
   const gfx::Rect& content_area() const { return content_area_; }
   const gfx::Rect& printable_area() const { return printable_area_; }
-  const PageMargins& effective_margins() const {
-    return effective_margins_;
-  }
+  const PageMargins& effective_margins() const { return effective_margins_; }
 
  private:
   // Store |requested_margins_| and update page setup values.
diff --git a/printing/page_setup_unittest.cc b/printing/page_setup_unittest.cc
index 83b1d83..bb0399d 100644
--- a/printing/page_setup_unittest.cc
+++ b/printing/page_setup_unittest.cc
@@ -45,60 +45,55 @@
   PageMargins effective_margins;
   effective_margins.header = std::max(margins.header, printable_area.y());
   effective_margins.left = std::max(margins.left, printable_area.x());
-  effective_margins.top = std::max(margins.top,
-                                   effective_margins.header + kTextHeight);
-  effective_margins.footer = std::max(margins.footer,
-                                      page_size.height() -
-                                          printable_area.bottom());
-  effective_margins.right = std::max(margins.right,
-                                      page_size.width() -
-                                          printable_area.right());
-  effective_margins.bottom = std::max(margins.bottom,
-                                      effective_margins.footer  + kTextHeight);
+  effective_margins.top =
+      std::max(margins.top, effective_margins.header + kTextHeight);
+  effective_margins.footer =
+      std::max(margins.footer, page_size.height() - printable_area.bottom());
+  effective_margins.right =
+      std::max(margins.right, page_size.width() - printable_area.right());
+  effective_margins.bottom =
+      std::max(margins.bottom, effective_margins.footer + kTextHeight);
 
   // Calculate the overlay area.
-  gfx::Rect overlay_area(effective_margins.left, effective_margins.header,
-                         page_size.width() - effective_margins.right -
-                            effective_margins.left,
-                         page_size.height() - effective_margins.footer -
-                            effective_margins.header);
+  gfx::Rect overlay_area(
+      effective_margins.left, effective_margins.header,
+      page_size.width() - effective_margins.right - effective_margins.left,
+      page_size.height() - effective_margins.footer - effective_margins.header);
 
   // Calculate the content area.
-  gfx::Rect content_area(overlay_area.x(),
-                         effective_margins.top,
-                         overlay_area.width(),
-                         page_size.height() - effective_margins.bottom -
-                             effective_margins.top);
+  gfx::Rect content_area(
+      overlay_area.x(), effective_margins.top, overlay_area.width(),
+      page_size.height() - effective_margins.bottom - effective_margins.top);
 
   // Test values.
-  EXPECT_EQ(page_size, setup.physical_size()) << seed << " " <<
-      page_size.ToString() << " " << printable_area.ToString() <<
-      " " << kTextHeight;
-  EXPECT_EQ(overlay_area, setup.overlay_area()) << seed << " " <<
-      page_size.ToString() << " " << printable_area.ToString() <<
-      " " << kTextHeight;
-  EXPECT_EQ(content_area, setup.content_area()) << seed << " " <<
-      page_size.ToString() << " " << printable_area.ToString() <<
-      " " << kTextHeight;
+  EXPECT_EQ(page_size, setup.physical_size())
+      << seed << " " << page_size.ToString() << " " << printable_area.ToString()
+      << " " << kTextHeight;
+  EXPECT_EQ(overlay_area, setup.overlay_area())
+      << seed << " " << page_size.ToString() << " " << printable_area.ToString()
+      << " " << kTextHeight;
+  EXPECT_EQ(content_area, setup.content_area())
+      << seed << " " << page_size.ToString() << " " << printable_area.ToString()
+      << " " << kTextHeight;
 
-  EXPECT_EQ(effective_margins.header, setup.effective_margins().header) <<
-      seed << " " << page_size.ToString() << " " << printable_area.ToString() <<
-      " " << kTextHeight;
-  EXPECT_EQ(effective_margins.footer, setup.effective_margins().footer) <<
-      seed << " " << page_size.ToString() << " " << printable_area.ToString() <<
-      " " << kTextHeight;
-  EXPECT_EQ(effective_margins.left, setup.effective_margins().left) << seed <<
-      " " << page_size.ToString() << " " << printable_area.ToString() <<
-      " " << kTextHeight;
-  EXPECT_EQ(effective_margins.top, setup.effective_margins().top) << seed <<
-      " " << page_size.ToString() << " " << printable_area.ToString() <<
-      " " << kTextHeight;
-  EXPECT_EQ(effective_margins.right, setup.effective_margins().right) << seed <<
-      " " << page_size.ToString() << " " << printable_area.ToString() <<
-      " " << kTextHeight;
-  EXPECT_EQ(effective_margins.bottom, setup.effective_margins().bottom) <<
-      seed << " " << page_size.ToString() << " " << printable_area.ToString() <<
-       " " << kTextHeight;
+  EXPECT_EQ(effective_margins.header, setup.effective_margins().header)
+      << seed << " " << page_size.ToString() << " " << printable_area.ToString()
+      << " " << kTextHeight;
+  EXPECT_EQ(effective_margins.footer, setup.effective_margins().footer)
+      << seed << " " << page_size.ToString() << " " << printable_area.ToString()
+      << " " << kTextHeight;
+  EXPECT_EQ(effective_margins.left, setup.effective_margins().left)
+      << seed << " " << page_size.ToString() << " " << printable_area.ToString()
+      << " " << kTextHeight;
+  EXPECT_EQ(effective_margins.top, setup.effective_margins().top)
+      << seed << " " << page_size.ToString() << " " << printable_area.ToString()
+      << " " << kTextHeight;
+  EXPECT_EQ(effective_margins.right, setup.effective_margins().right)
+      << seed << " " << page_size.ToString() << " " << printable_area.ToString()
+      << " " << kTextHeight;
+  EXPECT_EQ(effective_margins.bottom, setup.effective_margins().bottom)
+      << seed << " " << page_size.ToString() << " " << printable_area.ToString()
+      << " " << kTextHeight;
 }
 
 TEST(PageSetupTest, HardCoded) {
@@ -137,33 +132,34 @@
   gfx::Rect content_area(4, 6, 92, 88);
 
   // Test values.
-  EXPECT_EQ(page_size, setup.physical_size()) << " " << page_size.ToString() <<
-      " " << printable_area.ToString() << " " << kTextHeight;
-  EXPECT_EQ(overlay_area, setup.overlay_area()) << " " <<
-      page_size.ToString() <<  " " << printable_area.ToString() <<
-      " " << kTextHeight;
-  EXPECT_EQ(content_area, setup.content_area()) << " " <<
-      page_size.ToString() <<  " " << printable_area.ToString() <<
-      " " << kTextHeight;
+  EXPECT_EQ(page_size, setup.physical_size())
+      << " " << page_size.ToString() << " " << printable_area.ToString() << " "
+      << kTextHeight;
+  EXPECT_EQ(overlay_area, setup.overlay_area())
+      << " " << page_size.ToString() << " " << printable_area.ToString() << " "
+      << kTextHeight;
+  EXPECT_EQ(content_area, setup.content_area())
+      << " " << page_size.ToString() << " " << printable_area.ToString() << " "
+      << kTextHeight;
 
-  EXPECT_EQ(effective_margins.header, setup.effective_margins().header) <<
-      " " << page_size.ToString() << " " <<
-      printable_area.ToString() << " " << kTextHeight;
-  EXPECT_EQ(effective_margins.footer, setup.effective_margins().footer) <<
-      " " << page_size.ToString() << " " << printable_area.ToString() <<
-      " " << kTextHeight;
-  EXPECT_EQ(effective_margins.left, setup.effective_margins().left) <<
-      " " << page_size.ToString() << " " << printable_area.ToString() <<
-      " " << kTextHeight;
-  EXPECT_EQ(effective_margins.top, setup.effective_margins().top) <<
-      " " << page_size.ToString() << " " << printable_area.ToString() <<
-      " " << kTextHeight;
-  EXPECT_EQ(effective_margins.right, setup.effective_margins().right) <<
-      " " << page_size.ToString() << " " << printable_area.ToString() <<
-      " " << kTextHeight;
-  EXPECT_EQ(effective_margins.bottom, setup.effective_margins().bottom) <<
-      " " << page_size.ToString() << " " << printable_area.ToString() <<
-      " " << kTextHeight;
+  EXPECT_EQ(effective_margins.header, setup.effective_margins().header)
+      << " " << page_size.ToString() << " " << printable_area.ToString() << " "
+      << kTextHeight;
+  EXPECT_EQ(effective_margins.footer, setup.effective_margins().footer)
+      << " " << page_size.ToString() << " " << printable_area.ToString() << " "
+      << kTextHeight;
+  EXPECT_EQ(effective_margins.left, setup.effective_margins().left)
+      << " " << page_size.ToString() << " " << printable_area.ToString() << " "
+      << kTextHeight;
+  EXPECT_EQ(effective_margins.top, setup.effective_margins().top)
+      << " " << page_size.ToString() << " " << printable_area.ToString() << " "
+      << kTextHeight;
+  EXPECT_EQ(effective_margins.right, setup.effective_margins().right)
+      << " " << page_size.ToString() << " " << printable_area.ToString() << " "
+      << kTextHeight;
+  EXPECT_EQ(effective_margins.bottom, setup.effective_margins().bottom)
+      << " " << page_size.ToString() << " " << printable_area.ToString() << " "
+      << kTextHeight;
 }
 
 TEST(PageSetupTest, OutOfRangeMargins) {
diff --git a/printing/pdf_metafile_cg_mac.cc b/printing/pdf_metafile_cg_mac.cc
index 265eb3a..d9d62135 100644
--- a/printing/pdf_metafile_cg_mac.cc
+++ b/printing/pdf_metafile_cg_mac.cc
@@ -176,7 +176,7 @@
   CGRect source_rect = CGPDFPageGetBoxRect(pdf_page, kCGPDFCropBox);
   int pdf_src_rotation = CGPDFPageGetRotationAngle(pdf_page);
   const bool source_is_landscape =
-        (source_rect.size.width > source_rect.size.height);
+      (source_rect.size.width > source_rect.size.height);
   const bool dest_is_landscape = (rect.size.width > rect.size.height);
   const bool rotate =
       params.autorotate ? (source_is_landscape != dest_is_landscape) : false;
@@ -203,13 +203,17 @@
   const float y_origin_offset = -1 * source_rect.origin.y;
 
   // If the PDF needs to be centered, calculate the offsets here.
-  float x_offset = params.center_horizontally ?
-      ((rect.size.width - (source_width * scaling_factor)) / 2) : 0;
+  float x_offset =
+      params.center_horizontally
+          ? ((rect.size.width - (source_width * scaling_factor)) / 2)
+          : 0;
   if (rotate)
     x_offset = -x_offset;
 
-  float y_offset = params.center_vertically ?
-      ((rect.size.height - (source_height * scaling_factor)) / 2) : 0;
+  float y_offset =
+      params.center_vertically
+          ? ((rect.size.height - (source_height * scaling_factor)) / 2)
+          : 0;
 
   CGContextSaveGState(context);
 
diff --git a/printing/print_job_constants.cc b/printing/print_job_constants.cc
index 427a530..5817cb0 100644
--- a/printing/print_job_constants.cc
+++ b/printing/print_job_constants.cc
@@ -213,7 +213,7 @@
 // Whether to show PDF in view provided by OS. Implemented for MacOS only.
 const char kSettingOpenPDFInPreview[] = "OpenPDFInPreview";
 
-#if defined (USE_CUPS)
+#if defined(USE_CUPS)
 const char kBlack[] = "Black";
 const char kCMYK[] = "CMYK";
 const char kKCMY[] = "KCMY";
diff --git a/printing/print_job_constants.h b/printing/print_job_constants.h
index 4e601a99..017b6b8 100644
--- a/printing/print_job_constants.h
+++ b/printing/print_job_constants.h
@@ -81,7 +81,7 @@
 PRINTING_EXPORT extern const int COMPLETE_PREVIEW_DOCUMENT_INDEX;
 PRINTING_EXPORT extern const char kSettingOpenPDFInPreview[];
 
-#if defined (USE_CUPS)
+#if defined(USE_CUPS)
 // Printer color models
 PRINTING_EXPORT extern const char kBlack[];
 PRINTING_EXPORT extern const char kCMYK[];
@@ -111,17 +111,10 @@
 };
 
 // Specifies the horizontal alignment of the headers and footers.
-enum HorizontalHeaderFooterPosition {
-  LEFT,
-  CENTER,
-  RIGHT
-};
+enum HorizontalHeaderFooterPosition { LEFT, CENTER, RIGHT };
 
 // Specifies the vertical alignment of the Headers and Footers.
-enum VerticalHeaderFooterPosition {
-  TOP,
-  BOTTOM
-};
+enum VerticalHeaderFooterPosition { TOP, BOTTOM };
 
 // Print job color mode values.
 enum ColorModel {
diff --git a/printing/print_settings.cc b/printing/print_settings.cc
index 03a6b5e..bc22112 100644
--- a/printing/print_settings.cc
+++ b/printing/print_settings.cc
@@ -23,8 +23,9 @@
 }
 
 #if defined(USE_CUPS)
-void GetColorModelForMode(
-    int color_mode, std::string* color_setting_name, std::string* color_value) {
+void GetColorModelForMode(int color_mode,
+                          std::string* color_setting_name,
+                          std::string* color_value) {
 #if defined(OS_MACOSX)
   constexpr char kCUPSColorMode[] = "ColorMode";
   constexpr char kCUPSColorModel[] = "ColorModel";
@@ -246,22 +247,16 @@
     case CUSTOM_MARGINS: {
       margins.header = 0;
       margins.footer = 0;
-      margins.top = ConvertUnitDouble(
-          requested_custom_margins_in_points_.top,
-          kPointsPerInch,
-          units_per_inch);
-      margins.bottom = ConvertUnitDouble(
-          requested_custom_margins_in_points_.bottom,
-          kPointsPerInch,
-          units_per_inch);
-      margins.left = ConvertUnitDouble(
-          requested_custom_margins_in_points_.left,
-          kPointsPerInch,
-          units_per_inch);
-      margins.right = ConvertUnitDouble(
-          requested_custom_margins_in_points_.right,
-          kPointsPerInch,
-          units_per_inch);
+      margins.top = ConvertUnitDouble(requested_custom_margins_in_points_.top,
+                                      kPointsPerInch, units_per_inch);
+      margins.bottom =
+          ConvertUnitDouble(requested_custom_margins_in_points_.bottom,
+                            kPointsPerInch, units_per_inch);
+      margins.left = ConvertUnitDouble(requested_custom_margins_in_points_.left,
+                                       kPointsPerInch, units_per_inch);
+      margins.right =
+          ConvertUnitDouble(requested_custom_margins_in_points_.right,
+                            kPointsPerInch, units_per_inch);
       break;
     }
     default: {
diff --git a/printing/print_settings.h b/printing/print_settings.h
index 6a8c17a..7ac2275 100644
--- a/printing/print_settings.h
+++ b/printing/print_settings.h
@@ -82,9 +82,7 @@
   }
   // Media properties requested by the user. Translated into device media by the
   // platform specific layers.
-  const RequestedMedia& requested_media() const {
-    return requested_media_;
-  }
+  const RequestedMedia& requested_media() const { return requested_media_; }
 
   // Set printer printable area in in device units.
   // Some platforms already provide flipped area. Set |landscape_needs_flip|
@@ -126,7 +124,7 @@
   int device_units_per_inch() const {
 #if defined(OS_MACOSX)
     return 72;
-#else  // defined(OS_MACOSX)
+#else   // defined(OS_MACOSX)
     return dpi();
 #endif  // defined(OS_MACOSX)
   }
@@ -175,7 +173,7 @@
   bool printer_is_textonly() const {
     return printer_type_ == PrinterType::TYPE_TEXTONLY;
   }
-  bool printer_is_xps() const { return printer_type_ == PrinterType::TYPE_XPS;}
+  bool printer_is_xps() const { return printer_type_ == PrinterType::TYPE_XPS; }
   bool printer_is_ps2() const {
     return printer_type_ == PrinterType::TYPE_POSTSCRIPT_LEVEL2;
   }
diff --git a/printing/print_settings_conversion.h b/printing/print_settings_conversion.h
index 07b6b03..678440da 100644
--- a/printing/print_settings_conversion.h
+++ b/printing/print_settings_conversion.h
@@ -12,7 +12,7 @@
 namespace base {
 class DictionaryValue;
 class Value;
-}
+}  // namespace base
 
 namespace printing {
 
diff --git a/printing/print_settings_initializer_mac.cc b/printing/print_settings_initializer_mac.cc
index dcb9c97..065645a 100644
--- a/printing/print_settings_initializer_mac.cc
+++ b/printing/print_settings_initializer_mac.cc
@@ -25,9 +25,9 @@
   print_settings->SetOrientation(orientation == kPMLandscape);
 
   UInt32 resolution_count = 0;
-  PMResolution best_resolution = { 72.0, 72.0 };
-  OSStatus status = PMPrinterGetPrinterResolutionCount(printer,
-                                                       &resolution_count);
+  PMResolution best_resolution = {72.0, 72.0};
+  OSStatus status =
+      PMPrinterGetPrinterResolutionCount(printer, &resolution_count);
   if (status == noErr) {
     // Resolution indexes are 1-based.
     for (uint32_t i = 1; i <= resolution_count; ++i) {
@@ -48,19 +48,15 @@
   PMGetAdjustedPaperRect(page_format, &paper_rect);
 
   // Device units are in points. Units per inch is 72.
-  gfx::Size physical_size_device_units(
-      (paper_rect.right - paper_rect.left),
-      (paper_rect.bottom - paper_rect.top));
+  gfx::Size physical_size_device_units((paper_rect.right - paper_rect.left),
+                                       (paper_rect.bottom - paper_rect.top));
   gfx::Rect printable_area_device_units(
-      (page_rect.left - paper_rect.left),
-      (page_rect.top - paper_rect.top),
-      (page_rect.right - page_rect.left),
-      (page_rect.bottom - page_rect.top));
+      (page_rect.left - paper_rect.left), (page_rect.top - paper_rect.top),
+      (page_rect.right - page_rect.left), (page_rect.bottom - page_rect.top));
 
   DCHECK_EQ(print_settings->device_units_per_inch(), kPointsPerInch);
   print_settings->SetPrinterPrintableArea(physical_size_device_units,
-                                          printable_area_device_units,
-                                          false);
+                                          printable_area_device_units, false);
 }
 
 }  // namespace printing
diff --git a/printing/print_settings_initializer_win.cc b/printing/print_settings_initializer_win.cc
index 88b4eb0..f844882 100644
--- a/printing/print_settings_initializer_win.cc
+++ b/printing/print_settings_initializer_win.cc
@@ -112,7 +112,7 @@
   print_settings->set_dpi_xy(dpi_x, dpi_y);
   const int kAlphaCaps = SB_CONST_ALPHA | SB_PIXEL_ALPHA;
   print_settings->set_supports_alpha_blend(
-    (GetDeviceCaps(hdc, SHADEBLENDCAPS) & kAlphaCaps) == kAlphaCaps);
+      (GetDeviceCaps(hdc, SHADEBLENDCAPS) & kAlphaCaps) == kAlphaCaps);
 
   DCHECK_EQ(GetDeviceCaps(hdc, SCALINGFACTORX), 0);
   DCHECK_EQ(GetDeviceCaps(hdc, SCALINGFACTORY), 0);
@@ -134,14 +134,13 @@
   // Sanity check the printable_area: we've seen crashes caused by a printable
   // area rect of 0, 0, 0, 0, so it seems some drivers don't set it.
   if (printable_area_device_units.IsEmpty() ||
-      !gfx::Rect(physical_size_device_units).Contains(
-          printable_area_device_units)) {
+      !gfx::Rect(physical_size_device_units)
+           .Contains(printable_area_device_units)) {
     printable_area_device_units = gfx::Rect(physical_size_device_units);
   }
   DCHECK_EQ(print_settings->device_units_per_inch(), dpi);
   print_settings->SetPrinterPrintableArea(physical_size_device_units,
-                                          printable_area_device_units,
-                                          false);
+                                          printable_area_device_units, false);
 
   print_settings->set_color(IsDevModeWithColor(&dev_mode) ? COLOR : GRAY);
 
diff --git a/printing/printed_document.cc b/printing/printed_document.cc
index f1f61fd..2f695880a 100644
--- a/printing/printed_document.cc
+++ b/printing/printed_document.cc
@@ -81,8 +81,7 @@
       PrintedDocument::CreateDebugDumpPath(doc_name, extension);
   if (path.empty())
     return;
-  base::WriteFile(path,
-                  reinterpret_cast<const char*>(data->front()),
+  base::WriteFile(path, reinterpret_cast<const char*>(data->front()),
                   base::checked_cast<int>(data->size()));
 }
 
diff --git a/printing/printed_document_win.cc b/printing/printed_document_win.cc
index 19bfb45..e083520 100644
--- a/printing/printed_document_win.cc
+++ b/printing/printed_document_win.cc
@@ -16,7 +16,7 @@
                                 int offset_x,
                                 int offset_y,
                                 float shrink_factor) {
-  XFORM xform = { 0 };
+  XFORM xform = {0};
   xform.eDx = static_cast<float>(offset_x);
   xform.eDy = static_cast<float>(offset_y);
   xform.eM11 = xform.eM22 = 1.f / shrink_factor;
@@ -60,8 +60,7 @@
     // Note that the printing output is relative to printable area of the page.
     // That is 0,0 is offset by PHYSICALOFFSETX/Y from the page.
     SimpleModifyWorldTransform(
-        context,
-        content_area.x() - page_setup.printable_area().x(),
+        context, content_area.x() - page_setup.printable_area().x(),
         content_area.y() - page_setup.printable_area().y(),
         page.shrink_factor());
 
diff --git a/printing/printing_context.cc b/printing/printing_context.cc
index 6423238a..78e3c3b2 100644
--- a/printing/printing_context.cc
+++ b/printing/printing_context.cc
@@ -18,9 +18,7 @@
 }
 
 PrintingContext::PrintingContext(Delegate* delegate)
-    : delegate_(delegate),
-      in_print_job_(false),
-      abort_printing_(false) {
+    : delegate_(delegate), in_print_job_(false), abort_printing_(false) {
   DCHECK(delegate_);
 }
 
diff --git a/printing/printing_context.h b/printing/printing_context.h
index a5879b58..9ccc1a66 100644
--- a/printing/printing_context.h
+++ b/printing/printing_context.h
@@ -125,9 +125,7 @@
   void set_margin_type(MarginType type);
   void set_is_modifiable(bool is_modifiable);
 
-  const PrintSettings& settings() const {
-    return settings_;
-  }
+  const PrintSettings& settings() const { return settings_; }
 
   int job_id() const { return job_id_; }
 
diff --git a/printing/printing_context_android.cc b/printing/printing_context_android.cc
index a381582..ec40ab6 100644
--- a/printing/printing_context_android.cc
+++ b/printing/printing_context_android.cc
@@ -40,8 +40,7 @@
 
   settings->set_dpi(dpi);
   settings->SetPrinterPrintableArea(physical_size_device_units,
-                                    printable_area_device_units,
-                                    false);
+                                    printable_area_device_units, false);
 }
 
 void GetPageRanges(JNIEnv* env,
@@ -86,8 +85,7 @@
   // The constructor is run in the IO thread.
 }
 
-PrintingContextAndroid::~PrintingContextAndroid() {
-}
+PrintingContextAndroid::~PrintingContextAndroid() {}
 
 void PrintingContextAndroid::AskUserForSettings(
     int max_pages,
@@ -99,9 +97,8 @@
 
   JNIEnv* env = base::android::AttachCurrentThread();
   if (j_printing_context_.is_null()) {
-    j_printing_context_.Reset(Java_PrintingContext_create(
-        env,
-        reinterpret_cast<intptr_t>(this)));
+    j_printing_context_.Reset(
+        Java_PrintingContext_create(env, reinterpret_cast<intptr_t>(this)));
   }
 
   if (is_scripted) {
@@ -181,16 +178,16 @@
   int32_t width = 0;
   int32_t height = 0;
   UErrorCode error = U_ZERO_ERROR;
-  ulocdata_getPaperSize(
-      delegate_->GetAppLocale().c_str(), &height, &width, &error);
+  ulocdata_getPaperSize(delegate_->GetAppLocale().c_str(), &height, &width,
+                        &error);
   if (error > U_ZERO_ERROR) {
     // If the call failed, assume a paper size of 8.5 x 11 inches.
     LOG(WARNING) << "ulocdata_getPaperSize failed, using 8.5 x 11, error: "
                  << error;
-    width = static_cast<int>(
-        kLetterWidthInch * settings_.device_units_per_inch());
-    height = static_cast<int>(
-        kLetterHeightInch  * settings_.device_units_per_inch());
+    width =
+        static_cast<int>(kLetterWidthInch * settings_.device_units_per_inch());
+    height =
+        static_cast<int>(kLetterHeightInch * settings_.device_units_per_inch());
   } else {
     // ulocdata_getPaperSize returns the width and height in mm.
     // Convert this to pixels based on the dpi.
diff --git a/printing/printing_context_linux.h b/printing/printing_context_linux.h
index 1dd5085..2ceb3be 100644
--- a/printing/printing_context_linux.h
+++ b/printing/printing_context_linux.h
@@ -22,9 +22,8 @@
   ~PrintingContextLinux() override;
 
   // Sets the function that creates the print dialog.
-  static void SetCreatePrintDialogFunction(
-      PrintDialogGtkInterface* (*create_dialog_func)(
-          PrintingContextLinux* context));
+  static void SetCreatePrintDialogFunction(PrintDialogGtkInterface* (
+      *create_dialog_func)(PrintingContextLinux* context));
 
   // Sets the function that returns pdf paper size through the native API.
   static void SetPdfPaperSizeFunction(
diff --git a/printing/printing_context_mac.mm b/printing/printing_context_mac.mm
index bd1a197..dc1694d 100644
--- a/printing/printing_context_mac.mm
+++ b/printing/printing_context_mac.mm
@@ -42,7 +42,7 @@
   PMPaper best_matching_paper = NULL;
   int num_papers = CFArrayGetCount(paper_list);
   for (int i = 0; i < num_papers; ++i) {
-    PMPaper paper = (PMPaper)[(NSArray*)paper_list objectAtIndex : i];
+    PMPaper paper = (PMPaper)[(NSArray*)paper_list objectAtIndex:i];
     double paper_width = 0.0;
     double paper_height = 0.0;
     PMPaperGetWidth(paper, &paper_width);
@@ -75,8 +75,7 @@
 PrintingContextMac::PrintingContextMac(Delegate* delegate)
     : PrintingContext(delegate),
       print_info_([[NSPrintInfo sharedPrintInfo] copy]),
-      context_(NULL) {
-}
+      context_(NULL) {}
 
 PrintingContextMac::~PrintingContextMac() {
   ReleaseContext();
@@ -153,9 +152,8 @@
   PMGetAdjustedPaperRect(page_format, &paper_rect);
 
   // Device units are in points. Units per inch is 72.
-  gfx::Size physical_size_device_units(
-      (paper_rect.right - paper_rect.left),
-      (paper_rect.bottom - paper_rect.top));
+  gfx::Size physical_size_device_units((paper_rect.right - paper_rect.left),
+                                       (paper_rect.bottom - paper_rect.top));
   DCHECK(settings_.device_units_per_inch() == kPointsPerInch);
   return physical_size_device_units;
 }
@@ -211,9 +209,8 @@
       static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
   PMPrintSettings print_settings =
       static_cast<PMPrintSettings>([print_info_.get() PMPrintSettings]);
-  return PMSessionSetDestination(
-      print_session, print_settings, kPMDestinationPreview,
-      NULL, NULL) == noErr;
+  return PMSessionSetDestination(print_session, print_settings,
+                                 kPMDestinationPreview, NULL, NULL) == noErr;
 }
 
 void PrintingContextMac::InitPrintSettingsFromPrintInfo() {
@@ -223,8 +220,8 @@
       static_cast<PMPageFormat>([print_info_.get() PMPageFormat]);
   PMPrinter printer;
   PMSessionGetCurrentPrinter(print_session, &printer);
-  PrintSettingsInitializerMac::InitPrintSettings(
-      printer, page_format, &settings_);
+  PrintSettingsInitializerMac::InitPrintSettings(printer, page_format,
+                                                 &settings_);
 }
 
 bool PrintingContextMac::SetPrinter(const std::string& device_name) {
@@ -246,7 +243,7 @@
     return false;
 
   if (CFStringCompare(new_printer_id.get(), current_printer_id, 0) ==
-          kCFCompareEqualTo) {
+      kCFCompareEqualTo) {
     return true;
   }
 
@@ -312,13 +309,9 @@
     return true;
 
   PMPaper paper = NULL;
-  if (PMPaperCreateCustom(current_printer,
-                          CFSTR("Custom paper ID"),
-                          CFSTR("Custom paper"),
-                          page_width,
-                          page_height,
-                          &margins,
-                          &paper) != noErr) {
+  if (PMPaperCreateCustom(current_printer, CFSTR("Custom paper ID"),
+                          CFSTR("Custom paper"), page_width, page_height,
+                          &margins, &paper) != noErr) {
     return false;
   }
   bool result = UpdatePageFormatWithPaper(paper, default_page_format);
@@ -403,10 +396,8 @@
   base::ScopedCFTypeRef<CFStringRef> output_color(
       base::SysUTF8ToCFStringRef(color_value));
 
-  return PMPrintSettingsSetValue(pmPrintSettings,
-                                 color_setting.get(),
-                                 output_color.get(),
-                                 false) == noErr;
+  return PMPrintSettingsSetValue(pmPrintSettings, color_setting.get(),
+                                 output_color.get(), false) == noErr;
 }
 
 PageRanges PrintingContextMac::GetPageRangesFromPrintInfo() {
@@ -438,9 +429,8 @@
       base::SysUTF16ToCFStringRef(document_name));
   PMPrintSettingsSetJobName(print_settings, job_title.get());
 
-  OSStatus status = PMSessionBeginCGDocumentNoDialog(print_session,
-                                                     print_settings,
-                                                     page_format);
+  OSStatus status = PMSessionBeginCGDocumentNoDialog(
+      print_session, print_settings, page_format);
   if (status != noErr)
     return OnError();
 
diff --git a/printing/printing_context_no_system_dialog.cc b/printing/printing_context_no_system_dialog.cc
index 6679fd70..65d9bd2 100644
--- a/printing/printing_context_no_system_dialog.cc
+++ b/printing/printing_context_no_system_dialog.cc
@@ -25,8 +25,7 @@
 #endif  // !defined(USE_CUPS)
 
 PrintingContextNoSystemDialog::PrintingContextNoSystemDialog(Delegate* delegate)
-    : PrintingContext(delegate) {
-}
+    : PrintingContext(delegate) {}
 
 PrintingContextNoSystemDialog::~PrintingContextNoSystemDialog() {
   ReleaseContext();
@@ -58,16 +57,16 @@
   int32_t width = 0;
   int32_t height = 0;
   UErrorCode error = U_ZERO_ERROR;
-  ulocdata_getPaperSize(
-      delegate_->GetAppLocale().c_str(), &height, &width, &error);
+  ulocdata_getPaperSize(delegate_->GetAppLocale().c_str(), &height, &width,
+                        &error);
   if (error > U_ZERO_ERROR) {
     // If the call failed, assume a paper size of 8.5 x 11 inches.
     LOG(WARNING) << "ulocdata_getPaperSize failed, using 8.5 x 11, error: "
                  << error;
-    width = static_cast<int>(
-        kLetterWidthInch * settings_.device_units_per_inch());
-    height = static_cast<int>(
-        kLetterHeightInch  * settings_.device_units_per_inch());
+    width =
+        static_cast<int>(kLetterWidthInch * settings_.device_units_per_inch());
+    height =
+        static_cast<int>(kLetterHeightInch * settings_.device_units_per_inch());
   } else {
     // ulocdata_getPaperSize returns the width and height in mm.
     // Convert this to pixels based on the dpi.
@@ -142,4 +141,3 @@
 }
 
 }  // namespace printing
-
diff --git a/printing/printing_context_system_dialog_win.cc b/printing/printing_context_system_dialog_win.cc
index f146351..1c2d08a7 100644
--- a/printing/printing_context_system_dialog_win.cc
+++ b/printing/printing_context_system_dialog_win.cc
@@ -135,8 +135,8 @@
   settings_.set_ranges(ranges_vector);
   settings_.set_device_name(new_device_name);
   settings_.set_selection_only(selection_only);
-  PrintSettingsInitializerWin::InitPrintSettings(
-      context(), dev_mode, &settings_);
+  PrintSettingsInitializerWin::InitPrintSettings(context(), dev_mode,
+                                                 &settings_);
 
   return true;
 }
diff --git a/printing/printing_context_system_dialog_win.h b/printing/printing_context_system_dialog_win.h
index f402e1b..7781cfd55 100644
--- a/printing/printing_context_system_dialog_win.h
+++ b/printing/printing_context_system_dialog_win.h
@@ -5,8 +5,9 @@
 #ifndef PRINTING_PRINTING_CONTEXT_SYSTEM_DIALOG_WIN_H_
 #define PRINTING_PRINTING_CONTEXT_SYSTEM_DIALOG_WIN_H_
 
-#include <ocidl.h>
-#include <commdlg.h>
+#include <ocidl.h>  // NOLINT(build/include_order)
+
+#include <commdlg.h>  // Must come after ocidl.h.
 
 #include <string>
 
diff --git a/printing/printing_context_win.cc b/printing/printing_context_win.cc
index d0b9503..eee0f77 100644
--- a/printing/printing_context_win.cc
+++ b/printing/printing_context_win.cc
@@ -137,9 +137,8 @@
         break;
     }
   }
-  return gfx::Size(
-      paper_size.width() * settings_.device_units_per_inch(),
-      paper_size.height() * settings_.device_units_per_inch());
+  return gfx::Size(paper_size.width() * settings_.device_units_per_inch(),
+                   paper_size.height() * settings_.device_units_per_inch());
 }
 
 PrintingContext::Result PrintingContextWin::UpdatePrinterSettings(
@@ -166,8 +165,8 @@
     dev_mode->dmCopies = std::max(settings_.copies(), 1);
     if (dev_mode->dmCopies > 1) {  // do not change unless multiple copies
       dev_mode->dmFields |= DM_COPIES;
-      dev_mode->dmCollate = settings_.collate() ? DMCOLLATE_TRUE :
-                                                  DMCOLLATE_FALSE;
+      dev_mode->dmCollate =
+          settings_.collate() ? DMCOLLATE_TRUE : DMCOLLATE_FALSE;
     }
 
     switch (settings_.duplex_mode()) {
@@ -188,8 +187,8 @@
     }
 
     dev_mode->dmFields |= DM_ORIENTATION;
-    dev_mode->dmOrientation = settings_.landscape() ? DMORIENT_LANDSCAPE :
-                                                      DMORIENT_PORTRAIT;
+    dev_mode->dmOrientation =
+        settings_.landscape() ? DMORIENT_LANDSCAPE : DMORIENT_PORTRAIT;
 
     if (settings_.dpi_horizontal() > 0) {
       dev_mode->dmPrintQuality = settings_.dpi_horizontal();
@@ -261,7 +260,7 @@
     return OnError();
 
   DCHECK(SimplifyDocumentTitle(document_name) == document_name);
-  DOCINFO di = { sizeof(DOCINFO) };
+  DOCINFO di = {sizeof(DOCINFO)};
   di.lpszDocName = document_name.c_str();
 
   // Is there a debug dump directory specified? If so, force to print to a file.
@@ -365,8 +364,8 @@
 
   DCHECK(!in_print_job_);
   settings_.set_device_name(device_name);
-  PrintSettingsInitializerWin::InitPrintSettings(
-      context_, *dev_mode, &settings_);
+  PrintSettingsInitializerWin::InitPrintSettings(context_, *dev_mode,
+                                                 &settings_);
 
   return OK;
 }
diff --git a/printing/printing_context_win_unittest.cc b/printing/printing_context_win_unittest.cc
index 00e9ada..1868b9ea 100644
--- a/printing/printing_context_win_unittest.cc
+++ b/printing/printing_context_win_unittest.cc
@@ -183,7 +183,7 @@
 
   // The print may lie to use and may not support world transformation.
   // Verify right now.
-  XFORM random_matrix = { 1, 0.1f, 0, 1.5f, 0, 1 };
+  XFORM random_matrix = {1, 0.1f, 0, 1.5f, 0, 1};
   EXPECT_TRUE(SetWorldTransform(context.context(), &random_matrix));
   EXPECT_TRUE(ModifyWorldTransform(context.context(), nullptr, MWT_IDENTITY));
 }
diff --git a/printing/printing_test.h b/printing/printing_test.h
index df391958..0839204 100644
--- a/printing/printing_test.h
+++ b/printing/printing_test.h
@@ -15,12 +15,10 @@
 // Disable the whole test case when executing on a computer that has no printer
 // installed.
 // Note: Parent should be testing::Test or InProcessBrowserTest.
-template<typename Parent>
+template <typename Parent>
 class PrintingTest : public Parent {
  public:
-  static bool IsTestCaseDisabled() {
-    return GetDefaultPrinter().empty();
-  }
+  static bool IsTestCaseDisabled() { return GetDefaultPrinter().empty(); }
   static std::wstring GetDefaultPrinter() {
     wchar_t printer_name[MAX_PATH];
     DWORD size = base::size(printer_name);
diff --git a/printing/units.h b/printing/units.h
index 88aea52..7b83577a 100644
--- a/printing/units.h
+++ b/printing/units.h
@@ -47,7 +47,8 @@
 PRINTING_EXPORT int ConvertUnit(double value, int old_unit, int new_unit);
 
 // Converts from one unit system to another using doubles.
-PRINTING_EXPORT double ConvertUnitDouble(double value, double old_unit,
+PRINTING_EXPORT double ConvertUnitDouble(double value,
+                                         double old_unit,
                                          double new_unit);
 
 // Converts from 1 pixel to 1 point using integers.
diff --git a/printing/units_unittest.cc b/printing/units_unittest.cc
index f651895..f43dabd 100644
--- a/printing/units_unittest.cc
+++ b/printing/units_unittest.cc
@@ -3,8 +3,8 @@
 // found in the LICENSE file.
 
 #include "printing/units.h"
-#include "testing/gtest/include/gtest/gtest.h"
 #include "testing/gtest/include/gtest/gtest-spi.h"
+#include "testing/gtest/include/gtest/gtest.h"
 
 namespace printing {
 
diff --git a/remoting/base/BUILD.gn b/remoting/base/BUILD.gn
index d6d3ba7..f3d95104 100644
--- a/remoting/base/BUILD.gn
+++ b/remoting/base/BUILD.gn
@@ -111,6 +111,10 @@
     "//google_apis",
     "//net",
   ]
+
+  deps = [
+    "//remoting/proto/remoting/v1:directory_grpc_library",
+  ]
 }
 
 source_set("breakpad") {
diff --git a/remoting/base/oauth_token_exchanger.cc b/remoting/base/oauth_token_exchanger.cc
index 10f6575..d4c0429 100644
--- a/remoting/base/oauth_token_exchanger.cc
+++ b/remoting/base/oauth_token_exchanger.cc
@@ -11,7 +11,14 @@
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
 #include "google_apis/gaia/gaia_oauth_client.h"
+#include "google_apis/google_api_keys.h"
+#include "remoting/base/grpc_support/grpc_async_executor.h"
+#include "remoting/base/grpc_support/grpc_async_unary_request.h"
+#include "remoting/base/grpc_support/grpc_channel.h"
+#include "remoting/base/service_urls.h"
+#include "remoting/proto/remoting/v1/directory_service.grpc.pb.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "third_party/grpc/src/include/grpcpp/security/credentials.h"
 
 namespace remoting {
 
@@ -38,10 +45,56 @@
 
 }  // namespace
 
+class OAuthTokenExchanger::DirectoryServiceClient {
+ public:
+  using UpdateRobotTokenCallback =
+      base::OnceCallback<void(const grpc::Status&,
+                              const apis::v1::UpdateRobotTokenResponse&)>;
+
+  DirectoryServiceClient();
+  ~DirectoryServiceClient();
+
+  void UpdateRobotToken(const std::string& access_token,
+                        UpdateRobotTokenCallback callback);
+
+ private:
+  using RemotingDirectoryService = apis::v1::RemotingDirectoryService;
+
+  GrpcAsyncExecutor grpc_executor_;
+  std::unique_ptr<RemotingDirectoryService::Stub> stub_;
+};
+
+OAuthTokenExchanger::DirectoryServiceClient::DirectoryServiceClient() {
+  GrpcChannelSharedPtr channel = CreateSslChannelForEndpoint(
+      ServiceUrls::GetInstance()->remoting_server_endpoint());
+  stub_ = RemotingDirectoryService::NewStub(channel);
+}
+
+OAuthTokenExchanger::DirectoryServiceClient::~DirectoryServiceClient() =
+    default;
+
+void OAuthTokenExchanger::DirectoryServiceClient::UpdateRobotToken(
+    const std::string& access_token,
+    UpdateRobotTokenCallback callback) {
+  auto update_robot_token_request = apis::v1::UpdateRobotTokenRequest();
+  update_robot_token_request.set_client_id(
+      google_apis::GetOAuth2ClientID(google_apis::CLIENT_REMOTING_HOST));
+  update_robot_token_request.set_offline(false);
+
+  auto async_request = CreateGrpcAsyncUnaryRequest(
+      base::BindOnce(&RemotingDirectoryService::Stub::AsyncUpdateRobotToken,
+                     base::Unretained(stub_.get())),
+      update_robot_token_request, std::move(callback));
+  async_request->context()->set_credentials(
+      grpc::AccessTokenCredentials(access_token));
+  grpc_executor_.ExecuteRpc(std::move(async_request));
+}
+
 OAuthTokenExchanger::OAuthTokenExchanger(
     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
     : gaia_oauth_client_(std::make_unique<gaia::GaiaOAuthClient>(
-          std::move(url_loader_factory))) {}
+          std::move(url_loader_factory))),
+      directory_service_client_(std::make_unique<DirectoryServiceClient>()) {}
 
 OAuthTokenExchanger::~OAuthTokenExchanger() = default;
 
@@ -64,6 +117,14 @@
   NotifyCallbacks(OAuthTokenGetter::SUCCESS, oauth_access_token_);
 }
 
+void OAuthTokenExchanger::OnGetTokensResponse(const std::string& refresh_token,
+                                              const std::string& access_token,
+                                              int expires_in_seconds) {
+  // |expires_in_seconds| is unused - the exchanged token is assumed to be
+  // valid for at least as long as the original access token.
+  NotifyCallbacks(OAuthTokenGetter::SUCCESS, access_token);
+}
+
 void OAuthTokenExchanger::OnGetTokenInfoResponse(
     std::unique_ptr<base::DictionaryValue> token_info) {
   base::Value* scopes_value = token_info->FindKey(TOKENINFO_SCOPE_KEY);
@@ -105,8 +166,37 @@
 }
 
 void OAuthTokenExchanger::RequestNewToken() {
-  NOTIMPLEMENTED() << "Token exchange not yet implemented.";
-  NotifyCallbacks(OAuthTokenGetter::SUCCESS, oauth_access_token_);
+  directory_service_client_->UpdateRobotToken(
+      oauth_access_token_,
+      base::BindOnce(&OAuthTokenExchanger::OnRobotTokenResponse,
+                     base::Unretained(this)));
+}
+
+void OAuthTokenExchanger::OnRobotTokenResponse(
+    const grpc::Status& status,
+    const apis::v1::UpdateRobotTokenResponse& response) {
+  if (!status.ok()) {
+    LOG(ERROR) << "Received error code: " << status.error_code()
+               << ", message: " << status.error_message();
+    NotifyCallbacks(OAuthTokenGetter::AUTH_ERROR, std::string());
+    return;
+  }
+
+  if (!response.has_auth_code()) {
+    LOG(ERROR) << "Received response without auth_code.";
+    NotifyCallbacks(OAuthTokenGetter::AUTH_ERROR, std::string());
+    return;
+  }
+
+  // The redirect_uri parameter is required for GetTokensFromAuthCode(), but
+  // "oob" (out of band) can be used for robot accounts.
+  gaia::OAuthClientInfo client_info = {
+      google_apis::GetOAuth2ClientID(google_apis::CLIENT_REMOTING_HOST),
+      google_apis::GetOAuth2ClientSecret(google_apis::CLIENT_REMOTING_HOST),
+      "oob"};
+
+  gaia_oauth_client_->GetTokensFromAuthCode(client_info, response.auth_code(),
+                                            kMaxRetries, this);
 }
 
 }  // namespace remoting
diff --git a/remoting/base/oauth_token_exchanger.h b/remoting/base/oauth_token_exchanger.h
index 194c360..18cf54d17 100644
--- a/remoting/base/oauth_token_exchanger.h
+++ b/remoting/base/oauth_token_exchanger.h
@@ -16,8 +16,18 @@
 #include "google_apis/gaia/gaia_oauth_client.h"
 #include "remoting/base/oauth_token_getter.h"
 
+namespace grpc {
+class Status;
+}  // namespace grpc
+
 namespace remoting {
 
+namespace apis {
+namespace v1 {
+class UpdateRobotTokenResponse;
+}  // namespace v1
+}  // namespace apis
+
 class OAuthTokenExchanger : public gaia::GaiaOAuthClient::Delegate {
  public:
   typedef base::OnceCallback<void(OAuthTokenGetter::Status status,
@@ -32,15 +42,22 @@
                      TokenCallback on_new_token);
 
   // gaia::GaiaOAuthClient::Delegate interface.
+  void OnGetTokensResponse(const std::string& refresh_token,
+                           const std::string& access_token,
+                           int expires_in_seconds) override;
   void OnGetTokenInfoResponse(
       std::unique_ptr<base::DictionaryValue> token_info) override;
   void OnOAuthError() override;
   void OnNetworkError(int response_code) override;
 
  private:
+  class DirectoryServiceClient;
+
   void NotifyCallbacks(OAuthTokenGetter::Status status,
                        const std::string& access_token);
   void RequestNewToken();
+  void OnRobotTokenResponse(const grpc::Status& status,
+                            const apis::v1::UpdateRobotTokenResponse& response);
 
   std::unique_ptr<gaia::GaiaOAuthClient> gaia_oauth_client_;
   base::queue<TokenCallback> pending_callbacks_;
@@ -52,6 +69,9 @@
   // Unset if the scopes are unknown and the tokeninfo endpoint needs to be
   // queried.
   base::Optional<bool> need_token_exchange_;
+
+  std::unique_ptr<DirectoryServiceClient> directory_service_client_;
+
   DISALLOW_COPY_AND_ASSIGN(OAuthTokenExchanger);
 };
 
diff --git a/remoting/proto/remoting/v1/directory_messages.proto b/remoting/proto/remoting/v1/directory_messages.proto
index 560c76f6..68404a0 100644
--- a/remoting/proto/remoting/v1/directory_messages.proto
+++ b/remoting/proto/remoting/v1/directory_messages.proto
@@ -98,7 +98,12 @@
 
 // Requests an auth_code with updated OAuth scopes.  Should only be called by
 // robot accounts assigned to a Me2me host.
-message UpdateRobotTokenRequest {}
+message UpdateRobotTokenRequest {
+  // OAuth client ID of the requesting host.
+  optional string client_id = 1;
+  // Whether to request offline access.
+  optional bool offline = 2;
+}
 
 // Response to a UpdateRobotTokenRequest.
 message UpdateRobotTokenResponse {
diff --git a/services/BUILD.gn b/services/BUILD.gn
index 0f33ba75..773980e 100644
--- a/services/BUILD.gn
+++ b/services/BUILD.gn
@@ -48,7 +48,6 @@
     deps += [
       "//services/ws/input_devices:tests",
       "//services/ws/public/cpp/tests",
-      "//services/ws/public/mojom:tests",
     ]
   }
 
diff --git a/services/media_session/audio_focus_manager.cc b/services/media_session/audio_focus_manager.cc
index 5b334c2f..413782c 100644
--- a/services/media_session/audio_focus_manager.cc
+++ b/services/media_session/audio_focus_manager.cc
@@ -299,24 +299,9 @@
   if (audio_focus_stack_.empty())
     return;
 
-  if (audio_focus_stack_.back()->id() != id) {
-    RemoveFocusEntryIfPresent(id);
-    MaybeUpdateActiveSession();
-    return;
-  }
+  bool was_top_most_session = audio_focus_stack_.back()->id() == id;
 
-  auto row = std::move(audio_focus_stack_.back());
-  audio_focus_stack_.pop_back();
-
-  if (audio_focus_stack_.empty()) {
-    // Notify observers that we lost audio focus.
-    observers_.ForAllPtrs([&row](mojom::AudioFocusObserver* observer) {
-      observer->OnFocusLost(row->ToAudioFocusRequestState());
-    });
-
-    MaybeUpdateActiveSession();
-    return;
-  }
+  auto row = RemoveFocusEntryIfPresent(id);
 
   EnforceAudioFocus();
   MaybeUpdateActiveSession();
@@ -326,6 +311,9 @@
     observer->OnFocusLost(row->ToAudioFocusRequestState());
   });
 
+  if (!was_top_most_session || audio_focus_stack_.empty())
+    return;
+
   // Notify observers that the session on top gained focus.
   StackRow* new_session = audio_focus_stack_.back().get();
   observers_.ForAllPtrs([&new_session](mojom::AudioFocusObserver* observer) {
diff --git a/services/media_session/audio_focus_manager_unittest.cc b/services/media_session/audio_focus_manager_unittest.cc
index 1231095..518b937 100644
--- a/services/media_session/audio_focus_manager_unittest.cc
+++ b/services/media_session/audio_focus_manager_unittest.cc
@@ -1457,4 +1457,30 @@
             GetState(&media_session_1));
 }
 
+TEST_P(AudioFocusManagerTest, AudioFocusObserver_NotTopMost) {
+  test::MockMediaSession media_session_1;
+  test::MockMediaSession media_session_2;
+
+  AudioFocusManager::RequestId request_id_1 =
+      RequestAudioFocus(&media_session_1, mojom::AudioFocusType::kGain);
+  EXPECT_EQ(request_id_1, GetAudioFocusedSession());
+  EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kActive,
+            GetState(&media_session_1));
+
+  RequestAudioFocus(&media_session_2,
+                    mojom::AudioFocusType::kGainTransientMayDuck);
+  EXPECT_EQ(GetStateFromParam(mojom::MediaSessionInfo::SessionState::kDucking),
+            GetState(&media_session_1));
+  EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kActive,
+            GetState(&media_session_2));
+
+  {
+    std::unique_ptr<test::TestAudioFocusObserver> observer = CreateObserver();
+    media_session_1.AbandonAudioFocusFromClient();
+
+    EXPECT_TRUE(observer->focus_lost_session()->session_info.Equals(
+        test::GetMediaSessionInfoSync(&media_session_1)));
+  }
+}
+
 }  // namespace media_session
diff --git a/services/network/throttling/throttling_controller.cc b/services/network/throttling/throttling_controller.cc
index 06176d94..79a3829 100644
--- a/services/network/throttling/throttling_controller.cc
+++ b/services/network/throttling/throttling_controller.cc
@@ -14,7 +14,9 @@
 ThrottlingController* ThrottlingController::instance_ = nullptr;
 
 ThrottlingController::ThrottlingController() = default;
-ThrottlingController::~ThrottlingController() = default;
+ThrottlingController::~ThrottlingController() {
+  liveness_ = Liveness::kDead;
+}
 
 // static
 void ThrottlingController::SetConditions(
@@ -57,6 +59,7 @@
   // Null |instance_| means there is no network condition registered.
   if (!instance_)
     return false;
+  instance_->CheckValidThread();
   return instance_->interceptors_.find(throttling_profile_id) !=
          instance_->interceptors_.end();
 }
@@ -64,20 +67,20 @@
 void ThrottlingController::Register(
     uint32_t net_log_source_id,
     const base::UnguessableToken& throttling_profile_id) {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  CheckValidThread();
   if (interceptors_.find(throttling_profile_id) == interceptors_.end())
     return;
   net_log_source_profile_map_[net_log_source_id] = throttling_profile_id;
 }
 
 void ThrottlingController::Unregister(uint32_t net_log_source_id) {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  CheckValidThread();
   net_log_source_profile_map_.erase(net_log_source_id);
 }
 
 base::Optional<base::UnguessableToken> ThrottlingController::GetProfileID(
     uint32_t net_log_source_id) {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  CheckValidThread();
   auto it = net_log_source_profile_map_.find(net_log_source_id);
   if (it == net_log_source_profile_map_.end())
     return base::nullopt;
@@ -87,7 +90,7 @@
 void ThrottlingController::SetNetworkConditions(
     const base::UnguessableToken& throttling_profile_id,
     std::unique_ptr<NetworkConditions> conditions) {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  CheckValidThread();
 
   auto it = interceptors_.find(throttling_profile_id);
   if (it == interceptors_.end()) {
@@ -113,9 +116,28 @@
   }
 }
 
+static NOINLINE void CrashBecauseThrottlingControllerDeleted() {
+  LOG(ERROR) << "deleted";
+  CHECK(false);
+}
+
+static NOINLINE void CrashBecauseThrottlingControllerBad() {
+  LOG(ERROR) << "bad";
+  CHECK(false);
+}
+
 ThrottlingNetworkInterceptor* ThrottlingController::FindInterceptor(
     uint32_t net_log_source_id) {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  CheckValidThread();
+
+  if (liveness_ == Liveness::kDead) {
+    CrashBecauseThrottlingControllerDeleted();
+  } else if (liveness_ != Liveness::kAlive) {
+    Liveness liveness = liveness_;
+    base::debug::Alias(&liveness);
+    CrashBecauseThrottlingControllerBad();
+  }
+
   auto source_profile_map_it =
       net_log_source_profile_map_.find(net_log_source_id);
   if (source_profile_map_it == net_log_source_profile_map_.end())
@@ -124,4 +146,8 @@
   return it != interceptors_.end() ? it->second.get() : nullptr;
 }
 
+void ThrottlingController::CheckValidThread() {
+  CHECK(thread_checker_.CalledOnValidThread());
+}
+
 }  // namespace network
diff --git a/services/network/throttling/throttling_controller.h b/services/network/throttling/throttling_controller.h
index f304242..43751c4 100644
--- a/services/network/throttling/throttling_controller.h
+++ b/services/network/throttling/throttling_controller.h
@@ -36,6 +36,13 @@
  private:
   friend class ScopedThrottlingToken;
 
+  // TODO(https://crbug.com/960874): Debugging code to try and shed some light
+  // on why the owned maps are invalid.
+  enum class Liveness : int32_t {
+    kAlive = 0xCA11AB13,
+    kDead = 0xDEADBEEF,
+  };
+
   ThrottlingController();
   ~ThrottlingController();
 
@@ -65,6 +72,9 @@
 
   ThrottlingNetworkInterceptor* FindInterceptor(uint32_t net_log_source_id);
 
+  // TODO(https://crbug.com/960874): Debugging code.
+  void CheckValidThread();
+
   static ThrottlingController* instance_;
 
   using InterceptorMap =
@@ -74,9 +84,11 @@
       std::map<uint32_t /* net_log_source_id */,
                base::UnguessableToken /* throttling_profile_id */>;
 
+  // TODO(https://crbug.com/960874): Debugging code.
+  Liveness liveness_ = Liveness::kAlive;
   InterceptorMap interceptors_;
   NetLogSourceProfileMap net_log_source_profile_map_;
-  THREAD_CHECKER(thread_checker_);
+  base::ThreadCheckerImpl thread_checker_;
 
   DISALLOW_COPY_AND_ASSIGN(ThrottlingController);
 };
diff --git a/services/ws/public/cpp/gpu/client_gpu_memory_buffer_manager.cc b/services/ws/public/cpp/gpu/client_gpu_memory_buffer_manager.cc
index 1b0f0128..499c2a35 100644
--- a/services/ws/public/cpp/gpu/client_gpu_memory_buffer_manager.cc
+++ b/services/ws/public/cpp/gpu/client_gpu_memory_buffer_manager.cc
@@ -16,7 +16,6 @@
 #include "mojo/public/cpp/system/buffer.h"
 #include "mojo/public/cpp/system/platform_handle.h"
 #include "services/service_manager/public/cpp/connector.h"
-#include "services/ws/public/mojom/constants.mojom.h"
 #include "ui/gfx/buffer_format_util.h"
 
 namespace ws {
diff --git a/services/ws/public/cpp/gpu/gpu.cc b/services/ws/public/cpp/gpu/gpu.cc
index 942dbeb..0a55112 100644
--- a/services/ws/public/cpp/gpu/gpu.cc
+++ b/services/ws/public/cpp/gpu/gpu.cc
@@ -19,7 +19,6 @@
 #include "services/service_manager/public/cpp/connector.h"
 #include "services/ws/public/cpp/gpu/client_gpu_memory_buffer_manager.h"
 #include "services/ws/public/cpp/gpu/context_provider_command_buffer.h"
-#include "services/ws/public/mojom/constants.mojom.h"
 #include "services/ws/public/mojom/gpu.mojom.h"
 
 namespace ws {
diff --git a/services/ws/public/cpp/input_devices/input_device_controller_client.cc b/services/ws/public/cpp/input_devices/input_device_controller_client.cc
index ccbd655..4decb45c 100644
--- a/services/ws/public/cpp/input_devices/input_device_controller_client.cc
+++ b/services/ws/public/cpp/input_devices/input_device_controller_client.cc
@@ -8,7 +8,6 @@
 
 #include "base/bind_helpers.h"
 #include "services/service_manager/public/cpp/connector.h"
-#include "services/ws/public/mojom/constants.mojom.h"
 
 namespace ws {
 
@@ -16,9 +15,8 @@
     service_manager::Connector* connector,
     const std::string& service_name)
     : binding_(this) {
-  connector->BindInterface(
-      service_name.empty() ? mojom::kServiceName : service_name,
-      &input_device_controller_);
+  DCHECK(!service_name.empty());
+  connector->BindInterface(service_name, &input_device_controller_);
   mojom::KeyboardDeviceObserverPtr ptr;
   binding_.Bind(mojo::MakeRequest(&ptr));
   input_device_controller_->AddKeyboardDeviceObserver(std::move(ptr));
diff --git a/services/ws/public/mojom/BUILD.gn b/services/ws/public/mojom/BUILD.gn
index 504d20d..3eb3b00 100644
--- a/services/ws/public/mojom/BUILD.gn
+++ b/services/ws/public/mojom/BUILD.gn
@@ -7,14 +7,8 @@
 
 mojom("mojom") {
   sources = [
-    "event_injector.mojom",
     "gpu.mojom",
-    "remoting_event_injector.mojom",
-    "screen_provider_observer.mojom",
-    "user_activity_monitor.mojom",
     "window_manager.mojom",
-    "window_server_test.mojom",
-    "window_tree.mojom",
     "window_tree_constants.mojom",
   ]
 
@@ -29,7 +23,6 @@
     "//media/mojo/interfaces",
     "//mojo/public/mojom/base",
     "//services/viz/public/interfaces",
-    "//services/ws/public/mojom/ime",
     "//skia/public/interfaces",
     "//ui/base/ime/mojo",
     "//ui/base/mojo",
@@ -55,19 +48,3 @@
     "constants.mojom",
   ]
 }
-
-source_set("tests") {
-  testonly = true
-
-  sources = [
-    "ime/ime_struct_traits_unittest.cc",
-  ]
-
-  deps = [
-    "//base",
-    "//base/test:test_support",
-    "//services/ws/public/mojom/ime",
-    "//testing/gtest",
-    "//ui/gfx:test_support",
-  ]
-}
diff --git a/services/ws/public/mojom/event_injector.mojom b/services/ws/public/mojom/event_injector.mojom
deleted file mode 100644
index 4eaeb8c..0000000
--- a/services/ws/public/mojom/event_injector.mojom
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-module ws.mojom;
-
-import "ui/events/mojo/event.mojom";
-
-// An interface offered by the window service which allows clients to inject
-// events. This class is mainly provided for tests, but is also used for timing
-// (telemetry).
-interface EventInjector {
-  // Takes an event and dispatches it as if it came from the native platform.
-  // Responds with false on bad |display_id| or bad |event|; true once the
-  // event has completed processing.
-  //
-  // If |event| is a LocatedEvent, then the coordinates must be in DIPs, and
-  // relative to the origin of the display identified by |display_id|.
-  InjectEvent(int64 display_id, ui.mojom.Event event) => (bool result);
-
-  // Takes an event and dispatches it as if it came from the native platform.
-  // Similar to InjectEvent() but does not respond when processing is complete.
-  InjectEventNoAck(int64 display_id, ui.mojom.Event event);
-
-  // Similar to InjectEventNoAck(), but skips event rewriters.
-  InjectEventNoAckNoRewriters(int64 display_id, ui.mojom.Event event);
-};
diff --git a/services/ws/public/mojom/ime/BUILD.gn b/services/ws/public/mojom/ime/BUILD.gn
deleted file mode 100644
index 9484f1f..0000000
--- a/services/ws/public/mojom/ime/BUILD.gn
+++ /dev/null
@@ -1,20 +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.
-
-import("//mojo/public/tools/bindings/mojom.gni")
-
-mojom("ime") {
-  sources = [
-    "ime.mojom",
-  ]
-
-  public_deps = [
-    "//mojo/public/mojom/base",
-    "//ui/base/ime/mojo",
-    "//ui/events/mojo:interfaces",
-    "//ui/gfx/geometry/mojo",
-    "//ui/gfx/range/mojo",
-    "//ui/platform_window/mojo:interfaces",
-  ]
-}
diff --git a/services/ws/public/mojom/ime/OWNERS b/services/ws/public/mojom/ime/OWNERS
deleted file mode 100644
index e75daf74..0000000
--- a/services/ws/public/mojom/ime/OWNERS
+++ /dev/null
@@ -1,8 +0,0 @@
-per-file *_struct_traits*.*=set noparent
-per-file *_struct_traits*.*=file://ipc/SECURITY_OWNERS
-
-per-file *.mojom=set noparent
-per-file *.mojom=file://ipc/SECURITY_OWNERS
-
-per-file *.typemap=set noparent
-per-file *.typemap=file://ipc/SECURITY_OWNERS
diff --git a/services/ws/public/mojom/ime/ime.mojom b/services/ws/public/mojom/ime/ime.mojom
deleted file mode 100644
index 04ca6de..0000000
--- a/services/ws/public/mojom/ime/ime.mojom
+++ /dev/null
@@ -1,212 +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.
-
-module ws.mojom;
-
-import "mojo/public/mojom/base/text_direction.mojom";
-import "mojo/public/mojom/base/string16.mojom";
-import "ui/base/ime/mojo/ime_types.mojom";
-import "ui/events/mojo/event.mojom";
-import "ui/gfx/geometry/mojo/geometry.mojom";
-import "ui/gfx/range/mojo/range.mojom";
-import "ui/platform_window/mojo/text_input_state.mojom";
-
-// Specifies where to display the candidate window.
-enum CandidateWindowPosition {
-  // Candidate window follows the cursor.
-  kCursor,
-
-  // Candidate window is locked to the beginning of the composition.
-  kComposition
-};
-
-struct CandidateWindowProperties {
-  // Number of candidates to display per page.
-  int32 page_size;
-
-  // Should candidate window be rendered vertical or horizontal.
-  bool vertical;
-
-  // Text that is shown at the bottom of the candidate window.
-  string auxiliary_text;
-
-  // True to display auxiliary text, false to hide it.
-  bool auxiliary_text_visible;
-
-  // Position and visibility of cursor in the candidate window.
-  int32 cursor_position;
-  bool cursor_visible;
-
-  // Where to display the candidate window.
-  CandidateWindowPosition window_position;
-};
-
-// Represents a candidate window entry.
-struct CandidateWindowEntry {
-  // Value of the candidate.
-  mojo_base.mojom.String16 value;
-
-  // Short string displayed next to the candidate, often the shortcut key or
-  // index.
-  mojo_base.mojom.String16 label;
-
-  // Additional text describing the candidate.
-  mojo_base.mojom.String16 annotation;
-
-  // The usage or detailed description of the candidate.
-  mojo_base.mojom.String16 description_title;
-  mojo_base.mojom.String16 description_body;
-};
-
-// Represents the text input state of a client.
-struct TextInputState {
-  ui.mojom.TextInputType text_input_type;
-  ui.mojom.TextInputMode text_input_mode;
-  mojo_base.mojom.TextDirection text_direction;
-  int32 text_input_flags;  // A bitfield of ui::TextInputFlags.
-};
-
-// Cached input context information so that RemoteTextInputClient could support
-// synchronous getters.
-struct TextInputClientData {
-  gfx.mojom.Range? text_range;
-  mojo_base.mojom.String16? text;
-  bool has_composition_text;
-  gfx.mojom.Range? composition_text_range;
-  gfx.mojom.Range? editable_selection_range;
-  // Contains a boolean indicating the enabled state of each of the
-  // ui::TextEditCommands, where the index is the command. For example,
-  // edit_command_enabled[TextCommand::DELETE_BACKWARD] gives the enabled
-  // state of TextCommand::DELETE_BACKWARD.
-  array<bool>? edit_command_enabled;
-};
-
-// Detailed data of an IME session.
-struct SessionDetails {
-  // State of the text input client.
-  TextInputState state;
-
-  // Caret bounds of the text input client.
-  gfx.mojom.Rect caret_bounds;
-
-  // Data of the text input client.
-  TextInputClientData data;
-
-  // How the text input client was focused.
-  ui.mojom.FocusReason focus_reason;
-
-  // ukm::SourceId for identifying the text input client.
-  int64 client_source_for_metrics;
-
-  // Whether the text entered into this text input client should be used to
-  // improve IME suggestions.
-  bool should_do_learning;
-};
-
-// A service which provides the IMEDriver interface is responsible for doing
-// the composition logic. After starting a session, it receives events from
-// the client via the InputMethod interface, and sends composition events to
-// the client via the TextInputClient.
-interface IMEDriver {
-  StartSession(InputMethod& input_method_request,
-               TextInputClient client,
-               SessionDetails details);
-};
-
-// An IME driver register should register itself to Mus using the IMERegistrar
-// interface.
-interface IMERegistrar {
-  RegisterDriver(IMEDriver driver);
-};
-
-// A client sends updates to the IME driver using the InputMethod interface.
-// This interface is provided by IME drivers, and also by Mus as a lightweight
-// proxy between IME drivers and clients.
-interface InputMethod {
-  // Called when the text input state of client is changed.
-  OnTextInputStateChanged(TextInputState text_input_state);
-
-  // Client sends |caret_bounds| in focused window coordinates,
-  // Mus translates it to global coordinates and sends it to IME app.
-  OnCaretBoundsChanged(gfx.mojom.Rect caret_bounds);
-
-  // Update the cached text input client data.
-  OnTextInputClientDataChanged(TextInputClientData data);
-
-  // Called to process a key event. The callback function will be called to
-  // notify the client if the event was handled or not. A handled event may
-  // generate zero or more composition events which will be sent to the client
-  // using the "input method result" functions of TextInputClient interface.
-  ProcessKeyEvent(ui.mojom.Event key_event) => (bool handled);
-
-  CancelComposition();
-
-  // Plumbs requests to show the virtual keyboard.
-  ShowVirtualKeyboardIfEnabled();
-};
-
-// IME drivers send updates to clients using the TextInputClient interface.
-interface TextInputClient {
-  // Functions corresponding to "input method result" functions of
-  // ui::TextInputClient. See comments for InputMethod::ProcessKeyEvent() for
-  // when these are called.
-
-  // Sets composition text and attributes. See comments for
-  // ui::TextInputClient::SetCompositionText() for more details.
-  SetCompositionText(ui.mojom.CompositionText composition);
-
-  // Converts current composition text into final content.
-  ConfirmCompositionText();
-
-  // Removes current composition text.
-  ClearCompositionText();
-
-  // Inserts a given text at the insertion point. Current composition text or
-  // selection will be removed. This method should never be called when the
-  // current text input type is TEXT_INPUT_TYPE_NONE.
-  InsertText(mojo_base.mojom.String16 text);
-
-  // Inserts a single character at the insertion point. Unlike InsertText(),
-  // the character is not processed. See ui::TextInputClient::InsertChar()
-  // for more details.
-  InsertChar(ui.mojom.Event event);
-
-  // Dispatch a key event after minimal processing by the IME. The results of
-  // the callback indicated whether the event was handled, and whether any
-  // further processing should be performed. That is, if |stopped_propagation|
-  // is true, IME does no further processing.
-  DispatchKeyEventPostIME(ui.mojom.Event event) => (
-      bool handled,
-      bool stopped_propagation);
-
-  // Ensure the caret is not in |rect|.  |rect| is in dip screen coordinates
-  // and may extend beyond the bounds of this TextInputClient.
-  EnsureCaretNotInRect(gfx.mojom.Rect rect);
-
-  // Selects the given UTF-16 based character range. Current composition text
-  // will be confirmed before selecting the range.
-  SetEditableSelectionRange(gfx.mojom.Range range);
-
-  // Deletes contents in the given UTF-16 based character range. Current
-  // composition text will be confirmed before deleting the range.
-  DeleteRange(gfx.mojom.Range range);
-
-  // Called whenever current keyboard layout or input method is changed.
-  OnInputMethodChanged();
-
-  // Called whenever the user requests to change the text direction and layout
-  // alignment of the current text box.
-  ChangeTextDirectionAndLayoutAlignment(
-      mojo_base.mojom.TextDirection direction);
-
-  // Deletes the current selection plus the specified number of characters
-  // before and after the selection or caret.
-  ExtendSelectionAndDelete(uint32 before, uint32 after);
-
-  // TODO(moshayedi): Add functions corresponding to ui::TextInputClient for:
-  // - Input context information
-  // - Document content operations
-  // - Miscellaneous functions
-  // crbug.com/631527.
-};
diff --git a/services/ws/public/mojom/ime/ime.typemap b/services/ws/public/mojom/ime/ime.typemap
deleted file mode 100644
index c2a508d6..0000000
--- a/services/ws/public/mojom/ime/ime.typemap
+++ /dev/null
@@ -1,18 +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.
-
-mojom = "//services/ws/public/mojom/ime/ime.mojom"
-public_headers = [ "//ui/base/ime/candidate_window.h" ]
-traits_headers = [ "//services/ws/public/mojom/ime/ime_struct_traits.h" ]
-sources = [
-  "//services/ws/public/mojom/ime/ime_struct_traits.cc",
-]
-public_deps = [
-  "//ui/base/ime",
-]
-
-type_mappings = [
-  "ws.mojom.CandidateWindowEntry=ui::CandidateWindow::Entry",
-  "ws.mojom.CandidateWindowProperties=ui::CandidateWindow::CandidateWindowProperty",
-]
diff --git a/services/ws/public/mojom/ime/ime_struct_traits.cc b/services/ws/public/mojom/ime/ime_struct_traits.cc
deleted file mode 100644
index a5bc13b..0000000
--- a/services/ws/public/mojom/ime/ime_struct_traits.cc
+++ /dev/null
@@ -1,42 +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 "services/ws/public/mojom/ime/ime_struct_traits.h"
-
-#include "mojo/public/cpp/base/string16_mojom_traits.h"
-
-namespace mojo {
-
-// static
-bool StructTraits<ws::mojom::CandidateWindowPropertiesDataView,
-                  ui::CandidateWindow::CandidateWindowProperty>::
-    Read(ws::mojom::CandidateWindowPropertiesDataView data,
-         ui::CandidateWindow::CandidateWindowProperty* out) {
-  if (data.is_null())
-    return false;
-  if (!data.ReadAuxiliaryText(&out->auxiliary_text))
-    return false;
-  out->page_size = data.page_size();
-  out->is_vertical = data.vertical();
-  out->is_auxiliary_text_visible = data.auxiliary_text_visible();
-  out->cursor_position = data.cursor_position();
-  out->is_cursor_visible = data.cursor_visible();
-  out->show_window_at_composition =
-      data.window_position() ==
-      ws::mojom::CandidateWindowPosition::kComposition;
-  return true;
-}
-
-// static
-bool StructTraits<ws::mojom::CandidateWindowEntryDataView,
-                  ui::CandidateWindow::Entry>::
-    Read(ws::mojom::CandidateWindowEntryDataView data,
-         ui::CandidateWindow::Entry* out) {
-  return !data.is_null() && data.ReadValue(&out->value) &&
-         data.ReadLabel(&out->label) && data.ReadAnnotation(&out->annotation) &&
-         data.ReadDescriptionTitle(&out->description_title) &&
-         data.ReadDescriptionBody(&out->description_body);
-}
-
-}  // namespace mojo
diff --git a/services/ws/public/mojom/ime/ime_struct_traits.h b/services/ws/public/mojom/ime/ime_struct_traits.h
deleted file mode 100644
index 0d7e959..0000000
--- a/services/ws/public/mojom/ime/ime_struct_traits.h
+++ /dev/null
@@ -1,73 +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 SERVICES_WS_PUBLIC_MOJOM_IME_IME_STRUCT_TRAITS_H_
-#define SERVICES_WS_PUBLIC_MOJOM_IME_IME_STRUCT_TRAITS_H_
-
-#include "services/ws/public/mojom/ime/ime.mojom-shared.h"
-#include "ui/base/ime/candidate_window.h"
-
-namespace mojo {
-
-template <>
-struct StructTraits<ws::mojom::CandidateWindowPropertiesDataView,
-                    ui::CandidateWindow::CandidateWindowProperty> {
-  static int32_t page_size(
-      const ui::CandidateWindow::CandidateWindowProperty& p) {
-    return p.page_size;
-  }
-  static bool vertical(const ui::CandidateWindow::CandidateWindowProperty& p) {
-    return p.is_vertical;
-  }
-  static std::string auxiliary_text(
-      const ui::CandidateWindow::CandidateWindowProperty& p) {
-    return p.auxiliary_text;
-  }
-  static bool auxiliary_text_visible(
-      const ui::CandidateWindow::CandidateWindowProperty& p) {
-    return p.is_auxiliary_text_visible;
-  }
-  static int32_t cursor_position(
-      const ui::CandidateWindow::CandidateWindowProperty& p) {
-    return p.cursor_position;
-  }
-  static bool cursor_visible(
-      const ui::CandidateWindow::CandidateWindowProperty& p) {
-    return p.is_cursor_visible;
-  }
-  static ws::mojom::CandidateWindowPosition window_position(
-      const ui::CandidateWindow::CandidateWindowProperty& p) {
-    return p.show_window_at_composition
-               ? ws::mojom::CandidateWindowPosition::kComposition
-               : ws::mojom::CandidateWindowPosition::kCursor;
-  }
-  static bool Read(ws::mojom::CandidateWindowPropertiesDataView data,
-                   ui::CandidateWindow::CandidateWindowProperty* out);
-};
-
-template <>
-struct StructTraits<ws::mojom::CandidateWindowEntryDataView,
-                    ui::CandidateWindow::Entry> {
-  static base::string16 value(const ui::CandidateWindow::Entry& e) {
-    return e.value;
-  }
-  static base::string16 label(const ui::CandidateWindow::Entry& e) {
-    return e.label;
-  }
-  static base::string16 annotation(const ui::CandidateWindow::Entry& e) {
-    return e.annotation;
-  }
-  static base::string16 description_title(const ui::CandidateWindow::Entry& e) {
-    return e.description_title;
-  }
-  static base::string16 description_body(const ui::CandidateWindow::Entry& e) {
-    return e.description_body;
-  }
-  static bool Read(ws::mojom::CandidateWindowEntryDataView data,
-                   ui::CandidateWindow::Entry* out);
-};
-
-}  // namespace mojo
-
-#endif  // SERVICES_WS_PUBLIC_MOJOM_IME_IME_STRUCT_TRAITS_H_
diff --git a/services/ws/public/mojom/ime/ime_struct_traits_unittest.cc b/services/ws/public/mojom/ime/ime_struct_traits_unittest.cc
deleted file mode 100644
index ad28ee1f..0000000
--- a/services/ws/public/mojom/ime/ime_struct_traits_unittest.cc
+++ /dev/null
@@ -1,87 +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 "services/ws/public/mojom/ime/ime_struct_traits.h"
-
-#include <utility>
-
-#include "base/stl_util.h"
-#include "base/strings/utf_string_conversions.h"
-#include "mojo/public/cpp/base/string16_mojom_traits.h"
-#include "mojo/public/cpp/bindings/binding_set.h"
-#include "services/ws/public/mojom/ime/ime.mojom.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace ws {
-
-namespace {
-
-class IMEStructTraitsTest : public testing::Test {
- public:
-  IMEStructTraitsTest() {}
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(IMEStructTraitsTest);
-};
-
-}  // namespace
-
-TEST_F(IMEStructTraitsTest, CandidateWindowProperties) {
-  ui::CandidateWindow::CandidateWindowProperty input;
-  input.page_size = 7;
-  input.cursor_position = 3;
-  input.is_cursor_visible = true;
-  input.is_vertical = false;
-  input.show_window_at_composition = true;
-  input.auxiliary_text = "abcdefghij";
-  input.is_auxiliary_text_visible = false;
-
-  ui::CandidateWindow::CandidateWindowProperty output;
-  EXPECT_TRUE(mojom::CandidateWindowProperties::Deserialize(
-      mojom::CandidateWindowProperties::Serialize(&input), &output));
-
-  EXPECT_EQ(input.page_size, output.page_size);
-  EXPECT_EQ(input.cursor_position, output.cursor_position);
-  EXPECT_EQ(input.is_cursor_visible, output.is_cursor_visible);
-  EXPECT_EQ(input.is_vertical, output.is_vertical);
-  EXPECT_EQ(input.show_window_at_composition,
-            output.show_window_at_composition);
-  EXPECT_EQ(input.auxiliary_text, output.auxiliary_text);
-  EXPECT_EQ(input.is_auxiliary_text_visible, output.is_auxiliary_text_visible);
-
-  // Reverse boolean fields and check we still serialize/deserialize correctly.
-  input.is_cursor_visible = !input.is_cursor_visible;
-  input.is_vertical = !input.is_vertical;
-  input.show_window_at_composition = !input.show_window_at_composition;
-  input.is_auxiliary_text_visible = !input.is_auxiliary_text_visible;
-  EXPECT_TRUE(mojom::CandidateWindowProperties::Deserialize(
-      mojom::CandidateWindowProperties::Serialize(&input), &output));
-
-  EXPECT_EQ(input.is_cursor_visible, output.is_cursor_visible);
-  EXPECT_EQ(input.is_vertical, output.is_vertical);
-  EXPECT_EQ(input.show_window_at_composition,
-            output.show_window_at_composition);
-  EXPECT_EQ(input.is_auxiliary_text_visible, output.is_auxiliary_text_visible);
-}
-
-TEST_F(IMEStructTraitsTest, CandidateWindowEntry) {
-  ui::CandidateWindow::Entry input;
-  input.value = base::UTF8ToUTF16("entry_value");
-  input.label = base::UTF8ToUTF16("entry_label");
-  input.annotation = base::UTF8ToUTF16("entry_annotation");
-  input.description_title = base::UTF8ToUTF16("entry_description_title");
-  input.description_body = base::UTF8ToUTF16("entry_description_body");
-
-  ui::CandidateWindow::Entry output;
-  EXPECT_TRUE(mojom::CandidateWindowEntry::Deserialize(
-      mojom::CandidateWindowEntry::Serialize(&input), &output));
-
-  EXPECT_EQ(input.value, output.value);
-  EXPECT_EQ(input.label, output.label);
-  EXPECT_EQ(input.annotation, output.annotation);
-  EXPECT_EQ(input.description_title, output.description_title);
-  EXPECT_EQ(input.description_body, output.description_body);
-}
-
-}  // namespace ws
diff --git a/services/ws/public/mojom/ime/typemaps.gni b/services/ws/public/mojom/ime/typemaps.gni
deleted file mode 100644
index 9785b15..0000000
--- a/services/ws/public/mojom/ime/typemaps.gni
+++ /dev/null
@@ -1,5 +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.
-
-typemaps = [ "//services/ws/public/mojom/ime/ime.typemap" ]
diff --git a/services/ws/public/mojom/remoting_event_injector.mojom b/services/ws/public/mojom/remoting_event_injector.mojom
deleted file mode 100644
index 7a276c1..0000000
--- a/services/ws/public/mojom/remoting_event_injector.mojom
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-module ws.mojom;
-
-import "ui/gfx/geometry/mojo/geometry.mojom";
-
-enum InjectedMouseButtonType {
-  kLeft,
-  kMiddle,
-  kRight,
-};
-
-// An interface offered by the window service which allows clients to inject
-// events for remoting. This interfaces mirrors that of
-// ui/events/SystemEventInjector.
-interface RemotingEventInjector {
-  MoveCursorToLocationInPixels(gfx.mojom.PointF location);
-
-  // If |down| is true, injects a mouse press, otherwise a mouse release.
-  InjectMousePressOrRelease(InjectedMouseButtonType button, bool down);
-
-  InjectMouseWheelInPixels(int32 delta_x, int32 delta_y);
-
-  // |native_key_code| corresponds to the native key-code from a dom-code. See
-  // DomCodeToNativeKeycode().
-  InjectKeyEvent(int32 native_key_code, bool down, bool suppress_auto_repeat);
-};
diff --git a/services/ws/public/mojom/screen_provider_observer.mojom b/services/ws/public/mojom/screen_provider_observer.mojom
deleted file mode 100644
index 45304dd..0000000
--- a/services/ws/public/mojom/screen_provider_observer.mojom
+++ /dev/null
@@ -1,22 +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.
-
-module ws.mojom;
-
-import "services/ws/public/mojom/window_tree_constants.mojom";
-
-interface ScreenProviderObserver {
-  // Sent when the observer is added as well as any time the set of displays
-  // changes in any way. |displays| contains all known displays. If the system
-  // that WS is running on has an integrated display, for example a laptop
-  // internal display, then |internal_display_id| will be the corresponding
-  // display id. If there is no internal display then |internal_display_id| will
-  // be kInvalidDisplayID. |display_id_for_new_windows| is the display on which
-  // to place new top-level windows, usually the display on which a window was
-  // last activated.
-  OnDisplaysChanged(array<WsDisplay> displays,
-                    int64 primary_display_id,
-                    int64 internal_display_id,
-                    int64 display_id_for_new_windows);
-};
diff --git a/services/ws/public/mojom/user_activity_monitor.mojom b/services/ws/public/mojom/user_activity_monitor.mojom
deleted file mode 100644
index f0cf7448..0000000
--- a/services/ws/public/mojom/user_activity_monitor.mojom
+++ /dev/null
@@ -1,31 +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.
-
-module ws.mojom;
-
-interface UserActivityObserver {
-  OnUserActivity();
-};
-
-interface UserIdleObserver {
-  enum IdleState {
-    ACTIVE,
-    IDLE,
-  };
-  OnUserIdleStateChanged(IdleState new_state);
-};
-
-interface UserActivityMonitor {
-  // Notifies the observer of user activity at most once every
-  // |delay_between_notify_secs| seconds.
-  AddUserActivityObserver(uint32 delay_between_notify_secs,
-                          UserActivityObserver observer);
-
-  // Notifies the observer when user is idle for more than
-  // |idle_time_in_minutes| minutes. When the observer is first added, if the
-  // user has already been idle for |idle_time_in_minutes|, then
-  // OnUserIdleStateChanged(IDLE) is called on the observer, otherwise
-  // OnUserIdleStateChanged(ACTIVE) is called.
-  AddUserIdleObserver(uint32 idle_time_in_minutes, UserIdleObserver observer);
-};
diff --git a/services/ws/public/mojom/window_server_test.mojom b/services/ws/public/mojom/window_server_test.mojom
deleted file mode 100644
index 261aa94..0000000
--- a/services/ws/public/mojom/window_server_test.mojom
+++ /dev/null
@@ -1,11 +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.
-
-module ws.mojom;
-
-import "ui/events/mojo/event.mojom";
-
-interface WindowServerTest {
-  EnsureClientHasDrawnWindow(string client_name) => (bool success);
-};
diff --git a/services/ws/public/mojom/window_tree.mojom b/services/ws/public/mojom/window_tree.mojom
deleted file mode 100644
index aca3358..0000000
--- a/services/ws/public/mojom/window_tree.mojom
+++ /dev/null
@@ -1,709 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-module ws.mojom;
-
-import "mojo/public/mojom/base/unguessable_token.mojom";
-import "services/viz/public/interfaces/compositing/compositor_frame_sink.mojom";
-import "services/viz/public/interfaces/compositing/frame_sink_id.mojom";
-import "services/viz/public/interfaces/compositing/local_surface_id_allocation.mojom";
-import "services/viz/public/interfaces/compositing/surface_info.mojom";
-import "services/ws/public/mojom/window_manager.mojom";
-import "services/ws/public/mojom/screen_provider_observer.mojom";
-import "services/ws/public/mojom/window_tree_constants.mojom";
-import "ui/base/ime/mojo/ime.mojom";
-import "ui/base/mojo/cursor.mojom";
-import "ui/base/mojo/ui_base_types.mojom";
-import "ui/events/mojo/event.mojom";
-import "ui/events/mojo/event_constants.mojom";
-import "ui/gfx/geometry/mojo/geometry.mojom";
-import "ui/gfx/image/mojo/image.mojom";
-import "ui/gfx/mojo/transform.mojom";
-import "ui/platform_window/mojo/text_input_state.mojom";
-
-// Windows are identified by a uint64. The upper 32 bits are the id assigned to
-// the client by the Window Service, and the lower 32 an id assigned by the
-// client. Windows created by the client supply a client id of 0, which the
-// Window Service maps appropriately. For clients that see Windows created by
-// another client, the upper 32 bits are set to identify the other client.
-//
-// The root window is identified with a connection id of 0, and value of 1.
-//
-// Most functions to the WindowTree take a change_id parameter. When
-// WindowTree completes processing of a function WindowTree calls
-// WindowTreeClient::OnChangeCompleted() with the change_id supplied by the
-// client and the result of the function. This allows the client to track
-// whether the call succeeded or not. Calls are done via the client interface
-// rather than a callback to ensure ordering. The server does not interpret the
-// change id in anyway, it is up to the client to assign a value and use it.
-// Generally the change id is an ever increasing integer.
-//
-// Event processing happens in the following order:
-// . The event is sent to the accelerator registered for the PRE_TARGET. If
-//   the client consumes the event, matching event observers are notified and
-//   processing stops. If the client does not consume the event processing
-//   continues.
-// . Target window (lookup of the target window depends upon the event type) and
-//   matching event observers are notified at the same time. The target is only
-//   notified once, even if it has a matching event observer registered. If the
-//   target consumes the event, processing stops.
-// . Accelerator registered for POST_TARGET. No response is expected from the
-//   client for the POST_TARGET and processing of the next continues
-//   immediately.
-interface WindowTree {
-  // Creates a new window with the specified id. It is up to the client to
-  // ensure the id is unique to the connection (the id need not be globally
-  // unique). Additionally the connection id (embedded in |window_id|) must
-  // match that of the connection.
-  // Errors:
-  //   ERROR_CODE_VALUE_IN_USE: a window already exists with the specified id.
-  //   ERROR_CODE_ILLEGAL_ARGUMENT: The connection part of |window_id| does not
-  //     match the connection id of the client.
-  NewWindow(uint32 change_id,
-            uint64 window_id,
-            map<string, array<uint8>>? properties);
-
-  // Requests the WindowManager to create a new top level window. On success
-  // OnTopLevelCreated() is called with the WindowData for the new window. On
-  // failure OnChangeCompleted() is called.
-  NewTopLevelWindow(uint32 change_id,
-                    uint64 window_id,
-                    map<string, array<uint8>> properties);
-
-  // Deletes a window. This does not recurse. No hierarchy change notifications
-  // are sent as a result of this. Only the connection that created the window
-  // can delete it. DeleteWindow() may also be used to remove a window as an
-  // embed root from the client. When DeleteWindow() is used to remove an embed
-  // root, the client no longer has access to the embed root. The embedder of
-  // the root is notified of the change via OnEmbeddedAppDisconnected().
-  DeleteWindow(uint32 change_id, uint64 window_id);
-
-  // Requests input event capture for the given |window_id|. Capture is only
-  // allowed if the window is processing an event. When a window gains capture,
-  // current input events are canceled. The given window will receive all
-  // subsequent input until an alternate window is set via SetCapture, or
-  // ReleaseCapture is called for |window_id|. OnCaptureChanged() is called to
-  // notify of capture changing (as long as the client did not initiate the
-  // change).
-  SetCapture(uint32 change_id, uint64 window_id);
-
-  // Releases input event capture for the given |window_id|. This does nothing
-  // if |window_id| does not currently have capture.
-  ReleaseCapture(uint32 change_id, uint64 window_id);
-
-  // Start observing global events matching the supplied |types|, even if they
-  // are not targeted at the requesting client. Events that would normally be
-  // sent to the requesting client (ie. the event target is this window tree)
-  // are sent to OnWindowInputEvent() with |matches_event_observer| set to true.
-  //
-  // Clients should limit the requested types and the duration of observation,
-  // as there is a system-wide perf/battery penalty, especially for mouse moves.
-  //
-  // See class description for details on event delivery.
-  ObserveEventTypes(array<ui.mojom.EventType> types);
-
-  // Sets the specified bounds of the specified window. The window will paint
-  // the frame in the provided |local_frame_id|, if any.
-  // |local_surface_id_allocation| need only be supplied for roots.
-  // For top-levels, clients may supply a null LocalSurfaceIdAllocation, before
-  // OnTopLevelCreated() is called (clients receive the initial
-  // LocalSurfaceIdAllocation in OnTopLevelCreated()). While clients may supply
-  // a LocalSurfaceIdAllocation for top-levels, they are only allowed to
-  // change the child-sequence number portion of the id. Supplying a different
-  // embed token results in failure, and changes to the parent sequence number
-  // are ignored.
-  // |bounds| are in DIPs.
-  SetWindowBounds(
-      uint32 change_id,
-      uint64 window_id,
-      gfx.mojom.Rect bounds,
-      viz.mojom.LocalSurfaceIdAllocation? local_surface_id_allocation);
-
-  // Updates the LocalSurfaceIdAllocation for a window from the child. Does
-  // nothing if |window_id| does not identify a top-level. See comment in
-  // SetWindowBounds() for allowable values of |local_surface_id_allocation|.
-  UpdateLocalSurfaceIdFromChild(
-      uint64 window_id,
-      viz.mojom.LocalSurfaceIdAllocation local_surface_id_allocation);
-
-  // Asks the server to generate a new LocalSurfaceId for a window. The server
-  // responds with the new LocalSurfaceId by way of OnWindowBoundsChanged().
-  // This is only useful for top-levels.
-  AllocateLocalSurfaceId(uint64 window_id);
-
-  SetWindowTransform(uint32 change_id,
-                     uint64 window_id,
-                     gfx.mojom.Transform transform);
-
-  // Sets the client area of the specified window. The client area is specified
-  // by way of insets. Everything outside of the insets, and not in
-  // |additional_client_areas| is considered non-client area.
-  // TODO(sky): convert additional_client_areas to a path.
-  SetClientArea(uint64 window_id,
-                gfx.mojom.Insets insets,
-                array<gfx.mojom.Rect>? additional_client_areas);
-
-  // Insets the hit test of a window by the specified values. The insets must be
-  // positive (or zero). |mouse| applies to events originating from the mouse,
-  // and |touch| from a non-mouse pointer device (such as tap).
-  SetHitTestInsets(uint64 window_id,
-                   gfx.mojom.Insets mouse,
-                   gfx.mojom.Insets touch);
-
-  // Specifies the shape of the window. The region outside of the shape becomes
-  // transparent and should be considered to be outside of the widget, so
-  // underlying window will be targeted. Each element of |shape| is relative to
-  // the window's coordinate. Empty means to reset the settings, so the normal
-  // rectangular region will be used.
-  SetShape(uint64 window_id, array<gfx.mojom.Rect> shape);
-
-  // Called by clients that want to accept drag and drops. Windows default to
-  // this being disabled; a window must actively opt-in to receiving OnDrag*()
-  // calls.
-  SetCanAcceptDrops(uint64 window_id, bool accepts_drops);
-
-  // Sets the visibility of the specified window to |visible|. Connections are
-  // allowed to change the visibility of any window they have created, as well
-  // as any of their roots.
-  SetWindowVisibility(uint32 change_id, uint64 window_id, bool visible);
-
-  // Sets an individual named property. Setting an individual property to null
-  // deletes the property.
-  SetWindowProperty(uint32 change_id,
-                    uint64 window_id,
-                    string name,
-                    array<uint8>? value);
-
-  // Sets the opacity of the specified window to |opacity|.
-  SetWindowOpacity(uint32 change_id, uint64 window_id, float opacity);
-
-  // Sets the transparent status of the specified window to |transparent|.
-  SetWindowTransparent(uint32 change_id, uint64 window_id, bool transparent);
-
-  // Attaches a CompositorFrameSink to a particular window.
-  AttachCompositorFrameSink(
-      uint64 window_id,
-      viz.mojom.CompositorFrameSink& compositor_frame_sink,
-      viz.mojom.CompositorFrameSinkClient client);
-
-  // Reparents a window.
-  // This fails for any of the following reasons:
-  // . |parent| or |child| does not identify a valid window.
-  // . |child| is an ancestor of |parent|.
-  // . |child| is already a child of |parent|.
-  //
-  // This may result in a connection getting OnWindowDeleted(). See
-  // RemoveWindowFromParent for details.
-  AddWindow(uint32 change_id, uint64 parent, uint64 child);
-
-  // Removes a window from its current parent. This fails if the window is not
-  // valid or the window already has no parent.
-  //
-  // Removing a window from a parent may result in OnWindowDeleted() being sent
-  // to other connections. For example, connection A has windows 1 and 2, with 2
-  // a child of 1. Connection B has a root 1. If 2 is removed from 1 then B gets
-  // OnWindowDeleted(). This is done as window 2 is effectively no longer
-  // visible to connection B.
-  RemoveWindowFromParent(uint32 change_id, uint64 window_id);
-
-  // Ties the lifetime of |transient_window_id| to the lifetime of |window_id|.
-  // This also places |transient_window_id| on top of |window_id|.
-  // This fails for any of the following reasons:
-  // . |window_id| or |transient_window_id| does not identify a valid window.
-  // . |transient_window_id| is an ancestor of |window_id|.
-  // . |transient_window_id| is modal to system.
-  AddTransientWindow(uint32 change_id,
-                     uint64 window_id,
-                     uint64 transient_window_id);
-
-  // Decouples the lifetime of |transient_window_id| from its transient parent.
-  // This does not change transient window's position in the window hierarchy.
-  RemoveTransientWindowFromParent(uint32 change_id, uint64 transient_window_id);
-
-  // Changes modality type of |window_id|. This releases capture if necessary.
-  // This fails for any of the following reasons:
-  // . |window_id| does not identify a valid window.
-  // . Client does not have a valid user id (i.e., it is an embedded app).
-  SetModalType(uint32 change_id, uint64 window_id, ui.mojom.ModalType type);
-
-  // Reorders a window in its parent, relative to |relative_window_id| according
-  // to |direction|. Only the connection that created the window's parent can
-  // reorder its children.
-  ReorderWindow(uint32 change_id,
-                uint64 window_id,
-                uint64 relative_window_id,
-                OrderDirection direction);
-
-  // Returns the windows comprising the tree starting at |window_id|.
-  // |window_id| is the first result in the return value, unless |window_id| is
-  // invalid, in which case an empty vector is returned. The windows are visited
-  // using a depth first search (pre-order).
-  GetWindowTree(uint64 window_id) => (array<WindowData> windows);
-
-  // A connection may grant access to another connection by way of Embed().
-  // Embed() results in the supplied WindowTreeClient being configured with a
-  // root window of |window_id|. The supplied WindowTreeClient may create child
-  // windows and do other various tree operations (including Embed()), but does
-  // not see nor have access to any of the windows above the embed point.
-  //
-  // The caller must have created |window_id|. If not the request fails and the
-  // response is false.
-  //
-  // The embedder can dictate the behaviour of the embedded client by setting
-  // the appropriate embed flags (e.g. kEmbedFlagEmbedderInterceptsEvents).
-  //
-  // When a connection embeds a WindowTreeClient the originating connection no
-  // longer has privileges to access or see any of the children of the window.
-  // If the window had existing children the children are removed. The
-  // WindowManager gets to see the whole tree.
-  //
-  // A window may only have one embedding in it at a time. Subsequent calls to
-  // Embed() for the same window result in the currently embedded
-  // WindowTreeClient being removed. The embedded app is told this by way of
-  // OnUnembed(), which is followed by OnWindowDeleted() (as the connection no
-  // longer has access to the window).
-  //
-  // The embedder can detect when the embedded app disconnects by way of
-  // OnEmbeddedAppDisconnected().
-  //
-  // The callback returns whether the embedding was successful.
-  Embed(uint64 window_id, WindowTreeClient client, uint32 embed_flags)
-      => (bool success);
-
-  // Schedules a future call to Embed() using the returned token. This is used
-  // when two clients need to work together to complete an embedding without
-  // passing the WindowTreeClient between the two. This ensures a client isn't
-  // able to spoof another client (say by directly passing events to the
-  // client).
-  //
-  // For example, client A embeds client B in a window. Client A wants client B
-  // to embed a WindowTreeClient in a window created by client B. This can be
-  // accomplished by client A calling ScheduleEmbed() and then passing the
-  // token returned from ScheduleEmbed() to client B (using a separate pipe) so
-  // that client B may call EmbedUsingToken().
-  ScheduleEmbed(WindowTreeClient client)
-      => (mojo_base.mojom.UnguessableToken token);
-
-  // Creates an UnguessableToken for use in a future call, by another client, to
-  // EmbedUsingToken().
-  //
-  // The following example shows how to use this api. Client A wants client B
-  // to embed it in a window created by client B.
-  // 1. Client A calls ScheduleEmbedForExistingClient() to get a token.
-  // 2. Client A passes the token to client B. This communication is done using
-  //    an additional channel, outside of WindowTree/WindowTreeClient.
-  // 3. Client B calls EmbedUsingToken().
-  // 4. Client A receives OnEmbedFromToken() with the token from step 1.
-  //
-  // |window_id| is the id used for the window once EmbedUsingToken() is called.
-  // More specifically, when OnEmbedFromToken() is called |window_id| is the id
-  // of the window identified in the WindowData.
-  ScheduleEmbedForExistingClient(uint32 window_id) => (
-      mojo_base.mojom.UnguessableToken token);
-
-  // Pair with ScheduleEmbed() or ScheduleEmbedForExistingClient() to complete
-  // an embedding, see them for details.
-  EmbedUsingToken(uint64 window_id,
-                  mojo_base.mojom.UnguessableToken token,
-                  uint32 embed_flags)
-      => (bool success);
-
-  // Attaches/unattaches a FrameSinkId to this window. A window can only have
-  // a single frame-sink-id attached to it.
-  AttachFrameSinkId(uint64 window_id,
-                    viz.mojom.FrameSinkId frame_sink_id);
-  UnattachFrameSinkId(uint64 window_id);
-
-  // Sets focus to the specified window, use 0 to clear focus. For a window to
-  // get focus the following has to happen: the window is drawn, the window has
-  // been marked as focusable (see SetCanFocus()) and the window is in a
-  // container the WindowManager has identified as allowing activation
-  // (see WindowManagerClient::AddActivationParent()).
-  SetFocus(uint32 change_id, uint64 window_id);
-
-  // Marks the specified window as being able to receive focus.
-  SetCanFocus(uint64 window_id, bool can_focus);
-
-  // Sets the cursor when the pointer is inside |window_id|.
-  SetCursor(uint32 change_id, uint64 window_id, ui.mojom.Cursor cursor);
-
-  // Set text input state for the given window.
-  SetWindowTextInputState(uint64 window_id, ui.mojom.TextInputState state);
-
-  // Set the input method editor UI (software keyboard, etc) visibility.
-  // If state is non-null, the specified window's text input state is updated.
-  // Otherwise the existing state is used.
-  SetImeVisibility(uint64 window_id,
-                   bool visible,
-                   ui.mojom.TextInputState? state);
-
-  // Sets the EventTargetingPolicy. See EventTargetingPolicy for details.
-  SetEventTargetingPolicy(uint64 window_id, EventTargetingPolicy policy);
-
-  // See documentation for WindowTreeClient::OnWindowInputEvent().
-  OnWindowInputEventAck(uint32 event_id, EventResult result);
-
-  // If the current focus is (or is a child of) |window_id|, requests that the
-  // window manager change the focus to the next activatable window.
-  DeactivateWindow(uint64 window_id);
-
-  // Stacks the window |above_id| above |below_id|. These two windows must
-  // share the same parent. This function is intended for use with top-levels
-  // only. For non-top-levels, use ReorderWindow().
-  // TODO(sky): unify this and ReorderWindow(). https://crbug.com/850133.
-  StackAbove(uint32 change_id, uint64 above_id, uint64 below_id);
-
-  // Stacks the window above all sibling windows.
-  StackAtTop(uint32 change_id, uint64 window_id);
-
-  // Requests a window manager specific interface. |name| is the name of the
-  // interface. This function is typed to WindowManager, but that's purely
-  // by necessity. This function is used to request *any* interface known to
-  // the environment hosting the window service. If |name| is not the name of
-  // an interface known to the environment hosting the window service,
-  // |window_manager| is closed.
-  BindWindowManagerInterface(string name,
-                             associated WindowManager& window_manager);
-
-  // Returns a shared memory segment that contains two 16-bit ints packed into a
-  // single Atomic32, which represent the current location of the mouse cursor
-  // where the location is (x << 16) | y.
-  GetCursorLocationMemory() => (handle<shared_buffer> cursor_buffer);
-
-  // Tells the window manager to start moving or resizing the window.
-  // OnChangeCompleted is called on whether the move was canceled. Because
-  // there's a delay between when a client sends this message and when the
-  // window manager starts acting on it, pass the cursor location at the start
-  // of the move. |hit_test| specifies the type of the session; kCaption for
-  // moving.
-  PerformWindowMove(uint32 change_id, uint64 window_id, MoveLoopSource source,
-                    gfx.mojom.Point cursor, ui.mojom.HitTest hit_test);
-
-  // Tells the window manager to cancel any in progress window move started with
-  // StartWindowMove() and to revert the window bounds to how they were.
-  CancelWindowMove(uint64 window_id);
-
-  // Called by the client to start a drag operation. |source_window_id| is the
-  // source window, |screen_location| is what the source thinks their location
-  // of the pointer which started the drag is, |drag_data| is the entire set of
-  // mime to raw data mapping. |drag_image| and |drag_image_offset| describe
-  // an image to hold behind the cursor which represents the data on the
-  // clipboard. We send this during the start of the drag because most views
-  // clients will try to read all this data on first entry.
-  PerformDragDrop(uint32 change_id,
-                  uint64 source_window_id,
-                  gfx.mojom.Point screen_location,
-                  map<string, array<uint8>> drag_data,
-                  gfx.mojom.ImageSkia? drag_image,
-                  gfx.mojom.Vector2d drag_image_offset,
-                  uint32 drag_operation,
-                  ui.mojom.PointerKind source);
-
-  // Called by the client to cancel any in progress drag drop operation. This
-  // will result in a change completed for the underlying change.
-  CancelDragDrop(uint64 window_id);
-
-  // Called by the client to start a session of observing the topmost window
-  // under the cursor or touch point. Once called, OnTopmostWindowChanged() is
-  // called on the client whenever the topmost window changes. |source|
-  // specifies what type of located events should be observed (mouse or touch),
-  // and |window_id| specifies the initial target of the located event.
-  ObserveTopmostWindow(MoveLoopSource source, uint64 window_id);
-
-  // Called by the client to request stopping the ongoing session of observing
-  // the topmost window under the cursor.
-  StopObservingTopmostWindow();
-
-  // Sets the resize shadow for |hit_test| to the specified window. It hides
-  // the shadow when kNowhere is set.
-  SetWindowResizeShadow(uint64 window_id, ui.mojom.HitTest hit_test);
-
-  // Called by the client to cancel active touch events. not_cancelled_window_id
-  // is a window ID, and that window is excluded from cancelling. When
-  // not_cancelled_window_id is invalid, active touch events should be cancelled
-  // on all windows.
-  CancelActiveTouchesExcept(uint64 not_cancelled_window_id);
-
-  // Called by the client to cancel active touches on |window_id|.
-  CancelActiveTouches(uint64 window_id);
-
-  // Called by the client to transfer the gesture stream from the window of
-  // |current_id| to the window of |new_id|. If |should_cancel| is set, then
-  // cancel events are also dispatched to |current_id|. Both |current_id| and
-  // |new_id| need to be valid window ID created by the client. This operation
-  // is not allowed for embedded clients.
-  TransferGestureEventsTo(uint64 current_id, uint64 new_id, bool should_cancel);
-
-  // Called by the client to start occlusion tracking for a window.
-  TrackOcclusionState(uint64 window_id);
-
-  // Called by the client to pause occlusion states computation when there are
-  // massive window changes (such as a hierarchy change, or multiple window
-  // visibility change) to avoid unnecessary occlusion state updates. Server
-  // will re-compute the occlusion state after a balancing
-  // UnpauseWindowOcclusionTracking call.
-  PauseWindowOcclusionTracking();
-
-  // Called by the client the resume occlusion states computation. When
-  // all outstanding pause requests are cleared, server will re-compute
-  // occlusion states for the tracked windows and send back updates.
-  UnpauseWindowOcclusionTracking();
-
-  // Called by the client every time the focus changes, to build the connection
-  // between the client and the IME. And Window Service makes sure that only
-  // focused client can have the connection.
-  ConnectToImeEngine(ime.mojom.ImeEngine& engine_request,
-                     ime.mojom.ImeEngineClient client);
-};
-
-// Changes to windows are not sent to the connection that originated the
-// change. For example, if connection 1 changes the bounds of a window by
-// calling SetWindowBounds(), connection 1 does not receive
-// OnWindowBoundsChanged().
-interface WindowTreeClient {
-  // Sent when clients establishes a connection to the WindowService.
-  // |client_id| gives the unique id for the client. This value is generally
-  // only useful for debugging.
-  OnClientId(uint32 client_id);
-
-  // Invoked when the client application has been embedded at |root|.
-  // See Embed() on WindowTree for more details. |tree| will be a handle back to
-  // the window manager service, unless the connection is to the root connection
-  // in which case it will be null. |parent_drawn| is true if roots parent is
-  // drawn, see OnParentDrawnStateChanged() for details. |display_id| identifies
-  // the display this root window is on. If the embedded window has a size,
-  // |local_surface_id| identifies the ID to use to submit CompositorFrames.
-  OnEmbed(WindowData root,
-          WindowTree? tree,
-          int64 display_id,
-          uint64 focused_window,
-          bool parent_drawn,
-          viz.mojom.LocalSurfaceIdAllocation? local_surface_id_allocation);
-
-  // See description in ScheduleEmbedForExistingClient() for details on this.
-  // Supplied arguments match that of OnEmbed().
-  OnEmbedFromToken(
-      mojo_base.mojom.UnguessableToken token,
-      WindowData root,
-      int64 display_id,
-      viz.mojom.LocalSurfaceIdAllocation? local_surface_id_allocation);
-
-  // Invoked when the application embedded at |window| is disconnected. In other
-  // words the embedded app closes the connection to the server. This is called
-  // on the connection that created |window| as well as any ancestors that have
-  // the embed root policy.
-  OnEmbeddedAppDisconnected(uint64 window);
-
-  // Sent when another connection is embedded in the Window this connection was
-  // previously embedded in. See Embed() for more information.
-  OnUnembed(uint64 window);
-
-  // Sent when capture changes. This is not sent if the client initiated the
-  // change.
-  OnCaptureChanged(uint64 new_capture, uint64 old_capture);
-
-  // This is called on the client that initiated a call to Embed(), and informs
-  // the owner (embedder) of the new FrameSinkId of the window (embedding
-  // results in changing the FrameSinkId).
-  OnFrameSinkIdAllocated(uint64 window, viz.mojom.FrameSinkId frame_sink_id);
-
-  // Called in response to NewTopLevelWindow() successfully completing.
-  // |parent_drawn| is true if the parent of the window is drawn, see
-  // OnDrawnStateChanged() for details. |display_id| identifies the display this
-  // window is on.
-  OnTopLevelCreated(
-      uint32 change_id,
-      WindowData data,
-      int64 display_id,
-      bool parent_drawn,
-      viz.mojom.LocalSurfaceIdAllocation local_surface_id_allocation);
-
-  // Invoked when a window's bounds have changed. |state| is supplied for roots,
-  // and SHOW_STATE_DEFAULT for non-roots. It allows the client to
-  // simultaneously update both bounds and show state, e.g. after a maximize
-  // operation. |local_surface_id_allocation| is only supplied for roots.
-  OnWindowBoundsChanged(
-      uint64 window,
-      gfx.mojom.Rect new_bounds,
-      ui.mojom.WindowShowState state,
-      viz.mojom.LocalSurfaceIdAllocation? local_surface_id_allocation);
-
-  OnWindowTransformChanged(uint64 window,
-                           gfx.mojom.Transform new_transform);
-
-  OnTransientWindowAdded(uint64 window_id,
-                         uint64 transient_window_id);
-
-  OnTransientWindowRemoved(uint64 window_id,
-                           uint64 transient_window_id);
-
-  // Invoked when a change is done to the hierarchy. A value of 0 is used to
-  // identify a null window. For example, if the old_parent is NULL, 0 is
-  // supplied.
-  // If |window| was not visible to this client, but is visible now because
-  // |new_parent| is visible to this client, then |windows| contains details
-  // about |window|, and all its descendants. |windows| includes any windows
-  // the client may already know about, but did not know the parent because
-  // the parent was previously not visible to this client.
-  // This is not sent for hierarchy changes of windows not known to this client
-  // or not attached to the tree.
-  OnWindowHierarchyChanged(uint64 window,
-                           uint64 old_parent,
-                           uint64 new_parent,
-                           array<WindowData> windows);
-
-  // Invoked when the order of windows within a parent changes.
-  OnWindowReordered(uint64 window_id,
-                    uint64 relative_window_id,
-                    OrderDirection direction);
-
-  // Invoked when a window is deleted.
-  OnWindowDeleted(uint64 window);
-
-  // Invoked when the visibility of the specified window changes.
-  OnWindowVisibilityChanged(uint64 window, bool visible);
-
-  // Invoked when the window moves to a new display. This is only called on
-  // a top-level window or an embedded root.
-  OnWindowDisplayChanged(uint64 window, int64 display_id);
-
-  // Invoked when the drawn state of |window|'s parent changes. The drawn state
-  // is determined by the visibility of a Window and the Windows ancestors. A
-  // Window is drawn if all ancestors are visible, not drawn if any ancestor is
-  // hidden.
-  //
-  // The initial drawn state is communicated by way of OnTopLevelCreated() or
-  // OnEmbed().
-  //
-  // This function is only called for root Windows as the drawn state of all
-  // other windows can be determined from their parent.
-  OnWindowParentDrawnStateChanged(uint64 window, bool drawn);
-
-  // Invoked when a window property is changed. If this change is a removal,
-  // |new_data| is null.
-  OnWindowSharedPropertyChanged(uint64 window,
-                                string name,
-                                array<uint8>? new_data);
-
-  // Invoked when an event is targeted at the specified window. The client must
-  // call WindowTree::OnWindowInputEventAck() with the same |event_id| to ack
-  // that the event has been processed, and with an EventResult value to notify
-  // if the event was consumed. |matches_event_observer| is true if the event
-  // also matches the requested types passed via ObserveEventTypes(). The client
-  // must respond to ack these events, regardless of |matches_event_observer|.
-  OnWindowInputEvent(uint32 event_id,
-                     uint64 window,
-                     int64 display_id,
-                     ui.mojom.Event event,
-                     bool matches_event_observer);
-
-  // Called when an event not targeted at this client is observed, matching the
-  // event types passed to ObserveEventTypes(); see that function for details.
-  // Located events are always passed with the locations in screen coordinates.
-  // The client should not respond to ack these events.
-  OnObservedInputEvent(ui.mojom.Event event);
-
-  // Called in two distinct cases: when a window known to the connection gains
-  // focus, or when focus moves from a window known to the connection to a
-  // window not known to the connection. In the later case |focused_window_id|
-  // is 0. As with other functions this is only called if the client did not
-  // initiate the change.
-  OnWindowFocused(uint64 focused_window_id);
-
-  OnWindowCursorChanged(uint64 window_id, ui.mojom.Cursor cursor);
-
-  // Called when the mouse cursor enters a window on this connection for the
-  // first time, providing a list of available mime types. We want to send this
-  // set of data only one time, so this isn't part of OnDragEnter(), which
-  // occurs every time the mouse enters a window.
-  OnDragDropStart(map<string, array<uint8>> drag_data);
-
-  // Called when the mouse cursor enters a window that has opted into accepting
-  // drags through SetAcceptsDrags(), providing a list of available mime types.
-  // |location_in_root| is the location relative to the embed root of |window|.
-  // |location| is the location relative to |window|.
-  // If |window| is a top-level or embed root, then |location_in_root| is the
-  // same as |location|. Callback is supplied the supported operations.
-  OnDragEnter(uint64 window,
-              uint32 key_state,
-              gfx.mojom.PointF location_in_root,
-              gfx.mojom.PointF location,
-              uint32 effect_bitmask) => (uint32 supported_op_bitmask);
-
-  // Called when the pointer moves over the window after the initial DragEnter.
-  // Returns a bitmask of the supported operations at this location. See
-  // OnDragEnter() for details on coordinates.
-  OnDragOver(uint64 window,
-             uint32 key_state,
-             gfx.mojom.PointF location_in_root,
-             gfx.mojom.PointF location,
-             uint32 effect_bitmask) => (uint32 supported_op_bitmask);
-
-  // Called when the pointer leaves a window or if the drop is canceled.
-  OnDragLeave(uint64 window);
-
-  // Called when the drop occurs on a window. Returns the action taken. See
-  // OnDragEnter() for details on coordinates.
-  OnCompleteDrop(uint64 window,
-                 uint32 key_state,
-                 gfx.mojom.PointF location_in_root,
-                 gfx.mojom.PointF location,
-                 uint32 effect_bitmask) => (uint32 action_taken);
-
-  // Called on the client that requested PerformDragDrop() to return which drag
-  // action was completed. This is called instead of OnChangeCompleted().
-  OnPerformDragDropCompleted(uint32 change_id,
-                             bool success,
-                             uint32 action_taken);
-
-  // Called after OnCompleteDrop completes for every connection which received
-  // an OnDragDropStart() message. This signals that a client can forget the
-  // |drag_data| passed in via the first message.
-  OnDragDropDone();
-
-  // Called when the topmost window under the cursor/touch changes. The client
-  // receives at most two IDs for the topmost windows. The first one is for the
-  // topmost window. The second one is optionally for the second topmost window
-  // if the first one happens to be the current event target. The second one
-  // will be used by the client when they want to ignore the event target.
-  // Each ID can be 0 when the window is not hosted by the client.
-  OnTopmostWindowChanged(array<uint64> topmost_ids);
-
-  // A change initiated from the client has completed. See description of
-  // change ids for details.
-  OnChangeCompleted(uint32 change_id, bool success);
-
-  // The WindowManager is requesting the specified window to close. If the
-  // client allows the change it should delete the window.
-  RequestClose(uint64 window_id);
-
-  // Requests the ScreenProviderObserver from the client. See
-  // ScreenProviderObserver for details.
-  GetScreenProviderObserver(associated ScreenProviderObserver& observer);
-
-  // Called to send occlusion changes to client. |occlusion_changes| contains
-  // the changed info, with window id as its key and new occlusion state as its
-  // data. See also TrackOcclusionState on WindowTree.
-  OnOcclusionStatesChanged(map<uint64, OcclusionState> occlusion_changes);
-
-  // Cancels the ongoing touch gesture recognitions and clears related gesture
-  // recognition state of the window of |window_id| in the client.
-  CleanupGestureState(uint64 window_id);
-
-  // Called when the window manager may start resizing a window by a user
-  // gesture. For example, if the user clicks on a window-resize handle this may
-  // be called. This is followed by OnWindowResizeLoopEnded() when the loop is
-  // done. This function is only called for top-levels.
-  // OnWindowResizeLoopEnded() may not be called if the window is destroyed
-  // during the loop.
-  OnWindowResizeLoopStarted(uint64 window_id);
-  OnWindowResizeLoopEnded(uint64 window_id);
-};
-
-// Mus provides this interface as a way for clients to connect and obtain a
-// WindowTree handle with a supplied WindowTreeClient handle. The
-// WindowTreeClient has no roots, use NewTopLevelWindow() to create one.
-interface WindowTreeFactory {
-  CreateWindowTree(WindowTree& tree_request, WindowTreeClient client);
-};
diff --git a/storage/browser/fileapi/file_system_context_unittest.cc b/storage/browser/fileapi/file_system_context_unittest.cc
index 8cf9e34..b579dcd6 100644
--- a/storage/browser/fileapi/file_system_context_unittest.cc
+++ b/storage/browser/fileapi/file_system_context_unittest.cc
@@ -44,10 +44,9 @@
 
 GURL CreateRawFileSystemURL(const std::string& type_str,
                             const std::string& fs_id) {
-  std::string url_str = base::StringPrintf(
-      "filesystem:http://chromium.org/%s/%s/root/file",
-      type_str.c_str(),
-      fs_id.c_str());
+  std::string url_str =
+      base::StringPrintf("filesystem:http://chromium.org/%s/%s/root/file",
+                         type_str.c_str(), fs_id.c_str());
   return GURL(url_str);
 }
 
@@ -111,26 +110,21 @@
 
   // Cracking system external mount and isolated mount points should work.
   std::string isolated_name = "root";
-  std::string isolated_id =
+  IsolatedContext::ScopedFSHandle isolated_fs =
       IsolatedContext::GetInstance()->RegisterFileSystemForPath(
-          storage::kFileSystemTypeNativeLocal,
-          std::string(),
-          base::FilePath(DRIVE FPL("/test/isolated/root")),
-          &isolated_name);
+          storage::kFileSystemTypeNativeLocal, std::string(),
+          base::FilePath(DRIVE FPL("/test/isolated/root")), &isolated_name);
+  std::string isolated_id = isolated_fs.id();
   // Register system external mount point.
   ASSERT_TRUE(ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
-      "system",
-      storage::kFileSystemTypeNativeLocal,
-      FileSystemMountOption(),
+      "system", storage::kFileSystemTypeNativeLocal, FileSystemMountOption(),
       base::FilePath(DRIVE FPL("/test/sys/"))));
 
   FileSystemURL cracked_isolated = file_system_context->CrackURL(
       CreateRawFileSystemURL("isolated", isolated_id));
 
   ExpectFileSystemURLMatches(
-      cracked_isolated,
-      GURL(kTestOrigin),
-      storage::kFileSystemTypeIsolated,
+      cracked_isolated, GURL(kTestOrigin), storage::kFileSystemTypeIsolated,
       storage::kFileSystemTypeNativeLocal,
       base::FilePath(DRIVE FPL("/test/isolated/root/file"))
           .NormalizePathSeparators(),
@@ -143,9 +137,7 @@
       CreateRawFileSystemURL("external", "system"));
 
   ExpectFileSystemURLMatches(
-      cracked_external,
-      GURL(kTestOrigin),
-      storage::kFileSystemTypeExternal,
+      cracked_external, GURL(kTestOrigin), storage::kFileSystemTypeExternal,
       storage::kFileSystemTypeNativeLocal,
       base::FilePath(DRIVE FPL("/test/sys/root/file"))
           .NormalizePathSeparators(),
@@ -163,9 +155,7 @@
 
   // Register system external mount point.
   ASSERT_TRUE(mount_points->RegisterFileSystem(
-      "system",
-      storage::kFileSystemTypeNativeLocal,
-      FileSystemMountOption(),
+      "system", storage::kFileSystemTypeNativeLocal, FileSystemMountOption(),
       base::FilePath(DRIVE FPL("/test/sys/"))));
 
   scoped_refptr<FileSystemContext> file_system_context(
@@ -180,9 +170,7 @@
       CreateRawFileSystemURL("external", "system"));
 
   ExpectFileSystemURLMatches(
-      cracked_external,
-      GURL(kTestOrigin),
-      storage::kFileSystemTypeExternal,
+      cracked_external, GURL(kTestOrigin), storage::kFileSystemTypeExternal,
       storage::kFileSystemTypeNativeLocal,
       base::FilePath(DRIVE FPL("/test/sys/root/file"))
           .NormalizePathSeparators(),
@@ -201,36 +189,29 @@
 
   // Register an isolated mount point.
   std::string isolated_file_system_name = "root";
-  const std::string kIsolatedFileSystemID =
+  IsolatedContext::ScopedFSHandle isolated_fs =
       IsolatedContext::GetInstance()->RegisterFileSystemForPath(
-          storage::kFileSystemTypeNativeLocal,
-          std::string(),
+          storage::kFileSystemTypeNativeLocal, std::string(),
           base::FilePath(DRIVE FPL("/test/isolated/root")),
           &isolated_file_system_name);
+  const std::string kIsolatedFileSystemID = isolated_fs.id();
   // Register system external mount point.
   ASSERT_TRUE(ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
-      "system",
-      storage::kFileSystemTypeDrive,
-      FileSystemMountOption(),
+      "system", storage::kFileSystemTypeDrive, FileSystemMountOption(),
       base::FilePath(DRIVE FPL("/test/sys/"))));
   ASSERT_TRUE(ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
-      "ext",
-      storage::kFileSystemTypeNativeLocal,
-      FileSystemMountOption(),
+      "ext", storage::kFileSystemTypeNativeLocal, FileSystemMountOption(),
       base::FilePath(DRIVE FPL("/test/ext"))));
   // Register a system external mount point with the same name/id as the
   // registered isolated mount point.
   ASSERT_TRUE(ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
-      kIsolatedFileSystemID,
-      storage::kFileSystemTypeRestrictedNativeLocal,
+      kIsolatedFileSystemID, storage::kFileSystemTypeRestrictedNativeLocal,
       FileSystemMountOption(),
       base::FilePath(DRIVE FPL("/test/system/isolated"))));
   // Add a mount points with the same name as a system mount point to
   // FileSystemContext's external mount points.
   ASSERT_TRUE(external_mount_points->RegisterFileSystem(
-      "ext",
-      storage::kFileSystemTypeNativeLocal,
-      FileSystemMountOption(),
+      "ext", storage::kFileSystemTypeNativeLocal, FileSystemMountOption(),
       base::FilePath(DRIVE FPL("/test/local/ext/"))));
 
   const GURL kTestOrigin = GURL("http://chromium.org/");
@@ -252,14 +233,15 @@
   const TestCase kTestCases[] = {
       // Following should not be handled by the url crackers:
       {
-       "pers_mount", "persistent", true /* is_valid */,
-       storage::kFileSystemTypePersistent, storage::kFileSystemTypePersistent,
-       FPL("pers_mount/root/file"), std::string() /* filesystem id */
+          "pers_mount", "persistent", true /* is_valid */,
+          storage::kFileSystemTypePersistent,
+          storage::kFileSystemTypePersistent, FPL("pers_mount/root/file"),
+          std::string() /* filesystem id */
       },
       {
-       "temp_mount", "temporary", true /* is_valid */,
-       storage::kFileSystemTypeTemporary, storage::kFileSystemTypeTemporary,
-       FPL("temp_mount/root/file"), std::string() /* filesystem id */
+          "temp_mount", "temporary", true /* is_valid */,
+          storage::kFileSystemTypeTemporary, storage::kFileSystemTypeTemporary,
+          FPL("temp_mount/root/file"), std::string() /* filesystem id */
       },
       // Should be cracked by isolated mount points:
       {kIsolatedFileSystemID, "isolated", true /* is_valid */,
@@ -292,8 +274,8 @@
 
   for (size_t i = 0; i < base::size(kTestCases); ++i) {
     const base::FilePath virtual_path =
-        base::FilePath::FromUTF8Unsafe(
-            kTestCases[i].root).Append(kVirtualPathNoRoot);
+        base::FilePath::FromUTF8Unsafe(kTestCases[i].root)
+            .Append(kVirtualPathNoRoot);
 
     GURL raw_url =
         CreateRawFileSystemURL(kTestCases[i].type_str, kTestCases[i].root);
@@ -307,9 +289,7 @@
       continue;
 
     ExpectFileSystemURLMatches(
-        cracked_url,
-        GURL(kTestOrigin),
-        kTestCases[i].expect_mount_type,
+        cracked_url, GURL(kTestOrigin), kTestCases[i].expect_mount_type,
         kTestCases[i].expect_type,
         base::FilePath(kTestCases[i].expect_path).NormalizePathSeparators(),
         virtual_path.NormalizePathSeparators(),
@@ -338,26 +318,23 @@
 
   // A request for an isolated mount point should NOT be served.
   std::string isolated_fs_name = "root";
-  std::string isolated_fs_id =
+  IsolatedContext::ScopedFSHandle isolated_fs =
       IsolatedContext::GetInstance()->RegisterFileSystemForPath(
-          storage::kFileSystemTypeNativeLocal,
-          std::string(),
-          base::FilePath(DRIVE FPL("/test/isolated/root")),
-          &isolated_fs_name);
-  cracked_url = context->CrackURL(
-      CreateRawFileSystemURL("isolated", isolated_fs_id));
+          storage::kFileSystemTypeNativeLocal, std::string(),
+          base::FilePath(DRIVE FPL("/test/isolated/root")), &isolated_fs_name);
+  std::string isolated_fs_id = isolated_fs.id();
+  cracked_url =
+      context->CrackURL(CreateRawFileSystemURL("isolated", isolated_fs_id));
   EXPECT_EQ(storage::kFileSystemTypeIsolated, cracked_url.mount_type());
   EXPECT_FALSE(context->CanServeURLRequest(cracked_url));
 
   // A request for an external mount point should be served.
   const std::string kExternalMountName = "ext_mount";
   ASSERT_TRUE(ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
-      kExternalMountName,
-      storage::kFileSystemTypeDrive,
-      FileSystemMountOption(),
-      base::FilePath()));
-  cracked_url = context->CrackURL(
-      CreateRawFileSystemURL("external", kExternalMountName));
+      kExternalMountName, storage::kFileSystemTypeDrive,
+      FileSystemMountOption(), base::FilePath()));
+  cracked_url =
+      context->CrackURL(CreateRawFileSystemURL("external", kExternalMountName));
   EXPECT_EQ(storage::kFileSystemTypeExternal, cracked_url.mount_type());
   EXPECT_TRUE(context->CanServeURLRequest(cracked_url));
 
diff --git a/storage/browser/fileapi/isolated_context.cc b/storage/browser/fileapi/isolated_context.cc
index 6d930b8..acff276 100644
--- a/storage/browser/fileapi/isolated_context.cc
+++ b/storage/browser/fileapi/isolated_context.cc
@@ -58,8 +58,8 @@
 IsolatedContext::FileInfoSet::FileInfoSet() = default;
 IsolatedContext::FileInfoSet::~FileInfoSet() = default;
 
-bool IsolatedContext::FileInfoSet::AddPath(
-    const base::FilePath& path, std::string* registered_name) {
+bool IsolatedContext::FileInfoSet::AddPath(const base::FilePath& path,
+                                           std::string* registered_name) {
   // The given path should not contain any '..' and should be absolute.
   if (path.ReferencesParent() || !path.IsAbsolute())
     return false;
@@ -87,23 +87,64 @@
   return true;
 }
 
-bool IsolatedContext::FileInfoSet::AddPathWithName(
-    const base::FilePath& path, const std::string& name) {
+bool IsolatedContext::FileInfoSet::AddPathWithName(const base::FilePath& path,
+                                                   const std::string& name) {
   // The given path should not contain any '..' and should be absolute.
   if (path.ReferencesParent() || !path.IsAbsolute())
     return false;
-  return fileset_.insert(
-      MountPointInfo(name, path.NormalizePathSeparators())).second;
+  return fileset_.insert(MountPointInfo(name, path.NormalizePathSeparators()))
+      .second;
+}
+
+//--------------------------------------------------------------------------
+
+IsolatedContext::ScopedFSHandle::ScopedFSHandle(std::string file_system_id)
+    : file_system_id_(std::move(file_system_id)) {
+  if (!file_system_id_.empty())
+    IsolatedContext::GetInstance()->AddReference(file_system_id_);
+}
+
+IsolatedContext::ScopedFSHandle::~ScopedFSHandle() {
+  if (!file_system_id_.empty())
+    IsolatedContext::GetInstance()->RemoveReference(file_system_id_);
+}
+
+IsolatedContext::ScopedFSHandle::ScopedFSHandle(const ScopedFSHandle& other)
+    : ScopedFSHandle(other.id()) {}
+
+IsolatedContext::ScopedFSHandle::ScopedFSHandle(ScopedFSHandle&& other)
+    : file_system_id_(std::move(other.file_system_id_)) {
+  // Moving from a string leaves it in a unspecified state, we need to make sure
+  // to leave it empty, so explicitly clear it.
+  other.file_system_id_.clear();
+}
+
+IsolatedContext::ScopedFSHandle& IsolatedContext::ScopedFSHandle::operator=(
+    const ScopedFSHandle& other) {
+  if (!file_system_id_.empty())
+    IsolatedContext::GetInstance()->RemoveReference(file_system_id_);
+  file_system_id_ = other.id();
+  if (!file_system_id_.empty())
+    IsolatedContext::GetInstance()->AddReference(file_system_id_);
+  return *this;
+}
+
+IsolatedContext::ScopedFSHandle& IsolatedContext::ScopedFSHandle::operator=(
+    ScopedFSHandle&& other) {
+  if (!file_system_id_.empty())
+    IsolatedContext::GetInstance()->RemoveReference(file_system_id_);
+  file_system_id_ = std::move(other.file_system_id_);
+  // Moving from a string leaves it in a unspecified state, we need to make sure
+  // to leave it empty, so explicitly clear it.
+  other.file_system_id_.clear();
+  return *this;
 }
 
 //--------------------------------------------------------------------------
 
 class IsolatedContext::Instance {
  public:
-  enum PathType {
-    PLATFORM_PATH,
-    VIRTUAL_PATH
-  };
+  enum PathType { PLATFORM_PATH, VIRTUAL_PATH };
 
   // For a single-path isolated file system, which could be registered by
   // IsolatedContext::RegisterFileSystemForPath() or
@@ -167,10 +208,7 @@
 
 IsolatedContext::Instance::Instance(FileSystemType type,
                                     const std::set<MountPointInfo>& files)
-    : type_(type),
-      path_type_(PLATFORM_PATH),
-      files_(files),
-      ref_counts_(0) {
+    : type_(type), path_type_(PLATFORM_PATH), files_(files), ref_counts_(0) {
   DCHECK(!IsSinglePathIsolatedFileSystem(type_));
 }
 
@@ -224,14 +262,14 @@
   return filesystem_id;
 }
 
-std::string IsolatedContext::RegisterFileSystemForPath(
+IsolatedContext::ScopedFSHandle IsolatedContext::RegisterFileSystemForPath(
     FileSystemType type,
     const std::string& filesystem_id,
     const base::FilePath& path_in,
     std::string* register_name) {
   base::FilePath path(path_in.NormalizePathSeparators());
   if (path.ReferencesParent() || !path.IsAbsolute())
-    return std::string();
+    return ScopedFSHandle();
   std::string name;
   if (register_name && !register_name->empty()) {
     name = *register_name;
@@ -241,12 +279,16 @@
       register_name->assign(name);
   }
 
-  base::AutoLock locker(lock_);
-  std::string new_id = GetNewFileSystemId();
-  instance_map_[new_id] = std::make_unique<Instance>(
-      type, filesystem_id, MountPointInfo(name, path), Instance::PLATFORM_PATH);
-  path_to_id_map_[path].insert(new_id);
-  return new_id;
+  std::string new_id;
+  {
+    base::AutoLock locker(lock_);
+    new_id = GetNewFileSystemId();
+    instance_map_[new_id] = std::make_unique<Instance>(
+        type, filesystem_id, MountPointInfo(name, path),
+        Instance::PLATFORM_PATH);
+    path_to_id_map_[path].insert(new_id);
+  }
+  return ScopedFSHandle(new_id);
 }
 
 std::string IsolatedContext::RegisterFileSystemForVirtualPath(
@@ -276,8 +318,8 @@
   return UnregisterFileSystem(filesystem_id);
 }
 
-bool IsolatedContext::GetRegisteredPath(
-    const std::string& filesystem_id, base::FilePath* path) const {
+bool IsolatedContext::GetRegisteredPath(const std::string& filesystem_id,
+                                        base::FilePath* path) const {
   DCHECK(path);
   base::AutoLock locker(lock_);
   auto found = instance_map_.find(filesystem_id);
@@ -401,8 +443,7 @@
   if (found == instance_map_.end() ||
       found->second->type() != kFileSystemTypeDragged)
     return false;
-  files->assign(found->second->files().begin(),
-                found->second->files().end());
+  files->assign(found->second->files().begin(), found->second->files().end());
   return true;
 }
 
diff --git a/storage/browser/fileapi/isolated_context.h b/storage/browser/fileapi/isolated_context.h
index 5ad38f6a7..b29feae 100644
--- a/storage/browser/fileapi/isolated_context.h
+++ b/storage/browser/fileapi/isolated_context.h
@@ -61,6 +61,27 @@
     std::set<MountPointInfo> fileset_;
   };
 
+  // A handle to a Isolated File System, which properly refcounts the file
+  // system with the IsolatedContext when handles are created and destroyed.
+  class COMPONENT_EXPORT(STORAGE_BROWSER) ScopedFSHandle {
+   public:
+    ScopedFSHandle() = default;
+    // Like scoped_refptr, creating a new handle increases the refcount of the
+    // file system being referenced.
+    explicit ScopedFSHandle(std::string file_system_id);
+    ~ScopedFSHandle();
+    ScopedFSHandle(const ScopedFSHandle& other);
+    ScopedFSHandle(ScopedFSHandle&& other);
+    ScopedFSHandle& operator=(const ScopedFSHandle& other);
+    ScopedFSHandle& operator=(ScopedFSHandle&& other);
+
+    const std::string& id() const { return file_system_id_; }
+    bool is_valid() const { return !file_system_id_.empty(); }
+
+   private:
+    std::string file_system_id_;
+  };
+
   // The instance is lazily created per browser process.
   static IsolatedContext* GetInstance();
 
@@ -93,15 +114,16 @@
   std::string RegisterDraggedFileSystem(const FileInfoSet& files);
 
   // Registers a new isolated filesystem for a given |path| of filesystem
-  // |type| filesystem with |filesystem_id| and returns a new filesystem ID.
+  // |type| filesystem with |filesystem_id| and returns a handle for a new
+  // filesystem ID.
   // |path| must be an absolute path which has no parent references ('..').
   // If |register_name| is non-null and has non-empty string the path is
   // registered as the given |register_name|, otherwise it is populated
   // with the name internally assigned to the path.
-  std::string RegisterFileSystemForPath(FileSystemType type,
-                                        const std::string& filesystem_id,
-                                        const base::FilePath& path,
-                                        std::string* register_name);
+  ScopedFSHandle RegisterFileSystemForPath(FileSystemType type,
+                                           const std::string& filesystem_id,
+                                           const base::FilePath& path,
+                                           std::string* register_name);
 
   // Registers a virtual filesystem. This is different from
   // RegisterFileSystemForPath because register_name is required, and
diff --git a/storage/browser/fileapi/isolated_context_unittest.cc b/storage/browser/fileapi/isolated_context_unittest.cc
index b3c7354..6582748 100644
--- a/storage/browser/fileapi/isolated_context_unittest.cc
+++ b/storage/browser/fileapi/isolated_context_unittest.cc
@@ -125,56 +125,59 @@
   // Deref the current one and registering a new one.
   isolated_context()->RemoveReference(id_);
 
-  std::string id2 = isolated_context()->RegisterFileSystemForPath(
-      kFileSystemTypeNativeLocal, std::string(),
-      base::FilePath(DRIVE FPL("/foo")), nullptr);
+  IsolatedContext::ScopedFSHandle fs2 =
+      isolated_context()->RegisterFileSystemForPath(
+          kFileSystemTypeNativeLocal, std::string(),
+          base::FilePath(DRIVE FPL("/foo")), nullptr);
 
   // Make sure the GetDraggedFileInfo returns false for both ones.
-  ASSERT_FALSE(isolated_context()->GetDraggedFileInfo(id2, &toplevels));
+  ASSERT_FALSE(isolated_context()->GetDraggedFileInfo(fs2.id(), &toplevels));
   ASSERT_FALSE(isolated_context()->GetDraggedFileInfo(id_, &toplevels));
 
   // Make sure the GetRegisteredPath returns true only for the new one.
   ASSERT_FALSE(isolated_context()->GetRegisteredPath(id_, &path));
-  ASSERT_TRUE(isolated_context()->GetRegisteredPath(id2, &path));
+  ASSERT_TRUE(isolated_context()->GetRegisteredPath(fs2.id(), &path));
 
   // Try registering three more file systems for the same path as id2.
-  std::string id3 = isolated_context()->RegisterFileSystemForPath(
-      kFileSystemTypeNativeLocal, std::string(), path, nullptr);
-  std::string id4 = isolated_context()->RegisterFileSystemForPath(
-      kFileSystemTypeNativeLocal, std::string(), path, nullptr);
-  std::string id5 = isolated_context()->RegisterFileSystemForPath(
-      kFileSystemTypeNativeLocal, std::string(), path, nullptr);
+  IsolatedContext::ScopedFSHandle fs3 =
+      isolated_context()->RegisterFileSystemForPath(
+          kFileSystemTypeNativeLocal, std::string(), path, nullptr);
+  IsolatedContext::ScopedFSHandle fs4 =
+      isolated_context()->RegisterFileSystemForPath(
+          kFileSystemTypeNativeLocal, std::string(), path, nullptr);
+  IsolatedContext::ScopedFSHandle fs5 =
+      isolated_context()->RegisterFileSystemForPath(
+          kFileSystemTypeNativeLocal, std::string(), path, nullptr);
 
   // Remove file system for id4.
-  isolated_context()->AddReference(id4);
-  isolated_context()->RemoveReference(id4);
+  fs4 = IsolatedContext::ScopedFSHandle();
 
   // Only id4 should become invalid now.
-  ASSERT_TRUE(isolated_context()->GetRegisteredPath(id2, &path));
-  ASSERT_TRUE(isolated_context()->GetRegisteredPath(id3, &path));
-  ASSERT_FALSE(isolated_context()->GetRegisteredPath(id4, &path));
-  ASSERT_TRUE(isolated_context()->GetRegisteredPath(id5, &path));
+  ASSERT_TRUE(isolated_context()->GetRegisteredPath(fs2.id(), &path));
+  ASSERT_TRUE(isolated_context()->GetRegisteredPath(fs3.id(), &path));
+  ASSERT_FALSE(isolated_context()->GetRegisteredPath(fs4.id(), &path));
+  ASSERT_TRUE(isolated_context()->GetRegisteredPath(fs5.id(), &path));
 
   // Revoke file system id5, after adding multiple references.
-  isolated_context()->AddReference(id5);
-  isolated_context()->AddReference(id5);
-  isolated_context()->AddReference(id5);
-  isolated_context()->RevokeFileSystem(id5);
+  isolated_context()->AddReference(fs5.id());
+  isolated_context()->AddReference(fs5.id());
+  isolated_context()->AddReference(fs5.id());
+  isolated_context()->RevokeFileSystem(fs5.id());
 
   // No matter how many references we add id5 must be invalid now.
-  ASSERT_TRUE(isolated_context()->GetRegisteredPath(id2, &path));
-  ASSERT_TRUE(isolated_context()->GetRegisteredPath(id3, &path));
-  ASSERT_FALSE(isolated_context()->GetRegisteredPath(id4, &path));
-  ASSERT_FALSE(isolated_context()->GetRegisteredPath(id5, &path));
+  ASSERT_TRUE(isolated_context()->GetRegisteredPath(fs2.id(), &path));
+  ASSERT_TRUE(isolated_context()->GetRegisteredPath(fs3.id(), &path));
+  ASSERT_FALSE(isolated_context()->GetRegisteredPath(fs4.id(), &path));
+  ASSERT_FALSE(isolated_context()->GetRegisteredPath(fs5.id(), &path));
 
   // Revoke the file systems by path.
   isolated_context()->RevokeFileSystemByPath(path);
 
   // Now all the file systems associated to the path must be invalid.
-  ASSERT_FALSE(isolated_context()->GetRegisteredPath(id2, &path));
-  ASSERT_FALSE(isolated_context()->GetRegisteredPath(id3, &path));
-  ASSERT_FALSE(isolated_context()->GetRegisteredPath(id4, &path));
-  ASSERT_FALSE(isolated_context()->GetRegisteredPath(id5, &path));
+  ASSERT_FALSE(isolated_context()->GetRegisteredPath(fs2.id(), &path));
+  ASSERT_FALSE(isolated_context()->GetRegisteredPath(fs3.id(), &path));
+  ASSERT_FALSE(isolated_context()->GetRegisteredPath(fs4.id(), &path));
+  ASSERT_FALSE(isolated_context()->GetRegisteredPath(fs5.id(), &path));
 }
 
 TEST_F(IsolatedContextTest, CrackWithRelativePaths) {
diff --git a/storage/browser/fileapi/transient_file_util_unittest.cc b/storage/browser/fileapi/transient_file_util_unittest.cc
index 0567b32..4994ec5c 100644
--- a/storage/browser/fileapi/transient_file_util_unittest.cc
+++ b/storage/browser/fileapi/transient_file_util_unittest.cc
@@ -42,19 +42,19 @@
 
   void CreateAndRegisterTemporaryFile(
       FileSystemURL* file_url,
-      base::FilePath* file_path) {
+      base::FilePath* file_path,
+      storage::IsolatedContext::ScopedFSHandle* filesystem) {
     EXPECT_TRUE(base::CreateTemporaryFileInDir(data_dir_.GetPath(), file_path));
     storage::IsolatedContext* isolated_context =
         storage::IsolatedContext::GetInstance();
     std::string name = "tmp";
-    std::string fsid = isolated_context->RegisterFileSystemForPath(
-        storage::kFileSystemTypeForTransientFile,
-        std::string(),
-        *file_path,
+    *filesystem = isolated_context->RegisterFileSystemForPath(
+        storage::kFileSystemTypeForTransientFile, std::string(), *file_path,
         &name);
-    ASSERT_TRUE(!fsid.empty());
-    base::FilePath virtual_path = isolated_context->CreateVirtualRootPath(
-        fsid).AppendASCII(name);
+    ASSERT_TRUE(filesystem->is_valid());
+    base::FilePath virtual_path =
+        isolated_context->CreateVirtualRootPath(filesystem->id())
+            .AppendASCII(name);
     *file_url = file_system_context_->CreateCrackedFileSystemURL(
         GURL("http://foo"), storage::kFileSystemTypeIsolated, virtual_path);
   }
@@ -80,8 +80,9 @@
 TEST_F(TransientFileUtilTest, TransientFile) {
   FileSystemURL temp_url;
   base::FilePath temp_path;
+  storage::IsolatedContext::ScopedFSHandle filesystem;
 
-  CreateAndRegisterTemporaryFile(&temp_url, &temp_path);
+  CreateAndRegisterTemporaryFile(&temp_url, &temp_path, &filesystem);
 
   base::File::Error error;
   base::File::Info file_info;
@@ -103,8 +104,8 @@
     // The file should be still there.
     ASSERT_TRUE(base::PathExists(temp_path));
     ASSERT_EQ(base::File::FILE_OK,
-              file_util()->GetFileInfo(NewOperationContext().get(),
-                                       temp_url, &file_info, &path));
+              file_util()->GetFileInfo(NewOperationContext().get(), temp_url,
+                                       &file_info, &path));
     ASSERT_EQ(temp_path, path);
     ASSERT_FALSE(file_info.is_directory);
   }
@@ -115,8 +116,8 @@
   // Now the temporary file and the transient filesystem must be gone too.
   ASSERT_FALSE(base::PathExists(temp_path));
   ASSERT_EQ(base::File::FILE_ERROR_NOT_FOUND,
-            file_util()->GetFileInfo(NewOperationContext().get(),
-                                     temp_url, &file_info, &path));
+            file_util()->GetFileInfo(NewOperationContext().get(), temp_url,
+                                     &file_info, &path));
 }
 
 }  // namespace content
diff --git a/testing/buildbot/chromium.clang.json b/testing/buildbot/chromium.clang.json
index bf56458..3c0a9b8 100644
--- a/testing/buildbot/chromium.clang.json
+++ b/testing/buildbot/chromium.clang.json
@@ -7216,14 +7216,46 @@
     "gtest_tests": [
       {
         "args": [
-          "--tool=asan"
+          "--tool=asan",
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
         ],
         "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+          "args": [
+            "--bucket",
+            "chromium-result-details",
+            "--test-name",
+            "base_unittests"
+          ],
+          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
         "swarming": {
-          "can_use_on_swarming_builders": false
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "device_os": "MMB29Q",
+              "device_os_type": "userdebug",
+              "device_type": "bullhead",
+              "os": "Android"
+            }
+          ],
+          "output_links": [
+            {
+              "link": [
+                "https://luci-logdog.appspot.com/v/?s",
+                "=android%2Fswarming%2Flogcats%2F",
+                "${TASK_ID}%2F%2B%2Funified_logcats"
+              ],
+              "name": "shard #${SHARD_INDEX} logcats"
+            }
+          ]
         },
         "test": "base_unittests"
       }
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index 7f7326f7..d1ed8b4 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -776,7 +776,16 @@
           'gtest_tests': 'chromium_android_asan_gtests',
           'junit_tests': 'chromium_android_asan_junit_tests',
         },
-        'use_swarming': False,
+        'swarming': {
+          'dimension_sets': [
+            {
+              'device_os': 'MMB29Q',
+              'device_type': 'bullhead',
+              'os': 'Android',
+            },
+          ],
+        },
+        'os_type': 'android',
       },
       'ToTAndroidCFI': {
         'swarming': {
diff --git a/third_party/blink/renderer/core/display_lock/display_lock_context.cc b/third_party/blink/renderer/core/display_lock/display_lock_context.cc
index 03d86ba6..2aede93 100644
--- a/third_party/blink/renderer/core/display_lock/display_lock_context.cc
+++ b/third_party/blink/renderer/core/display_lock/display_lock_context.cc
@@ -829,7 +829,7 @@
   // forcing an update from the ancestor locks seems inefficient. For now, we
   // just optimistically assume that we have all of the right containment in
   // place. See crbug.com/926276 for more information.
-  if (IsElementDirtyForStyleRecalc())
+  if (element_->NeedsStyleRecalc())
     return true;
 
   // If we have a layout object, check that since it's a more authoritative
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc
index 91bd8ed..0b067e0 100644
--- a/third_party/blink/renderer/core/dom/element.cc
+++ b/third_party/blink/renderer/core/dom/element.cc
@@ -175,23 +175,28 @@
   DisplayLockStyleScope(DisplayLockContext* context)
       : context_(context),
         should_update_self_(!context ||
-                            context->ShouldStyle(DisplayLockContext::kSelf)),
-        should_update_children_(
-            !context || context->ShouldStyle(DisplayLockContext::kChildren)) {}
+                            context->ShouldStyle(DisplayLockContext::kSelf)) {}
+
   ~DisplayLockStyleScope() {
     if (!context_)
       return;
     if (did_update_children_)
       context_->DidStyle(DisplayLockContext::kChildren);
-    // There is no other condition to block us form updating self, so if we
-    // "should" update, then we "did" update.
-    if (should_update_self_)
-      context_->DidStyle(DisplayLockContext::kSelf);
   }
 
   bool ShouldUpdateSelfStyle() const { return should_update_self_; }
-  bool ShouldUpdateChildStyle() const { return should_update_children_; }
+  bool ShouldUpdateChildStyle() const {
+    // We can't calculate this on construction time, because the element might
+    // get unlocked after self-style calculation due to lack of containment,
+    // which might change the value of ShouldStyle(children).
+    return !context_ || context_->ShouldStyle(DisplayLockContext::kChildren);
+  }
   void DidUpdateChildStyle() { did_update_children_ = true; }
+  void DidUpdateSelfStyle() {
+    DCHECK(should_update_self_);
+    if (context_)
+      context_->DidStyle(DisplayLockContext::kSelf);
+  }
 
   void NotifyUpdateWasBlocked(DisplayLockContext::StyleType style) {
     DCHECK(!ShouldUpdateChildStyle());
@@ -201,7 +206,6 @@
  private:
   UntracedMember<DisplayLockContext> context_;
   const bool should_update_self_;
-  const bool should_update_children_;
   bool did_update_children_ = false;
 };
 
@@ -2357,6 +2361,9 @@
       child_change = child_change.ForceRecalcDescendants();
     ClearNeedsStyleRecalc();
   }
+  // We're done with self-style, notify so that containment checks etc for
+  // display locking can happen..
+  display_lock_style_scope.DidUpdateSelfStyle();
 
   if (child_change.TraversePseudoElements(*this)) {
     UpdatePseudoElement(kPseudoIdBackdrop, child_change);
@@ -2374,7 +2381,6 @@
     } else {
       RecalcDescendantStyles(child_change);
     }
-    display_lock_style_scope.DidUpdateChildStyle();
   }
 
   if (child_change.TraversePseudoElements(*this)) {
@@ -2389,6 +2395,8 @@
 
   if (should_update_child_style) {
     ClearChildNeedsStyleRecalc();
+    // We've updated all the children that needs an update (might be 0).
+    display_lock_style_scope.DidUpdateChildStyle();
   } else if (child_change.RecalcChildren()) {
     // If we should've calculated the style for children but was blocked,
     // notify so that we'd come back.
diff --git a/third_party/blink/renderer/core/editing/selection_modifier_line.cc b/third_party/blink/renderer/core/editing/selection_modifier_line.cc
index 80bae8c..a15df39 100644
--- a/third_party/blink/renderer/core/editing/selection_modifier_line.cc
+++ b/third_party/blink/renderer/core/editing/selection_modifier_line.cc
@@ -34,22 +34,210 @@
 #include "third_party/blink/renderer/core/editing/inline_box_position.h"
 #include "third_party/blink/renderer/core/editing/visible_position.h"
 #include "third_party/blink/renderer/core/editing/visible_units.h"
+#include "third_party/blink/renderer/core/layout/api/line_layout_api_shim.h"
 #include "third_party/blink/renderer/core/layout/api/line_layout_block_flow.h"
 #include "third_party/blink/renderer/core/layout/line/root_inline_box.h"
+#include "third_party/blink/renderer/core/layout/ng/inline/ng_line_utils.h"
+#include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h"
+#include "third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h"
 
 namespace blink {
 
 namespace {
 
-const RootInlineBox* ComputeContainingLineBox(const VisiblePosition& position) {
+// Abstracts similarities between RootInlineBox and NGPhysicalLineBoxFragment
+class AbstractLineBox {
+  STACK_ALLOCATED();
+
+ public:
+  AbstractLineBox() = default;
+
+  static AbstractLineBox CreateFor(const VisiblePosition&);
+
+  bool IsNull() const { return type_ == Type::kNull; }
+
+  bool CanBeCaretContainer() const {
+    DCHECK(!IsNull());
+    // We want to skip zero height boxes.
+    // This could happen in case it is a TrailingFloatsRootInlineBox.
+    if (IsOldLayout()) {
+      return GetRootInlineBox().LogicalHeight() &&
+             GetRootInlineBox().FirstLeafChild();
+    }
+    if (GetLineBoxFragment().IsEmptyLineBox())
+      return false;
+    const PhysicalSize physical_size = GetLineBoxFragment().Size();
+    const LogicalSize logical_size = physical_size.ConvertToLogical(
+        GetLineBoxFragment().Style().GetWritingMode());
+    if (!logical_size.block_size)
+      return false;
+    // Use |ClosestLeafChildForPoint| to check if there's any leaf child.
+    return GetLineBoxFragment().ClosestLeafChildForPoint(PhysicalOffset(),
+                                                         false);
+  }
+
+  AbstractLineBox PreviousLine() const {
+    DCHECK(!IsNull());
+    if (IsOldLayout()) {
+      const RootInlineBox* previous_root = GetRootInlineBox().PrevRootBox();
+      return previous_root ? AbstractLineBox(*previous_root)
+                           : AbstractLineBox();
+    }
+    const auto children = ng_box_fragment_->Children();
+    for (wtf_size_t i = ng_child_index_; i;) {
+      --i;
+      if (!children[i]->IsLineBox())
+        continue;
+      return AbstractLineBox(*ng_box_fragment_, i);
+    }
+    return AbstractLineBox();
+  }
+
+  AbstractLineBox NextLine() const {
+    DCHECK(!IsNull());
+    if (IsOldLayout()) {
+      const RootInlineBox* next_root = GetRootInlineBox().NextRootBox();
+      return next_root ? AbstractLineBox(*next_root) : AbstractLineBox();
+    }
+    const auto children = ng_box_fragment_->Children();
+    for (wtf_size_t i = ng_child_index_ + 1; i < children.size(); ++i) {
+      if (!children[i]->IsLineBox())
+        continue;
+      return AbstractLineBox(*ng_box_fragment_, i);
+    }
+    return AbstractLineBox();
+  }
+
+  LayoutPoint AbsoluteLineDirectionPointToLocalPointInBlock(
+      LayoutUnit line_direction_point) {
+    DCHECK(!IsNull());
+    const LayoutBlockFlow& containing_block = GetBlock();
+    FloatPoint absolute_block_point =
+        containing_block.LocalToAbsolute(FloatPoint());
+    if (containing_block.HasOverflowClip()) {
+      absolute_block_point -=
+          FloatSize(containing_block.ScrolledContentOffset());
+    }
+
+    if (containing_block.Style()->IsHorizontalWritingMode()) {
+      return LayoutPoint(
+          LayoutUnit(line_direction_point - absolute_block_point.X()),
+          BlockDirectionPointInLine());
+    }
+
+    return LayoutPoint(
+        BlockDirectionPointInLine(),
+        LayoutUnit(line_direction_point - absolute_block_point.Y()));
+  }
+
+  const LayoutObject* ClosestLeafChildForPoint(
+      const LayoutPoint& point,
+      bool only_editable_leaves) const {
+    DCHECK(!IsNull());
+    if (IsOldLayout()) {
+      return GetRootInlineBox().ClosestLeafChildForPoint(point,
+                                                         only_editable_leaves);
+    }
+    const LayoutSize unit_square(1, 1);
+    const PhysicalRect physical_rect =
+        GetBlock().FlipForWritingMode(LayoutRect(point, unit_square));
+    const PhysicalOffset physical_point = physical_rect.offset;
+    const PhysicalOffset local_physical_point =
+        physical_point - ng_box_fragment_->Children()[ng_child_index_].offset;
+    return GetLineBoxFragment().ClosestLeafChildForPoint(local_physical_point,
+                                                         only_editable_leaves);
+  }
+
+ private:
+  explicit AbstractLineBox(const RootInlineBox& root_inline_box)
+      : root_inline_box_(&root_inline_box), type_(Type::kOldLayout) {}
+
+  AbstractLineBox(const NGPhysicalBoxFragment& box_fragment,
+                  wtf_size_t child_index)
+      : ng_box_fragment_(&box_fragment),
+        ng_child_index_(child_index),
+        type_(Type::kLayoutNG) {
+    DCHECK_LT(child_index, box_fragment.Children().size());
+    DCHECK(box_fragment.Children()[child_index]->IsLineBox());
+  }
+
+  const LayoutBlockFlow& GetBlock() const {
+    DCHECK(!IsNull());
+    if (IsOldLayout()) {
+      return *To<LayoutBlockFlow>(
+          LineLayoutAPIShim::LayoutObjectFrom(GetRootInlineBox().Block()));
+    }
+    DCHECK(ng_box_fragment_->GetLayoutObject());
+    return *To<LayoutBlockFlow>(ng_box_fragment_->GetLayoutObject());
+  }
+
+  LayoutUnit BlockDirectionPointInLine() const {
+    DCHECK(!IsNull());
+    if (IsOldLayout())
+      return GetRootInlineBox().BlockDirectionPointInLine();
+    const PhysicalOffset physical_offset =
+        ng_box_fragment_->Children()[ng_child_index_].offset;
+    const LogicalOffset logical_offset = physical_offset.ConvertToLogical(
+        ng_box_fragment_->Style().GetWritingMode(),
+        GetLineBoxFragment().BaseDirection(), ng_box_fragment_->Size(),
+        GetLineBoxFragment().Size());
+    return logical_offset.block_offset;
+  }
+
+  bool IsOldLayout() const { return type_ == Type::kOldLayout; }
+
+  bool IsLayoutNG() const { return type_ == Type::kLayoutNG; }
+
+  const RootInlineBox& GetRootInlineBox() const {
+    DCHECK(IsOldLayout());
+    return *root_inline_box_;
+  }
+
+  const NGPhysicalLineBoxFragment& GetLineBoxFragment() const {
+    DCHECK(IsLayoutNG());
+    return To<NGPhysicalLineBoxFragment>(
+        *ng_box_fragment_->Children()[ng_child_index_]);
+  }
+
+  enum class Type { kNull, kOldLayout, kLayoutNG };
+
+  const RootInlineBox* root_inline_box_ = nullptr;
+  const NGPhysicalBoxFragment* ng_box_fragment_ = nullptr;
+  wtf_size_t ng_child_index_ = 0u;
+  Type type_ = Type::kNull;
+};
+
+// static
+AbstractLineBox AbstractLineBox::CreateFor(const VisiblePosition& position) {
   if (position.IsNull() ||
       !position.DeepEquivalent().AnchorNode()->GetLayoutObject()) {
-    return nullptr;
+    return AbstractLineBox();
   }
-  const InlineBox* box = ComputeInlineBoxPosition(position).inline_box;
+
+  const PositionWithAffinity adjusted = ComputeInlineAdjustedPosition(position);
+  if (adjusted.IsNull())
+    return AbstractLineBox();
+
+  if (const NGPaintFragment* line_paint_fragment =
+          NGContainingLineBoxOf(adjusted)) {
+    const NGPhysicalBoxFragment& box_fragment = To<NGPhysicalBoxFragment>(
+        line_paint_fragment->Parent()->PhysicalFragment());
+    const NGPhysicalFragment& line_box_fragment =
+        line_paint_fragment->PhysicalFragment();
+    for (wtf_size_t i = 0; i < box_fragment.Children().size(); ++i) {
+      if (box_fragment.Children()[i].get() == &line_box_fragment)
+        return AbstractLineBox(box_fragment, i);
+    }
+    NOTREACHED();
+    return AbstractLineBox();
+  }
+
+  const InlineBox* box =
+      ComputeInlineBoxPositionForInlineAdjustedPosition(adjusted).inline_box;
   if (!box)
-    return nullptr;
-  return &box->Root();
+    return AbstractLineBox();
+  return AbstractLineBox(box->Root());
 }
 
 ContainerNode* HighestEditableRootOfNode(const Node& node) {
@@ -125,27 +313,6 @@
   return nullptr;
 }
 
-LayoutPoint AbsoluteLineDirectionPointToLocalPointInBlock(
-    const RootInlineBox* root,
-    LayoutUnit line_direction_point) {
-  DCHECK(root);
-  LineLayoutBlockFlow containing_block = root->Block();
-  FloatPoint absolute_block_point =
-      containing_block.LocalToAbsolute(FloatPoint());
-  if (containing_block.HasOverflowClip())
-    absolute_block_point -= FloatSize(containing_block.ScrolledContentOffset());
-
-  if (containing_block.IsHorizontalWritingMode()) {
-    return LayoutPoint(
-        LayoutUnit(line_direction_point - absolute_block_point.X()),
-        root->BlockDirectionPointInLine());
-  }
-
-  return LayoutPoint(
-      root->BlockDirectionPointInLine(),
-      LayoutUnit(line_direction_point - absolute_block_point.Y()));
-}
-
 bool InSameLine(const Node& node, const VisiblePosition& visible_position) {
   if (!node.GetLayoutObject())
     return true;
@@ -234,22 +401,20 @@
   if (!layout_object)
     return VisiblePosition();
 
-  const RootInlineBox* root = ComputeContainingLineBox(visible_position);
-  if (root) {
-    root = root->PrevRootBox();
-    // We want to skip zero height boxes.
-    // This could happen in case it is a TrailingFloatsRootInlineBox.
-    if (!root || !root->LogicalHeight() || !root->FirstLeafChild())
-      root = nullptr;
+  AbstractLineBox line = AbstractLineBox::CreateFor(visible_position);
+  if (!line.IsNull()) {
+    line = line.PreviousLine();
+    if (line.IsNull() || !line.CanBeCaretContainer())
+      line = AbstractLineBox();
   }
 
-  if (!root) {
+  if (line.IsNull()) {
     Position position =
         PreviousRootInlineBoxCandidatePosition(node, visible_position);
     if (position.IsNotNull()) {
       const VisiblePosition candidate = CreateVisiblePosition(position);
-      root = ComputeContainingLineBox(candidate);
-      if (!root) {
+      line = AbstractLineBox::CreateFor(candidate);
+      if (line.IsNull()) {
         // TODO(editing-dev): Investigate if this is correct for null
         // |candidate|.
         return candidate;
@@ -257,17 +422,20 @@
     }
   }
 
-  if (root) {
+  if (!line.IsNull()) {
     // FIXME: Can be wrong for multi-column layout and with transforms.
-    LayoutPoint point_in_line = AbsoluteLineDirectionPointToLocalPointInBlock(
-        root, line_direction_point);
+    LayoutPoint point_in_line =
+        line.AbsoluteLineDirectionPointToLocalPointInBlock(
+            line_direction_point);
     const LayoutObject* closest_leaf_child =
-        root->ClosestLeafChildForPoint(point_in_line, IsEditablePosition(p));
-    const Node* node = closest_leaf_child->GetNode();
-    if (node && EditingIgnoresContent(*node))
-      return VisiblePosition::InParentBeforeNode(*node);
-    return CreateVisiblePosition(
-        closest_leaf_child->PositionForPoint(point_in_line));
+        line.ClosestLeafChildForPoint(point_in_line, IsEditablePosition(p));
+    if (closest_leaf_child) {
+      const Node* node = closest_leaf_child->GetNode();
+      if (node && EditingIgnoresContent(*node))
+        return VisiblePosition::InParentBeforeNode(*node);
+      return CreateVisiblePosition(
+          closest_leaf_child->PositionForPoint(point_in_line));
+    }
   }
 
   // Could not find a previous line. This means we must already be on the first
@@ -299,16 +467,14 @@
   if (!layout_object)
     return VisiblePosition();
 
-  const RootInlineBox* root = ComputeContainingLineBox(visible_position);
-  if (root) {
-    root = root->NextRootBox();
-    // We want to skip zero height boxes.
-    // This could happen in case it is a TrailingFloatsRootInlineBox.
-    if (!root || !root->LogicalHeight() || !root->FirstLeafChild())
-      root = nullptr;
+  AbstractLineBox line = AbstractLineBox::CreateFor(visible_position);
+  if (!line.IsNull()) {
+    line = line.NextLine();
+    if (line.IsNull() || !line.CanBeCaretContainer())
+      line = AbstractLineBox();
   }
 
-  if (!root) {
+  if (line.IsNull()) {
     // FIXME: We need do the same in previousLinePosition.
     Node* child = NodeTraversal::ChildAt(*node, p.ComputeEditingOffset());
     Node* search_start_node =
@@ -317,8 +483,8 @@
         NextRootInlineBoxCandidatePosition(search_start_node, visible_position);
     if (position.IsNotNull()) {
       const VisiblePosition candidate = CreateVisiblePosition(position);
-      root = ComputeContainingLineBox(candidate);
-      if (!root) {
+      line = AbstractLineBox::CreateFor(candidate);
+      if (line.IsNull()) {
         // TODO(editing-dev): Investigate if this is correct for null
         // |candidate|.
         return candidate;
@@ -326,17 +492,20 @@
     }
   }
 
-  if (root) {
+  if (!line.IsNull()) {
     // FIXME: Can be wrong for multi-column layout and with transforms.
-    LayoutPoint point_in_line = AbsoluteLineDirectionPointToLocalPointInBlock(
-        root, line_direction_point);
+    LayoutPoint point_in_line =
+        line.AbsoluteLineDirectionPointToLocalPointInBlock(
+            line_direction_point);
     const LayoutObject* closest_leaf_child =
-        root->ClosestLeafChildForPoint(point_in_line, IsEditablePosition(p));
-    const Node* node = closest_leaf_child->GetNode();
-    if (node && EditingIgnoresContent(*node))
-      return VisiblePosition::InParentBeforeNode(*node);
-    return CreateVisiblePosition(
-        closest_leaf_child->PositionForPoint(point_in_line));
+        line.ClosestLeafChildForPoint(point_in_line, IsEditablePosition(p));
+    if (closest_leaf_child) {
+      const Node* node = closest_leaf_child->GetNode();
+      if (node && EditingIgnoresContent(*node))
+        return VisiblePosition::InParentBeforeNode(*node);
+      return CreateVisiblePosition(
+          closest_leaf_child->PositionForPoint(point_in_line));
+    }
   }
 
   // Could not find a next line. This means we must already be on the last line.
diff --git a/third_party/blink/renderer/core/inspector/browser_protocol.pdl b/third_party/blink/renderer/core/inspector/browser_protocol.pdl
index 9de341fc..77b8f805 100644
--- a/third_party/blink/renderer/core/inspector/browser_protocol.pdl
+++ b/third_party/blink/renderer/core/inspector/browser_protocol.pdl
@@ -6112,6 +6112,10 @@
       string vendorString
       # String description of the GPU device, if the PCI ID is not available.
       string deviceString
+      # String description of the GPU driver vendor.
+      string driverVendor
+      # String description of the GPU driver version.
+      string driverVersion
 
   # Provides information about the GPU(s) on the system.
   type GPUInfo extends object
diff --git a/third_party/blink/renderer/core/layout/layout_block_flow.cc b/third_party/blink/renderer/core/layout/layout_block_flow.cc
index b5a31fbc..1cb5211d 100644
--- a/third_party/blink/renderer/core/layout/layout_block_flow.cc
+++ b/third_party/blink/renderer/core/layout/layout_block_flow.cc
@@ -4597,7 +4597,7 @@
 void LayoutBlockFlow::RecalcInlineChildrenVisualOverflow() {
   DCHECK(ChildrenInline());
 
-  if (NGPaintFragment* paint_fragment = PaintFragment()) {
+  if (const NGPaintFragment* paint_fragment = PaintFragment()) {
     paint_fragment->RecalcInlineChildrenInkOverflow();
     return;
   }
diff --git a/third_party/blink/renderer/core/layout/layout_block_flow_line.cc b/third_party/blink/renderer/core/layout/layout_block_flow_line.cc
index 57167c8f..e9a2365 100644
--- a/third_party/blink/renderer/core/layout/layout_block_flow_line.cc
+++ b/third_party/blink/renderer/core/layout/layout_block_flow_line.cc
@@ -2687,7 +2687,7 @@
   DCHECK(ChildrenInline());
   if (RootInlineBox* first_root_box = FirstRootBox())
     first_root_box->SetShouldDoFullPaintInvalidationRecursively();
-  else if (NGPaintFragment* paint_fragment = PaintFragment())
+  else if (const NGPaintFragment* paint_fragment = PaintFragment())
     paint_fragment->SetShouldDoFullPaintInvalidationForFirstLine();
 }
 
diff --git a/third_party/blink/renderer/core/layout/layout_object.h b/third_party/blink/renderer/core/layout/layout_object.h
index c800e7fac..7577d63 100644
--- a/third_party/blink/renderer/core/layout/layout_object.h
+++ b/third_party/blink/renderer/core/layout/layout_object.h
@@ -1330,7 +1330,7 @@
 
   // Returns the associated |NGPaintFragment|. When this is not a |nullptr|,
   // this is the root of an inline formatting context, laid out by LayoutNG.
-  virtual NGPaintFragment* PaintFragment() const { return nullptr; }
+  virtual const NGPaintFragment* PaintFragment() const { return nullptr; }
 
   // Paint/Physical fragments are not in sync with LayoutObject tree until it is
   // laid out. For inline, it needs to check if the containing block is
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_dirty_lines.h b/third_party/blink/renderer/core/layout/ng/inline/ng_dirty_lines.h
index 0bd6100..5e97423c 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_dirty_lines.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_dirty_lines.h
@@ -18,7 +18,7 @@
   STACK_ALLOCATED();
 
  public:
-  explicit NGDirtyLines(NGPaintFragment* block_fragment)
+  explicit NGDirtyLines(const NGPaintFragment* block_fragment)
       : block_fragment_(block_fragment) {
     DCHECK(block_fragment_);
   }
@@ -82,7 +82,7 @@
   // is |nullptr|, the first line box is marked as dirty.
   void MarkLastFragment();
 
-  NGPaintFragment* block_fragment_;
+  const NGPaintFragment* block_fragment_;
   NGPaintFragment* last_fragment_ = nullptr;
 };
 
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.cc
index cc3c4d4..e7462d8 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.cc
@@ -349,7 +349,7 @@
   }
 
   if (RuntimeEnabledFeatures::LayoutNGLineCacheEnabled()) {
-    if (NGPaintFragment* fragment = block_flow->PaintFragment()) {
+    if (const NGPaintFragment* fragment = block_flow->PaintFragment()) {
       NGDirtyLines dirty_lines(fragment);
       PrepareLayout(std::move(previous_data), &dirty_lines);
       return;
@@ -442,6 +442,10 @@
     LayoutBlockFlow* layout_block_flow) {
   DCHECK(!layout_block_flow->GetDocument().NeedsLayoutTreeUpdate());
 
+  // TODO(crbug.com/962129): Fix the root cause of the missing layout, and turn
+  // this into a DCHECK.
+  CHECK(!layout_block_flow->NeedsLayout()) << layout_block_flow;
+
   // If |layout_block_flow| is LayoutNG, compute from |NGInlineNode|.
   if (layout_block_flow->IsLayoutNGMixin()) {
     NGInlineNode node(layout_block_flow);
@@ -996,7 +1000,7 @@
   if (constraint_space.HasBlockFragmentation())
     return nullptr;
 
-  NGPaintFragment* paint_fragment = block_flow->PaintFragment();
+  const NGPaintFragment* paint_fragment = block_flow->PaintFragment();
   if (!paint_fragment)
     return nullptr;
 
@@ -1015,7 +1019,7 @@
 // |DirtyLinesFromChangedChild()|, but insertions and style changes are not
 // marked yet.
 bool NGInlineNode::MarkLineBoxesDirty(LayoutBlockFlow* block_flow,
-                                      NGPaintFragment* paint_fragment) {
+                                      const NGPaintFragment* paint_fragment) {
   DCHECK(RuntimeEnabledFeatures::LayoutNGLineCacheEnabled());
   NGDirtyLines dirty_lines(paint_fragment);
   if (block_flow->NeedsCollectInlines()) {
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h
index 25ab690b..a3bcb07a 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h
@@ -126,7 +126,7 @@
   void ShapeTextForFirstLineIfNeeded(NGInlineNodeData*);
   void AssociateItemsWithInlines(NGInlineNodeData*);
 
-  bool MarkLineBoxesDirty(LayoutBlockFlow*, NGPaintFragment*);
+  bool MarkLineBoxesDirty(LayoutBlockFlow*, const NGPaintFragment*);
 
   NGInlineNodeData* MutableData() {
     return To<LayoutBlockFlow>(box_)->GetNGInlineNodeData();
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.cc
index 8da9010..249da59 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.cc
@@ -4,7 +4,9 @@
 
 #include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.h"
 
+#include "third_party/blink/renderer/core/editing/editing_utilities.h"
 #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_break_token.h"
+#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_fragment_traversal.h"
 #include "third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_fragment.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_relative_utils.h"
@@ -22,6 +24,18 @@
                   sizeof(SameSizeAsNGPhysicalLineBoxFragment),
               "NGPhysicalLineBoxFragment should stay small");
 
+bool IsInlineLeaf(const NGPhysicalFragment& fragment) {
+  if (fragment.IsText())
+    return true;
+  return fragment.IsBox() && fragment.IsAtomicInline();
+}
+
+bool IsEditableFragment(const NGPhysicalFragment& fragment) {
+  if (!fragment.GetNode())
+    return false;
+  return HasEditableStyle(*fragment.GetNode());
+}
+
 }  // namespace
 
 scoped_refptr<const NGPhysicalLineBoxFragment>
@@ -149,4 +163,47 @@
                                        BaseDirection(), Size(), pixel_size);
 }
 
+const LayoutObject* NGPhysicalLineBoxFragment::ClosestLeafChildForPoint(
+    const PhysicalOffset& point,
+    bool only_editable_leaves) const {
+  const PhysicalSize unit_square(LayoutUnit(1), LayoutUnit(1));
+  const LogicalOffset logical_point = point.ConvertToLogical(
+      Style().GetWritingMode(), BaseDirection(), Size(), unit_square);
+  const LayoutUnit inline_offset = logical_point.inline_offset;
+  const NGPhysicalFragment* closest_leaf_child = nullptr;
+  LayoutUnit closest_leaf_distance;
+  for (const auto& descendant :
+       NGInlineFragmentTraversal::DescendantsOf(*this)) {
+    const NGPhysicalFragment& fragment = *descendant.fragment;
+    if (!fragment.GetLayoutObject())
+      continue;
+    if (!IsInlineLeaf(fragment) || fragment.IsListMarker())
+      continue;
+    if (only_editable_leaves && !IsEditableFragment(fragment))
+      continue;
+
+    const LogicalSize fragment_logical_size =
+        fragment.Size().ConvertToLogical(Style().GetWritingMode());
+    const LogicalOffset fragment_logical_offset =
+        descendant.offset_to_container_box.ConvertToLogical(
+            Style().GetWritingMode(), BaseDirection(), Size(), fragment.Size());
+    const LayoutUnit inline_min = fragment_logical_offset.inline_offset;
+    const LayoutUnit inline_max = fragment_logical_offset.inline_offset +
+                                  fragment_logical_size.inline_size;
+    if (inline_offset >= inline_min && inline_offset < inline_max)
+      return fragment.GetLayoutObject();
+
+    const LayoutUnit distance =
+        inline_offset < inline_min ? inline_min - inline_offset
+                                   : inline_offset - inline_max + LayoutUnit(1);
+    if (!closest_leaf_child || distance < closest_leaf_distance) {
+      closest_leaf_child = &fragment;
+      closest_leaf_distance = distance;
+    }
+  }
+  if (!closest_leaf_child)
+    return nullptr;
+  return closest_leaf_child->GetLayoutObject();
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.h b/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.h
index a919365..d12c481 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.h
@@ -69,6 +69,9 @@
   const NGPhysicalFragment* FirstLogicalLeaf() const;
   const NGPhysicalFragment* LastLogicalLeaf() const;
 
+  const LayoutObject* ClosestLeafChildForPoint(const PhysicalOffset&,
+                                               bool only_editable_leaves) const;
+
   // Returns a point at the visual start/end of the line.
   // Encapsulates the handling of text direction and writing mode.
   PhysicalOffset LineStartPoint() const;
diff --git a/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.h b/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.h
index 9799e2b..7f45d582 100644
--- a/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.h
+++ b/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.h
@@ -50,7 +50,7 @@
 
   PositionWithAffinity PositionForPoint(const LayoutPoint&) const override;
 
-  NGPaintFragment* PaintFragment() const final {
+  const NGPaintFragment* PaintFragment() const final {
     // TODO(layout-dev) crbug.com/963103
     // Safer option here is to return nullptr only if
     // Lifecycle > DocumentLifecycle::kAfterPerformLayout, but this breaks
diff --git a/third_party/blink/renderer/core/layout/scroll_anchor.cc b/third_party/blink/renderer/core/layout/scroll_anchor.cc
index 9bea1dca..430cb50 100644
--- a/third_party/blink/renderer/core/layout/scroll_anchor.cc
+++ b/third_party/blink/renderer/core/layout/scroll_anchor.cc
@@ -98,6 +98,9 @@
     }
   } else if (layout_object->IsText()) {
     const auto* text = ToLayoutText(layout_object);
+    // TODO(kojii): |PhysicalLinesBoundingBox()| cannot compute, and thus
+    // returns (0, 0) when changes are made that |DeleteLineBoxes()| or clear
+    // |SetPaintFragment()|, e.g., |SplitFlow()|. crbug.com/965352
     PhysicalRect bounds = text->PhysicalLinesBoundingBox();
     local_bounds.Unite(text->FlipForWritingMode(bounds));
   } else {
diff --git a/third_party/blink/renderer/core/paint/block_flow_paint_invalidator.cc b/third_party/blink/renderer/core/paint/block_flow_paint_invalidator.cc
index 7d02121..68051369 100644
--- a/third_party/blink/renderer/core/paint/block_flow_paint_invalidator.cc
+++ b/third_party/blink/renderer/core/paint/block_flow_paint_invalidator.cc
@@ -45,7 +45,7 @@
   ObjectPaintInvalidator object_paint_invalidator(block_flow_);
   object_paint_invalidator.InvalidateDisplayItemClient(block_flow_, reason);
 
-  NGPaintFragment* paint_fragment = block_flow_.PaintFragment();
+  const NGPaintFragment* paint_fragment = block_flow_.PaintFragment();
   if (paint_fragment) {
     object_paint_invalidator.InvalidateDisplayItemClient(*paint_fragment,
                                                          reason);
diff --git a/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.cc b/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.cc
index eb6c960..1ad6b03 100644
--- a/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.cc
+++ b/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.cc
@@ -429,11 +429,11 @@
   add_result.stored_value->value = this;
 }
 
-NGPaintFragment* NGPaintFragment::GetForInlineContainer(
+const NGPaintFragment* NGPaintFragment::GetForInlineContainer(
     const LayoutObject* layout_object) {
   DCHECK(layout_object && layout_object->IsInline());
   if (LayoutBlockFlow* block_flow = layout_object->ContainingNGBlockFlow()) {
-    if (NGPaintFragment* fragment = block_flow->PaintFragment())
+    if (const NGPaintFragment* fragment = block_flow->PaintFragment())
       return fragment;
 
     // TODO(kojii): IsLayoutFlowThread should probably be done in
@@ -558,12 +558,12 @@
   return rect;
 }
 
-void NGPaintFragment::RecalcInlineChildrenInkOverflow() {
+void NGPaintFragment::RecalcInlineChildrenInkOverflow() const {
   DCHECK(GetLayoutObject()->ChildrenInline());
   RecalcContentsInkOverflow();
 }
 
-PhysicalRect NGPaintFragment::RecalcContentsInkOverflow() {
+PhysicalRect NGPaintFragment::RecalcContentsInkOverflow() const {
   PhysicalRect contents_rect;
   for (NGPaintFragment* child : Children()) {
     const NGPhysicalFragment& child_fragment = child->PhysicalFragment();
@@ -769,7 +769,7 @@
 
   // The |layout_object| is inserted into an empty block.
   // Mark the first line box dirty.
-  if (NGPaintFragment* paint_fragment = parent.PaintFragment()) {
+  if (const NGPaintFragment* paint_fragment = parent.PaintFragment()) {
     if (NGPaintFragment* first_line = paint_fragment->FirstLineBox()) {
       first_line->is_dirty_inline_ = true;
       return;
@@ -825,7 +825,7 @@
     child->SetShouldDoFullPaintInvalidationRecursively();
 }
 
-void NGPaintFragment::SetShouldDoFullPaintInvalidationForFirstLine() {
+void NGPaintFragment::SetShouldDoFullPaintInvalidationForFirstLine() const {
   DCHECK(PhysicalFragment().IsBox() && GetLayoutObject() &&
          GetLayoutObject()->IsLayoutBlockFlow());
 
diff --git a/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h b/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h
index d71b3d1..52831710 100644
--- a/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h
+++ b/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h
@@ -157,7 +157,7 @@
   // overflow of this object (e.g. when not clipped,) in the local coordinate.
   PhysicalRect InkOverflow() const;
 
-  void RecalcInlineChildrenInkOverflow();
+  void RecalcInlineChildrenInkOverflow() const;
 
   void AddSelfOutlineRects(Vector<PhysicalRect>*,
                            const PhysicalOffset& offset,
@@ -185,7 +185,7 @@
 
   // Set ShouldDoFullPaintInvalidation flag to all objects in the first line of
   // this block-level fragment.
-  void SetShouldDoFullPaintInvalidationForFirstLine();
+  void SetShouldDoFullPaintInvalidationForFirstLine() const;
 
   // DisplayItemClient methods.
   String DebugName() const override;
@@ -255,7 +255,7 @@
   // When the LayoutObject is an inline block, it belongs to an inline
   // formatting context while it creates its own for its descendants. This
   // function always returns the one it belongs to.
-  static NGPaintFragment* GetForInlineContainer(const LayoutObject*);
+  static const NGPaintFragment* GetForInlineContainer(const LayoutObject*);
 
   // Returns a range of NGPaintFragment in an inline formatting context that are
   // for a LayoutObject.
@@ -320,7 +320,7 @@
 
   // Re-compute ink overflow of children and return the union.
   PhysicalRect RecalcInkOverflow();
-  PhysicalRect RecalcContentsInkOverflow();
+  PhysicalRect RecalcContentsInkOverflow() const;
 
   // This fragment will use the layout object's visual rect.
   const LayoutObject& VisualRectLayoutObject(bool& this_as_inline_box) const;
diff --git a/third_party/blink/renderer/core/paint/ng/ng_paint_fragment_traversal_test.cc b/third_party/blink/renderer/core/paint/ng/ng_paint_fragment_traversal_test.cc
index ca6e3ef..30d15d5 100644
--- a/third_party/blink/renderer/core/paint/ng/ng_paint_fragment_traversal_test.cc
+++ b/third_party/blink/renderer/core/paint/ng/ng_paint_fragment_traversal_test.cc
@@ -59,7 +59,7 @@
   }
 
   LayoutBlockFlow* layout_block_flow_;
-  NGPaintFragment* root_fragment_;
+  const NGPaintFragment* root_fragment_;
 };
 
 TEST_F(NGPaintFragmentTraversalTest, MoveToNext) {
diff --git a/third_party/blink/renderer/core/paint/paint_layer_painter_test.cc b/third_party/blink/renderer/core/paint/paint_layer_painter_test.cc
index 80dcd81..028504f 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_painter_test.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer_painter_test.cc
@@ -192,18 +192,20 @@
   )HTML");
   InvalidateAll(RootPaintController());
 
-  DisplayItemClient& container1 =
+  const DisplayItemClient& container1 =
       *GetDisplayItemClientFromElementId("container1");
-  DisplayItemClient& content1 = *GetDisplayItemClientFromElementId("content1");
-  DisplayItemClient& container2 =
+  const DisplayItemClient& content1 =
+      *GetDisplayItemClientFromElementId("content1");
+  const DisplayItemClient& container2 =
       *GetDisplayItemClientFromElementId("container2");
-  DisplayItemClient& content2a =
+  const DisplayItemClient& content2a =
       *GetDisplayItemClientFromElementId("content2a");
-  DisplayItemClient& content2b =
+  const DisplayItemClient& content2b =
       *GetDisplayItemClientFromElementId("content2b");
-  DisplayItemClient& container3 =
+  const DisplayItemClient& container3 =
       *GetDisplayItemClientFromElementId("container3");
-  DisplayItemClient& content3 = *GetDisplayItemClientFromElementId("content3");
+  const DisplayItemClient& content3 =
+      *GetDisplayItemClientFromElementId("content3");
 
   GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint();
   Paint(IntRect(0, 0, 400, 300));
@@ -287,12 +289,14 @@
   // PaintResult of all subsequences will be MayBeClippedByCullRect.
   Paint(IntRect(0, 0, 50, 300));
 
-  DisplayItemClient& container1 =
+  const DisplayItemClient& container1 =
       *GetDisplayItemClientFromElementId("container1");
-  DisplayItemClient& content1 = *GetDisplayItemClientFromElementId("content1");
-  DisplayItemClient& container2 =
+  const DisplayItemClient& content1 =
+      *GetDisplayItemClientFromElementId("content1");
+  const DisplayItemClient& container2 =
       *GetDisplayItemClientFromElementId("container2");
-  DisplayItemClient& content2 = *GetDisplayItemClientFromElementId("content2");
+  const DisplayItemClient& content2 =
+      *GetDisplayItemClientFromElementId("content2");
 
   const auto& background_display_item_client = ViewScrollingBackgroundClient();
   EXPECT_THAT(RootPaintController().GetDisplayItemList(),
diff --git a/third_party/blink/renderer/core/testing/core_unit_test_helper.h b/third_party/blink/renderer/core/testing/core_unit_test_helper.h
index b8f134e..7f525260 100644
--- a/third_party/blink/renderer/core/testing/core_unit_test_helper.h
+++ b/third_party/blink/renderer/core/testing/core_unit_test_helper.h
@@ -141,7 +141,7 @@
     return ToLayoutBoxModelObject(GetLayoutObjectByElementId(id))->Layer();
   }
 
-  DisplayItemClient* GetDisplayItemClientFromLayoutObject(
+  const DisplayItemClient* GetDisplayItemClientFromLayoutObject(
       LayoutObject* obj) const {
     LayoutNGBlockFlow* block_flow = ToLayoutNGBlockFlowOrNull(obj);
     if (block_flow && block_flow->PaintFragment())
@@ -149,7 +149,8 @@
     return obj;
   }
 
-  DisplayItemClient* GetDisplayItemClientFromElementId(const char* id) const {
+  const DisplayItemClient* GetDisplayItemClientFromElementId(
+      const char* id) const {
     return GetDisplayItemClientFromLayoutObject(GetLayoutObjectByElementId(id));
   }
 
diff --git a/third_party/blink/renderer/devtools/front_end/network/NetworkLogView.js b/third_party/blink/renderer/devtools/front_end/network/NetworkLogView.js
index c2cc71c..81b20b2 100644
--- a/third_party/blink/renderer/devtools/front_end/network/NetworkLogView.js
+++ b/third_party/blink/renderer/devtools/front_end/network/NetworkLogView.js
@@ -453,13 +453,13 @@
     if (entry.isDirectory)
       return;
 
-    entry.file(this._onLoadFromFile.bind(this));
+    entry.file(this.onLoadFromFile.bind(this));
   }
 
   /**
    * @param {!File} file
    */
-  async _onLoadFromFile(file) {
+  async onLoadFromFile(file) {
     const outputStream = new Common.StringOutputStream();
     const reader = new Bindings.ChunkedFileReader(file, /* chunkSize */ 10000000);
     const success = await reader.read(outputStream);
@@ -1212,7 +1212,7 @@
     }
     footerSection.appendItem(Common.UIString('Copy all as HAR'), this._copyAll.bind(this));
 
-    contextMenu.saveSection().appendItem(Common.UIString('Save all as HAR with content'), this._exportAll.bind(this));
+    contextMenu.saveSection().appendItem(ls`Save all as HAR with content`, this.exportAll.bind(this));
 
     contextMenu.editSection().appendItem(Common.UIString('Clear browser cache'), this._clearBrowserCache.bind(this));
     contextMenu.editSection().appendItem(
@@ -1325,7 +1325,7 @@
     InspectorFrontendHost.copyText(commands);
   }
 
-  async _exportAll() {
+  async exportAll() {
     const url = SDK.targetManager.mainTarget().inspectedURL();
     const parsedURL = url.asParsedURL();
     const filename = parsedURL ? parsedURL.host : 'network-log';
diff --git a/third_party/blink/renderer/devtools/front_end/network/NetworkPanel.js b/third_party/blink/renderer/devtools/front_end/network/NetworkPanel.js
index 6bbf1a8..6e8a942 100644
--- a/third_party/blink/renderer/devtools/front_end/network/NetworkPanel.js
+++ b/third_party/blink/renderer/devtools/front_end/network/NetworkPanel.js
@@ -118,6 +118,10 @@
         new Network.NetworkLogView(this._filterBar, this._progressBarContainer, this._networkLogLargeRowsSetting);
     this._splitWidget.setSidebarWidget(this._networkLogView);
 
+    this._fileSelectorElement =
+        UI.createFileSelectorElement(this._networkLogView.onLoadFromFile.bind(this._networkLogView));
+    panel.element.appendChild(this._fileSelectorElement);
+
     this._detailsWidget = new UI.VBox();
     this._detailsWidget.element.classList.add('network-details-view');
     this._splitWidget.setMainWidget(this._detailsWidget);
@@ -238,6 +242,14 @@
         new UI.ToolbarSettingCheckbox(Common.moduleSetting('network.group-by-frame'), '', ls`Group by frame`));
     settingsToolbarRight.appendToolbarItem(
         new UI.ToolbarSettingCheckbox(this._networkRecordFilmStripSetting, '', ls`Capture screenshots`));
+
+    this._panelToolbar.appendSeparator();
+    const importHarButton = new UI.ToolbarButton(ls`Import HAR file...`, 'largeicon-load');
+    importHarButton.addEventListener(UI.ToolbarButton.Events.Click, () => this._fileSelectorElement.click(), this);
+    this._panelToolbar.appendToolbarItem(importHarButton);
+    const exportHarButton = new UI.ToolbarButton(ls`Export all as HAR with content...`, 'largeicon-download');
+    exportHarButton.addEventListener(UI.ToolbarButton.Events.Click, () => this._networkLogView.exportAll(), this);
+    this._panelToolbar.appendToolbarItem(exportHarButton);
   }
 
   _updateSettingsPaneVisibility() {
diff --git a/third_party/blink/renderer/modules/background_sync/service_worker_registration_sync.cc b/third_party/blink/renderer/modules/background_sync/service_worker_registration_sync.cc
index 0ceffbf..758de35 100644
--- a/third_party/blink/renderer/modules/background_sync/service_worker_registration_sync.cc
+++ b/third_party/blink/renderer/modules/background_sync/service_worker_registration_sync.cc
@@ -37,8 +37,12 @@
 }
 
 SyncManager* ServiceWorkerRegistrationSync::sync() {
-  if (!sync_manager_)
-    sync_manager_ = SyncManager::Create(registration_);
+  if (!sync_manager_) {
+    ExecutionContext* execution_context = registration_->GetExecutionContext();
+    sync_manager_ = SyncManager::Create(
+        registration_,
+        execution_context->GetTaskRunner(TaskType::kInternalWorker));
+  }
   return sync_manager_.Get();
 }
 
diff --git a/third_party/blink/renderer/modules/background_sync/sync_manager.cc b/third_party/blink/renderer/modules/background_sync/sync_manager.cc
index ac40268..15360ce 100644
--- a/third_party/blink/renderer/modules/background_sync/sync_manager.cc
+++ b/third_party/blink/renderer/modules/background_sync/sync_manager.cc
@@ -18,8 +18,9 @@
 
 namespace blink {
 
-SyncManager::SyncManager(ServiceWorkerRegistration* registration)
-    : registration_(registration) {
+SyncManager::SyncManager(ServiceWorkerRegistration* registration,
+                         scoped_refptr<base::SequencedTaskRunner> task_runner)
+    : registration_(registration), task_runner_(std::move(task_runner)) {
   DCHECK(registration);
 }
 
@@ -63,7 +64,7 @@
 SyncManager::GetBackgroundSyncServicePtr() {
   if (!background_sync_service_.get()) {
     Platform::Current()->GetInterfaceProvider()->GetInterface(
-        mojo::MakeRequest(&background_sync_service_));
+        mojo::MakeRequest(&background_sync_service_, task_runner_));
   }
   return background_sync_service_;
 }
diff --git a/third_party/blink/renderer/modules/background_sync/sync_manager.h b/third_party/blink/renderer/modules/background_sync/sync_manager.h
index 33eedf61..5eedef11 100644
--- a/third_party/blink/renderer/modules/background_sync/sync_manager.h
+++ b/third_party/blink/renderer/modules/background_sync/sync_manager.h
@@ -21,11 +21,15 @@
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  static SyncManager* Create(ServiceWorkerRegistration* registration) {
-    return MakeGarbageCollected<SyncManager>(registration);
+  static SyncManager* Create(
+      ServiceWorkerRegistration* registration,
+      scoped_refptr<base::SequencedTaskRunner> task_runner) {
+    return MakeGarbageCollected<SyncManager>(registration,
+                                             std::move(task_runner));
   }
 
-  explicit SyncManager(ServiceWorkerRegistration*);
+  SyncManager(ServiceWorkerRegistration*,
+              scoped_refptr<base::SequencedTaskRunner>);
 
   ScriptPromise registerFunction(ScriptState*, const String& tag);
   ScriptPromise getTags(ScriptState*);
@@ -50,6 +54,7 @@
       WTF::Vector<mojom::blink::SyncRegistrationOptionsPtr> registrations);
 
   Member<ServiceWorkerRegistration> registration_;
+  scoped_refptr<base::SequencedTaskRunner> task_runner_;
   mojom::blink::BackgroundSyncServicePtr background_sync_service_;
 };
 
diff --git a/third_party/blink/renderer/modules/gamepad/navigator_gamepad.cc b/third_party/blink/renderer/modules/gamepad/navigator_gamepad.cc
index 771991ca..9c40aec 100644
--- a/third_party/blink/renderer/modules/gamepad/navigator_gamepad.cc
+++ b/third_party/blink/renderer/modules/gamepad/navigator_gamepad.cc
@@ -276,7 +276,7 @@
   if (processing_events_)
     return;
 
-  base::AutoReset<bool>(&processing_events_, true);
+  base::AutoReset<bool> processing_events_reset(&processing_events_, true);
   if (StartUpdatingIfAttached()) {
     if (GetPage()->IsPageVisible()) {
       // Allocate a buffer to hold the new gamepad state, if needed.
@@ -342,6 +342,8 @@
 
 void NavigatorGamepad::DispatchGamepadEvent(const AtomicString& event_name,
                                             Gamepad* gamepad) {
+  // Ensure that we're blocking re-entrancy.
+  DCHECK(processing_events_);
   DCHECK(has_connection_event_listener_);
   DCHECK(gamepad);
   DomWindow()->DispatchEvent(*GamepadEvent::Create(
diff --git a/third_party/blink/renderer/modules/webgl/webgl2_compute_rendering_context_base.cc b/third_party/blink/renderer/modules/webgl/webgl2_compute_rendering_context_base.cc
index b87af68c..a6181fa7 100644
--- a/third_party/blink/renderer/modules/webgl/webgl2_compute_rendering_context_base.cc
+++ b/third_party/blink/renderer/modules/webgl/webgl2_compute_rendering_context_base.cc
@@ -32,6 +32,24 @@
 
 void WebGL2ComputeRenderingContextBase::InitializeNewContext() {
   DCHECK(!isContextLost());
+  DCHECK(GetDrawingBuffer());
+
+  bound_atomic_counter_buffer_ = nullptr;
+  bound_shader_storage_buffer_ = nullptr;
+
+  GLint max_atomic_counter_buffer_bindings = 0;
+  ContextGL()->GetIntegerv(GL_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS,
+                           &max_atomic_counter_buffer_bindings);
+  bound_indexed_atomic_counter_buffers_.clear();
+  bound_indexed_atomic_counter_buffers_.resize(
+      max_atomic_counter_buffer_bindings);
+
+  GLint max_shader_storage_buffer_bindings = 0;
+  ContextGL()->GetIntegerv(GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS,
+                           &max_shader_storage_buffer_bindings);
+  bound_indexed_shader_storage_buffers_.clear();
+  bound_indexed_shader_storage_buffers_.resize(
+      max_shader_storage_buffer_bindings);
 
   WebGL2RenderingContextBase::InitializeNewContext();
 }
@@ -333,7 +351,51 @@
   }
 }
 
+ScriptValue WebGL2ComputeRenderingContextBase::getIndexedParameter(
+    ScriptState* script_state,
+    GLenum target,
+    GLuint index) {
+  if (isContextLost())
+    return ScriptValue::CreateNull(script_state);
+
+  switch (target) {
+    case GL_ATOMIC_COUNTER_BUFFER_BINDING:
+      if (index >= bound_indexed_atomic_counter_buffers_.size()) {
+        SynthesizeGLError(GL_INVALID_VALUE, "getIndexedParameter",
+                          "index out of range");
+        return ScriptValue::CreateNull(script_state);
+      }
+      return WebGLAny(script_state,
+                      bound_indexed_atomic_counter_buffers_[index].Get());
+    case GL_SHADER_STORAGE_BUFFER_BINDING:
+      if (index >= bound_indexed_shader_storage_buffers_.size()) {
+        SynthesizeGLError(GL_INVALID_VALUE, "getIndexedParameter",
+                          "index out of range");
+        return ScriptValue::CreateNull(script_state);
+      }
+      return WebGLAny(script_state,
+                      bound_indexed_shader_storage_buffers_[index].Get());
+    case GL_MAX_COMPUTE_WORK_GROUP_COUNT:
+    case GL_MAX_COMPUTE_WORK_GROUP_SIZE:
+    case GL_ATOMIC_COUNTER_BUFFER_SIZE:
+    case GL_ATOMIC_COUNTER_BUFFER_START:
+    case GL_SHADER_STORAGE_BUFFER_SIZE:
+    case GL_SHADER_STORAGE_BUFFER_START: {
+      GLint64 value = -1;
+      ContextGL()->GetInteger64i_v(target, index, &value);
+      return WebGLAny(script_state, value);
+    }
+    default:
+      return WebGL2RenderingContextBase::getIndexedParameter(
+          script_state, target, index);
+  }
+}
+
 void WebGL2ComputeRenderingContextBase::Trace(blink::Visitor* visitor) {
+  visitor->Trace(bound_atomic_counter_buffer_);
+  visitor->Trace(bound_indexed_atomic_counter_buffers_);
+  visitor->Trace(bound_shader_storage_buffer_);
+  visitor->Trace(bound_indexed_shader_storage_buffers_);
   WebGL2RenderingContextBase::Trace(visitor);
 }
 
@@ -361,4 +423,179 @@
   }
 }
 
+bool WebGL2ComputeRenderingContextBase::ValidateShaderType(
+    const char* function_name,
+    GLenum shader_type) {
+  switch (shader_type) {
+    case GL_COMPUTE_SHADER:
+      return true;
+    default:
+      return WebGL2RenderingContextBase::ValidateShaderType(
+          function_name, shader_type);
+  }
+}
+
+bool WebGL2ComputeRenderingContextBase::ValidateBufferTarget(
+    const char* function_name,
+    GLenum target) {
+  switch (target) {
+    case GL_ATOMIC_COUNTER_BUFFER:
+    case GL_SHADER_STORAGE_BUFFER:
+      return true;
+    default:
+      return WebGL2RenderingContextBase::ValidateBufferTarget(
+          function_name, target);
+  }
+}
+
+WebGLBuffer* WebGL2ComputeRenderingContextBase::ValidateBufferDataTarget(
+    const char* function_name,
+    GLenum target) {
+  WebGLBuffer* buffer = nullptr;
+  switch (target) {
+    case GL_ATOMIC_COUNTER_BUFFER:
+      buffer = bound_atomic_counter_buffer_.Get();
+      break;
+    case GL_SHADER_STORAGE_BUFFER:
+      buffer = bound_shader_storage_buffer_.Get();
+      break;
+    default:
+      return WebGL2RenderingContextBase::ValidateBufferDataTarget(
+          function_name, target);
+  }
+  if (!buffer) {
+    SynthesizeGLError(GL_INVALID_OPERATION, function_name, "no buffer");
+    return nullptr;
+  }
+  return buffer;
+}
+
+bool WebGL2ComputeRenderingContextBase::ValidateAndUpdateBufferBindTarget(
+    const char* function_name,
+    GLenum target,
+    WebGLBuffer* buffer) {
+  if (!ValidateBufferTarget(function_name, target))
+    return false;
+
+  if (buffer &&
+      !ValidateBufferTargetCompatibility(function_name, target, buffer))
+    return false;
+
+  switch (target) {
+    case GL_ATOMIC_COUNTER_BUFFER:
+      bound_atomic_counter_buffer_ = buffer;
+      break;
+    case GL_SHADER_STORAGE_BUFFER:
+      bound_shader_storage_buffer_ = buffer;
+      break;
+    default:
+      return WebGL2RenderingContextBase::ValidateAndUpdateBufferBindTarget(
+          function_name, target, buffer);
+  }
+
+  if (buffer && !buffer->GetInitialTarget())
+    buffer->SetInitialTarget(target);
+  return true;
+}
+
+void WebGL2ComputeRenderingContextBase::RemoveBoundBuffer(WebGLBuffer* buffer) {
+  if (bound_atomic_counter_buffer_ == buffer)
+    bound_atomic_counter_buffer_ = nullptr;
+  if (bound_shader_storage_buffer_ == buffer)
+    bound_shader_storage_buffer_ = nullptr;
+
+  WebGL2RenderingContextBase::RemoveBoundBuffer(buffer);
+}
+
+bool WebGL2ComputeRenderingContextBase::ValidateBufferTargetCompatibility(
+    const char* function_name,
+    GLenum target,
+    WebGLBuffer* buffer) {
+  DCHECK(buffer);
+
+  switch (buffer->GetInitialTarget()) {
+    case GL_ELEMENT_ARRAY_BUFFER:
+      switch (target) {
+        case GL_ATOMIC_COUNTER_BUFFER:
+        case GL_SHADER_STORAGE_BUFFER:
+          SynthesizeGLError(
+              GL_INVALID_OPERATION, function_name,
+              "element array buffers can not be bound to a different target");
+
+          return false;
+        default:
+          break;
+      }
+      break;
+    case GL_ATOMIC_COUNTER_BUFFER:
+    case GL_SHADER_STORAGE_BUFFER:
+      if (target == GL_ELEMENT_ARRAY_BUFFER) {
+        SynthesizeGLError(GL_INVALID_OPERATION, function_name,
+                          "buffers bound to non ELEMENT_ARRAY_BUFFER targets "
+                          "can not be bound to ELEMENT_ARRAY_BUFFER target");
+        return false;
+      }
+      return true;
+    default:
+      break;
+  }
+
+  return WebGL2RenderingContextBase::ValidateBufferTargetCompatibility(
+      function_name, target, buffer);
+}
+
+bool WebGL2ComputeRenderingContextBase::ValidateBufferBaseTarget(
+    const char* function_name,
+    GLenum target) {
+  switch (target) {
+    case GL_ATOMIC_COUNTER_BUFFER:
+    case GL_SHADER_STORAGE_BUFFER:
+      return true;
+    default:
+      return WebGL2RenderingContextBase::ValidateBufferBaseTarget(
+          function_name, target);
+  }
+}
+
+bool WebGL2ComputeRenderingContextBase::ValidateAndUpdateBufferBindBaseTarget(
+    const char* function_name,
+    GLenum target,
+    GLuint index,
+    WebGLBuffer* buffer) {
+  if (!ValidateBufferBaseTarget(function_name, target))
+    return false;
+
+  if (buffer &&
+      !ValidateBufferTargetCompatibility(function_name, target, buffer))
+    return false;
+
+  switch (target) {
+    case GL_ATOMIC_COUNTER_BUFFER:
+      if (index >= bound_indexed_atomic_counter_buffers_.size()) {
+        SynthesizeGLError(GL_INVALID_VALUE, function_name,
+                          "index out of range");
+        return false;
+      }
+      bound_indexed_atomic_counter_buffers_[index] = buffer;
+      bound_atomic_counter_buffer_ = buffer;
+      break;
+    case GL_SHADER_STORAGE_BUFFER:
+      if (index >= bound_indexed_shader_storage_buffers_.size()) {
+        SynthesizeGLError(GL_INVALID_VALUE, function_name,
+                          "index out of range");
+        return false;
+      }
+      bound_indexed_shader_storage_buffers_[index] = buffer;
+      bound_shader_storage_buffer_ = buffer;
+      break;
+    default:
+      return WebGL2RenderingContextBase::ValidateAndUpdateBufferBindBaseTarget(
+          function_name, target, index, buffer);
+  }
+
+  if (buffer && !buffer->GetInitialTarget())
+    buffer->SetInitialTarget(target);
+  return true;
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/webgl/webgl2_compute_rendering_context_base.h b/third_party/blink/renderer/modules/webgl/webgl2_compute_rendering_context_base.h
index 7383987..a713d1f4 100644
--- a/third_party/blink/renderer/modules/webgl/webgl2_compute_rendering_context_base.h
+++ b/third_party/blink/renderer/modules/webgl/webgl2_compute_rendering_context_base.h
@@ -59,6 +59,11 @@
   void InitializeNewContext() override;
   ScriptValue getParameter(ScriptState*, GLenum pname) override;
 
+  /* WebGL2RenderingContextBase overrides */
+  ScriptValue getIndexedParameter(ScriptState*,
+                                  GLenum target,
+                                  GLuint index) override;
+
   void Trace(blink::Visitor*) override;
 
  protected:
@@ -72,6 +77,34 @@
                            GLint location,
                            WebGLProgram* program,
                            GLenum program_interface);
+
+  /* WebGLRenderingContextBase overrides */
+  bool ValidateShaderType(const char* function_name,
+                          GLenum shader_type) override;
+  bool ValidateBufferTarget(const char* function_name, GLenum target) override;
+  WebGLBuffer* ValidateBufferDataTarget(const char* function_name,
+                                        GLenum target) override;
+  bool ValidateAndUpdateBufferBindTarget(const char* function_name,
+                                         GLenum target,
+                                         WebGLBuffer*) override;
+  void RemoveBoundBuffer(WebGLBuffer*) override;
+
+  /* WebGL2RenderingContextBase overrides */
+  bool ValidateBufferTargetCompatibility(const char* function_name,
+                                         GLenum target,
+                                         WebGLBuffer*) override;
+  bool ValidateBufferBaseTarget(const char* function_name,
+                                GLenum target) override;
+  bool ValidateAndUpdateBufferBindBaseTarget(const char* function_name,
+                                             GLenum target,
+                                             GLuint index,
+                                             WebGLBuffer*) override;
+
+  Member<WebGLBuffer> bound_atomic_counter_buffer_;
+  Member<WebGLBuffer> bound_shader_storage_buffer_;
+
+  HeapVector<Member<WebGLBuffer>> bound_indexed_atomic_counter_buffers_;
+  HeapVector<Member<WebGLBuffer>> bound_indexed_shader_storage_buffers_;
 };
 
 DEFINE_TYPE_CASTS(WebGL2ComputeRenderingContextBase,
diff --git a/third_party/blink/renderer/modules/webgl/webgl2_rendering_context_base.cc b/third_party/blink/renderer/modules/webgl/webgl2_rendering_context_base.cc
index 787aeae..3f94f1c 100644
--- a/third_party/blink/renderer/modules/webgl/webgl2_rendering_context_base.cc
+++ b/third_party/blink/renderer/modules/webgl/webgl2_rendering_context_base.cc
@@ -170,8 +170,6 @@
   bound_pixel_unpack_buffer_ = nullptr;
   bound_transform_feedback_buffer_ = nullptr;
   bound_uniform_buffer_ = nullptr;
-  bound_atomic_counter_buffer_ = nullptr;
-  bound_shader_storage_buffer_ = nullptr;
 
   current_boolean_occlusion_query_ = nullptr;
   current_transform_feedback_primitives_written_query_ = nullptr;
@@ -201,22 +199,6 @@
   bound_indexed_uniform_buffers_.resize(max_uniform_buffer_bindings);
   max_bound_uniform_buffer_index_ = 0;
 
-  if (ContextType() == Platform::kWebGL2ComputeContextType) {
-    GLint max_atomic_counter_buffer_bindings = 0;
-    ContextGL()->GetIntegerv(GL_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS,
-                             &max_atomic_counter_buffer_bindings);
-    bound_indexed_atomic_counter_buffers_.clear();
-    bound_indexed_atomic_counter_buffers_.resize(
-        max_atomic_counter_buffer_bindings);
-
-    GLint max_shader_storage_buffer_bindings = 0;
-    ContextGL()->GetIntegerv(GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS,
-                             &max_shader_storage_buffer_bindings);
-    bound_indexed_shader_storage_buffers_.clear();
-    bound_indexed_shader_storage_buffers_.resize(
-        max_shader_storage_buffer_bindings);
-  }
-
   pack_row_length_ = 0;
   pack_skip_pixels_ = 0;
   pack_skip_rows_ = 0;
@@ -4667,34 +4649,6 @@
       }
       return WebGLAny(script_state,
                       bound_indexed_uniform_buffers_[index].Get());
-    case GL_ATOMIC_COUNTER_BUFFER_BINDING: {
-      if (ContextType() != Platform::kWebGL2ComputeContextType) {
-        SynthesizeGLError(GL_INVALID_ENUM, "getIndexedParameter",
-                          "invalid parameter name");
-        return ScriptValue::CreateNull(script_state);
-      }
-      if (index >= bound_indexed_atomic_counter_buffers_.size()) {
-        SynthesizeGLError(GL_INVALID_VALUE, "getIndexedParameter",
-                          "index out of range");
-        return ScriptValue::CreateNull(script_state);
-      }
-      return WebGLAny(script_state,
-                      bound_indexed_atomic_counter_buffers_[index].Get());
-    }
-    case GL_SHADER_STORAGE_BUFFER_BINDING: {
-      if (ContextType() != Platform::kWebGL2ComputeContextType) {
-        SynthesizeGLError(GL_INVALID_ENUM, "getIndexedParameter",
-                          "invalid parameter name");
-        return ScriptValue::CreateNull(script_state);
-      }
-      if (index >= bound_indexed_shader_storage_buffers_.size()) {
-        SynthesizeGLError(GL_INVALID_VALUE, "getIndexedParameter",
-                          "index out of range");
-        return ScriptValue::CreateNull(script_state);
-      }
-      return WebGLAny(script_state,
-                      bound_indexed_shader_storage_buffers_[index].Get());
-    }
     case GL_TRANSFORM_FEEDBACK_BUFFER_SIZE:
     case GL_TRANSFORM_FEEDBACK_BUFFER_START:
     case GL_UNIFORM_BUFFER_SIZE:
@@ -4703,22 +4657,6 @@
       ContextGL()->GetInteger64i_v(target, index, &value);
       return WebGLAny(script_state, value);
     }
-    case GL_MAX_COMPUTE_WORK_GROUP_COUNT:
-    case GL_MAX_COMPUTE_WORK_GROUP_SIZE:
-    case GL_ATOMIC_COUNTER_BUFFER_SIZE:
-    case GL_ATOMIC_COUNTER_BUFFER_START:
-    case GL_SHADER_STORAGE_BUFFER_SIZE:
-    case GL_SHADER_STORAGE_BUFFER_START: {
-      if (ContextType() != Platform::kWebGL2ComputeContextType) {
-        SynthesizeGLError(GL_INVALID_ENUM, "getIndexedParameter",
-                          "invalid parameter name");
-        return ScriptValue::CreateNull(script_state);
-      }
-      GLint64 value = -1;
-      ContextGL()->GetInteger64i_v(target, index, &value);
-      return WebGLAny(script_state, value);
-    }
-
     default:
       SynthesizeGLError(GL_INVALID_ENUM, "getIndexedParameter",
                         "invalid parameter name");
@@ -5262,8 +5200,6 @@
         case GL_PIXEL_UNPACK_BUFFER:
         case GL_TRANSFORM_FEEDBACK_BUFFER:
         case GL_UNIFORM_BUFFER:
-        case GL_ATOMIC_COUNTER_BUFFER:
-        case GL_SHADER_STORAGE_BUFFER:
           SynthesizeGLError(
               GL_INVALID_OPERATION, function_name,
               "element array buffers can not be bound to a different target");
@@ -5280,8 +5216,6 @@
     case GL_PIXEL_UNPACK_BUFFER:
     case GL_UNIFORM_BUFFER:
     case GL_TRANSFORM_FEEDBACK_BUFFER:
-    case GL_ATOMIC_COUNTER_BUFFER:
-    case GL_SHADER_STORAGE_BUFFER:
       if (target == GL_ELEMENT_ARRAY_BUFFER) {
         SynthesizeGLError(GL_INVALID_OPERATION, function_name,
                           "buffers bound to non ELEMENT_ARRAY_BUFFER targets "
@@ -5308,13 +5242,6 @@
     case GL_TRANSFORM_FEEDBACK_BUFFER:
     case GL_UNIFORM_BUFFER:
       return true;
-    case GL_ATOMIC_COUNTER_BUFFER:
-    case GL_SHADER_STORAGE_BUFFER:
-      if (ContextType() != Platform::kWebGL2ComputeContextType) {
-        SynthesizeGLError(GL_INVALID_ENUM, function_name, "invalid target");
-        return false;
-      }
-      return true;
     default:
       SynthesizeGLError(GL_INVALID_ENUM, function_name, "invalid target");
       return false;
@@ -5357,12 +5284,6 @@
     case GL_UNIFORM_BUFFER:
       bound_uniform_buffer_ = buffer;
       break;
-    case GL_ATOMIC_COUNTER_BUFFER:
-      bound_atomic_counter_buffer_ = buffer;
-      break;
-    case GL_SHADER_STORAGE_BUFFER:
-      bound_shader_storage_buffer_ = buffer;
-      break;
     default:
       NOTREACHED();
       break;
@@ -5380,13 +5301,6 @@
     case GL_TRANSFORM_FEEDBACK_BUFFER:
     case GL_UNIFORM_BUFFER:
       return true;
-    case GL_ATOMIC_COUNTER_BUFFER:
-    case GL_SHADER_STORAGE_BUFFER:
-      if (ContextType() != Platform::kWebGL2ComputeContextType) {
-        SynthesizeGLError(GL_INVALID_ENUM, function_name, "invalid target");
-        return false;
-      }
-      return true;
     default:
       SynthesizeGLError(GL_INVALID_ENUM, function_name, "invalid target");
       return false;
@@ -5438,24 +5352,6 @@
         max_bound_uniform_buffer_index_ = i;
       }
       break;
-    case GL_ATOMIC_COUNTER_BUFFER:
-      if (index >= bound_indexed_atomic_counter_buffers_.size()) {
-        SynthesizeGLError(GL_INVALID_VALUE, function_name,
-                          "index out of range");
-        return false;
-      }
-      bound_indexed_atomic_counter_buffers_[index] = buffer;
-      bound_atomic_counter_buffer_ = buffer;
-      break;
-    case GL_SHADER_STORAGE_BUFFER:
-      if (index >= bound_indexed_shader_storage_buffers_.size()) {
-        SynthesizeGLError(GL_INVALID_VALUE, function_name,
-                          "index out of range");
-        return false;
-      }
-      bound_indexed_shader_storage_buffers_[index] = buffer;
-      bound_shader_storage_buffer_ = buffer;
-      break;
     default:
       NOTREACHED();
       break;
@@ -5819,10 +5715,6 @@
   visitor->Trace(bound_transform_feedback_buffer_);
   visitor->Trace(bound_uniform_buffer_);
   visitor->Trace(bound_indexed_uniform_buffers_);
-  visitor->Trace(bound_atomic_counter_buffer_);
-  visitor->Trace(bound_indexed_atomic_counter_buffers_);
-  visitor->Trace(bound_shader_storage_buffer_);
-  visitor->Trace(bound_indexed_shader_storage_buffers_);
   visitor->Trace(current_boolean_occlusion_query_);
   visitor->Trace(current_transform_feedback_primitives_written_query_);
   visitor->Trace(current_elapsed_query_);
@@ -5930,22 +5822,6 @@
     case GL_UNIFORM_BUFFER:
       buffer = bound_uniform_buffer_.Get();
       break;
-    case GL_ATOMIC_COUNTER_BUFFER: {
-      if (ContextType() != Platform::kWebGL2ComputeContextType) {
-        SynthesizeGLError(GL_INVALID_ENUM, function_name, "invalid target");
-        return nullptr;
-      }
-      buffer = bound_atomic_counter_buffer_.Get();
-      break;
-    }
-    case GL_SHADER_STORAGE_BUFFER: {
-      if (ContextType() != Platform::kWebGL2ComputeContextType) {
-        SynthesizeGLError(GL_INVALID_ENUM, function_name, "invalid target");
-        return nullptr;
-      }
-      buffer = bound_shader_storage_buffer_.Get();
-      break;
-    }
     default:
       SynthesizeGLError(GL_INVALID_ENUM, function_name, "invalid target");
       return nullptr;
@@ -6044,10 +5920,6 @@
     bound_transform_feedback_buffer_ = nullptr;
   if (bound_uniform_buffer_ == buffer)
     bound_uniform_buffer_ = nullptr;
-  if (bound_atomic_counter_buffer_ == buffer)
-    bound_atomic_counter_buffer_ = nullptr;
-  if (bound_shader_storage_buffer_ == buffer)
-    bound_shader_storage_buffer_ = nullptr;
 
   transform_feedback_binding_->UnbindBuffer(buffer);
 
diff --git a/third_party/blink/renderer/modules/webgl/webgl2_rendering_context_base.h b/third_party/blink/renderer/modules/webgl/webgl2_rendering_context_base.h
index 383a5ebc..54b02ee 100644
--- a/third_party/blink/renderer/modules/webgl/webgl2_rendering_context_base.h
+++ b/third_party/blink/renderer/modules/webgl/webgl2_rendering_context_base.h
@@ -907,7 +907,7 @@
   /* Uniform Buffer Objects and Transform Feedback Buffers */
   void bindBufferBase(GLenum, GLuint, WebGLBuffer*);
   void bindBufferRange(GLenum, GLuint, WebGLBuffer*, int64_t, int64_t);
-  ScriptValue getIndexedParameter(ScriptState*, GLenum, GLuint);
+  virtual ScriptValue getIndexedParameter(ScriptState*, GLenum, GLuint);
   Vector<GLuint> getUniformIndices(WebGLProgram*, const Vector<String>&);
   ScriptValue getActiveUniforms(ScriptState*,
                                 WebGLProgram*,
@@ -1021,13 +1021,16 @@
 
   bool IsBufferBoundToTransformFeedback(WebGLBuffer*);
   bool IsBufferBoundToNonTransformFeedback(WebGLBuffer*);
-  bool ValidateBufferTargetCompatibility(const char*, GLenum, WebGLBuffer*);
+  virtual bool ValidateBufferTargetCompatibility(const char*,
+                                                 GLenum,
+                                                 WebGLBuffer*);
 
-  bool ValidateBufferBaseTarget(const char* function_name, GLenum target);
-  bool ValidateAndUpdateBufferBindBaseTarget(const char* function_name,
-                                             GLenum,
-                                             GLuint,
-                                             WebGLBuffer*);
+  virtual bool ValidateBufferBaseTarget(const char* function_name,
+                                        GLenum target);
+  virtual bool ValidateAndUpdateBufferBindBaseTarget(const char* function_name,
+                                                     GLenum,
+                                                     GLuint,
+                                                     WebGLBuffer*);
 
   WebGLImageConversion::PixelStoreParams GetPackPixelStoreParams() override;
   WebGLImageConversion::PixelStoreParams GetUnpackPixelStoreParams(
@@ -1126,11 +1129,7 @@
   Member<WebGLBuffer> bound_pixel_unpack_buffer_;
   Member<WebGLBuffer> bound_transform_feedback_buffer_;
   Member<WebGLBuffer> bound_uniform_buffer_;
-  Member<WebGLBuffer> bound_atomic_counter_buffer_;
-  Member<WebGLBuffer> bound_shader_storage_buffer_;
 
-  HeapVector<Member<WebGLBuffer>> bound_indexed_atomic_counter_buffers_;
-  HeapVector<Member<WebGLBuffer>> bound_indexed_shader_storage_buffers_;
   HeapVector<Member<WebGLBuffer>> bound_indexed_uniform_buffers_;
   GLint max_transform_feedback_separate_attribs_;
   wtf_size_t max_bound_uniform_buffer_index_;
diff --git a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
index 66ffd77..8e88a27 100644
--- a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
+++ b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
@@ -2331,25 +2331,6 @@
     bound_vertex_array_object_ = default_vertex_array_object_;
 }
 
-bool WebGLRenderingContextBase::ValidateShaderType(const char* function_name,
-                                                   GLenum shader_type) {
-  switch (shader_type) {
-    case GL_VERTEX_SHADER:
-    case GL_FRAGMENT_SHADER:
-      return true;
-    case GL_COMPUTE_SHADER:
-      if (context_type_ != Platform::kWebGL2ComputeContextType) {
-        SynthesizeGLError(GL_INVALID_ENUM, function_name,
-                          "invalid shader type");
-        return false;
-      }
-      return true;
-    default:
-      SynthesizeGLError(GL_INVALID_ENUM, function_name, "invalid shader type");
-      return false;
-  }
-}
-
 WebGLShader* WebGLRenderingContextBase::createShader(GLenum type) {
   if (isContextLost())
     return nullptr;
@@ -7009,6 +6990,18 @@
   return true;
 }
 
+bool WebGLRenderingContextBase::ValidateShaderType(const char* function_name,
+                                                   GLenum shader_type) {
+  switch (shader_type) {
+    case GL_VERTEX_SHADER:
+    case GL_FRAGMENT_SHADER:
+      return true;
+    default:
+      SynthesizeGLError(GL_INVALID_ENUM, function_name, "invalid shader type");
+      return false;
+  }
+}
+
 void WebGLRenderingContextBase::AddExtensionSupportedFormatsTypes() {
   if (!is_oes_texture_float_formats_types_added_ &&
       ExtensionEnabled(kOESTextureFloatName)) {
diff --git a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h
index b57ae0d..17b89bdc 100644
--- a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h
+++ b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h
@@ -1195,6 +1195,9 @@
   // 2.0.
   bool ValidateShaderSource(const String&);
 
+  virtual bool ValidateShaderType(const char* function_name,
+                                  GLenum shader_type);
+
   // Helper function to check texture binding target and texture bound to the
   // target.  Generate GL errors and return 0 if target is invalid or texture
   // bound is null.  Otherwise, return the texture bound to the target.
@@ -1717,8 +1720,6 @@
                       bool flip_y);
   bool CanUseTexImageViaGPU(GLenum format, GLenum type);
 
-  bool ValidateShaderType(const char* function_name, GLenum shader_type);
-
   const Platform::ContextType context_type_;
 
   bool IsPaintable() const final { return GetDrawingBuffer(); }
diff --git a/third_party/blink/renderer/platform/fonts/shaping/shape_result_view.cc b/third_party/blink/renderer/platform/fonts/shaping/shape_result_view.cc
index eaedff6..490b73f 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/shape_result_view.cc
+++ b/third_party/blink/renderer/platform/fonts/shaping/shape_result_view.cc
@@ -432,6 +432,9 @@
   float advance_so_far = initial_advance;
 
   for (const auto& part : parts_) {
+    if (!part->NumGlyphs())
+      continue;
+
     const auto& run = part->run_;
     unsigned graphemes_in_cluster = 1;
     float cluster_advance = 0;
diff --git a/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.cc b/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.cc
index 20b03cb3..c90fe8b 100644
--- a/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.cc
+++ b/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.cc
@@ -42,19 +42,17 @@
     base::WeakPtr<WebGraphicsContext3DProviderWrapper>&&
         context_provider_wrapper,
     IntSize mailbox_size,
-    MailboxType mailbox_type,
     std::unique_ptr<viz::SingleReleaseCallback> release_callback) {
   return base::AdoptRef(new AcceleratedStaticBitmapImage(
       mailbox, sync_token, texture_id, std::move(context_provider_wrapper),
-      mailbox_size, mailbox_type, std::move(release_callback)));
+      mailbox_size, std::move(release_callback)));
 }
 
 AcceleratedStaticBitmapImage::AcceleratedStaticBitmapImage(
     sk_sp<SkImage> image,
     base::WeakPtr<WebGraphicsContext3DProviderWrapper>&&
         context_provider_wrapper)
-    : paint_image_content_id_(cc::PaintImage::GetNextContentId()),
-      mailbox_type_(MailboxType::kDeprecatedMailbox) {
+    : paint_image_content_id_(cc::PaintImage::GetNextContentId()) {
   CHECK(image && image->isTextureBacked());
   texture_holder_ = std::make_unique<SkiaTextureHolder>(
       std::move(image), std::move(context_provider_wrapper));
@@ -67,10 +65,8 @@
     base::WeakPtr<WebGraphicsContext3DProviderWrapper>&&
         context_provider_wrapper,
     IntSize mailbox_size,
-    MailboxType mailbox_type,
     std::unique_ptr<viz::SingleReleaseCallback> release_callback)
     : paint_image_content_id_(cc::PaintImage::GetNextContentId()),
-      mailbox_type_(mailbox_type),
       release_callback_(std::move(release_callback)) {
   texture_holder_ = std::make_unique<MailboxTextureHolder>(
       mailbox, sync_token, texture_id, std::move(context_provider_wrapper),
@@ -178,11 +174,14 @@
   // that the source and destination context or on the same stream.
   EnsureMailbox(kUnverifiedSyncToken, GL_NEAREST);
 
+  DCHECK(texture_holder_->IsMailboxTextureHolder());
+  bool is_shared_image = texture_holder_->GetMailbox().IsSharedImage();
+
   // Get a texture id that |destProvider| knows about and copy from it.
   dest_gl->WaitSyncTokenCHROMIUM(
       texture_holder_->GetSyncToken().GetConstData());
   GLuint source_texture_id;
-  if (mailbox_type_ == MailboxType::kSharedImageId) {
+  if (is_shared_image) {
     source_texture_id = dest_gl->CreateAndTexStorage2DSharedImageCHROMIUM(
         texture_holder_->GetMailbox().name);
     dest_gl->BeginSharedImageAccessDirectCHROMIUM(
@@ -197,7 +196,7 @@
       source_sub_rectangle.Y(), source_sub_rectangle.Width(),
       source_sub_rectangle.Height(), unpack_flip_y ? GL_FALSE : GL_TRUE,
       GL_FALSE, unpack_premultiply_alpha ? GL_FALSE : GL_TRUE);
-  if (mailbox_type_ == MailboxType::kSharedImageId) {
+  if (is_shared_image) {
     dest_gl->EndSharedImageAccessDirectCHROMIUM(source_texture_id);
   }
   // This drops the |destGL| context's reference on our |m_mailbox|, but it's
@@ -283,10 +282,8 @@
   if (texture_holder_->IsSkiaTextureHolder())
     return;
 
-  const bool backed_by_shared_image =
-      mailbox_type_ == MailboxType::kSharedImageId;
-  texture_holder_ = std::make_unique<SkiaTextureHolder>(
-      std::move(texture_holder_), backed_by_shared_image);
+  texture_holder_ =
+      std::make_unique<SkiaTextureHolder>(std::move(texture_holder_));
 }
 
 void AcceleratedStaticBitmapImage::EnsureMailbox(MailboxSyncMode mode,
diff --git a/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h b/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h
index d9e1a8e..58da304 100644
--- a/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h
+++ b/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h
@@ -27,8 +27,6 @@
 class PLATFORM_EXPORT AcceleratedStaticBitmapImage final
     : public StaticBitmapImage {
  public:
-  enum class MailboxType { kSharedImageId, kDeprecatedMailbox };
-
   ~AcceleratedStaticBitmapImage() override;
   // SkImage with a texture backing.
   static scoped_refptr<AcceleratedStaticBitmapImage> CreateFromSkImage(
@@ -50,7 +48,6 @@
       unsigned texture_id,
       base::WeakPtr<WebGraphicsContext3DProviderWrapper>&&,
       IntSize mailbox_size,
-      MailboxType mailbox_type = MailboxType::kDeprecatedMailbox,
       std::unique_ptr<viz::SingleReleaseCallback> release_callback = nullptr);
 
   bool CurrentFrameKnownToBeOpaque() override;
@@ -115,7 +112,6 @@
       unsigned texture_id,
       base::WeakPtr<WebGraphicsContext3DProviderWrapper>&&,
       IntSize mailbox_size,
-      MailboxType mailbox_type,
       std::unique_ptr<viz::SingleReleaseCallback> release_callback);
 
   void CreateImageFromMailboxIfNeeded();
@@ -133,7 +129,6 @@
   base::WeakPtr<WebGraphicsContext3DProviderWrapper>
       original_skia_image_context_provider_wrapper_;
 
-  const MailboxType mailbox_type_;
   std::unique_ptr<viz::SingleReleaseCallback> release_callback_;
 };
 
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource.cc b/third_party/blink/renderer/platform/graphics/canvas_resource.cc
index cd927ea..f199c5b5 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource.cc
@@ -797,8 +797,7 @@
   scoped_refptr<StaticBitmapImage> image =
       AcceleratedStaticBitmapImage::CreateFromWebGLContextImage(
           shared_image_mailbox_, GetSyncToken(), 0, ContextProviderWrapper(),
-          Size(), AcceleratedStaticBitmapImage::MailboxType::kSharedImageId,
-          std::move(release_callback));
+          Size(), std::move(release_callback));
   DCHECK(image);
   return image;
 }
diff --git a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc
index 9cc4219..adeb47f 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc
@@ -65,10 +65,12 @@
 }
 
 void PaintArtifactCompositor::EnableExtraDataForTesting() {
-  if (!extra_data_for_testing_enabled_)
-    SetNeedsUpdate();
+  if (extra_data_for_testing_enabled_)
+    return;
   extra_data_for_testing_enabled_ = true;
   extra_data_for_testing_ = std::make_unique<ExtraDataForTesting>();
+  // Ensure |extra_data_for_testing_| is populated.
+  SetNeedsUpdate();
 }
 
 void PaintArtifactCompositor::SetTracksRasterInvalidations(bool should_track) {
diff --git a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc
index 330047d7..1b544a54 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc
+++ b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc
@@ -602,8 +602,7 @@
   // in DrawingBuffer.
   return AcceleratedStaticBitmapImage::CreateFromWebGLContextImage(
       sk_image_mailbox, sk_image_sync_token, texture_id,
-      context_provider_->GetWeakPtr(), size_,
-      AcceleratedStaticBitmapImage::MailboxType::kSharedImageId);
+      context_provider_->GetWeakPtr(), size_);
 }
 
 scoped_refptr<DrawingBuffer::ColorBuffer>
diff --git a/third_party/blink/renderer/platform/graphics/graphics_context.cc b/third_party/blink/renderer/platform/graphics/graphics_context.cc
index 6c28903..3e3eaadd 100644
--- a/third_party/blink/renderer/platform/graphics/graphics_context.cc
+++ b/third_party/blink/renderer/platform/graphics/graphics_context.cc
@@ -751,7 +751,6 @@
   if (ContextDisabled())
     return;
 
-  DCHECK(!rect.IsEmpty());
   if (rect.IsEmpty())
     return;
 
diff --git a/third_party/blink/renderer/platform/graphics/skia_texture_holder.cc b/third_party/blink/renderer/platform/graphics/skia_texture_holder.cc
index 873d712..50267b7 100644
--- a/third_party/blink/renderer/platform/graphics/skia_texture_holder.cc
+++ b/third_party/blink/renderer/platform/graphics/skia_texture_holder.cc
@@ -23,8 +23,7 @@
       image_(std::move(image)) {}
 
 SkiaTextureHolder::SkiaTextureHolder(
-    std::unique_ptr<TextureHolder> texture_holder,
-    bool backed_by_shared_image)
+    std::unique_ptr<TextureHolder> texture_holder)
     : TextureHolder(SharedGpuContext::ContextProviderWrapper()) {
   DCHECK(texture_holder->IsMailboxTextureHolder());
   const gpu::Mailbox mailbox = texture_holder->GetMailbox();
@@ -41,7 +40,7 @@
 
   shared_gl->WaitSyncTokenCHROMIUM(sync_token.GetConstData());
   GLuint shared_context_texture_id = 0u;
-  if (backed_by_shared_image) {
+  if (mailbox.IsSharedImage()) {
     shared_context_texture_id =
         shared_gl->CreateAndTexStorage2DSharedImageCHROMIUM(mailbox.name);
     shared_gl->BeginSharedImageAccessDirectCHROMIUM(
diff --git a/third_party/blink/renderer/platform/graphics/skia_texture_holder.h b/third_party/blink/renderer/platform/graphics/skia_texture_holder.h
index 8c0a1043..33f51fd 100644
--- a/third_party/blink/renderer/platform/graphics/skia_texture_holder.h
+++ b/third_party/blink/renderer/platform/graphics/skia_texture_holder.h
@@ -35,8 +35,7 @@
                     base::WeakPtr<WebGraphicsContext3DProviderWrapper>&&);
   // This function consumes the mailbox in the input parameter and turn it into
   // a texture-backed SkImage.
-  SkiaTextureHolder(std::unique_ptr<TextureHolder>,
-                    bool backed_by_shared_image);
+  SkiaTextureHolder(std::unique_ptr<TextureHolder>);
 
  private:
   // The image_ should always be texture-backed.
diff --git a/third_party/blink/web_tests/FlagExpectations/enable-blink-features=CompositeAfterPaint b/third_party/blink/web_tests/FlagExpectations/enable-blink-features=CompositeAfterPaint
index 51aee5f..99811d9 100644
--- a/third_party/blink/web_tests/FlagExpectations/enable-blink-features=CompositeAfterPaint
+++ b/third_party/blink/web_tests/FlagExpectations/enable-blink-features=CompositeAfterPaint
@@ -391,17 +391,6 @@
 crbug.com/909749 fast/events/touch/compositor-touch-hit-rects-global.html [ Failure ]
 crbug.com/909749 fast/events/touch/compositor-touch-hit-rects-non-composited-scroll.html [ Failure ]
 
-# These were introduced by the new dirty bit on PaintArtifactCompositor.
-crbug.com/909749 fast/events/touch/compositor-touch-hit-rects-animation.html [ Failure ]
-crbug.com/909749 fast/events/touch/compositor-touch-hit-rects-many.html [ Failure ]
-crbug.com/909749 fast/events/touch/compositor-touch-hit-rects-scroll.html [ Failure ]
-crbug.com/909749 fast/events/touch/compositor-touch-hit-rects-squashing.html [ Failure ]
-crbug.com/909749 fast/events/touch/compositor-touch-hit-rects-table.html [ Failure ]
-crbug.com/909749 fast/events/touch/compositor-touch-hit-rects-transform-changed-nolayout.html [ Failure ]
-crbug.com/909749 fast/events/touch/compositor-touch-hit-rects-visibility-hidden.html [ Failure ]
-crbug.com/909749 fast/events/touch/compositor-touch-hit-rects.html [ Failure ]
-crbug.com/909749 fast/events/touch/touch-rect-crash-on-unpromote-layer.html [ Failure ]
-
 # Backdrop filter
 crbug.com/923429 css3/filters/backdrop-filter-basic-blur.html [ Failure ]
 crbug.com/923429 css3/filters/backdrop-filter-browser-zoom.html [ Failure ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index a87b8480..00350ea 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -1861,6 +1861,8 @@
 crbug.com/626703 external/wpt/css/css-text/word-break/word-break-break-all-004.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-text/word-break/word-break-break-all-006.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-text/word-break/word-break-break-all-008.html [ Failure ]
+crbug.com/964181 external/wpt/css/css-text/word-break/word-break-break-all-inline-004.html [ Failure ]
+crbug.com/964181 external/wpt/css/css-text/word-break/word-break-break-all-inline-007.html [ Failure ]
 crbug.com/626703 [ Linux ] external/wpt/css/css-text/word-break/word-break-normal-bo-000.html [ Failure ]
 crbug.com/626703 [ Win ] external/wpt/css/css-text/word-break/word-break-normal-km-000.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-text/word-break/word-break-normal-my-000.html [ Failure ]
@@ -2734,13 +2736,11 @@
 crbug.com/626703 external/wpt/css/css-overflow/webkit-line-clamp-024.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-overflow/webkit-line-clamp-010.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-overflow/webkit-line-clamp-012.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-text/word-break/word-break-break-all-inline-007.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-overflow/webkit-line-clamp-033.html [ Failure ]
 crbug.com/626703 virtual/layout_ng_experimental/external/wpt/css/css-multicol/multicol-width-005.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-multicol/multicol-width-005.html [ Failure ]
 crbug.com/626703 virtual/layout_ng_experimental/external/wpt/css/css-multicol/multicol-width-004.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-overflow/webkit-line-clamp-027.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-text/word-break/word-break-break-all-inline-004.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-overflow/webkit-line-clamp-011.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-overflow/webkit-line-clamp-013.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-overflow/webkit-line-clamp-014.html [ Failure ]
@@ -3242,7 +3242,6 @@
 crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-content-horiz-002.xhtml [ Failure ]
 crbug.com/875411 external/wpt/svg/text/reftests/text-shape-inside-002.svg [ Failure ]
 crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-horiz-002.xhtml [ Failure ]
-crbug.com/626703 [ Android ] virtual/media-gpu-accelerated/external/wpt/media-source/mediasource-play.html [ Crash ]
 crbug.com/626703 external/wpt/speech-api/SpeechSynthesis-speak-twice.html [ Timeout ]
 crbug.com/626703 external/wpt/speech-api/SpeechSynthesis-speak-without-activation-fails.tentative.html [ Timeout ]
 crbug.com/626703 virtual/speech-with-unified-autoplay/external/wpt/speech-api/SpeechSynthesis-speak-twice.html [ Timeout ]
@@ -5634,3 +5633,6 @@
 crbug.com/965137 [ Linux ] external/wpt/css/css-overflow/webkit-line-clamp-026.html [ Failure ]
 crbug.com/965134 [ Linux ] fast/events/pointerevents/multi-touch-events.html [ Failure ]
 crbug.com/965134 [ Linux ] virtual/mouseevent_fractional/fast/events/pointerevents/pointer-event-in-slop-region.html [ Failure ]
+crbug.com/965356 [ Mac ] inspector-protocol/heap-profiler/heap-snapshot-merged-nodes.js [ Pass Failure ]
+crbug.com/965356 [ Mac ] inspector-protocol/heap-profiler/heap-snapshot-with-event-listener.js [ Pass Failure ]
+crbug.com/965134 [ Linux ] fast/events/pointerevents/pointer-event-in-slop-region.html [ Pass Failure ]
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-emphasis-unrepresentable-characters.html b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-emphasis-unrepresentable-characters.html
new file mode 100644
index 0000000..1c108fa
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-emphasis-unrepresentable-characters.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<title>Emphasis marks for unrepresentable characters should not crash</title>
+<link rel="author" title="Emil A Eklund" href="eae@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-decor-3/#emphasis-marks" title="3 Emphasis Marks">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+  div {
+    text-emphasis: circle;
+    -webkit-text-emphasis-style: circle;
+  }
+</style>
+<body>
+  <div></div>
+</body>
+<script>
+  var el = document.body.firstElementChild;
+  el.appendChild(document.createTextNode(unescape('%udb14')));
+  el.appendChild(document.createTextNode(unescape('%udfe3%u7af4')));
+</script>
+<script>test(()=>{})</script>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/paint/background/huge-offset-border-crash.html b/third_party/blink/web_tests/paint/background/huge-offset-border-crash.html
new file mode 100644
index 0000000..bfb129f
--- /dev/null
+++ b/third_party/blink/web_tests/paint/background/huge-offset-border-crash.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script>test(function(){});</script>
+<div style="transform: translateX(-100000000px)">
+  <div style="margin-left: 100000000px">
+    <div style="margin-left: 100px; width: 100px; height: 100px;
+                border-top: 1px solid blue; border-radius: 5px">
+    </div>
+  </div>
+</div>
+
diff --git a/third_party/blink/web_tests/wpt_internal/display-lock/lock-after-append/acquire-changed-containment.html b/third_party/blink/web_tests/wpt_internal/display-lock/lock-after-append/acquire-changed-containment.html
index 76474bd..c1ef658f 100644
--- a/third_party/blink/web_tests/wpt_internal/display-lock/lock-after-append/acquire-changed-containment.html
+++ b/third_party/blink/web_tests/wpt_internal/display-lock/lock-after-append/acquire-changed-containment.html
@@ -4,7 +4,7 @@
 <title>Display Locking: acquire, containment changes</title>
 <link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
 <link rel="help" href="https://github.com/WICG/display-locking">
-<link rel="match" href="pass-no-containment-ref.html">
+<link rel="match" href="pass-no-containment-with-child-ref.html">
 <script src="/common/reftest-wait.js"></script>
 
 <style>
@@ -13,10 +13,15 @@
   height: 150px;
   background: lightblue;
 }
+#child {
+  width: 50px;
+  height: 50px;
+  background: lightgreen;
+}
 </style>
 
 <div id="log"></div>
-<div id="container" style="contain: style layout;"></div>
+<div id="container" style="contain: style layout;"><div id="child" style="display: none;"></div></div>
 
 <script>
 function finishTest(status_string) {
@@ -27,7 +32,9 @@
 
 function runTest() {
   const container = document.getElementById("container");
+  // Recalc child and container when acquiring.
   container.style = "";
+  child.style = "";
   container.displayLock.acquire({ timeout: Infinity }).then(
     () => { finishTest("FAIL"); },
     (e) => { finishTest("PASS " + e.message); });
diff --git a/third_party/blink/web_tests/wpt_internal/display-lock/lock-after-append/pass-no-containment-with-child-ref.html b/third_party/blink/web_tests/wpt_internal/display-lock/lock-after-append/pass-no-containment-with-child-ref.html
new file mode 100644
index 0000000..00127f4
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/display-lock/lock-after-append/pass-no-containment-with-child-ref.html
@@ -0,0 +1,24 @@
+<!doctype HTML>
+<html>
+<meta charset="utf8">
+<title>Display Locking: pass, container with child</title>
+<link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
+<link rel="help" href="https://github.com/WICG/display-locking">
+
+<style>
+#container {
+  contain: style layout;
+  width: 150px;
+  height: 150px;
+  background: lightblue;
+}
+#child {
+  width: 50px;
+  height: 50px;
+  background: lightgreen;
+}
+</style>
+
+<div id="log">PASS Containment requirement is not satisfied.</div>
+<div id="container"><div id="child"></div></div>
+</html>
diff --git a/third_party/blink/web_tests/wpt_internal/display-lock/lock-before-append/acquire-on-no-containment.html b/third_party/blink/web_tests/wpt_internal/display-lock/lock-before-append/acquire-on-no-containment.html
new file mode 100644
index 0000000..6404357a
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/display-lock/lock-before-append/acquire-on-no-containment.html
@@ -0,0 +1,50 @@
+<!doctype HTML>
+<html class="reftest-wait">
+<meta charset="utf8">
+<title>Display Locking: acquire, no containment</title>
+<link rel="author" title="Rakina Zata Amni" href="mailto:rakina@chromium.org">
+<link rel="help" href="https://github.com/WICG/display-locking">
+<link rel="match" href="pass-container-with-child-ref.html">
+<script src="/common/reftest-wait.js"></script>
+
+<style>
+#container {
+  width: 150px;
+  height: 150px;
+  background: lightblue;
+}
+#child {
+  width: 50px;
+  height: 50px;
+  background: lightgreen;
+}
+</style>
+
+<div id="log"></div>
+
+<script>
+function finishTest(status_string) {
+  if (document.getElementById("log").innerHTML === "")
+    document.getElementById("log").innerHTML = status_string;
+  takeScreenshot();
+}
+
+function runTest() {
+  const container = document.createElement("div");
+  container.id = "container";
+  container.innerHTML = "<div id='child'></div>";
+  container.displayLock.acquire({ timeout: Infinity }).then(
+    () => {
+      document.body.appendChild(container);
+      container.offsetTop;
+      if (container.displayLock.locked)
+        finishTest("FAIL didn't get unlocked");
+      else
+        finishTest("PASS");
+    },
+    (e) => { finishTest("FAIL" + e.message); });
+}
+
+window.onload = runTest;
+</script>
+</html>
diff --git a/third_party/freetype/README.chromium b/third_party/freetype/README.chromium
index 531e7e1..fd5e271 100644
--- a/third_party/freetype/README.chromium
+++ b/third_party/freetype/README.chromium
@@ -1,7 +1,7 @@
 Name: FreeType
 URL: http://www.freetype.org/
-Version: VER-2-9-1-352-g2f4b740ce
-Revision: 2f4b740ce435bc1ad5ef8570bb91ab7cd5682720
+Version: VER-2-10-0-0-gfbbcf5036
+Revision: fbbcf50367403a6316a013b51690071198962920
 License: Custom license "inspired by the BSD, Artistic, and IJG (Independent
          JPEG Group) licenses"
 License File: src/docs/FTL.TXT
diff --git a/third_party/metrics_proto/README.chromium b/third_party/metrics_proto/README.chromium
index 17d206c..06e3a8e 100644
--- a/third_party/metrics_proto/README.chromium
+++ b/third_party/metrics_proto/README.chromium
@@ -1,8 +1,8 @@
 Name: Metrics Protos
 Short Name: metrics_proto
 URL: This is the canonical public repository
-Version: 249077303
-Date: 2019/05/20 UTC
+Version: 249161107
+Date: 2019/05/21 UTC
 License: BSD
 Security Critical: Yes
 
diff --git a/third_party/metrics_proto/call_stack_profile.proto b/third_party/metrics_proto/call_stack_profile.proto
index 8b001bb..e0433c4 100644
--- a/third_party/metrics_proto/call_stack_profile.proto
+++ b/third_party/metrics_proto/call_stack_profile.proto
@@ -109,6 +109,13 @@
     // metadata items are specified only when their values change from the
     // previous sample. Items are not guaranteed to be in a particular order.
     repeated MetadataItem metadata = 5;
+
+    // Weight of the sample. When omitted the sample is presumed to have
+    // a weight of 1.
+    // Not currently used for CPU profiles.
+    // For heap profiles it represents the number of bytes attributed to the
+    // sample.
+    optional int64 weight = 6;
   }
 
   // The previous sample encoding. Deprecated 2018/08/04 in favor of
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index b07ac13..2e908fbe 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -19362,6 +19362,7 @@
   <int value="1336" label="AUTOTESTPRIVATE_SETSHELFALIGNMENT"/>
   <int value="1337" label="BLUETOOTH_RECORDPAIRING"/>
   <int value="1338" label="FILEMANAGERPRIVATE_SETARCSTORAGETOASTSHOWNFLAG"/>
+  <int value="1339" label="INPUTMETHODPRIVATE_SETCOMPOSITIONRANGE"/>
 </enum>
 
 <enum name="ExtensionIconState">
@@ -41838,6 +41839,7 @@
   <int value="3" label="Translate"/>
   <int value="4" label="News"/>
   <int value="5" label="Chrome Web Store"/>
+  <int value="6" label="Search"/>
 </enum>
 
 <enum name="NuxLandingPageInteractions">
@@ -46883,6 +46885,7 @@
   <int value="4" label="Can't configure native printers due to policy"/>
   <int value="5" label="Invalid values are used to update printer"/>
   <int value="6" label="Could not install component"/>
+  <int value="7" label="Edit success"/>
   <int value="10" label="PPD exceeds size limit"/>
   <int value="11" label="PPD Rejected by cupstestppd"/>
   <int value="12" label="Could not find PPD"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index f0e659b..37f19bd 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -14671,6 +14671,9 @@
     Logs when a user action triggers a command in the bookmark manager. Commands
     can be triggered by keyboard shortcuts, menu items or other buttons in the
     UI.
+
+    Revised in M-76 when the &quot;Open (double click/enter)&quot; bucket was
+    split into two separate buckets, one for bookmarks and one for folders.
   </summary>
 </histogram>
 
@@ -14679,6 +14682,9 @@
   <owner>calamity@chromium.org</owner>
   <summary>
     Logs when a keyboard shortcut triggers a command in the bookmark manager.
+
+    Revised in M-76 when the &quot;Open (double click/enter)&quot; bucket was
+    split into two separate buckets, one for bookmarks and one for folders.
   </summary>
 </histogram>
 
@@ -92737,6 +92743,70 @@
   </summary>
 </histogram>
 
+<histogram name="Platform.Memory.ARC" units="MiB" expires_after="2020-11-01">
+<!-- Name completed by histogram_suffixes name="ProcessMemoryType" -->
+
+  <owner>semenzato@chromium.org</owner>
+  <owner>sonnyrao@chromium.org</owner>
+  <summary>
+    Memory usage of all ARC processes in Chrome OS, reported every 10 minutes.
+  </summary>
+</histogram>
+
+<histogram name="Platform.Memory.Browser" units="MiB"
+    expires_after="2020-11-01">
+<!-- Name completed by histogram_suffixes name="ProcessMemoryType" -->
+
+  <owner>semenzato@chromium.org</owner>
+  <owner>sonnyrao@chromium.org</owner>
+  <summary>
+    Memory usage of browser and helper processes (excluding GPU process and
+    renderers) in Chrome OS, reported every 10 minutes.
+  </summary>
+</histogram>
+
+<histogram name="Platform.Memory.Daemons" units="MiB"
+    expires_after="2020-11-01">
+<!-- Name completed by histogram_suffixes name="ProcessMemoryType" -->
+
+  <owner>semenzato@chromium.org</owner>
+  <owner>sonnyrao@chromium.org</owner>
+  <summary>
+    Memory usage of user-level daemons in Chrome OS, reported every 10 minutes.
+  </summary>
+</histogram>
+
+<histogram name="Platform.Memory.Gpu" units="MiB" expires_after="2020-11-01">
+<!-- Name completed by histogram_suffixes name="ProcessMemoryType" -->
+
+  <owner>semenzato@chromium.org</owner>
+  <owner>sonnyrao@chromium.org</owner>
+  <summary>
+    Memory usage of the GPU process in Chrome OS, reported every 10 minutes.
+  </summary>
+</histogram>
+
+<histogram name="Platform.Memory.Renderers" units="MiB"
+    expires_after="2020-11-01">
+<!-- Name completed by histogram_suffixes name="ProcessMemoryType" -->
+
+  <owner>semenzato@chromium.org</owner>
+  <owner>sonnyrao@chromium.org</owner>
+  <summary>
+    Memory usage of renderer processes in Chrome OS, reported every 10 minutes.
+  </summary>
+</histogram>
+
+<histogram name="Platform.Memory.VMs" units="MiB" expires_after="2020-11-01">
+<!-- Name completed by histogram_suffixes name="ProcessMemoryType" -->
+
+  <owner>semenzato@chromium.org</owner>
+  <owner>sonnyrao@chromium.org</owner>
+  <summary>
+    Memory usage of VMs (crostini) in Chrome OS, reported every 10 minutes.
+  </summary>
+</histogram>
+
 <histogram name="Platform.MemoryBandwidth.ReadWrite" units="MB/s">
   <owner>hajimehoshi@chromium.org</owner>
   <owner>kouhei@chromium.org</owner>
@@ -155638,6 +155708,20 @@
   <affected-histogram name="Memory.Experimental.Utility2.Small"/>
 </histogram_suffixes>
 
+<histogram_suffixes name="ProcessMemoryType" separator=".">
+  <suffix name="Anon" label="Measures anonymous RSS."/>
+  <suffix name="File" label="Measures file RSS."/>
+  <suffix name="Shmem" label="Measures shmem RSS."/>
+  <suffix name="Swap" label="Measures swap in use."/>
+  <suffix name="Total" label="Measures total RSS."/>
+  <affected-histogram name="Platform.Memory.ARC"/>
+  <affected-histogram name="Platform.Memory.Browser"/>
+  <affected-histogram name="Platform.Memory.Daemons"/>
+  <affected-histogram name="Platform.Memory.Gpu"/>
+  <affected-histogram name="Platform.Memory.Renderers"/>
+  <affected-histogram name="Platform.Memory.VMs"/>
+</histogram_suffixes>
+
 <histogram_suffixes name="ProcessType" separator=".">
   <obsolete>
     Removed 2019-05
diff --git a/tools/traffic_annotation/summary/annotations.xml b/tools/traffic_annotation/summary/annotations.xml
index bd13b26..a9e8e21e 100644
--- a/tools/traffic_annotation/summary/annotations.xml
+++ b/tools/traffic_annotation/summary/annotations.xml
@@ -157,6 +157,7 @@
  <item id="network_location_request" hash_code="96590038" type="2" content_hash_code="80741011" os_list="linux,windows" semantics_fields="2,3,4,5" policy_fields="-1" file_path="services/device/geolocation/network_location_request.cc"/>
  <item id="network_time_component" hash_code="46188932" type="0" content_hash_code="28051857" os_list="linux,windows" file_path="components/network_time/network_time_tracker.cc"/>
  <item id="ntp_contextual_suggestions_fetch" hash_code="95711309" type="0" deprecated="2019-04-18" content_hash_code="107035434" file_path=""/>
+ <item id="ntp_custom_background" hash_code="92125886" type="0" content_hash_code="61176452" os_list="linux,windows" file_path="chrome/browser/search/instant_service.cc"/>
  <item id="ntp_custom_link_checker_request" hash_code="78408551" type="0" deprecated="2018-10-26" content_hash_code="13407730" file_path=""/>
  <item id="ntp_icon_source" hash_code="29197139" type="0" content_hash_code="16399294" os_list="linux,windows" file_path="chrome/browser/search/ntp_icon_source.cc"/>
  <item id="ntp_snippets_fetch" hash_code="15418154" type="0" content_hash_code="10078959" os_list="linux,windows" file_path="components/ntp_snippets/remote/json_request.cc"/>
diff --git a/ui/accessibility/ax_event_generator.h b/ui/accessibility/ax_event_generator.h
index 1f80d95e..44f62aa 100644
--- a/ui/accessibility/ax_event_generator.h
+++ b/ui/accessibility/ax_event_generator.h
@@ -231,7 +231,7 @@
   std::vector<AXNode*> active_descendant_changed_;
 
   // The value of the event from field in TargetedEvent.
-  ax::mojom::EventFrom event_from_;
+  ax::mojom::EventFrom event_from_ = ax::mojom::EventFrom::kNone;
 };
 
 }  // namespace ui
diff --git a/ui/accessibility/ax_node_position_unittest.cc b/ui/accessibility/ax_node_position_unittest.cc
index 368a5f8..02738ff3 100644
--- a/ui/accessibility/ax_node_position_unittest.cc
+++ b/ui/accessibility/ax_node_position_unittest.cc
@@ -2508,6 +2508,8 @@
                    "affinity=downstream annotated_text=Line 1\n<L>ine 2",
                    "TextPosition anchor_id=1 text_offset=12 "
                    "affinity=downstream annotated_text=Line 1\nLine <2>",
+                   "TextPosition anchor_id=1 text_offset=13 "
+                   "affinity=downstream annotated_text=Line 1\nLine 2<>",
                    "NullPosition"}},
         TestParam{base::BindRepeating([](const TestPositionType& position) {
                     return position->CreateNextWordStartPosition(
@@ -2521,6 +2523,8 @@
                    "affinity=downstream annotated_text=Line 1\n<L>ine 2",
                    "TextPosition anchor_id=4 text_offset=12 "
                    "affinity=downstream annotated_text=Line 1\nLine <2>",
+                   "TextPosition anchor_id=4 text_offset=13 "
+                   "affinity=downstream annotated_text=Line 1\nLine 2<>",
                    "NullPosition"}},
         TestParam{base::BindRepeating([](const TestPositionType& position) {
                     return position->CreateNextWordStartPosition(
@@ -2534,6 +2538,8 @@
                    "affinity=downstream annotated_text=<L>ine 2",
                    "TextPosition anchor_id=9 text_offset=5 "
                    "affinity=downstream annotated_text=Line <2>",
+                   "TextPosition anchor_id=9 text_offset=6 "
+                   "affinity=downstream annotated_text=Line 2<>",
                    "NullPosition"}},
         TestParam{base::BindRepeating([](const TestPositionType& position) {
                     return position->CreateNextWordStartPosition(
@@ -2543,6 +2549,8 @@
                   4 /* text_offset */,
                   {"TextPosition anchor_id=9 text_offset=5 "
                    "affinity=downstream annotated_text=Line <2>",
+                   "TextPosition anchor_id=9 text_offset=6 "
+                   "affinity=downstream annotated_text=Line 2<>",
                    "NullPosition"}}));
 
 INSTANTIATE_TEST_SUITE_P(
@@ -2977,6 +2985,8 @@
                    "affinity=downstream annotated_text=Line 1<\n>Line 2",
                    "TextPosition anchor_id=1 text_offset=4 "
                    "affinity=downstream annotated_text=Line< >1\nLine 2",
+                   "TextPosition anchor_id=1 text_offset=0 "
+                   "affinity=downstream annotated_text=<L>ine 1\nLine 2",
                    "NullPosition"}},
         TestParam{base::BindRepeating([](const TestPositionType& position) {
                     return position->CreatePreviousWordEndPosition(
@@ -2990,6 +3000,8 @@
                    "affinity=downstream annotated_text=Line 1<\n>Line 2",
                    "TextPosition anchor_id=4 text_offset=4 "
                    "affinity=downstream annotated_text=Line< >1\nLine 2",
+                   "TextPosition anchor_id=4 text_offset=0 "
+                   "affinity=downstream annotated_text=<L>ine 1\nLine 2",
                    "NullPosition"}},
         TestParam{base::BindRepeating([](const TestPositionType& position) {
                     return position->CreatePreviousWordEndPosition(
@@ -2999,6 +3011,8 @@
                   5 /* text_offset */,
                   {"TextPosition anchor_id=5 text_offset=4 "
                    "affinity=downstream annotated_text=Line< >1",
+                   "TextPosition anchor_id=5 text_offset=0 "
+                   "affinity=downstream annotated_text=<L>ine 1",
                    "NullPosition"}},
         TestParam{base::BindRepeating([](const TestPositionType& position) {
                     return position->CreatePreviousWordEndPosition(
@@ -3010,6 +3024,8 @@
                    "affinity=downstream annotated_text=Line 1<>",
                    "TextPosition anchor_id=6 text_offset=4 "
                    "affinity=downstream annotated_text=Line< >1",
+                   "TextPosition anchor_id=6 text_offset=0 "
+                   "affinity=downstream annotated_text=<L>ine 1",
                    "NullPosition"}}));
 
 INSTANTIATE_TEST_SUITE_P(
@@ -3116,6 +3132,8 @@
                   0 /* text_offset */,
                   {"TextPosition anchor_id=1 text_offset=7 "
                    "affinity=downstream annotated_text=Line 1\n<L>ine 2",
+                   "TextPosition anchor_id=1 text_offset=13 "
+                   "affinity=downstream annotated_text=Line 1\nLine 2<>",
                    "NullPosition"}},
         TestParam{base::BindRepeating([](const TestPositionType& position) {
                     return position->CreateNextLineStartPosition(
@@ -3125,6 +3143,8 @@
                   0 /* text_offset */,
                   {"TextPosition anchor_id=4 text_offset=7 "
                    "affinity=downstream annotated_text=Line 1\n<L>ine 2",
+                   "TextPosition anchor_id=4 text_offset=13 "
+                   "affinity=downstream annotated_text=Line 1\nLine 2<>",
                    "NullPosition"}},
         TestParam{base::BindRepeating([](const TestPositionType& position) {
                     return position->CreateNextLineStartPosition(
@@ -3134,6 +3154,8 @@
                   1 /* text_offset */,
                   {"TextPosition anchor_id=9 text_offset=0 "
                    "affinity=downstream annotated_text=<L>ine 2",
+                   "TextPosition anchor_id=9 text_offset=6 "
+                   "affinity=downstream annotated_text=Line 2<>",
                    "NullPosition"}},
         TestParam{base::BindRepeating([](const TestPositionType& position) {
                     return position->CreateNextLineStartPosition(
@@ -3141,7 +3163,9 @@
                   }),
                   INLINE_BOX2_ID,
                   4 /* text_offset */,
-                  {"NullPosition"}}));
+                  {"TextPosition anchor_id=9 text_offset=6 "
+                   "affinity=downstream annotated_text=Line 2<>",
+                   "NullPosition"}}));
 
 INSTANTIATE_TEST_SUITE_P(
     CreateNextLineStartPositionWithBoundaryBehaviorStopAtAnchorBoundary,
@@ -3224,7 +3248,10 @@
                   }),
                   INLINE_BOX2_ID,
                   4 /* text_offset */,
-                  {"NullPosition"}}));
+                  {"TextPosition anchor_id=9 text_offset=6 affinity=downstream "
+                   "annotated_text=Line 2<>",
+                   "TextPosition anchor_id=9 text_offset=6 affinity=downstream "
+                   "annotated_text=Line 2<>"}}));
 
 INSTANTIATE_TEST_SUITE_P(
     CreatePreviousLineStartPositionWithBoundaryBehaviorCrossBoundary,
@@ -3520,6 +3547,8 @@
                   13 /* text_offset at end of root. */,
                   {"TextPosition anchor_id=1 text_offset=6 "
                    "affinity=downstream annotated_text=Line 1<\n>Line 2",
+                   "TextPosition anchor_id=1 text_offset=0 "
+                   "affinity=downstream annotated_text=<L>ine 1\nLine 2",
                    "NullPosition"}},
         TestParam{base::BindRepeating([](const TestPositionType& position) {
                     return position->CreatePreviousLineEndPosition(
@@ -3529,6 +3558,8 @@
                   13 /* text_offset at end of text field */,
                   {"TextPosition anchor_id=4 text_offset=6 "
                    "affinity=downstream annotated_text=Line 1<\n>Line 2",
+                   "TextPosition anchor_id=4 text_offset=0 "
+                   "affinity=downstream annotated_text=<L>ine 1\nLine 2",
                    "NullPosition"}},
         TestParam{base::BindRepeating([](const TestPositionType& position) {
                     return position->CreatePreviousLineEndPosition(
@@ -3536,14 +3567,18 @@
                   }),
                   ROOT_ID,
                   5 /* text_offset on the last character of "Line 1". */,
-                  {"NullPosition"}},
+                  {"TextPosition anchor_id=1 text_offset=0 "
+                   "affinity=downstream annotated_text=<L>ine 1\nLine 2",
+                   "NullPosition"}},
         TestParam{base::BindRepeating([](const TestPositionType& position) {
                     return position->CreatePreviousLineEndPosition(
                         AXBoundaryBehavior::CrossBoundary);
                   }),
                   TEXT_FIELD_ID,
                   5 /* text_offset on the last character of "Line 1". */,
-                  {"NullPosition"}},
+                  {"TextPosition anchor_id=4 text_offset=0 "
+                   "affinity=downstream annotated_text=<L>ine 1\nLine 2",
+                   "NullPosition"}},
         TestParam{base::BindRepeating([](const TestPositionType& position) {
                     return position->CreatePreviousLineEndPosition(
                         AXBoundaryBehavior::CrossBoundary);
@@ -3552,6 +3587,8 @@
                   4 /* text_offset */,
                   {"TextPosition anchor_id=6 text_offset=6 "
                    "affinity=downstream annotated_text=Line 1<>",
+                   "TextPosition anchor_id=6 text_offset=0 "
+                   "affinity=downstream annotated_text=<L>ine 1",
                    "NullPosition"}},
         TestParam{base::BindRepeating([](const TestPositionType& position) {
                     return position->CreatePreviousLineEndPosition(
@@ -3561,6 +3598,8 @@
                   0 /* text_offset */,
                   {"TextPosition anchor_id=6 text_offset=6 "
                    "affinity=downstream annotated_text=Line 1<>",
+                   "TextPosition anchor_id=6 text_offset=0 "
+                   "affinity=downstream annotated_text=<L>ine 1",
                    "NullPosition"}}));
 
 INSTANTIATE_TEST_SUITE_P(
@@ -3660,6 +3699,16 @@
                     return position->CreatePreviousLineEndPosition(
                         AXBoundaryBehavior::StopIfAlreadyAtBoundary);
                   }),
+                  INLINE_BOX1_ID,
+                  2 /* text_offset */,
+                  {"TextPosition anchor_id=6 text_offset=0 "
+                   "affinity=downstream annotated_text=<L>ine 1",
+                   "TextPosition anchor_id=6 text_offset=0 "
+                   "affinity=downstream annotated_text=<L>ine 1"}},
+        TestParam{base::BindRepeating([](const TestPositionType& position) {
+                    return position->CreatePreviousLineEndPosition(
+                        AXBoundaryBehavior::StopIfAlreadyAtBoundary);
+                  }),
                   INLINE_BOX2_ID,
                   4 /* text_offset */,
                   {"TextPosition anchor_id=6 text_offset=6 "
diff --git a/ui/accessibility/ax_position.h b/ui/accessibility/ax_position.h
index 0e03689d..56c9c30 100644
--- a/ui/accessibility/ax_position.h
+++ b/ui/accessibility/ax_position.h
@@ -904,9 +904,10 @@
         do {
           text_position = text_position->CreateNextTextAnchorPosition();
           if (text_position->IsNullPosition()) {
-            if (boundary_behavior == AXBoundaryBehavior::StopAtAnchorBoundary)
-              return CreatePositionAtEndOfAnchor();
-            return text_position;
+            if (AtEndOfAnchor() &&
+                boundary_behavior == AXBoundaryBehavior::CrossBoundary)
+              return text_position;
+            return CreatePositionAtEndOfAnchor();
           }
         } while (!text_position->MaxTextOffset() ||
                  text_position->GetWordStartOffsets().empty());
@@ -1104,9 +1105,10 @@
           text_position = text_position->CreatePreviousTextAnchorPosition()
                               ->CreatePositionAtEndOfAnchor();
           if (text_position->IsNullPosition()) {
-            if (boundary_behavior == AXBoundaryBehavior::StopAtAnchorBoundary)
-              return CreatePositionAtStartOfAnchor();
-            return text_position;
+            if (AtStartOfAnchor() &&
+                boundary_behavior == AXBoundaryBehavior::CrossBoundary)
+              return text_position;
+            return CreatePositionAtStartOfAnchor();
           }
         } while (!text_position->MaxTextOffset() ||
                  text_position->GetWordStartOffsets().empty());
@@ -1160,9 +1162,10 @@
     do {
       text_position = text_position->CreateNextTextAnchorPosition();
       if (text_position->IsNullPosition()) {
-        if (boundary_behavior == AXBoundaryBehavior::StopAtAnchorBoundary)
-          return CreatePositionAtEndOfAnchor();
-        return text_position;
+        if (AtEndOfAnchor() &&
+            boundary_behavior == AXBoundaryBehavior::CrossBoundary)
+          return text_position;
+        return CreatePositionAtEndOfAnchor();
       }
 
       // Continue searching for the next line start until the next logical text
@@ -1319,9 +1322,10 @@
       text_position = text_position->CreatePreviousTextAnchorPosition()
                           ->CreatePositionAtEndOfAnchor();
       if (text_position->IsNullPosition()) {
-        if (boundary_behavior == AXBoundaryBehavior::StopAtAnchorBoundary)
-          return CreatePositionAtStartOfAnchor();
-        return text_position;
+        if (AtStartOfAnchor() &&
+            boundary_behavior == AXBoundaryBehavior::CrossBoundary)
+          return text_position;
+        return CreatePositionAtStartOfAnchor();
       }
 
       // Continue searching for the previous line end until the next logical
diff --git a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.cc b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.cc
index fc30f378..18fcd9b 100644
--- a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.cc
+++ b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.cc
@@ -379,6 +379,10 @@
 
     if (attribute_value_variant.type() == VT_EMPTY) {
       attribute_value_variant.Reset(current_variant);
+      if (attribute_value_variant.type() == VT_UNKNOWN) {
+        *value = attribute_value_variant.Release();
+        return S_OK;
+      }
     } else if (0 != attribute_value_variant.Compare(current_variant)) {
       V_VT(value) = VT_UNKNOWN;
       return ::UiaGetReservedMixedAttributeValue(&V_UNKNOWN(value));
@@ -509,16 +513,30 @@
   HRESULT hr = MoveEndpointByUnit(TextPatternRangeEndpoint_Start, unit, count,
                                   &start_units_moved);
 
-  bool succeeded_move = SUCCEEDED(hr) && start_units_moved == count;
+  bool succeeded_move = SUCCEEDED(hr) && start_units_moved != 0;
   if (succeeded_move) {
     end_ = start_->Clone();
     if (!is_degenerate_range) {
-      // Expand the text range from the degenerate state by moving the
-      // endpoint forward by one text unit.
-      int end_units_moved = 0;
-      hr = MoveEndpointByUnit(TextPatternRangeEndpoint_End, unit, 1,
-                              &end_units_moved);
-      succeeded_move = SUCCEEDED(hr) && end_units_moved == 1;
+      bool forwards = count > 0;
+      if (forwards && start_->AtEndOfDocument()) {
+        // The start is at the end of the document, so move the start backward
+        // by one text unit to expand the text range from the degenerate range
+        // state.
+        int current_start_units_moved = 0;
+        hr = MoveEndpointByUnit(TextPatternRangeEndpoint_Start, unit, -1,
+                                &current_start_units_moved);
+        start_units_moved -= 1;
+        succeeded_move = SUCCEEDED(hr) && current_start_units_moved == -1 &&
+                         start_units_moved > 0;
+      } else {
+        // The start is not at the end of the document, so move the endpoint
+        // forward by one text unit to expand the text range from the degenerate
+        // state.
+        int end_units_moved = 0;
+        hr = MoveEndpointByUnit(TextPatternRangeEndpoint_End, unit, 1,
+                                &end_units_moved);
+        succeeded_move = SUCCEEDED(hr) && end_units_moved == 1;
+      }
     }
   }
 
diff --git a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc
index f4264d9..c84e45dd 100644
--- a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc
+++ b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc
@@ -90,6 +90,30 @@
     EXPECT_EQ(0, scoped_variant.Compare(variant));                         \
   }
 
+#define EXPECT_UIA_TEXTATTRIBUTE_MIXED(provider, attribute)                \
+  {                                                                        \
+    CComPtr<IUnknown> expected_mixed;                                      \
+    EXPECT_HRESULT_SUCCEEDED(                                              \
+        ::UiaGetReservedMixedAttributeValue(&expected_mixed));             \
+    base::win::ScopedVariant scoped_variant;                               \
+    EXPECT_HRESULT_SUCCEEDED(                                              \
+        provider->GetAttributeValue(attribute, scoped_variant.Receive())); \
+    EXPECT_EQ(VT_UNKNOWN, scoped_variant.type());                          \
+    EXPECT_EQ(expected_mixed, V_UNKNOWN(scoped_variant.ptr()));            \
+  }
+
+#define EXPECT_UIA_TEXTATTRIBUTE_NOTSUPPORTED(provider, attribute)         \
+  {                                                                        \
+    CComPtr<IUnknown> expected_notsupported;                               \
+    EXPECT_HRESULT_SUCCEEDED(                                              \
+        ::UiaGetReservedNotSupportedValue(&expected_notsupported));        \
+    base::win::ScopedVariant scoped_variant;                               \
+    EXPECT_HRESULT_SUCCEEDED(                                              \
+        provider->GetAttributeValue(attribute, scoped_variant.Receive())); \
+    EXPECT_EQ(VT_UNKNOWN, scoped_variant.type());                          \
+    EXPECT_EQ(expected_notsupported, V_UNKNOWN(scoped_variant.ptr()));     \
+  }
+
 #define EXPECT_UIA_TEXTRANGE_EQ(provider, expected_content) \
   {                                                         \
     base::win::ScopedBstr provider_content;                 \
@@ -1133,7 +1157,7 @@
                   /*expected_text*/ L"b",
                   /*expected_count*/ 16);
   EXPECT_UIA_MOVE(text_range_provider, TextUnit_Character,
-                  /*count*/ 30,
+                  /*count*/ 60,
                   /*expected_text*/ L"2",
                   /*expected_count*/ 30);
 
@@ -1153,7 +1177,7 @@
                   /*expected_text*/ L"1",
                   /*expected_count*/ -9);
   EXPECT_UIA_MOVE(text_range_provider, TextUnit_Character,
-                  /*count*/ -54,
+                  /*count*/ -60,
                   /*expected_text*/ L"F",
                   /*expected_count*/ -54);
 
@@ -1175,6 +1199,10 @@
                   /*count*/ 4,
                   /*expected_text*/ L"",
                   /*expected_count*/ 4);
+  EXPECT_UIA_MOVE(text_range_provider, TextUnit_Character,
+                  /*count*/ 70,
+                  /*expected_text*/ L"",
+                  /*expected_count*/ 62);
 
   // Trying to move past the last character should have no effect.
   EXPECT_UIA_MOVE(text_range_provider, TextUnit_Character,
@@ -1303,6 +1331,10 @@
                   /*count*/ 3,
                   /*expected_text*/ L"",
                   /*expected_count*/ 3);
+  EXPECT_UIA_MOVE(text_range_provider, TextUnit_Format,
+                  /*count*/ 70,
+                  /*expected_text*/ L"",
+                  /*expected_count*/ 3);
 
   // Trying to move past the last format should have no effect.
   EXPECT_UIA_MOVE(text_range_provider, TextUnit_Format,
@@ -1351,7 +1383,7 @@
                   /*expected_text*/ L"Paragraph",
                   /*expected_count*/ 3);
   EXPECT_UIA_MOVE(text_range_provider, TextUnit_Word,
-                  /*count*/ 3,
+                  /*count*/ 6,
                   /*expected_text*/ L"2",
                   /*expected_count*/ 3);
 
@@ -1375,7 +1407,7 @@
                   /*expected_text*/ L"text",
                   /*expected_count*/ -2);
   EXPECT_UIA_MOVE(text_range_provider, TextUnit_Word,
-                  /*count*/ -3,
+                  /*count*/ -6,
                   /*expected_text*/ L"First",
                   /*expected_count*/ -3);
 
@@ -1387,15 +1419,19 @@
                   /*expected_count*/ 0);
 
   // Degenerate range moves.
-  EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(
-      text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Document,
-      /*count*/ -1,
-      /*expected_text*/ L"",
-      /*expected_count*/ -1);
+  EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(text_range_provider,
+                                   TextPatternRangeEndpoint_End, TextUnit_Word,
+                                   /*count*/ -1,
+                                   /*expected_text*/ L"",
+                                   /*expected_count*/ -1);
   EXPECT_UIA_MOVE(text_range_provider, TextUnit_Word,
                   /*count*/ 4,
                   /*expected_text*/ L"",
                   /*expected_count*/ 4);
+  EXPECT_UIA_MOVE(text_range_provider, TextUnit_Word,
+                  /*count*/ 70,
+                  /*expected_text*/ L"",
+                  /*expected_count*/ 8);
 
   // Trying to move past the last word should have no effect.
   EXPECT_UIA_MOVE(text_range_provider, TextUnit_Word,
@@ -1436,7 +1472,7 @@
                   /*expected_text*/ L"bold text",
                   /*expected_count*/ 1);
   EXPECT_UIA_MOVE(text_range_provider, TextUnit_Line,
-                  /*count*/ 2,
+                  /*count*/ 10,
                   /*expected_text*/ L"Paragraph 2",
                   /*expected_count*/ 2);
 
@@ -1452,7 +1488,7 @@
                   /*expected_text*/ L"Paragraph 1",
                   /*expected_count*/ -1);
   EXPECT_UIA_MOVE(text_range_provider, TextUnit_Line,
-                  /*count*/ -4,
+                  /*count*/ -5,
                   /*expected_text*/ L"First line of text",
                   /*expected_count*/ -4);
 
@@ -1464,15 +1500,19 @@
                   /*expected_count*/ 0);
 
   // Degenerate range moves.
-  EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(
-      text_range_provider, TextPatternRangeEndpoint_End, TextUnit_Document,
-      /*count*/ -1,
-      /*expected_text*/ L"",
-      /*expected_count*/ -1);
+  EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(text_range_provider,
+                                   TextPatternRangeEndpoint_End, TextUnit_Line,
+                                   /*count*/ -1,
+                                   /*expected_text*/ L"",
+                                   /*expected_count*/ -1);
   EXPECT_UIA_MOVE(text_range_provider, TextUnit_Line,
                   /*count*/ 4,
                   /*expected_text*/ L"",
                   /*expected_count*/ 4);
+  EXPECT_UIA_MOVE(text_range_provider, TextUnit_Line,
+                  /*count*/ 70,
+                  /*expected_text*/ L"",
+                  /*expected_count*/ 2);
 
   // Trying to move past the last line should have no effect.
   EXPECT_UIA_MOVE(text_range_provider, TextUnit_Line,
@@ -1551,21 +1591,21 @@
   EXPECT_UIA_MOVE(text_range_provider, TextUnit_Page,
                   /*count*/ 4,
                   /*expected_text*/ L"",
-                  /*expected_count*/ 0);
+                  /*expected_count*/ 1);
 
   // Trying to move past the last character should have no effect.
   EXPECT_UIA_MOVE(text_range_provider, TextUnit_Document,
                   /*count*/ 1,
                   /*expected_text*/ L"",
-                  /*expected_count*/ 1);
+                  /*expected_count*/ 0);
   EXPECT_UIA_MOVE(text_range_provider, TextUnit_Page,
                   /*count*/ -2,
                   /*expected_text*/ L"",
-                  /*expected_count*/ 0);
+                  /*expected_count*/ -1);
   EXPECT_UIA_MOVE(text_range_provider, TextUnit_Document,
                   /*count*/ -1,
                   /*expected_text*/ L"",
-                  /*expected_count*/ -1);
+                  /*expected_count*/ 0);
 
   AXNodePosition::SetTreeForTesting(nullptr);
 }
@@ -1839,18 +1879,18 @@
   EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(
       text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Word,
       /*count*/ 6,
-      /*expected_text*/ L"text",
-      /*expected_count*/ 4);
+      /*expected_text*/ L"",
+      /*expected_count*/ 5);
   EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(
       text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Word,
-      /*count*/ -7,
+      /*count*/ -8,
       /*expected_text*/ L"some textmore texteven more text",
-      /*expected_count*/ -6);
+      /*expected_count*/ -7);
   EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(text_range_provider,
                                    TextPatternRangeEndpoint_End, TextUnit_Word,
                                    /*count*/ -8,
-                                   /*expected_text*/ L"some",
-                                   /*expected_count*/ -6);
+                                   /*expected_text*/ L"",
+                                   /*expected_count*/ -7);
 }
 
 TEST_F(AXPlatformNodeTextRangeProviderTest,
@@ -1920,23 +1960,23 @@
   EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(text_range_provider,
                                    TextPatternRangeEndpoint_End, TextUnit_Line,
                                    /*count*/ -13,
-                                   /*expected_text*/ L"0",
-                                   /*expected_count*/ -5);
+                                   /*expected_text*/ L"",
+                                   /*expected_count*/ -6);
   EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(text_range_provider,
                                    TextPatternRangeEndpoint_End, TextUnit_Line,
                                    /*count*/ 11,
                                    /*expected_text*/ L"0123456",
-                                   /*expected_count*/ 6);
+                                   /*expected_count*/ 7);
   EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(
       text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Line,
       /*count*/ 9,
-      /*expected_text*/ L"6",
-      /*expected_count*/ 6);
+      /*expected_text*/ L"",
+      /*expected_count*/ 7);
   EXPECT_UIA_MOVE_ENDPOINT_BY_UNIT(
       text_range_provider, TextPatternRangeEndpoint_Start, TextUnit_Line,
       /*count*/ -7,
       /*expected_text*/ L"0123456",
-      /*expected_count*/ -6);
+      /*expected_count*/ -7);
 }
 
 // Verify that the endpoint can move past an empty text field.
@@ -2751,15 +2791,6 @@
   GetTextRangeProviderFromTextNode(list_item2_text_range_provider,
                                    list_item2_text_node);
 
-  base::win::ScopedVariant expected_mixed_variant;
-  {
-    VARIANT var;
-    V_VT(&var) = VT_UNKNOWN;
-    EXPECT_HRESULT_SUCCEEDED(
-        ::UiaGetReservedMixedAttributeValue(&V_UNKNOWN(&var)));
-    expected_mixed_variant.Reset(var);
-  }
-
   base::win::ScopedVariant expected_variant;
 
   // SkColor is ARGB, COLORREF is 0BGR
@@ -2822,8 +2853,8 @@
                               expected_variant);
   expected_variant.Reset();
 
-  EXPECT_UIA_TEXTATTRIBUTE_EQ(document_range_provider, UIA_IsHiddenAttributeId,
-                              expected_mixed_variant);
+  EXPECT_UIA_TEXTATTRIBUTE_MIXED(document_range_provider,
+                                 UIA_IsHiddenAttributeId);
 
   expected_variant.Set(true);
   EXPECT_UIA_TEXTATTRIBUTE_EQ(text_range_provider, UIA_IsItalicAttributeId,
@@ -2911,12 +2942,97 @@
       static_cast<int32_t>(FlowDirections::FlowDirections_RightToLeft));
   EXPECT_UIA_TEXTATTRIBUTE_EQ(
       text_range_provider, UIA_TextFlowDirectionsAttributeId, expected_variant);
-  EXPECT_UIA_TEXTATTRIBUTE_EQ(document_range_provider,
-                              UIA_TextFlowDirectionsAttributeId,
-                              expected_variant);
+  EXPECT_UIA_TEXTATTRIBUTE_MIXED(document_range_provider,
+                                 UIA_TextFlowDirectionsAttributeId);
   expected_variant.Reset();
 }
 
+TEST_F(AXPlatformNodeTextRangeProviderTest,
+       TestITextRangeProviderGetAttributeValueNotSupported) {
+  ui::AXNodeData root_data;
+  root_data.id = 0;
+  root_data.role = ax::mojom::Role::kRootWebArea;
+  root_data.child_ids = {1, 2};
+
+  ui::AXNodeData text_data_first;
+  text_data_first.id = 1;
+  text_data_first.role = ax::mojom::Role::kStaticText;
+  text_data_first.SetName("first");
+
+  ui::AXNodeData text_data_second;
+  text_data_second.id = 2;
+  text_data_second.role = ax::mojom::Role::kStaticText;
+  text_data_second.SetName("second");
+
+  ui::AXTreeUpdate update;
+  ui::AXTreeData tree_data;
+  tree_data.tree_id = ui::AXTreeID::CreateNewAXTreeID();
+  update.tree_data = tree_data;
+  update.has_tree_data = true;
+  update.root_id = root_data.id;
+  update.nodes.push_back(root_data);
+  update.nodes.push_back(text_data_first);
+  update.nodes.push_back(text_data_second);
+
+  Init(update);
+  AXNodePosition::SetTreeForTesting(tree_.get());
+  AXTreeManagerMap::GetInstance().AddTreeManager(tree_data.tree_id, this);
+
+  ComPtr<ITextRangeProvider> document_range_provider;
+  GetTextRangeProviderFromTextNode(document_range_provider, GetRootNode());
+
+  EXPECT_UIA_TEXTATTRIBUTE_NOTSUPPORTED(document_range_provider,
+                                        UIA_AfterParagraphSpacingAttributeId);
+  EXPECT_UIA_TEXTATTRIBUTE_NOTSUPPORTED(document_range_provider,
+                                        UIA_AnimationStyleAttributeId);
+  EXPECT_UIA_TEXTATTRIBUTE_NOTSUPPORTED(document_range_provider,
+                                        UIA_AnnotationObjectsAttributeId);
+  EXPECT_UIA_TEXTATTRIBUTE_NOTSUPPORTED(document_range_provider,
+                                        UIA_AnnotationTypesAttributeId);
+  EXPECT_UIA_TEXTATTRIBUTE_NOTSUPPORTED(document_range_provider,
+                                        UIA_BeforeParagraphSpacingAttributeId);
+  EXPECT_UIA_TEXTATTRIBUTE_NOTSUPPORTED(document_range_provider,
+                                        UIA_CapStyleAttributeId);
+  EXPECT_UIA_TEXTATTRIBUTE_NOTSUPPORTED(document_range_provider,
+                                        UIA_CaretBidiModeAttributeId);
+  EXPECT_UIA_TEXTATTRIBUTE_NOTSUPPORTED(document_range_provider,
+                                        UIA_CaretPositionAttributeId);
+  EXPECT_UIA_TEXTATTRIBUTE_NOTSUPPORTED(document_range_provider,
+                                        UIA_HorizontalTextAlignmentAttributeId);
+  EXPECT_UIA_TEXTATTRIBUTE_NOTSUPPORTED(document_range_provider,
+                                        UIA_IndentationFirstLineAttributeId);
+  EXPECT_UIA_TEXTATTRIBUTE_NOTSUPPORTED(document_range_provider,
+                                        UIA_IndentationLeadingAttributeId);
+  EXPECT_UIA_TEXTATTRIBUTE_NOTSUPPORTED(document_range_provider,
+                                        UIA_IndentationTrailingAttributeId);
+  EXPECT_UIA_TEXTATTRIBUTE_NOTSUPPORTED(document_range_provider,
+                                        UIA_IsActiveAttributeId);
+  EXPECT_UIA_TEXTATTRIBUTE_NOTSUPPORTED(document_range_provider,
+                                        UIA_LineSpacingAttributeId);
+  EXPECT_UIA_TEXTATTRIBUTE_NOTSUPPORTED(document_range_provider,
+                                        UIA_LinkAttributeId);
+  EXPECT_UIA_TEXTATTRIBUTE_NOTSUPPORTED(document_range_provider,
+                                        UIA_MarginBottomAttributeId);
+  EXPECT_UIA_TEXTATTRIBUTE_NOTSUPPORTED(document_range_provider,
+                                        UIA_MarginLeadingAttributeId);
+  EXPECT_UIA_TEXTATTRIBUTE_NOTSUPPORTED(document_range_provider,
+                                        UIA_MarginTopAttributeId);
+  EXPECT_UIA_TEXTATTRIBUTE_NOTSUPPORTED(document_range_provider,
+                                        UIA_MarginTrailingAttributeId);
+  EXPECT_UIA_TEXTATTRIBUTE_NOTSUPPORTED(document_range_provider,
+                                        UIA_OutlineStylesAttributeId);
+  EXPECT_UIA_TEXTATTRIBUTE_NOTSUPPORTED(document_range_provider,
+                                        UIA_OverlineColorAttributeId);
+  EXPECT_UIA_TEXTATTRIBUTE_NOTSUPPORTED(document_range_provider,
+                                        UIA_SelectionActiveEndAttributeId);
+  EXPECT_UIA_TEXTATTRIBUTE_NOTSUPPORTED(document_range_provider,
+                                        UIA_StrikethroughColorAttributeId);
+  EXPECT_UIA_TEXTATTRIBUTE_NOTSUPPORTED(document_range_provider,
+                                        UIA_TabsAttributeId);
+  EXPECT_UIA_TEXTATTRIBUTE_NOTSUPPORTED(document_range_provider,
+                                        UIA_UnderlineColorAttributeId);
+}
+
 TEST_F(AXPlatformNodeTextRangeProviderTest, TestITextRangeProviderSelect) {
   Init(BuildTextDocument({"some text", "more text2"}));
   AXNodePosition::SetTreeForTesting(tree_.get());
diff --git a/ui/accessibility/platform/ax_platform_node_win_unittest.cc b/ui/accessibility/platform/ax_platform_node_win_unittest.cc
index 42e099c9..16c9818 100644
--- a/ui/accessibility/platform/ax_platform_node_win_unittest.cc
+++ b/ui/accessibility/platform/ax_platform_node_win_unittest.cc
@@ -1135,23 +1135,32 @@
 
   // Logical directions should be supported.
   EXPECT_EQ(S_OK, ia_root->accNavigate(NAVDIR_FIRSTCHILD, SELF, end.AsInput()));
-  EXPECT_EQ(0, var_child1.Compare(end));
+  EXPECT_EQ(VT_DISPATCH, end.type());
+  EXPECT_EQ(V_DISPATCH(var_child1.ptr()), V_DISPATCH(end.ptr()));
+
   EXPECT_EQ(S_OK, ia_root->accNavigate(NAVDIR_LASTCHILD, SELF, end.AsInput()));
-  EXPECT_EQ(0, var_child2.Compare(end));
+  EXPECT_EQ(VT_DISPATCH, end.type());
+  EXPECT_EQ(V_DISPATCH(var_child2.ptr()), V_DISPATCH(end.ptr()));
 
   EXPECT_EQ(S_OK, ia_child1->accNavigate(NAVDIR_NEXT, SELF, end.AsInput()));
-  EXPECT_EQ(0, var_child2.Compare(end));
+  EXPECT_EQ(VT_DISPATCH, end.type());
+  EXPECT_EQ(V_DISPATCH(var_child2.ptr()), V_DISPATCH(end.ptr()));
+
   EXPECT_EQ(S_OK, ia_child2->accNavigate(NAVDIR_PREVIOUS, SELF, end.AsInput()));
-  EXPECT_EQ(0, var_child1.Compare(end));
+  EXPECT_EQ(VT_DISPATCH, end.type());
+  EXPECT_EQ(V_DISPATCH(var_child1.ptr()), V_DISPATCH(end.ptr()));
 
   // Child indices can also be passed by variant.
   // Indices are one-based.
   EXPECT_EQ(S_OK,
             ia_root->accNavigate(NAVDIR_NEXT, ScopedVariant(1), end.AsInput()));
-  EXPECT_EQ(0, var_child2.Compare(end));
+  EXPECT_EQ(VT_DISPATCH, end.type());
+  EXPECT_EQ(V_DISPATCH(var_child2.ptr()), V_DISPATCH(end.ptr()));
+
   EXPECT_EQ(S_OK, ia_root->accNavigate(NAVDIR_PREVIOUS, ScopedVariant(2),
                                        end.AsInput()));
-  EXPECT_EQ(0, var_child1.Compare(end));
+  EXPECT_EQ(VT_DISPATCH, end.type());
+  EXPECT_EQ(V_DISPATCH(var_child1.ptr()), V_DISPATCH(end.ptr()));
 
   // Test out-of-bounds.
   EXPECT_EQ(S_FALSE,
diff --git a/ui/aura/env.cc b/ui/aura/env.cc
index b090310d..d7b077d 100644
--- a/ui/aura/env.cc
+++ b/ui/aura/env.cc
@@ -9,7 +9,6 @@
 #include "base/memory/ptr_util.h"
 #include "base/observer_list_types.h"
 #include "components/viz/common/features.h"
-#include "services/ws/public/mojom/window_tree.mojom.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/env_input_state_controller.h"
 #include "ui/aura/env_observer.h"
diff --git a/ui/base/ime/chromeos/input_method_chromeos.cc b/ui/base/ime/chromeos/input_method_chromeos.cc
index c8daab1..7a9b966 100644
--- a/ui/base/ime/chromeos/input_method_chromeos.cc
+++ b/ui/base/ime/chromeos/input_method_chromeos.cc
@@ -151,6 +151,17 @@
   }
 }
 
+InputMethodChromeOS::PendingSetCompositionRange::PendingSetCompositionRange(
+    const gfx::Range& range,
+    const std::vector<ui::ImeTextSpan>& text_spans)
+    : range(range), text_spans(text_spans) {}
+
+InputMethodChromeOS::PendingSetCompositionRange::PendingSetCompositionRange(
+    const PendingSetCompositionRange& other) = default;
+
+InputMethodChromeOS::PendingSetCompositionRange::~PendingSetCompositionRange() =
+    default;
+
 void InputMethodChromeOS::DispatchKeyEventAsync(ui::KeyEvent* event,
                                                 AckCallback ack_callback) {
   ignore_result(DispatchKeyEventInternal(event, std::move(ack_callback)));
@@ -466,6 +477,34 @@
   OnCaretBoundsChanged(GetTextInputClient());
 }
 
+bool InputMethodChromeOS::SetCompositionRange(
+    uint32_t before,
+    uint32_t after,
+    const std::vector<ui::ImeTextSpan>& text_spans) {
+  if (IsTextInputTypeNone())
+    return false;
+
+  // The given range and spans are relative to the current selection.
+  gfx::Range range;
+  if (!GetTextInputClient()->GetEditableSelectionRange(&range))
+    return false;
+
+  const gfx::Range composition_range(range.start() - before,
+                                     range.end() + after);
+
+  // If we have pending key events, then delay the operation until
+  // |ProcessKeyEventPostIME|. Otherwise, process it immediately.
+  if (handling_key_event_) {
+    composition_changed_ = true;
+    pending_composition_range_ =
+        PendingSetCompositionRange{composition_range, text_spans};
+    return true;
+  } else {
+    return GetTextInputClient()->SetCompositionFromExistingText(
+        composition_range, text_spans);
+  }
+}
+
 void InputMethodChromeOS::ConfirmCompositionText() {
   TextInputClient* client = GetTextInputClient();
   if (client && client->HasCompositionText())
@@ -673,13 +712,22 @@
     }
   }
 
+  // TODO(https://crbug.com/952757): Refactor this code to be clearer and less
+  // error-prone.
   if (composition_changed_ && !IsTextInputTypeNone()) {
+    if (pending_composition_range_) {
+      client->SetCompositionFromExistingText(
+          pending_composition_range_->range,
+          pending_composition_range_->text_spans);
+    }
     if (composition_.text.length()) {
       composing_text_ = true;
       client->SetCompositionText(composition_);
-    } else if (result_text_.empty()) {
+    } else if (result_text_.empty() && !pending_composition_range_) {
       client->ClearCompositionText();
     }
+
+    pending_composition_range_.reset();
   }
 
   // We should not clear composition text here, as it may belong to the next
diff --git a/ui/base/ime/chromeos/input_method_chromeos.h b/ui/base/ime/chromeos/input_method_chromeos.h
index 41d809e..da5714f 100644
--- a/ui/base/ime/chromeos/input_method_chromeos.h
+++ b/ui/base/ime/chromeos/input_method_chromeos.h
@@ -56,6 +56,10 @@
                                  TextInputClient* focused) override;
   void OnDidChangeFocusedClient(TextInputClient* focused_before,
                                 TextInputClient* focused) override;
+  bool SetCompositionRange(
+      uint32_t before,
+      uint32_t after,
+      const std::vector<ui::ImeTextSpan>& text_spans) override;
 
  protected:
   // Converts |text| into CompositionText.
@@ -79,6 +83,17 @@
   class PendingKeyEvent;
   friend TestableInputMethodChromeOS;
 
+  // Representings a pending SetCompositionRange operation.
+  struct PendingSetCompositionRange {
+    PendingSetCompositionRange(const gfx::Range& range,
+                               const std::vector<ui::ImeTextSpan>& text_spans);
+    PendingSetCompositionRange(const PendingSetCompositionRange& other);
+    ~PendingSetCompositionRange();
+
+    gfx::Range range;
+    std::vector<ui::ImeTextSpan> text_spans;
+  };
+
   ui::EventDispatchDetails DispatchKeyEventInternal(ui::KeyEvent* event,
                                                     AckCallback ack_callback);
 
@@ -178,6 +193,9 @@
   // Indicates if the composition text is changed or deleted.
   bool composition_changed_;
 
+  // Indicates whether there is a pending SetCompositionRange operation.
+  base::Optional<PendingSetCompositionRange> pending_composition_range_;
+
   // An object to compose a character from a sequence of key presses
   // including dead key etc.
   CharacterComposer character_composer_;
diff --git a/ui/base/ime/ime_input_context_handler_interface.h b/ui/base/ime/ime_input_context_handler_interface.h
index 21c0f45c5..88abe7dd 100644
--- a/ui/base/ime/ime_input_context_handler_interface.h
+++ b/ui/base/ime/ime_input_context_handler_interface.h
@@ -25,6 +25,15 @@
   // Called when the engine commit a text.
   virtual void CommitText(const std::string& text) = 0;
 
+#if defined(OS_CHROMEOS)
+  // Called when the engine changes the composition range.
+  // Returns whether the operation was successful.
+  virtual bool SetCompositionRange(
+      uint32_t before,
+      uint32_t after,
+      const std::vector<ui::ImeTextSpan>& text_spans) = 0;
+#endif
+
   // Called when the engine updates composition text.
   virtual void UpdateCompositionText(const CompositionText& text,
                                      uint32_t cursor_pos,
diff --git a/ui/base/ime/input_method_base.cc b/ui/base/ime/input_method_base.cc
index b2203feb..b621ced 100644
--- a/ui/base/ime/input_method_base.cc
+++ b/ui/base/ime/input_method_base.cc
@@ -254,6 +254,15 @@
   SendFakeProcessKeyEvent(false);
 }
 
+#if defined(OS_CHROMEOS)
+bool InputMethodBase::SetCompositionRange(
+    uint32_t before,
+    uint32_t after,
+    const std::vector<ui::ImeTextSpan>& text_spans) {
+  return false;
+}
+#endif
+
 void InputMethodBase::DeleteSurroundingText(int32_t offset, uint32_t length) {}
 
 SurroundingTextInfo InputMethodBase::GetSurroundingTextInfo() {
diff --git a/ui/base/ime/input_method_base.h b/ui/base/ime/input_method_base.h
index be649f4..fb57c53 100644
--- a/ui/base/ime/input_method_base.h
+++ b/ui/base/ime/input_method_base.h
@@ -90,6 +90,14 @@
   void UpdateCompositionText(const CompositionText& text,
                              uint32_t cursor_pos,
                              bool visible) override;
+
+#if defined(OS_CHROMEOS)
+  bool SetCompositionRange(
+      uint32_t before,
+      uint32_t after,
+      const std::vector<ui::ImeTextSpan>& text_spans) override;
+#endif
+
   void DeleteSurroundingText(int32_t offset, uint32_t length) override;
   SurroundingTextInfo GetSurroundingTextInfo() override;
   void SendKeyEvent(KeyEvent* event) override;
diff --git a/ui/base/ime/mock_ime_input_context_handler.cc b/ui/base/ime/mock_ime_input_context_handler.cc
index e01c87e..fdb9a392 100644
--- a/ui/base/ime/mock_ime_input_context_handler.cc
+++ b/ui/base/ime/mock_ime_input_context_handler.cc
@@ -32,6 +32,15 @@
   last_update_composition_arg_.is_visible = visible;
 }
 
+#if defined(OS_CHROMEOS)
+bool MockIMEInputContextHandler::SetCompositionRange(
+    uint32_t before,
+    uint32_t after,
+    const std::vector<ui::ImeTextSpan>& text_spans) {
+  return false;
+}
+#endif
+
 void MockIMEInputContextHandler::DeleteSurroundingText(int32_t offset,
                                                        uint32_t length) {
   ++delete_surrounding_text_call_count_;
diff --git a/ui/base/ime/mock_ime_input_context_handler.h b/ui/base/ime/mock_ime_input_context_handler.h
index 2d0df39..9e66f32 100644
--- a/ui/base/ime/mock_ime_input_context_handler.h
+++ b/ui/base/ime/mock_ime_input_context_handler.h
@@ -36,6 +36,14 @@
   void UpdateCompositionText(const CompositionText& text,
                              uint32_t cursor_pos,
                              bool visible) override;
+
+#if defined(OS_CHROMEOS)
+  bool SetCompositionRange(
+      uint32_t before,
+      uint32_t after,
+      const std::vector<ui::ImeTextSpan>& text_spans) override;
+#endif
+
   void DeleteSurroundingText(int32_t offset, uint32_t length) override;
   SurroundingTextInfo GetSurroundingTextInfo() override;
   void SendKeyEvent(KeyEvent* event) override;
diff --git a/ui/file_manager/base/js/filtered_volume_manager.js b/ui/file_manager/base/js/filtered_volume_manager.js
index 92a0b0a..3e08cbb 100644
--- a/ui/file_manager/base/js/filtered_volume_manager.js
+++ b/ui/file_manager/base/js/filtered_volume_manager.js
@@ -83,6 +83,8 @@
     this.volumeInfoList = new FilteredVolumeInfoList(this.list_);
 
     this.volumeManager_ = null;
+
+    /** @private {?Array<function()>} */
     this.pendingTasks_ = [];
     this.onEventBound_ = this.onEvent_.bind(this);
     this.onVolumeInfoListUpdatedBound_ =
@@ -335,7 +337,7 @@
   /**
    * Obtains a volume information of the current profile.
    * @param {VolumeManagerCommon.VolumeType} volumeType Volume type.
-   * @return {VolumeInfo} Found volume info.
+   * @return {?VolumeInfo} Found volume info.
    */
   getCurrentProfileVolumeInfo(volumeType) {
     return this.filterDisallowedVolume_(
@@ -365,7 +367,7 @@
    * Obtains location information from an entry.
    *
    * @param {(!Entry|!FilesAppEntry)} entry File or directory entry.
-   * @return {EntryLocation} Location information.
+   * @return {?EntryLocation} Location information.
    */
   getLocationInfo(entry) {
     const locationInfo =
@@ -399,15 +401,15 @@
    * @return {!Promise<!VolumeInfo>} The VolumeInfo. Will not resolve
    *     if the volume is never mounted.
    */
-  whenVolumeInfoReady(volumeId) {
-    return new Promise(resolve => {
-      this.volumeManager_.whenVolumeInfoReady(volumeId).then((volumeInfo) => {
-        volumeInfo = this.filterDisallowedVolume_(volumeInfo);
-        if (volumeInfo) {
-          resolve(volumeInfo);
-        }
-      });
-    });
+  async whenVolumeInfoReady(volumeId) {
+    const volumeInfo = this.filterDisallowedVolume_(
+        await this.volumeManager_.whenVolumeInfoReady(volumeId));
+
+    if (!volumeInfo) {
+      throw new Error(`Volume not allowed: ${volumeId}`);
+    }
+
+    return volumeInfo;
   }
 
   /**
@@ -455,8 +457,7 @@
     if (this.pendingTasks_) {
       return new Promise((fulfill, reject) => {
         this.pendingTasks_.push(() => {
-          return this.volumeManager_.configure(volumeInfo)
-              .then(fulfill, reject);
+          this.volumeManager_.configure(volumeInfo).then(fulfill, reject);
         });
       });
     }
@@ -467,8 +468,8 @@
   /**
    * Filters volume info by isAllowedVolume_().
    *
-   * @param {VolumeInfo} volumeInfo Volume info.
-   * @return {VolumeInfo} Null if the volume is disallowed. Otherwise just
+   * @param {?VolumeInfo} volumeInfo Volume info.
+   * @return {?VolumeInfo} Null if the volume is disallowed. Otherwise just
    *     returns the volume.
    * @private
    */
diff --git a/ui/file_manager/file_manager/background/js/volume_manager_factory.js b/ui/file_manager/file_manager/background/js/volume_manager_factory.js
index de56fbe..57aa45e5 100644
--- a/ui/file_manager/file_manager/background/js/volume_manager_factory.js
+++ b/ui/file_manager/file_manager/background/js/volume_manager_factory.js
@@ -11,9 +11,9 @@
   let instance = null;
 
   /**
-   * @type {?Promise<!VolumeManager>}
+   * @type {?Promise<void>}
    */
-  let instancePromise = null;
+  let instanceInitialized = null;
 
   /**
    * Returns the VolumeManager instance asynchronously. If it has not been
@@ -22,13 +22,14 @@
    * @return {!Promise<!VolumeManager>} Promise to be fulfilled with the volume
    *     manager.
    */
-  function getInstance() {
-    if (!instancePromise) {
+  async function getInstance() {
+    if (!instance) {
       instance = new VolumeManagerImpl();
-      instancePromise =
-          new Promise(fulfill => instance.initialize(() => fulfill(instance)));
+      instanceInitialized =
+          new Promise(fulfill => instance.initialize(fulfill));
     }
-    return instancePromise;
+    await instanceInitialized;
+    return instance;
   }
 
   /**
@@ -45,7 +46,7 @@
    * Revokes the singleton instance for testing.
    */
   function revokeInstanceForTesting() {
-    instancePromise = null;
+    instanceInitialized = null;
     instance = null;
   }
 
diff --git a/ui/file_manager/file_manager/background/js/volume_manager_impl.js b/ui/file_manager/file_manager/background/js/volume_manager_impl.js
index 26ff613..f817e62 100644
--- a/ui/file_manager/file_manager/background/js/volume_manager_impl.js
+++ b/ui/file_manager/file_manager/background/js/volume_manager_impl.js
@@ -125,32 +125,30 @@
   initialize(callback) {
     chrome.fileManagerPrivate.onMountCompleted.addListener(
         this.onMountCompleted_.bind(this));
-    console.warn('Requesting volume list.');
+    console.warn('Getting volume list');
     chrome.fileManagerPrivate.getVolumeMetadataList(volumeMetadataList => {
-      console.warn(
-          'Volume list fetched with: ' + volumeMetadataList.length + ' items.');
+      console.warn(`There are ${volumeMetadataList.length} volumes`);
       // We must subscribe to the mount completed event in the callback of
       // getVolumeMetadataList. crbug.com/330061.
       // But volumes reported by onMountCompleted events must be added after the
       // volumes in the volumeMetadataList are mounted. crbug.com/135477.
-      this.mountQueue_.run(inCallback => {
-        // Create VolumeInfo for each volume.
-        Promise
-            .all(volumeMetadataList.map(volumeMetadata => {
-              console.warn('Initializing volume: ' + volumeMetadata.volumeId);
-              return this.addVolumeMetadata_(volumeMetadata)
-                  .then(volumeInfo => {
-                    console.warn('Initialized volume: ' + volumeInfo.volumeId);
-                  });
-            }))
-            .then(() => {
-              console.warn('Initialized all volumes.');
-              // Call the callback of the initialize function.
-              callback();
-              // Call the callback of AsyncQueue. Maybe it invokes callbacks
-              // registered by mountCompleted events.
-              inCallback();
-            });
+      this.mountQueue_.run(async (inCallback) => {
+        try {
+          // Create VolumeInfo for each volume.
+          await Promise.all(volumeMetadataList.map(async (volumeMetadata) => {
+            console.warn(`Initializing volume ${volumeMetadata.volumeId}`);
+            const volumeInfo = await this.addVolumeMetadata_(volumeMetadata);
+            console.warn(`Initialized volume ${volumeInfo.volumeId}`);
+          }));
+
+          console.warn('Initialized all volumes');
+        } finally {
+          // Call the callback of the initialize function.
+          callback();
+          // Call the callback of AsyncQueue. Maybe it invokes callbacks
+          // registered by mountCompleted events.
+          inCallback();
+        }
       });
     });
   }
diff --git a/ui/file_manager/file_manager/foreground/js/BUILD.gn b/ui/file_manager/file_manager/foreground/js/BUILD.gn
index 91fabdd..2614000 100644
--- a/ui/file_manager/file_manager/foreground/js/BUILD.gn
+++ b/ui/file_manager/file_manager/foreground/js/BUILD.gn
@@ -136,7 +136,6 @@
     ":folder_shortcuts_data_model",
     "../../common/js:util",
     "metadata:metadata_model",
-    "ui:error_dialog",
     "ui:files_alert_dialog",
     "ui:list_container",
     "//ui/file_manager/file_manager/foreground/js/ui:action_model_ui",
diff --git a/ui/file_manager/file_manager/foreground/js/actions_model_unittest.js b/ui/file_manager/file_manager/foreground/js/actions_model_unittest.js
index 870ca868a..c383577e 100644
--- a/ui/file_manager/file_manager/foreground/js/actions_model_unittest.js
+++ b/ui/file_manager/file_manager/foreground/js/actions_model_unittest.js
@@ -71,10 +71,6 @@
     this.alertDialog = /** @type {!FilesAlertDialog} */ ({
       showHtml: function() {},
     });
-
-    this.errorDialog = /** @type {!ErrorDialog} */ ({
-      showHtml: function() {},
-    });
   }
 }
 
diff --git a/ui/file_manager/file_manager/foreground/js/main_scripts.js b/ui/file_manager/file_manager/foreground/js/main_scripts.js
index 157303229..f2610ecd 100644
--- a/ui/file_manager/file_manager/foreground/js/main_scripts.js
+++ b/ui/file_manager/file_manager/foreground/js/main_scripts.js
@@ -158,7 +158,6 @@
 // <include src="ui/directory_tree.js">
 // <include src="ui/drag_selector.js">
 // <include src="ui/empty_folder.js">
-// <include src="ui/error_dialog.js">
 // <include src="ui/file_grid.js">
 // <include src="ui/file_manager_ui.js">
 // <include src="ui/file_metadata_formatter.js">
diff --git a/ui/file_manager/file_manager/foreground/js/ui/BUILD.gn b/ui/file_manager/file_manager/foreground/js/ui/BUILD.gn
index 6b21c8b..81296ea 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/BUILD.gn
+++ b/ui/file_manager/file_manager/foreground/js/ui/BUILD.gn
@@ -29,7 +29,6 @@
     ":directory_tree",
     ":drag_selector",
     ":empty_folder",
-    ":error_dialog",
     ":file_grid",
     ":file_list_selection_model",
     ":file_manager_dialog_base",
@@ -79,7 +78,6 @@
 
 js_library("action_model_ui") {
   deps = [
-    ":error_dialog",
     ":files_alert_dialog",
     ":list_container",
   ]
@@ -189,12 +187,6 @@
   ]
 }
 
-js_library("error_dialog") {
-  deps = [
-    "//ui/webui/resources/js/cr/ui:dialogs",
-  ]
-}
-
 js_library("file_grid") {
   deps = [
     ":drag_selector",
@@ -242,7 +234,6 @@
     ":dialog_footer",
     ":directory_tree",
     ":empty_folder",
-    ":error_dialog",
     ":file_grid",
     ":file_table",
     ":files_alert_dialog",
diff --git a/ui/file_manager/file_manager/foreground/js/ui/action_model_ui.js b/ui/file_manager/file_manager/foreground/js/ui/action_model_ui.js
index a3a7ec3..e6c3c8fa 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/action_model_ui.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/action_model_ui.js
@@ -7,8 +7,7 @@
   constructor() {
     /** @type {!FilesAlertDialog} */
     this.alertDialog;
-    /** @type {!ErrorDialog} */
-    this.errorDialog;
+
     /** @type {!ListContainer} */
     this.listContainer;
   }
diff --git a/ui/file_manager/file_manager/foreground/js/ui/default_task_dialog.js b/ui/file_manager/file_manager/foreground/js/ui/default_task_dialog.js
index 9e42d22..244bbf17 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/default_task_dialog.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/default_task_dialog.js
@@ -10,158 +10,158 @@
 cr.define('cr.filebrowser', () => {
   /**
    * Creates dialog in DOM tree.
-   *
-   * @param {HTMLElement} parentNode Node to be parent for this dialog.
-   * @constructor
-   * @extends {FileManagerDialogBase}
    */
-  function DefaultTaskDialog(parentNode) {
-    FileManagerDialogBase.call(this, parentNode);
+  class DefaultTaskDialog extends FileManagerDialogBase {
+    /**
+     * @param {HTMLElement} parentNode Node to be parent for this dialog.
+     */
+    constructor(parentNode) {
+      super(parentNode);
 
-    this.frame_.id = 'default-task-dialog';
+      this.frame_.id = 'default-task-dialog';
 
-    this.list_ = new cr.ui.List();
-    this.list_.id = 'default-tasks-list';
-    this.frame_.insertBefore(this.list_, this.text_.nextSibling);
+      this.list_ = new cr.ui.List();
+      this.list_.id = 'default-tasks-list';
+      this.frame_.insertBefore(this.list_, this.text_.nextSibling);
 
-    this.selectionModel_ = this.list_.selectionModel =
-        new cr.ui.ListSingleSelectionModel();
-    this.dataModel_ = this.list_.dataModel = new cr.ui.ArrayDataModel([]);
+      this.selectionModel_ = this.list_.selectionModel =
+          new cr.ui.ListSingleSelectionModel();
+      this.dataModel_ = this.list_.dataModel = new cr.ui.ArrayDataModel([]);
 
-    // List has max-height defined at css, so that list grows automatically,
-    // but doesn't exceed predefined size.
-    this.list_.autoExpands = true;
-    this.list_.activateItemAtIndex = this.activateItemAtIndex_.bind(this);
-    // Use 'click' instead of 'change' for keyboard users.
-    this.list_.addEventListener('click', this.onSelected_.bind(this));
-    this.list_.addEventListener('change', this.onListChange_.bind(this));
+      // List has max-height defined at css, so that list grows automatically,
+      // but doesn't exceed predefined size.
+      this.list_.autoExpands = true;
+      this.list_.activateItemAtIndex = this.activateItemAtIndex_.bind(this);
+      // Use 'click' instead of 'change' for keyboard users.
+      this.list_.addEventListener('click', this.onSelected_.bind(this));
+      this.list_.addEventListener('change', this.onListChange_.bind(this));
 
-    this.initialFocusElement_ = this.list_;
+      this.initialFocusElement_ = this.list_;
 
-    const self = this;
+      /** @private {?function(*)} */
+      this.onSelectedItemCallback_ = null;
 
-    // Binding stuff doesn't work with constructors, so we have to create
-    // closure here.
-    this.list_.itemConstructor = function(item) {
-      return self.renderItem(item);
-    };
-  }
+      const self = this;
 
-  DefaultTaskDialog.prototype = {__proto__: FileManagerDialogBase.prototype};
-
-  /**
-   * Renders item for list.
-   * @param {Object} item Item to render.
-   */
-  DefaultTaskDialog.prototype.renderItem = function(item) {
-    const result = this.document_.createElement('li');
-
-    const div = this.document_.createElement('div');
-    div.textContent = item.label;
-
-    if (item.iconType) {
-      div.setAttribute('file-type-icon', item.iconType);
-    } else if (item.iconUrl) {
-      div.style.backgroundImage = 'url(' + item.iconUrl + ')';
+      // Binding stuff doesn't work with constructors, so we have to create
+      // closure here.
+      this.list_.itemConstructor = function(item) {
+        return self.renderItem(item);
+      };
     }
 
-    if (item.class) {
-      div.classList.add(item.class);
+    /**
+     * Renders item for list.
+     * @param {Object} item Item to render.
+     */
+    renderItem(item) {
+      const result = this.document_.createElement('li');
+
+      const div = this.document_.createElement('div');
+      div.textContent = item.label;
+
+      if (item.iconType) {
+        div.setAttribute('file-type-icon', item.iconType);
+      } else if (item.iconUrl) {
+        div.style.backgroundImage = 'url(' + item.iconUrl + ')';
+      }
+
+      if (item.class) {
+        div.classList.add(item.class);
+      }
+
+      result.appendChild(div);
+      // A11y - make it focusable and readable.
+      result.setAttribute('tabindex', '-1');
+
+      cr.defineProperty(result, 'lead', cr.PropertyKind.BOOL_ATTR);
+      cr.defineProperty(result, 'selected', cr.PropertyKind.BOOL_ATTR);
+
+      return result;
     }
 
-    result.appendChild(div);
-    // A11y - make it focusable and readable.
-    result.setAttribute('tabindex', '-1');
+    /**
+     * Shows dialog.
+     *
+     * @param {string} title Title in dialog caption.
+     * @param {string} message Message in dialog caption.
+     * @param {Array<Object>} items Items to render in the list.
+     * @param {number} defaultIndex Item to select by default.
+     * @param {function(*)} onSelectedItem Callback which is called when an item
+     *     is selected.
+     */
+    showDefaultTaskDialog(title, message, items, defaultIndex, onSelectedItem) {
+      this.onSelectedItemCallback_ = onSelectedItem;
 
-    cr.defineProperty(result, 'lead', cr.PropertyKind.BOOL_ATTR);
-    cr.defineProperty(result, 'selected', cr.PropertyKind.BOOL_ATTR);
+      const show = super.showTitleAndTextDialog(title, message);
 
-    return result;
-  };
+      if (!show) {
+        console.error('DefaultTaskDialog can\'t be shown.');
+        return;
+      }
 
-  /**
-   * Shows dialog.
-   *
-   * @param {string} title Title in dialog caption.
-   * @param {string} message Message in dialog caption.
-   * @param {Array<Object>} items Items to render in the list.
-   * @param {number} defaultIndex Item to select by default.
-   * @param {function(Object)} onSelectedItem Callback which is called when an
-   *     item is selected.
-   */
-  DefaultTaskDialog.prototype.showDefaultTaskDialog = function(
-      title, message, items, defaultIndex, onSelectedItem) {
-    this.onSelectedItemCallback_ = onSelectedItem;
+      if (!message) {
+        this.text_.setAttribute('hidden', 'hidden');
+      } else {
+        this.text_.removeAttribute('hidden');
+      }
 
-    const show = FileManagerDialogBase.prototype.showTitleAndTextDialog.call(
-        this, title, message);
-
-    if (!show) {
-      console.error('DefaultTaskDialog can\'t be shown.');
-      return;
+      this.list_.startBatchUpdates();
+      this.dataModel_.splice(0, this.dataModel_.length);
+      for (let i = 0; i < items.length; i++) {
+        this.dataModel_.push(items[i]);
+      }
+      this.selectionModel_.selectedIndex = defaultIndex;
+      this.list_.endBatchUpdates();
     }
 
-    if (!message) {
-      this.text_.setAttribute('hidden', 'hidden');
-    } else {
-      this.text_.removeAttribute('hidden');
-    }
-
-    this.list_.startBatchUpdates();
-    this.dataModel_.splice(0, this.dataModel_.length);
-    for (let i = 0; i < items.length; i++) {
-      this.dataModel_.push(items[i]);
-    }
-    this.selectionModel_.selectedIndex = defaultIndex;
-    this.list_.endBatchUpdates();
-  };
-
-  /**
-   * List activation handler. Closes dialog and calls 'ok' callback.
-   * @param {number} index Activated index.
-   */
-  DefaultTaskDialog.prototype.activateItemAtIndex_ = function(index) {
-    this.hide();
-    this.onSelectedItemCallback_(this.dataModel_.item(index));
-  };
-
-  /**
-   * Closes dialog and invokes callback with currently-selected item.
-   */
-  DefaultTaskDialog.prototype.onSelected_ = function() {
-    if (this.selectionModel_.selectedIndex !== -1) {
-      this.activateItemAtIndex_(this.selectionModel_.selectedIndex);
-    }
-  };
-
-  /**
-   * Called when cr.ui.List triggers a change event, which means user
-   * focused a new item on the list. Used here to isue .focus() on
-   * currently active item so ChromeVox can read it out.
-   * @param {!Event} event triggered by cr.ui.List.
-   */
-  DefaultTaskDialog.prototype.onListChange_ = event => {
-    const list = /** @type {cr.ui.List} */ (event.target);
-    const activeItem =
-        list.getListItemByIndex(list.selectionModel_.selectedIndex);
-    if (activeItem) {
-      activeItem.focus();
-    }
-  };
-
-  /**
-   * @override
-   */
-  DefaultTaskDialog.prototype.onContainerKeyDown_ = function(event) {
-    // Handle Escape.
-    if (event.keyCode == 27) {
+    /**
+     * List activation handler. Closes dialog and calls 'ok' callback.
+     * @param {number} index Activated index.
+     */
+    activateItemAtIndex_(index) {
       this.hide();
-      event.preventDefault();
-    } else if (event.keyCode == 32 || event.keyCode == 13) {
-      this.onSelected_();
-      event.preventDefault();
+      this.onSelectedItemCallback_(this.dataModel_.item(index));
     }
-  };
+
+    /**
+     * Closes dialog and invokes callback with currently-selected item.
+     */
+    onSelected_() {
+      if (this.selectionModel_.selectedIndex !== -1) {
+        this.activateItemAtIndex_(this.selectionModel_.selectedIndex);
+      }
+    }
+
+    /**
+     * Called when cr.ui.List triggers a change event, which means user
+     * focused a new item on the list. Used here to issue .focus() on
+     * currently active item so ChromeVox can read it out.
+     * @param {!Event} event triggered by cr.ui.List.
+     */
+    onListChange_(event) {
+      const list = /** @type {cr.ui.List} */ (event.target);
+      const activeItem =
+          list.getListItemByIndex(list.selectionModel_.selectedIndex);
+      if (activeItem) {
+        activeItem.focus();
+      }
+    }
+
+    /**
+     * @override
+     */
+    onContainerKeyDown_(event) {
+      // Handle Escape.
+      if (event.keyCode == 27) {
+        this.hide();
+        event.preventDefault();
+      } else if (event.keyCode == 32 || event.keyCode == 13) {
+        this.onSelected_();
+        event.preventDefault();
+      }
+    }
+  }
 
   return {DefaultTaskDialog: DefaultTaskDialog};
 });
diff --git a/ui/file_manager/file_manager/foreground/js/ui/error_dialog.js b/ui/file_manager/file_manager/foreground/js/ui/error_dialog.js
deleted file mode 100644
index e53685d4..0000000
--- a/ui/file_manager/file_manager/foreground/js/ui/error_dialog.js
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2013 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.
-
-/**
- * @param {HTMLElement} parentNode Node to be parent for this dialog.
- * @constructor
- * @extends {cr.ui.dialogs.BaseDialog}
- */
-function ErrorDialog(parentNode) {
-  cr.ui.dialogs.BaseDialog.call(this, parentNode);
-}
-
-ErrorDialog.prototype = {
-  __proto__: cr.ui.dialogs.BaseDialog.prototype
-};
-
-/**
- * One-time initialization of DOM.
- * @protected
- */
-ErrorDialog.prototype.initDom_ = function() {
-  cr.ui.dialogs.BaseDialog.prototype.initDom_.call(this);
-  this.frame_.classList.add('error-dialog-frame');
-  const img = this.document_.createElement('div');
-  img.className = 'error-dialog-img';
-  this.frame_.insertBefore(img, this.text_);
-};
diff --git a/ui/file_manager/file_manager/foreground/js/ui/file_manager_dialog_base.js b/ui/file_manager/file_manager/foreground/js/ui/file_manager_dialog_base.js
index 8a97087..b3953fd 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/file_manager_dialog_base.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/file_manager_dialog_base.js
@@ -4,18 +4,94 @@
 
 /**
  * This class is an extended class, to manage the status of the dialogs.
- *
- * @param {HTMLElement} parentNode Parent node of the dialog.
- * @extends {cr.ui.dialogs.BaseDialog}
- * @constructor
  */
-const FileManagerDialogBase = function(parentNode) {
-  cr.ui.dialogs.BaseDialog.call(this, parentNode);
-};
+class FileManagerDialogBase extends cr.ui.dialogs.BaseDialog {
+  /**
+   * @param {HTMLElement} parentNode Parent node of the dialog.
+   */
+  constructor(parentNode) {
+    super(parentNode);
+  }
 
-FileManagerDialogBase.prototype = {
-  __proto__: cr.ui.dialogs.BaseDialog.prototype
-};
+  /**
+   * @param {string} title Title.
+   * @param {string} message Message.
+   * @param {?function()} onOk Called when the OK button is pressed.
+   * @param {?function()} onCancel Called when the cancel button is pressed.
+   * @return {boolean} True if the dialog can show successfully. False if the
+   *     dialog failed to show due to an existing dialog.
+   */
+  showOkCancelDialog(title, message, onOk, onCancel) {
+    return this.showImpl_(title, message, onOk, onCancel);
+  }
+
+  /**
+   * @param {string} title Title.
+   * @param {string} message Message.
+   * @param {?function()} onOk Called when the OK button is pressed.
+   * @param {?function()} onCancel Called when the cancel button is pressed.
+   * @return {boolean} True if the dialog can show successfully. False if the
+   *     dialog failed to show due to an existing dialog.
+   * @private
+   */
+  showImpl_(title, message, onOk, onCancel) {
+    if (FileManagerDialogBase.shown) {
+      return false;
+    }
+
+    FileManagerDialogBase.shown = true;
+
+    // If a dialog is shown, activate the window.
+    const appWindow = chrome.app.window.current();
+    if (appWindow) {
+      appWindow.focus();
+    }
+
+    super.showWithTitle(title, message, onOk, onCancel, null);
+
+    return true;
+  }
+
+  /**
+   * @return {boolean} True if the dialog can show successfully. False if the
+   *     dialog failed to show due to an existing dialog.
+   */
+  showBlankDialog() {
+    return this.showImpl_('', '', null, null);
+  }
+
+  /**
+   * @param {string} title Title.
+   * @return {boolean} True if the dialog can show successfully. False if the
+   *     dialog failed to show due to an existing dialog.
+   */
+  showTitleOnlyDialog(title) {
+    return this.showImpl_(title, '', null, null);
+  }
+
+  /**
+   * @param {string} title Title.
+   * @param {string} text Text to be shown in the dialog.
+   * @return {boolean} True if the dialog can show successfully. False if the
+   *     dialog failed to show due to an existing dialog.
+   */
+  showTitleAndTextDialog(title, text) {
+    this.buttons.style.display = 'none';
+    return this.showImpl_(title, text, null, null);
+  }
+
+  /**
+   * @param {Function=} opt_onHide Called when the dialog is hidden.
+   */
+  hide(opt_onHide) {
+    super.hide(() => {
+      if (opt_onHide) {
+        opt_onHide();
+      }
+      FileManagerDialogBase.shown = false;
+    });
+  }
+}
 
 /**
  * The flag if any dialog is shown. True if a dialog is visible, false
@@ -23,85 +99,3 @@
  * @type {boolean}
  */
 FileManagerDialogBase.shown = false;
-
-/**
- * @param {string} title Title.
- * @param {string} message Message.
- * @param {?function()} onOk Called when the OK button is pressed.
- * @param {?function()} onCancel Called when the cancel button is pressed.
- * @return {boolean} True if the dialog can show successfully. False if the
- *     dialog failed to show due to an existing dialog.
- */
-FileManagerDialogBase.prototype.showOkCancelDialog = function(
-    title, message, onOk, onCancel) {
-  return this.showImpl_(title, message, onOk, onCancel);
-};
-
-/**
- * @param {string} title Title.
- * @param {string} message Message.
- * @param {?function()} onOk Called when the OK button is pressed.
- * @param {?function()} onCancel Called when the cancel button is pressed.
- * @return {boolean} True if the dialog can show successfully. False if the
- *     dialog failed to show due to an existing dialog.
- * @private
- */
-FileManagerDialogBase.prototype.showImpl_ = function(
-    title, message, onOk, onCancel) {
-  if (FileManagerDialogBase.shown) {
-    return false;
-  }
-
-  FileManagerDialogBase.shown = true;
-
-  // If a dialog is shown, activate the window.
-  const appWindow = chrome.app.window.current();
-  if (appWindow) {
-    appWindow.focus();
-  }
-
-  cr.ui.dialogs.BaseDialog.prototype.showWithTitle.call(
-      this, title, message, onOk, onCancel, null);
-
-  return true;
-};
-
-/**
- * @return {boolean} True if the dialog can show successfully. False if the
- *     dialog failed to show due to an existing dialog.
- */
-FileManagerDialogBase.prototype.showBlankDialog = function() {
-  return this.showImpl_('', '', null, null);
-};
-
-/**
- * @param {string} title Title.
- * @return {boolean} True if the dialog can show successfully. False if the
- *     dialog failed to show due to an existing dialog.
- */
-FileManagerDialogBase.prototype.showTitleOnlyDialog = function(title) {
-  return this.showImpl_(title, '', null, null);
-};
-
-/**
- * @param {string} title Title.
- * @param {string} text Text to be shown in the dialog.
- * @return {boolean} True if the dialog can show successfully. False if the
- *     dialog failed to show due to an existing dialog.
- */
-FileManagerDialogBase.prototype.showTitleAndTextDialog = function(title, text) {
-  this.buttons.style.display = 'none';
-  return this.showImpl_(title, text, null, null);
-};
-
-/**
- * @param {Function=} opt_onHide Called when the dialog is hidden.
- */
-FileManagerDialogBase.prototype.hide = function(opt_onHide) {
-  cr.ui.dialogs.BaseDialog.prototype.hide.call(this, () => {
-    if (opt_onHide) {
-      opt_onHide();
-    }
-    FileManagerDialogBase.shown = false;
-  });
-};
diff --git a/ui/file_manager/file_manager/foreground/js/ui/file_manager_ui.js b/ui/file_manager/file_manager/foreground/js/ui/file_manager_ui.js
index fc72478..218a9b8 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/file_manager_ui.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/file_manager_ui.js
@@ -42,13 +42,6 @@
     this.separators_ = [].slice.call(document.querySelectorAll('cr-menu > hr'));
 
     /**
-     * Error dialog.
-     * @type {!ErrorDialog}
-     * @const
-     */
-    this.errorDialog = new ErrorDialog(this.element);
-
-    /**
      * Alert dialog.
      * @type {!FilesAlertDialog}
      * @const
diff --git a/ui/file_manager/file_manager/foreground/js/ui/file_table.js b/ui/file_manager/file_manager/foreground/js/ui/file_table.js
index dd65a40..0e627c1 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/file_table.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/file_table.js
@@ -4,13 +4,234 @@
 
 /**
  * Custom column model for advanced auto-resizing.
- *
- * @param {!Array<cr.ui.table.TableColumn>} tableColumns Table columns.
- * @extends {cr.ui.table.TableColumnModel}
- * @constructor
  */
-function FileTableColumnModel(tableColumns) {
-  cr.ui.table.TableColumnModel.call(this, tableColumns);
+class FileTableColumnModel extends cr.ui.table.TableColumnModel {
+  /**
+   * @param {!Array<cr.ui.table.TableColumn>} tableColumns Table columns.
+   */
+  constructor(tableColumns) {
+    super(tableColumns);
+
+    /** @private {?FileTableColumnModel.ColumnSnapshot} */
+    this.snapshot_ = null;
+  }
+
+  /**
+   * Sets column width so that the column dividers move to the specified
+   * position. This function also check the width of each column and keep the
+   * width larger than MIN_WIDTH_.
+   *
+   * @private
+   * @param {Array<number>} newPos Positions of each column dividers.
+   */
+  applyColumnPositions_(newPos) {
+    // Check the minimum width and adjust the positions.
+    for (let i = 0; i < newPos.length - 2; i++) {
+      if (!this.columns_[i].visible) {
+        newPos[i + 1] = newPos[i];
+      } else if (newPos[i + 1] - newPos[i] < FileTableColumnModel.MIN_WIDTH_) {
+        newPos[i + 1] = newPos[i] + FileTableColumnModel.MIN_WIDTH_;
+      }
+    }
+    for (let i = newPos.length - 1; i >= 2; i--) {
+      if (!this.columns_[i - 1].visible) {
+        newPos[i - 1] = newPos[i];
+      } else if (newPos[i] - newPos[i - 1] < FileTableColumnModel.MIN_WIDTH_) {
+        newPos[i - 1] = newPos[i] - FileTableColumnModel.MIN_WIDTH_;
+      }
+    }
+    // Set the new width of columns
+    for (let i = 0; i < this.columns_.length; i++) {
+      if (!this.columns_[i].visible) {
+        this.columns_[i].width = 0;
+      } else {
+        // Make sure each cell has the minimum width. This is necessary when the
+        // window size is too small to contain all the columns.
+        this.columns_[i].width = Math.max(
+            FileTableColumnModel.MIN_WIDTH_, newPos[i + 1] - newPos[i]);
+      }
+    }
+  }
+
+  /**
+   * Normalizes widths to make their sum 100% if possible. Uses the proportional
+   * approach with some additional constraints.
+   *
+   * @param {number} contentWidth Target width.
+   * @override
+   */
+  normalizeWidths(contentWidth) {
+    let totalWidth = 0;
+    // Some columns have fixed width.
+    for (let i = 0; i < this.columns_.length; i++) {
+      totalWidth += this.columns_[i].width;
+    }
+    const positions = [0];
+    let sum = 0;
+    for (let i = 0; i < this.columns_.length; i++) {
+      const column = this.columns_[i];
+      sum += column.width;
+      // Faster alternative to Math.floor for non-negative numbers.
+      positions[i + 1] = ~~(contentWidth * sum / totalWidth);
+    }
+    this.applyColumnPositions_(positions);
+  }
+
+  /**
+   * Handles to the start of column resizing by splitters.
+   */
+  handleSplitterDragStart() {
+    this.initializeColumnPos();
+  }
+
+  /**
+   * Handles to the end of column resizing by splitters.
+   */
+  handleSplitterDragEnd() {
+    this.destroyColumnPos();
+  }
+
+  /**
+   * Initialize a column snapshot which is used in setWidthAndKeepTotal().
+   */
+  initializeColumnPos() {
+    this.snapshot_ = new FileTableColumnModel.ColumnSnapshot(this.columns_);
+  }
+
+  /**
+   * Destroy the column snapshot which is used in setWidthAndKeepTotal().
+   */
+  destroyColumnPos() {
+    this.snapshot_ = null;
+  }
+
+  /**
+   * Sets the width of column while keeping the total width of table.
+   * Before and after calling this method, you must initialize and destroy
+   * columnPos with initializeColumnPos() and destroyColumnPos().
+   * @param {number} columnIndex Index of column that is resized.
+   * @param {number} columnWidth New width of the column.
+   */
+  setWidthAndKeepTotal(columnIndex, columnWidth) {
+    columnWidth = Math.max(columnWidth, FileTableColumnModel.MIN_WIDTH_);
+    this.snapshot_.setWidth(columnIndex, columnWidth);
+    this.applyColumnPositions_(this.snapshot_.newPos);
+
+    // Notify about resizing
+    cr.dispatchSimpleEvent(this, 'resize');
+  }
+
+  /**
+   * Obtains a column by the specified horizontal position.
+   * @param {number} x Horizontal position.
+   * @return {Object} The object that contains column index, column width, and
+   *     hitPosition where the horizontal position is hit in the column.
+   */
+  getHitColumn(x) {
+    for (var i = 0; x >= this.columns_[i].width; i++) {
+      x -= this.columns_[i].width;
+    }
+    if (i >= this.columns_.length) {
+      return null;
+    }
+    return {index: i, hitPosition: x, width: this.columns_[i].width};
+  }
+
+  /** @override */
+  setVisible(index, visible) {
+    if (index < 0 || index > this.columns_.length - 1) {
+      return;
+    }
+
+    const column = this.columns_[index];
+    if (column.visible === visible) {
+      return;
+    }
+
+    // Re-layout the table.  This overrides the default column layout code in
+    // the parent class.
+    const snapshot = new FileTableColumnModel.ColumnSnapshot(this.columns_);
+
+    column.visible = visible;
+
+    // Keep the current column width, but adjust the other columns to
+    // accommodate the new column.
+    snapshot.setWidth(index, column.width);
+    this.applyColumnPositions_(snapshot.newPos);
+  }
+
+  /**
+   * Export a set of column widths for use by #restoreColumnWidths.  Use these
+   * two methods instead of manually saving and setting column widths, because
+   * doing the latter will not correctly save/restore column widths for hidden
+   * columns.
+   * @see #restoreColumnWidths
+   * @return {!Object} config
+   */
+  exportColumnConfig() {
+    // Make a snapshot, and use that to compute a column layout where all the
+    // columns are visible.
+    const snapshot = new FileTableColumnModel.ColumnSnapshot(this.columns_);
+    for (let i = 0; i < this.columns_.length; i++) {
+      if (!this.columns_[i].visible) {
+        snapshot.setWidth(i, this.columns_[i].absoluteWidth);
+      }
+    }
+    // Export the column widths.
+    const config = {};
+    for (let i = 0; i < this.columns_.length; i++) {
+      config[this.columns_[i].id] = {
+        width: snapshot.newPos[i + 1] - snapshot.newPos[i]
+      };
+    }
+    return config;
+  }
+
+  /**
+   * Restores a set of column widths previously created by calling
+   * #exportColumnConfig.
+   * @see #exportColumnConfig
+   * @param {!Object} config
+   */
+  restoreColumnConfig(config) {
+    // Convert old-style raw column widths into new-style config objects.
+    if (Array.isArray(config)) {
+      const tmpConfig = {};
+      tmpConfig[this.columns_[0].id] = config[0];
+      tmpConfig[this.columns_[1].id] = config[1];
+      tmpConfig[this.columns_[3].id] = config[2];
+      tmpConfig[this.columns_[4].id] = config[3];
+      config = tmpConfig;
+    }
+
+    // Columns must all be made visible before restoring their widths.  Save the
+    // current visibility so it can be restored after.
+    const visibility = [];
+    for (let i = 0; i < this.columns_.length; i++) {
+      visibility[i] = this.columns_[i].visible;
+      this.columns_[i].visible = true;
+    }
+
+    // Do not use external setters (e.g. #setVisible, #setWidth) here because
+    // they trigger layout thrash, and also try to dynamically resize columns,
+    // which interferes with restoring the old column layout.
+    for (const columnId in config) {
+      const column = this.columns_[this.indexOf(columnId)];
+      if (column) {
+        // Set column width.  Ignore invalid widths.
+        const width = ~~config[columnId].width;
+        if (width > 0) {
+          column.width = width;
+        }
+      }
+    }
+
+    // Restore column visibility.  Use setVisible here, to trigger table
+    // relayout.
+    for (let i = 0; i < this.columns_.length; i++) {
+      this.setVisible(i, visibility[i]);
+    }
+  }
 }
 
 /**
@@ -40,12 +261,6 @@
 }
 
 /**
- * Inherits from cr.ui.TableColumnModel.
- */
-FileTableColumnModel.prototype.__proto__ =
-    cr.ui.table.TableColumnModel.prototype;
-
-/**
  * Minimum width of column. Note that is not marked private as it is used in the
  * unit tests.
  * @const {number}
@@ -53,273 +268,59 @@
 FileTableColumnModel.MIN_WIDTH_ = 10;
 
 /**
- * Sets column width so that the column dividers move to the specified position.
- * This function also check the width of each column and keep the width larger
- * than MIN_WIDTH_.
- *
- * @private
- * @param {Array<number>} newPos Positions of each column dividers.
- */
-FileTableColumnModel.prototype.applyColumnPositions_ = function(newPos) {
-  // Check the minimum width and adjust the positions.
-  for (let i = 0; i < newPos.length - 2; i++) {
-    if (!this.columns_[i].visible) {
-      newPos[i + 1] = newPos[i];
-    } else if (newPos[i + 1] - newPos[i] < FileTableColumnModel.MIN_WIDTH_) {
-      newPos[i + 1] = newPos[i] + FileTableColumnModel.MIN_WIDTH_;
-    }
-  }
-  for (let i = newPos.length - 1; i >= 2; i--) {
-    if (!this.columns_[i - 1].visible) {
-      newPos[i - 1] = newPos[i];
-    } else if (newPos[i] - newPos[i - 1] < FileTableColumnModel.MIN_WIDTH_) {
-      newPos[i - 1] = newPos[i] - FileTableColumnModel.MIN_WIDTH_;
-    }
-  }
-  // Set the new width of columns
-  for (let i = 0; i < this.columns_.length; i++) {
-    if (!this.columns_[i].visible) {
-      this.columns_[i].width = 0;
-    } else {
-      // Make sure each cell has the minumum width. This is necessary when the
-      // window size is too small to contain all the columns.
-      this.columns_[i].width =
-          Math.max(FileTableColumnModel.MIN_WIDTH_, newPos[i + 1] - newPos[i]);
-    }
-  }
-};
-
-/**
- * Normalizes widths to make their sum 100% if possible. Uses the proportional
- * approach with some additional constraints.
- *
- * @param {number} contentWidth Target width.
- * @override
- */
-FileTableColumnModel.prototype.normalizeWidths = function(contentWidth) {
-  let totalWidth = 0;
-  // Some columns have fixed width.
-  for (let i = 0; i < this.columns_.length; i++) {
-    totalWidth += this.columns_[i].width;
-  }
-  const positions = [0];
-  let sum = 0;
-  for (let i = 0; i < this.columns_.length; i++) {
-    const column = this.columns_[i];
-    sum += column.width;
-    // Faster alternative to Math.floor for non-negative numbers.
-    positions[i + 1] = ~~(contentWidth * sum / totalWidth);
-  }
-  this.applyColumnPositions_(positions);
-};
-
-/**
- * Handles to the start of column resizing by splitters.
- */
-FileTableColumnModel.prototype.handleSplitterDragStart = function() {
-  this.initializeColumnPos();
-};
-
-/**
- * Handles to the end of column resizing by splitters.
- */
-FileTableColumnModel.prototype.handleSplitterDragEnd = function() {
-  this.destroyColumnPos();
-};
-
-/**
- * Initialize a column snapshot which is used in setWidthAndKeepTotal().
- */
-FileTableColumnModel.prototype.initializeColumnPos = function() {
-  this.snapshot_ = new FileTableColumnModel.ColumnSnapshot(this.columns_);
-};
-
-/**
- * Destroy the column snapshot which is used in setWidthAndKeepTotal().
- */
-FileTableColumnModel.prototype.destroyColumnPos = function() {
-  this.snapshot_ = null;
-};
-
-/**
- * Sets the width of column while keeping the total width of table.
- * Before and after calling this method, you must initialize and destroy
- * columnPos with initializeColumnPos() and destroyColumnPos().
- * @param {number} columnIndex Index of column that is resized.
- * @param {number} columnWidth New width of the column.
- */
-FileTableColumnModel.prototype.setWidthAndKeepTotal = function(
-    columnIndex, columnWidth) {
-  columnWidth = Math.max(columnWidth, FileTableColumnModel.MIN_WIDTH_);
-  this.snapshot_.setWidth(columnIndex, columnWidth);
-  this.applyColumnPositions_(this.snapshot_.newPos);
-
-  // Notify about resizing
-  cr.dispatchSimpleEvent(this, 'resize');
-};
-
-/**
- * Obtains a column by the specified horizontal position.
- * @param {number} x Horizontal position.
- * @return {Object} The object that contains column index, column width, and
- *     hitPosition where the horizontal position is hit in the column.
- */
-FileTableColumnModel.prototype.getHitColumn = function(x) {
-  for (var i = 0; x >= this.columns_[i].width; i++) {
-    x -= this.columns_[i].width;
-  }
-  if (i >= this.columns_.length) {
-    return null;
-  }
-  return {index: i, hitPosition: x, width: this.columns_[i].width};
-};
-
-/** @override */
-FileTableColumnModel.prototype.setVisible = function(index, visible) {
-  if (index < 0 || index > this.columns_.length - 1) {
-    return;
-  }
-
-  const column = this.columns_[index];
-  if (column.visible === visible) {
-    return;
-  }
-
-  // Re-layout the table.  This overrides the default column layout code in the
-  // parent class.
-  const snapshot = new FileTableColumnModel.ColumnSnapshot(this.columns_);
-
-  column.visible = visible;
-
-  // Keep the current column width, but adjust the other columns to accomodate
-  // the new column.
-  snapshot.setWidth(index, column.width);
-  this.applyColumnPositions_(snapshot.newPos);
-};
-
-/**
- * Export a set of column widths for use by #restoreColumnWidths.  Use these two
- * methods instead of manually saving and setting column widths, because doing
- * the latter will not correctly save/restore column widths for hidden columns.
- * @see #restoreColumnWidths
- * @return {!Object} config
- */
-FileTableColumnModel.prototype.exportColumnConfig = function() {
-  // Make a snapshot, and use that to compute a column layout where all the
-  // columns are visible.
-  const snapshot = new FileTableColumnModel.ColumnSnapshot(this.columns_);
-  for (let i = 0; i < this.columns_.length; i++) {
-    if (!this.columns_[i].visible) {
-      snapshot.setWidth(i, this.columns_[i].absoluteWidth);
-    }
-  }
-  // Export the column widths.
-  const config = {};
-  for (let i = 0; i < this.columns_.length; i++) {
-    config[this.columns_[i].id] = {
-      width: snapshot.newPos[i + 1] - snapshot.newPos[i]
-    };
-  }
-  return config;
-};
-
-/**
- * Restores a set of column widths previously created by calling
- * #exportColumnConfig.
- * @see #exportColumnConfig
- * @param {!Object} config
- */
-FileTableColumnModel.prototype.restoreColumnConfig = function(config) {
-  // Convert old-style raw column widths into new-style config objects.
-  if (Array.isArray(config)) {
-    const tmpConfig = {};
-    tmpConfig[this.columns_[0].id] = config[0];
-    tmpConfig[this.columns_[1].id] = config[1];
-    tmpConfig[this.columns_[3].id] = config[2];
-    tmpConfig[this.columns_[4].id] = config[3];
-    config = tmpConfig;
-  }
-
-  // Columns must all be made visible before restoring their widths.  Save the
-  // current visibility so it can be restored after.
-  const visibility = [];
-  for (let i = 0; i < this.columns_.length; i++) {
-    visibility[i] = this.columns_[i].visible;
-    this.columns_[i].visible = true;
-  }
-
-  // Do not use external setters (e.g. #setVisible, #setWidth) here because they
-  // trigger layout thrash, and also try to dynamically resize columns, which
-  // interferes with restoring the old column layout.
-  for (const columnId in config) {
-    const column = this.columns_[this.indexOf(columnId)];
-    if (column) {
-      // Set column width.  Ignore invalid widths.
-      const width = ~~config[columnId].width;
-      if (width > 0) {
-        column.width = width;
-      }
-    }
-  }
-
-  // Restore column visibility.  Use setVisible here, to trigger table relayout.
-  for (let i = 0; i < this.columns_.length; i++) {
-    this.setVisible(i, visibility[i]);
-  }
-};
-
-/**
  * A helper class for performing resizing of columns.
- * @param {!Array<!cr.ui.table.TableColumn>} columns
- * @constructor
  */
-FileTableColumnModel.ColumnSnapshot = function(columns) {
-  /** @private {!Array<number>} */
-  this.columnPos_ = [0];
-  for (let i = 0; i < columns.length; i++) {
-    this.columnPos_[i + 1] = columns[i].width + this.columnPos_[i];
+FileTableColumnModel.ColumnSnapshot = class {
+  /**
+   * @param {!Array<!cr.ui.table.TableColumn>} columns
+   */
+  constructor(columns) {
+    /** @private {!Array<number>} */
+    this.columnPos_ = [0];
+    for (let i = 0; i < columns.length; i++) {
+      this.columnPos_[i + 1] = columns[i].width + this.columnPos_[i];
+    }
+
+    /**
+     * Starts off as a copy of the current column positions, but gets modified.
+     * @private {!Array<number>}
+     */
+    this.newPos = this.columnPos_.slice(0);
   }
 
   /**
-   * Starts off as a copy of the current column positions, but gets modified.
-   * @private {!Array<number>}
+   * Set the width of the given column.  The snapshot will keep the total width
+   * of the table constant.
+   * @param {number} index
+   * @param {number} width
    */
-  this.newPos = this.columnPos_.slice(0);
-};
+  setWidth(index, width) {
+    // Skip to resize 'selection' column
+    if (index < 0 || index >= this.columnPos_.length - 1 || !this.columnPos_) {
+      return;
+    }
 
-/**
- * Set the width of the given column.  The snapshot will keep the total width of
- * the table constant.
- * @param {number} index
- * @param {number} width
- */
-FileTableColumnModel.ColumnSnapshot.prototype.setWidth = function(
-    index, width) {
-  // Skip to resize 'selection' column
-  if (index < 0 || index >= this.columnPos_.length - 1 || !this.columnPos_) {
-    return;
-  }
+    // Round up if the column is shrinking, and down if the column is expanding.
+    // This prevents off-by-one drift.
+    const currentWidth = this.columnPos_[index + 1] - this.columnPos_[index];
+    const round = width < currentWidth ? Math.ceil : Math.floor;
 
-  // Round up if the column is shrinking, and down if the column is expanding.
-  // This prevents off-by-one drift.
-  const currentWidth = this.columnPos_[index + 1] - this.columnPos_[index];
-  const round = width < currentWidth ? Math.ceil : Math.floor;
-
-  // Calculate new positions of column splitters.
-  const newPosStart = this.columnPos_[index] + width;
-  const posEnd = this.columnPos_[this.columnPos_.length - 1];
-  for (let i = 0; i < index + 1; i++) {
-    this.newPos[i] = this.columnPos_[i];
+    // Calculate new positions of column splitters.
+    const newPosStart = this.columnPos_[index] + width;
+    const posEnd = this.columnPos_[this.columnPos_.length - 1];
+    for (let i = 0; i < index + 1; i++) {
+      this.newPos[i] = this.columnPos_[i];
+    }
+    for (let i = index + 1; i < this.columnPos_.length - 1; i++) {
+      const posStart = this.columnPos_[index + 1];
+      this.newPos[i] = (posEnd - newPosStart) *
+              (this.columnPos_[i] - posStart) / (posEnd - posStart) +
+          newPosStart;
+      this.newPos[i] = round(this.newPos[i]);
+    }
+    this.newPos[index] = this.columnPos_[index];
+    this.newPos[this.columnPos_.length - 1] = posEnd;
   }
-  for (let i = index + 1; i < this.columnPos_.length - 1; i++) {
-    const posStart = this.columnPos_[index + 1];
-    this.newPos[i] = (posEnd - newPosStart) * (this.columnPos_[i] - posStart) /
-            (posEnd - posStart) +
-        newPosStart;
-    this.newPos[i] = round(this.newPos[i]);
-  }
-  this.newPos[index] = this.columnPos_[index];
-  this.newPos[this.columnPos_.length - 1] = posEnd;
 };
 
 /**
@@ -360,787 +361,832 @@
 
 /**
  * File list Table View.
- * @constructor
- * @extends {cr.ui.Table}
  */
-function FileTable() {
-  throw new Error('Designed to decorate elements');
+class FileTable extends cr.ui.Table {
+  constructor() {
+    super();
+
+    /** @private {number} */
+    this.beginIndex_ = 0;
+
+    /** @private {number} */
+    this.endIndex_ = 0;
+
+    /** @private {?ListThumbnailLoader} */
+    this.listThumbnailLoader_ = null;
+
+    /** @private {?AsyncUtil.RateLimiter} */
+    this.relayoutRateLimiter_ = null;
+
+    /** @private {?MetadataModel} */
+    this.metadataModel_ = null;
+
+    /** @private {?cr.ui.table.TableList} */
+    this.list_ = null;
+
+    /** @private {?FileMetadataFormatter} */
+    this.formatter_ = null;
+
+    /** @private {boolean} */
+    this.useModificationByMeTime_ = false;
+
+    /** @private {?importer.HistoryLoader} */
+    this.historyLoader_ = null;
+
+    /** @private {boolean} */
+    this.importStatusVisible_ = false;
+
+    /** @private {?VolumeManager} */
+    this.volumeManager_ = null;
+
+    /** @private {!Array} */
+    this.lastSelection_ = [];
+
+    /** @private {?function(!Event)} */
+    this.onThumbnailLoadedBound_ = null;
+
+    throw new Error('Designed to decorate elements');
+  }
+
+  /**
+   * Decorates the element.
+   * @param {!Element} self Table to decorate.
+   * @param {!MetadataModel} metadataModel To retrieve
+   *     metadata.
+   * @param {!VolumeManager} volumeManager To retrieve volume info.
+   * @param {!importer.HistoryLoader} historyLoader
+   * @param {boolean} fullPage True if it's full page File Manager,
+   *                           False if a file open/save dialog.
+   */
+  static decorate(self, metadataModel, volumeManager, historyLoader, fullPage) {
+    cr.ui.Table.decorate(self);
+    self.__proto__ = FileTable.prototype;
+    FileTableList.decorate(self.list);
+    self.list.setOnMergeItems(self.updateHighPriorityRange_.bind(self));
+    self.metadataModel_ = metadataModel;
+    self.volumeManager_ = volumeManager;
+    self.historyLoader_ = historyLoader;
+
+    /** @private {ListThumbnailLoader} */
+    self.listThumbnailLoader_ = null;
+
+    /** @private {number} */
+    self.beginIndex_ = 0;
+
+    /** @private {number} */
+    self.endIndex_ = 0;
+
+    /** @private {function(!Event)} */
+    self.onThumbnailLoadedBound_ = self.onThumbnailLoaded_.bind(self);
+
+    /**
+     * Reflects the visibility of import status in the UI.  Assumption: import
+     * status is only enabled in import-eligible locations.  See
+     * ImportController#onDirectoryChanged.  For this reason, the code in this
+     * class checks if import status is visible, and if so, assumes that all
+     * the files are in an import-eligible location.
+     * TODO(kenobi): Clean this up once import status is queryable from
+     * metadata.
+     *
+     * @private {boolean}
+     */
+    self.importStatusVisible_ = true;
+
+    /** @private {boolean} */
+    self.useModificationByMeTime_ = false;
+
+    const nameColumn = new cr.ui.table.TableColumn(
+        'name', str('NAME_COLUMN_LABEL'), fullPage ? 386 : 324);
+    nameColumn.renderFunction = self.renderName_.bind(self);
+    nameColumn.headerRenderFunction = renderHeader_;
+
+    const sizeColumn = new cr.ui.table.TableColumn(
+        'size', str('SIZE_COLUMN_LABEL'), 110, true);
+    sizeColumn.renderFunction = self.renderSize_.bind(self);
+    sizeColumn.defaultOrder = 'desc';
+    sizeColumn.headerRenderFunction = renderHeader_;
+
+    const statusColumn = new cr.ui.table.TableColumn(
+        'status', str('STATUS_COLUMN_LABEL'), 60, true);
+    statusColumn.renderFunction = self.renderStatus_.bind(self);
+    statusColumn.visible = self.importStatusVisible_;
+    statusColumn.headerRenderFunction = renderHeader_;
+
+    const typeColumn = new cr.ui.table.TableColumn(
+        'type', str('TYPE_COLUMN_LABEL'), fullPage ? 110 : 110);
+    typeColumn.renderFunction = self.renderType_.bind(self);
+    typeColumn.headerRenderFunction = renderHeader_;
+
+    const modTimeColumn = new cr.ui.table.TableColumn(
+        'modificationTime', str('DATE_COLUMN_LABEL'), fullPage ? 150 : 210);
+    modTimeColumn.renderFunction = self.renderDate_.bind(self);
+    modTimeColumn.defaultOrder = 'desc';
+    modTimeColumn.headerRenderFunction = renderHeader_;
+
+    const columns =
+        [nameColumn, sizeColumn, statusColumn, typeColumn, modTimeColumn];
+
+    const columnModel = new FileTableColumnModel(columns);
+
+    self.columnModel = columnModel;
+
+    self.formatter_ = new FileMetadataFormatter();
+
+    const selfAsTable = /** @type {!cr.ui.Table} */ (self);
+    selfAsTable.setRenderFunction(
+        self.renderTableRow_.bind(self, selfAsTable.getRenderFunction()));
+
+    // Keep focus on the file list when clicking on the header.
+    selfAsTable.header.addEventListener('mousedown', e => {
+      self.list.focus();
+      e.preventDefault();
+    });
+
+    self.relayoutRateLimiter_ =
+        new AsyncUtil.RateLimiter(self.relayoutImmediately_.bind(self));
+
+    // Override header#redraw to use FileTableSplitter.
+    /** @this {cr.ui.table.TableHeader} */
+    selfAsTable.header.redraw = function() {
+      this.__proto__.redraw.call(this);
+      // Extend table splitters
+      const splitters = this.querySelectorAll('.table-header-splitter');
+      for (let i = 0; i < splitters.length; i++) {
+        if (splitters[i] instanceof FileTableSplitter) {
+          continue;
+        }
+        FileTableSplitter.decorate(splitters[i]);
+      }
+    };
+
+    // Save the last selection. This is used by shouldStartDragSelection.
+    self.list.addEventListener('mousedown', function(e) {
+      this.lastSelection_ = this.selectionModel.selectedIndexes;
+    }.bind(self), true);
+    self.list.addEventListener('touchstart', function(e) {
+      this.lastSelection_ = this.selectionModel.selectedIndexes;
+    }.bind(self), true);
+    self.list.shouldStartDragSelection =
+        self.shouldStartDragSelection_.bind(self);
+    self.list.hasDragHitElement = self.hasDragHitElement_.bind(self);
+
+    /**
+     * Obtains the index list of elements that are hit by the point or the
+     * rectangle.
+     *
+     * @param {number} x X coordinate value.
+     * @param {number} y Y coordinate value.
+     * @param {number=} opt_width Width of the coordinate.
+     * @param {number=} opt_height Height of the coordinate.
+     * @return {Array<number>} Index list of hit elements.
+     * @this {cr.ui.List}
+     */
+    self.list.getHitElements = function(x, y, opt_width, opt_height) {
+      const currentSelection = [];
+      const bottom = y + (opt_height || 0);
+      for (let i = 0; i < this.selectionModel_.length; i++) {
+        const itemMetrics = this.getHeightsForIndex(i);
+        if (itemMetrics.top < bottom &&
+            itemMetrics.top + itemMetrics.height >= y) {
+          currentSelection.push(i);
+        }
+      }
+      return currentSelection;
+    };
+  }
+
+  /**
+   * Updates high priority range of list thumbnail loader based on current
+   * viewport.
+   *
+   * @param {number} beginIndex Begin index.
+   * @param {number} endIndex End index.
+   * @private
+   */
+  updateHighPriorityRange_(beginIndex, endIndex) {
+    // Keep these values to set range when a new list thumbnail loader is set.
+    this.beginIndex_ = beginIndex;
+    this.endIndex_ = endIndex;
+
+    if (this.listThumbnailLoader_ !== null) {
+      this.listThumbnailLoader_.setHighPriorityRange(beginIndex, endIndex);
+    }
+  }
+
+  /**
+   * Sets list thumbnail loader.
+   * @param {ListThumbnailLoader} listThumbnailLoader A list thumbnail loader.
+   */
+  setListThumbnailLoader(listThumbnailLoader) {
+    if (this.listThumbnailLoader_) {
+      this.listThumbnailLoader_.removeEventListener(
+          'thumbnailLoaded', this.onThumbnailLoadedBound_);
+    }
+
+    this.listThumbnailLoader_ = listThumbnailLoader;
+
+    if (this.listThumbnailLoader_) {
+      this.listThumbnailLoader_.addEventListener(
+          'thumbnailLoaded', this.onThumbnailLoadedBound_);
+      this.listThumbnailLoader_.setHighPriorityRange(
+          this.beginIndex_, this.endIndex_);
+    }
+  }
+
+  /**
+   * Returns the element containing the thumbnail of a certain list item as
+   * background image.
+   * @param {number} index The index of the item containing the desired
+   *     thumbnail.
+   * @return {?Element} The element containing the thumbnail, or null, if an
+   *     error occurred.
+   */
+  getThumbnail(index) {
+    const listItem = this.getListItemByIndex(index);
+    if (!listItem) {
+      return null;
+    }
+    const container = listItem.querySelector('.detail-thumbnail');
+    if (!container) {
+      return null;
+    }
+    return container.querySelector('.thumbnail');
+  }
+
+  /**
+   * Handles thumbnail loaded event.
+   * @param {!Event} event An event.
+   * @private
+   */
+  onThumbnailLoaded_(event) {
+    const listItem = this.getListItemByIndex(event.index);
+    if (listItem) {
+      const box = listItem.querySelector('.detail-thumbnail');
+      if (box) {
+        if (event.dataUrl) {
+          this.setThumbnailImage_(
+              assertInstanceof(box, HTMLDivElement), event.dataUrl,
+              true /* with animation */);
+        } else {
+          this.clearThumbnailImage_(assertInstanceof(box, HTMLDivElement));
+        }
+      }
+    }
+  }
+
+  /**
+   * Adjust column width to fit its content.
+   * @param {number} index Index of the column to adjust width.
+   * @override
+   */
+  fitColumn(index) {
+    const render = this.columnModel.getRenderFunction(index);
+    const MAXIMUM_ROWS_TO_MEASURE = 1000;
+
+    // Create a temporaty list item, put all cells into it and measure its
+    // width. Then remove the item. It fits "list > *" CSS rules.
+    const container = this.ownerDocument.createElement('li');
+    container.style.display = 'inline-block';
+    container.style.textAlign = 'start';
+    // The container will have width of the longest cell.
+    container.style.webkitBoxOrient = 'vertical';
+
+    // Select at most MAXIMUM_ROWS_TO_MEASURE items around visible area.
+    const items = this.list.getItemsInViewPort(
+        this.list.scrollTop, this.list.clientHeight);
+    const firstIndex = Math.floor(
+        Math.max(0, (items.last + items.first - MAXIMUM_ROWS_TO_MEASURE) / 2));
+    const lastIndex =
+        Math.min(this.dataModel.length, firstIndex + MAXIMUM_ROWS_TO_MEASURE);
+    for (let i = firstIndex; i < lastIndex; i++) {
+      const item = this.dataModel.item(i);
+      const div = this.ownerDocument.createElement('div');
+      div.className = 'table-row-cell';
+      div.appendChild(render(item, this.columnModel.getId(index), this));
+      container.appendChild(div);
+    }
+    this.list.appendChild(container);
+    const width = parseFloat(window.getComputedStyle(container).width);
+    this.list.removeChild(container);
+
+    this.columnModel.initializeColumnPos();
+    this.columnModel.setWidthAndKeepTotal(index, Math.ceil(width));
+    this.columnModel.destroyColumnPos();
+  }
+
+  /**
+   * Sets the visibility of the cloud import status column.
+   * @param {boolean} visible
+   */
+  setImportStatusVisible(visible) {
+    if (this.importStatusVisible_ != visible) {
+      this.importStatusVisible_ = visible;
+      this.columnModel.setVisible(this.columnModel.indexOf('status'), visible);
+      this.relayout();
+    }
+  }
+
+  /**
+   * Sets date and time format.
+   * @param {boolean} use12hourClock True if 12 hours clock, False if 24 hours.
+   */
+  setDateTimeFormat(use12hourClock) {
+    this.formatter_.setDateTimeFormat(use12hourClock);
+  }
+
+  /**
+   * Sets whether to use modificationByMeTime as "Last Modified" time.
+   * @param {boolean} useModificationByMeTime
+   */
+  setUseModificationByMeTime(useModificationByMeTime) {
+    this.useModificationByMeTime_ = useModificationByMeTime;
+  }
+
+  /**
+   * Returns whether the drag event is inside a file entry in the list (and not
+   * the background padding area).
+   * @param {MouseEvent} event Drag start event.
+   * @return {boolean} True if the mouse is over an element in the list, False
+   *     if
+   *                   it is in the background.
+   */
+  hasDragHitElement_(event) {
+    const pos = DragSelector.getScrolledPosition(this.list, event);
+    return this.list.getHitElements(pos.x, pos.y).length !== 0;
+  }
+
+  /**
+   * Obtains if the drag selection should be start or not by referring the mouse
+   * event.
+   * @param {MouseEvent} event Drag start event.
+   * @return {boolean} True if the mouse is hit to the background of the list,
+   * or certain areas of the inside of the list that would start a drag
+   * selection.
+   * @private
+   */
+  shouldStartDragSelection_(event) {
+    // If the shift key is pressed, it should starts drag selection.
+    if (event.shiftKey) {
+      return true;
+    }
+
+    // If we're outside of the element list, start the drag selection.
+    if (!this.list.hasDragHitElement(event)) {
+      return true;
+    }
+
+    // If the position values are negative, it points the out of list.
+    const pos = DragSelector.getScrolledPosition(this.list, event);
+    if (!pos) {
+      return false;
+    }
+    if (pos.x < 0 || pos.y < 0) {
+      return true;
+    }
+
+    // If the item index is out of range, it should start the drag selection.
+    const itemHeight = this.list.measureItem().height;
+    // Faster alternative to Math.floor for non-negative numbers.
+    const itemIndex = ~~(pos.y / itemHeight);
+    if (itemIndex >= this.list.dataModel.length) {
+      return true;
+    }
+
+    // If the pointed item is already selected, it should not start the drag
+    // selection.
+    if (this.lastSelection_ && this.lastSelection_.indexOf(itemIndex) !== -1) {
+      return false;
+    }
+
+    // If the horizontal value is not hit to column, it should start the drag
+    // selection.
+    const hitColumn = this.columnModel.getHitColumn(pos.x);
+    if (!hitColumn) {
+      return true;
+    }
+
+    // Check if the point is on the column contents or not.
+    switch (this.columnModel.columns_[hitColumn.index].id) {
+      case 'name':
+        const item = this.list.getListItemByIndex(itemIndex);
+        if (!item) {
+          return false;
+        }
+
+        const spanElement = item.querySelector('.filename-label span');
+        const spanRect = spanElement.getBoundingClientRect();
+        // The this.list.cachedBounds_ object is set by
+        // DragSelector.getScrolledPosition.
+        if (!this.list.cachedBounds) {
+          return true;
+        }
+        const textRight =
+            spanRect.left - this.list.cachedBounds.left + spanRect.width;
+        return textRight <= hitColumn.hitPosition;
+      default:
+        return true;
+    }
+  }
+
+  /**
+   * Render the Name column of the detail table.
+   *
+   * Invoked by cr.ui.Table when a file needs to be rendered.
+   *
+   * @param {!Entry} entry The Entry object to render.
+   * @param {string} columnId The id of the column to be rendered.
+   * @param {cr.ui.Table} table The table doing the rendering.
+   * @return {!HTMLDivElement} Created element.
+   * @private
+   */
+  renderName_(entry, columnId, table) {
+    const label = /** @type {!HTMLDivElement} */
+        (this.ownerDocument.createElement('div'));
+
+    const mimeType =
+        this.metadataModel_.getCache([entry], ['contentMimeType'])[0]
+            .contentMimeType;
+    const locationInfo = this.volumeManager_.getLocationInfo(entry);
+    const icon = filelist.renderFileTypeIcon(
+        this.ownerDocument, entry, locationInfo, mimeType);
+    if (FileType.isImage(entry, mimeType) ||
+        FileType.isVideo(entry, mimeType) ||
+        FileType.isAudio(entry, mimeType) || FileType.isRaw(entry, mimeType)) {
+      icon.appendChild(this.renderThumbnail_(entry));
+    }
+    icon.appendChild(this.renderCheckmark_());
+    label.appendChild(icon);
+
+    label.entry = entry;
+    label.className = 'detail-name';
+    label.appendChild(
+        filelist.renderFileNameLabel(this.ownerDocument, entry, locationInfo));
+    return label;
+  }
+
+  /**
+   * Render the Size column of the detail table.
+   *
+   * @param {Entry} entry The Entry object to render.
+   * @param {string} columnId The id of the column to be rendered.
+   * @param {cr.ui.Table} table The table doing the rendering.
+   * @return {!HTMLDivElement} Created element.
+   * @private
+   */
+  renderSize_(entry, columnId, table) {
+    const div = /** @type {!HTMLDivElement} */
+        (this.ownerDocument.createElement('div'));
+    div.className = 'size';
+    this.updateSize_(div, entry);
+
+    return div;
+  }
+
+  /**
+   * Sets up or updates the size cell.
+   *
+   * @param {HTMLDivElement} div The table cell.
+   * @param {Entry} entry The corresponding entry.
+   * @private
+   */
+  updateSize_(div, entry) {
+    const metadata =
+        this.metadataModel_.getCache([entry], ['size', 'hosted'])[0];
+    const size = metadata.size;
+    const hosted = metadata.hosted;
+    div.textContent = this.formatter_.formatSize(size, hosted);
+  }
+
+  /**
+   * Render the Status column of the detail table.
+   *
+   * @param {Entry} entry The Entry object to render.
+   * @param {string} columnId The id of the column to be rendered.
+   * @param {cr.ui.Table} table The table doing the rendering.
+   * @return {!HTMLDivElement} Created element.
+   * @private
+   */
+  renderStatus_(entry, columnId, table) {
+    const div =
+        /** @type {!HTMLDivElement} */ (
+            this.ownerDocument.createElement('div'));
+    div.className = 'status status-icon';
+    if (entry) {
+      this.updateStatus_(div, entry);
+    }
+
+    return div;
+  }
+
+  /**
+   * Returns the status of the entry w.r.t. the given import destination.
+   * @param {Entry} entry
+   * @param {!importer.Destination} destination
+   * @return {!Promise<string>} The import status - will be 'imported',
+   *     'copied', or 'unknown'.
+   */
+  getImportStatus_(entry, destination) {
+    // If import status is not visible, early out because there's no point
+    // retrieving it.
+    if (!this.importStatusVisible_ || !importer.isEligibleType(entry)) {
+      // Our import history doesn't deal with directories.
+      // TODO(kenobi): May need to revisit this if the above assumption changes.
+      return Promise.resolve('unknown');
+    }
+    // For the compiler.
+    const fileEntry = /** @type {!FileEntry} */ (entry);
+
+    return this.historyLoader_.getHistory()
+        .then(
+            /** @param {!importer.ImportHistory} history */
+            history => {
+              return Promise.all([
+                history.wasImported(fileEntry, destination),
+                history.wasCopied(fileEntry, destination)
+              ]);
+            })
+        .then(
+            /** @param {!Array<boolean>} status */
+            status => {
+              if (status[0]) {
+                return 'imported';
+              } else if (status[1]) {
+                return 'copied';
+              } else {
+                return 'unknown';
+              }
+            });
+  }
+
+  /**
+   * Render the status icon of the detail table.
+   *
+   * @param {HTMLDivElement} div
+   * @param {Entry} entry The Entry object to render.
+   * @private
+   */
+  updateStatus_(div, entry) {
+    this.getImportStatus_(entry, importer.Destination.GOOGLE_DRIVE)
+        .then(
+            /** @param {string} status */
+            status => {
+              div.setAttribute('file-status-icon', status);
+            });
+  }
+
+  /**
+   * Render the Type column of the detail table.
+   *
+   * @param {Entry} entry The Entry object to render.
+   * @param {string} columnId The id of the column to be rendered.
+   * @param {cr.ui.Table} table The table doing the rendering.
+   * @return {!HTMLDivElement} Created element.
+   * @private
+   */
+  renderType_(entry, columnId, table) {
+    const div = /** @type {!HTMLDivElement} */
+        (this.ownerDocument.createElement('div'));
+    div.className = 'type';
+
+    const mimeType =
+        this.metadataModel_.getCache([entry], ['contentMimeType'])[0]
+            .contentMimeType;
+    div.textContent =
+        FileListModel.getFileTypeString(FileType.getType(entry, mimeType));
+
+    // For removable partitions, display file system type.
+    if (!mimeType && entry.volumeInfo && entry.volumeInfo.diskFileSystemType) {
+      div.textContent = entry.volumeInfo.diskFileSystemType;
+    }
+
+    return div;
+  }
+
+  /**
+   * Render the Date column of the detail table.
+   *
+   * @param {Entry} entry The Entry object to render.
+   * @param {string} columnId The id of the column to be rendered.
+   * @param {cr.ui.Table} table The table doing the rendering.
+   * @return {HTMLDivElement} Created element.
+   * @private
+   */
+  renderDate_(entry, columnId, table) {
+    const div = /** @type {!HTMLDivElement} */
+        (this.ownerDocument.createElement('div'));
+    div.className = 'date';
+
+    this.updateDate_(div, entry);
+    return div;
+  }
+
+  /**
+   * Sets up or updates the date cell.
+   *
+   * @param {HTMLDivElement} div The table cell.
+   * @param {Entry} entry Entry of file to update.
+   * @private
+   */
+  updateDate_(div, entry) {
+    // For now, Team Drive roots have the incorrect modified date value. Hide it
+    // until we get the proper one (see https://crbug.com/861622).
+    if (util.isTeamDriveRoot(entry)) {
+      div.textContent = '--';
+      return;
+    }
+
+    const item = this.metadataModel_.getCache(
+        [entry], ['modificationTime', 'modificationByMeTime'])[0];
+    const modTime = this.useModificationByMeTime_ ?
+        item.modificationByMeTime || item.modificationTime || null :
+        item.modificationTime || null;
+
+    div.textContent = this.formatter_.formatModDate(modTime);
+  }
+
+  /**
+   * Updates the file metadata in the table item.
+   *
+   * @param {Element} item Table item.
+   * @param {Entry} entry File entry.
+   */
+  updateFileMetadata(item, entry) {
+    this.updateDate_(
+        /** @type {!HTMLDivElement} */ (item.querySelector('.date')), entry);
+    this.updateSize_(
+        /** @type {!HTMLDivElement} */ (item.querySelector('.size')), entry);
+    this.updateStatus_(
+        /** @type {!HTMLDivElement} */ (item.querySelector('.status')), entry);
+  }
+
+  /**
+   * Updates list items 'in place' on metadata change.
+   * @param {string} type Type of metadata change.
+   * @param {Array<Entry>} entries Entries to update.
+   */
+  updateListItemsMetadata(type, entries) {
+    const urls = util.entriesToURLs(entries);
+    const forEachCell = (selector, callback) => {
+      const cells = this.querySelectorAll(selector);
+      for (let i = 0; i < cells.length; i++) {
+        const cell = /** @type {HTMLElement} */ (cells[i]);
+        const listItem = this.list_.getListItemAncestor(cell);
+        const entry = this.dataModel.item(listItem.listIndex);
+        if (entry && urls.indexOf(entry.toURL()) !== -1) {
+          callback.call(this, cell, entry, listItem);
+        }
+      }
+    };
+    if (type === 'filesystem') {
+      forEachCell('.table-row-cell > .date', function(item, entry, unused) {
+        this.updateDate_(item, entry);
+      });
+      forEachCell('.table-row-cell > .size', function(item, entry, unused) {
+        this.updateSize_(item, entry);
+      });
+    } else if (type === 'external') {
+      // The cell name does not matter as the entire list item is needed.
+      forEachCell('.table-row-cell > .date', function(item, entry, listItem) {
+        filelist.updateListItemExternalProps(
+            listItem,
+            this.metadataModel_.getCache(
+                [entry],
+                [
+                  'availableOffline', 'customIconUrl', 'shared',
+                  'isMachineRoot', 'isExternalMedia', 'hosted'
+                ])[0],
+            util.isTeamDriveRoot(entry));
+      });
+    } else if (type === 'import-history') {
+      forEachCell('.table-row-cell > .status', function(item, entry, unused) {
+        this.updateStatus_(item, entry);
+      });
+    }
+  }
+
+  /**
+   * Renders table row.
+   * @param {function(Entry, cr.ui.Table)} baseRenderFunction Base renderer.
+   * @param {Entry} entry Corresponding entry.
+   * @return {HTMLLIElement} Created element.
+   * @private
+   */
+  renderTableRow_(baseRenderFunction, entry) {
+    const item = baseRenderFunction(entry, this);
+    const nameId = item.id + '-entry-name';
+    const sizeId = item.id + '-size';
+    const dateId = item.id + '-date';
+    filelist.decorateListItem(item, entry, assert(this.metadataModel_));
+    item.setAttribute('file-name', entry.name);
+    item.querySelector('.entry-name').setAttribute('id', nameId);
+    item.querySelector('.size').setAttribute('id', sizeId);
+    item.querySelector('.date').setAttribute('id', dateId);
+    item.setAttribute('aria-labelledby', nameId + ' ' + sizeId + ' ' + dateId);
+    return item;
+  }
+
+  /**
+   * Renders the file thumbnail in the detail table.
+   * @param {Entry} entry The Entry object to render.
+   * @return {!HTMLDivElement} Created element.
+   * @private
+   */
+  renderThumbnail_(entry) {
+    const box = /** @type {!HTMLDivElement} */
+        (this.ownerDocument.createElement('div'));
+    box.className = 'detail-thumbnail';
+
+    // Set thumbnail if it's already in cache.
+    const thumbnailData = this.listThumbnailLoader_ ?
+        this.listThumbnailLoader_.getThumbnailFromCache(entry) :
+        null;
+    if (thumbnailData && thumbnailData.dataUrl) {
+      this.setThumbnailImage_(
+          box, thumbnailData.dataUrl, false /* without animation */);
+    }
+
+    return box;
+  }
+
+  /**
+   * Sets thumbnail image to the box.
+   * @param {!HTMLDivElement} box Detail thumbnail div element.
+   * @param {string} dataUrl Data url of thumbnail.
+   * @param {boolean} shouldAnimate Whether the thumbnail is shown with
+   *     animation or not.
+   * @private
+   */
+  setThumbnailImage_(box, dataUrl, shouldAnimate) {
+    const oldThumbnails = box.querySelectorAll('.thumbnail');
+
+    const thumbnail = box.ownerDocument.createElement('div');
+    thumbnail.classList.add('thumbnail');
+    thumbnail.style.backgroundImage = 'url(' + dataUrl + ')';
+    thumbnail.addEventListener('animationend', () => {
+      // Remove animation css once animation is completed in order not to
+      // animate again when an item is attached to the dom again.
+      thumbnail.classList.remove('animate');
+
+      for (let i = 0; i < oldThumbnails.length; i++) {
+        if (box.contains(oldThumbnails[i])) {
+          box.removeChild(oldThumbnails[i]);
+        }
+      }
+    });
+
+    if (shouldAnimate) {
+      thumbnail.classList.add('animate');
+    }
+
+    box.appendChild(thumbnail);
+  }
+
+  /**
+   * Clears thumbnail image from the box.
+   * @param {!HTMLDivElement} box Detail thumbnail div element.
+   * @private
+   */
+  clearThumbnailImage_(box) {
+    const oldThumbnails = box.querySelectorAll('.thumbnail');
+
+    for (let i = 0; i < oldThumbnails.length; i++) {
+      box.removeChild(oldThumbnails[i]);
+    }
+  }
+
+  /**
+   * Renders the selection checkmark in the detail table.
+   * @return {!HTMLDivElement} Created element.
+   * @private
+   */
+  renderCheckmark_() {
+    const checkmark = /** @type {!HTMLDivElement} */
+        (this.ownerDocument.createElement('div'));
+    checkmark.className = 'detail-checkmark';
+    return checkmark;
+  }
+
+  /**
+   * Redraws the UI. Skips multiple consecutive calls.
+   */
+  relayout() {
+    this.relayoutRateLimiter_.run();
+  }
+
+  /**
+   * Redraws the UI immediately.
+   * @private
+   */
+  relayoutImmediately_() {
+    if (this.clientWidth > 0) {
+      this.normalizeColumns();
+    }
+    this.redraw();
+    cr.dispatchSimpleEvent(this.list, 'relayout');
+  }
 }
 
 /**
  * Inherits from cr.ui.Table.
  */
 FileTable.prototype.__proto__ = cr.ui.Table.prototype;
-
-/**
- * Decorates the element.
- * @param {!Element} self Table to decorate.
- * @param {!MetadataModel} metadataModel To retrieve
- *     metadata.
- * @param {!VolumeManager} volumeManager To retrieve volume info.
- * @param {!importer.HistoryLoader} historyLoader
- * @param {boolean} fullPage True if it's full page File Manager,
- *                           False if a file open/save dialog.
- */
-FileTable.decorate =
-    (self, metadataModel, volumeManager, historyLoader, fullPage) => {
-      cr.ui.Table.decorate(self);
-      self.__proto__ = FileTable.prototype;
-      FileTableList.decorate(self.list);
-      self.list.setOnMergeItems(self.updateHighPriorityRange_.bind(self));
-      self.metadataModel_ = metadataModel;
-      self.volumeManager_ = volumeManager;
-      self.historyLoader_ = historyLoader;
-
-      /** @private {ListThumbnailLoader} */
-      self.listThumbnailLoader_ = null;
-
-      /** @private {number} */
-      self.beginIndex_ = 0;
-
-      /** @private {number} */
-      self.endIndex_ = 0;
-
-      /** @private {function(!Event)} */
-      self.onThumbnailLoadedBound_ = self.onThumbnailLoaded_.bind(self);
-
-      /**
-       * Reflects the visibility of import status in the UI.  Assumption: import
-       * status is only enabled in import-eligible locations.  See
-       * ImportController#onDirectoryChanged.  For this reason, the code in this
-       * class checks if import status is visible, and if so, assumes that all
-       * the files are in an import-eligible location.
-       * TODO(kenobi): Clean this up once import status is queryable from
-       * metadata.
-       *
-       * @private {boolean}
-       */
-      self.importStatusVisible_ = true;
-
-      /** @private {boolean} */
-      self.useModificationByMeTime_ = false;
-
-      const nameColumn = new cr.ui.table.TableColumn(
-          'name', str('NAME_COLUMN_LABEL'), fullPage ? 386 : 324);
-      nameColumn.renderFunction = self.renderName_.bind(self);
-      nameColumn.headerRenderFunction = renderHeader_;
-
-      const sizeColumn = new cr.ui.table.TableColumn(
-          'size', str('SIZE_COLUMN_LABEL'), 110, true);
-      sizeColumn.renderFunction = self.renderSize_.bind(self);
-      sizeColumn.defaultOrder = 'desc';
-      sizeColumn.headerRenderFunction = renderHeader_;
-
-      const statusColumn = new cr.ui.table.TableColumn(
-          'status', str('STATUS_COLUMN_LABEL'), 60, true);
-      statusColumn.renderFunction = self.renderStatus_.bind(self);
-      statusColumn.visible = self.importStatusVisible_;
-      statusColumn.headerRenderFunction = renderHeader_;
-
-      const typeColumn = new cr.ui.table.TableColumn(
-          'type', str('TYPE_COLUMN_LABEL'), fullPage ? 110 : 110);
-      typeColumn.renderFunction = self.renderType_.bind(self);
-      typeColumn.headerRenderFunction = renderHeader_;
-
-      const modTimeColumn = new cr.ui.table.TableColumn(
-          'modificationTime', str('DATE_COLUMN_LABEL'), fullPage ? 150 : 210);
-      modTimeColumn.renderFunction = self.renderDate_.bind(self);
-      modTimeColumn.defaultOrder = 'desc';
-      modTimeColumn.headerRenderFunction = renderHeader_;
-
-      const columns =
-          [nameColumn, sizeColumn, statusColumn, typeColumn, modTimeColumn];
-
-      const columnModel = new FileTableColumnModel(columns);
-
-      self.columnModel = columnModel;
-
-      self.formatter_ = new FileMetadataFormatter();
-
-      const selfAsTable = /** @type {!cr.ui.Table} */ (self);
-      selfAsTable.setRenderFunction(
-          self.renderTableRow_.bind(self, selfAsTable.getRenderFunction()));
-
-      // Keep focus on the file list when clicking on the header.
-      selfAsTable.header.addEventListener('mousedown', e => {
-        self.list.focus();
-        e.preventDefault();
-      });
-
-      self.relayoutRateLimiter_ =
-          new AsyncUtil.RateLimiter(self.relayoutImmediately_.bind(self));
-
-      // Override header#redraw to use FileTableSplitter.
-      /** @this {cr.ui.table.TableHeader} */
-      selfAsTable.header.redraw = function() {
-        this.__proto__.redraw.call(this);
-        // Extend table splitters
-        const splitters = this.querySelectorAll('.table-header-splitter');
-        for (let i = 0; i < splitters.length; i++) {
-          if (splitters[i] instanceof FileTableSplitter) {
-            continue;
-          }
-          FileTableSplitter.decorate(splitters[i]);
-        }
-      };
-
-      // Save the last selection. This is used by shouldStartDragSelection.
-      self.list.addEventListener('mousedown', function(e) {
-        this.lastSelection_ = this.selectionModel.selectedIndexes;
-      }.bind(self), true);
-      self.list.addEventListener('touchstart', function(e) {
-        this.lastSelection_ = this.selectionModel.selectedIndexes;
-      }.bind(self), true);
-      self.list.shouldStartDragSelection =
-          self.shouldStartDragSelection_.bind(self);
-      self.list.hasDragHitElement = self.hasDragHitElement_.bind(self);
-
-      /**
-       * Obtains the index list of elements that are hit by the point or the
-       * rectangle.
-       *
-       * @param {number} x X coordinate value.
-       * @param {number} y Y coordinate value.
-       * @param {number=} opt_width Width of the coordinate.
-       * @param {number=} opt_height Height of the coordinate.
-       * @return {Array<number>} Index list of hit elements.
-       * @this {cr.ui.List}
-       */
-      self.list.getHitElements = function(x, y, opt_width, opt_height) {
-        const currentSelection = [];
-        const bottom = y + (opt_height || 0);
-        for (let i = 0; i < this.selectionModel_.length; i++) {
-          const itemMetrics = this.getHeightsForIndex(i);
-          if (itemMetrics.top < bottom &&
-              itemMetrics.top + itemMetrics.height >= y) {
-            currentSelection.push(i);
-          }
-        }
-        return currentSelection;
-      };
-    };
-
-/**
- * Updates high priority range of list thumbnail loader based on current
- * viewport.
- *
- * @param {number} beginIndex Begin index.
- * @param {number} endIndex End index.
- * @private
- */
-FileTable.prototype.updateHighPriorityRange_ = function(beginIndex, endIndex) {
-  // Keep these values to set range when a new list thumbnail loader is set.
-  this.beginIndex_ = beginIndex;
-  this.endIndex_ = endIndex;
-
-  if (this.listThumbnailLoader_ !== null) {
-    this.listThumbnailLoader_.setHighPriorityRange(beginIndex, endIndex);
-  }
-};
-
-/**
- * Sets list thumbnail loader.
- * @param {ListThumbnailLoader} listThumbnailLoader A list thumbnail loader.
- */
-FileTable.prototype.setListThumbnailLoader = function(listThumbnailLoader) {
-  if (this.listThumbnailLoader_) {
-    this.listThumbnailLoader_.removeEventListener(
-        'thumbnailLoaded', this.onThumbnailLoadedBound_);
-  }
-
-  this.listThumbnailLoader_ = listThumbnailLoader;
-
-  if (this.listThumbnailLoader_) {
-    this.listThumbnailLoader_.addEventListener(
-        'thumbnailLoaded', this.onThumbnailLoadedBound_);
-    this.listThumbnailLoader_.setHighPriorityRange(
-        this.beginIndex_, this.endIndex_);
-  }
-};
-
-/**
- * Returns the element containing the thumbnail of a certain list item as
- * background image.
- * @param {number} index The index of the item containing the desired thumbnail.
- * @return {?Element} The element containing the thumbnail, or null, if an error
- *     occurred.
- */
-FileTable.prototype.getThumbnail = function(index) {
-  const listItem = this.getListItemByIndex(index);
-  if (!listItem) {
-    return null;
-  }
-  const container = listItem.querySelector('.detail-thumbnail');
-  if (!container) {
-    return null;
-  }
-  return container.querySelector('.thumbnail');
-};
-
-/**
- * Handles thumbnail loaded event.
- * @param {!Event} event An event.
- * @private
- */
-FileTable.prototype.onThumbnailLoaded_ = function(event) {
-  const listItem = this.getListItemByIndex(event.index);
-  if (listItem) {
-    const box = listItem.querySelector('.detail-thumbnail');
-    if (box) {
-      if (event.dataUrl) {
-        this.setThumbnailImage_(
-            assertInstanceof(box, HTMLDivElement), event.dataUrl,
-            true /* with animation */);
-      } else {
-        this.clearThumbnailImage_(assertInstanceof(box, HTMLDivElement));
-      }
-    }
-  }
-};
-
-/**
- * Adjust column width to fit its content.
- * @param {number} index Index of the column to adjust width.
- * @override
- */
-FileTable.prototype.fitColumn = function(index) {
-  const render = this.columnModel.getRenderFunction(index);
-  const MAXIMUM_ROWS_TO_MEASURE = 1000;
-
-  // Create a temporaty list item, put all cells into it and measure its
-  // width. Then remove the item. It fits "list > *" CSS rules.
-  const container = this.ownerDocument.createElement('li');
-  container.style.display = 'inline-block';
-  container.style.textAlign = 'start';
-  // The container will have width of the longest cell.
-  container.style.webkitBoxOrient = 'vertical';
-
-  // Select at most MAXIMUM_ROWS_TO_MEASURE items around visible area.
-  const items =
-      this.list.getItemsInViewPort(this.list.scrollTop, this.list.clientHeight);
-  const firstIndex = Math.floor(
-      Math.max(0, (items.last + items.first - MAXIMUM_ROWS_TO_MEASURE) / 2));
-  const lastIndex =
-      Math.min(this.dataModel.length, firstIndex + MAXIMUM_ROWS_TO_MEASURE);
-  for (let i = firstIndex; i < lastIndex; i++) {
-    const item = this.dataModel.item(i);
-    const div = this.ownerDocument.createElement('div');
-    div.className = 'table-row-cell';
-    div.appendChild(render(item, this.columnModel.getId(index), this));
-    container.appendChild(div);
-  }
-  this.list.appendChild(container);
-  const width = parseFloat(window.getComputedStyle(container).width);
-  this.list.removeChild(container);
-
-  this.columnModel.initializeColumnPos();
-  this.columnModel.setWidthAndKeepTotal(index, Math.ceil(width));
-  this.columnModel.destroyColumnPos();
-};
-
-/**
- * Sets the visibility of the cloud import status column.
- * @param {boolean} visible
- */
-FileTable.prototype.setImportStatusVisible = function(visible) {
-  if (this.importStatusVisible_ != visible) {
-    this.importStatusVisible_ = visible;
-    this.columnModel.setVisible(this.columnModel.indexOf('status'), visible);
-    this.relayout();
-  }
-};
-
-/**
- * Sets date and time format.
- * @param {boolean} use12hourClock True if 12 hours clock, False if 24 hours.
- */
-FileTable.prototype.setDateTimeFormat = function(use12hourClock) {
-  this.formatter_.setDateTimeFormat(use12hourClock);
-};
-
-/**
- * Sets whether to use modificationByMeTime as "Last Modified" time.
- * @param {boolean} useModificationByMeTime
- */
-FileTable.prototype.setUseModificationByMeTime = function(
-    useModificationByMeTime) {
-  this.useModificationByMeTime_ = useModificationByMeTime;
-};
-
-/**
- * Returns whether the drag event is inside a file entry in the list (and not
- * the background padding area).
- * @param {MouseEvent} event Drag start event.
- * @return {boolean} True if the mouse is over an element in the list, False if
- *                   it is in the background.
- */
-FileTable.prototype.hasDragHitElement_ = function(event) {
-  const pos = DragSelector.getScrolledPosition(this.list, event);
-  return this.list.getHitElements(pos.x, pos.y).length !== 0;
-};
-
-/**
- * Obtains if the drag selection should be start or not by referring the mouse
- * event.
- * @param {MouseEvent} event Drag start event.
- * @return {boolean} True if the mouse is hit to the background of the list, or
- *                   certain areas of the inside of the list that would start a
- *                   drag selection.
- * @private
- */
-FileTable.prototype.shouldStartDragSelection_ = function(event) {
-  // If the shift key is pressed, it should starts drag selection.
-  if (event.shiftKey) {
-    return true;
-  }
-
-  // If we're outside of the element list, start the drag selection.
-  if (!this.list.hasDragHitElement(event)) {
-    return true;
-  }
-
-  // If the position values are negative, it points the out of list.
-  const pos = DragSelector.getScrolledPosition(this.list, event);
-  if (!pos) {
-    return false;
-  }
-  if (pos.x < 0 || pos.y < 0) {
-    return true;
-  }
-
-  // If the item index is out of range, it should start the drag selection.
-  const itemHeight = this.list.measureItem().height;
-  // Faster alternative to Math.floor for non-negative numbers.
-  const itemIndex = ~~(pos.y / itemHeight);
-  if (itemIndex >= this.list.dataModel.length) {
-    return true;
-  }
-
-  // If the pointed item is already selected, it should not start the drag
-  // selection.
-  if (this.lastSelection_ && this.lastSelection_.indexOf(itemIndex) !== -1) {
-    return false;
-  }
-
-  // If the horizontal value is not hit to column, it should start the drag
-  // selection.
-  const hitColumn = this.columnModel.getHitColumn(pos.x);
-  if (!hitColumn) {
-    return true;
-  }
-
-  // Check if the point is on the column contents or not.
-  switch (this.columnModel.columns_[hitColumn.index].id) {
-    case 'name':
-      const item = this.list.getListItemByIndex(itemIndex);
-      if (!item) {
-        return false;
-      }
-
-      const spanElement = item.querySelector('.filename-label span');
-      const spanRect = spanElement.getBoundingClientRect();
-      // The this.list.cachedBounds_ object is set by
-      // DragSelector.getScrolledPosition.
-      if (!this.list.cachedBounds) {
-        return true;
-      }
-      const textRight =
-          spanRect.left - this.list.cachedBounds.left + spanRect.width;
-      return textRight <= hitColumn.hitPosition;
-    default:
-      return true;
-  }
-};
-
-/**
- * Render the Name column of the detail table.
- *
- * Invoked by cr.ui.Table when a file needs to be rendered.
- *
- * @param {!Entry} entry The Entry object to render.
- * @param {string} columnId The id of the column to be rendered.
- * @param {cr.ui.Table} table The table doing the rendering.
- * @return {!HTMLDivElement} Created element.
- * @private
- */
-FileTable.prototype.renderName_ = function(entry, columnId, table) {
-  const label = /** @type {!HTMLDivElement} */
-      (this.ownerDocument.createElement('div'));
-
-  const mimeType = this.metadataModel_.getCache([entry], ['contentMimeType'])[0]
-                       .contentMimeType;
-  const locationInfo = this.volumeManager_.getLocationInfo(entry);
-  const icon = filelist.renderFileTypeIcon(
-      this.ownerDocument, entry, locationInfo, mimeType);
-  if (FileType.isImage(entry, mimeType) || FileType.isVideo(entry, mimeType) ||
-      FileType.isAudio(entry, mimeType) || FileType.isRaw(entry, mimeType)) {
-    icon.appendChild(this.renderThumbnail_(entry));
-  }
-  icon.appendChild(this.renderCheckmark_());
-  label.appendChild(icon);
-
-  label.entry = entry;
-  label.className = 'detail-name';
-  label.appendChild(
-      filelist.renderFileNameLabel(this.ownerDocument, entry, locationInfo));
-  return label;
-};
-
-/**
- * Render the Size column of the detail table.
- *
- * @param {Entry} entry The Entry object to render.
- * @param {string} columnId The id of the column to be rendered.
- * @param {cr.ui.Table} table The table doing the rendering.
- * @return {!HTMLDivElement} Created element.
- * @private
- */
-FileTable.prototype.renderSize_ = function(entry, columnId, table) {
-  const div = /** @type {!HTMLDivElement} */
-      (this.ownerDocument.createElement('div'));
-  div.className = 'size';
-  this.updateSize_(div, entry);
-
-  return div;
-};
-
-/**
- * Sets up or updates the size cell.
- *
- * @param {HTMLDivElement} div The table cell.
- * @param {Entry} entry The corresponding entry.
- * @private
- */
-FileTable.prototype.updateSize_ = function(div, entry) {
-  const metadata = this.metadataModel_.getCache([entry], ['size', 'hosted'])[0];
-  const size = metadata.size;
-  const hosted = metadata.hosted;
-  div.textContent = this.formatter_.formatSize(size, hosted);
-};
-
-/**
- * Render the Status column of the detail table.
- *
- * @param {Entry} entry The Entry object to render.
- * @param {string} columnId The id of the column to be rendered.
- * @param {cr.ui.Table} table The table doing the rendering.
- * @return {!HTMLDivElement} Created element.
- * @private
- */
-FileTable.prototype.renderStatus_ = function(entry, columnId, table) {
-  const div =
-      /** @type {!HTMLDivElement} */ (this.ownerDocument.createElement('div'));
-  div.className = 'status status-icon';
-  if (entry) {
-    this.updateStatus_(div, entry);
-  }
-
-  return div;
-};
-
-/**
- * Returns the status of the entry w.r.t. the given import destination.
- * @param {Entry} entry
- * @param {!importer.Destination} destination
- * @return {!Promise<string>} The import status - will be 'imported', 'copied',
- *     or 'unknown'.
- */
-FileTable.prototype.getImportStatus_ = function(entry, destination) {
-  // If import status is not visible, early out because there's no point
-  // retrieving it.
-  if (!this.importStatusVisible_ || !importer.isEligibleType(entry)) {
-    // Our import history doesn't deal with directories.
-    // TODO(kenobi): May need to revisit this if the above assumption changes.
-    return Promise.resolve('unknown');
-  }
-  // For the compiler.
-  const fileEntry = /** @type {!FileEntry} */ (entry);
-
-  return this.historyLoader_.getHistory()
-      .then(
-          /** @param {!importer.ImportHistory} history */
-          history => {
-            return Promise.all([
-              history.wasImported(fileEntry, destination),
-              history.wasCopied(fileEntry, destination)
-            ]);
-          })
-      .then(
-          /** @param {!Array<boolean>} status */
-          status => {
-            if (status[0]) {
-              return 'imported';
-            } else if (status[1]) {
-              return 'copied';
-            } else {
-              return 'unknown';
-            }
-          });
-};
-
-/**
- * Render the status icon of the detail table.
- *
- * @param {HTMLDivElement} div
- * @param {Entry} entry The Entry object to render.
- * @private
- */
-FileTable.prototype.updateStatus_ = function(div, entry) {
-  this.getImportStatus_(entry, importer.Destination.GOOGLE_DRIVE)
-      .then(
-          /** @param {string} status */
-          status => {
-            div.setAttribute('file-status-icon', status);
-          });
-};
-
-/**
- * Render the Type column of the detail table.
- *
- * @param {Entry} entry The Entry object to render.
- * @param {string} columnId The id of the column to be rendered.
- * @param {cr.ui.Table} table The table doing the rendering.
- * @return {!HTMLDivElement} Created element.
- * @private
- */
-FileTable.prototype.renderType_ = function(entry, columnId, table) {
-  const div = /** @type {!HTMLDivElement} */
-      (this.ownerDocument.createElement('div'));
-  div.className = 'type';
-
-  const mimeType = this.metadataModel_.getCache([entry], ['contentMimeType'])[0]
-                       .contentMimeType;
-  div.textContent =
-      FileListModel.getFileTypeString(FileType.getType(entry, mimeType));
-
-  // For removable partitions, display file system type.
-  if (!mimeType && entry.volumeInfo && entry.volumeInfo.diskFileSystemType) {
-    div.textContent = entry.volumeInfo.diskFileSystemType;
-  }
-
-  return div;
-};
-
-/**
- * Render the Date column of the detail table.
- *
- * @param {Entry} entry The Entry object to render.
- * @param {string} columnId The id of the column to be rendered.
- * @param {cr.ui.Table} table The table doing the rendering.
- * @return {HTMLDivElement} Created element.
- * @private
- */
-FileTable.prototype.renderDate_ = function(entry, columnId, table) {
-  const div = /** @type {!HTMLDivElement} */
-      (this.ownerDocument.createElement('div'));
-  div.className = 'date';
-
-  this.updateDate_(div, entry);
-  return div;
-};
-
-/**
- * Sets up or updates the date cell.
- *
- * @param {HTMLDivElement} div The table cell.
- * @param {Entry} entry Entry of file to update.
- * @private
- */
-FileTable.prototype.updateDate_ = function(div, entry) {
-  // For now, Team Drive roots have the incorrect modified date value. Hide it
-  // until we get the proper one (see https://crbug.com/861622).
-  if (util.isTeamDriveRoot(entry)) {
-    div.textContent = '--';
-    return;
-  }
-
-  const item = this.metadataModel_.getCache(
-      [entry], ['modificationTime', 'modificationByMeTime'])[0];
-  const modTime = this.useModificationByMeTime_ ?
-      item.modificationByMeTime || item.modificationTime :
-      item.modificationTime;
-
-  div.textContent = this.formatter_.formatModDate(modTime);
-};
-
-/**
- * Updates the file metadata in the table item.
- *
- * @param {Element} item Table item.
- * @param {Entry} entry File entry.
- */
-FileTable.prototype.updateFileMetadata = function(item, entry) {
-  this.updateDate_(
-      /** @type {!HTMLDivElement} */ (item.querySelector('.date')), entry);
-  this.updateSize_(
-      /** @type {!HTMLDivElement} */ (item.querySelector('.size')), entry);
-  this.updateStatus_(
-      /** @type {!HTMLDivElement} */ (item.querySelector('.status')), entry);
-};
-
-/**
- * Updates list items 'in place' on metadata change.
- * @param {string} type Type of metadata change.
- * @param {Array<Entry>} entries Entries to update.
- */
-FileTable.prototype.updateListItemsMetadata = function(type, entries) {
-  const urls = util.entriesToURLs(entries);
-  const forEachCell = (selector, callback) => {
-    const cells = this.querySelectorAll(selector);
-    for (let i = 0; i < cells.length; i++) {
-      const cell = cells[i];
-      const listItem = this.list_.getListItemAncestor(cell);
-      const entry = this.dataModel.item(listItem.listIndex);
-      if (entry && urls.indexOf(entry.toURL()) !== -1) {
-        callback.call(this, cell, entry, listItem);
-      }
-    }
-  };
-  if (type === 'filesystem') {
-    forEachCell('.table-row-cell > .date', function(item, entry, unused) {
-      this.updateDate_(item, entry);
-    });
-    forEachCell('.table-row-cell > .size', function(item, entry, unused) {
-      this.updateSize_(item, entry);
-    });
-  } else if (type === 'external') {
-    // The cell name does not matter as the entire list item is needed.
-    forEachCell('.table-row-cell > .date', function(item, entry, listItem) {
-      filelist.updateListItemExternalProps(
-          listItem,
-          this.metadataModel_.getCache(
-              [entry],
-              [
-                'availableOffline', 'customIconUrl', 'shared', 'isMachineRoot',
-                'isExternalMedia', 'hosted'
-              ])[0],
-          util.isTeamDriveRoot(entry));
-    });
-  } else if (type === 'import-history') {
-    forEachCell('.table-row-cell > .status', function(item, entry, unused) {
-      this.updateStatus_(item, entry);
-    });
-  }
-};
-
-/**
- * Renders table row.
- * @param {function(Entry, cr.ui.Table)} baseRenderFunction Base renderer.
- * @param {Entry} entry Corresponding entry.
- * @return {HTMLLIElement} Created element.
- * @private
- */
-FileTable.prototype.renderTableRow_ = function(baseRenderFunction, entry) {
-  const item = baseRenderFunction(entry, this);
-  const nameId = item.id + '-entry-name';
-  const sizeId = item.id + '-size';
-  const dateId = item.id + '-date';
-  filelist.decorateListItem(item, entry, this.metadataModel_);
-  item.setAttribute('file-name', entry.name);
-  item.querySelector('.entry-name').setAttribute('id', nameId);
-  item.querySelector('.size').setAttribute('id', sizeId);
-  item.querySelector('.date').setAttribute('id', dateId);
-  item.setAttribute('aria-labelledby', nameId + ' ' + sizeId + ' ' + dateId);
-  return item;
-};
-
-/**
- * Renders the file thumbnail in the detail table.
- * @param {Entry} entry The Entry object to render.
- * @return {!HTMLDivElement} Created element.
- * @private
- */
-FileTable.prototype.renderThumbnail_ = function(entry) {
-  const box = /** @type {!HTMLDivElement} */
-      (this.ownerDocument.createElement('div'));
-  box.className = 'detail-thumbnail';
-
-  // Set thumbnail if it's already in cache.
-  const thumbnailData = this.listThumbnailLoader_ ?
-      this.listThumbnailLoader_.getThumbnailFromCache(entry) :
-      null;
-  if (thumbnailData && thumbnailData.dataUrl) {
-    this.setThumbnailImage_(
-        box, this.listThumbnailLoader_.getThumbnailFromCache(entry).dataUrl,
-        false /* without animation */);
-  }
-
-  return box;
-};
-
-/**
- * Sets thumbnail image to the box.
- * @param {!HTMLDivElement} box Detail thumbnail div element.
- * @param {string} dataUrl Data url of thumbnail.
- * @param {boolean} shouldAnimate Whether the thumbnail is shown with animation
- *     or not.
- * @private
- */
-FileTable.prototype.setThumbnailImage_ = (box, dataUrl, shouldAnimate) => {
-  const oldThumbnails = box.querySelectorAll('.thumbnail');
-
-  const thumbnail = box.ownerDocument.createElement('div');
-  thumbnail.classList.add('thumbnail');
-  thumbnail.style.backgroundImage = 'url(' + dataUrl + ')';
-  thumbnail.addEventListener('animationend', () => {
-    // Remove animation css once animation is completed in order not to animate
-    // again when an item is attached to the dom again.
-    thumbnail.classList.remove('animate');
-
-    for (let i = 0; i < oldThumbnails.length; i++) {
-      if (box.contains(oldThumbnails[i])) {
-        box.removeChild(oldThumbnails[i]);
-      }
-    }
-  });
-
-  if (shouldAnimate) {
-    thumbnail.classList.add('animate');
-  }
-
-  box.appendChild(thumbnail);
-};
-
-/**
- * Clears thumbnail image from the box.
- * @param {!HTMLDivElement} box Detail thumbnail div element.
- * @private
- */
-FileTable.prototype.clearThumbnailImage_ = box => {
-  const oldThumbnails = box.querySelectorAll('.thumbnail');
-
-  for (let i = 0; i < oldThumbnails.length; i++) {
-    box.removeChild(oldThumbnails[i]);
-  }
-};
-
-/**
- * Renders the selection checkmark in the detail table.
- * @return {!HTMLDivElement} Created element.
- * @private
- */
-FileTable.prototype.renderCheckmark_ = function() {
-  const checkmark = /** @type {!HTMLDivElement} */
-      (this.ownerDocument.createElement('div'));
-  checkmark.className = 'detail-checkmark';
-  return checkmark;
-};
-
-/**
- * Redraws the UI. Skips multiple consecutive calls.
- */
-FileTable.prototype.relayout = function() {
-  this.relayoutRateLimiter_.run();
-};
-
-/**
- * Redraws the UI immediately.
- * @private
- */
-FileTable.prototype.relayoutImmediately_ = function() {
-  if (this.clientWidth > 0) {
-    this.normalizeColumns();
-  }
-  this.redraw();
-  cr.dispatchSimpleEvent(this.list, 'relayout');
-};
diff --git a/ui/file_manager/file_manager/foreground/js/ui/install_linux_package_dialog.js b/ui/file_manager/file_manager/foreground/js/ui/install_linux_package_dialog.js
index 17b7ab9..9ccdc93c 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/install_linux_package_dialog.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/install_linux_package_dialog.js
@@ -8,181 +8,178 @@
 cr.define('cr.filebrowser', () => {
   /**
    * Creates dialog in DOM tree.
-   *
-   * @param {HTMLElement} parentNode Node to be parent for this dialog.
-   * @constructor
-   * @extends {FileManagerDialogBase}
    */
-  function InstallLinuxPackageDialog(parentNode) {
-    FileManagerDialogBase.call(this, parentNode);
+  class InstallLinuxPackageDialog extends FileManagerDialogBase {
+    /**
+     * @param {HTMLElement} parentNode Node to be parent for this dialog.
+     */
+    constructor(parentNode) {
+      super(parentNode);
 
-    this.frame_.id = 'install-linux-package-dialog';
+      this.frame_.id = 'install-linux-package-dialog';
 
-    this.details_frame_ = this.document_.createElement('div');
-    this.details_frame_.className = 'install-linux-package-details-frame';
-    this.frame_.insertBefore(this.details_frame_, this.buttons);
+      this.details_frame_ = this.document_.createElement('div');
+      this.details_frame_.className = 'install-linux-package-details-frame';
+      this.frame_.insertBefore(this.details_frame_, this.buttons);
 
-    this.details_label_ = this.document_.createElement('div');
-    this.details_label_.className = 'install-linux-package-details-label';
-    this.details_label_.textContent =
-        str('INSTALL_LINUX_PACKAGE_DETAILS_LABEL');
+      this.details_label_ = this.document_.createElement('div');
+      this.details_label_.className = 'install-linux-package-details-label';
+      this.details_label_.textContent =
+          str('INSTALL_LINUX_PACKAGE_DETAILS_LABEL');
 
-    // The OK button normally dismisses the dialog, so add a button we can
-    // customize.
-    this.installButton_ = this.okButton_.cloneNode(false /* deep */);
-    this.installButton_.textContent =
-        str('INSTALL_LINUX_PACKAGE_INSTALL_BUTTON');
-    this.installButton_.addEventListener(
-        'click', this.onInstallClick_.bind(this));
-    this.buttons.insertBefore(this.installButton_, this.okButton_);
-    this.initialFocusElement_ = this.installButton_;
-  }
+      // The OK button normally dismisses the dialog, so add a button we can
+      // customize.
+      this.installButton_ = this.okButton_.cloneNode(false /* deep */);
+      this.installButton_.textContent =
+          str('INSTALL_LINUX_PACKAGE_INSTALL_BUTTON');
+      this.installButton_.addEventListener(
+          'click', this.onInstallClick_.bind(this));
+      this.buttons.insertBefore(this.installButton_, this.okButton_);
+      this.initialFocusElement_ = this.installButton_;
 
-  InstallLinuxPackageDialog.prototype = {
-    __proto__: FileManagerDialogBase.prototype,
-  };
-
-  /**
-   * Shows the dialog.
-   *
-   * @param {!Entry} entry
-   */
-  InstallLinuxPackageDialog.prototype.showInstallLinuxPackageDialog = function(
-      entry) {
-    // We re-use the same object, so reset any visual state that may be changed.
-    this.installButton_.hidden = false;
-    this.okButton_.hidden = true;
-    this.cancelButton_.hidden = false;
-
-    this.entry_ = entry;
-
-    const title = str('INSTALL_LINUX_PACKAGE_TITLE');
-    const message = str('INSTALL_LINUX_PACKAGE_DESCRIPTION');
-    const show = FileManagerDialogBase.prototype.showOkCancelDialog.call(
-        this, title, message, null, null);
-
-    if (!show) {
-      console.error('InstallLinuxPackageDialog can\'t be shown.');
-      return;
+      /** @private {?Entry} */
+      this.entry_ = null;
     }
 
-    chrome.fileManagerPrivate.getLinuxPackageInfo(
-        this.entry_, this.onGetLinuxPackageInfo_.bind(this));
-    this.resetDetailsFrame_(str('INSTALL_LINUX_PACKAGE_DETAILS_LOADING'));
-  };
+    /**
+     * Shows the dialog.
+     *
+     * @param {!Entry} entry
+     */
+    showInstallLinuxPackageDialog(entry) {
+      // We re-use the same object, so reset any visual state that may be
+      // changed.
+      this.installButton_.hidden = false;
+      this.okButton_.hidden = true;
+      this.cancelButton_.hidden = false;
 
-  /**
-   * Resets the state of the details frame to just contain the 'Details' label,
-   * then appends |message| if non-empty.
-   *
-   * @param {string|null} message The (optional) message to display.
-   */
-  InstallLinuxPackageDialog.prototype.resetDetailsFrame_ = function(message) {
-    this.details_frame_.innerHTML = '';
-    this.details_frame_.appendChild(this.details_label_);
-    if (message) {
-      const text = this.document_.createElement('div');
-      text.textContent = message;
-      text.className = 'install-linux-package-detail-value';
-      this.details_frame_.appendChild(text);
-    }
-  };
+      this.entry_ = entry;
 
-  /**
-   * Updates the dialog with the package info.
-   *
-   * @param {(!chrome.fileManagerPrivate.LinuxPackageInfo|undefined)}
-   *     linux_package_info The retrieved package info.
-   */
-  InstallLinuxPackageDialog.prototype.onGetLinuxPackageInfo_ = function(
-      linux_package_info) {
-    if (chrome.runtime.lastError) {
-      this.resetDetailsFrame_(
-          str('INSTALL_LINUX_PACKAGE_DETAILS_NOT_AVAILABLE'));
-      console.error(
-          'Failed to retrieve app info: ' + chrome.runtime.lastError.message);
-      return;
-    }
+      const title = str('INSTALL_LINUX_PACKAGE_TITLE');
+      const message = str('INSTALL_LINUX_PACKAGE_DESCRIPTION');
+      const show = super.showOkCancelDialog(title, message, null, null);
 
-    this.resetDetailsFrame_(null);
-
-    const details = [
-      [
-        str('INSTALL_LINUX_PACKAGE_DETAILS_APPLICATION_LABEL'),
-        linux_package_info.name
-      ],
-      [
-        str('INSTALL_LINUX_PACKAGE_DETAILS_VERSION_LABEL'),
-        linux_package_info.version
-      ],
-    ];
-
-    // Summary and description are almost always set, but handle the case
-    // where they're missing gracefully.
-    let description = linux_package_info.summary;
-    if (linux_package_info.description) {
-      if (description) {
-        description += '\n\n';
+      if (!show) {
+        console.error('InstallLinuxPackageDialog can\'t be shown.');
+        return;
       }
-      description += linux_package_info.description;
-    }
-    if (description) {
-      details.push([
-        str('INSTALL_LINUX_PACKAGE_DETAILS_DESCRIPTION_LABEL'), description
-      ]);
+
+      chrome.fileManagerPrivate.getLinuxPackageInfo(
+          this.entry_, this.onGetLinuxPackageInfo_.bind(this));
+      this.resetDetailsFrame_(str('INSTALL_LINUX_PACKAGE_DETAILS_LOADING'));
     }
 
-    for (const detail of details) {
-      const label = this.document_.createElement('div');
-      label.textContent = detail[0] + ': ';
-      label.className = 'install-linux-package-detail-label';
-      const text = this.document_.createElement('div');
-      text.textContent = detail[1];
-      text.className = 'install-linux-package-detail-value';
-      this.details_frame_.appendChild(label);
-      this.details_frame_.appendChild(text);
-      this.details_frame_.appendChild(this.document_.createElement('br'));
-    }
-  };
-
-  /**
-   * Starts installing the Linux package.
-   */
-  InstallLinuxPackageDialog.prototype.onInstallClick_ = function() {
-    // Add the event listener first to avoid potential races.
-    chrome.fileManagerPrivate.installLinuxPackage(
-        this.entry_, this.onInstallLinuxPackage_.bind(this));
-
-    this.installButton_.hidden = true;
-    this.cancelButton_.hidden = true;
-
-    this.okButton_.hidden = false;
-    this.okButton_.focus();
-  };
-
-  /**
-   * The callback for installLinuxPackage(). Progress updates and completion
-   * for succesfully started installations will be displayed in a notification,
-   * rather than the file manager.
-   * @param {!chrome.fileManagerPrivate.InstallLinuxPackageResponse} response
-   *     Whether the install successfully started or not.
-   * @param {string} failure_reason A textual reason for the 'failed' case.
-   */
-  InstallLinuxPackageDialog.prototype.onInstallLinuxPackage_ = function(
-      response, failure_reason) {
-    if (response == 'started') {
-      this.text_.textContent =
-          str('INSTALL_LINUX_PACKAGE_INSTALLATION_STARTED');
-      return;
+    /**
+     * Resets the state of the details frame to just contain the 'Details'
+     * label, then appends |message| if non-empty.
+     *
+     * @param {string|null} message The (optional) message to display.
+     */
+    resetDetailsFrame_(message) {
+      this.details_frame_.innerHTML = '';
+      this.details_frame_.appendChild(this.details_label_);
+      if (message) {
+        const text = this.document_.createElement('div');
+        text.textContent = message;
+        text.className = 'install-linux-package-detail-value';
+        this.details_frame_.appendChild(text);
+      }
     }
 
-    // Currently we always display a generic error message. Eventually we'll
-    // want a different message for the 'install_already_active' case, and to
-    // surface the provided failure reason if one is provided.
-    this.title_.textContent = str('INSTALL_LINUX_PACKAGE_ERROR_TITLE');
-    this.text_.textContent = str('INSTALL_LINUX_PACKAGE_ERROR_DESCRIPTION');
-    console.error('Failed to begin package installation: ' + failure_reason);
-  };
+    /**
+     * Updates the dialog with the package info.
+     *
+     * @param {(!chrome.fileManagerPrivate.LinuxPackageInfo|undefined)}
+     *     linux_package_info The retrieved package info.
+     */
+    onGetLinuxPackageInfo_(linux_package_info) {
+      if (chrome.runtime.lastError) {
+        this.resetDetailsFrame_(
+            str('INSTALL_LINUX_PACKAGE_DETAILS_NOT_AVAILABLE'));
+        console.error(
+            'Failed to retrieve app info: ' + chrome.runtime.lastError.message);
+        return;
+      }
+
+      this.resetDetailsFrame_(null);
+
+      const details = [
+        [
+          str('INSTALL_LINUX_PACKAGE_DETAILS_APPLICATION_LABEL'),
+          linux_package_info.name
+        ],
+        [
+          str('INSTALL_LINUX_PACKAGE_DETAILS_VERSION_LABEL'),
+          linux_package_info.version
+        ],
+      ];
+
+      // Summary and description are almost always set, but handle the case
+      // where they're missing gracefully.
+      let description = linux_package_info.summary;
+      if (linux_package_info.description) {
+        if (description) {
+          description += '\n\n';
+        }
+        description += linux_package_info.description;
+      }
+      if (description) {
+        details.push([
+          str('INSTALL_LINUX_PACKAGE_DETAILS_DESCRIPTION_LABEL'), description
+        ]);
+      }
+
+      for (const detail of details) {
+        const label = this.document_.createElement('div');
+        label.textContent = detail[0] + ': ';
+        label.className = 'install-linux-package-detail-label';
+        const text = this.document_.createElement('div');
+        text.textContent = detail[1];
+        text.className = 'install-linux-package-detail-value';
+        this.details_frame_.appendChild(label);
+        this.details_frame_.appendChild(text);
+        this.details_frame_.appendChild(this.document_.createElement('br'));
+      }
+    }
+
+    /**
+     * Starts installing the Linux package.
+     */
+    onInstallClick_() {
+      // Add the event listener first to avoid potential races.
+      chrome.fileManagerPrivate.installLinuxPackage(
+          assert(this.entry_), this.onInstallLinuxPackage_.bind(this));
+
+      this.installButton_.hidden = true;
+      this.cancelButton_.hidden = true;
+
+      this.okButton_.hidden = false;
+      this.okButton_.focus();
+    }
+
+    /**
+     * The callback for installLinuxPackage(). Progress updates and completion
+     * for successfully started installations will be displayed in a
+     * notification, rather than the file manager.
+     * @param {!chrome.fileManagerPrivate.InstallLinuxPackageResponse} response
+     *     Whether the install successfully started or not.
+     * @param {string} failure_reason A textual reason for the 'failed' case.
+     */
+    onInstallLinuxPackage_(response, failure_reason) {
+      if (response == 'started') {
+        this.text_.textContent =
+            str('INSTALL_LINUX_PACKAGE_INSTALLATION_STARTED');
+        return;
+      }
+
+      // Currently we always display a generic error message. Eventually we'll
+      // want a different message for the 'install_already_active' case, and to
+      // surface the provided failure reason if one is provided.
+      this.title_.textContent = str('INSTALL_LINUX_PACKAGE_ERROR_TITLE');
+      this.text_.textContent = str('INSTALL_LINUX_PACKAGE_ERROR_DESCRIPTION');
+      console.error('Failed to begin package installation: ' + failure_reason);
+    }
+  }
 
   return {InstallLinuxPackageDialog: InstallLinuxPackageDialog};
 });
diff --git a/ui/file_manager/file_manager/foreground/js/ui/multi_profile_share_dialog.js b/ui/file_manager/file_manager/foreground/js/ui/multi_profile_share_dialog.js
index 39230b43..f091e10 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/multi_profile_share_dialog.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/multi_profile_share_dialog.js
@@ -5,48 +5,79 @@
 /**
  * Dialog to confirm the share between profiles.
  *
- * @param {HTMLElement} parentNode Node to be parent for this dialog.
- * @constructor
- * @extends {FileManagerDialogBase}
  */
-function MultiProfileShareDialog(parentNode) {
-  FileManagerDialogBase.call(this, parentNode);
+class MultiProfileShareDialog extends FileManagerDialogBase {
+  /**
+   * @param {HTMLElement} parentNode Node to be parent for this dialog.
+   */
+  constructor(parentNode) {
+    super(parentNode);
 
-  this.mailLabel_ = parentNode.ownerDocument.createElement('label');
-  this.mailLabel_.className = 'mail-label';
+    this.mailLabel_ = parentNode.ownerDocument.createElement('label');
+    this.mailLabel_.className = 'mail-label';
 
-  const canEdit = parentNode.ownerDocument.createElement('option');
-  canEdit.textContent = str('DRIVE_SHARE_TYPE_CAN_EDIT');
-  canEdit.value = MultiProfileShareDialog.Result.CAN_EDIT;
+    const canEdit = parentNode.ownerDocument.createElement('option');
+    canEdit.textContent = str('DRIVE_SHARE_TYPE_CAN_EDIT');
+    canEdit.value = MultiProfileShareDialog.Result.CAN_EDIT;
 
-  const canComment = parentNode.ownerDocument.createElement('option');
-  canComment.textContent = str('DRIVE_SHARE_TYPE_CAN_COMMENT');
-  canComment.value = MultiProfileShareDialog.Result.CAN_COMMET;
+    const canComment = parentNode.ownerDocument.createElement('option');
+    canComment.textContent = str('DRIVE_SHARE_TYPE_CAN_COMMENT');
+    canComment.value = MultiProfileShareDialog.Result.CAN_COMMET;
 
-  const canView = parentNode.ownerDocument.createElement('option');
-  canView.textContent = str('DRIVE_SHARE_TYPE_CAN_VIEW');
-  canView.value = MultiProfileShareDialog.Result.CAN_VIEW;
+    const canView = parentNode.ownerDocument.createElement('option');
+    canView.textContent = str('DRIVE_SHARE_TYPE_CAN_VIEW');
+    canView.value = MultiProfileShareDialog.Result.CAN_VIEW;
 
-  this.shareTypeSelect_ = parentNode.ownerDocument.createElement('select');
-  this.shareTypeSelect_.setAttribute('size', 1);
-  this.shareTypeSelect_.appendChild(canEdit);
-  this.shareTypeSelect_.appendChild(canComment);
-  this.shareTypeSelect_.appendChild(canView);
+    this.shareTypeSelect_ = parentNode.ownerDocument.createElement('select');
+    this.shareTypeSelect_.setAttribute('size', 1);
+    this.shareTypeSelect_.appendChild(canEdit);
+    this.shareTypeSelect_.appendChild(canComment);
+    this.shareTypeSelect_.appendChild(canView);
 
-  const shareLine = parentNode.ownerDocument.createElement('div');
-  shareLine.className = 'share-line';
-  shareLine.appendChild(this.mailLabel_);
-  shareLine.appendChild(this.shareTypeSelect_);
+    const shareLine = parentNode.ownerDocument.createElement('div');
+    shareLine.className = 'share-line';
+    shareLine.appendChild(this.mailLabel_);
+    shareLine.appendChild(this.shareTypeSelect_);
 
-  this.frame_.insertBefore(shareLine, this.buttons);
-  this.frame_.id = 'multi-profile-share-dialog';
+    this.frame_.insertBefore(shareLine, this.buttons);
+    this.frame_.id = 'multi-profile-share-dialog';
 
-  this.currentProfileId_ = new Promise(callback => {
-    chrome.fileManagerPrivate.getProfiles(
-        (profiles, currentId, displayedId) => {
-          callback(currentId);
-        });
-  });
+    this.currentProfileId_ = new Promise(callback => {
+      chrome.fileManagerPrivate.getProfiles(
+          (profiles, currentId, displayedId) => {
+            callback(currentId);
+          });
+    });
+  }
+
+  /**
+   * Shows the dialog.
+   * @param {boolean} plural Whether to use message of plural or not.
+   * @return {!Promise} Promise fulfilled with the result of dialog. If the
+   *     dialog is already opened, it returns null.
+   */
+  showMultiProfileShareDialog(plural) {
+    return this.currentProfileId_.then(currentProfileId => {
+      return new Promise((fulfill, reject) => {
+        this.shareTypeSelect_.selectedIndex = 0;
+        this.mailLabel_.textContent = currentProfileId;
+        const result = super.showOkCancelDialog(
+            str(plural ? 'MULTI_PROFILE_SHARE_DIALOG_TITLE_PLURAL' :
+                         'MULTI_PROFILE_SHARE_DIALOG_TITLE'),
+            str(plural ? 'MULTI_PROFILE_SHARE_DIALOG_MESSAGE_PLURAL' :
+                         'MULTI_PROFILE_SHARE_DIALOG_MESSAGE'),
+            () => {
+              fulfill(this.shareTypeSelect_.value);
+            },
+            () => {
+              fulfill(MultiProfileShareDialog.Result.CANCEL);
+            });
+        if (!result) {
+          reject(new Error('Another dialog has already shown.'));
+        }
+      });
+    });
+  }
 }
 
 /**
@@ -61,38 +92,3 @@
   CANCEL: 'cancel'
 };
 Object.freeze(MultiProfileShareDialog.Result);
-
-MultiProfileShareDialog.prototype = {
-  __proto__: FileManagerDialogBase.prototype
-};
-
-/**
- * Shows the dialog.
- * @param {boolean} plural Whether to use message of plural or not.
- * @return {!Promise} Promise fulfilled with the result of dialog. If the dialog
- *     is already opened, it returns null.
- */
-MultiProfileShareDialog.prototype.showMultiProfileShareDialog = function(
-    plural) {
-  return this.currentProfileId_.then(currentProfileId => {
-    return new Promise((fulfill, reject) => {
-      this.shareTypeSelect_.selectedIndex = 0;
-      this.mailLabel_.textContent = currentProfileId;
-      const result = FileManagerDialogBase.prototype.showOkCancelDialog.call(
-          this,
-          str(plural ? 'MULTI_PROFILE_SHARE_DIALOG_TITLE_PLURAL' :
-                       'MULTI_PROFILE_SHARE_DIALOG_TITLE'),
-          str(plural ? 'MULTI_PROFILE_SHARE_DIALOG_MESSAGE_PLURAL' :
-                       'MULTI_PROFILE_SHARE_DIALOG_MESSAGE'),
-          () => {
-            fulfill(this.shareTypeSelect_.value);
-          },
-          () => {
-            fulfill(MultiProfileShareDialog.Result.CANCEL);
-          });
-      if (!result) {
-        reject(new Error('Another dialog has already shown.'));
-      }
-    });
-  });
-};
diff --git a/ui/file_manager/file_manager/foreground/js/ui/suggest_apps_dialog.js b/ui/file_manager/file_manager/foreground/js/ui/suggest_apps_dialog.js
index 83b415b..b84349aa 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/suggest_apps_dialog.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/suggest_apps_dialog.js
@@ -11,59 +11,343 @@
 /**
  * Creates dialog in DOM tree.
  *
- * @param {!ProvidersModel} providersModel Model for providers.
- * @param {!HTMLElement} parentNode Node to be parent for this dialog.
- * @param {!SuggestAppDialogState} state Static state of suggest app dialog.
- * @constructor
- * @extends {FileManagerDialogBase}
  */
-function SuggestAppsDialog(providersModel, parentNode, state) {
-  FileManagerDialogBase.call(this, parentNode);
-
+class SuggestAppsDialog extends FileManagerDialogBase {
   /**
-   * @private {!ProvidersModel}
-   * @const
+   * @param {!ProvidersModel} providersModel Model for providers.
+   * @param {!HTMLElement} parentNode Node to be parent for this dialog.
+   * @param {!SuggestAppDialogState} state Static state of suggest app dialog.
    */
-  this.providersModel_ = providersModel;
+  constructor(providersModel, parentNode, state) {
+    super(parentNode);
 
-  this.frame_.id = 'suggest-app-dialog';
+    /**
+     * @private {!ProvidersModel}
+     * @const
+     */
+    this.providersModel_ = providersModel;
+
+    this.frame_.id = 'suggest-app-dialog';
+
+    /**
+     * The root element for the Chrome Web Store widget container.
+     * @const {!HTMLElement}
+     */
+    const widgetRoot = this.document_.createElement('div');
+    this.frame_.insertBefore(widgetRoot, this.text_.nextSibling);
+
+    /**
+     * The wrapper around Chrome Web Store widget.
+     * @const {!CWSWidgetContainer}
+     * @private
+     */
+    this.widget_ = new CWSWidgetContainer(
+        this.document_, widgetRoot, this.createWidgetPlatformDelegate_(),
+        state);
+
+    this.initialFocusElement_ = this.widget_.getInitiallyFocusedElement();
+
+    /**
+     * The reported widget result.
+     * @type {SuggestAppsDialog.Result}
+     * @private
+     */
+    this.result_ = SuggestAppsDialog.Result.FAILED;
+
+    // Hide default dialog buttons.
+    this.buttons.hidden = true;
+
+    // Override default dialog styles.
+    this.title_.classList.add('suggest-apps-dialog-title');
+    this.text_.classList.add('suggest-apps-dialog-text');
+
+    /** @private {?string} */
+    this.installedItemId_ = null;
+
+    /** @private {?function(SuggestAppsDialog.Result, ?string)} */
+    this.onDialogClosed_ = null;
+
+    /** @private {string} */
+    this.dialogText_ = '';
+  }
 
   /**
-   * The root element for the Chrome Web Store widget container.
-   * @const {!HTMLElement}
+   * Dummy function for SuggestAppsDialog.show() not to be called
+   * unintentionally.
    */
-  const widgetRoot = this.document_.createElement('div');
-  this.frame_.insertBefore(widgetRoot, this.text_.nextSibling);
+  show() {
+    console.error('SuggestAppsDialog.show() shouldn\'t be called directly.');
+  }
 
   /**
-   * The wrapper around Chrome Web Store widget.
-   * @const {!CWSWidgetContainer}
+   * Shows suggest-apps dialog by file extension and mime.
+   *
+   * @param {string} extension Extension of the file with a trailing dot.
+   * @param {?string} mime Mime of the file.
+   * @param {function(SuggestAppsDialog.Result, ?string)} onDialogClosed Called
+   *     when the dialog is closed, with a result code and an optionally an
+   *     extension id, if an extension was installed.
+   */
+  showByExtensionAndMime(extension, mime, onDialogClosed) {
+    assert(extension && extension[0] === '.');
+    const options = {file_extension: extension.substr(1)};
+    if (mime) {
+      options.mime_type = mime;
+    }
+    this.showInternal_(
+        options, str('SUGGEST_DIALOG_TITLE'),
+        webStoreUtils.createWebStoreLink(extension, mime), onDialogClosed);
+  }
+
+  /**
+   * Shows suggest-apps dialog for FSP API
+   * @param {function(SuggestAppsDialog.Result, ?string)} onDialogClosed Called
+   *     when the dialog is closed, with a result code and an optionally an
+   *     extension id, if an extension was installed.
+   */
+  showProviders(onDialogClosed) {
+    this.showInternal_(
+        {file_system_provider: true}, str('SUGGEST_DIALOG_FOR_PROVIDERS_TITLE'),
+        null /* webStoreUrl */, onDialogClosed);
+  }
+
+  /**
+   * Creates platform delegate for CWSWidgetContainer.
+   * @return {!CWSWidgetContainerPlatformDelegate}
    * @private
    */
-  this.widget_ = new CWSWidgetContainer(
-      this.document_, widgetRoot, this.createWidgetPlatformDelegate_(), state);
+  createWidgetPlatformDelegate_() {
+    return {
+      strings: {
+        UI_LOCALE: util.getCurrentLocaleOrDefault(),
+        LINK_TO_WEBSTORE: str('SUGGEST_DIALOG_LINK_TO_WEBSTORE'),
+        INSTALLATION_FAILED_MESSAGE: str('SUGGEST_DIALOG_INSTALLATION_FAILED'),
+        LOADING_SPINNER_ALT: str('SUGGEST_DIALOG_LOADING_SPINNER_ALT'),
+        INSTALLING_SPINNER_ALT: str('SUGGEST_DIALOG_INSTALLING_SPINNER_ALT')
+      },
 
-  this.initialFocusElement_ = this.widget_.getInitiallyFocusedElement();
+      metricsImpl: {
+        /**
+         * @param {string} enumName
+         * @param {number} value
+         * @param {number} enumSize
+         */
+        recordEnum: function(enumName, value, enumSize) {
+          metrics.recordEnum('SuggestApps.' + enumName, value, enumSize);
+        },
+
+        /** @param {string} actionName */
+        recordUserAction: function(actionName) {
+          metrics.recordUserAction('SuggestApps.' + actionName);
+        },
+
+        /** @param {string} intervalName */
+        startInterval: function(intervalName) {
+          metrics.startInterval('SuggestApps.' + intervalName);
+        },
+
+        /** @param {string} intervalName */
+        recordInterval: function(intervalName) {
+          metrics.recordInterval('SuggestApps.' + intervalName);
+        }
+      },
+
+      /**
+       * @param {string} itemId,
+       * @param {function(?string)} callback Callback argument is set to error
+       *     message (null on success)
+       */
+      installWebstoreItem: function(itemId, callback) {
+        chrome.webstoreWidgetPrivate.installWebstoreItem(
+            itemId, false /* show installation prompt */, () => {
+              callback(
+                  chrome.runtime.lastError ?
+                      chrome.runtime.lastError.message || 'UNKNOWN ERROR' :
+                      null);
+            });
+      },
+
+      /**
+       * @param {function(?Array<!string>)} callback Callback
+       *     argument is a list of installed item ids (null on error).
+       */
+      getInstalledItems: callback => {
+        // Return only installed provided extensions. Returning other
+        // extensions/apps is redundant, as the suggest app for non-providers is
+        // executed only when there is no extension/app matching a file task.
+        // Hence, none of the suggested extensions/apps can be already
+        // installed.
+        this.providersModel_.getInstalledProviders()
+            .then(providers => {
+              callback(providers.map(provider => {
+                // Assume that the provider is an extension backed provider. In
+                // such case the providerId is the same as extensionId.
+                return provider.providerId;
+              }));
+            })
+            .catch(error => {
+              console.error(error.stack || error);
+              callback(null);
+            });
+      },
+
+      /**
+       * @param {function(?string)} callback Callback argument is the requested
+       *     token (null on error).
+       */
+      requestWebstoreAccessToken: function(callback) {
+        chrome.fileManagerPrivate.requestWebStoreAccessToken(token => {
+          if (chrome.runtime.lastError) {
+            console.error(chrome.runtime.lastError.message);
+            callback(null);
+            return;
+          }
+          callback(assert(token));
+        });
+      }
+    };
+  }
 
   /**
-   * The reported widget result.
-   * @type {SuggestAppsDialog.Result}
+   * Internal method to show a dialog. This should be called only from 'Suggest.
+   * appDialog.showXxxx()' functions.
+   *
+   * @param {!Object<*>} options Map of options for the dialog.
+   * @param {string} title Title of the dialog.
+   * @param {?string} webStoreUrl Url for more results. Null if not supported.
+   * @param {function(SuggestAppsDialog.Result, ?string)} onDialogClosed Called
+   *     when the dialog is closed, with a result code and an optionally an
+   *     extension id, if an extension was installed.
    * @private
    */
-  this.result_ = SuggestAppsDialog.Result.FAILED;
+  showInternal_(options, title, webStoreUrl, onDialogClosed) {
+    this.text_.hidden = true;
+    this.dialogText_ = '';
 
-  // Hide default dialog buttons.
-  this.buttons.hidden = true;
+    if (!this.widget_.isInInitialState()) {
+      onDialogClosed(SuggestAppsDialog.Result.CANCELLED, null);
+      return;
+    }
 
-  // Override default dialog styles.
-  this.title_.classList.add('suggest-apps-dialog-title');
-  this.text_.classList.add('suggest-apps-dialog-text');
+    let dialogShown = false;
+    let tokenObtained = false;
+
+    this.widget_.ready()
+        .then(/** @return {!Promise} */
+              () => {
+                tokenObtained = true;
+                return this.showDialog_(title);
+              })
+        .then(/** @return {!Promise<CWSWidgetContainer.ResolveReason>} */
+              () => {
+                dialogShown = true;
+                // This is not set before so it doesn't pollute state if the
+                // previous dialog hasn't finished hiding.
+                this.onDialogClosed_ = onDialogClosed;
+                return this.widget_.start(options, webStoreUrl);
+              })
+        .then(/** @param {CWSWidgetContainer.ResolveReason} reason */
+              reason => {
+                if (reason !== CWSWidgetContainer.ResolveReason.RESET) {
+                  this.hide();
+                }
+              })
+        .catch(error => {
+          console.error('Failed to start CWS widget: ' + error);
+
+          if (!dialogShown) {
+            // Reset any widget state set in |this.widget_.ready()|. The
+            // returned value is ignored because it doesn't influence the
+            // value reported by dialog.
+            this.widget_.finalizeAndGetResult();
+
+            const result = tokenObtained ?
+                // Got access token but the widget dialog was not shown.
+                // Consider the widget was cancelled.
+                SuggestAppsDialog.Result.CANCELLED :
+                // Access token was unavailable.
+                // This can happen in the Guest mode. crbug.com/694419
+                // Callback shows an alert notifying the file was not opened
+                // because of the unsupported type.
+                SuggestAppsDialog.Result.FAILED;
+            onDialogClosed(result, null);
+            return;
+          }
+
+          this.result_ = SuggestAppsDialog.Result.FAILED;
+          this.hide();
+        });
+  }
+
+  /**
+   * Internal method for showing the dialog in the file manager window.
+   * @param {string} title The dialog title.
+   * @return {!Promise}
+   */
+  showDialog_(title) {
+    return new Promise((resolve, reject) => {
+      const success = this.dialogText_ ?
+          super.showTitleAndTextDialog(title, this.dialogText_) :
+          super.showTitleOnlyDialog(title);
+      if (!success) {
+        reject('SuggestAppsDialog cannot be shown.');
+        return;
+      }
+      resolve();
+    });
+  }
+
+  /**
+   * Called when the connection status is changed.
+   * @param {VolumeManagerCommon.DriveConnectionType} connectionType Current
+   *     connection type.
+   */
+  onDriveConnectionChanged(connectionType) {
+    if (connectionType === VolumeManagerCommon.DriveConnectionType.OFFLINE) {
+      this.widget_.onConnectionLost();
+    }
+  }
+
+  /**
+   * @param {Function=} opt_originalOnHide Called when the original dialog is
+   *     hidden.
+   * @override
+   */
+  hide(opt_originalOnHide) {
+    const widgetResult = this.widget_.finalizeAndGetResult();
+
+    switch (widgetResult.result) {
+      case CWSWidgetContainer.Result.INSTALL_SUCCESSFUL:
+        this.result_ = SuggestAppsDialog.Result.SUCCESS;
+        break;
+      case CWSWidgetContainer.Result.WEBSTORE_LINK_OPENED:
+      case CWSWidgetContainer.Result.USER_CANCEL:
+        this.result_ = SuggestAppsDialog.Result.CANCELLED;
+        break;
+      default:
+        this.result_ = SuggestAppsDialog.Result.FAILED;
+    }
+
+    this.installedItemId_ = widgetResult.installedItemId;
+
+    super.hide(this.onHide_.bind(this, opt_originalOnHide));
+  }
+
+  /**
+   * @param {Function=} opt_originalOnHide Original onHide function passed to
+   *     SuggestAppsDialog.hide().
+   * @private
+   */
+  onHide_(opt_originalOnHide) {
+    // Calls the callback after the dialog hides.
+    if (opt_originalOnHide) {
+      opt_originalOnHide();
+    }
+
+    this.onDialogClosed_(this.result_, this.installedItemId_);
+  }
 }
 
-SuggestAppsDialog.prototype = {
-  __proto__: FileManagerDialogBase.prototype
-};
-
 /**
  * @enum {string}
  */
@@ -76,282 +360,3 @@
   FAILED: 'SuggestAppsDialog.Result.FAILED'
 };
 Object.freeze(SuggestAppsDialog.Result);
-
-/**
- * Dummy function for SuggestAppsDialog.show() not to be called unintentionally.
- */
-SuggestAppsDialog.prototype.show = () => {
-  console.error('SuggestAppsDialog.show() shouldn\'t be called directly.');
-};
-
-/**
- * Shows suggest-apps dialog by file extension and mime.
- *
- * @param {string} extension Extension of the file with a trailing dot.
- * @param {?string} mime Mime of the file.
- * @param {function(SuggestAppsDialog.Result, ?string)} onDialogClosed Called
- *     when the dialog is closed, with a result code and an optionally an
- *     extension id, if an extension was installed.
- */
-SuggestAppsDialog.prototype.showByExtensionAndMime = function(
-    extension, mime, onDialogClosed) {
-  assert(extension && extension[0] === '.');
-  const options = {file_extension: extension.substr(1)};
-  if (mime) {
-    options.mime_type = mime;
-  }
-  this.showInternal_(
-      options, str('SUGGEST_DIALOG_TITLE'),
-      webStoreUtils.createWebStoreLink(extension, mime), onDialogClosed);
-};
-
-/**
- * Shows suggest-apps dialog for FSP API
- * @param {function(SuggestAppsDialog.Result, ?string)} onDialogClosed Called
- *     when the dialog is closed, with a result code and an optionally an
- *     extension id, if an extension was installed.
- */
-SuggestAppsDialog.prototype.showProviders = function(onDialogClosed) {
-  this.showInternal_(
-      {file_system_provider: true}, str('SUGGEST_DIALOG_FOR_PROVIDERS_TITLE'),
-      null /* webStoreUrl */, onDialogClosed);
-};
-
-/**
- * Creates platform delegate for CWSWidgetContainer.
- * @return {!CWSWidgetContainerPlatformDelegate}
- * @private
- */
-SuggestAppsDialog.prototype.createWidgetPlatformDelegate_ = function() {
-  return {
-    strings: {
-      UI_LOCALE: util.getCurrentLocaleOrDefault(),
-      LINK_TO_WEBSTORE: str('SUGGEST_DIALOG_LINK_TO_WEBSTORE'),
-      INSTALLATION_FAILED_MESSAGE: str('SUGGEST_DIALOG_INSTALLATION_FAILED'),
-      LOADING_SPINNER_ALT: str('SUGGEST_DIALOG_LOADING_SPINNER_ALT'),
-      INSTALLING_SPINNER_ALT: str('SUGGEST_DIALOG_INSTALLING_SPINNER_ALT')
-    },
-
-    metricsImpl: {
-      /**
-       * @param {string} enumName
-       * @param {number} value
-       * @param {number} enumSize
-       */
-      recordEnum: function(enumName, value, enumSize) {
-        metrics.recordEnum('SuggestApps.' + enumName, value, enumSize);
-      },
-
-      /** @param {string} actionName */
-      recordUserAction: function(actionName) {
-        metrics.recordUserAction('SuggestApps.' + actionName);
-      },
-
-      /** @param {string} intervalName */
-      startInterval: function(intervalName) {
-        metrics.startInterval('SuggestApps.' + intervalName);
-      },
-
-      /** @param {string} intervalName */
-      recordInterval: function(intervalName) {
-        metrics.recordInterval('SuggestApps.' + intervalName);
-      }
-    },
-
-    /**
-     * @param {string} itemId,
-     * @param {function(?string)} callback Callback argument is set to error
-     *     message (null on success)
-     */
-    installWebstoreItem: function(itemId, callback) {
-      chrome.webstoreWidgetPrivate.installWebstoreItem(
-          itemId, false /* show installation prompt */, () => {
-            callback(
-                chrome.runtime.lastError ?
-                    chrome.runtime.lastError.message || 'UNKNOWN ERROR' :
-                    null);
-          });
-    },
-
-    /**
-     * @param {function(?Array<!string>)} callback Callback
-     *     argument is a list of installed item ids (null on error).
-     */
-    getInstalledItems: callback => {
-      // Return only installed provided extensions. Returning other
-      // extensions/apps is redundant, as the suggest app for non-providers is
-      // executed only when there is no extension/app matching a file task.
-      // Hence, none of the suggested extensions/apps can be already installed.
-      this.providersModel_.getInstalledProviders()
-          .then(providers => {
-            callback(providers.map(provider => {
-              // Assume that the provider is an extension backed provider. In
-              // such case the providerId is the same as extensionId.
-              return provider.providerId;
-            }));
-          })
-          .catch(error => {
-            console.error(error.stack || error);
-            callback(null);
-          });
-    },
-
-    /**
-     * @param {function(?string)} callback Callback argument is the requested
-     *     token (null on error).
-     */
-    requestWebstoreAccessToken: function(callback) {
-      chrome.fileManagerPrivate.requestWebStoreAccessToken(token => {
-        if (chrome.runtime.lastError) {
-          console.error(chrome.runtime.lastError.message);
-          callback(null);
-          return;
-        }
-        callback(assert(token));
-      });
-    }
-  };
-};
-
-/**
- * Internal method to show a dialog. This should be called only from 'Suggest.
- * appDialog.showXxxx()' functions.
- *
- * @param {!Object<*>} options Map of options for the dialog.
- * @param {string} title Title of the dialog.
- * @param {?string} webStoreUrl Url for more results. Null if not supported.
- * @param {function(SuggestAppsDialog.Result, ?string)} onDialogClosed Called
- *     when the dialog is closed, with a result code and an optionally an
- *     extension id, if an extension was installed.
- * @private
- */
-SuggestAppsDialog.prototype.showInternal_ = function(
-    options, title, webStoreUrl, onDialogClosed) {
-  this.text_.hidden = true;
-  this.dialogText_ = '';
-
-  if (!this.widget_.isInInitialState()) {
-    onDialogClosed(SuggestAppsDialog.Result.CANCELLED, null);
-    return;
-  }
-
-  let dialogShown = false;
-  let tokenObtained = false;
-
-  this.widget_.ready()
-      .then(/** @return {!Promise} */
-            () => {
-              tokenObtained = true;
-              return this.showDialog_(title);
-            })
-      .then(/** @return {!Promise<CWSWidgetContainer.ResolveReason>} */
-            () => {
-              dialogShown = true;
-              // This is not set before so it doesn't pollute state if the
-              // previous dialog hasn't finished hiding.
-              this.onDialogClosed_ = onDialogClosed;
-              return this.widget_.start(options, webStoreUrl);
-            })
-      .then(/** @param {CWSWidgetContainer.ResolveReason} reason */
-            reason => {
-              if (reason !== CWSWidgetContainer.ResolveReason.RESET) {
-                this.hide();
-              }
-            })
-      .catch(error => {
-        console.error('Failed to start CWS widget: ' + error);
-
-        if (!dialogShown) {
-          // Reset any widget state set in |this.widget_.ready()|. The
-          // returned value is ignored because it doesn't influence the
-          // value reported by dialog.
-          this.widget_.finalizeAndGetResult();
-
-          const result = tokenObtained ?
-              // Got access token but the widget dialog was not shown.
-              // Consider the widget was cancelled.
-              SuggestAppsDialog.Result.CANCELLED :
-              // Access token was unavailable.
-              // This can happen in the Guest mode. crbug.com/694419
-              // Callback shows an alert notifying the file was not opened
-              // because of the unsupported type.
-              SuggestAppsDialog.Result.FAILED;
-          onDialogClosed(result, null);
-          return;
-        }
-
-        this.result_ = SuggestAppsDialog.Result.FAILED;
-        this.hide();
-      });
-};
-
-/**
- * Internal method for showing the dialog in the file manager window.
- * @param {string} title The dialog title.
- * @return {!Promise}
- */
-SuggestAppsDialog.prototype.showDialog_ = function(title) {
-  return new Promise((resolve, reject) => {
-    const success = this.dialogText_ ?
-        FileManagerDialogBase.prototype.showTitleAndTextDialog.call(
-            this, title, this.dialogText_) :
-        FileManagerDialogBase.prototype.showTitleOnlyDialog.call(this, title);
-    if (!success) {
-      reject('SuggestAppsDialog cannot be shown.');
-      return;
-    }
-    resolve();
-  });
-};
-
-/**
- * Called when the connection status is changed.
- * @param {VolumeManagerCommon.DriveConnectionType} connectionType Current
- *     connection type.
- */
-SuggestAppsDialog.prototype.onDriveConnectionChanged = function(
-    connectionType) {
-  if (connectionType === VolumeManagerCommon.DriveConnectionType.OFFLINE) {
-    this.widget_.onConnectionLost();
-  }
-};
-
-/**
- * @param {Function=} opt_originalOnHide Called when the original dialog is
- *     hidden.
- * @override
- */
-SuggestAppsDialog.prototype.hide = function(opt_originalOnHide) {
-  const widgetResult = this.widget_.finalizeAndGetResult();
-
-  switch (widgetResult.result) {
-    case CWSWidgetContainer.Result.INSTALL_SUCCESSFUL:
-      this.result_ = SuggestAppsDialog.Result.SUCCESS;
-      break;
-    case CWSWidgetContainer.Result.WEBSTORE_LINK_OPENED:
-    case CWSWidgetContainer.Result.USER_CANCEL:
-      this.result_ = SuggestAppsDialog.Result.CANCELLED;
-      break;
-    default:
-      this.result_ = SuggestAppsDialog.Result.FAILED;
-  }
-
-  this.installedItemId_ = widgetResult.installedItemId;
-
-  FileManagerDialogBase.prototype.hide.call(
-      this, this.onHide_.bind(this, opt_originalOnHide));
-};
-
-/**
- * @param {Function=} opt_originalOnHide Original onHide function passed to
- *     SuggestAppsDialog.hide().
- * @private
- */
-SuggestAppsDialog.prototype.onHide_ = function(opt_originalOnHide) {
-  // Calls the callback after the dialog hides.
-  if (opt_originalOnHide) {
-    opt_originalOnHide();
-  }
-
-  this.onDialogClosed_(this.result_, this.installedItemId_);
-};
diff --git a/ui/ozone/platform/drm/host/host_cursor_proxy.cc b/ui/ozone/platform/drm/host/host_cursor_proxy.cc
index 766980d..d2950b9 100644
--- a/ui/ozone/platform/drm/host/host_cursor_proxy.cc
+++ b/ui/ozone/platform/drm/host/host_cursor_proxy.cc
@@ -7,7 +7,6 @@
 #include <utility>
 
 #include "services/service_manager/public/cpp/connector.h"
-#include "services/ws/public/mojom/constants.mojom.h"
 #include "ui/ozone/public/gpu_platform_support_host.h"
 
 namespace ui {
diff --git a/ui/ozone/platform/drm/host/host_drm_device.cc b/ui/ozone/platform/drm/host/host_drm_device.cc
index 4ff514f..e4bcc07 100644
--- a/ui/ozone/platform/drm/host/host_drm_device.cc
+++ b/ui/ozone/platform/drm/host/host_drm_device.cc
@@ -13,7 +13,6 @@
 #include "base/task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "services/service_manager/public/cpp/connector.h"
-#include "services/ws/public/mojom/constants.mojom.h"
 #include "ui/display/types/display_snapshot.h"
 #include "ui/ozone/platform/drm/common/drm_util.h"
 #include "ui/ozone/platform/drm/host/drm_device_connector.h"
diff --git a/ui/views/controls/textfield/textfield.cc b/ui/views/controls/textfield/textfield.cc
index 1347bbf..d2863a2 100644
--- a/ui/views/controls/textfield/textfield.cc
+++ b/ui/views/controls/textfield/textfield.cc
@@ -1758,8 +1758,12 @@
 bool Textfield::SetCompositionFromExistingText(
     const gfx::Range& range,
     const std::vector<ui::ImeTextSpan>& ui_ime_text_spans) {
-  NOTIMPLEMENTED_LOG_ONCE();
-  return false;
+  // TODO(https://crbug.com/952355): Support custom text spans.
+  DCHECK(!model_->HasCompositionText());
+  model_->SetCompositionFromExistingText(range);
+  SchedulePaint();
+  OnAfterUserAction();
+  return true;
 }
 #endif
 
diff --git a/ui/views/controls/textfield/textfield_model.cc b/ui/views/controls/textfield/textfield_model.cc
index 4c3328e4..f65c66fa 100644
--- a/ui/views/controls/textfield/textfield_model.cc
+++ b/ui/views/controls/textfield/textfield_model.cc
@@ -669,6 +669,12 @@
   }
 }
 
+void TextfieldModel::SetCompositionFromExistingText(const gfx::Range& range) {
+  DCHECK(!HasCompositionText());
+  composition_range_ = range;
+  render_text_->SetCompositionRange(range);
+}
+
 void TextfieldModel::ConfirmCompositionText() {
   DCHECK(HasCompositionText());
   base::string16 composition = text().substr(
diff --git a/ui/views/controls/textfield/textfield_model.h b/ui/views/controls/textfield/textfield_model.h
index 19d434d5..c2391a35 100644
--- a/ui/views/controls/textfield/textfield_model.h
+++ b/ui/views/controls/textfield/textfield_model.h
@@ -219,6 +219,10 @@
   // composition text.
   void SetCompositionText(const ui::CompositionText& composition);
 
+  // Puts the text in the specified range into composition mode.
+  // This method should not be called with composition text or an invalid range.
+  void SetCompositionFromExistingText(const gfx::Range& range);
+
   // Converts current composition text into final content.
   void ConfirmCompositionText();
 
diff --git a/ui/views/controls/textfield/textfield_model_unittest.cc b/ui/views/controls/textfield/textfield_model_unittest.cc
index 99a863739..27a7566 100644
--- a/ui/views/controls/textfield/textfield_model_unittest.cc
+++ b/ui/views/controls/textfield/textfield_model_unittest.cc
@@ -1950,4 +1950,17 @@
   EXPECT_STR_EQ("aad", model.text());
 }
 
+TEST_F(TextfieldModelTest, SetCompositionFromExistingText) {
+  TextfieldModel model(nullptr);
+  model.SetText(base::ASCIIToUTF16("abcde"));
+
+  model.SetCompositionFromExistingText(gfx::Range(1, 3));
+  EXPECT_TRUE(model.HasCompositionText());
+
+  ui::CompositionText composition;
+  composition.text = base::ASCIIToUTF16("123");
+  model.SetCompositionText(composition);
+  EXPECT_STR_EQ("a123de", model.text());
+}
+
 }  // namespace views
diff --git a/ui/webui/resources/cr_components/chromeos/BUILD.gn b/ui/webui/resources/cr_components/chromeos/BUILD.gn
index 2ec0ddce..895487a 100644
--- a/ui/webui/resources/cr_components/chromeos/BUILD.gn
+++ b/ui/webui/resources/cr_components/chromeos/BUILD.gn
@@ -13,7 +13,6 @@
     "multidevice_setup:closure_compile",
     "network:closure_compile",
     "quick_unlock:closure_compile",
-    "smb_shares:closure_compile",
   ]
 }
 
diff --git a/ui/webui/resources/js/cr/ui/dialogs.js b/ui/webui/resources/js/cr/ui/dialogs.js
index c404399..59844c2b 100644
--- a/ui/webui/resources/js/cr/ui/dialogs.js
+++ b/ui/webui/resources/js/cr/ui/dialogs.js
@@ -44,6 +44,9 @@
     /** @protected {?Element} */
     this.cancelButton_ = null;
 
+    /** @protected {?Element} */
+    this.buttons = null;
+
     this.initDom_();
   }