diff --git a/DEPS b/DEPS
index faf5563..cc76a3f 100644
--- a/DEPS
+++ b/DEPS
@@ -105,11 +105,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': '64b0fb59951790b29131f299461a784f60640c70',
+  'skia_revision': '776a411b526770401ed5e72a6c4129e06b76b530',
   # 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': 'c12805f8d5c043bc83b9cb16de020c3f94fe5f27',
+  'v8_revision': '8f8f600c2f8d33acbba473f33baf9a2310a41193',
   # 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.
@@ -584,7 +584,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '71ddeb9859d4337575bcf07520d13af05e7425dc',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '4aefea51330c8ae9009ae8da736888ee8cc88695',
       'condition': 'checkout_linux',
   },
 
@@ -833,7 +833,7 @@
     Var('chromium_git') + '/external/github.com/google/libprotobuf-mutator.git' + '@' +  Var('libprotobuf-mutator'),
 
   'src/third_party/libsrtp':
-    Var('chromium_git') + '/chromium/deps/libsrtp.git' + '@' + '368abd6bb3091df2b354250818714f72f0692ca5',
+    Var('chromium_git') + '/chromium/deps/libsrtp.git' + '@' + '650611720ecc23e0e6b32b0e3100f8b4df91696c',
 
   # Android Explicit Synchronization.
   'src/third_party/libsync/src': {
@@ -1098,7 +1098,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '7ca87fb1d3da3b3d2060886e8c58e726d74c8219',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'bdc115c23d219bf91c2eb90bbf215a27114e7d22',
+    Var('webrtc_git') + '/src.git' + '@' + 'af6c139eb6ff1f1b8f4565765a94bdc50f9bce90',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
diff --git a/android_webview/browser/aw_web_contents_delegate.cc b/android_webview/browser/aw_web_contents_delegate.cc
index cb84f9c..01492f2 100644
--- a/android_webview/browser/aw_web_contents_delegate.cc
+++ b/android_webview/browser/aw_web_contents_delegate.cc
@@ -26,7 +26,6 @@
 #include "content/public/browser/render_widget_host.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/file_chooser_file_info.h"
-#include "content/public/common/file_chooser_params.h"
 #include "content/public/common/media_stream_request.h"
 #include "jni/AwWebContentsDelegate_jni.h"
 #include "net/base/escape.h"
@@ -36,7 +35,7 @@
 using base::android::ConvertUTF8ToJavaString;
 using base::android::JavaParamRef;
 using base::android::ScopedJavaLocalRef;
-using content::FileChooserParams;
+using blink::mojom::FileChooserParams;
 using content::WebContents;
 
 namespace android_webview {
@@ -95,18 +94,18 @@
     return;
 
   int mode_flags = 0;
-  if (params.mode == FileChooserParams::OpenMultiple) {
+  if (params.mode == FileChooserParams::Mode::kOpenMultiple) {
     mode_flags |= kFileChooserModeOpenMultiple;
-  } else if (params.mode == FileChooserParams::UploadFolder) {
+  } else if (params.mode == FileChooserParams::Mode::kUploadFolder) {
     // Folder implies multiple in Chrome.
     mode_flags |= kFileChooserModeOpenMultiple | kFileChooserModeOpenFolder;
-  } else if (params.mode == FileChooserParams::Save) {
+  } else if (params.mode == FileChooserParams::Mode::kSave) {
     // Save not supported, so cancel it.
     render_frame_host->FilesSelectedInChooser(
         std::vector<content::FileChooserFileInfo>(), params.mode);
     return;
   } else {
-    DCHECK_EQ(FileChooserParams::Open, params.mode);
+    DCHECK_EQ(FileChooserParams::Mode::kOpen, params.mode);
   }
   Java_AwWebContentsDelegate_runFileChooser(
       env, java_delegate, render_frame_host->GetProcess()->GetID(),
@@ -118,7 +117,7 @@
       params.default_file_name.empty()
           ? nullptr
           : ConvertUTF8ToJavaString(env, params.default_file_name.value()),
-      params.capture);
+      params.use_media_capture);
 }
 
 void AwWebContentsDelegate::AddNewContents(
@@ -308,11 +307,11 @@
   }
   FileChooserParams::Mode mode;
   if (mode_flags & kFileChooserModeOpenFolder) {
-    mode = FileChooserParams::UploadFolder;
+    mode = FileChooserParams::Mode::kUploadFolder;
   } else if (mode_flags & kFileChooserModeOpenMultiple) {
-    mode = FileChooserParams::OpenMultiple;
+    mode = FileChooserParams::Mode::kOpenMultiple;
   } else {
-    mode = FileChooserParams::Open;
+    mode = FileChooserParams::Mode::kOpen;
   }
   DVLOG(0) << "File Chooser result: mode = " << mode
            << ", file paths = " << base::JoinString(file_path_str, ":");
diff --git a/android_webview/browser/aw_web_contents_delegate.h b/android_webview/browser/aw_web_contents_delegate.h
index e78cab1..aff779e 100644
--- a/android_webview/browser/aw_web_contents_delegate.h
+++ b/android_webview/browser/aw_web_contents_delegate.h
@@ -29,7 +29,7 @@
                    const std::string& request_method,
                    const base::Callback<void(bool)>& callback) override;
   void RunFileChooser(content::RenderFrameHost* render_frame_host,
-                      const content::FileChooserParams& params) override;
+                      const blink::mojom::FileChooserParams& params) override;
   void AddNewContents(content::WebContents* source,
                       std::unique_ptr<content::WebContents> new_contents,
                       WindowOpenDisposition disposition,
diff --git a/ash/accelerators/accelerator_controller.cc b/ash/accelerators/accelerator_controller.cc
index 41f7a26..6754877 100644
--- a/ash/accelerators/accelerator_controller.cc
+++ b/ash/accelerators/accelerator_controller.cc
@@ -1034,10 +1034,9 @@
 ////////////////////////////////////////////////////////////////////////////////
 // AcceleratorController, public:
 
-AcceleratorController::AcceleratorController(
-    ui::AcceleratorManagerDelegate* manager_delegate)
-    : accelerator_manager_(new ui::AcceleratorManager(manager_delegate)),
-      accelerator_history_(new ui::AcceleratorHistory) {
+AcceleratorController::AcceleratorController()
+    : accelerator_manager_(std::make_unique<ui::AcceleratorManager>()),
+      accelerator_history_(std::make_unique<ui::AcceleratorHistory>()) {
   Init();
 }
 
diff --git a/ash/accelerators/accelerator_controller.h b/ash/accelerators/accelerator_controller.h
index b09dfda..23e6819 100644
--- a/ash/accelerators/accelerator_controller.h
+++ b/ash/accelerators/accelerator_controller.h
@@ -26,7 +26,6 @@
 
 namespace ui {
 class AcceleratorManager;
-class AcceleratorManagerDelegate;
 }
 
 namespace ash {
@@ -45,9 +44,7 @@
 class ASH_EXPORT AcceleratorController : public ui::AcceleratorTarget,
                                          public mojom::AcceleratorController {
  public:
-  // TODO(jamescook): Remove |manager_delegate|. https://crbug.com/842365
-  explicit AcceleratorController(
-      ui::AcceleratorManagerDelegate* manager_delegate);
+  AcceleratorController();
   ~AcceleratorController() override;
 
   // A list of possible ways in which an accelerator should be restricted before
diff --git a/ash/app_list/app_list_controller_impl.cc b/ash/app_list/app_list_controller_impl.cc
index b723593..f552a7f 100644
--- a/ash/app_list/app_list_controller_impl.cc
+++ b/ash/app_list/app_list_controller_impl.cc
@@ -406,8 +406,14 @@
 
 void AppListControllerImpl::OnAppListItemWillBeDeleted(
     app_list::AppListItem* item) {
-  if (client_ && item->is_folder())
+  if (!client_)
+    return;
+
+  if (item->is_folder())
     client_->OnFolderDeleted(item->CloneMetadata());
+
+  if (item->is_page_break())
+    client_->OnPageBreakItemDeleted(item->id());
 }
 
 void AppListControllerImpl::OnAppListItemUpdated(app_list::AppListItem* item) {
diff --git a/ash/app_list/model/app_list_item_list.cc b/ash/app_list/model/app_list_item_list.cc
index a50a0f5..8eb28f6 100644
--- a/ash/app_list/model/app_list_item_list.cc
+++ b/ash/app_list/model/app_list_item_list.cc
@@ -33,6 +33,8 @@
   return nullptr;
 }
 
+// TODO(https://crbug.com/883971): Make it return iterator to avoid unnecessary
+// check in this code.
 bool AppListItemList::FindItemIndex(const std::string& id, size_t* index) {
   for (size_t i = 0; i < app_list_items_.size(); ++i) {
     if (app_list_items_[i]->id() == id) {
diff --git a/ash/app_list/paged_view_structure.cc b/ash/app_list/paged_view_structure.cc
index 2d3ebd1..8ffd954 100644
--- a/ash/app_list/paged_view_structure.cc
+++ b/ash/app_list/paged_view_structure.cc
@@ -58,20 +58,45 @@
 void PagedViewStructure::SaveToMetadata() {
   auto* item_list = apps_grid_view_->item_list_;
   size_t item_index = 0;
+  AppListModel* model = apps_grid_view_->model_;
 
   for (const auto& page : pages_) {
-    // Skip all "page break" items before current page and after previous page.
+    // Skip all "page break" items before current page.
     while (item_index < item_list->item_count() &&
            item_list->item_at(item_index)->is_page_break()) {
       ++item_index;
     }
-    item_index += page.size();
+
+    // A "page break" item may exist between two app items in a full page after
+    // moving an item to the page from another page or folder. The last item in
+    // the page was pushed to the next page by ClearOverflow() while the "page
+    // break" item behind still exist. In this case, we need to remove the "page
+    // break" item.
+    for (size_t i = 0;
+         i < page.size() && item_index < item_list->item_count();) {
+      const auto* item = item_list->item_at(item_index);
+      if (item->is_page_break()) {
+        // Remove AppListItemListObserver temporarily to avoid |pages_| being
+        // reloaded.
+        item_list->RemoveObserver(apps_grid_view_);
+
+        // Do not increase |item_index| after this call because it modifies
+        // |item_list|.
+        model->DeleteItem(item->id());
+        item_list->AddObserver(apps_grid_view_);
+        continue;
+      }
+
+      DCHECK_EQ(item, page[i]->item());
+      ++i;
+      ++item_index;
+    }
+
     if (item_index < item_list->item_count() &&
         !item_list->item_at(item_index)->is_page_break()) {
       // There's no "page break" item at the end of current page, so add one to
       // push overflowing items to next page.
-      apps_grid_view_->model_->AddPageBreakItemAfter(
-          item_list->item_at(item_index - 1));
+      model->AddPageBreakItemAfter(item_list->item_at(item_index - 1));
     }
   }
 
@@ -81,61 +106,17 @@
   // operation to AppListSyncableService which has complete item list.
 }
 
-bool PagedViewStructure::Sanitize() {
-  bool changed = false;
-  std::vector<AppListItemView*> overflow_views;
-  auto iter = pages_.begin();
-  while (iter != pages_.end() || !overflow_views.empty()) {
-    if (iter == pages_.end()) {
-      // Add additional page if overflowing item views remain.
-      pages_.emplace_back();
-      iter = pages_.end() - 1;
-      changed = true;
-    }
-
-    const size_t max_item_views =
-        apps_grid_view_->TilesPerPage(static_cast<int>(iter - pages_.begin()));
-    auto& page = *iter;
-
-    if (!overflow_views.empty()) {
-      // Put overflowing item views in current page.
-      page.insert(page.begin(), overflow_views.begin(), overflow_views.end());
-      overflow_views.clear();
-      changed = true;
-    }
-
-    if (page.empty()) {
-      // Remove empty page.
-      iter = pages_.erase(iter);
-      changed = true;
-      continue;
-    }
-
-    if (page.size() > max_item_views) {
-      // Remove overflowing item views from current page.
-      overflow_views.insert(overflow_views.begin(),
-                            page.begin() + max_item_views, page.end());
-      page.erase(page.begin() + max_item_views, page.end());
-      changed = true;
-    }
-
-    ++iter;
-  }
-  return changed;
-}
-
 void PagedViewStructure::Move(AppListItemView* view,
-                              const GridIndex& target_index) {
-  RemoveWithoutSanitize(view);
-  Add(view, target_index);
+                              const GridIndex& target_index,
+                              bool clear_overflow,
+                              bool clear_empty_pages) {
+  Remove(view, false /* clear_overflow */, false /* clear_empty_pages */);
+  Add(view, target_index, clear_empty_pages, clear_empty_pages);
 }
 
-void PagedViewStructure::Remove(AppListItemView* view) {
-  RemoveWithoutSanitize(view);
-  Sanitize();
-}
-
-void PagedViewStructure::RemoveWithoutSanitize(AppListItemView* view) {
+void PagedViewStructure::Remove(AppListItemView* view,
+                                bool clear_overflow,
+                                bool clear_empty_pages) {
   for (auto& page : pages_) {
     auto iter = std::find(page.begin(), page.end(), view);
     if (iter != page.end()) {
@@ -143,16 +124,18 @@
       break;
     }
   }
+
+  if (clear_overflow)
+    ClearOverflow();
+
+  if (clear_empty_pages)
+    ClearEmptyPages();
 }
 
 void PagedViewStructure::Add(AppListItemView* view,
-                             const GridIndex& target_index) {
-  AddWithoutSanitize(view, target_index);
-  Sanitize();
-}
-
-void PagedViewStructure::AddWithoutSanitize(AppListItemView* view,
-                                            const GridIndex& target_index) {
+                             const GridIndex& target_index,
+                             bool clear_overflow,
+                             bool clear_empty_pages) {
   const int view_structure_size = total_pages();
   DCHECK((target_index.page < view_structure_size &&
           target_index.slot <= items_on_page(target_index.page)) ||
@@ -163,6 +146,12 @@
 
   auto& page = pages_[target_index.page];
   page.insert(page.begin() + target_index.slot, view);
+
+  if (clear_overflow)
+    ClearOverflow();
+
+  if (clear_empty_pages)
+    ClearEmptyPages();
 }
 
 GridIndex PagedViewStructure::GetIndexFromModelIndex(int model_index) const {
@@ -320,4 +309,55 @@
          apps_grid_view_->TilesPerPage(page_index);
 }
 
+bool PagedViewStructure::ClearOverflow() {
+  bool changed = false;
+  std::vector<AppListItemView*> overflow_views;
+  auto iter = pages_.begin();
+  while (iter != pages_.end() || !overflow_views.empty()) {
+    if (iter == pages_.end()) {
+      // Add additional page if overflowing item views remain.
+      pages_.emplace_back();
+      iter = pages_.end() - 1;
+      changed = true;
+    }
+
+    const size_t max_item_views =
+        apps_grid_view_->TilesPerPage(static_cast<int>(iter - pages_.begin()));
+    auto& page = *iter;
+
+    if (!overflow_views.empty()) {
+      // Put overflowing item views in current page.
+      page.insert(page.begin(), overflow_views.begin(), overflow_views.end());
+      overflow_views.clear();
+      changed = true;
+    }
+
+    if (page.size() > max_item_views) {
+      // Remove overflowing item views from current page.
+      overflow_views.insert(overflow_views.begin(),
+                            page.begin() + max_item_views, page.end());
+      page.erase(page.begin() + max_item_views, page.end());
+      changed = true;
+    }
+
+    ++iter;
+  }
+  return changed;
+}
+
+bool PagedViewStructure::ClearEmptyPages() {
+  bool changed = false;
+  auto iter = pages_.begin();
+  while (iter != pages_.end()) {
+    if (iter->empty()) {
+      // Remove empty page.
+      iter = pages_.erase(iter);
+      changed = true;
+    } else {
+      ++iter;
+    }
+  }
+  return changed;
+}
+
 }  // namespace app_list
diff --git a/ash/app_list/paged_view_structure.h b/ash/app_list/paged_view_structure.h
index 89343da..cfd445c 100644
--- a/ash/app_list/paged_view_structure.h
+++ b/ash/app_list/paged_view_structure.h
@@ -35,16 +35,20 @@
   // in the view model.
   void SaveToMetadata();
 
-  // Populates overflowing item views to next page and removes empty page.
-  // Returns true if view structure is changed.
-  bool Sanitize();
-
-  // Operations allowed to modify the view structure.
-  void Move(AppListItemView* view, const GridIndex& target_index);
-  void Remove(AppListItemView* view);
-  void RemoveWithoutSanitize(AppListItemView* view);
-  void Add(AppListItemView* view, const GridIndex& target_index);
-  void AddWithoutSanitize(AppListItemView* view, const GridIndex& target_index);
+  // Operations allowed to modify the view structure. Populates overflowing item
+  // views to next page if |clear_overflow| is true. Clears empty pages if
+  // |clear_empty_pages| is true. Both are true by default.
+  void Move(AppListItemView* view,
+            const GridIndex& target_index,
+            bool clear_overflow = true,
+            bool clear_empty_pages = true);
+  void Remove(AppListItemView* view,
+              bool clear_overflow = true,
+              bool clear_empty_pages = true);
+  void Add(AppListItemView* view,
+           const GridIndex& target_index,
+           bool clear_overflow = true,
+           bool clear_empty_pages = true);
 
   // Convert between the model index and the visual index. The model index
   // is the index of the item in AppListModel (Also the same index in
@@ -89,6 +93,13 @@
   const Pages& pages() const { return pages_; }
 
  private:
+  // Clear overflowing item views by moving them to the next page. Returns true
+  // if view structure is changed.
+  bool ClearOverflow();
+
+  // Removes empty page. Returns true if view structure is changed.
+  bool ClearEmptyPages();
+
   // Represents the item views' locations in each page. This is only used when
   // apps grid gap is enabled. (We don't need this in non-gap apps grid since
   // all item views are linearly laid out in |view_model_|.)
diff --git a/ash/app_list/test/test_app_list_client.h b/ash/app_list/test/test_app_list_client.h
index f0e3702..8727252 100644
--- a/ash/app_list/test/test_app_list_client.h
+++ b/ash/app_list/test/test_app_list_client.h
@@ -52,6 +52,7 @@
   void OnItemUpdated(mojom::AppListItemMetadataPtr item) override {}
   void OnPageBreakItemAdded(const std::string& id,
                             const syncer::StringOrdinal& position) override {}
+  void OnPageBreakItemDeleted(const std::string& id) override {}
 
   size_t voice_session_count() const { return voice_session_count_; }
 
diff --git a/ash/app_list/views/apps_grid_view.cc b/ash/app_list/views/apps_grid_view.cc
index 1f8bb69..9c61515 100644
--- a/ash/app_list/views/apps_grid_view.cc
+++ b/ash/app_list/views/apps_grid_view.cc
@@ -1462,13 +1462,6 @@
       std::min(GridIndex(pagination_model_.selected_page(), row * cols_ + col),
                GetLastTargetIndexOfPage(pagination_model_.selected_page()));
 
-  // Dragging to a full page is not allowed when apps grid gap is enabled.
-  if (IsAppsGridGapEnabled() && view_structure_.IsFullPage(drop_target->page) &&
-      (drag_start_page_ != drop_target->page ||
-       IsDraggingForReparentInRootLevelGridView())) {
-    return false;
-  }
-
   DCHECK(IsValidReorderTargetIndex(*drop_target));
   return true;
 }
@@ -2389,10 +2382,8 @@
   AppListItemView* item_view = GetItemViewAt(index);
   view_model_.Remove(index);
   if (IsAppsGridGapEnabled()) {
-    if (sanitize)
-      view_structure_.Remove(item_view);
-    else
-      view_structure_.RemoveWithoutSanitize(item_view);
+    view_structure_.Remove(item_view, sanitize /* clear_overflow */,
+                           sanitize /* clear_empty_pages */);
   }
   if (item_view == drag_view_)
     drag_view_ = nullptr;
@@ -2797,12 +2788,17 @@
   PagedViewStructure copied_view_structure(view_structure_);
 
   // Remove the item view being dragged.
-  if (drag_view_)
-    copied_view_structure.RemoveWithoutSanitize(drag_view_);
+  if (drag_view_) {
+    copied_view_structure.Remove(drag_view_, false /* clear_overflow */,
+                                 false /* clear_empty_pages */);
+  }
 
   // Leaves a blank space in the grid for the current reorder placeholder.
-  if (IsValidIndex(reorder_placeholder_))
-    copied_view_structure.AddWithoutSanitize(nullptr, reorder_placeholder_);
+  if (IsValidIndex(reorder_placeholder_)) {
+    copied_view_structure.Add(nullptr, reorder_placeholder_,
+                              true /* clear_overflow */,
+                              false /* clear_empty_pages */);
+  }
 
   // Convert visual index to ideal bounds.
   const auto& pages = copied_view_structure.pages();
diff --git a/ash/app_list/views/apps_grid_view_unittest.cc b/ash/app_list/views/apps_grid_view_unittest.cc
index 0078e62..77548c5 100644
--- a/ash/app_list/views/apps_grid_view_unittest.cc
+++ b/ash/app_list/views/apps_grid_view_unittest.cc
@@ -158,7 +158,7 @@
 
 const TestParams kAppsGridViewDragTestParams[] = {
     {false /* is_rtl_enabled */, false /* is_apps_grid_gap_enabled */,
-     false /* is_apps_grid_gap_enabled */},
+     false /* is_new_style_launcher_enabled */},
     {false, false, true},
     {true, false, false},
     {true, false, true},
@@ -170,7 +170,7 @@
 
 const TestParams kAppsGridGapTestParams[] = {
     {false /* is_rtl_enabled */, true /* is_apps_grid_gap_enabled */,
-     false /* is_apps_grid_gap_enabled */},
+     false /* is_new_style_launcher_enabled */},
     {false, true, true},
     {true, true, false},
     {true, true, true},
@@ -1320,8 +1320,8 @@
   EXPECT_EQ(std::string("Item 0"), model_->GetModelContent());
 }
 
-TEST_P(AppsGridGapTest, MoveItemToPreviousFullPageNotAllowed) {
-  const int kApps = 1 + GetTilesPerPage(0);
+TEST_P(AppsGridGapTest, MoveItemToPreviousFullPage) {
+  const int kApps = 2 + GetTilesPerPage(0);
   model_->PopulateApps(kApps);
 
   // There are two pages and last item is on second page.
@@ -1331,56 +1331,78 @@
   const views::ViewModelT<AppListItemView>* view_model =
       apps_grid_view_->view_model();
   EXPECT_EQ(kApps, view_model->view_size());
-  for (int i = 0; i < kApps - 1; ++i) {
-    EXPECT_EQ(view_model->view_at(i),
-              test_api_->GetViewAtVisualIndex(0 /* page */, i /* slot */));
-    EXPECT_EQ("Item " + std::to_string(i),
+  for (int i = 0; i < kApps; ++i) {
+    EXPECT_EQ(view_model->view_at(i), test_api_->GetViewAtVisualIndex(
+                                          i / GetTilesPerPage(0) /* page */,
+                                          i % GetTilesPerPage(0) /* slot */));
+    EXPECT_EQ("Item " + base::IntToString(i),
               view_model->view_at(i)->item()->id());
   }
-  EXPECT_EQ(view_model->view_at(kApps - 1),
-            test_api_->GetViewAtVisualIndex(1 /* page */, 0 /* slot */));
-  EXPECT_EQ("Item " + std::to_string(kApps - 1),
-            view_model->view_at(kApps - 1)->item()->id());
 
-  // There's no "page break" item between Item 19 and 20, although there are two
-  // pages. It will only be added after user operations.
-  EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3,Item 4,Item 5,Item 6,Item "
-                        "7,Item 8,Item 9,Item 10,Item 11,Item 12,Item 13,Item "
-                        "14,Item 15,Item 16,Item 17,Item 18,Item 19,Item 20"),
-            model_->GetModelContent());
+  // There's no "page break" item at the end of first page, although there are
+  // two pages. It will only be added after user operations.
+  std::string model_content = "Item 0";
+  for (int i = 1; i < kApps; ++i)
+    model_content.append(",Item " + base::IntToString(i));
+  EXPECT_EQ(model_content, model_->GetModelContent());
 
   // Drag the last item to the first item's left position in previous page.
-  gfx::Point from = test_api_->GetItemTileRectAtVisualIndex(1, 0).CenterPoint();
+  gfx::Point from = test_api_->GetItemTileRectAtVisualIndex(1, 1).CenterPoint();
   gfx::Rect tile_rect = test_api_->GetItemTileRectAtVisualIndex(0, 0);
   gfx::Point to_in_previous_page = tile_rect.CenterPoint();
   to_in_previous_page.set_x(tile_rect.x());
   GetPaginationModel()->SelectPage(1, false);
   SimulateDragToNeighborPage(false /* next_page */, from, to_in_previous_page);
 
-  // The dragging is not successfull, so nothing changes visually.
+  // The dragging is successful, the last item becomes the first item.
   EXPECT_EQ("0", page_flip_waiter_->selected_pages());
   EXPECT_EQ(0, GetPaginationModel()->selected_page());
   TestAppListItemViewIndice();
   EXPECT_EQ(kApps, view_model->view_size());
-  EXPECT_EQ(view_model->view_at(0),
-            test_api_->GetViewAtVisualIndex(0 /* page */, 0 /* slot */));
-  for (int i = 0; i < kApps - 1; ++i) {
-    EXPECT_EQ(view_model->view_at(i),
-              test_api_->GetViewAtVisualIndex(0 /* page */, i /* slot */));
-    EXPECT_EQ("Item " + std::to_string(i),
+  for (int i = 0; i < kApps; ++i) {
+    EXPECT_EQ(view_model->view_at(i), test_api_->GetViewAtVisualIndex(
+                                          i / GetTilesPerPage(0) /* page */,
+                                          i % GetTilesPerPage(0) /* slot */));
+    EXPECT_EQ("Item " + base::IntToString((i + kApps - 1) % kApps),
               view_model->view_at(i)->item()->id());
   }
-  EXPECT_EQ(view_model->view_at(kApps - 1),
-            test_api_->GetViewAtVisualIndex(1 /* page */, 0 /* slot */));
-  EXPECT_EQ("Item " + std::to_string(kApps - 1),
-            view_model->view_at(kApps - 1)->item()->id());
 
   // A "page break" item is added to split the pages.
-  EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3,Item 4,Item 5,Item 6,Item "
-                        "7,Item 8,Item 9,Item 10,Item 11,Item 12,Item 13,Item "
-                        "14,Item 15,Item 16,Item 17,Item 18,Item "
-                        "19,PageBreakItem,Item 20"),
-            model_->GetModelContent());
+  model_content = "Item " + base::IntToString(kApps - 1);
+  for (int i = 1; i < kApps; ++i) {
+    model_content.append(",Item " + base::IntToString(i - 1));
+    if (i == GetTilesPerPage(0) - 1)
+      model_content.append(",PageBreakItem");
+  }
+  EXPECT_EQ(model_content, model_->GetModelContent());
+
+  // Again drag the last item to the first item's left position in previous
+  // page.
+  GetPaginationModel()->SelectPage(1, false);
+  SimulateDragToNeighborPage(false /* next_page */, from, to_in_previous_page);
+
+  // The dragging is successful, the last item becomes the first item again.
+  EXPECT_EQ("0", page_flip_waiter_->selected_pages());
+  EXPECT_EQ(0, GetPaginationModel()->selected_page());
+  TestAppListItemViewIndice();
+  EXPECT_EQ(kApps, view_model->view_size());
+  for (int i = 0; i < kApps; ++i) {
+    EXPECT_EQ(view_model->view_at(i), test_api_->GetViewAtVisualIndex(
+                                          i / GetTilesPerPage(0) /* page */,
+                                          i % GetTilesPerPage(0) /* slot */));
+    EXPECT_EQ("Item " + base::IntToString((i + kApps - 2) % kApps),
+              view_model->view_at(i)->item()->id());
+  }
+
+  // A "page break" item still exists.
+  model_content = "Item " + base::IntToString(kApps - 2) + ",Item " +
+                  base::IntToString(kApps - 1);
+  for (int i = 2; i < kApps; ++i) {
+    model_content.append(",Item " + base::IntToString(i - 2));
+    if (i == GetTilesPerPage(0) - 1)
+      model_content.append(",PageBreakItem");
+  }
+  EXPECT_EQ(model_content, model_->GetModelContent());
 }
 
 }  // namespace test
diff --git a/ash/frame/caption_buttons/frame_caption_button.cc b/ash/frame/caption_buttons/frame_caption_button.cc
index b429e1c..025e214d 100644
--- a/ash/frame/caption_buttons/frame_caption_button.cc
+++ b/ash/frame/caption_buttons/frame_caption_button.cc
@@ -37,22 +37,6 @@
 // The ratio applied to the button's alpha when the button is disabled.
 const float kDisabledButtonAlphaRatio = 0.5f;
 
-// Minimum theme light color contrast.
-const float kContrastLightItemThreshold = 3;
-// The amount to darken a light theme color by for use as foreground color.
-const float kThemedForegroundBlackFraction = 0.64;
-
-// This mimics |shouldUseLightForegroundOnBackground| in ColorUtils.java.
-bool UseLightColor(FrameCaptionButton::ColorMode color_mode,
-                   SkColor background_color) {
-  if (color_mode == FrameCaptionButton::ColorMode::kThemed) {
-    return color_utils::GetContrastRatio(SK_ColorWHITE, background_color) >=
-           kContrastLightItemThreshold;
-  }
-  DCHECK_EQ(color_mode, FrameCaptionButton::ColorMode::kDefault);
-  return color_utils::IsDark(background_color);
-}
-
 // Returns the amount by which the inkdrop ripple and mask should be insetted
 // from the button size in order to achieve a circular inkdrop with a size
 // equals to kInkDropHighlightSize.
@@ -99,18 +83,12 @@
 // static
 SkColor FrameCaptionButton::GetButtonColor(ColorMode color_mode,
                                            SkColor background_color) {
-  bool use_light_color = UseLightColor(color_mode, background_color);
-
-  if (color_mode == ColorMode::kThemed) {
-    // This mimics |getThemedAssetColor| in ColorUtils.java.
-    return use_light_color
-               ? SK_ColorWHITE
-               : color_utils::AlphaBlend(SK_ColorBLACK, background_color,
-                                         255 * kThemedForegroundBlackFraction);
-  }
+  if (color_mode == ColorMode::kThemed)
+    return color_utils::GetThemedAssetColor(background_color);
 
   DCHECK_EQ(color_mode, ColorMode::kDefault);
-  return use_light_color ? gfx::kGoogleGrey200 : gfx::kGoogleGrey700;
+  return color_utils::IsDark(background_color) ? gfx::kGoogleGrey200
+                                               : gfx::kGoogleGrey700;
 }
 
 // static
@@ -303,9 +281,10 @@
 }
 
 void FrameCaptionButton::UpdateInkDropBaseColor() {
-  set_ink_drop_base_color(UseLightColor(color_mode_, background_color_)
-                              ? SK_ColorWHITE
-                              : SK_ColorBLACK);
+  set_ink_drop_base_color(
+      color_utils::IsDark(GetButtonColor(color_mode_, background_color_))
+          ? SK_ColorBLACK
+          : SK_ColorWHITE);
 }
 
 }  // namespace ash
diff --git a/ash/public/interfaces/app_list.mojom b/ash/public/interfaces/app_list.mojom
index b52c372..028155c 100644
--- a/ash/public/interfaces/app_list.mojom
+++ b/ash/public/interfaces/app_list.mojom
@@ -359,6 +359,8 @@
   OnItemUpdated(AppListItemMetadata folder);
   // Invoked when a "page break" item is added with |id| and |position|.
   OnPageBreakItemAdded(string id, syncer.mojom.StringOrdinal position);
+  // Invoked when a "page break" item with |id| is deleted.
+  OnPageBreakItemDeleted(string id);
 
   //////////////////////////////////////////////////////////////////////////////
   // Interfaces on voice interaction:
diff --git a/ash/shell.cc b/ash/shell.cc
index a05905a..5aae069 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -1104,7 +1104,7 @@
   cursor_manager_->SetDisplay(
       display::Screen::GetScreen()->GetPrimaryDisplay());
 
-  accelerator_controller_ = std::make_unique<AcceleratorController>(nullptr);
+  accelerator_controller_ = std::make_unique<AcceleratorController>();
   voice_interaction_controller_ =
       std::make_unique<VoiceInteractionController>();
 
diff --git a/cc/tiles/gpu_image_decode_cache.cc b/cc/tiles/gpu_image_decode_cache.cc
index 3f30649..2db4681 100644
--- a/cc/tiles/gpu_image_decode_cache.cc
+++ b/cc/tiles/gpu_image_decode_cache.cc
@@ -695,6 +695,9 @@
   }
   // Register this component with base::MemoryCoordinatorClientRegistry.
   base::MemoryCoordinatorClientRegistry::GetInstance()->Register(this);
+  memory_pressure_listener_.reset(
+      new base::MemoryPressureListener(base::BindRepeating(
+          &GpuImageDecodeCache::OnMemoryPressure, base::Unretained(this))));
 }
 
 GpuImageDecodeCache::~GpuImageDecodeCache() {
@@ -711,10 +714,6 @@
   // Unregister this component with memory_coordinator::ClientRegistry.
   base::MemoryCoordinatorClientRegistry::GetInstance()->Unregister(this);
 
-  memory_pressure_listener_.reset(
-      new base::MemoryPressureListener(base::BindRepeating(
-          &GpuImageDecodeCache::OnMemoryPressure, base::Unretained(this))));
-
   // TODO(vmpstr): If we don't have a client name, it may cause problems in
   // unittests, since most tests don't set the name but some do. The UMA system
   // expects the name to be always the same. This assertion is violated in the
diff --git a/chrome/VERSION b/chrome/VERSION
index 9ae7046..2dd6f5e 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=71
 MINOR=0
-BUILD=3552
+BUILD=3553
 PATCH=0
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedOfflineBridge.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedOfflineBridge.java
index 53b309d..bbcbd4f 100644
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedOfflineBridge.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedOfflineBridge.java
@@ -44,8 +44,7 @@
     public FeedOfflineBridge(Profile profile, KnownContentApi knownContentApi) {
         mNativeBridge = nativeInit(profile);
         mKnownContentApi = knownContentApi;
-        // TODO(skym): Remove this null check when a KnownContentApi is provided by Feed.
-        if (mKnownContentApi != null) mKnownContentApi.addListener(this);
+        mKnownContentApi.addListener(this);
     }
 
     @Override
@@ -53,8 +52,7 @@
         assert mNativeBridge != 0;
         nativeDestroy(mNativeBridge);
         mNativeBridge = 0;
-        // TODO(skym): Remove this null check when a KnownContentApi is provided by Feed.
-        if (mKnownContentApi != null) mKnownContentApi.removeListener(this);
+        mKnownContentApi.removeListener(this);
     }
 
     @Override
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedProcessScopeFactory.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedProcessScopeFactory.java
index 464756b7..3af6f8b 100644
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedProcessScopeFactory.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedProcessScopeFactory.java
@@ -68,8 +68,8 @@
         schedulerBridge.initializeFeedDependencies(
                 sFeedProcessScope.getRequestManager(), sFeedProcessScope.getSessionManager());
 
-        // TODO(skym): Use sFeedProcessScope.getKnownContentApi().
-        sFeedOfflineIndicator = new FeedOfflineBridge(profile, null);
+        sFeedOfflineIndicator =
+                new FeedOfflineBridge(profile, sFeedProcessScope.getKnownContentApi());
     }
 
     private static Configuration createConfiguration() {
diff --git a/chrome/android/java/res/drawable/download_circular_progress_bar_background.xml b/chrome/android/java/res_download/drawable/circular_progress_bar_background_large.xml
similarity index 63%
copy from chrome/android/java/res/drawable/download_circular_progress_bar_background.xml
copy to chrome/android/java/res_download/drawable/circular_progress_bar_background_large.xml
index 04b4fa0..e6ae53e 100644
--- a/chrome/android/java/res/drawable/download_circular_progress_bar_background.xml
+++ b/chrome/android/java/res_download/drawable/circular_progress_bar_background_large.xml
@@ -4,10 +4,13 @@
      found in the LICENSE file. -->
 
 <layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
-    <item android:id="@android:id/background">
+    <item android:drawable="?attr/selectableItemBackground" />
+    <item>
         <shape
             android:shape="oval">
-            <solid android:color="@color/modern_grey_300"  />
+            <solid android:color="@android:color/white"  />
+            <size android:width="48dp"
+                android:height="48dp" />
         </shape>
     </item>
 </layer-list>
\ No newline at end of file
diff --git a/chrome/android/java/res/drawable/download_circular_progress_bar_background.xml b/chrome/android/java/res_download/drawable/circular_progress_bar_background_small.xml
similarity index 73%
rename from chrome/android/java/res/drawable/download_circular_progress_bar_background.xml
rename to chrome/android/java/res_download/drawable/circular_progress_bar_background_small.xml
index 04b4fa0..0f9d3ac4 100644
--- a/chrome/android/java/res/drawable/download_circular_progress_bar_background.xml
+++ b/chrome/android/java/res_download/drawable/circular_progress_bar_background_small.xml
@@ -4,10 +4,13 @@
      found in the LICENSE file. -->
 
 <layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
-    <item android:id="@android:id/background">
+    <item android:drawable="?attr/selectableItemBackground" />
+    <item>
         <shape
             android:shape="oval">
             <solid android:color="@color/modern_grey_300"  />
+            <size android:width="36dp"
+                android:height="36dp" />
         </shape>
     </item>
 </layer-list>
\ No newline at end of file
diff --git a/chrome/android/java/res/drawable/download_circular_progress_bar.xml b/chrome/android/java/res_download/drawable/circular_progress_bar_determinate_large.xml
similarity index 71%
copy from chrome/android/java/res/drawable/download_circular_progress_bar.xml
copy to chrome/android/java/res_download/drawable/circular_progress_bar_determinate_large.xml
index d818585..1aa4470 100644
--- a/chrome/android/java/res/drawable/download_circular_progress_bar.xml
+++ b/chrome/android/java/res_download/drawable/circular_progress_bar_determinate_large.xml
@@ -6,16 +6,16 @@
 <layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
     <item android:id="@android:id/progress">
         <rotate
-            android:fromDegrees="180"
-            android:toDegrees="180" >
+            android:fromDegrees="270"
+            android:toDegrees="270" >
             <shape
-                android:innerRadius="16dp"
+                android:innerRadius="28dp"
                 android:thickness="2dp"
                 android:useLevel="true"
                 android:shape="ring">
                 <gradient
-                    android:startColor="@color/modern_grey_800"
-                    android:endColor="@color/modern_grey_800"
+                    android:startColor="@color/modern_grey_900"
+                    android:endColor="@color/modern_grey_900"
                     android:type="sweep" />
             </shape>
         </rotate>
diff --git a/chrome/android/java/res/drawable/download_circular_progress_bar.xml b/chrome/android/java/res_download/drawable/circular_progress_bar_determinate_small.xml
similarity index 90%
rename from chrome/android/java/res/drawable/download_circular_progress_bar.xml
rename to chrome/android/java/res_download/drawable/circular_progress_bar_determinate_small.xml
index d818585..636ddf2 100644
--- a/chrome/android/java/res/drawable/download_circular_progress_bar.xml
+++ b/chrome/android/java/res_download/drawable/circular_progress_bar_determinate_small.xml
@@ -6,8 +6,8 @@
 <layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
     <item android:id="@android:id/progress">
         <rotate
-            android:fromDegrees="180"
-            android:toDegrees="180" >
+            android:fromDegrees="270"
+            android:toDegrees="270" >
             <shape
                 android:innerRadius="16dp"
                 android:thickness="2dp"
diff --git a/chrome/android/java/res_download/drawable/circular_progress_bar_indeterminate_large.xml b/chrome/android/java/res_download/drawable/circular_progress_bar_indeterminate_large.xml
new file mode 100644
index 0000000..f1dba01
--- /dev/null
+++ b/chrome/android/java/res_download/drawable/circular_progress_bar_indeterminate_large.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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. -->
+
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    xmlns:aapt="http://schemas.android.com/aapt"
+    tools:targetApi="21"
+    tools:ignore="UnusedResources">
+    <aapt:attr name="android:drawable">
+        <vector
+            android:width="60dp"
+            android:height="60dp"
+            android:viewportWidth="60"
+            android:viewportHeight="60"
+            android:tint="@color/modern_grey_900">
+            <group android:name="container"
+                android:pivotX="30"
+                android:pivotY="30">
+                <path android:name="arc"
+                    android:fillColor="@android:color/transparent"
+                    android:strokeColor="@color/modern_grey_900"
+                    android:strokeLineCap="square"
+                    android:strokeLineJoin="miter"
+                    android:strokeWidth="2"
+                    android:trimPathStart="0"
+                    android:trimPathEnd="0.05"
+                    android:trimPathOffset="0"
+                    android:pathData="m 30,1 a 29,29 0 1,1 0,58 a 29,29 0 1,1 0,-58" />
+            </group>
+        </vector>
+    </aapt:attr>
+    
+    <target android:name="arc">
+        <aapt:attr name="android:animation">
+            <set>
+                <objectAnimator
+                    android:duration="1333"
+                    android:propertyName="trimPathStart"
+                    android:repeatCount="-1"
+                    android:valueFrom="0"
+                    android:valueTo="0.75"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator
+                            android:pathData="L0.5,0 C 0.7,0 0.6,1 1,1"/>
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="1333"
+                    android:propertyName="trimPathEnd"
+                    android:repeatCount="-1"
+                    android:valueFrom="0.05"
+                    android:valueTo="0.8"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator
+                            android:pathData="C0.2,0 0.1,1 0.5, 1 L 1,1"/>
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="1333"
+                    android:propertyName="trimPathOffset"
+                    android:repeatCount="-1"
+                    android:valueFrom="0"
+                    android:valueTo="0.25"
+                    android:valueType="floatType"
+                    android:interpolator="@android:anim/linear_interpolator" />
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="container">
+        <aapt:attr name="android:animation">
+            <objectAnimator
+                android:duration="4444"
+                android:interpolator="@android:anim/linear_interpolator"
+                android:propertyName="rotation"
+                android:repeatCount="-1"
+                android:valueFrom="0"
+                android:valueTo="720"
+                android:valueType="floatType"/>
+        </aapt:attr>
+    </target>
+</animated-vector>
\ No newline at end of file
diff --git a/chrome/android/java/res_download/drawable/circular_progress_bar_indeterminate_small.xml b/chrome/android/java/res_download/drawable/circular_progress_bar_indeterminate_small.xml
new file mode 100644
index 0000000..7b48ad2
--- /dev/null
+++ b/chrome/android/java/res_download/drawable/circular_progress_bar_indeterminate_small.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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. -->
+
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    xmlns:aapt="http://schemas.android.com/aapt"
+    tools:targetApi="21"
+    tools:ignore="UnusedResources">
+    <aapt:attr name="android:drawable">
+        <vector
+            android:width="36dp"
+            android:height="36dp"
+            android:viewportWidth="36"
+            android:viewportHeight="36"
+            android:tint="@color/modern_grey_800">
+            <group android:name="container"
+                android:pivotX="18"
+                android:pivotY="18">
+                <path android:name="arc"
+                    android:fillColor="@android:color/transparent"
+                    android:strokeColor="@color/modern_grey_800"
+                    android:strokeLineCap="square"
+                    android:strokeLineJoin="miter"
+                    android:strokeWidth="2"
+                    android:trimPathStart="0"
+                    android:trimPathEnd="0.05"
+                    android:trimPathOffset="0"
+                    android:pathData="m 18,1 a 17,17 0 1,1 0,34 a 17,17 0 1,1 0,-34" />
+            </group>
+        </vector>
+    </aapt:attr>
+
+    <target android:name="arc">
+        <aapt:attr name="android:animation">
+            <set>
+                <objectAnimator
+                    android:duration="1333"
+                    android:propertyName="trimPathStart"
+                    android:repeatCount="-1"
+                    android:valueFrom="0"
+                    android:valueTo="0.75"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator
+                            android:pathData="L0.5,0 C 0.7,0 0.6,1 1,1"/>
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="1333"
+                    android:propertyName="trimPathEnd"
+                    android:repeatCount="-1"
+                    android:valueFrom="0.05"
+                    android:valueTo="0.8"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator
+                            android:pathData="C0.2,0 0.1,1 0.5, 1 L 1,1"/>
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="1333"
+                    android:propertyName="trimPathOffset"
+                    android:repeatCount="-1"
+                    android:valueFrom="0"
+                    android:valueTo="0.25"
+                    android:valueType="floatType"
+                    android:interpolator="@android:anim/linear_interpolator" />
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="container">
+        <aapt:attr name="android:animation">
+            <objectAnimator
+                android:duration="4444"
+                android:interpolator="@android:anim/linear_interpolator"
+                android:propertyName="rotation"
+                android:repeatCount="-1"
+                android:valueFrom="0"
+                android:valueTo="720"
+                android:valueType="floatType"/>
+        </aapt:attr>
+    </target>
+</animated-vector>
\ No newline at end of file
diff --git a/chrome/android/java/res/drawable/image_loading_progress.xml b/chrome/android/java/res_download/drawable/image_loading_progress.xml
similarity index 100%
rename from chrome/android/java/res/drawable/image_loading_progress.xml
rename to chrome/android/java/res_download/drawable/image_loading_progress.xml
diff --git a/chrome/android/java/res_download/layout/download_manager_in_progress_item.xml b/chrome/android/java/res_download/layout/download_manager_in_progress_item.xml
index 083e898..b5304d6 100644
--- a/chrome/android/java/res_download/layout/download_manager_in_progress_item.xml
+++ b/chrome/android/java/res_download/layout/download_manager_in_progress_item.xml
@@ -16,44 +16,15 @@
     app:columnCount="3"
     app:rowCount="2">
 
-    <ImageView
-        android:layout_width="36dp"
-        android:layout_height="36dp"
-        app:layout_gravity="center"
-        app:layout_column="0"
-        app:layout_row="0"
-        app:layout_rowSpan="2"
-        tools:ignore="ContentDescription"
-        android:background="@drawable/download_circular_progress_bar_background"
-        android:layout_centerInParent="true"/>
-
-    <ProgressBar
-        android:id="@+id/progress_bar"
-        android:layout_width="36dp"
-        android:layout_height="36dp"
+    <org.chromium.chrome.browser.download.home.list.view.CircularProgressView
+        android:id="@+id/action_button"
         android:layout_marginStart="16dp"
         android:layout_marginEnd="16dp"
-        app:layout_gravity="center"
-        style="?android:attr/progressBarStyleHorizontal"
         app:layout_column="0"
         app:layout_row="0"
         app:layout_rowSpan="2"
-        android:max="100"
-        android:progress="0"
-        android:progressDrawable="@drawable/download_circular_progress_bar"/>
-
-    <org.chromium.chrome.browser.widget.TintedImageButton
-        android:id="@+id/pause_button"
-        android:layout_width="48dp"
-        android:layout_height="48dp"
-        app:layout_gravity="center"
-        android:background="?attr/selectableItemBackground"
-        android:contentDescription="@string/download_notification_pause_button"
-        android:src="@drawable/ic_pause_white_24dp"
-        app:chrometint="@color/default_icon_color"
-        app:layout_column="0"
-        app:layout_row="0"
-        app:layout_rowSpan="2"/>
+        app:layout_gravity="center_vertical"
+        style="@style/SmallCircularProgress" />
 
     <TextView
         android:id="@+id/title"
diff --git a/chrome/android/java/res_download/layout/download_manager_in_progress_video_item.xml b/chrome/android/java/res_download/layout/download_manager_in_progress_video_item.xml
new file mode 100644
index 0000000..3342d3b
--- /dev/null
+++ b/chrome/android/java/res_download/layout/download_manager_in_progress_video_item.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+ <android.support.v7.widget.GridLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:clickable="true"
+    android:background="@drawable/hairline_border_card_background"
+    app:columnCount="2"
+    app:rowCount="3">
+
+    <org.chromium.ui.widget.RoundedCornerImageView
+        android:id="@+id/thumbnail"
+        android:layout_width="match_parent"
+        android:layout_height="200dp"
+        android:scaleType="centerInside"
+        android:adjustViewBounds="true"
+        android:src="@color/modern_grey_300"
+        app:layout_column="0"
+        app:layout_row="0"
+        app:layout_columnSpan="2"
+        app:layout_gravity="center"
+        app:cornerRadiusTopStart="@dimen/download_manager_prefetch_thumbnail_corner_radius"
+        app:cornerRadiusTopEnd="@dimen/download_manager_prefetch_thumbnail_corner_radius"/>
+
+    <org.chromium.chrome.browser.download.home.list.view.CircularProgressView
+        android:id="@+id/action_button"
+        app:layout_column="0"
+        app:layout_row="0"
+        app:layout_columnSpan="2"
+        app:layout_gravity="center"
+        style="@style/LargeCircularProgress" />
+
+    <TextView
+        android:id="@+id/title"
+        style="@style/DownloadItemText"
+        android:layout_width="wrap_content"
+        android:layout_marginTop="11dp"
+        android:textAppearance="@style/BlackTitle1"
+        app:layout_column="0"
+        app:layout_row="1"
+        android:layout_marginStart="16dp"
+        app:layout_gravity="fill_horizontal" />
+
+    <TextView
+        android:id="@+id/caption"
+        style="@style/DownloadItemText"
+        android:layout_width="wrap_content"
+        android:layout_marginBottom="11dp"
+        android:textAppearance="@style/BlackHint2"
+        app:layout_column="0"
+        app:layout_row="2"
+        android:layout_marginStart="16dp"
+        app:layout_gravity="fill_horizontal" />
+
+    <org.chromium.chrome.browser.widget.TintedImageButton
+        android:id="@+id/cancel_button"
+        android:layout_width="48dp"
+        android:layout_height="48dp"
+        app:layout_column="1"
+        app:layout_row="1"
+        app:layout_rowSpan="2"
+        app:layout_gravity="center_vertical|end"
+        android:background="?attr/selectableItemBackground"
+        android:contentDescription="@string/download_notification_cancel_button"
+        android:src="@drawable/btn_close"
+        app:chrometint="@color/default_icon_color" />
+
+</android.support.v7.widget.GridLayout>
diff --git a/chrome/android/java/res_download/layout/download_manager_video_item.xml b/chrome/android/java/res_download/layout/download_manager_video_item.xml
index ec56c9b..d6aa37a 100644
--- a/chrome/android/java/res_download/layout/download_manager_video_item.xml
+++ b/chrome/android/java/res_download/layout/download_manager_video_item.xml
@@ -18,11 +18,10 @@
     <org.chromium.ui.widget.RoundedCornerImageView
         android:id="@+id/thumbnail"
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:scaleType="centerCrop"
+        android:layout_height="200dp"
+        android:scaleType="centerInside"
         android:adjustViewBounds="true"
-        android:background="@color/modern_grey_300"
-        android:src="@drawable/audio_playing_square"
+        android:src="@color/modern_grey_300"
         app:layout_column="0"
         app:layout_row="0"
         app:layout_columnSpan="2"
diff --git a/chrome/android/java/res_download/values-v17/attrs.xml b/chrome/android/java/res_download/values-v17/attrs.xml
new file mode 100644
index 0000000..91d0f9b
--- /dev/null
+++ b/chrome/android/java/res_download/values-v17/attrs.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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. -->
+
+<resources>
+    <declare-styleable name="CircularProgressView">
+        <attr name="indeterminateProgress" format="reference" />
+        <attr name="determinateProgress" format="reference" />
+        <attr name="resumeSrc" format="reference" />
+        <attr name="pauseSrc" format="reference" />
+        <attr name="retrySrc" format="reference" />
+    </declare-styleable>
+</resources>
\ No newline at end of file
diff --git a/chrome/android/java/res_download/values-v17/styles.xml b/chrome/android/java/res_download/values-v17/styles.xml
index b294e4b..c4a125d 100644
--- a/chrome/android/java/res_download/values-v17/styles.xml
+++ b/chrome/android/java/res_download/values-v17/styles.xml
@@ -3,7 +3,8 @@
      Use of this source code is governed by a BSD-style license that can be
      found in the LICENSE file. -->
 
-<resources xmlns:tools="http://schemas.android.com/tools">
+<resources xmlns:tools="http://schemas.android.com/tools"
+    xmlns:app="http://schemas.android.com/apk/res-auto">
     <!-- Download Home -->
     <style name="DownloadHomeStatusText">
         <item name="android:layout_width">wrap_content</item>
@@ -71,4 +72,28 @@
         <item name="android:layout_marginStart">8dp</item>
         <item name="android:layout_marginTop">8dp</item>
     </style>
+    <style name="SmallCircularProgress">
+        <item name="android:layout_width">36dp</item>
+        <item name="android:layout_height">36dp</item>
+        <item name="android:tint">@color/default_icon_color</item>
+        <item name="android:background">@drawable/circular_progress_bar_background_small</item>
+        <item name="indeterminateProgress">@drawable/circular_progress_bar_indeterminate_small</item>
+        <item name="determinateProgress">@drawable/circular_progress_bar_determinate_small</item>
+        <item name="resumeSrc">@drawable/ic_play_arrow_white_24dp</item>
+        <!-- TODO(883387): Support retrying failed downloads. -->
+        <item name="retrySrc">@drawable/ic_play_arrow_white_24dp</item>
+        <item name="pauseSrc">@drawable/ic_pause_white_24dp</item>
+    </style>
+    <style name="LargeCircularProgress">
+        <item name="android:layout_width">60dp</item>
+        <item name="android:layout_height">60dp</item>
+        <item name="android:tint">@color/default_icon_color</item>
+        <item name="android:background">@drawable/circular_progress_bar_background_large</item>
+        <item name="indeterminateProgress">@drawable/circular_progress_bar_indeterminate_large</item>
+        <item name="determinateProgress">@drawable/circular_progress_bar_determinate_large</item>
+        <item name="resumeSrc">@drawable/ic_play_arrow_white_36dp</item>
+        <!-- TODO(883387): Support retrying failed downloads. -->
+        <item name="retrySrc">@drawable/ic_play_arrow_white_36dp</item>
+        <item name="pauseSrc">@drawable/ic_pause_white_36dp</item>
+    </style>
 </resources>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/EdgeSwipeHandlerLayoutDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/EdgeSwipeHandlerLayoutDelegate.java
deleted file mode 100644
index 8b814be..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/EdgeSwipeHandlerLayoutDelegate.java
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.compositor.layouts;
-
-import org.chromium.chrome.browser.compositor.layouts.eventfilter.EdgeSwipeHandler;
-import org.chromium.chrome.browser.compositor.layouts.eventfilter.EmptyEdgeSwipeHandler;
-import org.chromium.chrome.browser.compositor.layouts.eventfilter.ScrollDirection;
-
-/**
- * A {@link EdgeSwipeHandler} that takes a {@link LayoutProvider} and delegates all swipe events
- * to {@link LayoutProvider#getActiveLayout()}.
- */
-public class EdgeSwipeHandlerLayoutDelegate extends EmptyEdgeSwipeHandler {
-    private final LayoutProvider mLayoutProvider;
-
-    /**
-     * Creates an instance of the {@link EdgeSwipeHandlerLayoutDelegate}.
-     * @param provider A {@link LayoutProvider} instance.
-     */
-    public EdgeSwipeHandlerLayoutDelegate(LayoutProvider provider) {
-        mLayoutProvider = provider;
-    }
-
-    @Override
-    public void swipeStarted(@ScrollDirection int direction, float x, float y) {
-        if (mLayoutProvider.getActiveLayout() == null) return;
-        mLayoutProvider.getActiveLayout().swipeStarted(LayoutManager.time(), direction, x, y);
-    }
-
-    @Override
-    public void swipeUpdated(float x, float y, float dx, float dy, float tx, float ty) {
-        if (mLayoutProvider.getActiveLayout() == null) return;
-        mLayoutProvider.getActiveLayout().swipeUpdated(LayoutManager.time(), x, y, dx, dy, tx, ty);
-    }
-
-    @Override
-    public void swipeFinished() {
-        if (mLayoutProvider.getActiveLayout() == null) return;
-        mLayoutProvider.getActiveLayout().swipeFinished(LayoutManager.time());
-    }
-
-    @Override
-    public void swipeFlingOccurred(float x, float y, float tx, float ty, float vx, float vy) {
-        if (mLayoutProvider.getActiveLayout() == null) return;
-        mLayoutProvider.getActiveLayout().swipeFlingOccurred(
-                LayoutManager.time(), x, y, tx, ty, vx, vy);
-    }
-}
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/Layout.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/Layout.java
index 7e9a04f..ca1901f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/Layout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/Layout.java
@@ -18,7 +18,6 @@
 import org.chromium.chrome.browser.compositor.layouts.components.VirtualView;
 import org.chromium.chrome.browser.compositor.layouts.content.TabContentManager;
 import org.chromium.chrome.browser.compositor.layouts.eventfilter.EventFilter;
-import org.chromium.chrome.browser.compositor.layouts.eventfilter.ScrollDirection;
 import org.chromium.chrome.browser.compositor.overlays.SceneOverlay;
 import org.chromium.chrome.browser.compositor.scene_layer.SceneLayer;
 import org.chromium.chrome.browser.compositor.scene_layer.SceneOverlayLayer;
@@ -486,55 +485,6 @@
     public void detachViews() { }
 
     /**
-     * Called when the swipe animation get initiated. It gives a chance to initialize everything.
-     * @param time      The current time of the app in ms.
-     * @param direction The direction the swipe is in.
-     * @param x         The horizontal coordinate the swipe started at in dp.
-     * @param y         The vertical coordinate the swipe started at in dp.
-     */
-    public void swipeStarted(long time, @ScrollDirection int direction, float x, float y) {}
-
-    /**
-     * Updates a swipe gesture.
-     * @param time The current time of the app in ms.
-     * @param x    The horizontal coordinate the swipe is currently at in dp.
-     * @param y    The vertical coordinate the swipe is currently at in dp.
-     * @param dx   The horizontal delta since the last update in dp.
-     * @param dy   The vertical delta since the last update in dp.
-     * @param tx   The horizontal difference between the start and the current position in dp.
-     * @param ty   The vertical difference between the start and the current position in dp.
-     */
-    public void swipeUpdated(long time, float x, float y, float dx, float dy, float tx, float ty) {
-    }
-
-    /**
-     * Called when the swipe ends; most likely on finger up event. It gives a chance to start
-     * an ending animation to exit the mode gracefully.
-     * @param time The current time of the app in ms.
-     */
-    public void swipeFinished(long time) { }
-
-    /**
-     * Called when the user has cancelled a swipe; most likely if they have dragged their finger
-     * back to the starting position.  Some handlers will throw swipeFinished() instead.
-     * @param time The current time of the app in ms.
-     */
-    public void swipeCancelled(long time) { }
-
-    /**
-     * Fling from a swipe gesture.
-     * @param time The current time of the app in ms.
-     * @param x    The horizontal coordinate the swipe is currently at in dp.
-     * @param y    The vertical coordinate the swipe is currently at in dp.
-     * @param tx   The horizontal difference between the start and the current position in dp.
-     * @param ty   The vertical difference between the start and the current position in dp.
-     * @param vx   The horizontal velocity of the fling.
-     * @param vy   The vertical velocity of the fling.
-     */
-    public void swipeFlingOccurred(
-            long time, float x, float y, float tx, float ty, float vx, float vy) {}
-
-    /**
      * Forces the current animation to finish and broadcasts the proper event.
      */
     protected void forceAnimationToFinish() {}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChrome.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChrome.java
index c80cabb..5eeab1f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChrome.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChrome.java
@@ -15,6 +15,7 @@
 import org.chromium.chrome.browser.compositor.layouts.components.VirtualView;
 import org.chromium.chrome.browser.compositor.layouts.content.TabContentManager;
 import org.chromium.chrome.browser.compositor.layouts.eventfilter.EdgeSwipeHandler;
+import org.chromium.chrome.browser.compositor.layouts.eventfilter.EmptyEdgeSwipeHandler;
 import org.chromium.chrome.browser.compositor.layouts.eventfilter.ScrollDirection;
 import org.chromium.chrome.browser.compositor.layouts.phone.StackLayout;
 import org.chromium.chrome.browser.compositor.overlays.SceneOverlay;
@@ -72,7 +73,7 @@
         mOverviewModeObservers = new ObserverList<OverviewModeObserver>();
 
         // Build Event Filter Handlers
-        mToolbarSwipeHandler = createToolbarSwipeHandler(this);
+        mToolbarSwipeHandler = new ToolbarSwipeHandler();
 
         // Build Layouts
         mOverviewListLayout = new OverviewListLayout(context, this, renderHost);
@@ -149,16 +150,6 @@
     }
 
     /**
-     * Meant to be overridden by child classes for when they need to extend the toolbar side swipe
-     * functionality.
-     * @param provider A {@link LayoutProvider} instance.
-     * @return         A {@link ToolbarSwipeHandler} instance that will be used by internal layouts.
-     */
-    protected ToolbarSwipeHandler createToolbarSwipeHandler(LayoutProvider provider) {
-        return new ToolbarSwipeHandler(provider);
-    }
-
-    /**
      * Simulates a click on the view at the specified pixel offset
      * from the top left of the view.
      * This is used by UI tests.
@@ -390,7 +381,7 @@
     /**
      * A {@link EdgeSwipeHandler} meant to respond to edge events for the toolbar.
      */
-    protected class ToolbarSwipeHandler extends EdgeSwipeHandlerLayoutDelegate {
+    protected class ToolbarSwipeHandler extends EmptyEdgeSwipeHandler {
         /** The scroll direction of the current gesture. */
         private @ScrollDirection int mScrollDirection;
 
@@ -400,14 +391,6 @@
          */
         private static final float SWIPE_RANGE_DEG = 25;
 
-        /**
-         * Creates an instance of the {@link ToolbarSwipeHandler}.
-         * @param provider A {@link LayoutProvider} instance.
-         */
-        public ToolbarSwipeHandler(LayoutProvider provider) {
-            super(provider);
-        }
-
         @Override
         public void swipeStarted(@ScrollDirection int direction, float x, float y) {
             mScrollDirection = ScrollDirection.UNKNOWN;
@@ -415,11 +398,11 @@
 
         @Override
         public void swipeUpdated(float x, float y, float dx, float dy, float tx, float ty) {
-            if (getActiveLayout() == null) return;
+            if (mToolbarSwipeLayout == null) return;
 
             // If scroll direction has been computed, send the event to super.
             if (mScrollDirection != ScrollDirection.UNKNOWN) {
-                super.swipeUpdated(x, y, dx, dy, tx, ty);
+                mToolbarSwipeLayout.swipeUpdated(time(), x, y, dx, dy, tx, ty);
                 return;
             }
 
@@ -435,7 +418,19 @@
                 startShowing(mToolbarSwipeLayout, true);
             }
 
-            super.swipeStarted(mScrollDirection, x, y);
+            mToolbarSwipeLayout.swipeStarted(time(), mScrollDirection, x, y);
+        }
+
+        @Override
+        public void swipeFinished() {
+            if (mToolbarSwipeLayout == null) return;
+            mToolbarSwipeLayout.swipeFinished(time());
+        }
+
+        @Override
+        public void swipeFlingOccurred(float x, float y, float tx, float ty, float vx, float vy) {
+            if (mToolbarSwipeLayout == null) return;
+            mToolbarSwipeLayout.swipeFlingOccurred(time(), x, y, tx, ty, vx, vy);
         }
 
         /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/ToolbarSwipeLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/ToolbarSwipeLayout.java
index 95c03b5..5561dc6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/ToolbarSwipeLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/ToolbarSwipeLayout.java
@@ -131,7 +131,6 @@
         prepareLayoutTabForSwipe(mFromTab, false);
     }
 
-    @Override
     public void swipeStarted(long time, @ScrollDirection int direction, float x, float y) {
         if (mTabModelSelector == null || mToTab != null || direction == ScrollDirection.DOWN) {
             return;
@@ -203,13 +202,11 @@
         layoutTab.setAnonymizeToolbar(anonymizeToolbar && ANONYMIZE_NON_FOCUSED_TAB);
     }
 
-    @Override
     public void swipeUpdated(long time, float x, float y, float dx, float dy, float tx, float ty) {
         mOffsetTarget = MathUtils.clamp(mOffsetStart + tx, 0, getWidth()) - mOffsetStart;
         requestUpdate();
     }
 
-    @Override
     public void swipeFlingOccurred(
             long time, float x, float y, float tx, float ty, float vx, float vy) {
         // Use the velocity to add on final step which simulate a fling.
@@ -220,7 +217,6 @@
         swipeUpdated(time, x, y, 0, 0, tx + kickX, ty + kickY);
     }
 
-    @Override
     public void swipeFinished(long time) {
         if (mFromTab == null || mTabModelSelector == null) return;
 
@@ -261,7 +257,6 @@
         }
     }
 
-    @Override
     public void swipeCancelled(long time) {
         swipeFinished(time);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/StackLayoutBase.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/StackLayoutBase.java
index cdc8e6b..5b4b090 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/StackLayoutBase.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/StackLayoutBase.java
@@ -32,7 +32,6 @@
 import org.chromium.chrome.browser.compositor.layouts.eventfilter.EventFilter;
 import org.chromium.chrome.browser.compositor.layouts.eventfilter.GestureEventFilter;
 import org.chromium.chrome.browser.compositor.layouts.eventfilter.GestureHandler;
-import org.chromium.chrome.browser.compositor.layouts.eventfilter.ScrollDirection;
 import org.chromium.chrome.browser.compositor.layouts.phone.stack.NonOverlappingStack;
 import org.chromium.chrome.browser.compositor.layouts.phone.stack.OverlappingStack;
 import org.chromium.chrome.browser.compositor.layouts.phone.stack.Stack;
@@ -851,32 +850,6 @@
     }
 
     @Override
-    public void swipeStarted(long time, @ScrollDirection int direction, float x, float y) {
-        mStacks.get(getTabStackIndex()).swipeStarted(time, direction, x, y);
-    }
-
-    @Override
-    public void swipeUpdated(long time, float x, float y, float dx, float dy, float tx, float ty) {
-        mStacks.get(getTabStackIndex()).swipeUpdated(time, x, y, dx, dy, tx, ty);
-    }
-
-    @Override
-    public void swipeFinished(long time) {
-        mStacks.get(getTabStackIndex()).swipeFinished(time);
-    }
-
-    @Override
-    public void swipeCancelled(long time) {
-        mStacks.get(getTabStackIndex()).swipeCancelled(time);
-    }
-
-    @Override
-    public void swipeFlingOccurred(
-            long time, float x, float y, float tx, float ty, float vx, float vy) {
-        mStacks.get(getTabStackIndex()).swipeFlingOccurred(time, x, y, tx, ty, vx, vy);
-    }
-
-    @Override
     public void notifySizeChanged(float width, float height, int orientation) {
         mWidth = width;
         mHeight = height;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/ModuleLoader.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/ModuleLoader.java
index e7615f8..53b32ba 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/ModuleLoader.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/ModuleLoader.java
@@ -6,6 +6,7 @@
 
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.os.IBinder;
 import android.os.Process;
@@ -38,17 +39,19 @@
     public ModuleLoader(ComponentName componentName) {
         mComponentName = componentName;
         String packageName = componentName.getPackageName();
-        String version = "";
+        int versionCode = 0;
+        String versionName = "";
         try {
-            version = ContextUtils.getApplicationContext()
-                              .getPackageManager()
-                              .getPackageInfo(packageName, 0)
-                              .versionName;
+            PackageInfo info = ContextUtils.getApplicationContext()
+                                     .getPackageManager()
+                                     .getPackageInfo(packageName, 0);
+            versionCode = info.versionCode;
+            versionName = info.versionName;
         } catch (PackageManager.NameNotFoundException ignored) {
             // Ignore the exception. Failure to find the package name will be handled in
             // getModuleContext() below.
         }
-        mModuleId = packageName + ":" + version;
+        mModuleId = String.format("%s v%s (%s)", packageName, versionCode, versionName);
     }
 
     public ComponentName getComponentName() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListView.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListView.java
index 1a95bb09..30e0354 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListView.java
@@ -150,7 +150,8 @@
                     outRect.top = mImagePaddingPx;
                     outRect.bottom = mImagePaddingPx;
                     break;
-                case ListUtils.ViewType.VIDEO:
+                case ListUtils.ViewType.VIDEO: // Intentional fallthrough.
+                case ListUtils.ViewType.IN_PROGRESS_VIDEO:
                     outRect.left = mPrefetchHorizontalPaddingPx;
                     outRect.right = mPrefetchHorizontalPaddingPx;
                     outRect.bottom = mPrefetchHorizontalPaddingPx;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/ListUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/ListUtils.java
index 652ee4e..d0b2d62 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/ListUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/ListUtils.java
@@ -26,7 +26,7 @@
     /** The potential types of list items that could be displayed. */
     @IntDef({ViewType.DATE, ViewType.IN_PROGRESS, ViewType.GENERIC, ViewType.VIDEO, ViewType.IMAGE,
             ViewType.CUSTOM_VIEW, ViewType.PREFETCH, ViewType.SECTION_HEADER,
-            ViewType.SEPARATOR_DATE, ViewType.SEPARATOR_SECTION})
+            ViewType.SEPARATOR_DATE, ViewType.SEPARATOR_SECTION, ViewType.IN_PROGRESS_VIDEO})
     @Retention(RetentionPolicy.SOURCE)
     public @interface ViewType {
         int DATE = 0;
@@ -39,6 +39,7 @@
         int SECTION_HEADER = 7;
         int SEPARATOR_DATE = 8;
         int SEPARATOR_SECTION = 9;
+        int IN_PROGRESS_VIDEO = 10;
     }
 
     /** Converts a given list of {@link ListItem}s to a list of {@link OfflineItem}s. */
@@ -72,25 +73,24 @@
                 OfflineItemListItem offlineItem = (OfflineItemListItem) item;
 
                 if (offlineItem.item.isSuggested) return ViewType.PREFETCH;
-                if (offlineItem.item.state == OfflineItemState.IN_PROGRESS
+
+                boolean inProgress = offlineItem.item.state == OfflineItemState.IN_PROGRESS
                         || offlineItem.item.state == OfflineItemState.PAUSED
                         || offlineItem.item.state == OfflineItemState.INTERRUPTED
                         || offlineItem.item.state == OfflineItemState.PENDING
-                        || offlineItem.item.state == OfflineItemState.FAILED) {
-                    return ViewType.IN_PROGRESS;
-                }
+                        || offlineItem.item.state == OfflineItemState.FAILED;
 
                 switch (offlineItem.item.filter) {
                     case OfflineItemFilter.FILTER_VIDEO:
-                        return ViewType.VIDEO;
+                        return inProgress ? ViewType.IN_PROGRESS_VIDEO : ViewType.VIDEO;
                     case OfflineItemFilter.FILTER_IMAGE:
-                        return ViewType.IMAGE;
+                        return inProgress ? ViewType.IN_PROGRESS : ViewType.IMAGE;
                     // case OfflineItemFilter.FILTER_PAGE:
                     // case OfflineItemFilter.FILTER_AUDIO:
                     // case OfflineItemFilter.FILTER_OTHER:
                     // case OfflineItemFilter.FILTER_DOCUMENT:
                     default:
-                        return ViewType.GENERIC;
+                        return inProgress ? ViewType.IN_PROGRESS : ViewType.GENERIC;
                 }
             } else {
                 return ViewType.DATE;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/UiUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/UiUtils.java
index 5103042..35ca576 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/UiUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/UiUtils.java
@@ -13,9 +13,13 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.download.DownloadUtils;
 import org.chromium.chrome.browser.download.home.filter.Filters;
+import org.chromium.chrome.browser.download.home.list.view.CircularProgressView;
+import org.chromium.chrome.browser.download.home.list.view.CircularProgressView.UiState;
 import org.chromium.chrome.browser.util.MathUtils;
 import org.chromium.components.offline_items_collection.OfflineItem;
+import org.chromium.components.offline_items_collection.OfflineItem.Progress;
 import org.chromium.components.offline_items_collection.OfflineItemFilter;
+import org.chromium.components.offline_items_collection.OfflineItemState;
 import org.chromium.components.url_formatter.UrlFormatter;
 
 import java.util.Calendar;
@@ -126,4 +130,67 @@
         return DownloadUtils.getIconResId(Filters.offlineItemFilterToDownloadFilter(item.filter),
                 DownloadUtils.IconSize.DP_24);
     }
+
+    /**
+     * Populates a {@link CircularProgressView} based on the contents of an {@link OfflineItem}.
+     * This is a helper glue method meant to consolidate the setting of {@link CircularProgressView}
+     * state.
+     * @param view The {@link CircularProgressView} to update.
+     * @param item The {@link OfflineItem} to use as the source of the update state.
+     */
+    public static void setProgressForOfflineItem(CircularProgressView view, OfflineItem item) {
+        Progress progress = item.progress;
+        final boolean indeterminate = progress != null && progress.isIndeterminate();
+        final int determinateProgress = progress != null ? progress.getPercentage() : 0;
+        final int activeProgress =
+                indeterminate ? CircularProgressView.INDETERMINATE : determinateProgress;
+        final int inactiveProgress = indeterminate ? 0 : determinateProgress;
+
+        @UiState
+        int shownState;
+        int shownProgress;
+
+        switch (item.state) {
+            case OfflineItemState.PENDING: // Intentional fallthrough.
+            case OfflineItemState.IN_PROGRESS:
+                shownState = CircularProgressView.UiState.RUNNING;
+                break;
+            case OfflineItemState.FAILED: // Intentional fallthrough.
+            case OfflineItemState.CANCELLED:
+                shownState = CircularProgressView.UiState.RETRY;
+                break;
+            case OfflineItemState.PAUSED:
+            case OfflineItemState.INTERRUPTED:
+                shownState = CircularProgressView.UiState.PAUSED;
+                break;
+            case OfflineItemState.COMPLETE: // Intentional fallthrough.
+            default:
+                assert false : "Unexpected state for progress bar.";
+                shownState = CircularProgressView.UiState.RETRY;
+                break;
+        }
+
+        switch (item.state) {
+            case OfflineItemState.INTERRUPTED: // Intentional fallthrough.
+            case OfflineItemState.PAUSED: // Intentional fallthrough.
+            case OfflineItemState.PENDING:
+                shownProgress = inactiveProgress;
+                break;
+            case OfflineItemState.IN_PROGRESS:
+                shownProgress = activeProgress;
+                break;
+            case OfflineItemState.FAILED: // Intentional fallthrough.
+            case OfflineItemState.CANCELLED: // Intentional fallthrough.
+                shownProgress = 0;
+                break;
+            case OfflineItemState.COMPLETE: // Intentional fallthrough.
+            default:
+                assert false : "Unexpected state for progress bar.";
+                shownProgress = 0;
+                break;
+        }
+
+        view.setState(shownState);
+        view.setProgress(shownProgress);
+    }
 }
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/InProgressVideoViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/InProgressVideoViewHolder.java
new file mode 100644
index 0000000..5a12ed8
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/InProgressVideoViewHolder.java
@@ -0,0 +1,83 @@
+// 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.
+
+package org.chromium.chrome.browser.download.home.list.holder;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import org.chromium.chrome.browser.download.DownloadUtils;
+import org.chromium.chrome.browser.download.home.list.ListItem;
+import org.chromium.chrome.browser.download.home.list.ListProperties;
+import org.chromium.chrome.browser.download.home.list.UiUtils;
+import org.chromium.chrome.browser.download.home.list.view.CircularProgressView;
+import org.chromium.chrome.browser.modelutil.PropertyModel;
+import org.chromium.chrome.browser.widget.TintedImageButton;
+import org.chromium.chrome.download.R;
+import org.chromium.components.offline_items_collection.OfflineItem;
+import org.chromium.components.offline_items_collection.OfflineItemState;
+
+/**
+ * A {@link RecyclerView.ViewHolder} specifically meant to display an in-progress video {@code
+ * OfflineItem}.
+ */
+public class InProgressVideoViewHolder extends ListItemViewHolder {
+    private final TextView mTitle;
+    private final TextView mCaption;
+    private final CircularProgressView mActionButton;
+    private final TintedImageButton mCancelButton;
+
+    /**
+     * Creates a new {@link InProgressViewHolder} instance.
+     */
+    public static InProgressVideoViewHolder create(ViewGroup parent) {
+        View view = LayoutInflater.from(parent.getContext())
+                            .inflate(R.layout.download_manager_in_progress_video_item, null);
+        return new InProgressVideoViewHolder(view);
+    }
+
+    /** Constructor. */
+    public InProgressVideoViewHolder(View view) {
+        super(view);
+
+        mTitle = view.findViewById(R.id.title);
+        mCaption = view.findViewById(R.id.caption);
+        mActionButton = view.findViewById(R.id.action_button);
+        mCancelButton = view.findViewById(R.id.cancel_button);
+    }
+
+    // ListItemViewHolder implementation.
+    @Override
+    public void bind(PropertyModel properties, ListItem item) {
+        OfflineItem offlineItem = ((ListItem.OfflineItemListItem) item).item;
+
+        mTitle.setText(offlineItem.title);
+        mCancelButton.setOnClickListener(
+                v -> properties.get(ListProperties.CALLBACK_CANCEL).onResult(offlineItem));
+        // TODO(shaktisahu): Create status string for the new specs.
+        mCaption.setText(DownloadUtils.getProgressTextForNotification(offlineItem.progress));
+
+        UiUtils.setProgressForOfflineItem(mActionButton, offlineItem);
+        mActionButton.setOnClickListener(view -> {
+            switch (offlineItem.state) {
+                case OfflineItemState.IN_PROGRESS: // Intentional fallthrough.
+                case OfflineItemState.PENDING:
+                    properties.get(ListProperties.CALLBACK_PAUSE).onResult(offlineItem);
+                    break;
+                case OfflineItemState.INTERRUPTED: // Intentional fallthrough.
+                case OfflineItemState.PAUSED:
+                    properties.get(ListProperties.CALLBACK_RESUME).onResult(offlineItem);
+                    break;
+                case OfflineItemState.COMPLETE: // Intentional fallthrough.
+                case OfflineItemState.CANCELLED: // Intentional fallthrough.
+                case OfflineItemState.FAILED:
+                    // TODO(883387): Support retrying failed downloads.
+                    properties.get(ListProperties.CALLBACK_RESUME).onResult(offlineItem);
+                    break;
+            }
+        });
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/InProgressViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/InProgressViewHolder.java
index 86f5ef8..186e8df 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/InProgressViewHolder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/InProgressViewHolder.java
@@ -7,15 +7,17 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.ProgressBar;
 import android.widget.TextView;
 
 import org.chromium.chrome.browser.download.DownloadUtils;
 import org.chromium.chrome.browser.download.home.list.ListItem;
 import org.chromium.chrome.browser.download.home.list.ListProperties;
+import org.chromium.chrome.browser.download.home.list.UiUtils;
+import org.chromium.chrome.browser.download.home.list.view.CircularProgressView;
 import org.chromium.chrome.browser.modelutil.PropertyModel;
 import org.chromium.chrome.browser.widget.TintedImageButton;
 import org.chromium.chrome.download.R;
+import org.chromium.components.offline_items_collection.OfflineItem;
 import org.chromium.components.offline_items_collection.OfflineItemState;
 
 /**
@@ -23,10 +25,9 @@
  * OfflineItem}.
  */
 public class InProgressViewHolder extends ListItemViewHolder {
-    private final ProgressBar mProgressBar;
     private final TextView mTitle;
     private final TextView mCaption;
-    private final TintedImageButton mPauseResumeButton;
+    private final CircularProgressView mActionButton;
     private final TintedImageButton mCancelButton;
 
     /**
@@ -41,54 +42,42 @@
     /** Constructor. */
     public InProgressViewHolder(View view) {
         super(view);
-        mProgressBar = view.findViewById(R.id.progress_bar);
+
         mTitle = view.findViewById(R.id.title);
         mCaption = view.findViewById(R.id.caption);
-        mPauseResumeButton = view.findViewById(R.id.pause_button);
+        mActionButton = view.findViewById(R.id.action_button);
         mCancelButton = view.findViewById(R.id.cancel_button);
     }
 
     // ListItemViewHolder implementation.
     @Override
     public void bind(PropertyModel properties, ListItem item) {
-        ListItem.OfflineItemListItem offlineItem = (ListItem.OfflineItemListItem) item;
-        mTitle.setText(offlineItem.item.title);
+        OfflineItem offlineItem = ((ListItem.OfflineItemListItem) item).item;
+
+        mTitle.setText(offlineItem.title);
         mCancelButton.setOnClickListener(
-                v -> properties.get(ListProperties.CALLBACK_CANCEL).onResult(offlineItem.item));
-
-        if (offlineItem.item.state == OfflineItemState.PAUSED) {
-            mPauseResumeButton.setImageResource(R.drawable.ic_play_arrow_white_24dp);
-            mPauseResumeButton.setContentDescription(
-                    itemView.getContext().getString(R.string.download_notification_resume_button));
-        } else {
-            mPauseResumeButton.setImageResource(R.drawable.ic_pause_white_24dp);
-            mPauseResumeButton.setContentDescription(
-                    itemView.getContext().getString(R.string.download_notification_pause_button));
-        }
-
+                v -> properties.get(ListProperties.CALLBACK_CANCEL).onResult(offlineItem));
         // TODO(shaktisahu): Create status string for the new specs.
-        mCaption.setText(DownloadUtils.getProgressTextForNotification(offlineItem.item.progress));
-        mPauseResumeButton.setOnClickListener(view -> {
-            if (offlineItem.item.state == OfflineItemState.PAUSED) {
-                properties.get(ListProperties.CALLBACK_RESUME).onResult(offlineItem.item);
-            } else {
-                properties.get(ListProperties.CALLBACK_PAUSE).onResult(offlineItem.item);
+        mCaption.setText(DownloadUtils.getProgressTextForNotification(offlineItem.progress));
+
+        UiUtils.setProgressForOfflineItem(mActionButton, offlineItem);
+        mActionButton.setOnClickListener(view -> {
+            switch (offlineItem.state) {
+                case OfflineItemState.IN_PROGRESS: // Intentional fallthrough.
+                case OfflineItemState.PENDING:
+                    properties.get(ListProperties.CALLBACK_PAUSE).onResult(offlineItem);
+                    break;
+                case OfflineItemState.INTERRUPTED: // Intentional fallthrough.
+                case OfflineItemState.PAUSED:
+                    properties.get(ListProperties.CALLBACK_RESUME).onResult(offlineItem);
+                    break;
+                case OfflineItemState.COMPLETE: // Intentional fallthrough.
+                case OfflineItemState.CANCELLED: // Intentional fallthrough.
+                case OfflineItemState.FAILED:
+                    // TODO(883387): Support retrying failed downloads.
+                    properties.get(ListProperties.CALLBACK_RESUME).onResult(offlineItem);
+                    break;
             }
         });
-
-        boolean showIndeterminate = offlineItem.item.progress.isIndeterminate()
-                && offlineItem.item.state != OfflineItemState.PAUSED
-                && offlineItem.item.state != OfflineItemState.PENDING;
-        if (showIndeterminate) {
-            mProgressBar.setIndeterminate(true);
-            mProgressBar.setIndeterminateDrawable(itemView.getContext().getResources().getDrawable(
-                    R.drawable.download_circular_progress_bar));
-        } else {
-            mProgressBar.setIndeterminate(false);
-        }
-
-        if (!offlineItem.item.progress.isIndeterminate()) {
-            mProgressBar.setProgress(offlineItem.item.progress.getPercentage());
-        }
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/ListItemViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/ListItemViewHolder.java
index cf54202..12365a6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/ListItemViewHolder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/holder/ListItemViewHolder.java
@@ -49,6 +49,8 @@
                 return SeparatorViewHolder.create(parent, true);
             case ListUtils.ViewType.SEPARATOR_SECTION:
                 return SeparatorViewHolder.create(parent, false);
+            case ListUtils.ViewType.IN_PROGRESS_VIDEO:
+                return InProgressVideoViewHolder.create(parent);
         }
 
         assert false;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/view/CircularProgressView.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/view/CircularProgressView.java
new file mode 100644
index 0000000..f9558c8
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/view/CircularProgressView.java
@@ -0,0 +1,165 @@
+// 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.
+
+package org.chromium.chrome.browser.download.home.list.view;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Animatable;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.DrawableRes;
+import android.support.annotation.IntDef;
+import android.support.annotation.StringRes;
+import android.support.annotation.StyleableRes;
+import android.support.v7.content.res.AppCompatResources;
+import android.support.v7.widget.AppCompatImageButton;
+import android.util.AttributeSet;
+
+import org.chromium.chrome.browser.util.MathUtils;
+import org.chromium.chrome.download.R;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * A representation of a progress bar that supports (1) an indeterminate state, (2) a determinate
+ * state, and (3) running, paused, and retry states.
+ *
+ * The determinate {@link Drawable} will have it's level set via {@link Drawable#setLevel(int)}
+ * based on the progress (0 - 10,000).
+ *
+ * The indeterminate {@link Drawable} supports {@link Animatable} drawables and the animation will
+ * be started/stopped when shown/hidden respectively.
+ */
+public class CircularProgressView extends AppCompatImageButton {
+    /**
+     * The value to use with {@link #setProgress(int)} to specify that the indeterminate
+     * {@link Drawable} should be used.
+     */
+    public static final int INDETERMINATE = -1;
+
+    /** The various states this {@link CircularProgressView} can be in. */
+    @IntDef({UiState.RUNNING, UiState.PAUSED, UiState.RETRY})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface UiState {
+        /** This progress bar will look like it is actively running based on the XML drawable. */
+        int RUNNING = 0;
+
+        /** This progress bar will look like it is paused based on the XML drawable. */
+        int PAUSED = 1;
+
+        /** This progress bar will look like it is able to be retried based on the XML drawable. */
+        int RETRY = 2;
+    }
+
+    private static final int MAX_LEVEL = 10000;
+
+    private final Drawable mIndeterminateProgress;
+    private final Drawable mDeterminateProgress;
+    private final Drawable mResumeButtonSrc;
+    private final Drawable mPauseButtonSrc;
+    private final Drawable mRetryButtonSrc;
+
+    // Tracking this here as the API {@link View#getForeground()} is not available in all supported
+    // Android versions.
+    private Drawable mForegroundDrawable;
+
+    /**
+     * Creates an instance of a {@link CircularProgressView}.
+     * @param context  The {@link Context} to use.
+     * @param attrs    An {@link AttributeSet} instance.
+     */
+    public CircularProgressView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+
+        TypedArray types =
+                context.obtainStyledAttributes(attrs, R.styleable.CircularProgressView, 0, 0);
+        try {
+            mIndeterminateProgress = getDrawable(
+                    context, types, R.styleable.CircularProgressView_indeterminateProgress);
+            mDeterminateProgress = getDrawable(
+                    context, types, R.styleable.CircularProgressView_determinateProgress);
+            mResumeButtonSrc =
+                    getDrawable(context, types, R.styleable.CircularProgressView_resumeSrc);
+            mPauseButtonSrc =
+                    getDrawable(context, types, R.styleable.CircularProgressView_pauseSrc);
+            mRetryButtonSrc =
+                    getDrawable(context, types, R.styleable.CircularProgressView_retrySrc);
+        } finally {
+            types.recycle();
+        }
+    }
+
+    /**
+     * Sets the progress of this {@link CircularProgressView} to {@code progress}.  If {@code
+     * progress} is {@link #INDETERMINATE} an indeterminate {@link Drawable} will be used.
+     * Otherwise the value will be clamped between 0 and 100 and a determinate {@link Drawable} will
+     * be used and have it's level set via {@link Drawable#setLevel(int)}.
+     *
+     * @param progress The progress value (0 to 100 or {@link #INDETERMINATE}) to show.
+     */
+    public void setProgress(int progress) {
+        if (progress == INDETERMINATE) {
+            if (mForegroundDrawable != mIndeterminateProgress) {
+                setForeground(mIndeterminateProgress);
+                if (mIndeterminateProgress instanceof Animatable) {
+                    ((Animatable) mIndeterminateProgress).start();
+                }
+            }
+        } else {
+            progress = MathUtils.clamp(progress, 0, 100);
+            mDeterminateProgress.setLevel(progress * MAX_LEVEL / 100);
+            setForeground(mDeterminateProgress);
+        }
+
+        // Stop any animation that might have previously been running.
+        if (mForegroundDrawable != mIndeterminateProgress) {
+            ((Animatable) mIndeterminateProgress).stop();
+        }
+    }
+
+    /**
+     * The state this {@link CircularProgressView} should show.  This can be one of the three
+     * UiStates defined above.  This will determine what the action drawable is in the view.
+     * @param state The UiState to use.
+     */
+    public void setState(@UiState int state) {
+        Drawable imageDrawable;
+        @StringRes
+        int contentDescription;
+        switch (state) {
+            case UiState.RUNNING:
+                imageDrawable = mPauseButtonSrc;
+                contentDescription = R.string.download_notification_pause_button;
+                break;
+            case UiState.PAUSED:
+                imageDrawable = mResumeButtonSrc;
+                contentDescription = R.string.download_notification_resume_button;
+                break;
+            case UiState.RETRY:
+            default:
+                imageDrawable = mRetryButtonSrc;
+                contentDescription = R.string.download_notification_resume_button;
+                break;
+        }
+
+        setImageDrawable(imageDrawable);
+        setContentDescription(getContext().getText(contentDescription));
+    }
+
+    // View implementation.
+    @Override
+    public void setForeground(Drawable foreground) {
+        mForegroundDrawable = foreground;
+        super.setForeground(foreground);
+    }
+
+    private static final Drawable getDrawable(
+            Context context, TypedArray attrs, @StyleableRes int attrId) {
+        @DrawableRes
+        int resId = attrs.getResourceId(attrId, -1);
+        if (resId == -1) return null;
+        return AppCompatResources.getDrawable(context, resId);
+    }
+}
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/view/LoadingBackground.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/view/LoadingBackground.java
index 7a4009e..69ecf82 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/view/LoadingBackground.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/view/LoadingBackground.java
@@ -10,7 +10,7 @@
 import android.support.graphics.drawable.AnimatedVectorDrawableCompat;
 import android.widget.ImageView;
 
-import org.chromium.chrome.R;
+import org.chromium.chrome.download.R;
 
 /**
  * A helper class to display the loading image and animation for image and video items. This class
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index 6d1948c..5a25504 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -205,7 +205,6 @@
   "java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchQuickActionControl.java",
   "java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchTermControl.java",
   "java/src/org/chromium/chrome/browser/compositor/layouts/ChromeAnimation.java",
-  "java/src/org/chromium/chrome/browser/compositor/layouts/EdgeSwipeHandlerLayoutDelegate.java",
   "java/src/org/chromium/chrome/browser/compositor/layouts/EmptyOverviewModeObserver.java",
   "java/src/org/chromium/chrome/browser/compositor/layouts/Layout.java",
   "java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManager.java",
@@ -509,6 +508,7 @@
   "java/src/org/chromium/chrome/browser/download/home/list/holder/DateViewHolder.java",
   "java/src/org/chromium/chrome/browser/download/home/list/holder/GenericViewHolder.java",
   "java/src/org/chromium/chrome/browser/download/home/list/holder/ImageViewHolder.java",
+  "java/src/org/chromium/chrome/browser/download/home/list/holder/InProgressVideoViewHolder.java",
   "java/src/org/chromium/chrome/browser/download/home/list/holder/InProgressViewHolder.java",
   "java/src/org/chromium/chrome/browser/download/home/list/holder/ListItemViewHolder.java",
   "java/src/org/chromium/chrome/browser/download/home/list/holder/MoreButtonViewHolder.java",
@@ -517,6 +517,7 @@
   "java/src/org/chromium/chrome/browser/download/home/list/holder/SeparatorViewHolder.java",
   "java/src/org/chromium/chrome/browser/download/home/list/holder/ThumbnailAwareViewHolder.java",
   "java/src/org/chromium/chrome/browser/download/home/list/holder/VideoViewHolder.java",
+  "java/src/org/chromium/chrome/browser/download/home/list/view/CircularProgressView.java",
   "java/src/org/chromium/chrome/browser/download/home/list/ListProperties.java",
   "java/src/org/chromium/chrome/browser/download/home/list/ListPropertyViewBinder.java",
   "java/src/org/chromium/chrome/browser/download/home/list/ListUtils.java",
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt
index bd2127c..2c70555 100644
--- a/chrome/android/profiles/newest.txt
+++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-71.0.3551.2_rc-r1.afdo.bz2
\ No newline at end of file
+chromeos-chrome-amd64-71.0.3552.4_rc-r1.afdo.bz2
\ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index dd12cc4..7de81e6 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -978,6 +978,8 @@
     "page_load_metrics/observers/security_state_page_load_metrics_observer.h",
     "page_load_metrics/observers/service_worker_page_load_metrics_observer.cc",
     "page_load_metrics/observers/service_worker_page_load_metrics_observer.h",
+    "page_load_metrics/observers/signed_exchange_page_load_metrics_observer.cc",
+    "page_load_metrics/observers/signed_exchange_page_load_metrics_observer.h",
     "page_load_metrics/observers/tab_restore_page_load_metrics_observer.cc",
     "page_load_metrics/observers/tab_restore_page_load_metrics_observer.h",
     "page_load_metrics/observers/ukm_page_load_metrics_observer.cc",
@@ -1445,6 +1447,8 @@
     "speech/tts_controller.h",
     "speech/tts_controller_impl.cc",
     "speech/tts_controller_impl.h",
+    "speech/tts_engine_delegate.cc",
+    "speech/tts_engine_delegate.h",
     "speech/tts_mac.mm",
     "speech/tts_message_filter.cc",
     "speech/tts_message_filter.h",
@@ -3754,6 +3758,10 @@
       "renderer_context_menu/context_menu_content_type_platform_app.h",
       "renderer_host/chrome_extension_message_filter.cc",
       "renderer_host/chrome_extension_message_filter.h",
+      "speech/extension_api/tts_engine_delegate_factory_impl.cc",
+      "speech/extension_api/tts_engine_delegate_factory_impl.h",
+      "speech/extension_api/tts_engine_delegate_impl.cc",
+      "speech/extension_api/tts_engine_delegate_impl.h",
       "speech/extension_api/tts_engine_extension_api.cc",
       "speech/extension_api/tts_engine_extension_api.h",
       "speech/extension_api/tts_engine_extension_observer.cc",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 829c421..45a10b1 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -1218,13 +1218,13 @@
 
 #if !defined(OS_ANDROID)
 const FeatureEntry::FeatureParam kProactiveTabFreezeAndDiscard_FreezeOnly[] = {
-    {resource_coordinator::
-         kProactiveTabFreezeAndDiscard_ShouldProactivelyDiscardParam,
+    {resource_coordinator::ProactiveTabFreezeAndDiscardParams::
+         kShouldProactivelyDiscard.name,
      "false"}};
 const FeatureEntry::FeatureParam
     kProactiveTabFreezeAndDiscard_FreezeAndDiscard[] = {
-        {resource_coordinator::
-             kProactiveTabFreezeAndDiscard_ShouldProactivelyDiscardParam,
+        {resource_coordinator::ProactiveTabFreezeAndDiscardParams::
+             kShouldProactivelyDiscard.name,
          "true"}};
 const FeatureEntry::FeatureVariation kProactiveTabFreezeAndDiscardVariations[] =
     {{"Freeze only", kProactiveTabFreezeAndDiscard_FreezeOnly,
@@ -4235,7 +4235,7 @@
      FEATURE_WITH_PARAMS_VALUE_TYPE(
          features::kProactiveTabFreezeAndDiscard,
          kProactiveTabFreezeAndDiscardVariations,
-         resource_coordinator::kProactiveTabFreezeAndDiscardFeatureName)},
+         features::kProactiveTabFreezeAndDiscard.name)},
     {"site-characteristics-database",
      flag_descriptions::kSiteCharacteristicsDatabaseName,
      flag_descriptions::kSiteCharacteristicsDatabaseDescription, kOsDesktop,
diff --git a/chrome/browser/android/tab_web_contents_delegate_android.cc b/chrome/browser/android/tab_web_contents_delegate_android.cc
index 6978405..d885497b 100644
--- a/chrome/browser/android/tab_web_contents_delegate_android.cc
+++ b/chrome/browser/android/tab_web_contents_delegate_android.cc
@@ -51,7 +51,6 @@
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/security_style_explanations.h"
 #include "content/public/browser/web_contents.h"
-#include "content/public/common/file_chooser_params.h"
 #include "content/public/common/media_stream_request.h"
 #include "jni/TabWebContentsDelegateAndroid_jni.h"
 #include "ppapi/buildflags/buildflags.h"
@@ -61,8 +60,8 @@
 using base::android::AttachCurrentThread;
 using base::android::JavaParamRef;
 using base::android::ScopedJavaLocalRef;
+using blink::mojom::FileChooserParams;
 using content::BluetoothChooser;
-using content::FileChooserParams;
 using content::WebContents;
 
 namespace {
diff --git a/chrome/browser/android/tab_web_contents_delegate_android.h b/chrome/browser/android/tab_web_contents_delegate_android.h
index 8a4f33c0..a8bd7d0 100644
--- a/chrome/browser/android/tab_web_contents_delegate_android.h
+++ b/chrome/browser/android/tab_web_contents_delegate_android.h
@@ -36,7 +36,7 @@
   ~TabWebContentsDelegateAndroid() override;
 
   void RunFileChooser(content::RenderFrameHost* render_frame_host,
-                      const content::FileChooserParams& params) override;
+                      const blink::mojom::FileChooserParams& params) override;
   std::unique_ptr<content::BluetoothChooser> RunBluetoothChooser(
       content::RenderFrameHost* frame,
       const content::BluetoothChooser::EventHandler& event_handler) override;
diff --git a/chrome/browser/android/vr/arcore_device/arcore_device.cc b/chrome/browser/android/vr/arcore_device/arcore_device.cc
index 60dcc33..b443bad 100644
--- a/chrome/browser/android/vr/arcore_device/arcore_device.cc
+++ b/chrome/browser/android/vr/arcore_device/arcore_device.cc
@@ -109,8 +109,12 @@
 
   is_paused_ = false;
 
-  if (!deferred_request_install_supported_arcore_callbacks_.empty())
-    CallDeferredRequestInstallSupportedARCore();
+  // TODO(crbug.com/883046): ResumeTracking does not fire after ArCore has been
+  // updated/installed or the update/installation was cancelled. Thus, we never
+  // handle queued up session requests.
+  if (on_request_arcore_install_or_update_result_callback_)
+    std::move(on_request_arcore_install_or_update_result_callback_)
+        .Run(!arcore_java_utils_->ShouldRequestInstallSupportedArCore());
 
   if (!is_arcore_gl_initialized_)
     return;
@@ -137,8 +141,8 @@
 
   is_arcore_gl_thread_initialized_ = true;
 
-  if (pending_request_session_callback_) {
-    std::move(pending_request_session_callback_).Run();
+  if (pending_request_ar_module_callback_) {
+    std::move(pending_request_ar_module_callback_).Run();
   }
 }
 
@@ -147,129 +151,161 @@
     mojom::XRRuntime::RequestSessionCallback callback) {
   DCHECK(IsOnMainThread());
 
+  // If we are currently handling another request defer this request. All
+  // deferred requests will be processed once handling is complete.
+  deferred_request_session_callbacks_.push_back(std::move(callback));
+  if (deferred_request_session_callbacks_.size() > 1) {
+    return;
+  }
+
   // TODO(https://crbug.com/849568): Instead of splitting the initialization
   // of this class between construction and RequestSession, perform all the
   // initialization at once on the first successful RequestSession call.
   if (!is_arcore_gl_thread_initialized_) {
-    if (pending_request_session_callback_) {
-      // We can only store one request at a time, so reject any further
-      // requests.
-      // TODO(http://crbug.com/836496) Make this queue session requests.
-      std::move(callback).Run(nullptr, nullptr);
-    }
-
-    pending_request_session_callback_ =
-        base::BindOnce(&ARCoreDevice::RequestSession, GetWeakPtr(),
-                       std::move(options), std::move(callback));
+    pending_request_ar_module_callback_ =
+        base::BindOnce(&ARCoreDevice::RequestArModule, GetWeakPtr(),
+                       options->render_process_id, options->render_frame_id,
+                       options->has_user_activation);
     return;
   }
 
-  auto preconditions_complete_callback =
-      base::BindOnce(&ARCoreDevice::OnRequestSessionPreconditionsComplete,
-                     GetWeakPtr(), std::move(callback));
-
-  SatisfyRequestSessionPreconditions(
-      options->render_process_id, options->render_frame_id,
-      options->has_user_activation, std::move(preconditions_complete_callback));
+  RequestArModule(options->render_process_id, options->render_frame_id,
+                  options->has_user_activation);
 }
 
-void ARCoreDevice::SatisfyRequestSessionPreconditions(
-    int render_process_id,
-    int render_frame_id,
-    bool has_user_activation,
-    base::OnceCallback<void(bool)> callback) {
-  DCHECK(IsOnMainThread());
-  DCHECK(is_arcore_gl_thread_initialized_);
-
-  if (!arcore_java_utils_->ShouldRequestInstallSupportedArCore()) {
-    // TODO(https://crbug.com/845792): Consider calling a method to ask for the
-    // appropriate permissions.
-    // ARCore sessions require camera permission.
-    RequestCameraPermission(
-        render_process_id, render_frame_id, has_user_activation,
-        base::BindOnce(&ARCoreDevice::OnRequestCameraPermissionComplete,
-                       GetWeakPtr(), std::move(callback)));
+void ARCoreDevice::RequestArModule(int render_process_id,
+                                   int render_frame_id,
+                                   bool has_user_activation) {
+  if (arcore_java_utils_->ShouldRequestInstallArModule()) {
+    arcore_java_utils_->RequestInstallArModule();
     return;
   }
 
-  // ARCore is not installed or requires an update. Store the callback to be
-  // processed later and only the first request session will trigger the
-  // request to install or update of the ARCore APK.
-  auto deferred_callback =
-      base::BindOnce(&ARCoreDevice::OnRequestARCoreInstallOrUpdateComplete,
-                     GetWeakPtr(), render_process_id, render_frame_id,
-                     has_user_activation, std::move(callback));
-  deferred_request_install_supported_arcore_callbacks_.push_back(
-      std::move(deferred_callback));
-  if (deferred_request_install_supported_arcore_callbacks_.size() > 1)
+  OnRequestArModuleResult(render_process_id, render_frame_id,
+                          has_user_activation, true);
+}
+
+void ARCoreDevice::OnRequestArModuleResult(int render_process_id,
+                                           int render_frame_id,
+                                           bool has_user_activation,
+                                           bool success) {
+  if (!success) {
+    CallDeferredRequestSessionCallbacks(/*success=*/false);
     return;
+  }
 
-  content::RenderFrameHost* render_frame_host =
-      content::RenderFrameHost::FromID(render_process_id, render_frame_id);
-  DCHECK(render_frame_host);
+  RequestArCoreInstallOrUpdate(render_process_id, render_frame_id,
+                               has_user_activation);
+}
 
-  content::WebContents* web_contents =
-      content::WebContents::FromRenderFrameHost(render_frame_host);
-  DCHECK(web_contents);
+void ARCoreDevice::RequestArCoreInstallOrUpdate(int render_process_id,
+                                                int render_frame_id,
+                                                bool has_user_activation) {
+  DCHECK(IsOnMainThread());
+  DCHECK(is_arcore_gl_thread_initialized_);
+  DCHECK(!on_request_arcore_install_or_update_result_callback_);
 
-  TabAndroid* tab_android = TabAndroid::FromWebContents(web_contents);
-  DCHECK(tab_android);
+  if (arcore_java_utils_->ShouldRequestInstallSupportedArCore()) {
+    // ARCore is not installed or requires an update. Store the callback to be
+    // processed later once installation/update is complete or got cancelled.
+    on_request_arcore_install_or_update_result_callback_ = base::BindOnce(
+        &ARCoreDevice::OnRequestArCoreInstallOrUpdateResult, GetWeakPtr(),
+        render_process_id, render_frame_id, has_user_activation);
 
-  base::android::ScopedJavaLocalRef<jobject> j_tab_android =
-      tab_android->GetJavaObject();
-  DCHECK(!j_tab_android.is_null());
+    content::RenderFrameHost* render_frame_host =
+        content::RenderFrameHost::FromID(render_process_id, render_frame_id);
+    DCHECK(render_frame_host);
 
-  arcore_java_utils_->RequestInstallSupportedArCore(j_tab_android);
+    content::WebContents* web_contents =
+        content::WebContents::FromRenderFrameHost(render_frame_host);
+    DCHECK(web_contents);
+
+    TabAndroid* tab_android = TabAndroid::FromWebContents(web_contents);
+    DCHECK(tab_android);
+
+    base::android::ScopedJavaLocalRef<jobject> j_tab_android =
+        tab_android->GetJavaObject();
+    DCHECK(!j_tab_android.is_null());
+
+    arcore_java_utils_->RequestInstallSupportedArCore(j_tab_android);
+    return;
+  }
+
+  OnRequestArCoreInstallOrUpdateResult(render_process_id, render_frame_id,
+                                       has_user_activation, true);
 }
 
 void ARCoreDevice::OnRequestInstallSupportedARCoreCanceled() {
   DCHECK(IsOnMainThread());
   DCHECK(is_arcore_gl_thread_initialized_);
-  DCHECK(!deferred_request_install_supported_arcore_callbacks_.empty());
+  DCHECK(on_request_arcore_install_or_update_result_callback_);
 
-  CallDeferredRequestInstallSupportedARCore();
+  std::move(on_request_arcore_install_or_update_result_callback_).Run(false);
 }
 
-void ARCoreDevice::CallDeferredRequestInstallSupportedARCore() {
+void ARCoreDevice::CallDeferredRequestSessionCallbacks(bool success) {
   DCHECK(IsOnMainThread());
   DCHECK(is_arcore_gl_thread_initialized_);
-  DCHECK(!deferred_request_install_supported_arcore_callbacks_.empty());
+  DCHECK(!deferred_request_session_callbacks_.empty());
 
-  for (auto& deferred_callback :
-       deferred_request_install_supported_arcore_callbacks_) {
-    std::move(deferred_callback).Run();
+  for (auto& deferred_callback : deferred_request_session_callbacks_) {
+    mojom::XRSessionControllerPtr controller;
+    mojom::XRSessionPtr session;
+    if (success) {
+      mojom::XRFrameDataProviderPtr data_provider;
+      mojom::XREnvironmentIntegrationProviderPtr environment_provider;
+      magic_window_sessions_.push_back(std::make_unique<VRDisplayImpl>(
+          this, mojo::MakeRequest(&data_provider),
+          mojo::MakeRequest(&environment_provider),
+          mojo::MakeRequest(&controller)));
+
+      session = mojom::XRSession::New();
+      session->data_provider = data_provider.PassInterface();
+      session->environment_provider = environment_provider.PassInterface();
+      session->display_info = display_info_.Clone();
+    }
+    // We don't expect this call to alter deferred_request_session_callbacks_.
+    // The call may request another session, which should be handled right here
+    // in this loop as well.
+    std::move(deferred_callback).Run(std::move(session), std::move(controller));
   }
-  deferred_request_install_supported_arcore_callbacks_.clear();
+  deferred_request_session_callbacks_.clear();
 }
 
-void ARCoreDevice::OnRequestARCoreInstallOrUpdateComplete(
+void ARCoreDevice::OnRequestArCoreInstallOrUpdateResult(
     int render_process_id,
     int render_frame_id,
     bool has_user_activation,
-    base::OnceCallback<void(bool)> callback) {
-  DCHECK(IsOnMainThread());
-  DCHECK(is_arcore_gl_thread_initialized_);
-
-  if (arcore_java_utils_->ShouldRequestInstallSupportedArCore()) {
-    std::move(callback).Run(false);
-    return;
-  }
-
-  RequestCameraPermission(
-      render_process_id, render_frame_id, has_user_activation,
-      base::BindOnce(&ARCoreDevice::OnRequestCameraPermissionComplete,
-                     GetWeakPtr(), std::move(callback)));
-}
-
-void ARCoreDevice::OnRequestCameraPermissionComplete(
-    base::OnceCallback<void(bool)> callback,
     bool success) {
   DCHECK(IsOnMainThread());
   DCHECK(is_arcore_gl_thread_initialized_);
 
-  // By this point ARCore has already been set up, so just return whether the
-  // permission request was a success.
-  std::move(callback).Run(success);
+  if (!success) {
+    CallDeferredRequestSessionCallbacks(/*success=*/false);
+    return;
+  }
+
+  // TODO(https://crbug.com/845792): Consider calling a method to ask for the
+  // appropriate permissions.
+  // ARCore sessions require camera permission.
+  RequestCameraPermission(
+      render_process_id, render_frame_id, has_user_activation,
+      base::BindOnce(&ARCoreDevice::OnRequestCameraPermissionComplete,
+                     GetWeakPtr()));
+}
+
+void ARCoreDevice::OnRequestCameraPermissionComplete(
+    bool success) {
+  DCHECK(IsOnMainThread());
+  DCHECK(is_arcore_gl_thread_initialized_);
+
+  if (!success) {
+    CallDeferredRequestSessionCallbacks(/*success=*/false);
+    return;
+  }
+
+  // By this point ARCore has already been set up, so continue handling request.
+  RequestArCoreGlInitialization();
 }
 
 bool ARCoreDevice::ShouldPauseTrackingWhenFrameDataRestricted() {
@@ -400,37 +436,28 @@
   NOTREACHED() << "Unknown show permission infobar state.";
 }
 
-void ARCoreDevice::OnRequestSessionPreconditionsComplete(
-    mojom::XRRuntime::RequestSessionCallback callback,
-    bool success) {
+void ARCoreDevice::RequestArCoreGlInitialization() {
   DCHECK(IsOnMainThread());
   DCHECK(is_arcore_gl_thread_initialized_);
 
-  if (!success) {
-    std::move(callback).Run(nullptr, nullptr);
+  if (!is_arcore_gl_initialized_) {
+    PostTaskToGlThread(base::BindOnce(
+        &ARCoreGl::Initialize, arcore_gl_thread_->GetARCoreGl()->GetWeakPtr(),
+        CreateMainThreadCallback(base::BindOnce(
+            &ARCoreDevice::OnARCoreGlInitializationComplete, GetWeakPtr()))));
     return;
   }
 
-  if (is_arcore_gl_initialized_) {
-    OnARCoreGlInitializationComplete(std::move(callback), true);
-    return;
-  }
-
-  PostTaskToGlThread(base::BindOnce(
-      &ARCoreGl::Initialize, arcore_gl_thread_->GetARCoreGl()->GetWeakPtr(),
-      CreateMainThreadCallback(
-          base::BindOnce(&ARCoreDevice::OnARCoreGlInitializationComplete,
-                         GetWeakPtr(), std::move(callback)))));
+  OnARCoreGlInitializationComplete(true);
 }
 
 void ARCoreDevice::OnARCoreGlInitializationComplete(
-    mojom::XRRuntime::RequestSessionCallback callback,
     bool success) {
   DCHECK(IsOnMainThread());
   DCHECK(is_arcore_gl_thread_initialized_);
 
   if (!success) {
-    std::move(callback).Run(nullptr, nullptr);
+    CallDeferredRequestSessionCallbacks(/*success=*/false);
     return;
   }
 
@@ -441,20 +468,7 @@
         &ARCoreGl::Resume, arcore_gl_thread_->GetARCoreGl()->GetWeakPtr()));
   }
 
-  mojom::XRFrameDataProviderPtr data_provider;
-  mojom::XREnvironmentIntegrationProviderPtr environment_provider;
-  mojom::XRSessionControllerPtr controller;
-  magic_window_sessions_.push_back(
-      std::make_unique<VRDisplayImpl>(this, mojo::MakeRequest(&data_provider),
-                                      mojo::MakeRequest(&environment_provider),
-                                      mojo::MakeRequest(&controller)));
-
-  auto session = mojom::XRSession::New();
-  session->data_provider = data_provider.PassInterface();
-  session->environment_provider = environment_provider.PassInterface();
-  session->display_info = display_info_.Clone();
-
-  std::move(callback).Run(std::move(session), std::move(controller));
+  CallDeferredRequestSessionCallbacks(/*success=*/true);
 }
 
 void ARCoreDevice::OnRequestAndroidCameraPermissionResult(
diff --git a/chrome/browser/android/vr/arcore_device/arcore_device.h b/chrome/browser/android/vr/arcore_device/arcore_device.h
index c7d54dd..d297be9 100644
--- a/chrome/browser/android/vr/arcore_device/arcore_device.h
+++ b/chrome/browser/android/vr/arcore_device/arcore_device.h
@@ -61,7 +61,6 @@
   void OnMailboxBridgeReady();
   void OnARCoreGlThreadInitialized();
   void OnRequestCameraPermissionComplete(
-      base::OnceCallback<void(bool)> callback,
       bool success);
 
   template <typename... Args>
@@ -84,17 +83,21 @@
 
   bool IsOnMainThread();
 
-  void SatisfyRequestSessionPreconditions(
-      int render_process_id,
-      int render_frame_id,
-      bool has_user_activation,
-      base::OnceCallback<void(bool)> callback);
-  void OnRequestARCoreInstallOrUpdateComplete(
-      int render_process_id,
-      int render_frame_id,
-      bool has_user_activation,
-      base::OnceCallback<void(bool)> callback);
-  void CallDeferredRequestInstallSupportedARCore();
+  void RequestArModule(int render_process_id,
+                       int render_frame_id,
+                       bool has_user_activation);
+  void OnRequestArModuleResult(int render_process_id,
+                               int render_frame_id,
+                               bool has_user_activation,
+                               bool success);
+  void RequestArCoreInstallOrUpdate(int render_process_id,
+                                    int render_frame_id,
+                                    bool has_user_activation);
+  void OnRequestArCoreInstallOrUpdateResult(int render_process_id,
+                                            int render_frame_id,
+                                            bool has_user_activation,
+                                            bool success);
+  void CallDeferredRequestSessionCallbacks(bool success);
   void RequestCameraPermission(int render_process_id,
                                int render_frame_id,
                                bool has_user_activation,
@@ -105,11 +108,8 @@
   void OnRequestAndroidCameraPermissionResult(
       base::OnceCallback<void(bool)> callback,
       bool was_android_camera_permission_granted);
-  void OnRequestSessionPreconditionsComplete(
-      mojom::XRRuntime::RequestSessionCallback callback,
-      bool success);
+  void RequestArCoreGlInitialization();
   void OnARCoreGlInitializationComplete(
-      mojom::XRRuntime::RequestSessionCallback callback,
       bool success);
 
   scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_;
@@ -120,9 +120,10 @@
   bool is_arcore_gl_thread_initialized_ = false;
   bool is_arcore_gl_initialized_ = false;
 
-  // If we get a requestSession before we are completely initialized, store it
-  // until we are intialized.
-  base::OnceClosure pending_request_session_callback_;
+  // If we get a requestSession before we are completely initialized, store a
+  // callback to requesting the AR module since that is the next step that needs
+  // to be taken.
+  base::OnceClosure pending_request_ar_module_callback_;
 
   // This object is not paused when it is created. Although it is not
   // necessarily running during initialization, it is not paused. If it is
@@ -130,9 +131,11 @@
   // not be resumed.
   bool is_paused_ = false;
 
-  // TODO(https://)
-  std::vector<base::OnceCallback<void()>>
-      deferred_request_install_supported_arcore_callbacks_;
+  std::vector<mojom::XRRuntime::RequestSessionCallback>
+      deferred_request_session_callbacks_;
+
+  base::OnceCallback<void(bool)>
+      on_request_arcore_install_or_update_result_callback_;
 
   // Must be last.
   base::WeakPtrFactory<ARCoreDevice> weak_ptr_factory_;
diff --git a/chrome/browser/android/vr/arcore_device/arcore_java_utils.cc b/chrome/browser/android/vr/arcore_device/arcore_java_utils.cc
index 49d2b1df..bc7b7ba 100644
--- a/chrome/browser/android/vr/arcore_device/arcore_java_utils.cc
+++ b/chrome/browser/android/vr/arcore_device/arcore_java_utils.cc
@@ -51,6 +51,15 @@
   arcore_device_->OnRequestInstallSupportedARCoreCanceled();
 }
 
+bool ArCoreJavaUtils::ShouldRequestInstallArModule() {
+  // TODO(crbug.com/863068): Check whether AR module is already installed.
+  return false;
+}
+
+void ArCoreJavaUtils::RequestInstallArModule() {
+  // TODO(crbug.com/863068): On-demand install AR module.
+}
+
 bool ArCoreJavaUtils::ShouldRequestInstallSupportedArCore() {
   JNIEnv* env = AttachCurrentThread();
   return Java_ArCoreJavaUtils_shouldRequestInstallSupportedArCore(
diff --git a/chrome/browser/android/vr/arcore_device/arcore_java_utils.h b/chrome/browser/android/vr/arcore_device/arcore_java_utils.h
index 7b48dcb..d94aa98 100644
--- a/chrome/browser/android/vr/arcore_device/arcore_java_utils.h
+++ b/chrome/browser/android/vr/arcore_device/arcore_java_utils.h
@@ -21,6 +21,8 @@
   static bool EnsureLoaded();
   explicit ArCoreJavaUtils(device::ARCoreDevice* arcore_device);
   ~ArCoreJavaUtils();
+  bool ShouldRequestInstallArModule();
+  void RequestInstallArModule();
   bool ShouldRequestInstallSupportedArCore();
   void RequestInstallSupportedArCore(
       base::android::ScopedJavaLocalRef<jobject> j_tab_android);
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 4797707..bff4e54 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -425,6 +425,7 @@
 #include "chrome/browser/extensions/bookmark_app_navigation_throttle.h"
 #include "chrome/browser/extensions/chrome_content_browser_client_extensions_part.h"
 #include "chrome/browser/extensions/chrome_extension_web_contents_observer.h"
+#include "chrome/browser/speech/extension_api/tts_engine_delegate_factory_impl.h"
 #include "chrome/browser/speech/extension_api/tts_engine_extension_api.h"
 #include "chrome/browser/ui/extensions/hosted_app_browser_controller.h"
 #include "chrome/services/media_gallery_util/public/mojom/constants.mojom.h"
@@ -1006,8 +1007,8 @@
 #endif
 
 #if !defined(OS_ANDROID)
-  TtsExtensionEngine* tts_extension_engine = TtsExtensionEngine::GetInstance();
-  TtsController::GetInstance()->SetTtsEngineDelegate(tts_extension_engine);
+  TtsController::GetInstance()->SetTtsEngineDelegateFactory(
+      TtsEngineDelegateFactoryImpl::GetInstance());
 #endif
 
 #if defined(OS_CHROMEOS)
diff --git a/chrome/browser/chrome_navigation_browsertest.cc b/chrome/browser/chrome_navigation_browsertest.cc
index 923fad8..55f66f1 100644
--- a/chrome/browser/chrome_navigation_browsertest.cc
+++ b/chrome/browser/chrome_navigation_browsertest.cc
@@ -538,7 +538,7 @@
   // The error page should have a unique origin.
   std::string origin;
   EXPECT_TRUE(ExecuteScriptAndExtractString(
-      error_host, "domAutomationController.send(document.origin);", &origin));
+      error_host, "domAutomationController.send(self.origin);", &origin));
   EXPECT_EQ("null", origin);
 }
 
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 15a27d6..e5a8cad 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -642,8 +642,6 @@
     "drive/drive_integration_service.h",
     "drive/file_system_util.cc",
     "drive/file_system_util.h",
-    "drive/file_task_executor.cc",
-    "drive/file_task_executor.h",
     "drive/fileapi/async_file_util.cc",
     "drive/fileapi/async_file_util.h",
     "drive/fileapi/drivefs_async_file_util.cc",
@@ -1581,8 +1579,11 @@
     "policy/weekly_time/weekly_time_interval.h",
     "policy/wildcard_login_checker.cc",
     "policy/wildcard_login_checker.h",
-    "power/auto_screen_brightness/als_reader.cc",
     "power/auto_screen_brightness/als_reader.h",
+    "power/auto_screen_brightness/als_reader_impl.cc",
+    "power/auto_screen_brightness/als_reader_impl.h",
+    "power/auto_screen_brightness/fake_als_reader.cc",
+    "power/auto_screen_brightness/fake_als_reader.h",
     "power/cpu_data_collector.cc",
     "power/cpu_data_collector.h",
     "power/extension_event_observer.cc",
@@ -2072,7 +2073,6 @@
     "drive/drive_file_stream_reader_unittest.cc",
     "drive/drive_integration_service_unittest.cc",
     "drive/file_system_util_unittest.cc",
-    "drive/file_task_executor_unittest.cc",
     "drive/fileapi/fileapi_worker_unittest.cc",
     "drive/fileapi/webkit_file_stream_reader_impl_unittest.cc",
     "drive/write_on_cache_file_unittest.cc",
@@ -2256,7 +2256,8 @@
     "policy/weekly_time/time_utils_unittest.cc",
     "policy/weekly_time/weekly_time_interval_unittest.cc",
     "policy/weekly_time/weekly_time_unittest.cc",
-    "power/auto_screen_brightness/als_reader_unittest.cc",
+    "power/auto_screen_brightness/als_reader_impl_unittest.cc",
+    "power/auto_screen_brightness/fake_als_reader_unittest.cc",
     "power/cpu_data_collector_unittest.cc",
     "power/extension_event_observer_unittest.cc",
     "power/ml/adaptive_screen_brightness_manager_unittest.cc",
diff --git a/chrome/browser/chromeos/drive/drive_integration_service.cc b/chrome/browser/chromeos/drive/drive_integration_service.cc
index 4f8bd2b..39eaecb 100644
--- a/chrome/browser/chromeos/drive/drive_integration_service.cc
+++ b/chrome/browser/chromeos/drive/drive_integration_service.cc
@@ -45,7 +45,6 @@
 #include "components/drive/chromeos/file_system_observer.h"
 #include "components/drive/chromeos/resource_metadata.h"
 #include "components/drive/drive_api_util.h"
-#include "components/drive/drive_app_registry.h"
 #include "components/drive/drive_notification_manager.h"
 #include "components/drive/drive_pref_names.h"
 #include "components/drive/event_logger.h"
@@ -561,8 +560,6 @@
       metadata_storage_.get(),
       cache_root_directory_.Append(kCacheFileDirectory),
       blocking_task_runner_.get(), nullptr /* free_disk_space_getter */));
-  drive_app_registry_ =
-      std::make_unique<DriveAppRegistry>(drive_service_.get());
 
   resource_metadata_.reset(new internal::ResourceMetadata(
       metadata_storage_.get(), cache_.get(), blocking_task_runner_));
@@ -605,7 +602,6 @@
   debug_info_collector_.reset();
   download_handler_.reset();
   file_system_.reset();
-  drive_app_registry_.reset();
   scheduler_.reset();
   drive_service_.reset();
 }
@@ -708,20 +704,15 @@
   logger_->Log(logging::LOG_INFO,
                "Received Drive update notification. Will check for update.");
   file_system_->CheckForUpdates(ids);
-  drive_app_registry_->Update();
 }
 
 void DriveIntegrationService::OnNotificationTimerFired() {
   logger_->Log(logging::LOG_INFO,
                "Drive notification timer erpired. Will check all for update.");
   file_system_->CheckForUpdates();
-  drive_app_registry_->Update();
 }
 
 void DriveIntegrationService::OnPushNotificationEnabled(bool enabled) {
-  if (enabled)
-    drive_app_registry_->Update();
-
   const char* status = (enabled ? "enabled" : "disabled");
   logger_->Log(logging::LOG_INFO, "Push notification is %s", status);
 }
@@ -744,8 +735,6 @@
   RemoveDriveMountPoint();
 
   state_ = REMOUNTING;
-  // Reloads the Drive app registry.
-  drive_app_registry_->Update();
   // Resetting the file system clears resource metadata and cache.
   file_system_->Reset(base::Bind(
       &DriveIntegrationService::AddBackDriveMountPoint,
@@ -975,9 +964,6 @@
         drive_notification_manager->push_notification_registered();
     const char* status = (registered ? "registered" : "not registered");
     logger_->Log(logging::LOG_INFO, "Push notification is %s", status);
-
-    if (drive_notification_manager->push_notification_enabled())
-      drive_app_registry_->Update();
   }
 
   state_ = INITIALIZED;
diff --git a/chrome/browser/chromeos/drive/drive_integration_service.h b/chrome/browser/chromeos/drive/drive_integration_service.h
index 5313d0d..b45bdce8 100644
--- a/chrome/browser/chromeos/drive/drive_integration_service.h
+++ b/chrome/browser/chromeos/drive/drive_integration_service.h
@@ -44,7 +44,6 @@
 
 class DebugInfoCollector;
 class DownloadHandler;
-class DriveAppRegistry;
 class DriveServiceInterface;
 class EventLogger;
 class FileSystemInterface;
@@ -143,7 +142,6 @@
   }
   FileSystemInterface* file_system() { return file_system_.get(); }
   DownloadHandler* download_handler() { return download_handler_.get(); }
-  DriveAppRegistry* drive_app_registry() { return drive_app_registry_.get(); }
   JobListInterface* job_list() { return scheduler_.get(); }
 
   // Clears all the local cache file, the local resource metadata, and
@@ -238,7 +236,6 @@
   std::unique_ptr<internal::FileCache, util::DestroyHelper> cache_;
   std::unique_ptr<DriveServiceInterface> drive_service_;
   std::unique_ptr<JobScheduler> scheduler_;
-  std::unique_ptr<DriveAppRegistry> drive_app_registry_;
   std::unique_ptr<internal::ResourceMetadata, util::DestroyHelper>
       resource_metadata_;
   std::unique_ptr<FileSystemInterface> file_system_;
diff --git a/chrome/browser/chromeos/drive/file_system_util.cc b/chrome/browser/chromeos/drive/file_system_util.cc
index 396153fa..db98fb3 100644
--- a/chrome/browser/chromeos/drive/file_system_util.cc
+++ b/chrome/browser/chromeos/drive/file_system_util.cc
@@ -128,15 +128,6 @@
   return GetFileSystemByProfile(profile);
 }
 
-DriveAppRegistry* GetDriveAppRegistryByProfile(Profile* profile) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-
-  DriveIntegrationService* integration_service =
-      GetIntegrationServiceByProfile(profile);
-  return integration_service ? integration_service->drive_app_registry()
-                             : nullptr;
-}
-
 DriveServiceInterface* GetDriveServiceByProfile(Profile* profile) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
diff --git a/chrome/browser/chromeos/drive/file_system_util.h b/chrome/browser/chromeos/drive/file_system_util.h
index 6a7a9cb..3471185 100644
--- a/chrome/browser/chromeos/drive/file_system_util.h
+++ b/chrome/browser/chromeos/drive/file_system_util.h
@@ -20,7 +20,6 @@
 
 namespace drive {
 
-class DriveAppRegistry;
 class DriveIntegrationService;
 class DriveServiceInterface;
 class FileSystemInterface;
@@ -61,10 +60,6 @@
 // This function must be called on UI thread.
 FileSystemInterface* GetFileSystemByProfileId(void* profile_id);
 
-// Returns the DriveAppRegistry for the |profile|. If not available (not
-// mounted or disabled), returns NULL.
-DriveAppRegistry* GetDriveAppRegistryByProfile(Profile* profile);
-
 // Returns the DriveService for the |profile|. If not available (not mounted
 // or disabled), returns NULL.
 DriveServiceInterface* GetDriveServiceByProfile(Profile* profile);
diff --git a/chrome/browser/chromeos/drive/file_task_executor.cc b/chrome/browser/chromeos/drive/file_task_executor.cc
deleted file mode 100644
index 43e5bbc6..0000000
--- a/chrome/browser/chromeos/drive/file_task_executor.cc
+++ /dev/null
@@ -1,165 +0,0 @@
-// Copyright (c) 2012 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/chromeos/drive/file_task_executor.h"
-
-#include <stddef.h>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "chrome/browser/chromeos/drive/drive_integration_service.h"
-#include "chrome/browser/chromeos/drive/file_system_util.h"
-#include "chrome/browser/profiles/profile_manager.h"
-#include "chrome/browser/ui/ash/multi_user/multi_user_util.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/browser_tabstrip.h"
-#include "chrome/browser/ui/browser_window.h"
-#include "chrome/browser/ui/scoped_tabbed_browser_displayer.h"
-#include "chrome/common/extensions/api/file_manager_private.h"
-#include "components/drive/chromeos/file_system_interface.h"
-#include "components/drive/drive.pb.h"
-#include "components/drive/service/drive_service_interface.h"
-#include "content/public/browser/browser_thread.h"
-#include "storage/browser/fileapi/file_system_url.h"
-
-using storage::FileSystemURL;
-
-namespace drive {
-
-namespace {
-
-class FileTaskExecutorDelegateImpl : public FileTaskExecutorDelegate {
- public:
-  explicit FileTaskExecutorDelegateImpl(Profile* profile) : profile_(profile) {
-  }
-
-  FileSystemInterface* GetFileSystem() override {
-    return util::GetFileSystemByProfile(profile_);
-  }
-
-  DriveServiceInterface* GetDriveService() override {
-    return util::GetDriveServiceByProfile(profile_);
-  }
-
-  void OpenBrowserWindow(const GURL& open_link) override {
-    chrome::ScopedTabbedBrowserDisplayer displayer(profile_);
-    chrome::AddSelectedTabWithURL(displayer.browser(), open_link,
-                                  ui::PAGE_TRANSITION_LINK);
-    // Since the ScopedTabbedBrowserDisplayer does not guarantee that the
-    // browser will be shown on the active desktop, we ensure the visibility.
-    multi_user_util::MoveWindowToCurrentDesktop(
-        displayer.browser()->window()->GetNativeWindow());
-  }
-
- private:
-  Profile* const profile_;
-};
-
-}  // namespace
-
-FileTaskExecutor::FileTaskExecutor(Profile* profile, const std::string& app_id)
-  : delegate_(new FileTaskExecutorDelegateImpl(profile)),
-    app_id_(app_id),
-    current_index_(0),
-    weak_ptr_factory_(this) {
-}
-
-FileTaskExecutor::FileTaskExecutor(
-    std::unique_ptr<FileTaskExecutorDelegate> delegate,
-    const std::string& app_id)
-    : delegate_(std::move(delegate)),
-      app_id_(app_id),
-      current_index_(0),
-      weak_ptr_factory_(this) {}
-
-FileTaskExecutor::~FileTaskExecutor() = default;
-
-void FileTaskExecutor::Execute(
-    const std::vector<FileSystemURL>& file_urls,
-    file_manager::file_tasks::FileTaskFinishedCallback done) {
-  DCHECK(!file_urls.empty());
-
-  done_ = std::move(done);
-
-  std::vector<base::FilePath> paths;
-  for (size_t i = 0; i < file_urls.size(); ++i) {
-    base::FilePath path = util::ExtractDrivePathFromFileSystemUrl(file_urls[i]);
-    if (path.empty()) {
-      Done(false);
-      return;
-    }
-    paths.push_back(path);
-  }
-
-  FileSystemInterface* const file_system = delegate_->GetFileSystem();
-  if (!file_system) {
-    Done(false);
-    return;
-  }
-
-  // Reset the index, so we know when we're done.
-  DCHECK_EQ(current_index_, 0);
-  current_index_ = paths.size();
-
-  for (size_t i = 0; i < paths.size(); ++i) {
-    file_system->GetResourceEntry(
-        paths[i], base::BindOnce(&FileTaskExecutor::OnFileEntryFetched,
-                                 weak_ptr_factory_.GetWeakPtr()));
-  }
-}
-
-void FileTaskExecutor::OnFileEntryFetched(
-    FileError error,
-    std::unique_ptr<ResourceEntry> entry) {
-  // Here, we are only interested in files.
-  if (entry.get() && !entry->has_file_specific_info())
-    error = FILE_ERROR_NOT_FOUND;
-
-  DriveServiceInterface* const drive_service = delegate_->GetDriveService();
-  if (!drive_service || error != FILE_ERROR_OK) {
-    Done(false);
-    return;
-  }
-
-  // Send off a request for the drive service to authorize the apps for the
-  // current document entry for this document so we can get the
-  // open-with-<app_id> urls from the document entry.
-  drive_service->AuthorizeApp(entry->resource_id(),
-                              app_id_,
-                              base::Bind(&FileTaskExecutor::OnAppAuthorized,
-                                         weak_ptr_factory_.GetWeakPtr(),
-                                         entry->resource_id()));
-}
-
-void FileTaskExecutor::OnAppAuthorized(const std::string& resource_id,
-                                       google_apis::DriveApiErrorCode error,
-                                       const GURL& open_link) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-
-  if (error != google_apis::HTTP_SUCCESS || open_link.is_empty()) {
-    Done(false);
-    return;
-  }
-
-  delegate_->OpenBrowserWindow(open_link);
-
-  // We're done with this file.  If this is the last one, then we're done.
-  current_index_--;
-  DCHECK_GE(current_index_, 0);
-  if (current_index_ == 0)
-    Done(true);
-}
-
-void FileTaskExecutor::Done(bool success) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  if (done_) {
-    std::move(done_).Run(
-        success ? extensions::api::file_manager_private::TASK_RESULT_OPENED
-                : extensions::api::file_manager_private::TASK_RESULT_FAILED);
-  }
-  delete this;
-}
-
-}  // namespace drive
diff --git a/chrome/browser/chromeos/drive/file_task_executor.h b/chrome/browser/chromeos/drive/file_task_executor.h
deleted file mode 100644
index 92b61e7a..0000000
--- a/chrome/browser/chromeos/drive/file_task_executor.h
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright (c) 2012 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_CHROMEOS_DRIVE_FILE_TASK_EXECUTOR_H_
-#define CHROME_BROWSER_CHROMEOS_DRIVE_FILE_TASK_EXECUTOR_H_
-
-#include <string>
-#include <vector>
-
-#include "base/callback.h"
-#include "base/memory/weak_ptr.h"
-#include "chrome/browser/chromeos/file_manager/file_tasks.h"
-#include "components/drive/file_errors.h"
-#include "google_apis/drive/drive_api_error_codes.h"
-
-namespace drive {
-
-class DriveServiceInterface;
-class FileSystemInterface;
-class ResourceEntry;
-
-// Delegate class for hooking fake instances and behaviors for testing.
-class FileTaskExecutorDelegate {
- public:
-  virtual ~FileTaskExecutorDelegate() {}
-
-  virtual FileSystemInterface* GetFileSystem() = 0;
-  virtual DriveServiceInterface* GetDriveService() = 0;
-  virtual void OpenBrowserWindow(const GURL& open_link) = 0;
-};
-
-// This class implements an "executor" class that will execute tasks for
-// third party Drive apps that store data in Drive itself.  To do that, it
-// needs to find the file resource IDs and pass them to a server-side function
-// that will authorize the app to open the given document and return a URL
-// for opening the document in that app directly.
-class FileTaskExecutor {
- public:
-  // Creates FileTaskExecutor with delegate derived from |profile|. Used in
-  // product environment.
-  FileTaskExecutor(Profile* profile, const std::string& app_id);
-
-  // Creates FileTaskExecutor with a specific delegate.
-  FileTaskExecutor(std::unique_ptr<FileTaskExecutorDelegate> delegate,
-                   const std::string& app_id);
-
-  // Executes file tasks, runs |done| and deletes |this|.
-  void Execute(const std::vector<storage::FileSystemURL>& file_urls,
-               file_manager::file_tasks::FileTaskFinishedCallback done);
-
- private:
-  ~FileTaskExecutor();
-
-  void OnFileEntryFetched(FileError error,
-                          std::unique_ptr<ResourceEntry> entry);
-  void OnAppAuthorized(const std::string& resource_id,
-                       google_apis::DriveApiErrorCode error,
-                       const GURL& open_link);
-
-  // Calls |done_| with |success| status and deletes |this|.
-  void Done(bool success);
-
-  std::unique_ptr<FileTaskExecutorDelegate> delegate_;
-  std::string app_id_;
-  int current_index_;
-  file_manager::file_tasks::FileTaskFinishedCallback done_;
-
-  base::WeakPtrFactory<FileTaskExecutor> weak_ptr_factory_;
-};
-
-}  // namespace drive
-
-#endif  // CHROME_BROWSER_CHROMEOS_DRIVE_FILE_TASK_EXECUTOR_H_
diff --git a/chrome/browser/chromeos/drive/file_task_executor_unittest.cc b/chrome/browser/chromeos/drive/file_task_executor_unittest.cc
deleted file mode 100644
index 5d7c585..0000000
--- a/chrome/browser/chromeos/drive/file_task_executor_unittest.cc
+++ /dev/null
@@ -1,152 +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.
-
-#include "chrome/browser/chromeos/drive/file_task_executor.h"
-
-#include <set>
-#include <string>
-
-#include "base/run_loop.h"
-#include "components/drive/chromeos/fake_file_system.h"
-#include "components/drive/service/fake_drive_service.h"
-#include "content/public/test/test_browser_thread_bundle.h"
-#include "google_apis/drive/drive_api_parser.h"
-#include "google_apis/drive/test_util.h"
-#include "storage/browser/fileapi/file_system_url.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace drive {
-
-namespace {
-
-// Test harness for verifying the behavior of FileTaskExecutor.
-class TestDelegate : public FileTaskExecutorDelegate {
- public:
-  explicit TestDelegate(std::set<std::string>* opend_urls)
-      : opend_urls_(opend_urls),
-        fake_drive_service_(new FakeDriveService),
-        fake_file_system_(new test_util::FakeFileSystem(
-            fake_drive_service_.get())) {
-    fake_drive_service_->set_open_url_format("http://openlink/%s/%s");
-  }
-
-  // FileTaskExecutorDelegate overrides.
-  FileSystemInterface* GetFileSystem() override {
-    return fake_file_system_.get();
-  }
-
-  DriveServiceInterface* GetDriveService() override {
-    return fake_drive_service_.get();
-  }
-
-  void OpenBrowserWindow(const GURL& open_link) override {
-    opend_urls_->insert(open_link.spec());
-  }
-
-  // Sets up files on the fake Drive service.
-  bool SetUpTestFiles() {
-    {
-      google_apis::DriveApiErrorCode result = google_apis::DRIVE_OTHER_ERROR;
-      std::unique_ptr<google_apis::FileResource> file;
-      fake_drive_service_->AddNewFileWithResourceId(
-          "id1",
-          "text/plain",
-          "random data",
-          fake_drive_service_->GetRootResourceId(),
-          "file1.txt",
-          false,
-          google_apis::test_util::CreateCopyResultCallback(&result, &file));
-      base::RunLoop().RunUntilIdle();
-      if (result != google_apis::HTTP_CREATED)
-        return false;
-    }
-    {
-      google_apis::DriveApiErrorCode result = google_apis::DRIVE_OTHER_ERROR;
-      std::unique_ptr<google_apis::FileResource> file;
-      fake_drive_service_->AddNewFileWithResourceId(
-          "id2",
-          "text/plain",
-          "random data",
-          fake_drive_service_->GetRootResourceId(),
-          "file2.txt",
-          false,
-          google_apis::test_util::CreateCopyResultCallback(&result, &file));
-      base::RunLoop().RunUntilIdle();
-      if (result != google_apis::HTTP_CREATED)
-        return false;
-    }
-    return true;
-  }
-
- private:
-  std::set<std::string>* const opend_urls_;
-  std::unique_ptr<FakeDriveService> fake_drive_service_;
-  std::unique_ptr<test_util::FakeFileSystem> fake_file_system_;
-};
-
-}  // namespace
-
-TEST(FileTaskExecutorTest, DriveAppOpenSuccess) {
-  content::TestBrowserThreadBundle thread_bundle;
-
-  std::set<std::string> opend_urls;
-
-  // |delegate_ptr| will be owned by |executor|.
-  TestDelegate* const delegate_ptr = new TestDelegate(&opend_urls);
-  ASSERT_TRUE(delegate_ptr->SetUpTestFiles());
-  // |executor| deletes itself after Execute() is finished.
-  FileTaskExecutor* const executor = new FileTaskExecutor(
-      std::unique_ptr<FileTaskExecutorDelegate>(delegate_ptr), "test-app-id");
-
-  std::vector<storage::FileSystemURL> urls;
-  urls.push_back(storage::FileSystemURL::CreateForTest(
-      GURL("http://origin/"),
-      storage::kFileSystemTypeDrive,
-      base::FilePath::FromUTF8Unsafe("/special/drive/root/file1.txt")));
-  urls.push_back(storage::FileSystemURL::CreateForTest(
-      GURL("http://origin/"),
-      storage::kFileSystemTypeDrive,
-      base::FilePath::FromUTF8Unsafe("/special/drive/root/file2.txt")));
-
-  extensions::api::file_manager_private::TaskResult result =
-      extensions::api::file_manager_private::TASK_RESULT_NONE;
-  executor->Execute(urls,
-                    google_apis::test_util::CreateCopyResultCallback(&result));
-  base::RunLoop().RunUntilIdle();
-
-  EXPECT_EQ(extensions::api::file_manager_private::TASK_RESULT_OPENED, result);
-  ASSERT_EQ(2u, opend_urls.size());
-  EXPECT_TRUE(opend_urls.count("http://openlink/id1/test-app-id"));
-  EXPECT_TRUE(opend_urls.count("http://openlink/id2/test-app-id"));
-}
-
-TEST(FileTaskExecutorTest, DriveAppOpenFailForNonExistingFile) {
-  content::TestBrowserThreadBundle thread_bundle;
-
-  std::set<std::string> opend_urls;
-
-  // |delegate_ptr| will be owned by |executor|.
-  TestDelegate* const delegate_ptr = new TestDelegate(&opend_urls);
-  ASSERT_TRUE(delegate_ptr->SetUpTestFiles());
-  // |executor| deletes itself after Execute() is finished.
-  FileTaskExecutor* const executor = new FileTaskExecutor(
-      std::unique_ptr<FileTaskExecutorDelegate>(delegate_ptr), "test-app-id");
-
-  std::vector<storage::FileSystemURL> urls;
-  urls.push_back(storage::FileSystemURL::CreateForTest(
-      GURL("http://origin/"),
-      storage::kFileSystemTypeDrive,
-      base::FilePath::FromUTF8Unsafe("/special/drive/root/not-exist.txt")));
-
-  extensions::api::file_manager_private::TaskResult result =
-      extensions::api::file_manager_private::TASK_RESULT_NONE;
-  executor->Execute(urls,
-                    google_apis::test_util::CreateCopyResultCallback(&result));
-  base::RunLoop().RunUntilIdle();
-
-  EXPECT_EQ(extensions::api::file_manager_private::TASK_RESULT_FAILED, result);
-  ASSERT_TRUE(opend_urls.empty());
-}
-
-}  // namespace drive
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_drive.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_drive.cc
index 99d9b65..e89e5ea 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_drive.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_drive.cc
@@ -35,7 +35,6 @@
 #include "chromeos/chromeos_switches.h"
 #include "chromeos/network/network_handler.h"
 #include "chromeos/network/network_state_handler.h"
-#include "components/drive/drive_app_registry.h"
 #include "components/drive/event_logger.h"
 #include "components/signin/core/browser/profile_oauth2_token_service.h"
 #include "components/signin/core/browser/signin_manager.h"
@@ -392,10 +391,8 @@
 
     drive::FileSystemInterface* const file_system =
         drive::util::GetFileSystemByProfile(file_owner_profile_);
-    drive::DriveAppRegistry* const app_registry =
-        drive::util::GetDriveAppRegistryByProfile(file_owner_profile_);
-    if (!file_system || !app_registry) {
-      // |file_system| or |app_registry| is NULL if Drive is disabled.
+    if (!file_system) {
+      // |file_system| is NULL if Drive is disabled.
       CompleteGetEntryProperties(drive::FILE_ERROR_FAILED);
       return;
     }
@@ -407,37 +404,6 @@
       return;
     }
 
-    const drive::FileSpecificInfo& file_specific_info =
-        owner_resource_entry_->file_specific_info();
-
-    // Get drive WebApps that can accept this file. We just need to extract the
-    // doc icon for the drive app, which is set as default.
-    std::vector<drive::DriveAppInfo> drive_apps;
-    app_registry->GetAppsForFile(file_path_.Extension(),
-                                 file_specific_info.content_mime_type(),
-                                 &drive_apps);
-    if (!drive_apps.empty()) {
-      std::string default_task_id =
-          file_manager::file_tasks::GetDefaultTaskIdFromPrefs(
-              *file_owner_profile_->GetPrefs(),
-              file_specific_info.content_mime_type(),
-              file_path_.Extension());
-      file_manager::file_tasks::TaskDescriptor default_task;
-      file_manager::file_tasks::ParseTaskID(default_task_id, &default_task);
-      DCHECK(default_task_id.empty() || !default_task.app_id.empty());
-      for (size_t i = 0; i < drive_apps.size(); ++i) {
-        const drive::DriveAppInfo& app_info = drive_apps[i];
-        if (default_task.app_id == app_info.app_id) {
-          // The drive app is set as default. The Files app should use the doc
-          // icon.
-          const GURL doc_icon = drive::util::FindPreferredIcon(
-              app_info.document_icons, drive::util::kPreferredIconSize);
-          properties_->custom_icon_url =
-              std::make_unique<std::string>(doc_icon.spec());
-        }
-      }
-    }
-
     CompleteGetEntryProperties(drive::FILE_ERROR_OK);
   }
 
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_tasks.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_tasks.cc
index 3d701c8..14d00a6 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_tasks.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_tasks.cc
@@ -186,8 +186,7 @@
   }
 
   file_manager::file_tasks::FindAllTypesOfTasks(
-      GetProfile(), drive::util::GetDriveAppRegistryByProfile(GetProfile()),
-      entries, urls_,
+      GetProfile(), entries, urls_,
       base::Bind(
           &FileManagerPrivateInternalGetFileTasksFunction::OnFileTasksListed,
           this));
diff --git a/chrome/browser/chromeos/file_manager/external_filesystem_apitest.cc b/chrome/browser/chromeos/file_manager/external_filesystem_apitest.cc
index 2934691..d0c2804 100644
--- a/chrome/browser/chromeos/file_manager/external_filesystem_apitest.cc
+++ b/chrome/browser/chromeos/file_manager/external_filesystem_apitest.cc
@@ -222,7 +222,6 @@
 // The hierarchy is the same as for the local file system.
 drive::FakeDriveService* CreateDriveService() {
   drive::FakeDriveService* service = new drive::FakeDriveService;
-  service->LoadAppListForDriveApi("drive/applist.json");
   AddDirectoryToDriveService(service, service->GetRootResourceId(), "test_dir",
                              "2012-01-02T00:00:00.000Z",
                              "2012-01-02T00:00:01.000Z");
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc b/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc
index 9ae88bd..b65d510 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc
@@ -777,7 +777,6 @@
 
     EXPECT_FALSE(fake_drive_service_);
     fake_drive_service_ = new drive::FakeDriveService;
-    fake_drive_service_->LoadAppListForDriveApi("drive/applist.json");
 
     EXPECT_FALSE(integration_service_);
     integration_service_ = new drive::DriveIntegrationService(
diff --git a/chrome/browser/chromeos/file_manager/file_tasks.cc b/chrome/browser/chromeos/file_manager/file_tasks.cc
index 4e66b66..4baf87d 100644
--- a/chrome/browser/chromeos/file_manager/file_tasks.cc
+++ b/chrome/browser/chromeos/file_manager/file_tasks.cc
@@ -16,7 +16,6 @@
 #include "base/strings/stringprintf.h"
 #include "chrome/browser/chromeos/crostini/crostini_util.h"
 #include "chrome/browser/chromeos/drive/file_system_util.h"
-#include "chrome/browser/chromeos/drive/file_task_executor.h"
 #include "chrome/browser/chromeos/file_manager/app_id.h"
 #include "chrome/browser/chromeos/file_manager/arc_file_tasks.h"
 #include "chrome/browser/chromeos/file_manager/crostini_file_tasks.h"
@@ -33,7 +32,6 @@
 #include "chrome/common/pref_names.h"
 #include "chromeos/chromeos_switches.h"
 #include "components/drive/drive_api_util.h"
-#include "components/drive/drive_app_registry.h"
 #include "components/prefs/pref_service.h"
 #include "components/prefs/scoped_user_pref_update.h"
 #include "extensions/browser/api/file_handlers/mime_util.h"
@@ -61,13 +59,9 @@
 // these are used in default task IDs stored in preferences.
 const char kFileBrowserHandlerTaskType[] = "file";
 const char kFileHandlerTaskType[] = "app";
-const char kDriveAppTaskType[] = "drive";
 const char kArcAppTaskType[] = "arc";
 const char kCrostiniAppTaskType[] = "crostini";
 
-// Drive apps always use the action ID.
-const char kDriveAppActionID[] = "open-with";
-
 // Converts a TaskType to a string.
 std::string TaskTypeToString(TaskType task_type) {
   switch (task_type) {
@@ -75,13 +69,12 @@
       return kFileBrowserHandlerTaskType;
     case TASK_TYPE_FILE_HANDLER:
       return kFileHandlerTaskType;
-    case TASK_TYPE_DRIVE_APP:
-      return kDriveAppTaskType;
     case TASK_TYPE_ARC_APP:
       return kArcAppTaskType;
     case TASK_TYPE_CROSTINI_APP:
       return kCrostiniAppTaskType;
     case TASK_TYPE_UNKNOWN:
+    case DEPRECATED_TASK_TYPE_DRIVE_APP:
     case NUM_TASK_TYPE:
       break;
   }
@@ -95,8 +88,6 @@
     return TASK_TYPE_FILE_BROWSER_HANDLER;
   if (str == kFileHandlerTaskType)
     return TASK_TYPE_FILE_HANDLER;
-  if (str == kDriveAppTaskType)
-    return TASK_TYPE_DRIVE_APP;
   if (str == kArcAppTaskType)
     return TASK_TYPE_ARC_APP;
   if (str == kCrostiniAppTaskType)
@@ -104,11 +95,6 @@
   return TASK_TYPE_UNKNOWN;
 }
 
-// Legacy Drive task extension prefix, used by CrackTaskID.
-const char kDriveTaskExtensionPrefix[] = "drive-app:";
-const size_t kDriveTaskExtensionPrefixLength =
-    arraysize(kDriveTaskExtensionPrefix) - 1;
-
 // Returns true if path_mime_set contains a Google document.
 bool ContainsGoogleDocument(const std::vector<extensions::EntryInfo>& entries) {
   for (const auto& it : entries) {
@@ -292,19 +278,11 @@
   std::vector<std::string> result = base::SplitString(
       task_id, "|", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
 
-  // Parse a legacy task ID that only contain two parts. Drive tasks are
-  // identified by a prefix "drive-app:" on the extension ID. The legacy task
-  // IDs can be stored in preferences.
+  // Parse a legacy task ID that only contain two parts. The legacy task IDs
+  // can be stored in preferences.
   if (result.size() == 2) {
-    if (base::StartsWith(result[0], kDriveTaskExtensionPrefix,
-                         base::CompareCase::SENSITIVE)) {
-      task->task_type = TASK_TYPE_DRIVE_APP;
-      task->app_id = result[0].substr(kDriveTaskExtensionPrefixLength);
-    } else {
-      task->task_type = TASK_TYPE_FILE_BROWSER_HANDLER;
-      task->app_id = result[0];
-    }
-
+    task->task_type = TASK_TYPE_FILE_BROWSER_HANDLER;
+    task->app_id = result[0];
     task->action_id = result[1];
 
     return true;
@@ -349,15 +327,6 @@
     return true;
   }
 
-  // drive::FileTaskExecutor is responsible to handle drive tasks.
-  if (task.task_type == TASK_TYPE_DRIVE_APP) {
-    DCHECK_EQ(kDriveAppActionID, task.action_id);
-    drive::FileTaskExecutor* executor =
-        new drive::FileTaskExecutor(profile, task.app_id);
-    executor->Execute(file_urls, std::move(done));
-    return true;
-  }
-
   // Get the extension.
   const Extension* extension = extensions::ExtensionRegistry::Get(
       profile)->enabled_extensions().GetByID(task.app_id);
@@ -387,68 +356,6 @@
   return false;
 }
 
-void FindDriveAppTasks(const drive::DriveAppRegistry& drive_app_registry,
-                       const std::vector<extensions::EntryInfo>& entries,
-                       std::vector<FullTaskDescriptor>* result_list) {
-  DCHECK(result_list);
-
-  bool is_first = true;
-  typedef std::map<std::string, drive::DriveAppInfo> DriveAppInfoMap;
-  DriveAppInfoMap drive_app_map;
-
-  for (std::vector<extensions::EntryInfo>::const_iterator it = entries.begin();
-       it != entries.end(); ++it) {
-    const base::FilePath& file_path = it->path;
-    const std::string& mime_type = it->mime_type;
-    // Return immediately if a file not on Drive is found, as Drive app tasks
-    // work only if all files are on Drive.
-    if (!drive::util::IsUnderDriveMountPoint(file_path))
-      return;
-
-    std::vector<drive::DriveAppInfo> app_info_list;
-    drive_app_registry.GetAppsForFile(file_path.Extension(),
-                                      mime_type,
-                                      &app_info_list);
-
-    if (is_first) {
-      // For the first file, we store all the info.
-      for (size_t j = 0; j < app_info_list.size(); ++j)
-        drive_app_map[app_info_list[j].app_id] = app_info_list[j];
-    } else {
-      // For remaining files, take the intersection with the current
-      // result, based on the app id.
-      std::set<std::string> app_id_set;
-      for (size_t j = 0; j < app_info_list.size(); ++j)
-        app_id_set.insert(app_info_list[j].app_id);
-      for (DriveAppInfoMap::iterator iter = drive_app_map.begin();
-           iter != drive_app_map.end();) {
-        if (app_id_set.count(iter->first) == 0) {
-          drive_app_map.erase(iter++);
-        } else {
-          ++iter;
-        }
-      }
-    }
-
-    is_first = false;
-  }
-
-  for (DriveAppInfoMap::const_iterator iter = drive_app_map.begin();
-       iter != drive_app_map.end(); ++iter) {
-    const drive::DriveAppInfo& app_info = iter->second;
-    TaskDescriptor descriptor(app_info.app_id,
-                              TASK_TYPE_DRIVE_APP,
-                              kDriveAppActionID);
-    GURL icon_url = drive::util::FindPreferredIcon(
-        app_info.app_icons,
-        drive::util::kPreferredIconSize);
-
-    result_list->push_back(FullTaskDescriptor(
-        descriptor, app_info.app_name, Verb::VERB_OPEN_WITH, icon_url,
-        false /* is_default */, false /* is_generic_file_handler */));
-  }
-}
-
 bool IsGoodMatchFileHandler(
     const extensions::FileHandlerInfo& file_handler_info,
     const std::vector<extensions::EntryInfo>& entries) {
@@ -537,7 +444,7 @@
           extension->id(), file_tasks::TASK_TYPE_FILE_HANDLER, handler->id);
 
       GURL best_icon = extensions::ExtensionIconSource::GetIconURL(
-          extension, drive::util::kPreferredIconSize,
+          extension, extension_misc::EXTENSION_ICON_SMALL,
           ExtensionIconSet::MATCH_BIGGER,
           false);  // grayscale
 
@@ -614,15 +521,15 @@
     std::unique_ptr<std::vector<FullTaskDescriptor>> result_list) {
   std::vector<FullTaskDescriptor>* result_list_ptr = result_list.get();
 
-  // 3. Continues from FindAllTypesOfTasks. Find and append file handler tasks.
+  // 2. Continues from FindAllTypesOfTasks. Find and append file handler tasks.
   FindFileHandlerTasks(profile, entries, result_list_ptr);
 
-  // 4. Find and append file browser handler tasks. We know there aren't
+  // 3. Find and append file browser handler tasks. We know there aren't
   // duplicates because "file_browser_handlers" and "file_handlers" shouldn't
   // be used in the same manifest.json.
   FindFileBrowserHandlerTasks(profile, file_urls, result_list_ptr);
 
-  // 5. Find and append Crostini tasks.
+  // 4. Find and append Crostini tasks.
   FindCrostiniTasks(
       profile, entries, result_list_ptr,
       // Done. Apply post-filtering and callback.
@@ -631,7 +538,6 @@
 }
 
 void FindAllTypesOfTasks(Profile* profile,
-                         const drive::DriveAppRegistry* drive_app_registry,
                          const std::vector<extensions::EntryInfo>& entries,
                          const std::vector<GURL>& file_urls,
                          FindTasksCallback callback) {
@@ -639,11 +545,7 @@
   std::unique_ptr<std::vector<FullTaskDescriptor>> result_list(
       new std::vector<FullTaskDescriptor>);
 
-  // 1. Find Drive app tasks, if the drive app registry is present.
-  if (drive_app_registry)
-    FindDriveAppTasks(*drive_app_registry, entries, result_list.get());
-
-  // 2. Find and append ARC handler tasks.
+  // Find and append ARC handler tasks.
   FindArcTasks(profile, entries, file_urls, std::move(result_list),
                base::BindOnce(&FindExtensionAndAppTasks, profile, entries,
                               file_urls, std::move(callback)));
diff --git a/chrome/browser/chromeos/file_manager/file_tasks.h b/chrome/browser/chromeos/file_manager/file_tasks.h
index 7a80ec7..26dbef4 100644
--- a/chrome/browser/chromeos/file_manager/file_tasks.h
+++ b/chrome/browser/chromeos/file_manager/file_tasks.h
@@ -25,27 +25,12 @@
 // See also:
 // ui/file_manager/file_manager/manifest.json
 //
-// 3) Drive app, which is a hosted app (i.e. just web site), that can work
-// with Drive (ex. Pixlr Editor). This information comes from
-// drive::DriveAppRegistry.
-//
-// See also:
-// https://chrome.google.com/webstore/category/collection/drive_apps
-//
 // For example, if the user is now selecting a JPEG file, the Files app will
 // receive file tasks represented as a JSON object via
 // chrome.fileManagerPrivate.getFileTasks() API, which look like:
 //
 // [
 //   {
-//     "driveApp": true,
-//     "iconUrl": "<app_icon_url>",
-//     "isDefault": false,
-//     "taskId": "<drive_app_id>|drive|open-with",
-//     "title": "Drive App Name (ex. Pixlr Editor)"
-//   },
-//   {
-//     "driveApp": false,
 //     "iconUrl":
 //       "chrome://extension-icon/hhaomjibdihmijegdhdafkllkbggdgoj/16/1",
 //     "isDefault": true,
@@ -54,8 +39,7 @@
 //   }
 // ]
 //
-// The first file task is a Drive app. The second file task is a built-in
-// handler from the Files app.
+// The file task is a built-in handler from the Files app.
 //
 // WHAT ARE TASK IDS?
 //
@@ -75,24 +59,20 @@
 //
 //     <app-id>|<task-type>|<task-action-id>
 //
-// <app-id> is either of Chrome Extension/App ID or Drive App ID. For some
-// reason, Chrome Extension/App IDs and Drive App IDs look differently. As of
-// writing, the former looks like "hhaomjibdihmijegdhdafkllkbggdgoj"
-// (the Files app) and the latter looks like "419782477519" (Pixlr Editor).
+// <app-id> is a Chrome Extension/App ID.
 //
 // <task-type> is either of
 // - "file" - File browser handler - app/extension declaring
 //            "file_browser_handlers" in manifest.
 // - "app" - File handler - app declaring "file_handlers" in manifest.json.
-// - "drive" - Drive App
 // - "arc" - ARC App
 // - "crostini" - Crostini App
 //
 // <task-action-id> is an ID string used for identifying actions provided
 // from a single Chrome Extension/App. In other words, a single
 // Chrome/Extension can provide multiple file handlers hence each of them
-// needs to have a unique action ID. For Drive and Crostini apps,
-// <task-action-id> is always "open-with".
+// needs to have a unique action ID. For Crostini apps, <task-action-id> is
+// always "open-with".
 //
 // HOW TASKS ARE EXECUTED?
 //
@@ -100,7 +80,7 @@
 // without any handler. Browser will take care of handling the file (ex. PDF).
 //
 // chrome.fileManagerPrivate.executeTasks() is used to open a file with a
-// handler (Chrome Extension/App or Drive App).
+// handler (Chrome Extension/App).
 //
 // Some built-in handlers such as "play" are handled internally in the Files
 // app. "mount-archive" is handled very differently. The task execution
@@ -126,10 +106,6 @@
 class PrefService;
 class Profile;
 
-namespace drive {
-class DriveAppRegistry;
-}
-
 namespace extensions {
 struct EntryInfo;
 }
@@ -146,7 +122,7 @@
   TASK_TYPE_UNKNOWN = 0,  // Used only for handling errors.
   TASK_TYPE_FILE_BROWSER_HANDLER,
   TASK_TYPE_FILE_HANDLER,
-  TASK_TYPE_DRIVE_APP,
+  DEPRECATED_TASK_TYPE_DRIVE_APP,
   TASK_TYPE_ARC_APP,
   TASK_TYPE_CROSTINI_APP,
   // The enum values must be kept in sync with FileManagerTaskType in
@@ -238,7 +214,7 @@
 // Generates task id for the task specified by |app_id|, |task_type| and
 // |action_id|.
 //
-// |app_id| is either of Chrome Extension/App ID or Drive App ID.
+// |app_id| is the Chrome Extension/App ID.
 // |action_id| is a free-form string ID for the action.
 std::string MakeTaskID(const std::string& app_id,
                        TaskType task_type,
@@ -280,13 +256,6 @@
                      const std::vector<storage::FileSystemURL>& file_urls,
                      FileTaskFinishedCallback done);
 
-// Finds the Drive app tasks that can be used with the given |entries|
-// from |drive_app_registry|, and append them to the |result_list|.
-// Drive app tasks will be found only if all of the files are on Drive.
-void FindDriveAppTasks(const drive::DriveAppRegistry& drive_app_registry,
-                       const std::vector<extensions::EntryInfo>& entries,
-                       std::vector<FullTaskDescriptor>* result_list);
-
 // Returns true if a file handler matches with entries as good match.
 bool IsGoodMatchFileHandler(
     const extensions::FileHandlerInfo& file_handler_info,
@@ -312,19 +281,14 @@
     std::unique_ptr<std::vector<FullTaskDescriptor>> result)>
     FindTasksCallback;
 
-// Finds all types (drive, file handlers, file browser handlers) of
-// tasks. See the comment at FindDriveAppTasks() about |result_list|.
-// Drive app tasks will be found only if all of the files are on Drive.
-// |drive_app_registry| can be NULL if the drive app registry is not
-// present.
+// Finds all types (file handlers, file browser handlers) of
+// tasks.
 //
 // If |entries| contains a Google document, only the internal tasks of the
 // Files app (i.e., tasks having the app ID of the Files app) are listed.
-// This is to avoid dups between Drive app tasks and an internal handler that
-// the Files app provides, and to avoid listing normal file handler and file
-// browser handler tasks, which can handle only normal files.
+// This is to avoid listing normal file handler and file browser handler tasks,
+// which can handle only normal files.
 void FindAllTypesOfTasks(Profile* profile,
-                         const drive::DriveAppRegistry* drive_app_registry,
                          const std::vector<extensions::EntryInfo>& entries,
                          const std::vector<GURL>& file_urls,
                          FindTasksCallback callback);
diff --git a/chrome/browser/chromeos/file_manager/file_tasks_browsertest.cc b/chrome/browser/chromeos/file_manager/file_tasks_browsertest.cc
index 740dde2..35ff5f6 100644
--- a/chrome/browser/chromeos/file_manager/file_tasks_browsertest.cc
+++ b/chrome/browser/chromeos/file_manager/file_tasks_browsertest.cc
@@ -106,7 +106,6 @@
       },
       run_loop.QuitClosure(), &remaining);
 
-  const drive::DriveAppRegistry* const kDriveAppRegistry = nullptr;
   const base::FilePath prefix = base::FilePath().AppendASCII("file");
 
   for (const auto& test : kExpectations) {
@@ -121,7 +120,7 @@
     std::vector<extensions::EntryInfo> entries = {{path, mime_type, false}};
     std::vector<GURL> file_urls{GURL()};
     FindAllTypesOfTasks(
-        browser()->profile(), kDriveAppRegistry, entries, file_urls,
+        browser()->profile(), entries, file_urls,
         base::BindRepeating(callback, test.file_extension, test.app_id));
   }
   run_loop.Run();
diff --git a/chrome/browser/chromeos/file_manager/file_tasks_unittest.cc b/chrome/browser/chromeos/file_manager/file_tasks_unittest.cc
index a55dff9..5c02db6 100644
--- a/chrome/browser/chromeos/file_manager/file_tasks_unittest.cc
+++ b/chrome/browser/chromeos/file_manager/file_tasks_unittest.cc
@@ -26,7 +26,6 @@
 #include "chrome/test/base/testing_profile.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/fake_concierge_client.h"
-#include "components/drive/drive_app_registry.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/testing_pref_service.h"
 #include "content/public/test/test_browser_thread_bundle.h"
@@ -69,8 +68,7 @@
 
 }  // namespace
 
-TEST(FileManagerFileTasksTest,
-     FullTaskDescriptor_NonDriveAppWithIconAndDefault) {
+TEST(FileManagerFileTasksTest, FullTaskDescriptor_WithIconAndDefault) {
   FullTaskDescriptor full_descriptor(
       TaskDescriptor("app-id", TASK_TYPE_FILE_BROWSER_HANDLER, "action-id"),
       "task title", Verb::VERB_OPEN_WITH, GURL("http://example.com/icon.png"),
@@ -85,30 +83,11 @@
   EXPECT_TRUE(full_descriptor.is_default());
 }
 
-TEST(FileManagerFileTasksTest,
-     FullTaskDescriptor_DriveAppWithoutIconAndNotDefault) {
-  FullTaskDescriptor full_descriptor(
-      TaskDescriptor("app-id", TASK_TYPE_DRIVE_APP, "action-id"), "task title",
-      Verb::VERB_OPEN_WITH,
-      GURL(),  // No icon URL.
-      false /* is_default */, false /* is_generic_file_handler */);
-
-  const std::string task_id =
-      TaskDescriptorToId(full_descriptor.task_descriptor());
-  EXPECT_EQ("app-id|drive|action-id", task_id);
-  EXPECT_TRUE(full_descriptor.icon_url().is_empty());
-  EXPECT_EQ("task title", full_descriptor.task_title());
-  EXPECT_EQ(Verb::VERB_OPEN_WITH, full_descriptor.task_verb());
-  EXPECT_FALSE(full_descriptor.is_default());
-}
-
 TEST(FileManagerFileTasksTest, MakeTaskID) {
   EXPECT_EQ("app-id|file|action-id",
             MakeTaskID("app-id", TASK_TYPE_FILE_BROWSER_HANDLER, "action-id"));
   EXPECT_EQ("app-id|app|action-id",
             MakeTaskID("app-id", TASK_TYPE_FILE_HANDLER, "action-id"));
-  EXPECT_EQ("app-id|drive|action-id",
-            MakeTaskID("app-id", TASK_TYPE_DRIVE_APP, "action-id"));
 }
 
 TEST(FileManagerFileTasksTest, TaskDescriptorToId) {
@@ -134,14 +113,6 @@
   EXPECT_EQ("action-id", task.action_id);
 }
 
-TEST(FileManagerFileTasksTest, ParseTaskID_DriveApp) {
-  TaskDescriptor task;
-  EXPECT_TRUE(ParseTaskID("app-id|drive|action-id", &task));
-  EXPECT_EQ("app-id", task.app_id);
-  EXPECT_EQ(TASK_TYPE_DRIVE_APP, task.task_type);
-  EXPECT_EQ("action-id", task.action_id);
-}
-
 TEST(FileManagerFileTasksTest, ParseTaskID_Legacy) {
   TaskDescriptor task;
   // A legacy task ID only has two parts. The task type should be
@@ -152,16 +123,6 @@
   EXPECT_EQ("action-id", task.action_id);
 }
 
-TEST(FileManagerFileTasksTest, ParseTaskID_LegacyDrive) {
-  TaskDescriptor task;
-  // A legacy task ID only has two parts. For Drive app, the app ID is
-  // prefixed with "drive-app:".
-  EXPECT_TRUE(ParseTaskID("drive-app:app-id|action-id", &task));
-  EXPECT_EQ("app-id", task.app_id);
-  EXPECT_EQ(TASK_TYPE_DRIVE_APP, task.task_type);
-  EXPECT_EQ("action-id", task.action_id);
-}
-
 TEST(FileManagerFileTasksTest, ParseTaskID_Invalid) {
   TaskDescriptor task;
   EXPECT_FALSE(ParseTaskID("invalid", &task));
@@ -172,86 +133,6 @@
   EXPECT_FALSE(ParseTaskID("app-id|unknown|action-id", &task));
 }
 
-TEST(FileManagerFileTasksTest, FindDriveAppTasks) {
-  // For DriveAppRegistry and TestingProfile, which check
-  // CurrentlyOn(BrowserThread::UI).
-  content::TestBrowserThreadBundle thread_bundle;
-
-  TestingProfile profile;
-
-  // Foo.app can handle "text/plain" and "text/html"
-  std::unique_ptr<google_apis::AppResource> foo_app(
-      new google_apis::AppResource);
-  foo_app->set_product_id("foo_app_id");
-  foo_app->set_application_id("foo_app_id");
-  foo_app->set_name("Foo");
-  foo_app->set_object_type("foo_object_type");
-  std::vector<std::unique_ptr<std::string>> foo_mime_types;
-  foo_mime_types.push_back(std::make_unique<std::string>("text/plain"));
-  foo_mime_types.push_back(std::make_unique<std::string>("text/html"));
-  foo_app->set_primary_mimetypes(std::move(foo_mime_types));
-
-  // Bar.app can only handle "text/plain".
-  std::unique_ptr<google_apis::AppResource> bar_app(
-      new google_apis::AppResource);
-  bar_app->set_product_id("bar_app_id");
-  bar_app->set_application_id("bar_app_id");
-  bar_app->set_name("Bar");
-  bar_app->set_object_type("bar_object_type");
-  std::vector<std::unique_ptr<std::string>> bar_mime_types;
-  bar_mime_types.push_back(std::make_unique<std::string>("text/plain"));
-  bar_app->set_primary_mimetypes(std::move(bar_mime_types));
-
-  // Prepare DriveAppRegistry from Foo.app and Bar.app.
-  std::vector<std::unique_ptr<google_apis::AppResource>> app_resources;
-  app_resources.push_back(std::move(foo_app));
-  app_resources.push_back(std::move(bar_app));
-  google_apis::AppList app_list;
-  app_list.set_items(std::move(app_resources));
-  drive::DriveAppRegistry drive_app_registry(nullptr);
-  drive_app_registry.UpdateFromAppList(app_list);
-
-  // Find apps for a "text/plain" file. Foo.app and Bar.app should be found.
-  std::vector<extensions::EntryInfo> entries;
-  entries.emplace_back(
-      drive::util::GetDriveMountPointPath(&profile).AppendASCII("foo.txt"),
-      "text/plain", false);
-  std::vector<FullTaskDescriptor> tasks;
-  FindDriveAppTasks(drive_app_registry, entries, &tasks);
-  ASSERT_EQ(2U, tasks.size());
-  // Sort the app IDs, as the order is not guaranteed.
-  std::vector<std::string> app_ids;
-  app_ids.push_back(tasks[0].task_descriptor().app_id);
-  app_ids.push_back(tasks[1].task_descriptor().app_id);
-  std::sort(app_ids.begin(), app_ids.end());
-  // Confirm that both Foo.app and Bar.app are found.
-  EXPECT_EQ("bar_app_id", app_ids[0]);
-  EXPECT_EQ("foo_app_id", app_ids[1]);
-
-  // Find apps for "text/plain" and "text/html" files. Only Foo.app should be
-  // found.
-  entries.clear();
-  entries.emplace_back(
-      drive::util::GetDriveMountPointPath(&profile).AppendASCII("foo.txt"),
-      "text/plain", false);
-  entries.emplace_back(
-      drive::util::GetDriveMountPointPath(&profile).AppendASCII("foo.html"),
-      "text/html", false);
-  tasks.clear();
-  FindDriveAppTasks(drive_app_registry, entries, &tasks);
-  ASSERT_EQ(1U, tasks.size());
-  // Confirm that only Foo.app is found.
-  EXPECT_EQ("foo_app_id", tasks[0].task_descriptor().app_id);
-
-  // Add a "text/plain" file not on Drive. No tasks should be found.
-  entries.emplace_back(base::FilePath::FromUTF8Unsafe("not_on_drive.txt"),
-                       "text/plain", false);
-  tasks.clear();
-  FindDriveAppTasks(drive_app_registry, entries, &tasks);
-  // Confirm no tasks are found.
-  ASSERT_TRUE(tasks.empty());
-}
-
 // Test that the right task is chosen from multiple choices per mime types
 // and file extensions.
 TEST(FileManagerFileTasksTest, ChooseAndSetDefaultTask_MultipleTasks) {
@@ -512,12 +393,11 @@
   class FindAllTypesOfTasksSynchronousWrapper {
    public:
     void Call(Profile* profile,
-              const drive::DriveAppRegistry* drive_app_registry,
               const std::vector<extensions::EntryInfo>& entries,
               const std::vector<GURL>& file_urls,
               std::vector<FullTaskDescriptor>* result) {
       FindAllTypesOfTasks(
-          profile, drive_app_registry, entries, file_urls,
+          profile, entries, file_urls,
           base::Bind(&FindAllTypesOfTasksSynchronousWrapper::OnReply,
                      base::Unretained(this), result));
       run_loop_.Run();
@@ -541,7 +421,6 @@
   extensions::ExtensionService* extension_service_;  // Owned by test_profile_;
 };
 
-// The basic logic is similar to a test case for FindDriveAppTasks above.
 TEST_F(FileManagerFileTasksComplexTest, FindFileHandlerTasks) {
   // Random IDs generated by
   // % ruby -le 'print (0...32).to_a.map{(?a + rand(16)).chr}.join'
@@ -647,7 +526,7 @@
   ASSERT_TRUE(tasks.empty());
 }
 
-// The basic logic is similar to a test case for FindDriveAppTasks above.
+// The basic logic is similar to a test case for FindFileHandlerTasks above.
 TEST_F(FileManagerFileTasksComplexTest, FindFileBrowserHandlerTasks) {
   // Copied from FindFileHandlerTasks test above.
   const char kFooId[] = "hhgbjpmdppecanaaogonaigmmifgpaph";
@@ -737,13 +616,12 @@
   ASSERT_TRUE(tasks.empty());
 }
 
-// Test that all kinds of apps (file handler, file browser handler, and Drive
-// app) are returned.
+// Test that all kinds of apps (file handler and file browser handler) are
+// returned.
 TEST_F(FileManagerFileTasksComplexTest, FindAllTypesOfTasks) {
   // kFooId and kBarId copied from FindFileHandlerTasks test above.
   const char kFooId[] = "hhgbjpmdppecanaaogonaigmmifgpaph";
   const char kBarId[] = "odlhccgofgkadkkhcmhgnhgahonahoca";
-  const char kBazId[] = "plifkpkakemokpflgbnnigcoldgcbdmc";
 
   // Foo.app can handle "text/plain".
   // This is a packaged app (file handler).
@@ -799,25 +677,6 @@
   bar_app.SetID(kBarId);
   extension_service_->AddExtension(bar_app.Build().get());
 
-  // Baz.app can handle "text/plain".
-  // This is a Drive app.
-  std::unique_ptr<google_apis::AppResource> baz_app(
-      new google_apis::AppResource);
-  baz_app->set_product_id("baz_app_id");
-  baz_app->set_application_id(kBazId);
-  baz_app->set_name("Baz");
-  baz_app->set_object_type("baz_object_type");
-  std::vector<std::unique_ptr<std::string>> baz_mime_types;
-  baz_mime_types.push_back(std::make_unique<std::string>("text/plain"));
-  baz_app->set_primary_mimetypes(std::move(baz_mime_types));
-  // Set up DriveAppRegistry.
-  std::vector<std::unique_ptr<google_apis::AppResource>> app_resources;
-  app_resources.push_back(std::move(baz_app));
-  google_apis::AppList app_list;
-  app_list.set_items(std::move(app_resources));
-  drive::DriveAppRegistry drive_app_registry(nullptr);
-  drive_app_registry.UpdateFromAppList(app_list);
-
   // Find apps for "foo.txt". All apps should be found.
   std::vector<extensions::EntryInfo> entries;
   std::vector<GURL> file_urls;
@@ -827,47 +686,24 @@
   file_urls.emplace_back("filesystem:chrome-extension://id/dir/foo.txt");
 
   std::vector<FullTaskDescriptor> tasks;
-  FindAllTypesOfTasksSynchronousWrapper().Call(
-      &test_profile_, &drive_app_registry, entries, file_urls, &tasks);
-  ASSERT_EQ(3U, tasks.size());
+  FindAllTypesOfTasksSynchronousWrapper().Call(&test_profile_, entries,
+                                               file_urls, &tasks);
+  ASSERT_EQ(2U, tasks.size());
 
   // Sort the app IDs, as the order is not guaranteed.
   std::vector<std::string> app_ids;
   app_ids.push_back(tasks[0].task_descriptor().app_id);
   app_ids.push_back(tasks[1].task_descriptor().app_id);
-  app_ids.push_back(tasks[2].task_descriptor().app_id);
   std::sort(app_ids.begin(), app_ids.end());
   // Confirm that all apps are found.
   EXPECT_EQ(kFooId, app_ids[0]);
   EXPECT_EQ(kBarId, app_ids[1]);
-  EXPECT_EQ(kBazId, app_ids[2]);
 }
 
 TEST_F(FileManagerFileTasksComplexTest, FindAllTypesOfTasks_GoogleDocument) {
   // kFooId and kBarId copied from FindFileHandlerTasks test above.
-  const char kFooId[] = "hhgbjpmdppecanaaogonaigmmifgpaph";
   const char kBarId[] = "odlhccgofgkadkkhcmhgnhgahonahoca";
 
-  // Foo.app can handle ".gdoc" files.
-  std::unique_ptr<google_apis::AppResource> foo_app(
-      new google_apis::AppResource);
-  foo_app->set_product_id("foo_app");
-  foo_app->set_application_id(kFooId);
-  foo_app->set_name("Foo");
-  foo_app->set_object_type("foo_object_type");
-  std::vector<std::unique_ptr<std::string>> foo_extensions;
-  foo_extensions.push_back(
-      std::make_unique<std::string>("gdoc"));  // Not ".gdoc"
-  foo_app->set_primary_file_extensions(std::move(foo_extensions));
-
-  // Prepare DriveAppRegistry from Foo.app.
-  std::vector<std::unique_ptr<google_apis::AppResource>> app_resources;
-  app_resources.push_back(std::move(foo_app));
-  google_apis::AppList app_list;
-  app_list.set_items(std::move(app_resources));
-  drive::DriveAppRegistry drive_app_registry(nullptr);
-  drive_app_registry.UpdateFromAppList(app_list);
-
   // Bar.app can handle ".gdoc" files.
   // This is an extension (file browser handler).
   extensions::ExtensionBuilder bar_app;
@@ -929,8 +765,8 @@
   file_urls.emplace_back("filesystem:chrome-extension://id/dir/foo.gdoc");
 
   std::vector<FullTaskDescriptor> tasks;
-  FindAllTypesOfTasksSynchronousWrapper().Call(
-      &test_profile_, &drive_app_registry, entries, file_urls, &tasks);
+  FindAllTypesOfTasksSynchronousWrapper().Call(&test_profile_, entries,
+                                               file_urls, &tasks);
   ASSERT_EQ(1U, tasks.size());
   EXPECT_EQ(kFileManagerAppId, tasks[0].task_descriptor().app_id);
 }
@@ -1119,7 +955,7 @@
   EXPECT_TRUE(dir_result[0].is_generic_file_handler());
 }
 
-// The basic logic is similar to a test case for FindDriveAppTasks above.
+// The basic logic is similar to a test case for FindFileHandlerTasks above.
 TEST_F(FileManagerFileTasksComplexTest, FindFileHandlerTask_Verbs) {
   // kFooId copied from FindFileHandlerTasks test above.
   const char kFooId[] = "hhgbjpmdppecanaaogonaigmmifgpaph";
@@ -1315,7 +1151,7 @@
       GURL("filesystem:chrome-extension://id/dir/foo.txt")};
 
   std::vector<FullTaskDescriptor> tasks;
-  FindAllTypesOfTasksSynchronousWrapper().Call(&test_profile_, nullptr, entries,
+  FindAllTypesOfTasksSynchronousWrapper().Call(&test_profile_, entries,
                                                file_urls, &tasks);
   ASSERT_EQ(1U, tasks.size());
   EXPECT_EQ(text_app_id_, tasks[0].task_descriptor().app_id);
@@ -1323,7 +1159,7 @@
   // Multiple text files
   entries.emplace_back(crostini_folder_.Append("bar.txt"), "text/plain", false);
   file_urls.emplace_back("filesystem:chrome-extension://id/dir/bar.txt");
-  FindAllTypesOfTasksSynchronousWrapper().Call(&test_profile_, nullptr, entries,
+  FindAllTypesOfTasksSynchronousWrapper().Call(&test_profile_, entries,
                                                file_urls, &tasks);
   ASSERT_EQ(1U, tasks.size());
   EXPECT_EQ(text_app_id_, tasks[0].task_descriptor().app_id);
@@ -1334,13 +1170,13 @@
       {crostini_folder_.Append("dir"), "", true}};
   std::vector<GURL> file_urls{GURL("filesystem:chrome-extension://id/dir/dir")};
   std::vector<FullTaskDescriptor> tasks;
-  FindAllTypesOfTasksSynchronousWrapper().Call(&test_profile_, nullptr, entries,
+  FindAllTypesOfTasksSynchronousWrapper().Call(&test_profile_, entries,
                                                file_urls, &tasks);
   EXPECT_EQ(0U, tasks.size());
 
   entries.emplace_back(crostini_folder_.Append("foo.txt"), "text/plain", false);
   file_urls.emplace_back("filesystem:chrome-extension://id/dir/foo.txt");
-  FindAllTypesOfTasksSynchronousWrapper().Call(&test_profile_, nullptr, entries,
+  FindAllTypesOfTasksSynchronousWrapper().Call(&test_profile_, entries,
                                                file_urls, &tasks);
   EXPECT_EQ(0U, tasks.size());
 }
@@ -1354,7 +1190,7 @@
       GURL("filesystem:chrome-extension://id/dir/bar.gif")};
 
   std::vector<FullTaskDescriptor> tasks;
-  FindAllTypesOfTasksSynchronousWrapper().Call(&test_profile_, nullptr, entries,
+  FindAllTypesOfTasksSynchronousWrapper().Call(&test_profile_, entries,
                                                file_urls, &tasks);
   // The returned values happen to be ordered alphabetically by app_id, so we
   // rely on this to keep the test simple.
@@ -1373,14 +1209,14 @@
       GURL("filesystem:chrome-extension://id/dir/bar.png")};
 
   std::vector<FullTaskDescriptor> tasks;
-  FindAllTypesOfTasksSynchronousWrapper().Call(&test_profile_, nullptr, entries,
+  FindAllTypesOfTasksSynchronousWrapper().Call(&test_profile_, entries,
                                                file_urls, &tasks);
   ASSERT_EQ(1U, tasks.size());
   EXPECT_EQ(image_app_id_, tasks[0].task_descriptor().app_id);
 
   entries.emplace_back(crostini_folder_.Append("qux.mp4"), "video/mp4", false);
   file_urls.emplace_back("filesystem:chrome-extension://id/dir/qux.mp4");
-  FindAllTypesOfTasksSynchronousWrapper().Call(&test_profile_, nullptr, entries,
+  FindAllTypesOfTasksSynchronousWrapper().Call(&test_profile_, entries,
                                                file_urls, &tasks);
   EXPECT_EQ(0U, tasks.size());
 }
@@ -1394,7 +1230,7 @@
       GURL("filesystem:chrome-extension://id/dir/bar2.foo")};
 
   std::vector<FullTaskDescriptor> tasks;
-  FindAllTypesOfTasksSynchronousWrapper().Call(&test_profile_, nullptr, entries,
+  FindAllTypesOfTasksSynchronousWrapper().Call(&test_profile_, entries,
                                                file_urls, &tasks);
   ASSERT_EQ(1U, tasks.size());
   EXPECT_EQ(alt_mime_app_id_, tasks[0].task_descriptor().app_id);
diff --git a/chrome/browser/chromeos/file_manager/open_util.cc b/chrome/browser/chromeos/file_manager/open_util.cc
index 25fc7f2..3b83e03 100644
--- a/chrome/browser/chromeos/file_manager/open_util.cc
+++ b/chrome/browser/chromeos/file_manager/open_util.cc
@@ -119,8 +119,7 @@
   file_urls.push_back(url);
 
   file_tasks::FindAllTypesOfTasks(
-      profile, drive::util::GetDriveAppRegistryByProfile(profile), entries,
-      file_urls,
+      profile, entries, file_urls,
       base::Bind(&OpenFileMimeTypeAfterTasksListed, profile, url, callback));
 }
 
diff --git a/chrome/browser/chromeos/power/auto_screen_brightness/als_reader.h b/chrome/browser/chromeos/power/auto_screen_brightness/als_reader.h
index 29208f5..b8ac5b4c 100644
--- a/chrome/browser/chromeos/power/auto_screen_brightness/als_reader.h
+++ b/chrome/browser/chromeos/power/auto_screen_brightness/als_reader.h
@@ -5,34 +5,15 @@
 #ifndef CHROME_BROWSER_CHROMEOS_POWER_AUTO_SCREEN_BRIGHTNESS_ALS_READER_H_
 #define CHROME_BROWSER_CHROMEOS_POWER_AUTO_SCREEN_BRIGHTNESS_ALS_READER_H_
 
-#include "base/files/file_path.h"
-#include "base/macros.h"
-#include "base/memory/scoped_refptr.h"
-#include "base/memory/weak_ptr.h"
-#include "base/observer_list.h"
-#include "base/sequenced_task_runner.h"
-#include "base/task_runner_util.h"
-#include "base/timer/timer.h"
+#include "base/observer_list_types.h"
 
 namespace chromeos {
 namespace power {
 namespace auto_screen_brightness {
 
-// AlsReader periodically reads lux values from the ambient light sensor (ALS)
-// if powerd has been configured to use it.
+// Interface to ambient light reader.
 class AlsReader {
  public:
-  // ALS file location may not be ready immediately, so we retry every
-  // |kAlsFileCheckingInterval| until |kMaxInitialAttempts| is reached, then
-  // we give up.
-  static constexpr base::TimeDelta kAlsFileCheckingInterval =
-      base::TimeDelta::FromSeconds(1);
-  static constexpr int kMaxInitialAttempts = 20;
-
-  // Interval for polling ambient light values.
-  static constexpr base::TimeDelta kAlsPollInterval =
-      base::TimeDelta::FromSeconds(1);
-
   // Status of AlsReader initialization.
   enum class AlsInitStatus {
     kSuccess = 0,
@@ -43,8 +24,8 @@
     kMaxValue = kMissingPath
   };
 
-  // Observers should take WeakPtr of AlsReader and remove themselves in
-  // observers' destructors if AlsReader hasn't be destructed.
+  // Observers should take WeakPtr of AlsReader and remove themselves
+  // in observers' destructors if AlsReader hasn't be destructed.
   class Observer : public base::CheckedObserver {
    public:
     Observer() = default;
@@ -56,72 +37,15 @@
     DISALLOW_COPY_AND_ASSIGN(Observer);
   };
 
-  AlsReader();
-  ~AlsReader();
+  virtual ~AlsReader() = default;
 
   // Adds or removes an observer.
-  void AddObserver(Observer* observer);
-  void RemoveObserver(Observer* observer);
+  virtual void AddObserver(Observer* observer) = 0;
+  virtual void RemoveObserver(Observer* observer) = 0;
 
   // An observer can call this method to check if ALS has been properly
   // initialized and ready to use.
-  AlsInitStatus GetInitStatus() const;
-
-  // Checks if an ALS is enabled, and if the config is valid . Also
-  // reads ambient light file path.
-  void Init();
-
-  // Sets the task runner for testing purpose.
-  void SetTaskRunnerForTesting(
-      scoped_refptr<base::SequencedTaskRunner> task_runner);
-
-  // Sets ambient light path for testing purpose and initialize. This will cause
-  // all the checks to be skipped, i.e. whether ALS is enabled and if config is
-  // valid.
-  void InitForTesting(const base::FilePath& ambient_light_path);
-
-  base::WeakPtr<AlsReader> AsWeakPtr();
-
- private:
-  friend class AlsReaderTest;
-
-  // Called when we've checked whether ALS is enabled.
-  void OnAlsEnableCheckDone(bool is_enabled);
-
-  // Called when we've checked whether ALS config is valid.
-  void OnAlsConfigCheckDone(bool is_config_valid);
-
-  // Called when we've tried to read ALS path. If |path| is empty, it would
-  // reschedule another attempt up to |kMaxInitialAttempts|.
-  void OnAlsPathReadAttempted(const std::string& path);
-
-  // Tries to read ALS path.
-  void RetryAlsPath();
-
-  // Notifies all observers with |status_| after AlsReader is initialized.
-  void OnInitializationComplete();
-
-  // Polls ambient light periodically and notifies all observers if a sample is
-  // read.
-  void ReadAlsPeriodically();
-
-  // This is called after ambient light (represented as |data|) is sampled. It
-  // parses |data| to int, notifies its observers and starts |als_timer_| for
-  // next sample.
-  void OnAlsRead(const std::string& data);
-
-  AlsInitStatus status_ = AlsInitStatus::kInProgress;
-  base::FilePath ambient_light_path_;
-  int num_failed_initialization_ = 0;
-
-  // Timer used to retry initialization and also for periodic ambient light
-  // sampling.
-  base::OneShotTimer als_timer_;
-  scoped_refptr<base::SequencedTaskRunner> als_task_runner_;
-  base::ObserverList<Observer> observers_;
-  base::WeakPtrFactory<AlsReader> weak_ptr_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(AlsReader);
+  virtual AlsInitStatus GetInitStatus() const = 0;
 };
 
 }  // namespace auto_screen_brightness
diff --git a/chrome/browser/chromeos/power/auto_screen_brightness/als_reader.cc b/chrome/browser/chromeos/power/auto_screen_brightness/als_reader_impl.cc
similarity index 80%
rename from chrome/browser/chromeos/power/auto_screen_brightness/als_reader.cc
rename to chrome/browser/chromeos/power/auto_screen_brightness/als_reader_impl.cc
index 0a57e8f..9f38458 100644
--- a/chrome/browser/chromeos/power/auto_screen_brightness/als_reader.cc
+++ b/chrome/browser/chromeos/power/auto_screen_brightness/als_reader_impl.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/power/auto_screen_brightness/als_reader.h"
+#include "chrome/browser/chromeos/power/auto_screen_brightness/als_reader_impl.h"
 
 #include "base/command_line.h"
 #include "base/files/file_util.h"
@@ -101,45 +101,49 @@
 }
 }  // namespace
 
-constexpr base::TimeDelta AlsReader::kAlsFileCheckingInterval;
-constexpr int AlsReader::kMaxInitialAttempts;
-constexpr base::TimeDelta AlsReader::kAlsPollInterval;
+constexpr base::TimeDelta AlsReaderImpl::kAlsFileCheckingInterval;
+constexpr int AlsReaderImpl::kMaxInitialAttempts;
+constexpr base::TimeDelta AlsReaderImpl::kAlsPollInterval;
 
-AlsReader::AlsReader()
+AlsReaderImpl::AlsReaderImpl()
     : als_task_runner_(base::CreateSequencedTaskRunnerWithTraits(
           {base::TaskPriority::BEST_EFFORT, base::MayBlock(),
            base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})),
       weak_ptr_factory_(this) {}
 
-AlsReader::~AlsReader() = default;
+AlsReaderImpl::~AlsReaderImpl() = default;
 
-void AlsReader::AddObserver(Observer* const observer) {
+void AlsReaderImpl::AddObserver(Observer* const observer) {
   DCHECK(observer);
   observers_.AddObserver(observer);
 }
 
-void AlsReader::RemoveObserver(Observer* const observer) {
+void AlsReaderImpl::RemoveObserver(Observer* const observer) {
   DCHECK(observer);
   observers_.RemoveObserver(observer);
 }
 
-AlsReader::AlsInitStatus AlsReader::GetInitStatus() const {
+AlsReader::AlsInitStatus AlsReaderImpl::GetInitStatus() const {
   return status_;
 }
 
-void AlsReader::Init() {
+void AlsReaderImpl::Init() {
   base::PostTaskAndReplyWithResult(
       als_task_runner_.get(), FROM_HERE, base::BindOnce(&IsAlsEnabled),
-      base::BindOnce(&AlsReader::OnAlsEnableCheckDone, AsWeakPtr()));
+      base::BindOnce(&AlsReaderImpl::OnAlsEnableCheckDone, AsWeakPtr()));
 }
 
-void AlsReader::SetTaskRunnerForTesting(
+base::WeakPtr<AlsReaderImpl> AlsReaderImpl::AsWeakPtr() {
+  return weak_ptr_factory_.GetWeakPtr();
+}
+
+void AlsReaderImpl::SetTaskRunnerForTesting(
     const scoped_refptr<base::SequencedTaskRunner> task_runner) {
   als_task_runner_ = task_runner;
   als_timer_.SetTaskRunner(task_runner);
 }
 
-void AlsReader::InitForTesting(const base::FilePath& ambient_light_path) {
+void AlsReaderImpl::InitForTesting(const base::FilePath& ambient_light_path) {
   DCHECK(!ambient_light_path.empty());
   ambient_light_path_ = ambient_light_path;
   status_ = AlsInitStatus::kSuccess;
@@ -147,11 +151,7 @@
   ReadAlsPeriodically();
 }
 
-base::WeakPtr<AlsReader> AlsReader::AsWeakPtr() {
-  return weak_ptr_factory_.GetWeakPtr();
-}
-
-void AlsReader::OnAlsEnableCheckDone(const bool is_enabled) {
+void AlsReaderImpl::OnAlsEnableCheckDone(const bool is_enabled) {
   if (!is_enabled) {
     status_ = AlsInitStatus::kDisabled;
     OnInitializationComplete();
@@ -160,10 +160,10 @@
 
   base::PostTaskAndReplyWithResult(
       als_task_runner_.get(), FROM_HERE, base::BindOnce(&VerifyAlsConfig),
-      base::BindOnce(&AlsReader::OnAlsConfigCheckDone, AsWeakPtr()));
+      base::BindOnce(&AlsReaderImpl::OnAlsConfigCheckDone, AsWeakPtr()));
 }
 
-void AlsReader::OnAlsConfigCheckDone(const bool is_config_valid) {
+void AlsReaderImpl::OnAlsConfigCheckDone(const bool is_config_valid) {
   if (!is_config_valid) {
     status_ = AlsInitStatus::kIncorrectConfig;
     OnInitializationComplete();
@@ -173,7 +173,7 @@
   RetryAlsPath();
 }
 
-void AlsReader::OnAlsPathReadAttempted(const std::string& path) {
+void AlsReaderImpl::OnAlsPathReadAttempted(const std::string& path) {
   if (!path.empty()) {
     ambient_light_path_ = base::FilePath(path);
     status_ = AlsInitStatus::kSuccess;
@@ -191,29 +191,29 @@
   }
 
   als_timer_.Start(FROM_HERE, kAlsFileCheckingInterval, this,
-                   &AlsReader::RetryAlsPath);
+                   &AlsReaderImpl::RetryAlsPath);
 }
 
-void AlsReader::RetryAlsPath() {
+void AlsReaderImpl::RetryAlsPath() {
   base::PostTaskAndReplyWithResult(
       als_task_runner_.get(), FROM_HERE, base::BindOnce(&GetAlsPath),
-      base::BindOnce(&AlsReader::OnAlsPathReadAttempted, AsWeakPtr()));
+      base::BindOnce(&AlsReaderImpl::OnAlsPathReadAttempted, AsWeakPtr()));
 }
 
-void AlsReader::OnInitializationComplete() {
+void AlsReaderImpl::OnInitializationComplete() {
   DCHECK_NE(status_, AlsInitStatus::kInProgress);
   for (auto& observer : observers_)
     observer.OnAlsReaderInitialized(status_);
 }
 
-void AlsReader::ReadAlsPeriodically() {
+void AlsReaderImpl::ReadAlsPeriodically() {
   base::PostTaskAndReplyWithResult(
       als_task_runner_.get(), FROM_HERE,
       base::BindOnce(&ReadAlsFromFile, ambient_light_path_),
-      base::BindOnce(&AlsReader::OnAlsRead, AsWeakPtr()));
+      base::BindOnce(&AlsReaderImpl::OnAlsRead, AsWeakPtr()));
 }
 
-void AlsReader::OnAlsRead(const std::string& data) {
+void AlsReaderImpl::OnAlsRead(const std::string& data) {
   std::string trimmed_data;
   base::TrimWhitespaceASCII(data, base::TRIM_ALL, &trimmed_data);
   int value = 0;
@@ -222,7 +222,7 @@
       observer.OnAmbientLightUpdated(value);
   }
   als_timer_.Start(FROM_HERE, kAlsPollInterval, this,
-                   &AlsReader::ReadAlsPeriodically);
+                   &AlsReaderImpl::ReadAlsPeriodically);
 }
 
 }  // namespace auto_screen_brightness
diff --git a/chrome/browser/chromeos/power/auto_screen_brightness/als_reader_impl.h b/chrome/browser/chromeos/power/auto_screen_brightness/als_reader_impl.h
new file mode 100644
index 0000000..cd39219
--- /dev/null
+++ b/chrome/browser/chromeos/power/auto_screen_brightness/als_reader_impl.h
@@ -0,0 +1,108 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_POWER_AUTO_SCREEN_BRIGHTNESS_ALS_READER_IMPL_H_
+#define CHROME_BROWSER_CHROMEOS_POWER_AUTO_SCREEN_BRIGHTNESS_ALS_READER_IMPL_H_
+
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/observer_list.h"
+#include "base/sequenced_task_runner.h"
+#include "base/task_runner_util.h"
+#include "base/timer/timer.h"
+#include "chrome/browser/chromeos/power/auto_screen_brightness/als_reader.h"
+
+namespace chromeos {
+namespace power {
+namespace auto_screen_brightness {
+
+// Real implementation of AlsReader.
+// It periodically reads lux values from the ambient light sensor (ALS)
+// if powerd has been configured to use it.
+class AlsReaderImpl : public AlsReader {
+ public:
+  // ALS file location may not be ready immediately, so we retry every
+  // |kAlsFileCheckingInterval| until |kMaxInitialAttempts| is reached, then
+  // we give up.
+  static constexpr base::TimeDelta kAlsFileCheckingInterval =
+      base::TimeDelta::FromSeconds(1);
+  static constexpr int kMaxInitialAttempts = 20;
+
+  // Interval for polling ambient light values.
+  // TODO(jiameng): revise frequency.
+  static constexpr base::TimeDelta kAlsPollInterval =
+      base::TimeDelta::FromSeconds(1);
+
+  AlsReaderImpl();
+  ~AlsReaderImpl() override;
+
+  // AlsReader overrides:
+  void AddObserver(Observer* observer) override;
+  void RemoveObserver(Observer* observer) override;
+  AlsInitStatus GetInitStatus() const override;
+
+  // Checks if an ALS is enabled, and if the config is valid . Also
+  // reads ambient light file path.
+  void Init();
+
+  base::WeakPtr<AlsReaderImpl> AsWeakPtr();
+
+  // Sets the task runner for testing purpose.
+  void SetTaskRunnerForTesting(
+      scoped_refptr<base::SequencedTaskRunner> task_runner);
+
+  // Sets ambient light path for testing purpose and initialize. This will cause
+  // all the checks to be skipped, i.e. whether ALS is enabled and if config is
+  // valid.
+  void InitForTesting(const base::FilePath& ambient_light_path);
+
+ private:
+  friend class AlsReaderImplTest;
+
+  // Called when we've checked whether ALS is enabled.
+  void OnAlsEnableCheckDone(bool is_enabled);
+
+  // Called when we've checked whether ALS config is valid.
+  void OnAlsConfigCheckDone(bool is_config_valid);
+
+  // Called when we've tried to read ALS path. If |path| is empty, it would
+  // reschedule another attempt up to |kMaxInitialAttempts|.
+  void OnAlsPathReadAttempted(const std::string& path);
+
+  // Tries to read ALS path.
+  void RetryAlsPath();
+
+  // Notifies all observers with |status_| after AlsReaderImpl is initialized.
+  void OnInitializationComplete();
+
+  // Polls ambient light periodically and notifies all observers if a sample is
+  // read.
+  void ReadAlsPeriodically();
+
+  // This is called after ambient light (represented as |data|) is sampled. It
+  // parses |data| to int, notifies its observers and starts |als_timer_| for
+  // next sample.
+  void OnAlsRead(const std::string& data);
+
+  AlsInitStatus status_ = AlsInitStatus::kInProgress;
+  base::FilePath ambient_light_path_;
+  int num_failed_initialization_ = 0;
+
+  // Timer used to retry initialization and also for periodic ambient light
+  // sampling.
+  base::OneShotTimer als_timer_;
+  scoped_refptr<base::SequencedTaskRunner> als_task_runner_;
+  base::ObserverList<Observer> observers_;
+  base::WeakPtrFactory<AlsReaderImpl> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(AlsReaderImpl);
+};
+
+}  // namespace auto_screen_brightness
+}  // namespace power
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_CHROMEOS_POWER_AUTO_SCREEN_BRIGHTNESS_ALS_READER_IMPL_H_
diff --git a/chrome/browser/chromeos/power/auto_screen_brightness/als_reader_unittest.cc b/chrome/browser/chromeos/power/auto_screen_brightness/als_reader_impl_unittest.cc
similarity index 85%
rename from chrome/browser/chromeos/power/auto_screen_brightness/als_reader_unittest.cc
rename to chrome/browser/chromeos/power/auto_screen_brightness/als_reader_impl_unittest.cc
index 3ee5007..1dad110 100644
--- a/chrome/browser/chromeos/power/auto_screen_brightness/als_reader_unittest.cc
+++ b/chrome/browser/chromeos/power/auto_screen_brightness/als_reader_impl_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/power/auto_screen_brightness/als_reader.h"
+#include "chrome/browser/chromeos/power/auto_screen_brightness/als_reader_impl.h"
 
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
@@ -50,9 +50,9 @@
 };
 }  // namespace
 
-class AlsReaderTest : public testing::Test {
+class AlsReaderImplTest : public testing::Test {
  public:
-  AlsReaderTest()
+  AlsReaderImplTest()
       : scoped_task_environment_(
             std::make_unique<base::test::ScopedTaskEnvironment>(
                 base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME)) {
@@ -63,7 +63,7 @@
     als_reader_.InitForTesting(ambient_light_path_);
   }
 
-  ~AlsReaderTest() override = default;
+  ~AlsReaderImplTest() override { als_reader_.RemoveObserver(&test_observer_); }
 
  protected:
   void WriteLux(int lux) {
@@ -81,39 +81,39 @@
   std::unique_ptr<base::test::ScopedTaskEnvironment> scoped_task_environment_;
 
   TestObserver test_observer_;
-  AlsReader als_reader_;
+  AlsReaderImpl als_reader_;
 
  private:
-  DISALLOW_COPY_AND_ASSIGN(AlsReaderTest);
+  DISALLOW_COPY_AND_ASSIGN(AlsReaderImplTest);
 };
 
-TEST_F(AlsReaderTest, CheckInitStatusAlsFileFound) {
+TEST_F(AlsReaderImplTest, CheckInitStatusAlsFileFound) {
   EXPECT_EQ(AlsReader::AlsInitStatus::kSuccess, als_reader_.GetInitStatus());
 }
 
-TEST_F(AlsReaderTest, OnAlsReaderInitialized) {
+TEST_F(AlsReaderImplTest, OnAlsReaderInitialized) {
   EXPECT_EQ(AlsReader::AlsInitStatus::kSuccess, test_observer_.get_status());
 }
 
-TEST_F(AlsReaderTest, OneAlsValue) {
+TEST_F(AlsReaderImplTest, OneAlsValue) {
   WriteLux(10);
   scoped_task_environment_->RunUntilIdle();
   EXPECT_EQ(10, test_observer_.get_ambient_light());
   EXPECT_EQ(1, test_observer_.get_num_received_ambient_lights());
 }
 
-TEST_F(AlsReaderTest, TwoAlsValues) {
+TEST_F(AlsReaderImplTest, TwoAlsValues) {
   WriteLux(10);
   // Ambient light is read immediately after initialization, and then
   // periodically every |kAlsPollInterval|. Below we move time for half of
   // |kAlsPollInterval| to ensure there is only one reading attempt.
-  scoped_task_environment_->FastForwardBy(AlsReader::kAlsPollInterval / 2);
+  scoped_task_environment_->FastForwardBy(AlsReaderImpl::kAlsPollInterval / 2);
   EXPECT_EQ(10, test_observer_.get_ambient_light());
   EXPECT_EQ(1, test_observer_.get_num_received_ambient_lights());
 
   WriteLux(20);
   // Now move time for another |kAlsPollInterval| to trigger another read.
-  scoped_task_environment_->FastForwardBy(AlsReader::kAlsPollInterval);
+  scoped_task_environment_->FastForwardBy(AlsReaderImpl::kAlsPollInterval);
   EXPECT_EQ(20, test_observer_.get_ambient_light());
   EXPECT_EQ(2, test_observer_.get_num_received_ambient_lights());
 }
diff --git a/chrome/browser/chromeos/power/auto_screen_brightness/fake_als_reader.cc b/chrome/browser/chromeos/power/auto_screen_brightness/fake_als_reader.cc
new file mode 100644
index 0000000..23d73f1
--- /dev/null
+++ b/chrome/browser/chromeos/power/auto_screen_brightness/fake_als_reader.cc
@@ -0,0 +1,45 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/power/auto_screen_brightness/fake_als_reader.h"
+
+namespace chromeos {
+namespace power {
+namespace auto_screen_brightness {
+
+FakeAlsReader::FakeAlsReader() : weak_ptr_factory_(this) {}
+
+FakeAlsReader::~FakeAlsReader() = default;
+
+void FakeAlsReader::ReportAmbientLightUpdate(const int lux) {
+  for (auto& observer : observers_)
+    observer.OnAmbientLightUpdated(lux);
+}
+
+void FakeAlsReader::ReportReaderInitialized() {
+  for (auto& observer : observers_)
+    observer.OnAlsReaderInitialized(status_);
+}
+
+AlsReader::AlsInitStatus FakeAlsReader::GetInitStatus() const {
+  return status_;
+}
+
+void FakeAlsReader::AddObserver(Observer* const observer) {
+  DCHECK(observer);
+  observers_.AddObserver(observer);
+}
+
+void FakeAlsReader::RemoveObserver(Observer* const observer) {
+  DCHECK(observer);
+  observers_.RemoveObserver(observer);
+}
+
+base::WeakPtr<FakeAlsReader> FakeAlsReader::AsWeakPtr() {
+  return weak_ptr_factory_.GetWeakPtr();
+}
+
+}  // namespace auto_screen_brightness
+}  // namespace power
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/power/auto_screen_brightness/fake_als_reader.h b/chrome/browser/chromeos/power/auto_screen_brightness/fake_als_reader.h
new file mode 100644
index 0000000..18c6784
--- /dev/null
+++ b/chrome/browser/chromeos/power/auto_screen_brightness/fake_als_reader.h
@@ -0,0 +1,46 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_POWER_AUTO_SCREEN_BRIGHTNESS_FAKE_ALS_READER_H_
+#define CHROME_BROWSER_CHROMEOS_POWER_AUTO_SCREEN_BRIGHTNESS_FAKE_ALS_READER_H_
+
+#include "base/memory/weak_ptr.h"
+#include "base/observer_list.h"
+#include "chrome/browser/chromeos/power/auto_screen_brightness/als_reader.h"
+
+namespace chromeos {
+namespace power {
+namespace auto_screen_brightness {
+
+// This is a fake AlsReader used for testing only.
+class FakeAlsReader : public AlsReader {
+ public:
+  FakeAlsReader();
+  ~FakeAlsReader() override;
+
+  void set_als_init_status(AlsInitStatus status) { status_ = status; }
+
+  void ReportReaderInitialized();
+  void ReportAmbientLightUpdate(int lux);
+
+  // AlsReader overrides:
+  AlsInitStatus GetInitStatus() const override;
+  void AddObserver(Observer* observer) override;
+  void RemoveObserver(Observer* observer) override;
+
+  base::WeakPtr<FakeAlsReader> AsWeakPtr();
+
+ private:
+  AlsInitStatus status_ = AlsInitStatus::kInProgress;
+  base::ObserverList<Observer> observers_;
+  base::WeakPtrFactory<FakeAlsReader> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeAlsReader);
+};
+
+}  // namespace auto_screen_brightness
+}  // namespace power
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_CHROMEOS_POWER_AUTO_SCREEN_BRIGHTNESS_FAKE_ALS_READER_H_
diff --git a/chrome/browser/chromeos/power/auto_screen_brightness/fake_als_reader_unittest.cc b/chrome/browser/chromeos/power/auto_screen_brightness/fake_als_reader_unittest.cc
new file mode 100644
index 0000000..323bfd2
--- /dev/null
+++ b/chrome/browser/chromeos/power/auto_screen_brightness/fake_als_reader_unittest.cc
@@ -0,0 +1,85 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/power/auto_screen_brightness/fake_als_reader.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chromeos {
+namespace power {
+namespace auto_screen_brightness {
+
+namespace {
+
+class TestObserver : public AlsReader::Observer {
+ public:
+  TestObserver() {}
+  ~TestObserver() override = default;
+
+  // AlsReader::Observer overrides:
+  void OnAmbientLightUpdated(const int lux) override {
+    ambient_light_ = lux;
+    ++num_received_ambient_lights_;
+  }
+
+  void OnAlsReaderInitialized(const AlsReader::AlsInitStatus status) override {
+    status_ = base::Optional<AlsReader::AlsInitStatus>(status);
+  }
+
+  int get_ambient_light() const { return ambient_light_; }
+  int get_num_received_ambient_lights() const {
+    return num_received_ambient_lights_;
+  }
+
+  AlsReader::AlsInitStatus get_status() const {
+    CHECK(status_);
+    return status_.value();
+  }
+
+ private:
+  int ambient_light_ = -1;
+  int num_received_ambient_lights_ = 0;
+  base::Optional<AlsReader::AlsInitStatus> status_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestObserver);
+};
+}  // namespace
+
+class FakeAlsReaderTest : public testing::Test {
+ public:
+  FakeAlsReaderTest() { fake_als_reader_.AddObserver(&test_observer_); }
+
+  ~FakeAlsReaderTest() override {
+    fake_als_reader_.RemoveObserver(&test_observer_);
+  };
+
+ protected:
+  TestObserver test_observer_;
+  FakeAlsReader fake_als_reader_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(FakeAlsReaderTest);
+};
+
+TEST_F(FakeAlsReaderTest, GetInitStatus) {
+  fake_als_reader_.set_als_init_status(AlsReader::AlsInitStatus::kDisabled);
+  EXPECT_EQ(AlsReader::AlsInitStatus::kDisabled,
+            fake_als_reader_.GetInitStatus());
+}
+
+TEST_F(FakeAlsReaderTest, ReportReaderInitialized) {
+  fake_als_reader_.set_als_init_status(AlsReader::AlsInitStatus::kSuccess);
+  fake_als_reader_.ReportReaderInitialized();
+  EXPECT_EQ(AlsReader::AlsInitStatus::kSuccess, test_observer_.get_status());
+}
+
+TEST_F(FakeAlsReaderTest, ReportAmbientLightUpdate) {
+  fake_als_reader_.ReportAmbientLightUpdate(10);
+  EXPECT_EQ(10, test_observer_.get_ambient_light());
+  EXPECT_EQ(1, test_observer_.get_num_received_ambient_lights());
+}
+
+}  // namespace auto_screen_brightness
+}  // namespace power
+}  // namespace chromeos
diff --git a/chrome/browser/devtools/devtools_sanity_browsertest.cc b/chrome/browser/devtools/devtools_sanity_browsertest.cc
index 5ae6a03..9289883 100644
--- a/chrome/browser/devtools/devtools_sanity_browsertest.cc
+++ b/chrome/browser/devtools/devtools_sanity_browsertest.cc
@@ -1347,7 +1347,7 @@
 
   std::string message;
   EXPECT_TRUE(ExecuteScriptAndExtractString(
-      devtools_iframe_rfh, "domAutomationController.send(document.origin)",
+      devtools_iframe_rfh, "domAutomationController.send(self.origin)",
       &message));
   EXPECT_EQ(devtools_url.GetOrigin().spec(), message + "/");
 }
diff --git a/chrome/browser/devtools/devtools_window.cc b/chrome/browser/devtools/devtools_window.cc
index 4623681..ee982a6 100644
--- a/chrome/browser/devtools/devtools_window.cc
+++ b/chrome/browser/devtools/devtools_window.cc
@@ -1274,8 +1274,9 @@
   return chrome::ShowColorChooser(web_contents, initial_color);
 }
 
-void DevToolsWindow::RunFileChooser(content::RenderFrameHost* render_frame_host,
-                                    const content::FileChooserParams& params) {
+void DevToolsWindow::RunFileChooser(
+    content::RenderFrameHost* render_frame_host,
+    const blink::mojom::FileChooserParams& params) {
   FileSelectHelper::RunFileChooser(render_frame_host, params);
 }
 
diff --git a/chrome/browser/devtools/devtools_window.h b/chrome/browser/devtools/devtools_window.h
index 32f107e9..5c6c9be 100644
--- a/chrome/browser/devtools/devtools_window.h
+++ b/chrome/browser/devtools/devtools_window.h
@@ -330,7 +330,7 @@
       const std::vector<blink::mojom::ColorSuggestionPtr>& suggestions)
       override;
   void RunFileChooser(content::RenderFrameHost* render_frame_host,
-                      const content::FileChooserParams& params) override;
+                      const blink::mojom::FileChooserParams& params) override;
   bool PreHandleGestureEvent(content::WebContents* source,
                              const blink::WebGestureEvent& event) override;
 
diff --git a/chrome/browser/extensions/api/file_system/file_system_apitest_chromeos.cc b/chrome/browser/extensions/api/file_system/file_system_apitest_chromeos.cc
index a1706c7..d4c7f93 100644
--- a/chrome/browser/extensions/api/file_system/file_system_apitest_chromeos.cc
+++ b/chrome/browser/extensions/api/file_system/file_system_apitest_chromeos.cc
@@ -166,7 +166,6 @@
     // exist simultaneously.
     DCHECK(!fake_drive_service_);
     fake_drive_service_ = new drive::FakeDriveService;
-    fake_drive_service_->LoadAppListForDriveApi("drive/applist.json");
 
     SetUpTestFileHierarchy();
 
diff --git a/chrome/browser/extensions/extension_view_host.cc b/chrome/browser/extensions/extension_view_host.cc
index 1ee4e41..3d824f0 100644
--- a/chrome/browser/extensions/extension_view_host.cc
+++ b/chrome/browser/extensions/extension_view_host.cc
@@ -224,7 +224,7 @@
 
 void ExtensionViewHost::RunFileChooser(
     content::RenderFrameHost* render_frame_host,
-    const content::FileChooserParams& params) {
+    const blink::mojom::FileChooserParams& params) {
   // For security reasons opening a file picker requires a visible <input>
   // element to click on, so this code only exists for extensions with a view.
   FileSelectHelper::RunFileChooser(render_frame_host, params);
diff --git a/chrome/browser/extensions/extension_view_host.h b/chrome/browser/extensions/extension_view_host.h
index 91beaf6..9d7d5e2 100644
--- a/chrome/browser/extensions/extension_view_host.h
+++ b/chrome/browser/extensions/extension_view_host.h
@@ -83,7 +83,7 @@
       const std::vector<blink::mojom::ColorSuggestionPtr>& suggestions)
       override;
   void RunFileChooser(content::RenderFrameHost* render_frame_host,
-                      const content::FileChooserParams& params) override;
+                      const blink::mojom::FileChooserParams& params) override;
   void ResizeDueToAutoResize(content::WebContents* source,
                              const gfx::Size& new_size) override;
 
diff --git a/chrome/browser/file_select_helper.cc b/chrome/browser/file_select_helper.cc
index 0afd859..a0a2bcf 100644
--- a/chrome/browser/file_select_helper.cc
+++ b/chrome/browser/file_select_helper.cc
@@ -34,7 +34,6 @@
 #include "content/public/browser/storage_partition.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/file_chooser_file_info.h"
-#include "content/public/common/file_chooser_params.h"
 #include "net/base/filename_util.h"
 #include "net/base/mime_util.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -51,8 +50,9 @@
 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
 #endif
 
+using blink::mojom::FileChooserParams;
+using blink::mojom::FileChooserParamsPtr;
 using content::BrowserThread;
-using content::FileChooserParams;
 using content::RenderViewHost;
 using content::RenderWidgetHost;
 using content::WebContents;
@@ -138,7 +138,7 @@
       select_file_dialog_(),
       select_file_types_(),
       dialog_type_(ui::SelectFileDialog::SELECT_OPEN_FILE),
-      dialog_mode_(FileChooserParams::Open),
+      dialog_mode_(FileChooserParams::Mode::kOpen),
       observer_(this) {}
 
 FileSelectHelper::~FileSelectHelper() {
@@ -159,7 +159,7 @@
     void* params) {
   if (IsValidProfile(profile_)) {
     base::FilePath path = file.file_path;
-    if (dialog_mode_ != FileChooserParams::UploadFolder)
+    if (dialog_mode_ != FileChooserParams::Mode::kUploadFolder)
       path = path.DirName();
     profile_->set_last_selected_directory(path);
   }
@@ -203,7 +203,7 @@
     void* params) {
   if (!files.empty() && IsValidProfile(profile_)) {
     base::FilePath path = files[0].file_path;
-    if (dialog_mode_ != FileChooserParams::UploadFolder)
+    if (dialog_mode_ != FileChooserParams::Mode::kUploadFolder)
       path = path.DirName();
     profile_->set_last_selected_directory(path);
   }
@@ -425,8 +425,7 @@
   // FileSelectHelper will keep itself alive until it sends the result message.
   scoped_refptr<FileSelectHelper> file_select_helper(
       new FileSelectHelper(profile));
-  file_select_helper->RunFileChooser(
-      render_frame_host, std::make_unique<content::FileChooserParams>(params));
+  file_select_helper->RunFileChooser(render_frame_host, params.Clone());
 }
 
 // static
@@ -443,11 +442,11 @@
 
 void FileSelectHelper::RunFileChooser(
     content::RenderFrameHost* render_frame_host,
-    std::unique_ptr<FileChooserParams> params) {
+    FileChooserParamsPtr params) {
   DCHECK(!render_frame_host_);
   DCHECK(!web_contents_);
   DCHECK(params->default_file_name.empty() ||
-         params->mode == FileChooserParams::Save)
+         params->mode == FileChooserParams::Mode::kSave)
       << "The default_file_name parameter should only be specified for Save "
          "file choosers";
   DCHECK(params->default_file_name == params->default_file_name.BaseName())
@@ -472,8 +471,7 @@
   AddRef();
 }
 
-void FileSelectHelper::GetFileTypesInThreadPool(
-    std::unique_ptr<FileChooserParams> params) {
+void FileSelectHelper::GetFileTypesInThreadPool(FileChooserParamsPtr params) {
   select_file_types_ = GetFileTypesFromAcceptType(params->accept_types);
   select_file_types_->allowed_paths =
       params->need_local_path ? ui::SelectFileDialog::FileTypeInfo::NATIVE_PATH
@@ -486,14 +484,14 @@
 }
 
 void FileSelectHelper::GetSanitizedFilenameOnUIThread(
-    std::unique_ptr<FileChooserParams> params) {
+    FileChooserParamsPtr params) {
   if (AbortIfWebContentsDestroyed())
     return;
 
   base::FilePath default_file_path = profile_->last_selected_directory().Append(
       GetSanitizedFileName(params->default_file_name));
 #if defined(FULL_SAFE_BROWSING)
-  if (params->mode == FileChooserParams::Save) {
+  if (params->mode == FileChooserParams::Mode::kSave) {
     CheckDownloadRequestWithSafeBrowsing(default_file_path, std::move(params));
     return;
   }
@@ -504,7 +502,7 @@
 #if defined(FULL_SAFE_BROWSING)
 void FileSelectHelper::CheckDownloadRequestWithSafeBrowsing(
     const base::FilePath& default_file_path,
-    std::unique_ptr<FileChooserParams> params) {
+    FileChooserParamsPtr params) {
   safe_browsing::SafeBrowsingService* sb_service =
       g_browser_process->safe_browsing_service();
 
@@ -539,7 +537,7 @@
 
 void FileSelectHelper::ProceedWithSafeBrowsingVerdict(
     const base::FilePath& default_file_path,
-    std::unique_ptr<content::FileChooserParams> params,
+    FileChooserParamsPtr params,
     bool allowed_by_safe_browsing) {
   if (!allowed_by_safe_browsing) {
     NotifyRenderFrameHostAndEnd(std::vector<ui::SelectedFileInfo>());
@@ -551,7 +549,7 @@
 
 void FileSelectHelper::RunFileChooserOnUIThread(
     const base::FilePath& default_file_path,
-    std::unique_ptr<FileChooserParams> params) {
+    FileChooserParamsPtr params) {
   DCHECK(params);
   if (AbortIfWebContentsDestroyed())
     return;
@@ -563,16 +561,16 @@
 
   dialog_mode_ = params->mode;
   switch (params->mode) {
-    case FileChooserParams::Open:
+    case FileChooserParams::Mode::kOpen:
       dialog_type_ = ui::SelectFileDialog::SELECT_OPEN_FILE;
       break;
-    case FileChooserParams::OpenMultiple:
+    case FileChooserParams::Mode::kOpenMultiple:
       dialog_type_ = ui::SelectFileDialog::SELECT_OPEN_MULTI_FILE;
       break;
-    case FileChooserParams::UploadFolder:
+    case FileChooserParams::Mode::kUploadFolder:
       dialog_type_ = ui::SelectFileDialog::SELECT_UPLOAD_FOLDER;
       break;
-    case FileChooserParams::Save:
+    case FileChooserParams::Mode::kSave:
       dialog_type_ = ui::SelectFileDialog::SELECT_SAVEAS_FILE;
       break;
     default:
@@ -587,7 +585,7 @@
 #if defined(OS_ANDROID)
   // Android needs the original MIME types and an additional capture value.
   std::pair<std::vector<base::string16>, bool> accept_types =
-      std::make_pair(params->accept_types, params->capture);
+      std::make_pair(params->accept_types, params->use_media_capture);
 #endif
 
   select_file_dialog_->SelectFile(
diff --git a/chrome/browser/file_select_helper.h b/chrome/browser/file_select_helper.h
index c755925..2ae4ee3 100644
--- a/chrome/browser/file_select_helper.h
+++ b/chrome/browser/file_select_helper.h
@@ -17,8 +17,8 @@
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/render_widget_host_observer.h"
 #include "content/public/browser/web_contents_observer.h"
-#include "content/public/common/file_chooser_params.h"
 #include "net/base/directory_lister.h"
+#include "third_party/blink/public/mojom/choosers/file_chooser.mojom.h"
 #include "ui/shell_dialogs/select_file_dialog.h"
 
 class Profile;
@@ -50,7 +50,7 @@
  public:
   // Show the file chooser dialog.
   static void RunFileChooser(content::RenderFrameHost* render_frame_host,
-                             const content::FileChooserParams& params);
+                             const blink::mojom::FileChooserParams& params);
 
   // Enumerates all the files in directory.
   static void EnumerateDirectory(content::WebContents* tab,
@@ -71,23 +71,20 @@
   ~FileSelectHelper() override;
 
   void RunFileChooser(content::RenderFrameHost* render_frame_host,
-                      std::unique_ptr<content::FileChooserParams> params);
-  void GetFileTypesInThreadPool(
-      std::unique_ptr<content::FileChooserParams> params);
+                      blink::mojom::FileChooserParamsPtr params);
+  void GetFileTypesInThreadPool(blink::mojom::FileChooserParamsPtr params);
   void GetSanitizedFilenameOnUIThread(
-      std::unique_ptr<content::FileChooserParams> params);
+      blink::mojom::FileChooserParamsPtr params);
 #if defined(FULL_SAFE_BROWSING)
   void CheckDownloadRequestWithSafeBrowsing(
       const base::FilePath& default_path,
-      std::unique_ptr<content::FileChooserParams> params);
-  void ProceedWithSafeBrowsingVerdict(
-      const base::FilePath& default_path,
-      std::unique_ptr<content::FileChooserParams> params,
-      bool allowed_by_safe_browsing);
+      blink::mojom::FileChooserParamsPtr params);
+  void ProceedWithSafeBrowsingVerdict(const base::FilePath& default_path,
+                                      blink::mojom::FileChooserParamsPtr params,
+                                      bool allowed_by_safe_browsing);
 #endif
-  void RunFileChooserOnUIThread(
-      const base::FilePath& default_path,
-      std::unique_ptr<content::FileChooserParams> params);
+  void RunFileChooserOnUIThread(const base::FilePath& default_path,
+                                blink::mojom::FileChooserParamsPtr params);
 
   // Cleans up and releases this instance. This must be called after the last
   // callback is received from the file chooser dialog.
@@ -221,7 +218,7 @@
   ui::SelectFileDialog::Type dialog_type_;
 
   // The mode of file dialog last shown.
-  content::FileChooserParams::Mode dialog_mode_;
+  blink::mojom::FileChooserParams::Mode dialog_mode_;
 
   // Maintain an active directory enumeration.  These could come from the file
   // select dialog or from drag-and-drop of directories.  There could not be
diff --git a/chrome/browser/file_select_helper_unittest.cc b/chrome/browser/file_select_helper_unittest.cc
index 8fe4b75..1f586f8 100644
--- a/chrome/browser/file_select_helper_unittest.cc
+++ b/chrome/browser/file_select_helper_unittest.cc
@@ -20,7 +20,7 @@
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-using content::FileChooserParams;
+using blink::mojom::FileChooserParams;
 
 class FileSelectHelperTest : public testing::Test {
  public:
@@ -143,9 +143,8 @@
 
   // Modes where the parent of the selection is remembered.
   const std::vector<FileChooserParams::Mode> modes = {
-    FileChooserParams::Open,
-    FileChooserParams::OpenMultiple,
-    FileChooserParams::Save,
+      FileChooserParams::Mode::kOpen, FileChooserParams::Mode::kOpenMultiple,
+      FileChooserParams::Mode::kSave,
   };
 
   for (const auto& mode : modes) {
@@ -169,7 +168,7 @@
   }
 
   // Type where the selected folder itself is remembered.
-  file_select_helper->dialog_mode_ = FileChooserParams::UploadFolder;
+  file_select_helper->dialog_mode_ = FileChooserParams::Mode::kUploadFolder;
 
   file_select_helper->AddRef();  // Normally called by RunFileChooser().
   file_select_helper->FileSelected(dir_path_1, index, params);
diff --git a/chrome/browser/media/media_engagement_autoplay_browsertest.cc b/chrome/browser/media/media_engagement_autoplay_browsertest.cc
index bf499cb..1c6b823 100644
--- a/chrome/browser/media/media_engagement_autoplay_browsertest.cc
+++ b/chrome/browser/media/media_engagement_autoplay_browsertest.cc
@@ -293,8 +293,14 @@
   ExpectAutoplayAllowedIfEnabled();
 }
 
+#if defined(OS_LINUX)
+// UsePreloadedData_Allowed/1 is flaky on Linux https://crbug.com/883706
+#define MAYBE_UsePreloadedData_Allowed DISABLED_UsePreloadedData_Allowed
+#else
+#define MAYBE_UsePreloadedData_Allowed UsePreloadedData_Allowed
+#endif
 IN_PROC_BROWSER_TEST_P(MediaEngagementAutoplayBrowserTest,
-                       UsePreloadedData_Allowed) {
+                       MAYBE_UsePreloadedData_Allowed) {
   // Autoplay should be blocked by default if we have a bad score.
   SetScores(PrimaryOrigin(), 0, 0);
   LoadTestPage("engagement_autoplay_test.html");
diff --git a/chrome/browser/metrics/thread_watcher_unittest.cc b/chrome/browser/metrics/thread_watcher_unittest.cc
index 0b87e0f..9c25361 100644
--- a/chrome/browser/metrics/thread_watcher_unittest.cc
+++ b/chrome/browser/metrics/thread_watcher_unittest.cc
@@ -10,6 +10,7 @@
 #include <memory>
 
 #include "base/bind.h"
+#include "base/cancelable_callback.h"
 #include "base/location.h"
 #include "base/logging.h"
 #include "base/macros.h"
@@ -21,10 +22,10 @@
 #include "base/strings/string_tokenizer.h"
 #include "base/synchronization/condition_variable.h"
 #include "base/synchronization/lock.h"
-#include "base/synchronization/spin_wait.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/task/post_task.h"
 #include "base/threading/platform_thread.h"
+#include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "chrome/common/chrome_switches.h"
@@ -67,17 +68,19 @@
 // expected state.
 class CustomThreadWatcher : public ThreadWatcher {
  public:
-  base::Lock custom_lock_;
-  base::ConditionVariable state_changed_;
   State thread_watcher_state_;
+  // Wait state may be accessed from VeryLongMethod on another thread.
+  base::Lock wait_state_lock_;
+  base::ConditionVariable wait_state_changed_;
   WaitState wait_state_;
   CheckResponseState check_response_state_;
   uint64_t ping_sent_;
   uint64_t pong_received_;
-  base::subtle::Atomic32 success_response_;
-  base::subtle::Atomic32 failed_response_;
+  int32_t success_response_;
+  int32_t failed_response_;
   base::TimeTicks saved_ping_time_;
   uint64_t saved_ping_sequence_number_;
+  base::RepeatingClosure on_state_changed_;
 
   CustomThreadWatcher(const BrowserThread::ID thread_id,
                       const std::string thread_name,
@@ -89,8 +92,8 @@
                                      unresponsive_time,
                                      ThreadWatcherList::kUnresponsiveCount,
                                      true)),
-        state_changed_(&custom_lock_),
         thread_watcher_state_(INITIALIZED),
+        wait_state_changed_(&wait_state_lock_),
         wait_state_(UNINITIALIZED),
         check_response_state_(UNKNOWN),
         ping_sent_(0),
@@ -101,41 +104,39 @@
         saved_ping_sequence_number_(0) {}
 
   State UpdateState(State new_state) {
-    State old_state;
-    {
-      base::AutoLock auto_lock(custom_lock_);
-      old_state = thread_watcher_state_;
-      if (old_state != DEACTIVATED)
-        thread_watcher_state_ = new_state;
-      if (new_state == SENT_PING)
-        ++ping_sent_;
-      if (new_state == RECEIVED_PONG)
-        ++pong_received_;
-      saved_ping_time_ = ping_time();
-      saved_ping_sequence_number_ = ping_sequence_number();
-    }
-    state_changed_.Broadcast();
+    DCHECK(WatchDogThread::CurrentlyOnWatchDogThread());
+    State old_state = thread_watcher_state_;
+    if (old_state != DEACTIVATED)
+      thread_watcher_state_ = new_state;
+    if (new_state == SENT_PING)
+      ++ping_sent_;
+    if (new_state == RECEIVED_PONG)
+      ++pong_received_;
+    saved_ping_time_ = ping_time();
+    saved_ping_sequence_number_ = ping_sequence_number();
+    OnStateChanged();
     return old_state;
   }
 
-  WaitState UpdateWaitState(WaitState new_state) {
-    WaitState old_state;
+  void UpdateWaitState(WaitState new_state) {
+    DCHECK(WatchDogThread::CurrentlyOnWatchDogThread());
     {
-      base::AutoLock auto_lock(custom_lock_);
-      old_state = wait_state_;
+      base::AutoLock auto_lock(wait_state_lock_);
       wait_state_ = new_state;
     }
-    state_changed_.Broadcast();
-    return old_state;
+    wait_state_changed_.Broadcast();
+    OnStateChanged();
   }
 
   void ActivateThreadWatching() override {
+    DCHECK(WatchDogThread::CurrentlyOnWatchDogThread());
     State old_state = UpdateState(ACTIVATED);
     EXPECT_EQ(old_state, INITIALIZED);
     ThreadWatcher::ActivateThreadWatching();
   }
 
   void DeActivateThreadWatching() override {
+    DCHECK(WatchDogThread::CurrentlyOnWatchDogThread());
     State old_state = UpdateState(DEACTIVATED);
     EXPECT_TRUE(old_state == ACTIVATED || old_state == SENT_PING ||
                 old_state == RECEIVED_PONG);
@@ -143,107 +144,150 @@
   }
 
   void PostPingMessage() override {
+    DCHECK(WatchDogThread::CurrentlyOnWatchDogThread());
     State old_state = UpdateState(SENT_PING);
     EXPECT_TRUE(old_state == ACTIVATED || old_state == RECEIVED_PONG);
     ThreadWatcher::PostPingMessage();
   }
 
   void OnPongMessage(uint64_t ping_sequence_number) override {
+    DCHECK(WatchDogThread::CurrentlyOnWatchDogThread());
     State old_state = UpdateState(RECEIVED_PONG);
     EXPECT_TRUE(old_state == SENT_PING || old_state == DEACTIVATED);
     ThreadWatcher::OnPongMessage(ping_sequence_number);
   }
 
   void OnCheckResponsiveness(uint64_t ping_sequence_number) override {
+    DCHECK(WatchDogThread::CurrentlyOnWatchDogThread());
     ThreadWatcher::OnCheckResponsiveness(ping_sequence_number);
-    {
-      base::AutoLock auto_lock(custom_lock_);
-      if (responsive_) {
-        base::subtle::Release_Store(&success_response_,
-            base::subtle::Acquire_Load(&success_response_) + 1);
-        check_response_state_ = SUCCESSFUL;
-      } else {
-        base::subtle::Release_Store(&failed_response_,
-            base::subtle::Acquire_Load(&failed_response_) + 1);
-        check_response_state_ = FAILED;
-      }
+    if (responsive_) {
+      ++success_response_;
+      check_response_state_ = SUCCESSFUL;
+    } else {
+      ++failed_response_;
+      check_response_state_ = FAILED;
     }
-    // Broadcast to indicate we have checked responsiveness of the thread that
-    // is watched.
-    state_changed_.Broadcast();
+    OnStateChanged();
   }
 
   void WaitForWaitStateChange(TimeDelta wait_time, WaitState expected_state) {
+    DCHECK(WatchDogThread::CurrentlyOnWatchDogThread());
+    base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
+    base::RepeatingClosure quit_closure = run_loop.QuitClosure();
+    on_state_changed_ = base::BindRepeating(
+        [](CustomThreadWatcher* watcher, base::RepeatingClosure quit_closure,
+           WaitState expected_state) {
+          DCHECK(WatchDogThread::CurrentlyOnWatchDogThread());
+          // No need to acquire wait_state_lock_ since we're on the same thread
+          // that modifies wait_state_.
+          if (watcher->wait_state_ == expected_state)
+            quit_closure.Run();
+        },
+        base::Unretained(this), quit_closure, expected_state);
+    base::CancelableClosure timeout_closure(base::BindRepeating(
+        [](base::RepeatingClosure quit_closure) {
+          FAIL() << "WaitForWaitStateChange timed out";
+          quit_closure.Run();
+        },
+        quit_closure));
+    base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+        FROM_HERE, timeout_closure.callback(), wait_time);
+    run_loop.Run();
+    on_state_changed_.Reset();
+  }
+
+  // May be called on any thread other than the WatchDogThread.
+  void BusyWaitForWaitStateChange(TimeDelta wait_time,
+                                  WaitState expected_state) {
     DCHECK(!WatchDogThread::CurrentlyOnWatchDogThread());
     TimeTicks end_time = TimeTicks::Now() + wait_time;
     {
-      base::AutoLock auto_lock(custom_lock_);
+      base::AutoLock auto_lock(wait_state_lock_);
       while (wait_state_ != expected_state && TimeTicks::Now() < end_time)
-        state_changed_.TimedWait(end_time - TimeTicks::Now());
+        wait_state_changed_.TimedWait(end_time - TimeTicks::Now());
     }
   }
 
   void VeryLongMethod(TimeDelta wait_time) {
     DCHECK(!WatchDogThread::CurrentlyOnWatchDogThread());
-    WaitForWaitStateChange(wait_time, STOPPED_WAITING);
-    UpdateWaitState(ALL_DONE);
+    // ThreadWatcher tasks should not be allowed to execute while we're waiting,
+    // so hog the thread until the state changes.
+    BusyWaitForWaitStateChange(wait_time, STOPPED_WAITING);
+    WatchDogThread::PostTask(
+        FROM_HERE, base::BindRepeating(&CustomThreadWatcher::UpdateWaitState,
+                                       base::Unretained(this), ALL_DONE));
   }
 
   State WaitForStateChange(const TimeDelta& wait_time, State expected_state) {
-    DCHECK(!WatchDogThread::CurrentlyOnWatchDogThread());
+    DCHECK(WatchDogThread::CurrentlyOnWatchDogThread());
     UpdateWaitState(STARTED_WAITING);
 
+    // Keep the watch dog thread looping until the state changes to the
+    // expected_state or until wait_time elapses enough times for the
+    // unresponsive threshold to be reached.
+    base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
+    base::RepeatingClosure quit_closure = run_loop.QuitClosure();
     State exit_state = INITIALIZED;
-    // Keep the thread that is running the tests waiting until ThreadWatcher
-    // object's state changes to the expected_state or until wait_time elapses.
-    for (uint32_t i = 0; i < unresponsive_threshold_; ++i) {
-        TimeTicks end_time = TimeTicks::Now() + wait_time;
-        {
-          base::AutoLock auto_lock(custom_lock_);
-          while (thread_watcher_state_ != expected_state &&
-                 TimeTicks::Now() < end_time) {
-            TimeDelta state_change_wait_time = end_time - TimeTicks::Now();
-            state_changed_.TimedWait(state_change_wait_time);
-          }
-          // Capture the thread_watcher_state_ before it changes and return it
-          // to the caller.
-          exit_state = thread_watcher_state_;
-          if (exit_state == expected_state)
-            break;
-        }
-    }
+    on_state_changed_ = base::BindRepeating(
+        [](CustomThreadWatcher* watcher, base::RepeatingClosure quit_closure,
+           State expected_state, State* exit_state) {
+          *exit_state = watcher->thread_watcher_state_;
+          if (watcher->thread_watcher_state_ == expected_state)
+            quit_closure.Run();
+        },
+        base::Unretained(this), quit_closure, expected_state,
+        base::Unretained(&exit_state));
+    base::CancelableClosure timeout_closure(base::BindRepeating(
+        [](base::RepeatingClosure quit_closure) { quit_closure.Run(); },
+        quit_closure));
+    base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+        FROM_HERE, timeout_closure.callback(),
+        wait_time * unresponsive_threshold_);
+    run_loop.Run();
+    on_state_changed_.Reset();
+
     UpdateWaitState(STOPPED_WAITING);
     return exit_state;
   }
 
   CheckResponseState WaitForCheckResponse(const TimeDelta& wait_time,
                                           CheckResponseState expected_state) {
-    DCHECK(!WatchDogThread::CurrentlyOnWatchDogThread());
+    DCHECK(WatchDogThread::CurrentlyOnWatchDogThread());
     UpdateWaitState(STARTED_WAITING);
 
+    // Keep the watch dog thread looping until the state changes to the
+    // expected_state or until wait_time elapses enough times for the
+    // unresponsive threshold to be reached.
+    base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
+    base::RepeatingClosure quit_closure = run_loop.QuitClosure();
     CheckResponseState exit_state = UNKNOWN;
-    // Keep the thread that is running the tests waiting until ThreadWatcher
-    // object's check_response_state_ changes to the expected_state or until
-    // wait_time elapses.
-    for (uint32_t i = 0; i < unresponsive_threshold_; ++i) {
-        TimeTicks end_time = TimeTicks::Now() + wait_time;
-        {
-          base::AutoLock auto_lock(custom_lock_);
-          while (check_response_state_ != expected_state &&
-                 TimeTicks::Now() < end_time) {
-            TimeDelta state_change_wait_time = end_time - TimeTicks::Now();
-            state_changed_.TimedWait(state_change_wait_time);
-          }
-          // Capture the check_response_state_ before it changes and return it
-          // to the caller.
-          exit_state = check_response_state_;
-          if (exit_state == expected_state)
-            break;
-        }
-    }
+    on_state_changed_ = base::BindRepeating(
+        [](CustomThreadWatcher* watcher, base::RepeatingClosure quit_closure,
+           CheckResponseState expected_state, CheckResponseState* exit_state) {
+          *exit_state = watcher->check_response_state_;
+          if (watcher->check_response_state_ == expected_state)
+            quit_closure.Run();
+        },
+        base::Unretained(this), quit_closure, expected_state,
+        base::Unretained(&exit_state));
+    base::CancelableClosure timeout_closure(base::BindRepeating(
+        [](base::RepeatingClosure quit_closure) { quit_closure.Run(); },
+        quit_closure));
+    base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+        FROM_HERE, timeout_closure.callback(),
+        wait_time * unresponsive_threshold_);
+    run_loop.Run();
+    on_state_changed_.Reset();
+
     UpdateWaitState(STOPPED_WAITING);
     return exit_state;
   }
+
+  void OnStateChanged() {
+    DCHECK(WatchDogThread::CurrentlyOnWatchDogThread());
+    if (on_state_changed_)
+      on_state_changed_.Run();
+  }
 };
 
 class ThreadWatcherTest : public ::testing::Test {
@@ -261,18 +305,18 @@
   ThreadWatcherList* thread_watcher_list_;
 
   ThreadWatcherTest()
-      : setup_complete_(&lock_),
+      : thread_bundle_(content::TestBrowserThreadBundle::REAL_IO_THREAD),
+        setup_complete_(&lock_),
         initialized_(false) {
-    ui_thread_.reset(new content::TestBrowserThread(BrowserThread::UI));
-    io_thread_.reset(new content::TestBrowserThread(BrowserThread::IO));
+    // Make sure UI and IO threads are started and ready.
+    thread_bundle_.RunIOThreadUntilIdle();
+
     watchdog_thread_.reset(new WatchDogThread());
-    ui_thread_->StartAndWaitForTesting();
-    io_thread_->StartAndWaitForTesting();
     watchdog_thread_->StartAndWaitForTesting();
 
     WatchDogThread::PostTask(
-        FROM_HERE,
-        base::Bind(&ThreadWatcherTest::SetUpObjects, base::Unretained(this)));
+        FROM_HERE, base::BindRepeating(&ThreadWatcherTest::SetUpObjects,
+                                       base::Unretained(this)));
 
     WaitForSetUp(TimeDelta::FromMinutes(1));
   }
@@ -284,9 +328,8 @@
     thread_watcher_list_ = new ThreadWatcherList();
 
     // Create thread watcher object for the IO thread.
-    std::unique_ptr<CustomThreadWatcher> io_watcher(
-        new CustomThreadWatcher(BrowserThread::IO, kIOThreadName,
-                                kSleepTime, kUnresponsiveTime));
+    std::unique_ptr<CustomThreadWatcher> io_watcher(new CustomThreadWatcher(
+        BrowserThread::IO, kIOThreadName, kSleepTime, kUnresponsiveTime));
     io_watcher_ = io_watcher.get();
     ThreadWatcher* registered_io_watcher =
         ThreadWatcherList::Register(std::move(io_watcher));
@@ -323,24 +366,46 @@
     ThreadWatcherList::DeleteAll();
     io_watcher_ = nullptr;
     ui_watcher_ = nullptr;
-    io_thread_.reset();
-    ui_thread_.reset();
     watchdog_thread_.reset();
     thread_watcher_list_ = nullptr;
   }
 
  private:
-  base::MessageLoop message_loop_;
+  content::TestBrowserThreadBundle thread_bundle_;
   base::Lock lock_;
   base::ConditionVariable setup_complete_;
   bool initialized_;
-  std::unique_ptr<content::TestBrowserThread> ui_thread_;
-  std::unique_ptr<content::TestBrowserThread> io_thread_;
   std::unique_ptr<WatchDogThread> watchdog_thread_;
 
   DISALLOW_COPY_AND_ASSIGN(ThreadWatcherTest);
 };
 
+// Test fixture that runs a test body on the WatchDogThread. Subclasses override
+// TestBodyOnWatchDogThread() and should call RunTestOnWatchDogThread() in their
+// TEST_F() declaration.
+class ThreadWatcherTestOnWatchDogThread : public ThreadWatcherTest {
+ public:
+  ThreadWatcherTestOnWatchDogThread()
+      : test_body_run_loop_(base::RunLoop::Type::kNestableTasksAllowed) {}
+
+ protected:
+  void RunTestOnWatchDogThread() {
+    WatchDogThread::PostTask(FROM_HERE,
+                             base::BindRepeating(
+                                 [](ThreadWatcherTestOnWatchDogThread* test) {
+                                   test->TestBodyOnWatchDogThread();
+                                   test->test_body_run_loop_.Quit();
+                                 },
+                                 base::Unretained(this)));
+    test_body_run_loop_.Run();
+  }
+
+  virtual void TestBodyOnWatchDogThread() = 0;
+
+ protected:
+  base::RunLoop test_body_run_loop_;
+};
+
 // Declare storage for ThreadWatcherTest's static constants.
 constexpr TimeDelta ThreadWatcherTest::kSleepTime;
 constexpr TimeDelta ThreadWatcherTest::kUnresponsiveTime;
@@ -416,20 +481,27 @@
 
 // Test registration. When thread_watcher_list_ goes out of scope after
 // TearDown, all thread watcher objects will be deleted.
-TEST_F(ThreadWatcherTest, Registration) {
-  // Check ThreadWatcher object has all correct parameters.
-  EXPECT_EQ(BrowserThread::IO, io_watcher_->thread_id());
-  EXPECT_EQ(kIOThreadName, io_watcher_->thread_name());
-  EXPECT_EQ(kSleepTime, io_watcher_->sleep_time());
-  EXPECT_EQ(kUnresponsiveTime, io_watcher_->unresponsive_time());
-  EXPECT_FALSE(io_watcher_->active());
+class ThreadWatcherTestRegistration : public ThreadWatcherTestOnWatchDogThread {
+ protected:
+  void TestBodyOnWatchDogThread() override {
+    // Check ThreadWatcher object has all correct parameters.
+    EXPECT_EQ(BrowserThread::IO, io_watcher_->thread_id());
+    EXPECT_EQ(kIOThreadName, io_watcher_->thread_name());
+    EXPECT_EQ(kSleepTime, io_watcher_->sleep_time());
+    EXPECT_EQ(kUnresponsiveTime, io_watcher_->unresponsive_time());
+    EXPECT_FALSE(io_watcher_->active());
 
-  // Check ThreadWatcher object of watched UI thread has correct data.
-  EXPECT_EQ(BrowserThread::UI, ui_watcher_->thread_id());
-  EXPECT_EQ(kUIThreadName, ui_watcher_->thread_name());
-  EXPECT_EQ(kSleepTime, ui_watcher_->sleep_time());
-  EXPECT_EQ(kUnresponsiveTime, ui_watcher_->unresponsive_time());
-  EXPECT_FALSE(ui_watcher_->active());
+    // Check ThreadWatcher object of watched UI thread has correct data.
+    EXPECT_EQ(BrowserThread::UI, ui_watcher_->thread_id());
+    EXPECT_EQ(kUIThreadName, ui_watcher_->thread_name());
+    EXPECT_EQ(kSleepTime, ui_watcher_->sleep_time());
+    EXPECT_EQ(kUnresponsiveTime, ui_watcher_->unresponsive_time());
+    EXPECT_FALSE(ui_watcher_->active());
+  }
+};
+
+TEST_F(ThreadWatcherTestRegistration, RunTest) {
+  RunTestOnWatchDogThread();
 }
 
 // Test ActivateThreadWatching and DeActivateThreadWatching of IO thread. This
@@ -437,172 +509,160 @@
 // message was received by the WatchDogThread. It also checks that
 // OnCheckResponsiveness has verified the ping-pong mechanism and the watched
 // thread is not hung.
-TEST_F(ThreadWatcherTest, ThreadResponding) {
-  TimeTicks time_before_ping = TimeTicks::Now();
-  // Activate watching IO thread.
-  WatchDogThread::PostTask(
-      FROM_HERE,
-      base::Bind(&ThreadWatcher::ActivateThreadWatching,
-                 base::Unretained(io_watcher_)));
+class ThreadWatcherTestThreadResponding
+    : public ThreadWatcherTestOnWatchDogThread {
+ protected:
+  void TestBodyOnWatchDogThread() override {
+    TimeTicks time_before_ping = TimeTicks::Now();
+    // Activate watching IO thread.
+    io_watcher_->ActivateThreadWatching();
 
-  // Activate would have started ping/pong messaging. Expect atleast one
-  // ping/pong messaging sequence to happen.
-  io_watcher_->WaitForStateChange(kSleepTime + TimeDelta::FromMinutes(1),
-                                  RECEIVED_PONG);
-  EXPECT_GT(io_watcher_->ping_sent_, static_cast<uint64_t>(0));
-  EXPECT_GT(io_watcher_->pong_received_, static_cast<uint64_t>(0));
-  EXPECT_TRUE(io_watcher_->active());
-  EXPECT_GE(io_watcher_->saved_ping_time_, time_before_ping);
-  EXPECT_GE(io_watcher_->saved_ping_sequence_number_, static_cast<uint64_t>(0));
+    // Activate would have started ping/pong messaging. Expect at least one
+    // ping/pong messaging sequence to happen.
+    io_watcher_->WaitForStateChange(kSleepTime + TimeDelta::FromMinutes(1),
+                                    RECEIVED_PONG);
+    EXPECT_GT(io_watcher_->ping_sent_, static_cast<uint64_t>(0));
+    EXPECT_GT(io_watcher_->pong_received_, static_cast<uint64_t>(0));
+    EXPECT_TRUE(io_watcher_->active());
+    EXPECT_GE(io_watcher_->saved_ping_time_, time_before_ping);
+    EXPECT_GE(io_watcher_->saved_ping_sequence_number_,
+              static_cast<uint64_t>(0));
 
-  // Verify watched thread is responding with ping/pong messaging.
-  io_watcher_->WaitForCheckResponse(
-      kUnresponsiveTime + TimeDelta::FromMinutes(1), SUCCESSFUL);
-  EXPECT_GT(base::subtle::NoBarrier_Load(&(io_watcher_->success_response_)),
-      static_cast<base::subtle::Atomic32>(0));
-  EXPECT_EQ(base::subtle::NoBarrier_Load(&(io_watcher_->failed_response_)),
-      static_cast<base::subtle::Atomic32>(0));
+    // Verify watched thread is responding with ping/pong messaging.
+    io_watcher_->WaitForCheckResponse(
+        kUnresponsiveTime + TimeDelta::FromMinutes(1), SUCCESSFUL);
+    EXPECT_GT(io_watcher_->success_response_, 0);
+    EXPECT_EQ(io_watcher_->failed_response_, 0);
 
-  // DeActivate thread watching for shutdown.
-  WatchDogThread::PostTask(
-      FROM_HERE,
-      base::Bind(&ThreadWatcher::DeActivateThreadWatching,
-      base::Unretained(io_watcher_)));
+    // DeActivate thread watching for shutdown.
+    io_watcher_->DeActivateThreadWatching();
+  }
+};
+
+TEST_F(ThreadWatcherTestThreadResponding, RunTest) {
+  RunTestOnWatchDogThread();
 }
 
 // This test posts a task on watched thread that takes very long time (this is
 // to simulate hanging of watched thread). It then checks for
 // OnCheckResponsiveness raising an alert (OnCheckResponsiveness returns false
 // if the watched thread is not responding).
-TEST_F(ThreadWatcherTest, ThreadNotResponding) {
-  // Simulate hanging of watched thread by making the watched thread wait for a
-  // very long time by posting a task on watched thread that keeps it busy.
-  // It is safe to use base::Unretained because test is waiting for the method
-  // to finish.
-  BrowserThread::PostTask(
-      BrowserThread::IO, FROM_HERE,
-      base::BindOnce(&CustomThreadWatcher::VeryLongMethod,
-                     base::Unretained(io_watcher_), kUnresponsiveTime * 10));
+class ThreadWatcherTestThreadNotResponding
+    : public ThreadWatcherTestOnWatchDogThread {
+ protected:
+  void TestBodyOnWatchDogThread() override {
+    // Simulate hanging of watched thread by making the watched thread wait for
+    // a very long time by posting a task on watched thread that keeps it busy.
+    // It is safe to use base::Unretained because test is waiting for the method
+    // to finish.
+    base::PostTaskWithTraits(
+        FROM_HERE, {BrowserThread::IO},
+        base::BindOnce(&CustomThreadWatcher::VeryLongMethod,
+                       base::Unretained(io_watcher_), kUnresponsiveTime * 10));
 
-  // Activate thread watching.
-  WatchDogThread::PostTask(
-      FROM_HERE,
-      base::Bind(&ThreadWatcher::ActivateThreadWatching,
-                 base::Unretained(io_watcher_)));
+    // Activate thread watching.
+    io_watcher_->ActivateThreadWatching();
 
-  // Verify watched thread is not responding for ping messages.
-  io_watcher_->WaitForCheckResponse(
-      kUnresponsiveTime + TimeDelta::FromMinutes(1), FAILED);
-  EXPECT_EQ(base::subtle::NoBarrier_Load(&(io_watcher_->success_response_)),
-      static_cast<base::subtle::Atomic32>(0));
-  EXPECT_GT(base::subtle::NoBarrier_Load(&(io_watcher_->failed_response_)),
-      static_cast<base::subtle::Atomic32>(0));
+    // Verify watched thread is not responding for ping messages.
+    io_watcher_->WaitForCheckResponse(
+        kUnresponsiveTime + TimeDelta::FromMinutes(1), FAILED);
+    EXPECT_EQ(io_watcher_->success_response_, 0);
+    EXPECT_GT(io_watcher_->failed_response_, 0);
 
-  // DeActivate thread watching for shutdown.
-  WatchDogThread::PostTask(
-      FROM_HERE,
-      base::Bind(&ThreadWatcher::DeActivateThreadWatching,
-                 base::Unretained(io_watcher_)));
+    // DeActivate thread watching for shutdown.
+    io_watcher_->DeActivateThreadWatching();
 
-  // Wait for the io_watcher_'s VeryLongMethod to finish.
-  io_watcher_->WaitForWaitStateChange(kUnresponsiveTime * 10, ALL_DONE);
+    // Wait for the io_watcher_'s VeryLongMethod to finish.
+    io_watcher_->WaitForWaitStateChange(kUnresponsiveTime * 10, ALL_DONE);
+  }
+};
+
+TEST_F(ThreadWatcherTestThreadNotResponding, RunTest) {
+  RunTestOnWatchDogThread();
 }
 
 // Test watching of multiple threads with all threads not responding.
-TEST_F(ThreadWatcherTest, MultipleThreadsResponding) {
-  // Check for UI thread to perform ping/pong messaging.
-  WatchDogThread::PostTask(FROM_HERE,
-                           base::Bind(&ThreadWatcher::ActivateThreadWatching,
-                                      base::Unretained(ui_watcher_)));
+class ThreadWatcherTestMultipleThreadsResponding
+    : public ThreadWatcherTestOnWatchDogThread {
+ protected:
+  void TestBodyOnWatchDogThread() override {
+    // Check for UI thread to perform ping/pong messaging.
+    ui_watcher_->ActivateThreadWatching();
 
-  // Check for IO thread to perform ping/pong messaging.
-  WatchDogThread::PostTask(
-      FROM_HERE,
-      base::Bind(&ThreadWatcher::ActivateThreadWatching,
-                 base::Unretained(io_watcher_)));
+    // Check for IO thread to perform ping/pong messaging.
+    io_watcher_->ActivateThreadWatching();
 
-  // Verify UI thread is responding with ping/pong messaging.
-  ui_watcher_->WaitForCheckResponse(
-      kUnresponsiveTime + TimeDelta::FromMinutes(1), SUCCESSFUL);
-  EXPECT_GT(ui_watcher_->ping_sent_, static_cast<uint64_t>(0));
-  EXPECT_GT(ui_watcher_->pong_received_, static_cast<uint64_t>(0));
-  EXPECT_GE(ui_watcher_->ping_sequence_number_, static_cast<uint64_t>(0));
-  EXPECT_GT(base::subtle::NoBarrier_Load(&(ui_watcher_->success_response_)),
-            static_cast<base::subtle::Atomic32>(0));
-  EXPECT_EQ(base::subtle::NoBarrier_Load(&(ui_watcher_->failed_response_)),
-            static_cast<base::subtle::Atomic32>(0));
+    // Verify UI thread is responding with ping/pong messaging.
+    ui_watcher_->WaitForCheckResponse(
+        kUnresponsiveTime + TimeDelta::FromMinutes(1), SUCCESSFUL);
+    EXPECT_GT(ui_watcher_->ping_sent_, static_cast<uint64_t>(0));
+    EXPECT_GT(ui_watcher_->pong_received_, static_cast<uint64_t>(0));
+    EXPECT_GE(ui_watcher_->ping_sequence_number(), static_cast<uint64_t>(0));
+    EXPECT_GT(ui_watcher_->success_response_, 0);
+    EXPECT_EQ(ui_watcher_->failed_response_, 0);
 
-  // Verify IO thread is responding with ping/pong messaging.
-  io_watcher_->WaitForCheckResponse(
-      kUnresponsiveTime + TimeDelta::FromMinutes(1), SUCCESSFUL);
-  EXPECT_GT(io_watcher_->ping_sent_, static_cast<uint64_t>(0));
-  EXPECT_GT(io_watcher_->pong_received_, static_cast<uint64_t>(0));
-  EXPECT_GE(io_watcher_->ping_sequence_number_, static_cast<uint64_t>(0));
-  EXPECT_GT(base::subtle::NoBarrier_Load(&(io_watcher_->success_response_)),
-      static_cast<base::subtle::Atomic32>(0));
-  EXPECT_EQ(base::subtle::NoBarrier_Load(&(io_watcher_->failed_response_)),
-      static_cast<base::subtle::Atomic32>(0));
+    // Verify IO thread is responding with ping/pong messaging.
+    io_watcher_->WaitForCheckResponse(
+        kUnresponsiveTime + TimeDelta::FromMinutes(1), SUCCESSFUL);
+    EXPECT_GT(io_watcher_->ping_sent_, static_cast<uint64_t>(0));
+    EXPECT_GT(io_watcher_->pong_received_, static_cast<uint64_t>(0));
+    EXPECT_GE(io_watcher_->ping_sequence_number(), static_cast<uint64_t>(0));
+    EXPECT_GT(io_watcher_->success_response_, 0);
+    EXPECT_EQ(io_watcher_->failed_response_, 0);
 
-  // DeActivate thread watching for shutdown.
-  WatchDogThread::PostTask(
-      FROM_HERE,
-      base::Bind(&ThreadWatcher::DeActivateThreadWatching,
-                 base::Unretained(io_watcher_)));
+    // DeActivate thread watching for shutdown.
+    io_watcher_->DeActivateThreadWatching();
+    ui_watcher_->DeActivateThreadWatching();
+  }
+};
 
-  WatchDogThread::PostTask(FROM_HERE,
-                           base::Bind(&ThreadWatcher::DeActivateThreadWatching,
-                                      base::Unretained(ui_watcher_)));
+TEST_F(ThreadWatcherTestMultipleThreadsResponding, RunTest) {
+  RunTestOnWatchDogThread();
 }
 
 // Test watching of multiple threads with one of the threads not responding.
-TEST_F(ThreadWatcherTest, MultipleThreadsNotResponding) {
-  // Simulate hanging of watched thread by making the watched thread wait for a
-  // very long time by posting a task on watched thread that keeps it busy.
-  // It is safe ot use base::Unretained because test is waiting for the method
-  // to finish.
-  BrowserThread::PostTask(
-      BrowserThread::IO, FROM_HERE,
-      base::BindOnce(&CustomThreadWatcher::VeryLongMethod,
-                     base::Unretained(io_watcher_), kUnresponsiveTime * 10));
+class ThreadWatcherTestMultipleThreadsNotResponding
+    : public ThreadWatcherTestOnWatchDogThread {
+ protected:
+  void TestBodyOnWatchDogThread() override {
+    // Simulate hanging of watched thread by making the watched thread wait for
+    // a very long time by posting a task on watched thread that keeps it busy.
+    // It is safe to use base::Unretained because test is waiting for the method
+    // to finish.
+    base::PostTaskWithTraits(
+        FROM_HERE, {BrowserThread::IO},
+        base::BindOnce(&CustomThreadWatcher::VeryLongMethod,
+                       base::Unretained(io_watcher_), kUnresponsiveTime * 10));
 
-  // Activate watching of UI thread.
-  WatchDogThread::PostTask(FROM_HERE,
-                           base::Bind(&ThreadWatcher::ActivateThreadWatching,
-                                      base::Unretained(ui_watcher_)));
+    // Activate watching of UI thread.
+    ui_watcher_->ActivateThreadWatching();
 
-  // Activate watching of IO thread.
-  WatchDogThread::PostTask(
-      FROM_HERE,
-      base::Bind(&ThreadWatcher::ActivateThreadWatching,
-                 base::Unretained(io_watcher_)));
+    // Activate watching of IO thread.
+    io_watcher_->ActivateThreadWatching();
 
-  // Verify UI thread is responding with ping/pong messaging.
-  ui_watcher_->WaitForCheckResponse(
-      kUnresponsiveTime + TimeDelta::FromMinutes(1), SUCCESSFUL);
-  EXPECT_GT(base::subtle::NoBarrier_Load(&(ui_watcher_->success_response_)),
-            static_cast<base::subtle::Atomic32>(0));
-  EXPECT_EQ(base::subtle::NoBarrier_Load(&(ui_watcher_->failed_response_)),
-            static_cast<base::subtle::Atomic32>(0));
+    // Verify UI thread is responding with ping/pong messaging.
+    ui_watcher_->WaitForCheckResponse(
+        kUnresponsiveTime + TimeDelta::FromMinutes(1), SUCCESSFUL);
+    EXPECT_GT(ui_watcher_->success_response_, 0);
+    EXPECT_EQ(ui_watcher_->failed_response_, 0);
 
-  // Verify IO thread is not responding for ping messages.
-  io_watcher_->WaitForCheckResponse(
-      kUnresponsiveTime + TimeDelta::FromMinutes(1), FAILED);
-  EXPECT_EQ(base::subtle::NoBarrier_Load(&(io_watcher_->success_response_)),
-      static_cast<base::subtle::Atomic32>(0));
-  EXPECT_GT(base::subtle::NoBarrier_Load(&(io_watcher_->failed_response_)),
-      static_cast<base::subtle::Atomic32>(0));
+    // Verify IO thread is not responding for ping messages.
+    io_watcher_->WaitForCheckResponse(
+        kUnresponsiveTime + TimeDelta::FromMinutes(1), FAILED);
+    EXPECT_EQ(io_watcher_->success_response_, 0);
+    EXPECT_GT(io_watcher_->failed_response_, 0);
 
-  // DeActivate thread watching for shutdown.
-  WatchDogThread::PostTask(
-      FROM_HERE,
-      base::Bind(&ThreadWatcher::DeActivateThreadWatching,
-                 base::Unretained(io_watcher_)));
-  WatchDogThread::PostTask(FROM_HERE,
-                           base::Bind(&ThreadWatcher::DeActivateThreadWatching,
-                                      base::Unretained(ui_watcher_)));
+    // DeActivate thread watching for shutdown.
+    io_watcher_->DeActivateThreadWatching();
+    ui_watcher_->DeActivateThreadWatching();
 
-  // Wait for the io_watcher_'s VeryLongMethod to finish.
-  io_watcher_->WaitForWaitStateChange(kUnresponsiveTime * 10, ALL_DONE);
+    // Wait for the io_watcher_'s VeryLongMethod to finish.
+    io_watcher_->WaitForWaitStateChange(kUnresponsiveTime * 10, ALL_DONE);
+  }
+};
+
+TEST_F(ThreadWatcherTestMultipleThreadsNotResponding, RunTest) {
+  RunTestOnWatchDogThread();
 }
 
 class ThreadWatcherListTest : public ::testing::Test {
diff --git a/chrome/browser/page_load_metrics/observers/signed_exchange_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/signed_exchange_page_load_metrics_observer.cc
new file mode 100644
index 0000000..76efa5a62
--- /dev/null
+++ b/chrome/browser/page_load_metrics/observers/signed_exchange_page_load_metrics_observer.cc
@@ -0,0 +1,148 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/page_load_metrics/observers/signed_exchange_page_load_metrics_observer.h"
+
+#include "chrome/browser/page_load_metrics/page_load_metrics_util.h"
+#include "third_party/blink/public/platform/web_loading_behavior_flag.h"
+
+namespace internal {
+
+#define HISTOGRAM_PREFIX "PageLoad.Clients.SignedExchange."
+
+constexpr char kHistogramSignedExchangePrefix[] = HISTOGRAM_PREFIX;
+constexpr char kHistogramSignedExchangeParseStart[] =
+    HISTOGRAM_PREFIX "ParseTiming.NavigationToParseStart";
+constexpr char kHistogramSignedExchangeFirstInputDelay[] =
+    HISTOGRAM_PREFIX "InteractiveTiming.FirstInputDelay";
+constexpr char kHistogramSignedExchangeFirstPaint[] =
+    HISTOGRAM_PREFIX "PaintTiming.NavigationToFirstPaint";
+constexpr char kHistogramSignedExchangeFirstContentfulPaint[] =
+    HISTOGRAM_PREFIX "PaintTiming.NavigationToFirstContentfulPaint";
+constexpr char kHistogramSignedExchangeParseStartToFirstContentfulPaint[] =
+    HISTOGRAM_PREFIX "PaintTiming.ParseStartToFirstContentfulPaint";
+constexpr char kHistogramSignedExchangeFirstMeaningfulPaint[] = HISTOGRAM_PREFIX
+    "Experimental.PaintTiming.NavigationToFirstMeaningfulPaint";
+constexpr char kHistogramSignedExchangeParseStartToFirstMeaningfulPaint[] =
+    HISTOGRAM_PREFIX
+    "Experimental.PaintTiming.ParseStartToFirstMeaningfulPaint";
+constexpr char kHistogramSignedExchangeDomContentLoaded[] =
+    HISTOGRAM_PREFIX "DocumentTiming.NavigationToDOMContentLoadedEventFired";
+constexpr char kHistogramSignedExchangeLoad[] =
+    HISTOGRAM_PREFIX "DocumentTiming.NavigationToLoadEventFired";
+
+#undef HISTOGRAM_PREFIX
+
+}  // namespace internal
+
+SignedExchangePageLoadMetricsObserver::SignedExchangePageLoadMetricsObserver() {
+}
+
+page_load_metrics::PageLoadMetricsObserver::ObservePolicy
+SignedExchangePageLoadMetricsObserver::OnCommit(
+    content::NavigationHandle* navigation_handle,
+    ukm::SourceId source_id) {
+  if (navigation_handle->IsSignedExchangeInnerResponse())
+    return CONTINUE_OBSERVING;
+
+  return STOP_OBSERVING;
+}
+
+void SignedExchangePageLoadMetricsObserver::OnFirstPaintInPage(
+    const page_load_metrics::mojom::PageLoadTiming& timing,
+    const page_load_metrics::PageLoadExtraInfo& info) {
+  if (!WasStartedInForegroundOptionalEventInForeground(
+          timing.paint_timing->first_paint, info)) {
+    return;
+  }
+
+  PAGE_LOAD_HISTOGRAM(internal::kHistogramSignedExchangeFirstPaint,
+                      timing.paint_timing->first_paint.value());
+}
+
+void SignedExchangePageLoadMetricsObserver::OnFirstContentfulPaintInPage(
+    const page_load_metrics::mojom::PageLoadTiming& timing,
+    const page_load_metrics::PageLoadExtraInfo& info) {
+  if (!WasStartedInForegroundOptionalEventInForeground(
+          timing.paint_timing->first_contentful_paint, info)) {
+    return;
+  }
+
+  PAGE_LOAD_HISTOGRAM(internal::kHistogramSignedExchangeFirstContentfulPaint,
+                      timing.paint_timing->first_contentful_paint.value());
+  PAGE_LOAD_HISTOGRAM(
+      internal::kHistogramSignedExchangeParseStartToFirstContentfulPaint,
+      timing.paint_timing->first_contentful_paint.value() -
+          timing.parse_timing->parse_start.value());
+}
+
+void SignedExchangePageLoadMetricsObserver::
+    OnFirstMeaningfulPaintInMainFrameDocument(
+        const page_load_metrics::mojom::PageLoadTiming& timing,
+        const page_load_metrics::PageLoadExtraInfo& info) {
+  if (!WasStartedInForegroundOptionalEventInForeground(
+          timing.paint_timing->first_meaningful_paint, info)) {
+    return;
+  }
+
+  PAGE_LOAD_HISTOGRAM(internal::kHistogramSignedExchangeFirstMeaningfulPaint,
+                      timing.paint_timing->first_meaningful_paint.value());
+  PAGE_LOAD_HISTOGRAM(
+      internal::kHistogramSignedExchangeParseStartToFirstMeaningfulPaint,
+      timing.paint_timing->first_meaningful_paint.value() -
+          timing.parse_timing->parse_start.value());
+}
+
+void SignedExchangePageLoadMetricsObserver::OnDomContentLoadedEventStart(
+    const page_load_metrics::mojom::PageLoadTiming& timing,
+    const page_load_metrics::PageLoadExtraInfo& info) {
+  if (!WasStartedInForegroundOptionalEventInForeground(
+          timing.document_timing->dom_content_loaded_event_start, info)) {
+    return;
+  }
+
+  PAGE_LOAD_HISTOGRAM(
+      internal::kHistogramSignedExchangeDomContentLoaded,
+      timing.document_timing->dom_content_loaded_event_start.value());
+}
+
+void SignedExchangePageLoadMetricsObserver::OnLoadEventStart(
+    const page_load_metrics::mojom::PageLoadTiming& timing,
+    const page_load_metrics::PageLoadExtraInfo& info) {
+  if (!WasStartedInForegroundOptionalEventInForeground(
+          timing.document_timing->load_event_start, info)) {
+    return;
+  }
+
+  PAGE_LOAD_HISTOGRAM(internal::kHistogramSignedExchangeLoad,
+                      timing.document_timing->load_event_start.value());
+}
+
+void SignedExchangePageLoadMetricsObserver::OnFirstInputInPage(
+    const page_load_metrics::mojom::PageLoadTiming& timing,
+    const page_load_metrics::PageLoadExtraInfo& info) {
+  if (!WasStartedInForegroundOptionalEventInForeground(
+          timing.interactive_timing->first_input_timestamp, info)) {
+    return;
+  }
+
+  // Copied from the CorePageLoadMetricsObserver implementation.
+  UMA_HISTOGRAM_CUSTOM_TIMES(
+      internal::kHistogramSignedExchangeFirstInputDelay,
+      timing.interactive_timing->first_input_delay.value(),
+      base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromSeconds(60),
+      50);
+}
+
+void SignedExchangePageLoadMetricsObserver::OnParseStart(
+    const page_load_metrics::mojom::PageLoadTiming& timing,
+    const page_load_metrics::PageLoadExtraInfo& info) {
+  if (!WasStartedInForegroundOptionalEventInForeground(
+          timing.parse_timing->parse_start, info)) {
+    return;
+  }
+
+  PAGE_LOAD_HISTOGRAM(internal::kHistogramSignedExchangeParseStart,
+                      timing.parse_timing->parse_start.value());
+}
diff --git a/chrome/browser/page_load_metrics/observers/signed_exchange_page_load_metrics_observer.h b/chrome/browser/page_load_metrics/observers/signed_exchange_page_load_metrics_observer.h
new file mode 100644
index 0000000..4bb3af2
--- /dev/null
+++ b/chrome/browser/page_load_metrics/observers/signed_exchange_page_load_metrics_observer.h
@@ -0,0 +1,60 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_PAGE_LOAD_METRICS_OBSERVERS_SIGNED_EXCHANGE_PAGE_LOAD_METRICS_OBSERVER_H_
+#define CHROME_BROWSER_PAGE_LOAD_METRICS_OBSERVERS_SIGNED_EXCHANGE_PAGE_LOAD_METRICS_OBSERVER_H_
+
+#include "base/macros.h"
+#include "chrome/browser/page_load_metrics/page_load_metrics_observer.h"
+
+namespace internal {
+
+// Expose metrics for tests.
+extern const char kHistogramSignedExchangePrefix[];
+extern const char kHistogramSignedExchangeParseStart[];
+extern const char kHistogramSignedExchangeFirstInputDelay[];
+extern const char kHistogramSignedExchangeFirstPaint[];
+extern const char kHistogramSignedExchangeFirstContentfulPaint[];
+extern const char kHistogramSignedExchangeParseStartToFirstContentfulPaint[];
+extern const char kHistogramSignedExchangeFirstMeaningfulPaint[];
+extern const char kHistogramSignedExchangeParseStartToFirstMeaningfulPaint[];
+extern const char kHistogramSignedExchangeDomContentLoaded[];
+extern const char kHistogramSignedExchangeLoad[];
+
+}  // namespace internal
+
+class SignedExchangePageLoadMetricsObserver
+    : public page_load_metrics::PageLoadMetricsObserver {
+ public:
+  SignedExchangePageLoadMetricsObserver();
+  // page_load_metrics::PageLoadMetricsObserver implementation:
+  ObservePolicy OnCommit(content::NavigationHandle* navigation_handle,
+                         ukm::SourceId source_id) override;
+  void OnFirstInputInPage(
+      const page_load_metrics::mojom::PageLoadTiming& timing,
+      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+  void OnParseStart(
+      const page_load_metrics::mojom::PageLoadTiming& timing,
+      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+  void OnFirstPaintInPage(
+      const page_load_metrics::mojom::PageLoadTiming& timing,
+      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+  void OnFirstContentfulPaintInPage(
+      const page_load_metrics::mojom::PageLoadTiming& timing,
+      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+  void OnFirstMeaningfulPaintInMainFrameDocument(
+      const page_load_metrics::mojom::PageLoadTiming& timing,
+      const page_load_metrics::PageLoadExtraInfo& info) override;
+  void OnDomContentLoadedEventStart(
+      const page_load_metrics::mojom::PageLoadTiming& timing,
+      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+  void OnLoadEventStart(
+      const page_load_metrics::mojom::PageLoadTiming& timing,
+      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(SignedExchangePageLoadMetricsObserver);
+};
+
+#endif  // CHROME_BROWSER_PAGE_LOAD_METRICS_OBSERVERS_SIGNED_EXCHANGE_PAGE_LOAD_METRICS_OBSERVER_H_
diff --git a/chrome/browser/page_load_metrics/observers/signed_exchange_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/signed_exchange_page_load_metrics_observer_unittest.cc
new file mode 100644
index 0000000..f4a074f
--- /dev/null
+++ b/chrome/browser/page_load_metrics/observers/signed_exchange_page_load_metrics_observer_unittest.cc
@@ -0,0 +1,157 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/page_load_metrics/observers/signed_exchange_page_load_metrics_observer.h"
+
+#include <memory>
+
+#include "chrome/browser/page_load_metrics/observers/page_load_metrics_observer_test_harness.h"
+#include "chrome/browser/page_load_metrics/page_load_tracker.h"
+#include "chrome/common/page_load_metrics/test/page_load_metrics_test_util.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/navigation_simulator.h"
+
+namespace {
+
+const char kDefaultTestUrl[] = "https://example.com/";
+
+}  // namespace
+
+class SignedExchangePageLoadMetricsObserverTest
+    : public page_load_metrics::PageLoadMetricsObserverTestHarness {
+ protected:
+  void RegisterObservers(page_load_metrics::PageLoadTracker* tracker) override {
+    tracker->AddObserver(
+        std::make_unique<SignedExchangePageLoadMetricsObserver>());
+  }
+
+  void NavigateAndCommitSignedExchange(const GURL& url) {
+    std::unique_ptr<content::NavigationSimulator> navigation =
+        content::NavigationSimulator::CreateBrowserInitiated(url,
+                                                             web_contents());
+    navigation->Start();
+    navigation->SetIsSignedExchangeInnerResponse(true);
+    navigation->Commit();
+  }
+
+  void AssertNoSignedExchangeHistogramsLogged() {
+    base::HistogramTester::CountsMap counts_map =
+        histogram_tester().GetTotalCountsForPrefix(
+            internal::kHistogramSignedExchangePrefix);
+    for (const auto& it : counts_map) {
+      base::HistogramBase::Count count = it.second;
+      EXPECT_EQ(0, count) << "Histogram \"" << it.first
+                          << "\" should be empty.";
+    }
+  }
+
+  void InitializeTestPageLoadTiming(
+      page_load_metrics::mojom::PageLoadTiming* timing) {
+    page_load_metrics::InitPageLoadTimingForTest(timing);
+    timing->navigation_start = base::Time::FromDoubleT(1);
+    timing->interactive_timing->first_input_delay =
+        base::TimeDelta::FromMilliseconds(50);
+    timing->interactive_timing->first_input_timestamp =
+        base::TimeDelta::FromMilliseconds(712);
+    timing->parse_timing->parse_start = base::TimeDelta::FromMilliseconds(100);
+    timing->paint_timing->first_paint = base::TimeDelta::FromMilliseconds(200);
+    timing->paint_timing->first_contentful_paint =
+        base::TimeDelta::FromMilliseconds(300);
+    timing->paint_timing->first_meaningful_paint =
+        base::TimeDelta::FromMilliseconds(700);
+    timing->document_timing->dom_content_loaded_event_start =
+        base::TimeDelta::FromMilliseconds(600);
+    timing->document_timing->load_event_start =
+        base::TimeDelta::FromMilliseconds(1000);
+    PopulateRequiredTimingFields(timing);
+  }
+};
+
+TEST_F(SignedExchangePageLoadMetricsObserverTest, NoMetrics) {
+  AssertNoSignedExchangeHistogramsLogged();
+}
+
+TEST_F(SignedExchangePageLoadMetricsObserverTest, NoSignedExchange) {
+  page_load_metrics::mojom::PageLoadTiming timing;
+  InitializeTestPageLoadTiming(&timing);
+
+  NavigateAndCommit(GURL(kDefaultTestUrl));
+  SimulateTimingUpdate(timing);
+
+  AssertNoSignedExchangeHistogramsLogged();
+}
+
+TEST_F(SignedExchangePageLoadMetricsObserverTest, WithSignedExchange) {
+  page_load_metrics::mojom::PageLoadTiming timing;
+  InitializeTestPageLoadTiming(&timing);
+
+  NavigateAndCommitSignedExchange(GURL(kDefaultTestUrl));
+  SimulateTimingUpdate(timing);
+
+  histogram_tester().ExpectTotalCount(
+      internal::kHistogramSignedExchangeFirstInputDelay, 1);
+  histogram_tester().ExpectBucketCount(
+      internal::kHistogramSignedExchangeFirstInputDelay,
+      timing.interactive_timing->first_input_delay.value().InMilliseconds(), 1);
+
+  histogram_tester().ExpectTotalCount(
+      internal::kHistogramSignedExchangeFirstPaint, 1);
+  histogram_tester().ExpectBucketCount(
+      internal::kHistogramSignedExchangeFirstPaint,
+      timing.paint_timing->first_paint.value().InMilliseconds(), 1);
+
+  histogram_tester().ExpectTotalCount(
+      internal::kHistogramSignedExchangeFirstContentfulPaint, 1);
+  histogram_tester().ExpectBucketCount(
+      internal::kHistogramSignedExchangeFirstContentfulPaint,
+      timing.paint_timing->first_contentful_paint.value().InMilliseconds(), 1);
+
+  histogram_tester().ExpectTotalCount(
+      internal::kHistogramSignedExchangeParseStartToFirstContentfulPaint, 1);
+  histogram_tester().ExpectBucketCount(
+      internal::kHistogramSignedExchangeParseStartToFirstContentfulPaint,
+      (timing.paint_timing->first_contentful_paint.value() -
+       timing.parse_timing->parse_start.value())
+          .InMilliseconds(),
+      1);
+
+  histogram_tester().ExpectTotalCount(
+      internal::kHistogramSignedExchangeDomContentLoaded, 1);
+  histogram_tester().ExpectBucketCount(
+      internal::kHistogramSignedExchangeDomContentLoaded,
+      timing.document_timing->dom_content_loaded_event_start.value()
+          .InMilliseconds(),
+      1);
+
+  histogram_tester().ExpectTotalCount(internal::kHistogramSignedExchangeLoad,
+                                      1);
+  histogram_tester().ExpectBucketCount(
+      internal::kHistogramSignedExchangeLoad,
+      timing.document_timing->load_event_start.value().InMilliseconds(), 1);
+
+  histogram_tester().ExpectTotalCount(
+      internal::kHistogramSignedExchangeParseStart, 1);
+  histogram_tester().ExpectBucketCount(
+      internal::kHistogramSignedExchangeParseStart,
+      timing.parse_timing->parse_start.value().InMilliseconds(), 1);
+}
+
+TEST_F(SignedExchangePageLoadMetricsObserverTest,
+       WithSignedExchangeBackground) {
+  page_load_metrics::mojom::PageLoadTiming timing;
+  page_load_metrics::InitPageLoadTimingForTest(&timing);
+  PopulateRequiredTimingFields(&timing);
+
+  NavigateAndCommitSignedExchange(GURL(kDefaultTestUrl));
+  SimulateTimingUpdate(timing);
+
+  // Background the tab, then foreground it.
+  web_contents()->WasHidden();
+  web_contents()->WasShown();
+
+  InitializeTestPageLoadTiming(&timing);
+  SimulateTimingUpdate(timing);
+
+  AssertNoSignedExchangeHistogramsLogged();
+}
diff --git a/chrome/browser/page_load_metrics/observers/use_counter/ukm_features.cc b/chrome/browser/page_load_metrics/observers/use_counter/ukm_features.cc
index 8507acf..d19b182 100644
--- a/chrome/browser/page_load_metrics/observers/use_counter/ukm_features.cc
+++ b/chrome/browser/page_load_metrics/observers/use_counter/ukm_features.cc
@@ -55,6 +55,8 @@
           WebFeature::kPrefixedVideoEnterFullScreen,
           WebFeature::kPrefixedVideoExitFullScreen,
           WebFeature::kDocumentLevelPassiveDefaultEventListenerPreventedWheel,
+          WebFeature::kDocumentDomainBlockedCrossOriginAccess,
+          WebFeature::kDocumentDomainEnabledCrossOriginAccess,
       }));
   return opt_in_features.count(feature);
 }
diff --git a/chrome/browser/page_load_metrics/page_load_metrics_initialize.cc b/chrome/browser/page_load_metrics/page_load_metrics_initialize.cc
index fe7c16e..6abf3c5 100644
--- a/chrome/browser/page_load_metrics/page_load_metrics_initialize.cc
+++ b/chrome/browser/page_load_metrics/page_load_metrics_initialize.cc
@@ -38,6 +38,7 @@
 #include "chrome/browser/page_load_metrics/observers/scheme_page_load_metrics_observer.h"
 #include "chrome/browser/page_load_metrics/observers/security_state_page_load_metrics_observer.h"
 #include "chrome/browser/page_load_metrics/observers/service_worker_page_load_metrics_observer.h"
+#include "chrome/browser/page_load_metrics/observers/signed_exchange_page_load_metrics_observer.h"
 #include "chrome/browser/page_load_metrics/observers/tab_restore_page_load_metrics_observer.h"
 #include "chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer.h"
 #include "chrome/browser/page_load_metrics/observers/use_counter_page_load_metrics_observer.h"
@@ -116,6 +117,8 @@
     tracker->AddObserver(
         std::make_unique<ServiceWorkerPageLoadMetricsObserver>());
     tracker->AddObserver(
+        std::make_unique<SignedExchangePageLoadMetricsObserver>());
+    tracker->AddObserver(
         std::make_unique<HttpsEngagementPageLoadMetricsObserver>(
             web_contents_->GetBrowserContext()));
     tracker->AddObserver(std::make_unique<CssScanningMetricsObserver>());
diff --git a/chrome/browser/printing/printer_manager_dialog_linux.cc b/chrome/browser/printing/printer_manager_dialog_linux.cc
index 4f56120..4ab84a0 100644
--- a/chrome/browser/printing/printer_manager_dialog_linux.cc
+++ b/chrome/browser/printing/printer_manager_dialog_linux.cc
@@ -66,13 +66,13 @@
       opened = OpenPrinterConfigDialog(kKde5KcmPrinterCommand) ||
                OpenPrinterConfigDialog(kSystemConfigPrinterCommand);
       break;
-    case base::nix::DESKTOP_ENVIRONMENT_CINNAMON:
     case base::nix::DESKTOP_ENVIRONMENT_KDE3:
     case base::nix::DESKTOP_ENVIRONMENT_PANTHEON:
     case base::nix::DESKTOP_ENVIRONMENT_UNITY:
     case base::nix::DESKTOP_ENVIRONMENT_XFCE:
       opened = OpenPrinterConfigDialog(kSystemConfigPrinterCommand);
       break;
+    case base::nix::DESKTOP_ENVIRONMENT_CINNAMON:
     case base::nix::DESKTOP_ENVIRONMENT_GNOME:
     case base::nix::DESKTOP_ENVIRONMENT_OTHER:
       opened = OpenPrinterConfigDialog(kSystemConfigPrinterCommand) ||
diff --git a/chrome/browser/renderer_host/render_process_host_chrome_browsertest.cc b/chrome/browser/renderer_host/render_process_host_chrome_browsertest.cc
index a063519..6943fee 100644
--- a/chrome/browser/renderer_host/render_process_host_chrome_browsertest.cc
+++ b/chrome/browser/renderer_host/render_process_host_chrome_browsertest.cc
@@ -92,22 +92,21 @@
   ChromeRenderProcessHostTest() {}
 
   // Show a tab, activating the current one if there is one, and wait for
-  // the renderer process to be created or foregrounded, returning the process
-  // handle.
-  base::Process ShowSingletonTab(const GURL& page) {
+  // the renderer process to be created or foregrounded, returning the
+  // WebContents associated with the tab.
+  WebContents* ShowSingletonTab(const GURL& page) {
     ::ShowSingletonTab(browser(), page);
     WebContents* wc = browser()->tab_strip_model()->GetActiveWebContents();
     CHECK(wc->GetURL() == page);
 
     WaitForLauncherThread();
     WaitForMessageProcessing(wc);
-    return ProcessFromHandle(
-        wc->GetMainFrame()->GetProcess()->GetProcess().Handle());
+    return wc;
   }
 
-  // Loads the given url in a new background tab and returns the handle of its
-  // renderer.
-  base::Process OpenBackgroundTab(const GURL& page) {
+  // Loads the given url in a new background tab and returns the WebContents
+  // associated with the tab.
+  WebContents* OpenBackgroundTab(const GURL& page) {
     ui_test_utils::NavigateToURLWithDisposition(
         browser(), page, WindowOpenDisposition::NEW_BACKGROUND_TAB,
         ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
@@ -119,8 +118,7 @@
 
     WaitForLauncherThread();
     WaitForMessageProcessing(wc);
-    return ProcessFromHandle(
-        wc->GetMainFrame()->GetProcess()->GetProcess().Handle());
+    return wc;
   }
 
   // Ensures that the backgrounding / foregrounding gets a chance to run.
@@ -236,15 +234,25 @@
     EXPECT_NE(rph1, rph3);
     EXPECT_NE(rph2, rph3);
   }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ChromeRenderProcessHostTest);
 };
 
 class ChromeRenderProcessHostTestWithCommandLine
     : public ChromeRenderProcessHostTest {
+ public:
+  ChromeRenderProcessHostTestWithCommandLine() = default;
+  ~ChromeRenderProcessHostTestWithCommandLine() override = default;
+
  protected:
   void SetUpCommandLine(base::CommandLine* command_line) override {
     ChromeRenderProcessHostTest::SetUpCommandLine(command_line);
     command_line->AppendSwitchASCII(switches::kRendererProcessLimit, "1");
   }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ChromeRenderProcessHostTestWithCommandLine);
 };
 
 // Disable on Windows and Mac due to ongoing flakiness. (crbug.com/442785)
@@ -317,99 +325,125 @@
 class ChromeRenderProcessHostBackgroundingTest
     : public ChromeRenderProcessHostTest {
  public:
+  ChromeRenderProcessHostBackgroundingTest() = default;
+  ~ChromeRenderProcessHostBackgroundingTest() override = default;
+
   void SetUpCommandLine(base::CommandLine* command_line) override {
     ChromeRenderProcessHostTest::SetUpCommandLine(command_line);
 
     command_line->AppendSwitch(switches::kProcessPerTab);
   }
 
+  void VerifyProcessIsBackgrounded(WebContents* web_contents) {
+    constexpr bool kExpectedIsBackground = true;
+    VerifyProcessPriority(web_contents->GetMainFrame()->GetProcess(),
+                          kExpectedIsBackground);
+  }
+
+  void VerifyProcessIsForegrounded(WebContents* web_contents) {
+    constexpr bool kExpectedIsBackground = false;
+    VerifyProcessPriority(web_contents->GetMainFrame()->GetProcess(),
+                          kExpectedIsBackground);
+  }
+
   void SetUpOnMainThread() override {
     ASSERT_TRUE(embedded_test_server()->Start());
   }
+
+ private:
+  void VerifyProcessPriority(content::RenderProcessHost* process,
+                             bool expected_is_backgrounded) {
+    EXPECT_TRUE(process->IsInitializedAndNotDead());
+    EXPECT_EQ(expected_is_backgrounded, process->IsProcessBackgrounded());
+
+    if (base::Process::CanBackgroundProcesses()) {
+      base::Process p = ProcessFromHandle(process->GetProcess().Handle());
+      ASSERT_TRUE(p.IsValid());
+#if defined(OS_MACOSX)
+      base::PortProvider* port_provider =
+          content::BrowserChildProcessHost::GetPortProvider();
+      EXPECT_EQ(expected_is_backgrounded,
+                p.IsProcessBackgrounded(port_provider));
+#else
+      EXPECT_EQ(expected_is_backgrounded, p.IsProcessBackgrounded());
+#endif
+    }
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(ChromeRenderProcessHostBackgroundingTest);
 };
 
+#define EXPECT_PROCESS_IS_BACKGROUNDED(process_or_tab)                       \
+  do {                                                                       \
+    SCOPED_TRACE("Verifying that |" #process_or_tab "| is backgrounded..."); \
+    VerifyProcessIsBackgrounded(process_or_tab);                             \
+  } while (0);
+
+#define EXPECT_PROCESS_IS_FOREGROUNDED(process_or_tab)                       \
+  do {                                                                       \
+    SCOPED_TRACE("Verifying that |" #process_or_tab "| is foregrounded..."); \
+    VerifyProcessIsForegrounded(process_or_tab);                             \
+  } while (0);
+
 IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostBackgroundingTest, MultipleTabs) {
-  // This test is invalid on platforms that can't background.
-  if (!base::Process::CanBackgroundProcesses())
-    return;
-
-#if defined(OS_MACOSX)
-  base::PortProvider* port_provider =
-      content::BrowserChildProcessHost::GetPortProvider();
-#endif  //  defined(OS_MACOSX)
-
   // Change the first tab to be the omnibox page (TYPE_WEBUI).
   GURL omnibox(chrome::kChromeUIOmniboxURL);
   ui_test_utils::NavigateToURL(browser(), omnibox);
 
   // Create a new tab. It should be foreground.
   GURL page1("data:text/html,hello world1");
-  base::Process process1 = ShowSingletonTab(page1);
-  ASSERT_TRUE(process1.IsValid());
-#if defined(OS_MACOSX)
-  EXPECT_FALSE(process1.IsProcessBackgrounded(port_provider));
-#else
-  EXPECT_FALSE(process1.IsProcessBackgrounded());
-#endif
+  WebContents* tab1 = ShowSingletonTab(page1);
+  {
+    SCOPED_TRACE("TEST STEP: Single tab");
+    EXPECT_PROCESS_IS_FOREGROUNDED(tab1);
+  }
 
   // Create another tab. It should be foreground, and the first tab should
   // now be background.
   GURL page2("data:text/html,hello world2");
-  base::Process process2 = ShowSingletonTab(page2);
-  ASSERT_TRUE(process2.IsValid());
-  EXPECT_NE(process1.Pid(), process2.Pid());
-#if defined(OS_MACOSX)
-  EXPECT_TRUE(process1.IsProcessBackgrounded(port_provider));
-  EXPECT_FALSE(process2.IsProcessBackgrounded(port_provider));
-#else
-  EXPECT_TRUE(process1.IsProcessBackgrounded());
-  EXPECT_FALSE(process2.IsProcessBackgrounded());
-#endif  // defined(OS_MACOSX)
+  WebContents* tab2 = ShowSingletonTab(page2);
+  {
+    SCOPED_TRACE("TEST STEP: 2nd tab opened in foreground");
+    EXPECT_NE(tab1->GetMainFrame()->GetProcess(),
+              tab2->GetMainFrame()->GetProcess());
+    EXPECT_PROCESS_IS_BACKGROUNDED(tab1);
+    EXPECT_PROCESS_IS_FOREGROUNDED(tab2);
+  }
 
   // Load another tab in background. The renderer of the new tab should be
   // backgrounded, while visibility of the other renderers should not change.
   GURL page3("data:text/html,hello world3");
-  base::Process process3 = OpenBackgroundTab(page3);
-  ASSERT_TRUE(process3.IsValid());
-  EXPECT_NE(process3.Pid(), process1.Pid());
-  EXPECT_NE(process3.Pid(), process2.Pid());
-#if defined(OS_MACOSX)
-  EXPECT_TRUE(process1.IsProcessBackgrounded(port_provider));
-  EXPECT_FALSE(process2.IsProcessBackgrounded(port_provider));
-  EXPECT_TRUE(process3.IsProcessBackgrounded(port_provider));
+  WebContents* tab3 = OpenBackgroundTab(page3);
+  {
+    SCOPED_TRACE("TEST STEP: 3rd tab opened in background");
+    EXPECT_NE(tab1->GetMainFrame()->GetProcess(),
+              tab3->GetMainFrame()->GetProcess());
+    EXPECT_NE(tab2->GetMainFrame()->GetProcess(),
+              tab3->GetMainFrame()->GetProcess());
+    EXPECT_PROCESS_IS_BACKGROUNDED(tab1);
+    EXPECT_PROCESS_IS_FOREGROUNDED(tab2);
+    EXPECT_PROCESS_IS_BACKGROUNDED(tab3);
+  }
 
   // Navigate back to the first page. Its renderer should be in foreground
   // again while the other renderers should be backgrounded.
-  EXPECT_EQ(process1.Pid(), ShowSingletonTab(page1).Pid());
-  EXPECT_FALSE(process1.IsProcessBackgrounded(port_provider));
-  EXPECT_TRUE(process2.IsProcessBackgrounded(port_provider));
-  EXPECT_TRUE(process3.IsProcessBackgrounded(port_provider));
+  ShowSingletonTab(page1);
+  {
+    SCOPED_TRACE("TEST STEP: First tab activated again");
+    EXPECT_PROCESS_IS_FOREGROUNDED(tab1);
+    EXPECT_PROCESS_IS_BACKGROUNDED(tab2);
+    EXPECT_PROCESS_IS_BACKGROUNDED(tab3);
+  }
 
-  // Confirm that |process3| remains backgrounded after being shown/hidden.
-  EXPECT_EQ(process3.Pid(), ShowSingletonTab(page3).Pid());
-  EXPECT_EQ(process1.Pid(), ShowSingletonTab(page1).Pid());
-  EXPECT_FALSE(process1.IsProcessBackgrounded(port_provider));
-  EXPECT_TRUE(process2.IsProcessBackgrounded(port_provider));
-  EXPECT_TRUE(process3.IsProcessBackgrounded(port_provider));
-#else
-  EXPECT_TRUE(process1.IsProcessBackgrounded());
-  EXPECT_FALSE(process2.IsProcessBackgrounded());
-  EXPECT_TRUE(process3.IsProcessBackgrounded());
-
-  // Navigate back to the first page. Its renderer should be in foreground
-  // again while the other renderers should be backgrounded.
-  EXPECT_EQ(process1.Pid(), ShowSingletonTab(page1).Pid());
-  EXPECT_FALSE(process1.IsProcessBackgrounded());
-  EXPECT_TRUE(process2.IsProcessBackgrounded());
-  EXPECT_TRUE(process3.IsProcessBackgrounded());
-
-  // Confirm that |process3| remains backgrounded after being shown/hidden.
-  EXPECT_EQ(process3.Pid(), ShowSingletonTab(page3).Pid());
-  EXPECT_EQ(process1.Pid(), ShowSingletonTab(page1).Pid());
-  EXPECT_FALSE(process1.IsProcessBackgrounded());
-  EXPECT_TRUE(process2.IsProcessBackgrounded());
-  EXPECT_TRUE(process3.IsProcessBackgrounded());
-#endif
+  // Confirm that |tab3| remains backgrounded after being shown/hidden.
+  ShowSingletonTab(page3);
+  ShowSingletonTab(page1);
+  {
+    SCOPED_TRACE("TEST STEP: Third tab activated and deactivated");
+    EXPECT_PROCESS_IS_FOREGROUNDED(tab1);
+    EXPECT_PROCESS_IS_BACKGROUNDED(tab2);
+    EXPECT_PROCESS_IS_BACKGROUNDED(tab3);
+  }
 }
 
 // TODO(nasko): crbug.com/173137
@@ -631,7 +665,9 @@
                                            ->GetProcess()
                                            ->GetProcess()
                                            .Handle());
-    no_audio_process_ = ShowSingletonTab(no_audio_url_);
+    WebContents* wc = ShowSingletonTab(no_audio_url_);
+    no_audio_process_ = ProcessFromHandle(
+        wc->GetMainFrame()->GetProcess()->GetProcess().Handle());
     ASSERT_NE(audio_process_.Pid(), no_audio_process_.Pid());
     ASSERT_TRUE(no_audio_process_.IsValid());
     ASSERT_TRUE(audio_process_.IsValid());
diff --git a/chrome/browser/resource_coordinator/lifecycle_unit_base.cc b/chrome/browser/resource_coordinator/lifecycle_unit_base.cc
index 8f0d8a4..0d2765b 100644
--- a/chrome/browser/resource_coordinator/lifecycle_unit_base.cc
+++ b/chrome/browser/resource_coordinator/lifecycle_unit_base.cc
@@ -83,6 +83,10 @@
   return ukm::kInvalidSourceId;
 }
 
+void LifecycleUnitBase::SetDiscardCountForTesting(size_t discard_count) {
+  discard_count_ = discard_count;
+}
+
 void LifecycleUnitBase::SetState(LifecycleUnitState state,
                                  LifecycleUnitStateChangeReason reason) {
   if (state == state_)
diff --git a/chrome/browser/resource_coordinator/lifecycle_unit_base.h b/chrome/browser/resource_coordinator/lifecycle_unit_base.h
index 1f771a7..daed664 100644
--- a/chrome/browser/resource_coordinator/lifecycle_unit_base.h
+++ b/chrome/browser/resource_coordinator/lifecycle_unit_base.h
@@ -41,6 +41,8 @@
   void RemoveObserver(LifecycleUnitObserver* observer) override;
   ukm::SourceId GetUkmSourceId() const override;
 
+  void SetDiscardCountForTesting(size_t discard_count);
+
  protected:
   // TODO(chrisha|fdoray): Clean up the virtual methods below and make them
   // pure virtual.
diff --git a/chrome/browser/resource_coordinator/tab_lifecycle_unit.cc b/chrome/browser/resource_coordinator/tab_lifecycle_unit.cc
index a1be8ab..453de87 100644
--- a/chrome/browser/resource_coordinator/tab_lifecycle_unit.cc
+++ b/chrome/browser/resource_coordinator/tab_lifecycle_unit.cc
@@ -288,6 +288,12 @@
   DCHECK(web_contents);
   DCHECK(tab_strip_model_);
 
+  // Attach the ResourceCoordinatorTabHelper. In production code this has
+  // already been attached by now due to AttachTabHelpers, but there's a long
+  // tail of tests that don't add these helpers. This ensures that the various
+  // DCHECKs in the state transition machinery don't fail.
+  ResourceCoordinatorTabHelper::CreateForWebContents(web_contents);
+
   // Visible tabs are treated as having been immediately focused, while
   // non-visible tabs have their focus set to the last active time (the time at
   // which they stopped being the active tab in a tabstrip).
@@ -584,6 +590,23 @@
     return false;
   }
 
+// Fix for urgent discarding woes in crbug.com/883071. These protections only
+// apply on non-ChromeOS desktop platforms (Linux, Mac, Win).
+// NOTE: These do not currently provide DecisionDetails!
+#if !defined(OS_CHROMEOS)
+  if (reason == LifecycleUnitDiscardReason::URGENT) {
+    // Limit urgent discarding to once only.
+    if (GetDiscardCount() > 0)
+      return false;
+    // Protect non-visible tabs from urgent discarding for a period of time.
+    if (web_contents()->GetVisibility() != content::Visibility::VISIBLE) {
+      base::TimeDelta time_in_bg = NowTicks() - GetWallTimeWhenHidden();
+      if (time_in_bg < kBackgroundUrgentProtectionTime)
+        return false;
+    }
+  }
+#endif
+
   // We deliberately run through all of the logic without early termination.
   // This ensures that the decision details lists all possible reasons that the
   // transition can be denied.
diff --git a/chrome/browser/resource_coordinator/tab_lifecycle_unit.h b/chrome/browser/resource_coordinator/tab_lifecycle_unit.h
index ac7392e..c13f1a4 100644
--- a/chrome/browser/resource_coordinator/tab_lifecycle_unit.h
+++ b/chrome/browser/resource_coordinator/tab_lifecycle_unit.h
@@ -29,6 +29,11 @@
 class UsageClock;
 class TabLifecycleObserver;
 
+// Time during which backgrounded tabs are protected from urgent discarding
+// (not on ChromeOS).
+static constexpr base::TimeDelta kBackgroundUrgentProtectionTime =
+    base::TimeDelta::FromMinutes(10);
+
 // Time during which a tab cannot be discarded after having played audio.
 static constexpr base::TimeDelta kTabAudioProtectionTime =
     base::TimeDelta::FromMinutes(1);
diff --git a/chrome/browser/resource_coordinator/tab_lifecycle_unit_source_unittest.cc b/chrome/browser/resource_coordinator/tab_lifecycle_unit_source_unittest.cc
index 633d7fa..a26a430 100644
--- a/chrome/browser/resource_coordinator/tab_lifecycle_unit_source_unittest.cc
+++ b/chrome/browser/resource_coordinator/tab_lifecycle_unit_source_unittest.cc
@@ -275,6 +275,9 @@
     CreateTwoTabs(true /* focus_tab_strip */, &first_lifecycle_unit,
                   &second_lifecycle_unit);
 
+    // Advance time so tabs are urgent discardable.
+    task_runner_->AdvanceMockTickClock(kBackgroundUrgentProtectionTime);
+
     // Detach the non-active tab. Verify that it can no longer be discarded.
     ExpectCanDiscardTrueAllReasons(first_lifecycle_unit);
     std::unique_ptr<content::WebContents> owned_contents =
@@ -323,6 +326,9 @@
     content::WebContentsTester::For(initial_web_contents)
         ->SetLastActiveTime(kDummyLastActiveTime);
 
+    // Advance time so tabs are urgent discardable.
+    task_runner_->AdvanceMockTickClock(kBackgroundUrgentProtectionTime);
+
     // Discard the tab.
     EXPECT_EQ(LifecycleUnitState::ACTIVE,
               background_lifecycle_unit->GetState());
@@ -352,6 +358,9 @@
     content::WebContents* initial_web_contents =
         tab_strip_model_->GetWebContentsAt(0);
 
+    // Advance time so tabs are urgent discardable.
+    task_runner_->AdvanceMockTickClock(kBackgroundUrgentProtectionTime);
+
     // Discard the tab.
     EXPECT_EQ(LifecycleUnitState::ACTIVE,
               background_lifecycle_unit->GetState());
@@ -386,6 +395,9 @@
     content::WebContents* initial_web_contents =
         tab_strip_model_->GetWebContentsAt(0);
 
+    // Advance time so tabs are urgent discardable.
+    task_runner_->AdvanceMockTickClock(kBackgroundUrgentProtectionTime);
+
     // Discard the tab.
     EXPECT_EQ(LifecycleUnitState::ACTIVE,
               background_lifecycle_unit->GetState());
@@ -602,18 +614,17 @@
       tab_strip_model_->GetWebContentsAt(0);
   task_runner_->FastForwardBy(kShortDelay);
 
-  // It should be possible to discard the background tab.
-  ExpectCanDiscardTrueAllReasons(background_lifecycle_unit);
+  // Advance time so tabs are urgent discardable.
+  task_runner_->AdvanceMockTickClock(kBackgroundUrgentProtectionTime);
 
-  // Discard the tab. Use LifecycleUnitDiscardReason::URGENT to force the
-  // discard.
+  // Discard the tab.
   EXPECT_EQ(LifecycleUnitState::ACTIVE, background_lifecycle_unit->GetState());
   EXPECT_CALL(tab_observer_, OnDiscardedStateChange(::testing::_, true));
-  background_lifecycle_unit->Discard(LifecycleUnitDiscardReason::URGENT);
+  background_lifecycle_unit->Discard(LifecycleUnitDiscardReason::PROACTIVE);
 
   ::testing::Mock::VerifyAndClear(&tab_observer_);
   TransitionFromPendingDiscardToDiscardedIfNeeded(
-      LifecycleUnitDiscardReason::URGENT, background_lifecycle_unit);
+      LifecycleUnitDiscardReason::PROACTIVE, background_lifecycle_unit);
   EXPECT_EQ(LifecycleUnitState::DISCARDED,
             background_lifecycle_unit->GetState());
   EXPECT_NE(initial_web_contents, tab_strip_model_->GetWebContentsAt(0));
diff --git a/chrome/browser/resource_coordinator/tab_lifecycle_unit_unittest.cc b/chrome/browser/resource_coordinator/tab_lifecycle_unit_unittest.cc
index 0f0ed65..3dabe14 100644
--- a/chrome/browser/resource_coordinator/tab_lifecycle_unit_unittest.cc
+++ b/chrome/browser/resource_coordinator/tab_lifecycle_unit_unittest.cc
@@ -191,6 +191,9 @@
   TabLifecycleUnit tab_lifecycle_unit(GetSource(), &observers_,
                                       usage_clock_.get(), web_contents_,
                                       tab_strip_model_.get());
+  // Advance time enough that the tab is urgent discardable.
+  test_clock_.Advance(kBackgroundUrgentProtectionTime);
+
   auto* observer = ResourceCoordinatorTabHelper::FromWebContents(web_contents_)
                        ->local_site_characteristics_wc_observer_for_testing();
   test_clock_.Advance(base::TimeDelta::FromSeconds(1));
@@ -243,6 +246,8 @@
   TabLifecycleUnit tab_lifecycle_unit(GetSource(), &observers_,
                                       usage_clock_.get(), web_contents_,
                                       tab_strip_model_.get());
+  // Advance time enough that the tab is urgent discardable.
+  test_clock_.Advance(kBackgroundUrgentProtectionTime);
   ExpectCanDiscardTrueAllReasons(&tab_lifecycle_unit);
 }
 
@@ -251,6 +256,8 @@
                                       usage_clock_.get(), web_contents_,
                                       tab_strip_model_.get());
   EXPECT_EQ(NowTicks(), tab_lifecycle_unit.GetLastFocusedTime());
+  // Advance time enough that the tab is urgent discardable.
+  test_clock_.Advance(kBackgroundUrgentProtectionTime);
   ExpectCanDiscardTrueAllReasons(&tab_lifecycle_unit);
 
   tab_lifecycle_unit.SetFocused(true);
@@ -264,6 +271,8 @@
   tab_strip_model_->ActivateTabAt(1, false);
   web_contents_->WasHidden();
   EXPECT_EQ(test_clock_.NowTicks(), tab_lifecycle_unit.GetLastFocusedTime());
+  // Advance time enough that the tab is urgent discardable.
+  test_clock_.Advance(kBackgroundUrgentProtectionTime);
   ExpectCanDiscardTrueAllReasons(&tab_lifecycle_unit);
 }
 
@@ -272,6 +281,8 @@
                                       usage_clock_.get(), web_contents_,
                                       tab_strip_model_.get());
 
+  // Advance time enough that the tab is urgent discardable.
+  test_clock_.Advance(kBackgroundUrgentProtectionTime);
   EXPECT_TRUE(tab_lifecycle_unit.IsAutoDiscardable());
   ExpectCanDiscardTrueAllReasons(&tab_lifecycle_unit);
 
@@ -307,9 +318,48 @@
 
   tab_strip_model_->ActivateTabAt(0, false);
 
+  // Advance time enough that the tab is urgent discardable.
+  test_clock_.Advance(kBackgroundUrgentProtectionTime);
+
   ExpectCanDiscardFalseAllReasons(&tab_lifecycle_unit,
                                   DecisionFailureReason::LIVE_STATE_VISIBLE);
 }
+
+TEST_F(TabLifecycleUnitTest, UrgentDiscardProtections) {
+  TabLifecycleUnit tab_lifecycle_unit(GetSource(), &observers_,
+                                      usage_clock_.get(), web_contents_,
+                                      tab_strip_model_.get());
+  // Initial external and proactive discarding are fine, but urgent discarding
+  // is blocked because the tab is too recent.
+  ExpectCanDiscardTrue(&tab_lifecycle_unit,
+                       LifecycleUnitDiscardReason::EXTERNAL);
+  ExpectCanDiscardTrue(&tab_lifecycle_unit,
+                       LifecycleUnitDiscardReason::PROACTIVE);
+  ExpectCanDiscardFalseTrivial(&tab_lifecycle_unit,
+                               LifecycleUnitDiscardReason::URGENT);
+
+  // Advance time enough that the tab is urgent discardable.
+  test_clock_.Advance(kBackgroundUrgentProtectionTime);
+
+  // The tab should now be discardable for all reasons.
+  ExpectCanDiscardTrueAllReasons(&tab_lifecycle_unit);
+
+  // Mark the tab as having been discarded.
+  tab_lifecycle_unit.SetDiscardCountForTesting(1);
+
+  // Advance time enough that the time protection no longer applies. The tab
+  // should still not be urgent discardable at this point, because it has
+  // already been discarded at least once.
+  test_clock_.Advance(kBackgroundUrgentProtectionTime);
+
+  // Proactive and external discarding should be fine, but not urgent.
+  ExpectCanDiscardTrue(&tab_lifecycle_unit,
+                       LifecycleUnitDiscardReason::EXTERNAL);
+  ExpectCanDiscardTrue(&tab_lifecycle_unit,
+                       LifecycleUnitDiscardReason::PROACTIVE);
+  ExpectCanDiscardFalseTrivial(&tab_lifecycle_unit,
+                               LifecycleUnitDiscardReason::URGENT);
+}
 #endif  // !defined(OS_CHROMEOS)
 
 TEST_F(TabLifecycleUnitTest, CannotDiscardInvalidURL) {
@@ -339,6 +389,9 @@
   TabLifecycleUnit tab_lifecycle_unit(GetSource(), &observers_,
                                       usage_clock_.get(), web_contents_,
                                       tab_strip_model_.get());
+  // Advance time enough that the tab is urgent discardable.
+  test_clock_.Advance(kBackgroundUrgentProtectionTime);
+  ExpectCanDiscardTrueAllReasons(&tab_lifecycle_unit);
 
   content::MediaStreamDevices video_devices{
       content::MediaStreamDevice(content::MEDIA_DEVICE_VIDEO_CAPTURE,
@@ -359,6 +412,9 @@
   TabLifecycleUnit tab_lifecycle_unit(GetSource(), &observers_,
                                       usage_clock_.get(), web_contents_,
                                       tab_strip_model_.get());
+  // Advance time enough that the tab is urgent discardable.
+  test_clock_.Advance(kBackgroundUrgentProtectionTime);
+  ExpectCanDiscardTrueAllReasons(&tab_lifecycle_unit);
 
   content::MediaStreamDevices desktop_capture_devices{
       content::MediaStreamDevice(content::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE,
@@ -379,6 +435,9 @@
   TabLifecycleUnit tab_lifecycle_unit(GetSource(), &observers_,
                                       usage_clock_.get(), web_contents_,
                                       tab_strip_model_.get());
+  // Advance time enough that the tab is urgent discardable.
+  test_clock_.Advance(kBackgroundUrgentProtectionTime);
+  ExpectCanDiscardTrueAllReasons(&tab_lifecycle_unit);
 
   // Cannot discard when the "recently audible" bit is set.
   tab_lifecycle_unit.SetRecentlyAudible(true);
@@ -414,6 +473,9 @@
                                       tab_strip_model_.get());
   TabLoadTracker::Get()->TransitionStateForTesting(web_contents_,
                                                    LoadingState::LOADED);
+  // Advance time enough that the tab is urgent discardable.
+  test_clock_.Advance(kBackgroundUrgentProtectionTime);
+  ExpectCanDiscardTrueAllReasons(&tab_lifecycle_unit);
 
   // Make sure there is a DeviceClient instance.
   device::MockDeviceClient device_client;
@@ -452,6 +514,9 @@
   TabLifecycleUnit tab_lifecycle_unit(GetSource(), &observers_,
                                       usage_clock_.get(), web_contents_,
                                       tab_strip_model_.get());
+  // Advance time enough that the tab is urgent discardable.
+  test_clock_.Advance(kBackgroundUrgentProtectionTime);
+  ExpectCanDiscardTrueAllReasons(&tab_lifecycle_unit);
 
   tab_lifecycle_unit.SetRecentlyAudible(false);
   // Since the tab was never audible, it should be possible to discard it,
@@ -463,6 +528,9 @@
   TabLifecycleUnit tab_lifecycle_unit(GetSource(), &observers_,
                                       usage_clock_.get(), web_contents_,
                                       tab_strip_model_.get());
+  // Advance time enough that the tab is urgent discardable.
+  test_clock_.Advance(kBackgroundUrgentProtectionTime);
+  ExpectCanDiscardTrueAllReasons(&tab_lifecycle_unit);
 
   content::WebContentsTester::For(web_contents_)
       ->SetMainFrameMimeType("application/pdf");
@@ -511,6 +579,9 @@
   TabLifecycleUnit tab_lifecycle_unit(GetSource(), &observers_,
                                       usage_clock_.get(), web_contents_,
                                       tab_strip_model_.get());
+  // Advance time enough that the tab is urgent discardable.
+  test_clock_.Advance(kBackgroundUrgentProtectionTime);
+
   // Proactive discarding shouldn't be possible, urgent and external discarding
   // should still be possible.
   {
@@ -706,6 +777,9 @@
   TabLifecycleUnit tab_lifecycle_unit(GetSource(), &observers_,
                                       usage_clock_.get(), web_contents_,
                                       tab_strip_model_.get());
+  // Advance time enough that the tab is urgent discardable.
+  test_clock_.Advance(kBackgroundUrgentProtectionTime);
+  ExpectCanDiscardTrueAllReasons(&tab_lifecycle_unit);
 
   ExpectCanDiscardTrueAllReasons(&tab_lifecycle_unit);
 
diff --git a/chrome/browser/resource_coordinator/tab_manager.h b/chrome/browser/resource_coordinator/tab_manager.h
index 9759d42..5e8ddaef 100644
--- a/chrome/browser/resource_coordinator/tab_manager.h
+++ b/chrome/browser/resource_coordinator/tab_manager.h
@@ -222,7 +222,8 @@
                            ProactiveFastShutdownWithUnloadHandler);
   FRIEND_TEST_ALL_PREFIXES(TabManagerTest, ProtectDevToolsTabsFromDiscarding);
   FRIEND_TEST_ALL_PREFIXES(TabManagerTest, ProtectPDFPages);
-  FRIEND_TEST_ALL_PREFIXES(TabManagerTest, ProtectRecentlyUsedTabs);
+  FRIEND_TEST_ALL_PREFIXES(TabManagerTest,
+                           ProtectRecentlyUsedTabsFromUrgentDiscarding);
   FRIEND_TEST_ALL_PREFIXES(TabManagerTest, ProtectVideoTabs);
   FRIEND_TEST_ALL_PREFIXES(TabManagerTest, PurgeBackgroundRenderer);
   FRIEND_TEST_ALL_PREFIXES(TabManagerTest,
diff --git a/chrome/browser/resource_coordinator/tab_manager_browsertest.cc b/chrome/browser/resource_coordinator/tab_manager_browsertest.cc
index 2ba8ffc..20c5b59 100644
--- a/chrome/browser/resource_coordinator/tab_manager_browsertest.cc
+++ b/chrome/browser/resource_coordinator/tab_manager_browsertest.cc
@@ -381,6 +381,9 @@
   load5.Wait();
   EXPECT_EQ(3, tsm()->count());
 
+  // Advance time so everything is urgent discardable.
+  test_clock_.Advance(kBackgroundUrgentProtectionTime);
+
   // Discard a tab.
   EXPECT_TRUE(
       tab_manager()->DiscardTabImpl(LifecycleUnitDiscardReason::URGENT));
@@ -416,6 +419,9 @@
   // Kill the third tab after making second tab active.
   tsm()->ActivateTabAt(1, true);
 
+  // Advance time so everything is urgent discardable again.
+  test_clock_.Advance(kBackgroundUrgentProtectionTime);
+
   EXPECT_EQ(1, tsm()->active_index());
   EXPECT_FALSE(IsTabDiscarded(GetWebContentsAt(1)));
   tab_manager()->DiscardTabImpl(LifecycleUnitDiscardReason::URGENT);
@@ -500,6 +506,9 @@
   EXPECT_FALSE(IsTabDiscarded(GetWebContentsAt(0)));
   EXPECT_FALSE(IsTabDiscarded(GetWebContentsAt(1)));
 
+  // Advance time so everything is urgent discardable.
+  test_clock_.Advance(kBackgroundUrgentProtectionTime);
+
   // Nothing should happen with a moderate memory pressure event.
   fake_memory_pressure_monitor_.SetAndNotifyMemoryPressure(
       base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE);
@@ -582,6 +591,55 @@
       tab_manager()->DiscardTabImpl(LifecycleUnitDiscardReason::PROACTIVE));
 }
 
+#if !defined(OS_CHROMEOS)
+// Makes sure that recently opened or used tabs are protected.
+IN_PROC_BROWSER_TEST_F(TabManagerTest,
+                       ProtectRecentlyUsedTabsFromUrgentDiscarding) {
+  TabManager* tab_manager = g_browser_process->GetTabManager();
+
+  auto* tsm = browser()->tab_strip_model();
+
+  // Open 2 tabs, the second one being in the background.
+  ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUIAboutURL));
+  ui_test_utils::NavigateToURLWithDisposition(
+      browser(), GURL(chrome::kChromeUIAboutURL),
+      WindowOpenDisposition::NEW_BACKGROUND_TAB,
+      ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
+  EXPECT_EQ(2, tsm->count());
+
+  // Advance the clock for less than the protection time.
+  test_clock_.Advance(kBackgroundUrgentProtectionTime / 2);
+
+  // Should not be able to discard a tab.
+  ASSERT_FALSE(tab_manager->DiscardTabImpl(LifecycleUnitDiscardReason::URGENT));
+
+  // Advance the clock for more than the protection time.
+  test_clock_.Advance(kBackgroundUrgentProtectionTime / 2 +
+                      base::TimeDelta::FromSeconds(1));
+
+  // Should be able to discard the background tab now.
+  EXPECT_TRUE(tab_manager->DiscardTabImpl(LifecycleUnitDiscardReason::URGENT));
+
+  // Activate the 2nd tab.
+  tsm->ActivateTabAt(1, true);
+  EXPECT_EQ(1, tsm->active_index());
+
+  // Advance the clock for less than the protection time.
+  test_clock_.Advance(kBackgroundUrgentProtectionTime / 2);
+
+  // Should not be able to urgent discard the tab.
+  ASSERT_FALSE(tab_manager->DiscardTabImpl(LifecycleUnitDiscardReason::URGENT));
+
+  // But should be able to proactive discard the tab.
+  EXPECT_TRUE(
+      tab_manager->DiscardTabImpl(LifecycleUnitDiscardReason::PROACTIVE));
+
+  // This is necessary otherwise the test crashes in
+  // WebContentsData::WebContentsDestroyed.
+  tsm->CloseAllTabs();
+}
+#endif  // !defined(OS_CHROMEOS)
+
 // Makes sure that tabs using media devices are protected.
 IN_PROC_BROWSER_TEST_F(TabManagerTest, ProtectVideoTabs) {
   // Open 2 tabs, the second one being in the background.
@@ -853,6 +911,8 @@
   content::WindowedNotificationObserver observer(
       content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
       content::NotificationService::AllSources());
+  // Advance time so everything is urgent discardable.
+  test_clock_.Advance(kBackgroundUrgentProtectionTime);
   base::HistogramTester tester;
   EXPECT_TRUE(
       tab_manager()->DiscardTabImpl(LifecycleUnitDiscardReason::URGENT));
@@ -888,6 +948,9 @@
   OpenTwoTabs(GURL(chrome::kChromeUIAboutURL),
               GURL(chrome::kChromeUICreditsURL));
 
+  // Advance time so everything is urgent discardable.
+  test_clock_.Advance(kBackgroundUrgentProtectionTime);
+
   // The Tab Manager will not be able to fast-kill either of the tabs since they
   // share the same process regardless of the discard reason. An unsafe attempt
   // will be made on some platforms.
@@ -910,6 +973,9 @@
   OpenTwoTabs(GURL(chrome::kChromeUIAboutURL),
               GURL(embedded_test_server()->GetURL("/unload.html")));
 
+  // Advance time so everything is urgent discardable.
+  test_clock_.Advance(kBackgroundUrgentProtectionTime);
+
   base::HistogramTester tester;
   // The Tab Manager will not be able to safely fast-kill either of the tabs as
   // one of them is current, and the other has an unload handler. No unsafe
@@ -928,6 +994,9 @@
   OpenTwoTabs(GURL(chrome::kChromeUIAboutURL),
               GURL(embedded_test_server()->GetURL("/unload.html")));
 
+  // Advance time so everything is urgent discardable.
+  test_clock_.Advance(kBackgroundUrgentProtectionTime);
+
   // The Tab Manager will not be able to safely fast-kill either of the tabs as
   // one of them is current, and the other has an unload handler. An unsafe
   // attempt will be made on some platforms.
@@ -961,6 +1030,9 @@
   OpenTwoTabs(GURL(chrome::kChromeUIAboutURL),
               GURL(embedded_test_server()->GetURL("/beforeunload.html")));
 
+  // Advance time so everything is urgent discardable.
+  test_clock_.Advance(kBackgroundUrgentProtectionTime);
+
   // The Tab Manager will not be able to safely fast-kill either of the tabs as
   // one of them is current, and the other has a beforeunload handler. No unsafe
   // attempts will be made.
@@ -980,6 +1052,9 @@
   OpenTwoTabs(GURL(chrome::kChromeUIAboutURL),
               GURL(embedded_test_server()->GetURL("/beforeunload.html")));
 
+  // Advance time so everything is urgent discardable.
+  test_clock_.Advance(kBackgroundUrgentProtectionTime);
+
   // The Tab Manager will not be able to safely fast-kill either of the tabs as
   // one of them is current, and the other has a beforeunload handler. An unsafe
   // attempt will be made on some platforms.
@@ -1078,6 +1153,9 @@
 // - Discard(kUrgent): PENDING_FREEZE->DISCARDED
 IN_PROC_BROWSER_TEST_F(TabManagerTestWithTwoTabs,
                        TabFreezeAndUrgentDiscardBeforeFreezeCompletes) {
+  // Advance time so everything is urgent discardable.
+  test_clock_.Advance(kBackgroundUrgentProtectionTime);
+
   // Freeze the background tab.
   EXPECT_EQ(LifecycleUnitState::ACTIVE, GetLifecycleUnitAt(1)->GetState());
   EXPECT_TRUE(GetLifecycleUnitAt(1)->Freeze());
@@ -1096,6 +1174,9 @@
 // - Freeze happens in renderer: PENDING_FREEZE->FROZEN
 // - Discard(kUrgent): FROZEN->DISCARDED
 IN_PROC_BROWSER_TEST_F(TabManagerTest, TabFreezeAndUrgentDiscard) {
+  // Advance time so everything is urgent discardable.
+  test_clock_.Advance(kBackgroundUrgentProtectionTime);
+
   TestTransitionFromActiveToFrozen();
 
   // Urgently discard the background tab.
@@ -1427,6 +1508,9 @@
   EXPECT_NE(browser2, browser4);
   EXPECT_NE(browser3, browser4);
 
+  // Advance time so everything is urgent discardable.
+  test_clock_.Advance(kBackgroundUrgentProtectionTime);
+
   for (int i = 0; i < 8; ++i)
     tab_manager()->DiscardTab(LifecycleUnitDiscardReason::PROACTIVE);
 
diff --git a/chrome/browser/resource_coordinator/tab_manager_features.cc b/chrome/browser/resource_coordinator/tab_manager_features.cc
index f75c32f..b4827d7 100644
--- a/chrome/browser/resource_coordinator/tab_manager_features.cc
+++ b/chrome/browser/resource_coordinator/tab_manager_features.cc
@@ -30,8 +30,7 @@
 
 // Enables proactive tab freezing and discarding.
 const base::Feature kProactiveTabFreezeAndDiscard{
-    resource_coordinator::kProactiveTabFreezeAndDiscardFeatureName,
-    base::FEATURE_DISABLED_BY_DEFAULT};
+    "ProactiveTabFreezeAndDiscard", base::FEATURE_DISABLED_BY_DEFAULT};
 
 // Enables the site characteristics database.
 const base::Feature kSiteCharacteristicsDatabase{
@@ -65,10 +64,8 @@
 int GetModerateThresholdTabCountBasedOnSystemMemory(
     ProactiveTabFreezeAndDiscardParams* params,
     int memory_in_gb) {
-  int moderate_loaded_tab_count_per_gb = base::GetFieldTrialParamByFeatureAsInt(
-      features::kProactiveTabFreezeAndDiscard,
-      kProactiveTabFreezeAndDiscard_ModerateLoadedTabsPerGbRamParam,
-      kProactiveTabFreezeAndDiscard_ModerateLoadedTabsPerGbRamDefault);
+  int moderate_loaded_tab_count_per_gb =
+      ProactiveTabFreezeAndDiscardParams::kModerateLoadedTabsPerGbRam.Get();
 
   int moderate_level = moderate_loaded_tab_count_per_gb * memory_in_gb;
 
@@ -81,146 +78,63 @@
 
 }  // namespace
 
-const char kProactiveTabFreezeAndDiscardFeatureName[] =
-    "ProactiveTabFreezeAndDiscard";
+// Instantiate the feature parameters for proactive tab discarding.
+constexpr base::FeatureParam<bool>
+    ProactiveTabFreezeAndDiscardParams::kShouldProactivelyDiscard;
+constexpr base::FeatureParam<bool>
+    ProactiveTabFreezeAndDiscardParams::kShouldPeriodicallyUnfreeze;
+constexpr base::FeatureParam<bool> ProactiveTabFreezeAndDiscardParams::
+    kShouldProtectTabsSharingBrowsingInstance;
+constexpr base::FeatureParam<int>
+    ProactiveTabFreezeAndDiscardParams::kLowLoadedTabCount;
+constexpr base::FeatureParam<int>
+    ProactiveTabFreezeAndDiscardParams::kModerateLoadedTabsPerGbRam;
+constexpr base::FeatureParam<int>
+    ProactiveTabFreezeAndDiscardParams::kHighLoadedTabCount;
+constexpr base::FeatureParam<int>
+    ProactiveTabFreezeAndDiscardParams::kLowOccludedTimeout;
+constexpr base::FeatureParam<int>
+    ProactiveTabFreezeAndDiscardParams::kModerateOccludedTimeout;
+constexpr base::FeatureParam<int>
+    ProactiveTabFreezeAndDiscardParams::kHighOccludedTimeout;
+constexpr base::FeatureParam<int>
+    ProactiveTabFreezeAndDiscardParams::kFreezeTimeout;
+constexpr base::FeatureParam<int>
+    ProactiveTabFreezeAndDiscardParams::kUnfreezeTimeout;
+constexpr base::FeatureParam<int>
+    ProactiveTabFreezeAndDiscardParams::kRefreezeTimeout;
 
-// Field-trial parameter names for proactive tab discarding.
-const char kProactiveTabFreezeAndDiscard_ShouldProactivelyDiscardParam[] =
-    "ShouldProactivelyDiscard";
-const char kProactiveTabFreezeAndDiscard_ShouldPeriodicallyUnfreezeParam[] =
-    "ShouldPeriodicallyUnfreeze";
-const char
-    kProactiveTabFreezeAndDiscard_ShouldProtectTabsSharingBrowsingInstanceParam
-        [] = "ShouldProtectTabsSharingBrowsingInstance";
-const char kProactiveTabFreezeAndDiscard_LowLoadedTabCountParam[] =
-    "LowLoadedTabCount";
-const char kProactiveTabFreezeAndDiscard_ModerateLoadedTabsPerGbRamParam[] =
-    "ModerateLoadedTabsPerGbRam";
-const char kProactiveTabFreezeAndDiscard_HighLoadedTabCountParam[] =
-    "HighLoadedTabCount";
-const char kProactiveTabFreezeAndDiscard_LowOccludedTimeoutParam[] =
-    "LowOccludedTimeoutSeconds";
-const char kProactiveTabFreezeAndDiscard_ModerateOccludedTimeoutParam[] =
-    "ModerateOccludedTimeoutSeconds";
-const char kProactiveTabFreezeAndDiscard_HighOccludedTimeoutParam[] =
-    "HighOccludedTimeoutSeconds";
-const char kProactiveTabFreezeAndDiscard_FreezeTimeoutParam[] = "FreezeTimeout";
-const char kProactiveTabFreezeAndDiscard_UnfreezeTimeoutParam[] =
-    "UnfreezeTimeout";
-const char kProactiveTabFreezeAndDiscard_RefreezeTimeoutParam[] =
-    "RefreezeTimeout";
+// Instantiate the feature parameters for the site characteristics database.
+constexpr base::FeatureParam<int>
+    SiteCharacteristicsDatabaseParams::kFaviconUpdateObservationWindow;
+constexpr base::FeatureParam<int>
+    SiteCharacteristicsDatabaseParams::kTitleUpdateObservationWindow;
+constexpr base::FeatureParam<int>
+    SiteCharacteristicsDatabaseParams::kAudioUsageObservationWindow;
+constexpr base::FeatureParam<int>
+    SiteCharacteristicsDatabaseParams::kNotificationsUsageObservationWindow;
+constexpr base::FeatureParam<int>
+    SiteCharacteristicsDatabaseParams::kTitleOrFaviconChangeGracePeriod;
+constexpr base::FeatureParam<int>
+    SiteCharacteristicsDatabaseParams::kAudioUsageGracePeriod;
 
-// Field-trial parameter names for the site characteristics database.
-const char kSiteCharacteristicsDb_FaviconUpdateObservationWindow[] =
-    "FaviconUpdateObservationWindow";
-const char kSiteCharacteristicsDb_TitleUpdateObservationWindow[] =
-    "TitleUpdateObservationWindow";
-const char kSiteCharacteristicsDb_AudioUsageObservationWindow[] =
-    "AudioUsageObservationWindow";
-const char kSiteCharacteristicsDb_NotificationsUsageObservationWindow[] =
-    "NotificationsUsageObservationWindow";
-const char kSiteCharacteristicsDb_TitleOrFaviconChangeGracePeriod[] =
-    "TitleOrFaviconChangeGracePeriod";
-const char kSiteCharacteristicsDb_AudioUsageGracePeriod[] =
-    "AudioUsageGracePeriod";
-
-const char kInfiniteSessionRestore_MinSimultaneousTabLoads[] =
-    "MinSimultaneousTabLoads";
-const char kInfiniteSessionRestore_MaxSimultaneousTabLoads[] =
-    "MaxSimultaneousTabLoads";
-const char kInfiniteSessionRestore_CoresPerSimultaneousTabLoad[] =
-    "CoresPerSimultaneousTabLoad";
-const char kInfiniteSessionRestore_MinTabsToRestore[] = "MinTabsToRestore";
-const char kInfiniteSessionRestore_MaxTabsToRestore[] = "MaxTabsToRestore";
-const char kInfiniteSessionRestore_MbFreeMemoryPerTabToRestore[] =
-    "MbFreeMemoryPerTabToRestore";
-const char kInfiniteSessionRestore_MaxTimeSinceLastUseToRestore[] =
-    "MaxTimeSinceLastUseToRestore";
-const char kInfiniteSessionRestore_MinSiteEngagementToRestore[] =
-    "MinSiteEngagementToRestore";
-
-// Default values for ProactiveTabFreezeAndDiscardParams.
-const bool kProactiveTabFreezeAndDiscard_ShouldProactivelyDiscardDefault =
-    false;
-const bool kProactiveTabFreezeAndDiscard_ShouldPeriodicallyUnfreezeDefault =
-    false;
-const bool
-    kProactiveTabFreezeAndDiscard_ShouldProtectTabsSharingBrowsingInstanceDefault =
-        true;
-
-// 50% of people cap out at 4 tabs, so for them proactive discarding won't even
-// be invoked. See Tabs.MaxTabsInADay.
-// TODO(chrisha): This should eventually be informed by the number of tabs
-// typically used over a given time horizon (metric being developed).
-const uint32_t kProactiveTabFreezeAndDiscard_LowLoadedTabCountDefault = 4;
-// Testing in the lab shows that 2GB devices suffer beyond 6 tabs, and 4GB
-// devices suffer beyond about 12 tabs. As a very simple first step, we'll aim
-// at allowing 3 tabs per GB of RAM on a system before proactive discarding
-// kicks in. This is a system resource dependent max, which is combined with the
-// DefaultMaxLoadedTabCount to determine the max on a system.
-const uint32_t kProactiveTabFreezeAndDiscard_ModerateLoadedTabsPerGbRamDefault =
-    3;
-// 99.9% of people cap out with fewer than this number, so only 0.1% of the
-// population should ever encounter proactive discarding based on this cap.
-const uint32_t kProactiveTabFreezeAndDiscard_HighLoadedTabCountDefault = 100;
-// Current discarding uses 10 minutes as a minimum cap. This uses exponentially
-// increasing timeouts beyond that.
-const base::TimeDelta kProactiveTabFreezeAndDiscard_LowOccludedTimeoutDefault =
-    base::TimeDelta::FromHours(6);
-const base::TimeDelta
-    kProactiveTabFreezeAndDiscard_ModerateOccludedTimeoutDefault =
-        base::TimeDelta::FromHours(1);
-const base::TimeDelta kProactiveTabFreezeAndDiscard_HighOccludedTimeoutDefault =
-    base::TimeDelta::FromMinutes(10);
-const base::TimeDelta kProactiveTabFreezeAndDiscard_FreezeTimeoutDefault =
-    base::TimeDelta::FromMinutes(10);
-const base::TimeDelta kProactiveTabFreezeAndDiscard_UnfreezeTimeoutDefault =
-    base::TimeDelta::FromMinutes(15);
-const base::TimeDelta kProactiveTabFreezeAndDiscard_RefreezeTimeoutDefault =
-    base::TimeDelta::FromSeconds(10);
-
-// Default values for SiteCharacteristicsDatabaseParams.
-//
-// Observations windows have a default value of 2 hours, 95% of backgrounded
-// tabs don't use any of these features in this time window.
-const base::TimeDelta
-    kSiteCharacteristicsDb_FaviconUpdateObservationWindow_Default =
-        base::TimeDelta::FromHours(2);
-const base::TimeDelta
-    kSiteCharacteristicsDb_TitleUpdateObservationWindow_Default =
-        base::TimeDelta::FromHours(2);
-const base::TimeDelta
-    kSiteCharacteristicsDb_AudioUsageObservationWindow_Default =
-        base::TimeDelta::FromHours(2);
-const base::TimeDelta
-    kSiteCharacteristicsDb_NotificationsUsageObservationWindow_Default =
-        base::TimeDelta::FromHours(2);
-
-// TODO(sebmarchand): Get some real-world data and choose an appropriate value
-// here.
-const base::TimeDelta
-    kSiteCharacteristicsDb_TitleOrFaviconChangeGracePeriod_Default =
-        base::TimeDelta::FromSeconds(20);
-const base::TimeDelta kSiteCharacteristicsDb_AudioUsageGracePeriod_Default =
-    base::TimeDelta::FromSeconds(10);
-
-// Default values for infinite session restore feature. Many of these are taken
-// from thin air, but others are motivated by existing metrics.
-const uint32_t kInfiniteSessionRestore_MinSimultaneousTabLoadsDefault = 1;
-const uint32_t kInfiniteSessionRestore_MaxSimultaneousTabLoadsDefault = 4;
-const uint32_t kInfiniteSessionRestore_CoresPerSimultaneousTabLoadDefault = 2;
-const uint32_t kInfiniteSessionRestore_MinTabsToRestoreDefault = 4;
-const uint32_t kInfiniteSessionRestore_MaxTabsToRestoreDefault = 20;
-// This is the 75th percentile of Memory.Renderer.PrivateMemoryFootprint.
-const uint32_t kInfiniteSessionRestore_MbFreeMemoryPerTabToRestoreDefault = 150;
-// This is the 75th percentile of SessionRestore.RestoredTab.TimeSinceActive.
-const base::TimeDelta
-    kInfiniteSessionRestore_MaxTimeSinceLastUseToRestoreDefault =
-        base::TimeDelta::FromHours(6);
-// Taken from an informal survey of Googlers on min engagement of things they
-// think *must* load. Note that about 25% of session-restore tabs fall above
-// this threshold (see SessionRestore.RestoredTab.SiteEngagementScore).
-const uint32_t kInfiniteSessionRestore_MinSiteEngagementToRestoreDefault = 15;
+// Instantiate the feature parameters for infinite session restore.
+constexpr base::FeatureParam<int>
+    InfiniteSessionRestoreParams::kMinSimultaneousTabLoads;
+constexpr base::FeatureParam<int>
+    InfiniteSessionRestoreParams::kMaxSimultaneousTabLoads;
+constexpr base::FeatureParam<int>
+    InfiniteSessionRestoreParams::kCoresPerSimultaneousTabLoad;
+constexpr base::FeatureParam<int>
+    InfiniteSessionRestoreParams::kMinTabsToRestore;
+constexpr base::FeatureParam<int>
+    InfiniteSessionRestoreParams::kMaxTabsToRestore;
+constexpr base::FeatureParam<int>
+    InfiniteSessionRestoreParams::kMbFreeMemoryPerTabToRestore;
+constexpr base::FeatureParam<int>
+    InfiniteSessionRestoreParams::kMaxTimeSinceLastUseToRestore;
+constexpr base::FeatureParam<int>
+    InfiniteSessionRestoreParams::kMinSiteEngagementToRestore;
 
 ProactiveTabFreezeAndDiscardParams::ProactiveTabFreezeAndDiscardParams() =
     default;
@@ -240,74 +154,44 @@
     int memory_in_gb) {
   ProactiveTabFreezeAndDiscardParams params = {};
 
-  params.should_proactively_discard = base::GetFieldTrialParamByFeatureAsBool(
-      features::kProactiveTabFreezeAndDiscard,
-      kProactiveTabFreezeAndDiscard_ShouldProactivelyDiscardParam,
-      kProactiveTabFreezeAndDiscard_ShouldProactivelyDiscardDefault);
+  params.should_proactively_discard =
+      ProactiveTabFreezeAndDiscardParams::kShouldProactivelyDiscard.Get();
 
-  params.should_periodically_unfreeze = base::GetFieldTrialParamByFeatureAsBool(
-      features::kProactiveTabFreezeAndDiscard,
-      kProactiveTabFreezeAndDiscard_ShouldPeriodicallyUnfreezeParam,
-      kProactiveTabFreezeAndDiscard_ShouldPeriodicallyUnfreezeDefault);
+  params.should_periodically_unfreeze =
+      ProactiveTabFreezeAndDiscardParams::kShouldPeriodicallyUnfreeze.Get();
 
   params.should_protect_tabs_sharing_browsing_instance =
-      base::GetFieldTrialParamByFeatureAsBool(
-          features::kProactiveTabFreezeAndDiscard,
-          kProactiveTabFreezeAndDiscard_ShouldProtectTabsSharingBrowsingInstanceParam,
-          kProactiveTabFreezeAndDiscard_ShouldProtectTabsSharingBrowsingInstanceDefault);
+      ProactiveTabFreezeAndDiscardParams::
+          kShouldProtectTabsSharingBrowsingInstance.Get();
 
-  params.low_loaded_tab_count = base::GetFieldTrialParamByFeatureAsInt(
-      features::kProactiveTabFreezeAndDiscard,
-      kProactiveTabFreezeAndDiscard_LowLoadedTabCountParam,
-      kProactiveTabFreezeAndDiscard_LowLoadedTabCountDefault);
+  params.low_loaded_tab_count =
+      ProactiveTabFreezeAndDiscardParams::kLowLoadedTabCount.Get();
 
-  params.high_loaded_tab_count = base::GetFieldTrialParamByFeatureAsInt(
-      features::kProactiveTabFreezeAndDiscard,
-      kProactiveTabFreezeAndDiscard_HighLoadedTabCountParam,
-      kProactiveTabFreezeAndDiscard_HighLoadedTabCountDefault);
+  params.high_loaded_tab_count =
+      ProactiveTabFreezeAndDiscardParams::kHighLoadedTabCount.Get();
 
   // |moderate_loaded_tab_count| determined after |high_loaded_tab_count| so it
   // can be enforced that it is lower than |high_loaded_tab_count|.
   params.moderate_loaded_tab_count =
       GetModerateThresholdTabCountBasedOnSystemMemory(&params, memory_in_gb);
 
-  params.low_occluded_timeout =
-      base::TimeDelta::FromSeconds(base::GetFieldTrialParamByFeatureAsInt(
-          features::kProactiveTabFreezeAndDiscard,
-          kProactiveTabFreezeAndDiscard_LowOccludedTimeoutParam,
-          kProactiveTabFreezeAndDiscard_LowOccludedTimeoutDefault.InSeconds()));
+  params.low_occluded_timeout = base::TimeDelta::FromSeconds(
+      ProactiveTabFreezeAndDiscardParams::kLowOccludedTimeout.Get());
 
-  params.moderate_occluded_timeout =
-      base::TimeDelta::FromSeconds(base::GetFieldTrialParamByFeatureAsInt(
-          features::kProactiveTabFreezeAndDiscard,
-          kProactiveTabFreezeAndDiscard_ModerateOccludedTimeoutParam,
-          kProactiveTabFreezeAndDiscard_ModerateOccludedTimeoutDefault
-              .InSeconds()));
+  params.moderate_occluded_timeout = base::TimeDelta::FromSeconds(
+      ProactiveTabFreezeAndDiscardParams::kModerateOccludedTimeout.Get());
 
-  params.high_occluded_timeout =
-      base::TimeDelta::FromSeconds(base::GetFieldTrialParamByFeatureAsInt(
-          features::kProactiveTabFreezeAndDiscard,
-          kProactiveTabFreezeAndDiscard_HighOccludedTimeoutParam,
-          kProactiveTabFreezeAndDiscard_HighOccludedTimeoutDefault
-              .InSeconds()));
+  params.high_occluded_timeout = base::TimeDelta::FromSeconds(
+      ProactiveTabFreezeAndDiscardParams::kHighOccludedTimeout.Get());
 
-  params.freeze_timeout =
-      base::TimeDelta::FromSeconds(base::GetFieldTrialParamByFeatureAsInt(
-          features::kProactiveTabFreezeAndDiscard,
-          kProactiveTabFreezeAndDiscard_FreezeTimeoutParam,
-          kProactiveTabFreezeAndDiscard_FreezeTimeoutDefault.InSeconds()));
+  params.freeze_timeout = base::TimeDelta::FromSeconds(
+      ProactiveTabFreezeAndDiscardParams::kFreezeTimeout.Get());
 
-  params.unfreeze_timeout =
-      base::TimeDelta::FromSeconds(base::GetFieldTrialParamByFeatureAsInt(
-          features::kProactiveTabFreezeAndDiscard,
-          kProactiveTabFreezeAndDiscard_UnfreezeTimeoutParam,
-          kProactiveTabFreezeAndDiscard_UnfreezeTimeoutDefault.InSeconds()));
+  params.unfreeze_timeout = base::TimeDelta::FromSeconds(
+      ProactiveTabFreezeAndDiscardParams::kUnfreezeTimeout.Get());
 
-  params.refreeze_timeout =
-      base::TimeDelta::FromSeconds(base::GetFieldTrialParamByFeatureAsInt(
-          features::kProactiveTabFreezeAndDiscard,
-          kProactiveTabFreezeAndDiscard_RefreezeTimeoutParam,
-          kProactiveTabFreezeAndDiscard_RefreezeTimeoutDefault.InSeconds()));
+  params.refreeze_timeout = base::TimeDelta::FromSeconds(
+      ProactiveTabFreezeAndDiscardParams::kRefreezeTimeout.Get());
 
   return params;
 }
@@ -339,46 +223,25 @@
 SiteCharacteristicsDatabaseParams GetSiteCharacteristicsDatabaseParams() {
   SiteCharacteristicsDatabaseParams params = {};
 
-  params.favicon_update_observation_window =
-      base::TimeDelta::FromSeconds(base::GetFieldTrialParamByFeatureAsInt(
-          features::kSiteCharacteristicsDatabase,
-          kSiteCharacteristicsDb_FaviconUpdateObservationWindow,
-          kSiteCharacteristicsDb_FaviconUpdateObservationWindow_Default
-              .InSeconds()));
+  params.favicon_update_observation_window = base::TimeDelta::FromSeconds(
+      SiteCharacteristicsDatabaseParams::kFaviconUpdateObservationWindow.Get());
 
-  params.title_update_observation_window =
-      base::TimeDelta::FromSeconds(base::GetFieldTrialParamByFeatureAsInt(
-          features::kSiteCharacteristicsDatabase,
-          kSiteCharacteristicsDb_TitleUpdateObservationWindow,
-          kSiteCharacteristicsDb_TitleUpdateObservationWindow_Default
-              .InSeconds()));
+  params.title_update_observation_window = base::TimeDelta::FromSeconds(
+      SiteCharacteristicsDatabaseParams::kTitleUpdateObservationWindow.Get());
 
-  params.audio_usage_observation_window =
-      base::TimeDelta::FromSeconds(base::GetFieldTrialParamByFeatureAsInt(
-          features::kSiteCharacteristicsDatabase,
-          kSiteCharacteristicsDb_AudioUsageObservationWindow,
-          kSiteCharacteristicsDb_AudioUsageObservationWindow_Default
-              .InSeconds()));
+  params.audio_usage_observation_window = base::TimeDelta::FromSeconds(
+      SiteCharacteristicsDatabaseParams::kAudioUsageObservationWindow.Get());
 
-  params.notifications_usage_observation_window =
-      base::TimeDelta::FromSeconds(base::GetFieldTrialParamByFeatureAsInt(
-          features::kSiteCharacteristicsDatabase,
-          kSiteCharacteristicsDb_NotificationsUsageObservationWindow,
-          kSiteCharacteristicsDb_NotificationsUsageObservationWindow_Default
-              .InSeconds()));
+  params.notifications_usage_observation_window = base::TimeDelta::FromSeconds(
+      SiteCharacteristicsDatabaseParams::kNotificationsUsageObservationWindow
+          .Get());
 
-  params.title_or_favicon_change_grace_period =
-      base::TimeDelta::FromSeconds(base::GetFieldTrialParamByFeatureAsInt(
-          features::kSiteCharacteristicsDatabase,
-          kSiteCharacteristicsDb_TitleOrFaviconChangeGracePeriod,
-          kSiteCharacteristicsDb_TitleOrFaviconChangeGracePeriod_Default
-              .InSeconds()));
+  params.title_or_favicon_change_grace_period = base::TimeDelta::FromSeconds(
+      SiteCharacteristicsDatabaseParams::kTitleOrFaviconChangeGracePeriod
+          .Get());
 
-  params.audio_usage_grace_period =
-      base::TimeDelta::FromSeconds(base::GetFieldTrialParamByFeatureAsInt(
-          features::kSiteCharacteristicsDatabase,
-          kSiteCharacteristicsDb_AudioUsageGracePeriod,
-          kSiteCharacteristicsDb_AudioUsageGracePeriod_Default.InSeconds()));
+  params.audio_usage_grace_period = base::TimeDelta::FromSeconds(
+      SiteCharacteristicsDatabaseParams::kAudioUsageGracePeriod.Get());
 
   return params;
 }
@@ -393,43 +256,22 @@
 InfiniteSessionRestoreParams GetInfiniteSessionRestoreParams() {
   InfiniteSessionRestoreParams params = {};
 
-  params.min_simultaneous_tab_loads = base::GetFieldTrialParamByFeatureAsInt(
-      features::kInfiniteSessionRestore,
-      kInfiniteSessionRestore_MinSimultaneousTabLoads,
-      kInfiniteSessionRestore_MinSimultaneousTabLoadsDefault);
-  params.max_simultaneous_tab_loads = base::GetFieldTrialParamByFeatureAsInt(
-      features::kInfiniteSessionRestore,
-      kInfiniteSessionRestore_MaxSimultaneousTabLoads,
-      kInfiniteSessionRestore_MaxSimultaneousTabLoadsDefault);
+  params.min_simultaneous_tab_loads =
+      InfiniteSessionRestoreParams::kMinSimultaneousTabLoads.Get();
+  params.max_simultaneous_tab_loads =
+      InfiniteSessionRestoreParams::kMaxSimultaneousTabLoads.Get();
   params.cores_per_simultaneous_tab_load =
-      base::GetFieldTrialParamByFeatureAsInt(
-          features::kInfiniteSessionRestore,
-          kInfiniteSessionRestore_CoresPerSimultaneousTabLoad,
-          kInfiniteSessionRestore_CoresPerSimultaneousTabLoadDefault);
-  params.min_tabs_to_restore = base::GetFieldTrialParamByFeatureAsInt(
-      features::kInfiniteSessionRestore,
-      kInfiniteSessionRestore_MinTabsToRestore,
-      kInfiniteSessionRestore_MinTabsToRestoreDefault);
-  params.max_tabs_to_restore = base::GetFieldTrialParamByFeatureAsInt(
-      features::kInfiniteSessionRestore,
-      kInfiniteSessionRestore_MaxTabsToRestore,
-      kInfiniteSessionRestore_MaxTabsToRestoreDefault);
+      InfiniteSessionRestoreParams::kCoresPerSimultaneousTabLoad.Get();
+  params.min_tabs_to_restore =
+      InfiniteSessionRestoreParams::kMinTabsToRestore.Get();
+  params.max_tabs_to_restore =
+      InfiniteSessionRestoreParams::kMaxTabsToRestore.Get();
   params.mb_free_memory_per_tab_to_restore =
-      base::GetFieldTrialParamByFeatureAsInt(
-          features::kInfiniteSessionRestore,
-          kInfiniteSessionRestore_MbFreeMemoryPerTabToRestore,
-          kInfiniteSessionRestore_MbFreeMemoryPerTabToRestoreDefault);
-  params.max_time_since_last_use_to_restore =
-      base::TimeDelta::FromSeconds(base::GetFieldTrialParamByFeatureAsInt(
-          features::kInfiniteSessionRestore,
-          kInfiniteSessionRestore_MaxTimeSinceLastUseToRestore,
-          kInfiniteSessionRestore_MaxTimeSinceLastUseToRestoreDefault
-              .InSeconds()));
+      InfiniteSessionRestoreParams::kMbFreeMemoryPerTabToRestore.Get();
+  params.max_time_since_last_use_to_restore = base::TimeDelta::FromSeconds(
+      InfiniteSessionRestoreParams::kMaxTimeSinceLastUseToRestore.Get());
   params.min_site_engagement_to_restore =
-      base::GetFieldTrialParamByFeatureAsInt(
-          features::kInfiniteSessionRestore,
-          kInfiniteSessionRestore_MinSiteEngagementToRestore,
-          kInfiniteSessionRestore_MinSiteEngagementToRestoreDefault);
+      InfiniteSessionRestoreParams::kMinSiteEngagementToRestore.Get();
 
   return params;
 }
diff --git a/chrome/browser/resource_coordinator/tab_manager_features.h b/chrome/browser/resource_coordinator/tab_manager_features.h
index 415be41..207e1a8 100644
--- a/chrome/browser/resource_coordinator/tab_manager_features.h
+++ b/chrome/browser/resource_coordinator/tab_manager_features.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_RESOURCE_COORDINATOR_TAB_MANAGER_FEATURES_H_
 
 #include "base/feature_list.h"
+#include "base/metrics/field_trial_params.h"
 #include "base/no_destructor.h"
 #include "base/sys_info.h"
 #include "base/time/time.h"
@@ -24,101 +25,6 @@
 
 namespace resource_coordinator {
 
-// The name of the ProactiveTabFreezeAndDiscard feature.
-extern const char kProactiveTabFreezeAndDiscardFeatureName[];
-
-// Variations parameter names related to proactive discarding.
-// See ProactiveTabFreezeAndDiscardsParams for details.
-//
-// TODO(sebmarchand): Use the base::FeatureParam API here.
-extern const char kProactiveTabFreezeAndDiscard_ShouldProactivelyDiscardParam[];
-extern const char
-    kProactiveTabFreezeAndDiscard_ShouldPeriodicallyUnfreezeParam[];
-extern const char
-    kProactiveTabFreezeAndDiscard_ShouldProtectTabsSharingBrowsingInstanceParam
-        [];
-extern const char kProactiveTabFreezeAndDiscard_LowLoadedTabCountParam[];
-extern const char
-    kProactiveTabFreezeAndDiscard_ModerateLoadedTabsPerGbRamParam[];
-extern const char kProactiveTabFreezeAndDiscard_HighLoadedTabCountParam[];
-extern const char kProactiveTabFreezeAndDiscard_LowOccludedTimeoutParam[];
-extern const char kProactiveTabFreezeAndDiscard_ModerateOccludedTimeoutParam[];
-extern const char kProactiveTabFreezeAndDiscard_HighOccludedTimeoutParam[];
-extern const char kProactiveTabFreezeAndDiscard_FreezeTimeoutParam[];
-extern const char kProactiveTabFreezeAndDiscard_UnfreezeTimeoutParam[];
-extern const char kProactiveTabFreezeAndDiscard_RefreezeTimeoutParam[];
-
-// Variations parameter names related to the site characteristics database.
-// See ProactiveTabFreezeAndDiscardsParams for details.
-extern const char kSiteCharacteristicsDb_FaviconUpdateObservationWindow[];
-extern const char kSiteCharacteristicsDb_TitleUpdateObservationWindow[];
-extern const char kSiteCharacteristicsDb_AudioUsageObservationWindow[];
-extern const char kSiteCharacteristicsDb_NotificationsUsageObservationWindow[];
-extern const char kSiteCharacteristicsDb_TitleOrFaviconChangeGracePeriod[];
-extern const char kSiteCharacteristicsDb_AudioUsageGracePeriod[];
-
-// Variation parameter names related to infinite session restore.
-extern const char kInfiniteSessionRestore_MinSimultaneousTabLoads[];
-extern const char kInfiniteSessionRestore_MaxSimultaneousTabLoads[];
-extern const char kInfiniteSessionRestore_CoresPerSimultaneousTabLoad[];
-extern const char kInfiniteSessionRestore_MinTabsToRestore[];
-extern const char kInfiniteSessionRestore_MaxTabsToRestore[];
-extern const char kInfiniteSessionRestore_MbFreeMemoryPerTabToRestore[];
-// This is expressed in seconds.
-extern const char kInfiniteSessionRestore_MaxTimeSinceLastUseToRestore[];
-extern const char kInfiniteSessionRestore_MinSiteEngagementToRestore[];
-
-// Default values of parameters related to the site characteristics database.
-// See ProactiveTabFreezeAndDiscardsParams for details.
-extern const bool kProactiveTabFreezeAndDiscard_ShouldProactivelyDiscardDefault;
-extern const bool
-    kProactiveTabFreezeAndDiscard_ShouldPeriodicallyUnfreezeDefault;
-extern const bool
-    kProactiveTabFreezeAndDiscard_ShouldProtectTabsSharingBrowsingInstanceDefault;
-extern const uint32_t kProactiveTabFreezeAndDiscard_LowLoadedTabCountDefault;
-extern const uint32_t
-    kProactiveTabFreezeAndDiscard_ModerateLoadedTabsPerGbRamDefault;
-extern const uint32_t kProactiveTabFreezeAndDiscard_HighLoadedTabCountDefault;
-extern const base::TimeDelta
-    kProactiveTabFreezeAndDiscard_LowOccludedTimeoutDefault;
-extern const base::TimeDelta
-    kProactiveTabFreezeAndDiscard_ModerateOccludedTimeoutDefault;
-extern const base::TimeDelta
-    kProactiveTabFreezeAndDiscard_HighOccludedTimeoutDefault;
-extern const base::TimeDelta kProactiveTabFreezeAndDiscard_FreezeTimeoutDefault;
-extern const base::TimeDelta
-    kProactiveTabFreezeAndDiscard_UnfreezeTimeoutDefault;
-extern const base::TimeDelta
-    kProactiveTabFreezeAndDiscard_RefreezeTimeoutDefault;
-
-// Default values of parameters related to the site characteristics database.
-// See SiteCharacteristicsDatabaseParams for details.
-extern const base::TimeDelta
-    kSiteCharacteristicsDb_FaviconUpdateObservationWindow_Default;
-extern const base::TimeDelta
-    kSiteCharacteristicsDb_TitleUpdateObservationWindow_Default;
-extern const base::TimeDelta
-    kSiteCharacteristicsDb_AudioUsageObservationWindow_Default;
-extern const base::TimeDelta
-    kSiteCharacteristicsDb_NotificationsUsageObservationWindow_Default;
-extern const base::TimeDelta
-    kSiteCharacteristicsDb_TitleOrFaviconChangeGracePeriod_Default;
-extern const base::TimeDelta
-    kSiteCharacteristicsDb_AudioUsageGracePeriod_Default;
-
-// Default values for infinite session restore feature.
-extern const uint32_t kInfiniteSessionRestore_MinSimultaneousTabLoadsDefault;
-extern const uint32_t kInfiniteSessionRestore_MaxSimultaneousTabLoadsDefault;
-extern const uint32_t
-    kInfiniteSessionRestore_CoresPerSimultaneousTabLoadDefault;
-extern const uint32_t kInfiniteSessionRestore_MinTabsToRestoreDefault;
-extern const uint32_t kInfiniteSessionRestore_MaxTabsToRestoreDefault;
-extern const uint32_t
-    kInfiniteSessionRestore_MbFreeMemoryPerTabToRestoreDefault;
-extern const base::TimeDelta
-    kInfiniteSessionRestore_MaxTimeSinceLastUseToRestoreDefault;
-extern const uint32_t kInfiniteSessionRestore_MinSiteEngagementToRestoreDefault;
-
 // Parameters used by the proactive tab discarding feature.
 //
 // Proactive discarding has 5 key parameters:
@@ -159,6 +65,59 @@
   ProactiveTabFreezeAndDiscardParams(
       const ProactiveTabFreezeAndDiscardParams& rhs);
 
+  // Static definition of the different parameters that can be used by this
+  // feature.
+
+  static constexpr base::FeatureParam<bool> kShouldProactivelyDiscard{
+      &features::kProactiveTabFreezeAndDiscard, "ShouldProactivelyDiscard",
+      false};
+  static constexpr base::FeatureParam<bool> kShouldPeriodicallyUnfreeze{
+      &features::kProactiveTabFreezeAndDiscard, "ShouldPeriodicallyUnfreeze",
+      false};
+  static constexpr base::FeatureParam<bool>
+      kShouldProtectTabsSharingBrowsingInstance{
+          &features::kProactiveTabFreezeAndDiscard,
+          "ShouldProtectTabsSharingBrowsingInstance", true};
+  // 50% of people cap out at 4 tabs, so for them proactive discarding won't
+  // even be invoked. See Tabs.MaxTabsInADay.
+  // TODO(chrisha): This should eventually be informed by the number of tabs
+  // typically used over a given time horizon (metric being developed).
+  static constexpr base::FeatureParam<int> kLowLoadedTabCount{
+      &features::kProactiveTabFreezeAndDiscard, "LowLoadedTabCount", 4};
+  // Testing in the lab shows that 2GB devices suffer beyond 6 tabs, and 4GB
+  // devices suffer beyond about 12 tabs. As a very simple first step, we'll aim
+  // at allowing 3 tabs per GB of RAM on a system before proactive discarding
+  // kicks in. This is a system resource dependent max, which is combined with
+  // the DefaultMaxLoadedTabCount to determine the max on a system.
+  static constexpr base::FeatureParam<int> kModerateLoadedTabsPerGbRam{
+      &features::kProactiveTabFreezeAndDiscard, "ModerateLoadedTabsPerGbRam",
+      3};
+  // 99.9% of people cap out with fewer than this number, so only 0.1% of the
+  // population should ever encounter proactive discarding based on this cap.
+  static constexpr base::FeatureParam<int> kHighLoadedTabCount{
+      &features::kProactiveTabFreezeAndDiscard, "HighLoadedTabCount", 100};
+  // Current discarding uses 10 minutes as a minimum cap. This uses
+  // exponentially increasing timeouts beyond that.
+  static constexpr base::FeatureParam<int> kLowOccludedTimeout{
+      &features::kProactiveTabFreezeAndDiscard, "LowOccludedTimeoutSeconds",
+      base::TimeDelta::FromHours(6).InSeconds()};
+  static constexpr base::FeatureParam<int> kModerateOccludedTimeout{
+      &features::kProactiveTabFreezeAndDiscard,
+      "ModerateOccludedTimeoutSeconds",
+      base::TimeDelta::FromHours(1).InSeconds()};
+  static constexpr base::FeatureParam<int> kHighOccludedTimeout{
+      &features::kProactiveTabFreezeAndDiscard, "HighOccludedTimeoutSeconds",
+      static_cast<int>(base::TimeDelta::FromMinutes(10).InSeconds())};
+  static constexpr base::FeatureParam<int> kFreezeTimeout{
+      &features::kProactiveTabFreezeAndDiscard, "FreezeTimeout",
+      base::TimeDelta::FromMinutes(10).InSeconds()};
+  static constexpr base::FeatureParam<int> kUnfreezeTimeout{
+      &features::kProactiveTabFreezeAndDiscard, "UnfreezeTimeout",
+      base::TimeDelta::FromMinutes(15).InSeconds()};
+  static constexpr base::FeatureParam<int> kRefreezeTimeout{
+      &features::kProactiveTabFreezeAndDiscard, "RefreezeTimeout",
+      base::TimeDelta::FromMinutes(10).InSeconds()};
+
   // Whether tabs should be proactively discarded. When the
   // |kProactiveTabFreezeAndDiscard| feature is enabled and this is false, only
   // proactive tab freezing happens.
@@ -214,6 +173,31 @@
   SiteCharacteristicsDatabaseParams(
       const SiteCharacteristicsDatabaseParams& rhs);
 
+  // Static definition of the different parameters that can be used by this
+  // feature.
+
+  // Observations windows have a default value of 2 hours, 95% of backgrounded
+  // tabs don't use any of these features in this time window.
+  static constexpr base::FeatureParam<int> kFaviconUpdateObservationWindow{
+      &features::kSiteCharacteristicsDatabase, "FaviconUpdateObservationWindow",
+      base::TimeDelta::FromHours(2).InSeconds()};
+  static constexpr base::FeatureParam<int> kTitleUpdateObservationWindow{
+      &features::kSiteCharacteristicsDatabase, "TitleUpdateObservationWindow",
+      base::TimeDelta::FromHours(2).InSeconds()};
+  static constexpr base::FeatureParam<int> kAudioUsageObservationWindow{
+      &features::kSiteCharacteristicsDatabase, "AudioUsageObservationWindow",
+      base::TimeDelta::FromHours(2).InSeconds()};
+  static constexpr base::FeatureParam<int> kNotificationsUsageObservationWindow{
+      &features::kSiteCharacteristicsDatabase,
+      "NotificationsUsageObservationWindow",
+      base::TimeDelta::FromHours(2).InSeconds()};
+  static constexpr base::FeatureParam<int> kTitleOrFaviconChangeGracePeriod{
+      &features::kSiteCharacteristicsDatabase,
+      "TitleOrFaviconChangeGracePeriod", 20 /* 20 seconds */};
+  static constexpr base::FeatureParam<int> kAudioUsageGracePeriod{
+      &features::kSiteCharacteristicsDatabase, "AudioUsageGracePeriod",
+      10 /* 10 seconds */};
+
   // Minimum observation window before considering that this website doesn't
   // update its favicon while in background.
   base::TimeDelta favicon_update_observation_window;
@@ -243,6 +227,32 @@
   InfiniteSessionRestoreParams();
   InfiniteSessionRestoreParams(const InfiniteSessionRestoreParams& rhs);
 
+  // Static definition of the different parameters that can be used by this
+  // feature.
+
+  static constexpr base::FeatureParam<int> kMinSimultaneousTabLoads{
+      &features::kInfiniteSessionRestore, "MinSimultaneousTabLoads", 1};
+  static constexpr base::FeatureParam<int> kMaxSimultaneousTabLoads{
+      &features::kInfiniteSessionRestore, "MaxSimultaneousTabLoads", 4};
+  static constexpr base::FeatureParam<int> kCoresPerSimultaneousTabLoad{
+      &features::kInfiniteSessionRestore, "CoresPerSimultaneousTabLoad", 2};
+  static constexpr base::FeatureParam<int> kMinTabsToRestore{
+      &features::kInfiniteSessionRestore, "MinTabsToRestore", 4};
+  static constexpr base::FeatureParam<int> kMaxTabsToRestore{
+      &features::kInfiniteSessionRestore, "MaxTabsToRestore", 20};
+  // This is the 75th percentile of Memory.Renderer.PrivateMemoryFootprint.
+  static constexpr base::FeatureParam<int> kMbFreeMemoryPerTabToRestore{
+      &features::kInfiniteSessionRestore, "MbFreeMemoryPerTabToRestore", 150};
+  // This is the 75th percentile of SessionRestore.RestoredTab.TimeSinceActive.
+  static constexpr base::FeatureParam<int> kMaxTimeSinceLastUseToRestore{
+      &features::kInfiniteSessionRestore, "MaxTimeSinceLastUseToRestore",
+      base::TimeDelta::FromHours(6).InSeconds()};
+  // Taken from an informal survey of Googlers on min engagement of things they
+  // think *must* load. Note that about 25% of session-restore tabs fall above
+  // this threshold (see SessionRestore.RestoredTab.SiteEngagementScore).
+  static constexpr base::FeatureParam<int> kMinSiteEngagementToRestore{
+      &features::kInfiniteSessionRestore, "MinSiteEngagementToRestore", 15};
+
   // Parameters directly retrieved from the experiment configuration.
 
   // The minimum number of tabs to ever load simultaneously. This can be
diff --git a/chrome/browser/resource_coordinator/tab_manager_features_unittest.cc b/chrome/browser/resource_coordinator/tab_manager_features_unittest.cc
index c41dcf0..aa7af5c 100644
--- a/chrome/browser/resource_coordinator/tab_manager_features_unittest.cc
+++ b/chrome/browser/resource_coordinator/tab_manager_features_unittest.cc
@@ -137,41 +137,73 @@
   void ExpectDefaultProactiveTabFreezeAndDiscardParams() {
     int memory_in_gb = 4;
     ExpectProactiveTabFreezeAndDiscardParams(
-        kProactiveTabFreezeAndDiscard_ShouldProactivelyDiscardDefault,
-        kProactiveTabFreezeAndDiscard_ShouldPeriodicallyUnfreezeDefault,
-        kProactiveTabFreezeAndDiscard_ShouldProtectTabsSharingBrowsingInstanceDefault,
-        kProactiveTabFreezeAndDiscard_LowLoadedTabCountDefault,
-        kProactiveTabFreezeAndDiscard_ModerateLoadedTabsPerGbRamDefault *
+        ProactiveTabFreezeAndDiscardParams::kShouldProactivelyDiscard
+            .default_value,
+        ProactiveTabFreezeAndDiscardParams::kShouldPeriodicallyUnfreeze
+            .default_value,
+        ProactiveTabFreezeAndDiscardParams::
+            kShouldProtectTabsSharingBrowsingInstance.default_value,
+        ProactiveTabFreezeAndDiscardParams::kLowLoadedTabCount.default_value,
+        ProactiveTabFreezeAndDiscardParams::kModerateLoadedTabsPerGbRam
+                .default_value *
             memory_in_gb,
-        kProactiveTabFreezeAndDiscard_HighLoadedTabCountDefault, memory_in_gb,
-        kProactiveTabFreezeAndDiscard_LowOccludedTimeoutDefault,
-        kProactiveTabFreezeAndDiscard_ModerateOccludedTimeoutDefault,
-        kProactiveTabFreezeAndDiscard_HighOccludedTimeoutDefault,
-        kProactiveTabFreezeAndDiscard_FreezeTimeoutDefault,
-        kProactiveTabFreezeAndDiscard_UnfreezeTimeoutDefault,
-        kProactiveTabFreezeAndDiscard_RefreezeTimeoutDefault);
+        ProactiveTabFreezeAndDiscardParams::kHighLoadedTabCount.default_value,
+        memory_in_gb,
+        base::TimeDelta::FromSeconds(
+            ProactiveTabFreezeAndDiscardParams::kLowOccludedTimeout
+                .default_value),
+        base::TimeDelta::FromSeconds(
+            ProactiveTabFreezeAndDiscardParams::kModerateOccludedTimeout
+                .default_value),
+        base::TimeDelta::FromSeconds(
+            ProactiveTabFreezeAndDiscardParams::kHighOccludedTimeout
+                .default_value),
+        base::TimeDelta::FromSeconds(
+            ProactiveTabFreezeAndDiscardParams::kFreezeTimeout.default_value),
+        base::TimeDelta::FromSeconds(
+            ProactiveTabFreezeAndDiscardParams::kUnfreezeTimeout.default_value),
+        base::TimeDelta::FromSeconds(
+            ProactiveTabFreezeAndDiscardParams::kRefreezeTimeout
+                .default_value));
   }
 
   void ExpectDefaultSiteCharacteristicsDatabaseParams() {
     ExpectSiteCharacteristicsDatabaseParams(
-        kSiteCharacteristicsDb_FaviconUpdateObservationWindow_Default,
-        kSiteCharacteristicsDb_TitleUpdateObservationWindow_Default,
-        kSiteCharacteristicsDb_AudioUsageObservationWindow_Default,
-        kSiteCharacteristicsDb_NotificationsUsageObservationWindow_Default,
-        kSiteCharacteristicsDb_TitleOrFaviconChangeGracePeriod_Default,
-        kSiteCharacteristicsDb_AudioUsageGracePeriod_Default);
+        base::TimeDelta::FromSeconds(
+            SiteCharacteristicsDatabaseParams::kFaviconUpdateObservationWindow
+                .default_value),
+        base::TimeDelta::FromSeconds(
+            SiteCharacteristicsDatabaseParams::kTitleUpdateObservationWindow
+                .default_value),
+        base::TimeDelta::FromSeconds(
+            SiteCharacteristicsDatabaseParams::kAudioUsageObservationWindow
+                .default_value),
+        base::TimeDelta::FromSeconds(
+            SiteCharacteristicsDatabaseParams::
+                kNotificationsUsageObservationWindow.default_value),
+        base::TimeDelta::FromSeconds(
+            SiteCharacteristicsDatabaseParams::kTitleOrFaviconChangeGracePeriod
+                .default_value),
+        base::TimeDelta::FromSeconds(
+            SiteCharacteristicsDatabaseParams::kAudioUsageGracePeriod
+                .default_value));
   }
 
   void ExpectDefaultInfiniteSessionRestoreParams() {
     ExpectInfiniteSessionRestoreParams(
-        kInfiniteSessionRestore_MinSimultaneousTabLoadsDefault,
-        kInfiniteSessionRestore_MaxSimultaneousTabLoadsDefault,
-        kInfiniteSessionRestore_CoresPerSimultaneousTabLoadDefault,
-        kInfiniteSessionRestore_MinTabsToRestoreDefault,
-        kInfiniteSessionRestore_MaxTabsToRestoreDefault,
-        kInfiniteSessionRestore_MbFreeMemoryPerTabToRestoreDefault,
-        kInfiniteSessionRestore_MaxTimeSinceLastUseToRestoreDefault,
-        kInfiniteSessionRestore_MinSiteEngagementToRestoreDefault);
+        InfiniteSessionRestoreParams::kMinSimultaneousTabLoads.default_value,
+        InfiniteSessionRestoreParams::kMaxSimultaneousTabLoads.default_value,
+        InfiniteSessionRestoreParams::kCoresPerSimultaneousTabLoad
+            .default_value,
+        InfiniteSessionRestoreParams::kMinTabsToRestore.default_value,
+        InfiniteSessionRestoreParams::kMaxTabsToRestore.default_value,
+        InfiniteSessionRestoreParams::kMbFreeMemoryPerTabToRestore
+            .default_value,
+        base::TimeDelta::FromSeconds(
+            InfiniteSessionRestoreParams::kMaxTimeSinceLastUseToRestore
+                .default_value),
+        InfiniteSessionRestoreParams::kMinSiteEngagementToRestore
+            .default_value);
   }
 
  private:
@@ -195,43 +227,49 @@
 
 TEST_F(TabManagerFeaturesTest,
        GetProactiveTabFreezeAndDiscardParamsInvalidGoesToDefault) {
-  SetParam(kProactiveTabFreezeAndDiscard_ShouldProactivelyDiscardParam, "blah");
-  SetParam(kProactiveTabFreezeAndDiscard_ShouldPeriodicallyUnfreezeParam,
+  SetParam(ProactiveTabFreezeAndDiscardParams::kShouldProactivelyDiscard.name,
            "blah");
-  SetParam(
-      kProactiveTabFreezeAndDiscard_ShouldProtectTabsSharingBrowsingInstanceParam,
-      "bleh");
-  SetParam(kProactiveTabFreezeAndDiscard_LowLoadedTabCountParam, "ab");
-  SetParam(kProactiveTabFreezeAndDiscard_ModerateLoadedTabsPerGbRamParam,
+  SetParam(ProactiveTabFreezeAndDiscardParams::kShouldPeriodicallyUnfreeze.name,
+           "blah");
+  SetParam(ProactiveTabFreezeAndDiscardParams::
+               kShouldProtectTabsSharingBrowsingInstance.name,
+           "bleh");
+  SetParam(ProactiveTabFreezeAndDiscardParams::kLowLoadedTabCount.name, "ab");
+  SetParam(ProactiveTabFreezeAndDiscardParams::kModerateLoadedTabsPerGbRam.name,
            "27.8");
-  SetParam(kProactiveTabFreezeAndDiscard_HighLoadedTabCountParam, "4e8");
-  SetParam(kProactiveTabFreezeAndDiscard_LowOccludedTimeoutParam, "---");
-  SetParam(kProactiveTabFreezeAndDiscard_ModerateOccludedTimeoutParam, " ");
-  SetParam(kProactiveTabFreezeAndDiscard_HighOccludedTimeoutParam, "");
-  SetParam(kProactiveTabFreezeAndDiscard_FreezeTimeoutParam, "b");
-  SetParam(kProactiveTabFreezeAndDiscard_UnfreezeTimeoutParam, "i");
-  SetParam(kProactiveTabFreezeAndDiscard_RefreezeTimeoutParam, "m");
+  SetParam(ProactiveTabFreezeAndDiscardParams::kHighLoadedTabCount.name, "4e8");
+  SetParam(ProactiveTabFreezeAndDiscardParams::kLowOccludedTimeout.name, "---");
+  SetParam(ProactiveTabFreezeAndDiscardParams::kModerateOccludedTimeout.name,
+           " ");
+  SetParam(ProactiveTabFreezeAndDiscardParams::kHighOccludedTimeout.name, "");
+  SetParam(ProactiveTabFreezeAndDiscardParams::kFreezeTimeout.name, "b");
+  SetParam(ProactiveTabFreezeAndDiscardParams::kUnfreezeTimeout.name, "i");
+  SetParam(ProactiveTabFreezeAndDiscardParams::kRefreezeTimeout.name, "m");
   EnableProactiveTabFreezeAndDiscard();
   ExpectDefaultProactiveTabFreezeAndDiscardParams();
 }
 
 TEST_F(TabManagerFeaturesTest, GetProactiveTabFreezeAndDiscardParams) {
-  SetParam(kProactiveTabFreezeAndDiscard_ShouldProactivelyDiscardParam, "true");
-  SetParam(kProactiveTabFreezeAndDiscard_ShouldPeriodicallyUnfreezeParam,
+  SetParam(ProactiveTabFreezeAndDiscardParams::kShouldProactivelyDiscard.name,
            "true");
-  SetParam(
-      kProactiveTabFreezeAndDiscard_ShouldProtectTabsSharingBrowsingInstanceParam,
-      "true");
-  SetParam(kProactiveTabFreezeAndDiscard_LowLoadedTabCountParam, "7");
-  SetParam(kProactiveTabFreezeAndDiscard_ModerateLoadedTabsPerGbRamParam, "4");
-  SetParam(kProactiveTabFreezeAndDiscard_HighLoadedTabCountParam, "42");
+  SetParam(ProactiveTabFreezeAndDiscardParams::kShouldPeriodicallyUnfreeze.name,
+           "true");
+  SetParam(ProactiveTabFreezeAndDiscardParams::
+               kShouldProtectTabsSharingBrowsingInstance.name,
+           "true");
+  SetParam(ProactiveTabFreezeAndDiscardParams::kLowLoadedTabCount.name, "7");
+  SetParam(ProactiveTabFreezeAndDiscardParams::kModerateLoadedTabsPerGbRam.name,
+           "4");
+  SetParam(ProactiveTabFreezeAndDiscardParams::kHighLoadedTabCount.name, "42");
   // These are expressed in seconds.
-  SetParam(kProactiveTabFreezeAndDiscard_LowOccludedTimeoutParam, "60");
-  SetParam(kProactiveTabFreezeAndDiscard_ModerateOccludedTimeoutParam, "120");
-  SetParam(kProactiveTabFreezeAndDiscard_HighOccludedTimeoutParam, "247");
-  SetParam(kProactiveTabFreezeAndDiscard_FreezeTimeoutParam, "10");
-  SetParam(kProactiveTabFreezeAndDiscard_UnfreezeTimeoutParam, "20");
-  SetParam(kProactiveTabFreezeAndDiscard_RefreezeTimeoutParam, "30");
+  SetParam(ProactiveTabFreezeAndDiscardParams::kLowOccludedTimeout.name, "60");
+  SetParam(ProactiveTabFreezeAndDiscardParams::kModerateOccludedTimeout.name,
+           "120");
+  SetParam(ProactiveTabFreezeAndDiscardParams::kHighOccludedTimeout.name,
+           "247");
+  SetParam(ProactiveTabFreezeAndDiscardParams::kFreezeTimeout.name, "10");
+  SetParam(ProactiveTabFreezeAndDiscardParams::kUnfreezeTimeout.name, "20");
+  SetParam(ProactiveTabFreezeAndDiscardParams::kRefreezeTimeout.name, "30");
   EnableProactiveTabFreezeAndDiscard();
 
   // Should snap |moderate_loaded_tab_count| to |low_loaded_tab_count|, when the
@@ -277,24 +315,45 @@
 
 TEST_F(TabManagerFeaturesTest,
        GetSiteCharacteristicsDatabaseParamsInvalidGoesToDefault) {
-  SetParam(kSiteCharacteristicsDb_FaviconUpdateObservationWindow, "    ");
-  SetParam(kSiteCharacteristicsDb_TitleUpdateObservationWindow, "foo");
-  SetParam(kSiteCharacteristicsDb_AudioUsageObservationWindow, ".");
-  SetParam(kSiteCharacteristicsDb_NotificationsUsageObservationWindow, "abc");
-  SetParam(kSiteCharacteristicsDb_TitleOrFaviconChangeGracePeriod, "bleh");
-  SetParam(kSiteCharacteristicsDb_AudioUsageGracePeriod, "!!!");
+  SetParam(
+      SiteCharacteristicsDatabaseParams::kFaviconUpdateObservationWindow.name,
+      "    ");
+  SetParam(
+      SiteCharacteristicsDatabaseParams::kTitleUpdateObservationWindow.name,
+      "foo");
+  SetParam(SiteCharacteristicsDatabaseParams::kAudioUsageObservationWindow.name,
+           ".");
+  SetParam(
+      SiteCharacteristicsDatabaseParams::kNotificationsUsageObservationWindow
+          .name,
+      "abc");
+  SetParam(
+      SiteCharacteristicsDatabaseParams::kTitleOrFaviconChangeGracePeriod.name,
+      "bleh");
+  SetParam(SiteCharacteristicsDatabaseParams::kAudioUsageGracePeriod.name,
+           "!!!");
   EnableSiteCharacteristicsDatabase();
   ExpectDefaultSiteCharacteristicsDatabaseParams();
 }
 
 TEST_F(TabManagerFeaturesTest, GetSiteCharacteristicsDatabaseParams) {
-  SetParam(kSiteCharacteristicsDb_FaviconUpdateObservationWindow, "3600");
-  SetParam(kSiteCharacteristicsDb_TitleUpdateObservationWindow, "36000");
-  SetParam(kSiteCharacteristicsDb_AudioUsageObservationWindow, "360000");
-  SetParam(kSiteCharacteristicsDb_NotificationsUsageObservationWindow,
-           "3600000");
-  SetParam(kSiteCharacteristicsDb_TitleOrFaviconChangeGracePeriod, "42");
-  SetParam(kSiteCharacteristicsDb_AudioUsageGracePeriod, "43");
+  SetParam(
+      SiteCharacteristicsDatabaseParams::kFaviconUpdateObservationWindow.name,
+      "3600");
+  SetParam(
+      SiteCharacteristicsDatabaseParams::kTitleUpdateObservationWindow.name,
+      "36000");
+  SetParam(SiteCharacteristicsDatabaseParams::kAudioUsageObservationWindow.name,
+           "360000");
+  SetParam(
+      SiteCharacteristicsDatabaseParams::kNotificationsUsageObservationWindow
+          .name,
+      "3600000");
+  SetParam(
+      SiteCharacteristicsDatabaseParams::kTitleOrFaviconChangeGracePeriod.name,
+      "42");
+  SetParam(SiteCharacteristicsDatabaseParams::kAudioUsageGracePeriod.name,
+           "43");
 
   EnableSiteCharacteristicsDatabase();
 
@@ -307,27 +366,34 @@
 
 TEST_F(TabManagerFeaturesTest,
        GetInfiniteSessionRestoreParamsInvalidGoesToDefault) {
-  SetParam(kInfiniteSessionRestore_MinSimultaneousTabLoads, "  ");
-  SetParam(kInfiniteSessionRestore_MaxSimultaneousTabLoads, "a.b");
-  SetParam(kInfiniteSessionRestore_CoresPerSimultaneousTabLoad, "-- ");
-  SetParam(kInfiniteSessionRestore_MinTabsToRestore, "hey");
-  SetParam(kInfiniteSessionRestore_MaxTabsToRestore, ".");
-  SetParam(kInfiniteSessionRestore_MbFreeMemoryPerTabToRestore, "0x0");
-  SetParam(kInfiniteSessionRestore_MaxTimeSinceLastUseToRestore, "foo");
-  SetParam(kInfiniteSessionRestore_MinSiteEngagementToRestore, "bar");
+  SetParam(InfiniteSessionRestoreParams::kMinSimultaneousTabLoads.name, "  ");
+  SetParam(InfiniteSessionRestoreParams::kMaxSimultaneousTabLoads.name, "a.b");
+  SetParam(InfiniteSessionRestoreParams::kCoresPerSimultaneousTabLoad.name,
+           "-- ");
+  SetParam(InfiniteSessionRestoreParams::kMinTabsToRestore.name, "hey");
+  SetParam(InfiniteSessionRestoreParams::kMaxTabsToRestore.name, ".");
+  SetParam(InfiniteSessionRestoreParams::kMbFreeMemoryPerTabToRestore.name,
+           "0x0");
+  SetParam(InfiniteSessionRestoreParams::kMaxTimeSinceLastUseToRestore.name,
+           "foo");
+  SetParam(InfiniteSessionRestoreParams::kMinSiteEngagementToRestore.name,
+           "bar");
   EnableInfiniteSessionRestore();
   ExpectDefaultInfiniteSessionRestoreParams();
 }
 
 TEST_F(TabManagerFeaturesTest, GetInfiniteSessionRestoreParams) {
-  SetParam(kInfiniteSessionRestore_MinSimultaneousTabLoads, "10");
-  SetParam(kInfiniteSessionRestore_MaxSimultaneousTabLoads, "20");
-  SetParam(kInfiniteSessionRestore_CoresPerSimultaneousTabLoad, "2");
-  SetParam(kInfiniteSessionRestore_MinTabsToRestore, "13");
-  SetParam(kInfiniteSessionRestore_MaxTabsToRestore, "27");
-  SetParam(kInfiniteSessionRestore_MbFreeMemoryPerTabToRestore, "1337");
-  SetParam(kInfiniteSessionRestore_MaxTimeSinceLastUseToRestore, "60");
-  SetParam(kInfiniteSessionRestore_MinSiteEngagementToRestore, "9");
+  SetParam(InfiniteSessionRestoreParams::kMinSimultaneousTabLoads.name, "10");
+  SetParam(InfiniteSessionRestoreParams::kMaxSimultaneousTabLoads.name, "20");
+  SetParam(InfiniteSessionRestoreParams::kCoresPerSimultaneousTabLoad.name,
+           "2");
+  SetParam(InfiniteSessionRestoreParams::kMinTabsToRestore.name, "13");
+  SetParam(InfiniteSessionRestoreParams::kMaxTabsToRestore.name, "27");
+  SetParam(InfiniteSessionRestoreParams::kMbFreeMemoryPerTabToRestore.name,
+           "1337");
+  SetParam(InfiniteSessionRestoreParams::kMaxTimeSinceLastUseToRestore.name,
+           "60");
+  SetParam(InfiniteSessionRestoreParams::kMinSiteEngagementToRestore.name, "9");
   EnableInfiniteSessionRestore();
   ExpectInfiniteSessionRestoreParams(10, 20, 2, 13, 27, 1337,
                                      base::TimeDelta::FromMinutes(1), 9);
diff --git a/chrome/browser/resource_coordinator/tab_manager_stats_collector.cc b/chrome/browser/resource_coordinator/tab_manager_stats_collector.cc
index 85bef76..2fdb88b 100644
--- a/chrome/browser/resource_coordinator/tab_manager_stats_collector.cc
+++ b/chrome/browser/resource_coordinator/tab_manager_stats_collector.cc
@@ -641,6 +641,7 @@
         "TabManager.SessionOverlap.BackgroundTabOpening";
 
 // static
-const base::TimeDelta TabManagerStatsCollector::kLowFrequencySamplingInterval;
+constexpr base::TimeDelta
+    TabManagerStatsCollector::kLowFrequencySamplingInterval;
 
 }  // namespace resource_coordinator
diff --git a/chrome/browser/resource_coordinator/tab_manager_unittest.cc b/chrome/browser/resource_coordinator/tab_manager_unittest.cc
index 415bc01..a2a3f56 100644
--- a/chrome/browser/resource_coordinator/tab_manager_unittest.cc
+++ b/chrome/browser/resource_coordinator/tab_manager_unittest.cc
@@ -523,6 +523,9 @@
   tab_strip2->GetWebContentsAt(0)->WasHidden();
   tab_strip2->GetWebContentsAt(1)->WasHidden();
 
+  // Advance time enough that the tabs are urgent discardable.
+  task_runner_->AdvanceMockTickClock(kBackgroundUrgentProtectionTime);
+
   for (int i = 0; i < 4; ++i)
     tab_manager_->DiscardTab(LifecycleUnitDiscardReason::URGENT);
 
diff --git a/chrome/browser/resources/chromeos/chromevox/BUILD.gn b/chrome/browser/resources/chromeos/chromevox/BUILD.gn
index 30a3a87..6515f76 100644
--- a/chrome/browser/resources/chromeos/chromevox/BUILD.gn
+++ b/chrome/browser/resources/chromeos/chromevox/BUILD.gn
@@ -488,22 +488,9 @@
   sources = [
     "//chrome/browser/extensions/browsertest_util.cc",
     "//chrome/browser/extensions/browsertest_util.h",
-    "//chrome/browser/ui/webui/web_ui_test_handler.cc",
-    "//chrome/browser/ui/webui/web_ui_test_handler.h",
-    "//chrome/test/base/extension_js_browser_test.cc",
-    "//chrome/test/base/extension_js_browser_test.h",
-    "//chrome/test/base/extension_load_waiter_one_shot.cc",
-    "//chrome/test/base/extension_load_waiter_one_shot.h",
-    "//chrome/test/base/javascript_browser_test.cc",
-    "//chrome/test/base/javascript_browser_test.h",
-    "//chrome/test/base/test_chrome_web_ui_controller_factory.cc",
-    "//chrome/test/base/test_chrome_web_ui_controller_factory.h",
-    "//chrome/test/base/web_ui_browser_test.cc",
-    "//chrome/test/base/web_ui_browser_test.h",
   ]
 
   deps = [
-    ":chromevox_extjs_tests",
     ":chromevox_unitjs_tests",
     "//base",
     "//base:i18n",
@@ -515,6 +502,7 @@
     "//chrome/browser",
     "//chrome/renderer",
     "//chrome/test:browser_tests_runner",
+    "//chrome/test:extension_js_test_support",
     "//chrome/test:test_support",
     "//chrome/test:test_support_ui",
     "//content/test:test_support",
diff --git a/chrome/browser/resources/chromeos/chromevox/braille/braille_table_test.extjs b/chrome/browser/resources/chromeos/chromevox/braille/braille_table_test.extjs
index 6adae04..c778bd7 100644
--- a/chrome/browser/resources/chromeos/chromevox/braille/braille_table_test.extjs
+++ b/chrome/browser/resources/chromeos/chromevox/braille/braille_table_test.extjs
@@ -13,11 +13,11 @@
  * @constructor
  * @extends {ChromeVoxE2ETest}
  */
-function CvoxBrailleTableTest() {
+function ChromeVoxBrailleTableTest() {
   ChromeVoxE2ETest.call(this);
 }
 
-CvoxBrailleTableTest.prototype = {
+ChromeVoxBrailleTableTest.prototype = {
   __proto__: ChromeVoxE2ETest.prototype,
 };
 
@@ -25,7 +25,7 @@
  * Tests that {@code getAll} can fetch and parse the tables file.
  * NOTE: This will need to be adjusted when more tables are added.
  */
-TEST_F('CvoxBrailleTableTest', 'testGetAllAndValidate', function() {
+TEST_F('ChromeVoxBrailleTableTest', 'testGetAllAndValidate', function() {
   cvox.BrailleTable.getAll(this.newCallback(function(tables) {
     expectEquals(68, tables.length);
     assertNotNullNorUndefined(
@@ -40,7 +40,7 @@
 });
 
 /** Tests getDisplayName for some specific representative cases. */
-TEST_F('CvoxBrailleTableTest', 'testGetDisplayName', function() {
+TEST_F('ChromeVoxBrailleTableTest', 'testGetDisplayName', function() {
   cvox.BrailleTable.getAll(this.newCallback(function(tables) {
     var table = cvox.BrailleTable.forId(tables, 'bg-comp8');
     expectEquals('Bulgarian', cvox.BrailleTable.getDisplayName(table));
@@ -55,7 +55,7 @@
 /**
  * Tests the getUncontracted function.
  */
-TEST_F('CvoxBrailleTableTest', 'testGetUncontracted', function() {
+TEST_F('ChromeVoxBrailleTableTest', 'testGetUncontracted', function() {
   cvox.BrailleTable.getAll(this.newCallback(function(tables) {
     function expectUncontracted(uncontractedId, idToCheck) {
       var checkedTable = cvox.BrailleTable.forId(tables, idToCheck);
diff --git a/chrome/browser/resources/chromeos/chromevox/braille/braille_translator_manager_test.extjs b/chrome/browser/resources/chromeos/chromevox/braille/braille_translator_manager_test.extjs
index ea5dbdd..ec4ce84 100644
--- a/chrome/browser/resources/chromeos/chromevox/braille/braille_translator_manager_test.extjs
+++ b/chrome/browser/resources/chromeos/chromevox/braille/braille_translator_manager_test.extjs
@@ -13,11 +13,11 @@
  * @constructor
  * @extends {ChromeVoxE2ETest}
  */
-function CvoxBrailleTranslatorManagerTest() {
+function ChromeVoxBrailleTranslatorManagerTest() {
   ChromeVoxE2ETest.call(this);
 }
 
-CvoxBrailleTranslatorManagerTest.prototype = {
+ChromeVoxBrailleTranslatorManagerTest.prototype = {
   __proto__: ChromeVoxE2ETest.prototype,
 
   /** @override */
@@ -78,7 +78,7 @@
   };
 }
 
-TEST_F('CvoxBrailleTranslatorManagerTest', 'testInitial', function() {
+TEST_F('ChromeVoxBrailleTranslatorManagerTest', 'testInitial', function() {
   assertEquals(null, this.manager.getExpandingTranslator());
   assertEquals(null, this.manager.getDefaultTranslator());
   assertEquals(null, this.manager.getUncontractedTranslator());
@@ -89,7 +89,7 @@
   });
 });
 
-TEST_F('CvoxBrailleTranslatorManagerTest', 'testRefreshWithoutChange',
+TEST_F('ChromeVoxBrailleTranslatorManagerTest', 'testRefreshWithoutChange',
        function() {
   this.addChangeListener(function() {
     assertNotEquals(null, this.manager.getExpandingTranslator());
@@ -101,7 +101,7 @@
   });
 });
 
-TEST_F('CvoxBrailleTranslatorManagerTest', 'testRefreshWithChange',
+TEST_F('ChromeVoxBrailleTranslatorManagerTest', 'testRefreshWithChange',
        function() {
   this.addChangeListener(function() {
     assertNotEquals(null, this.manager.getExpandingTranslator());
diff --git a/chrome/browser/resources/chromeos/chromevox/braille/liblouis_test.extjs b/chrome/browser/resources/chromeos/chromevox/braille/liblouis_test.extjs
index ee2533c..c5e88e3 100644
--- a/chrome/browser/resources/chromeos/chromevox/braille/liblouis_test.extjs
+++ b/chrome/browser/resources/chromeos/chromevox/braille/liblouis_test.extjs
@@ -15,11 +15,11 @@
  * @constructor
  * @extends {ChromeVoxE2ETest}
  */
-function CvoxLibLouisTest() {
+function ChromeVoxLibLouisTest() {
   ChromeVoxE2ETest.call(this);
 }
 
-CvoxLibLouisTest.prototype = {
+ChromeVoxLibLouisTest.prototype = {
   __proto__: ChromeVoxE2ETest.prototype,
 
   createLiblouis: function() {
@@ -48,7 +48,7 @@
   assertEqualsJSON(expected, as_array);
 }
 
-TEST_F('CvoxLibLouisTest', 'checkAllTables', function() {
+TEST_F('ChromeVoxLibLouisTest', 'checkAllTables', function() {
   var liblouis = this.createAndAttachLiblouis();
   cvox.BrailleTable.getAll(this.newCallback(function(tables) {
     var i = 0;
@@ -66,7 +66,7 @@
   }.bind(this)));
 });
 
-TEST_F('CvoxLibLouisTest', 'testTranslateComputerBraille', function() {
+TEST_F('ChromeVoxLibLouisTest', 'testTranslateComputerBraille', function() {
   var liblouis = this.createAndAttachLiblouis();
   this.withTranslator(liblouis, 'en-us-comp8.ctb', function(translator) {
     translator.translate('Hello!', [], this.newCallback(
@@ -78,7 +78,7 @@
   });
 });
 
-TEST_F('CvoxLibLouisTest', 'testBackTranslateComputerBraille', function() {
+TEST_F('ChromeVoxLibLouisTest', 'testBackTranslateComputerBraille', function() {
   var liblouis = this.createAndAttachLiblouis();
   this.withTranslator(liblouis, 'en-us-comp8.ctb', function(translator) {
     var cells = new Uint8Array([0x53, 0x11, 0x07, 0x07, 0x15, 0x2e]);
@@ -88,7 +88,7 @@
   });
 });
 
-TEST_F('CvoxLibLouisTest', 'testTranslateGermanGrade2Braille', function() {
+TEST_F('ChromeVoxLibLouisTest', 'testTranslateGermanGrade2Braille', function() {
   var liblouis = this.createAndAttachLiblouis();
   // This is one of the moderately large tables.
   this.withTranslator(liblouis, 'de-de-g2.ctb', function(translator) {
@@ -101,7 +101,7 @@
   });
 });
 
-TEST_F('CvoxLibLouisTest', 'testBackTranslateGermanComputerBraille', function() {
+TEST_F('ChromeVoxLibLouisTest', 'testBackTranslateGermanComputerBraille', function() {
   var liblouis = this.createAndAttachLiblouis();
   this.withTranslator(liblouis, 'de-de-comp8.ctb', function(translator) {
     var cells = new Uint8Array([0xb3]);
@@ -111,7 +111,7 @@
   });
 });
 
-TEST_F('CvoxLibLouisTest', 'testBackTranslateEmptyCells', function() {
+TEST_F('ChromeVoxLibLouisTest', 'testBackTranslateEmptyCells', function() {
   var liblouis = this.createAndAttachLiblouis();
   this.withTranslator(liblouis, 'de-de-comp8.ctb', function(translator) {
        translator.backTranslate(
@@ -123,7 +123,7 @@
   });
 });
 
-TEST_F('CvoxLibLouisTest', 'testGetTranslatorBeforeAttach', function() {
+TEST_F('ChromeVoxLibLouisTest', 'testGetTranslatorBeforeAttach', function() {
   var liblouis = this.createLiblouis();
   assertFalse(liblouis.isAttached());
   this.withTranslator(liblouis, 'en-us-comp8.ctb', function(translator) {
@@ -131,14 +131,14 @@
   });
 });
 
-TEST_F('CvoxLibLouisTest', 'testGetInvalidTranslator', function() {
+TEST_F('ChromeVoxLibLouisTest', 'testGetInvalidTranslator', function() {
   var liblouis = this.createAndAttachLiblouis();
   this.withTranslator(liblouis, 'nonexistant-table', function(translator) {
     assertEquals(null, translator);
   });
 });
 
-TEST_F('CvoxLibLouisTest', 'testTranslateAfterDetach', function() {
+TEST_F('ChromeVoxLibLouisTest', 'testTranslateAfterDetach', function() {
   var liblouis = this.createAndAttachLiblouis();
   this.withTranslator(liblouis, 'de-de-comp8.ctb', function(translator) {
     liblouis.detach();
@@ -151,7 +151,7 @@
   });
 });
 
-TEST_F('CvoxLibLouisTest', 'testDetachWithOutstandingCallbacks', function() {
+TEST_F('ChromeVoxLibLouisTest', 'testDetachWithOutstandingCallbacks', function() {
   var liblouis = this.createAndAttachLiblouis();
   this.withTranslator(liblouis, 'de-de-comp8.ctb', function(translator) {
     var called = false;
@@ -167,7 +167,7 @@
   });
 });
 
-TEST_F('CvoxLibLouisTest', 'testKeyEventStaticData', function() {
+TEST_F('ChromeVoxLibLouisTest', 'testKeyEventStaticData', function() {
   var liblouis = this.createAndAttachLiblouis();
   this.withTranslator(liblouis, 'en-us-comp8.ctb', function(translator) {
     translator.translate('abcdefghijklmnopqrstuvwxyz 0123456789', [],
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/automation_util_test.extjs b/chrome/browser/resources/chromeos/chromevox/cvox2/background/automation_util_test.extjs
index 8f983de..f5b5834 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/automation_util_test.extjs
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/automation_util_test.extjs
@@ -13,11 +13,11 @@
  * @constructor
  * @extends {ChromeVoxE2ETestBase}
  */
-function AutomationUtilE2ETest() {
+function ChromeVoxAutomationUtilE2ETest() {
   ChromeVoxNextE2ETest.call(this);
 }
 
-AutomationUtilE2ETest.prototype = {
+ChromeVoxAutomationUtilE2ETest.prototype = {
   __proto__: ChromeVoxNextE2ETest.prototype,
 
   /** @override */
@@ -63,7 +63,7 @@
   */}
 };
 
-TEST_F('AutomationUtilE2ETest', 'GetAncestors', function() {
+TEST_F('ChromeVoxAutomationUtilE2ETest', 'GetAncestors', function() {
   this.runWithLoadedTree(this.basicDoc, function(root) {
     var expectedLength = 1;
     while (root) {
@@ -74,7 +74,7 @@
   });
 });
 
-TEST_F('AutomationUtilE2ETest', 'GetUniqueAncestors', function() {
+TEST_F('ChromeVoxAutomationUtilE2ETest', 'GetUniqueAncestors', function() {
   this.runWithLoadedTree(this.basicDoc, function(root) {
     var leftmost = root, rightmost = root;
     while (leftmost.firstChild)
@@ -117,7 +117,7 @@
   }.bind(this));
 });
 
-TEST_F('AutomationUtilE2ETest', 'GetDirection', function() {
+TEST_F('ChromeVoxAutomationUtilE2ETest', 'GetDirection', function() {
   this.runWithLoadedTree(this.basicDoc, function(root) {
     var left = root, right = root;
 
@@ -138,7 +138,7 @@
   });
 });
 
-TEST_F('AutomationUtilE2ETest', 'VisitContainer', function() {
+TEST_F('ChromeVoxAutomationUtilE2ETest', 'VisitContainer', function() {
   this.runWithLoadedTree(toolbarDoc, function(r) {
     var pred = function(n) { return n.role != 'rootWebArea'; };
 
@@ -157,7 +157,7 @@
   });
 });
 
-TEST_F('AutomationUtilE2ETest', 'HitTest', function() {
+TEST_F('ChromeVoxAutomationUtilE2ETest', 'HitTest', function() {
   this.runWithLoadedTree(headingDoc, function(r) {
     // Gets the center point of a rect.
     function getCP(node) {
@@ -179,7 +179,7 @@
   });
 });
 
-TEST_F('AutomationUtilE2ETest', 'FindLastNodeSimple', function() {
+TEST_F('ChromeVoxAutomationUtilE2ETest', 'FindLastNodeSimple', function() {
   this.runWithLoadedTree(function() {/*!
     <p aria-label=" "><div aria-label="x"></div></p>
   */}, function(r) {
@@ -190,7 +190,7 @@
   });
 });
 
-TEST_F('AutomationUtilE2ETest', 'FindLastNodeNonLeaf', function() {
+TEST_F('ChromeVoxAutomationUtilE2ETest', 'FindLastNodeNonLeaf', function() {
   this.runWithLoadedTree(function() {/*!
     <div aria-label="x"><div aria-label=" "></div></div>
   */}, function(r) {
@@ -201,7 +201,7 @@
   });
 });
 
-TEST_F('AutomationUtilE2ETest', 'FindLastNodeComplex', function() {
+TEST_F('ChromeVoxAutomationUtilE2ETest', 'FindLastNodeComplex', function() {
   this.runWithLoadedTree(function() {/*!
     <p>start</p>
     <div aria-label="x"><div aria-label=" "></div></div>
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/background_test.extjs b/chrome/browser/resources/chromeos/chromevox/cvox2/background/background_test.extjs
index 87099903..db39113a 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/background_test.extjs
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/background_test.extjs
@@ -13,11 +13,11 @@
  * @constructor
  * @extends {ChromeVoxNextE2ETest}
  */
-function BackgroundTest() {
+function ChromeVoxBackgroundTest() {
   ChromeVoxNextE2ETest.call(this);
 }
 
-BackgroundTest.prototype = {
+ChromeVoxBackgroundTest.prototype = {
   __proto__: ChromeVoxNextE2ETest.prototype,
 
   /** @override */
@@ -131,18 +131,18 @@
 };
 
 /** Tests that ChromeVox classic is in this context. */
-SYNC_TEST_F('BackgroundTest', 'ClassicNamespaces', function() {
+SYNC_TEST_F('ChromeVoxBackgroundTest', 'ClassicNamespaces', function() {
   assertEquals('object', typeof(cvox));
   assertEquals('function', typeof(cvox.ChromeVoxBackground));
 });
 
 /** Tests that ChromeVox next is in this context. */
-SYNC_TEST_F('BackgroundTest', 'NextNamespaces', function() {
+SYNC_TEST_F('ChromeVoxBackgroundTest', 'NextNamespaces', function() {
   assertEquals('function', typeof(Background));
 });
 
 /** Tests consistency of navigating forward and backward. */
-TEST_F('BackgroundTest', 'ForwardBackwardNavigation', function() {
+TEST_F('ChromeVoxBackgroundTest', 'ForwardBackwardNavigation', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(this.linksAndHeadingsDoc, function() {
     mockFeedback.expectSpeech('start').expectBraille('start');
@@ -201,7 +201,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'CaretNavigation', function() {
+TEST_F('ChromeVoxBackgroundTest', 'CaretNavigation', function() {
   // TODO(plundblad): Add braille expectaions when crbug.com/523285 is fixed.
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(this.linksAndHeadingsDoc, function() {
@@ -249,7 +249,7 @@
 });
 
 /** Tests that individual buttons are stops for move-by-word functionality. */
-TEST_F('BackgroundTest', 'CaretNavigationTreatsButtonAsWord', function() {
+TEST_F('ChromeVoxBackgroundTest', 'CaretNavigationTreatsButtonAsWord', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(this.buttonDoc, function() {
     mockFeedback.expectSpeech('start');
@@ -271,7 +271,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'SelectSingleBasic', function() {
+TEST_F('ChromeVoxBackgroundTest', 'SelectSingleBasic', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(this.formsDoc, function() {
     var incrementSelectedIndex =
@@ -288,7 +288,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'ContinuousRead', function() {
+TEST_F('ChromeVoxBackgroundTest', 'ContinuousRead', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(this.linksAndHeadingsDoc, function() {
     mockFeedback.expectSpeech('start')
@@ -302,7 +302,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'InitialFocus', function() {
+TEST_F('ChromeVoxBackgroundTest', 'InitialFocus', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree('<a href="a">a</a>',
     function(rootNode) {
@@ -312,7 +312,7 @@
     });
 });
 
-TEST_F('BackgroundTest', 'AriaLabel', function() {
+TEST_F('ChromeVoxBackgroundTest', 'AriaLabel', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree('<a aria-label="foo" href="a">a</a>',
     function(rootNode) {
@@ -326,7 +326,7 @@
   );
 });
 
-TEST_F('BackgroundTest', 'ShowContextMenu', function() {
+TEST_F('ChromeVoxBackgroundTest', 'ShowContextMenu', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree('<p>before</p><a href="a">a</a>',
     function(rootNode) {
@@ -341,7 +341,7 @@
     }.bind(this));
 });
 
-TEST_F('BackgroundTest', 'BrailleRouting', function() {
+TEST_F('ChromeVoxBackgroundTest', 'BrailleRouting', function() {
   var mockFeedback = this.createMockFeedback();
   var route = function(position) {
     assertTrue(ChromeVoxState.instance.onBrailleKeyEvent(
@@ -384,7 +384,7 @@
       });
 });
 
-TEST_F('BackgroundTest', 'FocusInputElement', function() {
+TEST_F('ChromeVoxBackgroundTest', 'FocusInputElement', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(
     function() {/*!
@@ -409,7 +409,7 @@
 });
 
 // Flaky, see https://crbug.com/622387.
-TEST_F('BackgroundTest', 'DISABLED_UseEditableState', function() {
+TEST_F('ChromeVoxBackgroundTest', 'DISABLED_UseEditableState', function() {
   this.runWithLoadedTree(
     function() {/*!
       <input type="text"></input>
@@ -440,7 +440,7 @@
     }.bind(this));
 });
 
-TEST_F('BackgroundTest', 'EarconsForControls', function() {
+TEST_F('ChromeVoxBackgroundTest', 'EarconsForControls', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(
     function() {/*!
@@ -488,7 +488,7 @@
     }.bind(this));
 });
 
-SYNC_TEST_F('BackgroundTest', 'GlobsToRegExp', function() {
+SYNC_TEST_F('ChromeVoxBackgroundTest', 'GlobsToRegExp', function() {
   assertEquals('/^()$/', Background.globsToRegExp_([]).toString());
   assertEquals(
       '/^(http:\\/\\/host\\/path\\+here)$/',
@@ -499,7 +499,7 @@
 });
 
 // Flaky, see https://crbug.com/622387.
-TEST_F('BackgroundTest', 'DISABLED_ActiveOrInactive', function() {
+TEST_F('ChromeVoxBackgroundTest', 'DISABLED_ActiveOrInactive', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function() {/*!
       <a href="a">a</a>
@@ -537,7 +537,7 @@
     });
 });
 
-TEST_F('BackgroundTest', 'ShouldNotFocusIframe', function() {
+TEST_F('ChromeVoxBackgroundTest', 'ShouldNotFocusIframe', function() {
   this.runWithLoadedTree(  function() {/*!
     <iframe tabindex=0 src="data:text/html,<p>Inside</p>"></iframe>
     <button>outside</button>
@@ -559,7 +559,7 @@
   }.bind(this));
 });
 
-TEST_F('BackgroundTest', 'ShouldFocusLink', function() {
+TEST_F('ChromeVoxBackgroundTest', 'ShouldFocusLink', function() {
   this.runWithLoadedTree(  function() {/*!
     <div><a href="#">mylink</a></div>
     <button>after</button>
@@ -580,7 +580,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'NoisySlider', function() {
+TEST_F('ChromeVoxBackgroundTest', 'NoisySlider', function() {
   var mockFeedback = this.createMockFeedback();
   // Slider aria-valuetext must change otherwise blink suppresses event.
   this.runWithLoadedTree(  function() {/*!
@@ -609,7 +609,7 @@
   }.bind(this));
 });
 
-TEST_F('BackgroundTest', 'Checkbox', function() {
+TEST_F('ChromeVoxBackgroundTest', 'Checkbox', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function() {/*!
     <div id="go" role="checkbox">go</div>
@@ -644,7 +644,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'MixedCheckbox', function() {
+TEST_F('ChromeVoxBackgroundTest', 'MixedCheckbox', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function() {/*!
     <div id="go" role="checkbox" aria-checked="mixed">go</div>
@@ -654,7 +654,7 @@
 });
 
 /** Tests navigating into and out of iframes using nextButton */
-TEST_F('BackgroundTest', 'ForwardNavigationThroughIframeButtons', function() {
+TEST_F('ChromeVoxBackgroundTest', 'ForwardNavigationThroughIframeButtons', function() {
   var mockFeedback = this.createMockFeedback();
 
   var running = false;
@@ -696,7 +696,7 @@
 });
 
 /** Tests navigating into and out of iframes using nextObject */
-TEST_F('BackgroundTest', 'ForwardObjectNavigationThroughIframes', function() {
+TEST_F('ChromeVoxBackgroundTest', 'ForwardObjectNavigationThroughIframes', function() {
   var mockFeedback = this.createMockFeedback();
 
   var running = false;
@@ -745,7 +745,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'SelectOptionSelected', function() {
+TEST_F('ChromeVoxBackgroundTest', 'SelectOptionSelected', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function() {/*!
     <select>
@@ -769,7 +769,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'ToggleButton', function() {
+TEST_F('ChromeVoxBackgroundTest', 'ToggleButton', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function() {/*!
     <div aria-pressed="mixed" role="button">boldface</div>
@@ -802,7 +802,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'EditText', function() {
+TEST_F('ChromeVoxBackgroundTest', 'EditText', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function() {/*!
     <input type="text"></input>
@@ -818,7 +818,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'BackwardForwardSync', function() {
+TEST_F('ChromeVoxBackgroundTest', 'BackwardForwardSync', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function() {/*!
     <div aria-label="Group" role="group" tabindex=0>
@@ -851,7 +851,7 @@
 });
 
 /** Tests that navigation works when the current object disappears. */
-TEST_F('BackgroundTest', 'DisappearingObject', function() {
+TEST_F('ChromeVoxBackgroundTest', 'DisappearingObject', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(this.disappearingObjectDoc, function(rootNode) {
     var deleteButton = rootNode.find({role: RoleType.BUTTON,
@@ -883,7 +883,7 @@
 });
 
 /** Tests that focus jumps to details properly when indicated. */
-TEST_F('BackgroundTest', 'JumpToDetails', function() {
+TEST_F('ChromeVoxBackgroundTest', 'JumpToDetails', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(this.detailsDoc, function(rootNode) {
     mockFeedback.call(doCmd('jumpToDetails'))
@@ -893,7 +893,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'ButtonNameValueDescription', function() {
+TEST_F('ChromeVoxBackgroundTest', 'ButtonNameValueDescription', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function(root) {/*!
     <input type="submit" aria-label="foo" value="foo"></input>
@@ -906,7 +906,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'NameFromHeadingLink', function() {
+TEST_F('ChromeVoxBackgroundTest', 'NameFromHeadingLink', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function(root) {/*!
     <p>before</p>
@@ -921,7 +921,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'OptionChildIndexCount', function() {
+TEST_F('ChromeVoxBackgroundTest', 'OptionChildIndexCount', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function(root) {/*!
     <div role="listbox">
@@ -942,7 +942,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'ListMarkerIsIgnored', function() {
+TEST_F('ChromeVoxBackgroundTest', 'ListMarkerIsIgnored', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function(root) {/*!
     <ul><li>apple</ul>
@@ -954,7 +954,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'SymetricComplexHeading', function() {
+TEST_F('ChromeVoxBackgroundTest', 'SymetricComplexHeading', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function(root) {/*!
     <h4><p>NW</p><p>NE</p></h4>
@@ -970,7 +970,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'ContentEditableJumpSyncsRange', function() {
+TEST_F('ChromeVoxBackgroundTest', 'ContentEditableJumpSyncsRange', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function(root) {/*!
     <p>start</p>
@@ -1002,7 +1002,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'Selection', function() {
+TEST_F('ChromeVoxBackgroundTest', 'Selection', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function(root) {/*!
     <p>simple</p>
@@ -1027,7 +1027,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'BasicTableCommands', function() {
+TEST_F('ChromeVoxBackgroundTest', 'BasicTableCommands', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function(root) {/*!
   <table border=1>
@@ -1097,7 +1097,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'MissingTableCells', function() {
+TEST_F('ChromeVoxBackgroundTest', 'MissingTableCells', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function(root) {/*!
   <table border=1>
@@ -1139,7 +1139,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'DisabledState', function() {
+TEST_F('ChromeVoxBackgroundTest', 'DisabledState', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function(root) {/*!
     <button aria-disabled="true">ok</button>
@@ -1148,7 +1148,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'HeadingLevels', function() {
+TEST_F('ChromeVoxBackgroundTest', 'HeadingLevels', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function(root) {/*!
     <h1>1</h1><h2>2</h2><h3>3</h3><h4>4</h4><h5>5</h5><h6>6</h6>
@@ -1168,7 +1168,7 @@
 });
 
 // Flaky, see https://crbug.com/622387.
-TEST_F('BackgroundTest', 'DISABLED_EditableNavigation', function() {
+TEST_F('ChromeVoxBackgroundTest', 'DISABLED_EditableNavigation', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function(root) {/*!
     <div contenteditable>this is a test</div>
@@ -1186,7 +1186,7 @@
 });
 
 // Flaky, see https://crbug.com/622387.
-TEST_F('BackgroundTest', 'DISABLED_NavigationMovesFocus', function() {
+TEST_F('ChromeVoxBackgroundTest', 'DISABLED_NavigationMovesFocus', function() {
   this.runWithLoadedTree(function(root) {/*!
     <p>start</p>
     <input type="text"></input>
@@ -1201,7 +1201,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'BrailleCaretNavigation', function() {
+TEST_F('ChromeVoxBackgroundTest', 'BrailleCaretNavigation', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function(root) {/*!
     <p>This is a<em>test</em> of inline braille<br>with a second line</p>
@@ -1222,7 +1222,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'InPageLinks', function() {
+TEST_F('ChromeVoxBackgroundTest', 'InPageLinks', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function(root) {/*!
     <a href="#there">hi</a>
@@ -1235,7 +1235,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'ListItem', function() {
+TEST_F('ChromeVoxBackgroundTest', 'ListItem', function() {
   this.resetContextualOutput();
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function(root) {/*!
@@ -1266,7 +1266,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'BusyHeading', function() {
+TEST_F('ChromeVoxBackgroundTest', 'BusyHeading', function() {
   this.resetContextualOutput();
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function(root) {/*!
@@ -1283,7 +1283,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'NodeVsSubnode', function() {
+TEST_F('ChromeVoxBackgroundTest', 'NodeVsSubnode', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function(root) {/*!
     <a href="#">test</a>
@@ -1313,7 +1313,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'NativeFind', function() {
+TEST_F('ChromeVoxBackgroundTest', 'NativeFind', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function(root) {/*!
     <a href="#">grape</a>
@@ -1330,7 +1330,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'EditableKeyCommand', function() {
+TEST_F('ChromeVoxBackgroundTest', 'EditableKeyCommand', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function(root) {/*!
     <input type="text"></input>
@@ -1363,7 +1363,7 @@
 });
 
 // Flaky. See http://crbug.com/857382.
-TEST_F('BackgroundTest', 'DISABLED_TextSelectionAndLiveRegion', function() {
+TEST_F('ChromeVoxBackgroundTest', 'DISABLED_TextSelectionAndLiveRegion', function() {
   DesktopAutomationHandler.announceActions = true;
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function(root) {/*!
@@ -1401,7 +1401,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'TableColumnHeaders', function() {
+TEST_F('ChromeVoxBackgroundTest', 'TableColumnHeaders', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function(root) {/*!
     <div role="grid">
@@ -1442,7 +1442,7 @@
 });
 
 // Flaky, see https://crbug.com/622387.
-TEST_F('BackgroundTest', 'DISABLED_ActiveDescendantUpdates', function() {
+TEST_F('ChromeVoxBackgroundTest', 'DISABLED_ActiveDescendantUpdates', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function(root) {/*!
     <div aria-label="container" tabindex=0 role="group" id="active"
@@ -1475,7 +1475,7 @@
 });
 
 // Flaky, see https://crbug.com/622387.
-TEST_F('BackgroundTest', 'DISABLED_NavigationEscapesEdit', function() {
+TEST_F('ChromeVoxBackgroundTest', 'DISABLED_NavigationEscapesEdit', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function() {/*!
     <p>before content editable</p>
@@ -1550,7 +1550,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'NavigationSyncsSelect', function() {
+TEST_F('ChromeVoxBackgroundTest', 'NavigationSyncsSelect', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function() {/*!
     <select>
@@ -1572,7 +1572,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'NavigationIgnoresLabels', function() {
+TEST_F('ChromeVoxBackgroundTest', 'NavigationIgnoresLabels', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function() {/*!
     <p>before</p>
@@ -1604,7 +1604,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'NavigationIgnoresDescriptions', function() {
+TEST_F('ChromeVoxBackgroundTest', 'NavigationIgnoresDescriptions', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function() {/*!
     <p>before</p>
@@ -1636,7 +1636,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'MathContentViaInnerHtml', function() {
+TEST_F('ChromeVoxBackgroundTest', 'MathContentViaInnerHtml', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function(root) {/*
     <div role="math">
@@ -1679,7 +1679,7 @@
   });
 });
 
-TEST_F('BackgroundTest', 'GestureGranularity', function() {
+TEST_F('ChromeVoxBackgroundTest', 'GestureGranularity', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function(root) {/*
     <p>This is a test</p>
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/cursors_test.extjs b/chrome/browser/resources/chromeos/chromevox/cvox2/background/cursors_test.extjs
index 20d7dd2..b5c74be 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/cursors_test.extjs
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/cursors_test.extjs
@@ -10,11 +10,11 @@
  * @constructor
  * @extends {ChromeVoxNextE2ETest}
  */
-function CursorsTest() {
+function ChromeVoxCursorsTest() {
   ChromeVoxNextE2ETest.call(this);
 }
 
-CursorsTest.prototype = {
+ChromeVoxCursorsTest.prototype = {
   __proto__: ChromeVoxNextE2ETest.prototype,
 
   /** Test cursors.Cursor. @const {string} */
@@ -129,7 +129,7 @@
   */}
 };
 
-TEST_F('CursorsTest', 'CharacterCursor', function() {
+TEST_F('ChromeVoxCursorsTest', 'CharacterCursor', function() {
   this.runCursorMovesOnDocument(this.simpleDoc, [
     [CHARACTER, DIRECTIONAL, FORWARD, {index: 1, value: 'start '}],
     [CHARACTER, DIRECTIONAL, BACKWARD, {index: 0, value: 'start '}],
@@ -149,7 +149,7 @@
     [CHARACTER, DIRECTIONAL, BACKWARD, {index: 5, value: 'start '}],]);
 });
 
-TEST_F('CursorsTest', 'WordCursor', function() {
+TEST_F('ChromeVoxCursorsTest', 'WordCursor', function() {
   this.runCursorMovesOnDocument(this.simpleDoc, [
     // Word (BOUND).
     [WORD, BOUND, BACKWARD, {index: 0, value: 'start '}],
@@ -171,7 +171,7 @@
     [WORD, DIRECTIONAL, BACKWARD, {index: 0, value: undefined}]]);
 });
 
-TEST_F('CursorsTest', 'CharacterWordCursor', function() {
+TEST_F('ChromeVoxCursorsTest', 'CharacterWordCursor', function() {
   this.runCursorMovesOnDocument(this.simpleDoc, [
     [CHARACTER, DIRECTIONAL, FORWARD, {index: 1, value: 'start '}],
 
@@ -187,7 +187,7 @@
     [WORD, DIRECTIONAL, BACKWARD, {index: 0, value: 'start '}]]);
 });
 
-TEST_F('CursorsTest', 'LineCursor', function() {
+TEST_F('ChromeVoxCursorsTest', 'LineCursor', function() {
   this.runCursorMovesOnDocument(this.simpleDoc, [
     // Line (BOUND).
     [LINE, BOUND, FORWARD, {value: 'same line'}],
@@ -204,7 +204,7 @@
     [LINE, BOUND, BACKWARD, {value: 'start '}]]);
 });
 
-TEST_F('CursorsTest', 'CharacterRange', function() {
+TEST_F('ChromeVoxCursorsTest', 'CharacterRange', function() {
   this.runCursorMovesOnDocument(this.simpleDoc, [
       [CHARACTER, FORWARD,
           {value: 'start ', index: 1}, {value: 'start ', index: 2}],
@@ -237,7 +237,7 @@
   ], this.RANGE);
 });
 
-TEST_F('CursorsTest', 'WordRange', function() {
+TEST_F('ChromeVoxCursorsTest', 'WordRange', function() {
   this.runCursorMovesOnDocument(this.simpleDoc, [
       [WORD, FORWARD,
           {value: 'same line', index: 0}, {value: 'same line', index: 4}],
@@ -262,7 +262,7 @@
 });
 
 
-TEST_F('CursorsTest', 'LineRange', function() {
+TEST_F('ChromeVoxCursorsTest', 'LineRange', function() {
   this.runCursorMovesOnDocument(this.simpleDoc, [
         [LINE, FORWARD, {value: 'end', index: 0}, {value: 'end', index: 3}],
       [LINE, FORWARD, {value: 'end', index: 0}, {value: 'end', index: 3}],
@@ -277,7 +277,7 @@
   ], this.RANGE);
 });
 
-TEST_F('CursorsTest', 'DontSplitOnNodeNavigation', function() {
+TEST_F('ChromeVoxCursorsTest', 'DontSplitOnNodeNavigation', function() {
   this.runWithLoadedTree(this.multiInlineDoc, function(root) {
     var para = root.firstChild;
     assertEquals('paragraph', para.role);
@@ -297,7 +297,7 @@
   });
 });
 
-TEST_F('CursorsTest', 'WrappingCursors', function() {
+TEST_F('ChromeVoxCursorsTest', 'WrappingCursors', function() {
   this.runWithLoadedTree(this.multiInlineDoc, function(root) {
     var first = root;
     var last = root.lastChild.firstChild;
@@ -313,7 +313,7 @@
   });
 });
 
-TEST_F('CursorsTest', 'IsInWebRange', function() {
+TEST_F('ChromeVoxCursorsTest', 'IsInWebRange', function() {
   this.runWithLoadedTree(this.simpleDoc, function(root) {
     var para = root.firstChild;
     var webRange = new cursors.Range.fromNode(para);
@@ -323,7 +323,7 @@
   });
 });
 
-TEST_F('CursorsTest', 'DISABLED_SingleDocSelection', function() {
+TEST_F('ChromeVoxCursorsTest', 'DISABLED_SingleDocSelection', function() {
   this.runWithLoadedTree(function() {/*!
     <span>start</span>
     <p><a href="google.com">google home page</a></p>
@@ -364,7 +364,7 @@
   });
 });
 
-TEST_F('CursorsTest', 'MultiLineOffsetSelection', function() {
+TEST_F('ChromeVoxCursorsTest', 'MultiLineOffsetSelection', function() {
   this.runWithLoadedTree(this.multiInlineDoc, function(root) {
     var secondLine = root.firstChild.firstChild.firstChild.nextSibling;
     assertEquals('inlineTextBox', secondLine.role);
@@ -393,7 +393,7 @@
   });
 });
 
-TEST_F('CursorsTest', 'InlineElementOffset', function() {
+TEST_F('ChromeVoxCursorsTest', 'InlineElementOffset', function() {
   this.runWithLoadedTree(function() {/*!
     <span>start</span>
     <p>This<br> is a<a href="#g">test</a>of selection</p>
@@ -431,7 +431,7 @@
   });
 });
 
-TEST_F('CursorsTest', 'ContentEquality', function() {
+TEST_F('ChromeVoxCursorsTest', 'ContentEquality', function() {
   this.runWithLoadedTree(function() {/*!
     <div role="region">this is a test</button>
   */}, function(root) {
@@ -463,7 +463,7 @@
   });
 });
 
-TEST_F('CursorsTest', 'DeepEquivalency', function() {
+TEST_F('ChromeVoxCursorsTest', 'DeepEquivalency', function() {
   this.runWithLoadedTree(function() {/*!
     <p style="word-spacing:100000px">this is a test</p>
   */}, function(root) {
@@ -514,7 +514,7 @@
   });
 });
 
-TEST_F('CursorsTest', 'SelectionAdjustmentsRichText', function() {
+TEST_F('ChromeVoxCursorsTest', 'SelectionAdjustmentsRichText', function() {
   this.runWithLoadedTree(function() {/*!
     <div contenteditable><p>test</p><p>123</p></div>
   */}, function(root) {
@@ -563,7 +563,7 @@
   });
 });
 
-TEST_F('CursorsTest', 'SelectionAdjustmentsNonRichText', function() {
+TEST_F('ChromeVoxCursorsTest', 'SelectionAdjustmentsNonRichText', function() {
   this.runWithLoadedTree(function() {/*!
     <input type="text"></input>
     <textarea></textarea>
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/editing_test.extjs b/chrome/browser/resources/chromeos/chromevox/cvox2/background/editing_test.extjs
index 6ecad019..b93c0b3 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/editing_test.extjs
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/editing_test.extjs
@@ -13,12 +13,12 @@
  * @constructor
  * @extends {ChromeVoxNextE2ETest}
  */
-function EditingTest() {
+function ChromeVoxEditingTest() {
   ChromeVoxNextE2ETest.call(this);
   window.RoleType = chrome.automation.RoleType;
 }
 
-EditingTest.prototype = {
+ChromeVoxEditingTest.prototype = {
   __proto__: ChromeVoxNextE2ETest.prototype,
 
   /**
@@ -44,7 +44,7 @@
 </textarea>
 */};
 
-TEST_F('EditingTest', 'Focus', function() {
+TEST_F('ChromeVoxEditingTest', 'Focus', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(doc, function(root) {
     var singleLine = root.find({role: RoleType.TEXT_FIELD,
@@ -67,7 +67,7 @@
   });
 });
 
-TEST_F('EditingTest', 'Multiline', function() {
+TEST_F('ChromeVoxEditingTest', 'Multiline', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(doc, function(root) {
     var textarea = root.find({role: RoleType.TEXT_FIELD,
@@ -96,7 +96,7 @@
   });
 });
 
-TEST_F('EditingTest', 'TextButNoSelectionChange', function() {
+TEST_F('ChromeVoxEditingTest', 'TextButNoSelectionChange', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(
     function() {/*!
@@ -131,7 +131,7 @@
     });
 });
 
-TEST_F('EditingTest', 'RichTextMoveByLine', function() {
+TEST_F('ChromeVoxEditingTest', 'RichTextMoveByLine', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function() {/*!
     <div id="go" role="textbox" contenteditable>
@@ -178,7 +178,7 @@
   });
 });
 
-TEST_F('EditingTest', 'RichTextMoveByCharacter', function() {
+TEST_F('ChromeVoxEditingTest', 'RichTextMoveByCharacter', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function() {/*!
     <div id="go" role="textbox" contenteditable>This <b>is</b> a test.</div>
@@ -248,7 +248,7 @@
 });
 
 // Tests specifically for cursor workarounds.
-TEST_F('EditingTest', 'RichTextMoveByCharacterNodeWorkaround', function() {
+TEST_F('ChromeVoxEditingTest', 'RichTextMoveByCharacterNodeWorkaround', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function() {/*!
     <div id="go" role="textbox" contenteditable>hello <b>world</b></div>
@@ -289,7 +289,7 @@
   });
 });
 
-TEST_F('EditingTest', 'RichTextMoveByCharacterEndOfLine', function() {
+TEST_F('ChromeVoxEditingTest', 'RichTextMoveByCharacterEndOfLine', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function() {/*!
     <div id="go" role="textbox" contenteditable>Test</div>
@@ -325,7 +325,7 @@
   });
 });
 
-TEST_F('EditingTest', 'RichTextLinkOutput', function() {
+TEST_F('ChromeVoxEditingTest', 'RichTextLinkOutput', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function() {/*!
     <div id="go" role="textbox" contenteditable>a <a href="#">test</a></div>
@@ -369,7 +369,7 @@
   });
 });
 
-TEST_F('EditingTest', 'RichTextExtendByCharacter', function() {
+TEST_F('ChromeVoxEditingTest', 'RichTextExtendByCharacter', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function() {/*!
     <div id="go" role="textbox" contenteditable>Te<br>st</div>
@@ -403,7 +403,7 @@
   });
 });
 
-TEST_F('EditingTest', 'RichTextImageByCharacter', function() {
+TEST_F('ChromeVoxEditingTest', 'RichTextImageByCharacter', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function() {/*!
     <p id="go" contenteditable>
@@ -464,7 +464,7 @@
   });
 });
 
-TEST_F('EditingTest', 'RichTextSelectByLine', function() {
+TEST_F('ChromeVoxEditingTest', 'RichTextSelectByLine', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function() {/*!
     <p id="go" contenteditable>
@@ -520,17 +520,17 @@
           // By line (notice the partial selections from the first and second
           // lines).
           .call(move)
-          .expectSpeech('rst line \n se', 'selected')
-          .expectBraille('second line\n', {startIndex: 0, endIndex: 2})
+          .expectSpeech('rst line \n s', 'selected')
+          .expectBraille('second line\n', {startIndex: 0, endIndex: 1})
 
           .call(move)
-          .expectSpeech('cond line \n th', 'selected')
+          .expectSpeech('econd line \n th', 'selected')
           .expectBraille('third line\n', {startIndex: 0, endIndex: 2})
 
           // Shrinking.
           .call(move)
-          .expectSpeech('cond line \n th', 'unselected')
-          .expectBraille('second line\n', {startIndex: 0, endIndex: 2})
+          .expectSpeech('econd line \n th', 'unselected')
+          .expectBraille('second line\n', {startIndex: 0, endIndex: 1})
 
           .call(move)
           .expectSpeech('fi', 'selected')
@@ -575,7 +575,7 @@
   });
 });
 
-TEST_F('EditingTest', 'EditableLineOneStaticText', function() {
+TEST_F('ChromeVoxEditingTest', 'EditableLineOneStaticText', function() {
   this.runWithLoadedTree(function() {/*!
     <p contenteditable style="word-spacing:100000px">this is a test</p>
   */}, function(root) {
@@ -627,7 +627,7 @@
   });
 });
 
-TEST_F('EditingTest', 'EditableLineTwoStaticTexts', function() {
+TEST_F('ChromeVoxEditingTest', 'EditableLineTwoStaticTexts', function() {
   this.runWithLoadedTree(function() {/*!
     <p contenteditable>hello <b>world</b></p>
   */}, function(root) {
@@ -680,7 +680,7 @@
   });
 });
 
-TEST_F('EditingTest', 'EditableLineEquality', function() {
+TEST_F('ChromeVoxEditingTest', 'EditableLineEquality', function() {
   this.runWithLoadedTree(function() {/*!
     <div contenteditable role="textbox">
       <p style="word-spacing:100000px">this is a test</p>
@@ -757,7 +757,7 @@
   });
 });
 
-TEST_F('EditingTest', 'EditableLineStrictEquality', function() {
+TEST_F('ChromeVoxEditingTest', 'EditableLineStrictEquality', function() {
   this.runWithLoadedTree(function() {/*!
     <div contenteditable role="textbox">
       <p style="word-spacing:100000px">this is a test</p>
@@ -812,7 +812,7 @@
   });
 });
 
-TEST_F('EditingTest', 'EditableLineBaseLineAnchorOrFocus', function() {
+TEST_F('ChromeVoxEditingTest', 'EditableLineBaseLineAnchorOrFocus', function() {
   this.runWithLoadedTree(function() {/*!
     <div contenteditable role="textbox">
       <p style="word-spacing:100000px">this is a test</p>
@@ -856,7 +856,7 @@
   })
 });
 
-TEST_F('EditingTest', 'IsValidLine', function() {
+TEST_F('ChromeVoxEditingTest', 'IsValidLine', function() {
   this.runWithLoadedTree(function() {/*!
     <div contenteditable role="textbox">
       <p style="word-spacing:100000px">this is a test</p>
@@ -898,7 +898,7 @@
   })
 });
 
-TEST_F('EditingTest', 'TelTrimsWhitespace', function() {
+TEST_F('ChromeVoxEditingTest', 'TelTrimsWhitespace', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function() {/*!
     <div id="go"></div>
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/i_search_test.extjs b/chrome/browser/resources/chromeos/chromevox/cvox2/background/i_search_test.extjs
index 27ff448..9f00435 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/i_search_test.extjs
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/i_search_test.extjs
@@ -10,11 +10,11 @@
  * @constructor
  * @extends {ChromeVoxNextE2ETest}
  */
-function ISearchTest() {
+function ChromeVoxISearchTest() {
   ChromeVoxNextE2ETest.call(this);
 }
 
-ISearchTest.prototype = {
+ChromeVoxISearchTest.prototype = {
   __proto__: ChromeVoxNextE2ETest.prototype,
 
   /** @override */
@@ -64,7 +64,7 @@
   }
 };
 
-TEST_F('ISearchTest', 'DISABLED_Simple', function() {
+TEST_F('ChromeVoxISearchTest', 'DISABLED_Simple', function() {
   this.runWithLoadedTree(this.linksAndHeadingsDoc, function(rootNode) {
     var handler = new FakeISearchHandler(this);
     var search = new ISearch(rootNode);
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/live_regions_test.extjs b/chrome/browser/resources/chromeos/chromevox/cvox2/background/live_regions_test.extjs
index 684f758..d02627a 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/live_regions_test.extjs
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/live_regions_test.extjs
@@ -13,11 +13,11 @@
  * @constructor
  * @extends {ChromeVoxNextE2ETest}
  */
-function LiveRegionsTest() {
+function ChromeVoxLiveRegionsTest() {
   ChromeVoxNextE2ETest.call(this);
 }
 
-LiveRegionsTest.prototype = {
+ChromeVoxLiveRegionsTest.prototype = {
   __proto__: ChromeVoxNextE2ETest.prototype,
 
   /** @override */
@@ -48,7 +48,7 @@
   },
 };
 
-TEST_F('LiveRegionsTest', 'LiveRegionAddElement', function() {
+TEST_F('ChromeVoxLiveRegionsTest', 'LiveRegionAddElement', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(
     function() {/*!
@@ -69,7 +69,7 @@
     });
 });
 
-TEST_F('LiveRegionsTest', 'LiveRegionRemoveElement', function() {
+TEST_F('ChromeVoxLiveRegionsTest', 'LiveRegionRemoveElement', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(
     function() {/*!
@@ -91,7 +91,7 @@
     });
 });
 
-TEST_F('LiveRegionsTest', 'LiveRegionChangeAtomic', function() {
+TEST_F('ChromeVoxLiveRegionsTest', 'LiveRegionChangeAtomic', function() {
   LiveRegions.LIVE_REGION_QUEUE_TIME_MS = 0;
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(
@@ -116,7 +116,7 @@
 });
 
 // Flaky test: https://crbug.com/819696
-TEST_F('LiveRegionsTest', 'DISABLED_LiveRegionChangeAtomicText', function() {
+TEST_F('ChromeVoxLiveRegionsTest', 'DISABLED_LiveRegionChangeAtomicText', function() {
   LiveRegions.LIVE_REGION_QUEUE_TIME_MS = 0;
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(
@@ -138,7 +138,7 @@
 });
 
 // Flaky. See https://crbug.com/847028.
-TEST_F('LiveRegionsTest', 'DISABLED_LiveRegionChangeImageAlt', function() {
+TEST_F('ChromeVoxLiveRegionsTest', 'DISABLED_LiveRegionChangeImageAlt', function() {
   // Note that there is a live region outputted as a result of page load; the
   // test expects a live region announcement after a click on the button, but
   // the LiveRegions module has a half second filter for live region
@@ -167,7 +167,7 @@
     });
 });
 
-TEST_F('LiveRegionsTest', 'LiveRegionThenFocus', function() {
+TEST_F('ChromeVoxLiveRegionsTest', 'LiveRegionThenFocus', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(
     function() {/*!
@@ -192,7 +192,7 @@
     });
 });
 
-TEST_F('LiveRegionsTest', 'FocusThenLiveRegion', function() {
+TEST_F('ChromeVoxLiveRegionsTest', 'FocusThenLiveRegion', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(
     function() {/*!
@@ -217,7 +217,7 @@
     });
 });
 
-TEST_F('LiveRegionsTest', 'LiveRegionCategoryFlush', function() {
+TEST_F('ChromeVoxLiveRegionsTest', 'LiveRegionCategoryFlush', function() {
   // Adjust the live region queue time to be shorter (i.e. flushes happen for
   // live regions coming 1 ms in time). Also, can help with flakeyness.
   LiveRegions.LIVE_REGION_QUEUE_TIME_MS = 1;
@@ -246,7 +246,7 @@
     });
 });
 
-TEST_F('LiveRegionsTest', 'SilentOnNodeChange', function() {
+TEST_F('ChromeVoxLiveRegionsTest', 'SilentOnNodeChange', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function() {/*!
     <p>start</p>
@@ -275,7 +275,7 @@
   });
 });
 
-TEST_F('LiveRegionsTest', 'SimulateTreeChanges', function() {
+TEST_F('ChromeVoxLiveRegionsTest', 'SimulateTreeChanges', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function() {/*!
     <button></button>
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/log_store_test.extjs b/chrome/browser/resources/chromeos/chromevox/cvox2/background/log_store_test.extjs
index af7a2ec..10c462d 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/log_store_test.extjs
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/log_store_test.extjs
@@ -10,15 +10,15 @@
  * @constructor
  * @extends {ChromeVoxE2ETestBase}
  */
-function LogStoreTest() {
+function ChromeVoxLogStoreTest() {
   ChromeVoxNextE2ETest.call(this);
 }
 
-LogStoreTest.prototype = {
+ChromeVoxLogStoreTest.prototype = {
   __proto__: ChromeVoxNextE2ETest.prototype,
 };
 
-SYNC_TEST_F('LogStoreTest', 'ShortLogs', function() {
+SYNC_TEST_F('ChromeVoxLogStoreTest', 'ShortLogs', function() {
   var logStore = new LogStore();
   for (var i = 0; i < 100; i++)
     logStore.writeTextLog('test' + i, 'speech');
@@ -29,7 +29,7 @@
     assertEquals(logs[i].toString(), 'test' + i);
 });
 
-SYNC_TEST_F('LogStoreTest', 'LongLogs', function() {
+SYNC_TEST_F('ChromeVoxLogStoreTest', 'LongLogs', function() {
   var logStore = new LogStore();
   for (var i = 0; i < LogStore.LOG_LIMIT + 500; i++)
     logStore.writeTextLog('test' + i, 'speech');
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/output_test.extjs b/chrome/browser/resources/chromeos/chromevox/cvox2/background/output_test.extjs
index 68917ff..a092233 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/output_test.extjs
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/output_test.extjs
@@ -90,11 +90,11 @@
  * @constructor
  * @extends {ChromeVoxNextE2ETestBase}
  */
-function OutputE2ETest() {
+function ChromeVoxOutputE2ETest() {
   ChromeVoxNextE2ETest.call(this);
 }
 
-OutputE2ETest.prototype = {
+ChromeVoxOutputE2ETest.prototype = {
   __proto__: ChromeVoxNextE2ETest.prototype,
 
   /** @override */
@@ -105,7 +105,7 @@
   }
 };
 
-TEST_F('OutputE2ETest', 'Links', function() {
+TEST_F('ChromeVoxOutputE2ETest', 'Links', function() {
   this.runWithLoadedTree('<a href="#">Click here</a>',
     function(root) {
       var el = root.firstChild.firstChild;
@@ -128,7 +128,7 @@
   });
 });
 
-TEST_F('OutputE2ETest', 'Checkbox', function() {
+TEST_F('ChromeVoxOutputE2ETest', 'Checkbox', function() {
   this.runWithLoadedTree('<input type="checkbox">',
     function(root) {
       var el = root.firstChild.firstChild;
@@ -147,7 +147,7 @@
   });
 });
 
-TEST_F('OutputE2ETest', 'InLineTextBoxValueGetsIgnored', function() {
+TEST_F('ChromeVoxOutputE2ETest', 'InLineTextBoxValueGetsIgnored', function() {
   this.runWithLoadedTree('<p>OK',
     function(root) {
       var el = root.firstChild.firstChild.firstChild;
@@ -178,7 +178,7 @@
   });
 });
 
-TEST_F('OutputE2ETest', 'Headings', function() {
+TEST_F('ChromeVoxOutputE2ETest', 'Headings', function() {
   this.runWithLoadedTree(function() {/*!
       <h1>a</h1><h2>b</h2><h3>c</h3><h4>d</h4><h5>e</h5><h6>f</h6>
       <h1><a href="a.com">b</a></h1> */},
@@ -221,7 +221,7 @@
 });
 
 // Flaky, see https://crbug.com/876145.
-TEST_F('OutputE2ETest', 'DISABLED_Audio', function() {
+TEST_F('ChromeVoxOutputE2ETest', 'DISABLED_Audio', function() {
   this.runWithLoadedTree('<audio src="foo.mp3" controls></audio>',
     function(root) {
       var el = root.find({role: 'button'});
@@ -269,7 +269,7 @@
   });
 });
 
-TEST_F('OutputE2ETest', 'Input', function() {
+TEST_F('ChromeVoxOutputE2ETest', 'Input', function() {
   this.runWithLoadedTree(
       '<input type="text"></input>' +
       '<input type="email"></input>' +
@@ -375,7 +375,7 @@
   });
 });
 
-TEST_F('OutputE2ETest', 'List', function() {
+TEST_F('ChromeVoxOutputE2ETest', 'List', function() {
   this.runWithLoadedTree(
       '<ul><li aria-label="a">a<li>b<li>c</ul>',
     function(root) {
@@ -401,7 +401,7 @@
   });
 });
 
-TEST_F('OutputE2ETest', 'Tree', function() {
+TEST_F('ChromeVoxOutputE2ETest', 'Tree', function() {
   this.runWithLoadedTree(function() {/*!
     <ul role="tree" style="list-style-type:none">
       <li aria-expanded="true" role="treeitem">a
@@ -465,7 +465,7 @@
   });
 });
 
-TEST_F('OutputE2ETest', 'Menu', function() {
+TEST_F('ChromeVoxOutputE2ETest', 'Menu', function() {
   this.runWithLoadedTree(function() {/*!
     <div role="menu">
       <div role="menuitem">a</div>
@@ -488,7 +488,7 @@
   });
 });
 
-TEST_F('OutputE2ETest', 'ListBox', function() {
+TEST_F('ChromeVoxOutputE2ETest', 'ListBox', function() {
   this.runWithLoadedTree(function() {/*!
     <select multiple>
       <option>1</option>
@@ -514,7 +514,7 @@
   });
 });
 
-SYNC_TEST_F('OutputE2ETest', 'MessageIdAndEarconValidity', function() {
+SYNC_TEST_F('ChromeVoxOutputE2ETest', 'MessageIdAndEarconValidity', function() {
   const kNoBrailleMessageRequired = new Set([
     'contentDeletion',
     'contentInsertion',
@@ -595,7 +595,7 @@
   }
 });
 
-TEST_F('OutputE2ETest', 'DivOmitsRole', function() {
+TEST_F('ChromeVoxOutputE2ETest', 'DivOmitsRole', function() {
   this.runWithLoadedTree(function() {/*!
     <div>that has content</div>
     <div></div>
@@ -614,7 +614,7 @@
   });
 });
 
-TEST_F('OutputE2ETest', 'LessVerboseAncestry', function() {
+TEST_F('ChromeVoxOutputE2ETest', 'LessVerboseAncestry', function() {
   this.runWithLoadedTree(function() {/*!
     <div role="banner"><p>inside</p></div>
     <div role="banner"><p>inside</p></div>
@@ -644,7 +644,7 @@
   });
 });
 
-TEST_F('OutputE2ETest', 'Brief', function() {
+TEST_F('ChromeVoxOutputE2ETest', 'Brief', function() {
   this.runWithLoadedTree(function() {/*!
     <div role="article"><p>inside</p></div>
   */},
@@ -658,7 +658,7 @@
   });
 });
 
-TEST_F('OutputE2ETest', 'AuralStyledHeadings', function() {
+TEST_F('ChromeVoxOutputE2ETest', 'AuralStyledHeadings', function() {
   function toFixed(num) {
     return parseFloat(Number(num).toFixed(1));
   }
@@ -685,7 +685,7 @@
   });
 });
 
-TEST_F('OutputE2ETest', 'ToggleButton', function() {
+TEST_F('ChromeVoxOutputE2ETest', 'ToggleButton', function() {
   this.runWithLoadedTree(function() {/*!
       <div role="button" aria-pressed="true">Subscribe</div>*/},
     function(root) {
@@ -700,7 +700,7 @@
   });
 });
 
-TEST_F('OutputE2ETest', 'JoinDescendants', function() {
+TEST_F('ChromeVoxOutputE2ETest', 'JoinDescendants', function() {
   this.runWithLoadedTree(function() {/*!
       <p>This</p>
       <p>fragment</p>
@@ -718,7 +718,7 @@
   });
 });
 
-TEST_F('OutputE2ETest', 'ComplexDiv', function() {
+TEST_F('ChromeVoxOutputE2ETest', 'ComplexDiv', function() {
   this.runWithLoadedTree(function() {/*!
       <div><button>ok</button></div>
     */},
@@ -729,7 +729,7 @@
   });
 });
 
-TEST_F('OutputE2ETest', 'ContainerFocus', function() {
+TEST_F('ChromeVoxOutputE2ETest', 'ContainerFocus', function() {
   this.runWithLoadedTree(function() {/*!
       <div role="row" tabindex=0 aria-label="start"></div>
       <div role="row" tabindex=0 aria-label="end"></div>
@@ -742,7 +742,7 @@
   });
 });
 
-TEST_F('OutputE2ETest', 'BraileWhitespace', function() {
+TEST_F('ChromeVoxOutputE2ETest', 'BraileWhitespace', function() {
   this.runWithLoadedTree(function() {/*!
     <p>this is a <em>test</em>of emphasized text</p>
   */},
@@ -765,7 +765,7 @@
   });
 });
 
-TEST_F('OutputE2ETest', 'BrailleAncestry', function() {
+TEST_F('ChromeVoxOutputE2ETest', 'BrailleAncestry', function() {
   this.runWithLoadedTree(function() {/*!
     <ul><li><a href="#">test</a></li></ul>
   */},
@@ -789,7 +789,7 @@
   });
 });
 
-TEST_F('OutputE2ETest', 'RangeOutput', function() {
+TEST_F('ChromeVoxOutputE2ETest', 'RangeOutput', function() {
   this.runWithLoadedTree(function(root) {/*!
     <div role="slider" aria-valuemin="1" aria-valuemax="10" aria-valuenow="2"
                        aria-label="volume"></div>
@@ -853,7 +853,7 @@
   });
 });
 
-TEST_F('OutputE2ETest', 'RoleDescription', function() {
+TEST_F('ChromeVoxOutputE2ETest', 'RoleDescription', function() {
   this.runWithLoadedTree(function(root) {/*!
     <div aria-label="hi" role="button" aria-roledescription="foo"></div>
   */}, function(root) {
@@ -871,7 +871,7 @@
   });
 });
 
-SYNC_TEST_F('OutputE2ETest', 'ValidateCommonProperties', function() {
+SYNC_TEST_F('ChromeVoxOutputE2ETest', 'ValidateCommonProperties', function() {
   var stateStr = '$state';
   var restrictionStr = '$restriction';
   var descStr = '$description';
@@ -941,7 +941,7 @@
   // descriptions somewhere in the output.
 });
 
-TEST_F('OutputE2ETest', 'InlineBraille', function() {
+TEST_F('ChromeVoxOutputE2ETest', 'InlineBraille', function() {
   this.runWithLoadedTree(function(root) {/*!
     <table border=1>
       <tr><td>Name</td><td id="active">Age</td><td>Address</td></tr>
@@ -956,7 +956,7 @@
   });
 });
 
-TEST_F('OutputE2ETest', 'TextFieldObeysRoleDescription', function() {
+TEST_F('ChromeVoxOutputE2ETest', 'TextFieldObeysRoleDescription', function() {
   this.runWithLoadedTree(function(root) {/*!
     <div role="textbox" aria-roledescription="square"></div>
     <div role="region" aria-roledescription="circle"></div>
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/panel_test.extjs b/chrome/browser/resources/chromeos/chromevox/cvox2/background/panel_test.extjs
index f4ac8bd..37491a8 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/panel_test.extjs
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/panel_test.extjs
@@ -12,11 +12,11 @@
  * @constructor
  * @extends {ChromeVoxNextE2ETest}
  */
-function PanelTest() {
+function ChromeVoxPanelTest() {
   ChromeVoxNextE2ETest.call(this);
 }
 
-PanelTest.prototype = {
+ChromeVoxPanelTest.prototype = {
   __proto__: ChromeVoxNextE2ETest.prototype,
 
   /**
@@ -66,7 +66,7 @@
 };
 
 // Flaky, see https://crbug.com/795840
-TEST_F('PanelTest', 'DISABLED_ActivateMenu', function() {
+TEST_F('ChromeVoxPanelTest', 'DISABLED_ActivateMenu', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(this.linksDoc, function(root) {
     var openMenus = new PanelCommand(PanelCommandType.OPEN_MENUS);
@@ -83,7 +83,7 @@
 });
 
 // Flaky, see https://crbug.com/813320
-TEST_F('PanelTest', 'DISABLED_LinkMenu', function() {
+TEST_F('ChromeVoxPanelTest', 'DISABLED_LinkMenu', function() {
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(this.linksDoc, function(root) {
     var openMenus = new PanelCommand(PanelCommandType.OPEN_MENUS, 'role_link');
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/recovery_strategy_test.extjs b/chrome/browser/resources/chromeos/chromevox/cvox2/background/recovery_strategy_test.extjs
index 41d70a1..72e6398 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/recovery_strategy_test.extjs
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/recovery_strategy_test.extjs
@@ -11,16 +11,16 @@
  * @constructor
  * @extends {ChromeVoxNextE2ETest}
  */
-function RecoveryStrategyTest() {
+function ChromeVoxRecoveryStrategyTest() {
   ChromeVoxNextE2ETest.call(this);
   window.RoleType = chrome.automation.RoleType;
 }
 
-RecoveryStrategyTest.prototype = {
+ChromeVoxRecoveryStrategyTest.prototype = {
   __proto__: ChromeVoxNextE2ETest.prototype,
 };
 
-TEST_F('RecoveryStrategyTest', 'ReparentedRecovery', function() {
+TEST_F('ChromeVoxRecoveryStrategyTest', 'ReparentedRecovery', function() {
   this.runWithLoadedTree(function() {/*!
     <input type="text"></input>
     <p id="p">hi</p>
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/tree_walker_test.extjs b/chrome/browser/resources/chromeos/chromevox/cvox2/background/tree_walker_test.extjs
index 3851903..479c936 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/tree_walker_test.extjs
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/tree_walker_test.extjs
@@ -12,11 +12,11 @@
  * @constructor
  * @extends {ChromeVoxE2ETestBase}
  */
-function AutomationTreeWalkerTest() {
+function ChromeVoxAutomationTreeWalkerTest() {
   ChromeVoxNextE2ETest.call(this);
 }
 
-AutomationTreeWalkerTest.prototype = {
+ChromeVoxAutomationTreeWalkerTest.prototype = {
   __proto__: ChromeVoxNextE2ETest.prototype,
 
   flattenTree: function(node, outResult) {
@@ -45,7 +45,7 @@
   }
 };
 
-TEST_F('AutomationTreeWalkerTest', 'Forward', function() {
+TEST_F('ChromeVoxAutomationTreeWalkerTest', 'Forward', function() {
   chrome.automation.getDesktop(this.newCallback(function(d) {
     var resultList = [];
     this.flattenTree(d, resultList);
@@ -72,7 +72,7 @@
   }.bind(this)));
 });
 
-TEST_F('AutomationTreeWalkerTest', 'Backward', function() {
+TEST_F('ChromeVoxAutomationTreeWalkerTest', 'Backward', function() {
   chrome.automation.getDesktop(this.newCallback(function(d) {
     var resultList = [];
     this.flattenTree(d, resultList);
@@ -99,7 +99,7 @@
   }.bind(this)));
 });
 
-TEST_F('AutomationTreeWalkerTest', 'RootLeafRestriction', function() {
+TEST_F('ChromeVoxAutomationTreeWalkerTest', 'RootLeafRestriction', function() {
   this.runWithLoadedTree(function() {/*!
       <div role="group" aria-label="1">
         <div role="group" aria-label="2">
@@ -163,7 +163,7 @@
     });
 });
 
-TEST_F('AutomationTreeWalkerTest', 'LeafPredicateSymmetry', function() {
+TEST_F('ChromeVoxAutomationTreeWalkerTest', 'LeafPredicateSymmetry', function() {
   this.runWithLoadedTree(toolbarDoc, function(r) {
     var d = r.root.parent.root;
     var forwardWalker = new AutomationTreeWalker(d, 'forward');
@@ -185,7 +185,7 @@
   });
 });
 
-TEST_F('AutomationTreeWalkerTest', 'RootPredicateEnding', function() {
+TEST_F('ChromeVoxAutomationTreeWalkerTest', 'RootPredicateEnding', function() {
   this.runWithLoadedTree(toolbarDoc, function(r) {
     var backwardWalker = new AutomationTreeWalker(r.firstChild, 'backward',
         {root: function(node) { return node === r; }});
diff --git a/chrome/browser/resources/chromeos/chromevox/host/chrome/tts_background_test.extjs b/chrome/browser/resources/chromeos/chromevox/host/chrome/tts_background_test.extjs
index 824e3e2..9e3c0e6 100644
--- a/chrome/browser/resources/chromeos/chromevox/host/chrome/tts_background_test.extjs
+++ b/chrome/browser/resources/chromeos/chromevox/host/chrome/tts_background_test.extjs
@@ -13,15 +13,15 @@
  * @constructor
  * @extends {ChromeVoxE2ETest}
  */
-function CvoxTtsBackgroundTest() {
+function ChromeVoxTtsBackgroundTest() {
   ChromeVoxE2ETest.call(this);
 }
 
-CvoxTtsBackgroundTest.prototype = {
+ChromeVoxTtsBackgroundTest.prototype = {
   __proto__: ChromeVoxE2ETest.prototype
 };
 
-SYNC_TEST_F('CvoxTtsBackgroundTest', 'Preprocess', function() {
+SYNC_TEST_F('ChromeVoxTtsBackgroundTest', 'Preprocess', function() {
   var tts = new cvox.TtsBackground(false);
   var preprocess = tts.preprocess.bind(tts);
 
@@ -44,7 +44,7 @@
   assertEquals('bullet 2 bullets', preprocess('\u2022 \u2022\u2022'));
 });
 
-TEST_F('CvoxTtsBackgroundTest', 'UpdateVoice', function() {
+TEST_F('ChromeVoxTtsBackgroundTest', 'UpdateVoice', function() {
   var tts = new cvox.TtsBackground(false);
   var voices = [
     {lang: 'zh-CN', voiceName: 'Chinese'},
@@ -96,7 +96,7 @@
 
 // This test only works if Google tts is installed. Run it locally.
 TEST_F(
-    'CvoxTtsBackgroundTest', 'DISABLED_EmptyStringCallsCallbacks', function() {
+    'ChromeVoxTtsBackgroundTest', 'DISABLED_EmptyStringCallsCallbacks', function() {
   var tts = new cvox.TtsBackground(false);
   var startCalls = 0, endCalls = 0;
   assertCallsCallbacks = function(text, speakCalls) {
diff --git a/chrome/browser/resources/chromeos/drive_internals.html b/chrome/browser/resources/chromeos/drive_internals.html
index 9a3d82b..fbffd55 100644
--- a/chrome/browser/resources/chromeos/drive_internals.html
+++ b/chrome/browser/resources/chromeos/drive_internals.html
@@ -124,21 +124,6 @@
     <ul id="drive-related-preferences">
     </ul>
 
-    <h2 id="app-list-section">Application List</h2>
-    <ul>
-      <li>ETag: <span id="app-list-etag"></span></li>
-    </ul>
-    <table>
-      <tbody id="app-list-items">
-        <tr>
-          <th>App Name</th>
-          <th>App ID</th>
-          <th>Object Type</th>
-          <th>Support Create</th>
-        </tr>
-      </tbody>
-    </table>
-
     <h2 id="event-log-section">Event Log</h2>
     <ul id="event-log">
     </ul>
diff --git a/chrome/browser/resources/chromeos/drive_internals.js b/chrome/browser/resources/chromeos/drive_internals.js
index 5860cc6..b713cab 100644
--- a/chrome/browser/resources/chromeos/drive_internals.js
+++ b/chrome/browser/resources/chromeos/drive_internals.js
@@ -164,27 +164,6 @@
   $('root-resource-id').textContent = aboutResource['root-resource-id'];
 }
 
-/**
- * Updates the summary about app list.
- * @param {Object} appList Dictionary describing app list.
- */
-function updateAppList(appList) {
-  $('app-list-etag').textContent = appList['etag'];
-
-  var itemContainer = $('app-list-items');
-  for (var i = 0; i < appList['items'].length; i++) {
-    var app = appList['items'][i];
-    var tr = document.createElement('tr');
-    tr.className = 'installed-app';
-    tr.appendChild(createElementFromText('td', app.name));
-    tr.appendChild(createElementFromText('td', app.application_id));
-    tr.appendChild(createElementFromText('td', app.object_type));
-    tr.appendChild(createElementFromText('td', app.supports_create));
-
-    itemContainer.appendChild(tr);
-  }
-}
-
 /*
  * Updates the summary about delta update status.
  * @param {Object} deltaUpdateStatus Dictionary describing delta update status.
diff --git a/chrome/browser/resources/chromeos/login/custom_elements_login.html b/chrome/browser/resources/chromeos/login/custom_elements_login.html
index 97121b2..dd85949 100644
--- a/chrome/browser/resources/chromeos/login/custom_elements_login.html
+++ b/chrome/browser/resources/chromeos/login/custom_elements_login.html
@@ -21,13 +21,12 @@
 <include src="oobe_buttons.html">
 <include src="oobe_change_picture.html">
 <include src="oobe_dialog.html">
+<include src="oobe_enrollment.html">
 <include src="oobe_reset.html">
 <include src="oobe_reset_confirmation_overlay.html">
 <include src="oobe_voice_interaction_value_prop.html">
 <include src="oobe_wait_for_container_ready.html">
 <include src="encryption_migration.html">
-<include src="enterprise_card.html">
-<include src="enterprise_header.html">
 <include src="enrollment_license_card.html">
 <include src="sync_consent.html">
 <include src="fingerprint_setup.html">
diff --git a/chrome/browser/resources/chromeos/login/custom_elements_login.js b/chrome/browser/resources/chromeos/login/custom_elements_login.js
index a8ca617..feda281 100644
--- a/chrome/browser/resources/chromeos/login/custom_elements_login.js
+++ b/chrome/browser/resources/chromeos/login/custom_elements_login.js
@@ -31,8 +31,6 @@
 // <include src="encryption_migration.js">
 // <include src="oobe_voice_interaction_value_prop.js">
 // <include src="oobe_wait_for_container_ready.js">
-// <include src="enterprise_card.js">
-// <include src="enterprise_header.js">
 // <include src="enrollment_license_card.js">
 // <include src="sync_consent.js">
 // <include src="fingerprint_setup.js">
diff --git a/chrome/browser/resources/chromeos/login/custom_elements_oobe.html b/chrome/browser/resources/chromeos/login/custom_elements_oobe.html
index 59419af..825134c 100644
--- a/chrome/browser/resources/chromeos/login/custom_elements_oobe.html
+++ b/chrome/browser/resources/chromeos/login/custom_elements_oobe.html
@@ -20,6 +20,7 @@
 <include src="oobe_buttons.html">
 <include src="oobe_change_picture.html">
 <include src="oobe_dialog.html">
+<include src="oobe_enrollment.html">
 <include src="oobe_eula.html">
 <include src="oobe_hid_detection.html">
 <include src="oobe_reset.html">
@@ -35,8 +36,6 @@
 <include src="offline_ad_login.html">
 <include src="active_directory_password_change.html">
 <include src="arc_terms_of_service.html">
-<include src="enterprise_card.html">
-<include src="enterprise_header.html">
 <include src="enrollment_license_card.html">
 <include src="sync_consent.html">
 <include src="fingerprint_setup.html">
diff --git a/chrome/browser/resources/chromeos/login/custom_elements_oobe.js b/chrome/browser/resources/chromeos/login/custom_elements_oobe.js
index 20e88e5..742a2e2 100644
--- a/chrome/browser/resources/chromeos/login/custom_elements_oobe.js
+++ b/chrome/browser/resources/chromeos/login/custom_elements_oobe.js
@@ -45,8 +45,6 @@
 // <include src="arc_terms_of_service.js">
 // <include src="oobe_voice_interaction_value_prop.js">
 // <include src="oobe_wait_for_container_ready.js">
-// <include src="enterprise_card.js">
-// <include src="enterprise_header.js">
 // <include src="enrollment_license_card.js">
 // <include src="sync_consent.js">
 // <include src="fingerprint_setup.js">
diff --git a/chrome/browser/resources/chromeos/login/enrollment_license_card.html b/chrome/browser/resources/chromeos/login/enrollment_license_card.html
index 024ce26..58ec66d 100644
--- a/chrome/browser/resources/chromeos/login/enrollment_license_card.html
+++ b/chrome/browser/resources/chromeos/login/enrollment_license_card.html
@@ -4,10 +4,10 @@
 
 <link rel="import" href="chrome://resources/html/polymer.html">
 
+<link rel="import" href="chrome://oobe/custom_elements.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_radio_button/cr_radio_button.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-radio-group/paper-radio-group.html">
-
 <!--
   UI for the Enrollment license type selection.
 
@@ -25,43 +25,36 @@
 <dom-module id="enrollment-license-card">
   <template>
     <link rel="stylesheet" href="gaia_input_form.css">
-    <link rel="stylesheet" href="enterprise_card.css">
-    <link rel="stylesheet" href="enterprise_card_footer.css">
     <link rel="stylesheet" href="enrollment_license_card.css">
+    <style
+        include="shared-style iron-flex iron-flex-alignment iron-positioning">
+    </style>
 
-    <enterprise-card id="license-selection-prompt-card" class="fit">
-      <enterprise-header slot="header"
-                         i18n-values="header-title:oauthEnrollScreenTitle">
-        <hd-iron-icon slot="enterprise-icon"
-                      icon1x="enterprise-header-32:briefcase"
-                      icon2x="enterprise-header-64:briefcase"></hd-iron-icon>
-        <div slot="header-comment" class="header-comment">
-          [[i18nDynamic(locale, 'licenseSelectionCardExplanation')]]
-        </div>
-      </enterprise-header>
-      <div slot="content" class="content flex vertical layout justified">
-        <div>
-          <paper-radio-group selected="{{selected}}"
-                             selectable="cr-radio-button">
-            <template is="dom-repeat" items="[[licenses]]"
-                      id="repeatTemplate">
-              <cr-radio-button class="license-radio-button"
-                               disabled$="[[or_(item.disabled, disabled)]]"
-                               hidden$="[[item.hidden]]" name="[[item.id]]">
-                [[formatTitle_(item)]]
-              </cr-radio-button>
-            </template>
-          </paper-radio-group>
-        </div>
+    <oobe-dialog id="license-selection-prompt-card" has-buttons>
+      <hd-iron-icon slot="oobe-icon"
+                    icon1x="oobe-enrollment-32:briefcase"
+                    icon2x="oobe-enrollment-64:briefcase"></hd-iron-icon>
+      <h1 slot="title" i18n-content="oauthEnrollScreenTitle"></h1>
+      <div slot="subtitle">
+        [[i18nDynamic(locale, 'licenseSelectionCardExplanation')]]
       </div>
-      <div slot="footer" class="footer">
-        <div class="horizontal-reverse justified layout center">
-          <oobe-next-button disabled="[[disabled]]"
-                            on-tap="buttonClicked_"
-                            id="submitButton" class="self-start">
-          </oobe-next-button>
-        </div>
+      <div slot="footer" class="flex layout vertical justified">
+        <paper-radio-group selected="{{selected}}"
+                           selectable="cr-radio-button">
+          <template is="dom-repeat" items="[[licenses]]"
+                    id="repeatTemplate">
+            <cr-radio-button class="license-radio-button"
+                             disabled$="[[or_(item.disabled, disabled)]]"
+                             hidden$="[[item.hidden]]" name="[[item.id]]">
+              [[formatTitle_(item)]]
+            </cr-radio-button>
+          </template>
+        </paper-radio-group>
       </div>
-    </enterprise-card>
+      <div slot="bottom-buttons" class="flex layout horizontal end-justified">
+        <oobe-next-button disabled="[[disabled]]" on-tap="buttonClicked_"
+            class="focus-on-show"></oobe-next-button>
+      </div>
+    </oobe-dialog>
   </template>
 </dom-module>
diff --git a/chrome/browser/resources/chromeos/login/enrollment_license_card.js b/chrome/browser/resources/chromeos/login/enrollment_license_card.js
index 6f3de85..5b04f38 100644
--- a/chrome/browser/resources/chromeos/login/enrollment_license_card.js
+++ b/chrome/browser/resources/chromeos/login/enrollment_license_card.js
@@ -10,7 +10,7 @@
 Polymer({
   is: 'enrollment-license-card',
 
-  behaviors: [I18nBehavior],
+  behaviors: [I18nBehavior, OobeDialogHostBehavior],
 
   properties: {
     /**
@@ -49,8 +49,12 @@
     },
   },
 
-  get submitButton() {
-    return this.$.submitButton;
+  show: function() {
+    this.behaviors.forEach((behavior) => {
+      if (behavior.onBeforeShow)
+        behavior.onBeforeShow.call(this);
+    });
+    this.$['license-selection-prompt-card'].show();
   },
 
   buttonClicked_: function() {
diff --git a/chrome/browser/resources/chromeos/login/enterprise_card.css b/chrome/browser/resources/chromeos/login/enterprise_card.css
deleted file mode 100644
index 914d8ef..0000000
--- a/chrome/browser/resources/chromeos/login/enterprise_card.css
+++ /dev/null
@@ -1,73 +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. */
-
-:host {
-  display: flex;
-  flex-direction: column;
-  height: 100%;
-  position: relative;
-  width: 100%;
-}
-
-.enterprise-header {
-  background-color: white;
-}
-
-.enterprise-footer {
-  background-color: white;
-}
-
-:host(:not(.disabled)) :host(:not(.no-footer)) {
-  box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.17);
-  /* z-index is needed to make shadow visible. */
-  z-index: 1;
-}
-
-.header-container {
-  padding: 64px 64px 0 64px;
-}
-
-.header-comment {
-  color: #333;
-  font-size: 13px;
-  line-height: 20px;
-  min-height: 40px;
-}
-
-.content-container {
-  padding: 0 64px 0 64px;
-  z-index: 1;
-}
-
-.footer-container {
-  height: 44px;
-  padding: 18px;
-}
-
-.overlay {
-  background-color: rgba(0, 0, 0, 0.5);
-  display: none;
-  height: 100%;
-  position: absolute;
-  right: 0;
-  top: 0;
-  width: 100%;
-  z-index: 11;
-}
-
-paper-progress#progress-bar {
-  --paper-progress-active-color: var(--google-blue-500);
-  --paper-progress-container-color: var(--google-blue-100);
-  display: none;
-  height: 3px;
-  padding-top: 16px;
-  width: 100%;
-}
-
-:host(.full-disabled) #full-overlay,
-:host(.disabled) #actionable-overlay,
-:host(.disabled) #progress-bar,
-:host(.with-progress) #progress-bar {
-  display: block;
-}
diff --git a/chrome/browser/resources/chromeos/login/enterprise_card.html b/chrome/browser/resources/chromeos/login/enterprise_card.html
deleted file mode 100644
index 0055b59..0000000
--- a/chrome/browser/resources/chromeos/login/enterprise_card.html
+++ /dev/null
@@ -1,57 +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. -->
-
-<link rel="import" href="chrome://resources/html/polymer.html">
-
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-progress/paper-progress.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html">
-
-<!--
-  Simple Gaia v2 looking card which should be used for local OOBE UI elements
-  that look like Gaia v2 card (large white dialog with buttons in footer).
-  This is usually used in Enterprise Enrollment flow.
-
-  Example:
-
-    <enterprise-card>
-      <enterprise-header class="header" icon="enterprise"
-      i18n-values="header-title:...;
-                   header-comment:...">
-      </enterprise-header >
-      <div class="content">
-        ...
-      </div
-      <div class="footer">
-        <oobe-next-button class="autofocus main-button"> /
-        <gaia-button> / <oobe-back-button>
-      </div>
-    </enterprise-card>
-
-  Add class |header| to all which you want to go inside header.  Similar
-  with classes |content| and |footer|.
--->
-<dom-module id="enterprise-card">
-  <template>
-    <link rel="stylesheet" href="enterprise_card.css">
-    <link rel="stylesheet" href="oobe_flex_layout.css">
-    <div class="enterprise-header vertical layout justified layout-start">
-      <div class="header-container flex vertical layout relative">
-        <slot name="header"></slot>
-      </div>
-      <paper-progress class="self-end" id="progress-bar" indeterminate>
-      </paper-progress>
-    </div>
-    <div class="flex vertical layout">
-      <div class="content-container flex vertical layout">
-        <slot name="content"></slot>
-      </div>
-      <div class="footer-container horizontal layout center"
-           hidden="[[noFooter]]">
-        <slot name="footer"></slot>
-      </div>
-      <div id="actionable-overlay" class="overlay"></div>
-    </div>
-    <div id="full-overlay" class="overlay"></div>
-  </template>
-</dom-module>
diff --git a/chrome/browser/resources/chromeos/login/enterprise_card.js b/chrome/browser/resources/chromeos/login/enterprise_card.js
deleted file mode 100644
index 25ed6c9..0000000
--- a/chrome/browser/resources/chromeos/login/enterprise_card.js
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-Polymer({
-  is: 'enterprise-card',
-
-  properties: {
-    /**
-     * Control visibility of the footer container.
-     */
-    noFooter: {
-      type: Boolean,
-      value: false,
-    },
-  },
-
-  /** @override */
-  focus: function() {
-    if (this.autofocus) {
-      this.autofocus.focus();
-    }
-  },
-
-  /** @override */
-  ready: function() {
-    this.autofocus = this.querySelector('.autofocus');
-  },
-});
diff --git a/chrome/browser/resources/chromeos/login/enterprise_header.css b/chrome/browser/resources/chromeos/login/enterprise_header.css
deleted file mode 100644
index 0139178..0000000
--- a/chrome/browser/resources/chromeos/login/enterprise_header.css
+++ /dev/null
@@ -1,17 +0,0 @@
-/* Copyright 2015 The Chromium Authors. All rights reserved.
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file. */
-
-:host {
-  display: flex;
-  flex-direction: column;
-  position: relative;
-}
-
-.header-title {
-  color: #333;
-  display: flex;
-  flex-direction: column-reverse;
-  font-size: 28px;
-  height: 64px;
-}
diff --git a/chrome/browser/resources/chromeos/login/enterprise_header.js b/chrome/browser/resources/chromeos/login/enterprise_header.js
deleted file mode 100644
index bc78c0a..0000000
--- a/chrome/browser/resources/chromeos/login/enterprise_header.js
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-Polymer({
-  is: 'enterprise-header',
-
-  behaviors: [I18nBehavior],
-
-  properties: {
-    /**
-     * Title of the header
-     * @type {String}
-     */
-    headerTitle: {type: String, value: ''},
-  },
-});
diff --git a/chrome/browser/resources/chromeos/login/oobe_dialog.css b/chrome/browser/resources/chromeos/login/oobe_dialog.css
index 2dd5681..0f68c18 100644
--- a/chrome/browser/resources/chromeos/login/oobe_dialog.css
+++ b/chrome/browser/resources/chromeos/login/oobe_dialog.css
@@ -47,6 +47,13 @@
                + var(--subtitle-font-distance-to-baseline)) 0 0 0;
 }
 
+#oobe-progress ::slotted(paper-progress) {
+  --paper-progress-active-color: var(--google-blue-500);
+  --paper-progress-container-color: var(--google-blue-100);
+  height: 3px;
+  width: 100%;
+}
+
 #footer-container {
   padding: 0 64px;
 }
diff --git a/chrome/browser/resources/chromeos/login/oobe_dialog.html b/chrome/browser/resources/chromeos/login/oobe_dialog.html
index 8644d9c..5b145a7 100644
--- a/chrome/browser/resources/chromeos/login/oobe_dialog.html
+++ b/chrome/browser/resources/chromeos/login/oobe_dialog.html
@@ -67,6 +67,9 @@
       <div id="oobe-subtitle" class="layout vertical">
         <slot name="subtitle"></slot>
       </div>
+      <div id="oobe-progress" class="layout vertical">
+        <slot name="progress"></slot>
+      </div>
     </div>
     <div id="footer-container" noFooterPadding$="[[noFooterPadding]]"
         class="flex layout vertical">
diff --git a/chrome/browser/resources/chromeos/login/enterprise_header.html b/chrome/browser/resources/chromeos/login/oobe_enrollment.html
similarity index 67%
rename from chrome/browser/resources/chromeos/login/enterprise_header.html
rename to chrome/browser/resources/chromeos/login/oobe_enrollment.html
index ab383a1..c227586 100644
--- a/chrome/browser/resources/chromeos/login/enterprise_header.html
+++ b/chrome/browser/resources/chromeos/login/oobe_enrollment.html
@@ -1,13 +1,13 @@
-<!-- Copyright 2015 The Chromium Authors. All rights reserved.
+<!-- 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. -->
 
 <link rel="import" href="chrome://resources/html/polymer.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-iconset-svg/iron-iconset-svg.html">
 
-<!-- Icons to use in enterprise-header -->
+<!-- Icons to use during enterprise enrollment -->
 
-<iron-iconset-svg name="enterprise-header-32" size="32">
+<iron-iconset-svg name="oobe-enrollment-32" size="32">
   <svg>
     <defs>
       <g id="briefcase" fill="none" fill-rule="evenodd">
@@ -20,7 +20,7 @@
   </svg>
 </iron-iconset-svg>
 
-<iron-iconset-svg name="enterprise-header-64" size="64">
+<iron-iconset-svg name="oobe-enrollment-64" size="64">
   <svg>
     <defs>
       <g id="briefcase" fill="none" fill-rule="evenodd">
@@ -32,24 +32,3 @@
     </defs>
   </svg>
 </iron-iconset-svg>
-
-
-<!--
-  Header for enterprise-card.
--->
-<dom-module id="enterprise-header">
-  <link rel="stylesheet" href="enterprise_header.css">
-  <link rel="stylesheet" href="oobe_flex_layout.css">
-
-  <template>
-    <div class="flex vertical layout start start-justified">
-        <div id="icon-container" class="vertical layout center">
-          <slot name="enterprise-icon"></slot>
-        </div>
-      <div class="header-title" hidden$="{{!headerTitle}}">
-        <span>[[headerTitle]]</span>
-      </div>
-      <slot name="header-comment"></slot>
-    </div>
-  </template>
-</dom-module>
diff --git a/chrome/browser/resources/chromeos/login/oobe_screen_oauth_enrollment.css b/chrome/browser/resources/chromeos/login/oobe_screen_oauth_enrollment.css
index 8aac607..89c8cd1 100644
--- a/chrome/browser/resources/chromeos/login/oobe_screen_oauth_enrollment.css
+++ b/chrome/browser/resources/chromeos/login/oobe_screen_oauth_enrollment.css
@@ -127,3 +127,4 @@
 #oauth-enrollment.saml #oauth-enroll-navigation {
   color: rgba(0, 0, 0, .54);
 }
+
diff --git a/chrome/browser/resources/chromeos/login/oobe_screen_oauth_enrollment.html b/chrome/browser/resources/chromeos/login/oobe_screen_oauth_enrollment.html
index 4ede965..284b968 100644
--- a/chrome/browser/resources/chromeos/login/oobe_screen_oauth_enrollment.html
+++ b/chrome/browser/resources/chromeos/login/oobe_screen_oauth_enrollment.html
@@ -5,13 +5,11 @@
 <link rel="import" href="chrome://oobe/custom_elements.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-iconset-svg/iron-iconset-svg.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
-<link rel="stylesheet" href="enterprise_card.css">
 <link rel="stylesheet" href="gaia_card_parameters.css">
 
 <div id="oauth-enrollment" class="step no-logo hidden" hidden>
   <div id="oauth-enroll-step-contents">
     <link rel="stylesheet" href="oobe_flex_layout.css">
-    <link rel="stylesheet" href="enterprise_card_footer.css">
     <div id="oauth-enroll-step-signin">
       <oobe-dialog class="gaia-dialog" role="dialog" id="enrollment-gaia-dialog"
                    has-buttons no-header no-footer-padding>
@@ -21,26 +19,24 @@
         </div>
         <div slot="bottom-buttons" class="flex layout horizontal center">
           <oobe-back-button id="oobe-signin-back-button"></oobe-back-button>
-          <div class="flex">
-          </div>
         </div>
       </oobe-dialog>
     </div>
     <div id="oauth-enroll-step-working">
-        <enterprise-card id="oauth-enroll-working"
-            class="with-progress" no-footer>
-            <enterprise-header slot="header"
-                i18n-values="header-title:oauthEnrollScreenTitle;">
-              <hd-iron-icon slot="enterprise-icon"
-                  icon1x="enterprise-header-32:briefcase"
-                  icon2x="enterprise-header-64:briefcase"></hd-iron-icon>
-            </enterprise-header >
-            <div slot="content" class="layout vertical start">
-                <div class="oauth-enroll-step-message">
-                    <span i18n-content="oauthEnrollWorking"></span>
-                </div>
+        <oobe-dialog id="oauth-enroll-working">
+          <hd-iron-icon slot="oobe-icon"
+                        icon1x="oobe-enrollment-32:briefcase"
+                        icon2x="oobe-enrollment-64:briefcase"></hd-iron-icon>
+          <h1 slot="title" i18n-content="oauthEnrollScreenTitle"></h1>
+          <paper-progress slot="progress" indeterminate>
+          </paper-progress>
+
+          <div slot="footer" class="flex layout vertical">
+            <div class="oauth-enroll-step-message">
+              <span i18n-content="oauthEnrollWorking"></span>
             </div>
-        </enterprise-card>
+          </div>
+        </oobe-dialog>
     </div>
     <div id="oauth-enroll-step-license">
       <enrollment-license-card id="oauth-enroll-license-ui"
@@ -60,91 +56,82 @@
       </notification-card>
     </div>
     <div id="oauth-enroll-step-success" role="alert">
-      <enterprise-card id="oauth-enroll-success-card">
-        <enterprise-header slot="header"
-            i18n-values="header-title:oauthEnrollSuccessTitle">
-          <hd-iron-icon slot="enterprise-icon"
-              icon1x="enterprise-header-32:briefcase"
-              icon2x="enterprise-header-64:briefcase"></hd-iron-icon>
-          <div slot="header-comment" class="header-comment"
-               i18n-content="oauthEnrollSuccess">
-          </div>
-        </enterprise-header >
-        <div slot="content" class="layout vertical center">
+      <oobe-dialog id="oauth-enroll-success-card" has-buttons>
+        <hd-iron-icon slot="oobe-icon"
+                      icon1x="oobe-enrollment-32:briefcase"
+                      icon2x="oobe-enrollment-64:briefcase"></hd-iron-icon>
+        <h1 slot="title" i18n-content="oauthEnrollSuccessTitle"></h1>
+        <div slot="subtitle" i18n-content="oauthEnrollSuccess"></div>
+        <div slot="footer" class="flex layout vertical center end-justified">
           <img srcset="images/enrollment_success_illustration_1x.png 1x,
                   images/enrollment_success_illustration_2x.png 2x"
-                i18n-values="alt:enrollmentSuccessIllustrationTitle">
+               i18n-values="alt:enrollmentSuccessIllustrationTitle">
         </div>
-        <div slot="footer"
-            class="footer horizontal-reverse justified layout center">
-          <gaia-button i18n-content="oauthEnrollDone"
-              id="enroll-success-done-button" class="autofocus"></gaia-button>
+        <div slot="bottom-buttons" class="layout horizontal end-justified">
+          <oobe-text-button inverse id="enroll-success-done-button"
+              class="focus-on-show">
+            <div i18n-content="oauthEnrollDone"></div>
+          </oobe-text-button>
         </div>
-      </enterprise-card>
+      </oobe-dialog>
     </div>
       <div id="oauth-enroll-step-abe-success" role="alert">
-        <enterprise-card id="oauth-enroll-abe-success-card">
-            <enterprise-header slot="header"
-               i18n-values="header-title:oauthEnrollSuccessTitle">
-              <hd-iron-icon slot="enterprise-icon"
-                  icon1x="enterprise-header-32:briefcase"
-                  icon2x="enterprise-header-64:briefcase"></hd-iron-icon>
-              <div slot="header-comment" class="header-comment"
-                   id="oauth-enroll-abe-success-comment-no-domain"
-                   i18n-content="oauthEnrollSuccess"></div>
-              <div slot="header-comment" class="header-comment"
-                     id="oauth-enroll-abe-success-comment-domain" hidden></div>
-            </enterprise-header >
-            <div slot="content" class="layout vertical center">
-              <img srcset="images/enrollment_success_illustration_1x.png 1x,
-                    images/enrollment_success_illustration_2x.png 2x"
-                   i18n-values="alt:enrollmentSuccessIllustrationTitle">
-            </div>
-          <div slot="footer"
-              class="footer horizontal-reverse justified layout center">
-            <gaia-button i18n-content="oauthEnrollDone"
-                id="enroll-success-abe-done-button"
-                class="autofocus"></gaia-button>
-           </div>
-        </enterprise-card>
+        <oobe-dialog id="oauth-enroll-abe-success-card" has-buttons>
+          <hd-iron-icon slot="oobe-icon"
+                        icon1x="oobe-enrollment-32:briefcase"
+                        icon2x="oobe-enrollment-64:briefcase"></hd-iron-icon>
+          <h1 slot="title" i18n-content="oauthEnrollSuccessTitle"></h1>
+          <div slot="subtitle" id="oauth-enroll-abe-success-comment-no-domain"
+               i18n-content="oauthEnrollSuccess"></div>
+          <div slot="subtitle" id="oauth-enroll-abe-success-comment-domain"
+               hidden></div>
+          <div slot="footer" class="flex layout vertical center end-justified">
+            <img srcset="images/enrollment_success_illustration_1x.png 1x,
+                  images/enrollment_success_illustration_2x.png 2x"
+                 i18n-values="alt:enrollmentSuccessIllustrationTitle">
+          </div>
+          <div slot="bottom-buttons" class="layout horizontal end-justified">
+            <oobe-text-button inverse id="enroll-success-abe-done-button"
+                class="focus-on-show">
+              <div i18n-content="oauthEnrollDone"></div>
+            </oobe-text-button>
+          </div>
+        </oobe-dialog>
     </div>
     <div id="oauth-enroll-step-attribute-prompt">
-        <enterprise-card id="oauth-enroll-attribute-prompt-card">
-            <enterprise-header slot="header"
-               i18n-values="header-title:oauthEnrollScreenTitle">
-              <hd-iron-icon slot="enterprise-icon"
-                  icon1x="enterprise-header-32:briefcase"
-                  icon2x="enterprise-header-64:briefcase"></hd-iron-icon>
-              <div slot="header-comment" class="header-comment"
-                   i18n-content="oauthEnrollDeviceInformation">
-              </div>
-            </enterprise-header >
-            <div slot="content" class="layout vertical start">
-                <div class="oauth-enroll-step-message">
+      <oobe-dialog id="oauth-enroll-attribute-prompt-card" has-buttons>
+        <hd-iron-icon slot="oobe-icon"
+                      icon1x="oobe-enrollment-32:briefcase"
+                      icon2x="oobe-enrollment-64:briefcase"></hd-iron-icon>
+        <h1 slot="title" i18n-content="oauthEnrollScreenTitle"></h1>
+        <div slot="subtitle" i18n-content="oauthEnrollDeviceInformation"></div>
+        <div slot="footer" class="layout vertical start">
+          <div class="oauth-enroll-step-message">
                     <span id="oauth-enroll-attribute-prompt-message"
-                        i18n-content="oauthEnrollAttributeExplanation"></span>
-                    <a href="#" id="oauth-enroll-learn-more-link"
-                        class="oauth-enroll-link"
-                        i18n-content="oauthEnrollExplainAttributeLink"></a>
-                </div>
-                <gaia-input id="oauth-enroll-asset-id" type="text"
-                    class="autofocus">
-                  <div slot="label" i18n-content="oauthEnrollAssetIdLabel">
-                  </div>
-                </gaia-input>
-                <gaia-input id="oauth-enroll-location" type="text">
-                  <div slot="label" i18n-content="oauthEnrollLocationLabel">
-                  </div>
-                </gaia-input>
+                          i18n-content="oauthEnrollAttributeExplanation"></span>
+            <a href="#" id="oauth-enroll-learn-more-link"
+               class="oauth-enroll-link"
+               i18n-content="oauthEnrollExplainAttributeLink"></a>
+          </div>
+          <gaia-input id="oauth-enroll-asset-id" type="text"
+                      class="focus-on-show">
+            <div slot="label" i18n-content="oauthEnrollAssetIdLabel">
             </div>
-            <div slot="footer" class="footer">
-                <oobe-next-button
-                    id="enroll-attributes-submit-button"></oobe-next-button>
-                <oobe-text-button
-                    id="enroll-attributes-skip-button"
-                    i18n-content="oauthEnrollSkip"></oobe-text-button>
+          </gaia-input>
+          <gaia-input id="oauth-enroll-location" type="text">
+            <div slot="label" i18n-content="oauthEnrollLocationLabel">
             </div>
-        </enterprise-card>
+          </gaia-input>
+        </div>
+        <div slot="bottom-buttons" class="layout horizontal end-justified">
+          <oobe-text-button id="enroll-attributes-skip-button">
+            <div i18n-content="oauthEnrollSkip"></div>
+          </oobe-text-button>
+          <div class="flex"></div>
+          <oobe-next-button
+              id="enroll-attributes-submit-button"></oobe-next-button>
+        </div>
+      </oobe-dialog>
     </div>
     <div id="oauth-enroll-step-attribute-prompt-error">
       <notification-card id="oauth-enroll-attribute-prompt-error-card"
diff --git a/chrome/browser/resources/chromeos/login/oobe_screen_oauth_enrollment.js b/chrome/browser/resources/chromeos/login/oobe_screen_oauth_enrollment.js
index a1dca61..432c447 100644
--- a/chrome/browser/resources/chromeos/login/oobe_screen_oauth_enrollment.js
+++ b/chrome/browser/resources/chromeos/login/oobe_screen_oauth_enrollment.js
@@ -387,15 +387,15 @@
       if (step == STEP_SIGNIN) {
         $('oauth-enroll-auth-view').focus();
       } else if (step == STEP_LICENSE_TYPE) {
-        $('oauth-enroll-license-ui').submitButton.focus();
+        $('oauth-enroll-license-ui').show();
       } else if (step == STEP_ERROR) {
         $('oauth-enroll-error-card').submitButton.focus();
       } else if (step == STEP_SUCCESS) {
-        $('oauth-enroll-success-card').focus();
+        $('oauth-enroll-success-card').show();
       } else if (step == STEP_ABE_SUCCESS) {
-        $('oauth-enroll-abe-success-card').focus();
+        $('oauth-enroll-abe-success-card').show();
       } else if (step == STEP_ATTRIBUTE_PROMPT) {
-        $('oauth-enroll-step-attribute-prompt').focus();
+        $('oauth-enroll-attribute-prompt-card').show();
       } else if (step == STEP_ATTRIBUTE_PROMPT_ERROR) {
         $('oauth-enroll-attribute-prompt-error-card').submitButton.focus();
       } else if (step == STEP_ACTIVE_DIRECTORY_JOIN_ERROR) {
diff --git a/chrome/browser/resources/chromeos/login/oobe_update.css b/chrome/browser/resources/chromeos/login/oobe_update.css
index af58bc5..5376f3a 100644
--- a/chrome/browser/resources/chromeos/login/oobe_update.css
+++ b/chrome/browser/resources/chromeos/login/oobe_update.css
@@ -26,11 +26,3 @@
 .update-illustration {
   padding-bottom: 82px;
 }
-
-paper-progress {
-  --paper-progress-active-color: var(--google-blue-500);
-  --paper-progress-secondary-color: var(--google-blue-100);
-  bottom: 0;
-  height: 3px;
-  width: 100%;
-}
diff --git a/chrome/browser/resources/chromeos/login/oobe_update.html b/chrome/browser/resources/chromeos/login/oobe_update.html
index bf2a842..1fdd361 100644
--- a/chrome/browser/resources/chromeos/login/oobe_update.html
+++ b/chrome/browser/resources/chromeos/login/oobe_update.html
@@ -33,10 +33,10 @@
       <div slot="subtitle" class="update-subtitle" id="checkingForUpdateCancelHint"
           i18n-content="cancelUpdateHint" hidden="[[!cancelAllowed]]">
       </div>
-      <div slot="footer" class="flex layout vertical">
-        <paper-progress id="checking-progress" hidden="[[!checkingForUpdate]]">
-        </paper-progress>
-        <div class="flex"></div>
+      <paper-progress slot="progress" id="checking-progress"
+          hidden="[[!checkingForUpdate]]">
+      </paper-progress>
+      <div slot="footer" class="flex layout vertical end-justified">
         <img class="update-illustration"
             srcset="images/1x/updating_1x.png 1x,
                     images/2x/updating_2x.png 2x">
@@ -56,10 +56,10 @@
       </div>
       <div slot="subtitle" class="update-subtitle" i18n-content="updateCompeletedMsg"
           hidden="[[!updateCompleted]]"></div>
+      <paper-progress slot="progress" min="0" max="100"
+          value="[[progressValue]]" hidden="[[updateCompleted]]">
+      </paper-progress>
       <div slot="footer" class="flex layout vertical">
-        <paper-progress min="0" max="100" value="[[progressValue]]"
-          hidden="[[updateCompleted]]">
-        </paper-progress>
         <div id="estimated-time-left" class="progress-message text"
             hidden="[[isNotAllowedOrUpdateCompleted_(estimatedTimeLeftShown,
                                                      updateCompleted)]]">
diff --git a/chrome/browser/resources/print_preview/new/print_preview_search_box.html b/chrome/browser/resources/print_preview/new/print_preview_search_box.html
index a80550f..e1563aa 100644
--- a/chrome/browser/resources/print_preview/new/print_preview_search_box.html
+++ b/chrome/browser/resources/print_preview/new/print_preview_search_box.html
@@ -41,7 +41,7 @@
     <div id="icon" class="cr-icon icon-search" alt=""></div>
     <cr-input type="search" id="searchInput" class="search-box-input"
         on-search="onSearchTermSearch" on-input="onSearchTermInput"
-        incremental aria-label$="[[label]]" placeholder="[[label]]"
+        aria-label$="[[label]]" placeholder="[[label]]"
         autofocus="[[autofocus]]" spellcheck="false"></cr-input>
   </template>
   <script src="print_preview_search_box.js"></script>
diff --git a/chrome/browser/resources/settings/settings_page/settings_subpage_search.html b/chrome/browser/resources/settings/settings_page/settings_subpage_search.html
index 100316f..916eb74 100644
--- a/chrome/browser/resources/settings/settings_page/settings_subpage_search.html
+++ b/chrome/browser/resources/settings/settings_page/settings_subpage_search.html
@@ -70,7 +70,7 @@
       }
     </style>
     <iron-icon id="searchIcon" icon="cr:search"></iron-icon>
-    <cr-input id="searchInput" on-search="onSearchTermSearch" incremental
+    <cr-input id="searchInput" on-search="onSearchTermSearch"
         on-input="onSearchTermInput" aria-label$="[[label]]" type="search"
         autofocus="[[autofocus]]" placeholder="[[label]]" spellcheck="false">
       <paper-icon-button-light id="clearSearchContainer" class="icon-cancel"
diff --git a/chrome/browser/safe_browsing/chrome_password_protection_service.cc b/chrome/browser/safe_browsing/chrome_password_protection_service.cc
index 018717f..b8f8a2e 100644
--- a/chrome/browser/safe_browsing/chrome_password_protection_service.cc
+++ b/chrome/browser/safe_browsing/chrome_password_protection_service.cc
@@ -1064,9 +1064,6 @@
   const Origin origin = Origin::Create(url);
 
   if (action == WarningAction::CHANGE_PASSWORD) {
-    MaybeLogPasswordReuseDialogInteraction(
-        GetNavigationIDFromPrefsByOrigin(profile_->GetPrefs(), origin),
-        PasswordReuseDialogInteraction::WARNING_ACTION_TAKEN);
     // Directly open enterprise change password page in a new tab for enterprise
     // reuses.
     if (password_type == PasswordReuseEvent::ENTERPRISE_PASSWORD) {
@@ -1117,7 +1114,7 @@
   // event the user is responding to, so just pick the first one.
   MaybeLogPasswordReuseDialogInteraction(
       GetFirstNavIdOrZero(profile_->GetPrefs()),
-      PasswordReuseDialogInteraction::WARNING_ACTION_TAKEN);
+      PasswordReuseDialogInteraction::WARNING_ACTION_TAKEN_ON_SETTINGS);
   // Opens change password page in a new tab for user to change password.
   OpenUrl(web_contents, GetDefaultChangePasswordURL(),
           content::Referrer(web_contents->GetLastCommittedURL(),
diff --git a/chrome/browser/sessions/tab_loader_unittest.cc b/chrome/browser/sessions/tab_loader_unittest.cc
index d29c57a..c0f0885 100644
--- a/chrome/browser/sessions/tab_loader_unittest.cc
+++ b/chrome/browser/sessions/tab_loader_unittest.cc
@@ -350,16 +350,20 @@
   // it such that there are 2 max simultaneous tab loads, and 3 maximum tabs to
   // restore.
   std::map<std::string, std::string> params;
-  params[rc::kInfiniteSessionRestore_MinSimultaneousTabLoads] = "2";
-  params[rc::kInfiniteSessionRestore_MaxSimultaneousTabLoads] = "2";
-  params[rc::kInfiniteSessionRestore_CoresPerSimultaneousTabLoad] = "0";
-  params[rc::kInfiniteSessionRestore_MinTabsToRestore] = "1";
-  params[rc::kInfiniteSessionRestore_MaxTabsToRestore] = "3";
+  params[rc::InfiniteSessionRestoreParams::kMinSimultaneousTabLoads.name] = "2";
+  params[rc::InfiniteSessionRestoreParams::kMaxSimultaneousTabLoads.name] = "2";
+  params[rc::InfiniteSessionRestoreParams::kCoresPerSimultaneousTabLoad.name] =
+      "0";
+  params[rc::InfiniteSessionRestoreParams::kMinTabsToRestore.name] = "1";
+  params[rc::InfiniteSessionRestoreParams::kMaxTabsToRestore.name] = "3";
 
   // Disable these policy features.
-  params[rc::kInfiniteSessionRestore_MbFreeMemoryPerTabToRestore] = "0";
-  params[rc::kInfiniteSessionRestore_MaxTimeSinceLastUseToRestore] = "0";
-  params[rc::kInfiniteSessionRestore_MinSiteEngagementToRestore] = "0";
+  params[rc::InfiniteSessionRestoreParams::kMbFreeMemoryPerTabToRestore.name] =
+      "0";
+  params[rc::InfiniteSessionRestoreParams::kMaxTimeSinceLastUseToRestore.name] =
+      "0";
+  params[rc::InfiniteSessionRestoreParams::kMinSiteEngagementToRestore.name] =
+      "0";
 
   variations::testing::VariationParamsManager variations_manager;
   variations_manager.SetVariationParamsWithFeatureAssociations(
diff --git a/chrome/browser/speech/extension_api/tts_engine_delegate_factory_impl.cc b/chrome/browser/speech/extension_api/tts_engine_delegate_factory_impl.cc
new file mode 100644
index 0000000..b7074b9
--- /dev/null
+++ b/chrome/browser/speech/extension_api/tts_engine_delegate_factory_impl.cc
@@ -0,0 +1,52 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/speech/extension_api/tts_engine_delegate_factory_impl.h"
+
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/signin/identity_manager_factory.h"
+#include "chrome/browser/signin/signin_manager_factory.h"
+#include "chrome/browser/sync/profile_sync_service_factory.h"
+#include "components/keyed_service/content/browser_context_dependency_manager.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/pref_service.h"
+
+TtsEngineDelegateFactoryImpl::TtsEngineDelegateFactoryImpl()
+    : BrowserContextKeyedServiceFactory(
+          "TtsEngineService",
+          BrowserContextDependencyManager::GetInstance()) {}
+
+TtsEngineDelegateFactoryImpl::~TtsEngineDelegateFactoryImpl() = default;
+
+// static
+TtsEngineDelegateImpl* TtsEngineDelegateFactoryImpl::GetForBrowserContext(
+    content::BrowserContext* browser_context) {
+  return static_cast<TtsEngineDelegateImpl*>(
+      GetInstance()->GetServiceForBrowserContext(browser_context, true));
+}
+
+// static
+TtsEngineDelegateFactoryImpl* TtsEngineDelegateFactoryImpl::GetInstance() {
+  return base::Singleton<TtsEngineDelegateFactoryImpl>::get();
+}
+
+TtsEngineDelegate*
+TtsEngineDelegateFactoryImpl::GetTtsEngineDelegateForBrowserContext(
+    content::BrowserContext* browser_context) {
+  return static_cast<TtsEngineDelegateImpl*>(
+      GetServiceForBrowserContext(browser_context, true));
+}
+
+KeyedService* TtsEngineDelegateFactoryImpl::BuildServiceInstanceFor(
+    content::BrowserContext* context) const {
+  return new TtsEngineDelegateImpl(context);
+}
+
+bool TtsEngineDelegateFactoryImpl::ServiceIsNULLWhileTesting() const {
+  return false;
+}
+
+bool TtsEngineDelegateFactoryImpl::ServiceIsCreatedWithBrowserContext() const {
+  return false;
+}
diff --git a/chrome/browser/speech/extension_api/tts_engine_delegate_factory_impl.h b/chrome/browser/speech/extension_api/tts_engine_delegate_factory_impl.h
new file mode 100644
index 0000000..5510ed60
--- /dev/null
+++ b/chrome/browser/speech/extension_api/tts_engine_delegate_factory_impl.h
@@ -0,0 +1,47 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SPEECH_EXTENSION_API_TTS_ENGINE_DELEGATE_FACTORY_IMPL_H_
+#define CHROME_BROWSER_SPEECH_EXTENSION_API_TTS_ENGINE_DELEGATE_FACTORY_IMPL_H_
+
+#include "base/macros.h"
+#include "base/memory/singleton.h"
+#include "chrome/browser/speech/extension_api/tts_engine_delegate_impl.h"
+#include "chrome/browser/speech/tts_controller.h"
+#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
+
+// Factory that creates an instance of TtsEngineDelegateImpl given a
+// BrowserContext. Implements the TtsEngineDelegateFactory interface to abstract
+// away the details of chrome extensions from chrome/browser/speech.
+class TtsEngineDelegateFactoryImpl : public BrowserContextKeyedServiceFactory,
+                                     public TtsEngineDelegateFactory {
+ public:
+  // Returns the instance of TtsEngineDelegateImpl associated with
+  // |browser_context| (creating one if none exists).
+  static TtsEngineDelegateImpl* GetForBrowserContext(
+      content::BrowserContext* browser_context);
+
+  // Returns an instance of the factory singleton.
+  static TtsEngineDelegateFactoryImpl* GetInstance();
+
+  // Overridden from TtsEngineDelegateFactory:
+  TtsEngineDelegate* GetTtsEngineDelegateForBrowserContext(
+      content::BrowserContext* browser_context) override;
+
+ private:
+  friend struct base::DefaultSingletonTraits<TtsEngineDelegateFactoryImpl>;
+
+  TtsEngineDelegateFactoryImpl();
+  ~TtsEngineDelegateFactoryImpl() override;
+
+  // Overridden from BrowserContextKeyedDelegateFactoryImpl:
+  KeyedService* BuildServiceInstanceFor(
+      content::BrowserContext* profile) const override;
+  bool ServiceIsNULLWhileTesting() const override;
+  bool ServiceIsCreatedWithBrowserContext() const override;
+
+  DISALLOW_COPY_AND_ASSIGN(TtsEngineDelegateFactoryImpl);
+};
+
+#endif  // CHROME_BROWSER_SPEECH_EXTENSION_API_TTS_ENGINE_DELEGATE_FACTORY_IMPL_H_
diff --git a/chrome/browser/speech/extension_api/tts_engine_delegate_impl.cc b/chrome/browser/speech/extension_api/tts_engine_delegate_impl.cc
new file mode 100644
index 0000000..9bbf7fb
--- /dev/null
+++ b/chrome/browser/speech/extension_api/tts_engine_delegate_impl.cc
@@ -0,0 +1,250 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/speech/extension_api/tts_engine_delegate_impl.h"
+
+#include <stddef.h>
+#include <string>
+#include <utility>
+
+#include "base/json/json_writer.h"
+#include "base/values.h"
+#include "build/build_config.h"
+#include "chrome/browser/extensions/component_loader.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/speech/extension_api/tts_engine_delegate_impl.h"
+#include "chrome/browser/speech/extension_api/tts_engine_extension_api.h"
+#include "chrome/browser/speech/extension_api/tts_engine_extension_observer.h"
+#include "chrome/browser/speech/extension_api/tts_extension_api.h"
+#include "chrome/browser/speech/extension_api/tts_extension_api_constants.h"
+#include "chrome/common/extensions/api/speech/tts_engine_manifest_handler.h"
+#include "chrome/common/extensions/extension_constants.h"
+#include "content/public/common/console_message_level.h"
+#include "extensions/browser/event_router.h"
+#include "extensions/browser/extension_host.h"
+#include "extensions/browser/extension_registry.h"
+#include "extensions/browser/extension_system.h"
+#include "extensions/browser/process_manager.h"
+#include "extensions/common/extension.h"
+#include "extensions/common/extension_set.h"
+#include "net/base/network_change_notifier.h"
+
+using extensions::EventRouter;
+using extensions::Extension;
+using extensions::ExtensionSystem;
+
+namespace constants = tts_extension_api_constants;
+
+namespace {
+
+void WarnIfMissingPauseOrResumeListener(Profile* profile,
+                                        EventRouter* event_router,
+                                        std::string extension_id) {
+  bool has_onpause = event_router->ExtensionHasEventListener(
+      extension_id, tts_engine_events::kOnPause);
+  bool has_onresume = event_router->ExtensionHasEventListener(
+      extension_id, tts_engine_events::kOnResume);
+  if (has_onpause == has_onresume)
+    return;
+
+  extensions::ExtensionHost* host =
+      extensions::ProcessManager::Get(profile)->GetBackgroundHostForExtension(
+          extension_id);
+  host->host_contents()->GetMainFrame()->AddMessageToConsole(
+      content::CONSOLE_MESSAGE_LEVEL_WARNING,
+      constants::kErrorMissingPauseOrResume);
+}
+
+}  // namespace
+
+TtsEngineDelegateImpl::TtsEngineDelegateImpl(
+    content::BrowserContext* browser_context)
+#if defined(OS_CHROMEOS)
+    : browser_context_(browser_context)
+#endif  // OS_CHROMEOS
+{
+}
+
+void TtsEngineDelegateImpl::GetVoices(content::BrowserContext* browser_context,
+                                      std::vector<VoiceData>* out_voices) {
+  Profile* profile = Profile::FromBrowserContext(browser_context);
+  EventRouter* event_router = EventRouter::Get(profile);
+  DCHECK(event_router);
+
+  bool is_offline = (net::NetworkChangeNotifier::GetConnectionType() ==
+                     net::NetworkChangeNotifier::CONNECTION_NONE);
+
+  const extensions::ExtensionSet& extensions =
+      extensions::ExtensionRegistry::Get(profile)->enabled_extensions();
+  extensions::ExtensionSet::const_iterator iter;
+  for (iter = extensions.begin(); iter != extensions.end(); ++iter) {
+    const Extension* extension = iter->get();
+
+    if (!event_router->ExtensionHasEventListener(extension->id(),
+                                                 tts_engine_events::kOnSpeak) ||
+        !event_router->ExtensionHasEventListener(extension->id(),
+                                                 tts_engine_events::kOnStop)) {
+      continue;
+    }
+
+    const std::vector<extensions::TtsVoice>* tts_voices =
+        TtsEngineExtensionObserver::GetInstance(profile)->GetAllVoices(
+            extension);
+    if (!tts_voices)
+      continue;
+
+    for (size_t i = 0; i < tts_voices->size(); ++i) {
+      const extensions::TtsVoice& voice = tts_voices->at(i);
+
+      // Don't return remote voices when the system is offline.
+      if (voice.remote && is_offline)
+        continue;
+
+      out_voices->push_back(VoiceData());
+      VoiceData& result_voice = out_voices->back();
+
+      result_voice.native = false;
+      result_voice.name = voice.voice_name;
+      result_voice.lang = voice.lang;
+      result_voice.remote = voice.remote;
+      result_voice.extension_id = extension->id();
+
+      for (std::set<std::string>::const_iterator iter =
+               voice.event_types.begin();
+           iter != voice.event_types.end(); ++iter) {
+        result_voice.events.insert(TtsEventTypeFromString(*iter));
+      }
+
+      // If the extension sends end events, the controller will handle
+      // queueing and send interrupted and cancelled events.
+      if (voice.event_types.find(constants::kEventTypeEnd) !=
+          voice.event_types.end()) {
+        result_voice.events.insert(TTS_EVENT_CANCELLED);
+        result_voice.events.insert(TTS_EVENT_INTERRUPTED);
+      }
+    }
+  }
+}
+
+void TtsEngineDelegateImpl::Speak(Utterance* utterance,
+                                  const VoiceData& voice) {
+  // See if the engine supports the "end" event; if so, we can keep the
+  // utterance around and track it. If not, we're finished with this
+  // utterance now.
+  bool sends_end_event = voice.events.find(TTS_EVENT_END) != voice.events.end();
+
+  std::unique_ptr<base::ListValue> args(new base::ListValue());
+  args->AppendString(utterance->text());
+
+  // Pass through most options to the speech engine, but remove some
+  // that are handled internally.
+  std::unique_ptr<base::DictionaryValue> options(
+      static_cast<base::DictionaryValue*>(utterance->options()->DeepCopy()));
+  if (options->HasKey(constants::kRequiredEventTypesKey))
+    options->Remove(constants::kRequiredEventTypesKey, NULL);
+  if (options->HasKey(constants::kDesiredEventTypesKey))
+    options->Remove(constants::kDesiredEventTypesKey, NULL);
+  if (sends_end_event && options->HasKey(constants::kEnqueueKey))
+    options->Remove(constants::kEnqueueKey, NULL);
+  if (options->HasKey(constants::kSrcIdKey))
+    options->Remove(constants::kSrcIdKey, NULL);
+  if (options->HasKey(constants::kIsFinalEventKey))
+    options->Remove(constants::kIsFinalEventKey, NULL);
+  if (options->HasKey(constants::kOnEventKey))
+    options->Remove(constants::kOnEventKey, NULL);
+
+  // Get the volume, pitch, and rate, but only if they weren't already in
+  // the options. TODO(dmazzoni): these shouldn't be redundant.
+  // http://crbug.com/463264
+  if (!options->HasKey(constants::kRateKey)) {
+    options->SetDouble(constants::kRateKey,
+                       utterance->continuous_parameters().rate);
+  }
+  if (!options->HasKey(constants::kPitchKey)) {
+    options->SetDouble(constants::kPitchKey,
+                       utterance->continuous_parameters().pitch);
+  }
+  if (!options->HasKey(constants::kVolumeKey)) {
+    options->SetDouble(constants::kVolumeKey,
+                       utterance->continuous_parameters().volume);
+  }
+
+  // Add the voice name and language to the options if they're not
+  // already there, since they might have been picked by the TTS controller
+  // rather than directly by the client that requested the speech.
+  if (!options->HasKey(constants::kVoiceNameKey))
+    options->SetString(constants::kVoiceNameKey, voice.name);
+  if (!options->HasKey(constants::kLangKey))
+    options->SetString(constants::kLangKey, voice.lang);
+
+  args->Append(std::move(options));
+  args->AppendInteger(utterance->id());
+
+  std::string json;
+  base::JSONWriter::Write(*args, &json);
+
+  Profile* profile = Profile::FromBrowserContext(utterance->browser_context());
+  auto event = std::make_unique<extensions::Event>(
+      extensions::events::TTS_ENGINE_ON_SPEAK, tts_engine_events::kOnSpeak,
+      std::move(args), profile);
+  EventRouter::Get(profile)->DispatchEventToExtension(utterance->extension_id(),
+                                                      std::move(event));
+}
+
+void TtsEngineDelegateImpl::Stop(Utterance* utterance) {
+  std::unique_ptr<base::ListValue> args(new base::ListValue());
+  Profile* profile = Profile::FromBrowserContext(utterance->browser_context());
+  auto event = std::make_unique<extensions::Event>(
+      extensions::events::TTS_ENGINE_ON_STOP, tts_engine_events::kOnStop,
+      std::move(args), profile);
+  EventRouter::Get(profile)->DispatchEventToExtension(utterance->extension_id(),
+                                                      std::move(event));
+}
+
+void TtsEngineDelegateImpl::Pause(Utterance* utterance) {
+  std::unique_ptr<base::ListValue> args(new base::ListValue());
+  Profile* profile = Profile::FromBrowserContext(utterance->browser_context());
+  auto event = std::make_unique<extensions::Event>(
+      extensions::events::TTS_ENGINE_ON_PAUSE, tts_engine_events::kOnPause,
+      std::move(args), profile);
+  EventRouter* event_router = EventRouter::Get(profile);
+  std::string id = utterance->extension_id();
+  event_router->DispatchEventToExtension(id, std::move(event));
+  WarnIfMissingPauseOrResumeListener(profile, event_router, id);
+}
+
+void TtsEngineDelegateImpl::Resume(Utterance* utterance) {
+  std::unique_ptr<base::ListValue> args(new base::ListValue());
+  Profile* profile = Profile::FromBrowserContext(utterance->browser_context());
+  auto event = std::make_unique<extensions::Event>(
+      extensions::events::TTS_ENGINE_ON_RESUME, tts_engine_events::kOnResume,
+      std::move(args), profile);
+  EventRouter* event_router = EventRouter::Get(profile);
+  std::string id = utterance->extension_id();
+  event_router->DispatchEventToExtension(id, std::move(event));
+  WarnIfMissingPauseOrResumeListener(profile, event_router, id);
+}
+
+bool TtsEngineDelegateImpl::LoadBuiltInTtsExtension() {
+#if defined(OS_CHROMEOS)
+  Profile* profile = Profile::FromBrowserContext(browser_context_);
+  // Check to see if the engine was previously loaded.
+  if (TtsEngineExtensionObserver::GetInstance(profile)->SawExtensionLoad(
+          extension_misc::kSpeechSynthesisExtensionId, true)) {
+    return false;
+  }
+
+  // Load the component extension into this profile.
+  extensions::ExtensionService* extension_service =
+      extensions::ExtensionSystem::Get(profile)->extension_service();
+  DCHECK(extension_service);
+  extension_service->component_loader()->AddChromeOsSpeechSynthesisExtension();
+  return true;
+#else
+  return false;
+#endif
+}
+
+void TtsEngineDelegateImpl::Shutdown() {}
diff --git a/chrome/browser/speech/extension_api/tts_engine_delegate_impl.h b/chrome/browser/speech/extension_api/tts_engine_delegate_impl.h
new file mode 100644
index 0000000..0074b8d1
--- /dev/null
+++ b/chrome/browser/speech/extension_api/tts_engine_delegate_impl.h
@@ -0,0 +1,41 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SPEECH_EXTENSION_API_TTS_ENGINE_DELEGATE_IMPL_H_
+#define CHROME_BROWSER_SPEECH_EXTENSION_API_TTS_ENGINE_DELEGATE_IMPL_H_
+
+#include <vector>
+
+#include "build/build_config.h"
+#include "chrome/browser/speech/tts_engine_delegate.h"
+#include "chrome/common/extensions/api/speech/tts_engine_manifest_handler.h"
+#include "components/keyed_service/core/keyed_service.h"
+
+// This class represents all of the TTS Extension engines for a given
+// BrowserContext. It implements the TtsEngineDelegate interface used by
+// TtsController.
+class TtsEngineDelegateImpl : public TtsEngineDelegate, public KeyedService {
+ public:
+  // Overridden from TtsEngineDelegate:
+  void GetVoices(content::BrowserContext* browser_context,
+                 std::vector<VoiceData>* out_voices) override;
+  void Speak(Utterance* utterance, const VoiceData& voice) override;
+  void Stop(Utterance* utterance) override;
+  void Pause(Utterance* utterance) override;
+  void Resume(Utterance* utterance) override;
+  bool LoadBuiltInTtsExtension() override;
+
+  // Overridden from KeyedService:
+  void Shutdown() override;
+
+ private:
+  friend class TtsEngineDelegateFactoryImpl;
+  explicit TtsEngineDelegateImpl(content::BrowserContext* browser_context);
+
+#if defined(OS_CHROMEOS)
+  content::BrowserContext* browser_context_;
+#endif  // OS_CHROMEOS
+};
+
+#endif  // CHROME_BROWSER_SPEECH_EXTENSION_API_TTS_ENGINE_DELEGATE_IMPL_H_
diff --git a/chrome/browser/speech/extension_api/tts_engine_extension_api.cc b/chrome/browser/speech/extension_api/tts_engine_extension_api.cc
index 0c275b9..4eb1983 100644
--- a/chrome/browser/speech/extension_api/tts_engine_extension_api.cc
+++ b/chrome/browser/speech/extension_api/tts_engine_extension_api.cc
@@ -8,36 +8,21 @@
 #include <string>
 #include <utility>
 
-#include "base/json/json_writer.h"
-#include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
-#include "build/build_config.h"
-#include "chrome/browser/extensions/component_loader.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/speech/extension_api/tts_engine_delegate_impl.h"
 #include "chrome/browser/speech/extension_api/tts_engine_extension_observer.h"
 #include "chrome/browser/speech/extension_api/tts_extension_api.h"
 #include "chrome/browser/speech/extension_api/tts_extension_api_constants.h"
 #include "chrome/browser/speech/tts_controller.h"
 #include "chrome/common/extensions/api/speech/tts_engine_manifest_handler.h"
 #include "chrome/common/extensions/extension_constants.h"
-#include "content/public/browser/render_frame_host.h"
-#include "content/public/browser/render_process_host.h"
-#include "content/public/browser/web_contents.h"
-#include "content/public/common/console_message_level.h"
-#include "extensions/browser/event_router.h"
-#include "extensions/browser/extension_host.h"
-#include "extensions/browser/extension_registry.h"
-#include "extensions/browser/extension_system.h"
 #include "extensions/browser/process_manager.h"
 #include "extensions/common/extension.h"
-#include "extensions/common/extension_set.h"
-#include "net/base/network_change_notifier.h"
 #include "ui/base/l10n/l10n_util.h"
 
-using extensions::EventRouter;
 using extensions::Extension;
-using extensions::ExtensionSystem;
 
 namespace constants = tts_extension_api_constants;
 
@@ -48,223 +33,6 @@
 const char kOnResume[] = "ttsEngine.onResume";
 };  // namespace tts_engine_events
 
-namespace {
-
-void WarnIfMissingPauseOrResumeListener(
-    Profile* profile, EventRouter* event_router, std::string extension_id) {
-  bool has_onpause = event_router->ExtensionHasEventListener(
-      extension_id, tts_engine_events::kOnPause);
-  bool has_onresume = event_router->ExtensionHasEventListener(
-      extension_id, tts_engine_events::kOnResume);
-  if (has_onpause == has_onresume)
-    return;
-
-  extensions::ExtensionHost* host =
-      extensions::ProcessManager::Get(profile)
-          ->GetBackgroundHostForExtension(extension_id);
-  host->host_contents()->GetMainFrame()->AddMessageToConsole(
-      content::CONSOLE_MESSAGE_LEVEL_WARNING,
-      constants::kErrorMissingPauseOrResume);
-}
-
-const std::vector<extensions::TtsVoice>* GetVoicesInternal(
-    content::BrowserContext* context,
-    const extensions::Extension* extension) {
-  Profile* profile = Profile::FromBrowserContext(context);
-  const std::vector<extensions::TtsVoice>* voices =
-      TtsEngineExtensionObserver::GetInstance(profile)->GetRuntimeVoices(
-          extension->id());
-  return voices ? voices : extensions::TtsVoices::GetTtsVoices(extension);
-}
-
-}  // namespace
-
-TtsExtensionEngine* TtsExtensionEngine::GetInstance() {
-  return base::Singleton<TtsExtensionEngine>::get();
-}
-
-void TtsExtensionEngine::GetVoices(content::BrowserContext* browser_context,
-    std::vector<VoiceData>* out_voices) {
-  Profile* profile = Profile::FromBrowserContext(browser_context);
-  EventRouter* event_router = EventRouter::Get(profile);
-  DCHECK(event_router);
-
-  bool is_offline = (net::NetworkChangeNotifier::GetConnectionType() ==
-                     net::NetworkChangeNotifier::CONNECTION_NONE);
-
-  const extensions::ExtensionSet& extensions =
-      extensions::ExtensionRegistry::Get(profile)->enabled_extensions();
-  extensions::ExtensionSet::const_iterator iter;
-  for (iter = extensions.begin(); iter != extensions.end(); ++iter) {
-    const Extension* extension = iter->get();
-
-    if (!event_router->ExtensionHasEventListener(
-            extension->id(), tts_engine_events::kOnSpeak) ||
-        !event_router->ExtensionHasEventListener(
-            extension->id(), tts_engine_events::kOnStop)) {
-      continue;
-    }
-
-    const std::vector<extensions::TtsVoice>* tts_voices =
-        GetVoicesInternal(profile, extension);
-    if (!tts_voices)
-      continue;
-
-    for (size_t i = 0; i < tts_voices->size(); ++i) {
-      const extensions::TtsVoice& voice = tts_voices->at(i);
-
-      // Don't return remote voices when the system is offline.
-      if (voice.remote && is_offline)
-        continue;
-
-      out_voices->push_back(VoiceData());
-      VoiceData& result_voice = out_voices->back();
-
-      result_voice.native = false;
-      result_voice.name = voice.voice_name;
-      result_voice.lang = voice.lang;
-      result_voice.remote = voice.remote;
-      result_voice.extension_id = extension->id();
-
-      for (std::set<std::string>::const_iterator iter =
-               voice.event_types.begin();
-           iter != voice.event_types.end();
-           ++iter) {
-        result_voice.events.insert(TtsEventTypeFromString(*iter));
-      }
-
-      // If the extension sends end events, the controller will handle
-      // queueing and send interrupted and cancelled events.
-      if (voice.event_types.find(constants::kEventTypeEnd) !=
-          voice.event_types.end()) {
-        result_voice.events.insert(TTS_EVENT_CANCELLED);
-        result_voice.events.insert(TTS_EVENT_INTERRUPTED);
-      }
-    }
-  }
-}
-
-void TtsExtensionEngine::Speak(Utterance* utterance,
-                               const VoiceData& voice) {
-  // See if the engine supports the "end" event; if so, we can keep the
-  // utterance around and track it. If not, we're finished with this
-  // utterance now.
-  bool sends_end_event = voice.events.find(TTS_EVENT_END) != voice.events.end();
-
-  std::unique_ptr<base::ListValue> args(new base::ListValue());
-  args->AppendString(utterance->text());
-
-  // Pass through most options to the speech engine, but remove some
-  // that are handled internally.
-  std::unique_ptr<base::DictionaryValue> options(
-      static_cast<base::DictionaryValue*>(utterance->options()->DeepCopy()));
-  if (options->HasKey(constants::kRequiredEventTypesKey))
-    options->Remove(constants::kRequiredEventTypesKey, NULL);
-  if (options->HasKey(constants::kDesiredEventTypesKey))
-    options->Remove(constants::kDesiredEventTypesKey, NULL);
-  if (sends_end_event && options->HasKey(constants::kEnqueueKey))
-    options->Remove(constants::kEnqueueKey, NULL);
-  if (options->HasKey(constants::kSrcIdKey))
-    options->Remove(constants::kSrcIdKey, NULL);
-  if (options->HasKey(constants::kIsFinalEventKey))
-    options->Remove(constants::kIsFinalEventKey, NULL);
-  if (options->HasKey(constants::kOnEventKey))
-    options->Remove(constants::kOnEventKey, NULL);
-
-  // Get the volume, pitch, and rate, but only if they weren't already in
-  // the options. TODO(dmazzoni): these shouldn't be redundant.
-  // http://crbug.com/463264
-  if (!options->HasKey(constants::kRateKey)) {
-    options->SetDouble(constants::kRateKey,
-                       utterance->continuous_parameters().rate);
-  }
-  if (!options->HasKey(constants::kPitchKey)) {
-    options->SetDouble(constants::kPitchKey,
-                       utterance->continuous_parameters().pitch);
-  }
-  if (!options->HasKey(constants::kVolumeKey)) {
-    options->SetDouble(constants::kVolumeKey,
-                       utterance->continuous_parameters().volume);
-  }
-
-  // Add the voice name and language to the options if they're not
-  // already there, since they might have been picked by the TTS controller
-  // rather than directly by the client that requested the speech.
-  if (!options->HasKey(constants::kVoiceNameKey))
-    options->SetString(constants::kVoiceNameKey, voice.name);
-  if (!options->HasKey(constants::kLangKey))
-    options->SetString(constants::kLangKey, voice.lang);
-
-  args->Append(std::move(options));
-  args->AppendInteger(utterance->id());
-
-  std::string json;
-  base::JSONWriter::Write(*args, &json);
-
-  Profile* profile = Profile::FromBrowserContext(utterance->browser_context());
-  auto event = std::make_unique<extensions::Event>(
-      extensions::events::TTS_ENGINE_ON_SPEAK, tts_engine_events::kOnSpeak,
-      std::move(args), profile);
-  EventRouter::Get(profile)
-      ->DispatchEventToExtension(utterance->extension_id(), std::move(event));
-}
-
-void TtsExtensionEngine::Stop(Utterance* utterance) {
-  std::unique_ptr<base::ListValue> args(new base::ListValue());
-  Profile* profile = Profile::FromBrowserContext(utterance->browser_context());
-  auto event = std::make_unique<extensions::Event>(
-      extensions::events::TTS_ENGINE_ON_STOP, tts_engine_events::kOnStop,
-      std::move(args), profile);
-  EventRouter::Get(profile)
-      ->DispatchEventToExtension(utterance->extension_id(), std::move(event));
-}
-
-void TtsExtensionEngine::Pause(Utterance* utterance) {
-  std::unique_ptr<base::ListValue> args(new base::ListValue());
-  Profile* profile = Profile::FromBrowserContext(utterance->browser_context());
-  auto event = std::make_unique<extensions::Event>(
-      extensions::events::TTS_ENGINE_ON_PAUSE, tts_engine_events::kOnPause,
-      std::move(args), profile);
-  EventRouter* event_router = EventRouter::Get(profile);
-  std::string id = utterance->extension_id();
-  event_router->DispatchEventToExtension(id, std::move(event));
-  WarnIfMissingPauseOrResumeListener(profile, event_router, id);
-}
-
-void TtsExtensionEngine::Resume(Utterance* utterance) {
-  std::unique_ptr<base::ListValue> args(new base::ListValue());
-  Profile* profile = Profile::FromBrowserContext(utterance->browser_context());
-  auto event = std::make_unique<extensions::Event>(
-      extensions::events::TTS_ENGINE_ON_RESUME, tts_engine_events::kOnResume,
-      std::move(args), profile);
-  EventRouter* event_router = EventRouter::Get(profile);
-  std::string id = utterance->extension_id();
-  event_router->DispatchEventToExtension(id, std::move(event));
-  WarnIfMissingPauseOrResumeListener(profile, event_router, id);
-}
-
-bool TtsExtensionEngine::LoadBuiltInTtsExtension(
-    content::BrowserContext* browser_context) {
-#if defined(OS_CHROMEOS)
-  Profile* profile = Profile::FromBrowserContext(browser_context);
-  // Check to see if the engine was previously loaded.
-  if (TtsEngineExtensionObserver::GetInstance(profile)->SawExtensionLoad(
-          extension_misc::kSpeechSynthesisExtensionId, true)) {
-    return false;
-  }
-
-  // Load the component extension into this profile.
-  extensions::ExtensionService* extension_service =
-      extensions::ExtensionSystem::Get(profile)->extension_service();
-  DCHECK(extension_service);
-  extension_service->component_loader()
-      ->AddChromeOsSpeechSynthesisExtension();
-  return true;
-#else
-  return false;
-#endif
-}
-
 ExtensionFunction::ResponseAction
 ExtensionTtsEngineUpdateVoicesFunction::Run() {
   base::ListValue* voices_data = nullptr;
@@ -344,11 +112,13 @@
         event->GetInteger(constants::kCharIndexKey, &char_index));
   }
 
-  // Make sure the extension has included this event type in its manifest.
+  // Make sure the extension has included this event type in its manifest
+  // or in the runtime list of voices exposed.
   bool event_type_allowed = false;
   Profile* profile = Profile::FromBrowserContext(browser_context());
   const std::vector<extensions::TtsVoice>* tts_voices =
-      GetVoicesInternal(profile, extension());
+      TtsEngineExtensionObserver::GetInstance(profile)->GetAllVoices(
+          extension());
   if (!tts_voices)
     return RespondNow(Error(constants::kErrorUndeclaredEventType));
 
diff --git a/chrome/browser/speech/extension_api/tts_engine_extension_api.h b/chrome/browser/speech/extension_api/tts_engine_extension_api.h
index 5e60d5c..638fd45 100644
--- a/chrome/browser/speech/extension_api/tts_engine_extension_api.h
+++ b/chrome/browser/speech/extension_api/tts_engine_extension_api.h
@@ -5,18 +5,8 @@
 #ifndef CHROME_BROWSER_SPEECH_EXTENSION_API_TTS_ENGINE_EXTENSION_API_H_
 #define CHROME_BROWSER_SPEECH_EXTENSION_API_TTS_ENGINE_EXTENSION_API_H_
 
-#include <vector>
-
-#include "base/memory/singleton.h"
-#include "chrome/browser/speech/tts_controller.h"
 #include "extensions/browser/extension_function.h"
 
-class Utterance;
-
-namespace content {
-class BrowserContext;
-}
-
 namespace tts_engine_events {
 extern const char kOnSpeak[];
 extern const char kOnStop[];
@@ -24,22 +14,6 @@
 extern const char kOnResume[];
 }
 
-// TtsEngineDelegate implementation used by TtsController.
-class TtsExtensionEngine : public TtsEngineDelegate {
- public:
-  static TtsExtensionEngine* GetInstance();
-
-  // Overridden from TtsEngineDelegate:
-  void GetVoices(content::BrowserContext* browser_context,
-                 std::vector<VoiceData>* out_voices) override;
-  void Speak(Utterance* utterance, const VoiceData& voice) override;
-  void Stop(Utterance* utterance) override;
-  void Pause(Utterance* utterance) override;
-  void Resume(Utterance* utterance) override;
-  bool LoadBuiltInTtsExtension(
-      content::BrowserContext* browser_context) override;
-};
-
 // Function that allows tts engines to update its list of supported voices at
 // runtime.
 class ExtensionTtsEngineUpdateVoicesFunction
diff --git a/chrome/browser/speech/extension_api/tts_engine_extension_observer.cc b/chrome/browser/speech/extension_api/tts_engine_extension_observer.cc
index 43540ed..19691bc 100644
--- a/chrome/browser/speech/extension_api/tts_engine_extension_observer.cc
+++ b/chrome/browser/speech/extension_api/tts_engine_extension_observer.cc
@@ -94,6 +94,14 @@
 }
 
 const std::vector<extensions::TtsVoice>*
+TtsEngineExtensionObserver::GetAllVoices(
+    const extensions::Extension* extension) {
+  const std::vector<extensions::TtsVoice>* voices =
+      GetRuntimeVoices(extension->id());
+  return voices ? voices : extensions::TtsVoices::GetTtsVoices(extension);
+}
+
+const std::vector<extensions::TtsVoice>*
 TtsEngineExtensionObserver::GetRuntimeVoices(const std::string extension_id) {
   auto it = extension_id_to_runtime_voices_.find(extension_id);
   if (it == extension_id_to_runtime_voices_.end())
diff --git a/chrome/browser/speech/extension_api/tts_engine_extension_observer.h b/chrome/browser/speech/extension_api/tts_engine_extension_observer.h
index 507247e..fbc99d1 100644
--- a/chrome/browser/speech/extension_api/tts_engine_extension_observer.h
+++ b/chrome/browser/speech/extension_api/tts_engine_extension_observer.h
@@ -35,7 +35,13 @@
   // Gets the currently loaded TTS extension ids.
   const std::set<std::string> GetTtsExtensions();
 
-  // Gets voices for |extension_id| updated through TtsEngine.updateVoices.
+  // Gets voices for |extension_id| either from TtsEngine.updateVoices,
+  // or falling back on the manifest voices otherwise.
+  const std::vector<extensions::TtsVoice>* GetAllVoices(
+      const extensions::Extension* extension);
+
+  // Gets only the voices for |extension_id| that were updated through
+  // TtsEngine.updateVoices.
   const std::vector<extensions::TtsVoice>* GetRuntimeVoices(
       const std::string extension_id);
 
diff --git a/chrome/browser/speech/tts_chromeos.cc b/chrome/browser/speech/tts_chromeos.cc
index 7c5e722..9464abf 100644
--- a/chrome/browser/speech/tts_chromeos.cc
+++ b/chrome/browser/speech/tts_chromeos.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "base/macros.h"
+#include "chrome/browser/speech/tts_engine_delegate.h"
 #include "chrome/browser/speech/tts_platform.h"
 #include "components/arc/arc_bridge_service.h"
 #include "components/arc/arc_service_manager.h"
@@ -24,9 +25,9 @@
   bool LoadBuiltInTtsExtension(
       content::BrowserContext* browser_context) override {
     TtsEngineDelegate* tts_engine_delegate =
-        TtsController::GetInstance()->GetTtsEngineDelegate();
+        TtsController::GetInstance()->GetTtsEngineDelegate(browser_context);
     if (tts_engine_delegate)
-      return tts_engine_delegate->LoadBuiltInTtsExtension(browser_context);
+      return tts_engine_delegate->LoadBuiltInTtsExtension();
     return false;
   }
 
diff --git a/chrome/browser/speech/tts_controller.h b/chrome/browser/speech/tts_controller.h
index 6aba287..2e4b439 100644
--- a/chrome/browser/speech/tts_controller.h
+++ b/chrome/browser/speech/tts_controller.h
@@ -26,6 +26,9 @@
 class BrowserContext;
 }
 
+class TtsEngineDelegate;
+class TtsEngineDelegateFactory;
+
 // Events sent back from the TTS engine indicating the progress.
 enum TtsEventType {
   TTS_EVENT_START,
@@ -74,33 +77,6 @@
   std::string native_voice_identifier;
 };
 
-// Interface that delegates TTS requests to user-installed extensions.
-class TtsEngineDelegate {
- public:
-  virtual ~TtsEngineDelegate() {}
-
-  // Return a list of all available voices registered.
-  virtual void GetVoices(content::BrowserContext* browser_context,
-                         std::vector<VoiceData>* out_voices) = 0;
-
-  // Speak the given utterance by sending an event to the given TTS engine.
-  virtual void Speak(Utterance* utterance, const VoiceData& voice) = 0;
-
-  // Stop speaking the given utterance by sending an event to the target
-  // associated with this utterance.
-  virtual void Stop(Utterance* utterance) = 0;
-
-  // Pause in the middle of speaking this utterance.
-  virtual void Pause(Utterance* utterance) = 0;
-
-  // Resume speaking this utterance.
-  virtual void Resume(Utterance* utterance) = 0;
-
-  // Load the built-in component extension for ChromeOS.
-  virtual bool LoadBuiltInTtsExtension(
-      content::BrowserContext* browser_context) = 0;
-};
-
 // Class that wants to receive events on utterances.
 class UtteranceEventDelegate {
  public:
@@ -312,13 +288,15 @@
   virtual void RemoveUtteranceEventDelegate(UtteranceEventDelegate* delegate)
       = 0;
 
-  // Set the delegate that processes TTS requests with user-installed
-  // extensions.
-  virtual void SetTtsEngineDelegate(TtsEngineDelegate* delegate) = 0;
+  // Set the factory that returns a per-BrowserContext TtsEngineDelegate,
+  // which processes TTS requests with user-installed extensions.
+  virtual void SetTtsEngineDelegateFactory(
+      TtsEngineDelegateFactory* factory) = 0;
 
   // Get the delegate that processes TTS requests with user-installed
   // extensions.
-  virtual TtsEngineDelegate* GetTtsEngineDelegate() = 0;
+  virtual TtsEngineDelegate* GetTtsEngineDelegate(
+      content::BrowserContext* browser_context) = 0;
 
   // For unit testing.
   virtual void SetPlatformImpl(TtsPlatformImpl* platform_impl) = 0;
diff --git a/chrome/browser/speech/tts_controller_impl.cc b/chrome/browser/speech/tts_controller_impl.cc
index e47690e..6b7d739 100644
--- a/chrome/browser/speech/tts_controller_impl.cc
+++ b/chrome/browser/speech/tts_controller_impl.cc
@@ -17,6 +17,7 @@
 #include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/speech/tts_engine_delegate.h"
 #include "chrome/browser/speech/tts_platform.h"
 #include "chrome/common/pref_names.h"
 #include "components/prefs/pref_service.h"
@@ -156,7 +157,7 @@
     : current_utterance_(nullptr),
       paused_(false),
       platform_impl_(nullptr),
-      tts_engine_delegate_(nullptr) {}
+      tts_engine_delegate_factory_(nullptr) {}
 
 TtsControllerImpl::~TtsControllerImpl() {
   if (current_utterance_) {
@@ -231,8 +232,8 @@
     DCHECK(!voice.extension_id.empty());
     current_utterance_ = utterance;
     utterance->set_extension_id(voice.extension_id);
-    if (tts_engine_delegate_)
-      tts_engine_delegate_->Speak(utterance, voice);
+    if (TtsEngineDelegate* delegate = GetTtsEngineDelegate(utterance))
+      delegate->Speak(utterance, voice);
     bool sends_end_event =
         voice.events.find(TTS_EVENT_END) != voice.events.end();
     if (!sends_end_event) {
@@ -277,8 +278,8 @@
 
   paused_ = false;
   if (current_utterance_ && !current_utterance_->extension_id().empty()) {
-    if (tts_engine_delegate_)
-      tts_engine_delegate_->Stop(current_utterance_);
+    if (TtsEngineDelegate* delegate = GetTtsEngineDelegate(current_utterance_))
+      delegate->Stop(current_utterance_);
   } else {
     GetPlatformImpl()->clear_error();
     GetPlatformImpl()->StopSpeaking();
@@ -296,8 +297,8 @@
 
   paused_ = true;
   if (current_utterance_ && !current_utterance_->extension_id().empty()) {
-    if (tts_engine_delegate_)
-      tts_engine_delegate_->Pause(current_utterance_);
+    if (TtsEngineDelegate* delegate = GetTtsEngineDelegate(current_utterance_))
+      delegate->Pause(current_utterance_);
   } else if (current_utterance_) {
     GetPlatformImpl()->clear_error();
     GetPlatformImpl()->Pause();
@@ -309,8 +310,8 @@
 
   paused_ = false;
   if (current_utterance_ && !current_utterance_->extension_id().empty()) {
-    if (tts_engine_delegate_)
-      tts_engine_delegate_->Resume(current_utterance_);
+    if (TtsEngineDelegate* delegate = GetTtsEngineDelegate(current_utterance_))
+      delegate->Resume(current_utterance_);
   } else if (current_utterance_) {
     GetPlatformImpl()->clear_error();
     GetPlatformImpl()->Resume();
@@ -378,7 +379,7 @@
 }
 
 void TtsControllerImpl::GetVoices(content::BrowserContext* browser_context,
-                              std::vector<VoiceData>* out_voices) {
+                                  std::vector<VoiceData>* out_voices) {
   TtsPlatformImpl* platform_impl = GetPlatformImpl();
   if (platform_impl) {
     // Ensure we have all built-in voices loaded. This is a no-op if already
@@ -388,8 +389,13 @@
       platform_impl->GetVoices(out_voices);
   }
 
-  if (browser_context && tts_engine_delegate_)
-    tts_engine_delegate_->GetVoices(browser_context, out_voices);
+  if (browser_context && tts_engine_delegate_factory_) {
+    TtsEngineDelegate* delegate =
+        tts_engine_delegate_factory_->GetTtsEngineDelegateForBrowserContext(
+            browser_context);
+    if (delegate)
+      delegate->GetVoices(browser_context, out_voices);
+  }
 }
 
 bool TtsControllerImpl::IsSpeaking() {
@@ -605,6 +611,15 @@
   utterance->set_continuous_parameters(rate, pitch, volume);
 }
 
+TtsEngineDelegate* TtsControllerImpl::GetTtsEngineDelegate(
+    Utterance* utterance) {
+  if (!utterance->browser_context() || !tts_engine_delegate_factory_)
+    return nullptr;
+
+  return tts_engine_delegate_factory_->GetTtsEngineDelegateForBrowserContext(
+      utterance->browser_context());
+}
+
 const PrefService* TtsControllerImpl::GetPrefService(
     const Utterance* utterance) {
   const PrefService* prefs = nullptr;
@@ -658,8 +673,9 @@
   if (current_utterance_ && current_utterance_->event_delegate() == delegate) {
     current_utterance_->set_event_delegate(nullptr);
     if (!current_utterance_->extension_id().empty()) {
-      if (tts_engine_delegate_)
-        tts_engine_delegate_->Stop(current_utterance_);
+      if (TtsEngineDelegate* delegate =
+              GetTtsEngineDelegate(current_utterance_))
+        delegate->Stop(current_utterance_);
     } else {
       GetPlatformImpl()->clear_error();
       GetPlatformImpl()->StopSpeaking();
@@ -671,11 +687,13 @@
   }
 }
 
-void TtsControllerImpl::SetTtsEngineDelegate(
-    TtsEngineDelegate* delegate) {
-  tts_engine_delegate_ = delegate;
+void TtsControllerImpl::SetTtsEngineDelegateFactory(
+    TtsEngineDelegateFactory* factory) {
+  tts_engine_delegate_factory_ = factory;
 }
 
-TtsEngineDelegate* TtsControllerImpl::GetTtsEngineDelegate() {
-  return tts_engine_delegate_;
+TtsEngineDelegate* TtsControllerImpl::GetTtsEngineDelegate(
+    content::BrowserContext* browser_context) {
+  return tts_engine_delegate_factory_->GetTtsEngineDelegateForBrowserContext(
+      browser_context);
 }
diff --git a/chrome/browser/speech/tts_controller_impl.h b/chrome/browser/speech/tts_controller_impl.h
index 49a22aa..89cd0f8 100644
--- a/chrome/browser/speech/tts_controller_impl.h
+++ b/chrome/browser/speech/tts_controller_impl.h
@@ -46,8 +46,9 @@
   void AddVoicesChangedDelegate(VoicesChangedDelegate* delegate) override;
   void RemoveVoicesChangedDelegate(VoicesChangedDelegate* delegate) override;
   void RemoveUtteranceEventDelegate(UtteranceEventDelegate* delegate) override;
-  void SetTtsEngineDelegate(TtsEngineDelegate* delegate) override;
-  TtsEngineDelegate* GetTtsEngineDelegate() override;
+  void SetTtsEngineDelegateFactory(TtsEngineDelegateFactory* factory) override;
+  TtsEngineDelegate* GetTtsEngineDelegate(
+      content::BrowserContext* browser_context) override;
   void SetPlatformImpl(TtsPlatformImpl* platform_impl) override;
   int QueueSize() override;
 
@@ -88,6 +89,10 @@
   // pulled from user prefs, and may not be the same as other platforms.
   void UpdateUtteranceDefaults(Utterance* utterance);
 
+  // Helper; returns the TtsEngineDelegate for the given utterance or
+  // nullptr if there's no BrowserContext associated with this utterance.
+  TtsEngineDelegate* GetTtsEngineDelegate(Utterance* utterance);
+
   virtual const PrefService* GetPrefService(const Utterance* utterance);
 
   friend struct base::DefaultSingletonTraits<TtsControllerImpl>;
@@ -108,8 +113,9 @@
   // dependency injection.
   TtsPlatformImpl* platform_impl_;
 
-  // The delegate that processes TTS requests with user-installed extensions.
-  TtsEngineDelegate* tts_engine_delegate_;
+  // The factory that creates delegate that processes TTS requests
+  // with user-installed extensions.
+  TtsEngineDelegateFactory* tts_engine_delegate_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(TtsControllerImpl);
 };
diff --git a/chrome/browser/speech/tts_engine_delegate.cc b/chrome/browser/speech/tts_engine_delegate.cc
new file mode 100644
index 0000000..52de847
--- /dev/null
+++ b/chrome/browser/speech/tts_engine_delegate.cc
@@ -0,0 +1,9 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/speech/tts_engine_delegate.h"
+
+TtsEngineDelegate::~TtsEngineDelegate() {}
+
+TtsEngineDelegateFactory::~TtsEngineDelegateFactory() {}
diff --git a/chrome/browser/speech/tts_engine_delegate.h b/chrome/browser/speech/tts_engine_delegate.h
new file mode 100644
index 0000000..35932d29
--- /dev/null
+++ b/chrome/browser/speech/tts_engine_delegate.h
@@ -0,0 +1,52 @@
+// Copyright (c) 2012 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_SPEECH_TTS_ENGINE_DELEGATE_H_
+#define CHROME_BROWSER_SPEECH_TTS_ENGINE_DELEGATE_H_
+
+#include <vector>
+
+class Utterance;
+struct VoiceData;
+
+namespace content {
+class BrowserContext;
+}
+
+// Interface that delegates TTS requests to user-installed extensions,
+// for a given BrowserContext.
+class TtsEngineDelegate {
+ public:
+  virtual ~TtsEngineDelegate();
+
+  // Return a list of all available voices registered.
+  virtual void GetVoices(content::BrowserContext* browser_context,
+                         std::vector<VoiceData>* out_voices) = 0;
+
+  // Speak the given utterance by sending an event to the given TTS engine.
+  virtual void Speak(Utterance* utterance, const VoiceData& voice) = 0;
+
+  // Stop speaking the given utterance by sending an event to the target
+  // associated with this utterance.
+  virtual void Stop(Utterance* utterance) = 0;
+
+  // Pause in the middle of speaking this utterance.
+  virtual void Pause(Utterance* utterance) = 0;
+
+  // Resume speaking this utterance.
+  virtual void Resume(Utterance* utterance) = 0;
+
+  // Load the built-in component extension for ChromeOS.
+  virtual bool LoadBuiltInTtsExtension() = 0;
+};
+
+class TtsEngineDelegateFactory {
+ public:
+  virtual ~TtsEngineDelegateFactory();
+
+  virtual TtsEngineDelegate* GetTtsEngineDelegateForBrowserContext(
+      content::BrowserContext* browser_context) = 0;
+};
+
+#endif  // CHROME_BROWSER_SPEECH_TTS_ENGINE_DELEGATE_H__
diff --git a/chrome/browser/ssl/security_state_tab_helper_browsertest.cc b/chrome/browser/ssl/security_state_tab_helper_browsertest.cc
index 6c62846..ae5188e 100644
--- a/chrome/browser/ssl/security_state_tab_helper_browsertest.cc
+++ b/chrome/browser/ssl/security_state_tab_helper_browsertest.cc
@@ -55,7 +55,6 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_delegate.h"
 #include "content/public/common/file_chooser_file_info.h"
-#include "content/public/common/file_chooser_params.h"
 #include "content/public/common/page_type.h"
 #include "content/public/common/referrer.h"
 #include "content/public/test/browser_test_utils.h"
@@ -212,27 +211,27 @@
   bool file_chosen() const { return file_chosen_; }
 
   // Copy of the params passed to RunFileChooser.
-  content::FileChooserParams params() const { return params_; }
+  const blink::mojom::FileChooserParams& params() const { return *params_; }
 
   // WebContentsDelegate:
   void RunFileChooser(content::RenderFrameHost* render_frame_host,
-                      const content::FileChooserParams& params) override {
+                      const blink::mojom::FileChooserParams& params) override {
     // Send the selected file to the renderer process.
     content::FileChooserFileInfo file_info;
     file_info.file_path = file_;
     std::vector<content::FileChooserFileInfo> files;
     files.push_back(file_info);
-    render_frame_host->FilesSelectedInChooser(files,
-                                              content::FileChooserParams::Open);
+    render_frame_host->FilesSelectedInChooser(
+        files, blink::mojom::FileChooserParams::Mode::kOpen);
 
     file_chosen_ = true;
-    params_ = params;
+    params_ = params.Clone();
   }
 
  private:
   base::FilePath file_;
   bool file_chosen_;
-  content::FileChooserParams params_;
+  blink::mojom::FileChooserParamsPtr params_;
 
   DISALLOW_COPY_AND_ASSIGN(FileChooserDelegate);
 };
diff --git a/chrome/browser/sync_file_system/drive_backend/drive_service_on_worker.cc b/chrome/browser/sync_file_system/drive_backend/drive_service_on_worker.cc
index 2113894..e895a76 100644
--- a/chrome/browser/sync_file_system/drive_backend/drive_service_on_worker.cc
+++ b/chrome/browser/sync_file_system/drive_backend/drive_service_on_worker.cc
@@ -318,12 +318,6 @@
   return google_apis::CancelCallback();
 }
 
-google_apis::CancelCallback DriveServiceOnWorker::GetAppList(
-    const google_apis::AppListCallback& callback) {
-  NOTREACHED();
-  return google_apis::CancelCallback();
-}
-
 google_apis::CancelCallback DriveServiceOnWorker::TrashResource(
     const std::string& resource_id,
     const google_apis::EntryActionCallback& callback) {
@@ -434,21 +428,6 @@
   return std::unique_ptr<drive::BatchRequestConfiguratorInterface>();
 }
 
-google_apis::CancelCallback DriveServiceOnWorker::AuthorizeApp(
-    const std::string& resource_id,
-    const std::string& app_id,
-    const google_apis::AuthorizeAppCallback& callback) {
-  NOTREACHED();
-  return google_apis::CancelCallback();
-}
-
-google_apis::CancelCallback DriveServiceOnWorker::UninstallApp(
-    const std::string& app_id,
-    const google_apis::EntryActionCallback& callback) {
-  NOTREACHED();
-  return google_apis::CancelCallback();
-}
-
 google_apis::CancelCallback DriveServiceOnWorker::AddPermission(
     const std::string& resource_id,
     const std::string& email,
diff --git a/chrome/browser/sync_file_system/drive_backend/drive_service_on_worker.h b/chrome/browser/sync_file_system/drive_backend/drive_service_on_worker.h
index 2cf7a25..9d386ba 100644
--- a/chrome/browser/sync_file_system/drive_backend/drive_service_on_worker.h
+++ b/chrome/browser/sync_file_system/drive_backend/drive_service_on_worker.h
@@ -124,8 +124,6 @@
   google_apis::CancelCallback Search(
       const std::string& search_query,
       const google_apis::FileListCallback& callback) override;
-  google_apis::CancelCallback GetAppList(
-      const google_apis::AppListCallback& callback) override;
   google_apis::CancelCallback TrashResource(
       const std::string& resource_id,
       const google_apis::EntryActionCallback& callback) override;
@@ -192,13 +190,6 @@
       const google_apis::ProgressCallback& progress_callback) override;
   std::unique_ptr<drive::BatchRequestConfiguratorInterface> StartBatchRequest()
       override;
-  google_apis::CancelCallback AuthorizeApp(
-      const std::string& resource_id,
-      const std::string& app_id,
-      const google_apis::AuthorizeAppCallback& callback) override;
-  google_apis::CancelCallback UninstallApp(
-      const std::string& app_id,
-      const google_apis::EntryActionCallback& callback) override;
   google_apis::CancelCallback AddPermission(
       const std::string& resource_id,
       const std::string& email,
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 d3bebcf..c381248 100644
--- a/chrome/browser/ui/app_list/app_list_client_impl.cc
+++ b/chrome/browser/ui/app_list/app_list_client_impl.cc
@@ -247,6 +247,12 @@
   model_updater_->OnPageBreakItemAdded(id, position);
 }
 
+void AppListClientImpl::OnPageBreakItemDeleted(const std::string& id) {
+  if (!model_updater_)
+    return;
+  model_updater_->OnPageBreakItemDeleted(id);
+}
+
 void AppListClientImpl::ActiveUserChanged(
     const user_manager::User* active_user) {
   if (!active_user->is_profile_created())
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 68cc10a..c9745de 100644
--- a/chrome/browser/ui/app_list/app_list_client_impl.h
+++ b/chrome/browser/ui/app_list/app_list_client_impl.h
@@ -76,6 +76,7 @@
   void OnItemUpdated(ash::mojom::AppListItemMetadataPtr item) override;
   void OnPageBreakItemAdded(const std::string& id,
                             const syncer::StringOrdinal& position) override;
+  void OnPageBreakItemDeleted(const std::string& id) override;
 
   // user_manager::UserManager::UserSessionStateObserver:
   void ActiveUserChanged(const user_manager::User* active_user) 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 c3a90ba..36c0090 100644
--- a/chrome/browser/ui/app_list/app_list_model_updater.h
+++ b/chrome/browser/ui/app_list/app_list_model_updater.h
@@ -138,6 +138,7 @@
   virtual void OnItemUpdated(ash::mojom::AppListItemMetadataPtr item) = 0;
   virtual void OnPageBreakItemAdded(const std::string& id,
                                     const syncer::StringOrdinal& position) = 0;
+  virtual void OnPageBreakItemDeleted(const std::string& id) = 0;
 
   virtual void SetDelegate(AppListModelUpdaterDelegate* delegate) = 0;
 };
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 c2cb98d..ab97bab 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
@@ -533,3 +533,17 @@
   if (delegate_)
     delegate_->OnAppListItemAdded(chrome_item);
 }
+
+void ChromeAppListModelUpdater::OnPageBreakItemDeleted(const std::string& id) {
+  ChromeAppListItem* chrome_item = FindItem(id);
+
+  if (!chrome_item) {
+    LOG(ERROR) << "OnPageBreakItemDeleted: " << id << " does not exist.";
+    return;
+  }
+
+  DCHECK(chrome_item->is_page_break());
+  if (delegate_)
+    delegate_->OnAppListItemWillBeDeleted(chrome_item);
+  items_.erase(id);
+}
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 3983373..b0e9ffb 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
@@ -109,6 +109,7 @@
   void OnItemUpdated(ash::mojom::AppListItemMetadataPtr item) override;
   void OnPageBreakItemAdded(const std::string& id,
                             const syncer::StringOrdinal& position) override;
+  void OnPageBreakItemDeleted(const std::string& id) override;
 
   void SetDelegate(AppListModelUpdaterDelegate* delegate) override;
 
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 bd6f586..6ddfd3a2 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
@@ -65,6 +65,7 @@
   void OnItemUpdated(ash::mojom::AppListItemMetadataPtr item) override {}
   void OnPageBreakItemAdded(const std::string& id,
                             const syncer::StringOrdinal& position) override {}
+  void OnPageBreakItemDeleted(const std::string& id) override {}
 
   void SetDelegate(AppListModelUpdaterDelegate* delegate) override;
 
diff --git a/chrome/browser/ui/apps/chrome_app_delegate.cc b/chrome/browser/ui/apps/chrome_app_delegate.cc
index deeb577..bde283b 100644
--- a/chrome/browser/ui/apps/chrome_app_delegate.cc
+++ b/chrome/browser/ui/apps/chrome_app_delegate.cc
@@ -277,7 +277,7 @@
 
 void ChromeAppDelegate::RunFileChooser(
     content::RenderFrameHost* render_frame_host,
-    const content::FileChooserParams& params) {
+    const blink::mojom::FileChooserParams& params) {
   FileSelectHelper::RunFileChooser(render_frame_host, params);
 }
 
diff --git a/chrome/browser/ui/apps/chrome_app_delegate.h b/chrome/browser/ui/apps/chrome_app_delegate.h
index 73eb6cf..7af92e1 100644
--- a/chrome/browser/ui/apps/chrome_app_delegate.h
+++ b/chrome/browser/ui/apps/chrome_app_delegate.h
@@ -55,7 +55,7 @@
   content::ColorChooser* ShowColorChooser(content::WebContents* web_contents,
                                           SkColor initial_color) override;
   void RunFileChooser(content::RenderFrameHost* render_frame_host,
-                      const content::FileChooserParams& params) override;
+                      const blink::mojom::FileChooserParams& params) override;
   void RequestMediaAccessPermission(
       content::WebContents* web_contents,
       const content::MediaStreamRequest& request,
diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc
index 9775dca..bb80a01 100644
--- a/chrome/browser/ui/browser.cc
+++ b/chrome/browser/ui/browser.cc
@@ -87,8 +87,6 @@
 #include "chrome/browser/sessions/session_tab_helper.h"
 #include "chrome/browser/sessions/tab_restore_service_factory.h"
 #include "chrome/browser/ssl/security_state_tab_helper.h"
-#include "chrome/browser/sync/profile_sync_service_factory.h"
-#include "chrome/browser/sync/sync_ui_util.h"
 #include "chrome/browser/tab_contents/tab_util.h"
 #include "chrome/browser/task_manager/web_contents_tags.h"
 #include "chrome/browser/themes/theme_service.h"
@@ -161,7 +159,6 @@
 #include "components/bookmarks/browser/bookmark_model.h"
 #include "components/bookmarks/browser/bookmark_utils.h"
 #include "components/bookmarks/common/bookmark_pref_names.h"
-#include "components/browser_sync/profile_sync_service.h"
 #include "components/bubble/bubble_controller.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/favicon/content/content_favicon_driver.h"
@@ -1756,7 +1753,7 @@
 }
 
 void Browser::RunFileChooser(content::RenderFrameHost* render_frame_host,
-                             const content::FileChooserParams& params) {
+                             const blink::mojom::FileChooserParams& params) {
   FileSelectHelper::RunFileChooser(render_frame_host, params);
 }
 
diff --git a/chrome/browser/ui/browser.h b/chrome/browser/ui/browser.h
index 2371095..ee41c8e2 100644
--- a/chrome/browser/ui/browser.h
+++ b/chrome/browser/ui/browser.h
@@ -653,7 +653,7 @@
       const std::vector<blink::mojom::ColorSuggestionPtr>& suggestions)
       override;
   void RunFileChooser(content::RenderFrameHost* render_frame_host,
-                      const content::FileChooserParams& params) override;
+                      const blink::mojom::FileChooserParams& params) override;
   void EnumerateDirectory(content::WebContents* web_contents,
                           int request_id,
                           const base::FilePath& path) override;
diff --git a/chrome/browser/ui/browser_command_controller.cc b/chrome/browser/ui/browser_command_controller.cc
index a6d7b2c..a5a4bf8 100644
--- a/chrome/browser/ui/browser_command_controller.cc
+++ b/chrome/browser/ui/browser_command_controller.cc
@@ -45,7 +45,6 @@
 #include "chrome/common/pref_names.h"
 #include "chrome/common/profiling.h"
 #include "components/bookmarks/common/bookmark_pref_names.h"
-#include "components/browser_sync/profile_sync_service.h"
 #include "components/dom_distiller/core/dom_distiller_switches.h"
 #include "components/feature_engagement/buildflags.h"
 #include "components/prefs/pref_service.h"
diff --git a/chrome/browser/ui/cocoa/tab_dialogs_cocoa.mm b/chrome/browser/ui/cocoa/tab_dialogs_cocoa.mm
index b95fdb4..6aa2126 100644
--- a/chrome/browser/ui/cocoa/tab_dialogs_cocoa.mm
+++ b/chrome/browser/ui/cocoa/tab_dialogs_cocoa.mm
@@ -6,7 +6,6 @@
 
 #include <memory>
 
-#include "chrome/browser/ui/cocoa/browser_dialogs_views_mac.h"
 #import "chrome/browser/ui/cocoa/hung_renderer_controller.h"
 #import "chrome/browser/ui/cocoa/profiles/profile_signin_confirmation_dialog_cocoa.h"
 #include "chrome/browser/ui/cocoa/tab_dialogs_views_mac.h"
@@ -14,21 +13,6 @@
 #include "content/public/browser/web_contents.h"
 #include "ui/base/ui_features.h"
 
-#if !BUILDFLAG(MAC_VIEWS_BROWSER)
-// static
-void TabDialogs::CreateForWebContents(content::WebContents* contents) {
-  DCHECK(contents);
-
-  if (!FromWebContents(contents)) {
-    std::unique_ptr<TabDialogs> tab_dialogs =
-        chrome::ShowAllDialogsWithViewsToolkit()
-            ? std::make_unique<TabDialogsViewsMac>(contents)
-            : std::make_unique<TabDialogsCocoa>(contents);
-    contents->SetUserData(UserDataKey(), std::move(tab_dialogs));
-  }
-}
-#endif
-
 TabDialogsCocoa::TabDialogsCocoa(content::WebContents* contents)
     : web_contents_(contents) {
   DCHECK(contents);
diff --git a/chrome/browser/ui/cocoa/tab_dialogs_views_mac.mm b/chrome/browser/ui/cocoa/tab_dialogs_views_mac.mm
index 3eb71f9..d07691b 100644
--- a/chrome/browser/ui/cocoa/tab_dialogs_views_mac.mm
+++ b/chrome/browser/ui/cocoa/tab_dialogs_views_mac.mm
@@ -8,7 +8,6 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_window.h"
-#include "chrome/browser/ui/cocoa/browser_dialogs_views_mac.h"
 #import "chrome/browser/ui/cocoa/browser_window_controller.h"
 #import "chrome/browser/ui/cocoa/bubble_anchor_helper_views.h"
 #include "chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h"
@@ -31,7 +30,6 @@
 }
 }
 
-#if BUILDFLAG(MAC_VIEWS_BROWSER)
 // static
 void TabDialogs::CreateForWebContents(content::WebContents* contents) {
   DCHECK(contents);
@@ -55,8 +53,6 @@
     contents->SetUserData(UserDataKey(), std::move(tab_dialogs));
   }
 }
-#endif
-
 TabDialogsViewsMac::TabDialogsViewsMac(content::WebContents* contents)
     : TabDialogsCocoa(contents) {}
 
@@ -77,10 +73,6 @@
 }
 
 void TabDialogsViewsMac::ShowManagePasswordsBubble(bool user_action) {
-  if (!chrome::ShowAllDialogsWithViewsToolkit()) {
-    TabDialogsCocoa::ShowManagePasswordsBubble(user_action);
-    return;
-  }
   NSWindow* window = [web_contents()->GetNativeView() window];
   if (!window) {
     // The tab isn't active right now.
diff --git a/chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_bubble_controller_unittest.cc b/chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_bubble_controller_unittest.cc
index c5feb0e..ea75844 100644
--- a/chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_bubble_controller_unittest.cc
+++ b/chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_bubble_controller_unittest.cc
@@ -7,18 +7,15 @@
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/test_timeouts.h"
 #include "chrome/browser/prefs/browser_prefs.h"
-#include "chrome/browser/sync/profile_sync_test_util.h"
 #include "chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_util.h"
 #include "chrome/browser/ui/desktop_ios_promotion/sms_service.h"
 #include "chrome/browser/ui/desktop_ios_promotion/sms_service_factory.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
-#include "components/browser_sync/profile_sync_service.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/pref_notifier_impl.h"
 #include "components/prefs/pref_service.h"
 #include "components/prefs/testing_pref_service.h"
-#include "components/sync/driver/fake_sync_service.h"
 #include "components/sync_preferences/pref_service_mock_factory.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "content/public/test/test_browser_thread_bundle.h"
diff --git a/chrome/browser/ui/desktop_ios_promotion/sms_service_factory.cc b/chrome/browser/ui/desktop_ios_promotion/sms_service_factory.cc
index aa34dd4..cab95dc 100644
--- a/chrome/browser/ui/desktop_ios_promotion/sms_service_factory.cc
+++ b/chrome/browser/ui/desktop_ios_promotion/sms_service_factory.cc
@@ -5,9 +5,7 @@
 #include "chrome/browser/ui/desktop_ios_promotion/sms_service_factory.h"
 
 #include "chrome/browser/signin/identity_manager_factory.h"
-#include "chrome/browser/sync/profile_sync_service_factory.h"
 #include "chrome/browser/ui/desktop_ios_promotion/sms_service.h"
-#include "components/browser_sync/profile_sync_service.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "components/signin/core/browser/profile_oauth2_token_service.h"
 #include "components/signin/core/browser/signin_manager.h"
diff --git a/chrome/browser/ui/views/autofill/save_card_bubble_views_browsertest_base.cc b/chrome/browser/ui/views/autofill/save_card_bubble_views_browsertest_base.cc
index bb3f4a8..841ee5e 100644
--- a/chrome/browser/ui/views/autofill/save_card_bubble_views_browsertest_base.cc
+++ b/chrome/browser/ui/views/autofill/save_card_bubble_views_browsertest_base.cc
@@ -31,9 +31,12 @@
 #include "net/url_request/test_url_fetcher_factory.h"
 #include "services/device/public/cpp/test/scoped_geolocation_overrider.h"
 #include "ui/events/base_event_utils.h"
+#include "ui/views/bubble/bubble_frame_view.h"
 #include "ui/views/controls/button/button.h"
 #include "ui/views/test/widget_test.h"
+#include "ui/views/widget/widget.h"
 #include "ui/views/window/dialog_client_view.h"
+#include "ui/views/window/non_client_view.h"
 
 namespace autofill {
 
@@ -334,6 +337,16 @@
 }
 
 void SaveCardBubbleViewsBrowserTestBase::ClickOnDialogView(views::View* view) {
+  GetSaveCardBubbleViews()
+      ->GetDialogClientView()
+      ->ResetViewShownTimeStampForTesting();
+  views::BubbleFrameView* bubble_frame_view =
+      static_cast<views::BubbleFrameView*>(GetSaveCardBubbleViews()
+                                               ->GetWidget()
+                                               ->non_client_view()
+                                               ->frame_view());
+  bubble_frame_view->ResetViewShownTimeStampForTesting();
+
   DCHECK(view);
   ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
                          ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc b/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc
index c93e4d0..218eab2 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc
@@ -30,7 +30,6 @@
 #include "chrome/browser/defaults.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/search/search.h"
-#include "chrome/browser/sync/profile_sync_service_factory.h"
 #include "chrome/browser/themes/theme_properties.h"
 #include "chrome/browser/ui/bookmarks/bookmark_drag_drop.h"
 #include "chrome/browser/ui/bookmarks/bookmark_tab_helper.h"
@@ -61,7 +60,6 @@
 #include "components/bookmarks/browser/bookmark_utils.h"
 #include "components/bookmarks/common/bookmark_pref_names.h"
 #include "components/bookmarks/managed/managed_bookmark_service.h"
-#include "components/browser_sync/profile_sync_service.h"
 #include "components/metrics/metrics_service.h"
 #include "components/omnibox/browser/omnibox_popup_model.h"
 #include "components/omnibox/browser/omnibox_view.h"
diff --git a/chrome/browser/ui/views/frame/hosted_app_button_container.h b/chrome/browser/ui/views/frame/hosted_app_button_container.h
index 768cc08..f843ac4 100644
--- a/chrome/browser/ui/views/frame/hosted_app_button_container.h
+++ b/chrome/browser/ui/views/frame/hosted_app_button_container.h
@@ -77,6 +77,8 @@
                         int y,
                         int available_height);
 
+  SkColor active_color_for_testing() const { return active_color_; }
+
  private:
   friend class HostedAppNonClientFrameViewAshTest;
   friend class HostedAppGlassBrowserFrameViewTest;
diff --git a/chrome/browser/ui/views/frame/opaque_browser_frame_view.cc b/chrome/browser/ui/views/frame/opaque_browser_frame_view.cc
index a872014..22fda3f 100644
--- a/chrome/browser/ui/views/frame/opaque_browser_frame_view.cc
+++ b/chrome/browser/ui/views/frame/opaque_browser_frame_view.cc
@@ -111,6 +111,8 @@
 ///////////////////////////////////////////////////////////////////////////////
 // OpaqueBrowserFrameView, public:
 
+constexpr char OpaqueBrowserFrameView::kClassName[];
+
 OpaqueBrowserFrameView::OpaqueBrowserFrameView(
     BrowserFrame* frame,
     BrowserView* browser_view,
@@ -168,7 +170,8 @@
 
   window_title_ = new views::Label(browser_view->GetWindowTitle());
   window_title_->SetVisible(browser_view->ShouldShowWindowTitle());
-  window_title_->SetEnabledColor(kTitleBarFeatureColor);
+  // Readability is ensured by |GetReadableFrameForegroundColor()|.
+  window_title_->SetAutoColorReadabilityEnabled(false);
   window_title_->SetSubpixelRenderingEnabled(false);
   window_title_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
   window_title_->set_id(VIEW_ID_WINDOW_TITLE);
@@ -177,11 +180,8 @@
   if (extensions::HostedAppBrowserController::IsForExperimentalHostedAppBrowser(
           browser_view->browser())) {
     hosted_app_button_container_ = new HostedAppButtonContainer(
-        frame, browser_view,
-        color_utils::GetReadableColor(kTitleBarFeatureColor,
-                                      GetFrameColor(kActive)),
-        color_utils::GetReadableColor(kTitleBarFeatureColor,
-                                      GetFrameColor(kInactive)));
+        frame, browser_view, GetReadableFrameForegroundColor(kActive),
+        GetReadableFrameForegroundColor(kInactive));
     hosted_app_button_container_->set_id(VIEW_ID_HOSTED_APP_BUTTON_CONTAINER);
     AddChildView(hosted_app_button_container_);
   }
@@ -383,6 +383,10 @@
 ///////////////////////////////////////////////////////////////////////////////
 // OpaqueBrowserFrameView, views::View overrides:
 
+const char* OpaqueBrowserFrameView::GetClassName() const {
+  return kClassName;
+}
+
 void OpaqueBrowserFrameView::ChildPreferredSizeChanged(views::View* child) {
   BrowserNonClientFrameView::ChildPreferredSizeChanged(child);
   if (browser_view()->initialized() && child == hosted_app_button_container_)
@@ -560,7 +564,7 @@
     return;  // Nothing is visible, so don't bother to paint.
 
   SkColor frame_color = GetFrameColor();
-  window_title_->SetBackgroundColor(frame_color);
+  window_title_->SetEnabledColor(GetReadableFrameForegroundColor(kUseCurrent));
   frame_background_->set_frame_color(frame_color);
   frame_background_->set_use_custom_frame(frame()->UseCustomFrame());
   frame_background_->set_is_active(ShouldPaintAsActive());
@@ -731,6 +735,19 @@
       IsMaximized());
 }
 
+SkColor OpaqueBrowserFrameView::GetReadableFrameForegroundColor(
+    ActiveState active_state) const {
+  if (extensions::HostedAppBrowserController::IsForExperimentalHostedAppBrowser(
+          browser_view()->browser())) {
+    base::Optional<SkColor> theme_color =
+        browser_view()->browser()->hosted_app_controller()->GetThemeColor();
+    if (theme_color)
+      return color_utils::GetThemedAssetColor(*theme_color);
+  }
+  return color_utils::GetReadableColor(kTitleBarFeatureColor,
+                                       GetFrameColor(active_state));
+}
+
 void OpaqueBrowserFrameView::PaintRestoredFrameBorder(
     gfx::Canvas* canvas) const {
   const ui::ThemeProvider* tp = GetThemeProvider();
diff --git a/chrome/browser/ui/views/frame/opaque_browser_frame_view.h b/chrome/browser/ui/views/frame/opaque_browser_frame_view.h
index cb3b8d8..1c504f7 100644
--- a/chrome/browser/ui/views/frame/opaque_browser_frame_view.h
+++ b/chrome/browser/ui/views/frame/opaque_browser_frame_view.h
@@ -41,6 +41,8 @@
                                public TabIconViewModel,
                                public OpaqueBrowserFrameViewLayoutDelegate {
  public:
+  static constexpr char kClassName[] = "OpaqueBrowserFrameView";
+
   // Constructs a non-client view for an BrowserFrame.
   OpaqueBrowserFrameView(BrowserFrame* frame,
                          BrowserView* browser_view,
@@ -72,6 +74,7 @@
   bool HasClientEdge() const override;
 
   // views::View:
+  const char* GetClassName() const override;
   void ChildPreferredSizeChanged(views::View* child) override;
   void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
   void OnNativeThemeChanged(const ui::NativeTheme* native_theme) override;
@@ -109,6 +112,10 @@
   bool UseCustomFrame() const override;
   bool EverHasVisibleBackgroundTabShapes() const override;
 
+  HostedAppButtonContainer* hosted_app_button_container_for_testing() {
+    return hosted_app_button_container_;
+  }
+
  protected:
   views::ImageButton* minimize_button() const { return minimize_button_; }
   views::ImageButton* maximize_button() const { return maximize_button_; }
@@ -181,6 +188,10 @@
   // Returns true if the view should draw its own custom title bar.
   bool ShouldShowWindowTitleBar() const;
 
+  // Returns the color to use for text and other title bar elements given the
+  // frame background color for |active_state|.
+  SkColor GetReadableFrameForegroundColor(ActiveState active_state) const;
+
   // Paint various sub-components of this view.  The *FrameBorder() functions
   // also paint the background of the titlebar area, since the top frame border
   // and titlebar background are a contiguous component.
diff --git a/chrome/browser/ui/views/frame/opaque_browser_frame_view_browsertest.cc b/chrome/browser/ui/views/frame/opaque_browser_frame_view_browsertest.cc
new file mode 100644
index 0000000..ddab231
--- /dev/null
+++ b/chrome/browser/ui/views/frame/opaque_browser_frame_view_browsertest.cc
@@ -0,0 +1,98 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/views/frame/opaque_browser_frame_view.h"
+
+#include "base/test/scoped_feature_list.h"
+#include "build/build_config.h"
+#include "chrome/browser/extensions/browsertest_util.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/views/frame/browser_view.h"
+#include "chrome/browser/ui/views/frame/hosted_app_button_container.h"
+#include "chrome/common/chrome_features.h"
+#include "chrome/common/web_application_info.h"
+#include "chrome/test/base/in_process_browser_test.h"
+