diff --git a/.gn b/.gn
index 70856f6..3a7b224f 100644
--- a/.gn
+++ b/.gn
@@ -386,7 +386,6 @@
   # "//third_party/crashpad/*", 20ish errors
   "//third_party/crc32c/*",
   "//third_party/cros_system_api/*",
-  "//third_party/custom_tabs_client/*",
   "//third_party/cython/*",
   "//third_party/d3/*",
   "//third_party/dawn/*",
diff --git a/DEPS b/DEPS
index 6deb803..bbb7a38 100644
--- a/DEPS
+++ b/DEPS
@@ -149,7 +149,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': 'd795c6cc17ce4bc9a29ed4c55f1908e66a64a349',
+  'v8_revision': 'a062e70735257f2e8f253772c867299bee3ae1fe',
   # 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.
@@ -157,7 +157,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': '7c7dec01ce348d24e768b33633c7711cd0bd4182',
+  'angle_revision': '0fec5eabc671e58b1ced902928ae02155fba48fb',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -165,7 +165,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': '24374a6f352b29540f669b0b5a362e76664b037c',
+  'pdfium_revision': '3c168bba58d8bc96bd35a821cf77b81b0bb4d5e0',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling BoringSSL
   # and whatever else without interference from each other.
@@ -208,7 +208,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': 'cad35e22dcad126c6a20663ded101565e6326d82',
+  'catapult_revision': '5508416f19586f3b2eba9aee68ece7ceb00b71ae',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -264,7 +264,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'spv_tools_revision': '698b56a8f02495d115cd6c5eefd89b812d55b13d',
+  'spv_tools_revision': 'b029d3697ea34b82b4f77d63a45dcc181ff59a65',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -280,11 +280,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': '421584173cae282ee496291b1cb08eebcad71be7',
+  'dawn_revision': 'bb10a9187623469df7d90db59c88de13fc9a0c5f',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'quiche_revision': 'f35ea98e9c0aae429b91fac10a2cc40fff84c938',
+  'quiche_revision': '092d8219e053265bc5462122ec89e90b4c935bae',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ios_webkit
   # and whatever else without interference from each other.
@@ -664,6 +664,11 @@
       'dep_type': 'cipd',
   },
 
+  'src/third_party/android_sdk/androidx_browser/src': {
+      'url': Var('chromium_git') + '/external/gob/android/platform/frameworks/support/browser.git' + '@' + 'aeeea8bd0a6703bc4a148e9bcd6998553def74ab',
+      'condition': 'checkout_android',
+  },
+
   'src/third_party/android_sdk/public': {
       'packages': [
           {
@@ -809,7 +814,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'fb6466042695a7c0946db568a434d6ea79aee163',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '171da61e7635b6b755122d9fcfec2af469851aac',
       'condition': 'checkout_linux',
   },
 
@@ -1207,7 +1212,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'be9d120c47f782f367475821744b00242bd6ce72',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '6e0df67d958d5bd242cf5e33a6906c8988c5dcfb',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1375,7 +1380,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + 'abaae129d9a0c6e1e092067e0b105475df43352e',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '3cc2f70bc33c3bfba6732d896e8c25a861d9b8b7',
+    Var('webrtc_git') + '/src.git' + '@' + 'efffd0a5fa22c3eb5c3182d44cfe2917563cedbc',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
@@ -1416,7 +1421,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@575554b5ae385cbea1c38179fc66934d46ee474f',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@619e49fa7df7c6f1d23d5603d5cf2a767fae6085',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/BUILD.gn b/android_webview/BUILD.gn
index 9d99f36..a9a8d74 100644
--- a/android_webview/BUILD.gn
+++ b/android_webview/BUILD.gn
@@ -296,6 +296,8 @@
   # The .grd contains references to generated files.
   source_is_generated = true
 
+  use_brotli = true
+
   # See :generate_webui_resources for an explanation of the whitelist
   _whitelist = "ui/grit_resources_whitelist.txt"
   inputs = [
diff --git a/android_webview/browser/aw_content_browser_client.cc b/android_webview/browser/aw_content_browser_client.cc
index 44ad27b..de2480e6 100644
--- a/android_webview/browser/aw_content_browser_client.cc
+++ b/android_webview/browser/aw_content_browser_client.cc
@@ -478,21 +478,6 @@
   return true;
 }
 
-void AwContentBrowserClient::AllowWorkerFileSystem(
-    const GURL& url,
-    content::ResourceContext* context,
-    const std::vector<content::GlobalFrameRoutingId>& render_frames,
-    base::Callback<void(bool)> callback) {
-  callback.Run(true);
-}
-
-bool AwContentBrowserClient::AllowWorkerIndexedDB(
-    const GURL& url,
-    content::ResourceContext* context,
-    const std::vector<content::GlobalFrameRoutingId>& render_frames) {
-  return true;
-}
-
 scoped_refptr<content::QuotaPermissionContext>
 AwContentBrowserClient::CreateQuotaPermissionContext() {
   return new AwQuotaPermissionContext;
diff --git a/android_webview/browser/aw_content_browser_client.h b/android_webview/browser/aw_content_browser_client.h
index dee82cd0..0e95c20 100644
--- a/android_webview/browser/aw_content_browser_client.h
+++ b/android_webview/browser/aw_content_browser_client.h
@@ -76,15 +76,6 @@
   bool AllowAppCache(const GURL& manifest_url,
                      const GURL& first_party,
                      content::BrowserContext* context) override;
-  void AllowWorkerFileSystem(
-      const GURL& url,
-      content::ResourceContext* context,
-      const std::vector<content::GlobalFrameRoutingId>& render_frames,
-      base::Callback<void(bool)> callback) override;
-  bool AllowWorkerIndexedDB(
-      const GURL& url,
-      content::ResourceContext* context,
-      const std::vector<content::GlobalFrameRoutingId>& render_frames) override;
   scoped_refptr<content::QuotaPermissionContext> CreateQuotaPermissionContext()
       override;
   void GetQuotaSettings(
diff --git a/ash/app_list/views/app_list_view_unittest.cc b/ash/app_list/views/app_list_view_unittest.cc
index a250264..9521b2c 100644
--- a/ash/app_list/views/app_list_view_unittest.cc
+++ b/ash/app_list/views/app_list_view_unittest.cc
@@ -748,7 +748,7 @@
   // The selected view will always be a result when using
   // |result_selection_controller|
   if (app_list_features::IsSearchBoxSelectionEnabled())
-    forward_view_list.push_back(tile_views[0]);
+    forward_view_list.push_back(nullptr);
   else
     forward_view_list.push_back(search_box_view()->search_box());
 
@@ -757,10 +757,22 @@
 
   if (app_list_features::IsSearchBoxSelectionEnabled()) {
     // Test traversal triggered by tab.
+    EXPECT_TRUE(search_box_view()->search_box()->HasFocus());
     TestSelectionTraversal(forward_view_list, ui::VKEY_TAB, false);
+    EXPECT_TRUE(search_box_view()->close_button()->HasFocus());
+
+    // Focus cycles from the close button to the first result.
+    TestSelectionTraversal({nullptr, forward_view_list[0]}, ui::VKEY_TAB,
+                           false);
+    EXPECT_TRUE(search_box_view()->search_box()->HasFocus());
+
+    // The shift+tab key should move focus back to the close button.
+    TestSelectionTraversal({forward_view_list[0], nullptr}, ui::VKEY_TAB, true);
+    EXPECT_TRUE(search_box_view()->close_button()->HasFocus());
 
     // Test traversal triggered by shift+tab.
     TestSelectionTraversal(backward_view_list, ui::VKEY_TAB, true);
+    EXPECT_TRUE(search_box_view()->search_box()->HasFocus());
   } else {
     // Test traversal triggered by tab.
     TestFocusTraversal(forward_view_list, ui::VKEY_TAB, false);
@@ -770,6 +782,61 @@
   }
 }
 
+// Tests return key with search box close button focused (with app list view in
+// half state):
+// *   search box text is cleared
+// *   search box gets focus, but it's not active
+// *   subsequent tab keys move focus to app list folder view.
+TEST_F(AppListViewFocusTest, CloseButtonClearsSearchOnEnter) {
+  Show();
+
+  // Type something in search box to transition to HALF state and populate
+  // fake search results.
+  search_box_view()->search_box()->InsertText(base::ASCIIToUTF16("test"));
+  EXPECT_EQ(app_list_view()->app_list_state(), ash::AppListViewState::kHalf);
+  constexpr int kTileResults = 3;
+  constexpr int kListResults = 2;
+  SetUpSearchResults(kTileResults, kListResults, true);
+
+  const std::vector<SearchResultTileItemView*>& tile_views =
+      contents_view()
+          ->search_result_tile_item_list_view_for_test()
+          ->tile_views_for_test();
+  ASSERT_FALSE(tile_views.empty());
+  views::View* first_result_view = tile_views[0];
+
+  // Shift+Tab to focus close button.
+  if (app_list_features::IsSearchBoxSelectionEnabled()) {
+    TestSelectionTraversal({first_result_view, nullptr}, ui::VKEY_TAB, true);
+    EXPECT_TRUE(search_box_view()->close_button()->HasFocus());
+  } else {
+    TestFocusTraversal(
+        {search_box_view()->search_box(), search_box_view()->close_button()},
+        ui::VKEY_TAB, false);
+  }
+
+  // Enter - it should clear the search box.
+  SimulateKeyPress(ui::VKEY_RETURN, false /*shift_down*/);
+  EXPECT_TRUE(search_box_view()->search_box()->HasFocus());
+  EXPECT_EQ(base::string16(), search_box_view()->search_box()->GetText());
+  EXPECT_FALSE(search_box_view()->is_search_box_active());
+  EXPECT_FALSE(contents_view()->search_results_page_view()->GetVisible());
+  ResultSelectionController* selection_controller =
+      contents_view()
+          ->search_results_page_view()
+          ->result_selection_controller();
+  EXPECT_EQ(nullptr, selection_controller->selected_result());
+
+  // Tab traversal continues with app list folder items.
+  std::vector<views::View*> forward_view_list;
+  forward_view_list.push_back(search_box_view()->search_box());
+  const views::ViewModelT<AppListItemView>* view_model =
+      app_list_folder_view()->items_grid_view()->view_model();
+  for (int i = 0; i < view_model->view_size(); ++i)
+    forward_view_list.push_back(view_model->view_at(i));
+  TestFocusTraversal(forward_view_list, ui::VKEY_TAB, false);
+}
+
 // Tests focus traversal in HALF state with opened search box using |VKEY_LEFT|
 // and |VKEY_RIGHT|.
 TEST_P(AppListViewFocusTest, LeftRightFocusTraversalInHalfState) {
@@ -988,7 +1055,7 @@
     contents_view()
         ->search_results_page_view()
         ->result_selection_controller()
-        ->ResetSelection();
+        ->ResetSelection(nullptr);
   }
 
   if (app_list_features::IsSearchBoxSelectionEnabled()) {
diff --git a/ash/app_list/views/result_selection_controller.cc b/ash/app_list/views/result_selection_controller.cc
index 881c66c..02357529 100644
--- a/ash/app_list/views/result_selection_controller.cc
+++ b/ash/app_list/views/result_selection_controller.cc
@@ -9,6 +9,8 @@
 
 namespace app_list {
 
+ResultLocationDetails::ResultLocationDetails() = default;
+
 ResultLocationDetails::ResultLocationDetails(int container_index,
                                              int container_count,
                                              int result_index,
@@ -40,16 +42,21 @@
 
 ResultSelectionController::~ResultSelectionController() = default;
 
-bool ResultSelectionController::MoveSelection(const ui::KeyEvent& event) {
-  ResultLocationDetails next_location = GetNextResultLocation(event);
-  bool selection_changed = !(next_location == *selected_location_details_);
-  if (selection_changed) {
-    SetSelection(next_location, event.IsShiftDown());
+ResultSelectionController::MoveResult ResultSelectionController::MoveSelection(
+    const ui::KeyEvent& event) {
+  ResultLocationDetails next_location;
+  if (!selected_location_details_) {
+    ResetSelection(&event);
+    return MoveResult::kResultChanged;
   }
-  return selection_changed;
+
+  MoveResult result = GetNextResultLocation(event, &next_location);
+  if (result == MoveResult::kResultChanged)
+    SetSelection(next_location, event.IsShiftDown());
+  return result;
 }
 
-void ResultSelectionController::ResetSelection() {
+void ResultSelectionController::ResetSelection(const ui::KeyEvent* key_event) {
   // Prevents crash on start up
   if (result_selection_model_->size() == 0)
     return;
@@ -62,7 +69,20 @@
       result_selection_model_->at(0)
           ->horizontally_traversable() /* container_is_horizontal */);
 
-  auto* new_selection = result_selection_model_->at(0)->GetFirstResultView();
+  const bool is_up_key = key_event && key_event->key_code() == ui::VKEY_UP;
+  const bool is_shift_tab = key_event &&
+                            key_event->key_code() == ui::VKEY_TAB &&
+                            key_event->IsShiftDown();
+  // Note: left and right arrows are used primarily for traversal in horizontal
+  // containers, so treat "back" arrow as other non-traversal keys when deciding
+  // whether to reverse selection direction.
+  if (is_up_key || is_shift_tab)
+    ChangeContainer(selected_location_details_.get(), -1);
+
+  auto* new_selection =
+      result_selection_model_->at(selected_location_details_->container_index)
+          ->GetResultViewAt(selected_location_details_->result_index);
+
   if (new_selection && new_selection->selected())
     return;
 
@@ -72,7 +92,7 @@
   selected_result_ = new_selection;
 
   if (selected_result_)
-    selected_result_->SetSelected(true, base::nullopt);
+    selected_result_->SetSelected(true, is_shift_tab);
 }
 
 void ResultSelectionController::ClearSelection() {
@@ -82,25 +102,29 @@
   selected_result_ = nullptr;
 }
 
-ResultLocationDetails ResultSelectionController::GetNextResultLocation(
-    const ui::KeyEvent& event) {
-  return GetNextResultLocationForLocation(event, *selected_location_details_);
+ResultSelectionController::MoveResult
+ResultSelectionController::GetNextResultLocation(
+    const ui::KeyEvent& event,
+    ResultLocationDetails* next_location) {
+  return GetNextResultLocationForLocation(event, *selected_location_details_,
+                                          next_location);
 }
 
-ResultLocationDetails
+ResultSelectionController::MoveResult
 ResultSelectionController::GetNextResultLocationForLocation(
     const ui::KeyEvent& event,
-    const ResultLocationDetails& location) {
-  ResultLocationDetails new_location(location);
+    const ResultLocationDetails& location,
+    ResultLocationDetails* next_location) {
+  *next_location = location;
 
   // Only arrow keys (unhandled and unmodified) or the tab key will change our
   // selection.
   if (!(IsUnhandledArrowKeyEvent(event) || event.key_code() == ui::VKEY_TAB))
-    return new_location;
+    return MoveResult::kNone;
 
   if (selected_result_ && event.key_code() == ui::VKEY_TAB &&
       selected_result_->SelectNextResultAction(event.IsShiftDown())) {
-    return new_location;
+    return MoveResult::kNone;
   }
 
   switch (event.key_code()) {
@@ -108,16 +132,23 @@
       if (event.IsShiftDown()) {
         // Reverse tab traversal always goes to the 'previous' result.
         if (location.is_first_result()) {
-          ChangeContainer(&new_location, location.container_index - 1);
+          ChangeContainer(next_location, location.container_index - 1);
+
+          if (next_location->container_index >= location.container_index)
+            return MoveResult::kSelectionCycleRejected;
+
         } else {
-          --new_location.result_index;
+          --next_location->result_index;
         }
       } else {
         // Forward tab traversal always goes to the 'next' result.
         if (location.is_last_result()) {
-          ChangeContainer(&new_location, location.container_index + 1);
+          ChangeContainer(next_location, location.container_index + 1);
+
+          if (next_location->container_index <= location.container_index)
+            return MoveResult::kSelectionCycleRejected;
         } else {
-          ++new_location.result_index;
+          ++next_location->result_index;
         }
       }
 
@@ -125,19 +156,24 @@
     case ui::VKEY_UP:
       if (location.container_is_horizontal || location.is_first_result()) {
         // Traversing 'up' from the top of a container changes containers.
-        ChangeContainer(&new_location, location.container_index - 1);
+        ChangeContainer(next_location, location.container_index - 1);
+
+        if (next_location->container_index >= location.container_index)
+          return MoveResult::kSelectionCycleRejected;
       } else {
         // Traversing 'up' moves up one result.
-        --new_location.result_index;
+        --next_location->result_index;
       }
       break;
     case ui::VKEY_DOWN:
       if (location.container_is_horizontal || location.is_last_result()) {
         // Traversing 'down' from the bottom of a container changes containers.
-        ChangeContainer(&new_location, location.container_index + 1);
+        ChangeContainer(next_location, location.container_index + 1);
+        if (next_location->container_index <= location.container_index)
+          return MoveResult::kSelectionCycleRejected;
       } else {
         // Traversing 'down' moves down one result.
-        ++new_location.result_index;
+        ++next_location->result_index;
       }
       break;
     case ui::VKEY_RIGHT:
@@ -158,24 +194,25 @@
       if (event.key_code() == forward) {
         // If not at the last result, increment forward.
         if (location.result_index != location.result_count - 1)
-          ++new_location.result_index;
+          ++next_location->result_index;
         else
           // Loop back to the first result.
-          new_location.result_index = 0;
+          next_location->result_index = 0;
       } else {
         // If not at the first result, increment backward.
         if (location.result_index != 0)
-          --new_location.result_index;
+          --next_location->result_index;
         else
           // Loop around to the last result.
-          new_location.result_index = location.result_count - 1;
+          next_location->result_index = location.result_count - 1;
       }
     } break;
 
     default:
       NOTREACHED();
   }
-  return new_location;
+  return *next_location == location ? MoveResult::kNone
+                                    : MoveResult::kResultChanged;
 }
 
 void ResultSelectionController::SetSelection(
diff --git a/ash/app_list/views/result_selection_controller.h b/ash/app_list/views/result_selection_controller.h
index 32549ab..065a6d7 100644
--- a/ash/app_list/views/result_selection_controller.h
+++ b/ash/app_list/views/result_selection_controller.h
@@ -26,6 +26,7 @@
 // including both inter- and intra-container details, along with the traversal
 // direction for the container.
 struct APP_LIST_EXPORT ResultLocationDetails {
+  ResultLocationDetails();
   ResultLocationDetails(int container_index,
                         int container_count,
                         int result_index,
@@ -60,6 +61,22 @@
 // A controller class to manage result selection across containers.
 class APP_LIST_EXPORT ResultSelectionController {
  public:
+  enum class MoveResult {
+    // The selection has not changed (excluding the case covered by
+    // kSelectionCycleRejected).
+    kNone,
+
+    // The selection has not changed because the selection would cycle.
+    kSelectionCycleRejected,
+
+    // The currently selected result has changed.
+    //
+    // Note: As long as the selected result remains the same, the result action
+    // changes will be reported as kNone, mainly because the code that uses
+    // MoveSelection() treats them the same.
+    kResultChanged,
+  };
+
   explicit ResultSelectionController(
       const ResultSelectionModel* result_container_views);
   ~ResultSelectionController();
@@ -72,12 +89,13 @@
     return selected_location_details_.get();
   }
 
-  // Calls |SetSelection| using the result of |GetNextResultLocation|. Returns
-  // true if selection was changed.
-  bool MoveSelection(const ui::KeyEvent& event);
+  // Calls |SetSelection| using the result of |GetNextResultLocation|.
+  MoveResult MoveSelection(const ui::KeyEvent& event);
 
   // Resets the selection to the first result.
-  void ResetSelection();
+  // |key_event| - The key event that triggered reselect, if any. Used to
+  //     determine whether selection should start at the last element.
+  void ResetSelection(const ui::KeyEvent* key_event);
 
   // Clears the |selected_result_|, |selected_location_details_|.
   void ClearSelection();
@@ -85,13 +103,15 @@
  private:
   // Calls |GetNextResultLocationForLocation| using |selected_location_details_|
   // as the location
-  ResultLocationDetails GetNextResultLocation(const ui::KeyEvent& event);
+  MoveResult GetNextResultLocation(const ui::KeyEvent& event,
+                                   ResultLocationDetails* next_location);
 
   // Logic for next is separated for modular use. You can ask for the "next"
   // location to be generated using any starting location/event combination.
-  ResultLocationDetails GetNextResultLocationForLocation(
+  MoveResult GetNextResultLocationForLocation(
       const ui::KeyEvent& event,
-      const ResultLocationDetails& location);
+      const ResultLocationDetails& location,
+      ResultLocationDetails* next_location);
 
   // Sets the current selection to the provided |location|.
   void SetSelection(const ResultLocationDetails& location,
diff --git a/ash/app_list/views/result_selection_controller_unittest.cc b/ash/app_list/views/result_selection_controller_unittest.cc
index 1135054..235a1215c 100644
--- a/ash/app_list/views/result_selection_controller_unittest.cc
+++ b/ash/app_list/views/result_selection_controller_unittest.cc
@@ -280,9 +280,17 @@
 
     // These are divided to give as much detail as possible for a test failure.
     TestSingleAxisForward(forward, locations);
-    TestSingleAxisLoop(forward, locations);
-    TestSingleAxisLoopBack(backward, locations);
+    if (horizontal) {
+      TestSingleAxisLoop(forward, locations);
+      TestSingleAxisLoopBack(backward, locations);
+    } else {
+      TestSingleAxisLoopRejected(forward, locations);
+    }
     TestSingleAxisBackward(backward, locations);
+
+    if (!horizontal) {
+      TestSingleAxisLoopBackRejected(backward, locations);
+    }
   }
 
   void TestSingleAxisForward(
@@ -292,7 +300,8 @@
     ASSERT_EQ(*result_selection_controller_->selected_location_details(),
               locations[0]);
     for (size_t i = 1; i < locations.size(); i++) {
-      result_selection_controller_->MoveSelection(*forward);
+      ASSERT_EQ(ResultSelectionController::MoveResult::kResultChanged,
+                result_selection_controller_->MoveSelection(*forward));
       ASSERT_EQ(*result_selection_controller_->selected_location_details(),
                 locations[i]);
     }
@@ -306,7 +315,8 @@
               locations[3]);
 
     // Expect loop back to first result.
-    result_selection_controller_->MoveSelection(*forward);
+    EXPECT_EQ(ResultSelectionController::MoveResult::kResultChanged,
+              result_selection_controller_->MoveSelection(*forward));
     ASSERT_EQ(*result_selection_controller_->selected_location_details(),
               locations[0]);
   }
@@ -319,11 +329,40 @@
               locations[0]);
 
     // Expect loop back to last result.
-    result_selection_controller_->MoveSelection(*backward);
+    EXPECT_EQ(ResultSelectionController::MoveResult::kResultChanged,
+              result_selection_controller_->MoveSelection(*backward));
     ASSERT_EQ(*result_selection_controller_->selected_location_details(),
               locations[3]);
   }
 
+  void TestSingleAxisLoopRejected(
+      ui::KeyEvent* forward,
+      const std::vector<ResultLocationDetails>& locations) {
+    // Starts at the end
+    ASSERT_EQ(*result_selection_controller_->selected_location_details(),
+              locations[3]);
+
+    // Expect no change in location.
+    ASSERT_EQ(ResultSelectionController::MoveResult::kSelectionCycleRejected,
+              result_selection_controller_->MoveSelection(*forward));
+    ASSERT_EQ(*result_selection_controller_->selected_location_details(),
+              locations[3]);
+  }
+
+  void TestSingleAxisLoopBackRejected(
+      ui::KeyEvent* backward,
+      const std::vector<ResultLocationDetails>& locations) {
+    // Starts at the first
+    ASSERT_EQ(*result_selection_controller_->selected_location_details(),
+              locations[0]);
+
+    // Expect no change in location.
+    ASSERT_EQ(ResultSelectionController::MoveResult::kSelectionCycleRejected,
+              result_selection_controller_->MoveSelection(*backward));
+    ASSERT_EQ(*result_selection_controller_->selected_location_details(),
+              locations[0]);
+  }
+
   void TestSingleAxisBackward(
       ui::KeyEvent* backward,
       const std::vector<ResultLocationDetails>& locations) {
@@ -334,7 +373,8 @@
     ASSERT_EQ(*result_selection_controller_->selected_location_details(),
               locations[last_index]);
     for (size_t i = last_index; i > 0; i--) {
-      result_selection_controller_->MoveSelection(*backward);
+      ASSERT_EQ(ResultSelectionController::MoveResult::kResultChanged,
+                result_selection_controller_->MoveSelection(*backward));
       ASSERT_EQ(*result_selection_controller_->selected_location_details(),
                 locations[i - 1]);
     }
@@ -382,8 +422,12 @@
         // End on the last result.
       }
 
-      // Change Containers
-      result_selection_controller_->MoveSelection(*vertical_forward);
+      // Change Containers, if not the last container.
+      ASSERT_EQ(
+          i == num_containers - 1
+              ? ResultSelectionController::MoveResult::kSelectionCycleRejected
+              : ResultSelectionController::MoveResult::kResultChanged,
+          result_selection_controller_->MoveSelection(*vertical_forward));
     }
   }
 
@@ -408,28 +452,31 @@
     };
 
     // Initialize the RSC for test.
-    result_selection_controller_->ResetSelection();
+    result_selection_controller_->ResetSelection(nullptr);
 
     ASSERT_EQ(create_test_location(0, 0), GetCurrentLocation());
     EXPECT_TRUE(CurrentResultActionNotSelected());
 
     // TAB - the result should remain the same, but the selected action is
     // expected to change.
-    EXPECT_FALSE(result_selection_controller_->MoveSelection(tab_key_));
+    EXPECT_EQ(ResultSelectionController::MoveResult::kNone,
+              result_selection_controller_->MoveSelection(tab_key_));
 
     ASSERT_EQ(create_test_location(0, 0), GetCurrentLocation());
     EXPECT_TRUE(CurrentResultActionSelected(0));
 
     // TAB - the result should remain the same, but the selected action is
     // expected to change.
-    EXPECT_FALSE(result_selection_controller_->MoveSelection(tab_key_));
+    EXPECT_EQ(ResultSelectionController::MoveResult::kNone,
+              result_selection_controller_->MoveSelection(tab_key_));
 
     ASSERT_EQ(create_test_location(0, 0), GetCurrentLocation());
     EXPECT_TRUE(CurrentResultActionSelected(1));
 
     // TAB - move to the next result.
     TestResultView* previous_result = GetCurrentSelection();
-    EXPECT_TRUE(result_selection_controller_->MoveSelection(tab_key_));
+    EXPECT_EQ(ResultSelectionController::MoveResult::kResultChanged,
+              result_selection_controller_->MoveSelection(tab_key_));
 
     ASSERT_EQ(create_test_location(0, 1), GetCurrentLocation());
     EXPECT_TRUE(CurrentResultActionNotSelected());
@@ -437,46 +484,53 @@
 
     // Shift-TAB - move back to the previous result, and expect the last action
     // to be selected.
-    EXPECT_TRUE(result_selection_controller_->MoveSelection(shift_tab_key_));
+    EXPECT_EQ(ResultSelectionController::MoveResult::kResultChanged,
+              result_selection_controller_->MoveSelection(shift_tab_key_));
 
     ASSERT_EQ(create_test_location(0, 0), GetCurrentLocation());
     EXPECT_TRUE(CurrentResultActionSelected(1));
 
     // TAB - move back to next result.
     previous_result = GetCurrentSelection();
-    EXPECT_TRUE(result_selection_controller_->MoveSelection(tab_key_));
+    EXPECT_EQ(ResultSelectionController::MoveResult::kResultChanged,
+              result_selection_controller_->MoveSelection(tab_key_));
 
     ASSERT_EQ(create_test_location(0, 1), GetCurrentLocation());
     EXPECT_TRUE(CurrentResultActionNotSelected());
     EXPECT_FALSE(previous_result->selected());
 
     // TAB - stay at the same result, but select next action.
-    EXPECT_FALSE(result_selection_controller_->MoveSelection(tab_key_));
+    EXPECT_EQ(ResultSelectionController::MoveResult::kNone,
+              result_selection_controller_->MoveSelection(tab_key_));
 
     ASSERT_EQ(create_test_location(0, 1), GetCurrentLocation());
     EXPECT_TRUE(CurrentResultActionSelected(0));
 
     // Shift-TAB - same result, but deselects actions.
-    EXPECT_FALSE(result_selection_controller_->MoveSelection(shift_tab_key_));
+    EXPECT_EQ(ResultSelectionController::MoveResult::kNone,
+              result_selection_controller_->MoveSelection(shift_tab_key_));
 
     ASSERT_EQ(create_test_location(0, 1), GetCurrentLocation());
     EXPECT_TRUE(CurrentResultActionNotSelected());
 
     // TAB - reselect the first action.
-    EXPECT_FALSE(result_selection_controller_->MoveSelection(tab_key_));
+    EXPECT_EQ(ResultSelectionController::MoveResult::kNone,
+              result_selection_controller_->MoveSelection(tab_key_));
 
     ASSERT_EQ(create_test_location(0, 1), GetCurrentLocation());
     EXPECT_TRUE(CurrentResultActionSelected(0));
 
     // TAB - select the next action.
-    EXPECT_FALSE(result_selection_controller_->MoveSelection(tab_key_));
+    EXPECT_EQ(ResultSelectionController::MoveResult::kNone,
+              result_selection_controller_->MoveSelection(tab_key_));
 
     ASSERT_EQ(create_test_location(0, 1), GetCurrentLocation());
     EXPECT_TRUE(CurrentResultActionSelected(1));
 
     // TAB - select a result in the next container.
     previous_result = GetCurrentSelection();
-    EXPECT_TRUE(result_selection_controller_->MoveSelection(tab_key_));
+    EXPECT_EQ(ResultSelectionController::MoveResult::kResultChanged,
+              result_selection_controller_->MoveSelection(tab_key_));
 
     ASSERT_EQ(create_test_location(1, 0), GetCurrentLocation());
     EXPECT_TRUE(CurrentResultActionNotSelected());
@@ -484,19 +538,57 @@
 
     // Shift-TAB - move to previous result/action.
     previous_result = GetCurrentSelection();
-    EXPECT_TRUE(result_selection_controller_->MoveSelection(shift_tab_key_));
+    EXPECT_EQ(ResultSelectionController::MoveResult::kResultChanged,
+              result_selection_controller_->MoveSelection(shift_tab_key_));
 
     ASSERT_EQ(create_test_location(0, 1), GetCurrentLocation());
     EXPECT_TRUE(CurrentResultActionSelected(1));
     EXPECT_FALSE(previous_result->selected());
 
     // Shift-TAB - move to previous action.
-    EXPECT_FALSE(result_selection_controller_->MoveSelection(shift_tab_key_));
+    EXPECT_EQ(ResultSelectionController::MoveResult::kNone,
+              result_selection_controller_->MoveSelection(shift_tab_key_));
 
     ASSERT_EQ(create_test_location(0, 1), GetCurrentLocation());
     EXPECT_TRUE(CurrentResultActionSelected(0));
   }
 
+  // Tests calling MoveSelection when none of the results are selected. The
+  // effect should be the same as resetting selection.
+  void TestMoveNullSelection(const ui::KeyEvent& key_event,
+                             bool expect_reverse,
+                             bool expect_action_selected) {
+    const int kContainerCount = 2;
+    const int kResultsPerContainer = 2;
+    const int kActionsPerResult = 1;
+    std::vector<std::unique_ptr<SearchResultContainerView>> containers =
+        CreateContainerVector(kContainerCount,
+                              TestContainerParams(false, kResultsPerContainer,
+                                                  kActionsPerResult));
+    SetContainers(containers);
+
+    auto create_test_location = [](int container_index, int result_index) {
+      return ResultLocationDetails(container_index, kContainerCount,
+                                   result_index, kResultsPerContainer,
+                                   false /*container_is_horizontal*/);
+    };
+
+    EXPECT_EQ(ResultSelectionController::MoveResult::kResultChanged,
+              result_selection_controller_->MoveSelection(key_event));
+
+    if (expect_reverse) {
+      ASSERT_EQ(create_test_location(1, 1), GetCurrentLocation());
+    } else {
+      ASSERT_EQ(create_test_location(0, 0), GetCurrentLocation());
+    }
+
+    if (expect_action_selected) {
+      EXPECT_TRUE(CurrentResultActionSelected(0));
+    } else {
+      EXPECT_TRUE(CurrentResultActionNotSelected());
+    }
+  }
+
   std::unique_ptr<ResultSelectionController> result_selection_controller_;
   std::vector<SearchResultContainerView*> containers_;
 
@@ -533,7 +625,7 @@
   containers_.emplace_back(vertical_container.get());
 
   // Initialize the RSC for test.
-  result_selection_controller_->ResetSelection();
+  result_selection_controller_->ResetSelection(nullptr);
 
   TestSingleAxisTraversal(&down_arrow_, &up_arrow_);
 }
@@ -549,7 +641,7 @@
   containers_.emplace_back(vertical_container.get());
 
   // Initialize the RSC for test.
-  result_selection_controller_->ResetSelection();
+  result_selection_controller_->ResetSelection(nullptr);
 
   TestSingleAxisTraversal(&tab_key_, &shift_tab_key_);
 }
@@ -568,7 +660,7 @@
   containers_.emplace_back(horizontal_container.get());
 
   // Initialize the RSC for test.
-  result_selection_controller_->ResetSelection();
+  result_selection_controller_->ResetSelection(nullptr);
 
   TestSingleAxisTraversal(forward, backward);
 }
@@ -584,7 +676,7 @@
   containers_.emplace_back(vertical_container.get());
 
   // Initialize the RSC for test.
-  result_selection_controller_->ResetSelection();
+  result_selection_controller_->ResetSelection(nullptr);
 
   TestMultiAxisTraversal(false);
 }
@@ -600,7 +692,7 @@
   containers_.emplace_back(vertical_container.get());
 
   // Initialize the RSC for test.
-  result_selection_controller_->ResetSelection();
+  result_selection_controller_->ResetSelection(nullptr);
 
   TestMultiAxisTraversal(true);
 }
@@ -611,7 +703,7 @@
   SetContainers(vertical_containers);
 
   // Initialize the RSC for test.
-  result_selection_controller_->ResetSelection();
+  result_selection_controller_->ResetSelection(nullptr);
 
   TestMultiAxisTraversal(false);
 }
@@ -622,7 +714,7 @@
   SetContainers(vertical_containers);
 
   // Initialize the RSC for test.
-  result_selection_controller_->ResetSelection();
+  result_selection_controller_->ResetSelection(nullptr);
 
   TestMultiAxisTraversal(true);
 }
@@ -634,7 +726,7 @@
   SetContainers(horizontal_containers);
 
   // Initialize the RSC for test.
-  result_selection_controller_->ResetSelection();
+  result_selection_controller_->ResetSelection(nullptr);
 
   TestMultiAxisTraversal(false);
 }
@@ -646,7 +738,7 @@
   SetContainers(horizontal_containers);
 
   // Initialize the RSC for test.
-  result_selection_controller_->ResetSelection();
+  result_selection_controller_->ResetSelection(nullptr);
 
   TestMultiAxisTraversal(true);
 }
@@ -657,7 +749,7 @@
   SetContainers(containers);
 
   // Initialize the RSC for test.
-  result_selection_controller_->ResetSelection();
+  result_selection_controller_->ResetSelection(nullptr);
 
   TestMultiAxisTraversal(false);
 }
@@ -668,7 +760,7 @@
   SetContainers(containers);
 
   // Initialize the RSC for test.
-  result_selection_controller_->ResetSelection();
+  result_selection_controller_->ResetSelection(nullptr);
 
   TestMultiAxisTraversal(false);
 }
@@ -679,7 +771,7 @@
   SetContainers(containers);
 
   // Initialize the RSC for test.
-  result_selection_controller_->ResetSelection();
+  result_selection_controller_->ResetSelection(nullptr);
 
   TestMultiAxisTraversal(false);
 }
@@ -690,7 +782,7 @@
   SetContainers(containers);
 
   // Initialize the RSC for test.
-  result_selection_controller_->ResetSelection();
+  result_selection_controller_->ResetSelection(nullptr);
 
   TestMultiAxisTraversal(false);
 }
@@ -703,6 +795,176 @@
   TestTabTraversalWithResultActions(false /*horizontal_container*/);
 }
 
+TEST_F(ResultSelectionTest, TabCycleInContainerWithResultActions) {
+  const int kContainerCount = 2;
+  const int kResultsPerContainer = 1;
+  const int kActionsPerResult = 1;
+  std::vector<std::unique_ptr<SearchResultContainerView>> containers =
+      CreateContainerVector(
+          kContainerCount,
+          TestContainerParams(false, kResultsPerContainer, kActionsPerResult));
+  SetContainers(containers);
+
+  auto create_test_location = [](int container_index, int result_index) {
+    return ResultLocationDetails(container_index, kContainerCount, result_index,
+                                 kResultsPerContainer, false);
+  };
+
+  // Initialize the RSC for test.
+  result_selection_controller_->ResetSelection(nullptr);
+
+  ASSERT_EQ(create_test_location(0, 0), GetCurrentLocation());
+  EXPECT_TRUE(CurrentResultActionNotSelected());
+
+  // Shift TAB - reject.
+  EXPECT_EQ(ResultSelectionController::MoveResult::kSelectionCycleRejected,
+            result_selection_controller_->MoveSelection(shift_tab_key_));
+
+  ASSERT_EQ(create_test_location(0, 0), GetCurrentLocation());
+  EXPECT_TRUE(CurrentResultActionNotSelected());
+
+  // TAB - the result should remain the same, but the selected action is
+  // expected to change.
+  EXPECT_EQ(ResultSelectionController::MoveResult::kNone,
+            result_selection_controller_->MoveSelection(tab_key_));
+
+  ASSERT_EQ(create_test_location(0, 0), GetCurrentLocation());
+  EXPECT_TRUE(CurrentResultActionSelected(0));
+
+  // TAB - move to the next result, which is in the next container.
+  TestResultView* previous_result = GetCurrentSelection();
+  EXPECT_EQ(ResultSelectionController::MoveResult::kResultChanged,
+            result_selection_controller_->MoveSelection(tab_key_));
+
+  ASSERT_EQ(create_test_location(1, 0), GetCurrentLocation());
+  EXPECT_TRUE(CurrentResultActionNotSelected());
+  EXPECT_FALSE(previous_result->selected());
+
+  // TAB - next action selected.
+  EXPECT_EQ(ResultSelectionController::MoveResult::kNone,
+            result_selection_controller_->MoveSelection(tab_key_));
+
+  ASSERT_EQ(create_test_location(1, 0), GetCurrentLocation());
+  EXPECT_TRUE(CurrentResultActionSelected(0));
+
+  // TAB - rejected, as selection would cycle to the beginning.
+  EXPECT_EQ(ResultSelectionController::MoveResult::kSelectionCycleRejected,
+            result_selection_controller_->MoveSelection(tab_key_));
+  ASSERT_EQ(create_test_location(1, 0), GetCurrentLocation());
+  EXPECT_TRUE(CurrentResultActionSelected(0));
+}
+
+TEST_F(ResultSelectionTest, TabCycleInContainerSingleResult) {
+  const int kContainerCount = 1;
+  const int kResultsPerContainer = 1;
+  std::vector<std::unique_ptr<SearchResultContainerView>> containers =
+      CreateContainerVector(kContainerCount,
+                            TestContainerParams(false, kResultsPerContainer));
+  SetContainers(containers);
+
+  auto create_test_location = [](int container_index, int result_index) {
+    return ResultLocationDetails(container_index, kContainerCount, result_index,
+                                 kResultsPerContainer, false);
+  };
+
+  // Initialize the RSC for test.
+  result_selection_controller_->ResetSelection(nullptr);
+
+  ASSERT_EQ(create_test_location(0, 0), GetCurrentLocation());
+
+  // Shift TAB - reject going to the last result (even though it's the same as
+  // the first result).
+  EXPECT_EQ(ResultSelectionController::MoveResult::kSelectionCycleRejected,
+            result_selection_controller_->MoveSelection(shift_tab_key_));
+  ASSERT_EQ(create_test_location(0, 0), GetCurrentLocation());
+
+  // TAB - reject goting to the first result (event though it's the same as the
+  // last result).
+  EXPECT_EQ(ResultSelectionController::MoveResult::kSelectionCycleRejected,
+            result_selection_controller_->MoveSelection(tab_key_));
+  ASSERT_EQ(create_test_location(0, 0), GetCurrentLocation());
+}
+
+TEST_F(ResultSelectionTest, TabCycleInContainerSingleResultWithActionUsingTab) {
+  const int kContainerCount = 1;
+  const int kResultsPerContainer = 1;
+  const int kActionsPerResult = 1;
+  std::vector<std::unique_ptr<SearchResultContainerView>> containers =
+      CreateContainerVector(
+          kContainerCount,
+          TestContainerParams(false, kResultsPerContainer, kActionsPerResult));
+  SetContainers(containers);
+
+  auto create_test_location = [](int container_index, int result_index) {
+    return ResultLocationDetails(container_index, kContainerCount, result_index,
+                                 kResultsPerContainer, false);
+  };
+
+  // Initialize the RSC for test.
+  result_selection_controller_->ResetSelection(nullptr);
+
+  ASSERT_EQ(create_test_location(0, 0), GetCurrentLocation());
+  EXPECT_TRUE(CurrentResultActionNotSelected());
+
+  // Shift TAB - reject going to the last result.
+  EXPECT_EQ(ResultSelectionController::MoveResult::kSelectionCycleRejected,
+            result_selection_controller_->MoveSelection(shift_tab_key_));
+
+  ASSERT_EQ(create_test_location(0, 0), GetCurrentLocation());
+  EXPECT_TRUE(CurrentResultActionNotSelected());
+
+  // TAB - the result should remain the same, but the selected action is
+  // expected to change.
+  EXPECT_EQ(ResultSelectionController::MoveResult::kNone,
+            result_selection_controller_->MoveSelection(tab_key_));
+
+  ASSERT_EQ(create_test_location(0, 0), GetCurrentLocation());
+  EXPECT_TRUE(CurrentResultActionSelected(0));
+
+  // TAB - rejected, as selection would cycle to the beginning.
+  EXPECT_EQ(ResultSelectionController::MoveResult::kSelectionCycleRejected,
+            result_selection_controller_->MoveSelection(tab_key_));
+  ASSERT_EQ(create_test_location(0, 0), GetCurrentLocation());
+  EXPECT_TRUE(CurrentResultActionSelected(0));
+}
+
+TEST_F(ResultSelectionTest,
+       TabCycleInContainerSingleResultWithActionUsingUpDown) {
+  const int kContainerCount = 1;
+  const int kResultsPerContainer = 1;
+  const int kActionsPerResult = 1;
+  std::vector<std::unique_ptr<SearchResultContainerView>> containers =
+      CreateContainerVector(
+          kContainerCount,
+          TestContainerParams(false, kResultsPerContainer, kActionsPerResult));
+  SetContainers(containers);
+
+  auto create_test_location = [](int container_index, int result_index) {
+    return ResultLocationDetails(container_index, kContainerCount, result_index,
+                                 kResultsPerContainer, false);
+  };
+
+  // Initialize the RSC for test.
+  result_selection_controller_->ResetSelection(nullptr);
+
+  ASSERT_EQ(create_test_location(0, 0), GetCurrentLocation());
+  EXPECT_TRUE(CurrentResultActionNotSelected());
+
+  // UP - reject going to the last result.
+  EXPECT_EQ(ResultSelectionController::MoveResult::kSelectionCycleRejected,
+            result_selection_controller_->MoveSelection(up_arrow_));
+
+  ASSERT_EQ(create_test_location(0, 0), GetCurrentLocation());
+  EXPECT_TRUE(CurrentResultActionNotSelected());
+
+  // DOWN - rejected, as selection would cycle to the beginning (even though the
+  // first element is the same as the last).
+  EXPECT_EQ(ResultSelectionController::MoveResult::kSelectionCycleRejected,
+            result_selection_controller_->MoveSelection(down_arrow_));
+  ASSERT_EQ(create_test_location(0, 0), GetCurrentLocation());
+  EXPECT_TRUE(CurrentResultActionNotSelected());
+}
+
 TEST_P(ResultSelectionTest,
        TestHorizontalStackWithResultActions_ForwardBackWithActionSelected) {
   const int kContainerCount = 2;
@@ -721,7 +983,7 @@
   };
 
   // Initialize the RSC for test.
-  result_selection_controller_->ResetSelection();
+  result_selection_controller_->ResetSelection(nullptr);
 
   ui::KeyEvent* forward = is_rtl_ ? &left_arrow_ : &right_arrow_;
   ui::KeyEvent* backward = is_rtl_ ? &right_arrow_ : &left_arrow_;
@@ -730,25 +992,29 @@
   EXPECT_TRUE(CurrentResultActionNotSelected());
 
   // TAB to select an action.
-  EXPECT_FALSE(result_selection_controller_->MoveSelection(tab_key_));
+  EXPECT_EQ(ResultSelectionController::MoveResult::kNone,
+            result_selection_controller_->MoveSelection(tab_key_));
   ASSERT_EQ(create_test_location(0, 0), GetCurrentLocation());
   EXPECT_TRUE(CurrentResultActionSelected(0));
 
   // Forward selects the next result.
   TestResultView* previous_result = GetCurrentSelection();
-  EXPECT_TRUE(result_selection_controller_->MoveSelection(*forward));
+  EXPECT_EQ(ResultSelectionController::MoveResult::kResultChanged,
+            result_selection_controller_->MoveSelection(*forward));
   ASSERT_EQ(create_test_location(0, 1), GetCurrentLocation());
   EXPECT_TRUE(CurrentResultActionNotSelected());
   EXPECT_FALSE(previous_result->selected());
 
   // TAB to select an action.
-  EXPECT_FALSE(result_selection_controller_->MoveSelection(tab_key_));
+  EXPECT_EQ(ResultSelectionController::MoveResult::kNone,
+            result_selection_controller_->MoveSelection(tab_key_));
   ASSERT_EQ(create_test_location(0, 1), GetCurrentLocation());
   EXPECT_TRUE(CurrentResultActionSelected(0));
 
   // Backward selects the previous result.
   previous_result = GetCurrentSelection();
-  EXPECT_TRUE(result_selection_controller_->MoveSelection(*backward));
+  EXPECT_EQ(ResultSelectionController::MoveResult::kResultChanged,
+            result_selection_controller_->MoveSelection(*backward));
   ASSERT_EQ(create_test_location(0, 0), GetCurrentLocation());
   EXPECT_TRUE(CurrentResultActionNotSelected());
   EXPECT_FALSE(previous_result->selected());
@@ -772,31 +1038,35 @@
   };
 
   // Initialize the RSC for test.
-  result_selection_controller_->ResetSelection();
+  result_selection_controller_->ResetSelection(nullptr);
 
   ASSERT_EQ(create_test_location(0, 0), GetCurrentLocation());
   EXPECT_TRUE(CurrentResultActionNotSelected());
 
   // TAB to select an action.
-  EXPECT_FALSE(result_selection_controller_->MoveSelection(tab_key_));
+  EXPECT_EQ(ResultSelectionController::MoveResult::kNone,
+            result_selection_controller_->MoveSelection(tab_key_));
   ASSERT_EQ(create_test_location(0, 0), GetCurrentLocation());
   EXPECT_TRUE(CurrentResultActionSelected(0));
 
   // DOWN selects the next result.
   TestResultView* previous_result = GetCurrentSelection();
-  EXPECT_TRUE(result_selection_controller_->MoveSelection(down_arrow_));
+  EXPECT_EQ(ResultSelectionController::MoveResult::kResultChanged,
+            result_selection_controller_->MoveSelection(down_arrow_));
   ASSERT_EQ(create_test_location(0, 1), GetCurrentLocation());
   EXPECT_TRUE(CurrentResultActionNotSelected());
   EXPECT_FALSE(previous_result->selected());
 
   // TAB to select an action.
-  EXPECT_FALSE(result_selection_controller_->MoveSelection(tab_key_));
+  EXPECT_EQ(ResultSelectionController::MoveResult::kNone,
+            result_selection_controller_->MoveSelection(tab_key_));
   ASSERT_EQ(create_test_location(0, 1), GetCurrentLocation());
   EXPECT_TRUE(CurrentResultActionSelected(0));
 
   // UP selects the previous result.
   previous_result = GetCurrentSelection();
-  EXPECT_TRUE(result_selection_controller_->MoveSelection(up_arrow_));
+  EXPECT_EQ(ResultSelectionController::MoveResult::kResultChanged,
+            result_selection_controller_->MoveSelection(up_arrow_));
   ASSERT_EQ(create_test_location(0, 0), GetCurrentLocation());
   EXPECT_TRUE(CurrentResultActionNotSelected());
   EXPECT_FALSE(previous_result->selected());
@@ -819,20 +1089,21 @@
   };
 
   // Initialize the RSC for test.
-  result_selection_controller_->ResetSelection();
+  result_selection_controller_->ResetSelection(nullptr);
 
   ASSERT_EQ(create_test_location(0, 0), GetCurrentLocation());
   EXPECT_TRUE(CurrentResultActionNotSelected());
 
   // TAB to select an action.
-  EXPECT_FALSE(result_selection_controller_->MoveSelection(tab_key_));
+  EXPECT_EQ(ResultSelectionController::MoveResult::kNone,
+            result_selection_controller_->MoveSelection(tab_key_));
   ASSERT_EQ(create_test_location(0, 0), GetCurrentLocation());
   EXPECT_TRUE(CurrentResultActionSelected(0));
 
   // Reset selection - reset selects the new first result, i.e. the same result
   // as before reset. The selected action should remain the same.
   TestResultView* pre_reset_selection = GetCurrentSelection();
-  result_selection_controller_->ResetSelection();
+  result_selection_controller_->ResetSelection(nullptr);
   EXPECT_EQ(pre_reset_selection, GetCurrentSelection());
   ASSERT_EQ(create_test_location(0, 0), GetCurrentLocation());
   EXPECT_TRUE(CurrentResultActionSelected(0));
@@ -855,24 +1126,26 @@
   };
 
   // Initialize the RSC for test.
-  result_selection_controller_->ResetSelection();
+  result_selection_controller_->ResetSelection(nullptr);
 
   ASSERT_EQ(create_test_location(0, 0), GetCurrentLocation());
   EXPECT_TRUE(CurrentResultActionNotSelected());
 
   // DOWN to select another result.
-  EXPECT_TRUE(result_selection_controller_->MoveSelection(down_arrow_));
+  EXPECT_EQ(ResultSelectionController::MoveResult::kResultChanged,
+            result_selection_controller_->MoveSelection(down_arrow_));
   ASSERT_EQ(create_test_location(0, 1), GetCurrentLocation());
   EXPECT_TRUE(CurrentResultActionNotSelected());
 
   // TAB to select an action.
-  EXPECT_FALSE(result_selection_controller_->MoveSelection(tab_key_));
+  EXPECT_EQ(ResultSelectionController::MoveResult::kNone,
+            result_selection_controller_->MoveSelection(tab_key_));
   ASSERT_EQ(create_test_location(0, 1), GetCurrentLocation());
   EXPECT_TRUE(CurrentResultActionSelected(0));
 
   // Reset selection.
   TestResultView* pre_reset_selection = GetCurrentSelection();
-  result_selection_controller_->ResetSelection();
+  result_selection_controller_->ResetSelection(nullptr);
   ASSERT_EQ(create_test_location(0, 0), GetCurrentLocation());
   EXPECT_TRUE(CurrentResultActionNotSelected());
   EXPECT_FALSE(pre_reset_selection->selected());
@@ -895,20 +1168,24 @@
   };
 
   // Initialize the RSC for test.
-  result_selection_controller_->ResetSelection();
+  result_selection_controller_->ResetSelection(nullptr);
 
   ASSERT_EQ(create_test_location(0, 0), GetCurrentLocation());
   EXPECT_TRUE(CurrentResultActionNotSelected());
 
   // DOWN to select another result.
-  EXPECT_TRUE(result_selection_controller_->MoveSelection(down_arrow_));
+  EXPECT_EQ(ResultSelectionController::MoveResult::kResultChanged,
+            result_selection_controller_->MoveSelection(down_arrow_));
   ASSERT_EQ(create_test_location(0, 1), GetCurrentLocation());
   EXPECT_TRUE(CurrentResultActionNotSelected());
 
   // TAB to select the last action.
-  EXPECT_FALSE(result_selection_controller_->MoveSelection(tab_key_));
-  EXPECT_FALSE(result_selection_controller_->MoveSelection(tab_key_));
-  EXPECT_FALSE(result_selection_controller_->MoveSelection(tab_key_));
+  EXPECT_EQ(ResultSelectionController::MoveResult::kNone,
+            result_selection_controller_->MoveSelection(tab_key_));
+  EXPECT_EQ(ResultSelectionController::MoveResult::kNone,
+            result_selection_controller_->MoveSelection(tab_key_));
+  EXPECT_EQ(ResultSelectionController::MoveResult::kNone,
+            result_selection_controller_->MoveSelection(tab_key_));
   ASSERT_EQ(create_test_location(0, 1), GetCurrentLocation());
   EXPECT_TRUE(CurrentResultActionSelected(2));
 
@@ -924,9 +1201,40 @@
   EXPECT_TRUE(CurrentResultActionNotSelected());
 
   // Shift-TAB move selection to the previous result.
-  EXPECT_TRUE(result_selection_controller_->MoveSelection(shift_tab_key_));
+  EXPECT_EQ(ResultSelectionController::MoveResult::kResultChanged,
+            result_selection_controller_->MoveSelection(shift_tab_key_));
   ASSERT_EQ(create_test_location(0, 0), GetCurrentLocation());
   EXPECT_TRUE(CurrentResultActionSelected(2));
 }
 
+TEST_F(ResultSelectionTest, MoveNullSelectionTab) {
+  TestMoveNullSelection(tab_key_, false /*reverse*/,
+                        false /*expect_action_selected*/);
+}
+
+TEST_F(ResultSelectionTest, MoveNullSelectionShiftTab) {
+  TestMoveNullSelection(shift_tab_key_, true /*reverse*/,
+                        true /*expect_action_selected*/);
+}
+
+TEST_F(ResultSelectionTest, MoveNullSelectionDown) {
+  TestMoveNullSelection(down_arrow_, false /*reverse*/,
+                        false /*expect_action_selected*/);
+}
+
+TEST_F(ResultSelectionTest, MoveNullSelectionUp) {
+  TestMoveNullSelection(up_arrow_, true /*reverse*/,
+                        false /*expect_action_selected*/);
+}
+
+TEST_F(ResultSelectionTest, MoveNullSelectionForward) {
+  TestMoveNullSelection(right_arrow_, false /*reverse*/,
+                        false /*expect_action_selected*/);
+}
+
+TEST_F(ResultSelectionTest, MoveNullSelectionBack) {
+  TestMoveNullSelection(left_arrow_, false /*reverse*/,
+                        false /*expect_action_selected*/);
+}
+
 }  // namespace app_list
diff --git a/ash/app_list/views/search_box_view.cc b/ash/app_list/views/search_box_view.cc
index 911b7933..dad6fa7 100644
--- a/ash/app_list/views/search_box_view.cc
+++ b/ash/app_list/views/search_box_view.cc
@@ -493,7 +493,11 @@
   // Avoid triggering subsequent query by temporarily setting controller to
   // nullptr.
   search_box()->set_controller(nullptr);
-  search_box()->ClearCompositionText();
+  // search_box()->ClearCompositionText() does not work here because
+  // SetAutocompleteText() calls SelectRange(), which comfirms the active
+  // composition text (so there is nothing to clear here). Set empty composition
+  // text to clear the selected range.
+  search_box()->SetCompositionText(ui::CompositionText());
   search_box()->set_controller(this);
   ResetHighlightRange();
 }
@@ -588,9 +592,33 @@
     return false;
   }
 
+  // Nothing to do if no results are available (the rest of the method handles
+  // result actions and result traversal). This might happen if zero state
+  // suggestions are not enabled, and search box textfield is empty.
+  if (!contents_view_->search_results_page_view()->first_result_view())
+    return false;
+
   ResultSelectionController* selection_controller =
       contents_view_->search_results_page_view()->result_selection_controller();
 
+  // When search box is active, the focus cycles between close button and the
+  // search_box - when close button is focused, traversal keys (arrows and
+  // tab) should move the focus to the search box, and reset the selection
+  // (which might have been cleared when focus moved to the close button).
+  if (!search_box()->HasFocus()) {
+    // Only handle result traversal keys.
+    if (!IsUnhandledArrowKeyEvent(key_event) &&
+        key_event.key_code() != ui::VKEY_TAB) {
+      return false;
+    }
+
+    search_box()->RequestFocus();
+    selection_controller->MoveSelection(key_event);
+    UpdateSearchBoxTextForSelectedResult(
+        selection_controller->selected_result());
+    return true;
+  }
+
   // Handle return - opens the selected result.
   if (key_event.key_code() == ui::VKEY_RETURN) {
     // Hitting Enter when focus is on search box opens the selected result.
@@ -610,15 +638,11 @@
     views::View* selected_result = selection_controller->selected_result();
     if (selected_result)
       selected_result->OnKeyEvent(&event);
-    selection_controller->ResetSelection();
+    selection_controller->ResetSelection(nullptr);
     search_box()->SetText(base::string16());
     return true;
   }
 
-  // Record the |last_key_pressed_| for autocomplete.
-  if (!search_box()->GetText().empty() && ShouldProcessAutocomplete())
-    last_key_pressed_ = key_event.key_code();
-
   // Do not handle keys intended for result selection traversal here - these
   // should be handled elsewhere, for example by the search box text field.
   // Keys used for result selection traversal:
@@ -631,27 +655,47 @@
       key_event.key_code() == ui::VKEY_TAB ||
       IsUnhandledUpDownKeyEvent(key_event) ||
       (IsUnhandledLeftRightKeyEvent(key_event) &&
+       selection_controller->selected_location_details() &&
        selection_controller->selected_location_details()
            ->container_is_horizontal);
-  if (!result_selection_traversal_key_event)
+  if (!result_selection_traversal_key_event) {
+    // Record the |last_key_pressed_| for autocomplete.
+    if (!search_box()->GetText().empty() && ShouldProcessAutocomplete())
+      last_key_pressed_ = key_event.key_code();
     return false;
-
-  // If the |ResultSelectionController| decided not to change selection,
-  // return early, as what follows is actions for updating based on change.
-  if (!selection_controller->MoveSelection(key_event))
-    return true;
-
-  // Fill text on result change.
-  SearchResultBaseView* selected_result_view =
-      selection_controller->selected_result();
-  if (selected_result_view->result()->result_type() ==
-          ash::SearchResultType::kOmnibox &&
-      !selected_result_view->result()->is_omnibox_search()) {
-    // Use details to ensure url results fill url
-    search_box()->SetText(selected_result_view->result()->details());
-  } else {
-    search_box()->SetText(selected_result_view->result()->title());
   }
+
+  // Clear non-auto-complete generated selection to prevent navigation keys from
+  // deleting selected text.
+  if (search_box()->HasSelection() && !HasAutocompleteText())
+    search_box()->ClearSelection();
+
+  ResultSelectionController::MoveResult move_result =
+      selection_controller->MoveSelection(key_event);
+  switch (move_result) {
+    case ResultSelectionController::MoveResult::kNone:
+      // If the |ResultSelectionController| decided not to change selection,
+      // return early, as what follows is actions for updating based on
+      // change.
+      break;
+    case ResultSelectionController::MoveResult::kSelectionCycleRejected:
+      // If move was about to cycle, clear the selection and move the focus to
+      // the next element in the SearchBoxView - close_button() (only
+      // close_button() and search_box() are expected to be in the focus cycle
+      // while the search box is active).
+      if (HasAutocompleteText())
+        ClearAutocompleteText();
+      selection_controller->ClearSelection();
+
+      DCHECK(close_button()->GetVisible());
+      close_button()->RequestFocus();
+      break;
+    case ResultSelectionController::MoveResult::kResultChanged:
+      UpdateSearchBoxTextForSelectedResult(
+          selection_controller->selected_result());
+      break;
+  }
+
   return true;
 }
 
@@ -690,6 +734,18 @@
   search_box::SearchBoxViewBase::ButtonPressed(sender, event);
 }
 
+void SearchBoxView::UpdateSearchBoxTextForSelectedResult(
+    SearchResultBaseView* selected_result_view) {
+  if (selected_result_view->result()->result_type() ==
+          ash::SearchResultType::kOmnibox &&
+      !selected_result_view->result()->is_omnibox_search()) {
+    // Use details to ensure url results fill url.
+    search_box()->SetText(selected_result_view->result()->details());
+  } else {
+    search_box()->SetText(selected_result_view->result()->title());
+  }
+}
+
 void SearchBoxView::HintTextChanged() {
   const app_list::SearchBoxModel* search_box_model =
       search_model_->search_box();
diff --git a/ash/app_list/views/search_box_view.h b/ash/app_list/views/search_box_view.h
index 5ccddb0..bc8eb9a 100644
--- a/ash/app_list/views/search_box_view.h
+++ b/ash/app_list/views/search_box_view.h
@@ -24,6 +24,7 @@
 class AppListViewDelegate;
 class ContentsView;
 class SearchModel;
+class SearchResultBaseView;
 
 // Subclass of search_box::SearchBoxViewBase. SearchBoxModel is its data model
 // that controls what icon to display, what placeholder text to use for
@@ -145,6 +146,11 @@
   void SearchEngineChanged() override;
   void ShowAssistantChanged() override;
 
+  // Updates search_box() text to match |selected_result_view|. Should be called
+  // when the selected search result changes.
+  void UpdateSearchBoxTextForSelectedResult(
+      SearchResultBaseView* selected_result_view);
+
   // Returns true if the event to trigger autocomplete should be handled.
   bool ShouldProcessAutocomplete();
 
diff --git a/ash/app_list/views/search_result_page_view.cc b/ash/app_list/views/search_result_page_view.cc
index e0846c2..b0f9522 100644
--- a/ash/app_list/views/search_result_page_view.cc
+++ b/ash/app_list/views/search_result_page_view.cc
@@ -338,7 +338,7 @@
 
   if (app_list_features::IsSearchBoxSelectionEnabled()) {
     // Reset selection to first when things change.
-    result_selection_controller_->ResetSelection();
+    result_selection_controller_->ResetSelection(nullptr /*key_event*/);
   } else {
     // Highlight the first result after search results are updated. Note that
     // the focus is not set on the first result to prevent frequent focus switch
@@ -353,6 +353,9 @@
   if (!focused_result_view->result())
     return;
 
+  if (app_list_features::IsSearchBoxSelectionEnabled())
+    return;
+
   views::Textfield* search_box =
       AppListPage::contents_view()->GetSearchBoxView()->search_box();
   if (focused_result_view->result()->result_type() ==
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index abfd7312..945a528 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-8905772899826882240
\ No newline at end of file
+8905715944998699872
\ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index d31bc514..1e48276 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-8905773019186674272
\ No newline at end of file
+8905719107958498256
\ No newline at end of file
diff --git a/chrome/VERSION b/chrome/VERSION
index 9097319..b4056f3 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=78
 MINOR=0
-BUILD=3877
+BUILD=3878
 PATCH=0
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index b3145e8..9a52a23 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -369,6 +369,7 @@
     "//third_party/android_deps:javax_inject_javax_inject_java",
     "//third_party/android_media:android_media_java",
     "//third_party/android_sdk:android_gcm_java",
+    "//third_party/android_sdk/androidx_browser:androidx_browser_java",
     "//third_party/android_swipe_refresh:android_swipe_refresh_java",
     "//third_party/blink/public:blink_headers_java",
     "//third_party/blink/public/mojom:android_mojo_bindings_java",
@@ -376,7 +377,6 @@
     "//third_party/cacheinvalidation:cacheinvalidation_javalib",
     "//third_party/cacheinvalidation:cacheinvalidation_proto_java",
     "//third_party/cct_dynamic_module:cct_dynamic_module_java",
-    "//third_party/custom_tabs_client:custom_tabs_support_java",
     "//third_party/feed:feed_lib_proto_java",
     "//third_party/gif_player:gif_player_java",
     "//third_party/google_android_play_core:com_google_android_play_core_java",
@@ -710,10 +710,10 @@
     "//third_party/android_deps:com_android_support_mediarouter_v7_java",
     "//third_party/android_deps:com_android_support_recyclerview_v7_java",
     "//third_party/android_deps:com_android_support_support_annotations_java",
+    "//third_party/android_sdk/androidx_browser:androidx_browser_java",
     "//third_party/blink/public:blink_headers_java",
     "//third_party/blink/public/mojom:android_mojo_bindings_java",
     "//third_party/cacheinvalidation:cacheinvalidation_javalib",
-    "//third_party/custom_tabs_client:custom_tabs_support_java",
     "//third_party/hamcrest:hamcrest_java",
     "//ui/android:ui_java",
     "//url/mojom:url_mojom_gurl_java",
@@ -755,7 +755,7 @@
     "//third_party/android_deps:android_support_v4_java",
     "//third_party/android_media:android_media_resources",
     "//third_party/android_support_test_runner:runner_java",
-    "//third_party/custom_tabs_client:custom_tabs_support_java",
+    "//third_party/android_sdk/androidx_browser:androidx_browser_java",
     "//third_party/espresso:espresso_all_java",
     "//third_party/junit:junit",
   ]
@@ -862,13 +862,13 @@
     "//third_party/android_sdk:android_test_runner_java",
     "//third_party/android_support_test_runner:rules_java",
     "//third_party/android_support_test_runner:runner_java",
+    "//third_party/android_sdk/androidx_browser:androidx_browser_java",
     "//third_party/blink/public:blink_headers_java",
     "//third_party/blink/public/mojom:android_mojo_bindings_java",
     "//third_party/blink/public/mojom:mojom_mhtml_load_result_java",
     "//third_party/blink/public/mojom:mojom_platform_java",
     "//third_party/cacheinvalidation:cacheinvalidation_javalib",
     "//third_party/cct_dynamic_module:cct_dynamic_module_java",
-    "//third_party/custom_tabs_client:custom_tabs_support_java",
     "//third_party/espresso:espresso_all_java",
     "//third_party/hamcrest:hamcrest_java",
     "//third_party/jsr-305:jsr_305_javalib",
@@ -950,13 +950,13 @@
     "//content/public/android:content_java",
     "//content/public/test/android:content_java_test_support",
     "//net/android:net_java_test_support",
-    "//third_party/android_support_test_runner:rules_java",
-    "//third_party/android_support_test_runner:runner_java",
     "//third_party/android_deps:android_arch_lifecycle_common_java",
-    "//third_party/android_deps:com_android_support_support_annotations_java",
     "//third_party/android_deps:android_support_v7_appcompat_java",
     "//third_party/android_deps:com_android_support_recyclerview_v7_java",
-    "//third_party/custom_tabs_client:custom_tabs_support_java",
+    "//third_party/android_deps:com_android_support_support_annotations_java",
+    "//third_party/android_support_test_runner:rules_java",
+    "//third_party/android_support_test_runner:runner_java",
+    "//third_party/android_sdk/androidx_browser:androidx_browser_java",
     "//third_party/junit",
     "//third_party/ub-uiautomator:ub_uiautomator_java",
     "//ui/android:ui_java",
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index 0d41b49..5abc5cf 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -1153,6 +1153,11 @@
   "java/src/org/chromium/chrome/browser/password_manager/AutoSigninFirstRunDialog.java",
   "java/src/org/chromium/chrome/browser/password_manager/Credential.java",
   "java/src/org/chromium/chrome/browser/password_manager/GooglePasswordManagerUIProvider.java",
+  "java/src/org/chromium/chrome/browser/password_manager/OnboardingDialogCoordinator.java",
+  "java/src/org/chromium/chrome/browser/password_manager/OnboardingDialogMediator.java",
+  "java/src/org/chromium/chrome/browser/password_manager/OnboardingDialogProperties.java",
+  "java/src/org/chromium/chrome/browser/password_manager/OnboardingDialogView.java",
+  "java/src/org/chromium/chrome/browser/password_manager/OnboardingDialogViewBinder.java",
   "java/src/org/chromium/chrome/browser/password_manager/PasswordGenerationDialogBridge.java",
   "java/src/org/chromium/chrome/browser/password_manager/PasswordGenerationDialogCoordinator.java",
   "java/src/org/chromium/chrome/browser/password_manager/PasswordGenerationDialogCustomView.java",
diff --git a/chrome/android/chrome_test_java_sources.gni b/chrome/android/chrome_test_java_sources.gni
index 1c5a57d..72d8905 100644
--- a/chrome/android/chrome_test_java_sources.gni
+++ b/chrome/android/chrome_test_java_sources.gni
@@ -295,6 +295,7 @@
   "javatests/src/org/chromium/chrome/browser/partnercustomizations/PartnerDisableIncognitoModeUnitTest.java",
   "javatests/src/org/chromium/chrome/browser/partnercustomizations/PartnerHomepageIntegrationTest.java",
   "javatests/src/org/chromium/chrome/browser/partnercustomizations/PartnerHomepageUnitTest.java",
+  "javatests/src/org/chromium/chrome/browser/password_manager/OnboardingDialogTest.java",
   "javatests/src/org/chromium/chrome/browser/password_manager/PasswordGenerationDialogTest.java",
   "javatests/src/org/chromium/chrome/browser/payments/AndroidPaymentAppFinderTest.java",
   "javatests/src/org/chromium/chrome/browser/payments/CurrencyFormatterTest.java",
diff --git a/chrome/android/features/keyboard_accessory/internal/java/res/layout/keyboard_accessory_sheet_tab_credit_card_info.xml b/chrome/android/features/keyboard_accessory/internal/java/res/layout/keyboard_accessory_sheet_tab_credit_card_info.xml
index 2f000bc5b..ca6e3a5 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/res/layout/keyboard_accessory_sheet_tab_credit_card_info.xml
+++ b/chrome/android/features/keyboard_accessory/internal/java/res/layout/keyboard_accessory_sheet_tab_credit_card_info.xml
@@ -83,10 +83,10 @@
 
         </LinearLayout>
 
-        <!-- TODO(crbug.com/969708): Replace placeholder dimensions once icon is replaced -->
         <ImageView
             android:id="@+id/icon"
-            android:layout_width="@dimen/keyboard_accessory_suggestion_icon_size"
+            android:layout_marginTop="@dimen/keyboard_accessory_image_top_padding"
+            android:layout_width="@dimen/keyboard_accessory_bar_item_cc_icon_width"
             android:layout_height="@dimen/keyboard_accessory_suggestion_icon_size"
             android:layout_weight="0"
             android:importantForAccessibility="no"
diff --git a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/CreditCardAccessorySheetViewBinder.java b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/CreditCardAccessorySheetViewBinder.java
index 205259e..b15e3b2 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/CreditCardAccessorySheetViewBinder.java
+++ b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/CreditCardAccessorySheetViewBinder.java
@@ -4,6 +4,7 @@
 
 package org.chromium.chrome.browser.keyboard_accessory.sheet_tabs;
 
+import android.support.annotation.DrawableRes;
 import android.support.v7.content.res.AppCompatResources;
 import android.support.v7.widget.RecyclerView;
 import android.view.View;
@@ -49,12 +50,11 @@
             bindChipView(view.getExpYear(), info.getFields().get(2));
             bindChipView(view.getCardholder(), info.getFields().get(3));
 
-            // TODO(crbug.com/969708): Replace placeholder icon with data-specific icon.
             view.setIcon(AppCompatResources.getDrawable(
-                    view.getContext(), R.drawable.infobar_autofill_cc));
+                    view.getContext(), getDrawableForOrigin(info.getOrigin())));
         }
 
-        void bindChipView(ChipView chip, UserInfoField field) {
+        private static void bindChipView(ChipView chip, UserInfoField field) {
             chip.getPrimaryTextView().setText(field.getDisplayText());
             chip.getPrimaryTextView().setContentDescription(field.getA11yDescription());
             if (!field.isSelectable() || field.getDisplayText().isEmpty()) {
@@ -66,6 +66,30 @@
             chip.setClickable(true);
             chip.setEnabled(true);
         }
+
+        private static @DrawableRes int getDrawableForOrigin(String origin) {
+            switch (origin) {
+                case "americanExpressCC":
+                    return R.drawable.amex_card;
+                case "dinersCC":
+                    return R.drawable.diners_card;
+                case "discoverCC":
+                    return R.drawable.discover_card;
+                case "eloCC":
+                    return R.drawable.elo_card;
+                case "jcbCC":
+                    return R.drawable.jcb_card;
+                case "masterCardCC":
+                    return R.drawable.mc_card;
+                case "mirCC":
+                    return R.drawable.mir_card;
+                case "unionPayCC":
+                    return R.drawable.unionpay_card;
+                case "visaCC":
+                    return R.drawable.visa_card;
+            }
+            return R.drawable.infobar_autofill_cc;
+        }
     }
 
     static void initializeView(RecyclerView view, AccessorySheetTabModel model) {
diff --git a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessorySheetModernViewBinder.java b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessorySheetModernViewBinder.java
index 603a35d..92a773c 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessorySheetModernViewBinder.java
+++ b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessorySheetModernViewBinder.java
@@ -50,8 +50,8 @@
             bindChipView(view.getUsername(), info.getFields().get(0));
             bindChipView(view.getPassword(), info.getFields().get(1));
 
-            view.getTitle().setVisibility(info.getTitle().isEmpty() ? View.GONE : View.VISIBLE);
-            view.getTitle().setText(info.getTitle());
+            view.getTitle().setVisibility(info.getOrigin().isEmpty() ? View.GONE : View.VISIBLE);
+            view.getTitle().setText(info.getOrigin());
 
             view.setIconForBitmap(null); // Set the default icon, then try to get a better one.
             if (info.getFaviconProvider() != null) {
diff --git a/chrome/android/features/keyboard_accessory/public/java/src/org/chromium/chrome/browser/keyboard_accessory/data/KeyboardAccessoryData.java b/chrome/android/features/keyboard_accessory/public/java/src/org/chromium/chrome/browser/keyboard_accessory/data/KeyboardAccessoryData.java
index 73c690ef..4f9c350f7 100644
--- a/chrome/android/features/keyboard_accessory/public/java/src/org/chromium/chrome/browser/keyboard_accessory/data/KeyboardAccessoryData.java
+++ b/chrome/android/features/keyboard_accessory/public/java/src/org/chromium/chrome/browser/keyboard_accessory/data/KeyboardAccessoryData.java
@@ -175,7 +175,7 @@
      * (username + password), to be shown on the manual fallback UI.
      */
     public final static class UserInfo {
-        private final String mTitle;
+        private final String mOrigin;
         private final List<UserInfoField> mFields = new ArrayList<>();
         private final @Nullable FaviconProvider mFaviconProvider;
 
@@ -191,8 +191,8 @@
             void fetchFavicon(@Px int desiredSize, Callback<Bitmap> favicon);
         }
 
-        public UserInfo(String title, @Nullable FaviconProvider faviconProvider) {
-            mTitle = title;
+        public UserInfo(String origin, @Nullable FaviconProvider faviconProvider) {
+            mOrigin = origin;
             mFaviconProvider = faviconProvider;
         }
 
@@ -212,10 +212,10 @@
         }
 
         /**
-         * @return A string to be used as title. May be empty but not null.
+         * @return A string indicating the origin. May be empty but not null.
          */
-        public String getTitle() {
-            return mTitle;
+        public String getOrigin() {
+            return mOrigin;
         }
 
         /**
diff --git a/chrome/android/features/start_surface/internal/BUILD.gn b/chrome/android/features/start_surface/internal/BUILD.gn
index 9a3863d1..0d9a901 100644
--- a/chrome/android/features/start_surface/internal/BUILD.gn
+++ b/chrome/android/features/start_surface/internal/BUILD.gn
@@ -108,7 +108,7 @@
     deps += [
       "//chrome/android/public/profiles:java",
       "//content/public/android:content_java",
-      "//third_party/custom_tabs_client:custom_tabs_support_java",
+      "//third_party/android_sdk/androidx_browser:androidx_browser_java",
       "//third_party/feed:feed_lib_java",
       "//ui/android:ui_utils_java",
       "//ui/base/mojom:mojom_java",
diff --git a/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/ExploreSurfaceNavigationDelegate.java b/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/ExploreSurfaceNavigationDelegate.java
index 9675009..8de5396b 100644
--- a/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/ExploreSurfaceNavigationDelegate.java
+++ b/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/ExploreSurfaceNavigationDelegate.java
@@ -8,7 +8,6 @@
 import android.net.Uri;
 import android.provider.Browser;
 import android.support.annotation.Nullable;
-import android.support.customtabs.CustomTabsIntent;
 
 import org.chromium.chrome.browser.IntentHandler;
 import org.chromium.chrome.browser.native_page.NativePageNavigationDelegate;
@@ -17,6 +16,8 @@
 import org.chromium.content_public.browser.LoadUrlParams;
 import org.chromium.ui.mojom.WindowOpenDisposition;
 
+import androidx.browser.customtabs.CustomTabsIntent;
+
 /** Implementation of the {@link NativePageNavigationDelegate} for the explore surface. */
 class ExploreSurfaceNavigationDelegate implements NativePageNavigationDelegate {
     private final Context mContext;
@@ -56,4 +57,4 @@
     public void setIncognito(boolean isIncognito) {
         mIsInCognito = isIncognito;
     }
-}
\ No newline at end of file
+}
diff --git a/chrome/android/features/tab_ui/java/res/layout/iph_card_item_layout.xml b/chrome/android/features/tab_ui/java/res/layout/iph_card_item_layout.xml
index 9988e57..97b011b8 100644
--- a/chrome/android/features/tab_ui/java/res/layout/iph_card_item_layout.xml
+++ b/chrome/android/features/tab_ui/java/res/layout/iph_card_item_layout.xml
@@ -4,24 +4,22 @@
      found in the LICENSE file. -->
 <org.chromium.chrome.browser.tasks.tab_management.TabGridIphItemView
     xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
     android:id="@+id/tab_grid_iph_item"
     android:layout_width="match_parent"
     android:layout_height="@dimen/tab_grid_iph_card_height"
-    android:layout_marginTop="@dimen/tab_grid_iph_card_padding"
-    android:layout_marginBottom="@dimen/tab_grid_iph_card_padding"
-    android:background="@color/modern_primary_color">
+    android:layout_margin="@dimen/tab_grid_iph_card_margin"
+    android:background="@drawable/popup_bg_tinted"
+    android:visibility="invisible">
     <LinearLayout
         android:orientation="horizontal"
         android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:background="@drawable/popup_bg">
+        android:layout_height="match_parent">
         <TextView
             android:id="@+id/iph_description"
             android:layout_width="0dp"
             android:layout_height="match_parent"
-            android:layout_weight="3"
-            android:layout_marginStart="@dimen/tab_grid_iph_card_padding"
+            android:layout_weight="4"
+            android:layout_marginStart="@dimen/tab_grid_iph_card_text_inset"
             android:text="@string/iph_drag_and_drop_introduction"
             android:textAppearance="@style/TextAppearance.BlackBodyDefault"
             android:layout_gravity="center"
@@ -30,10 +28,10 @@
             android:id="@+id/show_me_button"
             android:layout_width="0dp"
             android:layout_height="match_parent"
-            android:layout_weight="1.5"
             android:text="@string/iph_drag_and_drop_show_me"
             android:textAppearance="@style/TextAppearance.BlueTitle2"
             android:layout_gravity="center"
+            android:layout_weight="2"
             android:gravity="center_vertical|end"/>
         <org.chromium.ui.widget.ChromeImageView
             android:id="@+id/close_iph_button"
@@ -41,8 +39,7 @@
             android:layout_height="match_parent"
             android:layout_weight="1"
             style="@style/BottomToolbarButton"
-            android:src="@drawable/btn_close"
-            app:tint="@color/standard_mode_tint"
-            android:contentDescription="@string/close" />
+            android:contentDescription="@string/close"
+            android:tint="@color/default_icon_color" />
     </LinearLayout>
 </org.chromium.chrome.browser.tasks.tab_management.TabGridIphItemView>
diff --git a/chrome/android/features/tab_ui/java/res/layout/iph_drag_and_drop_dialog_layout.xml b/chrome/android/features/tab_ui/java/res/layout/iph_drag_and_drop_dialog_layout.xml
index 63d0aab..6144826c 100644
--- a/chrome/android/features/tab_ui/java/res/layout/iph_drag_and_drop_dialog_layout.xml
+++ b/chrome/android/features/tab_ui/java/res/layout/iph_drag_and_drop_dialog_layout.xml
@@ -8,7 +8,7 @@
     android:layout_height="@dimen/tab_grid_iph_dialog_size"
     android:layout_gravity="center"
     android:orientation="vertical"
-    android:background="@drawable/tab_grid_card_background"
+    android:background="@drawable/popup_bg_tinted"
     android:clickable="true"
     android:focusable="true">
     <ImageView
@@ -25,7 +25,7 @@
         android:layout_marginStart="32dp"
         android:layout_marginEnd="32dp"
         android:text="@string/iph_drag_and_drop_title"
-        android:textAppearance="@style/TextAppearance.BlackTitle1"
+        android:textAppearance="@style/TextAppearance.BlackHeadline"
         android:gravity="center_vertical" />
     <TextView
         android:layout_width="match_parent"
diff --git a/chrome/android/features/tab_ui/java/res/values/dimens.xml b/chrome/android/features/tab_ui/java/res/values/dimens.xml
index caa27aa..9cecc5a 100644
--- a/chrome/android/features/tab_ui/java/res/values/dimens.xml
+++ b/chrome/android/features/tab_ui/java/res/values/dimens.xml
@@ -12,8 +12,10 @@
     <dimen name="tab_grid_close_button_size">18dp</dimen>
     <dimen name="tab_grid_dialog_side_margin">16dp</dimen>
     <dimen name="tab_grid_dialog_top_margin">85dp</dimen>
-    <dimen name="tab_grid_iph_card_height">48dp</dimen>
-    <dimen name="tab_grid_iph_card_padding">8dp</dimen>
+    <dimen name="tab_grid_iph_card_height">60dp</dimen>
+    <dimen name="tab_grid_iph_card_margin">9dp</dimen>
+    <dimen name="tab_grid_iph_card_text_inset">16dp</dimen>
+    <dimen name="tab_grid_iph_card_close_button_size">18dp</dimen>
     <dimen name="tab_grid_iph_dialog_size">370dp</dimen>
     <dimen name="tab_grid_merge_threshold">45dp</dimen>
     <dimen name="tab_grid_thumbnail_card_default_size">152dp</dimen>
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridIphItemView.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridIphItemView.java
index 3dc24ad..6e4281f 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridIphItemView.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridIphItemView.java
@@ -5,8 +5,11 @@
 package org.chromium.chrome.browser.tasks.tab_management;
 
 import android.content.Context;
+import android.graphics.Bitmap;
 import android.graphics.drawable.Animatable;
+import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
+import android.support.v7.content.res.AppCompatResources;
 import android.util.AttributeSet;
 import android.view.Gravity;
 import android.view.LayoutInflater;
@@ -48,6 +51,8 @@
                         .inflate(R.layout.iph_drag_and_drop_dialog_layout, backgroundView, false);
         mShowIPHDialogButton = findViewById(R.id.show_me_button);
         mCloseIPHEntranceButton = findViewById(R.id.close_iph_button);
+        Drawable closeButtonDrawable = getScaledCloseImageDrawable();
+        mCloseIPHEntranceButton.setImageDrawable(closeButtonDrawable);
         mCloseIPHDialogButton =
                 mIphDialogView.findViewById(R.id.iph_drag_and_drop_dialog_close_button);
         mIphDrawable =
@@ -112,4 +117,15 @@
         mIphWindow.showAtLocation((View) getParent(), Gravity.CENTER, 0, 0);
         ((Animatable) mIphDrawable).start();
     }
+
+    private Drawable getScaledCloseImageDrawable() {
+        // Scale down the close button image to 18dp.
+        Context context = getContext();
+        int size = (int) context.getResources().getDimension(
+                R.dimen.tab_grid_iph_card_close_button_size);
+        Drawable closeDrawable = AppCompatResources.getDrawable(getContext(), R.drawable.btn_close);
+        Bitmap closeBitmap = ((BitmapDrawable) closeDrawable).getBitmap();
+        return new BitmapDrawable(
+                getResources(), Bitmap.createScaledBitmap(closeBitmap, size, size, true));
+    }
 }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListRecyclerView.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListRecyclerView.java
index e1b9ea3..fa59647 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListRecyclerView.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListRecyclerView.java
@@ -31,7 +31,6 @@
 import org.chromium.base.Log;
 import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.util.FeatureUtilities;
-import org.chromium.chrome.tab_ui.R;
 import org.chromium.ui.interpolators.BakedBezierInterpolator;
 import org.chromium.ui.resources.dynamics.DynamicResourceLoader;
 import org.chromium.ui.resources.dynamics.ViewResourceAdapter;
@@ -351,10 +350,8 @@
             if (mRecyclerViewFooter.getVisibility() != VISIBLE) {
                 mRecyclerViewFooter.setVisibility(VISIBLE);
             }
-            final int padding =
-                    (int) getResources().getDimension(R.dimen.tab_grid_iph_card_padding);
             mRecyclerViewFooter.setY(
-                    viewHolder.itemView.getBottom() + mRecyclerViewFooter.getHeight() + padding);
+                    viewHolder.itemView.getBottom() + mRecyclerViewFooter.getHeight());
         }
     }
 
@@ -477,10 +474,7 @@
      */
     void setupRecyclerViewFooter(View footer) {
         if (mRecyclerViewFooter != null) return;
-        final int height = (int) getResources().getDimension(R.dimen.tab_grid_iph_card_height);
-        final int padding = (int) getResources().getDimension(R.dimen.tab_grid_iph_card_padding);
         mRecyclerViewFooter = footer;
-        setPadding(0, 0, 0, height + padding);
         setScrollBarStyle(SCROLLBARS_OUTSIDE_OVERLAY);
         mRecyclerViewFooter.setVisibility(INVISIBLE);
     }
diff --git a/chrome/android/java/AndroidManifest.xml b/chrome/android/java/AndroidManifest.xml
index 545468e1..22591d1 100644
--- a/chrome/android/java/AndroidManifest.xml
+++ b/chrome/android/java/AndroidManifest.xml
@@ -1169,7 +1169,7 @@
               <category android:name="androidx.browser.customtabs.category.ColorSchemeCustomization"/>
             </intent-filter>
         </service>
-        <service android:name="android.support.customtabs.PostMessageService" />
+        <service android:name="androidx.browser.customtabs.PostMessageService" />
 
         <!-- Crash reporting services. -->
         <service android:name="org.chromium.chrome.browser.crash.ChromeMinidumpUploadJobService"
diff --git a/chrome/android/java/monochrome_public_bundle__base_bundle_module.AndroidManifest.expected b/chrome/android/java/monochrome_public_bundle__base_bundle_module.AndroidManifest.expected
index 337c7b9..7c4c3c4 100644
--- a/chrome/android/java/monochrome_public_bundle__base_bundle_module.AndroidManifest.expected
+++ b/chrome/android/java/monochrome_public_bundle__base_bundle_module.AndroidManifest.expected
@@ -1578,7 +1578,7 @@
         <action android:name="com.google.android.gms.gcm.ACTION_TASK_READY"/>
       </intent-filter>
     </service>
-    <service android:name="android.support.customtabs.PostMessageService"/>
+    <service android:name="androidx.browser.customtabs.PostMessageService"/>
     <service
         android:exported="true"
         android:externalService="true"
diff --git a/chrome/android/java/res/layout/password_manager_onboarding_dialog.xml b/chrome/android/java/res/layout/password_manager_onboarding_dialog.xml
new file mode 100644
index 0000000..cb44e52
--- /dev/null
+++ b/chrome/android/java/res/layout/password_manager_onboarding_dialog.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2019 The Chromium Authors. All rights reserved.
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file. -->
+
+<org.chromium.chrome.browser.password_manager.OnboardingDialogView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:padding="@dimen/onboarding_dialog_padding"
+    android:gravity="center">
+
+    <ImageView
+        android:id="@+id/onboarding_illustration"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:paddingTop="@dimen/onboarding_illustration_margin"
+        android:layout_marginStart="@dimen/onboarding_illustration_margin"
+        android:layout_marginEnd="@dimen/onboarding_illustration_margin"
+        android:scaleType="fitCenter"
+        tools:ignore="ContentDescription" />
+
+    <org.chromium.ui.widget.TextViewWithLeading
+        android:id="@+id/onboarding_title"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="@dimen/dialog_header_margin"
+        android:layout_marginBottom="@dimen/dialog_header_margin"
+        android:textAppearance="@style/TextAppearance.BlackHeadline" />
+
+    <org.chromium.ui.widget.TextViewWithLeading
+        android:id="@+id/onboarding_details"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        style="@style/TextAppearance.BlackBody" />
+</org.chromium.chrome.browser.password_manager.OnboardingDialogView>
\ No newline at end of file
diff --git a/chrome/android/java/res/values/dimens.xml b/chrome/android/java/res/values/dimens.xml
index b9c96686..e13571c 100644
--- a/chrome/android/java/res/values/dimens.xml
+++ b/chrome/android/java/res/values/dimens.xml
@@ -157,6 +157,10 @@
     <!-- Password generation dialog dimensions -->
     <dimen name="password_generation_dialog_padding">24dp</dimen>
 
+    <!-- Password manager onboarding experience -->
+    <dimen name="onboarding_dialog_padding">16dp</dimen>
+    <dimen name="onboarding_illustration_margin">24dp</dimen>
+
     <!-- Promo dialogs -->
     <dimen name="promo_dialog_illustration_margin">24dp</dimen>
     <dimen name="promo_dialog_illustration_width">150dp</dimen>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/LaunchIntentDispatcher.java b/chrome/android/java/src/org/chromium/chrome/browser/LaunchIntentDispatcher.java
index d0916c7..c0a4746 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/LaunchIntentDispatcher.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/LaunchIntentDispatcher.java
@@ -16,9 +16,6 @@
 import android.os.Bundle;
 import android.os.StrictMode;
 import android.support.annotation.IntDef;
-import android.support.customtabs.CustomTabsIntent;
-import android.support.customtabs.CustomTabsSessionToken;
-import android.support.customtabs.TrustedWebUtils;
 
 import org.chromium.base.ApplicationStatus;
 import org.chromium.base.CommandLine;
@@ -57,6 +54,10 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.UUID;
 
+import androidx.browser.customtabs.CustomTabsIntent;
+import androidx.browser.customtabs.CustomTabsSessionToken;
+import androidx.browser.customtabs.TrustedWebUtils;
+
 /**
  * Dispatches incoming intents to the appropriate activity based on the current configuration and
  * Intent fired.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ServiceTabLauncher.java b/chrome/android/java/src/org/chromium/chrome/browser/ServiceTabLauncher.java
index e24e9a9..70985ed 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ServiceTabLauncher.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ServiceTabLauncher.java
@@ -11,7 +11,6 @@
 import android.net.Uri;
 import android.provider.Browser;
 import android.support.annotation.Nullable;
-import android.support.customtabs.CustomTabsIntent;
 
 import org.chromium.base.ApplicationStatus;
 import org.chromium.base.ContextUtils;
@@ -41,6 +40,8 @@
 
 import java.util.List;
 
+import androidx.browser.customtabs.CustomTabsIntent;
+
 /**
  * Tab Launcher to be used to launch new tabs from background Android Services,
  * when it is not known whether an activity is available. It will send an intent to launch the
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/BrowserSessionDataProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/BrowserSessionDataProvider.java
index c1e0f06..57ccd40 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/BrowserSessionDataProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/BrowserSessionDataProvider.java
@@ -9,12 +9,13 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.support.annotation.Nullable;
-import android.support.customtabs.CustomTabsIntent;
-import android.support.customtabs.CustomTabsSessionToken;
 
 import org.chromium.chrome.browser.IntentHandler;
 import org.chromium.chrome.browser.util.IntentUtils;
 
+import androidx.browser.customtabs.CustomTabsIntent;
+import androidx.browser.customtabs.CustomTabsSessionToken;
+
 /**
  * A model class that parses intent from third-party apps for data related with various browser
  * services related Intent types.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ManageTrustedWebActivityDataActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ManageTrustedWebActivityDataActivity.java
index 505d60f..0abfec2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ManageTrustedWebActivityDataActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ManageTrustedWebActivityDataActivity.java
@@ -5,9 +5,6 @@
 package org.chromium.chrome.browser.browserservices;
 
 import android.os.Bundle;
-import android.support.customtabs.CustomTabsService;
-import android.support.customtabs.CustomTabsSessionToken;
-import android.support.customtabs.TrustedWebUtils;
 import android.support.v7.app.AppCompatActivity;
 
 import org.chromium.base.Log;
@@ -18,6 +15,10 @@
 import org.chromium.chrome.browser.preferences.PreferencesLauncher;
 import org.chromium.chrome.browser.preferences.website.SettingsNavigationSource;
 
+import androidx.browser.customtabs.CustomTabsService;
+import androidx.browser.customtabs.CustomTabsSessionToken;
+import androidx.browser.customtabs.TrustedWebUtils;
+
 /**
  * Launched by {@link android.support.customtabs.TrustedWebUtils#launchBrowserSiteSettings}.
  * Verifies that url provided in intent has valid Digital Asset Link with the calling application,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/OriginVerifier.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/OriginVerifier.java
index 741695f..460629d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/OriginVerifier.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/OriginVerifier.java
@@ -11,8 +11,6 @@
 import android.os.SystemClock;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
-import android.support.customtabs.CustomTabsService;
-import android.support.customtabs.CustomTabsService.Relation;
 import android.text.TextUtils;
 
 import org.chromium.base.CommandLine;
@@ -48,6 +46,8 @@
 
 import javax.inject.Inject;
 
+import androidx.browser.customtabs.CustomTabsService;
+import androidx.browser.customtabs.CustomTabsService.Relation;
 import dagger.Reusable;
 
 /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/PostMessageHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/PostMessageHandler.java
index e860b0a..5027891 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/PostMessageHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/PostMessageHandler.java
@@ -5,9 +5,6 @@
 package org.chromium.chrome.browser.browserservices;
 
 import android.net.Uri;
-import android.support.customtabs.CustomTabsService;
-import android.support.customtabs.CustomTabsSessionToken;
-import android.support.customtabs.PostMessageBackend;
 
 import org.chromium.base.ContextUtils;
 import org.chromium.base.VisibleForTesting;
@@ -21,6 +18,10 @@
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.content_public.browser.WebContentsObserver;
 
+import androidx.browser.customtabs.CustomTabsService;
+import androidx.browser.customtabs.CustomTabsSessionToken;
+import androidx.browser.customtabs.PostMessageBackend;
+
 /**
  * A class that handles postMessage communications with a designated {@link CustomTabsSessionToken}.
  */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/SessionDataHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/SessionDataHolder.java
index d819ca9..7bffd3b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/SessionDataHolder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/SessionDataHolder.java
@@ -10,7 +10,6 @@
 import android.net.Uri;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
-import android.support.customtabs.CustomTabsSessionToken;
 import android.util.SparseArray;
 
 import org.chromium.base.Callback;
@@ -19,6 +18,8 @@
 import javax.inject.Inject;
 import javax.inject.Singleton;
 
+import androidx.browser.customtabs.CustomTabsSessionToken;
+
 import dagger.Lazy;
 
 /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/SessionHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/SessionHandler.java
index fcc8cc95..6745237 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/SessionHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/SessionHandler.java
@@ -10,9 +10,10 @@
 import android.graphics.Bitmap;
 import android.net.Uri;
 import android.support.annotation.Nullable;
-import android.support.customtabs.CustomTabsSessionToken;
 import android.widget.RemoteViews;
 
+import androidx.browser.customtabs.CustomTabsSessionToken;
+
 /**
  * Interface to handle browser services calls whenever the session id matched.
  * TODO(yusufo): Add a way to handle mayLaunchUrl as well.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClient.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClient.java
index 7e88948..3f04db4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClient.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClient.java
@@ -17,9 +17,6 @@
 import android.net.Uri;
 import android.os.RemoteException;
 import android.support.annotation.Nullable;
-import android.support.customtabs.trusted.TrustedWebActivityService;
-import android.support.customtabs.trusted.TrustedWebActivityServiceConnectionManager;
-import android.support.customtabs.trusted.TrustedWebActivityServiceWrapper;
 
 import org.chromium.base.ContextUtils;
 import org.chromium.chrome.R;
@@ -37,6 +34,10 @@
 import javax.inject.Inject;
 import javax.inject.Singleton;
 
+import androidx.browser.trusted.TrustedWebActivityService;
+import androidx.browser.trusted.TrustedWebActivityServiceConnectionManager;
+import androidx.browser.trusted.TrustedWebActivityServiceWrapper;
+
 /**
  * Uses a Trusted Web Activity client to display notifications.
  */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityVerifier.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityVerifier.java
index ae8dfae..3b0db84 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityVerifier.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityVerifier.java
@@ -8,7 +8,6 @@
 import android.support.annotation.IntDef;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
-import android.support.customtabs.CustomTabsService;
 
 import org.chromium.base.ContextUtils;
 import org.chromium.base.ObserverList;
@@ -41,6 +40,7 @@
 
 import javax.inject.Inject;
 
+import androidx.browser.customtabs.CustomTabsService;
 import dagger.Lazy;
 
 /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/splashscreen/SplashImageHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/splashscreen/SplashImageHolder.java
index 82da66ea..d774c520 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/splashscreen/SplashImageHolder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/splashscreen/SplashImageHolder.java
@@ -6,7 +6,6 @@
 
 import android.graphics.Bitmap;
 import android.support.annotation.Nullable;
-import android.support.customtabs.CustomTabsSessionToken;
 import android.util.ArrayMap;
 
 import java.util.Collections;
@@ -15,6 +14,8 @@
 import javax.inject.Inject;
 import javax.inject.Singleton;
 
+import androidx.browser.customtabs.CustomTabsSessionToken;
+
 /**
  * Stores the splash images received from TWA clients between the call to
  * {@link android.support.customtabs.CustomTabsService#receiveFile} and a Trusted Web Activity
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/splashscreen/TwaSplashController.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/splashscreen/TwaSplashController.java
index 3472d63..3847f8b1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/splashscreen/TwaSplashController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/splashscreen/TwaSplashController.java
@@ -13,8 +13,6 @@
 import android.graphics.Matrix;
 import android.os.Build;
 import android.os.Bundle;
-import android.support.customtabs.TrustedWebUtils;
-import android.support.customtabs.TrustedWebUtils.SplashScreenParamKey;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.ImageView;
@@ -34,6 +32,9 @@
 
 import javax.inject.Inject;
 
+import androidx.browser.customtabs.TrustedWebUtils;
+import androidx.browser.customtabs.TrustedWebUtils.SplashScreenParamKey;
+
 /**
  * Orchestrates the flow of showing and removing splash screens for apps based on Trusted Web
  * Activities.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/ClientManager.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/ClientManager.java
index 6b57a5f..630833556 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/ClientManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/ClientManager.java
@@ -15,11 +15,6 @@
 import android.os.SystemClock;
 import android.support.annotation.IntDef;
 import android.support.annotation.NonNull;
-import android.support.customtabs.CustomTabsCallback;
-import android.support.customtabs.CustomTabsService;
-import android.support.customtabs.CustomTabsService.Relation;
-import android.support.customtabs.CustomTabsSessionToken;
-import android.support.customtabs.PostMessageServiceConnection;
 import android.text.TextUtils;
 import android.text.format.DateUtils;
 import android.util.SparseBooleanArray;
@@ -50,6 +45,12 @@
 import java.util.Map;
 import java.util.Set;
 
+import androidx.browser.customtabs.CustomTabsCallback;
+import androidx.browser.customtabs.CustomTabsService;
+import androidx.browser.customtabs.CustomTabsService.Relation;
+import androidx.browser.customtabs.CustomTabsSessionToken;
+import androidx.browser.customtabs.PostMessageServiceConnection;
+
 /** Manages the clients' state for Custom Tabs. This class is threadsafe. */
 class ClientManager {
     // Values for the "CustomTabs.MayLaunchUrlType" UMA histogram. Append-only.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomButtonParams.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomButtonParams.java
index 8970624..1362ef9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomButtonParams.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomButtonParams.java
@@ -13,7 +13,6 @@
 import android.os.Bundle;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
-import android.support.customtabs.CustomTabsIntent;
 import android.text.TextUtils;
 import android.view.Gravity;
 import android.view.LayoutInflater;
@@ -35,6 +34,8 @@
 import java.util.List;
 import java.util.Set;
 
+import androidx.browser.customtabs.CustomTabsIntent;
+
 /**
  * Container for all parameters related to creating a customizable button.
  */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
index b8aed09..4cbb26c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
@@ -4,11 +4,11 @@
 
 package org.chromium.chrome.browser.customtabs;
 
-import static android.support.customtabs.CustomTabsIntent.COLOR_SCHEME_DARK;
-import static android.support.customtabs.CustomTabsIntent.COLOR_SCHEME_LIGHT;
-
 import static org.chromium.chrome.browser.customtabs.content.CustomTabActivityNavigationController.FinishReason.USER_NAVIGATION;
 
+import static androidx.browser.customtabs.CustomTabsIntent.COLOR_SCHEME_DARK;
+import static androidx.browser.customtabs.CustomTabsIntent.COLOR_SCHEME_LIGHT;
+
 import android.app.Activity;
 import android.app.PendingIntent;
 import android.content.Context;
@@ -24,9 +24,6 @@
 import android.support.annotation.IntDef;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
-import android.support.customtabs.CustomTabsIntent;
-import android.support.customtabs.CustomTabsService;
-import android.support.customtabs.CustomTabsSessionToken;
 import android.text.TextUtils;
 import android.util.Pair;
 import android.view.KeyEvent;
@@ -90,6 +87,10 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
+import androidx.browser.customtabs.CustomTabsIntent;
+import androidx.browser.customtabs.CustomTabsService;
+import androidx.browser.customtabs.CustomTabsSessionToken;
+
 /**
  * The activity for custom tabs. It will be launched on top of a client's task.
  */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabBottomBarDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabBottomBarDelegate.java
index a280b952..7d532c91 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabBottomBarDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabBottomBarDelegate.java
@@ -9,7 +9,6 @@
 import android.content.Intent;
 import android.net.Uri;
 import android.support.annotation.Nullable;
-import android.support.customtabs.CustomTabsIntent;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.view.View.OnLayoutChangeListener;
@@ -37,6 +36,8 @@
 
 import javax.inject.Inject;
 
+import androidx.browser.customtabs.CustomTabsIntent;
+
 /**
  * Delegate that manages bottom bar area inside of {@link CustomTabActivity}.
  */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java
index f9c287b3..cb865b5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java
@@ -4,8 +4,8 @@
 
 package org.chromium.chrome.browser.customtabs;
 
-import static android.support.customtabs.CustomTabsIntent.COLOR_SCHEME_LIGHT;
-import static android.support.customtabs.CustomTabsIntent.COLOR_SCHEME_SYSTEM;
+import static androidx.browser.customtabs.CustomTabsIntent.COLOR_SCHEME_LIGHT;
+import static androidx.browser.customtabs.CustomTabsIntent.COLOR_SCHEME_SYSTEM;
 
 import android.app.PendingIntent;
 import android.app.PendingIntent.CanceledException;
@@ -21,10 +21,6 @@
 import android.support.annotation.IntDef;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
-import android.support.customtabs.CustomTabColorSchemeParams;
-import android.support.customtabs.CustomTabsIntent;
-import android.support.customtabs.CustomTabsSessionToken;
-import android.support.customtabs.TrustedWebUtils;
 import android.text.TextUtils;
 import android.util.Pair;
 import android.view.View;
@@ -56,6 +52,11 @@
 import java.util.List;
 import java.util.regex.Pattern;
 
+import androidx.browser.customtabs.CustomTabColorSchemeParams;
+import androidx.browser.customtabs.CustomTabsIntent;
+import androidx.browser.customtabs.CustomTabsSessionToken;
+import androidx.browser.customtabs.TrustedWebUtils;
+
 /**
  * A model class that parses the incoming intent for Custom Tabs specific customization data.
  *
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabNavigationEventObserver.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabNavigationEventObserver.java
index b4e249a8..e564da8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabNavigationEventObserver.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabNavigationEventObserver.java
@@ -4,9 +4,6 @@
 
 package org.chromium.chrome.browser.customtabs;
 
-import android.support.customtabs.CustomTabsCallback;
-import android.support.customtabs.CustomTabsSessionToken;
-
 import org.chromium.chrome.browser.dependency_injection.ActivityScope;
 import org.chromium.chrome.browser.tab.EmptyTabObserver;
 import org.chromium.chrome.browser.tab.Tab;
@@ -16,6 +13,9 @@
 
 import javax.inject.Inject;
 
+import androidx.browser.customtabs.CustomTabsCallback;
+import androidx.browser.customtabs.CustomTabsSessionToken;
+
 /**
  * An observer for firing navigation events on {@link CustomTabsCallback}.
  */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabNightModeStateController.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabNightModeStateController.java
index 7e97613..f7e9540c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabNightModeStateController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabNightModeStateController.java
@@ -6,7 +6,6 @@
 
 import android.content.Intent;
 import android.support.annotation.NonNull;
-import android.support.customtabs.CustomTabsIntent;
 import android.support.v7.app.AppCompatDelegate;
 
 import org.chromium.base.ObserverList;
@@ -18,6 +17,8 @@
 import org.chromium.chrome.browser.util.FeatureUtilities;
 import org.chromium.chrome.browser.util.IntentUtils;
 
+import androidx.browser.customtabs.CustomTabsIntent;
+
 /**
  * Maintains and provides the night mode state for {@link CustomTabActivity}.
  */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabObserver.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabObserver.java
index a8224ac..0c25cc3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabObserver.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabObserver.java
@@ -11,7 +11,6 @@
 import android.net.Uri;
 import android.os.SystemClock;
 import android.support.annotation.IntDef;
-import android.support.customtabs.CustomTabsSessionToken;
 import android.text.TextUtils;
 import android.text.format.DateUtils;
 
@@ -34,6 +33,8 @@
 import javax.inject.Inject;
 import javax.inject.Named;
 
+import androidx.browser.customtabs.CustomTabsSessionToken;
+
 /**
  * A {@link TabObserver} that also handles custom tabs specific logging and messaging.
  */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsClientFileProcessor.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsClientFileProcessor.java
index c22ff34c..dacb04b0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsClientFileProcessor.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsClientFileProcessor.java
@@ -11,8 +11,6 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.support.annotation.WorkerThread;
-import android.support.customtabs.CustomTabsService;
-import android.support.customtabs.CustomTabsSessionToken;
 
 import org.chromium.base.FileUtils;
 import org.chromium.base.Log;
@@ -22,6 +20,8 @@
 import javax.inject.Named;
 import javax.inject.Singleton;
 
+import androidx.browser.customtabs.CustomTabsService;
+import androidx.browser.customtabs.CustomTabsSessionToken;
 import dagger.Lazy;
 
 /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsConnection.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsConnection.java
index 2c602ec..ae1c4ea 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsConnection.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsConnection.java
@@ -19,11 +19,6 @@
 import android.os.SystemClock;
 import android.support.annotation.IntDef;
 import android.support.annotation.Nullable;
-import android.support.customtabs.CustomTabsCallback;
-import android.support.customtabs.CustomTabsIntent;
-import android.support.customtabs.CustomTabsService;
-import android.support.customtabs.CustomTabsSessionToken;
-import android.support.customtabs.PostMessageServiceConnection;
 import android.text.TextUtils;
 import android.widget.RemoteViews;
 
@@ -89,6 +84,12 @@
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicBoolean;
 
+import androidx.browser.customtabs.CustomTabsCallback;
+import androidx.browser.customtabs.CustomTabsIntent;
+import androidx.browser.customtabs.CustomTabsService;
+import androidx.browser.customtabs.CustomTabsSessionToken;
+import androidx.browser.customtabs.PostMessageServiceConnection;
+
 /**
  * Implementation of the ICustomTabsService interface.
  *
@@ -344,7 +345,11 @@
                         .onSessionDisconnected(session);
             }
         };
-        PostMessageServiceConnection serviceConnection = new PostMessageServiceConnection(session);
+
+        // TODO(peconn): Make this not an anonymous class once PostMessageServiceConnection is made
+        // non-abstract in AndroidX.
+        PostMessageServiceConnection serviceConnection =
+                new PostMessageServiceConnection(session) {};
         PostMessageHandler handler = new PostMessageHandler(serviceConnection);
         return mClientManager.newSession(
                 session, Binder.getCallingUid(), onDisconnect, handler, serviceConnection);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsConnectionService.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsConnectionService.java
index 028c4ba..bfba782e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsConnectionService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsConnectionService.java
@@ -10,8 +10,6 @@
 import android.os.IBinder;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
-import android.support.customtabs.CustomTabsService;
-import android.support.customtabs.CustomTabsSessionToken;
 
 import org.chromium.base.ContextUtils;
 import org.chromium.chrome.browser.browserservices.Origin;
@@ -20,6 +18,9 @@
 
 import java.util.List;
 
+import androidx.browser.customtabs.CustomTabsService;
+import androidx.browser.customtabs.CustomTabsSessionToken;
+
 /**
  * Custom tabs connection service, used by the embedded Chrome activities.
  */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/HiddenTabHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/HiddenTabHolder.java
index fb75a3fa..730e0cfac 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/HiddenTabHolder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/HiddenTabHolder.java
@@ -9,7 +9,6 @@
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.support.annotation.Nullable;
-import android.support.customtabs.CustomTabsSessionToken;
 import android.text.TextUtils;
 
 import org.chromium.base.ContextUtils;
@@ -28,6 +27,8 @@
 import org.chromium.network.mojom.ReferrerPolicy;
 import org.chromium.ui.base.WindowAndroid;
 
+import androidx.browser.customtabs.CustomTabsSessionToken;
+
 /**
  * Holds a hidden tab which may be used to preload pages before a CustomTabActivity is launched.
  *
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/PageLoadMetricsObserver.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/PageLoadMetricsObserver.java
index 8fa4dee..1301ff5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/PageLoadMetricsObserver.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/PageLoadMetricsObserver.java
@@ -5,13 +5,14 @@
 package org.chromium.chrome.browser.customtabs;
 
 import android.os.Bundle;
-import android.support.customtabs.CustomTabsSessionToken;
 
 import org.chromium.chrome.browser.metrics.PageLoadMetrics;
 import org.chromium.chrome.browser.net.spdyproxy.DataReductionProxySettings;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.content_public.browser.WebContents;
 
+import androidx.browser.customtabs.CustomTabsSessionToken;
+
 /**
  * Notifies the provided {@link CustomTabsConnection} of page load metrics, such as time until first
  * contentful paint.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabController.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabController.java
index 8caf375..477747d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabController.java
@@ -10,7 +10,6 @@
 import android.provider.Browser;
 import android.support.annotation.IntDef;
 import android.support.annotation.Nullable;
-import android.support.customtabs.CustomTabsSessionToken;
 import android.text.TextUtils;
 import android.view.Window;
 
@@ -56,6 +55,7 @@
 
 import javax.inject.Inject;
 
+import androidx.browser.customtabs.CustomTabsSessionToken;
 import dagger.Lazy;
 
 /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabIntentHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabIntentHandler.java
index 05ba38c2..56842ab 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabIntentHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabIntentHandler.java
@@ -10,7 +10,6 @@
 import android.content.Intent;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
-import android.support.customtabs.CustomTabsSessionToken;
 
 import org.chromium.chrome.browser.customtabs.CustomTabIntentDataProvider;
 import org.chromium.chrome.browser.dependency_injection.ActivityScope;
@@ -19,6 +18,8 @@
 import javax.inject.Inject;
 import javax.inject.Named;
 
+import androidx.browser.customtabs.CustomTabsSessionToken;
+
 /**
  * Handles the incoming intents: the one that starts the activity, as well as subsequent intents
  * received in onNewIntent.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/ActivityDelegatePostMessageBackend.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/ActivityDelegatePostMessageBackend.java
index 4a3be88..435fec3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/ActivityDelegatePostMessageBackend.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/ActivityDelegatePostMessageBackend.java
@@ -6,7 +6,8 @@
 
 import android.content.Context;
 import android.os.Bundle;
-import android.support.customtabs.PostMessageBackend;
+
+import androidx.browser.customtabs.PostMessageBackend;
 
 /**
  * A {@link PostMessageBackend} which delegates incoming notifications to the {@link
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/DynamicModuleCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/DynamicModuleCoordinator.java
index dd0cf78..f4b2453d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/DynamicModuleCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/DynamicModuleCoordinator.java
@@ -11,8 +11,6 @@
 import android.net.Uri;
 import android.support.annotation.IntDef;
 import android.support.annotation.Nullable;
-import android.support.customtabs.CustomTabsService;
-import android.support.customtabs.PostMessageBackend;
 import android.text.TextUtils;
 import android.view.View;
 import android.view.ViewGroup;
@@ -55,6 +53,8 @@
 
 import javax.inject.Inject;
 
+import androidx.browser.customtabs.CustomTabsService;
+import androidx.browser.customtabs.PostMessageBackend;
 import dagger.Lazy;
 
 /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/DynamicModuleNavigationEventObserver.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/DynamicModuleNavigationEventObserver.java
index ecaf288..aa07693b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/DynamicModuleNavigationEventObserver.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/DynamicModuleNavigationEventObserver.java
@@ -4,17 +4,16 @@
 
 package org.chromium.chrome.browser.customtabs.dynamicmodule;
 
-import static android.support.customtabs.CustomTabsCallback.NAVIGATION_FAILED;
-import static android.support.customtabs.CustomTabsCallback.NAVIGATION_FINISHED;
-import static android.support.customtabs.CustomTabsCallback.NAVIGATION_STARTED;
-import static android.support.customtabs.CustomTabsCallback.TAB_HIDDEN;
-import static android.support.customtabs.CustomTabsCallback.TAB_SHOWN;
+import static androidx.browser.customtabs.CustomTabsCallback.NAVIGATION_FAILED;
+import static androidx.browser.customtabs.CustomTabsCallback.NAVIGATION_FINISHED;
+import static androidx.browser.customtabs.CustomTabsCallback.NAVIGATION_STARTED;
+import static androidx.browser.customtabs.CustomTabsCallback.TAB_HIDDEN;
+import static androidx.browser.customtabs.CustomTabsCallback.TAB_SHOWN;
 
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.SystemClock;
 import android.support.annotation.Nullable;
-import android.support.customtabs.CustomTabsCallback;
 import android.text.TextUtils;
 
 import org.chromium.base.VisibleForTesting;
@@ -28,6 +27,8 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import androidx.browser.customtabs.CustomTabsCallback;
+
 /**
  * An observer for firing navigation events to the CCT dynamic module.
  */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarCoordinator.java
index deb2a5e..eb98eff9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarCoordinator.java
@@ -7,7 +7,6 @@
 import static org.chromium.chrome.browser.dependency_injection.ChromeCommonQualifiers.APP_CONTEXT;
 
 import android.content.Context;
-import android.support.customtabs.CustomTabsIntent;
 import android.text.TextUtils;
 import android.view.View;
 
@@ -39,6 +38,7 @@
 import javax.inject.Inject;
 import javax.inject.Named;
 
+import androidx.browser.customtabs.CustomTabsIntent;
 import dagger.Lazy;
 
 /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/dependency_injection/ChromeAppModule.java b/chrome/android/java/src/org/chromium/chrome/browser/dependency_injection/ChromeAppModule.java
index 76cf447f..3cd42a5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/dependency_injection/ChromeAppModule.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/dependency_injection/ChromeAppModule.java
@@ -8,7 +8,6 @@
 import static org.chromium.chrome.browser.dependency_injection.ChromeCommonQualifiers.LAST_USED_PROFILE;
 
 import android.content.Context;
-import android.support.customtabs.trusted.TrustedWebActivityServiceConnectionManager;
 
 import org.chromium.base.ContextUtils;
 import org.chromium.chrome.browser.WarmupManager;
@@ -23,6 +22,7 @@
 import javax.inject.Named;
 import javax.inject.Singleton;
 
+import androidx.browser.trusted.TrustedWebActivityServiceConnectionManager;
 import dagger.Module;
 import dagger.Provides;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManager.java b/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManager.java
index 66a1d84..75e9ecb 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManager.java
@@ -8,7 +8,6 @@
 import android.content.Intent;
 import android.net.Uri;
 import android.support.annotation.NonNull;
-import android.support.customtabs.CustomTabsIntent;
 import android.text.TextUtils;
 
 import org.chromium.base.CommandLine;
@@ -44,6 +43,8 @@
 import java.util.HashMap;
 import java.util.Map;
 
+import androidx.browser.customtabs.CustomTabsIntent;
+
 /**
  * Manages UI effects for reader mode including hiding and showing the
  * reader mode and reader mode preferences toolbar icon and hiding the
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/MediaViewerUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/media/MediaViewerUtils.java
index 1a7c05a..dae6e65a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/MediaViewerUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/MediaViewerUtils.java
@@ -17,7 +17,6 @@
 import android.net.Uri;
 import android.os.Build;
 import android.provider.Browser;
-import android.support.customtabs.CustomTabsIntent;
 import android.text.TextUtils;
 
 import org.chromium.base.ApiCompatibilityUtils;
@@ -33,6 +32,8 @@
 
 import java.util.Locale;
 
+import androidx.browser.customtabs.CustomTabsIntent;
+
 /**
  * A class containing some utility static methods.
  */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/downloads/OfflinePageDownloadBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/downloads/OfflinePageDownloadBridge.java
index 824c255..0c8c0245 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/downloads/OfflinePageDownloadBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/downloads/OfflinePageDownloadBridge.java
@@ -10,7 +10,6 @@
 import android.content.Intent;
 import android.net.Uri;
 import android.provider.Browser;
-import android.support.customtabs.CustomTabsIntent;
 
 import org.chromium.base.ApplicationStatus;
 import org.chromium.base.ContextUtils;
@@ -41,6 +40,8 @@
 import org.chromium.content_public.browser.LoadUrlParams;
 import org.chromium.ui.widget.Toast;
 
+import androidx.browser.customtabs.CustomTabsIntent;
+
 /**
  * Serves as an interface between Download Home UI and offline page related items that are to be
  * displayed in the downloads UI.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/OnboardingDialogCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/OnboardingDialogCoordinator.java
new file mode 100644
index 0000000..b6d4ee9
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/OnboardingDialogCoordinator.java
@@ -0,0 +1,54 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.password_manager;
+
+import android.content.res.Resources;
+import android.support.annotation.NonNull;
+import android.view.LayoutInflater;
+import android.view.View;
+
+import org.chromium.base.Callback;
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ChromeActivity;
+import org.chromium.ui.modaldialog.DialogDismissalCause;
+import org.chromium.ui.modaldialog.ModalDialogProperties;
+import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
+
+/**
+ * The coordinator for the password manager onboarding modal dialog. Manages the sub-component
+ * objects.
+ */
+public class OnboardingDialogCoordinator {
+    private final OnboardingDialogMediator mMediator;
+
+    OnboardingDialogCoordinator(@NonNull ChromeActivity activity) {
+        PropertyModel mModel = OnboardingDialogProperties.defaultModelBuilder().build();
+        View customView = LayoutInflater.from(activity).inflate(
+                R.layout.password_manager_onboarding_dialog, null);
+        mMediator = new OnboardingDialogMediator(
+                mModel, activity.getModalDialogManager(), createDialogModelBuilder(customView));
+        PropertyModelChangeProcessor.create(mModel, customView, OnboardingDialogViewBinder::bind);
+    }
+
+    public void showDialog(
+            String onboardingTitle, String onboardingDetails, Callback<Boolean> onClick) {
+        mMediator.setContents(onboardingTitle, onboardingDetails);
+        mMediator.showDialog(onClick);
+    }
+
+    public void dismissDialog(@DialogDismissalCause int dismissalClause) {
+        mMediator.dismissDialog(dismissalClause);
+    }
+
+    private static PropertyModel.Builder createDialogModelBuilder(View customView) {
+        Resources resources = customView.getResources();
+        return new PropertyModel.Builder(ModalDialogProperties.ALL_KEYS)
+                .with(ModalDialogProperties.CUSTOM_VIEW, customView)
+                .with(ModalDialogProperties.POSITIVE_BUTTON_TEXT, resources,
+                        R.string.continue_button)
+                .with(ModalDialogProperties.NEGATIVE_BUTTON_TEXT, resources, R.string.cancel);
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/OnboardingDialogMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/OnboardingDialogMediator.java
new file mode 100644
index 0000000..50dbdca
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/OnboardingDialogMediator.java
@@ -0,0 +1,76 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.password_manager;
+
+import static org.chromium.chrome.browser.password_manager.OnboardingDialogProperties.DETAILS;
+import static org.chromium.chrome.browser.password_manager.OnboardingDialogProperties.ILLUSTRATION;
+import static org.chromium.chrome.browser.password_manager.OnboardingDialogProperties.TITLE;
+
+import org.chromium.base.Callback;
+import org.chromium.chrome.R;
+import org.chromium.ui.modaldialog.ModalDialogManager;
+import org.chromium.ui.modaldialog.ModalDialogProperties;
+import org.chromium.ui.modelutil.PropertyModel;
+
+/** Mediator class responsible for the logic of showing the onboarding dialog. */
+class OnboardingDialogMediator {
+    private final PropertyModel mModel;
+    private final ModalDialogManager mDialogManager;
+    private PropertyModel.Builder mModalDialogBuilder;
+    private PropertyModel mDialogModel;
+
+    private static class DialogClickHandler implements ModalDialogProperties.Controller {
+        private Callback<Boolean> mCallback;
+
+        DialogClickHandler(Callback<Boolean> onClick) {
+            mCallback = onClick;
+        }
+
+        @Override
+        public void onClick(PropertyModel model, int buttonType) {
+            switch (buttonType) {
+                case ModalDialogProperties.ButtonType.POSITIVE:
+                    mCallback.onResult(true);
+                    break;
+                case ModalDialogProperties.ButtonType.NEGATIVE:
+                    mCallback.onResult(false);
+                    break;
+                default:
+                    assert false : "Unexpected button pressed in dialog: " + buttonType;
+            }
+        }
+
+        @Override
+        public void onDismiss(PropertyModel model, int dismissalCause) {
+            mCallback.onResult(false);
+        }
+    }
+
+    OnboardingDialogMediator(
+            PropertyModel model, ModalDialogManager manager, PropertyModel.Builder dialogBuilder) {
+        mModel = model;
+        mDialogManager = manager;
+        mModalDialogBuilder = dialogBuilder;
+    }
+
+    void setContents(String onboardingTitle, String onboardingDetails) {
+        // TODO(crbug.com/983445): Replace once real image is available.
+        mModel.set(ILLUSTRATION, R.drawable.data_reduction_illustration);
+        mModel.set(TITLE, onboardingTitle);
+        mModel.set(DETAILS, onboardingDetails);
+    }
+
+    void showDialog(Callback<Boolean> onClick) {
+        mDialogModel =
+                mModalDialogBuilder
+                        .with(ModalDialogProperties.CONTROLLER, new DialogClickHandler(onClick))
+                        .build();
+        mDialogManager.showDialog(mDialogModel, ModalDialogManager.ModalDialogType.TAB);
+    }
+
+    void dismissDialog(int dismissalClause) {
+        mDialogManager.dismissDialog(mDialogModel, dismissalClause);
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/OnboardingDialogProperties.java b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/OnboardingDialogProperties.java
new file mode 100644
index 0000000..c4589e6c
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/OnboardingDialogProperties.java
@@ -0,0 +1,29 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.password_manager;
+
+import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.PropertyModel.WritableIntPropertyKey;
+import org.chromium.ui.modelutil.PropertyModel.WritableObjectPropertyKey;
+
+/**
+ * Data properties for the password manager onboarding modal dialog.
+ */
+class OnboardingDialogProperties {
+    // Illustration drawable resource id for the password manager.
+    static final WritableIntPropertyKey ILLUSTRATION = new WritableIntPropertyKey();
+
+    // Title that appears below the illustration.
+    static final WritableObjectPropertyKey<String> TITLE = new WritableObjectPropertyKey<>();
+
+    // Explanation text for how the password manager works.
+    static final WritableObjectPropertyKey<String> DETAILS = new WritableObjectPropertyKey<>();
+
+    private OnboardingDialogProperties() {}
+
+    static PropertyModel.Builder defaultModelBuilder() {
+        return new PropertyModel.Builder(ILLUSTRATION, TITLE, DETAILS);
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/OnboardingDialogView.java b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/OnboardingDialogView.java
new file mode 100644
index 0000000..53d39b00
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/OnboardingDialogView.java
@@ -0,0 +1,51 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.password_manager;
+
+import android.content.Context;
+import android.support.annotation.Nullable;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import org.chromium.chrome.R;
+
+/**
+ * The dialog content view for the password manager onboarding dialog.
+ */
+public class OnboardingDialogView extends LinearLayout {
+    // View containing the illustration for the onboarding.
+    private ImageView mOnboardingIllustrationView;
+    // View containing the header of the onboarding.
+    private TextView mOnboardingTitleView;
+    // View containing the explanation text.
+    private TextView mOnboardingDetailsView;
+
+    public OnboardingDialogView(Context context, @Nullable AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+
+        mOnboardingIllustrationView = findViewById(R.id.onboarding_illustration);
+        mOnboardingTitleView = findViewById(R.id.onboarding_title);
+        mOnboardingDetailsView = findViewById(R.id.onboarding_details);
+    }
+
+    void setOnboardingIllustration(int onboardingIllustration) {
+        mOnboardingIllustrationView.setImageResource(onboardingIllustration);
+    }
+
+    void setOnboardingTitle(String onboardingTitle) {
+        mOnboardingTitleView.setText(onboardingTitle);
+    }
+
+    void setOnboardingDetails(String onboardingDetails) {
+        mOnboardingDetailsView.setText(onboardingDetails);
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/OnboardingDialogViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/OnboardingDialogViewBinder.java
new file mode 100644
index 0000000..2a939389
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/OnboardingDialogViewBinder.java
@@ -0,0 +1,30 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.password_manager;
+
+import static org.chromium.chrome.browser.password_manager.OnboardingDialogProperties.DETAILS;
+import static org.chromium.chrome.browser.password_manager.OnboardingDialogProperties.ILLUSTRATION;
+import static org.chromium.chrome.browser.password_manager.OnboardingDialogProperties.TITLE;
+
+import android.view.View;
+
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel;
+
+/**
+ * Class responsible for binding the model and the view.
+ */
+class OnboardingDialogViewBinder {
+    static void bind(PropertyModel model, View view, PropertyKey propertyKey) {
+        OnboardingDialogView onboardingCustomView = (OnboardingDialogView) view;
+        if (ILLUSTRATION == propertyKey) {
+            onboardingCustomView.setOnboardingIllustration(model.get(ILLUSTRATION));
+        } else if (TITLE == propertyKey) {
+            onboardingCustomView.setOnboardingTitle(model.get(TITLE));
+        } else if (DETAILS == propertyKey) {
+            onboardingCustomView.setOnboardingDetails(model.get(DETAILS));
+        }
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/sync/SyncPreferenceUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/sync/SyncPreferenceUtils.java
index bc320d8e..361d772 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/sync/SyncPreferenceUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/sync/SyncPreferenceUtils.java
@@ -11,7 +11,6 @@
 import android.net.Uri;
 import android.provider.Browser;
 import android.support.annotation.Nullable;
-import android.support.customtabs.CustomTabsIntent;
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.PreferenceFragmentCompat;
 
@@ -30,6 +29,8 @@
 import org.chromium.components.sync.StopSource;
 import org.chromium.ui.UiUtils;
 
+import androidx.browser.customtabs.CustomTabsIntent;
+
 /**
  * Helper methods for sync preferences.
  */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninManager.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninManager.java
index e58378db..ad30862 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninManager.java
@@ -5,7 +5,6 @@
 package org.chromium.chrome.browser.signin;
 
 import android.accounts.Account;
-import android.annotation.SuppressLint;
 import android.app.Activity;
 import android.content.Context;
 import android.support.annotation.MainThread;
@@ -178,8 +177,6 @@
         }
     }
 
-    @SuppressLint("StaticFieldLeak")
-    private static SigninManager sTestingSigninManager;
     private static int sSignInAccessPoint = SigninAccessPoint.UNKNOWN;
 
     /**
@@ -467,7 +464,7 @@
         // This method should be called at most once per sign-in flow.
         assert mSignInState != null;
 
-        SigninManagerJni.get().onSignInCompleted(
+        SigninManagerJni.get().setPrimaryAccount(
                 mNativeSigninManagerAndroid, mSignInState.mAccount.name);
 
         // Cache the signed-in account name. This must be done after the native call, otherwise
@@ -747,7 +744,7 @@
 
         boolean isForceSigninEnabled(long nativeSigninManagerAndroid);
 
-        void onSignInCompleted(long nativeSigninManagerAndroid, String username);
+        void setPrimaryAccount(long nativeSigninManagerAndroid, String username);
 
         void signOut(long nativeSigninManagerAndroid, @SignoutReason int reason);
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/ui/PassphraseDialogFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/sync/ui/PassphraseDialogFragment.java
index 6692173..b714c536 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/sync/ui/PassphraseDialogFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/ui/PassphraseDialogFragment.java
@@ -13,7 +13,6 @@
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.Bundle;
-import android.support.customtabs.CustomTabsIntent;
 import android.support.v4.app.DialogFragment;
 import android.support.v4.app.Fragment;
 import android.support.v7.app.AlertDialog;
@@ -43,6 +42,8 @@
 import org.chromium.ui.text.SpanApplier;
 import org.chromium.ui.text.SpanApplier.SpanInfo;
 
+import androidx.browser.customtabs.CustomTabsIntent;
+
 /**
  * Dialog to ask to user to enter their sync passphrase.
  */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/ui/PassphraseTypeDialogFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/sync/ui/PassphraseTypeDialogFragment.java
index 3f337695..2f26f0f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/sync/ui/PassphraseTypeDialogFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/ui/PassphraseTypeDialogFragment.java
@@ -10,7 +10,6 @@
 import android.content.Intent;
 import android.net.Uri;
 import android.os.Bundle;
-import android.support.customtabs.CustomTabsIntent;
 import android.support.v4.app.DialogFragment;
 import android.support.v7.app.AlertDialog;
 import android.text.SpannableString;
@@ -39,6 +38,8 @@
 import java.util.Date;
 import java.util.List;
 
+import androidx.browser.customtabs.CustomTabsIntent;
+
 /**
  * Dialog to ask the user select what type of password to use for encryption.
  */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabContextMenuItemDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabContextMenuItemDelegate.java
index 63a844a..bee31fac 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabContextMenuItemDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabContextMenuItemDelegate.java
@@ -11,7 +11,6 @@
 import android.net.Uri;
 import android.provider.Browser;
 import android.provider.ContactsContract;
-import android.support.customtabs.CustomTabsIntent;
 
 import org.chromium.base.ContextUtils;
 import org.chromium.base.metrics.RecordUserAction;
@@ -37,6 +36,8 @@
 import java.net.URI;
 import java.util.Locale;
 
+import androidx.browser.customtabs.CustomTabsIntent;
+
 /**
  * A default {@link ContextMenuItemDelegate} that supports the context menu functionality in Tab.
  */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab_activity_glue/ActivityTabWebContentsDelegateAndroid.java b/chrome/android/java/src/org/chromium/chrome/browser/tab_activity_glue/ActivityTabWebContentsDelegateAndroid.java
index 0eb3868..305ff845 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab_activity_glue/ActivityTabWebContentsDelegateAndroid.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab_activity_glue/ActivityTabWebContentsDelegateAndroid.java
@@ -80,7 +80,9 @@
     }
 
     private FullscreenManager getFullscreenManager() {
-        return mActivity != null ? mActivity.getFullscreenManager() : null;
+        return mActivity != null && !mActivity.isActivityFinishingOrDestroyed()
+                ? mActivity.getFullscreenManager()
+                : null;
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/usage_stats/SuspendedTab.java b/chrome/android/java/src/org/chromium/chrome/browser/usage_stats/SuspendedTab.java
index b1c4854..32aa9449 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/usage_stats/SuspendedTab.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/usage_stats/SuspendedTab.java
@@ -25,6 +25,7 @@
 import org.chromium.chrome.browser.tab.EmptyTabObserver;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.content_public.browser.WebContents;
+import org.chromium.content_public.browser.WebContentsAccessibility;
 
 /**
  * Represents the suspension page presented when a user tries to visit a site whose fully-qualified
@@ -77,6 +78,7 @@
         WebContents webContents = mTab.getWebContents();
         if (webContents != null) {
             webContents.onHide();
+            WebContentsAccessibility.fromWebContents(webContents).setObscuredByAnotherView(true);
         }
 
         InfoBarContainer infoBarContainer = InfoBarContainer.get(mTab);
@@ -89,7 +91,6 @@
         } else {
             attachView();
         }
-        mTab.updateAccessibilityVisibility();
 
         TabContentManager tabContentManager = mTab.getActivity().getTabContentManager();
         if (tabContentManager != null) {
@@ -109,8 +110,8 @@
         WebContents webContents = mTab.getWebContents();
         if (webContents != null) {
             webContents.onShow();
+            WebContentsAccessibility.fromWebContents(webContents).setObscuredByAnotherView(false);
         }
-        mTab.updateAccessibilityVisibility();
 
         mView = null;
         mFqdn = null;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappInfo.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappInfo.java
index 49cda5b..d327fd70 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappInfo.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappInfo.java
@@ -10,7 +10,6 @@
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.graphics.Bitmap;
 import android.net.Uri;
-import android.support.customtabs.CustomTabsSessionToken;
 import android.text.TextUtils;
 
 import org.chromium.base.ContextUtils;
@@ -23,6 +22,8 @@
 import org.chromium.content_public.common.ScreenOrientationValues;
 import org.chromium.webapk.lib.common.splash.SplashLayout;
 
+import androidx.browser.customtabs.CustomTabsSessionToken;
+
 /**
  * Stores info about a web app.
  */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappTabDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappTabDelegate.java
index be2e6e2..b1e0f83 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappTabDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappTabDelegate.java
@@ -9,7 +9,6 @@
 import android.net.Uri;
 import android.os.StrictMode;
 import android.provider.Browser;
-import android.support.customtabs.CustomTabsIntent;
 
 import org.chromium.base.ContextUtils;
 import org.chromium.base.Log;
@@ -27,6 +26,8 @@
 import java.net.URISyntaxException;
 import java.util.List;
 
+import androidx.browser.customtabs.CustomTabsIntent;
+
 /**
  * Asynchronously creates Tabs for navigation originating from an installed PWA.
  *
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/OriginVerifierTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/OriginVerifierTest.java
index 310017d..02e5753e 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/OriginVerifierTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/OriginVerifierTest.java
@@ -5,7 +5,6 @@
 package org.chromium.chrome.browser.browserservices;
 
 import android.net.Uri;
-import android.support.customtabs.CustomTabsService;
 import android.support.test.filters.SmallTest;
 
 import org.junit.Assert;
@@ -37,6 +36,8 @@
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 
+import androidx.browser.customtabs.CustomTabsService;
+
 /** Tests for OriginVerifier. */
 @RunWith(ChromeJUnit4ClassRunner.class)
 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClientTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClientTest.java
index 00364e5..ccf87f7 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClientTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClientTest.java
@@ -13,7 +13,6 @@
 import android.os.Message;
 import android.os.Messenger;
 import android.os.RemoteException;
-import android.support.customtabs.trusted.TrustedWebActivityServiceConnectionManager;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.rule.ServiceTestRule;
@@ -38,6 +37,8 @@
 
 import java.util.concurrent.TimeoutException;
 
+import androidx.browser.trusted.TrustedWebActivityServiceConnectionManager;
+
 /**
  * Tests the TrustedWebActivityClient.
  *
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityTest.java
index 3ee1926..c719db2 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityTest.java
@@ -8,9 +8,6 @@
 import static org.junit.Assert.assertTrue;
 
 import android.content.Intent;
-import android.support.customtabs.CustomTabsService;
-import android.support.customtabs.CustomTabsSessionToken;
-import android.support.customtabs.TrustedWebUtils;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.MediumTest;
 
@@ -37,6 +34,10 @@
 
 import java.util.concurrent.TimeoutException;
 
+import androidx.browser.customtabs.CustomTabsService;
+import androidx.browser.customtabs.CustomTabsSessionToken;
+import androidx.browser.customtabs.TrustedWebUtils;
+
 /**
  * Instrumentation tests for launching
  * {@link org.chromium.chrome.browser.customtabs.CustomTabActivity} in Trusted Web Activity Mode.
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/ClientManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/ClientManagerTest.java
index df2f619..b9fc507 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/ClientManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/ClientManagerTest.java
@@ -7,9 +7,6 @@
 import android.content.Context;
 import android.net.Uri;
 import android.os.Process;
-import android.support.customtabs.CustomTabsService;
-import android.support.customtabs.CustomTabsSessionToken;
-import android.support.customtabs.PostMessageServiceConnection;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 
@@ -32,6 +29,10 @@
 import org.chromium.content_public.browser.test.util.CriteriaHelper;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 
+import androidx.browser.customtabs.CustomTabsService;
+import androidx.browser.customtabs.CustomTabsSessionToken;
+import androidx.browser.customtabs.PostMessageServiceConnection;
+
 /** Tests for ClientManager. */
 @RunWith(BaseJUnit4ClassRunner.class)
 public class ClientManagerTest {
@@ -173,7 +174,10 @@
     @SmallTest
     public void testPostMessageOriginVerification() {
         final ClientManager cm = mClientManager;
-        PostMessageServiceConnection serviceConnection = new PostMessageServiceConnection(mSession);
+        // TODO(peconn): Get rid of this anonymous class once PostMessageServiceConnection is made
+        // non-abstract. Same with the other occurrences below.
+        PostMessageServiceConnection serviceConnection =
+                new PostMessageServiceConnection(mSession) {};
         Assert.assertTrue(cm.newSession(mSession, mUid, null,
                 new PostMessageHandler(serviceConnection), serviceConnection));
         // Should always start with no origin.
@@ -216,7 +220,8 @@
     @SmallTest
     public void testPostMessageOriginDifferentRelations() {
         final ClientManager cm = mClientManager;
-        PostMessageServiceConnection serviceConnection = new PostMessageServiceConnection(mSession);
+        PostMessageServiceConnection serviceConnection =
+                new PostMessageServiceConnection(mSession) {};
         Assert.assertTrue(cm.newSession(mSession, mUid, null,
                 new PostMessageHandler(serviceConnection), serviceConnection));
         // Should always start with no origin.
@@ -255,7 +260,8 @@
     @SmallTest
     public void testPostMessageOriginHttpNotAllowed() {
         final ClientManager cm = mClientManager;
-        PostMessageServiceConnection serviceConnection = new PostMessageServiceConnection(mSession);
+        PostMessageServiceConnection serviceConnection =
+                new PostMessageServiceConnection(mSession) {};
         Assert.assertTrue(cm.newSession(mSession, mUid, null,
                 new PostMessageHandler(serviceConnection), serviceConnection));
         // Should always start with no origin.
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityIncognitoTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityIncognitoTest.java
index cf1768ac..5b5b132 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityIncognitoTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityIncognitoTest.java
@@ -6,7 +6,6 @@
 
 import android.content.Intent;
 import android.graphics.Color;
-import android.support.customtabs.CustomTabsIntent;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.view.MenuItem;
@@ -32,6 +31,8 @@
 
 import java.util.concurrent.ExecutionException;
 
+import androidx.browser.customtabs.CustomTabsIntent;
+
 /**
  * Instrumentation tests for {@link CustomTabActivity} launched in incognito mode.
  */
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java
index 2dd5efa..6761dd7 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java
@@ -34,11 +34,6 @@
 import android.os.Bundle;
 import android.os.SystemClock;
 import android.support.annotation.DrawableRes;
-import android.support.customtabs.CustomTabsCallback;
-import android.support.customtabs.CustomTabsIntent;
-import android.support.customtabs.CustomTabsService;
-import android.support.customtabs.CustomTabsSession;
-import android.support.customtabs.CustomTabsSessionToken;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.MediumTest;
 import android.support.test.filters.SmallTest;
@@ -143,6 +138,12 @@
 import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicReference;
 
+import androidx.browser.customtabs.CustomTabsCallback;
+import androidx.browser.customtabs.CustomTabsIntent;
+import androidx.browser.customtabs.CustomTabsService;
+import androidx.browser.customtabs.CustomTabsSession;
+import androidx.browser.customtabs.CustomTabsSessionToken;
+
 /**
  * Instrumentation tests for app menu, context menu, and toolbar of a {@link CustomTabActivity}.
  */
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabsConnectionTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabsConnectionTest.java
index e63f864..ec03c34 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabsConnectionTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabsConnectionTest.java
@@ -13,12 +13,6 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Process;
-import android.support.customtabs.CustomTabsCallback;
-import android.support.customtabs.CustomTabsClient;
-import android.support.customtabs.CustomTabsService;
-import android.support.customtabs.CustomTabsServiceConnection;
-import android.support.customtabs.CustomTabsSession;
-import android.support.customtabs.CustomTabsSessionToken;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 
@@ -51,6 +45,13 @@
 import java.util.List;
 import java.util.concurrent.atomic.AtomicReference;
 
+import androidx.browser.customtabs.CustomTabsCallback;
+import androidx.browser.customtabs.CustomTabsClient;
+import androidx.browser.customtabs.CustomTabsService;
+import androidx.browser.customtabs.CustomTabsServiceConnection;
+import androidx.browser.customtabs.CustomTabsSession;
+import androidx.browser.customtabs.CustomTabsSessionToken;
+
 /** Tests for CustomTabsConnection. */
 @RunWith(ChromeJUnit4ClassRunner.class)
 public class CustomTabsConnectionTest {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabsTestUtils.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabsTestUtils.java
index 2c9929c..86e2116e 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabsTestUtils.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabsTestUtils.java
@@ -10,12 +10,6 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Process;
-import android.support.customtabs.CustomTabsCallback;
-import android.support.customtabs.CustomTabsClient;
-import android.support.customtabs.CustomTabsIntent;
-import android.support.customtabs.CustomTabsServiceConnection;
-import android.support.customtabs.CustomTabsSession;
-import android.support.customtabs.CustomTabsSessionToken;
 import android.support.test.InstrumentationRegistry;
 
 import org.junit.Assert;
@@ -31,6 +25,13 @@
 import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicReference;
 
+import androidx.browser.customtabs.CustomTabsCallback;
+import androidx.browser.customtabs.CustomTabsClient;
+import androidx.browser.customtabs.CustomTabsIntent;
+import androidx.browser.customtabs.CustomTabsServiceConnection;
+import androidx.browser.customtabs.CustomTabsSession;
+import androidx.browser.customtabs.CustomTabsSessionToken;
+
 /**
  * Utility class that contains convenience calls related with custom tabs testing.
  */
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/DetachedResourceRequestTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/DetachedResourceRequestTest.java
index 0945878..6734ed1 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/DetachedResourceRequestTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/DetachedResourceRequestTest.java
@@ -9,11 +9,6 @@
 import android.content.Intent;
 import android.net.Uri;
 import android.os.Bundle;
-import android.support.customtabs.CustomTabsCallback;
-import android.support.customtabs.CustomTabsIntent;
-import android.support.customtabs.CustomTabsService;
-import android.support.customtabs.CustomTabsSession;
-import android.support.customtabs.CustomTabsSessionToken;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 
@@ -53,6 +48,12 @@
 import java.util.List;
 import java.util.concurrent.TimeoutException;
 
+import androidx.browser.customtabs.CustomTabsCallback;
+import androidx.browser.customtabs.CustomTabsIntent;
+import androidx.browser.customtabs.CustomTabsService;
+import androidx.browser.customtabs.CustomTabsSession;
+import androidx.browser.customtabs.CustomTabsSessionToken;
+
 /** Tests for detached resource requests. */
 @RunWith(ChromeJUnit4ClassRunner.class)
 public class DetachedResourceRequestTest {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/TrustedCdnPublisherUrlTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/TrustedCdnPublisherUrlTest.java
index a031eaa5..47545e7c 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/TrustedCdnPublisherUrlTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/TrustedCdnPublisherUrlTest.java
@@ -12,8 +12,6 @@
 import android.graphics.BitmapFactory;
 import android.graphics.drawable.BitmapDrawable;
 import android.support.annotation.Nullable;
-import android.support.customtabs.CustomTabsIntent;
-import android.support.customtabs.CustomTabsSessionToken;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.util.Pair;
@@ -69,6 +67,9 @@
 import java.util.Locale;
 import java.util.concurrent.TimeoutException;
 
+import androidx.browser.customtabs.CustomTabsIntent;
+import androidx.browser.customtabs.CustomTabsSessionToken;
+
 /**
  * Instrumentation tests for showing the publisher URL for a trusted CDN.
  */
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/dynamicmodule/CustomTabsDynamicModuleNavigationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/dynamicmodule/CustomTabsDynamicModuleNavigationTest.java
index 6fb583bb..12963bdf5 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/dynamicmodule/CustomTabsDynamicModuleNavigationTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/dynamicmodule/CustomTabsDynamicModuleNavigationTest.java
@@ -5,7 +5,6 @@
 package org.chromium.chrome.browser.customtabs.dynamicmodule;
 
 import android.content.Intent;
-import android.support.customtabs.CustomTabsCallback;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 
@@ -48,6 +47,8 @@
 import java.util.List;
 import java.util.concurrent.TimeoutException;
 
+import androidx.browser.customtabs.CustomTabsCallback;
+
 /**
  * Instrumentation tests for the CustomTabsDynamicModuleNavigationObserver.
  */
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/dynamicmodule/CustomTabsDynamicModulePostMessageTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/dynamicmodule/CustomTabsDynamicModulePostMessageTest.java
index f8e36c76..d9443ba 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/dynamicmodule/CustomTabsDynamicModulePostMessageTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/dynamicmodule/CustomTabsDynamicModulePostMessageTest.java
@@ -12,8 +12,6 @@
 import android.content.Intent;
 import android.net.Uri;
 import android.os.Bundle;
-import android.support.customtabs.CustomTabsService;
-import android.support.customtabs.PostMessageBackend;
 import android.support.test.filters.SmallTest;
 
 import org.junit.After;
@@ -41,6 +39,9 @@
 import org.chromium.chrome.test.util.browser.Features;
 import org.chromium.net.test.util.TestWebServer;
 
+import androidx.browser.customtabs.CustomTabsService;
+import androidx.browser.customtabs.PostMessageBackend;
+
 /**
  * Instrumentation tests for the CCT Dynamic Module post message API.
  */
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/dynamicmodule/CustomTabsDynamicModuleTestUtils.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/dynamicmodule/CustomTabsDynamicModuleTestUtils.java
index ff4bca4..f791d70 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/dynamicmodule/CustomTabsDynamicModuleTestUtils.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/dynamicmodule/CustomTabsDynamicModuleTestUtils.java
@@ -20,7 +20,6 @@
 import android.os.Bundle;
 import android.os.SystemClock;
 import android.support.annotation.Nullable;
-import android.support.customtabs.CustomTabsCallback;
 import android.support.test.InstrumentationRegistry;
 
 import org.junit.Assert;
@@ -36,6 +35,8 @@
 import java.io.InputStream;
 import java.util.concurrent.TimeoutException;
 
+import androidx.browser.customtabs.CustomTabsCallback;
+
 /**
  * Utility class that contains fake CCT dynamic module classes and convenience calls
  * related with CCT dynamic module testing.
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/externalnav/ExternalNavigationHandlerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/externalnav/ExternalNavigationHandlerTest.java
index ab7b3b01..1961cdd 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/externalnav/ExternalNavigationHandlerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/externalnav/ExternalNavigationHandlerTest.java
@@ -16,7 +16,6 @@
 import android.os.Bundle;
 import android.os.SystemClock;
 import android.provider.Browser;
-import android.support.customtabs.CustomTabsIntent;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.test.mock.MockPackageManager;
@@ -52,6 +51,8 @@
 import java.util.List;
 import java.util.regex.Pattern;
 
+import androidx.browser.customtabs.CustomTabsIntent;
+
 /**
  * Instrumentation tests for {@link ExternalNavigationHandler}.
  */
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/password_manager/OnboardingDialogTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/password_manager/OnboardingDialogTest.java
new file mode 100644
index 0000000..9972f4ef
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/password_manager/OnboardingDialogTest.java
@@ -0,0 +1,80 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.password_manager;
+
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+
+import static org.mockito.Mockito.verify;
+
+import android.support.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.mockito.quality.Strictness;
+
+import org.chromium.base.Callback;
+import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ChromeActivity;
+import org.chromium.chrome.browser.ChromeSwitches;
+import org.chromium.chrome.test.ChromeActivityTestRule;
+import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.content_public.browser.test.util.TestThreadUtils;
+
+/** Test for the password manager onboarding modal dialog. */
+@RunWith(ChromeJUnit4ClassRunner.class)
+@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
+public class OnboardingDialogTest {
+    private OnboardingDialogCoordinator mDialog;
+    private static final String TITLE = "Onboarding title!";
+    private static final String DETAILS = "Explanation text.";
+
+    @Mock
+    private Callback<Boolean> mOnClick;
+
+    @Rule
+    public ChromeActivityTestRule<ChromeActivity> mActivityTestRule =
+            new ChromeActivityTestRule<>(ChromeActivity.class);
+
+    @Rule
+    public MockitoRule mMockitoRule = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS);
+
+    @Before
+    public void setUp() throws InterruptedException {
+        mActivityTestRule.startMainActivityOnBlankPage();
+        mDialog = new OnboardingDialogCoordinator(mActivityTestRule.getActivity());
+        TestThreadUtils.runOnUiThreadBlocking(() -> mDialog.showDialog(TITLE, DETAILS, mOnClick));
+    }
+
+    @Test
+    @SmallTest
+    public void testDialogSubviewsData() {
+        onView(withId(R.id.onboarding_title)).check(matches(withText(TITLE)));
+        onView(withId(R.id.onboarding_details)).check(matches(withText(DETAILS)));
+    }
+
+    @Test
+    @SmallTest
+    public void testOkPressedCallback() {
+        onView(withId(R.id.positive_button)).perform(click());
+        verify(mOnClick).onResult(true);
+    }
+
+    @Test
+    @SmallTest
+    public void testCancelPressedCallback() {
+        onView(withId(R.id.negative_button)).perform(click());
+        verify(mOnClick).onResult(false);
+    }
+}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappActivityTestRule.java b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappActivityTestRule.java
index e589293..250f22b 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappActivityTestRule.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappActivityTestRule.java
@@ -8,7 +8,6 @@
 
 import android.content.Intent;
 import android.net.Uri;
-import android.support.customtabs.TrustedWebUtils;
 import android.support.test.InstrumentationRegistry;
 import android.view.View;
 import android.view.ViewGroup;
@@ -29,6 +28,8 @@
 import org.chromium.content_public.browser.test.util.JavaScriptUtils;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 
+import androidx.browser.customtabs.TrustedWebUtils;
+
 /**
  * Custom {@link ChromeActivityTestRule} for tests using {@link WebappActivity}.
  */
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/SessionDataHolderTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/SessionDataHolderTest.java
index 2297878..7d18a33 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/SessionDataHolderTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/SessionDataHolderTest.java
@@ -12,8 +12,6 @@
 import android.app.Activity;
 import android.app.PendingIntent;
 import android.content.Intent;
-import android.support.customtabs.CustomTabsIntent;
-import android.support.customtabs.CustomTabsSessionToken;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -31,6 +29,9 @@
 import org.chromium.chrome.browser.customtabs.CustomTabsConnection;
 import org.chromium.chrome.browser.customtabs.TranslucentCustomTabActivity;
 
+import androidx.browser.customtabs.CustomTabsIntent;
+import androidx.browser.customtabs.CustomTabsSessionToken;
+
 /** Unit tests for {@link SessionDataHolder}. */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClientTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClientTest.java
index 3176221..dd1ee52 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClientTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClientTest.java
@@ -15,9 +15,6 @@
 import android.graphics.Bitmap;
 import android.net.Uri;
 import android.os.RemoteException;
-import android.support.customtabs.trusted.TrustedWebActivityServiceConnectionManager;
-import android.support.customtabs.trusted.TrustedWebActivityServiceConnectionManager.ExecutionCallback;
-import android.support.customtabs.trusted.TrustedWebActivityServiceWrapper;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -32,6 +29,10 @@
 import org.chromium.chrome.browser.notifications.NotificationBuilderBase;
 import org.chromium.chrome.browser.notifications.NotificationUmaTracker;
 
+import androidx.browser.trusted.TrustedWebActivityServiceConnectionManager;
+import androidx.browser.trusted.TrustedWebActivityServiceConnectionManager.ExecutionCallback;
+import androidx.browser.trusted.TrustedWebActivityServiceWrapper;
+
 /**
  * Unit tests for {@link TrustedWebActivityClient}.
  */
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProviderTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProviderTest.java
index 836d35c..13e33886 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProviderTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProviderTest.java
@@ -4,14 +4,12 @@
 
 package org.chromium.chrome.browser.customtabs;
 
-import static android.support.customtabs.CustomTabsIntent.COLOR_SCHEME_DARK;
-import static android.support.customtabs.CustomTabsIntent.COLOR_SCHEME_LIGHT;
-
 import static org.junit.Assert.assertEquals;
 
+import static androidx.browser.customtabs.CustomTabsIntent.COLOR_SCHEME_DARK;
+import static androidx.browser.customtabs.CustomTabsIntent.COLOR_SCHEME_LIGHT;
+
 import android.content.Intent;
-import android.support.customtabs.CustomTabColorSchemeParams;
-import android.support.customtabs.CustomTabsIntent;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -20,6 +18,9 @@
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
 
+import androidx.browser.customtabs.CustomTabColorSchemeParams;
+import androidx.browser.customtabs.CustomTabsIntent;
+
 /** Tests for {@link CustomTabIntentDataProvider}. */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityContentTestEnvironment.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityContentTestEnvironment.java
index 726128a8..4b3f827 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityContentTestEnvironment.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityContentTestEnvironment.java
@@ -13,7 +13,6 @@
 
 import android.content.Intent;
 import android.os.Bundle;
-import android.support.customtabs.CustomTabsSessionToken;
 import android.view.View;
 
 import org.junit.rules.TestWatcher;
@@ -55,6 +54,8 @@
 import org.chromium.content_public.browser.NavigationController;
 import org.chromium.content_public.browser.WebContents;
 
+import androidx.browser.customtabs.CustomTabsSessionToken;
+
 /**
  * A TestRule that sets up the mocks and contains helper methods for JUnit/Robolectric tests scoped
  * to the content layer of Custom Tabs code.
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/firstrun/FirstRunIntegrationUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/firstrun/FirstRunIntegrationUnitTest.java
index e1317c0..d2806eb 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/firstrun/FirstRunIntegrationUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/firstrun/FirstRunIntegrationUnitTest.java
@@ -11,7 +11,6 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.UserManager;
-import android.support.customtabs.CustomTabsIntent;
 
 import org.junit.Assert;
 import org.junit.Before;
@@ -43,6 +42,8 @@
 import org.chromium.webapk.lib.common.WebApkMetaDataKeys;
 import org.chromium.webapk.test.WebApkTestHelper;
 
+import androidx.browser.customtabs.CustomTabsIntent;
+
 /** JUnit tests for first run triggering code. */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE,
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/signin/SigninManagerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/signin/SigninManagerTest.java
index d348b14..5cb826cc 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/signin/SigninManagerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/signin/SigninManagerTest.java
@@ -219,7 +219,7 @@
         doNothing().when(mNativeMock).fetchAndApplyCloudPolicy(anyLong(), any(), any());
 
         doReturn(true).when(mSigninManager).isSigninSupported();
-        doNothing().when(mNativeMock).onSignInCompleted(anyLong(), any());
+        doNothing().when(mNativeMock).setPrimaryAccount(anyLong(), any());
         doNothing().when(mSigninManager).logInSignedInUser();
 
         mSigninManager.onFirstRunCheckDone(); // Allow sign-in.
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/usage_stats/PageViewObserverTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/usage_stats/PageViewObserverTest.java
index 12e95e983..6f17253 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/usage_stats/PageViewObserverTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/usage_stats/PageViewObserverTest.java
@@ -41,7 +41,6 @@
 import org.chromium.chrome.browser.tabmodel.TabModelObserver;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.tabmodel.TabSelectionType;
-import org.chromium.content_public.browser.WebContents;
 
 import java.util.Arrays;
 
@@ -72,8 +71,6 @@
     @Mock
     private SuspensionTracker mSuspensionTracker;
     @Mock
-    private WebContents mWebContents;
-    @Mock
     private ChromeActivity mChromeActivity;
     @Captor
     private ArgumentCaptor<TabObserver> mTabObserverCaptor;
@@ -94,7 +91,6 @@
         doReturn(false).when(mTab).isIncognito();
         doReturn(null).when(mTab).getUrl();
         doReturn(mChromeActivity).when(mTab).getActivity();
-        doReturn(mWebContents).when(mTab).getWebContents();
         doReturn(Arrays.asList(mTabModel)).when(mTabModelSelector).getModels();
         doReturn(mTab).when(mTabModelSelector).getCurrentTab();
         doReturn(mUserDataHost).when(mTab).getUserDataHost();
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt
index 6c7856c..384e76e1 100644
--- a/chrome/android/profiles/newest.txt
+++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-78.0.3876.0_rc-r1-merged.afdo.bz2
\ No newline at end of file
+chromeos-chrome-amd64-78.0.3877.0_rc-r1-merged.afdo.bz2
\ No newline at end of file
diff --git a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessTabDelegate.java b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessTabDelegate.java
index b26af1a..92a7084 100644
--- a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessTabDelegate.java
+++ b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessTabDelegate.java
@@ -6,7 +6,6 @@
 import android.content.Intent;
 import android.net.Uri;
 import android.provider.Browser;
-import android.support.customtabs.CustomTabsIntent;
 
 import org.chromium.base.ContextUtils;
 import org.chromium.chrome.browser.IntentHandler;
@@ -20,6 +19,8 @@
 import org.chromium.chrome.browser.tabmodel.document.TabDelegate;
 import org.chromium.chrome.browser.util.UrlUtilities;
 
+import androidx.browser.customtabs.CustomTabsIntent;
+
 /**
  * Asynchronously creates Tabs for navigation originating from {@link NoTouchActivity}.
  *
diff --git a/chrome/app/BUILD.gn b/chrome/app/BUILD.gn
index b4fba68..7081675b 100644
--- a/chrome/app/BUILD.gn
+++ b/chrome/app/BUILD.gn
@@ -7,7 +7,6 @@
 import("//chromeos/assistant/assistant.gni")
 import("//components/gwp_asan/buildflags/buildflags.gni")
 import("//components/nacl/features.gni")
-import("//device/vr/buildflags/buildflags.gni")
 import("//ppapi/buildflags/buildflags.gni")
 import("//printing/buildflags/buildflags.gni")
 import("//tools/grit/grit_rule.gni")
@@ -564,31 +563,10 @@
     "//services/service_manager/public/cpp",
   ]
 
-  if (enable_extensions) {
-    deps += [ "//chrome/services/removable_storage_writer/public/cpp:manifest" ]
-  }
-
-  if (enable_extensions || is_android) {
-    deps += [ "//chrome/services/media_gallery_util/public/cpp:manifest" ]
-  }
-
   if (enable_basic_printing) {
     deps += [ "//components/services/pdf_compositor/public/cpp:manifest" ]
   }
 
-  if (enable_print_preview) {
-    deps += [ "//chrome/services/printing/public/cpp:manifest" ]
-  }
-
-  if (enable_vr && !is_android) {
-    deps += [ "//chrome/services/isolated_xr_device:manifest" ]
-  }
-
-  if (enable_simple_browser_service_in_process ||
-      enable_simple_browser_service_out_of_process) {
-    deps += [ "//services/content/simple_browser/public/cpp:manifest" ]
-  }
-
   if (is_win) {
     deps += [ "//chrome/services/wifi_util_win/public/cpp:manifest" ]
   }
diff --git a/chrome/app/DEPS b/chrome/app/DEPS
index fe69675..18e3d8aa 100644
--- a/chrome/app/DEPS
+++ b/chrome/app/DEPS
@@ -82,12 +82,7 @@
     "+third_party/blink/public/mojom",
   ],
   "builtin_service_manifests\.cc": [
-    "+chrome/services/file_util/public",
-    "+chrome/services/isolated_xr_device/manifest.h",
-    "+chrome/services/media_gallery_util/public",
     "+chrome/services/printing/public",
-    "+chrome/services/removable_storage_writer/public",
-    "+chrome/services/util_win/public",
     "+chrome/services/wifi_util_win/public",
     "+chrome/utility/importer/profile_import_manifest.h",
     "+chromeos/services/cellular_setup",
@@ -97,12 +92,9 @@
     "+components/mirroring/service",
     "+components/services/pdf_compositor",
     "+components/services/quarantine",
-    "+components/services/unzip/public",
     "+components/startup_metric_utils/common",
-    "+device/vr/buildflags",
     "+extensions/buildflags",
     "+mash/public",
-    "+services/content/simple_browser",
     "+services/proxy_resolver",
   ],
 }
diff --git a/chrome/app/builtin_service_manifests.cc b/chrome/app/builtin_service_manifests.cc
index 3b7dcf2e..ebd6807 100644
--- a/chrome/app/builtin_service_manifests.cc
+++ b/chrome/app/builtin_service_manifests.cc
@@ -8,7 +8,6 @@
 #include "build/build_config.h"
 #include "chrome/common/buildflags.h"
 #include "components/services/quarantine/public/cpp/manifest.h"
-#include "device/vr/buildflags/buildflags.h"
 #include "extensions/buildflags/buildflags.h"
 #include "printing/buildflags/buildflags.h"
 
@@ -30,54 +29,17 @@
 #include "components/mirroring/service/manifest.h"  // nogncheck
 #endif
 
-#if BUILDFLAG(ENABLE_EXTENSIONS)
-#include "chrome/services/removable_storage_writer/public/cpp/manifest.h"
-#endif
-
-#if BUILDFLAG(ENABLE_EXTENSIONS) || defined(OS_ANDROID)
-#include "chrome/services/media_gallery_util/public/cpp/manifest.h"
-#endif
-
 #if BUILDFLAG(ENABLE_PRINTING)
 #include "components/services/pdf_compositor/public/cpp/manifest.h"  // nogncheck
 #endif
 
-#if BUILDFLAG(ENABLE_PRINT_PREVIEW)
-#include "chrome/services/printing/public/cpp/manifest.h"
-#endif
-
-#if BUILDFLAG(ENABLE_VR) && !defined(OS_ANDROID)
-#include "chrome/services/isolated_xr_device/manifest.h"
-#endif
-
-#if BUILDFLAG(ENABLE_SIMPLE_BROWSER_SERVICE_IN_PROCESS) || \
-    BUILDFLAG(ENABLE_SIMPLE_BROWSER_SERVICE_OUT_OF_PROCESS)
-#include "services/content/simple_browser/public/cpp/manifest.h"  // nogncheck
-#endif
-
 const std::vector<service_manager::Manifest>&
 GetChromeBuiltinServiceManifests() {
   static base::NoDestructor<std::vector<service_manager::Manifest>> manifests{{
       quarantine::GetQuarantineManifest(),
-#if BUILDFLAG(ENABLE_EXTENSIONS)
-      GetRemovableStorageWriterManifest(),
-#endif
-#if BUILDFLAG(ENABLE_EXTENSIONS) || defined(OS_ANDROID)
-      GetMediaGalleryUtilManifest(),
-#endif
 #if BUILDFLAG(ENABLE_PRINTING)
       printing::GetPdfCompositorManifest(),
 #endif
-#if BUILDFLAG(ENABLE_PRINT_PREVIEW) || \
-    (BUILDFLAG(ENABLE_PRINTING) && defined(OS_WIN))
-      GetChromePrintingManifest(),
-#endif
-#if BUILDFLAG(ENABLE_VR) && !defined(OS_ANDROID)
-      GetXrDeviceServiceManifest(),
-#endif
-#if BUILDFLAG(ENABLE_SIMPLE_BROWSER_SERVICE_IN_PROCESS)
-      simple_browser::GetManifest(),
-#endif
 #if defined(OS_WIN)
       GetWifiUtilWinManifest(),
 #endif
diff --git a/chrome/app/chrome_content_browser_overlay_manifest.cc b/chrome/app/chrome_content_browser_overlay_manifest.cc
index b6f7695..f98223f 100644
--- a/chrome/app/chrome_content_browser_overlay_manifest.cc
+++ b/chrome/app/chrome_content_browser_overlay_manifest.cc
@@ -131,7 +131,6 @@
         .RequireCapability(image_annotation::mojom::kServiceName,
                            image_annotation::mojom::kAnnotationCapability)
         .RequireCapability("ime", "input_engine")
-        .RequireCapability("media_gallery_util", "parse_media")
         .RequireCapability("mirroring", "mirroring")
         .RequireCapability("nacl_broker", "browser")
         .RequireCapability("nacl_loader", "browser")
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 7ca622f..9bb91493 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -3696,6 +3696,9 @@
         <message name="IDS_EXTENSION_PROMPT_WARNING_LOGIN_SCREEN_UI" desc="Permission string for access to login screen UI.">
           Display UI on the login screen
         </message>
+        <message name="IDS_EXTENSION_PROMPT_WARNING_LOGIN_SCREEN_STORAGE" desc="Permission string for access to login screen storage.">
+          Store persistent data on the login screen and inject credentials into the session.
+        </message>
         <message name="IDS_EXTENSION_PROMPT_WARNING_MANAGEMENT" desc="Permisson string for access to extension management.">
           Manage your apps, extensions, and themes
         </message>
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index c6d0fde0..ad38556 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -1361,6 +1361,9 @@
     <message name="IDS_SETTINGS_CERTIFICATE_MANAGER_SET_TRUST_ERROR_TITLE" desc="The title in the error dialog for certificate trust editing errors.">
       Error Setting Certificate Trust
     </message>
+    <message name="IDS_SETTINGS_CERTIFICATE_MANAGER_ERROR_NOT_ALLOWED" desc="The text in the error dialog for policy restriction errors.">
+      Action is disabled by your administrator
+    </message>
     <message name="IDS_SETTINGS_CERTIFICATE_MANAGER_UNKNOWN_ERROR" desc="The text in the error dialog when an unknown error occurs during an operation on the certificate database.">
       Unknown error
     </message>
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index eec412cc..8952bf8 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -4214,6 +4214,8 @@
         "printing/print_preview_message_handler.h",
         "printing/print_view_manager.cc",
         "printing/print_view_manager.h",
+        "printing/printing_service.cc",
+        "printing/printing_service.h",
         "printing/pwg_raster_converter.cc",
         "printing/pwg_raster_converter.h",
       ]
@@ -4919,17 +4921,6 @@
     ]
   }
 
-  if (enable_simple_browser_service_in_process) {
-    deps += [
-      "//services/content/simple_browser",
-      "//services/content/simple_browser/public/mojom",
-    ]
-  }
-
-  if (enable_simple_browser_service_out_of_process) {
-    deps += [ "//services/content/simple_browser/public/mojom" ]
-  }
-
   if (enable_spellcheck) {
     sources += [
       "spellchecker/spell_check_host_chrome_impl.cc",
diff --git a/chrome/browser/DEPS b/chrome/browser/DEPS
index 678317b..730fa4f 100644
--- a/chrome/browser/DEPS
+++ b/chrome/browser/DEPS
@@ -291,7 +291,6 @@
   "+rlz",
   "+sandbox/win/src",  # The path doesn't say it, but this is the Windows sandbox.
   "+services/audio/public",
-  "+services/content/simple_browser",
   "+services/data_decoder/public",
   "+services/device/public",
   "+services/identity/public",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index f14c3f7..b08a453 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -2310,7 +2310,7 @@
     {"enable-experimental-accessibility-language-detection",
      flag_descriptions::kExperimentalAccessibilityLanguageDetectionName,
      flag_descriptions::kExperimentalAccessibilityLanguageDetectionDescription,
-     kOsCrOS | kExpireM77,
+     kOsAll,
      SINGLE_VALUE_TYPE(
          ::switches::kEnableExperimentalAccessibilityLanguageDetection)},
 #if defined(OS_CHROMEOS)
diff --git a/chrome/browser/android/download/download_media_parser.cc b/chrome/browser/android/download/download_media_parser.cc
index 3516c3d3..17a4f14 100644
--- a/chrome/browser/android/download/download_media_parser.cc
+++ b/chrome/browser/android/download/download_media_parser.cc
@@ -96,20 +96,21 @@
   }
 
   size_ = file_size;
-  RetrieveMediaParser(content::GetSystemConnector());
+  RetrieveMediaParser();
 }
 
 void DownloadMediaParser::OnMediaParserCreated() {
   auto media_source_factory = std::make_unique<LocalMediaDataSourceFactory>(
       file_path_, file_task_runner_);
-  chrome::mojom::MediaDataSourcePtr source_ptr;
+  mojo::PendingRemote<chrome::mojom::MediaDataSource> source;
   media_data_source_ = media_source_factory->CreateMediaDataSource(
-      &source_ptr, base::BindRepeating(&DownloadMediaParser::OnMediaDataReady,
-                                       weak_factory_.GetWeakPtr()));
+      source.InitWithNewPipeAndPassReceiver(),
+      base::BindRepeating(&DownloadMediaParser::OnMediaDataReady,
+                          weak_factory_.GetWeakPtr()));
 
   RecordMediaMetadataEvent(MediaMetadataEvent::kMetadataStart);
   media_parser()->ParseMediaMetadata(
-      mime_type_, size_, false /* get_attached_images */, std::move(source_ptr),
+      mime_type_, size_, false /* get_attached_images */, std::move(source),
       base::BindOnce(&DownloadMediaParser::OnMediaMetadataParsed,
                      weak_factory_.GetWeakPtr()));
 }
@@ -153,13 +154,14 @@
 
   auto media_source_factory = std::make_unique<LocalMediaDataSourceFactory>(
       file_path_, file_task_runner_);
-  chrome::mojom::MediaDataSourcePtr source_ptr;
+  mojo::PendingRemote<chrome::mojom::MediaDataSource> source;
   media_data_source_ = media_source_factory->CreateMediaDataSource(
-      &source_ptr, base::BindRepeating(&DownloadMediaParser::OnMediaDataReady,
-                                       weak_factory_.GetWeakPtr()));
+      source.InitWithNewPipeAndPassReceiver(),
+      base::BindRepeating(&DownloadMediaParser::OnMediaDataReady,
+                          weak_factory_.GetWeakPtr()));
 
   media_parser()->ExtractVideoFrame(
-      mime_type_, base::saturated_cast<uint32_t>(size_), std::move(source_ptr),
+      mime_type_, base::saturated_cast<uint32_t>(size_), std::move(source),
       base::BindOnce(&DownloadMediaParser::OnVideoFrameRetrieved,
                      weak_factory_.GetWeakPtr()));
 }
diff --git a/chrome/browser/android/download/local_media_data_source_factory.cc b/chrome/browser/android/download/local_media_data_source_factory.cc
index d17f283..a2635500 100644
--- a/chrome/browser/android/download/local_media_data_source_factory.cc
+++ b/chrome/browser/android/download/local_media_data_source_factory.cc
@@ -14,7 +14,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
-#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/receiver.h"
 
 namespace {
 
@@ -78,14 +78,14 @@
 class LocalMediaDataSource : public chrome::mojom::MediaDataSource {
  public:
   LocalMediaDataSource(
-      chrome::mojom::MediaDataSourcePtr* interface,
+      mojo::PendingReceiver<chrome::mojom::MediaDataSource> receiver,
       const base::FilePath& file_path,
       scoped_refptr<base::SequencedTaskRunner> file_task_runner,
       MediaDataCallback media_data_callback)
       : file_path_(file_path),
         file_task_runner_(file_task_runner),
         media_data_callback_(media_data_callback),
-        binding_(this, mojo::MakeRequest(interface)),
+        receiver_(this, std::move(receiver)),
         weak_ptr_factory_(this) {}
   ~LocalMediaDataSource() override = default;
 
@@ -124,7 +124,7 @@
   // Pass through callback that is used to send data across IPC channel.
   chrome::mojom::MediaDataSource::ReadCallback ipc_read_callback_;
 
-  mojo::Binding<chrome::mojom::MediaDataSource> binding_;
+  mojo::Receiver<chrome::mojom::MediaDataSource> receiver_;
   base::WeakPtrFactory<LocalMediaDataSource> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(LocalMediaDataSource);
@@ -141,8 +141,8 @@
 
 std::unique_ptr<chrome::mojom::MediaDataSource>
 LocalMediaDataSourceFactory::CreateMediaDataSource(
-    chrome::mojom::MediaDataSourcePtr* request,
+    mojo::PendingReceiver<chrome::mojom::MediaDataSource> receiver,
     MediaDataCallback media_data_callback) {
   return std::make_unique<LocalMediaDataSource>(
-      request, file_path_, file_task_runner_, media_data_callback);
+      std::move(receiver), file_path_, file_task_runner_, media_data_callback);
 }
diff --git a/chrome/browser/android/download/local_media_data_source_factory.h b/chrome/browser/android/download/local_media_data_source_factory.h
index fb8b17ae..1fa474a 100644
--- a/chrome/browser/android/download/local_media_data_source_factory.h
+++ b/chrome/browser/android/download/local_media_data_source_factory.h
@@ -29,7 +29,7 @@
 
   // SafeMediaMetadataParser::MediaDataSourceFactory implementation.
   std::unique_ptr<chrome::mojom::MediaDataSource> CreateMediaDataSource(
-      chrome::mojom::MediaDataSourcePtr* request,
+      mojo::PendingReceiver<chrome::mojom::MediaDataSource> receiver,
       MediaDataCallback media_data_callback) override;
 
  private:
diff --git a/chrome/browser/android/signin/signin_manager_android.cc b/chrome/browser/android/signin/signin_manager_android.cc
index 90f2d69..59b2d59 100644
--- a/chrome/browser/android/signin/signin_manager_android.cc
+++ b/chrome/browser/android/signin/signin_manager_android.cc
@@ -162,16 +162,19 @@
                              java_signin_manager_);
 }
 
-void SigninManagerAndroid::OnSignInCompleted(
+void SigninManagerAndroid::SetPrimaryAccount(
     JNIEnv* env,
     const JavaParamRef<jstring>& username) {
-  DVLOG(1) << "SigninManagerAndroid::OnSignInCompleted";
+  DVLOG(1) << "SigninManagerAndroid::SetPrimaryAccount";
 
-  // TODO(crbug.com/987965): Migrate away from this direct usage of
-  // PrimaryAccountManager and eliminate this class needing to know about
-  // PrimaryAccountManager.
-  identity_manager_->GetPrimaryAccountManager()->SignIn(
-      base::android::ConvertJavaStringToUTF8(env, username));
+  auto account =
+      identity_manager_
+          ->FindExtendedAccountInfoForAccountWithRefreshTokenByEmailAddress(
+              base::android::ConvertJavaStringToUTF8(env, username));
+  DCHECK(account.has_value());
+  auto* account_mutator = identity_manager_->GetPrimaryAccountMutator();
+  bool result = account_mutator->SetPrimaryAccount(account->account_id);
+  CHECK(result);
 }
 
 void SigninManagerAndroid::SignOut(JNIEnv* env,
diff --git a/chrome/browser/android/signin/signin_manager_android.h b/chrome/browser/android/signin/signin_manager_android.h
index 90dc9e7d..d56bf01 100644
--- a/chrome/browser/android/signin/signin_manager_android.h
+++ b/chrome/browser/android/signin/signin_manager_android.h
@@ -44,7 +44,7 @@
 
   // Indicates that the user has made the choice to sign-in. |username|
   // contains the email address of the account to use as primary.
-  void OnSignInCompleted(JNIEnv* env,
+  void SetPrimaryAccount(JNIEnv* env,
                          const base::android::JavaParamRef<jstring>& username);
 
   void SignOut(JNIEnv* env,
diff --git a/chrome/browser/apps/app_service/crostini_apps.cc b/chrome/browser/apps/app_service/crostini_apps.cc
index 986808e..7edf0c6 100644
--- a/chrome/browser/apps/app_service/crostini_apps.cc
+++ b/chrome/browser/apps/app_service/crostini_apps.cc
@@ -256,12 +256,16 @@
 
   auto show = !registration.NoDisplay() ? apps::mojom::OptionalBool::kTrue
                                         : apps::mojom::OptionalBool::kFalse;
+  auto show_in_search = show;
   if (registration.is_terminal_app()) {
     show = crostini_enabled_ ? apps::mojom::OptionalBool::kTrue
                              : apps::mojom::OptionalBool::kFalse;
+    // The Crostini Terminal should appear in the app search, even when
+    // Crostini is not installed.
+    show_in_search = apps::mojom::OptionalBool::kTrue;
   }
   app->show_in_launcher = show;
-  app->show_in_search = show;
+  app->show_in_search = show_in_search;
   // TODO(crbug.com/955937): Enable once Crostini apps are managed inside App
   // Management.
   app->show_in_management = apps::mojom::OptionalBool::kFalse;
diff --git a/chrome/browser/apps/platform_apps/api/media_galleries/blob_data_source_factory.cc b/chrome/browser/apps/platform_apps/api/media_galleries/blob_data_source_factory.cc
index 5c5b82d..bcf79f9 100644
--- a/chrome/browser/apps/platform_apps/api/media_galleries/blob_data_source_factory.cc
+++ b/chrome/browser/apps/platform_apps/api/media_galleries/blob_data_source_factory.cc
@@ -6,7 +6,7 @@
 #include "base/bind.h"
 #include "content/public/browser/browser_context.h"
 #include "extensions/browser/blob_reader.h"
-#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/receiver.h"
 
 namespace chrome_apps {
 namespace api {
@@ -15,11 +15,12 @@
 // Media data source that reads data from a blob in browser process.
 class BlobMediaDataSource : public chrome::mojom::MediaDataSource {
  public:
-  BlobMediaDataSource(chrome::mojom::MediaDataSourcePtr* interface_ptr,
-                      content::BrowserContext* browser_context,
-                      const std::string& blob_uuid,
-                      BlobDataSourceFactory::MediaDataCallback callback)
-      : binding_(this, mojo::MakeRequest(interface_ptr)),
+  BlobMediaDataSource(
+      mojo::PendingReceiver<chrome::mojom::MediaDataSource> receiver,
+      content::BrowserContext* browser_context,
+      const std::string& blob_uuid,
+      BlobDataSourceFactory::MediaDataCallback callback)
+      : receiver_(this, std::move(receiver)),
         browser_context_(browser_context),
         blob_uuid_(blob_uuid),
         callback_(callback) {}
@@ -52,7 +53,7 @@
     callback_.Run(std::move(callback), std::move(data));
   }
 
-  mojo::Binding<chrome::mojom::MediaDataSource> binding_;
+  mojo::Receiver<chrome::mojom::MediaDataSource> receiver_;
 
   content::BrowserContext* const browser_context_;
   std::string blob_uuid_;
@@ -75,10 +76,10 @@
 
 std::unique_ptr<chrome::mojom::MediaDataSource>
 BlobDataSourceFactory::CreateMediaDataSource(
-    chrome::mojom::MediaDataSourcePtr* request,
+    mojo::PendingReceiver<chrome::mojom::MediaDataSource> receiver,
     MediaDataCallback media_data_callback) {
-  return std::make_unique<BlobMediaDataSource>(request, browser_context_,
-                                               blob_uuid_, media_data_callback);
+  return std::make_unique<BlobMediaDataSource>(
+      std::move(receiver), browser_context_, blob_uuid_, media_data_callback);
 }
 
 }  // namespace api
diff --git a/chrome/browser/apps/platform_apps/api/media_galleries/blob_data_source_factory.h b/chrome/browser/apps/platform_apps/api/media_galleries/blob_data_source_factory.h
index f78e939..bd81318 100644
--- a/chrome/browser/apps/platform_apps/api/media_galleries/blob_data_source_factory.h
+++ b/chrome/browser/apps/platform_apps/api/media_galleries/blob_data_source_factory.h
@@ -11,6 +11,7 @@
 #include "base/macros.h"
 #include "chrome/services/media_gallery_util/public/cpp/safe_media_metadata_parser.h"
 #include "chrome/services/media_gallery_util/public/mojom/media_parser.mojom.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
 
 namespace content {
 class BrowserContext;
@@ -31,7 +32,7 @@
  private:
   // SafeMediaMetadataParser::MediaDataSourceFactory implementation.
   std::unique_ptr<chrome::mojom::MediaDataSource> CreateMediaDataSource(
-      chrome::mojom::MediaDataSourcePtr* request,
+      mojo::PendingReceiver<chrome::mojom::MediaDataSource> receiver,
       MediaDataCallback media_data_callback) override;
 
   content::BrowserContext* browser_context_;
diff --git a/chrome/browser/apps/platform_apps/api/media_galleries/media_galleries_api.cc b/chrome/browser/apps/platform_apps/api/media_galleries/media_galleries_api.cc
index b9d3e3b..85bc6c3 100644
--- a/chrome/browser/apps/platform_apps/api/media_galleries/media_galleries_api.cc
+++ b/chrome/browser/apps/platform_apps/api/media_galleries/media_galleries_api.cc
@@ -51,7 +51,6 @@
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/render_view_host.h"
-#include "content/public/browser/system_connector.h"
 #include "content/public/browser/web_contents.h"
 #include "extensions/browser/api/file_system/file_system_api.h"
 #include "extensions/browser/app_window/app_window.h"
@@ -679,7 +678,6 @@
       std::move(media_data_source_factory));
   SafeMediaMetadataParser* parser_ptr = parser.get();
   parser_ptr->Start(
-      content::GetSystemConnector(),
       base::BindOnce(
           &MediaGalleriesGetMetadataFunction::OnSafeMediaMetadataParserDone,
           this, std::move(parser)));
diff --git a/chrome/browser/autofill/credit_card_accessory_controller_impl.cc b/chrome/browser/autofill/credit_card_accessory_controller_impl.cc
index d1cf124..012d337 100644
--- a/chrome/browser/autofill/credit_card_accessory_controller_impl.cc
+++ b/chrome/browser/autofill/credit_card_accessory_controller_impl.cc
@@ -41,7 +41,7 @@
 UserInfo TranslateCard(const CreditCard* data) {
   DCHECK(data);
 
-  UserInfo user_info;
+  UserInfo user_info(data->network());
 
   base::string16 obfuscated_number = data->ObfuscatedLastFourDigits();
   user_info.add_field(UserInfo::Field(obfuscated_number, obfuscated_number,
diff --git a/chrome/browser/autofill/credit_card_accessory_controller_impl_unittest.cc b/chrome/browser/autofill/credit_card_accessory_controller_impl_unittest.cc
index c9c0a9eb..29795a3 100644
--- a/chrome/browser/autofill/credit_card_accessory_controller_impl_unittest.cc
+++ b/chrome/browser/autofill/credit_card_accessory_controller_impl_unittest.cc
@@ -129,7 +129,7 @@
   ASSERT_EQ(
       result,
       CreditCardAccessorySheetDataBuilder()
-          .AddUserInfo()
+          .AddUserInfo(kVisaCard)
           .AppendField(card.ObfuscatedLastFourDigits(),
                        card.ObfuscatedLastFourDigits(), card.guid(),
                        /*is_obfuscated=*/false,
diff --git a/chrome/browser/banners/app_banner_manager_desktop.cc b/chrome/browser/banners/app_banner_manager_desktop.cc
index c2e2b71c..4af8e60 100644
--- a/chrome/browser/banners/app_banner_manager_desktop.cc
+++ b/chrome/browser/banners/app_banner_manager_desktop.cc
@@ -160,8 +160,11 @@
   DCHECK(provider);
   base::Optional<web_app::AppId> app_id =
       provider->registrar().FindAppWithUrlInScope(validated_url_);
-  if (app_id.has_value() && *app_id == installed_app_id)
+  if (app_id.has_value() && *app_id == installed_app_id &&
+      provider->registrar().GetAppLaunchContainer(*app_id) ==
+          web_app::LaunchContainer::kWindow) {
     OnInstall(blink::kWebDisplayModeStandalone);
+  }
 }
 
 void AppBannerManagerDesktop::OnAppRegistrarDestroyed() {
diff --git a/chrome/browser/chrome_browser_main.cc b/chrome/browser/chrome_browser_main.cc
index 084f7416..b91d4b9 100644
--- a/chrome/browser/chrome_browser_main.cc
+++ b/chrome/browser/chrome_browser_main.cc
@@ -328,11 +328,6 @@
 #include "ui/aura/env.h"
 #endif
 
-#if BUILDFLAG(ENABLE_SIMPLE_BROWSER_SERVICE_IN_PROCESS) || \
-    BUILDFLAG(ENABLE_SIMPLE_BROWSER_SERVICE_OUT_OF_PROCESS)
-#include "services/content/simple_browser/public/mojom/constants.mojom.h"
-#endif
-
 #if !defined(OS_ANDROID)
 #include "chrome/browser/component_updater/intervention_policy_database_component_installer.h"
 #include "chrome/browser/resource_coordinator/tab_manager.h"
@@ -1825,19 +1820,6 @@
   }
 #endif  // defined(OS_ANDROID)
 
-
-#if BUILDFLAG(ENABLE_SIMPLE_BROWSER_SERVICE_OUT_OF_PROCESS) || \
-    BUILDFLAG(ENABLE_SIMPLE_BROWSER_SERVICE_IN_PROCESS)
-  if (parsed_command_line().HasSwitch(switches::kLaunchSimpleBrowserSwitch) ||
-      parsed_command_line().HasSwitch(
-          switches::kLaunchInProcessSimpleBrowserSwitch)) {
-    // TODO(https://crbug.com/904148): This should not use |WarmService()|.
-    content::BrowserContext::GetConnectorFor(profile_)->WarmService(
-        service_manager::ServiceFilter::ByName(
-            simple_browser::mojom::kServiceName));
-  }
-#endif
-
   return result_code_;
 }
 
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index b9a17c0..0632743 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -434,7 +434,6 @@
 #include "chrome/browser/offline_pages/android/offline_page_auto_fetcher.h"
 #include "chrome/browser/ui/android/tab_model/tab_model_list.h"
 #include "chrome/common/chrome_descriptors.h"
-#include "chrome/services/media_gallery_util/public/mojom/constants.mojom.h"
 #include "components/crash/content/browser/child_exit_observer_android.h"
 #include "components/crash/content/browser/crash_memory_metrics_collector_android.h"
 #include "components/navigation_interception/intercept_navigation_delegate.h"
@@ -534,8 +533,6 @@
 #include "chrome/browser/media/cast_transport_host_filter.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"
-#include "chrome/services/removable_storage_writer/public/mojom/constants.mojom.h"
 #include "components/guest_view/browser/guest_view_base.h"
 #include "components/guest_view/browser/guest_view_manager.h"
 #include "extensions/browser/api/web_request/web_request_api.h"
@@ -578,11 +575,6 @@
 #include "components/services/pdf_compositor/public/mojom/pdf_compositor.mojom.h"
 #endif
 
-#if BUILDFLAG(ENABLE_PRINT_PREVIEW) || \
-    (BUILDFLAG(ENABLE_PRINTING) && defined(OS_WIN))
-#include "chrome/services/printing/public/mojom/constants.mojom.h"
-#endif
-
 #if BUILDFLAG(ENABLE_LIBRARY_CDMS)
 #include "chrome/browser/media/output_protection_impl.h"
 #include "chrome/browser/media/platform_verification_impl.h"
@@ -614,15 +606,6 @@
 #include "chrome/browser/offline_pages/offline_page_url_loader_request_interceptor.h"
 #endif
 
-#if BUILDFLAG(ENABLE_SIMPLE_BROWSER_SERVICE_IN_PROCESS)
-#include "services/content/simple_browser/public/mojom/constants.mojom.h"
-#include "services/content/simple_browser/simple_browser_service.h"
-#endif
-
-#if BUILDFLAG(ENABLE_SIMPLE_BROWSER_SERVICE_OUT_OF_PROCESS)
-#include "services/content/simple_browser/public/mojom/constants.mojom.h"
-#endif
-
 #if BUILDFLAG(ENABLE_VR) && !defined(OS_ANDROID)
 #include "device/vr/public/mojom/isolated_xr_service.mojom.h"
 #endif
@@ -977,12 +960,6 @@
          process_map->Contains(extension->id(), opener_render_process_id);
 }
 
-void InvokeCallbackOnThread(
-    scoped_refptr<base::SequencedTaskRunner> task_runner,
-    base::Callback<void(bool)> callback,
-    bool result) {
-  task_runner->PostTask(FROM_HERE, base::BindOnce(std::move(callback), result));
-}
 #endif
 
 chrome::mojom::PrerenderCanceler* GetPrerenderCanceller(
@@ -2428,19 +2405,17 @@
 
 void ChromeContentBrowserClient::AllowWorkerFileSystem(
     const GURL& url,
-    content::ResourceContext* context,
+    content::BrowserContext* browser_context,
     const std::vector<content::GlobalFrameRoutingId>& render_frames,
-    base::Callback<void(bool)> callback) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  ProfileIOData* io_data = ProfileIOData::FromResourceContext(context);
-  content_settings::CookieSettings* cookie_settings =
-      io_data->GetCookieSettings();
+    base::OnceCallback<void(bool)> callback) {
+  Profile* profile = Profile::FromBrowserContext(browser_context);
+  auto cookie_settings = CookieSettingsFactory::GetForProfile(profile);
   bool allow = cookie_settings->IsCookieAccessAllowed(url, url);
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
-  GuestPermissionRequestHelper(url, render_frames, callback, allow);
+  GuestPermissionRequestHelper(url, render_frames, std::move(callback), allow);
 #else
-  FileSystemAccessed(url, render_frames, callback, allow);
+  FileSystemAccessed(url, render_frames, std::move(callback), allow);
 #endif
 }
 
@@ -2448,9 +2423,9 @@
 void ChromeContentBrowserClient::GuestPermissionRequestHelper(
     const GURL& url,
     const std::vector<content::GlobalFrameRoutingId>& render_frames,
-    base::Callback<void(bool)> callback,
+    base::OnceCallback<void(bool)> callback,
     bool allow) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   std::map<int, int> process_map;
   std::map<int, int>::const_iterator it;
   bool has_web_view_guest = false;
@@ -2465,70 +2440,48 @@
       has_web_view_guest = true;
   }
   if (!has_web_view_guest) {
-    FileSystemAccessed(url, render_frames, callback, allow);
+    FileSystemAccessed(url, render_frames, std::move(callback), allow);
     return;
   }
   DCHECK_EQ(1U, process_map.size());
   it = process_map.begin();
-  base::PostTaskWithTraits(
-      FROM_HERE, {BrowserThread::UI},
-      base::BindOnce(
-          &ChromeContentBrowserClient::RequestFileSystemPermissionOnUIThread,
-          it->first, it->second, url, allow,
-          base::Bind(
-              &ChromeContentBrowserClient::FileSystemAccessed,
-              weak_factory_.GetWeakPtr(), url, render_frames,
-              base::Bind(&InvokeCallbackOnThread,
-                         base::SequencedTaskRunnerHandle::Get(), callback))));
-}
 
-void ChromeContentBrowserClient::RequestFileSystemPermissionOnUIThread(
-    int render_process_id,
-    int render_frame_id,
-    const GURL& url,
-    bool allowed_by_default,
-    const base::Callback<void(bool)>& callback) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   extensions::WebViewPermissionHelper* web_view_permission_helper =
-      extensions::WebViewPermissionHelper::FromFrameID(
-          render_process_id, render_frame_id);
-  web_view_permission_helper->RequestFileSystemPermission(url,
-                                                          allowed_by_default,
-                                                          callback);
+      extensions::WebViewPermissionHelper::FromFrameID(it->first, it->second);
+  web_view_permission_helper->RequestFileSystemPermission(
+      url, allow,
+      base::BindOnce(&ChromeContentBrowserClient::FileSystemAccessed,
+                     weak_factory_.GetWeakPtr(), url, render_frames,
+                     std::move(callback)));
 }
 #endif
 
 void ChromeContentBrowserClient::FileSystemAccessed(
     const GURL& url,
     const std::vector<content::GlobalFrameRoutingId>& render_frames,
-    base::Callback<void(bool)> callback,
+    base::OnceCallback<void(bool)> callback,
     bool allow) {
   // Record access to file system for potential display in UI.
   for (const auto& it : render_frames) {
-    base::PostTaskWithTraits(
-        FROM_HERE, {BrowserThread::UI},
-        base::BindOnce(&TabSpecificContentSettings::FileSystemAccessed,
-                       it.child_id, it.frame_routing_id, url, !allow));
+    TabSpecificContentSettings::FileSystemAccessed(
+        it.child_id, it.frame_routing_id, url, !allow);
   }
-  callback.Run(allow);
+  std::move(callback).Run(allow);
 }
 
 bool ChromeContentBrowserClient::AllowWorkerIndexedDB(
     const GURL& url,
-    content::ResourceContext* context,
+    content::BrowserContext* browser_context,
     const std::vector<content::GlobalFrameRoutingId>& render_frames) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  ProfileIOData* io_data = ProfileIOData::FromResourceContext(context);
-  content_settings::CookieSettings* cookie_settings =
-      io_data->GetCookieSettings();
+  Profile* profile = Profile::FromBrowserContext(browser_context);
+  auto cookie_settings = CookieSettingsFactory::GetForProfile(profile);
+
   bool allow = cookie_settings->IsCookieAccessAllowed(url, url);
 
   // Record access to IndexedDB for potential display in UI.
   for (const auto& it : render_frames) {
-    base::PostTaskWithTraits(
-        FROM_HERE, {BrowserThread::UI},
-        base::BindOnce(&TabSpecificContentSettings::IndexedDBAccessed,
-                       it.child_id, it.frame_routing_id, url, !allow));
+    TabSpecificContentSettings::IndexedDBAccessed(
+        it.child_id, it.frame_routing_id, url, !allow);
   }
 
   return allow;
@@ -2536,20 +2489,16 @@
 
 bool ChromeContentBrowserClient::AllowWorkerCacheStorage(
     const GURL& url,
-    content::ResourceContext* context,
+    content::BrowserContext* browser_context,
     const std::vector<content::GlobalFrameRoutingId>& render_frames) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  ProfileIOData* io_data = ProfileIOData::FromResourceContext(context);
-  content_settings::CookieSettings* cookie_settings =
-      io_data->GetCookieSettings();
+  Profile* profile = Profile::FromBrowserContext(browser_context);
+  auto cookie_settings = CookieSettingsFactory::GetForProfile(profile);
   bool allow = cookie_settings->IsCookieAccessAllowed(url, url);
 
   // Record access to CacheStorage for potential display in UI.
   for (const auto& it : render_frames) {
-    base::PostTaskWithTraits(
-        FROM_HERE, {BrowserThread::UI},
-        base::BindOnce(&TabSpecificContentSettings::CacheStorageAccessed,
-                       it.child_id, it.frame_routing_id, url, !allow));
+    TabSpecificContentSettings::CacheStorageAccessed(
+        it.child_id, it.frame_routing_id, url, !allow);
   }
 
   return allow;
@@ -3968,18 +3917,6 @@
   }
 #endif
 
-#if BUILDFLAG(ENABLE_SIMPLE_BROWSER_SERVICE_IN_PROCESS)
-  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kLaunchInProcessSimpleBrowserSwitch) &&
-      service_name == simple_browser::mojom::kServiceName) {
-    service_manager::Service::RunAsyncUntilTermination(
-        std::make_unique<simple_browser::SimpleBrowserService>(
-            std::move(*receiver), simple_browser::SimpleBrowserService::
-                                      UIInitializationMode::kUseEnvironmentUI));
-    return;
-  }
-#endif
-
 #if defined(OS_CHROMEOS)
   if (base::FeatureList::IsEnabled(
           chromeos::features::kUpdatedCellularActivationUi) &&
diff --git a/chrome/browser/chrome_content_browser_client.h b/chrome/browser/chrome_content_browser_client.h
index 8c9a373..1a593674 100644
--- a/chrome/browser/chrome_content_browser_client.h
+++ b/chrome/browser/chrome_content_browser_client.h
@@ -232,16 +232,16 @@
   bool AllowSignedExchange(content::BrowserContext* browser_context) override;
   void AllowWorkerFileSystem(
       const GURL& url,
-      content::ResourceContext* context,
+      content::BrowserContext* browser_context,
       const std::vector<content::GlobalFrameRoutingId>& render_frames,
-      base::Callback<void(bool)> callback) override;
+      base::OnceCallback<void(bool)> callback) override;
   bool AllowWorkerIndexedDB(
       const GURL& url,
-      content::ResourceContext* context,
+      content::BrowserContext* browser_context,
       const std::vector<content::GlobalFrameRoutingId>& render_frames) override;
   bool AllowWorkerCacheStorage(
       const GURL& url,
-      content::ResourceContext* context,
+      content::BrowserContext* browser_context,
       const std::vector<content::GlobalFrameRoutingId>& render_frames) override;
   AllowWebBluetoothResult AllowWebBluetooth(
       content::BrowserContext* browser_context,
@@ -633,22 +633,15 @@
   void FileSystemAccessed(
       const GURL& url,
       const std::vector<content::GlobalFrameRoutingId>& render_frames,
-      base::Callback<void(bool)> callback,
+      base::OnceCallback<void(bool)> callback,
       bool allow);
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
   void GuestPermissionRequestHelper(
       const GURL& url,
       const std::vector<content::GlobalFrameRoutingId>& render_frames,
-      base::Callback<void(bool)> callback,
+      base::OnceCallback<void(bool)> callback,
       bool allow);
-
-  static void RequestFileSystemPermissionOnUIThread(
-      int render_process_id,
-      int render_frame_id,
-      const GURL& url,
-      bool allowed_by_default,
-      const base::Callback<void(bool)>& callback);
 #endif
 
   // The value pointed to by |settings| should remain valid until the
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index c2db887..d08222d 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -755,6 +755,8 @@
     "crostini/crostini_simple_types.h",
     "crostini/crostini_terminal.cc",
     "crostini/crostini_terminal.h",
+    "crostini/crostini_unsupported_action_notifier.cc",
+    "crostini/crostini_unsupported_action_notifier.h",
     "crostini/crostini_util.cc",
     "crostini/crostini_util.h",
     "crostini/crosvm_metrics.cc",
@@ -2153,6 +2155,8 @@
     "extensions/input_method_api.h",
     "extensions/login_screen/login/login_api.cc",
     "extensions/login_screen/login/login_api.h",
+    "extensions/login_screen/login_screen_storage/login_screen_storage_api.cc",
+    "extensions/login_screen/login_screen_storage/login_screen_storage_api.h",
     "extensions/login_screen/login_screen_ui/login_screen_extension_ui_handler.cc",
     "extensions/login_screen/login_screen_ui/login_screen_extension_ui_handler.h",
     "extensions/login_screen/login_screen_ui/login_screen_ui_api.cc",
@@ -2423,6 +2427,7 @@
     "crostini/crostini_package_service_unittest.cc",
     "crostini/crostini_registry_service_unittest.cc",
     "crostini/crostini_reporting_util_unittest.cc",
+    "crostini/crostini_unsupported_action_notifier_unittest.cc",
     "crostini/crosvm_metrics_unittest.cc",
     "crostini/crosvm_process_list_unittest.cc",
     "cryptauth/client_app_metadata_provider_service_unittest.cc",
diff --git a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
index 52db0ad0..aad1c0e2 100644
--- a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
+++ b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
@@ -45,6 +45,8 @@
 #include "chrome/browser/chromeos/arc/arc_service_launcher.h"
 #include "chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client.h"
 #include "chrome/browser/chromeos/boot_times_recorder.h"
+#include "chrome/browser/chromeos/crostini/crostini_unsupported_action_notifier.h"
+#include "chrome/browser/chromeos/crostini/crosvm_metrics.h"
 #include "chrome/browser/chromeos/dbus/chrome_features_service_provider.h"
 #include "chrome/browser/chromeos/dbus/component_updater_service_provider.h"
 #include "chrome/browser/chromeos/dbus/cryptohome_key_delegate_service_provider.h"
@@ -1069,6 +1071,9 @@
     cros_usb_detector_->ConnectToDeviceManager();
   }
 
+  crostini_unsupported_action_notifier_ =
+      std::make_unique<crostini::CrostiniUnsupportedActionNotifier>();
+
   dark_resume_controller_ = std::make_unique<system::DarkResumeController>(
       content::GetSystemConnector());
 
@@ -1077,6 +1082,8 @@
 
 // Shut down services before the browser process, etc are destroyed.
 void ChromeBrowserMainPartsChromeos::PostMainMessageLoopRun() {
+  crostini_unsupported_action_notifier_.reset();
+
   ResourceReporter::GetInstance()->StopMonitoring();
 
   BootTimesRecorder::Get()->AddLogoutTimeMarker("UIMessageLoopEnded", true);
diff --git a/chrome/browser/chromeos/chrome_browser_main_chromeos.h b/chrome/browser/chromeos/chrome_browser_main_chromeos.h
index f6eacd7..30f6a16 100644
--- a/chrome/browser/chromeos/chrome_browser_main_chromeos.h
+++ b/chrome/browser/chromeos/chrome_browser_main_chromeos.h
@@ -10,7 +10,6 @@
 #include "base/macros.h"
 #include "base/task/cancelable_task_tracker.h"
 #include "chrome/browser/chrome_browser_main_linux.h"
-#include "chrome/browser/chromeos/crostini/crosvm_metrics.h"
 #include "chrome/browser/chromeos/external_metrics.h"
 #include "chrome/browser/memory/memory_kills_monitor.h"
 #include "chromeos/assistant/buildflags.h"
@@ -35,6 +34,11 @@
 class AssistantClient;
 #endif
 
+namespace crostini {
+class CrostiniUnsupportedActionNotifier;
+class CrosvmMetrics;
+}  // namespace crostini
+
 namespace chromeos {
 
 class ArcKioskAppManager;
@@ -165,6 +169,9 @@
 
   std::unique_ptr<CrosUsbDetector> cros_usb_detector_;
 
+  std::unique_ptr<crostini::CrostiniUnsupportedActionNotifier>
+      crostini_unsupported_action_notifier_;
+
   std::unique_ptr<chromeos::system::DarkResumeController>
       dark_resume_controller_;
 
diff --git a/chrome/browser/chromeos/crostini/crostini_package_service.cc b/chrome/browser/chromeos/crostini/crostini_package_service.cc
index de126ac..1ab1e1eb 100644
--- a/chrome/browser/chromeos/crostini/crostini_package_service.cc
+++ b/chrome/browser/chromeos/crostini/crostini_package_service.cc
@@ -457,8 +457,16 @@
   auto registration =
       CrostiniRegistryServiceFactory::GetForProfile(profile_)->GetRegistration(
           app_id);
-  DCHECK(registration);
-  UninstallApplication(*registration, app_id);
+
+  // It's possible that some other process has uninstalled this application
+  // already. If this happens, we want to skip the notification directly to the
+  // success state.
+  if (registration) {
+    UninstallApplication(*registration, app_id);
+  } else {
+    UpdatePackageOperationStatus(container_id,
+                                 PackageOperationStatus::SUCCEEDED, 100);
+  }
 
   // Clean up memory.
   if (uninstall_queue.empty()) {
diff --git a/chrome/browser/chromeos/crostini/crostini_package_service_unittest.cc b/chrome/browser/chromeos/crostini/crostini_package_service_unittest.cc
index 2b60f63..f582283 100644
--- a/chrome/browser/chromeos/crostini/crostini_package_service_unittest.cc
+++ b/chrome/browser/chromeos/crostini/crostini_package_service_unittest.cc
@@ -873,6 +873,27 @@
                            IsUninstallProgressNotification(0, SECOND_APP)));
 }
 
+TEST_F(CrostiniPackageServiceTest, DuplicateUninstallSucceeds) {
+  service_->QueueUninstallApplication(kDefaultAppId);
+  service_->QueueUninstallApplication(kDefaultAppId);
+
+  UninstallPackageOwningFileRequest request;
+  StartAndSignalUninstall(UninstallPackageProgressSignal::UNINSTALLING,
+                          50 /*progress_percent*/, kDefaultAppFileId, &request);
+
+  crostini_test_helper_->RemoveApp(0);
+
+  UninstallPackageProgressSignal signal_success = MakeUninstallSignal(request);
+  signal_success.set_status(UninstallPackageProgressSignal::SUCCEEDED);
+  fake_cicerone_client_->UninstallPackageProgress(signal_success);
+
+  EXPECT_THAT(
+      Printable(notification_display_service_->GetDisplayedNotificationsForType(
+          NotificationHandler::Type::TRANSIENT)),
+      UnorderedElementsAre(IsUninstallSuccessNotification(DEFAULT_APP),
+                           IsUninstallSuccessNotification(DEFAULT_APP)));
+}
+
 TEST_F(CrostiniPackageServiceTest,
        AfterSecondInstallStartsProgressAppliesToSecond) {
   service_->QueueUninstallApplication(kDefaultAppId);
diff --git a/chrome/browser/chromeos/crostini/crostini_unsupported_action_notifier.cc b/chrome/browser/chromeos/crostini/crostini_unsupported_action_notifier.cc
new file mode 100644
index 0000000..b6a43f6b
--- /dev/null
+++ b/chrome/browser/chromeos/crostini/crostini_unsupported_action_notifier.cc
@@ -0,0 +1,111 @@
+// 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/crostini/crostini_unsupported_action_notifier.h"
+
+#include <utility>
+
+#include "ash/public/cpp/app_types.h"
+#include "ash/public/cpp/tablet_mode.h"
+#include "ash/public/cpp/toast_manager.h"
+#include "base/logging.h"
+#include "base/optional.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/exo/wm_helper.h"
+#include "ui/aura/client/aura_constants.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace crostini {
+
+CrostiniUnsupportedActionNotifier::CrostiniUnsupportedActionNotifier()
+    : CrostiniUnsupportedActionNotifier(std::make_unique<Delegate>()) {}
+
+CrostiniUnsupportedActionNotifier::CrostiniUnsupportedActionNotifier(
+    std::unique_ptr<Delegate> delegate)
+    : delegate_(std::move(delegate)) {
+  delegate_->AddTabletModeObserver(this);
+  delegate_->AddFocusObserver(this);
+}
+
+CrostiniUnsupportedActionNotifier::~CrostiniUnsupportedActionNotifier() {
+  delegate_->RemoveTabletModeObserver(this);
+  delegate_->RemoveFocusObserver(this);
+}
+
+void CrostiniUnsupportedActionNotifier::
+    ShowVirtualKeyboardUnsupportedNotifictionIfNeeded() {
+  if (!virtual_keyboard_unsupported_message_shown_ &&
+      delegate_->IsInTabletMode() && delegate_->IsFocusedWindowCrostini()) {
+    ash::ToastData data = {
+        /*id=*/"VKUnsupportedInCrostini",
+        /*text=*/
+        l10n_util::GetStringUTF16(IDS_CROSTINI_UNSUPPORTED_VIRTUAL_KEYBOARD),
+        /*timeout_ms=*/5000,
+        /*dismiss_text=*/base::Optional<base::string16>()};
+    delegate_->ShowToast(data);
+    virtual_keyboard_unsupported_message_shown_ = true;
+  }
+}
+
+void CrostiniUnsupportedActionNotifier::OnTabletModeStarted() {
+  ShowVirtualKeyboardUnsupportedNotifictionIfNeeded();
+}
+
+void CrostiniUnsupportedActionNotifier::OnWindowFocused(
+    aura::Window* gained_focus,
+    aura::Window* lost_focus) {
+  ShowVirtualKeyboardUnsupportedNotifictionIfNeeded();
+}
+
+CrostiniUnsupportedActionNotifier::Delegate::Delegate() = default;
+
+CrostiniUnsupportedActionNotifier::Delegate::~Delegate() = default;
+
+bool CrostiniUnsupportedActionNotifier::Delegate::IsInTabletMode() {
+  return ash::TabletMode::Get()->InTabletMode();
+}
+
+bool CrostiniUnsupportedActionNotifier::Delegate::IsFocusedWindowCrostini() {
+  if (!exo::WMHelper::HasInstance()) {
+    return false;
+  }
+  auto* focused_window = exo::WMHelper::GetInstance()->GetFocusedWindow();
+  return focused_window->GetProperty(aura::client::kAppType) ==
+         static_cast<int>(ash::AppType::CROSTINI_APP);
+}
+
+void CrostiniUnsupportedActionNotifier::Delegate::ShowToast(
+    const ash::ToastData& toast_data) {
+  ash::ToastManager::Get()->Show(toast_data);
+}
+
+void CrostiniUnsupportedActionNotifier::Delegate::AddFocusObserver(
+    aura::client::FocusChangeObserver* observer) {
+  if (exo::WMHelper::HasInstance()) {
+    exo::WMHelper::GetInstance()->AddFocusObserver(observer);
+  }
+}
+
+void CrostiniUnsupportedActionNotifier::Delegate::RemoveFocusObserver(
+    aura::client::FocusChangeObserver* observer) {
+  if (exo::WMHelper::HasInstance()) {
+    exo::WMHelper::GetInstance()->RemoveFocusObserver(observer);
+  }
+}
+
+void CrostiniUnsupportedActionNotifier::Delegate::AddTabletModeObserver(
+    TabletModeObserver* observer) {
+  auto* client = ash::TabletMode::Get();
+  DCHECK(client);
+  client->AddObserver(observer);
+}
+
+void CrostiniUnsupportedActionNotifier::Delegate::RemoveTabletModeObserver(
+    TabletModeObserver* observer) {
+  auto* client = ash::TabletMode::Get();
+  DCHECK(client);
+  client->RemoveObserver(observer);
+}
+
+}  // namespace crostini
diff --git a/chrome/browser/chromeos/crostini/crostini_unsupported_action_notifier.h b/chrome/browser/chromeos/crostini/crostini_unsupported_action_notifier.h
new file mode 100644
index 0000000..f7c28b6f
--- /dev/null
+++ b/chrome/browser/chromeos/crostini/crostini_unsupported_action_notifier.h
@@ -0,0 +1,75 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_CROSTINI_CROSTINI_UNSUPPORTED_ACTION_NOTIFIER_H_
+#define CHROME_BROWSER_CHROMEOS_CROSTINI_CROSTINI_UNSUPPORTED_ACTION_NOTIFIER_H_
+
+#include <memory>
+
+#include "ash/public/cpp/tablet_mode_observer.h"
+#include "ash/public/cpp/toast_data.h"
+#include "ui/aura/client/focus_change_observer.h"
+
+namespace crostini {
+
+// Notifies the user when they try to do something Crostini doesn't yet support
+// e.g. use the virtual keyboard in a crostini app.
+// TODO(davidmunro): Emit metrics around how often we're hitting these issues so
+// we can prioritise appropriately.
+class CrostiniUnsupportedActionNotifier
+    : public ash::TabletModeObserver,
+      public aura::client::FocusChangeObserver {
+ public:
+  // Adapter around external integrations which we can mock out for testing,
+  // stateless.
+  class Delegate {
+   public:
+    Delegate();
+    virtual ~Delegate();
+
+    virtual bool IsInTabletMode();
+
+    // True if the window which currently has focus is a crostini window,
+    // doesn't count the terminal.
+    virtual bool IsFocusedWindowCrostini();
+
+    // Shows a toast to the user
+    virtual void ShowToast(const ash::ToastData& toast_data);
+
+    virtual void AddFocusObserver(aura::client::FocusChangeObserver* observer);
+    virtual void RemoveFocusObserver(
+        aura::client::FocusChangeObserver* observer);
+    virtual void AddTabletModeObserver(ash::TabletModeObserver* observer);
+    virtual void RemoveTabletModeObserver(ash::TabletModeObserver* observer);
+  };
+
+  CrostiniUnsupportedActionNotifier();
+  explicit CrostiniUnsupportedActionNotifier(
+      std::unique_ptr<Delegate> delegate);
+  ~CrostiniUnsupportedActionNotifier() override;
+
+  // ash::TabletModeObserver
+  void OnTabletModeStarted() override;
+
+  // aura::client::FocusChangeObserver
+  void OnWindowFocused(aura::Window* gained_focus,
+                       aura::Window* lost_focus) override;
+
+  Delegate* get_delegate_for_testing() { return delegate_.get(); }
+
+ private:
+  // Checks if the user is trying to use a virtual keyboard with a crostini
+  // app and, if so and if they haven't already been notified that it's not
+  // supported, notify them.
+  void ShowVirtualKeyboardUnsupportedNotifictionIfNeeded();
+
+  std::unique_ptr<Delegate> delegate_;
+  bool virtual_keyboard_unsupported_message_shown_ = false;
+
+  DISALLOW_COPY_AND_ASSIGN(CrostiniUnsupportedActionNotifier);
+};
+
+}  // namespace crostini
+
+#endif  // CHROME_BROWSER_CHROMEOS_CROSTINI_CROSTINI_UNSUPPORTED_ACTION_NOTIFIER_H_
diff --git a/chrome/browser/chromeos/crostini/crostini_unsupported_action_notifier_unittest.cc b/chrome/browser/chromeos/crostini/crostini_unsupported_action_notifier_unittest.cc
new file mode 100644
index 0000000..162b791
--- /dev/null
+++ b/chrome/browser/chromeos/crostini/crostini_unsupported_action_notifier_unittest.cc
@@ -0,0 +1,98 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/crostini/crostini_unsupported_action_notifier.h"
+
+#include <memory>
+#include <tuple>
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace crostini {
+
+using ::testing::_;
+using ::testing::Bool;
+using ::testing::Exactly;
+using ::testing::InSequence;
+using ::testing::NiceMock;
+using ::testing::Return;
+
+class MockDelegate : public CrostiniUnsupportedActionNotifier::Delegate {
+ public:
+  MOCK_METHOD(bool, IsInTabletMode, (), (override));
+  MOCK_METHOD(bool, IsFocusedWindowCrostini, (), (override));
+  MOCK_METHOD(void, ShowToast, (const ash::ToastData& toast_data), (override));
+  MOCK_METHOD(void,
+              AddFocusObserver,
+              (aura::client::FocusChangeObserver * observer),
+              (override));
+  MOCK_METHOD(void,
+              RemoveFocusObserver,
+              (aura::client::FocusChangeObserver * observer),
+              (override));
+  MOCK_METHOD(void,
+              AddTabletModeObserver,
+              (ash::TabletModeObserver * observer),
+              (override));
+  MOCK_METHOD(void,
+              RemoveTabletModeObserver,
+              (ash::TabletModeObserver * observer),
+              (override));
+};
+
+class CrostiniUnsupportedActionNotifierTest
+    : public testing::TestWithParam<std::tuple<bool, bool>> {
+ public:
+  CrostiniUnsupportedActionNotifierTest()
+      : notifier(std::make_unique<NiceMock<MockDelegate>>()) {}
+  virtual ~CrostiniUnsupportedActionNotifierTest() = default;
+
+  MockDelegate& get_delegate() {
+    auto* ptr = notifier.get_delegate_for_testing();
+    DCHECK(ptr);
+
+    // Our delegate is always a mock delegate in these tests, but we don't have
+    // RTTI so have to use a static cast.
+    return static_cast<NiceMock<MockDelegate>&>(*ptr);
+  }
+  bool is_tablet_mode() const { return std::get<0>(GetParam()); }
+  bool is_crostini_focused() const { return std::get<1>(GetParam()); }
+
+  CrostiniUnsupportedActionNotifier notifier;
+};
+
+TEST_P(CrostiniUnsupportedActionNotifierTest,
+       ToastShownOnceOnlyWhenEnteringTabletModeWhileCrostiniAppFocused) {
+  EXPECT_CALL(get_delegate(), IsInTabletMode)
+      .WillRepeatedly(Return(is_tablet_mode()));
+  EXPECT_CALL(get_delegate(), IsFocusedWindowCrostini)
+      .WillRepeatedly(Return(is_crostini_focused()));
+  EXPECT_CALL(get_delegate(), ShowToast(_))
+      .Times((is_tablet_mode() && is_crostini_focused()) ? 1 : 0);
+
+  notifier.OnTabletModeStarted();
+  notifier.OnTabletModeStarted();
+  notifier.OnTabletModeStarted();
+}
+
+TEST_P(CrostiniUnsupportedActionNotifierTest,
+       ToastShownOnceOnlyWhenFocusingCrostiniWhileInTabletMode) {
+  EXPECT_CALL(get_delegate(), IsInTabletMode)
+      .WillRepeatedly(Return(is_tablet_mode()));
+  EXPECT_CALL(get_delegate(), IsFocusedWindowCrostini)
+      .WillRepeatedly(Return(is_crostini_focused()));
+  EXPECT_CALL(get_delegate(), ShowToast(_))
+      .Times((is_tablet_mode() && is_crostini_focused()) ? 1 : 0);
+
+  notifier.OnWindowFocused(nullptr, nullptr);
+  notifier.OnWindowFocused(nullptr, nullptr);
+  notifier.OnWindowFocused(nullptr, nullptr);
+}
+
+INSTANTIATE_TEST_CASE_P(CrostiniUnsupportedActionNotifierTestCombination,
+                        CrostiniUnsupportedActionNotifierTest,
+                        ::testing::Combine(Bool(), Bool()));
+
+}  // namespace crostini
diff --git a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
index 54195d7..83f3c1b 100644
--- a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
+++ b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
@@ -35,6 +35,7 @@
 #include "base/time/time.h"
 #include "base/values.h"
 #include "build/build_config.h"
+#include "chrome/browser/browser_process.h"
 #include "chrome/browser/chromeos/arc/arc_util.h"
 #include "chrome/browser/chromeos/assistant/assistant_util.h"
 #include "chrome/browser/chromeos/crostini/crostini_export_import.h"
@@ -1258,6 +1259,28 @@
 }
 
 ///////////////////////////////////////////////////////////////////////////////
+// AutotestPrivateRegisterComponentFunction
+///////////////////////////////////////////////////////////////////////////////
+
+AutotestPrivateRegisterComponentFunction::
+    ~AutotestPrivateRegisterComponentFunction() = default;
+
+ExtensionFunction::ResponseAction
+AutotestPrivateRegisterComponentFunction::Run() {
+  std::unique_ptr<api::autotest_private::RegisterComponent::Params> params(
+      api::autotest_private::RegisterComponent::Params::Create(*args_));
+  EXTENSION_FUNCTION_VALIDATE(params);
+  DVLOG(1) << "AutotestPrivateRegisterComponentFunction " << params->name
+           << ", " << params->path;
+
+  g_browser_process->platform_part()
+      ->cros_component_manager()
+      ->RegisterCompatiblePath(params->name, base::FilePath(params->path));
+
+  return RespondNow(NoArguments());
+}
+
+///////////////////////////////////////////////////////////////////////////////
 // AutotestPrivateTakeScreenshotFunction
 ///////////////////////////////////////////////////////////////////////////////
 
diff --git a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.h b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.h
index 0235421..c2eb632 100644
--- a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.h
+++ b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.h
@@ -365,6 +365,16 @@
   void CrostiniImported(crostini::CrostiniResult);
 };
 
+class AutotestPrivateRegisterComponentFunction : public ExtensionFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("autotestPrivate.registerComponent",
+                             AUTOTESTPRIVATE_REGISTERCOMPONENT)
+
+ private:
+  ~AutotestPrivateRegisterComponentFunction() override;
+  ResponseAction Run() override;
+};
+
 class AutotestPrivateTakeScreenshotFunction : public ExtensionFunction {
  public:
   DECLARE_EXTENSION_FUNCTION("autotestPrivate.takeScreenshot",
diff --git a/chrome/browser/chromeos/extensions/login_screen/login_screen_storage/login_screen_storage_api.cc b/chrome/browser/chromeos/extensions/login_screen/login_screen_storage/login_screen_storage_api.cc
new file mode 100644
index 0000000..d604cf74
--- /dev/null
+++ b/chrome/browser/chromeos/extensions/login_screen/login_screen_storage/login_screen_storage_api.cc
@@ -0,0 +1,144 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/extensions/login_screen/login_screen_storage/login_screen_storage_api.h"
+
+#include "base/values.h"
+#include "chrome/common/extensions/api/login_screen_storage.h"
+#include "chromeos/dbus/session_manager/session_manager_client.h"
+#include "components/user_manager/user_manager.h"
+
+namespace login_screen_storage = extensions::api::login_screen_storage;
+
+namespace extensions {
+
+namespace {
+
+const char kPersistentDataKeyPrefix[] = "persistent_data_";
+const char kCredentialsKeyPrefix[] = "credentials_";
+
+}  // namespace
+
+LoginScreenStorageExtensionFunction::LoginScreenStorageExtensionFunction() =
+    default;
+LoginScreenStorageExtensionFunction::~LoginScreenStorageExtensionFunction() =
+    default;
+
+void LoginScreenStorageExtensionFunction::OnDataStored(
+    base::Optional<std::string> error) {
+  Respond(error ? Error(*error) : NoArguments());
+}
+
+void LoginScreenStorageExtensionFunction::OnDataRetrieved(
+    base::Optional<std::string> data,
+    base::Optional<std::string> error) {
+  if (error) {
+    Respond(Error(*error));
+    return;
+  }
+  Respond(OneArgument(data ? std::make_unique<base::Value>(*data) : nullptr));
+}
+
+LoginScreenStorageStorePersistentDataFunction::
+    LoginScreenStorageStorePersistentDataFunction() = default;
+LoginScreenStorageStorePersistentDataFunction::
+    ~LoginScreenStorageStorePersistentDataFunction() = default;
+
+ExtensionFunction::ResponseAction
+LoginScreenStorageStorePersistentDataFunction::Run() {
+  std::unique_ptr<login_screen_storage::StorePersistentData::Params> params =
+      login_screen_storage::StorePersistentData::Params::Create(*args_);
+  EXTENSION_FUNCTION_VALIDATE(params);
+  login_manager::LoginScreenStorageMetadata metadata;
+  metadata.set_clear_on_session_exit(false);
+  StoreDataForExtensions(std::move(params->extension_ids), metadata,
+                         params->data);
+  return RespondLater();
+}
+
+void LoginScreenStorageStorePersistentDataFunction::OnDataStored(
+    std::vector<std::string> extension_ids,
+    const login_manager::LoginScreenStorageMetadata& metadata,
+    const std::string& data,
+    base::Optional<std::string> error) {
+  if (error) {
+    Respond(Error(*error));
+    return;
+  }
+
+  if (extension_ids.empty()) {
+    Respond(NoArguments());
+    return;
+  }
+
+  StoreDataForExtensions(std::move(extension_ids), metadata, data);
+}
+
+void LoginScreenStorageStorePersistentDataFunction::StoreDataForExtensions(
+    std::vector<std::string> extension_ids,
+    const login_manager::LoginScreenStorageMetadata& metadata,
+    const std::string& data) {
+  if (extension_ids.empty())
+    return;
+
+  std::string extension_id = extension_ids.back();
+  extension_ids.pop_back();
+  chromeos::SessionManagerClient::Get()->LoginScreenStorageStore(
+      kPersistentDataKeyPrefix + extension_id, metadata, data,
+      base::BindOnce(
+          &LoginScreenStorageStorePersistentDataFunction::OnDataStored, this,
+          std::move(extension_ids), metadata, data));
+}
+
+LoginScreenStorageRetrievePersistentDataFunction::
+    LoginScreenStorageRetrievePersistentDataFunction() = default;
+LoginScreenStorageRetrievePersistentDataFunction::
+    ~LoginScreenStorageRetrievePersistentDataFunction() = default;
+
+ExtensionFunction::ResponseAction
+LoginScreenStorageRetrievePersistentDataFunction::Run() {
+  chromeos::SessionManagerClient::Get()->LoginScreenStorageRetrieve(
+      kPersistentDataKeyPrefix + extension_id(),
+      base::BindOnce(
+          &LoginScreenStorageRetrievePersistentDataFunction::OnDataRetrieved,
+          this));
+  return RespondLater();
+}
+
+LoginScreenStorageStoreCredentialsFunction::
+    LoginScreenStorageStoreCredentialsFunction() = default;
+LoginScreenStorageStoreCredentialsFunction::
+    ~LoginScreenStorageStoreCredentialsFunction() = default;
+
+ExtensionFunction::ResponseAction
+LoginScreenStorageStoreCredentialsFunction::Run() {
+  std::unique_ptr<login_screen_storage::StoreCredentials::Params> params =
+      login_screen_storage::StoreCredentials::Params::Create(*args_);
+  EXTENSION_FUNCTION_VALIDATE(params);
+  login_manager::LoginScreenStorageMetadata metadata;
+  metadata.set_clear_on_session_exit(true);
+  chromeos::SessionManagerClient::Get()->LoginScreenStorageStore(
+      kCredentialsKeyPrefix + params->extension_id, metadata,
+      params->credentials,
+      base::BindOnce(&LoginScreenStorageStoreCredentialsFunction::OnDataStored,
+                     this));
+  return RespondLater();
+}
+
+LoginScreenStorageRetrieveCredentialsFunction::
+    LoginScreenStorageRetrieveCredentialsFunction() = default;
+LoginScreenStorageRetrieveCredentialsFunction::
+    ~LoginScreenStorageRetrieveCredentialsFunction() = default;
+
+ExtensionFunction::ResponseAction
+LoginScreenStorageRetrieveCredentialsFunction::Run() {
+  chromeos::SessionManagerClient::Get()->LoginScreenStorageRetrieve(
+      kCredentialsKeyPrefix + extension_id(),
+      base::BindOnce(
+          &LoginScreenStorageRetrieveCredentialsFunction::OnDataRetrieved,
+          this));
+  return RespondLater();
+}
+
+}  // namespace extensions
diff --git a/chrome/browser/chromeos/extensions/login_screen/login_screen_storage/login_screen_storage_api.h b/chrome/browser/chromeos/extensions/login_screen/login_screen_storage/login_screen_storage_api.h
new file mode 100644
index 0000000..b78f87d8
--- /dev/null
+++ b/chrome/browser/chromeos/extensions/login_screen/login_screen_storage/login_screen_storage_api.h
@@ -0,0 +1,115 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_EXTENSIONS_LOGIN_SCREEN_LOGIN_SCREEN_STORAGE_LOGIN_SCREEN_STORAGE_API_H_
+#define CHROME_BROWSER_CHROMEOS_EXTENSIONS_LOGIN_SCREEN_LOGIN_SCREEN_STORAGE_LOGIN_SCREEN_STORAGE_API_H_
+
+#include "chromeos/dbus/login_manager/login_screen_storage.pb.h"
+#include "extensions/browser/extension_function.h"
+
+namespace extensions {
+
+// Provides common callback functions to return results from
+// 'LoginScreenStorageStore' and 'LoginScreenStorageRetrieve' D-Bus methods.
+class LoginScreenStorageExtensionFunction : public ExtensionFunction {
+ protected:
+  LoginScreenStorageExtensionFunction();
+  ~LoginScreenStorageExtensionFunction() override;
+
+  // When passed as a callback to the 'LoginScreenStorageStore' D-Bus method,
+  // returns its result to the calling extension.
+  void OnDataStored(base::Optional<std::string> error);
+
+  // When passed as a callback to the 'LoginScreenStorageRetrieve' D-Bus method,
+  // returns its result to the calling extension.
+  void OnDataRetrieved(base::Optional<std::string> data,
+                       base::Optional<std::string> error);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(LoginScreenStorageExtensionFunction);
+};
+
+class LoginScreenStorageStorePersistentDataFunction : public ExtensionFunction {
+ public:
+  LoginScreenStorageStorePersistentDataFunction();
+  DECLARE_EXTENSION_FUNCTION("loginScreenStorage.storePersistentData",
+                             LOGINSCREENSTORAGE_STOREPERSISTENTDATA)
+
+ protected:
+  ~LoginScreenStorageStorePersistentDataFunction() override;
+
+  // ExtensionFunction:
+  ResponseAction Run() override;
+
+ private:
+  // Called when data for one of the extension was stored, |extension_ids| is a
+  // list of the extensions that the data wasn't yet stored for.
+  void OnDataStored(std::vector<std::string> extension_ids,
+                    const login_manager::LoginScreenStorageMetadata& metadata,
+                    const std::string& data,
+                    base::Optional<std::string> error);
+
+  // Asynchronously stores data for every extension from |extension_ids|.
+  void StoreDataForExtensions(
+      std::vector<std::string> extension_ids,
+      const login_manager::LoginScreenStorageMetadata& metadata,
+      const std::string& data);
+
+  DISALLOW_COPY_AND_ASSIGN(LoginScreenStorageStorePersistentDataFunction);
+};
+
+class LoginScreenStorageRetrievePersistentDataFunction
+    : public LoginScreenStorageExtensionFunction {
+ public:
+  LoginScreenStorageRetrievePersistentDataFunction();
+  DECLARE_EXTENSION_FUNCTION("loginScreenStorage.retrievePersistentData",
+                             LOGINSCREENSTORAGE_RETRIEVEPERSISTENTDATA)
+
+ protected:
+  ~LoginScreenStorageRetrievePersistentDataFunction() override;
+
+  // ExtensionFunction:
+  ResponseAction Run() override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(LoginScreenStorageRetrievePersistentDataFunction);
+};
+
+class LoginScreenStorageStoreCredentialsFunction
+    : public LoginScreenStorageExtensionFunction {
+ public:
+  LoginScreenStorageStoreCredentialsFunction();
+  DECLARE_EXTENSION_FUNCTION("loginScreenStorage.storeCredentials",
+                             LOGINSCREENSTORAGE_STORECREDENTIALS)
+
+ protected:
+  ~LoginScreenStorageStoreCredentialsFunction() override;
+
+  // ExtensionFunction:
+  ResponseAction Run() override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(LoginScreenStorageStoreCredentialsFunction);
+};
+
+class LoginScreenStorageRetrieveCredentialsFunction
+    : public LoginScreenStorageExtensionFunction {
+ public:
+  LoginScreenStorageRetrieveCredentialsFunction();
+  DECLARE_EXTENSION_FUNCTION("loginScreenStorage.retrieveCredentials",
+                             LOGINSCREENSTORAGE_RETRIEVECREDENTIALS)
+
+ protected:
+  ~LoginScreenStorageRetrieveCredentialsFunction() override;
+
+  // ExtensionFunction:
+  ResponseAction Run() override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(LoginScreenStorageRetrieveCredentialsFunction);
+};
+
+}  // namespace extensions
+
+#endif  // CHROME_BROWSER_CHROMEOS_EXTENSIONS_LOGIN_SCREEN_LOGIN_SCREEN_STORAGE_LOGIN_SCREEN_STORAGE_API_H_
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
index e0c8697e..c5341f7 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
@@ -613,6 +613,7 @@
         // Zip tests times out too often on ASAN and DEBUG. crbug.com/936429
         // and crbug.com/944697
         ZipCase("dirContextMenuZip"),
+        ZipCase("dirEjectContextMenuZip"),
 #endif
         TestCase("dirContextMenuRecent"),
         TestCase("dirContextMenuMyFiles").EnableMyFilesVolume(),
diff --git a/chrome/browser/chromeos/file_manager/file_manager_jstest_base.cc b/chrome/browser/chromeos/file_manager/file_manager_jstest_base.cc
index 714c227..aa72418 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_jstest_base.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_jstest_base.cc
@@ -4,18 +4,126 @@
 
 #include "chrome/browser/chromeos/file_manager/file_manager_jstest_base.h"
 
+#include "base/lazy_instance.h"
+#include "base/memory/ref_counted_memory.h"
 #include "base/path_service.h"
+#include "base/task/post_task.h"
+#include "base/task/task_traits.h"
 #include "base/threading/thread_restrictions.h"
+#include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/test/base/test_chrome_web_ui_controller_factory.h"
 #include "chrome/test/base/ui_test_utils.h"
+#include "content/public/browser/url_data_source.h"
 #include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui_controller.h"
+#include "content/public/common/url_constants.h"
 #include "content/public/test/browser_test_utils.h"
 #include "net/base/filename_util.h"
 
+namespace {
+
+// URLDataSource for the test URL chrome://file_manager_test/. It reads files
+// directly from repository source.
+class TestFilesDataSource : public content::URLDataSource {
+ public:
+  TestFilesDataSource() {}
+  ~TestFilesDataSource() override {}
+
+ private:
+  // This has to match kTestResourceURL
+  std::string GetSource() override { return "file_manager_test"; }
+
+  void StartDataRequest(
+      const std::string& path,
+      const content::WebContents::Getter& wc_getter,
+      const content::URLDataSource::GotDataCallback& callback) override {
+    base::PostTaskWithTraits(
+        FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_BLOCKING},
+        base::BindOnce(&TestFilesDataSource::ReadFile, base::Unretained(this),
+                       path, callback));
+  }
+
+  void ReadFile(const std::string& path,
+                const content::URLDataSource::GotDataCallback& callback) {
+    if (source_root_.empty()) {
+      CHECK(base::PathService::Get(base::DIR_SOURCE_ROOT, &source_root_));
+    }
+    base::FilePath root =
+        source_root_.Append(FILE_PATH_LITERAL("ui/file_manager"));
+    std::string content;
+
+    base::FilePath file_path =
+        root.Append(base::FilePath::FromUTF8Unsafe(path));
+
+    // Do some basic validation of the file extension.
+    CHECK(file_path.Extension() == ".html" || file_path.Extension() == ".js" ||
+          file_path.Extension() == ".css")
+        << "chrome://file_manager_test/ only supports .html/.js/.css extension "
+           "files";
+
+    CHECK(base::PathExists(file_path)) << file_path.value();
+    CHECK(base::ReadFileToString(file_path, &content)) << file_path.value();
+
+    scoped_refptr<base::RefCountedString> response =
+        base::RefCountedString::TakeString(&content);
+    callback.Run(response.get());
+  }
+
+  // It currently only serves HTML/JS/CSS.
+  std::string GetMimeType(const std::string& path) override {
+    if (base::EndsWith(path, ".html", base::CompareCase::INSENSITIVE_ASCII)) {
+      return "text/html";
+    }
+
+    if (base::EndsWith(path, ".css", base::CompareCase::INSENSITIVE_ASCII)) {
+      return "text/css";
+    }
+
+    CHECK(base::EndsWith(path, ".js", base::CompareCase::INSENSITIVE_ASCII));
+    return "application/javascript";
+  }
+
+  // Root of repository source, where files are served directly from.
+  base::FilePath source_root_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestFilesDataSource);
+};
+
+// WebUIProvider to attach the URLDataSource for the test URL during tests.
+// Used to start the unittest from a chrome:// URL which allows unittest files
+// (HTML/JS/CSS) to load other resources from WebUI URLs chrome://*.
+class TestWebUIProvider
+    : public TestChromeWebUIControllerFactory::WebUIProvider {
+ public:
+  TestWebUIProvider() = default;
+  ~TestWebUIProvider() override = default;
+
+  std::unique_ptr<content::WebUIController> NewWebUI(content::WebUI* web_ui,
+                                                     const GURL& url) override {
+    content::URLDataSource::Add(Profile::FromWebUI(web_ui),
+                                std::make_unique<TestFilesDataSource>());
+    return std::make_unique<content::WebUIController>(web_ui);
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TestWebUIProvider);
+};
+
+base::LazyInstance<TestWebUIProvider>::DestructorAtExit test_webui_provider_ =
+    LAZY_INSTANCE_INITIALIZER;
+
+}  // namespace
+
 FileManagerJsTestBase::FileManagerJsTestBase(const base::FilePath& base_path)
     : base_path_(base_path) {}
 
+FileManagerJsTestBase::~FileManagerJsTestBase() {}
+
+const std::string FileManagerJsTestBase::kTestResourceURL =
+    content::GetWebUIURLString("file_manager_test");
+
 void FileManagerJsTestBase::RunTest(const base::FilePath& file) {
   base::FilePath root_path;
   ASSERT_TRUE(base::PathService::Get(base::DIR_SOURCE_ROOT, &root_path));
@@ -50,3 +158,33 @@
   ASSERT_TRUE(web_contents);
   EXPECT_TRUE(ExecuteWebUIResourceTest(web_contents, {}));
 }
+
+void FileManagerJsTestBase::SetUpOnMainThread() {
+  InProcessBrowserTest::SetUpOnMainThread();
+
+  content::WebUIControllerFactory::UnregisterFactoryForTesting(
+      ChromeWebUIControllerFactory::GetInstance());
+
+  webui_controller_factory_ =
+      std::make_unique<TestChromeWebUIControllerFactory>();
+  content::WebUIControllerFactory::RegisterFactory(
+      webui_controller_factory_.get());
+  webui_controller_factory_->AddFactoryOverride(GURL(kTestResourceURL).host(),
+                                                test_webui_provider_.Pointer());
+}
+
+void FileManagerJsTestBase::TearDownOnMainThread() {
+  InProcessBrowserTest::TearDownOnMainThread();
+
+  webui_controller_factory_->RemoveFactoryOverride(
+      GURL(kTestResourceURL).host());
+  content::WebUIControllerFactory::UnregisterFactoryForTesting(
+      webui_controller_factory_.get());
+
+  // This is needed to avoid a debug assert after the test completes, see stack
+  // trace in http://crrev.com/179347
+  content::WebUIControllerFactory::RegisterFactory(
+      ChromeWebUIControllerFactory::GetInstance());
+
+  webui_controller_factory_.reset();
+}
diff --git a/chrome/browser/chromeos/file_manager/file_manager_jstest_base.h b/chrome/browser/chromeos/file_manager/file_manager_jstest_base.h
index 979b23c..c219642 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_jstest_base.h
+++ b/chrome/browser/chromeos/file_manager/file_manager_jstest_base.h
@@ -9,10 +9,12 @@
 
 #include "base/files/file_path.h"
 #include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/test_chrome_web_ui_controller_factory.h"
 
 class FileManagerJsTestBase : public InProcessBrowserTest {
  protected:
   explicit FileManagerJsTestBase(const base::FilePath& base_path);
+  ~FileManagerJsTestBase() override;
 
   // Runs all test functions in |file|, waiting for them to complete.
   void RunTest(const base::FilePath& file);
@@ -21,9 +23,17 @@
   // |file|, relative to DIR_EXE/gen/base_path.
   void RunGeneratedTest(const std::string& file);
 
- private:
+  // Set up & tear down
+  void SetUpOnMainThread() override;
+  void TearDownOnMainThread() override;
+
+  // chrome://file_manager_test.
+  static const std::string kTestResourceURL;
+
   void RunTestImpl(const GURL& url);
 
+ private:
+  std::unique_ptr<TestChromeWebUIControllerFactory> webui_controller_factory_;
   base::FilePath base_path_;
 };
 
diff --git a/chrome/browser/chromeos/login/demo_mode/demo_extensions_external_loader.cc b/chrome/browser/chromeos/login/demo_mode/demo_extensions_external_loader.cc
index 17011169..0c09c388 100644
--- a/chrome/browser/chromeos/login/demo_mode/demo_extensions_external_loader.cc
+++ b/chrome/browser/chromeos/login/demo_mode/demo_extensions_external_loader.cc
@@ -97,6 +97,11 @@
         true /* always_check_updates */,
         false /* wait_for_cache_initialization */);
   }
+
+  // TODO(crbug.com/991453): In offline Demo Mode, this would overwrite the
+  // prefs from the Offline Demo Resources, so we don't call LoadApp() if the
+  // enrollment is offline. Instead, we should merge these prefs or treat the
+  // cache as a separate provider.
   external_cache_->UpdateExtensionsList(base::DictionaryValue::From(
       base::Value::ToUniquePtrValue(std::move(prefs))));
 }
diff --git a/chrome/browser/chromeos/login/demo_mode/demo_session.cc b/chrome/browser/chromeos/login/demo_mode/demo_session.cc
index fe666a0..16c053fb3 100644
--- a/chrome/browser/chromeos/login/demo_mode/demo_session.cc
+++ b/chrome/browser/chromeos/login/demo_mode/demo_session.cc
@@ -394,7 +394,8 @@
 void DemoSession::SetExtensionsExternalLoader(
     scoped_refptr<DemoExtensionsExternalLoader> extensions_external_loader) {
   extensions_external_loader_ = extensions_external_loader;
-  InstallAppFromUpdateUrl(GetScreensaverAppId());
+  if (!offline_enrolled_)
+    InstallAppFromUpdateUrl(GetScreensaverAppId());
 }
 
 void DemoSession::OverrideIgnorePinPolicyAppsForTesting(
diff --git a/chrome/browser/chromeos/login/saml/in_session_password_change_manager.cc b/chrome/browser/chromeos/login/saml/in_session_password_change_manager.cc
index 4a0b6a573..f4d7c43 100644
--- a/chrome/browser/chromeos/login/saml/in_session_password_change_manager.cc
+++ b/chrome/browser/chromeos/login/saml/in_session_password_change_manager.cc
@@ -7,6 +7,7 @@
 #include "ash/public/cpp/session/session_activation_observer.h"
 #include "ash/public/cpp/session/session_controller.h"
 #include "base/feature_list.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/task/post_task.h"
 #include "base/task/task_traits.h"
 #include "chrome/browser/browser_process.h"
@@ -28,6 +29,75 @@
 
 namespace {
 
+using PasswordSource = InSessionPasswordChangeManager::PasswordSource;
+
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused. This must be kept in sync with
+// SamlInSessionPasswordChangeEvent in tools/metrics/histogram/enums.xml
+enum class InSessionPasswordChangeEvent {
+  kManagerCreated = 0,
+  kNotified = 1,
+  kUrgentNotified = 2,
+  kNotifiedAlreadyExpired = 3,
+  kStartPasswordChange = 4,
+  kSamlPasswordChanged = 5,
+  kPasswordScrapeSuccess = 6,
+  kPasswordScrapePartial = 7,
+  kPasswordScrapeFailure = 8,
+  kCryptohomePasswordChangeSuccessScraped = 9,
+  kCryptohomePasswordChangeSuccessRetyped = 10,
+  kCryptohomePasswordChangeFailureScraped = 11,
+  kCryptohomePasswordChangeFailureRetyped = 12,
+  kFinishPasswordChange = 13,
+  kMaxValue = kFinishPasswordChange,
+};
+
+void RecordEvent(InSessionPasswordChangeEvent event) {
+  UMA_HISTOGRAM_ENUMERATION("ChromeOS.SAML.InSessionPasswordChangeEvent",
+                            event);
+}
+
+void RecordNotificationShown(bool is_expired, bool show_as_urgent) {
+  if (is_expired) {
+    RecordEvent(InSessionPasswordChangeEvent::kNotifiedAlreadyExpired);
+  } else if (show_as_urgent) {
+    RecordEvent(InSessionPasswordChangeEvent::kUrgentNotified);
+  } else {
+    RecordEvent(InSessionPasswordChangeEvent::kNotified);
+  }
+}
+
+void RecordScrapingResult(bool old_password_scraped,
+                          bool new_password_scraped) {
+  if (old_password_scraped && new_password_scraped) {
+    RecordEvent(InSessionPasswordChangeEvent::kPasswordScrapeSuccess);
+  } else if (old_password_scraped || new_password_scraped) {
+    RecordEvent(InSessionPasswordChangeEvent::kPasswordScrapePartial);
+  } else {
+    RecordEvent(InSessionPasswordChangeEvent::kPasswordScrapeFailure);
+  }
+}
+
+void RecordCryptohomePasswordChangeSuccess(PasswordSource password_source) {
+  if (password_source == PasswordSource::PASSWORDS_SCRAPED) {
+    RecordEvent(
+        InSessionPasswordChangeEvent::kCryptohomePasswordChangeSuccessScraped);
+  } else {
+    RecordEvent(
+        InSessionPasswordChangeEvent::kCryptohomePasswordChangeSuccessRetyped);
+  }
+}
+
+void RecordCryptohomePasswordChangeFailure(PasswordSource password_source) {
+  if (password_source == PasswordSource::PASSWORDS_SCRAPED) {
+    RecordEvent(
+        InSessionPasswordChangeEvent::kCryptohomePasswordChangeFailureScraped);
+  } else {
+    RecordEvent(
+        InSessionPasswordChangeEvent::kCryptohomePasswordChangeFailureRetyped);
+  }
+}
+
 InSessionPasswordChangeManager* g_test_instance = nullptr;
 
 // Traits for running RecheckPasswordExpiryTask.
@@ -94,6 +164,7 @@
     std::unique_ptr<InSessionPasswordChangeManager> manager =
         std::make_unique<InSessionPasswordChangeManager>(primary_profile);
     manager->MaybeShowExpiryNotification();
+    RecordEvent(InSessionPasswordChangeEvent::kManagerCreated);
     return manager;
   } else {
     // If the policy is disabled, clear the SAML password attributes.
@@ -181,6 +252,7 @@
     } else {
       ShowStandardExpiryNotification(time_until_expiry);
     }
+    RecordNotificationShown(is_expired, show_as_urgent);
 
     // We check again whether to reshow / update the notification after one day:
     recheck_task_.RecheckAfter(kOneDay);
@@ -230,7 +302,8 @@
 }
 
 void InSessionPasswordChangeManager::StartInSessionPasswordChange() {
-  NotifyObservers(START_SAML_IDP_PASSWORD_CHANGE);
+  RecordEvent(InSessionPasswordChangeEvent::kStartPasswordChange);
+  NotifyObservers(Event::START_SAML_IDP_PASSWORD_CHANGE);
   DismissExpiryNotification();
   PasswordChangeDialog::Show();
   ConfirmPasswordChangeDialog::Dismiss();
@@ -239,13 +312,17 @@
 void InSessionPasswordChangeManager::OnSamlPasswordChanged(
     const std::string& scraped_old_password,
     const std::string& scraped_new_password) {
-  NotifyObservers(START_SAML_IDP_PASSWORD_CHANGE);
+  RecordEvent(InSessionPasswordChangeEvent::kSamlPasswordChanged);
+  NotifyObservers(Event::SAML_IDP_PASSWORD_CHANGED);
 
   user_manager::UserManager::Get()->SaveForceOnlineSignin(
       primary_user_->GetAccountId(), true);
   DismissExpiryNotification();
   PasswordChangeDialog::Dismiss();
 
+  RecordScrapingResult(!scraped_old_password.empty(),
+                       !scraped_new_password.empty());
+
   const bool both_passwords_scraped =
       !scraped_old_password.empty() && !scraped_new_password.empty();
   if (both_passwords_scraped) {
@@ -256,7 +333,8 @@
     ConfirmPasswordChangeDialog::Show(scraped_old_password,
                                       scraped_new_password,
                                       /*show_spinner_initially=*/true);
-    ChangePassword(scraped_old_password, scraped_new_password);
+    ChangePassword(scraped_old_password, scraped_new_password,
+                   PasswordSource::PASSWORDS_SCRAPED);
   } else {
     // Failed to scrape passwords - prompt for passwords immediately.
     ConfirmPasswordChangeDialog::Show(scraped_old_password,
@@ -267,8 +345,10 @@
 
 void InSessionPasswordChangeManager::ChangePassword(
     const std::string& old_password,
-    const std::string& new_password) {
-  NotifyObservers(START_CRYPTOHOME_PASSWORD_CHANGE);
+    const std::string& new_password,
+    PasswordSource password_source) {
+  password_source_ = password_source;
+  NotifyObservers(Event::START_CRYPTOHOME_PASSWORD_CHANGE);
   UserContext user_context(*primary_user_);
   user_context.SetKey(Key(new_password));
   authenticator_->MigrateKey(user_context, old_password);
@@ -284,18 +364,21 @@
 
 void InSessionPasswordChangeManager::OnAuthFailure(const AuthFailure& error) {
   VLOG(1) << "Failed to change cryptohome password: " << error.GetErrorString();
-  NotifyObservers(CRYPTOHOME_PASSWORD_CHANGE_FAILURE);
+  RecordCryptohomePasswordChangeFailure(password_source_);
+  NotifyObservers(Event::CRYPTOHOME_PASSWORD_CHANGE_FAILURE);
 }
 
 void InSessionPasswordChangeManager::OnPasswordChangeDetected() {
   VLOG(1) << "Failed to change cryptohome password: PasswordChangeDetected";
-  NotifyObservers(CRYPTOHOME_PASSWORD_CHANGE_FAILURE);
+  RecordCryptohomePasswordChangeFailure(password_source_);
+  NotifyObservers(Event::CRYPTOHOME_PASSWORD_CHANGE_FAILURE);
 }
 
 void InSessionPasswordChangeManager::OnAuthSuccess(
     const UserContext& user_context) {
   VLOG(3) << "Cryptohome password is changed.";
-  NotifyObservers(CRYPTOHOME_PASSWORD_CHANGED);
+  RecordCryptohomePasswordChangeSuccess(password_source_);
+  NotifyObservers(Event::CRYPTOHOME_PASSWORD_CHANGED);
 
   user_manager::UserManager::Get()->SaveForceOnlineSignin(
       user_context.GetAccountId(), false);
@@ -312,6 +395,7 @@
   DismissExpiryNotification();
   PasswordChangeDialog::Dismiss();
   ConfirmPasswordChangeDialog::Dismiss();
+  RecordEvent(InSessionPasswordChangeEvent::kFinishPasswordChange);
 }
 
 void InSessionPasswordChangeManager::OnSessionActivated(bool activated) {
diff --git a/chrome/browser/chromeos/login/saml/in_session_password_change_manager.h b/chrome/browser/chromeos/login/saml/in_session_password_change_manager.h
index 281e05b..32d5ff64 100644
--- a/chrome/browser/chromeos/login/saml/in_session_password_change_manager.h
+++ b/chrome/browser/chromeos/login/saml/in_session_password_change_manager.h
@@ -62,7 +62,7 @@
                                        public ash::SessionActivationObserver {
  public:
   // Events in the in-session SAML password change flow.
-  enum Event {
+  enum class Event {
     // Dialog is open showing third-party IdP SAML password change page:
     START_SAML_IDP_PASSWORD_CHANGE,
     // Third party IdP SAML password is changed (but not cryptohome yet):
@@ -75,6 +75,12 @@
     CRYPTOHOME_PASSWORD_CHANGED,
   };
 
+  // How the passwords were able to be obtained.
+  enum class PasswordSource {
+    PASSWORDS_SCRAPED,  // Passwords were scraped during SAML password change.
+    PASSWORDS_RETYPED,  // Passwords had to be manually confirmed by user.
+  };
+
   // Observers of InSessionPasswordChangeManager are notified of certain events.
   class Observer : public base::CheckedObserver {
    public:
@@ -134,7 +140,8 @@
 
   // Change cryptohome password for primary user.
   void ChangePassword(const std::string& old_password,
-                      const std::string& new_password);
+                      const std::string& new_password,
+                      PasswordSource password_source);
 
   // Handle a failure to scrape the passwords during in-session password change,
   // by showing a dialog for the user to confirm their old + new password.
@@ -168,6 +175,7 @@
   scoped_refptr<CryptohomeAuthenticator> authenticator_;
   int urgent_warning_days_;
   bool renotify_on_unlock_ = false;
+  PasswordSource password_source_ = PasswordSource::PASSWORDS_SCRAPED;
 
   friend class InSessionPasswordChangeManagerTest;
 
diff --git a/chrome/browser/chromeos/release_notes/release_notes_storage.cc b/chrome/browser/chromeos/release_notes/release_notes_storage.cc
index 4238c43..38fac21 100644
--- a/chrome/browser/chromeos/release_notes/release_notes_storage.cc
+++ b/chrome/browser/chromeos/release_notes/release_notes_storage.cc
@@ -4,17 +4,20 @@
 
 #include "chrome/browser/chromeos/release_notes/release_notes_storage.h"
 
+#include "base/command_line.h"
 #include "base/version.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/channel_info.h"
+#include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
 #include "chromeos/login/login_state/login_state.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 #include "components/user_manager/user_manager.h"
 #include "components/version_info/version_info.h"
+#include "content/public/common/content_switches.h"
 
 namespace {
 
@@ -36,6 +39,17 @@
 ReleaseNotesStorage::~ReleaseNotesStorage() = default;
 
 bool ReleaseNotesStorage::ShouldNotify() {
+  // TODO: remove after fixing http://crbug.com/991767.
+  const base::CommandLine* current_command_line =
+      base::CommandLine::ForCurrentProcess();
+  const bool is_running_test =
+      current_command_line->HasSwitch(::switches::kTestName) ||
+      current_command_line->HasSwitch(::switches::kTestType);
+  if (is_running_test) {
+    DLOG(WARNING) << "Ignoring Release Notes Notification for test.";
+    return false;
+  }
+
   std::string user_email = profile_->GetProfileUserName();
   if (base::EndsWith(user_email, "@google.com",
                      base::CompareCase::INSENSITIVE_ASCII) ||
diff --git a/chrome/browser/extensions/api/image_writer_private/destroy_partitions_operation.cc b/chrome/browser/extensions/api/image_writer_private/destroy_partitions_operation.cc
index 6b229b0..8560050 100644
--- a/chrome/browser/extensions/api/image_writer_private/destroy_partitions_operation.cc
+++ b/chrome/browser/extensions/api/image_writer_private/destroy_partitions_operation.cc
@@ -9,7 +9,6 @@
 #include "chrome/browser/extensions/api/image_writer_private/destroy_partitions_operation.h"
 #include "chrome/browser/extensions/api/image_writer_private/error_messages.h"
 #include "content/public/browser/browser_thread.h"
-#include "services/service_manager/public/cpp/connector.h"
 
 namespace extensions {
 namespace image_writer {
@@ -21,12 +20,10 @@
 
 DestroyPartitionsOperation::DestroyPartitionsOperation(
     base::WeakPtr<OperationManager> manager,
-    std::unique_ptr<service_manager::Connector> connector,
     const ExtensionId& extension_id,
     const std::string& storage_unit_id,
     const base::FilePath& download_folder)
     : Operation(manager,
-                std::move(connector),
                 extension_id,
                 storage_unit_id,
                 download_folder) {}
diff --git a/chrome/browser/extensions/api/image_writer_private/destroy_partitions_operation.h b/chrome/browser/extensions/api/image_writer_private/destroy_partitions_operation.h
index 3373953c..c1fc6448 100644
--- a/chrome/browser/extensions/api/image_writer_private/destroy_partitions_operation.h
+++ b/chrome/browser/extensions/api/image_writer_private/destroy_partitions_operation.h
@@ -19,7 +19,6 @@
  public:
   DestroyPartitionsOperation(
       base::WeakPtr<OperationManager> manager,
-      std::unique_ptr<service_manager::Connector> connector,
       const ExtensionId& extension_id,
       const std::string& storage_unit_id,
       const base::FilePath& download_folder);
diff --git a/chrome/browser/extensions/api/image_writer_private/destroy_partitions_operation_unittest.cc b/chrome/browser/extensions/api/image_writer_private/destroy_partitions_operation_unittest.cc
index 1a495482..35ca237 100644
--- a/chrome/browser/extensions/api/image_writer_private/destroy_partitions_operation_unittest.cc
+++ b/chrome/browser/extensions/api/image_writer_private/destroy_partitions_operation_unittest.cc
@@ -9,7 +9,6 @@
 #include "chrome/browser/extensions/api/image_writer_private/error_messages.h"
 #include "chrome/browser/extensions/api/image_writer_private/test_utils.h"
 #include "chrome/test/base/testing_profile.h"
-#include "services/service_manager/public/cpp/connector.h"
 
 namespace extensions {
 namespace image_writer {
@@ -42,8 +41,7 @@
 
   scoped_refptr<DestroyPartitionsOperation> operation(
       new DestroyPartitionsOperation(
-          manager.AsWeakPtr(),
-          /*connector=*/nullptr, kDummyExtensionId,
+          manager.AsWeakPtr(), kDummyExtensionId,
           test_utils_.GetDevicePath().AsUTF8Unsafe(),
           base::FilePath(FILE_PATH_LITERAL("/var/tmp"))));
 
diff --git a/chrome/browser/extensions/api/image_writer_private/image_writer_utility_client.cc b/chrome/browser/extensions/api/image_writer_private/image_writer_utility_client.cc
index 03aff05..657ecfd 100644
--- a/chrome/browser/extensions/api/image_writer_private/image_writer_utility_client.cc
+++ b/chrome/browser/extensions/api/image_writer_private/image_writer_utility_client.cc
@@ -8,23 +8,26 @@
 #include "base/files/file_path.h"
 #include "base/location.h"
 #include "base/optional.h"
+#include "build/build_config.h"
 #include "chrome/grit/generated_resources.h"
-#include "chrome/services/removable_storage_writer/public/mojom/constants.mojom.h"
 #include "content/public/browser/browser_thread.h"
-#include "mojo/public/cpp/bindings/binding.h"
-#include "services/service_manager/public/cpp/connector.h"
+#include "content/public/browser/sandbox_type.h"
+#include "content/public/browser/service_process_host.h"
+#include "mojo/public/cpp/bindings/receiver.h"
 #include "ui/base/l10n/l10n_util.h"
 
 namespace extensions {
 namespace image_writer {
 
 namespace {
+
 ImageWriterUtilityClient::ImageWriterUtilityClientFactory*
     g_factory_for_testing = nullptr;
 
-void DeleteInterfacePtr(chrome::mojom::RemovableStorageWriterPtr writer_ptr) {
-  // Just let the parameters go out of scope so they are deleted.
+void DeleteRemote(mojo::Remote<chrome::mojom::RemovableStorageWriter> writer) {
+  // Just let the parameter go out of scope so it's deleted.
 }
+
 }  // namespace
 
 class ImageWriterUtilityClient::RemovableStorageWriterClientImpl
@@ -32,10 +35,11 @@
  public:
   RemovableStorageWriterClientImpl(
       ImageWriterUtilityClient* owner,
-      chrome::mojom::RemovableStorageWriterClientPtr* interface_ptr)
-      : binding_(this, mojo::MakeRequest(interface_ptr)),
+      mojo::PendingReceiver<chrome::mojom::RemovableStorageWriterClient>
+          receiver)
+      : receiver_(this, std::move(receiver)),
         image_writer_utility_client_(owner) {
-    binding_.set_connection_error_handler(
+    receiver_.set_disconnect_handler(
         base::BindOnce(&ImageWriterUtilityClient::OnConnectionError,
                        image_writer_utility_client_));
   }
@@ -55,7 +59,8 @@
     }
   }
 
-  mojo::Binding<chrome::mojom::RemovableStorageWriterClient> binding_;
+  mojo::Receiver<chrome::mojom::RemovableStorageWriterClient> receiver_;
+
   // |image_writer_utility_client_| owns |this|.
   ImageWriterUtilityClient* const image_writer_utility_client_;
 
@@ -63,29 +68,23 @@
 };
 
 ImageWriterUtilityClient::ImageWriterUtilityClient(
-    const scoped_refptr<base::SequencedTaskRunner>& task_runner,
-    std::unique_ptr<service_manager::Connector> connector)
-    : task_runner_(task_runner), connector_(std::move(connector)) {}
+    const scoped_refptr<base::SequencedTaskRunner>& task_runner)
+    : task_runner_(task_runner) {}
 
 ImageWriterUtilityClient::~ImageWriterUtilityClient() {
   // We could be running on a different TaskRunner (typically, the UI thread).
   // Post to be safe.
-  task_runner_->DeleteSoon(FROM_HERE, std::move(connector_));
-  task_runner_->PostTask(FROM_HERE,
-                         base::BindOnce(&DeleteInterfacePtr,
-                                        std::move(removable_storage_writer_)));
+  task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&DeleteRemote, std::move(removable_storage_writer_)));
 }
 
 // static
 scoped_refptr<ImageWriterUtilityClient> ImageWriterUtilityClient::Create(
-    const scoped_refptr<base::SequencedTaskRunner>& task_runner,
-    std::unique_ptr<service_manager::Connector> connector) {
-  // connector_ can be null in unit-tests.
-  DCHECK(!connector || !connector->IsBound());
+    const scoped_refptr<base::SequencedTaskRunner>& task_runner) {
   if (g_factory_for_testing)
     return g_factory_for_testing->Run();
-  return base::WrapRefCounted(
-      new ImageWriterUtilityClient(task_runner, std::move(connector)));
+  return base::WrapRefCounted(new ImageWriterUtilityClient(task_runner));
 }
 
 // static
@@ -108,11 +107,12 @@
 
   BindServiceIfNeeded();
 
-  chrome::mojom::RemovableStorageWriterClientPtr client;
+  mojo::PendingRemote<chrome::mojom::RemovableStorageWriterClient>
+      remote_client;
   removable_storage_writer_client_ =
-      std::make_unique<RemovableStorageWriterClientImpl>(this, &client);
-
-  removable_storage_writer_->Write(source, target, std::move(client));
+      std::make_unique<RemovableStorageWriterClientImpl>(
+          this, remote_client.InitWithNewPipeAndPassReceiver());
+  removable_storage_writer_->Write(source, target, std::move(remote_client));
 }
 
 void ImageWriterUtilityClient::Verify(const ProgressCallback& progress_callback,
@@ -129,11 +129,12 @@
 
   BindServiceIfNeeded();
 
-  chrome::mojom::RemovableStorageWriterClientPtr client;
+  mojo::PendingRemote<chrome::mojom::RemovableStorageWriterClient>
+      remote_client;
   removable_storage_writer_client_ =
-      std::make_unique<RemovableStorageWriterClientImpl>(this, &client);
-
-  removable_storage_writer_->Verify(source, target, std::move(client));
+      std::make_unique<RemovableStorageWriterClientImpl>(
+          this, remote_client.InitWithNewPipeAndPassReceiver());
+  removable_storage_writer_->Verify(source, target, std::move(remote_client));
 }
 
 void ImageWriterUtilityClient::Cancel(const CancelCallback& cancel_callback) {
@@ -157,9 +158,19 @@
   if (removable_storage_writer_)
     return;
 
-  connector_->BindInterface(chrome::mojom::kRemovableStorageWriterServiceName,
-                            mojo::MakeRequest(&removable_storage_writer_));
-  removable_storage_writer_.set_connection_error_handler(
+#if defined(OS_WIN)
+  constexpr auto kSandboxType =
+      service_manager::SANDBOX_TYPE_NO_SANDBOX_AND_ELEVATED_PRIVILEGES;
+#else
+  constexpr auto kSandboxType = service_manager::SANDBOX_TYPE_NO_SANDBOX;
+#endif
+  content::ServiceProcessHost::Launch(
+      removable_storage_writer_.BindNewPipeAndPassReceiver(),
+      content::ServiceProcessHost::Options()
+          .WithDisplayName(IDS_UTILITY_PROCESS_IMAGE_WRITER_NAME)
+          .WithSandboxType(kSandboxType)
+          .Pass());
+  removable_storage_writer_.set_disconnect_handler(
       base::BindOnce(&ImageWriterUtilityClient::OnConnectionError, this));
 }
 
diff --git a/chrome/browser/extensions/api/image_writer_private/image_writer_utility_client.h b/chrome/browser/extensions/api/image_writer_private/image_writer_utility_client.h
index 6ac29f5a..63c4548 100644
--- a/chrome/browser/extensions/api/image_writer_private/image_writer_utility_client.h
+++ b/chrome/browser/extensions/api/image_writer_private/image_writer_utility_client.h
@@ -16,10 +16,7 @@
 #include "base/sequence_checker.h"
 #include "base/sequenced_task_runner.h"
 #include "chrome/services/removable_storage_writer/public/mojom/removable_storage_writer.mojom.h"
-
-namespace service_manager {
-class Connector;
-}
+#include "mojo/public/cpp/bindings/remote.h"
 
 namespace extensions {
 namespace image_writer {
@@ -36,10 +33,8 @@
   using ImageWriterUtilityClientFactory =
       base::Callback<scoped_refptr<ImageWriterUtilityClient>()>;
 
-  // |connector| should be a fresh connector not yet bound to any thread.
   static scoped_refptr<ImageWriterUtilityClient> Create(
-      const scoped_refptr<base::SequencedTaskRunner>& task_runner,
-      std::unique_ptr<service_manager::Connector> connector);
+      const scoped_refptr<base::SequencedTaskRunner>& task_runner);
 
   static void SetFactoryForTesting(ImageWriterUtilityClientFactory* factory);
 
@@ -79,9 +74,8 @@
   friend class base::RefCountedThreadSafe<ImageWriterUtilityClient>;
   friend class ImageWriterUtilityClientTest;
 
-  ImageWriterUtilityClient(
-      const scoped_refptr<base::SequencedTaskRunner>& task_runner,
-      std::unique_ptr<service_manager::Connector> connector);
+  explicit ImageWriterUtilityClient(
+      const scoped_refptr<base::SequencedTaskRunner>& task_runner);
   virtual ~ImageWriterUtilityClient();
 
  private:
@@ -102,9 +96,7 @@
 
   scoped_refptr<base::SequencedTaskRunner> task_runner_;
 
-  std::unique_ptr<service_manager::Connector> connector_;
-
-  chrome::mojom::RemovableStorageWriterPtr removable_storage_writer_;
+  mojo::Remote<chrome::mojom::RemovableStorageWriter> removable_storage_writer_;
 
   std::unique_ptr<RemovableStorageWriterClientImpl>
       removable_storage_writer_client_;
diff --git a/chrome/browser/extensions/api/image_writer_private/image_writer_utility_client_browsertest.cc b/chrome/browser/extensions/api/image_writer_private/image_writer_utility_client_browsertest.cc
index 7447bbc..ff16813 100644
--- a/chrome/browser/extensions/api/image_writer_private/image_writer_utility_client_browsertest.cc
+++ b/chrome/browser/extensions/api/image_writer_private/image_writer_utility_client_browsertest.cc
@@ -19,8 +19,6 @@
 #include "chrome/test/base/in_process_browser_test.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
-#include "content/public/browser/system_connector.h"
-#include "services/service_manager/public/cpp/connector.h"
 
 namespace extensions {
 namespace image_writer {
@@ -102,16 +100,13 @@
   const std::string& error() const { return error_; }
 
  private:
-  void SetUpOnMainThread() override {
-    connector_ = content::GetSystemConnector()->Clone();
-  }
-
   void StartWriteTest() {
     DCHECK(IsRunningInCorrectSequence());
 
-    if (!image_writer_utility_client_)
+    if (!image_writer_utility_client_) {
       image_writer_utility_client_ =
-          new ImageWriterUtilityClient(GetTaskRunner(), std::move(connector_));
+          new ImageWriterUtilityClient(GetTaskRunner());
+    }
     success_ = false;
     progress_ = 0;
 
@@ -156,9 +151,10 @@
   void StartVerifyTest() {
     DCHECK(IsRunningInCorrectSequence());
 
-    if (!image_writer_utility_client_)
+    if (!image_writer_utility_client_) {
       image_writer_utility_client_ =
-          new ImageWriterUtilityClient(GetTaskRunner(), std::move(connector_));
+          new ImageWriterUtilityClient(GetTaskRunner());
+    }
     success_ = false;
     progress_ = 0;
 
@@ -255,7 +251,6 @@
   bool cancel_ = false;
   std::string error_;
   scoped_refptr<base::SequencedTaskRunner> task_runner_;
-  std::unique_ptr<service_manager::Connector> connector_;
 
   DISALLOW_COPY_AND_ASSIGN(ImageWriterUtilityClientTest);
 };
diff --git a/chrome/browser/extensions/api/image_writer_private/operation.cc b/chrome/browser/extensions/api/image_writer_private/operation.cc
index a3cd8085..2107847d 100644
--- a/chrome/browser/extensions/api/image_writer_private/operation.cc
+++ b/chrome/browser/extensions/api/image_writer_private/operation.cc
@@ -15,7 +15,6 @@
 #include "chrome/browser/extensions/api/image_writer_private/unzip_helper.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
-#include "services/service_manager/public/cpp/connector.h"
 
 namespace extensions {
 namespace image_writer {
@@ -29,7 +28,6 @@
 }  // namespace
 
 Operation::Operation(base::WeakPtr<OperationManager> manager,
-                     std::unique_ptr<service_manager::Connector> connector,
                      const ExtensionId& extension_id,
                      const std::string& device_path,
                      const base::FilePath& download_folder)
@@ -41,7 +39,6 @@
       device_path_(device_path),
 #endif
       temp_dir_(std::make_unique<base::ScopedTempDir>()),
-      connector_(std::move(connector)),
       stage_(image_writer_api::STAGE_UNKNOWN),
       progress_(0),
       download_folder_(download_folder),
@@ -50,9 +47,6 @@
 }
 
 Operation::~Operation() {
-  // The connector_ is bound to the |task_runner_| and must be deleted there.
-  task_runner_->DeleteSoon(FROM_HERE, std::move(connector_));
-
   // base::ScopedTempDir must be destroyed on a thread that allows blocking IO
   // because it will try delete the directory if a call to Delete() hasn't been
   // made or was unsuccessful.
@@ -205,9 +199,7 @@
 void Operation::StartUtilityClient() {
   DCHECK(IsRunningInCorrectSequence());
   if (!image_writer_client_.get()) {
-    // connector_ can be null in tests.
-    image_writer_client_ = ImageWriterUtilityClient::Create(
-        task_runner_, connector_ ? connector_->Clone() : nullptr);
+    image_writer_client_ = ImageWriterUtilityClient::Create(task_runner_);
     AddCleanUpFunction(base::BindOnce(&Operation::StopUtilityClient, this));
   }
 }
diff --git a/chrome/browser/extensions/api/image_writer_private/operation.h b/chrome/browser/extensions/api/image_writer_private/operation.h
index cfc95222..575d2c3 100644
--- a/chrome/browser/extensions/api/image_writer_private/operation.h
+++ b/chrome/browser/extensions/api/image_writer_private/operation.h
@@ -32,10 +32,6 @@
 class FilePath;
 }  // namespace base
 
-namespace service_manager {
-class Connector;
-}
-
 namespace extensions {
 namespace image_writer {
 
@@ -67,7 +63,6 @@
       base::OnceCallback<void(bool, const std::string&)>;
 
   Operation(base::WeakPtr<OperationManager> manager,
-            std::unique_ptr<service_manager::Connector> connector,
             const ExtensionId& extension_id,
             const std::string& device_path,
             const base::FilePath& download_folder);
@@ -219,9 +214,6 @@
   // Runs all cleanup functions.
   void CleanUp();
 
-  // Connector to the service manager. Used and deleted on |task_runner_|.
-  std::unique_ptr<service_manager::Connector> connector_;
-
   // |stage_| and |progress_| are owned by the FILE thread, use |SetStage| and
   // |SetProgress| to update.  Progress should be in the interval [0,100]
   image_writer_api::Stage stage_;
diff --git a/chrome/browser/extensions/api/image_writer_private/operation_manager.cc b/chrome/browser/extensions/api/image_writer_private/operation_manager.cc
index 3f5d95f..7343e74 100644
--- a/chrome/browser/extensions/api/image_writer_private/operation_manager.cc
+++ b/chrome/browser/extensions/api/image_writer_private/operation_manager.cc
@@ -20,12 +20,10 @@
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/storage_partition.h"
-#include "content/public/browser/system_connector.h"
 #include "extensions/browser/event_router.h"
 #include "extensions/browser/extension_host.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/notification_types.h"
-#include "services/service_manager/public/cpp/connector.h"
 
 #if defined(OS_CHROMEOS)
 #include "chrome/browser/chromeos/file_manager/path_util.h"
@@ -87,10 +85,10 @@
       ->GetURLLoaderFactoryForBrowserProcess()
       ->Clone(mojo::MakeRequest(&url_loader_factory_info));
 
-  scoped_refptr<Operation> operation(new WriteFromUrlOperation(
-      weak_factory_.GetWeakPtr(), CreateConnector(), extension_id,
-      std::move(url_loader_factory_info), url, hash, device_path,
-      GetAssociatedDownloadFolder()));
+  scoped_refptr<Operation> operation(
+      new WriteFromUrlOperation(weak_factory_.GetWeakPtr(), extension_id,
+                                std::move(url_loader_factory_info), url, hash,
+                                device_path, GetAssociatedDownloadFolder()));
   operations_[extension_id] = operation;
   operation->PostTask(base::BindOnce(&Operation::Start, operation));
 
@@ -114,9 +112,9 @@
     return;
   }
 
-  scoped_refptr<Operation> operation(new WriteFromFileOperation(
-      weak_factory_.GetWeakPtr(), CreateConnector(), extension_id, path,
-      device_path, GetAssociatedDownloadFolder()));
+  scoped_refptr<Operation> operation(
+      new WriteFromFileOperation(weak_factory_.GetWeakPtr(), extension_id, path,
+                                 device_path, GetAssociatedDownloadFolder()));
   operations_[extension_id] = operation;
   operation->PostTask(base::BindOnce(&Operation::Start, operation));
   std::move(callback).Run(true, "");
@@ -148,7 +146,7 @@
   }
 
   scoped_refptr<Operation> operation(new DestroyPartitionsOperation(
-      weak_factory_.GetWeakPtr(), CreateConnector(), extension_id, device_path,
+      weak_factory_.GetWeakPtr(), extension_id, device_path,
       GetAssociatedDownloadFolder()));
   operations_[extension_id] = operation;
   operation->PostTask(base::BindOnce(&Operation::Start, operation));
@@ -243,11 +241,6 @@
   DeleteOperation(extension->id());
 }
 
-std::unique_ptr<service_manager::Connector>
-OperationManager::CreateConnector() {
-  return content::GetSystemConnector()->Clone();
-}
-
 void OperationManager::Observe(int type,
                                const content::NotificationSource& source,
                                const content::NotificationDetails& details) {
diff --git a/chrome/browser/extensions/api/image_writer_private/operation_manager.h b/chrome/browser/extensions/api/image_writer_private/operation_manager.h
index e9c88e3..ae2a04f 100644
--- a/chrome/browser/extensions/api/image_writer_private/operation_manager.h
+++ b/chrome/browser/extensions/api/image_writer_private/operation_manager.h
@@ -86,10 +86,6 @@
   static BrowserContextKeyedAPIFactory<OperationManager>* GetFactoryInstance();
   static OperationManager* Get(content::BrowserContext* context);
 
- protected:
-  // Overridden in test.
-  virtual std::unique_ptr<service_manager::Connector> CreateConnector();
-
  private:
   static const char* service_name() {
     return "OperationManager";
diff --git a/chrome/browser/extensions/api/image_writer_private/operation_manager_unittest.cc b/chrome/browser/extensions/api/image_writer_private/operation_manager_unittest.cc
index b27b88e9..2ae9ff7 100644
--- a/chrome/browser/extensions/api/image_writer_private/operation_manager_unittest.cc
+++ b/chrome/browser/extensions/api/image_writer_private/operation_manager_unittest.cc
@@ -13,24 +13,12 @@
 #include "chrome/browser/extensions/api/image_writer_private/test_utils.h"
 #include "chrome/test/base/testing_profile.h"
 #include "extensions/browser/test_event_router.h"
-#include "services/service_manager/public/cpp/connector.h"
 
 namespace extensions {
 namespace image_writer {
 
 namespace {
 
-class TestOperationManager : public OperationManager {
- public:
-  explicit TestOperationManager(content::BrowserContext* context)
-      : OperationManager(context) {}
-
- private:
-  std::unique_ptr<service_manager::Connector> CreateConnector() override {
-    return nullptr;
-  }
-};
-
 class ImageWriterOperationManagerTest : public ImageWriterUnitTestBase {
  public:
   void StartCallback(bool success, const std::string& error) {
@@ -68,7 +56,7 @@
 };
 
 TEST_F(ImageWriterOperationManagerTest, WriteFromFile) {
-  TestOperationManager manager(&test_profile_);
+  OperationManager manager(&test_profile_);
 
   manager.StartWriteFromFile(
       kDummyExtensionId, test_utils_.GetImagePath(),
@@ -93,7 +81,7 @@
 }
 
 TEST_F(ImageWriterOperationManagerTest, DestroyPartitions) {
-  TestOperationManager manager(&test_profile_);
+  OperationManager manager(&test_profile_);
 
   manager.DestroyPartitions(
       kDummyExtensionId, test_utils_.GetDevicePath().AsUTF8Unsafe(),
diff --git a/chrome/browser/extensions/api/image_writer_private/operation_unittest.cc b/chrome/browser/extensions/api/image_writer_private/operation_unittest.cc
index 4cb1802..bec408f 100644
--- a/chrome/browser/extensions/api/image_writer_private/operation_unittest.cc
+++ b/chrome/browser/extensions/api/image_writer_private/operation_unittest.cc
@@ -16,7 +16,6 @@
 #include "chrome/test/base/testing_profile.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/test/test_browser_thread_bundle.h"
-#include "services/service_manager/public/cpp/connector.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/zlib/google/zip.h"
@@ -53,11 +52,7 @@
                    const ExtensionId& extension_id,
                    const std::string& device_path,
                    const base::FilePath& download_path)
-      : Operation(manager_,
-                  /*connector=*/nullptr,
-                  extension_id,
-                  device_path,
-                  download_path) {}
+      : Operation(manager_, extension_id, device_path, download_path) {}
 
   void StartImpl() override {}
 
diff --git a/chrome/browser/extensions/api/image_writer_private/test_utils.cc b/chrome/browser/extensions/api/image_writer_private/test_utils.cc
index 6a74545..17643fe1 100644
--- a/chrome/browser/extensions/api/image_writer_private/test_utils.cc
+++ b/chrome/browser/extensions/api/image_writer_private/test_utils.cc
@@ -14,7 +14,6 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
 #include "chrome/browser/extensions/api/image_writer_private/error_messages.h"
-#include "services/service_manager/public/cpp/connector.h"
 
 #if defined(OS_CHROMEOS)
 #include "chromeos/dbus/dbus_thread_manager.h"
@@ -92,11 +91,9 @@
     default;
 
 FakeImageWriterClient::FakeImageWriterClient()
-    : ImageWriterUtilityClient(
-          base::CreateSequencedTaskRunnerWithTraits(
-              {base::MayBlock(), base::TaskPriority::USER_VISIBLE,
-               base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN}),
-          /*connector=*/nullptr) {}
+    : ImageWriterUtilityClient(base::CreateSequencedTaskRunnerWithTraits(
+          {base::MayBlock(), base::TaskPriority::USER_VISIBLE,
+           base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})) {}
 FakeImageWriterClient::~FakeImageWriterClient() {}
 
 void FakeImageWriterClient::SimulateProgressAndCompletion(
diff --git a/chrome/browser/extensions/api/image_writer_private/write_from_file_operation.cc b/chrome/browser/extensions/api/image_writer_private/write_from_file_operation.cc
index 26b06b1..dfb46fe6 100644
--- a/chrome/browser/extensions/api/image_writer_private/write_from_file_operation.cc
+++ b/chrome/browser/extensions/api/image_writer_private/write_from_file_operation.cc
@@ -8,7 +8,6 @@
 #include "base/files/file_util.h"
 #include "chrome/browser/extensions/api/image_writer_private/error_messages.h"
 #include "content/public/browser/browser_thread.h"
-#include "services/service_manager/public/cpp/connector.h"
 
 namespace extensions {
 namespace image_writer {
@@ -17,16 +16,11 @@
 
 WriteFromFileOperation::WriteFromFileOperation(
     base::WeakPtr<OperationManager> manager,
-    std::unique_ptr<service_manager::Connector> connector,
     const ExtensionId& extension_id,
     const base::FilePath& user_file_path,
     const std::string& device_path,
     const base::FilePath& download_folder)
-    : Operation(manager,
-                std::move(connector),
-                extension_id,
-                device_path,
-                download_folder) {
+    : Operation(manager, extension_id, device_path, download_folder) {
   image_path_ = user_file_path;
 }
 
diff --git a/chrome/browser/extensions/api/image_writer_private/write_from_file_operation.h b/chrome/browser/extensions/api/image_writer_private/write_from_file_operation.h
index 5e6951e2..ef82e25 100644
--- a/chrome/browser/extensions/api/image_writer_private/write_from_file_operation.h
+++ b/chrome/browser/extensions/api/image_writer_private/write_from_file_operation.h
@@ -7,10 +7,6 @@
 
 #include "chrome/browser/extensions/api/image_writer_private/operation.h"
 
-namespace service_manager {
-class Connector;
-}
-
 namespace extensions {
 namespace image_writer {
 
@@ -18,7 +14,6 @@
 class WriteFromFileOperation : public Operation {
  public:
   WriteFromFileOperation(base::WeakPtr<OperationManager> manager,
-                         std::unique_ptr<service_manager::Connector> connector,
                          const ExtensionId& extension_id,
                          const base::FilePath& user_file_path,
                          const std::string& storage_unit_id,
diff --git a/chrome/browser/extensions/api/image_writer_private/write_from_file_operation_unittest.cc b/chrome/browser/extensions/api/image_writer_private/write_from_file_operation_unittest.cc
index 11af9bb..cc3d447 100644
--- a/chrome/browser/extensions/api/image_writer_private/write_from_file_operation_unittest.cc
+++ b/chrome/browser/extensions/api/image_writer_private/write_from_file_operation_unittest.cc
@@ -9,7 +9,6 @@
 #include "chrome/browser/extensions/api/image_writer_private/error_messages.h"
 #include "chrome/browser/extensions/api/image_writer_private/test_utils.h"
 #include "chrome/test/base/testing_profile.h"
-#include "services/service_manager/public/cpp/connector.h"
 
 namespace extensions {
 namespace image_writer {
@@ -42,8 +41,7 @@
 
 TEST_F(ImageWriterFromFileTest, InvalidFile) {
   scoped_refptr<WriteFromFileOperation> op = new WriteFromFileOperation(
-      manager_.AsWeakPtr(),
-      /*connector=*/nullptr, kDummyExtensionId, test_utils_.GetImagePath(),
+      manager_.AsWeakPtr(), kDummyExtensionId, test_utils_.GetImagePath(),
       test_utils_.GetDevicePath().AsUTF8Unsafe(),
       base::FilePath(FILE_PATH_LITERAL("/var/tmp")));
 
@@ -70,8 +68,7 @@
 #endif
 
   scoped_refptr<WriteFromFileOperation> op = new WriteFromFileOperation(
-      manager_.AsWeakPtr(),
-      /*connector=*/nullptr, kDummyExtensionId, test_utils_.GetImagePath(),
+      manager_.AsWeakPtr(), kDummyExtensionId, test_utils_.GetImagePath(),
       test_utils_.GetDevicePath().AsUTF8Unsafe(),
       base::FilePath(FILE_PATH_LITERAL("/var/tmp")));
   EXPECT_CALL(manager_,
diff --git a/chrome/browser/extensions/api/image_writer_private/write_from_url_operation.cc b/chrome/browser/extensions/api/image_writer_private/write_from_url_operation.cc
index 7c4669b4..fdebd68 100644
--- a/chrome/browser/extensions/api/image_writer_private/write_from_url_operation.cc
+++ b/chrome/browser/extensions/api/image_writer_private/write_from_url_operation.cc
@@ -12,7 +12,6 @@
 #include "net/url_request/url_fetcher.h"
 #include "services/network/public/cpp/resource_response.h"
 #include "services/network/public/cpp/simple_url_loader.h"
-#include "services/service_manager/public/cpp/connector.h"
 
 namespace extensions {
 namespace image_writer {
@@ -21,18 +20,13 @@
 
 WriteFromUrlOperation::WriteFromUrlOperation(
     base::WeakPtr<OperationManager> manager,
-    std::unique_ptr<service_manager::Connector> connector,
     const ExtensionId& extension_id,
     network::mojom::URLLoaderFactoryPtrInfo factory_info,
     GURL url,
     const std::string& hash,
     const std::string& device_path,
     const base::FilePath& download_folder)
-    : Operation(manager,
-                std::move(connector),
-                extension_id,
-                device_path,
-                download_folder),
+    : Operation(manager, extension_id, device_path, download_folder),
       url_loader_factory_ptr_info_(std::move(factory_info)),
       url_(url),
       hash_(hash),
diff --git a/chrome/browser/extensions/api/image_writer_private/write_from_url_operation.h b/chrome/browser/extensions/api/image_writer_private/write_from_url_operation.h
index 757fc07..617f609 100644
--- a/chrome/browser/extensions/api/image_writer_private/write_from_url_operation.h
+++ b/chrome/browser/extensions/api/image_writer_private/write_from_url_operation.h
@@ -25,7 +25,6 @@
 class WriteFromUrlOperation : public Operation {
  public:
   WriteFromUrlOperation(base::WeakPtr<OperationManager> manager,
-                        std::unique_ptr<service_manager::Connector> connector,
                         const ExtensionId& extension_id,
                         network::mojom::URLLoaderFactoryPtrInfo factory_info,
                         GURL url,
diff --git a/chrome/browser/extensions/api/image_writer_private/write_from_url_operation_unittest.cc b/chrome/browser/extensions/api/image_writer_private/write_from_url_operation_unittest.cc
index bcb23055..5c87a075f 100644
--- a/chrome/browser/extensions/api/image_writer_private/write_from_url_operation_unittest.cc
+++ b/chrome/browser/extensions/api/image_writer_private/write_from_url_operation_unittest.cc
@@ -14,7 +14,6 @@
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/storage_partition.h"
 #include "content/public/test/url_loader_interceptor.h"
-#include "services/service_manager/public/cpp/connector.h"
 
 namespace extensions {
 namespace image_writer {
@@ -44,7 +43,6 @@
       const std::string& hash,
       const std::string& storage_unit_id)
       : WriteFromUrlOperation(manager,
-                              /*connector=*/nullptr,
                               extension_id,
                               std::move(factory_info),
                               url,
diff --git a/chrome/browser/extensions/app_data_migrator_unittest.cc b/chrome/browser/extensions/app_data_migrator_unittest.cc
index e79ca1ef..b1af8ff 100644
--- a/chrome/browser/extensions/app_data_migrator_unittest.cc
+++ b/chrome/browser/extensions/app_data_migrator_unittest.cc
@@ -23,10 +23,11 @@
 #include "extensions/common/extension.h"
 #include "extensions/common/extension_builder.h"
 #include "extensions/common/manifest.h"
+#include "storage/browser/blob/blob_storage_context.h"
 #include "storage/browser/fileapi/file_system_context.h"
 #include "storage/browser/fileapi/file_system_operation_runner.h"
 #include "storage/browser/fileapi/file_system_url.h"
-#include "storage/browser/test/mock_blob_url_request_context.h"
+#include "storage/browser/test/mock_blob_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
@@ -56,8 +57,7 @@
 
     default_fs_context_ = default_partition_->GetFileSystemContext();
 
-    url_request_context_ = std::unique_ptr<content::MockBlobURLRequestContext>(
-        new content::MockBlobURLRequestContext());
+    blob_storage_context_ = std::make_unique<storage::BlobStorageContext>();
   }
 
   void TearDown() override {}
@@ -70,7 +70,7 @@
   ExtensionRegistry* registry_;
   storage::FileSystemContext* default_fs_context_;
   content::IndexedDBContext* idb_context_;
-  std::unique_ptr<content::MockBlobURLRequestContext> url_request_context_;
+  std::unique_ptr<storage::BlobStorageContext> blob_storage_context_;
 };
 
 scoped_refptr<const Extension> GetTestExtension(bool platform_app) {
@@ -141,7 +141,7 @@
   content::RunAllTasksUntilIdle();
 }
 
-void GenerateTestFiles(content::MockBlobURLRequestContext* url_request_context,
+void GenerateTestFiles(storage::BlobStorageContext* blob_storage_context,
                        const Extension* ext,
                        storage::FileSystemContext* fs_context,
                        Profile* profile) {
@@ -160,7 +160,7 @@
       fs_context->CreateCrackedFileSystemURL(
           extension_url, storage::kFileSystemTypePersistent, path);
 
-  content::ScopedTextBlob blob1(*url_request_context, "blob-id:success1",
+  storage::ScopedTextBlob blob1(blob_storage_context, "blob-id:success1",
                                 "Hello, world!\n");
 
   fs_context->operation_runner()->CreateFile(fs_temp_url, false,
@@ -259,7 +259,7 @@
   scoped_refptr<const Extension> old_ext = GetTestExtension(false);
   scoped_refptr<const Extension> new_ext = GetTestExtension(true);
 
-  GenerateTestFiles(url_request_context_.get(), old_ext.get(),
+  GenerateTestFiles(blob_storage_context_.get(), old_ext.get(),
                     default_fs_context_, profile_.get());
 
   migrator_->DoMigrationAndReply(old_ext.get(), new_ext.get(),
diff --git a/chrome/browser/extensions/extension_sync_service.h b/chrome/browser/extensions/extension_sync_service.h
index 1654082..6557a8a 100644
--- a/chrome/browser/extensions/extension_sync_service.h
+++ b/chrome/browser/extensions/extension_sync_service.h
@@ -77,7 +77,8 @@
   void DeleteThemeDoNotUse(const extensions::Extension& theme);
 
  private:
-  FRIEND_TEST_ALL_PREFIXES(TwoClientAppsSyncTest, UnexpectedLaunchType);
+  FRIEND_TEST_ALL_PREFIXES(TwoClientExtensionAppsSyncTest,
+                           UnexpectedLaunchType);
   FRIEND_TEST_ALL_PREFIXES(ExtensionDisabledGlobalErrorTest,
                            HigherPermissionsFromSync);
 
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index e5b50df..7255f2a 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -244,12 +244,12 @@
   {
     "name": "autofill-enable-local-card-migration-for-non-sync-user",
     "owners": [ "siyua" ],
-    "expiry_milestone": 76
+    "expiry_milestone": 83
   },
   {
     "name": "autofill-enable-toolbar-status-chip",
     "owners": [ "siyua" ],
-    "expiry_milestone": 77
+    "expiry_milestone": 83
   },
   {
     "name": "autofill-enforce-min-required-fields-for-heuristics",
@@ -279,16 +279,14 @@
   {
     "name": "autofill-no-local-save-on-unmask-success",
     "owners": [ "jsaul@google.com" ],
-    // Currently unclear if this is launching independently with M75 or
-    // alongside the Autofill Auth Project, which could be as late as ~Q3.
-    "expiry_milestone": 79
+    // Must wait for the Autofill Auth Project to be launched.
+    "expiry_milestone": 84
   },
   {
     "name": "autofill-no-local-save-on-upload-success",
     "owners": [ "jsaul@google.com", "annelim@google.com" ],
-    // Currently unclear if this is launching independently with M75 or
-    // alongside the Autofill Auth Project, which could be as late as ~Q3.
-    "expiry_milestone": 79
+    // Must wait for the Autofill Auth Project to be launched.
+    "expiry_milestone": 84
   },
   {
     "name": "autofill-profile-client-validation",
@@ -893,12 +891,12 @@
   {
     "name": "enable-autofill-credit-card-ablation-experiment",
     "owners": [ "dlkumar@google.com" ],
-    "expiry_milestone": 76
+    "expiry_milestone": 88
   },
   {
     "name": "enable-autofill-credit-card-authentication",
     "owners": [ "jsaul@google.com", "manasverma@google.com" ],
-    "expiry_milestone": 79
+    "expiry_milestone": 84
   },
   {
     "name": "enable-autofill-credit-card-upload",
@@ -911,17 +909,17 @@
   {
     "name": "enable-autofill-credit-card-upload-editable-cardholder-name",
     "owners": [ "jsaul@google.com" ],
-    "expiry_milestone": 76
+    "expiry_milestone": 83
   },
   {
     "name": "enable-autofill-credit-card-upload-editable-expiration-date",
     "owners": [ "hozhng@google.com", "jsaul@google.com" ],
-    "expiry_milestone": 76
+    "expiry_milestone": 83
   },
   {
     "name": "enable-autofill-credit-card-upload-feedback",
     "owners": [ "siyua@chromium.org, payments-autofill-team@google.com" ],
-    "expiry_milestone": 79
+    "expiry_milestone": 84
   },
   {
     "name": "enable-autofill-do-not-migrate-unsupported-local-cards",
@@ -931,17 +929,17 @@
   {
     "name": "enable-autofill-do-not-upload-save-unsupported-cards",
     "owners": [ "annelim@google.com", "jsaul@google.com" ],
-    "expiry_milestone": 76
+    "expiry_milestone": 79
   },
   {
     "name": "enable-autofill-import-dynamic-forms",
     "owners": [ "hozhng@google.com", "jiahuiguo@google.com" ],
-    "expiry_milestone": 76
+    "expiry_milestone": 79
   },
   {
     "name": "enable-autofill-local-card-migration-uses-strike-system-v2",
     "owners": [ "annelim@google.com", "jsaul@google.com" , "jiahuiguo@google.com"],
-    "expiry_milestone": 76
+    "expiry_milestone": 84
   },
   {
     "name": "enable-autofill-manual-fallback",
@@ -956,7 +954,7 @@
   {
     "name": "enable-autofill-save-credit-card-uses-improved-messaging",
     "owners": [ "siyua@chromium.org", "payments-autofill-team@google.com" ],
-    "expiry_milestone": 78
+    "expiry_milestone": 80
   },
   {
     "name": "enable-autofill-send-experiment-ids-in-payments-rpcs",
@@ -1100,7 +1098,7 @@
   {
     "name": "enable-experimental-accessibility-language-detection",
     "owners": [ "chrishall", "//ui/accessibility/OWNERS" ],
-    "expiry_milestone": 76
+    "expiry_milestone": 80
   },
   {
     "name": "enable-experimental-accessibility-switch-access",
diff --git a/chrome/browser/guest_view/web_view/chrome_web_view_permission_helper_delegate.cc b/chrome/browser/guest_view/web_view/chrome_web_view_permission_helper_delegate.cc
index e84f6c4..699043a 100644
--- a/chrome/browser/guest_view/web_view/chrome_web_view_permission_helper_delegate.cc
+++ b/chrome/browser/guest_view/web_view/chrome_web_view_permission_helper_delegate.cc
@@ -222,24 +222,22 @@
 void ChromeWebViewPermissionHelperDelegate::RequestFileSystemPermission(
     const GURL& url,
     bool allowed_by_default,
-    const base::Callback<void(bool)>& callback) {
+    base::OnceCallback<void(bool)> callback) {
   base::DictionaryValue request_info;
   request_info.SetString(guest_view::kUrl, url.spec());
   web_view_permission_helper()->RequestPermission(
-      WEB_VIEW_PERMISSION_TYPE_FILESYSTEM,
-      request_info,
-      base::Bind(&ChromeWebViewPermissionHelperDelegate::
-                     OnFileSystemPermissionResponse,
-                 weak_factory_.GetWeakPtr(),
-                 callback),
+      WEB_VIEW_PERMISSION_TYPE_FILESYSTEM, request_info,
+      base::BindOnce(&ChromeWebViewPermissionHelperDelegate::
+                         OnFileSystemPermissionResponse,
+                     weak_factory_.GetWeakPtr(), std::move(callback)),
       allowed_by_default);
 }
 
 void ChromeWebViewPermissionHelperDelegate::OnFileSystemPermissionResponse(
-    const base::Callback<void(bool)>& callback,
+    base::OnceCallback<void(bool)> callback,
     bool allow,
     const std::string& user_input) {
-  callback.Run(allow && web_view_guest()->attached());
+  std::move(callback).Run(allow && web_view_guest()->attached());
 }
 
 void ChromeWebViewPermissionHelperDelegate::FileSystemAccessedAsync(
@@ -249,15 +247,11 @@
     const GURL& url,
     bool blocked_by_policy) {
   RequestFileSystemPermission(
-      url,
-      !blocked_by_policy,
-      base::Bind(&ChromeWebViewPermissionHelperDelegate::
-                     FileSystemAccessedAsyncResponse,
-                 weak_factory_.GetWeakPtr(),
-                 render_process_id,
-                 render_frame_id,
-                 request_id,
-                 url));
+      url, !blocked_by_policy,
+      base::BindOnce(&ChromeWebViewPermissionHelperDelegate::
+                         FileSystemAccessedAsyncResponse,
+                     weak_factory_.GetWeakPtr(), render_process_id,
+                     render_frame_id, request_id, url));
 }
 
 void ChromeWebViewPermissionHelperDelegate::FileSystemAccessedAsyncResponse(
diff --git a/chrome/browser/guest_view/web_view/chrome_web_view_permission_helper_delegate.h b/chrome/browser/guest_view/web_view/chrome_web_view_permission_helper_delegate.h
index 1f9cc1b..72c8659 100644
--- a/chrome/browser/guest_view/web_view/chrome_web_view_permission_helper_delegate.h
+++ b/chrome/browser/guest_view/web_view/chrome_web_view_permission_helper_delegate.h
@@ -43,7 +43,7 @@
   void RequestFileSystemPermission(
       const GURL& url,
       bool allowed_by_default,
-      const base::Callback<void(bool)>& callback) override;
+      base::OnceCallback<void(bool)> callback) override;
   void FileSystemAccessedAsync(int render_process_id,
                                int render_frame_id,
                                int request_id,
@@ -78,10 +78,9 @@
       bool allow,
       const std::string& user_input);
 
-  void OnFileSystemPermissionResponse(
-      const base::Callback<void(bool)>& callback,
-      bool allow,
-      const std::string& user_input);
+  void OnFileSystemPermissionResponse(base::OnceCallback<void(bool)> callback,
+                                      bool allow,
+                                      const std::string& user_input);
 
   void OnDownloadPermissionResponse(base::OnceCallback<void(bool)> callback,
                                     bool allow,
diff --git a/chrome/browser/media_galleries/fileapi/supported_audio_video_checker.cc b/chrome/browser/media_galleries/fileapi/supported_audio_video_checker.cc
index a6b033e3..4d26c5d6 100644
--- a/chrome/browser/media_galleries/fileapi/supported_audio_video_checker.cc
+++ b/chrome/browser/media_galleries/fileapi/supported_audio_video_checker.cc
@@ -22,9 +22,7 @@
 #include "chrome/services/media_gallery_util/public/cpp/safe_audio_video_checker.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
-#include "content/public/browser/system_connector.h"
 #include "net/base/mime_util.h"
-#include "services/service_manager/public/cpp/connector.h"
 #include "third_party/blink/public/common/mime_util/mime_util.h"
 
 namespace {
@@ -79,9 +77,11 @@
   DCHECK(callback_.is_null());
   callback_ = result_callback;
 
-  base::PostTask(
-      FROM_HERE, {content::BrowserThread::UI},
-      base::BindOnce(&SupportedAudioVideoChecker::RetrieveConnectorOnUIThread,
+  base::PostTaskAndReplyWithResult(
+      FROM_HERE,
+      {base::ThreadPool(), base::MayBlock(), base::TaskPriority::USER_VISIBLE},
+      base::BindOnce(&OpenBlocking, path_),
+      base::BindOnce(&SupportedAudioVideoChecker::OnFileOpen,
                      weak_factory_.GetWeakPtr()));
 }
 
@@ -89,45 +89,14 @@
     const base::FilePath& path)
     : path_(path) {}
 
-// static
-void SupportedAudioVideoChecker::RetrieveConnectorOnUIThread(
-    base::WeakPtr<SupportedAudioVideoChecker> this_ptr) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  // We need a fresh connector so that we can use it on the IO thread. It has
-  // to be retrieved from the UI thread. We must use static method and pass a
-  // WeakPtr around as WeakPtrs are not thread-safe.
-  base::PostTask(
-      FROM_HERE, {content::BrowserThread::IO},
-      base::BindOnce(&SupportedAudioVideoChecker::OnConnectorRetrieved,
-                     this_ptr, content::GetSystemConnector()->Clone()));
-}
-
-// static
-void SupportedAudioVideoChecker::OnConnectorRetrieved(
-    base::WeakPtr<SupportedAudioVideoChecker> this_ptr,
-    std::unique_ptr<service_manager::Connector> connector) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-  if (!this_ptr)
-    return;
-
-  base::PostTaskAndReplyWithResult(
-      FROM_HERE,
-      {base::ThreadPool(), base::MayBlock(), base::TaskPriority::USER_VISIBLE},
-      base::BindOnce(&OpenBlocking, this_ptr->path_),
-      base::BindOnce(&SupportedAudioVideoChecker::OnFileOpen, this_ptr,
-                     std::move(connector)));
-}
-
-void SupportedAudioVideoChecker::OnFileOpen(
-    std::unique_ptr<service_manager::Connector> connector,
-    base::File file) {
+void SupportedAudioVideoChecker::OnFileOpen(base::File file) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
   if (!file.IsValid()) {
     callback_.Run(base::File::FILE_ERROR_SECURITY);
     return;
   }
 
-  safe_checker_ = std::make_unique<SafeAudioVideoChecker>(
-      std::move(file), callback_, std::move(connector));
+  safe_checker_ =
+      std::make_unique<SafeAudioVideoChecker>(std::move(file), callback_);
   safe_checker_->Start();
 }
diff --git a/chrome/browser/media_galleries/fileapi/supported_audio_video_checker.h b/chrome/browser/media_galleries/fileapi/supported_audio_video_checker.h
index 9b550e2..da5107e7 100644
--- a/chrome/browser/media_galleries/fileapi/supported_audio_video_checker.h
+++ b/chrome/browser/media_galleries/fileapi/supported_audio_video_checker.h
@@ -16,10 +16,6 @@
 class MediaFileValidatorFactory;
 class SafeAudioVideoChecker;
 
-namespace service_manager {
-class Connector;
-}
-
 // Uses SafeAudioVideoChecker to validate supported audio and video files in
 // the utility process and then uses AVScanningFileValidator to ask the OS to
 // virus scan them. The entire file is not decoded so a positive result from
@@ -37,15 +33,7 @@
 
   explicit SupportedAudioVideoChecker(const base::FilePath& file);
 
-  static void RetrieveConnectorOnUIThread(
-      base::WeakPtr<SupportedAudioVideoChecker> this_ptr);
-
-  static void OnConnectorRetrieved(
-      base::WeakPtr<SupportedAudioVideoChecker> this_ptr,
-      std::unique_ptr<service_manager::Connector> connector);
-
-  void OnFileOpen(std::unique_ptr<service_manager::Connector> connector,
-                  base::File file);
+  void OnFileOpen(base::File file);
 
   base::FilePath path_;
   storage::CopyOrMoveFileValidator::ResultCallback callback_;
diff --git a/chrome/browser/printing/pdf_nup_converter_client.cc b/chrome/browser/printing/pdf_nup_converter_client.cc
index 7f1bc0b..a251fe6 100644
--- a/chrome/browser/printing/pdf_nup_converter_client.cc
+++ b/chrome/browser/printing/pdf_nup_converter_client.cc
@@ -7,10 +7,8 @@
 #include <utility>
 
 #include "base/bind.h"
-#include "chrome/services/printing/public/mojom/constants.mojom.h"
+#include "chrome/browser/printing/printing_service.h"
 #include "chrome/services/printing/public/mojom/pdf_nup_converter.mojom.h"
-#include "content/public/browser/system_connector.h"
-#include "services/service_manager/public/cpp/connector.h"
 
 namespace printing {
 
@@ -77,11 +75,9 @@
 
 mojom::PdfNupConverterPtr
 PdfNupConverterClient::CreatePdfNupConverterRequest() {
-  if (!connector_)
-    connector_ = content::GetSystemConnector()->Clone();
   mojom::PdfNupConverterPtr pdf_nup_converter;
-  connector_->BindInterface(printing::mojom::kChromePrintingServiceName,
-                            &pdf_nup_converter);
+  GetPrintingService()->BindPdfNupConverter(
+      mojo::MakeRequest(&pdf_nup_converter));
   pdf_nup_converter->SetWebContentsURL(web_contents_->GetLastCommittedURL());
   return pdf_nup_converter;
 }
diff --git a/chrome/browser/printing/pdf_to_emf_converter.cc b/chrome/browser/printing/pdf_to_emf_converter.cc
index a6f2dcbb..7f8ced8 100644
--- a/chrome/browser/printing/pdf_to_emf_converter.cc
+++ b/chrome/browser/printing/pdf_to_emf_converter.cc
@@ -24,15 +24,13 @@
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "base/threading/thread_restrictions.h"
 #include "base/threading/thread_task_runner_handle.h"
-#include "chrome/services/printing/public/mojom/constants.mojom.h"
+#include "chrome/browser/printing/printing_service.h"
 #include "chrome/services/printing/public/mojom/pdf_to_emf_converter.mojom.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/child_process_data.h"
-#include "content/public/browser/system_connector.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "printing/emf_win.h"
 #include "printing/pdf_render_settings.h"
-#include "services/service_manager/public/cpp/connector.h"
 
 using content::BrowserThread;
 
@@ -272,9 +270,8 @@
 
   memcpy(memory.mapping.memory(), data->front(), data->size());
 
-  content::GetSystemConnector()->BindInterface(
-      printing::mojom::kChromePrintingServiceName,
-      &pdf_to_emf_converter_factory_);
+  GetPrintingService()->BindPdfToEmfConverterFactory(
+      mojo::MakeRequest(&pdf_to_emf_converter_factory_));
   pdf_to_emf_converter_factory_.set_connection_error_handler(base::BindOnce(
       &PdfConverterImpl::OnFailed, weak_ptr_factory_.GetWeakPtr(),
       std::string("Connection to PdfToEmfConverterFactory error.")));
diff --git a/chrome/browser/printing/printing_service.cc b/chrome/browser/printing/printing_service.cc
new file mode 100644
index 0000000..9b0f43d
--- /dev/null
+++ b/chrome/browser/printing/printing_service.cc
@@ -0,0 +1,32 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/printing/printing_service.h"
+
+#include "base/no_destructor.h"
+#include "chrome/grit/generated_resources.h"
+#include "content/public/browser/service_process_host.h"
+
+const mojo::Remote<printing::mojom::PrintingService>& GetPrintingService() {
+  static base::NoDestructor<mojo::Remote<printing::mojom::PrintingService>>
+      remote;
+  if (!*remote) {
+    content::ServiceProcessHost::Launch(
+        remote->BindNewPipeAndPassReceiver(),
+        content::ServiceProcessHost::Options()
+            .WithDisplayName(IDS_UTILITY_PROCESS_PRINTING_SERVICE_NAME)
+            .WithSandboxType(service_manager::SANDBOX_TYPE_UTILITY)
+            .Pass());
+
+    // Ensure that if the interface is ever disconnected (e.g. the service
+    // process crashes) or goes idle for a short period of time -- meaning there
+    // are no in-flight messages and no other interfaces bound through this
+    // one -- then we will reset |remote|, causing the service process to be
+    // terminated if it isn't already.
+    remote->reset_on_disconnect();
+    remote->reset_on_idle_timeout(base::TimeDelta::FromSeconds(5));
+  }
+
+  return *remote;
+}
diff --git a/chrome/browser/printing/printing_service.h b/chrome/browser/printing/printing_service.h
new file mode 100644
index 0000000..59529e75
--- /dev/null
+++ b/chrome/browser/printing/printing_service.h
@@ -0,0 +1,15 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_PRINTING_PRINTING_SERVICE_H_
+#define CHROME_BROWSER_PRINTING_PRINTING_SERVICE_H_
+
+#include "chrome/services/printing/public/mojom/printing_service.mojom.h"
+#include "mojo/public/cpp/bindings/remote.h"
+
+// Acquires a remote handle to the sandboxed Printing Service
+// instance, launching a process to host the service if necessary.
+const mojo::Remote<printing::mojom::PrintingService>& GetPrintingService();
+
+#endif  // CHROME_BROWSER_PRINTING_PRINTING_SERVICE_H_
diff --git a/chrome/browser/printing/pwg_raster_converter.cc b/chrome/browser/printing/pwg_raster_converter.cc
index 5a50534..bf25bc4 100644
--- a/chrome/browser/printing/pwg_raster_converter.cc
+++ b/chrome/browser/printing/pwg_raster_converter.cc
@@ -14,18 +14,16 @@
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/metrics/histogram_macros.h"
-#include "chrome/services/printing/public/mojom/constants.mojom.h"
+#include "chrome/browser/printing/printing_service.h"
 #include "chrome/services/printing/public/mojom/pdf_to_pwg_raster_converter.mojom.h"
 #include "components/cloud_devices/common/cloud_device_description.h"
 #include "components/cloud_devices/common/printer_description.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/child_process_data.h"
-#include "content/public/browser/system_connector.h"
 #include "mojo/public/cpp/system/platform_handle.h"
 #include "printing/pdf_render_settings.h"
 #include "printing/pwg_raster_settings.h"
 #include "printing/units.h"
-#include "services/service_manager/public/cpp/connector.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/size.h"
 
@@ -80,10 +78,8 @@
 
   callback_ = std::move(callback);
 
-  content::GetSystemConnector()->BindInterface(
-      printing::mojom::kChromePrintingServiceName,
-      &pdf_to_pwg_raster_converter_ptr_);
-
+  GetPrintingService()->BindPdfToPwgRasterConverter(
+      mojo::MakeRequest(&pdf_to_pwg_raster_converter_ptr_));
   pdf_to_pwg_raster_converter_ptr_.set_connection_error_handler(
       base::BindOnce(&PwgRasterConverterHelper::RunCallback, this,
                      base::ReadOnlySharedMemoryRegion(), /*page_count=*/0));
diff --git a/chrome/browser/profiles/profile_shortcut_manager_unittest_win.cc b/chrome/browser/profiles/profile_shortcut_manager_unittest_win.cc
index 2ab47ea..b10c5d1 100644
--- a/chrome/browser/profiles/profile_shortcut_manager_unittest_win.cc
+++ b/chrome/browser/profiles/profile_shortcut_manager_unittest_win.cc
@@ -14,6 +14,7 @@
 #include "base/task_runner_util.h"
 #include "base/test/scoped_path_override.h"
 #include "base/test/test_shortcut_win.h"
+#include "base/threading/scoped_blocking_call.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_attributes_entry.h"
 #include "chrome/browser/profiles/profile_attributes_storage.h"
@@ -97,7 +98,47 @@
     // by |CreateProfileShortcut()| since there is only one profile.
     profile_shortcut_manager_->CreateProfileShortcut(profile_1_path_);
     thread_bundle_.RunUntilIdle();
-    // Verify that there's now a shortcut with no profile information.
+    // Verify that there's now an unbadged, unmamed shortcut for the single
+    // profile.
+    ValidateSingleProfileShortcut(location, profile_1_path_);
+  }
+
+  void SetupNonProfileShortcut(const base::Location& location) {
+    ASSERT_EQ(0u, profile_attributes_storage_->GetNumberOfProfiles())
+        << location.ToString();
+    ASSERT_FALSE(ProfileShortcutExistsAtDefaultPath(profile_1_name_))
+        << location.ToString();
+    profile_attributes_storage_->AddProfile(profile_1_path_, profile_1_name_,
+                                            std::string(), base::string16(), 0,
+                                            std::string(), EmptyAccountId());
+    // Create a non profile shortcut for Chrome.
+    base::ScopedBlockingCall scoped_blocking_call(
+        FROM_HERE, base::BlockingType::MAY_BLOCK);
+
+    base::FilePath chrome_exe;
+    if (!base::PathService::Get(base::FILE_EXE, &chrome_exe)) {
+      NOTREACHED();
+      return;
+    }
+
+    ShellUtil::ShortcutProperties properties(ShellUtil::CURRENT_USER);
+    ShellUtil::AddDefaultShortcutProperties(chrome_exe, &properties);
+
+    properties.set_app_id(
+        shell_integration::win::GetChromiumModelIdForProfile(profile_1_path_));
+
+    const base::FilePath shortcut_path(
+        profiles::internal::GetShortcutFilenameForProfile(L""));
+
+    const base::FilePath new_shortcut_name =
+        shortcut_path.BaseName().RemoveExtension();
+    properties.set_shortcut_name(new_shortcut_name.value());
+    ShellUtil::CreateOrUpdateShortcut(
+        ShellUtil::SHORTCUT_LOCATION_DESKTOP, properties,
+        ShellUtil::SHELL_SHORTCUT_CREATE_IF_NO_SYSTEM_LEVEL);
+    thread_bundle_.RunUntilIdle();
+    // Verify that there's now an unbadged, unmamed shortcut for the single
+    // profile.
     ValidateNonProfileShortcut(location);
   }
 
@@ -107,6 +148,11 @@
     ValidateProfileShortcut(location, profile_1_name_, profile_1_path_);
   }
 
+  void SetupTwoProfilesAndNonProfileShortcut(const base::Location& location) {
+    SetupNonProfileShortcut(location);
+    CreateProfileWithShortcut(location, profile_2_name_, profile_2_path_);
+  }
+
   // Returns the default shortcut path for this profile.
   base::FilePath GetDefaultShortcutPathForProfile(
       const base::string16& profile_name) {
@@ -164,8 +210,32 @@
         location, GetDefaultShortcutPathForProfile(profile_name), profile_path);
   }
 
-  void ValidateNonProfileShortcutAtPath(const base::Location& location,
-                                        const base::FilePath& shortcut_path) {
+  void ValidateSingleProfileShortcutAtPath(
+      const base::Location& location,
+      const base::FilePath& profile_path,
+      const base::FilePath& shortcut_path) {
+    EXPECT_TRUE(base::PathExists(shortcut_path)) << location.ToString();
+
+    base::win::ShortcutProperties expected_properties;
+    expected_properties.set_target(GetExePath());
+    expected_properties.set_arguments(
+        profiles::internal::CreateProfileShortcutFlags(profile_path));
+    expected_properties.set_icon(GetExePath(), 0);
+    expected_properties.set_description(InstallUtil::GetAppDescription());
+    expected_properties.set_dual_mode(false);
+    PostValidateShortcut(location, shortcut_path, expected_properties);
+  }
+
+  void ValidateSingleProfileShortcut(const base::Location& location,
+                                     const base::FilePath& profile_path) {
+    const base::FilePath shortcut_path =
+        GetDefaultShortcutPathForProfile(base::string16());
+    ValidateSingleProfileShortcutAtPath(location, profile_path, shortcut_path);
+  }
+
+  void ValidateNonProfileShortcut(const base::Location& location) {
+    const base::FilePath shortcut_path =
+        GetDefaultShortcutPathForProfile(base::string16());
     EXPECT_TRUE(base::PathExists(shortcut_path)) << location.ToString();
 
     base::win::ShortcutProperties expected_properties;
@@ -177,12 +247,6 @@
     PostValidateShortcut(location, shortcut_path, expected_properties);
   }
 
-  void ValidateNonProfileShortcut(const base::Location& location) {
-    const base::FilePath shortcut_path =
-        GetDefaultShortcutPathForProfile(base::string16());
-    ValidateNonProfileShortcutAtPath(location, shortcut_path);
-  }
-
   void CreateProfileWithShortcut(const base::Location& location,
                                  const base::string16& profile_name,
                                  const base::FilePath& profile_path) {
@@ -370,6 +434,16 @@
   ValidateProfileShortcut(FROM_HERE, profile_1_name_, profile_1_path_);
 }
 
+// Test that adding a second profile, when the first profile has a non-profile
+// shortcut, updates the non-profile shortcut to be a badged profile shortcut.
+// This happens if the user has one profile, created with a version of Chrome
+// that creates non-profile shortcuts, upgrades to a newer version of Chrome
+// that creates default profile shortcuts, and adds a second profile.
+TEST_F(ProfileShortcutManagerTest, NonProfileShortcutUpgrade) {
+  SetupTwoProfilesAndNonProfileShortcut(FROM_HERE);
+  ValidateProfileShortcut(FROM_HERE, profile_1_name_, profile_1_path_);
+}
+
 TEST_F(ProfileShortcutManagerTest, DesktopShortcutsDeleteSecondToLast) {
   SetupAndCreateTwoShortcuts(FROM_HERE);
 
@@ -378,11 +452,8 @@
   thread_bundle_.RunUntilIdle();
   EXPECT_FALSE(ProfileShortcutExistsAtDefaultPath(profile_2_name_));
 
-  // Verify that the profile name has been removed from the remaining shortcut.
-  ValidateNonProfileShortcut(FROM_HERE);
-  // Verify that an additional shortcut, with the default profile's name does
-  // not exist.
-  EXPECT_FALSE(ProfileShortcutExistsAtDefaultPath(profile_1_name_));
+  // Verify that the remaining shortcut points at the remaining profile.
+  ValidateSingleProfileShortcut(FROM_HERE, profile_1_path_);
 }
 
 TEST_F(ProfileShortcutManagerTest, DeleteSecondToLastProfileWithoutShortcut) {
@@ -403,7 +474,7 @@
   thread_bundle_.RunUntilIdle();
 
   // Verify that the remaining shortcut does not have a profile name.
-  ValidateNonProfileShortcut(FROM_HERE);
+  ValidateSingleProfileShortcut(FROM_HERE, profile_2_path_);
   // Verify that shortcuts with profile names do not exist.
   EXPECT_FALSE(base::PathExists(profile_1_shortcut_path));
   EXPECT_FALSE(base::PathExists(profile_2_shortcut_path));
@@ -426,8 +497,8 @@
   profile_attributes_storage_->RemoveProfile(profile_2_path_);
   thread_bundle_.RunUntilIdle();
 
-  // Verify that the remaining shortcut does not have a profile name.
-  ValidateNonProfileShortcut(FROM_HERE);
+  // Verify that the remaining shortcut is a single profile shortcut.
+  ValidateSingleProfileShortcut(FROM_HERE, profile_1_path_);
   // Verify that shortcuts with profile names do not exist.
   EXPECT_FALSE(base::PathExists(profile_1_shortcut_path));
   EXPECT_FALSE(base::PathExists(profile_2_shortcut_path));
@@ -473,8 +544,8 @@
   profile_attributes_storage_->RemoveProfile(profile_2_path_);
   thread_bundle_.RunUntilIdle();
 
-  // Verify that a default shortcut exists (no profile name/avatar).
-  ValidateNonProfileShortcut(FROM_HERE);
+  // Verify that an unbadged, default named shortcut for profile1 exists.
+  ValidateSingleProfileShortcut(FROM_HERE, profile_1_path_);
   // Verify that an additional shortcut, with the first profile's name does
   // not exist.
   EXPECT_FALSE(ProfileShortcutExistsAtDefaultPath(profile_1_name_));
@@ -541,8 +612,8 @@
   thread_bundle_.RunUntilIdle();
   EXPECT_FALSE(base::PathExists(profile_2_shortcut_path_1));
   EXPECT_FALSE(base::PathExists(profile_2_shortcut_path_2));
-  ValidateNonProfileShortcutAtPath(FROM_HERE,
-                                   preserved_profile_1_shortcut_path);
+  ValidateSingleProfileShortcutAtPath(FROM_HERE, profile_1_path_,
+                                      preserved_profile_1_shortcut_path);
 }
 
 TEST_F(ProfileShortcutManagerTest, RenamedDesktopShortcutsAfterProfileRename) {
@@ -1023,7 +1094,7 @@
   thread_bundle_.RunUntilIdle();
   EXPECT_FALSE(base::PathExists(
       GetDefaultShortcutPathForProfile(profile_2_name_)));
-  // Only profile3 exists. There should be non-profile shortcut only.
+  // Only profile3 exists. There should be a single profile shortcut only.
   EXPECT_FALSE(base::PathExists(profile_3_shortcut_path));
-  ValidateNonProfileShortcut(FROM_HERE);
+  ValidateSingleProfileShortcut(FROM_HERE, profile_3_path_);
 }
diff --git a/chrome/browser/profiles/profile_shortcut_manager_win.cc b/chrome/browser/profiles/profile_shortcut_manager_win.cc
index 1d1f975f..de06c42 100644
--- a/chrome/browser/profiles/profile_shortcut_manager_win.cc
+++ b/chrome/browser/profiles/profile_shortcut_manager_win.cc
@@ -464,8 +464,12 @@
   CreateOrUpdateShortcutsParams(
       base::FilePath profile_path,
       ProfileShortcutManagerWin::CreateOrUpdateMode create_mode,
-      ProfileShortcutManagerWin::NonProfileShortcutAction action)
-      : create_mode(create_mode), action(action), profile_path(profile_path) {}
+      ProfileShortcutManagerWin::NonProfileShortcutAction action,
+      bool single_profile)
+      : create_mode(create_mode),
+        action(action),
+        profile_path(profile_path),
+        single_profile(single_profile) {}
   ~CreateOrUpdateShortcutsParams() {}
 
   ProfileShortcutManagerWin::CreateOrUpdateMode create_mode;
@@ -477,6 +481,11 @@
   base::string16 old_profile_name;
   // The new profile name.
   base::string16 profile_name;
+
+  // If true, this is for a shortcut to a single profile, which won't have a
+  // badged icon or the name of profile in the shortcut name.
+  bool single_profile;
+
   // Avatar images for this profile.
   SkBitmap avatar_image_1x;
   SkBitmap avatar_image_2x;
@@ -521,26 +530,28 @@
   std::copy_if(desktop_contents.begin(), desktop_contents.end(),
                std::inserter(shortcuts, shortcuts.begin()), filter);
 
-  if (params.old_profile_name != params.profile_name) {
-    RenameChromeDesktopShortcutForProfile(params.old_profile_name,
-                                          params.profile_name, &shortcuts,
+  if (params.old_profile_name != params.profile_name || params.single_profile) {
+    RenameChromeDesktopShortcutForProfile(
+        params.old_profile_name,
+        params.single_profile ? L"" : params.profile_name, &shortcuts,
+        &desktop_contents);
+  }
+  // Rename default named profile shortcuts as well, e.g., Chrome.lnk, by
+  // passing "" for the old profile name.
+  if (params.action ==
+      ProfileShortcutManagerWin::UPDATE_NON_PROFILE_SHORTCUTS) {
+    RenameChromeDesktopShortcutForProfile(L"", params.profile_name, &shortcuts,
                                           &desktop_contents);
   }
 
   ShellUtil::ShortcutProperties properties(ShellUtil::CURRENT_USER);
   ShellUtil::AddDefaultShortcutProperties(chrome_exe, &properties);
 
-  // Only set the profile-specific properties when |profile_name| is non empty.
-  // If it is empty, it means the shortcut being created should be a regular,
-  // non-profile Chrome shortcut.
-  if (!params.profile_name.empty()) {
-    properties.set_arguments(command_line);
+  // All shortcuts will point to a profile, but only set the shortcut icon
+  // if we're not generating a shortcut in the single profile case.
+  properties.set_arguments(command_line);
+  if (!params.single_profile)
     properties.set_icon(shortcut_icon, 0);
-  } else {
-    // Set the arguments explicitly to the empty string to ensure that
-    // |ShellUtil::CreateOrUpdateShortcut| updates that part of the shortcut.
-    properties.set_arguments(base::string16());
-  }
 
   properties.set_app_id(shell_integration::win::GetChromiumModelIdForProfile(
       params.profile_path));
@@ -552,7 +563,8 @@
       shortcuts.empty()) {
     const base::string16 shortcut_name =
         profiles::internal::GetUniqueShortcutFilenameForProfile(
-            params.profile_name, desktop_contents);
+            params.single_profile ? L"" : params.profile_name,
+            desktop_contents);
     shortcuts.insert(base::FilePath(shortcut_name));
     operation = ShellUtil::SHELL_SHORTCUT_CREATE_IF_NO_SYSTEM_LEVEL;
   }
@@ -587,8 +599,13 @@
 // |ensure_shortcuts_remain| is true, then a regular non-profile shortcut will
 // be created if this function would otherwise delete the last Chrome desktop
 // shortcut(s). File and COM operations must be allowed on the calling thread.
-void DeleteDesktopShortcuts(const base::FilePath& profile_path,
-                            bool ensure_shortcuts_remain) {
+// |default_profile_path| is used to create the command line for the shortcut
+// created if |ensure_shortcuts_remain| is true and the last desktop shortcut
+// was deleted.
+void DeleteDesktopShortcuts(
+    const base::FilePath& profile_path,
+    const base::Optional<base::FilePath>& default_profile_path,
+    bool ensure_shortcuts_remain) {
   base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
                                                 base::BlockingType::MAY_BLOCK);
 
@@ -615,12 +632,17 @@
   }
 
   // If |ensure_shortcuts_remain| is true and deleting this profile caused the
-  // last shortcuts to be removed, re-create a regular non-profile shortcut.
+  // last shortcuts to be removed, re-create a regular single profile shortcut
+  // pointing at the default profile.
   const bool had_shortcuts = !shortcuts.empty();
   if (ensure_shortcuts_remain && had_shortcuts &&
       !ChromeDesktopShortcutsExist(chrome_exe)) {
     ShellUtil::ShortcutProperties properties(ShellUtil::CURRENT_USER);
     ShellUtil::AddDefaultShortcutProperties(chrome_exe, &properties);
+    if (default_profile_path.has_value()) {
+      properties.set_arguments(profiles::internal::CreateProfileShortcutFlags(
+          default_profile_path.value()));
+    }
     properties.set_shortcut_name(
         profiles::internal::GetShortcutFilenameForProfile(base::string16()));
     ShellUtil::CreateOrUpdateShortcut(
@@ -830,9 +852,9 @@
 
 void ProfileShortcutManagerWin::RemoveProfileShortcuts(
     const base::FilePath& profile_path) {
-  base::CreateCOMSTATaskRunner({base::ThreadPool(), base::MayBlock()})
-      ->PostTask(FROM_HERE,
-                 base::BindOnce(&DeleteDesktopShortcuts, profile_path, false));
+  base::CreateCOMSTATaskRunnerWithTraits({base::ThreadPool(), base::MayBlock()})
+      ->PostTask(FROM_HERE, base::BindOnce(&DeleteDesktopShortcuts,
+                                           profile_path, base::nullopt, false));
 }
 
 void ProfileShortcutManagerWin::HasProfileShortcuts(
@@ -861,7 +883,7 @@
   bool has_entry = storage.GetProfileAttributesWithPath(profile_path, &entry);
   DCHECK(has_entry);
 
-  // The used profile name should be empty if there is only 1 profile.
+  // The shortcut shouldn't include the profile name if there is only 1 profile.
   base::string16 shortcut_profile_name;
   if (storage.GetNumberOfProfiles() > 1u)
     shortcut_profile_name = entry->GetName();
@@ -883,8 +905,9 @@
   CreateOrUpdateProfileIcon(profile_path);
   if (profile_manager_->GetProfileAttributesStorage().GetNumberOfProfiles() ==
       2u) {
-    // When the second profile is added, make existing non-profile shortcuts
-    // point to the first profile and be badged/named appropriately.
+    // When the second profile is added, make existing non-profile and
+    // non-badged shortcuts point to the first profile and be badged/named
+    // appropriately.
     CreateOrUpdateShortcutsForProfileAtPath(GetOtherProfilePath(profile_path),
                                             UPDATE_EXISTING_ONLY,
                                             UPDATE_NON_PROFILE_SHORTCUTS);
@@ -907,10 +930,16 @@
         UPDATE_EXISTING_ONLY, IGNORE_NON_PROFILE_SHORTCUTS);
   }
 
-  base::CreateCOMSTATaskRunner({base::ThreadPool(), base::MayBlock()})
-      ->PostTask(FROM_HERE,
-                 base::BindOnce(&DeleteDesktopShortcuts, profile_path,
-                                deleting_down_to_last_profile));
+  base::FilePath first_profile_path;
+  std::vector<ProfileAttributesEntry*> all_profiles =
+      storage.GetAllProfilesAttributes();
+  if (all_profiles.size() > 0)
+    first_profile_path = all_profiles[0]->GetPath();
+
+  base::CreateCOMSTATaskRunnerWithTraits({base::ThreadPool(), base::MayBlock()})
+      ->PostTask(FROM_HERE, base::BindOnce(&DeleteDesktopShortcuts,
+                                           profile_path, first_profile_path,
+                                           deleting_down_to_last_profile));
 }
 
 void ProfileShortcutManagerWin::OnProfileNameChanged(
@@ -951,8 +980,6 @@
     NonProfileShortcutAction action) {
   DCHECK(!BrowserThread::IsThreadInitialized(BrowserThread::UI) ||
          BrowserThread::CurrentlyOn(BrowserThread::UI));
-  CreateOrUpdateShortcutsParams params(profile_path, create_mode, action);
-
   ProfileAttributesStorage& storage =
       profile_manager_->GetProfileAttributesStorage();
   ProfileAttributesEntry* entry;
@@ -960,7 +987,10 @@
 
   if (!has_entry)
     return;
-  bool remove_badging = (storage.GetNumberOfProfiles() == 1u);
+  bool remove_badging = storage.GetNumberOfProfiles() == 1u;
+
+  CreateOrUpdateShortcutsParams params(profile_path, create_mode, action,
+                                       /*single_profile=*/remove_badging);
 
   params.old_profile_name = entry->GetShortcutName();
 
@@ -972,7 +1002,13 @@
     return;
   }
 
-  if (!remove_badging) {
+  if (remove_badging) {
+    // Only one profile left, so make the shortcut point at it.
+    std::vector<ProfileAttributesEntry*> all_profiles =
+        storage.GetAllProfilesAttributes();
+    if (all_profiles.size() == 1)
+      params.profile_name = all_profiles[0]->GetName();
+  } else {
     params.profile_name = entry->GetName();
 
     // The profile might be using the Gaia avatar, which is not in the
diff --git a/chrome/browser/profiles/profile_shortcut_manager_win.h b/chrome/browser/profiles/profile_shortcut_manager_win.h
index 5678dd4..784940e 100644
--- a/chrome/browser/profiles/profile_shortcut_manager_win.h
+++ b/chrome/browser/profiles/profile_shortcut_manager_win.h
@@ -68,7 +68,9 @@
     CREATE_WHEN_NONE_FOUND,
     CREATE_OR_UPDATE_ICON_ONLY,
   };
-  // Specifies whether non-profile shortcuts should be updated.
+  // Specifies whether non-profile shortcuts should be updated. This also
+  // includes default profile shortcuts, which point at the default
+  // profile, but don't have a profile name in their filename.
   enum NonProfileShortcutAction {
     IGNORE_NON_PROFILE_SHORTCUTS,
     UPDATE_NON_PROFILE_SHORTCUTS,
diff --git a/chrome/browser/renderer_host/chrome_render_message_filter.cc b/chrome/browser/renderer_host/chrome_render_message_filter.cc
index 64c6eec..8aee10f4 100644
--- a/chrome/browser/renderer_host/chrome_render_message_filter.cc
+++ b/chrome/browser/renderer_host/chrome_render_message_filter.cc
@@ -294,13 +294,9 @@
   if (!web_view_permission_helper)
     return;
   web_view_permission_helper->RequestFileSystemPermission(
-      url,
-      allowed,
-      base::Bind(&ChromeRenderMessageFilter::FileSystemAccessedResponse,
-                 render_process_id,
-                 render_frame_id,
-                 url,
-                 callback));
+      url, allowed,
+      base::BindOnce(&ChromeRenderMessageFilter::FileSystemAccessedResponse,
+                     render_process_id, render_frame_id, url, callback));
 }
 
 void ChromeRenderMessageFilter::FileSystemAccessedResponse(
diff --git a/chrome/browser/resources/app_management/BUILD.gn b/chrome/browser/resources/app_management/BUILD.gn
index d52e422..05147eec 100644
--- a/chrome/browser/resources/app_management/BUILD.gn
+++ b/chrome/browser/resources/app_management/BUILD.gn
@@ -256,6 +256,7 @@
   js_library("toggle_row") {
     deps = [
       ":types",
+      "//ui/webui/resources/cr_elements/cr_toggle:cr_toggle",
     ]
   }
 
diff --git a/chrome/browser/resources/app_management/pin_to_shelf_item.html b/chrome/browser/resources/app_management/pin_to_shelf_item.html
index fe4821f..d366390 100644
--- a/chrome/browser/resources/app_management/pin_to_shelf_item.html
+++ b/chrome/browser/resources/app_management/pin_to_shelf_item.html
@@ -9,10 +9,11 @@
       }
     </style>
     <app-management-toggle-row
-      label_="$i18n{pinToShelf}"
-      managed_$="[[isManaged_(app_)]]"
-      policy-label_="$i18n{pinControlledByPolicy}"
-      value_$="[[getValue_(app_)]]">
+      id="toggle-row"
+      label="$i18n{pinToShelf}"
+      managed$="[[isManaged_(app_)]]"
+      policy-label="$i18n{pinControlledByPolicy}"
+      value$="[[getValue_(app_)]]">
     </app-management-toggle-row>
   </template>
   <script src="pin_to_shelf_item.js"></script>
diff --git a/chrome/browser/resources/app_management/pin_to_shelf_item.js b/chrome/browser/resources/app_management/pin_to_shelf_item.js
index ee5d2ac..b53779f 100644
--- a/chrome/browser/resources/app_management/pin_to_shelf_item.js
+++ b/chrome/browser/resources/app_management/pin_to_shelf_item.js
@@ -32,9 +32,9 @@
     },
   },
 
-  ready: function() {
-    // capture the onClick event before it reaches the toggle.
-    this.addEventListener('click', this.onClick_, true);
+  listeners: {
+    click: 'onClick_',
+    change: 'toggleSetting_',
   },
 
   /**
@@ -75,21 +75,22 @@
     return app.isPolicyPinned === OptionalBool.kTrue;
   },
 
-  /**
-   * @param {Event} event
-   * @private
-   */
-  onClick_: function(event) {
-    event.stopPropagation();
-
-    // Disabled
-    if (this.isManaged_(this.app_)) {
-      return;
-    }
-
+  toggleSetting_: function() {
+    const newState =
+        assert(app_management.util.toggleOptionalBool(this.app_.isPinned));
+    assert(
+        app_management.util.convertOptionalBoolToBool(newState) ===
+        this.$['toggle-row'].isChecked());
     app_management.BrowserProxy.getInstance().handler.setPinned(
         this.app_.id,
-        assert(app_management.util.toggleOptionalBool(this.app_.isPinned)),
+        newState,
     );
   },
+
+  /**
+   * @private
+   */
+  onClick_: function() {
+    this.$['toggle-row'].click();
+  },
 });
diff --git a/chrome/browser/resources/app_management/toggle_row.html b/chrome/browser/resources/app_management/toggle_row.html
index 8e2ccf12..981915d 100644
--- a/chrome/browser/resources/app_management/toggle_row.html
+++ b/chrome/browser/resources/app_management/toggle_row.html
@@ -27,27 +27,27 @@
     </style>
 
     <div id="left-content" class="horizontal-align">
-      <template is="dom-if" if="[[icon_]]">
-        <iron-icon id="icon" icon="[[icon_]]"></iron-icon>
+      <template is="dom-if" if="[[icon]]">
+        <iron-icon id="icon" icon="[[icon]]"></iron-icon>
       </template>
-      <div id="label">[[label_]]</div>
+      <div id="label">[[label]]</div>
     </div>
     <div id="right-content" class="horizontal-align">
       <iron-icon id="policy-indicator"
         icon="cr:domain"
         tabindex="0"
         aria-describedby="tooltip"
-        hidden$="[[!managed_]]">
+        hidden$="[[!managed]]">
       </iron-icon>
       <paper-tooltip id="tooltip"
         for="policy-indicator"
         position="top"
         fit-to-visible-bounds>
-        [[policyLabel_]]
+        [[policyLabel]]
       </paper-tooltip>
       <cr-toggle id="toggle"
-        checked="[[value_]]"
-        disabled$="[[managed_]]">
+        checked="[[value]]"
+        disabled$="[[managed]]">
       </cr-toggle>
     </div>
   </template>
diff --git a/chrome/browser/resources/app_management/toggle_row.js b/chrome/browser/resources/app_management/toggle_row.js
index 16aff0b..bdc943c 100644
--- a/chrome/browser/resources/app_management/toggle_row.js
+++ b/chrome/browser/resources/app_management/toggle_row.js
@@ -7,28 +7,43 @@
   properties: {
     /**
      * @type {string}
-     * @private
      */
-    icon_: String,
+    icon: String,
     /**
      * @type {string}
-     * @private
      */
-    label_: String,
+    label: String,
     /**
      * @type {boolean}
-     * @private
      */
-    managed_: {type: Boolean, value: false, reflectToAttribute: true},
+    managed: {type: Boolean, value: false, reflectToAttribute: true},
     /**
      * @type {string}
-     * @private
      */
-    policyLabel_: String,
+    policyLabel: String,
     /**
      * @type {boolean}
-     * @private
      */
-    value_: {type: Boolean, value: false, reflectToAttribute: true},
+    value: {type: Boolean, value: false, reflectToAttribute: true},
+  },
+
+  listeners: {
+    click: 'onClick_',
+  },
+
+  /**
+   * @returns {boolean} true if the toggle is checked.
+   */
+  isChecked: function() {
+    return this.$.toggle.checked;
+  },
+
+  /**
+   * @param {MouseEvent} event
+   * @private
+   */
+  onClick_: function(event) {
+    event.stopPropagation();
+    this.$['toggle'].click();
   },
 });
diff --git a/chrome/browser/resources/app_management/util.js b/chrome/browser/resources/app_management/util.js
index 1e94d7c..a2489bd 100644
--- a/chrome/browser/resources/app_management/util.js
+++ b/chrome/browser/resources/app_management/util.js
@@ -242,9 +242,25 @@
     }
   }
 
+  /**
+   * @param {OptionalBool} optionalBool
+   * @returns {boolean}
+   */
+  function convertOptionalBoolToBool(optionalBool) {
+    switch (optionalBool) {
+      case OptionalBool.kTrue:
+        return true;
+      case OptionalBool.kFalse:
+        return false;
+      default:
+        assertNotReached();
+    }
+  }
+
   return {
     addIfNeeded: addIfNeeded,
     alphabeticalSort: alphabeticalSort,
+    convertOptionalBoolToBool: convertOptionalBoolToBool,
     createEmptyState: createEmptyState,
     createInitialState: createInitialState,
     createPermission: createPermission,
diff --git a/chrome/browser/resources/chromeos/camera/src/js/mojo/imagecapture.js b/chrome/browser/resources/chromeos/camera/src/js/mojo/imagecapture.js
index 914c549..78ab374 100644
--- a/chrome/browser/resources/chromeos/camera/src/js/mojo/imagecapture.js
+++ b/chrome/browser/resources/chromeos/camera/src/js/mojo/imagecapture.js
@@ -32,25 +32,25 @@
   /** @public */
   constructor() {
     /**
-     * @type {cros.mojom.CrosImageCaptureProxy} A interface proxy that used to
+     * @type {cros.mojom.CrosImageCaptureRemote} A interface remote that used to
      *     construct the mojo interface.
      */
-    this.proxy = cros.mojom.CrosImageCapture.getProxy();
+    this.remote = cros.mojom.CrosImageCapture.getRemote();
   }
 
   /**
-   * Gets the mojo interface proxy which could be used to communicate with
+   * Gets the mojo interface remote which could be used to communicate with
    * Chrome.
-   * @return {cros.mojom.CrosImageCaptureProxy} The mojo interface proxy.
+   * @return {cros.mojom.CrosImageCaptureRemote} The mojo interface remote.
    */
-  static getProxy() {
+  static getRemote() {
     if (!cca.mojo.MojoInterface.instance_) {
       /**
        * @type {cca.mojo.MojoInterface} The singleton instance of this object.
        */
       cca.mojo.MojoInterface.instance_ = new cca.mojo.MojoInterface();
     }
-    return cca.mojo.MojoInterface.instance_.proxy;
+    return cca.mojo.MojoInterface.instance_.remote;
   }
 };
 
@@ -143,7 +143,7 @@
        {/** @type {cros.mojom.CameraInfo} */cameraInfo},
   ] = await Promise.all([
     this.capture_.getPhotoCapabilities(),
-    cca.mojo.MojoInterface.getProxy().getCameraInfo(this.deviceId_),
+    cca.mojo.MojoInterface.getRemote().getCameraInfo(this.deviceId_),
   ]);
 
   if (cameraInfo === null) {
@@ -173,7 +173,7 @@
   const takes = [];
   if (photoEffects) {
     photoEffects.forEach((effect) => {
-      takes.push((cca.mojo.MojoInterface.getProxy().setReprocessOption(
+      takes.push((cca.mojo.MojoInterface.getRemote().setReprocessOption(
                       this.deviceId_, effect))
                      .then(({status, blob}) => {
                        if (status != 0) {
@@ -202,7 +202,7 @@
   const numElementPerEntry = 4;
 
   let {cameraInfo} =
-      await cca.mojo.MojoInterface.getProxy().getCameraInfo(deviceId);
+      await cca.mojo.MojoInterface.getRemote().getCameraInfo(deviceId);
   if (cameraInfo === null) {
     throw new Error('No photo resolutions is found for given device id.');
   }
@@ -244,7 +244,7 @@
   const numElementPerEntry = 4;
 
   let {cameraInfo} =
-      await cca.mojo.MojoInterface.getProxy().getCameraInfo(deviceId);
+      await cca.mojo.MojoInterface.getRemote().getCameraInfo(deviceId);
   if (cameraInfo === null) {
     throw new Error('No video configs is found for given device id.');
   }
@@ -281,7 +281,7 @@
  */
 cca.mojo.getCameraFacing = async function(deviceId) {
   let {cameraInfo} =
-      await cca.mojo.MojoInterface.getProxy().getCameraInfo(deviceId);
+      await cca.mojo.MojoInterface.getRemote().getCameraInfo(deviceId);
   if (cameraInfo === null) {
     throw new Error('No camera facing is found for given device id.');
   }
@@ -299,7 +299,7 @@
   const numElementPerEntry = 2;
 
   let {cameraInfo} =
-      await cca.mojo.MojoInterface.getProxy().getCameraInfo(deviceId);
+      await cca.mojo.MojoInterface.getRemote().getCameraInfo(deviceId);
   if (cameraInfo === null) {
     throw new Error('No supported Fps Ranges is found for given device id.');
   }
@@ -373,7 +373,7 @@
     // |constraints| , we assume the app wants to use default frame rate range.
     // We set the frame rate range to an invalid range (e.g. 0 fps) so that it
     // will fallback to use the default one.
-    const {isSuccess} = await cca.mojo.MojoInterface.getProxy().setFpsRange(
+    const {isSuccess} = await cca.mojo.MojoInterface.getRemote().setFpsRange(
         deviceId, streamWidth, streamHeight, minFrameRate, maxFrameRate);
 
     if (!isSuccess && hasSpecifiedFrameRateRange) {
diff --git a/chrome/browser/resources/local_ntp/customize.js b/chrome/browser/resources/local_ntp/customize.js
index ced9b9c6..9689a03 100644
--- a/chrome/browser/resources/local_ntp/customize.js
+++ b/chrome/browser/resources/local_ntp/customize.js
@@ -101,19 +101,16 @@
   ATTRIBUTIONS: 'custom-bg-attr',
   BACK_CIRCLE: 'bg-sel-back-circle',
   BACKGROUNDS_DEFAULT: 'backgrounds-default',
-  BACKGROUNDS_DEFAULT_ICON: 'backgrounds-default-icon',
   BACKGROUNDS_BUTTON: 'backgrounds-button',
   BACKGROUNDS_IMAGE_MENU: 'backgrounds-image-menu',
   BACKGROUNDS_MENU: 'backgrounds-menu',
   BACKGROUNDS_UPLOAD: 'backgrounds-upload',
-  BACKGROUNDS_UPLOAD_ICON: 'backgrounds-upload-icon',
   CANCEL: 'bg-sel-footer-cancel',
   COLOR_PICKER: 'color-picker',
-  COLOR_PICKER_TILE: 'color-picker-tile',
   COLOR_PICKER_CONTAINER: 'color-picker-container',
   COLOR_PICKER_ICON: 'color-picker-icon',
   COLORS_BUTTON: 'colors-button',
-  COLORS_DEFAULT_ICON: 'colors-default-icon',
+  COLORS_DEFAULT: 'colors-default',
   COLORS_THEME: 'colors-theme',
   COLORS_THEME_NAME: 'colors-theme-name',
   COLORS_THEME_UNINSTALL: 'colors-theme-uninstall',
@@ -167,6 +164,7 @@
   COLLECTION_SELECTED: 'bg-selected',  // Highlight selected tile
   COLLECTION_TILE: 'bg-sel-tile',  // Preview tile for background customization
   COLLECTION_TILE_BG: 'bg-sel-tile-bg',
+  COLLECTION_TILE_WRAPPER: 'bg-sel-tile-wrapper',
   COLLECTION_TITLE: 'bg-sel-tile-title',  // Title of a background image
   IMAGE_DIALOG: 'is-img-sel',
   ON_IMAGE_MENU: 'on-img-menu',
@@ -548,21 +546,24 @@
  */
 customize.createTileWithTitle = function(
     id, imageUrl, name, dataset, onClickInteraction, onKeyInteraction) {
-  const tile = customize.createTileThumbnail(
-      id, imageUrl, dataset, onClickInteraction, onKeyInteraction);
-  tile.setAttribute('aria-label', name);
-  tile.title = name;
-  customize.fadeInImageTile(tile, imageUrl, null);
+  const tileBackground = customize.createTileThumbnail(
+      id, imageUrl, dataset, onClickInteraction, onKeyInteraction, true);
+  tileBackground.setAttribute('aria-label', name);
+  tileBackground.title = name;
 
   const title = document.createElement('div');
   title.classList.add(customize.CLASSES.COLLECTION_TITLE);
   title.textContent = name;
-  tile.appendChild(title);
 
-  const tileBackground = document.createElement('div');
-  tileBackground.classList.add(customize.CLASSES.COLLECTION_TILE_BG);
-  tileBackground.appendChild(tile);
-  return tileBackground;
+  const tileWrapper = document.createElement('div');
+  tileWrapper.classList.add(customize.CLASSES.COLLECTION_TILE_WRAPPER);
+  tileWrapper.appendChild(tileBackground);
+  tileWrapper.appendChild(title);
+  // Enable click support for the whole wrapper including title.
+  tileWrapper.onclick = function() {
+    tileWrapper.firstElementChild.click();
+  };
+  return tileWrapper;
 };
 
 /**
@@ -575,14 +576,13 @@
  */
 customize.createTileWithoutTitle = function(
     id, imageUrl, dataset, onClickInteraction, onKeyInteraction) {
-  const tile = customize.createTileThumbnail(
-      id, imageUrl, dataset, onClickInteraction, onKeyInteraction);
-  customize.fadeInImageTile(tile, imageUrl, null);
+  const tileBackground = customize.createTileThumbnail(
+      id, imageUrl, dataset, onClickInteraction, onKeyInteraction, true);
 
-  const tileBackground = document.createElement('div');
-  tileBackground.classList.add(customize.CLASSES.COLLECTION_TILE_BG);
-  tileBackground.appendChild(tile);
-  return tileBackground;
+  const tileWrapper = document.createElement('div');
+  tileWrapper.classList.add(customize.CLASSES.COLLECTION_TILE_WRAPPER);
+  tileWrapper.appendChild(tileBackground);
+  return tileWrapper;
 };
 
 /**
@@ -592,24 +592,35 @@
  * @param {Object} dataset The dataset for the new element.
  * @param {?Function} onClickInteraction Function for onclick interaction.
  * @param {?Function} onKeyInteraction Function for onkeydown interaction.
+ * @param {boolean} fadeIn Whether thumbnail images should faded in. Defaults to
+ *     false.
  */
 customize.createTileThumbnail = function(
-    id, imageUrl, dataset, onClickInteraction, onKeyInteraction) {
+    id, imageUrl, dataset, onClickInteraction, onKeyInteraction,
+    fadeIn = false) {
   const tile = document.createElement('div');
-  tile.id = id;
   tile.classList.add(customize.CLASSES.COLLECTION_TILE);
   tile.style.backgroundImage = 'url(' + imageUrl + ')';
-  for (const key in dataset) {
-    tile.dataset[key] = dataset[key];
+  if (fadeIn) {
+    customize.fadeInImageTile(tile, imageUrl, null);
   }
-  tile.tabIndex = -1;
+
+  const tileBackground = document.createElement('div');
+  tileBackground.id = id;
+  tileBackground.classList.add(customize.CLASSES.COLLECTION_TILE_BG);
+  tileBackground.appendChild(tile);
+
+  for (const key in dataset) {
+    tileBackground.dataset[key] = dataset[key];
+  }
+  tileBackground.tabIndex = -1;
 
   // Accessibility support for screen readers.
-  tile.setAttribute('role', 'button');
+  tileBackground.setAttribute('role', 'button');
 
-  tile.onclick = onClickInteraction;
-  tile.onkeydown = onKeyInteraction;
-  return tile;
+  tileBackground.onclick = onClickInteraction;
+  tileBackground.onkeydown = onKeyInteraction;
+  return tileBackground;
 };
 
 /**
@@ -638,8 +649,8 @@
   const menu = $(customize.IDS.CUSTOMIZATION_MENU);
   const container = customize.richerPicker_selectedSubmenu.menu;
 
-  const tiles = Array.from(
-      container.getElementsByClassName(customize.CLASSES.COLLECTION_TILE_BG));
+  const tiles = Array.from(container.getElementsByClassName(
+      customize.CLASSES.COLLECTION_TILE_WRAPPER));
   let nextIndex = tiles.indexOf(current.parentElement);
   if (deltaX != 0) {
     nextIndex += deltaX;
@@ -655,7 +666,7 @@
     }
   }
   if (tiles[nextIndex]) {
-    return tiles[nextIndex].children[0];
+    return tiles[nextIndex].firstElementChild;
   }
   return null;
 };
@@ -708,10 +719,6 @@
       event.keyCode === customize.KEYCODES.SPACE) {
     event.preventDefault();
     event.stopPropagation();
-    if (tile.onClickOverride) {
-      tile.onClickOverride(event);
-      return;
-    }
     tile.onclick(event);
   } else if (customize.arrowKeys.includes(event.keyCode)) {
     // Handle arrow key navigation.
@@ -829,15 +836,10 @@
   }
 
   // Attach event listeners for upload and default tiles
-  $(customize.IDS.BACKGROUNDS_UPLOAD_ICON).onkeydown =
+  $(customize.IDS.BACKGROUNDS_UPLOAD).onkeydown =
       customize.tileOnKeyDownInteraction;
-  $(customize.IDS.BACKGROUNDS_DEFAULT_ICON).onkeydown =
+  $(customize.IDS.BACKGROUNDS_DEFAULT).onkeydown =
       customize.tileOnKeyDownInteraction;
-  $(customize.IDS.BACKGROUNDS_UPLOAD_ICON).onClickOverride =
-      $(customize.IDS.BACKGROUNDS_UPLOAD).onkeydown;
-  $(customize.IDS.BACKGROUNDS_DEFAULT_ICON).onClickOverride =
-      $(customize.IDS.BACKGROUNDS_DEFAULT).onkeydown;
-
   $(customize.IDS.TILES).focus();
 };
 
@@ -884,7 +886,7 @@
     return;
   }
 
-  option.parentElement.classList.toggle(customize.CLASSES.SELECTED, true);
+  option.classList.toggle(customize.CLASSES.SELECTED, true);
   // Create and append a blue checkmark to the selected option.
   const selectedCircle = document.createElement('div');
   const selectedCheck = document.createElement('div');
@@ -905,7 +907,7 @@
     return;
   }
 
-  option.parentElement.classList.toggle(customize.CLASSES.SELECTED, false);
+  option.classList.toggle(customize.CLASSES.SELECTED, false);
   // Remove all blue checkmarks from the selected option (this includes the
   // checkmark and the encompassing circle).
   const select = option.querySelectorAll(
@@ -928,11 +930,11 @@
   // Set preview images at 720p by replacing the params in the url.
   const background = $(customize.IDS.CUSTOM_BG);
   const preview = $(customize.IDS.CUSTOM_BG_PREVIEW);
-  if (tile.id === customize.IDS.BACKGROUNDS_DEFAULT_ICON) {
+  if (tile.id === customize.IDS.BACKGROUNDS_DEFAULT) {
     preview.dataset.hasImage = false;
     preview.style.backgroundImage = '';
     preview.style.backgroundColor = document.body.style.backgroundColor;
-  } else if (tile.id === customize.IDS.BACKGROUNDS_UPLOAD_ICON) {
+  } else if (tile.id === customize.IDS.BACKGROUNDS_UPLOAD) {
     // No previews for uploaded images.
     return;
   } else {
@@ -940,7 +942,7 @@
 
     const re = /w\d+\-h\d+/;
     preview.style.backgroundImage =
-        tile.style.backgroundImage.replace(re, 'w1280-h720');
+        tile.firstElementChild.style.backgroundImage.replace(re, 'w1280-h720');
   }
   background.style.opacity = 0;
   preview.style.opacity = 1;
@@ -1219,7 +1221,7 @@
     }
 
     const tileBackground = document.createElement('div');
-    tileBackground.classList.add(customize.CLASSES.COLLECTION_TILE_BG);
+    tileBackground.classList.add(customize.CLASSES.COLLECTION_TILE_WRAPPER);
     tileBackground.appendChild(tile);
     tileContainer.appendChild(tileBackground);
   }
@@ -1263,10 +1265,11 @@
  *     loading.
  */
 customize.loadTile = function(tile, imageData, countLoad) {
-  tile.style.backgroundImage =
+  tile.firstElementChild.style.backgroundImage =
       'url(' + imageData[tile.dataset.tileIndex].thumbnailImageUrl + ')';
   customize.fadeInImageTile(
-      tile, imageData[tile.dataset.tileIndex].thumbnailImageUrl, countLoad);
+      tile.firstElementChild,
+      imageData[tile.dataset.tileIndex].thumbnailImageUrl, countLoad);
 };
 
 /**
@@ -1426,12 +1429,12 @@
   if (!themeInfo.customBackgroundConfigured) {
     // Default.
     customize.preselectedOptions.backgroundsMenuTile =
-        $(customize.IDS.BACKGROUNDS_DEFAULT_ICON);
+        $(customize.IDS.BACKGROUNDS_DEFAULT);
   } else if (themeInfo.imageUrl.includes(
                  'chrome-search://local-ntp/background.jpg')) {
     // Local image.
     customize.preselectedOptions.backgroundsMenuTile =
-        $(customize.IDS.BACKGROUNDS_UPLOAD_ICON);
+        $(customize.IDS.BACKGROUNDS_UPLOAD);
   } else if (!customize.selectedOptions.backgroundData) {
     // Image tile. Only if another background hasn't already been selected.
     customize.preselectedOptions.backgroundsMenuTile =
@@ -1927,7 +1930,7 @@
 
   $(customize.IDS.BACKGROUNDS_MENU).onkeydown = function(event) {
     if (customize.arrowKeys.includes(event.keyCode)) {
-      $(customize.IDS.BACKGROUNDS_UPLOAD_ICON).focus();
+      $(customize.IDS.BACKGROUNDS_UPLOAD).focus();
     }
   };
 
@@ -1938,15 +1941,9 @@
   };
 
   $(customize.IDS.BACKGROUNDS_UPLOAD).onclick = uploadImageInteraction;
-  $(customize.IDS.BACKGROUNDS_UPLOAD).onkeydown = function(event) {
-    if (event.keyCode === customize.KEYCODES.ENTER ||
-        event.keyCode === customize.KEYCODES.SPACE) {
-      uploadImageInteraction();
-    }
-  };
 
   $(customize.IDS.BACKGROUNDS_DEFAULT).onclick = function(event) {
-    const tile = $(customize.IDS.BACKGROUNDS_DEFAULT_ICON);
+    const tile = $(customize.IDS.BACKGROUNDS_DEFAULT);
     tile.dataset.url = '';
     tile.dataset.attributionLine1 = '';
     tile.dataset.attributionLine2 = '';
@@ -1957,12 +1954,6 @@
       customize.richerPicker_selectBackgroundTile(tile);
     }
   };
-  $(customize.IDS.BACKGROUNDS_DEFAULT).onkeydown = function(event) {
-    if (event.keyCode === customize.KEYCODES.ENTER ||
-        event.keyCode === customize.KEYCODES.SPACE) {
-      $(customize.IDS.BACKGROUNDS_DEFAULT).onclick(event);
-    }
-  };
 
   const richerPicker = $(customize.IDS.CUSTOMIZATION_MENU);
   richerPicker.onmousedown = function(event) {
@@ -2203,10 +2194,11 @@
   // If the picker is preselected and the user picks a new color, we need to
   // treat the picker as a new selection and not a preselection.
   if (customize.preselectedOptions.colorsMenuTile ===
-      $(customize.IDS.COLOR_PICKER_TILE)) {
+      $(customize.IDS.COLOR_PICKER_CONTAINER)) {
     customize.preselectedOptions.colorsMenuTile = null;
   }
-  customize.updateColorsMenuTileSelection($(customize.IDS.COLOR_PICKER_TILE));
+  customize.updateColorsMenuTileSelection(
+      $(customize.IDS.COLOR_PICKER_CONTAINER));
   ntpApiHandle.applyAutogeneratedTheme(0, [r, g, b, 255]);
 };
 
@@ -2243,28 +2235,28 @@
   }
 
   // Configure the default tile.
-  $(customize.IDS.COLORS_DEFAULT_ICON).onclick =
+  $(customize.IDS.COLORS_DEFAULT).onclick =
       customize.defaultThemeTileInteraction;
-  $(customize.IDS.COLORS_DEFAULT_ICON).onkeydown =
+  $(customize.IDS.COLORS_DEFAULT).onkeydown =
       customize.tileOnKeyDownInteraction;
 
   // On arrow keys focus the first element.
   $(customize.IDS.COLORS_MENU).onkeydown = function(event) {
     if (customize.arrowKeys.includes(event.keyCode)) {
       if (configData.chromeColorsCustomColorPicker) {
-        $(customize.IDS.COLOR_PICKER_TILE).focus();
+        $(customize.IDS.COLOR_PICKER_CONTAINER).focus();
       } else {
-        $(customize.IDS.COLORS_DEFAULT_ICON).focus();
+        $(customize.IDS.COLORS_DEFAULT).focus();
       }
     }
   };
 
   // Configure custom color picker.
   if (configData.chromeColorsCustomColorPicker) {
-    $(customize.IDS.COLOR_PICKER_TILE).onclick = function(event) {
+    $(customize.IDS.COLOR_PICKER_CONTAINER).onclick = function(event) {
       $(customize.IDS.COLOR_PICKER).click();
     };
-    $(customize.IDS.COLOR_PICKER_TILE).onkeydown =
+    $(customize.IDS.COLOR_PICKER_CONTAINER).onkeydown =
         customize.tileOnKeyDownInteraction;
     $(customize.IDS.COLOR_PICKER).onchange =
         customize.colorPickerTileInteraction;
@@ -2305,12 +2297,12 @@
 
   let tile;
   if (themeInfo.usingDefaultTheme) {
-    tile = $(customize.IDS.COLORS_DEFAULT_ICON);
+    tile = $(customize.IDS.COLORS_DEFAULT);
   } else if (themeInfo.colorId && themeInfo.colorId > 0) {
     // Color from predefined set is selected.
     const tiles = Array.from(
         $(customize.IDS.COLORS_MENU)
-            .getElementsByClassName(customize.CLASSES.COLLECTION_TILE));
+            .getElementsByClassName(customize.CLASSES.COLLECTION_TILE_BG));
     for (let i = 0; i < tiles.length; i++) {
       if (tiles[i].dataset && tiles[i].dataset.id == themeInfo.colorId) {
         tile = tiles[i];
@@ -2321,7 +2313,7 @@
       configData.chromeColorsCustomColorPicker && themeInfo.colorDark &&
       themeInfo.colorLight && themeInfo.colorPicked) {
     // Custom color is selected.
-    tile = $(customize.IDS.COLOR_PICKER_TILE);
+    tile = $(customize.IDS.COLOR_PICKER_CONTAINER);
 
     // Update color picker tile colors.
     $(customize.IDS.COLOR_PICKER).value =
diff --git a/chrome/browser/resources/local_ntp/local_ntp.css b/chrome/browser/resources/local_ntp/local_ntp.css
index 5b617d0..33c80f9 100644
--- a/chrome/browser/resources/local_ntp/local_ntp.css
+++ b/chrome/browser/resources/local_ntp/local_ntp.css
@@ -915,24 +915,38 @@
   visibility: visible;
 }
 
+#customization-menu .bg-sel-tile-wrapper {
+  cursor: pointer;
+  display: inline-block;
+  position: relative;
+  vertical-align: top;
+}
+
 #backgrounds-menu .bg-sel-tile-bg,
 #backgrounds-image-menu .bg-sel-tile-bg {
   border-radius: 4px;
   height: 176px;
-  margin-bottom: 45px;
   margin-inline-end: 8px;
   margin-inline-start: 0;
   margin-top: 0;
   width: 176px;
 }
 
+#backgrounds-menu .bg-sel-tile-wrapper,
+#backgrounds-image-menu .bg-sel-tile-wrapper {
+  height: 176px;
+  margin-bottom: 45px;
+  margin-inline-end: 8px;
+  width: 176px;
+}
+
 /* Remove left/right spacing from the last tile in each row. */
-#backgrounds-menu .bg-sel-tile-bg:nth-of-type(3n),
-#backgrounds-image-menu .bg-sel-tile-bg:nth-of-type(3n) {
+#backgrounds-menu .bg-sel-tile-wrapper:nth-of-type(3n),
+#backgrounds-image-menu .bg-sel-tile-wrapper:nth-of-type(3n) {
   margin-inline-end: 0;
 }
 
-#backgrounds-image-menu .bg-sel-tile-bg {
+#backgrounds-image-menu .bg-sel-tile-wrapper {
   margin-bottom: 8px;
 }
 
@@ -952,7 +966,12 @@
   }
 }
 
-.using-mouse-nav .bg-sel-tile:focus {
+:not(.using-mouse-nav) :-webkit-any(#backgrounds-menu, #backgrounds-image-menu)
+  .bg-sel-tile-bg.selected:focus-within {
+  outline: auto -webkit-focus-ring-color;
+}
+
+.using-mouse-nav .bg-sel-tile-bg:focus {
   outline: none;
 }
 
@@ -960,7 +979,7 @@
 #backgrounds-image-menu .bg-sel-tile {
   background-position: center;
   border-radius: 4px;
-  cursor: pointer;
+  pointer-events: none;
 }
 
 #customization-menu .bg-sel-tile-title {
@@ -1114,7 +1133,7 @@
   }
 }
 
-#customization-menu .bg-sel-tile .selected-circle {
+#customization-menu .bg-sel-tile-bg .selected-circle {
   height: 20px;
   left: initial;
   right: 10px;
@@ -1122,12 +1141,12 @@
   width: 20px;
 }
 
-html[dir=rtl] #customization-menu .bg-sel-tile .selected-circle {
+html[dir=rtl] #customization-menu .bg-sel-tile-bg .selected-circle {
   left: 10px;
   right: initial;
 }
 
-#customization-menu .bg-sel-tile .selected-check {
+#customization-menu .bg-sel-tile-bg .selected-check {
   height: 24px;
   left: initial;
   right: 9px;
@@ -1135,7 +1154,7 @@
   width: 24px;
 }
 
-html[dir=rtl] #customization-menu .bg-sel-tile .selected-check {
+html[dir=rtl] #customization-menu .bg-sel-tile-bg .selected-check {
   left: 9px;
   right: initial;
 }
@@ -1250,13 +1269,13 @@
   }
 }
 
-.selected .sh-option-image {
+.sh-option-image.selected {
   background-color: rgb(var(--GB050-rgb));
   border-color: rgb(var(--GB600-rgb));
 }
 
 @media (prefers-color-scheme: dark) {
-  .selected .sh-option-image {
+  .sh-option-image.selected {
     background-color: rgba(var(--GB200-rgb), .1);
     border-color: rgb(var(--GB300-rgb));
   }
@@ -1567,30 +1586,42 @@
   --custom-color-border: rgb(var(--GG300-rgb));
   --custom-color-dark: rgb(var(--GG100-rgb));
   --custom-color-light: white;
+  --border-size: 4px;
   --tile-size: 64px;
   --tile-margin: 25px;
 }
 
+/*
+ Tile wrapper on colors menu should be the desired tile-size + 2 * border-size.
+ And the margin between the wrapper size should be decreased by 2 * border-size.
+ */
+#colors-menu .bg-sel-tile-wrapper {
+  height: calc(var(--tile-size) + 2 * var(--border-size));
+  margin-bottom: calc(var(--tile-margin) - 2 * var(--border-size));
+  margin-inline-end: calc(var(--tile-margin) - 2 * var(--border-size));
+  position: relative;
+  width: calc(var(--tile-size) + 2 * var(--border-size));
+}
+
 #colors-menu .bg-sel-tile-bg {
   background-color: unset;
   border-radius: 50%;
-  box-sizing: border-box;
   cursor: pointer;
   height: var(--tile-size);
-  margin-bottom: var(--tile-margin);
-  margin-inline-end: var(--tile-margin);
-  margin-inline-start: 0;
-  margin-top: 0;
+  margin-inline-start: var(--border-size);
+  margin-top: var(--border-size);
   width: var(--tile-size);
 }
 
 #colors-menu .bg-sel-tile-bg.selected {
-  box-shadow: 0 0 0 4px rgba(var(--GB600-rgb), .4);
+  border: var(--border-size) solid rgba(var(--GB600-rgb), .4);
+  margin-inline-start: 0;
+  margin-top: 0;
 }
 
 @media (prefers-color-scheme: dark) {
   #colors-menu .bg-sel-tile-bg.selected {
-    box-shadow: 0 0 0 4px rgba(var(--GB600-rgb), .4);
+    border: var(--border-size) solid rgba(var(--GB600-rgb), .4);
   }
 }
 
@@ -1598,18 +1629,18 @@
  To avoid circle showing behind the checkmark, draw it 2px smaller and 1px
  below so that it is always 1px smaller then the checkmark from all sides.
  */
-#colors-menu .bg-sel-tile .selected-circle {
+#colors-menu .bg-sel-tile-bg .selected-circle {
   height: calc(var(--check-mark-size) - 2px);
   right: 0;
   top: 1px;
   width: calc(var(--check-mark-size) - 2px);
 }
 
-html[dir=rtl] #colors-menu .bg-sel-tile .selected-circle {
+html[dir=rtl] #colors-menu .bg-sel-tile-bg .selected-circle {
   left: 0;
 }
 
-#colors-menu .bg-sel-tile .selected-check {
+#colors-menu .bg-sel-tile-bg .selected-check {
   background: url(icons/check_circle.svg) no-repeat center;
   background-size: var(--check-mark-size) var(--check-mark-size);
   height: var(--check-mark-size);
@@ -1618,12 +1649,12 @@
   width: var(--check-mark-size);
 }
 
-html[dir=rtl] #colors-menu .bg-sel-tile .selected-check {
+html[dir=rtl] #colors-menu .bg-sel-tile-bg .selected-check {
   left: 0;
 }
 
 @media (prefers-color-scheme: dark) {
-  #colors-menu .bg-sel-tile .selected-check::after {
+  #colors-menu .bg-sel-tile-bg .selected-check::after {
     -webkit-mask-image: url(icons/check_circle.svg);
     -webkit-mask-size: var(--check-mark-size);
     height: var(--check-mark-size);
@@ -1631,6 +1662,10 @@
   }
 }
 
+#colors-menu .bg-sel-tile {
+  pointer-events: none;
+}
+
 #colors-default-icon {
   background-image: url(icons/default_theme.svg);
   background-repeat: no-repeat;
@@ -1732,6 +1767,7 @@
   border-radius: 50%;
   box-sizing: border-box;
   opacity: 1;
+  position: absolute;
 }
 
 #color-picker-icon {
diff --git a/chrome/browser/resources/local_ntp/local_ntp.html b/chrome/browser/resources/local_ntp/local_ntp.html
index 126cfef9..beff1e8 100644
--- a/chrome/browser/resources/local_ntp/local_ntp.html
+++ b/chrome/browser/resources/local_ntp/local_ntp.html
@@ -227,21 +227,25 @@
       </div>
       <div id="backgrounds-menu" class="menu-panel" tabindex="0"
           role="tabpanel" aria-label="$i18n{backgroundsOption}">
-        <div id="backgrounds-upload" class="bg-sel-tile-bg">
-          <div id="backgrounds-upload-icon" class="bg-sel-tile" tabindex="-1"
+        <div id="backgrounds-upload-wrapper" class="bg-sel-tile-wrapper">
+          <div id="backgrounds-upload" class="bg-sel-tile-bg" tabindex="-1"
               role="button" aria-label="$i18n{uploadImage}"
               title="$i18n{uploadImage}">
-            <div id="backgrounds-upload-arrow"></div>
-            <div id="backgrounds-upload-text">$i18n{uploadImage}</div>
+            <div id="backgrounds-upload-icon" class="bg-sel-tile">
+              <div id="backgrounds-upload-arrow"></div>
+              <div id="backgrounds-upload-text">$i18n{uploadImage}</div>
+            </div>
           </div>
         </div>
-        <div id="backgrounds-default" class="bg-sel-tile-bg">
-          <div id="backgrounds-default-icon" class="bg-sel-tile" tabindex="-1"
+        <div id="backgrounds-default-wrapper" class="bg-sel-tile-wrapper">
+          <div id="backgrounds-default" class="bg-sel-tile-bg" tabindex="-1"
               role="button" aria-label="$i18n{noBackground}"
               title="$i18n{noBackground}">
-            <div class="mini-page">
-              <div class="mini-header-colorful"></div>
-              <div class="mini-shortcuts"></div>
+            <div id="backgrounds-default-icon" class="bg-sel-tile">
+              <div class="mini-page">
+                <div class="mini-header-colorful"></div>
+                <div class="mini-shortcuts"></div>
+              </div>
             </div>
           </div>
           <div class="bg-sel-tile-title">$i18n{noBackground}</div>
@@ -320,20 +324,24 @@
               $i18n{uninstallButton}
             </button>
         </div>
-        <div id="color-picker-container" class="bg-sel-tile-bg"
-            aria-label="$i18n{colorPickerLabel}"
-            title="$i18n{colorPickerLabel}">
-          <div id="color-picker-tile" class="bg-sel-tile" tabindex="-1">
-            <div id="left-semicircle"></div>
-            <div id="color-picker-icon"></div>
-            <input id="color-picker" type="color" style="display:none">
-            </input>
+        <div id="color-picker-wrapper" class="bg-sel-tile-wrapper">
+          <div id="color-picker-container" class="bg-sel-tile-bg"
+              aria-label="$i18n{colorPickerLabel}"
+              title="$i18n{colorPickerLabel}"  tabindex="-1">
+            <div id="color-picker-tile" class="bg-sel-tile">
+              <div id="left-semicircle"></div>
+              <div id="color-picker-icon"></div>
+              <input id="color-picker" type="color" style="display:none">
+              </input>
+            </div>
           </div>
         </div>
-        <div id="colors-default" class="bg-sel-tile-bg"
-            aria-label="$i18n{defaultThemeLabel}"
-            title="$i18n{defaultThemeLabel}">
-          <div id="colors-default-icon" class="bg-sel-tile" tabindex="-1"></div>
+        <div id="colors-default-wrapper" class="bg-sel-tile-wrapper">
+          <div id="colors-default" class="bg-sel-tile-bg"
+              aria-label="$i18n{defaultThemeLabel}"
+              title="$i18n{defaultThemeLabel}" tabindex="-1">
+            <div id="colors-default-icon" class="bg-sel-tile"></div>
+          </div>
         </div>
       </div>
     </div>
diff --git a/chrome/browser/resources/settings/appearance_page/BUILD.gn b/chrome/browser/resources/settings/appearance_page/BUILD.gn
index 7b27f61..a3c3d26c 100644
--- a/chrome/browser/resources/settings/appearance_page/BUILD.gn
+++ b/chrome/browser/resources/settings/appearance_page/BUILD.gn
@@ -12,6 +12,10 @@
     ":fonts_browser_proxy",
     ":home_url_input",
   ]
+
+  if (is_chromeos) {
+    deps += [ ":wallpaper_browser_proxy" ]
+  }
 }
 
 js_library("appearance_fonts_page") {
@@ -42,6 +46,7 @@
 js_library("appearance_page") {
   deps = [
     ":appearance_browser_proxy",
+    ":wallpaper_browser_proxy",
     "..:page_visibility",
     "..:route",
     "../controls:settings_dropdown_menu",
@@ -75,3 +80,10 @@
   ]
   externs_list = [ "$externs_path/settings_private.js" ]
 }
+
+js_library("wallpaper_browser_proxy") {
+  deps = [
+    "//ui/webui/resources/js:cr",
+  ]
+  externs_list = [ "$externs_path/chrome_send.js" ]
+}
diff --git a/chrome/browser/resources/settings/appearance_page/appearance_browser_proxy.js b/chrome/browser/resources/settings/appearance_page/appearance_browser_proxy.js
index c75464d..7e8bd80 100644
--- a/chrome/browser/resources/settings/appearance_page/appearance_browser_proxy.js
+++ b/chrome/browser/resources/settings/appearance_page/appearance_browser_proxy.js
@@ -17,22 +17,6 @@
     /** @return {boolean} Whether the current profile is supervised. */
     isSupervised() {}
 
-    /**
-     * @return {!Promise<boolean>} Whether the wallpaper setting row should be
-     *     visible.
-     */
-    isWallpaperSettingVisible() {}
-
-    /**
-     * @return {!Promise<boolean>} Whether the wallpaper is policy controlled.
-     */
-    isWallpaperPolicyControlled() {}
-
-    // <if expr="chromeos">
-    openWallpaperManager() {}
-
-    // </if>
-
     useDefaultTheme() {}
 
     // <if expr="is_linux and not chromeos">
@@ -70,24 +54,6 @@
       return loadTimeData.getBoolean('isSupervised');
     }
 
-    // <if expr="chromeos">
-    /** @override */
-    isWallpaperSettingVisible() {
-      return cr.sendWithPromise('isWallpaperSettingVisible');
-    }
-
-    /** @override */
-    isWallpaperPolicyControlled() {
-      return cr.sendWithPromise('isWallpaperPolicyControlled');
-    }
-
-    /** @override */
-    openWallpaperManager() {
-      chrome.send('openWallpaperManager');
-    }
-
-    // </if>
-
     /** @override */
     useDefaultTheme() {
       chrome.send('useDefaultTheme');
diff --git a/chrome/browser/resources/settings/appearance_page/appearance_page.html b/chrome/browser/resources/settings/appearance_page/appearance_page.html
index df9863a..3a61951 100644
--- a/chrome/browser/resources/settings/appearance_page/appearance_page.html
+++ b/chrome/browser/resources/settings/appearance_page/appearance_page.html
@@ -18,6 +18,10 @@
 <link rel="import" href="appearance_fonts_page.html">
 <link rel="import" href="home_url_input.html">
 
+<if expr="chromeos">
+<link rel="import" href="../appearance_page/wallpaper_browser_proxy.html">
+</if>
+
 <dom-module id="settings-appearance-page">
   <template>
     <style include="settings-shared md-select iron-flex">
diff --git a/chrome/browser/resources/settings/appearance_page/appearance_page.js b/chrome/browser/resources/settings/appearance_page/appearance_page.js
index 9fa3b60a..2551cf3b 100644
--- a/chrome/browser/resources/settings/appearance_page/appearance_page.js
+++ b/chrome/browser/resources/settings/appearance_page/appearance_page.js
@@ -119,7 +119,12 @@
   },
 
   /** @private {?settings.AppearanceBrowserProxy} */
-  browserProxy_: null,
+  appearanceBrowserProxy_: null,
+
+  // <if expr="chromeos">
+  /** @private {?settings.WallpaperBrowserProxy} */
+  wallpaperBrowserProxy_: null,
+  // </if>
 
   observers: [
     'defaultFontSizeChanged_(prefs.webkit.webprefs.default_font_size.value)',
@@ -133,7 +138,12 @@
 
   /** @override */
   created: function() {
-    this.browserProxy_ = settings.AppearanceBrowserProxyImpl.getInstance();
+    this.appearanceBrowserProxy_ =
+        settings.AppearanceBrowserProxyImpl.getInstance();
+    // <if expr="chromeos">
+    this.wallpaperBrowserProxy_ =
+        settings.WallpaperBrowserProxyImpl.getInstance();
+    // </if>
   },
 
   /** @override */
@@ -141,16 +151,16 @@
     this.$.defaultFontSize.menuOptions = this.fontSizeOptions_;
     // TODO(dschuyler): Look into adding a listener for the
     // default zoom percent.
-    this.browserProxy_.getDefaultZoom().then(zoom => {
+    this.appearanceBrowserProxy_.getDefaultZoom().then(zoom => {
       this.defaultZoom_ = zoom;
     });
     // <if expr="chromeos">
-    this.browserProxy_.isWallpaperSettingVisible().then(
+    this.wallpaperBrowserProxy_.isWallpaperSettingVisible().then(
         isWallpaperSettingVisible => {
           assert(this.pageVisibility);
           this.pageVisibility.setWallpaper = isWallpaperSettingVisible;
         });
-    this.browserProxy_.isWallpaperPolicyControlled().then(
+    this.wallpaperBrowserProxy_.isWallpaperPolicyControlled().then(
         isPolicyControlled => {
           this.isWallpaperPolicyControlled_ = isPolicyControlled;
         });
@@ -219,13 +229,13 @@
    * @private
    */
   openWallpaperManager_: function() {
-    this.browserProxy_.openWallpaperManager();
+    this.wallpaperBrowserProxy_.openWallpaperManager();
   },
   // </if>
 
   /** @private */
   onUseDefaultTap_: function() {
-    this.browserProxy_.useDefaultTheme();
+    this.appearanceBrowserProxy_.useDefaultTheme();
   },
 
   // <if expr="is_linux and not chromeos">
@@ -254,7 +264,8 @@
    * @private
    */
   showUseSystem_: function(themeId, useSystemTheme) {
-    return (!!themeId || !useSystemTheme) && !this.browserProxy_.isSupervised();
+    return (!!themeId || !useSystemTheme) &&
+        !this.appearanceBrowserProxy_.isSupervised();
   },
 
   /**
@@ -271,7 +282,7 @@
 
   /** @private */
   onUseSystemTap_: function() {
-    this.browserProxy_.useSystemTheme();
+    this.appearanceBrowserProxy_.useSystemTheme();
   },
   // </if>
 
@@ -288,7 +299,7 @@
     if (themeId.length > 0 && themeId != AUTOGENERATED_THEME_ID) {
       assert(!useSystemTheme);
 
-      this.browserProxy_.getThemeInfo(themeId).then(info => {
+      this.appearanceBrowserProxy_.getThemeInfo(themeId).then(info => {
         this.themeSublabel_ = info.name;
       });
 
diff --git a/chrome/browser/resources/settings/appearance_page/wallpaper_browser_proxy.html b/chrome/browser/resources/settings/appearance_page/wallpaper_browser_proxy.html
new file mode 100644
index 0000000..59b04ee
--- /dev/null
+++ b/chrome/browser/resources/settings/appearance_page/wallpaper_browser_proxy.html
@@ -0,0 +1,2 @@
+<link rel="href" src="chrome://resources/html/cr.html">
+<script src="wallpaper_browser_proxy.js"></script>
diff --git a/chrome/browser/resources/settings/chromeos/personalization_page/personalization_browser_proxy.js b/chrome/browser/resources/settings/appearance_page/wallpaper_browser_proxy.js
similarity index 73%
rename from chrome/browser/resources/settings/chromeos/personalization_page/personalization_browser_proxy.js
rename to chrome/browser/resources/settings/appearance_page/wallpaper_browser_proxy.js
index d8e26a95..90a2e4d 100644
--- a/chrome/browser/resources/settings/chromeos/personalization_page/personalization_browser_proxy.js
+++ b/chrome/browser/resources/settings/appearance_page/wallpaper_browser_proxy.js
@@ -4,7 +4,7 @@
 
 cr.define('settings', function() {
   /** @interface */
-  class PersonalizationBrowserProxy {
+  class WallpaperBrowserProxy {
     /**
      * @return {!Promise<boolean>} Whether the wallpaper setting row should be
      *     visible.
@@ -20,9 +20,9 @@
   }
 
   /**
-   * @implements {settings.PersonalizationBrowserProxy}
+   * @implements {settings.WallpaperBrowserProxy}
    */
-  class PersonalizationBrowserProxyImpl {
+  class WallpaperBrowserProxyImpl {
     /** @override */
     isWallpaperSettingVisible() {
       return cr.sendWithPromise('isWallpaperSettingVisible');
@@ -39,10 +39,10 @@
     }
   }
 
-  cr.addSingletonGetter(PersonalizationBrowserProxyImpl);
+  cr.addSingletonGetter(WallpaperBrowserProxyImpl);
 
   return {
-    PersonalizationBrowserProxy: PersonalizationBrowserProxy,
-    PersonalizationBrowserProxyImpl: PersonalizationBrowserProxyImpl,
+    WallpaperBrowserProxy: WallpaperBrowserProxy,
+    WallpaperBrowserProxyImpl: WallpaperBrowserProxyImpl,
   };
 });
diff --git a/chrome/browser/resources/settings/chromeos/os_settings_menu/os_settings_menu.html b/chrome/browser/resources/settings/chromeos/os_settings_menu/os_settings_menu.html
index 16a9604..5726b61d 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings_menu/os_settings_menu.html
+++ b/chrome/browser/resources/settings/chromeos/os_settings_menu/os_settings_menu.html
@@ -163,6 +163,12 @@
           $i18n{peoplePageTitle}
         </div>
       </a>
+      <a href="/device">
+        <div class="item">
+          <iron-icon icon="os-settings:laptop-chromebook"></iron-icon>
+          $i18n{devicePageTitle}
+        </div>
+      </a>
       <a id="personalization" href="/personalization"
           hidden="[[isGuestMode_]]">
         <div class="item">
@@ -170,12 +176,6 @@
           $i18n{personalizationPageTitle}
         </div>
       </a>
-      <a href="/device">
-        <div class="item">
-          <iron-icon icon="os-settings:laptop-chromebook"></iron-icon>
-          $i18n{devicePageTitle}
-        </div>
-      </a>
       <a href="/search">
         <div class="item">
           <iron-icon icon="cr:search"></iron-icon>
diff --git a/chrome/browser/resources/settings/chromeos/os_settings_page/os_settings_page.html b/chrome/browser/resources/settings/chromeos/os_settings_page/os_settings_page.html
index 5d2be942..907af4d 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings_page/os_settings_page.html
+++ b/chrome/browser/resources/settings/chromeos/os_settings_page/os_settings_page.html
@@ -151,18 +151,18 @@
             </os-settings-people-page>
           </settings-section>
         </template>
-        <template is="dom-if" if="[[!isGuestMode_]]">
-          <settings-section page-title="$i18n{personalizationPageTitle}"
-              section="personalization">
-            <settings-personalization-page></settings-personalization-page>
-          </settings-section>
-        </template>
         <settings-section page-title="$i18n{devicePageTitle}"
             section="device">
           <settings-device-page prefs="{{prefs}}"
               show-crostini="[[showCrostini]]">
           </settings-device-page>
         </settings-section>
+        <template is="dom-if" if="[[!isGuestMode_]]">
+          <settings-section page-title="$i18n{personalizationPageTitle}"
+              section="personalization">
+            <settings-personalization-page></settings-personalization-page>
+          </settings-section>
+        </template>
         <settings-section page-title="$i18n{osSearchPageTitle}"
             section="search">
           <os-settings-search-page prefs="{{prefs}}">
diff --git a/chrome/browser/resources/settings/chromeos/personalization_page/BUILD.gn b/chrome/browser/resources/settings/chromeos/personalization_page/BUILD.gn
index 7e68a16..ca1cedf 100644
--- a/chrome/browser/resources/settings/chromeos/personalization_page/BUILD.gn
+++ b/chrome/browser/resources/settings/chromeos/personalization_page/BUILD.gn
@@ -6,21 +6,13 @@
 
 js_type_check("closure_compile") {
   deps = [
-    ":personalization_browser_proxy",
     ":personalization_page",
   ]
 }
 
-js_library("personalization_browser_proxy") {
-  deps = [
-    "//ui/webui/resources/js:cr",
-  ]
-  externs_list = [ "$externs_path/chrome_send.js" ]
-}
-
 js_library("personalization_page") {
   deps = [
-    ":personalization_browser_proxy",
+    "../../appearance_page:wallpaper_browser_proxy",
     "../../settings_page:settings_animated_pages",
     "//ui/webui/resources/js:cr",
   ]
diff --git a/chrome/browser/resources/settings/chromeos/personalization_page/personalization_browser_proxy.html b/chrome/browser/resources/settings/chromeos/personalization_page/personalization_browser_proxy.html
deleted file mode 100644
index 2061065..0000000
--- a/chrome/browser/resources/settings/chromeos/personalization_page/personalization_browser_proxy.html
+++ /dev/null
@@ -1,2 +0,0 @@
-<link rel="href" src="chrome://resources/html/cr.html">
-<script src="personalization_browser_proxy.js"></script>
diff --git a/chrome/browser/resources/settings/chromeos/personalization_page/personalization_page.html b/chrome/browser/resources/settings/chromeos/personalization_page/personalization_page.html
index 023df0a2..1597a2a 100644
--- a/chrome/browser/resources/settings/chromeos/personalization_page/personalization_page.html
+++ b/chrome/browser/resources/settings/chromeos/personalization_page/personalization_page.html
@@ -1,7 +1,7 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
 <link rel="import" href="chrome://resources/cr_elements/cr_link_row/cr_link_row.html">
-<link rel="import" href="personalization_browser_proxy.html">
+<link rel="import" href="../../appearance_page/wallpaper_browser_proxy.html">
 <link rel="import" href="../../people_page/change_picture.html">
 <link rel="import" href="../../route.html">
 <link rel="import" href="../../settings_page/settings_animated_pages.html">
diff --git a/chrome/browser/resources/settings/chromeos/personalization_page/personalization_page.js b/chrome/browser/resources/settings/chromeos/personalization_page/personalization_page.js
index c836834..c67a0883 100644
--- a/chrome/browser/resources/settings/chromeos/personalization_page/personalization_page.js
+++ b/chrome/browser/resources/settings/chromeos/personalization_page/personalization_page.js
@@ -32,12 +32,12 @@
     },
   },
 
-  /** @private {?settings.PersonalizationBrowserProxy} */
+  /** @private {?settings.WallpaperBrowserProxy} */
   browserProxy_: null,
 
   /** @override */
   created: function() {
-    this.browserProxy_ = settings.PersonalizationBrowserProxyImpl.getInstance();
+    this.browserProxy_ = settings.WallpaperBrowserProxyImpl.getInstance();
   },
 
   /** @override */
diff --git a/chrome/browser/resources/settings/os_settings_resources.grd b/chrome/browser/resources/settings/os_settings_resources.grd
index fa1e5af..546bcce 100644
--- a/chrome/browser/resources/settings/os_settings_resources.grd
+++ b/chrome/browser/resources/settings/os_settings_resources.grd
@@ -125,12 +125,6 @@
                  file="settings_page/settings_animated_pages.js"
                  type="chrome_html"
                  preprocess="true" />
-      <structure name="IDR_OS_SETTINGS_PERSONALIZATION_BROWSER_PROXY_HTML"
-                 file="chromeos/personalization_page/personalization_browser_proxy.html"
-                 type="chrome_html" />
-      <structure name="IDR_OS_SETTINGS_PERSONALIZATION_BROWSER_PROXY_JS"
-                 file="chromeos/personalization_page/personalization_browser_proxy.js"
-                 type="chrome_html" />
       <structure name="IDR_OS_SETTINGS_PERSONALIZATION_PAGE_HTML"
                  file="chromeos/personalization_page/personalization_page.html"
                  type="chrome_html"
@@ -1351,6 +1345,12 @@
                  type="chrome_html"
                  preprocess="true"
                  allowexternalscript="true" />
+      <structure name="IDR_OS_SETTINGS_WALLPAPER_BROWSER_PROXY_HTML"
+                 file="appearance_page/wallpaper_browser_proxy.html"
+                 type="chrome_html" />
+      <structure name="IDR_OS_SETTINGS_WALLPAPER_BROWSER_PROXY_JS"
+                 file="appearance_page/wallpaper_browser_proxy.js"
+                 type="chrome_html" />
     </structures>
   </release>
 </grit>
diff --git a/chrome/browser/resources/settings/settings_resources.grd b/chrome/browser/resources/settings/settings_resources.grd
index 682e3b8a..6fad16b 100644
--- a/chrome/browser/resources/settings/settings_resources.grd
+++ b/chrome/browser/resources/settings/settings_resources.grd
@@ -151,6 +151,14 @@
                  file="appearance_page/appearance_browser_proxy.js"
                  type="chrome_html"
                  preprocess="true" />
+      <if expr="chromeos">
+        <structure name="IDR_SETTINGS_WALLPAPER_BROWSER_PROXY_HTML"
+                   file="appearance_page/wallpaper_browser_proxy.html"
+                   type="chrome_html" />
+        <structure name="IDR_SETTINGS_WALLPAPER_BROWSER_PROXY_JS"
+                   file="appearance_page/wallpaper_browser_proxy.js"
+                   type="chrome_html" />
+      </if>
       <structure name="IDR_SETTINGS_APPEARANCE_FONTS_PAGE_HTML"
                  file="appearance_page/appearance_fonts_page.html"
                  type="chrome_html"
diff --git a/chrome/browser/sync/OWNERS b/chrome/browser/sync/OWNERS
index bcfd98f..f3387ed 100644
--- a/chrome/browser/sync/OWNERS
+++ b/chrome/browser/sync/OWNERS
@@ -1,5 +1,6 @@
 file://components/sync/OWNERS
 
 per-file profile_sync_service_android*=nyquist@chromium.org
+per-file *web_app*=file://chrome/browser/web_applications/OWNERS
 
 # COMPONENT: Services>Sync
diff --git a/chrome/browser/sync/test/integration/two_client_apps_sync_test.cc b/chrome/browser/sync/test/integration/two_client_apps_sync_test.cc
deleted file mode 100644
index 002541b..0000000
--- a/chrome/browser/sync/test/integration/two_client_apps_sync_test.cc
+++ /dev/null
@@ -1,557 +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 <stddef.h>
-#include <memory>
-#include <utility>
-
-#include "base/bind.h"
-#include "base/macros.h"
-#include "base/strings/utf_string_conversions.h"
-#include "chrome/browser/chrome_notification_types.h"
-#include "chrome/browser/extensions/extension_sync_data.h"
-#include "chrome/browser/extensions/extension_sync_service.h"
-#include "chrome/browser/extensions/launch_util.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/sync/test/integration/apps_helper.h"
-#include "chrome/browser/sync/test/integration/profile_sync_service_harness.h"
-#include "chrome/browser/sync/test/integration/sync_app_helper.h"
-#include "chrome/browser/sync/test/integration/sync_integration_test_util.h"
-#include "chrome/browser/sync/test/integration/sync_test.h"
-#include "chrome/browser/web_applications/components/install_manager.h"
-#include "chrome/browser/web_applications/components/web_app_provider_base.h"
-#include "chrome/browser/web_applications/extensions/bookmark_app_util.h"
-#include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
-#include "chrome/common/extensions/manifest_handlers/app_theme_color_info.h"
-#include "chrome/common/web_application_info.h"
-#include "components/sync/model/string_ordinal.h"
-#include "content/public/browser/notification_service.h"
-#include "content/public/test/test_utils.h"
-#include "extensions/browser/app_sorting.h"
-#include "extensions/browser/extension_prefs.h"
-#include "extensions/browser/extension_registry.h"
-#include "extensions/browser/extension_system.h"
-#include "extensions/common/constants.h"
-
-using apps_helper::AllProfilesHaveSameApps;
-using apps_helper::CopyNTPOrdinals;
-using apps_helper::DisableApp;
-using apps_helper::EnableApp;
-using apps_helper::FixNTPOrdinalCollisions;
-using apps_helper::GetAppLaunchOrdinalForApp;
-using apps_helper::IncognitoDisableApp;
-using apps_helper::IncognitoEnableApp;
-using apps_helper::InstallApp;
-using apps_helper::InstallPlatformApp;
-using apps_helper::SetAppLaunchOrdinalForApp;
-using apps_helper::SetPageOrdinalForApp;
-using apps_helper::UninstallApp;
-
-namespace {
-
-extensions::ExtensionRegistry* GetExtensionRegistry(Profile* profile) {
-  return extensions::ExtensionRegistry::Get(profile);
-}
-
-}  // namespace
-
-class TwoClientAppsSyncTest : public SyncTest {
- public:
-  TwoClientAppsSyncTest() : SyncTest(TWO_CLIENT) { DisableVerifier(); }
-
-  ~TwoClientAppsSyncTest() override {}
-
-  // Needed for AwaitQuiescence().
-  bool TestUsesSelfNotifications() override { return true; }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(TwoClientAppsSyncTest);
-};
-
-IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest, E2E_ENABLED(StartWithNoApps)) {
-  ASSERT_TRUE(SetupSync());
-  ASSERT_TRUE(AppsMatchChecker().Wait());
-}
-
-IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest, E2E_ENABLED(StartWithSameApps)) {
-  ASSERT_TRUE(SetupClients());
-
-  const int kNumApps = 5;
-  for (int i = 0; i < kNumApps; ++i) {
-    InstallApp(GetProfile(0), i);
-    InstallApp(GetProfile(1), i);
-  }
-
-  ASSERT_TRUE(SetupSync());
-  ASSERT_TRUE(AppsMatchChecker().Wait());
-}
-
-// Install some apps on both clients, some on only one client, some on only the
-// other, and sync.  Both clients should end up with all apps, and the app and
-// page ordinals should be identical.
-IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest, StartWithDifferentApps) {
-  ASSERT_TRUE(SetupClients());
-
-  int i = 0;
-
-  const int kNumCommonApps = 5;
-  for (int j = 0; j < kNumCommonApps; ++i, ++j) {
-    InstallApp(GetProfile(0), i);
-    InstallApp(GetProfile(1), i);
-  }
-
-  const int kNumProfile0Apps = 10;
-  for (int j = 0; j < kNumProfile0Apps; ++i, ++j) {
-    InstallApp(GetProfile(0), i);
-  }
-
-  const int kNumProfile1Apps = 10;
-  for (int j = 0; j < kNumProfile1Apps; ++i, ++j) {
-    InstallApp(GetProfile(1), i);
-  }
-
-  const int kNumPlatformApps = 5;
-  for (int j = 0; j < kNumPlatformApps; ++i, ++j) {
-    InstallPlatformApp(GetProfile(1), i);
-  }
-
-  ASSERT_TRUE(SetupSync());
-  ASSERT_TRUE(AppsMatchChecker().Wait());
-}
-
-// Install some apps on both clients, then sync.  Then install some apps on only
-// one client, some on only the other, and then sync again.  Both clients should
-// end up with all apps, and the app and page ordinals should be identical.
-IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest,
-                       E2E_ENABLED(InstallDifferentApps)) {
-  ASSERT_TRUE(SetupClients());
-
-  int i = 0;
-
-  const int kNumCommonApps = 5;
-  for (int j = 0; j < kNumCommonApps; ++i, ++j) {
-    InstallApp(GetProfile(0), i);
-    InstallApp(GetProfile(1), i);
-  }
-
-  ASSERT_TRUE(SetupSync());
-
-  const int kNumProfile0Apps = 10;
-  for (int j = 0; j < kNumProfile0Apps; ++i, ++j) {
-    InstallApp(GetProfile(0), i);
-  }
-
-  const int kNumProfile1Apps = 10;
-  for (int j = 0; j < kNumProfile1Apps; ++i, ++j) {
-    InstallApp(GetProfile(1), i);
-  }
-
-  ASSERT_TRUE(AppsMatchChecker().Wait());
-}
-
-IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest, E2E_ENABLED(Add)) {
-  ASSERT_TRUE(SetupSync());
-  ASSERT_TRUE(AppsMatchChecker().Wait());
-
-  InstallApp(GetProfile(0), 0);
-
-  ASSERT_TRUE(AppsMatchChecker().Wait());
-}
-
-IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest, E2E_ENABLED(Uninstall)) {
-  ASSERT_TRUE(SetupSync());
-  ASSERT_TRUE(AppsMatchChecker().Wait());
-
-  InstallApp(GetProfile(0), 0);
-  ASSERT_TRUE(AppsMatchChecker().Wait());
-
-  UninstallApp(GetProfile(0), 0);
-  ASSERT_TRUE(AppsMatchChecker().Wait());
-}
-
-// Install an app on one client, then sync. Then uninstall the app on the first
-// client and sync again. Now install a new app on the first client and sync.
-// Both client should only have the second app, with identical app and page
-// ordinals.
-IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest,
-                       E2E_ENABLED(UninstallThenInstall)) {
-  ASSERT_TRUE(SetupSync());
-  ASSERT_TRUE(AppsMatchChecker().Wait());
-
-  InstallApp(GetProfile(0), 0);
-  ASSERT_TRUE(AppsMatchChecker().Wait());
-
-  UninstallApp(GetProfile(0), 0);
-  ASSERT_TRUE(AppsMatchChecker().Wait());
-
-  InstallApp(GetProfile(0), 1);
-  ASSERT_TRUE(AppsMatchChecker().Wait());
-}
-
-IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest, E2E_ENABLED(Merge)) {
-  ASSERT_TRUE(SetupSync());
-  ASSERT_TRUE(AppsMatchChecker().Wait());
-
-  InstallApp(GetProfile(0), 0);
-  ASSERT_TRUE(AppsMatchChecker().Wait());
-
-  UninstallApp(GetProfile(0), 0);
-
-  InstallApp(GetProfile(0), 1);
-  InstallApp(GetProfile(0), 2);
-
-  InstallApp(GetProfile(1), 2);
-  InstallApp(GetProfile(1), 3);
-
-  ASSERT_TRUE(AppsMatchChecker().Wait());
-}
-
-IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest,
-                       E2E_ENABLED(UpdateEnableDisableApp)) {
-  ASSERT_TRUE(SetupSync());
-  ASSERT_TRUE(AppsMatchChecker().Wait());
-
-  InstallApp(GetProfile(0), 0);
-  ASSERT_TRUE(AppsMatchChecker().Wait());
-
-  DisableApp(GetProfile(0), 0);
-  ASSERT_TRUE(AppsMatchChecker().Wait());
-
-  EnableApp(GetProfile(1), 0);
-  ASSERT_TRUE(AppsMatchChecker().Wait());
-}
-
-IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest,
-                       E2E_ENABLED(UpdateIncognitoEnableDisable)) {
-  ASSERT_TRUE(SetupSync());
-  ASSERT_TRUE(AppsMatchChecker().Wait());
-
-  InstallApp(GetProfile(0), 0);
-  ASSERT_TRUE(AppsMatchChecker().Wait());
-
-  IncognitoEnableApp(GetProfile(0), 0);
-  ASSERT_TRUE(AppsMatchChecker().Wait());
-
-  IncognitoDisableApp(GetProfile(1), 0);
-  ASSERT_TRUE(AppsMatchChecker().Wait());
-}
-
-// Install the same app on both clients, then sync. Change the page ordinal on
-// one client and sync. Both clients should have the updated page ordinal for
-// the app.
-IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest, E2E_ENABLED(UpdatePageOrdinal)) {
-  ASSERT_TRUE(SetupSync());
-  ASSERT_TRUE(AppsMatchChecker().Wait());
-
-  syncer::StringOrdinal initial_page =
-      syncer::StringOrdinal::CreateInitialOrdinal();
-  InstallApp(GetProfile(0), 0);
-  ASSERT_TRUE(AppsMatchChecker().Wait());
-
-  syncer::StringOrdinal second_page = initial_page.CreateAfter();
-  SetPageOrdinalForApp(GetProfile(0), 0, second_page);
-  ASSERT_TRUE(AppsMatchChecker().Wait());
-}
-
-// Install the same app on both clients, then sync. Change the app launch
-// ordinal on one client and sync. Both clients should have the updated app
-// launch ordinal for the app.
-IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest,
-                       E2E_ENABLED(UpdateAppLaunchOrdinal)) {
-  ASSERT_TRUE(SetupSync());
-  ASSERT_TRUE(AppsMatchChecker().Wait());
-
-  InstallApp(GetProfile(0), 0);
-  ASSERT_TRUE(AppsMatchChecker().Wait());
-
-  syncer::StringOrdinal initial_position =
-      GetAppLaunchOrdinalForApp(GetProfile(0), 0);
-
-  syncer::StringOrdinal second_position = initial_position.CreateAfter();
-  SetAppLaunchOrdinalForApp(GetProfile(0), 0, second_position);
-  ASSERT_TRUE(AppsMatchChecker().Wait());
-}
-
-// Adjust the CWS location within a page on the first client and sync. Adjust
-// which page the CWS appears on and sync. Both clients should have the same
-// page and app launch ordinal values for the CWS.
-IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest, E2E_ENABLED(UpdateCWSOrdinals)) {
-  ASSERT_TRUE(SetupSync());
-  ASSERT_TRUE(AppsMatchChecker().Wait());
-
-  // Change the app launch ordinal.
-  syncer::StringOrdinal cws_app_launch_ordinal =
-      extensions::ExtensionSystem::Get(GetProfile(0))
-          ->app_sorting()
-          ->GetAppLaunchOrdinal(extensions::kWebStoreAppId);
-  extensions::ExtensionSystem::Get(GetProfile(0))
-      ->app_sorting()
-      ->SetAppLaunchOrdinal(extensions::kWebStoreAppId,
-                            cws_app_launch_ordinal.CreateAfter());
-  ASSERT_TRUE(AppsMatchChecker().Wait());
-
-  // Change the page ordinal.
-  syncer::StringOrdinal cws_page_ordinal =
-      extensions::ExtensionSystem::Get(GetProfile(1))
-          ->app_sorting()
-          ->GetPageOrdinal(extensions::kWebStoreAppId);
-  extensions::ExtensionSystem::Get(GetProfile(1))
-      ->app_sorting()
-      ->SetPageOrdinal(extensions::kWebStoreAppId,
-                       cws_page_ordinal.CreateAfter());
-  ASSERT_TRUE(AppsMatchChecker().Wait());
-}
-
-// Adjust the launch type on the first client and sync. Both clients should
-// have the same launch type values for the CWS.
-IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest, E2E_ENABLED(UpdateLaunchType)) {
-  ASSERT_TRUE(SetupSync());
-  // Wait until sync settles before we override the apps below.
-  ASSERT_TRUE(AwaitQuiescence());
-  ASSERT_TRUE(AppsMatchChecker().Wait());
-
-  // Change the launch type to window.
-  extensions::SetLaunchType(GetProfile(1), extensions::kWebStoreAppId,
-                            extensions::LAUNCH_TYPE_WINDOW);
-  ASSERT_TRUE(AppsMatchChecker().Wait());
-  ASSERT_EQ(
-      extensions::GetLaunchTypePrefValue(
-          extensions::ExtensionPrefs::Get(GetProfile(0)),
-          extensions::kWebStoreAppId),
-      extensions::LAUNCH_TYPE_WINDOW);
-
-  // Change the launch type to regular tab.
-  extensions::SetLaunchType(GetProfile(1), extensions::kWebStoreAppId,
-                            extensions::LAUNCH_TYPE_REGULAR);
-  ASSERT_TRUE(AppsMatchChecker().Wait());
-
-  ASSERT_EQ(
-      extensions::GetLaunchTypePrefValue(
-          extensions::ExtensionPrefs::Get(GetProfile(0)),
-          extensions::kWebStoreAppId),
-      extensions::LAUNCH_TYPE_REGULAR);
-}
-
-IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest, UnexpectedLaunchType) {
-  ASSERT_TRUE(SetupSync());
-  // Wait until sync settles before we override the apps below.
-  ASSERT_TRUE(AwaitQuiescence());
-  ASSERT_TRUE(AllProfilesHaveSameApps());
-
-  extensions::SetLaunchType(GetProfile(1), extensions::kWebStoreAppId,
-                            extensions::LAUNCH_TYPE_REGULAR);
-  ASSERT_TRUE(AppsMatchChecker().Wait());
-
-  const extensions::Extension* extension =
-      GetExtensionRegistry(GetProfile(1))->GetExtensionById(
-          extensions::kWebStoreAppId,
-          extensions::ExtensionRegistry::EVERYTHING);
-  ASSERT_TRUE(extension);
-
-  ExtensionSyncService* extension_sync_service =
-      ExtensionSyncService::Get(GetProfile(1));
-
-  extensions::ExtensionSyncData original_data(
-      extension_sync_service->CreateSyncData(*extension));
-
-  // Create an invalid launch type and ensure it doesn't get down-synced. This
-  // simulates the case of a future launch type being added which old versions
-  // don't yet understand.
-  extensions::ExtensionSyncData invalid_launch_type_data(
-      *extension,
-      original_data.enabled(),
-      original_data.disable_reasons(),
-      original_data.incognito_enabled(),
-      original_data.remote_install(),
-      original_data.installed_by_custodian(),
-      original_data.app_launch_ordinal(),
-      original_data.page_ordinal(),
-      extensions::NUM_LAUNCH_TYPES);
-  extension_sync_service->ApplySyncData(invalid_launch_type_data);
-
-  // The launch type should remain the same.
-  ASSERT_TRUE(AppsMatchChecker().Wait());
-}
-
-IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest, BookmarkAppBasic) {
-  ASSERT_TRUE(SetupSync());
-  ASSERT_TRUE(AllProfilesHaveSameApps());
-
-  size_t num_extensions =
-      GetExtensionRegistry(GetProfile(0))->enabled_extensions().size();
-
-  auto web_app_info = std::make_unique<WebApplicationInfo>();
-  web_app_info->app_url = GURL("http://www.chromium.org/path");
-  web_app_info->scope = GURL("http://www.chromium.org/");
-  web_app_info->title = base::UTF8ToUTF16("Test name");
-  web_app_info->description = base::UTF8ToUTF16("Test description");
-  ++num_extensions;
-  {
-    content::WindowedNotificationObserver windowed_observer(
-        extensions::NOTIFICATION_CRX_INSTALLER_DONE,
-        content::NotificationService::AllSources());
-
-    auto* provider =
-        web_app::WebAppProviderBase::GetProviderBase(GetProfile(0));
-    DCHECK(provider);
-    provider->install_manager().InstallWebAppForTesting(std::move(web_app_info),
-                                                        base::DoNothing());
-
-    windowed_observer.Wait();
-    EXPECT_EQ(num_extensions,
-              GetExtensionRegistry(GetProfile(0))->enabled_extensions().size());
-  }
-  {
-    // Wait for the synced app to install.
-    content::WindowedNotificationObserver windowed_observer(
-        extensions::NOTIFICATION_CRX_INSTALLER_DONE,
-        base::BindRepeating(&AllProfilesHaveSameApps));
-    windowed_observer.Wait();
-  }
-}
-
-IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest, BookmarkAppMinimal) {
-  ASSERT_TRUE(SetupSync());
-  ASSERT_TRUE(AllProfilesHaveSameApps());
-
-  size_t num_extensions =
-      GetExtensionRegistry(GetProfile(0))->enabled_extensions().size();
-
-  auto web_app_info = std::make_unique<WebApplicationInfo>();
-  web_app_info->app_url = GURL("http://www.chromium.org/");
-  web_app_info->title = base::UTF8ToUTF16("Test name");
-  ++num_extensions;
-  {
-    content::WindowedNotificationObserver windowed_observer(
-        extensions::NOTIFICATION_CRX_INSTALLER_DONE,
-        content::NotificationService::AllSources());
-
-    auto* provider =
-        web_app::WebAppProviderBase::GetProviderBase(GetProfile(0));
-    DCHECK(provider);
-    provider->install_manager().InstallWebAppForTesting(std::move(web_app_info),
-                                                        base::DoNothing());
-
-    windowed_observer.Wait();
-    EXPECT_EQ(num_extensions,
-              GetExtensionRegistry(GetProfile(0))->enabled_extensions().size());
-  }
-  {
-    // Wait for the synced app to install.
-    content::WindowedNotificationObserver windowed_observer(
-        extensions::NOTIFICATION_CRX_INSTALLER_DONE,
-        base::BindRepeating(&AllProfilesHaveSameApps));
-    windowed_observer.Wait();
-  }
-}
-
-const extensions::Extension* GetAppByLaunchURL(const GURL& url,
-                                               Profile* profile) {
-  for (auto extension_it :
-       GetExtensionRegistry(profile)->enabled_extensions()) {
-    if (extensions::AppLaunchInfo::GetLaunchWebURL(extension_it.get()) == url)
-      return extension_it.get();
-  }
-
-  return nullptr;
-}
-
-IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest, BookmarkAppThemeColor) {
-  ASSERT_TRUE(SetupSync());
-  ASSERT_TRUE(AllProfilesHaveSameApps());
-
-  size_t num_extensions =
-      GetExtensionRegistry(GetProfile(0))->enabled_extensions().size();
-
-  const GURL app_url("http://www.chromium.org/");
-  auto web_app_info = std::make_unique<WebApplicationInfo>();
-  web_app_info->app_url = app_url;
-  web_app_info->title = base::UTF8ToUTF16("Test name");
-  web_app_info->theme_color = SK_ColorBLUE;
-  ++num_extensions;
-  {
-    content::WindowedNotificationObserver windowed_observer(
-        extensions::NOTIFICATION_CRX_INSTALLER_DONE,
-        content::NotificationService::AllSources());
-
-    auto* provider =
-        web_app::WebAppProviderBase::GetProviderBase(GetProfile(0));
-    DCHECK(provider);
-    provider->install_manager().InstallWebAppForTesting(std::move(web_app_info),
-                                                        base::DoNothing());
-
-    windowed_observer.Wait();
-    EXPECT_EQ(num_extensions,
-              GetExtensionRegistry(GetProfile(0))->enabled_extensions().size());
-  }
-  {
-    // Wait for the synced app to install.
-    content::WindowedNotificationObserver windowed_observer(
-        extensions::NOTIFICATION_CRX_INSTALLER_DONE,
-        base::BindRepeating(&AllProfilesHaveSameApps));
-    windowed_observer.Wait();
-  }
-  auto* extension = GetAppByLaunchURL(app_url, GetProfile(1));
-  base::Optional<SkColor> theme_color =
-      extensions::AppThemeColorInfo::GetThemeColor(extension);
-  EXPECT_EQ(SK_ColorBLUE, theme_color.value());
-}
-
-IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest, IsLocallyInstalled) {
-  ASSERT_TRUE(SetupSync());
-  ASSERT_TRUE(AllProfilesHaveSameApps());
-
-  size_t num_extensions =
-      GetExtensionRegistry(GetProfile(0))->enabled_extensions().size();
-
-  const GURL app_url("http://www.chromium.org/");
-  auto web_app_info = std::make_unique<WebApplicationInfo>();
-  web_app_info->app_url = app_url;
-  web_app_info->title = base::UTF8ToUTF16("Test name");
-  web_app_info->theme_color = SK_ColorBLUE;
-  ++num_extensions;
-  {
-    content::WindowedNotificationObserver windowed_observer(
-        extensions::NOTIFICATION_CRX_INSTALLER_DONE,
-        content::NotificationService::AllSources());
-
-    auto* provider =
-        web_app::WebAppProviderBase::GetProviderBase(GetProfile(0));
-    DCHECK(provider);
-    provider->install_manager().InstallWebAppForTesting(std::move(web_app_info),
-                                                        base::DoNothing());
-
-    windowed_observer.Wait();
-    EXPECT_EQ(num_extensions,
-              GetExtensionRegistry(GetProfile(0))->enabled_extensions().size());
-  }
-  {
-    // Wait for the synced app to install.
-    content::WindowedNotificationObserver windowed_observer(
-        extensions::NOTIFICATION_CRX_INSTALLER_DONE,
-        base::BindRepeating(&AllProfilesHaveSameApps));
-    windowed_observer.Wait();
-
-    // The is_locally_installed pref is set in a post install task which
-    // completes asynchronously after the CRX_INSTALLER_DONE notification is
-    // sent. This test needs to wait for this to complete before checking the
-    // is_locally_installed_pref, so it waits until all tasks are complete.
-    //
-    // Other tests do not need to do this, as all the fields they check are set
-    // before the CRX_INSTALLER_DONE notification is sent.
-    //
-    // Note this cannot replace the CRX_INSTALLER_DONE notification observer as
-    // it would not wait for the sync stuff to happen.
-    content::RunAllTasksUntilIdle();
-  }
-  auto* extension = GetAppByLaunchURL(app_url, GetProfile(1));
-#if defined(OS_CHROMEOS)
-  EXPECT_TRUE(BookmarkAppIsLocallyInstalled(GetProfile(1), extension));
-#else
-  EXPECT_FALSE(BookmarkAppIsLocallyInstalled(GetProfile(1), extension));
-#endif
-}
-// TODO(akalin): Add tests exercising:
-//   - Offline installation/uninstallation behavior
-//   - App-specific properties
diff --git a/chrome/browser/sync/test/integration/two_client_extension_apps_sync_test.cc b/chrome/browser/sync/test/integration/two_client_extension_apps_sync_test.cc
new file mode 100644
index 0000000..b6bbedc6
--- /dev/null
+++ b/chrome/browser/sync/test/integration/two_client_extension_apps_sync_test.cc
@@ -0,0 +1,375 @@
+// 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 <stddef.h>
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/macros.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/extensions/extension_sync_data.h"
+#include "chrome/browser/extensions/extension_sync_service.h"
+#include "chrome/browser/extensions/launch_util.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/sync/test/integration/apps_helper.h"
+#include "chrome/browser/sync/test/integration/profile_sync_service_harness.h"
+#include "chrome/browser/sync/test/integration/sync_app_helper.h"
+#include "chrome/browser/sync/test/integration/sync_integration_test_util.h"
+#include "chrome/browser/sync/test/integration/sync_test.h"
+#include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
+#include "chrome/common/extensions/manifest_handlers/app_theme_color_info.h"
+#include "chrome/common/web_application_info.h"
+#include "components/sync/model/string_ordinal.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/test/test_utils.h"
+#include "extensions/browser/app_sorting.h"
+#include "extensions/browser/extension_prefs.h"
+#include "extensions/browser/extension_registry.h"
+#include "extensions/browser/extension_system.h"
+#include "extensions/common/constants.h"
+
+using apps_helper::AllProfilesHaveSameApps;
+using apps_helper::CopyNTPOrdinals;
+using apps_helper::DisableApp;
+using apps_helper::EnableApp;
+using apps_helper::FixNTPOrdinalCollisions;
+using apps_helper::GetAppLaunchOrdinalForApp;
+using apps_helper::IncognitoDisableApp;
+using apps_helper::IncognitoEnableApp;
+using apps_helper::InstallApp;
+using apps_helper::InstallPlatformApp;
+using apps_helper::SetAppLaunchOrdinalForApp;
+using apps_helper::SetPageOrdinalForApp;
+using apps_helper::UninstallApp;
+
+namespace {
+
+extensions::ExtensionRegistry* GetExtensionRegistry(Profile* profile) {
+  return extensions::ExtensionRegistry::Get(profile);
+}
+
+}  // namespace
+
+class TwoClientExtensionAppsSyncTest : public SyncTest {
+ public:
+  TwoClientExtensionAppsSyncTest() : SyncTest(TWO_CLIENT) { DisableVerifier(); }
+
+  ~TwoClientExtensionAppsSyncTest() override {}
+
+  // Needed for AwaitQuiescence().
+  bool TestUsesSelfNotifications() override { return true; }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TwoClientExtensionAppsSyncTest);
+};
+
+IN_PROC_BROWSER_TEST_F(TwoClientExtensionAppsSyncTest,
+                       E2E_ENABLED(StartWithNoApps)) {
+  ASSERT_TRUE(SetupSync());
+  ASSERT_TRUE(AppsMatchChecker().Wait());
+}
+
+IN_PROC_BROWSER_TEST_F(TwoClientExtensionAppsSyncTest,
+                       E2E_ENABLED(StartWithSameApps)) {
+  ASSERT_TRUE(SetupClients());
+
+  const int kNumApps = 5;
+  for (int i = 0; i < kNumApps; ++i) {
+    InstallApp(GetProfile(0), i);
+    InstallApp(GetProfile(1), i);
+  }
+
+  ASSERT_TRUE(SetupSync());
+  ASSERT_TRUE(AppsMatchChecker().Wait());
+}
+
+// Install some apps on both clients, some on only one client, some on only the
+// other, and sync.  Both clients should end up with all apps, and the app and
+// page ordinals should be identical.
+IN_PROC_BROWSER_TEST_F(TwoClientExtensionAppsSyncTest, StartWithDifferentApps) {
+  ASSERT_TRUE(SetupClients());
+
+  int i = 0;
+
+  const int kNumCommonApps = 5;
+  for (int j = 0; j < kNumCommonApps; ++i, ++j) {
+    InstallApp(GetProfile(0), i);
+    InstallApp(GetProfile(1), i);
+  }
+
+  const int kNumProfile0Apps = 10;
+  for (int j = 0; j < kNumProfile0Apps; ++i, ++j) {
+    InstallApp(GetProfile(0), i);
+  }
+
+  const int kNumProfile1Apps = 10;
+  for (int j = 0; j < kNumProfile1Apps; ++i, ++j) {
+    InstallApp(GetProfile(1), i);
+  }
+
+  const int kNumPlatformApps = 5;
+  for (int j = 0; j < kNumPlatformApps; ++i, ++j) {
+    InstallPlatformApp(GetProfile(1), i);
+  }
+
+  ASSERT_TRUE(SetupSync());
+  ASSERT_TRUE(AppsMatchChecker().Wait());
+}
+
+// Install some apps on both clients, then sync.  Then install some apps on only
+// one client, some on only the other, and then sync again.  Both clients should
+// end up with all apps, and the app and page ordinals should be identical.
+IN_PROC_BROWSER_TEST_F(TwoClientExtensionAppsSyncTest,
+                       E2E_ENABLED(InstallDifferentApps)) {
+  ASSERT_TRUE(SetupClients());
+
+  int i = 0;
+
+  const int kNumCommonApps = 5;
+  for (int j = 0; j < kNumCommonApps; ++i, ++j) {
+    InstallApp(GetProfile(0), i);
+    InstallApp(GetProfile(1), i);
+  }
+
+  ASSERT_TRUE(SetupSync());
+
+  const int kNumProfile0Apps = 10;
+  for (int j = 0; j < kNumProfile0Apps; ++i, ++j) {
+    InstallApp(GetProfile(0), i);
+  }
+
+  const int kNumProfile1Apps = 10;
+  for (int j = 0; j < kNumProfile1Apps; ++i, ++j) {
+    InstallApp(GetProfile(1), i);
+  }
+
+  ASSERT_TRUE(AppsMatchChecker().Wait());
+}
+
+IN_PROC_BROWSER_TEST_F(TwoClientExtensionAppsSyncTest, E2E_ENABLED(Add)) {
+  ASSERT_TRUE(SetupSync());
+  ASSERT_TRUE(AppsMatchChecker().Wait());
+
+  InstallApp(GetProfile(0), 0);
+
+  ASSERT_TRUE(AppsMatchChecker().Wait());
+}
+
+IN_PROC_BROWSER_TEST_F(TwoClientExtensionAppsSyncTest, E2E_ENABLED(Uninstall)) {
+  ASSERT_TRUE(SetupSync());
+  ASSERT_TRUE(AppsMatchChecker().Wait());
+
+  InstallApp(GetProfile(0), 0);
+  ASSERT_TRUE(AppsMatchChecker().Wait());
+
+  UninstallApp(GetProfile(0), 0);
+  ASSERT_TRUE(AppsMatchChecker().Wait());
+}
+
+// Install an app on one client, then sync. Then uninstall the app on the first
+// client and sync again. Now install a new app on the first client and sync.
+// Both client should only have the second app, with identical app and page
+// ordinals.
+IN_PROC_BROWSER_TEST_F(TwoClientExtensionAppsSyncTest,
+                       E2E_ENABLED(UninstallThenInstall)) {
+  ASSERT_TRUE(SetupSync());
+  ASSERT_TRUE(AppsMatchChecker().Wait());
+
+  InstallApp(GetProfile(0), 0);
+  ASSERT_TRUE(AppsMatchChecker().Wait());
+
+  UninstallApp(GetProfile(0), 0);
+  ASSERT_TRUE(AppsMatchChecker().Wait());
+
+  InstallApp(GetProfile(0), 1);
+  ASSERT_TRUE(AppsMatchChecker().Wait());
+}
+
+IN_PROC_BROWSER_TEST_F(TwoClientExtensionAppsSyncTest, E2E_ENABLED(Merge)) {
+  ASSERT_TRUE(SetupSync());
+  ASSERT_TRUE(AppsMatchChecker().Wait());
+
+  InstallApp(GetProfile(0), 0);
+  ASSERT_TRUE(AppsMatchChecker().Wait());
+
+  UninstallApp(GetProfile(0), 0);
+
+  InstallApp(GetProfile(0), 1);
+  InstallApp(GetProfile(0), 2);
+
+  InstallApp(GetProfile(1), 2);
+  InstallApp(GetProfile(1), 3);
+
+  ASSERT_TRUE(AppsMatchChecker().Wait());
+}
+
+IN_PROC_BROWSER_TEST_F(TwoClientExtensionAppsSyncTest,
+                       E2E_ENABLED(UpdateEnableDisableApp)) {
+  ASSERT_TRUE(SetupSync());
+  ASSERT_TRUE(AppsMatchChecker().Wait());
+
+  InstallApp(GetProfile(0), 0);
+  ASSERT_TRUE(AppsMatchChecker().Wait());
+
+  DisableApp(GetProfile(0), 0);
+  ASSERT_TRUE(AppsMatchChecker().Wait());
+
+  EnableApp(GetProfile(1), 0);
+  ASSERT_TRUE(AppsMatchChecker().Wait());
+}
+
+IN_PROC_BROWSER_TEST_F(TwoClientExtensionAppsSyncTest,
+                       E2E_ENABLED(UpdateIncognitoEnableDisable)) {
+  ASSERT_TRUE(SetupSync());
+  ASSERT_TRUE(AppsMatchChecker().Wait());
+
+  InstallApp(GetProfile(0), 0);
+  ASSERT_TRUE(AppsMatchChecker().Wait());
+
+  IncognitoEnableApp(GetProfile(0), 0);
+  ASSERT_TRUE(AppsMatchChecker().Wait());
+
+  IncognitoDisableApp(GetProfile(1), 0);
+  ASSERT_TRUE(AppsMatchChecker().Wait());
+}
+
+// Install the same app on both clients, then sync. Change the page ordinal on
+// one client and sync. Both clients should have the updated page ordinal for
+// the app.
+IN_PROC_BROWSER_TEST_F(TwoClientExtensionAppsSyncTest,
+                       E2E_ENABLED(UpdatePageOrdinal)) {
+  ASSERT_TRUE(SetupSync());
+  ASSERT_TRUE(AppsMatchChecker().Wait());
+
+  syncer::StringOrdinal initial_page =
+      syncer::StringOrdinal::CreateInitialOrdinal();
+  InstallApp(GetProfile(0), 0);
+  ASSERT_TRUE(AppsMatchChecker().Wait());
+
+  syncer::StringOrdinal second_page = initial_page.CreateAfter();
+  SetPageOrdinalForApp(GetProfile(0), 0, second_page);
+  ASSERT_TRUE(AppsMatchChecker().Wait());
+}
+
+// Install the same app on both clients, then sync. Change the app launch
+// ordinal on one client and sync. Both clients should have the updated app
+// launch ordinal for the app.
+IN_PROC_BROWSER_TEST_F(TwoClientExtensionAppsSyncTest,
+                       E2E_ENABLED(UpdateAppLaunchOrdinal)) {
+  ASSERT_TRUE(SetupSync());
+  ASSERT_TRUE(AppsMatchChecker().Wait());
+
+  InstallApp(GetProfile(0), 0);
+  ASSERT_TRUE(AppsMatchChecker().Wait());
+
+  syncer::StringOrdinal initial_position =
+      GetAppLaunchOrdinalForApp(GetProfile(0), 0);
+
+  syncer::StringOrdinal second_position = initial_position.CreateAfter();
+  SetAppLaunchOrdinalForApp(GetProfile(0), 0, second_position);
+  ASSERT_TRUE(AppsMatchChecker().Wait());
+}
+
+// Adjust the CWS location within a page on the first client and sync. Adjust
+// which page the CWS appears on and sync. Both clients should have the same
+// page and app launch ordinal values for the CWS.
+IN_PROC_BROWSER_TEST_F(TwoClientExtensionAppsSyncTest,
+                       E2E_ENABLED(UpdateCWSOrdinals)) {
+  ASSERT_TRUE(SetupSync());
+  ASSERT_TRUE(AppsMatchChecker().Wait());
+
+  // Change the app launch ordinal.
+  syncer::StringOrdinal cws_app_launch_ordinal =
+      extensions::ExtensionSystem::Get(GetProfile(0))
+          ->app_sorting()
+          ->GetAppLaunchOrdinal(extensions::kWebStoreAppId);
+  extensions::ExtensionSystem::Get(GetProfile(0))
+      ->app_sorting()
+      ->SetAppLaunchOrdinal(extensions::kWebStoreAppId,
+                            cws_app_launch_ordinal.CreateAfter());
+  ASSERT_TRUE(AppsMatchChecker().Wait());
+
+  // Change the page ordinal.
+  syncer::StringOrdinal cws_page_ordinal =
+      extensions::ExtensionSystem::Get(GetProfile(1))
+          ->app_sorting()
+          ->GetPageOrdinal(extensions::kWebStoreAppId);
+  extensions::ExtensionSystem::Get(GetProfile(1))
+      ->app_sorting()
+      ->SetPageOrdinal(extensions::kWebStoreAppId,
+                       cws_page_ordinal.CreateAfter());
+  ASSERT_TRUE(AppsMatchChecker().Wait());
+}
+
+// Adjust the launch type on the first client and sync. Both clients should
+// have the same launch type values for the CWS.
+IN_PROC_BROWSER_TEST_F(TwoClientExtensionAppsSyncTest,
+                       E2E_ENABLED(UpdateLaunchType)) {
+  ASSERT_TRUE(SetupSync());
+  // Wait until sync settles before we override the apps below.
+  ASSERT_TRUE(AwaitQuiescence());
+  ASSERT_TRUE(AppsMatchChecker().Wait());
+
+  // Change the launch type to window.
+  extensions::SetLaunchType(GetProfile(1), extensions::kWebStoreAppId,
+                            extensions::LAUNCH_TYPE_WINDOW);
+  ASSERT_TRUE(AppsMatchChecker().Wait());
+  ASSERT_EQ(extensions::GetLaunchTypePrefValue(
+                extensions::ExtensionPrefs::Get(GetProfile(0)),
+                extensions::kWebStoreAppId),
+            extensions::LAUNCH_TYPE_WINDOW);
+
+  // Change the launch type to regular tab.
+  extensions::SetLaunchType(GetProfile(1), extensions::kWebStoreAppId,
+                            extensions::LAUNCH_TYPE_REGULAR);
+  ASSERT_TRUE(AppsMatchChecker().Wait());
+
+  ASSERT_EQ(extensions::GetLaunchTypePrefValue(
+                extensions::ExtensionPrefs::Get(GetProfile(0)),
+                extensions::kWebStoreAppId),
+            extensions::LAUNCH_TYPE_REGULAR);
+}
+
+IN_PROC_BROWSER_TEST_F(TwoClientExtensionAppsSyncTest, UnexpectedLaunchType) {
+  ASSERT_TRUE(SetupSync());
+  // Wait until sync settles before we override the apps below.
+  ASSERT_TRUE(AwaitQuiescence());
+  ASSERT_TRUE(AllProfilesHaveSameApps());
+
+  extensions::SetLaunchType(GetProfile(1), extensions::kWebStoreAppId,
+                            extensions::LAUNCH_TYPE_REGULAR);
+  ASSERT_TRUE(AppsMatchChecker().Wait());
+
+  const extensions::Extension* extension =
+      GetExtensionRegistry(GetProfile(1))
+          ->GetExtensionById(extensions::kWebStoreAppId,
+                             extensions::ExtensionRegistry::EVERYTHING);
+  ASSERT_TRUE(extension);
+
+  ExtensionSyncService* extension_sync_service =
+      ExtensionSyncService::Get(GetProfile(1));
+
+  extensions::ExtensionSyncData original_data(
+      extension_sync_service->CreateSyncData(*extension));
+
+  // Create an invalid launch type and ensure it doesn't get down-synced. This
+  // simulates the case of a future launch type being added which old versions
+  // don't yet understand.
+  extensions::ExtensionSyncData invalid_launch_type_data(
+      *extension, original_data.enabled(), original_data.disable_reasons(),
+      original_data.incognito_enabled(), original_data.remote_install(),
+      original_data.installed_by_custodian(),
+      original_data.app_launch_ordinal(), original_data.page_ordinal(),
+      extensions::NUM_LAUNCH_TYPES);
+  extension_sync_service->ApplySyncData(invalid_launch_type_data);
+
+  // The launch type should remain the same.
+  ASSERT_TRUE(AppsMatchChecker().Wait());
+}
+
+// TODO(akalin): Add tests exercising:
+//   - Offline installation/uninstallation behavior
+//   - App-specific properties
diff --git a/chrome/browser/sync/test/integration/two_client_web_apps_sync_test.cc b/chrome/browser/sync/test/integration/two_client_web_apps_sync_test.cc
new file mode 100644
index 0000000..5d642f1
--- /dev/null
+++ b/chrome/browser/sync/test/integration/two_client_web_apps_sync_test.cc
@@ -0,0 +1,152 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/bind.h"
+#include "base/macros.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/bind_test_util.h"
+#include "chrome/browser/installable/installable_metrics.h"
+#include "chrome/browser/sync/test/integration/sync_test.h"
+#include "chrome/browser/web_applications/components/install_manager.h"
+#include "chrome/browser/web_applications/test/web_app_install_observer.h"
+#include "chrome/browser/web_applications/web_app_provider.h"
+#include "chrome/common/web_application_info.h"
+#include "content/public/test/test_utils.h"
+
+namespace web_app {
+
+class TwoClientWebAppsSyncTest : public SyncTest {
+ public:
+  TwoClientWebAppsSyncTest() : SyncTest(TWO_CLIENT) { DisableVerifier(); }
+  ~TwoClientWebAppsSyncTest() override = default;
+
+  AppId InstallApp(const WebApplicationInfo& info, Profile* profile) {
+    base::RunLoop run_loop;
+    AppId app_id;
+
+    WebAppProvider::Get(profile)->install_manager().InstallWebAppFromInfo(
+        std::make_unique<WebApplicationInfo>(info),
+        /*no_network_install=*/false, WebappInstallSource::DEVTOOLS,
+        base::BindLambdaForTesting(
+            [&run_loop, &app_id](const AppId& new_app_id,
+                                 InstallResultCode code) {
+              DCHECK_EQ(code, InstallResultCode::kSuccess);
+              app_id = new_app_id;
+              run_loop.Quit();
+            }));
+    run_loop.Run();
+
+    const AppRegistrar& registrar = GetRegistrar(profile);
+    DCHECK_EQ(base::UTF8ToUTF16(registrar.GetAppShortName(app_id)), info.title);
+    DCHECK_EQ(registrar.GetAppLaunchURL(app_id), info.app_url);
+
+    return app_id;
+  }
+
+  const AppRegistrar& GetRegistrar(Profile* profile) {
+    return WebAppProvider::Get(profile)->registrar();
+  }
+
+  bool AllProfilesHaveSameWebAppIds() {
+    base::Optional<base::flat_set<AppId>> app_ids;
+    for (Profile* profile : GetAllProfiles()) {
+      base::flat_set<AppId> profile_app_ids =
+          GetRegistrar(profile).GetAppIdsForTesting();
+      if (!app_ids) {
+        app_ids = profile_app_ids;
+      } else {
+        if (app_ids != profile_app_ids)
+          return false;
+      }
+    }
+    return true;
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TwoClientWebAppsSyncTest);
+};
+
+IN_PROC_BROWSER_TEST_F(TwoClientWebAppsSyncTest, Basic) {
+  ASSERT_TRUE(SetupSync());
+  ASSERT_TRUE(AllProfilesHaveSameWebAppIds());
+
+  WebApplicationInfo info;
+  info.title = base::UTF8ToUTF16("Test name");
+  info.description = base::UTF8ToUTF16("Test description");
+  info.app_url = GURL("http://www.chromium.org/path");
+  info.scope = GURL("http://www.chromium.org/");
+  AppId app_id = InstallApp(info, GetProfile(0));
+
+  EXPECT_EQ(WebAppInstallObserver(GetProfile(1)).AwaitNextInstall(), app_id);
+  const AppRegistrar& registrar = GetRegistrar(GetProfile(1));
+  EXPECT_EQ(base::UTF8ToUTF16(registrar.GetAppShortName(app_id)), info.title);
+  EXPECT_EQ(base::UTF8ToUTF16(registrar.GetAppDescription(app_id)),
+            info.description);
+  EXPECT_EQ(registrar.GetAppLaunchURL(app_id), info.app_url);
+  EXPECT_EQ(registrar.GetAppScope(app_id), info.scope);
+
+  EXPECT_TRUE(AllProfilesHaveSameWebAppIds());
+}
+
+IN_PROC_BROWSER_TEST_F(TwoClientWebAppsSyncTest, Minimal) {
+  ASSERT_TRUE(SetupSync());
+  ASSERT_TRUE(AllProfilesHaveSameWebAppIds());
+
+  WebApplicationInfo info;
+  info.title = base::UTF8ToUTF16("Test name");
+  info.app_url = GURL("http://www.chromium.org/");
+  AppId app_id = InstallApp(info, GetProfile(0));
+
+  EXPECT_EQ(WebAppInstallObserver(GetProfile(1)).AwaitNextInstall(), app_id);
+  const AppRegistrar& registrar = GetRegistrar(GetProfile(1));
+  EXPECT_EQ(base::UTF8ToUTF16(registrar.GetAppShortName(app_id)), info.title);
+  EXPECT_EQ(registrar.GetAppLaunchURL(app_id), info.app_url);
+
+  EXPECT_TRUE(AllProfilesHaveSameWebAppIds());
+}
+
+IN_PROC_BROWSER_TEST_F(TwoClientWebAppsSyncTest, ThemeColor) {
+  ASSERT_TRUE(SetupSync());
+  ASSERT_TRUE(AllProfilesHaveSameWebAppIds());
+
+  WebApplicationInfo info;
+  info.title = base::UTF8ToUTF16("Test name");
+  info.app_url = GURL("http://www.chromium.org/");
+  info.theme_color = SK_ColorBLUE;
+  AppId app_id = InstallApp(info, GetProfile(0));
+  EXPECT_EQ(GetRegistrar(GetProfile(0)).GetAppThemeColor(app_id),
+            info.theme_color);
+
+  EXPECT_EQ(WebAppInstallObserver(GetProfile(1)).AwaitNextInstall(), app_id);
+  const AppRegistrar& registrar = GetRegistrar(GetProfile(1));
+  EXPECT_EQ(base::UTF8ToUTF16(registrar.GetAppShortName(app_id)), info.title);
+  EXPECT_EQ(registrar.GetAppLaunchURL(app_id), info.app_url);
+  EXPECT_EQ(registrar.GetAppThemeColor(app_id), info.theme_color);
+
+  EXPECT_TRUE(AllProfilesHaveSameWebAppIds());
+}
+
+IN_PROC_BROWSER_TEST_F(TwoClientWebAppsSyncTest, IsLocallyInstalled) {
+  ASSERT_TRUE(SetupSync());
+  ASSERT_TRUE(AllProfilesHaveSameWebAppIds());
+
+  WebApplicationInfo info;
+  info.title = base::UTF8ToUTF16("Test name");
+  info.app_url = GURL("http://www.chromium.org/");
+  AppId app_id = InstallApp(info, GetProfile(0));
+  EXPECT_TRUE(GetRegistrar(GetProfile(0)).IsLocallyInstalled(app_id));
+
+  EXPECT_EQ(WebAppInstallObserver(GetProfile(1)).AwaitNextInstall(), app_id);
+  bool is_locally_installed =
+      GetRegistrar(GetProfile(1)).IsLocallyInstalled(app_id);
+#if defined(OS_CHROMEOS)
+  EXPECT_TRUE(is_locally_installed);
+#else
+  EXPECT_FALSE(is_locally_installed);
+#endif
+
+  EXPECT_TRUE(AllProfilesHaveSameWebAppIds());
+}
+
+}  // namespace web_app
diff --git a/chrome/browser/sync_file_system/local/canned_syncable_file_system.cc b/chrome/browser/sync_file_system/local/canned_syncable_file_system.cc
index 111beee..d0b6e6f4 100644
--- a/chrome/browser/sync_file_system/local/canned_syncable_file_system.cc
+++ b/chrome/browser/sync_file_system/local/canned_syncable_file_system.cc
@@ -24,6 +24,7 @@
 #include "chrome/browser/sync_file_system/local/local_file_sync_context.h"
 #include "chrome/browser/sync_file_system/local/sync_file_system_backend.h"
 #include "chrome/browser/sync_file_system/syncable_file_system_util.h"
+#include "storage/browser/blob/blob_storage_context.h"
 #include "storage/browser/blob/shareable_file_reference.h"
 #include "storage/browser/fileapi/external_mount_points.h"
 #include "storage/browser/fileapi/file_system_backend.h"
@@ -31,7 +32,7 @@
 #include "storage/browser/fileapi/file_system_operation_context.h"
 #include "storage/browser/fileapi/file_system_operation_runner.h"
 #include "storage/browser/quota/quota_manager.h"
-#include "storage/browser/test/mock_blob_url_request_context.h"
+#include "storage/browser/test/mock_blob_util.h"
 #include "storage/browser/test/mock_special_storage_policy.h"
 #include "storage/browser/test/test_file_system_options.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -43,8 +44,7 @@
 using storage::FileSystemURL;
 using storage::FileSystemURLSet;
 using storage::QuotaManager;
-using content::MockBlobURLRequestContext;
-using content::ScopedTextBlob;
+using storage::ScopedTextBlob;
 
 namespace sync_file_system {
 
@@ -153,21 +153,15 @@
 class WriteHelper {
  public:
   WriteHelper() : bytes_written_(0) {}
-  WriteHelper(MockBlobURLRequestContext* request_context,
+  WriteHelper(std::unique_ptr<storage::BlobStorageContext> blob_storage_context,
               const std::string& blob_data)
       : bytes_written_(0),
-        request_context_(request_context),
-        blob_data_(new ScopedTextBlob(*request_context,
+        blob_storage_context_(std::move(blob_storage_context)),
+        blob_data_(new ScopedTextBlob(blob_storage_context_.get(),
                                       base::GenerateGUID(),
-                                      blob_data)) {
-  }
+                                      blob_data)) {}
 
-  ~WriteHelper() {
-    if (request_context_) {
-      base::ThreadTaskRunnerHandle::Get()->DeleteSoon(
-          FROM_HERE, request_context_.release());
-    }
-  }
+  ~WriteHelper() {}
 
   ScopedTextBlob* scoped_text_blob() const { return blob_data_.get(); }
 
@@ -187,7 +181,7 @@
 
  private:
   int64_t bytes_written_;
-  std::unique_ptr<MockBlobURLRequestContext> request_context_;
+  std::unique_ptr<storage::BlobStorageContext> blob_storage_context_;
   std::unique_ptr<ScopedTextBlob> blob_data_;
 
   DISALLOW_COPY_AND_ASSIGN(WriteHelper);
@@ -652,9 +646,8 @@
     const WriteCallback& callback) {
   EXPECT_TRUE(io_task_runner_->RunsTasksInCurrentSequence());
   EXPECT_TRUE(is_filesystem_opened_);
-  MockBlobURLRequestContext* url_request_context(
-      new MockBlobURLRequestContext());
-  WriteHelper* helper = new WriteHelper(url_request_context, data);
+  auto blob_storage_context = std::make_unique<storage::BlobStorageContext>();
+  WriteHelper* helper = new WriteHelper(std::move(blob_storage_context), data);
   operation_runner()->Write(url,
                             helper->scoped_text_blob()->GetBlobDataHandle(), 0,
                             base::BindRepeating(&WriteHelper::DidWrite,
diff --git a/chrome/browser/sync_file_system/local/local_file_change_tracker_unittest.cc b/chrome/browser/sync_file_system/local/local_file_change_tracker_unittest.cc
index 5fc90e3..f16a785 100644
--- a/chrome/browser/sync_file_system/local/local_file_change_tracker_unittest.cc
+++ b/chrome/browser/sync_file_system/local/local_file_change_tracker_unittest.cc
@@ -22,17 +22,18 @@
 #include "chrome/browser/sync_file_system/syncable_file_system_util.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "content/public/test/test_utils.h"
+#include "storage/browser/blob/blob_storage_context.h"
 #include "storage/browser/fileapi/file_system_context.h"
 #include "storage/browser/quota/quota_manager.h"
-#include "storage/browser/test/mock_blob_url_request_context.h"
+#include "storage/browser/test/mock_blob_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/leveldatabase/leveldb_chrome.h"
 
-using content::MockBlobURLRequestContext;
-using content::ScopedTextBlob;
+using storage::BlobStorageContext;
 using storage::FileSystemContext;
 using storage::FileSystemURL;
 using storage::FileSystemURLSet;
+using storage::ScopedTextBlob;
 
 namespace sync_file_system {
 
@@ -280,8 +281,8 @@
   ASSERT_EQ(0U, urls.size());
 
   const std::string kData("Lorem ipsum.");
-  MockBlobURLRequestContext url_request_context;
-  ScopedTextBlob blob(url_request_context, "blob_id:test", kData);
+  BlobStorageContext blob_storage_context;
+  ScopedTextBlob blob(&blob_storage_context, "blob_id:test", kData);
 
   // Create files and nested directories.
   EXPECT_EQ(base::File::FILE_OK,
@@ -430,8 +431,8 @@
   ASSERT_EQ(0U, urls.size());
 
   const std::string kData("Lorem ipsum.");
-  MockBlobURLRequestContext url_request_context;
-  ScopedTextBlob blob(url_request_context, "blob_id:test", kData);
+  BlobStorageContext blob_storage_context;
+  ScopedTextBlob blob(&blob_storage_context, "blob_id:test", kData);
 
   // Create files and nested directories.
   EXPECT_EQ(base::File::FILE_OK,
diff --git a/chrome/browser/sync_file_system/local/local_file_sync_context_unittest.cc b/chrome/browser/sync_file_system/local/local_file_sync_context_unittest.cc
index 6476d6d..8a38eba 100644
--- a/chrome/browser/sync_file_system/local/local_file_sync_context_unittest.cc
+++ b/chrome/browser/sync_file_system/local/local_file_sync_context_unittest.cc
@@ -31,7 +31,7 @@
 #include "storage/browser/fileapi/file_system_context.h"
 #include "storage/browser/fileapi/file_system_operation_runner.h"
 #include "storage/browser/fileapi/isolated_context.h"
-#include "storage/browser/test/mock_blob_url_request_context.h"
+#include "storage/browser/test/mock_blob_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/leveldatabase/leveldb_chrome.h"
 
diff --git a/chrome/browser/sync_file_system/local/syncable_file_operation_runner_unittest.cc b/chrome/browser/sync_file_system/local/syncable_file_operation_runner_unittest.cc
index 0a021f6b..ad1adee6 100644
--- a/chrome/browser/sync_file_system/local/syncable_file_operation_runner_unittest.cc
+++ b/chrome/browser/sync_file_system/local/syncable_file_operation_runner_unittest.cc
@@ -26,17 +26,17 @@
 #include "chrome/browser/sync_file_system/syncable_file_system_util.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "content/public/test/test_utils.h"
+#include "storage/browser/blob/blob_storage_context.h"
 #include "storage/browser/fileapi/file_system_context.h"
 #include "storage/browser/fileapi/file_system_operation_runner.h"
-#include "storage/browser/test/mock_blob_url_request_context.h"
+#include "storage/browser/test/mock_blob_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/leveldatabase/leveldb_chrome.h"
 
+using base::File;
 using storage::FileSystemOperation;
 using storage::FileSystemURL;
-using content::MockBlobURLRequestContext;
-using content::ScopedTextBlob;
-using base::File;
+using storage::ScopedTextBlob;
 
 namespace sync_file_system {
 
@@ -156,7 +156,7 @@
   size_t write_bytes_;
   bool write_complete_;
 
-  MockBlobURLRequestContext url_request_context_;
+  storage::BlobStorageContext blob_storage_context_;
 
  private:
   base::WeakPtrFactory<SyncableFileOperationRunnerTest> weak_factory_{this};
@@ -310,7 +310,7 @@
 TEST_F(SyncableFileOperationRunnerTest, Write) {
   EXPECT_EQ(File::FILE_OK, file_system_.CreateFile(URL(kFile)));
   const std::string kData("Lorem ipsum.");
-  ScopedTextBlob blob(url_request_context_, "blob:foo", kData);
+  ScopedTextBlob blob(&blob_storage_context_, "blob:foo", kData);
 
   sync_status()->StartSyncing(URL(kFile));
 
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 75cd7f5..938c777 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -1854,6 +1854,8 @@
       "webui/settings/chromeos/parental_controls_handler.h",
       "webui/settings/chromeos/plugin_vm_handler.cc",
       "webui/settings/chromeos/plugin_vm_handler.h",
+      "webui/settings/chromeos/wallpaper_handler.cc",
+      "webui/settings/chromeos/wallpaper_handler.h",
       "webui/settings/tts_handler.cc",
       "webui/settings/tts_handler.h",
       "webui/signin/inline_login_handler_chromeos.cc",
diff --git a/chrome/browser/ui/app_list/app_list_client_impl_browsertest.cc b/chrome/browser/ui/app_list/app_list_client_impl_browsertest.cc
index f4aa4e9..27e923c 100644
--- a/chrome/browser/ui/app_list/app_list_client_impl_browsertest.cc
+++ b/chrome/browser/ui/app_list/app_list_client_impl_browsertest.cc
@@ -9,6 +9,7 @@
 #include "ash/public/cpp/app_list/app_list_features.h"
 #include "ash/public/cpp/app_list/app_list_switches.h"
 #include "base/command_line.h"
+#include "base/feature_list.h"
 #include "base/macros.h"
 #include "base/path_service.h"
 #include "base/run_loop.h"
@@ -37,6 +38,7 @@
 #include "chrome/browser/ui/app_list/test/chrome_app_list_test_support.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_finder.h"
+#include "chrome/common/chrome_features.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
@@ -123,6 +125,11 @@
 }
 
 IN_PROC_BROWSER_TEST_F(AppListClientImplBrowserTest, ShowAppInfo) {
+  if (base::FeatureList::IsEnabled(features::kAppManagement)) {
+    // When App Management is enabled, App Info opens in the browser.
+    return;
+  }
+
   AppListClientImpl* client = AppListClientImpl::GetInstance();
   const extensions::Extension* app = InstallPlatformApp("minimal");
 
diff --git a/chrome/browser/ui/webui/certificates_handler.cc b/chrome/browser/ui/webui/certificates_handler.cc
index 90ee1b5f..02077494 100644
--- a/chrome/browser/ui/webui/certificates_handler.cc
+++ b/chrome/browser/ui/webui/certificates_handler.cc
@@ -445,8 +445,14 @@
   if (!cert_info)
     return;
 
-  CHECK(CanEditCertificate(cert_info))
-      << "Editing this certificate is not allowed";
+  if (!CanEditCertificate(cert_info)) {
+    RejectCallbackWithError(
+        l10n_util::GetStringUTF8(
+            IDS_SETTINGS_CERTIFICATE_MANAGER_SET_TRUST_ERROR_TITLE),
+        l10n_util::GetStringUTF8(
+            IDS_SETTINGS_CERTIFICATE_MANAGER_ERROR_NOT_ALLOWED));
+    return;
+  }
 
   bool trust_ssl = false;
   bool trust_email = false;
@@ -562,15 +568,19 @@
 }
 
 void CertificatesHandler::HandleImportPersonal(const base::ListValue* args) {
+#if defined(OS_CHROMEOS)
+  // When policy changes while user on the certificate manager page, the UI
+  // doesn't update without page refresh and user can still see and use import
+  // button. Because of this 'return' the button will do nothing.
+  if (!IsClientCertificateManagementAllowedPolicy(Slot::kUser)) {
+    return;
+  }
+#endif
+
   CHECK_EQ(2U, args->GetSize());
   AssignWebUICallbackId(args);
   CHECK(args->GetBoolean(1, &use_hardware_backed_));
 
-#if defined(OS_CHROMEOS)
-  CHECK(IsClientCertificateManagementAllowedPolicy(Slot::kUser))
-      << "Importing certificates not allowed by policy";
-#endif
-
   ui::SelectFileDialog::FileTypeInfo file_type_info;
   file_type_info.extensions.resize(1);
   file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("p12"));
@@ -731,11 +741,6 @@
   CHECK_EQ(1U, args->GetSize());
   AssignWebUICallbackId(args);
 
-#if defined(OS_CHROMEOS)
-  CHECK(IsClientCertificateManagementAllowedPolicy(Slot::kUser))
-      << "Importing certificates not allowed by policy";
-#endif
-
   select_file_dialog_ = ui::SelectFileDialog::Create(
       this,
       std::make_unique<ChromeSelectFilePolicy>(web_ui()->GetWebContents()));
@@ -800,14 +805,18 @@
 }
 
 void CertificatesHandler::HandleImportCA(const base::ListValue* args) {
+#if defined(OS_CHROMEOS)
+  // When policy changes while user on the certificate manager page, the UI
+  // doesn't update without page refresh and user can still see and use import
+  // button. Because of this 'return' the button will do nothing.
+  if (!IsCACertificateManagementAllowedPolicy(CertificateSource::kImported)) {
+    return;
+  }
+#endif  // defined(OS_CHROMEOS)
+
   CHECK_EQ(1U, args->GetSize());
   AssignWebUICallbackId(args);
 
-#if defined(OS_CHROMEOS)
-  CHECK(IsCACertificateManagementAllowedPolicy(CertificateSource::kImported))
-      << "Importing certificates is not allowed by policy";
-#endif  // defined(OS_CHROMEOS)
-
   select_file_dialog_ = ui::SelectFileDialog::Create(
       this,
       std::make_unique<ChromeSelectFilePolicy>(web_ui()->GetWebContents()));
@@ -920,8 +929,14 @@
   if (!cert_info)
     return;
 
-  CHECK(CanDeleteCertificate(cert_info))
-      << "Deleting this certificate is not allowed";
+  if (!CanDeleteCertificate(cert_info)) {
+    RejectCallbackWithError(
+        l10n_util::GetStringUTF8(
+            IDS_SETTINGS_CERTIFICATE_MANAGER_DELETE_CERT_ERROR_TITLE),
+        l10n_util::GetStringUTF8(
+            IDS_SETTINGS_CERTIFICATE_MANAGER_ERROR_NOT_ALLOWED));
+    return;
+  }
 
   bool result = certificate_manager_model_->Delete(cert_info->cert());
   if (!result) {
diff --git a/chrome/browser/ui/webui/chromeos/in_session_password_change/confirm_password_change_handler.cc b/chrome/browser/ui/webui/chromeos/in_session_password_change/confirm_password_change_handler.cc
index e46a6ea..c27b1d20 100644
--- a/chrome/browser/ui/webui/chromeos/in_session_password_change/confirm_password_change_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/in_session_password_change/confirm_password_change_handler.cc
@@ -19,6 +19,16 @@
 
 namespace chromeos {
 
+namespace {
+
+const InSessionPasswordChangeManager::Event kIncorrectPasswordEvent =
+    InSessionPasswordChangeManager::Event::CRYPTOHOME_PASSWORD_CHANGE_FAILURE;
+
+const InSessionPasswordChangeManager::PasswordSource kPasswordSource =
+    InSessionPasswordChangeManager::PasswordSource::PASSWORDS_RETYPED;
+
+}  // namespace
+
 ConfirmPasswordChangeHandler::ConfirmPasswordChangeHandler() {
   if (InSessionPasswordChangeManager::IsInitialized()) {
     InSessionPasswordChangeManager::Get()->AddObserver(this);
@@ -33,8 +43,7 @@
 
 void ConfirmPasswordChangeHandler::OnEvent(
     InSessionPasswordChangeManager::Event event) {
-  if (event ==
-      InSessionPasswordChangeManager::CRYPTOHOME_PASSWORD_CHANGE_FAILURE) {
+  if (event == kIncorrectPasswordEvent) {
     AllowJavascript();
     FireWebUIListener("incorrect-old-password");
   }
@@ -44,8 +53,8 @@
     const base::ListValue* params) {
   const std::string old_password = params->GetList()[0].GetString();
   const std::string new_password = params->GetList()[1].GetString();
-  InSessionPasswordChangeManager::Get()->ChangePassword(old_password,
-                                                        new_password);
+  InSessionPasswordChangeManager::Get()->ChangePassword(
+      old_password, new_password, kPasswordSource);
 }
 
 void ConfirmPasswordChangeHandler::RegisterMessages() {
diff --git a/chrome/browser/ui/webui/settings/appearance_handler.cc b/chrome/browser/ui/webui/settings/appearance_handler.cc
index 8669504..4fceb88 100644
--- a/chrome/browser/ui/webui/settings/appearance_handler.cc
+++ b/chrome/browser/ui/webui/settings/appearance_handler.cc
@@ -12,10 +12,6 @@
 #include "chrome/browser/themes/theme_service_factory.h"
 #include "content/public/browser/web_ui.h"
 
-#if defined(OS_CHROMEOS)
-#include "chrome/browser/ui/ash/wallpaper_controller_client.h"
-#endif
-
 namespace settings {
 
 AppearanceHandler::AppearanceHandler(content::WebUI* webui)
@@ -37,22 +33,6 @@
       base::BindRepeating(&AppearanceHandler::HandleUseSystemTheme,
                           base::Unretained(this)));
 #endif
-#if defined(OS_CHROMEOS)
-  web_ui()->RegisterMessageCallback(
-      "openWallpaperManager",
-      base::BindRepeating(&AppearanceHandler::HandleOpenWallpaperManager,
-                          base::Unretained(this)));
-
-  web_ui()->RegisterMessageCallback(
-      "isWallpaperSettingVisible",
-      base::BindRepeating(&AppearanceHandler::IsWallpaperSettingVisible,
-                          base::Unretained(this)));
-
-  web_ui()->RegisterMessageCallback(
-      "isWallpaperPolicyControlled",
-      base::BindRepeating(&AppearanceHandler::IsWallpaperPolicyControlled,
-                          base::Unretained(this)));
-#endif
 }
 
 void AppearanceHandler::HandleUseDefaultTheme(const base::ListValue* args) {
@@ -68,31 +48,4 @@
 }
 #endif
 
-#if defined(OS_CHROMEOS)
-void AppearanceHandler::IsWallpaperSettingVisible(const base::ListValue* args) {
-  CHECK_EQ(args->GetSize(), 1U);
-  bool result = WallpaperControllerClient::Get()->ShouldShowWallpaperSetting();
-  ResolveCallback(args->GetList()[0], result);
-}
-
-void AppearanceHandler::IsWallpaperPolicyControlled(
-    const base::ListValue* args) {
-  CHECK_EQ(args->GetSize(), 1U);
-  bool result = WallpaperControllerClient::Get()
-                    ->IsActiveUserWallpaperControlledByPolicy();
-  ResolveCallback(args->GetList()[0], result);
-}
-
-void AppearanceHandler::HandleOpenWallpaperManager(
-    const base::ListValue* args) {
-  WallpaperControllerClient::Get()->OpenWallpaperPickerIfAllowed();
-}
-
-void AppearanceHandler::ResolveCallback(const base::Value& callback_id,
-                                        bool result) {
-  AllowJavascript();
-  ResolveJavascriptCallback(callback_id, base::Value(result));
-}
-#endif
-
 }  // namespace settings
diff --git a/chrome/browser/ui/webui/settings/appearance_handler.h b/chrome/browser/ui/webui/settings/appearance_handler.h
index dabbd536..961e16e 100644
--- a/chrome/browser/ui/webui/settings/appearance_handler.h
+++ b/chrome/browser/ui/webui/settings/appearance_handler.h
@@ -41,20 +41,6 @@
   void HandleUseSystemTheme(const base::ListValue* args);
 #endif
 
-#if defined(OS_CHROMEOS)
-  // Whether the wallpaper setting should be shown.
-  void IsWallpaperSettingVisible(const base::ListValue* args);
-
-  // Whether the wallpaper is policy controlled.
-  void IsWallpaperPolicyControlled(const base::ListValue* args);
-
-  // Open the wallpaper manager app.
-  void HandleOpenWallpaperManager(const base::ListValue* args);
-
-  // Helper function to resolve the Javascript callback.
-  void ResolveCallback(const base::Value& callback_id, bool result);
-#endif
-
   Profile* profile_;  // Weak pointer.
 
   base::WeakPtrFactory<AppearanceHandler> weak_ptr_factory_{this};
diff --git a/chrome/browser/ui/webui/settings/chromeos/os_settings_ui.cc b/chrome/browser/ui/webui/settings/chromeos/os_settings_ui.cc
index 5b699a5..d2796d9 100644
--- a/chrome/browser/ui/webui/settings/chromeos/os_settings_ui.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/os_settings_ui.cc
@@ -19,8 +19,8 @@
 #include "chrome/browser/ui/webui/plural_string_handler.h"
 #include "chrome/browser/ui/webui/settings/about_handler.h"
 #include "chrome/browser/ui/webui/settings/accessibility_main_handler.h"
-#include "chrome/browser/ui/webui/settings/appearance_handler.h"
 #include "chrome/browser/ui/webui/settings/browser_lifetime_handler.h"
+#include "chrome/browser/ui/webui/settings/chromeos/wallpaper_handler.h"
 #include "chrome/browser/ui/webui/settings/downloads_handler.h"
 #include "chrome/browser/ui/webui/settings/extension_control_handler.h"
 #include "chrome/browser/ui/webui/settings/languages_handler.h"
@@ -69,9 +69,6 @@
   html_source->AddBoolean("showOSSettings", true);
 
   AddSettingsPageUIHandler(
-      std::make_unique<::settings::AppearanceHandler>(web_ui));
-
-  AddSettingsPageUIHandler(
       std::make_unique<::settings::AccessibilityMainHandler>());
   AddSettingsPageUIHandler(
       std::make_unique<::settings::BrowserLifetimeHandler>());
@@ -92,6 +89,8 @@
       std::make_unique<::settings::ProtocolHandlersHandler>());
   AddSettingsPageUIHandler(
       std::make_unique<::settings::SearchEnginesHandler>(profile));
+  AddSettingsPageUIHandler(
+      std::make_unique<chromeos::settings::WallpaperHandler>(web_ui));
 
   html_source->AddBoolean("unifiedConsentEnabled",
                           unified_consent::IsUnifiedConsentFeatureEnabled());
diff --git a/chrome/browser/ui/webui/settings/chromeos/wallpaper_handler.cc b/chrome/browser/ui/webui/settings/chromeos/wallpaper_handler.cc
new file mode 100644
index 0000000..5f169f01
--- /dev/null
+++ b/chrome/browser/ui/webui/settings/chromeos/wallpaper_handler.cc
@@ -0,0 +1,67 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/settings/chromeos/wallpaper_handler.h"
+
+#include "base/bind.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/ash/wallpaper_controller_client.h"
+#include "content/public/browser/web_ui.h"
+
+namespace chromeos {
+namespace settings {
+
+WallpaperHandler::WallpaperHandler(content::WebUI* webui)
+    : profile_(Profile::FromWebUI(webui)) {}
+
+WallpaperHandler::~WallpaperHandler() = default;
+
+void WallpaperHandler::OnJavascriptAllowed() {}
+void WallpaperHandler::OnJavascriptDisallowed() {}
+
+void WallpaperHandler::RegisterMessages() {
+  web_ui()->RegisterMessageCallback(
+      "openWallpaperManager",
+      base::BindRepeating(&WallpaperHandler::HandleOpenWallpaperManager,
+                          base::Unretained(this)));
+
+  web_ui()->RegisterMessageCallback(
+      "isWallpaperSettingVisible",
+      base::BindRepeating(&WallpaperHandler::HandleIsWallpaperSettingVisible,
+                          base::Unretained(this)));
+
+  web_ui()->RegisterMessageCallback(
+      "isWallpaperPolicyControlled",
+      base::BindRepeating(&WallpaperHandler::HandleIsWallpaperPolicyControlled,
+                          base::Unretained(this)));
+}
+
+void WallpaperHandler::HandleIsWallpaperSettingVisible(
+    const base::ListValue* args) {
+  CHECK_EQ(args->GetSize(), 1U);
+  ResolveCallback(
+      args->GetList()[0],
+      WallpaperControllerClient::Get()->ShouldShowWallpaperSetting());
+}
+
+void WallpaperHandler::HandleIsWallpaperPolicyControlled(
+    const base::ListValue* args) {
+  CHECK_EQ(args->GetSize(), 1U);
+  bool result = WallpaperControllerClient::Get()
+                    ->IsActiveUserWallpaperControlledByPolicy();
+  ResolveCallback(args->GetList()[0], result);
+}
+
+void WallpaperHandler::HandleOpenWallpaperManager(const base::ListValue* args) {
+  WallpaperControllerClient::Get()->OpenWallpaperPickerIfAllowed();
+}
+
+void WallpaperHandler::ResolveCallback(const base::Value& callback_id,
+                                       bool result) {
+  AllowJavascript();
+  ResolveJavascriptCallback(callback_id, base::Value(result));
+}
+
+}  // namespace settings
+}  // namespace chromeos
diff --git a/chrome/browser/ui/webui/settings/chromeos/wallpaper_handler.h b/chrome/browser/ui/webui/settings/chromeos/wallpaper_handler.h
new file mode 100644
index 0000000..6e0c811
--- /dev/null
+++ b/chrome/browser/ui/webui/settings/chromeos/wallpaper_handler.h
@@ -0,0 +1,56 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_WALLPAPER_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_WALLPAPER_HANDLER_H_
+
+#include "base/macros.h"
+#include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
+
+namespace base {
+class ListValue;
+}
+
+namespace content {
+class WebUI;
+}
+
+class Profile;
+
+namespace chromeos {
+namespace settings {
+
+// Chrome "Personalization" settings page UI handler.
+class WallpaperHandler : public ::settings::SettingsPageUIHandler {
+ public:
+  explicit WallpaperHandler(content::WebUI* webui);
+  ~WallpaperHandler() override;
+
+  // SettingsPageUIHandler implementation.
+  void RegisterMessages() override;
+  void OnJavascriptAllowed() override;
+  void OnJavascriptDisallowed() override;
+
+ private:
+  // Whether the wallpaper setting should be shown.
+  void HandleIsWallpaperSettingVisible(const base::ListValue* args);
+
+  // Whether the wallpaper is policy controlled.
+  void HandleIsWallpaperPolicyControlled(const base::ListValue* args);
+
+  // Open the wallpaper manager app.
+  void HandleOpenWallpaperManager(const base::ListValue* args);
+
+  // Helper function to resolve the Javascript callback.
+  void ResolveCallback(const base::Value& callback_id, bool result);
+
+  Profile* const profile_;
+
+  DISALLOW_COPY_AND_ASSIGN(WallpaperHandler);
+};
+
+}  // namespace settings
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_WALLPAPER_HANDLER_H_
diff --git a/chrome/browser/ui/webui/settings/settings_ui.cc b/chrome/browser/ui/webui/settings/settings_ui.cc
index dd41d4cb..a7c609d0 100644
--- a/chrome/browser/ui/webui/settings/settings_ui.cc
+++ b/chrome/browser/ui/webui/settings/settings_ui.cc
@@ -114,6 +114,7 @@
 #include "chrome/browser/ui/webui/settings/chromeos/multidevice_handler.h"
 #include "chrome/browser/ui/webui/settings/chromeos/parental_controls_handler.h"
 #include "chrome/browser/ui/webui/settings/chromeos/plugin_vm_handler.h"
+#include "chrome/browser/ui/webui/settings/chromeos/wallpaper_handler.h"
 #include "chrome/browser/web_applications/system_web_app_manager.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/grit/browser_resources.h"
@@ -422,6 +423,12 @@
   }
   web_ui->AddMessageHandler(
       std::make_unique<chromeos::settings::KeyboardHandler>());
+
+  // TODO(crbug/950007): Remove adding WallpaperHandler when
+  // SplitSettings complete.
+  web_ui->AddMessageHandler(
+      std::make_unique<chromeos::settings::WallpaperHandler>(web_ui));
+
   if (plugin_vm::IsPluginVmEnabled(profile)) {
     web_ui->AddMessageHandler(
         std::make_unique<chromeos::settings::PluginVmHandler>(profile));
diff --git a/chrome/browser/vr/BUILD.gn b/chrome/browser/vr/BUILD.gn
index 4533f89d..bf72c34 100644
--- a/chrome/browser/vr/BUILD.gn
+++ b/chrome/browser/vr/BUILD.gn
@@ -246,6 +246,8 @@
     "service/interface_set.h",
     "service/vr_service_impl.cc",
     "service/vr_service_impl.h",
+    "service/xr_device_service.cc",
+    "service/xr_device_service.h",
     "service/xr_runtime_manager.cc",
     "service/xr_runtime_manager.h",
     "service/xr_runtime_manager_observer.h",
@@ -274,6 +276,7 @@
 
   public_deps = [
     ":vr_base",
+    "//device/vr/public/mojom",
   ]
 
   deps = [
@@ -769,6 +772,7 @@
       ]
 
       deps += [
+        ":vr_common",
         "//components/content_settings/core/browser",
         "//device/vr:directx_helpers",
         "//third_party/openvr",
diff --git a/chrome/browser/vr/service/isolated_device_provider.cc b/chrome/browser/vr/service/isolated_device_provider.cc
index a5597ef0..b5f21325 100644
--- a/chrome/browser/vr/service/isolated_device_provider.cc
+++ b/chrome/browser/vr/service/isolated_device_provider.cc
@@ -6,10 +6,9 @@
 
 #include "base/bind.h"
 #include "chrome/browser/vr/service/vr_ui_host.h"
-#include "content/public/browser/system_connector.h"
+#include "chrome/browser/vr/service/xr_device_service.h"
 #include "device/vr/buildflags/buildflags.h"
 #include "device/vr/isolated_gamepad_data_fetcher.h"
-#include "services/service_manager/public/cpp/connector.h"
 
 namespace {
 constexpr int kMaxRetries = 3;
@@ -98,10 +97,8 @@
 }
 
 void IsolatedVRDeviceProvider::SetupDeviceProvider() {
-  content::GetSystemConnector()->BindInterface(
-      device::mojom::kVrIsolatedServiceName,
+  GetXRDeviceService()->BindRuntimeProvider(
       mojo::MakeRequest(&device_provider_));
-
   device_provider_.set_connection_error_handler(base::BindOnce(
       &IsolatedVRDeviceProvider::OnServerError, base::Unretained(this)));
 
diff --git a/chrome/browser/vr/service/xr_device_service.cc b/chrome/browser/vr/service/xr_device_service.cc
new file mode 100644
index 0000000..891ef07
--- /dev/null
+++ b/chrome/browser/vr/service/xr_device_service.cc
@@ -0,0 +1,58 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/vr/service/xr_device_service.h"
+
+#include "base/no_destructor.h"
+#include "build/build_config.h"
+#include "content/public/browser/service_process_host.h"
+
+namespace vr {
+
+namespace {
+
+base::RepeatingClosure& GetStartupCallback() {
+  static base::NoDestructor<base::RepeatingClosure> callback;
+  return *callback;
+}
+
+}  // namespace
+
+const mojo::Remote<device::mojom::XRDeviceService>& GetXRDeviceService() {
+  static base::NoDestructor<mojo::Remote<device::mojom::XRDeviceService>>
+      remote;
+  if (!*remote) {
+    content::ServiceProcessHost::Launch(
+        remote->BindNewPipeAndPassReceiver(),
+        content::ServiceProcessHost::Options()
+#if defined(OS_WIN)
+            .WithSandboxType(service_manager::SANDBOX_TYPE_XRCOMPOSITING)
+#else
+            .WithSandboxType(service_manager::SANDBOX_TYPE_UTILITY)
+#endif
+            .WithDisplayName("Isolated XR Device Service")
+            .Pass());
+
+    // Ensure that if the interface is ever disconnected (e.g. the service
+    // process crashes) or goes idle for a short period of time -- meaning there
+    // are no in-flight messages and no other interfaces bound through this
+    // one -- then we will reset |remote|, causing the service process to be
+    // terminated if it isn't already.
+    remote->reset_on_disconnect();
+    remote->reset_on_idle_timeout(base::TimeDelta::FromSeconds(5));
+
+    auto& startup_callback = GetStartupCallback();
+    if (startup_callback)
+      startup_callback.Run();
+  }
+
+  return *remote;
+}
+
+void SetXRDeviceServiceStartupCallbackForTesting(
+    base::RepeatingClosure callback) {
+  GetStartupCallback() = std::move(callback);
+}
+
+}  // namespace vr
diff --git a/chrome/browser/vr/service/xr_device_service.h b/chrome/browser/vr/service/xr_device_service.h
new file mode 100644
index 0000000..280fa02
--- /dev/null
+++ b/chrome/browser/vr/service/xr_device_service.h
@@ -0,0 +1,29 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_VR_SERVICE_XR_DEVICE_SERVICE_H_
+#define CHROME_BROWSER_VR_SERVICE_XR_DEVICE_SERVICE_H_
+
+#include "base/callback.h"
+#include "chrome/browser/vr/vr_export.h"
+#include "device/vr/public/mojom/isolated_xr_service.mojom.h"
+#include "mojo/public/cpp/bindings/remote.h"
+
+namespace vr {
+
+// Acquires a remote handle to the sandboxed isolated XR Device Service
+// instance, launching a process to host the service if necessary.
+VR_EXPORT const mojo::Remote<device::mojom::XRDeviceService>&
+GetXRDeviceService();
+
+// Allows tests to perform extra initialization steps on any new XR Device
+// Service instance before other client code can use it. Any time a new instance
+// of the service is started by |GetXRDeviceService()|, this callback (if
+// non-null) is invoked.
+VR_EXPORT void SetXRDeviceServiceStartupCallbackForTesting(
+    base::RepeatingClosure callback);
+
+}  // namespace vr
+
+#endif  // CHROME_BROWSER_VR_SERVICE_XR_DEVICE_SERVICE_H_
diff --git a/chrome/browser/vr/test/mock_xr_device_hook_base.cc b/chrome/browser/vr/test/mock_xr_device_hook_base.cc
index 5baa8b6..924435c 100644
--- a/chrome/browser/vr/test/mock_xr_device_hook_base.cc
+++ b/chrome/browser/vr/test/mock_xr_device_hook_base.cc
@@ -3,9 +3,8 @@
 // found in the LICENSE file.
 
 #include "chrome/browser/vr/test/mock_xr_device_hook_base.h"
-#include "content/public/browser/system_connector.h"
+#include "chrome/browser/vr/service/xr_device_service.h"
 #include "device/vr/public/mojom/isolated_xr_service.mojom.h"
-#include "services/service_manager/public/cpp/connector.h"
 
 // TODO(https://crbug.com/891832): Remove these conversion functions as part of
 // the switch to only mojom types.
@@ -52,8 +51,7 @@
     : tracked_classes_{device_test::mojom::TrackedDeviceClass::
                            kTrackedDeviceInvalid},
       binding_(this) {
-  content::GetSystemConnector()->BindInterface(
-      device::mojom::kVrIsolatedServiceName,
+  vr::GetXRDeviceService()->BindTestHook(
       mojo::MakeRequest(&service_test_hook_));
 
   device_test::mojom::XRTestHookPtr client;
diff --git a/chrome/browser/vr/webxr_vr_isolated_device_service_test.cc b/chrome/browser/vr/webxr_vr_isolated_device_service_test.cc
index 2348ed7..8611550a 100644
--- a/chrome/browser/vr/webxr_vr_isolated_device_service_test.cc
+++ b/chrome/browser/vr/webxr_vr_isolated_device_service_test.cc
@@ -4,7 +4,11 @@
 
 #include "chrome/browser/vr/test/webvr_browser_test.h"
 
+#include "base/bind_helpers.h"
+#include "base/optional.h"
+#include "base/test/bind_test_util.h"
 #include "build/build_config.h"
+#include "chrome/browser/vr/service/xr_device_service.h"
 #include "chrome/browser/vr/test/mock_xr_device_hook_base.h"
 #include "chrome/browser/vr/test/multi_class_browser_test.h"
 #include "chrome/browser/vr/test/webxr_vr_browser_test.h"
@@ -14,6 +18,13 @@
 
 // Tests that we can recover from a crash/disconnect on the DeviceService
 WEBXR_VR_ALL_RUNTIMES_BROWSER_TEST_F(TestDeviceServiceDisconnect) {
+  // Ensure that any time the XR Device Service is started, we have installed
+  // a new local hook before the IsolatedDeviceProvider has a chance to issue
+  // any enumeration requests.
+  base::Optional<MockXRDeviceHookBase> device_hook(base::in_place);
+  vr::SetXRDeviceServiceStartupCallbackForTesting(
+      base::BindLambdaForTesting([&] { device_hook.emplace(); }));
+
   t->LoadUrlAndAwaitInitialization(
       t->GetFileUrlForHtmlTestFile("test_isolated_device_service_disconnect"));
 
@@ -22,20 +33,14 @@
                                  WebXrVrBrowserTestBase::kPollTimeoutMedium);
 
   t->EnterSessionWithUserGestureOrFail();
-  MockXRDeviceHookBase device_hook;
-  device_hook.TerminateDeviceServiceProcessForTesting();
+
+  device_hook->TerminateDeviceServiceProcessForTesting();
 
   // Ensure that we've actually exited the session.
   t->PollJavaScriptBooleanOrFail(
       "sessionInfos[sessionTypes.IMMERSIVE].currentSession === null",
       WebXrVrBrowserTestBase::kPollTimeoutLong);
 
-  // We need to create a mock here because otherwise WMR opens the real Mixed
-  // Reality Portal if it's installed. The killing of the process unsets the
-  // hook created by the one above, and the presence of the hook is what WMR
-  // uses to determine whether it should uses the mock or real implementation.
-  MockXRDeviceHookBase another_hook;
-
   // We expect one change indicating the device was disconnected, and then
   // one more indicating that the device was re-connected.
   t->PollJavaScriptBooleanOrFail("deviceChanges === 3",
@@ -44,5 +49,7 @@
   // One last check now that we have the device change that we can actually
   // still enter an immersive session.
   t->EnterSessionWithUserGestureOrFail();
+
+  vr::SetXRDeviceServiceStartupCallbackForTesting(base::NullCallback());
 }
 }  // namespace vr
diff --git a/chrome/browser/web_applications/BUILD.gn b/chrome/browser/web_applications/BUILD.gn
index 515fb462..1116b00 100644
--- a/chrome/browser/web_applications/BUILD.gn
+++ b/chrome/browser/web_applications/BUILD.gn
@@ -97,6 +97,8 @@
     "test/test_web_app_url_loader.h",
     "test/web_app_icon_test_utils.cc",
     "test/web_app_icon_test_utils.h",
+    "test/web_app_install_observer.cc",
+    "test/web_app_install_observer.h",
     "test/web_app_test.h",
   ]
 
diff --git a/chrome/browser/web_applications/components/app_registrar.h b/chrome/browser/web_applications/components/app_registrar.h
index 6deeb4b..85286fc 100644
--- a/chrome/browser/web_applications/components/app_registrar.h
+++ b/chrome/browser/web_applications/components/app_registrar.h
@@ -8,7 +8,9 @@
 #include <map>
 
 #include "base/callback_forward.h"
+#include "base/containers/flat_set.h"
 #include "base/observer_list.h"
+#include "chrome/browser/web_applications/components/web_app_constants.h"
 #include "chrome/browser/web_applications/components/web_app_helpers.h"
 #include "third_party/skia/include/core/SkColor.h"
 
@@ -40,6 +42,9 @@
   // Returns true if the app with |app_id| is currently installed.
   virtual bool IsInstalled(const AppId& app_id) const = 0;
 
+  // Returns true if the app with |app_id| is currently fully locally installed.
+  virtual bool IsLocallyInstalled(const AppId& app_id) const = 0;
+
   // Returns true if the app with the specified |start_url| is currently fully
   // locally installed. The provided |start_url| must exactly match the launch
   // URL for the app; this method does not consult the app scope or match URLs
@@ -85,12 +90,15 @@
       const AppId& app_id) const = 0;
   virtual const GURL& GetAppLaunchURL(const AppId& app_id) const = 0;
   virtual base::Optional<GURL> GetAppScope(const AppId& app_id) const = 0;
+  virtual LaunchContainer GetAppLaunchContainer(const AppId& app_id) const = 0;
 
   void AddObserver(AppRegistrarObserver* observer);
   void RemoveObserver(const AppRegistrarObserver* observer);
 
   void NotifyWebAppInstalled(const AppId& app_id);
 
+  virtual base::flat_set<AppId> GetAppIdsForTesting() const = 0;
+
  protected:
   Profile* profile() const { return profile_; }
 
diff --git a/chrome/browser/web_applications/extensions/bookmark_app_registrar.cc b/chrome/browser/web_applications/extensions/bookmark_app_registrar.cc
index cd0c1431..4f095a5 100644
--- a/chrome/browser/web_applications/extensions/bookmark_app_registrar.cc
+++ b/chrome/browser/web_applications/extensions/bookmark_app_registrar.cc
@@ -9,6 +9,7 @@
 #include "base/one_shot_event.h"
 #include "chrome/browser/extensions/convert_web_app.h"
 #include "chrome/browser/extensions/extension_util.h"
+#include "chrome/browser/extensions/launch_util.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/web_applications/components/app_registrar_observer.h"
 #include "chrome/browser/web_applications/extensions/bookmark_app_util.h"
@@ -42,6 +43,12 @@
   return GetExtension(app_id) != nullptr;
 }
 
+bool BookmarkAppRegistrar::IsLocallyInstalled(
+    const web_app::AppId& app_id) const {
+  const Extension* extension = GetExtension(app_id);
+  return extension && BookmarkAppIsLocallyInstalled(profile(), extension);
+}
+
 bool BookmarkAppRegistrar::IsLocallyInstalled(const GURL& start_url) const {
   ExtensionRegistry* registry = ExtensionRegistry::Get(profile());
   const ExtensionSet& extensions = registry->enabled_extensions();
@@ -152,4 +159,35 @@
   return base::nullopt;
 }
 
+web_app::LaunchContainer BookmarkAppRegistrar::GetAppLaunchContainer(
+    const web_app::AppId& app_id) const {
+  const Extension* extension = GetExtension(app_id);
+  if (!extension)
+    return web_app::LaunchContainer::kWindow;
+
+  switch (extensions::GetLaunchContainer(
+      extensions::ExtensionPrefs::Get(profile()), extension)) {
+    case LaunchContainer::kLaunchContainerWindow:
+    case LaunchContainer::kLaunchContainerPanelDeprecated:
+      return web_app::LaunchContainer::kWindow;
+    case LaunchContainer::kLaunchContainerTab:
+      return web_app::LaunchContainer::kTab;
+    case LaunchContainer::kLaunchContainerNone:
+      NOTREACHED();
+      return web_app::LaunchContainer::kDefault;
+  }
+}
+
+base::flat_set<web_app::AppId> BookmarkAppRegistrar::GetAppIdsForTesting()
+    const {
+  base::flat_set<web_app::AppId> app_ids;
+  for (scoped_refptr<const Extension> app :
+       ExtensionRegistry::Get(profile())->enabled_extensions()) {
+    if (app->from_bookmark()) {
+      app_ids.insert(app->id());
+    }
+  }
+  return app_ids;
+}
+
 }  // namespace extensions
diff --git a/chrome/browser/web_applications/extensions/bookmark_app_registrar.h b/chrome/browser/web_applications/extensions/bookmark_app_registrar.h
index fd57ebf2..2321324 100644
--- a/chrome/browser/web_applications/extensions/bookmark_app_registrar.h
+++ b/chrome/browser/web_applications/extensions/bookmark_app_registrar.h
@@ -27,6 +27,7 @@
   void Init(base::OnceClosure callback) override;
   BookmarkAppRegistrar* AsBookmarkAppRegistrar() override;
   bool IsInstalled(const web_app::AppId& app_id) const override;
+  bool IsLocallyInstalled(const web_app::AppId& app_id) const override;
   bool IsLocallyInstalled(const GURL& start_url) const override;
   bool WasExternalAppUninstalledByUser(
       const web_app::AppId& app_id) const override;
@@ -39,6 +40,9 @@
       const web_app::AppId& app_id) const override;
   const GURL& GetAppLaunchURL(const web_app::AppId& app_id) const override;
   base::Optional<GURL> GetAppScope(const web_app::AppId& app_id) const override;
+  web_app::LaunchContainer GetAppLaunchContainer(
+      const web_app::AppId& app_id) const override;
+  base::flat_set<web_app::AppId> GetAppIdsForTesting() const override;
 
   // ExtensionRegistryObserver:
   void OnExtensionUninstalled(content::BrowserContext* browser_context,
diff --git a/chrome/browser/web_applications/test/test_app_registrar.cc b/chrome/browser/web_applications/test/test_app_registrar.cc
index eb53fbae..0728570 100644
--- a/chrome/browser/web_applications/test/test_app_registrar.cc
+++ b/chrome/browser/web_applications/test/test_app_registrar.cc
@@ -42,6 +42,11 @@
   return base::Contains(installed_apps_, app_id);
 }
 
+bool TestAppRegistrar::IsLocallyInstalled(const AppId& app_id) const {
+  NOTIMPLEMENTED();
+  return false;
+}
+
 bool TestAppRegistrar::IsLocallyInstalled(const GURL& start_url) const {
   NOTIMPLEMENTED();
   return false;
@@ -120,4 +125,18 @@
   return base::nullopt;
 }
 
+LaunchContainer TestAppRegistrar::GetAppLaunchContainer(
+    const AppId& app_id) const {
+  NOTIMPLEMENTED();
+  return LaunchContainer::kTab;
+}
+
+base::flat_set<AppId> TestAppRegistrar::GetAppIdsForTesting() const {
+  base::flat_set<AppId> result;
+  for (const std::pair<AppId, AppInfo>& it : installed_apps_) {
+    result.insert(it.first);
+  }
+  return result;
+}
+
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/test/test_app_registrar.h b/chrome/browser/web_applications/test/test_app_registrar.h
index cd288c16..1097250 100644
--- a/chrome/browser/web_applications/test/test_app_registrar.h
+++ b/chrome/browser/web_applications/test/test_app_registrar.h
@@ -40,6 +40,7 @@
   // AppRegistrar
   void Init(base::OnceClosure callback) override;
   bool IsInstalled(const AppId& app_id) const override;
+  bool IsLocallyInstalled(const AppId& app_id) const override;
   bool IsLocallyInstalled(const GURL& start_url) const override;
   bool WasExternalAppUninstalledByUser(const AppId& app_id) const override;
   std::map<AppId, GURL> GetExternallyInstalledApps(
@@ -56,6 +57,9 @@
   base::Optional<SkColor> GetAppThemeColor(const AppId& app_id) const override;
   const GURL& GetAppLaunchURL(const AppId& app_id) const override;
   base::Optional<GURL> GetAppScope(const AppId& app_id) const override;
+  web_app::LaunchContainer GetAppLaunchContainer(
+      const web_app::AppId& app_id) const override;
+  base::flat_set<AppId> GetAppIdsForTesting() const override;
 
  private:
   std::map<AppId, AppInfo> installed_apps_;
diff --git a/chrome/browser/web_applications/test/web_app_install_observer.cc b/chrome/browser/web_applications/test/web_app_install_observer.cc
new file mode 100644
index 0000000..964967e92
--- /dev/null
+++ b/chrome/browser/web_applications/test/web_app_install_observer.cc
@@ -0,0 +1,27 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/web_applications/test/web_app_install_observer.h"
+
+#include "chrome/browser/web_applications/components/web_app_provider_base.h"
+
+namespace web_app {
+
+WebAppInstallObserver::WebAppInstallObserver(Profile* profile) {
+  observer_.Add(&WebAppProviderBase::GetProviderBase(profile)->registrar());
+}
+
+WebAppInstallObserver::~WebAppInstallObserver() = default;
+
+AppId WebAppInstallObserver::AwaitNextInstall() {
+  run_loop_.Run();
+  return std::move(app_id_);
+}
+
+void WebAppInstallObserver::OnWebAppInstalled(const AppId& app_id) {
+  app_id_ = app_id;
+  run_loop_.Quit();
+}
+
+}  // namespace web_app
diff --git a/chrome/browser/web_applications/test/web_app_install_observer.h b/chrome/browser/web_applications/test/web_app_install_observer.h
new file mode 100644
index 0000000..fa57525
--- /dev/null
+++ b/chrome/browser/web_applications/test/web_app_install_observer.h
@@ -0,0 +1,34 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_WEB_APPLICATIONS_TEST_WEB_APP_INSTALL_OBSERVER_H_
+#define CHROME_BROWSER_WEB_APPLICATIONS_TEST_WEB_APP_INSTALL_OBSERVER_H_
+
+#include "base/callback.h"
+#include "base/run_loop.h"
+#include "base/scoped_observer.h"
+#include "chrome/browser/web_applications/components/app_registrar.h"
+#include "chrome/browser/web_applications/components/app_registrar_observer.h"
+
+namespace web_app {
+
+class WebAppInstallObserver final : public AppRegistrarObserver {
+ public:
+  explicit WebAppInstallObserver(Profile* profile);
+  ~WebAppInstallObserver() override;
+
+  AppId AwaitNextInstall();
+
+  // AppRegistrarObserver:
+  void OnWebAppInstalled(const AppId& app_id) override;
+
+ private:
+  base::RunLoop run_loop_;
+  AppId app_id_;
+  ScopedObserver<AppRegistrar, WebAppInstallObserver> observer_{this};
+};
+
+}  // namespace web_app
+
+#endif  // CHROME_BROWSER_WEB_APPLICATIONS_TEST_WEB_APP_INSTALL_OBSERVER_H_
diff --git a/chrome/browser/web_applications/web_app_registrar.cc b/chrome/browser/web_applications/web_app_registrar.cc
index 2cdd115..fe29f6e 100644
--- a/chrome/browser/web_applications/web_app_registrar.cc
+++ b/chrome/browser/web_applications/web_app_registrar.cc
@@ -83,6 +83,11 @@
   return GetAppById(app_id) != nullptr;
 }
 
+bool WebAppRegistrar::IsLocallyInstalled(const AppId& app_id) const {
+  NOTIMPLEMENTED();
+  return false;
+}
+
 bool WebAppRegistrar::IsLocallyInstalled(const GURL& start_url) const {
   NOTIMPLEMENTED();
   return false;
@@ -132,4 +137,15 @@
   return web_app ? base::Optional<GURL>(web_app->scope()) : base::nullopt;
 }
 
+LaunchContainer WebAppRegistrar::GetAppLaunchContainer(
+    const AppId& app_id) const {
+  NOTIMPLEMENTED();
+  return LaunchContainer::kTab;
+}
+
+base::flat_set<AppId> WebAppRegistrar::GetAppIdsForTesting() const {
+  NOTIMPLEMENTED();
+  return {};
+}
+
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/web_app_registrar.h b/chrome/browser/web_applications/web_app_registrar.h
index 8a34314..2a35e12e6 100644
--- a/chrome/browser/web_applications/web_app_registrar.h
+++ b/chrome/browser/web_applications/web_app_registrar.h
@@ -39,6 +39,7 @@
   void Init(base::OnceClosure callback) override;
   WebAppRegistrar* AsWebAppRegistrar() override;
   bool IsInstalled(const AppId& app_id) const override;
+  bool IsLocallyInstalled(const AppId& app_id) const override;
   bool IsLocallyInstalled(const GURL& start_url) const override;
   bool WasExternalAppUninstalledByUser(const AppId& app_id) const override;
   base::Optional<AppId> FindAppWithUrlInScope(const GURL& url) const override;
@@ -48,6 +49,8 @@
   base::Optional<SkColor> GetAppThemeColor(const AppId& app_id) const override;
   const GURL& GetAppLaunchURL(const AppId& app_id) const override;
   base::Optional<GURL> GetAppScope(const AppId& app_id) const override;
+  LaunchContainer GetAppLaunchContainer(const AppId& app_id) const override;
+  base::flat_set<AppId> GetAppIdsForTesting() const override;
 
  private:
   void OnDatabaseOpened(base::OnceClosure callback, Registry registry);
diff --git a/chrome/common/BUILD.gn b/chrome/common/BUILD.gn
index 6c749b5..ccc4366f 100644
--- a/chrome/common/BUILD.gn
+++ b/chrome/common/BUILD.gn
@@ -45,8 +45,6 @@
     "ENABLE_NATIVE_NOTIFICATIONS=$enable_native_notifications",
     "ENABLE_SERVICE_DISCOVERY=$enable_service_discovery",
     "ENABLE_SESSION_SERVICE=$enable_session_service",
-    "ENABLE_SIMPLE_BROWSER_SERVICE_IN_PROCESS=$enable_simple_browser_service_in_process",
-    "ENABLE_SIMPLE_BROWSER_SERVICE_OUT_OF_PROCESS=$enable_simple_browser_service_out_of_process",
     "ENABLE_SUPERVISED_USERS=$enable_supervised_users",
     "ENABLE_WAYLAND_SERVER=$enable_wayland_server",
     "ENABLE_WEBUI_TAB_STRIP=$enable_webui_tab_strip",
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc
index 7e3c9918..220e576f 100644
--- a/chrome/common/chrome_features.cc
+++ b/chrome/common/chrome_features.cc
@@ -662,7 +662,7 @@
 
 // Enables or disables the App Management UI.
 const base::Feature kAppManagement{"AppManagement",
-                                   base::FEATURE_DISABLED_BY_DEFAULT};
+                                   base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Disable downloads of unsafe file types over insecure transports if initiated
 // from a secure page
diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc
index 348f63b..d5cd5ca 100644
--- a/chrome/common/chrome_switches.cc
+++ b/chrome/common/chrome_switches.cc
@@ -377,17 +377,6 @@
 // Enable automatically pressing the print button in print preview.
 const char kKioskModePrinting[]             = "kiosk-printing";
 
-// Launches an in-process instance of the simple_browser service on startup if
-// this Chrome build supports it. See |enable_simple_browser_service_in_process|
-// in chrome/common/features.gni.
-const char kLaunchInProcessSimpleBrowserSwitch[] =
-    "launch-in-process-simple-browser";
-
-// Launches a sandboxed out-of-process instance of the simple_browser service on
-// startup if this Chrome build supports it. See
-// |enable_simple_browser_service_out_of_process| in chrome/common/features.gni.
-const char kLaunchSimpleBrowserSwitch[] = "launch-simple-browser";
-
 // Loads the Media Router component extension on startup.
 const char kLoadMediaRouterComponentExtension[] =
     "load-media-router-component-extension";
diff --git a/chrome/common/extensions/api/_api_features.json b/chrome/common/extensions/api/_api_features.json
index 513980a2..4c9f765 100644
--- a/chrome/common/extensions/api/_api_features.json
+++ b/chrome/common/extensions/api/_api_features.json
@@ -547,6 +547,10 @@
     "dependencies": ["permission:login"],
     "contexts": ["blessed_extension"]
   },
+  "loginScreenStorage": {
+    "dependencies": ["permission:loginScreenStorage"],
+    "contexts": ["blessed_extension"]
+  },
   "loginScreenUi": {
     "dependencies": ["permission:loginScreenUi"],
     "contexts": ["blessed_extension"]
diff --git a/chrome/common/extensions/api/_permission_features.json b/chrome/common/extensions/api/_permission_features.json
index 8b17aed8..a856d0a 100644
--- a/chrome/common/extensions/api/_permission_features.json
+++ b/chrome/common/extensions/api/_permission_features.json
@@ -442,7 +442,21 @@
     "platforms": ["chromeos"],
     "whitelist": [
       "E219EE36A3B40612FD2A8CD6937B03EF0C97D3FE",  // Imprivata (login screen)
-      "A24DE1B21A67E25FB62AC8491642038FE25DA75B"   // Imprivata (in session)
+      "4DBFC1C52D6660DD90791976DF7FEF7B3D360509",  // Imprivata (login screen) DEV
+      "A24DE1B21A67E25FB62AC8491642038FE25DA75B",  // Imprivata (in session)
+      "6B25164FFE2BADB5F1DBBD301CC022170267022D"   // Imprivata (in session) DEV
+    ]
+  },
+  "loginScreenStorage": {
+    "channel": "dev",
+    "extension_types": ["login_screen_extension", "extension"],
+    "location": "policy",
+    "platforms": ["chromeos"],
+    "whitelist": [
+      "E219EE36A3B40612FD2A8CD6937B03EF0C97D3FE",  // Imprivata (login screen)
+      "4DBFC1C52D6660DD90791976DF7FEF7B3D360509",  // Imprivata (login screen) DEV
+      "A24DE1B21A67E25FB62AC8491642038FE25DA75B",  // Imprivata (in session)
+      "6B25164FFE2BADB5F1DBBD301CC022170267022D"   // Imprivata (in session) DEV
     ]
   },
   "loginScreenUi": {
@@ -452,7 +466,8 @@
     "platforms": ["chromeos"],
     "whitelist": [
       "7FE4A999359A456C4B0FB7B7AD85CEA29CA50519", // chrome.loginScreenUi test extension
-      "E219EE36A3B40612FD2A8CD6937B03EF0C97D3FE"  // Imprivata (login screen)
+      "E219EE36A3B40612FD2A8CD6937B03EF0C97D3FE", // Imprivata (login screen)
+      "4DBFC1C52D6660DD90791976DF7FEF7B3D360509"  // Imprivata (login screen) DEV
     ]
   },
   "webcamPrivate": {
diff --git a/chrome/common/extensions/api/api_sources.gni b/chrome/common/extensions/api/api_sources.gni
index 9c0c100..6652939f 100644
--- a/chrome/common/extensions/api/api_sources.gni
+++ b/chrome/common/extensions/api/api_sources.gni
@@ -98,6 +98,7 @@
     "input_method_private.json",
     "launcher_search_provider.idl",
     "login.idl",
+    "login_screen_storage.idl",
     "login_screen_ui.idl",
     "platform_keys.idl",
     "platform_keys_internal.idl",
diff --git a/chrome/common/extensions/api/autotest_private.idl b/chrome/common/extensions/api/autotest_private.idl
index f1b72de8..4590a9c 100644
--- a/chrome/common/extensions/api/autotest_private.idl
+++ b/chrome/common/extensions/api/autotest_private.idl
@@ -379,6 +379,11 @@
     // |callback|: Called when the operation has completed.
     static void importCrostini(DOMString path, VoidCallback callback);
 
+    // Register a component with CrOSComponentManager.
+    // |name|: The name of the component.
+    // |path|: Path to the component.
+    static void registerComponent(DOMString name, DOMString path);
+
     // Takes a screenshot and returns the data in base64 encoded PNG format.
     static void takeScreenshot(TakeScreenshotCallback callback);
 
diff --git a/chrome/common/extensions/api/login_screen_storage.idl b/chrome/common/extensions/api/login_screen_storage.idl
new file mode 100644
index 0000000..9d6aa4d
--- /dev/null
+++ b/chrome/common/extensions/api/login_screen_storage.idl
@@ -0,0 +1,44 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Use the <code>chrome.loginScreenStorage</code> API to store persistent data
+// from the login screen or inject data into the session.
+[platforms=("chromeos"),
+ implemented_in="chrome/browser/chromeos/extensions/login_screen/login_screen_storage/login_screen_storage_api.h"]
+namespace loginScreenStorage {
+  callback StoreCallback = void ();
+  callback RetrieveCallback = void (DOMString data);
+
+  interface Functions {
+    // Stores persistent data from the login screen. This data can be accessed
+    // later using $(ref:retrievePersistentData) by any extension from the
+    // specified extension ids. This method will fail if called while a user
+    // session is active.
+    // |extensionIds|: IDs of the extensions that should have access to the
+    // stored data.
+    // |data|: The data to store.
+    static void storePersistentData(DOMString[] extensionIds,
+                                    DOMString data,
+                                    StoreCallback callback);
+
+    // Retrieves persistent data that was previously stored using
+    // $(ref:storePersistentData) for the caller's extension ID.
+    static void retrievePersistentData(RetrieveCallback callback);
+
+    // Stores credentials for later access from the user session. This method
+    // will fail if called while a user session is active.
+    // |extensionId|: ID of the in-session extension that should have access to
+    // these credentials. Credentials stored using this method are deleted on
+    // session exit.
+    // |credentials|: The credentials to store.
+    static void storeCredentials(DOMString extensionId,
+                                 DOMString credentials,
+                                 StoreCallback callback);
+
+    // Retrieves credentials that were previosly stored using
+    // $(ref:storeCredentials). The caller's extension ID should be the same as
+    // the extension id passed to the $(ref:storeCredentials).
+    static void retrieveCredentials(RetrieveCallback callback);
+  };
+};
diff --git a/chrome/common/extensions/permissions/chrome_permission_message_rules.cc b/chrome/common/extensions/permissions/chrome_permission_message_rules.cc
index 623aa14..606258ce 100644
--- a/chrome/common/extensions/permissions/chrome_permission_message_rules.cc
+++ b/chrome/common/extensions/permissions/chrome_permission_message_rules.cc
@@ -669,6 +669,9 @@
       {IDS_EXTENSION_PROMPT_WARNING_LOGIN_SCREEN_UI,
        {APIPermission::kLoginScreenUi},
        {}},
+      {IDS_EXTENSION_PROMPT_WARNING_LOGIN_SCREEN_STORAGE,
+       {APIPermission::kLoginScreenStorage},
+       {}},
       {IDS_EXTENSION_PROMPT_WARNING_TRANSIENT_BACKGROUND,
        {APIPermission::kTransientBackground},
        {}},
diff --git a/chrome/common/features.gni b/chrome/common/features.gni
index 3a0f643..552ce41b 100644
--- a/chrome/common/features.gni
+++ b/chrome/common/features.gni
@@ -50,14 +50,6 @@
   # Android stores them separately on the Java side.
   enable_session_service = !is_android && !is_chromecast
 
-  # Enables embedding of the simple_browser service, a lightweight browser
-  # application which consumes the Content Service. Useful primarily for
-  # developer testing.
-  enable_simple_browser_service_in_process =
-      use_aura && (is_debug || dcheck_always_on) && !is_chromecast
-  enable_simple_browser_service_out_of_process =
-      is_chromeos && (is_debug || dcheck_always_on)
-
   enable_supervised_users = is_chromeos || is_android
 
   # Indicates if Wayland display server support is enabled.
diff --git a/chrome/elevation_service/elevated_recovery_impl.cc b/chrome/elevation_service/elevated_recovery_impl.cc
index 0082f63..e2f36a01 100644
--- a/chrome/elevation_service/elevated_recovery_impl.cc
+++ b/chrome/elevation_service/elevated_recovery_impl.cc
@@ -95,6 +95,14 @@
   const base::FilePath to_crx_path =
       to_dir.GetPath().Append(from_crx_path.BaseName());
 
+  // We check the input file for CRX and signature validity before copying to
+  // the secure location. This is to prevent us from copying unintended files,
+  // including files that may be private to a particular user or group.
+  if (crx_file::Verify(from_crx_path, crx_format, {crx_hash}, {}, nullptr,
+                       nullptr) != crx_file::VerifierResult::OK_FULL) {
+    return CRYPT_E_NO_MATCH;
+  }
+
   if (!base::CopyFile(from_crx_path, to_crx_path))
     return HRESULTFromLastError();
 
diff --git a/chrome/elevation_service/elevated_recovery_unittest.cc b/chrome/elevation_service/elevated_recovery_unittest.cc
index 54ef8bdc..895ae00 100644
--- a/chrome/elevation_service/elevated_recovery_unittest.cc
+++ b/chrome/elevation_service/elevated_recovery_unittest.cc
@@ -113,7 +113,7 @@
                 base::FilePath(kManifestJSONFileName), 0, &proc_handle));
 
   // Non-existent CRX file.
-  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
+  EXPECT_EQ(CRYPT_E_NO_MATCH,
             elevation_service::RunCRX(
                 TestFile("nonexistent.crx3"),
                 base::CommandLine(base::CommandLine::NO_PROGRAM),
diff --git a/chrome/services/isolated_xr_device/BUILD.gn b/chrome/services/isolated_xr_device/BUILD.gn
index 33a5b14..5e3335e 100644
--- a/chrome/services/isolated_xr_device/BUILD.gn
+++ b/chrome/services/isolated_xr_device/BUILD.gn
@@ -26,22 +26,5 @@
     "//device/vr:vr",
     "//device/vr/public/mojom",
     "//device/vr/public/mojom:test_mojom",
-    "//services/service_manager/public/cpp",
-    "//services/service_manager/public/mojom",
-  ]
-}
-
-source_set("manifest") {
-  sources = [
-    "manifest.cc",
-    "manifest.h",
-  ]
-
-  deps = [
-    "//base",
-    "//chrome:strings",
-    "//device/vr/public/mojom",
-    "//device/vr/public/mojom:test_mojom",
-    "//services/service_manager/public/cpp",
   ]
 }
diff --git a/chrome/services/isolated_xr_device/DEPS b/chrome/services/isolated_xr_device/DEPS
index a210e73d..4a8d7e8 100644
--- a/chrome/services/isolated_xr_device/DEPS
+++ b/chrome/services/isolated_xr_device/DEPS
@@ -1,5 +1,4 @@
 include_rules = [
   "+device/vr",
   "+mojo/public",
-  "+services/service_manager/public"
-]
\ No newline at end of file
+]
diff --git a/chrome/services/isolated_xr_device/OWNERS b/chrome/services/isolated_xr_device/OWNERS
index b95d4bd6..598e59b 100644
--- a/chrome/services/isolated_xr_device/OWNERS
+++ b/chrome/services/isolated_xr_device/OWNERS
@@ -2,10 +2,5 @@
 ddorwin@chromium.org
 klausw@chromium.org
 
-per-file manifest.cc=set noparent
-per-file manifest.cc=file://ipc/SECURITY_OWNERS
-per-file manifest.h=set noparent
-per-file manifest.h=file://ipc/SECURITY_OWNERS
-
 # TEAM: xr-dev@chromium.org
 # COMPONENT: Internals>XR
diff --git a/chrome/services/isolated_xr_device/manifest.cc b/chrome/services/isolated_xr_device/manifest.cc
deleted file mode 100644
index 9c1deb6..0000000
--- a/chrome/services/isolated_xr_device/manifest.cc
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/services/isolated_xr_device/manifest.h"
-
-#include "base/no_destructor.h"
-#include "chrome/grit/generated_resources.h"
-#include "device/vr/public/mojom/browser_test_interfaces.mojom.h"
-#include "device/vr/public/mojom/isolated_xr_service.mojom.h"
-#include "services/service_manager/public/cpp/manifest_builder.h"
-
-const service_manager::Manifest& GetXrDeviceServiceManifest() {
-  static base::NoDestructor<service_manager::Manifest> manifest{
-      service_manager::ManifestBuilder()
-          .WithServiceName(device::mojom::kVrIsolatedServiceName)
-          .WithDisplayName("XR Isolated Device Service")
-          .WithOptions(
-              service_manager::ManifestOptionsBuilder()
-                  .WithExecutionMode(service_manager::Manifest::ExecutionMode::
-                                         kOutOfProcessBuiltin)
-                  .WithSandboxType("xr_compositing")
-                  .Build())
-          .ExposeCapability("xr_device_test_hook",
-                            service_manager::Manifest::InterfaceList<
-                                device_test::mojom::XRServiceTestHook>())
-          .ExposeCapability("xr_device_provider",
-                            service_manager::Manifest::InterfaceList<
-                                device::mojom::IsolatedXRRuntimeProvider>())
-          .Build()};
-  return *manifest;
-}
diff --git a/chrome/services/isolated_xr_device/manifest.h b/chrome/services/isolated_xr_device/manifest.h
deleted file mode 100644
index 9905c33..0000000
--- a/chrome/services/isolated_xr_device/manifest.h
+++ /dev/null
@@ -1,12 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_SERVICES_ISOLATED_XR_DEVICE_MANIFEST_H_
-#define CHROME_SERVICES_ISOLATED_XR_DEVICE_MANIFEST_H_
-
-#include "services/service_manager/public/cpp/manifest.h"
-
-const service_manager::Manifest& GetXrDeviceServiceManifest();
-
-#endif  // CHROME_SERVICES_ISOLATED_XR_DEVICE_MANIFEST_H_
diff --git a/chrome/services/isolated_xr_device/xr_device_service.cc b/chrome/services/isolated_xr_device/xr_device_service.cc
index b4811c6e..10ce174 100644
--- a/chrome/services/isolated_xr_device/xr_device_service.cc
+++ b/chrome/services/isolated_xr_device/xr_device_service.cc
@@ -4,11 +4,10 @@
 
 #include "chrome/services/isolated_xr_device/xr_device_service.h"
 
-#include "base/bind.h"
 #include "build/build_config.h"
 #include "chrome/services/isolated_xr_device/xr_runtime_provider.h"
 #include "chrome/services/isolated_xr_device/xr_service_test_hook.h"
-#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "mojo/public/cpp/bindings/self_owned_receiver.h"
 
 #if defined(OS_WIN)
 #include "base/win/com_init_check_hook.h"
@@ -16,41 +15,26 @@
 
 namespace device {
 
-void XrDeviceService::OnDeviceProviderRequest(
-    device::mojom::IsolatedXRRuntimeProviderRequest request) {
-  mojo::MakeStrongBinding(std::make_unique<IsolatedXRRuntimeProvider>(
-                              service_keepalive_.CreateRef()),
-                          std::move(request));
-}
-
-void XrDeviceService::OnTestHookRequest(
-    device_test::mojom::XRServiceTestHookRequest request) {
-  mojo::MakeStrongBinding(
-      std::make_unique<XRServiceTestHook>(service_keepalive_.CreateRef()),
-      std::move(request));
-}
-
-XrDeviceService::XrDeviceService(service_manager::mojom::ServiceRequest request)
-    : service_binding_(this, std::move(request)),
-      service_keepalive_(&service_binding_, base::TimeDelta()) {
+XrDeviceService::XrDeviceService(
+    mojo::PendingReceiver<mojom::XRDeviceService> receiver)
+    : receiver_(this, std::move(receiver)) {
 #if defined(OS_WIN)
   base::win::ComInitCheckHook::DisableCOMChecksForProcess();
 #endif  // defined(OS_WIN)
-
-  // Register device provider here.
-  registry_.AddInterface(base::BindRepeating(
-      &XrDeviceService::OnDeviceProviderRequest, base::Unretained(this)));
-  registry_.AddInterface(base::BindRepeating(
-      &XrDeviceService::OnTestHookRequest, base::Unretained(this)));
 }
 
 XrDeviceService::~XrDeviceService() = default;
 
-void XrDeviceService::OnBindInterface(
-    const service_manager::BindSourceInfo& source_info,
-    const std::string& interface_name,
-    mojo::ScopedMessagePipeHandle interface_pipe) {
-  registry_.BindInterface(interface_name, std::move(interface_pipe));
+void XrDeviceService::BindRuntimeProvider(
+    mojo::PendingReceiver<mojom::IsolatedXRRuntimeProvider> receiver) {
+  mojo::MakeSelfOwnedReceiver(std::make_unique<IsolatedXRRuntimeProvider>(),
+                              std::move(receiver));
+}
+
+void XrDeviceService::BindTestHook(
+    mojo::PendingReceiver<device_test::mojom::XRServiceTestHook> receiver) {
+  mojo::MakeSelfOwnedReceiver(std::make_unique<XRServiceTestHook>(),
+                              std::move(receiver));
 }
 
 }  // namespace device
diff --git a/chrome/services/isolated_xr_device/xr_device_service.h b/chrome/services/isolated_xr_device/xr_device_service.h
index 26f24a1e..fe74db5 100644
--- a/chrome/services/isolated_xr_device/xr_device_service.h
+++ b/chrome/services/isolated_xr_device/xr_device_service.h
@@ -7,33 +7,26 @@
 
 #include "device/vr/public/mojom/browser_test_interfaces.mojom.h"
 #include "device/vr/public/mojom/isolated_xr_service.mojom.h"
-#include "mojo/public/cpp/bindings/binding_set.h"
-#include "services/service_manager/public/cpp/binder_registry.h"
-#include "services/service_manager/public/cpp/service.h"
-#include "services/service_manager/public/cpp/service_binding.h"
-#include "services/service_manager/public/cpp/service_keepalive.h"
-#include "services/service_manager/public/mojom/service.mojom.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/receiver.h"
 
 namespace device {
 
-class XrDeviceService : public service_manager::Service {
+class XrDeviceService : public mojom::XRDeviceService {
  public:
-  explicit XrDeviceService(service_manager::mojom::ServiceRequest request);
+  explicit XrDeviceService(
+      mojo::PendingReceiver<mojom::XRDeviceService> receiver);
   ~XrDeviceService() override;
 
-  void OnDeviceProviderRequest(
-      device::mojom::IsolatedXRRuntimeProviderRequest request);
-  void OnTestHookRequest(device_test::mojom::XRServiceTestHookRequest request);
-
  private:
-  // service_manager::Service:
-  void OnBindInterface(const service_manager::BindSourceInfo& source_info,
-                       const std::string& interface_name,
-                       mojo::ScopedMessagePipeHandle interface_pipe) override;
+  // mojom::XRDeviceService implementation:
+  void BindRuntimeProvider(
+      mojo::PendingReceiver<mojom::IsolatedXRRuntimeProvider> receiver)
+      override;
+  void BindTestHook(mojo::PendingReceiver<device_test::mojom::XRServiceTestHook>
+                        receiver) override;
 
-  service_manager::ServiceBinding service_binding_;
-  service_manager::ServiceKeepalive service_keepalive_;
-  service_manager::BinderRegistry registry_;
+  mojo::Receiver<mojom::XRDeviceService> receiver_;
 
   DISALLOW_COPY_AND_ASSIGN(XrDeviceService);
 };
diff --git a/chrome/services/isolated_xr_device/xr_runtime_provider.cc b/chrome/services/isolated_xr_device/xr_runtime_provider.cc
index a456e9d..0777761 100644
--- a/chrome/services/isolated_xr_device/xr_runtime_provider.cc
+++ b/chrome/services/isolated_xr_device/xr_runtime_provider.cc
@@ -221,9 +221,7 @@
 }
 #endif  // BUILDFLAG(ENABLE_OPENXR)
 
-IsolatedXRRuntimeProvider::IsolatedXRRuntimeProvider(
-    std::unique_ptr<service_manager::ServiceKeepaliveRef> service_ref)
-    : service_ref_(std::move(service_ref)) {}
+IsolatedXRRuntimeProvider::IsolatedXRRuntimeProvider() = default;
 
 IsolatedXRRuntimeProvider::~IsolatedXRRuntimeProvider() {
 #if BUILDFLAG(ENABLE_WINDOWS_MR)
diff --git a/chrome/services/isolated_xr_device/xr_runtime_provider.h b/chrome/services/isolated_xr_device/xr_runtime_provider.h
index ded32b9..1d8b8784 100644
--- a/chrome/services/isolated_xr_device/xr_runtime_provider.h
+++ b/chrome/services/isolated_xr_device/xr_runtime_provider.h
@@ -9,7 +9,6 @@
 
 #include "device/vr/buildflags/buildflags.h"
 #include "device/vr/public/mojom/isolated_xr_service.mojom.h"
-#include "services/service_manager/public/cpp/service_keepalive.h"
 
 namespace device {
 class OculusDevice;
@@ -22,8 +21,7 @@
 class IsolatedXRRuntimeProvider
     : public device::mojom::IsolatedXRRuntimeProvider {
  public:
-  IsolatedXRRuntimeProvider(
-      std::unique_ptr<service_manager::ServiceKeepaliveRef> service_ref);
+  IsolatedXRRuntimeProvider();
   ~IsolatedXRRuntimeProvider() final;
 
   void RequestDevices(
@@ -32,9 +30,6 @@
   enum class RuntimeStatus;
 
  private:
-  const std::unique_ptr<service_manager::ServiceKeepaliveRef> service_ref_;
-
-  IsolatedXRRuntimeProvider();
   void PollForDeviceChanges();
   void SetupPollingForDeviceChanges();
 
diff --git a/chrome/services/isolated_xr_device/xr_service_test_hook.cc b/chrome/services/isolated_xr_device/xr_service_test_hook.cc
index a8fcfbd..a7b8123 100644
--- a/chrome/services/isolated_xr_device/xr_service_test_hook.cc
+++ b/chrome/services/isolated_xr_device/xr_service_test_hook.cc
@@ -86,8 +86,6 @@
   }
 }
 
-XRServiceTestHook::XRServiceTestHook(
-    std::unique_ptr<service_manager::ServiceKeepaliveRef> service_ref)
-    : service_ref_(std::move(service_ref)) {}
+XRServiceTestHook::XRServiceTestHook() = default;
 
 }  // namespace device
diff --git a/chrome/services/isolated_xr_device/xr_service_test_hook.h b/chrome/services/isolated_xr_device/xr_service_test_hook.h
index 58a8f375c5..325df24 100644
--- a/chrome/services/isolated_xr_device/xr_service_test_hook.h
+++ b/chrome/services/isolated_xr_device/xr_service_test_hook.h
@@ -8,15 +8,13 @@
 #include <memory>
 
 #include "device/vr/public/mojom/browser_test_interfaces.mojom.h"
-#include "services/service_manager/public/cpp/service_keepalive.h"
 
 namespace device {
 class XRTestHookWrapper;
 
 class XRServiceTestHook : public device_test::mojom::XRServiceTestHook {
  public:
-  explicit XRServiceTestHook(
-      std::unique_ptr<service_manager::ServiceKeepaliveRef> service_ref);
+  XRServiceTestHook();
   ~XRServiceTestHook() final;
 
   using DeviceCrashCallback = device_test::mojom::XRServiceTestHook::
@@ -30,7 +28,6 @@
 
  private:
   std::unique_ptr<XRTestHookWrapper> wrapper_;
-  const std::unique_ptr<service_manager::ServiceKeepaliveRef> service_ref_;
 };
 
 }  // namespace device
diff --git a/chrome/services/media_gallery_util/BUILD.gn b/chrome/services/media_gallery_util/BUILD.gn
index 47a9aa1..a8afac8 100644
--- a/chrome/services/media_gallery_util/BUILD.gn
+++ b/chrome/services/media_gallery_util/BUILD.gn
@@ -8,8 +8,6 @@
   sources = [
     "ipc_data_source.cc",
     "ipc_data_source.h",
-    "media_gallery_util_service.cc",
-    "media_gallery_util_service.h",
     "media_metadata_parser.cc",
     "media_metadata_parser.h",
     "media_parser.cc",
@@ -35,7 +33,6 @@
 
   public_deps = [
     "//chrome/services/media_gallery_util/public/mojom",
-    "//services/service_manager/public/cpp",
   ]
 
   if (media_use_ffmpeg) {
diff --git a/chrome/services/media_gallery_util/DEPS b/chrome/services/media_gallery_util/DEPS
index 63340cea..ad73870 100644
--- a/chrome/services/media_gallery_util/DEPS
+++ b/chrome/services/media_gallery_util/DEPS
@@ -1,5 +1,4 @@
 include_rules = [
-  "+content/public",
   "+media",
   "+third_party/libyuv",
 ]
diff --git a/chrome/services/media_gallery_util/ipc_data_source.cc b/chrome/services/media_gallery_util/ipc_data_source.cc
index eb346f3d..506da79 100644
--- a/chrome/services/media_gallery_util/ipc_data_source.cc
+++ b/chrome/services/media_gallery_util/ipc_data_source.cc
@@ -6,10 +6,9 @@
 
 #include "base/bind.h"
 #include "base/threading/thread_task_runner_handle.h"
-#include "content/public/utility/utility_thread.h"
 
 IPCDataSource::IPCDataSource(
-    chrome::mojom::MediaDataSourcePtr media_data_source,
+    mojo::PendingRemote<chrome::mojom::MediaDataSource> media_data_source,
     int64_t total_size)
     : media_data_source_(std::move(media_data_source)),
       total_size_(total_size),
diff --git a/chrome/services/media_gallery_util/ipc_data_source.h b/chrome/services/media_gallery_util/ipc_data_source.h
index 11ab63134..36e83ea 100644
--- a/chrome/services/media_gallery_util/ipc_data_source.h
+++ b/chrome/services/media_gallery_util/ipc_data_source.h
@@ -13,6 +13,8 @@
 #include "base/threading/thread_checker.h"
 #include "chrome/services/media_gallery_util/public/mojom/media_parser.mojom.h"
 #include "media/base/data_source.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/remote.h"
 
 namespace base {
 class TaskRunner;
@@ -25,8 +27,9 @@
 class IPCDataSource : public media::DataSource {
  public:
   // May only be called on the utility thread.
-  IPCDataSource(chrome::mojom::MediaDataSourcePtr media_data_source,
-                int64_t total_size);
+  IPCDataSource(
+      mojo::PendingRemote<chrome::mojom::MediaDataSource> media_data_source,
+      int64_t total_size);
   ~IPCDataSource() override;
 
   // media::DataSource implementation. The methods may be called on any single
@@ -51,7 +54,7 @@
                 const ReadCB& callback,
                 const std::vector<uint8_t>& data);
 
-  chrome::mojom::MediaDataSourcePtr media_data_source_;
+  mojo::Remote<chrome::mojom::MediaDataSource> media_data_source_;
   const int64_t total_size_;
 
   scoped_refptr<base::TaskRunner> utility_task_runner_;
diff --git a/chrome/services/media_gallery_util/media_gallery_util_service.cc b/chrome/services/media_gallery_util/media_gallery_util_service.cc
deleted file mode 100644
index f39ce5c..0000000
--- a/chrome/services/media_gallery_util/media_gallery_util_service.cc
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/services/media_gallery_util/media_gallery_util_service.h"
-
-#include "base/bind.h"
-#include "build/build_config.h"
-#include "chrome/services/media_gallery_util/media_parser_factory.h"
-#include "mojo/public/cpp/bindings/strong_binding.h"
-
-namespace {
-
-void OnMediaParserFactoryRequest(
-    service_manager::ServiceKeepalive* keepalive,
-    chrome::mojom::MediaParserFactoryRequest request) {
-  mojo::MakeStrongBinding(
-      std::make_unique<MediaParserFactory>(keepalive->CreateRef()),
-      std::move(request));
-}
-
-}  // namespace
-
-MediaGalleryUtilService::MediaGalleryUtilService(
-    service_manager::mojom::ServiceRequest request)
-    : service_binding_(this, std::move(request)),
-      service_keepalive_(&service_binding_, base::TimeDelta()) {}
-
-MediaGalleryUtilService::~MediaGalleryUtilService() = default;
-
-void MediaGalleryUtilService::OnStart() {
-  registry_.AddInterface(
-      base::BindRepeating(&OnMediaParserFactoryRequest, &service_keepalive_));
-}
-
-void MediaGalleryUtilService::OnBindInterface(
-    const service_manager::BindSourceInfo& source_info,
-    const std::string& interface_name,
-    mojo::ScopedMessagePipeHandle interface_pipe) {
-  registry_.BindInterface(interface_name, std::move(interface_pipe));
-}
diff --git a/chrome/services/media_gallery_util/media_gallery_util_service.h b/chrome/services/media_gallery_util/media_gallery_util_service.h
deleted file mode 100644
index 05961fc1..0000000
--- a/chrome/services/media_gallery_util/media_gallery_util_service.h
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_SERVICES_MEDIA_GALLERY_UTIL_MEDIA_GALLERY_UTIL_SERVICE_H_
-#define CHROME_SERVICES_MEDIA_GALLERY_UTIL_MEDIA_GALLERY_UTIL_SERVICE_H_
-
-#include <memory>
-
-#include "services/service_manager/public/cpp/binder_registry.h"
-#include "services/service_manager/public/cpp/service.h"
-#include "services/service_manager/public/cpp/service_binding.h"
-#include "services/service_manager/public/cpp/service_keepalive.h"
-#include "services/service_manager/public/mojom/service.mojom.h"
-
-class MediaGalleryUtilService : public service_manager::Service {
- public:
-  explicit MediaGalleryUtilService(
-      service_manager::mojom::ServiceRequest request);
-  ~MediaGalleryUtilService() override;
-
-  // Lifescycle events that occur after the service has started to spinup.
-  void OnStart() override;
-  void OnBindInterface(const service_manager::BindSourceInfo& source_info,
-                       const std::string& interface_name,
-                       mojo::ScopedMessagePipeHandle interface_pipe) override;
-
- private:
-  service_manager::ServiceBinding service_binding_;
-  service_manager::ServiceKeepalive service_keepalive_;
-  service_manager::BinderRegistry registry_;
-
-  DISALLOW_COPY_AND_ASSIGN(MediaGalleryUtilService);
-};
-
-#endif  // CHROME_SERVICES_MEDIA_GALLERY_UTIL_MEDIA_GALLERY_UTIL_SERVICE_H_
diff --git a/chrome/services/media_gallery_util/media_parser.cc b/chrome/services/media_gallery_util/media_parser.cc
index bb9737f..54cfdead 100644
--- a/chrome/services/media_gallery_util/media_parser.cc
+++ b/chrome/services/media_gallery_util/media_parser.cc
@@ -29,9 +29,7 @@
 
 }  // namespace
 
-MediaParser::MediaParser(
-    std::unique_ptr<service_manager::ServiceContextRef> service_ref)
-    : service_ref_(std::move(service_ref)) {}
+MediaParser::MediaParser() = default;
 
 MediaParser::~MediaParser() = default;
 
@@ -39,7 +37,7 @@
     const std::string& mime_type,
     int64_t total_size,
     bool get_attached_images,
-    chrome::mojom::MediaDataSourcePtr media_data_source,
+    mojo::PendingRemote<chrome::mojom::MediaDataSource> media_data_source,
     ParseMediaMetadataCallback callback) {
   auto source =
       std::make_unique<IPCDataSource>(std::move(media_data_source), total_size);
diff --git a/chrome/services/media_gallery_util/media_parser.h b/chrome/services/media_gallery_util/media_parser.h
index a61b49a..c39c681 100644
--- a/chrome/services/media_gallery_util/media_parser.h
+++ b/chrome/services/media_gallery_util/media_parser.h
@@ -11,28 +11,25 @@
 #include "base/files/file.h"
 #include "base/time/time.h"
 #include "chrome/services/media_gallery_util/public/mojom/media_parser.mojom.h"
-#include "services/service_manager/public/cpp/service_context_ref.h"
 
 class MediaParser : public chrome::mojom::MediaParser {
  public:
-  explicit MediaParser(
-      std::unique_ptr<service_manager::ServiceContextRef> service_ref);
+  MediaParser();
   ~MediaParser() override;
 
  private:
   // chrome::mojom::MediaParser:
-  void ParseMediaMetadata(const std::string& mime_type,
-                          int64_t total_size,
-                          bool get_attached_images,
-                          chrome::mojom::MediaDataSourcePtr media_data_source,
-                          ParseMediaMetadataCallback callback) override;
+  void ParseMediaMetadata(
+      const std::string& mime_type,
+      int64_t total_size,
+      bool get_attached_images,
+      mojo::PendingRemote<chrome::mojom::MediaDataSource> media_data_source,
+      ParseMediaMetadataCallback callback) override;
   void CheckMediaFile(base::TimeDelta decode_time,
                       base::File file,
                       CheckMediaFileCallback callback) override;
   void GetCpuInfo(GetCpuInfoCallback callback) override;
 
-  const std::unique_ptr<service_manager::ServiceContextRef> service_ref_;
-
   DISALLOW_COPY_AND_ASSIGN(MediaParser);
 };
 
diff --git a/chrome/services/media_gallery_util/media_parser_android.cc b/chrome/services/media_gallery_util/media_parser_android.cc
index 57c94b6..651a03e 100644
--- a/chrome/services/media_gallery_util/media_parser_android.cc
+++ b/chrome/services/media_gallery_util/media_parser_android.cc
@@ -25,16 +25,14 @@
 
 }  // namespace
 
-MediaParserAndroid::MediaParserAndroid(
-    std::unique_ptr<service_manager::ServiceContextRef> service_ref)
-    : MediaParser(std::move(service_ref)) {}
+MediaParserAndroid::MediaParserAndroid() = default;
 
 MediaParserAndroid::~MediaParserAndroid() = default;
 
 void MediaParserAndroid::ExtractVideoFrame(
     const std::string& mime_type,
     uint32_t total_size,
-    chrome::mojom::MediaDataSourcePtr media_data_source,
+    mojo::PendingRemote<chrome::mojom::MediaDataSource> media_data_source,
     MediaParser::ExtractVideoFrameCallback video_frame_callback) {
   auto data_source = std::make_unique<IPCDataSource>(
       std::move(media_data_source), static_cast<int64_t>(total_size));
diff --git a/chrome/services/media_gallery_util/media_parser_android.h b/chrome/services/media_gallery_util/media_parser_android.h
index 369f16f8..2b34e18 100644
--- a/chrome/services/media_gallery_util/media_parser_android.h
+++ b/chrome/services/media_gallery_util/media_parser_android.h
@@ -13,15 +13,14 @@
 // The media parser on Android that provides video thumbnail generation utility.
 class MediaParserAndroid : public MediaParser {
  public:
-  MediaParserAndroid(
-      std::unique_ptr<service_manager::ServiceContextRef> service_ref);
+  MediaParserAndroid();
   ~MediaParserAndroid() override;
 
   // MediaParser implementation.
   void ExtractVideoFrame(
       const std::string& mime_type,
       uint32_t total_size,
-      chrome::mojom::MediaDataSourcePtr media_data_source,
+      mojo::PendingRemote<chrome::mojom::MediaDataSource> media_data_source,
       ExtractVideoFrameCallback video_frame_callback) override;
 
  private:
diff --git a/chrome/services/media_gallery_util/media_parser_android_unittest.cc b/chrome/services/media_gallery_util/media_parser_android_unittest.cc
index 38bd5fa..fe00a86 100644
--- a/chrome/services/media_gallery_util/media_parser_android_unittest.cc
+++ b/chrome/services/media_gallery_util/media_parser_android_unittest.cc
@@ -17,8 +17,8 @@
 #include "chrome/services/media_gallery_util/public/mojom/media_parser.mojom.h"
 #include "media/base/test_data_util.h"
 #include "media/media_buildflags.h"
-#include "mojo/public/cpp/bindings/binding.h"
-#include "services/service_manager/public/cpp/service_keepalive.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/receiver.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
@@ -54,11 +54,12 @@
 // Used in test that do blocking reads from a local file.
 class TestMediaDataSource : public chrome::mojom::MediaDataSource {
  public:
-  TestMediaDataSource(chrome::mojom::MediaDataSourcePtr* interface,
-                      const base::FilePath& file_path)
-      : file_path_(file_path), binding_(this, mojo::MakeRequest(interface)) {}
+  TestMediaDataSource(
+      mojo::PendingReceiver<chrome::mojom::MediaDataSource> receiver,
+      const base::FilePath& file_path)
+      : file_path_(file_path), receiver_(this, std::move(receiver)) {}
 
-  ~TestMediaDataSource() override {}
+  ~TestMediaDataSource() override = default;
 
  private:
   // chrome::mojom::MediaDataSource implementation.
@@ -76,18 +77,18 @@
   }
 
   base::FilePath file_path_;
-  mojo::Binding<chrome::mojom::MediaDataSource> binding_;
+  mojo::Receiver<chrome::mojom::MediaDataSource> receiver_;
 
   DISALLOW_COPY_AND_ASSIGN(TestMediaDataSource);
 };
 
 class MediaParserAndroidTest : public testing::Test {
  public:
-  MediaParserAndroidTest() : keepalive_(nullptr, base::nullopt) {}
+  MediaParserAndroidTest() = default;
   ~MediaParserAndroidTest() override = default;
 
   void SetUp() override {
-    parser_ = std::make_unique<MediaParserAndroid>(keepalive_.CreateRef());
+    parser_ = std::make_unique<MediaParserAndroid>();
     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
   }
 
@@ -103,13 +104,14 @@
     int64_t size = 0;
     EXPECT_TRUE(base::GetFileSize(file_path, &size));
 
-    chrome::mojom::MediaDataSourcePtr data_source_ptr;
-    TestMediaDataSource test_data_source(&data_source_ptr, file_path);
+    mojo::PendingRemote<chrome::mojom::MediaDataSource> remote_data_source;
+    TestMediaDataSource test_data_source(
+        remote_data_source.InitWithNewPipeAndPassReceiver(), file_path);
 
     ExtractVideoFrameResult result;
     base::RunLoop run_loop;
     parser()->ExtractVideoFrame(
-        mime_type, size, std::move(data_source_ptr),
+        mime_type, size, std::move(remote_data_source),
         base::BindLambdaForTesting(
             [&](bool success, chrome::mojom::VideoFrameDataPtr video_frame_data,
                 const base::Optional<media::VideoDecoderConfig>& config) {
@@ -125,12 +127,10 @@
   }
 
  private:
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
   std::unique_ptr<MediaParserAndroid> parser_;
   base::ScopedTempDir temp_dir_;
 
-  service_manager::ServiceKeepalive keepalive_;
-  base::test::ScopedTaskEnvironment scoped_task_environment_;
-
   DISALLOW_COPY_AND_ASSIGN(MediaParserAndroidTest);
 };
 
diff --git a/chrome/services/media_gallery_util/media_parser_factory.cc b/chrome/services/media_gallery_util/media_parser_factory.cc
index 276a137..9512237 100644
--- a/chrome/services/media_gallery_util/media_parser_factory.cc
+++ b/chrome/services/media_gallery_util/media_parser_factory.cc
@@ -9,16 +9,16 @@
 #include "chrome/services/media_gallery_util/media_parser.h"
 #include "media/base/media.h"
 #include "media/media_buildflags.h"
-#include "mojo/public/cpp/bindings/interface_request.h"
-#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/self_owned_receiver.h"
 
 #if defined(OS_ANDROID)
 #include "chrome/services/media_gallery_util/media_parser_android.h"
 #endif
 
 MediaParserFactory::MediaParserFactory(
-    std::unique_ptr<service_manager::ServiceContextRef> service_ref)
-    : service_ref_(std::move(service_ref)) {}
+    mojo::PendingReceiver<chrome::mojom::MediaParserFactory> receiver)
+    : receiver_(this, std::move(receiver)) {}
 
 MediaParserFactory::~MediaParserFactory() = default;
 
@@ -27,15 +27,16 @@
                                            CreateMediaParserCallback callback) {
   media::InitializeMediaLibraryInSandbox(libyuv_cpu_flags, libavutil_cpu_flags);
 
-  chrome::mojom::MediaParserPtr media_parser_ptr;
+  mojo::PendingRemote<chrome::mojom::MediaParser> remote_media_parser;
   std::unique_ptr<MediaParser> media_parser;
 #if defined(OS_ANDROID)
-  media_parser = std::make_unique<MediaParserAndroid>(service_ref_->Clone());
+  media_parser = std::make_unique<MediaParserAndroid>();
 #else
-  media_parser = std::make_unique<MediaParser>(service_ref_->Clone());
+  media_parser = std::make_unique<MediaParser>();
 #endif
-  mojo::MakeStrongBinding(std::move(media_parser),
-                          mojo::MakeRequest(&media_parser_ptr));
+  mojo::MakeSelfOwnedReceiver(
+      std::move(media_parser),
+      remote_media_parser.InitWithNewPipeAndPassReceiver());
 
-  std::move(callback).Run(std::move(media_parser_ptr));
+  std::move(callback).Run(std::move(remote_media_parser));
 }
diff --git a/chrome/services/media_gallery_util/media_parser_factory.h b/chrome/services/media_gallery_util/media_parser_factory.h
index c168350..5ebdb2c9 100644
--- a/chrome/services/media_gallery_util/media_parser_factory.h
+++ b/chrome/services/media_gallery_util/media_parser_factory.h
@@ -10,12 +10,13 @@
 #include <memory>
 
 #include "chrome/services/media_gallery_util/public/mojom/media_parser.mojom.h"
-#include "services/service_manager/public/cpp/service_context_ref.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/receiver.h"
 
 class MediaParserFactory : public chrome::mojom::MediaParserFactory {
  public:
   explicit MediaParserFactory(
-      std::unique_ptr<service_manager::ServiceContextRef> service_ref);
+      mojo::PendingReceiver<chrome::mojom::MediaParserFactory> receiver);
   ~MediaParserFactory() override;
 
  private:
@@ -24,7 +25,7 @@
                          int64_t ffmpeg_cpu_flags,
                          CreateMediaParserCallback callback) override;
 
-  const std::unique_ptr<service_manager::ServiceContextRef> service_ref_;
+  mojo::Receiver<chrome::mojom::MediaParserFactory> receiver_;
 
   DISALLOW_COPY_AND_ASSIGN(MediaParserFactory);
 };
diff --git a/chrome/services/media_gallery_util/public/cpp/BUILD.gn b/chrome/services/media_gallery_util/public/cpp/BUILD.gn
index 367672c..2cfc031 100644
--- a/chrome/services/media_gallery_util/public/cpp/BUILD.gn
+++ b/chrome/services/media_gallery_util/public/cpp/BUILD.gn
@@ -15,9 +15,10 @@
   ]
 
   deps = [
+    "//chrome/app:generated_resources",
+    "//content/public/browser",
     "//media:media_buildflags",
     "//net:buildflags",
-    "//services/service_manager/public/cpp",
     "//third_party/boringssl",
     "//third_party/libyuv",
   ]
@@ -34,20 +35,6 @@
   }
 }
 
-source_set("manifest") {
-  sources = [
-    "manifest.cc",
-    "manifest.h",
-  ]
-
-  deps = [
-    "//base",
-    "//chrome:strings",
-    "//chrome/services/media_gallery_util/public/mojom",
-    "//services/service_manager/public/cpp",
-  ]
-}
-
 source_set("browser_tests") {
   testonly = true
 
diff --git a/chrome/services/media_gallery_util/public/cpp/DEPS b/chrome/services/media_gallery_util/public/cpp/DEPS
index b8c00a8..6e6710c 100644
--- a/chrome/services/media_gallery_util/public/cpp/DEPS
+++ b/chrome/services/media_gallery_util/public/cpp/DEPS
@@ -1,4 +1,7 @@
 include_rules = [
+  "+chrome/grit/generated_resources.h",
+  "+content/public/browser/service_process_host.h",
+
   "+third_party/libyuv",
   "+third_party/ffmpeg",
-]
\ No newline at end of file
+]
diff --git a/chrome/services/media_gallery_util/public/cpp/OWNERS b/chrome/services/media_gallery_util/public/cpp/OWNERS
deleted file mode 100644
index 6faeaa47..0000000
--- a/chrome/services/media_gallery_util/public/cpp/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-per-file manifest.cc=set noparent
-per-file manifest.cc=file://ipc/SECURITY_OWNERS
-per-file manifest.h=set noparent
-per-file manifest.h=file://ipc/SECURITY_OWNERS
diff --git a/chrome/services/media_gallery_util/public/cpp/manifest.cc b/chrome/services/media_gallery_util/public/cpp/manifest.cc
deleted file mode 100644
index 06d5604..0000000
--- a/chrome/services/media_gallery_util/public/cpp/manifest.cc
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/services/media_gallery_util/public/cpp/manifest.h"
-
-#include "base/no_destructor.h"
-#include "chrome/grit/generated_resources.h"
-#include "chrome/services/media_gallery_util/public/mojom/constants.mojom.h"
-#include "chrome/services/media_gallery_util/public/mojom/media_parser.mojom.h"
-#include "services/service_manager/public/cpp/manifest_builder.h"
-
-const service_manager::Manifest& GetMediaGalleryUtilManifest() {
-  static base::NoDestructor<service_manager::Manifest> manifest{
-      service_manager::ManifestBuilder()
-          .WithServiceName(chrome::mojom::kMediaGalleryUtilServiceName)
-          .WithDisplayName(IDS_UTILITY_PROCESS_MEDIA_GALLERY_UTILITY_NAME)
-          .WithOptions(
-              service_manager::ManifestOptionsBuilder()
-                  .WithExecutionMode(service_manager::Manifest::ExecutionMode::
-                                         kOutOfProcessBuiltin)
-                  .WithSandboxType("utility")
-                  .WithInstanceSharingPolicy(
-                      service_manager::Manifest::InstanceSharingPolicy::
-                          kSharedAcrossGroups)
-                  .Build())
-          .ExposeCapability("parse_media",
-                            service_manager::Manifest::InterfaceList<
-                                chrome::mojom::MediaParserFactory>())
-          .Build()};
-  return *manifest;
-}
diff --git a/chrome/services/media_gallery_util/public/cpp/manifest.h b/chrome/services/media_gallery_util/public/cpp/manifest.h
deleted file mode 100644
index 190b8d8c..0000000
--- a/chrome/services/media_gallery_util/public/cpp/manifest.h
+++ /dev/null
@@ -1,12 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_SERVICES_MEDIA_GALLERY_UTIL_PUBLIC_CPP_MANIFEST_H_
-#define CHROME_SERVICES_MEDIA_GALLERY_UTIL_PUBLIC_CPP_MANIFEST_H_
-
-#include "services/service_manager/public/cpp/manifest.h"
-
-const service_manager::Manifest& GetMediaGalleryUtilManifest();
-
-#endif  // CHROME_SERVICES_MEDIA_GALLERY_UTIL_PUBLIC_CPP_MANIFEST_H_
diff --git a/chrome/services/media_gallery_util/public/cpp/media_gallery_util_browsertest.cc b/chrome/services/media_gallery_util/public/cpp/media_gallery_util_browsertest.cc
index 3fdbe5d..9a05de9 100644
--- a/chrome/services/media_gallery_util/public/cpp/media_gallery_util_browsertest.cc
+++ b/chrome/services/media_gallery_util/public/cpp/media_gallery_util_browsertest.cc
@@ -7,11 +7,8 @@
 #include "base/bind.h"
 #include "base/run_loop.h"
 #include "chrome/services/media_gallery_util/public/cpp/media_parser_provider.h"
-#include "chrome/services/media_gallery_util/public/mojom/constants.mojom.h"
 #include "chrome/test/base/in_process_browser_test.h"
-#include "content/public/browser/system_connector.h"
 #include "media/media_buildflags.h"
-#include "services/service_manager/public/cpp/connector.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/libyuv/include/libyuv.h"
 
@@ -29,14 +26,13 @@
  public:
   TestMediaParserProvider() = default;
 
-  chrome::mojom::MediaParser* GetMediaParser(
-      service_manager::Connector* connector) {
+  chrome::mojom::MediaParser* GetMediaParser() {
     DCHECK(!quit_loop_);
     base::RunLoop run_loop;
     quit_loop_ = run_loop.QuitClosure();
-    RetrieveMediaParser(connector);
+    RetrieveMediaParser();
     run_loop.Run();
-    return media_parser();
+    return media_parser().get();
   }
 
  private:
@@ -54,7 +50,7 @@
 IN_PROC_BROWSER_TEST_F(MediaGalleryUtilBrowserTest, TestThirdPartyCpuInfo) {
   TestMediaParserProvider media_parser_provider;
   chrome::mojom::MediaParser* media_parser =
-      media_parser_provider.GetMediaParser(content::GetSystemConnector());
+      media_parser_provider.GetMediaParser();
 
   base::RunLoop run_loop;
   media_parser->GetCpuInfo(base::BindOnce(
diff --git a/chrome/services/media_gallery_util/public/cpp/media_parser_provider.cc b/chrome/services/media_gallery_util/public/cpp/media_parser_provider.cc
index db87b4f..d7995fc 100644
--- a/chrome/services/media_gallery_util/public/cpp/media_parser_provider.cc
+++ b/chrome/services/media_gallery_util/public/cpp/media_parser_provider.cc
@@ -6,9 +6,9 @@
 
 #include "base/allocator/buildflags.h"
 #include "base/bind.h"
-#include "chrome/services/media_gallery_util/public/mojom/constants.mojom.h"
+#include "chrome/grit/generated_resources.h"
+#include "content/public/browser/service_process_host.h"
 #include "media/media_buildflags.h"
-#include "services/service_manager/public/cpp/connector.h"
 #include "third_party/libyuv/include/libyuv.h"
 
 #if BUILDFLAG(ENABLE_FFMPEG)
@@ -22,14 +22,17 @@
 
 MediaParserProvider::~MediaParserProvider() = default;
 
-void MediaParserProvider::RetrieveMediaParser(
-    service_manager::Connector* connector) {
-  DCHECK(!media_parser_factory_ptr_);
-  DCHECK(!media_parser_ptr_);
+void MediaParserProvider::RetrieveMediaParser() {
+  DCHECK(!remote_media_parser_factory_);
+  DCHECK(!remote_media_parser_);
 
-  connector->BindInterface(chrome::mojom::kMediaGalleryUtilServiceName,
-                           mojo::MakeRequest(&media_parser_factory_ptr_));
-  media_parser_factory_ptr_.set_connection_error_handler(base::BindOnce(
+  content::ServiceProcessHost::Launch(
+      remote_media_parser_factory_.BindNewPipeAndPassReceiver(),
+      content::ServiceProcessHost::Options()
+          .WithDisplayName(IDS_UTILITY_PROCESS_MEDIA_GALLERY_UTILITY_NAME)
+          .WithSandboxType(service_manager::SANDBOX_TYPE_UTILITY)
+          .Pass());
+  remote_media_parser_factory_.set_disconnect_handler(base::BindOnce(
       &MediaParserProvider::OnConnectionError, base::Unretained(this)));
 
   int libyuv_cpu_flags = libyuv::InitCpuFlags();
@@ -40,23 +43,22 @@
   int avutil_cpu_flags = -1;
 #endif
 
-  media_parser_factory_ptr_->CreateMediaParser(
+  remote_media_parser_factory_->CreateMediaParser(
       libyuv_cpu_flags, avutil_cpu_flags,
       base::BindOnce(&MediaParserProvider::OnMediaParserCreatedImpl,
                      base::Unretained(this)));
 }
 
 void MediaParserProvider::OnMediaParserCreatedImpl(
-    chrome::mojom::MediaParserPtr media_parser_ptr) {
-  media_parser_ptr_ = std::move(media_parser_ptr);
-  media_parser_ptr_.set_connection_error_handler(base::BindOnce(
+    mojo::PendingRemote<chrome::mojom::MediaParser> remote_media_parser) {
+  remote_media_parser_.Bind(std::move(remote_media_parser));
+  remote_media_parser_.set_disconnect_handler(base::BindOnce(
       &MediaParserProvider::OnConnectionError, base::Unretained(this)));
-  media_parser_factory_ptr_.reset();
 
   OnMediaParserCreated();
 }
 
 void MediaParserProvider::ResetMediaParser() {
-  media_parser_ptr_.reset();
-  media_parser_factory_ptr_.reset();
+  remote_media_parser_.reset();
+  remote_media_parser_factory_.reset();
 }
diff --git a/chrome/services/media_gallery_util/public/cpp/media_parser_provider.h b/chrome/services/media_gallery_util/public/cpp/media_parser_provider.h
index 7d87428..f76f455b 100644
--- a/chrome/services/media_gallery_util/public/cpp/media_parser_provider.h
+++ b/chrome/services/media_gallery_util/public/cpp/media_parser_provider.h
@@ -7,10 +7,8 @@
 
 #include "base/macros.h"
 #include "chrome/services/media_gallery_util/public/mojom/media_parser.mojom.h"
-
-namespace service_manager {
-class Connector;
-}
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/remote.h"
 
 // Base class used by SafeMediaMetadataParser and SafeAudioVideoChecker to
 // retrieve a MediaParserPtr.
@@ -20,12 +18,13 @@
   virtual ~MediaParserProvider();
 
  protected:
-  // Retrieves the MediaParserPtr. OnMediaParserCreated() is called when the
-  // media parser is available.
-  void RetrieveMediaParser(service_manager::Connector* connector);
+  // Acquires the remote MediaParser interface asynchronously from a new
+  // service process. |OnMediaParserCreated()| is called if and when the media
+  // parser is available.
+  void RetrieveMediaParser();
 
   // Invoked when the media parser was successfully created. It can then be
-  // obtained by calling media_parser() and is guaranteed to be non null.
+  // obtained by calling media_parser() which is then guaranteed to be bound.
   virtual void OnMediaParserCreated() = 0;
 
   // Invoked when there was an error with the connection to the media gallerie
@@ -33,18 +32,19 @@
   // expected from media_parser() will not happen.
   virtual void OnConnectionError() = 0;
 
-  chrome::mojom::MediaParser* media_parser() const {
-    return media_parser_ptr_.get();
+  const mojo::Remote<chrome::mojom::MediaParser>& media_parser() const {
+    return remote_media_parser_;
   }
 
-  // Clears all interface ptr to the media gallery service.
+  // Clears all remote handles to the media gallery service process.
   void ResetMediaParser();
 
  private:
-  void OnMediaParserCreatedImpl(chrome::mojom::MediaParserPtr media_parser_ptr);
+  void OnMediaParserCreatedImpl(
+      mojo::PendingRemote<chrome::mojom::MediaParser> remote_media_parser);
 
-  chrome::mojom::MediaParserFactoryPtr media_parser_factory_ptr_;
-  chrome::mojom::MediaParserPtr media_parser_ptr_;
+  mojo::Remote<chrome::mojom::MediaParserFactory> remote_media_parser_factory_;
+  mojo::Remote<chrome::mojom::MediaParser> remote_media_parser_;
 
   DISALLOW_COPY_AND_ASSIGN(MediaParserProvider);
 };
diff --git a/chrome/services/media_gallery_util/public/cpp/safe_audio_video_checker.cc b/chrome/services/media_gallery_util/public/cpp/safe_audio_video_checker.cc
index 0dcd1ab..b5337a6 100644
--- a/chrome/services/media_gallery_util/public/cpp/safe_audio_video_checker.cc
+++ b/chrome/services/media_gallery_util/public/cpp/safe_audio_video_checker.cc
@@ -9,16 +9,11 @@
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/time/time.h"
-#include "chrome/services/media_gallery_util/public/mojom/constants.mojom.h"
-#include "services/service_manager/public/cpp/connector.h"
+#include "chrome/services/media_gallery_util/public/mojom/media_parser.mojom.h"
 
-SafeAudioVideoChecker::SafeAudioVideoChecker(
-    base::File file,
-    ResultCallback callback,
-    std::unique_ptr<service_manager::Connector> connector)
-    : file_(std::move(file)),
-      connector_(std::move(connector)),
-      callback_(std::move(callback)) {
+SafeAudioVideoChecker::SafeAudioVideoChecker(base::File file,
+                                             ResultCallback callback)
+    : file_(std::move(file)), callback_(std::move(callback)) {
   DCHECK(callback_);
 }
 
@@ -28,7 +23,7 @@
     return;
   }
 
-  RetrieveMediaParser(connector_.get());
+  RetrieveMediaParser();
 }
 
 void SafeAudioVideoChecker::OnMediaParserCreated() {
diff --git a/chrome/services/media_gallery_util/public/cpp/safe_audio_video_checker.h b/chrome/services/media_gallery_util/public/cpp/safe_audio_video_checker.h
index ebc7a00..dddfdbb 100644
--- a/chrome/services/media_gallery_util/public/cpp/safe_audio_video_checker.h
+++ b/chrome/services/media_gallery_util/public/cpp/safe_audio_video_checker.h
@@ -8,11 +8,6 @@
 #include "base/files/file.h"
 #include "base/macros.h"
 #include "chrome/services/media_gallery_util/public/cpp/media_parser_provider.h"
-#include "chrome/services/media_gallery_util/public/mojom/media_parser.mojom.h"
-
-namespace service_manager {
-class Connector;
-}
 
 // Uses a utility process to validate a media file.  If the callback returns
 // File::FILE_OK, then file appears to be valid.  File validation does not
@@ -23,14 +18,12 @@
   using ResultCallback = base::OnceCallback<void(base::File::Error result)>;
 
   // Takes responsibility for closing |file|.
-  SafeAudioVideoChecker(base::File file,
-                        ResultCallback callback,
-                        std::unique_ptr<service_manager::Connector> connector);
+  SafeAudioVideoChecker(base::File file, ResultCallback callback);
   ~SafeAudioVideoChecker() override;
 
   // Checks the file. Can be called on a different thread than the UI thread.
-  // Note that the callback specified in the construtor will be called on the
-  // thread this method is called.
+  // Note that the callback specified in the constructor will be called on the
+  // thread from which this method is called.
   void Start();
 
  private:
@@ -44,9 +37,6 @@
   // Media file to check.
   base::File file_;
 
-  // Connector to the ServiceManager used to ind the MediaParser interface.
-  std::unique_ptr<service_manager::Connector> connector_;
-
   // Report the check result to |callback_|.
   ResultCallback callback_;
 
diff --git a/chrome/services/media_gallery_util/public/cpp/safe_media_metadata_parser.cc b/chrome/services/media_gallery_util/public/cpp/safe_media_metadata_parser.cc
index c0d0f07..e80ee8b5 100644
--- a/chrome/services/media_gallery_util/public/cpp/safe_media_metadata_parser.cc
+++ b/chrome/services/media_gallery_util/public/cpp/safe_media_metadata_parser.cc
@@ -8,8 +8,7 @@
 
 #include "base/bind.h"
 #include "base/callback.h"
-#include "chrome/services/media_gallery_util/public/mojom/constants.mojom.h"
-#include "services/service_manager/public/cpp/connector.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 
 SafeMediaMetadataParser::SafeMediaMetadataParser(
     int64_t size,
@@ -23,21 +22,21 @@
 
 SafeMediaMetadataParser::~SafeMediaMetadataParser() = default;
 
-void SafeMediaMetadataParser::Start(service_manager::Connector* connector,
-                                    DoneCallback callback) {
+void SafeMediaMetadataParser::Start(DoneCallback callback) {
   DCHECK(!media_parser());
   DCHECK(callback);
 
   callback_ = std::move(callback);
 
-  RetrieveMediaParser(connector);
+  RetrieveMediaParser();
 }
 
 void SafeMediaMetadataParser::OnMediaParserCreated() {
-  chrome::mojom::MediaDataSourcePtr source;
+  mojo::PendingRemote<chrome::mojom::MediaDataSource> source;
   media_data_source_ = media_source_factory_->CreateMediaDataSource(
-      &source, base::BindRepeating(&SafeMediaMetadataParser::OnMediaDataReady,
-                                   weak_factory_.GetWeakPtr()));
+      source.InitWithNewPipeAndPassReceiver(),
+      base::BindRepeating(&SafeMediaMetadataParser::OnMediaDataReady,
+                          weak_factory_.GetWeakPtr()));
   media_parser()->ParseMediaMetadata(
       mime_type_, size_, get_attached_images_, std::move(source),
       base::BindOnce(&SafeMediaMetadataParser::ParseMediaMetadataDone,
diff --git a/chrome/services/media_gallery_util/public/cpp/safe_media_metadata_parser.h b/chrome/services/media_gallery_util/public/cpp/safe_media_metadata_parser.h
index 3987563e..a9fb6e1 100644
--- a/chrome/services/media_gallery_util/public/cpp/safe_media_metadata_parser.h
+++ b/chrome/services/media_gallery_util/public/cpp/safe_media_metadata_parser.h
@@ -17,10 +17,7 @@
 #include "chrome/common/media_galleries/metadata_types.h"
 #include "chrome/services/media_gallery_util/public/cpp/media_parser_provider.h"
 #include "chrome/services/media_gallery_util/public/mojom/media_parser.mojom.h"
-
-namespace service_manager {
-class Connector;
-}
+#include "mojo/public/cpp/bindings/pending_receiver.h"
 
 // Parses the media metadata safely in a utility process. This class expects the
 // MIME type and the size of media data to be already known. It creates a
@@ -44,8 +41,9 @@
         MediaDataCallback;
 
     virtual std::unique_ptr<chrome::mojom::MediaDataSource>
-    CreateMediaDataSource(chrome::mojom::MediaDataSourcePtr* request,
-                          MediaDataCallback media_data_callback) = 0;
+    CreateMediaDataSource(
+        mojo::PendingReceiver<chrome::mojom::MediaDataSource> receiver,
+        MediaDataCallback media_data_callback) = 0;
     virtual ~MediaDataSourceFactory() {}
   };
 
@@ -56,9 +54,9 @@
       std::unique_ptr<MediaDataSourceFactory> media_source_factory);
   ~SafeMediaMetadataParser() override;
 
-  // Should be called on the thread |connector| is associated with. |callback|
-  // is invoked on that same thread.
-  void Start(service_manager::Connector* connector, DoneCallback callback);
+  // Initiates parsing. |callback| is invoked on the same sequence that calls
+  // this method.
+  void Start(DoneCallback callback);
 
  private:
   // MediaParserProvider implementation:
diff --git a/chrome/services/media_gallery_util/public/mojom/BUILD.gn b/chrome/services/media_gallery_util/public/mojom/BUILD.gn
index 20522e95..93a0c900 100644
--- a/chrome/services/media_gallery_util/public/mojom/BUILD.gn
+++ b/chrome/services/media_gallery_util/public/mojom/BUILD.gn
@@ -6,7 +6,6 @@
 
 mojom("mojom") {
   sources = [
-    "constants.mojom",
     "media_parser.mojom",
   ]
 
diff --git a/chrome/services/media_gallery_util/public/mojom/constants.mojom b/chrome/services/media_gallery_util/public/mojom/constants.mojom
deleted file mode 100644
index 4b496359..0000000
--- a/chrome/services/media_gallery_util/public/mojom/constants.mojom
+++ /dev/null
@@ -1,7 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-module chrome.mojom;
-
-const string kMediaGalleryUtilServiceName = "media_gallery_util";
diff --git a/chrome/services/media_gallery_util/public/mojom/media_parser.mojom b/chrome/services/media_gallery_util/public/mojom/media_parser.mojom
index a00a61ce..2449a84 100644
--- a/chrome/services/media_gallery_util/public/mojom/media_parser.mojom
+++ b/chrome/services/media_gallery_util/public/mojom/media_parser.mojom
@@ -24,7 +24,7 @@
   ParseMediaMetadata(string mime_type,
                      int64 total_size,
                      bool get_attached_images,
-                     MediaDataSource media_data_source)
+                     pending_remote<MediaDataSource> media_data_source)
       => (bool parse_success,
           MediaMetadata metadata,
           array<AttachedImage> attached_images);
@@ -35,7 +35,7 @@
   [EnableIf=is_android]
   ExtractVideoFrame(string mime_type,
                     uint32 total_size,
-                    MediaDataSource media_data_source)
+                    pending_remote<MediaDataSource> media_data_source)
       => (bool success,
           VideoFrameData frame_data,
           media.mojom.VideoDecoderConfig? config);
@@ -62,7 +62,7 @@
   // for instance) and the media parser may be run in a sandboxed process with
   // no such access.
   CreateMediaParser(int64 libyuv_cpu_flags, int64 libavutil_cpu_flags)
-      => (MediaParser media_parser);
+      => (pending_remote<MediaParser> media_parser);
 };
 
 interface MediaDataSource {
diff --git a/chrome/services/printing/BUILD.gn b/chrome/services/printing/BUILD.gn
index c6f601d8..4c33867 100644
--- a/chrome/services/printing/BUILD.gn
+++ b/chrome/services/printing/BUILD.gn
@@ -13,15 +13,14 @@
   ]
 
   deps = [
-    "//base",
     "//components/crash/core/common:crash_key",
     "//components/pwg_encoder",
-    "//mojo/public/cpp/bindings",
   ]
 
   public_deps = [
+    "//base",
     "//chrome/services/printing/public/mojom",
-    "//services/service_manager/public/cpp",
+    "//mojo/public/cpp/bindings",
   ]
 
   if (is_win) {
diff --git a/chrome/services/printing/pdf_nup_converter.cc b/chrome/services/printing/pdf_nup_converter.cc
index 281c98d..7db0063 100644
--- a/chrome/services/printing/pdf_nup_converter.cc
+++ b/chrome/services/printing/pdf_nup_converter.cc
@@ -51,9 +51,7 @@
 
 }  // namespace
 
-PdfNupConverter::PdfNupConverter(
-    std::unique_ptr<service_manager::ServiceContextRef> service_ref)
-    : service_ref_(std::move(service_ref)) {}
+PdfNupConverter::PdfNupConverter() = default;
 
 PdfNupConverter::~PdfNupConverter() {}
 
diff --git a/chrome/services/printing/pdf_nup_converter.h b/chrome/services/printing/pdf_nup_converter.h
index 18473366..039c53b 100644
--- a/chrome/services/printing/pdf_nup_converter.h
+++ b/chrome/services/printing/pdf_nup_converter.h
@@ -10,14 +10,12 @@
 #include "base/macros.h"
 #include "base/memory/read_only_shared_memory_region.h"
 #include "chrome/services/printing/public/mojom/pdf_nup_converter.mojom.h"
-#include "services/service_manager/public/cpp/service_context_ref.h"
 
 namespace printing {
 
 class PdfNupConverter : public printing::mojom::PdfNupConverter {
  public:
-  explicit PdfNupConverter(
-      std::unique_ptr<service_manager::ServiceContextRef> service_ref);
+  PdfNupConverter();
   ~PdfNupConverter() override;
 
   // printing::mojom::PdfNupConverter
@@ -35,8 +33,6 @@
   void SetWebContentsURL(const GURL& url) override;
 
  private:
-  const std::unique_ptr<service_manager::ServiceContextRef> service_ref_;
-
   DISALLOW_COPY_AND_ASSIGN(PdfNupConverter);
 };
 
diff --git a/chrome/services/printing/pdf_to_emf_converter_factory.cc b/chrome/services/printing/pdf_to_emf_converter_factory.cc
index d3b41812..600ef889 100644
--- a/chrome/services/printing/pdf_to_emf_converter_factory.cc
+++ b/chrome/services/printing/pdf_to_emf_converter_factory.cc
@@ -12,10 +12,6 @@
 
 namespace printing {
 
-PdfToEmfConverterFactory::PdfToEmfConverterFactory(
-    std::unique_ptr<service_manager::ServiceContextRef> service_ref)
-    : service_ref_(std::move(service_ref)) {}
-
 PdfToEmfConverterFactory::PdfToEmfConverterFactory() = default;
 
 PdfToEmfConverterFactory::~PdfToEmfConverterFactory() = default;
diff --git a/chrome/services/printing/pdf_to_emf_converter_factory.h b/chrome/services/printing/pdf_to_emf_converter_factory.h
index c6293ba..66c8b47 100644
--- a/chrome/services/printing/pdf_to_emf_converter_factory.h
+++ b/chrome/services/printing/pdf_to_emf_converter_factory.h
@@ -9,19 +9,14 @@
 
 #include "base/macros.h"
 #include "chrome/services/printing/public/mojom/pdf_to_emf_converter.mojom.h"
-#include "services/service_manager/public/cpp/service_context_ref.h"
 
 namespace printing {
 
 class PdfToEmfConverterFactory : public mojom::PdfToEmfConverterFactory {
  public:
-  explicit PdfToEmfConverterFactory(
-      std::unique_ptr<service_manager::ServiceContextRef> service_ref);
+  PdfToEmfConverterFactory();
   ~PdfToEmfConverterFactory() override;
 
-  // TODO(crbug.com/798782): remove when the Cloud print chrome/service is
-  // removed.
-  PdfToEmfConverterFactory();
   static void Create(mojom::PdfToEmfConverterFactoryRequest request);
 
  private:
@@ -31,8 +26,6 @@
                        mojom::PdfToEmfConverterClientPtr client,
                        CreateConverterCallback callback) override;
 
-  const std::unique_ptr<service_manager::ServiceContextRef> service_ref_;
-
   DISALLOW_COPY_AND_ASSIGN(PdfToEmfConverterFactory);
 };
 
diff --git a/chrome/services/printing/pdf_to_pwg_raster_converter.cc b/chrome/services/printing/pdf_to_pwg_raster_converter.cc
index 59b7200..572fce4 100644
--- a/chrome/services/printing/pdf_to_pwg_raster_converter.cc
+++ b/chrome/services/printing/pdf_to_pwg_raster_converter.cc
@@ -126,9 +126,7 @@
 
 }  // namespace
 
-PdfToPwgRasterConverter::PdfToPwgRasterConverter(
-    std::unique_ptr<service_manager::ServiceContextRef> service_ref)
-    : service_ref_(std::move(service_ref)) {}
+PdfToPwgRasterConverter::PdfToPwgRasterConverter() = default;
 
 PdfToPwgRasterConverter::~PdfToPwgRasterConverter() {}
 
diff --git a/chrome/services/printing/pdf_to_pwg_raster_converter.h b/chrome/services/printing/pdf_to_pwg_raster_converter.h
index 8d3849c..4dcedb8 100644
--- a/chrome/services/printing/pdf_to_pwg_raster_converter.h
+++ b/chrome/services/printing/pdf_to_pwg_raster_converter.h
@@ -10,7 +10,6 @@
 #include "base/macros.h"
 #include "chrome/services/printing/public/mojom/pdf_to_pwg_raster_converter.mojom.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
-#include "services/service_manager/public/cpp/service_context_ref.h"
 
 namespace printing {
 
@@ -19,8 +18,7 @@
 class PdfToPwgRasterConverter
     : public printing::mojom::PdfToPwgRasterConverter {
  public:
-  explicit PdfToPwgRasterConverter(
-      std::unique_ptr<service_manager::ServiceContextRef> service_ref);
+  PdfToPwgRasterConverter();
   ~PdfToPwgRasterConverter() override;
 
  private:
@@ -30,8 +28,6 @@
                const PwgRasterSettings& pwg_raster_settings,
                ConvertCallback callback) override;
 
-  const std::unique_ptr<service_manager::ServiceContextRef> service_ref_;
-
   DISALLOW_COPY_AND_ASSIGN(PdfToPwgRasterConverter);
 };
 
diff --git a/chrome/services/printing/printing_service.cc b/chrome/services/printing/printing_service.cc
index 674038bd..f1ea89f 100644
--- a/chrome/services/printing/printing_service.cc
+++ b/chrome/services/printing/printing_service.cc
@@ -4,11 +4,10 @@
 
 #include "chrome/services/printing/printing_service.h"
 
-#include "base/bind.h"
 #include "build/build_config.h"
 #include "chrome/services/printing/pdf_nup_converter.h"
 #include "chrome/services/printing/pdf_to_pwg_raster_converter.h"
-#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "mojo/public/cpp/bindings/self_owned_receiver.h"
 
 #if defined(OS_WIN)
 #include "chrome/services/printing/pdf_to_emf_converter.h"
@@ -17,57 +16,32 @@
 
 namespace printing {
 
-namespace {
-
-#if defined(OS_WIN)
-void OnPdfToEmfConverterFactoryRequest(
-    service_manager::ServiceKeepalive* keepalive,
-    printing::mojom::PdfToEmfConverterFactoryRequest request) {
-  mojo::MakeStrongBinding(std::make_unique<printing::PdfToEmfConverterFactory>(
-                              keepalive->CreateRef()),
-                          std::move(request));
-}
-#endif
-
-void OnPdfToPwgRasterConverterRequest(
-    service_manager::ServiceKeepalive* keepalive,
-    printing::mojom::PdfToPwgRasterConverterRequest request) {
-  mojo::MakeStrongBinding(std::make_unique<printing::PdfToPwgRasterConverter>(
-                              keepalive->CreateRef()),
-                          std::move(request));
-}
-
-void OnPdfNupConverterRequest(service_manager::ServiceKeepalive* keepalive,
-                              printing::mojom::PdfNupConverterRequest request) {
-  mojo::MakeStrongBinding(
-      std::make_unique<printing::PdfNupConverter>(keepalive->CreateRef()),
-      std::move(request));
-}
-
-}  // namespace
-
-PrintingService::PrintingService(service_manager::mojom::ServiceRequest request)
-    : service_binding_(this, std::move(request)),
-      service_keepalive_(&service_binding_, base::TimeDelta()) {}
+PrintingService::PrintingService(
+    mojo::PendingReceiver<mojom::PrintingService> receiver)
+    : receiver_(this, std::move(receiver)) {}
 
 PrintingService::~PrintingService() = default;
 
-void PrintingService::OnStart() {
-#if defined(OS_WIN)
-  registry_.AddInterface(base::BindRepeating(&OnPdfToEmfConverterFactoryRequest,
-                                             &service_keepalive_));
-#endif
-  registry_.AddInterface(base::BindRepeating(&OnPdfToPwgRasterConverterRequest,
-                                             &service_keepalive_));
-  registry_.AddInterface(
-      base::BindRepeating(&OnPdfNupConverterRequest, &service_keepalive_));
+void PrintingService::BindPdfNupConverter(
+    mojo::PendingReceiver<mojom::PdfNupConverter> receiver) {
+  mojo::MakeSelfOwnedReceiver(std::make_unique<printing::PdfNupConverter>(),
+                              std::move(receiver));
 }
 
-void PrintingService::OnBindInterface(
-    const service_manager::BindSourceInfo& source_info,
-    const std::string& interface_name,
-    mojo::ScopedMessagePipeHandle interface_pipe) {
-  registry_.BindInterface(interface_name, std::move(interface_pipe));
+void PrintingService::BindPdfToPwgRasterConverter(
+    mojo::PendingReceiver<mojom::PdfToPwgRasterConverter> receiver) {
+  mojo::MakeSelfOwnedReceiver(
+      std::make_unique<printing::PdfToPwgRasterConverter>(),
+      std::move(receiver));
 }
 
+#if defined(OS_WIN)
+void PrintingService::BindPdfToEmfConverterFactory(
+    mojo::PendingReceiver<mojom::PdfToEmfConverterFactory> receiver) {
+  mojo::MakeSelfOwnedReceiver(
+      std::make_unique<printing::PdfToEmfConverterFactory>(),
+      std::move(receiver));
+}
+#endif
+
 }  // namespace printing
diff --git a/chrome/services/printing/printing_service.h b/chrome/services/printing/printing_service.h
index e1b6ba7..7eac8add 100644
--- a/chrome/services/printing/printing_service.h
+++ b/chrome/services/printing/printing_service.h
@@ -5,29 +5,32 @@
 #ifndef CHROME_SERVICES_PRINTING_PRINTING_SERVICE_H_
 #define CHROME_SERVICES_PRINTING_PRINTING_SERVICE_H_
 
-#include "services/service_manager/public/cpp/binder_registry.h"
-#include "services/service_manager/public/cpp/service.h"
-#include "services/service_manager/public/cpp/service_binding.h"
-#include "services/service_manager/public/cpp/service_keepalive.h"
-#include "services/service_manager/public/mojom/service.mojom.h"
+#include "base/macros.h"
+#include "build/build_config.h"
+#include "chrome/services/printing/public/mojom/printing_service.mojom.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/receiver.h"
 
 namespace printing {
 
-class PrintingService : public service_manager::Service {
+class PrintingService : public mojom::PrintingService {
  public:
-  explicit PrintingService(service_manager::mojom::ServiceRequest request);
+  explicit PrintingService(
+      mojo::PendingReceiver<mojom::PrintingService> receiver);
   ~PrintingService() override;
 
-  // Lifescycle events that occur after the service has started to spinup.
-  void OnStart() override;
-  void OnBindInterface(const service_manager::BindSourceInfo& source_info,
-                       const std::string& interface_name,
-                       mojo::ScopedMessagePipeHandle interface_pipe) override;
-
  private:
-  service_manager::ServiceBinding service_binding_;
-  service_manager::ServiceKeepalive service_keepalive_;
-  service_manager::BinderRegistry registry_;
+  // mojom::PrintingService implementation:
+  void BindPdfNupConverter(
+      mojo::PendingReceiver<mojom::PdfNupConverter> receiver) override;
+  void BindPdfToPwgRasterConverter(
+      mojo::PendingReceiver<mojom::PdfToPwgRasterConverter> receiver) override;
+#if defined(OS_WIN)
+  void BindPdfToEmfConverterFactory(
+      mojo::PendingReceiver<mojom::PdfToEmfConverterFactory> receiver) override;
+#endif
+
+  mojo::Receiver<mojom::PrintingService> receiver_;
 
   DISALLOW_COPY_AND_ASSIGN(PrintingService);
 };
diff --git a/chrome/services/printing/public/cpp/BUILD.gn b/chrome/services/printing/public/cpp/BUILD.gn
deleted file mode 100644
index 281696c4..0000000
--- a/chrome/services/printing/public/cpp/BUILD.gn
+++ /dev/null
@@ -1,17 +0,0 @@
-# Copyright 2019 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-source_set("manifest") {
-  sources = [
-    "manifest.cc",
-    "manifest.h",
-  ]
-
-  deps = [
-    "//base",
-    "//chrome:strings",
-    "//chrome/services/printing/public/mojom",
-    "//services/service_manager/public/cpp",
-  ]
-}
diff --git a/chrome/services/printing/public/cpp/OWNERS b/chrome/services/printing/public/cpp/OWNERS
deleted file mode 100644
index 6faeaa47..0000000
--- a/chrome/services/printing/public/cpp/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-per-file manifest.cc=set noparent
-per-file manifest.cc=file://ipc/SECURITY_OWNERS
-per-file manifest.h=set noparent
-per-file manifest.h=file://ipc/SECURITY_OWNERS
diff --git a/chrome/services/printing/public/cpp/manifest.cc b/chrome/services/printing/public/cpp/manifest.cc
deleted file mode 100644
index 50421d74..0000000
--- a/chrome/services/printing/public/cpp/manifest.cc
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/services/printing/public/cpp/manifest.h"
-
-#include "base/no_destructor.h"
-#include "build/build_config.h"
-#include "chrome/grit/generated_resources.h"
-#include "chrome/services/printing/public/mojom/constants.mojom.h"
-#include "chrome/services/printing/public/mojom/pdf_nup_converter.mojom.h"
-#include "chrome/services/printing/public/mojom/pdf_to_pwg_raster_converter.mojom.h"
-#include "services/service_manager/public/cpp/manifest_builder.h"
-
-#if defined(OS_WIN)
-#include "chrome/services/printing/public/mojom/pdf_to_emf_converter.mojom.h"
-#endif
-
-const service_manager::Manifest& GetChromePrintingManifest() {
-  static base::NoDestructor<service_manager::Manifest> manifest {
-    service_manager::ManifestBuilder()
-        .WithServiceName(printing::mojom::kChromePrintingServiceName)
-        .WithDisplayName(IDS_UTILITY_PROCESS_PRINTING_SERVICE_NAME)
-        .WithOptions(
-            service_manager::ManifestOptionsBuilder()
-                .WithExecutionMode(service_manager::Manifest::ExecutionMode::
-                                       kOutOfProcessBuiltin)
-                .WithSandboxType("utility")
-                .WithInstanceSharingPolicy(
-                    service_manager::Manifest::InstanceSharingPolicy::
-                        kSharedAcrossGroups)
-                .Build())
-        .ExposeCapability("converter",
-                          service_manager::Manifest::InterfaceList<
-#if defined(OS_WIN)
-                              printing::mojom::PdfToEmfConverterFactory,
-#endif
-                              printing::mojom::PdfNupConverter,
-                              printing::mojom::PdfToPwgRasterConverter>())
-
-        .Build()
-  };
-  return *manifest;
-}
diff --git a/chrome/services/printing/public/cpp/manifest.h b/chrome/services/printing/public/cpp/manifest.h
deleted file mode 100644
index 7791151..0000000
--- a/chrome/services/printing/public/cpp/manifest.h
+++ /dev/null
@@ -1,12 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_SERVICES_PRINTING_PUBLIC_CPP_MANIFEST_H_
-#define CHROME_SERVICES_PRINTING_PUBLIC_CPP_MANIFEST_H_
-
-#include "services/service_manager/public/cpp/manifest.h"
-
-const service_manager::Manifest& GetChromePrintingManifest();
-
-#endif  // CHROME_SERVICES_PRINTING_PUBLIC_CPP_MANIFEST_H_
diff --git a/chrome/services/printing/public/mojom/BUILD.gn b/chrome/services/printing/public/mojom/BUILD.gn
index d8b0cef..191f257 100644
--- a/chrome/services/printing/public/mojom/BUILD.gn
+++ b/chrome/services/printing/public/mojom/BUILD.gn
@@ -6,10 +6,10 @@
 
 mojom("mojom") {
   sources = [
-    "constants.mojom",
     "pdf_nup_converter.mojom",
     "pdf_render_settings.mojom",
     "pdf_to_pwg_raster_converter.mojom",
+    "printing_service.mojom",
   ]
 
   deps = [
diff --git a/chrome/services/printing/public/mojom/constants.mojom b/chrome/services/printing/public/mojom/constants.mojom
deleted file mode 100644
index ab49398..0000000
--- a/chrome/services/printing/public/mojom/constants.mojom
+++ /dev/null
@@ -1,7 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-module printing.mojom;
-
-const string kChromePrintingServiceName = "chrome_printing";
diff --git a/chrome/services/printing/public/mojom/printing_service.mojom b/chrome/services/printing/public/mojom/printing_service.mojom
new file mode 100644
index 0000000..f92dc00
--- /dev/null
+++ b/chrome/services/printing/public/mojom/printing_service.mojom
@@ -0,0 +1,28 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module printing.mojom;
+
+import "chrome/services/printing/public/mojom/pdf_nup_converter.mojom";
+import "chrome/services/printing/public/mojom/pdf_to_pwg_raster_converter.mojom";
+
+[EnableIf=is_win]
+import "chrome/services/printing/public/mojom/pdf_to_emf_converter.mojom";
+
+// The main interface to Chrome's Printing Service, which performs various PDF
+// conversion operations in an isolated sandboxed process.
+interface PrintingService {
+  // Binds an interface that can be used to do Nup PDF conversion.
+  BindPdfNupConverter(pending_receiver<PdfNupConverter> receiver);
+
+  // Binds an interface that can be used to do PDF to PWG Raster conversion.
+  BindPdfToPwgRasterConverter(
+      pending_receiver<PdfToPwgRasterConverter> receiver);
+
+  // Binds an interface that can be used to do PDF to EMF conversion. Windows
+  // only.
+  [EnableIf=is_win]
+  BindPdfToEmfConverterFactory(
+      pending_receiver<PdfToEmfConverterFactory> receiver);
+};
diff --git a/chrome/services/removable_storage_writer/BUILD.gn b/chrome/services/removable_storage_writer/BUILD.gn
index 07729f6..0f5a3864 100644
--- a/chrome/services/removable_storage_writer/BUILD.gn
+++ b/chrome/services/removable_storage_writer/BUILD.gn
@@ -6,17 +6,11 @@
   sources = [
     "removable_storage_writer.cc",
     "removable_storage_writer.h",
-    "removable_storage_writer_service.cc",
-    "removable_storage_writer_service.h",
-  ]
-
-  deps = [
-    "//base",
-    "//mojo/public/cpp/bindings",
   ]
 
   public_deps = [
+    "//base",
     "//chrome/services/removable_storage_writer/public/mojom",
-    "//services/service_manager/public/cpp",
+    "//mojo/public/cpp/bindings",
   ]
 }
diff --git a/chrome/services/removable_storage_writer/public/cpp/BUILD.gn b/chrome/services/removable_storage_writer/public/cpp/BUILD.gn
deleted file mode 100644
index 2d402f0..0000000
--- a/chrome/services/removable_storage_writer/public/cpp/BUILD.gn
+++ /dev/null
@@ -1,17 +0,0 @@
-# Copyright 2019 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-source_set("manifest") {
-  sources = [
-    "manifest.cc",
-    "manifest.h",
-  ]
-
-  deps = [
-    "//base",
-    "//chrome:strings",
-    "//chrome/services/removable_storage_writer/public/mojom",
-    "//services/service_manager/public/cpp",
-  ]
-}
diff --git a/chrome/services/removable_storage_writer/public/cpp/OWNERS b/chrome/services/removable_storage_writer/public/cpp/OWNERS
deleted file mode 100644
index 6faeaa47..0000000
--- a/chrome/services/removable_storage_writer/public/cpp/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-per-file manifest.cc=set noparent
-per-file manifest.cc=file://ipc/SECURITY_OWNERS
-per-file manifest.h=set noparent
-per-file manifest.h=file://ipc/SECURITY_OWNERS
diff --git a/chrome/services/removable_storage_writer/public/cpp/manifest.cc b/chrome/services/removable_storage_writer/public/cpp/manifest.cc
deleted file mode 100644
index 6a104e7..0000000
--- a/chrome/services/removable_storage_writer/public/cpp/manifest.cc
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/services/removable_storage_writer/public/cpp/manifest.h"
-
-#include "base/no_destructor.h"
-#include "chrome/grit/generated_resources.h"
-#include "chrome/services/removable_storage_writer/public/mojom/constants.mojom.h"
-#include "chrome/services/removable_storage_writer/public/mojom/removable_storage_writer.mojom.h"
-#include "services/service_manager/public/cpp/manifest_builder.h"
-
-const service_manager::Manifest& GetRemovableStorageWriterManifest() {
-  static base::NoDestructor<service_manager::Manifest> manifest{
-      service_manager::ManifestBuilder()
-          .WithServiceName(chrome::mojom::kRemovableStorageWriterServiceName)
-          .WithDisplayName(IDS_UTILITY_PROCESS_IMAGE_WRITER_NAME)
-          .WithOptions(
-              service_manager::ManifestOptionsBuilder()
-                  .WithExecutionMode(service_manager::Manifest::ExecutionMode::
-                                         kOutOfProcessBuiltin)
-                  .WithSandboxType("none_and_elevated")
-                  .WithInstanceSharingPolicy(
-                      service_manager::Manifest::InstanceSharingPolicy::
-                          kSharedAcrossGroups)
-                  .Build())
-          .ExposeCapability("removable_storage_writer",
-                            service_manager::Manifest::InterfaceList<
-                                chrome::mojom::RemovableStorageWriter>())
-
-          .Build()};
-  return *manifest;
-}
diff --git a/chrome/services/removable_storage_writer/public/cpp/manifest.h b/chrome/services/removable_storage_writer/public/cpp/manifest.h
deleted file mode 100644
index 596c966..0000000
--- a/chrome/services/removable_storage_writer/public/cpp/manifest.h
+++ /dev/null
@@ -1,12 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_SERVICES_REMOVABLE_STORAGE_WRITER_PUBLIC_CPP_MANIFEST_H_
-#define CHROME_SERVICES_REMOVABLE_STORAGE_WRITER_PUBLIC_CPP_MANIFEST_H_
-
-#include "services/service_manager/public/cpp/manifest.h"
-
-const service_manager::Manifest& GetRemovableStorageWriterManifest();
-
-#endif  // CHROME_SERVICES_REMOVABLE_STORAGE_WRITER_PUBLIC_CPP_MANIFEST_H_
diff --git a/chrome/services/removable_storage_writer/public/mojom/BUILD.gn b/chrome/services/removable_storage_writer/public/mojom/BUILD.gn
index 15c7047..e0659789 100644
--- a/chrome/services/removable_storage_writer/public/mojom/BUILD.gn
+++ b/chrome/services/removable_storage_writer/public/mojom/BUILD.gn
@@ -6,7 +6,6 @@
 
 mojom("mojom") {
   sources = [
-    "constants.mojom",
     "removable_storage_writer.mojom",
   ]
 
diff --git a/chrome/services/removable_storage_writer/public/mojom/constants.mojom b/chrome/services/removable_storage_writer/public/mojom/constants.mojom
deleted file mode 100644
index b18fbe7..0000000
--- a/chrome/services/removable_storage_writer/public/mojom/constants.mojom
+++ /dev/null
@@ -1,7 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-module chrome.mojom;
-
-const string kRemovableStorageWriterServiceName = "removable_storage_writer";
diff --git a/chrome/services/removable_storage_writer/public/mojom/removable_storage_writer.mojom b/chrome/services/removable_storage_writer/public/mojom/removable_storage_writer.mojom
index a031d27..ddf7062 100644
--- a/chrome/services/removable_storage_writer/public/mojom/removable_storage_writer.mojom
+++ b/chrome/services/removable_storage_writer/public/mojom/removable_storage_writer.mojom
@@ -13,14 +13,14 @@
   // restricted to removable drives by the utility process.
   Write(mojo_base.mojom.FilePath source,
         mojo_base.mojom.FilePath target,
-        RemovableStorageWriterClient client);
+        pending_remote<RemovableStorageWriterClient> client);
 
   // Verifies that the contents of the source file was written to the target
   // file. Again, the target is restricted to removable drives by the utility
   // process.
   Verify(mojo_base.mojom.FilePath source,
          mojo_base.mojom.FilePath target,
-         RemovableStorageWriterClient client);
+         pending_remote<RemovableStorageWriterClient> client);
 };
 
 interface RemovableStorageWriterClient {
diff --git a/chrome/services/removable_storage_writer/removable_storage_writer.cc b/chrome/services/removable_storage_writer/removable_storage_writer.cc
index 2990cf9..e2fe1ac1 100644
--- a/chrome/services/removable_storage_writer/removable_storage_writer.cc
+++ b/chrome/services/removable_storage_writer/removable_storage_writer.cc
@@ -9,21 +9,25 @@
 #include "base/files/file_path.h"
 
 RemovableStorageWriter::RemovableStorageWriter(
-    std::unique_ptr<service_manager::ServiceContextRef> service_ref)
-    : service_ref_(std::move(service_ref)) {}
+    mojo::PendingReceiver<chrome::mojom::RemovableStorageWriter> receiver)
+    : receiver_(this, std::move(receiver)) {}
 
 RemovableStorageWriter::~RemovableStorageWriter() = default;
 
 void RemovableStorageWriter::Write(
     const base::FilePath& source,
     const base::FilePath& target,
-    chrome::mojom::RemovableStorageWriterClientPtr client) {
-  writer_.Write(source, target, std::move(client));
+    mojo::PendingRemote<chrome::mojom::RemovableStorageWriterClient> client) {
+  writer_.Write(
+      source, target,
+      chrome::mojom::RemovableStorageWriterClientPtr(std::move(client)));
 }
 
 void RemovableStorageWriter::Verify(
     const base::FilePath& source,
     const base::FilePath& target,
-    chrome::mojom::RemovableStorageWriterClientPtr client) {
-  writer_.Verify(source, target, std::move(client));
+    mojo::PendingRemote<chrome::mojom::RemovableStorageWriterClient> client) {
+  writer_.Verify(
+      source, target,
+      chrome::mojom::RemovableStorageWriterClientPtr(std::move(client)));
 }
diff --git a/chrome/services/removable_storage_writer/removable_storage_writer.h b/chrome/services/removable_storage_writer/removable_storage_writer.h
index 42e64ac9..57b4510 100644
--- a/chrome/services/removable_storage_writer/removable_storage_writer.h
+++ b/chrome/services/removable_storage_writer/removable_storage_writer.h
@@ -10,7 +10,8 @@
 #include "base/macros.h"
 #include "chrome/services/removable_storage_writer/public/mojom/removable_storage_writer.mojom.h"
 #include "chrome/utility/image_writer/image_writer_handler.h"
-#include "services/service_manager/public/cpp/service_context_ref.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/receiver.h"
 
 namespace base {
 class FilePath;
@@ -19,20 +20,22 @@
 class RemovableStorageWriter : public chrome::mojom::RemovableStorageWriter {
  public:
   explicit RemovableStorageWriter(
-      std::unique_ptr<service_manager::ServiceContextRef> service_ref);
+      mojo::PendingReceiver<chrome::mojom::RemovableStorageWriter> receiver);
   ~RemovableStorageWriter() override;
 
  private:
   // mojom::RemovableStorageWriter implementation:
   void Write(const base::FilePath& source,
              const base::FilePath& target,
-             chrome::mojom::RemovableStorageWriterClientPtr client) override;
+             mojo::PendingRemote<chrome::mojom::RemovableStorageWriterClient>
+                 client) override;
 
   void Verify(const base::FilePath& source,
               const base::FilePath& target,
-              chrome::mojom::RemovableStorageWriterClientPtr client) override;
+              mojo::PendingRemote<chrome::mojom::RemovableStorageWriterClient>
+                  client) override;
 
-  const std::unique_ptr<service_manager::ServiceContextRef> service_ref_;
+  mojo::Receiver<chrome::mojom::RemovableStorageWriter> receiver_;
   image_writer::ImageWriterHandler writer_;
 
   DISALLOW_COPY_AND_ASSIGN(RemovableStorageWriter);
diff --git a/chrome/services/removable_storage_writer/removable_storage_writer_service.cc b/chrome/services/removable_storage_writer/removable_storage_writer_service.cc
deleted file mode 100644
index 359c9ff..0000000
--- a/chrome/services/removable_storage_writer/removable_storage_writer_service.cc
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/services/removable_storage_writer/removable_storage_writer_service.h"
-
-#include <memory>
-#include <utility>
-
-#include "base/bind.h"
-#include "build/build_config.h"
-#include "chrome/services/removable_storage_writer/public/mojom/removable_storage_writer.mojom.h"
-#include "chrome/services/removable_storage_writer/removable_storage_writer.h"
-#include "mojo/public/cpp/bindings/strong_binding.h"
-
-namespace {
-
-void OnRemovableStorageWriterGetterRequest(
-    service_manager::ServiceKeepalive* keepalive,
-    chrome::mojom::RemovableStorageWriterRequest request) {
-  mojo::MakeStrongBinding(
-      std::make_unique<RemovableStorageWriter>(keepalive->CreateRef()),
-      std::move(request));
-}
-
-}  // namespace
-
-RemovableStorageWriterService::RemovableStorageWriterService(
-    service_manager::mojom::ServiceRequest request)
-    : service_binding_(this, std::move(request)),
-      service_keepalive_(&service_binding_, base::TimeDelta()) {}
-
-RemovableStorageWriterService::~RemovableStorageWriterService() = default;
-
-void RemovableStorageWriterService::OnStart() {
-  registry_.AddInterface(base::BindRepeating(
-      &OnRemovableStorageWriterGetterRequest, &service_keepalive_));
-}
-
-void RemovableStorageWriterService::OnBindInterface(
-    const service_manager::BindSourceInfo& source_info,
-    const std::string& interface_name,
-    mojo::ScopedMessagePipeHandle interface_pipe) {
-  registry_.BindInterface(interface_name, std::move(interface_pipe));
-}
diff --git a/chrome/services/removable_storage_writer/removable_storage_writer_service.h b/chrome/services/removable_storage_writer/removable_storage_writer_service.h
deleted file mode 100644
index f2fb299b..0000000
--- a/chrome/services/removable_storage_writer/removable_storage_writer_service.h
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_SERVICES_REMOVABLE_STORAGE_WRITER_REMOVABLE_STORAGE_WRITER_SERVICE_H_
-#define CHROME_SERVICES_REMOVABLE_STORAGE_WRITER_REMOVABLE_STORAGE_WRITER_SERVICE_H_
-
-#include "services/service_manager/public/cpp/binder_registry.h"
-#include "services/service_manager/public/cpp/service.h"
-#include "services/service_manager/public/cpp/service_binding.h"
-#include "services/service_manager/public/cpp/service_keepalive.h"
-#include "services/service_manager/public/mojom/service.mojom.h"
-
-class RemovableStorageWriterService : public service_manager::Service {
- public:
-  explicit RemovableStorageWriterService(
-      service_manager::mojom::ServiceRequest request);
-  ~RemovableStorageWriterService() override;
-
-  // Lifescycle events that occur after the service has started to spinup.
-  void OnStart() override;
-  void OnBindInterface(const service_manager::BindSourceInfo& source_info,
-                       const std::string& interface_name,
-                       mojo::ScopedMessagePipeHandle interface_pipe) override;
-
- private:
-  service_manager::ServiceBinding service_binding_;
-  service_manager::ServiceKeepalive service_keepalive_;
-  service_manager::BinderRegistry registry_;
-
-  DISALLOW_COPY_AND_ASSIGN(RemovableStorageWriterService);
-};
-
-#endif  // CHROME_SERVICES_REMOVABLE_STORAGE_WRITER_REMOVABLE_STORAGE_WRITER_SERVICE_H_
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 3b57dd12..5fe094f 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -5874,12 +5874,12 @@
       "../browser/sync/test/integration/sync_errors_test.cc",
       "../browser/sync/test/integration/sync_exponential_backoff_test.cc",
       "../browser/sync/test/integration/two_client_app_list_sync_test.cc",
-      "../browser/sync/test/integration/two_client_apps_sync_test.cc",
       "../browser/sync/test/integration/two_client_autocomplete_sync_test.cc",
       "../browser/sync/test/integration/two_client_autofill_sync_test.cc",
       "../browser/sync/test/integration/two_client_bookmarks_sync_test.cc",
       "../browser/sync/test/integration/two_client_custom_passphrase_sync_test.cc",
       "../browser/sync/test/integration/two_client_dictionary_sync_test.cc",
+      "../browser/sync/test/integration/two_client_extension_apps_sync_test.cc",
       "../browser/sync/test/integration/two_client_extension_settings_and_app_settings_sync_test.cc",
       "../browser/sync/test/integration/two_client_extensions_sync_test.cc",
       "../browser/sync/test/integration/two_client_passwords_sync_test.cc",
@@ -5892,6 +5892,7 @@
       "../browser/sync/test/integration/two_client_typed_urls_sync_test.cc",
       "../browser/sync/test/integration/two_client_user_events_sync_test.cc",
       "../browser/sync/test/integration/two_client_wallet_sync_test.cc",
+      "../browser/sync/test/integration/two_client_web_apps_sync_test.cc",
     ]
 
     data = [
diff --git a/chrome/test/android/test_trusted_web_activity/BUILD.gn b/chrome/test/android/test_trusted_web_activity/BUILD.gn
index 1201e70..36dac01b 100644
--- a/chrome/test/android/test_trusted_web_activity/BUILD.gn
+++ b/chrome/test/android/test_trusted_web_activity/BUILD.gn
@@ -14,6 +14,6 @@
     "src/org/chromium/chrome/browser/browserservices/TestTrustedWebActivityService.java",
   ]
   deps = [
-    "//third_party/custom_tabs_client:custom_tabs_support_java",
+    "//third_party/android_sdk/androidx_browser:androidx_browser_java"
   ]
 }
diff --git a/chrome/test/android/test_trusted_web_activity/src/org/chromium/chrome/browser/browserservices/TestTrustedWebActivityService.java b/chrome/test/android/test_trusted_web_activity/src/org/chromium/chrome/browser/browserservices/TestTrustedWebActivityService.java
index 5735160d..f660b53 100644
--- a/chrome/test/android/test_trusted_web_activity/src/org/chromium/chrome/browser/browserservices/TestTrustedWebActivityService.java
+++ b/chrome/test/android/test_trusted_web_activity/src/org/chromium/chrome/browser/browserservices/TestTrustedWebActivityService.java
@@ -5,7 +5,8 @@
 package org.chromium.chrome.browser.browserservices;
 
 import android.app.Notification;
-import android.support.customtabs.trusted.TrustedWebActivityService;
+
+import androidx.browser.trusted.TrustedWebActivityService;
 
 /**
  * A TrustedWebActivityService to be used in TrustedWebActivityClientTest.
diff --git a/chrome/test/data/local_ntp/customize_menu_browsertest.js b/chrome/test/data/local_ntp/customize_menu_browsertest.js
index ece62623..87d5a43 100644
--- a/chrome/test/data/local_ntp/customize_menu_browsertest.js
+++ b/chrome/test/data/local_ntp/customize_menu_browsertest.js
@@ -49,7 +49,7 @@
  * @const
  */
 test.customizeMenu.CLASSES = {
-  COLLECTION_TILE: 'bg-sel-tile',
+  COLLECTION_TILE_BG: 'bg-sel-tile-bg',
   ENTRY_POINT_ENHANCED: 'ep-enhanced',
   SELECTED: 'selected',
 };
@@ -226,8 +226,8 @@
   $('coll_tile_0').click();
   const background = $('coll_0_img_tile_0');
   background.click();
-  assertTrue(background.parentElement.classList.contains(
-      test.customizeMenu.CLASSES.SELECTED));
+  assertTrue(
+      background.classList.contains(test.customizeMenu.CLASSES.SELECTED));
   // The submenu's title should be the image collection's.
   assertEquals(
       'Collection 1', $(test.customizeMenu.IDS.MENU_TITLE).textContent);
@@ -243,18 +243,17 @@
 
   // Select a color.
   $(test.customizeMenu.IDS.COLORS_BUTTON).click();
-  const colorOptions =
-      $(test.customizeMenu.IDS.COLORS_MENU)
-          .getElementsByClassName(test.customizeMenu.CLASSES.COLLECTION_TILE);
+  const colorOptions = $(test.customizeMenu.IDS.COLORS_MENU)
+                           .getElementsByClassName(
+                               test.customizeMenu.CLASSES.COLLECTION_TILE_BG);
   const color = colorOptions[1];  // Skip the default theme option.
   color.click();
-  assertTrue(color.parentElement.classList.contains(
-      test.customizeMenu.CLASSES.SELECTED));
+  assertTrue(color.classList.contains(test.customizeMenu.CLASSES.SELECTED));
 
   // Open the Background submenu and check that it's still selected.
   $(test.customizeMenu.IDS.BACKGROUNDS_BUTTON).click();
-  assertTrue(background.parentElement.classList.contains(
-      test.customizeMenu.CLASSES.SELECTED));
+  assertTrue(
+      background.classList.contains(test.customizeMenu.CLASSES.SELECTED));
   // The image collection should still be open.
   assertEquals(
       'Collection 1', $(test.customizeMenu.IDS.MENU_TITLE).textContent);
@@ -266,8 +265,7 @@
 
   // Open the Color submenu and check that it's still selected.
   $(test.customizeMenu.IDS.COLORS_BUTTON).click();
-  assertTrue(color.parentElement.classList.contains(
-      test.customizeMenu.CLASSES.SELECTED));
+  assertTrue(color.classList.contains(test.customizeMenu.CLASSES.SELECTED));
 
   $(test.customizeMenu.IDS.MENU_CANCEL).click();
 };
@@ -292,9 +290,9 @@
 
   // Select a color.
   $(test.customizeMenu.IDS.COLORS_BUTTON).click();
-  const colorOptions =
-      $(test.customizeMenu.IDS.COLORS_MENU)
-          .getElementsByClassName(test.customizeMenu.CLASSES.COLLECTION_TILE);
+  const colorOptions = $(test.customizeMenu.IDS.COLORS_MENU)
+                           .getElementsByClassName(
+                               test.customizeMenu.CLASSES.COLLECTION_TILE_BG);
   // Skip the color picker and the default theme option.
   colorOptions[2].click();
 
@@ -368,9 +366,9 @@
 
   // Select a color.
   $(test.customizeMenu.IDS.COLORS_BUTTON).click();
-  const colorOptions =
-      $(test.customizeMenu.IDS.COLORS_MENU)
-          .getElementsByClassName(test.customizeMenu.CLASSES.COLLECTION_TILE);
+  const colorOptions = $(test.customizeMenu.IDS.COLORS_MENU)
+                           .getElementsByClassName(
+                               test.customizeMenu.CLASSES.COLLECTION_TILE_BG);
   // Skip the color picker and the default theme option.
   const color = colorOptions[2];
   color.click();
@@ -404,9 +402,9 @@
 
   // Select a color.
   $(test.customizeMenu.IDS.COLORS_BUTTON).click();
-  const colorOptions =
-      $(test.customizeMenu.IDS.COLORS_MENU)
-          .getElementsByClassName(test.customizeMenu.CLASSES.COLLECTION_TILE);
+  const colorOptions = $(test.customizeMenu.IDS.COLORS_MENU)
+                           .getElementsByClassName(
+                               test.customizeMenu.CLASSES.COLLECTION_TILE_BG);
   // Skip the color picker and the default theme option.
   const color = colorOptions[2];
   color.click();
@@ -641,7 +639,7 @@
   $(test.customizeMenu.IDS.COLORS_BUTTON).click();
   assertTrue($(test.customizeMenu.IDS.COLORS_MENU).children.length > 1);
   assertTrue(!!$('color_0').dataset.color);
-  assertTrue(!!$('color_0').style.backgroundImage);
+  assertTrue(!!$('color_0').firstElementChild.style.backgroundImage);
 };
 
 /**
@@ -729,7 +727,7 @@
   !!$('color_0').click();
   assertFalse($(test.customizeMenu.IDS.MENU_DONE).disabled);
 
-  $(test.customizeMenu.IDS.COLORS_DEFAULT_ICON).click();
+  $(test.customizeMenu.IDS.COLORS_DEFAULT).click();
   assertTrue($(test.customizeMenu.IDS.MENU_DONE).disabled);
 };
 
@@ -750,8 +748,7 @@
           .getElementsByClassName('selected')
           .length === 1);
   const tile = $(test.customizeMenu.IDS.COLORS_MENU)
-                   .getElementsByClassName('selected')[0]
-                   .firstChild;
+                   .getElementsByClassName('selected')[0];
   assertTrue(
       parseInt(tile.dataset.id) ===
       test.customizeMenu.mockThemeBackgroundInfo.colorId);
@@ -1093,12 +1090,10 @@
   const hiddenToggle = $(test.customizeMenu.IDS.SHORTCUTS_HIDE_TOGGLE);
   assertEquals(
       clSelected,
-      clOption.parentElement.classList.contains(
-          test.customizeMenu.CLASSES.SELECTED));
+      clOption.classList.contains(test.customizeMenu.CLASSES.SELECTED));
   assertEquals(
       mvSelected,
-      mvOption.parentElement.classList.contains(
-          test.customizeMenu.CLASSES.SELECTED));
+      mvOption.classList.contains(test.customizeMenu.CLASSES.SELECTED));
   assertEquals(isHidden, hiddenToggle.checked);
 };
 
@@ -1113,18 +1108,21 @@
 
   assertFalse(elementIsVisible(backgroundSubmenu));
   assertTrue(elementIsVisible(backgroundImageSubmenu));
-  assertTrue(
-      $(test.customizeMenu.IDS.BACKGROUNDS_IMAGE_MENU)
-          .getElementsByClassName('bg-sel-tile')
-          .length === 4);
-  assertTrue(
-      $(test.customizeMenu.IDS.BACKGROUNDS_IMAGE_MENU)
-          .getElementsByClassName('selected')
-          .length === 1);
-  assertTrue(
-      $(test.customizeMenu.IDS.BACKGROUNDS_IMAGE_MENU)
-          .getElementsByClassName('selected')[0]
-          .firstChild.id === 'coll_0_img_tile_0');
+  assertEquals(
+      4,
+      backgroundImageSubmenu
+          .getElementsByClassName(test.customizeMenu.CLASSES.COLLECTION_TILE_BG)
+          .length);
+  assertEquals(
+      1,
+      backgroundImageSubmenu
+          .getElementsByClassName(test.customizeMenu.CLASSES.SELECTED)
+          .length);
+  assertEquals(
+      'coll_0_img_tile_0',
+      backgroundImageSubmenu
+          .getElementsByClassName(test.customizeMenu.CLASSES.SELECTED)[0]
+          .id);
 };
 
 /**
@@ -1138,14 +1136,16 @@
 
   assertFalse(elementIsVisible(backgroundSubmenu));
   assertTrue(elementIsVisible(backgroundImageSubmenu));
-  assertTrue(
-      $(test.customizeMenu.IDS.BACKGROUNDS_IMAGE_MENU)
-          .getElementsByClassName('bg-sel-tile')
-          .length === 4);
-  assertTrue(
-      $(test.customizeMenu.IDS.BACKGROUNDS_IMAGE_MENU)
-          .getElementsByClassName('selected')
-          .length === 0);
+  assertEquals(
+      4,
+      backgroundImageSubmenu
+          .getElementsByClassName(test.customizeMenu.CLASSES.COLLECTION_TILE_BG)
+          .length);
+  assertEquals(
+      0,
+      backgroundImageSubmenu
+          .getElementsByClassName(test.customizeMenu.CLASSES.SELECTED)
+          .length);
 };
 
 /**
diff --git a/chrome/test/data/local_ntp/local_ntp_browsertest.html b/chrome/test/data/local_ntp/local_ntp_browsertest.html
index 0e3c9292..33ec7bef 100644
--- a/chrome/test/data/local_ntp/local_ntp_browsertest.html
+++ b/chrome/test/data/local_ntp/local_ntp_browsertest.html
@@ -217,21 +217,25 @@
         </div>
         <div id="backgrounds-menu" class="menu-panel" tabindex="0"
             role="tabpanel" aria-label="$i18n{backgroundsOption}">
-          <div id="backgrounds-upload" class="bg-sel-tile-bg">
-            <div id="backgrounds-upload-icon" class="bg-sel-tile" tabindex="-1"
+          <div id="backgrounds-upload-wrapper" class="bg-sel-tile-wrapper">
+            <div id="backgrounds-upload" class="bg-sel-tile-bg" tabindex="-1"
                 role="button" aria-label="$i18n{uploadImage}"
                 title="$i18n{uploadImage}">
-              <div id="backgrounds-upload-arrow"></div>
-              <div id="backgrounds-upload-text">$i18n{uploadImage}</div>
+              <div id="backgrounds-upload-icon" class="bg-sel-tile">
+                <div id="backgrounds-upload-arrow"></div>
+                <div id="backgrounds-upload-text">$i18n{uploadImage}</div>
+              </div>
             </div>
           </div>
-          <div id="backgrounds-default" class="bg-sel-tile-bg">
-            <div id="backgrounds-default-icon" class="bg-sel-tile" tabindex="-1"
+          <div id="backgrounds-default-wrapper" class="bg-sel-tile-wrapper">
+            <div id="backgrounds-default" class="bg-sel-tile-bg" tabindex="-1"
                 role="button" aria-label="$i18n{noBackground}"
                 title="$i18n{noBackground}">
-              <div class="mini-page">
-                <div class="mini-header-colorful"></div>
-                <div class="mini-shortcuts"></div>
+              <div id="backgrounds-default-icon" class="bg-sel-tile">
+                <div class="mini-page">
+                  <div class="mini-header-colorful"></div>
+                  <div class="mini-shortcuts"></div>
+                </div>
               </div>
             </div>
             <div class="bg-sel-tile-title">$i18n{noBackground}</div>
@@ -312,20 +316,23 @@
                 $i18n{uninstallButton}
               </button>
           </div>
-          <div id="color-picker-container" class="bg-sel-tile-bg"
-              aria-label="$i18n{colorPickerLabel}"
-              title="$i18n{colorPickerLabel}">
-            <div id="color-picker-tile" class="bg-sel-tile" tabindex="-1">
-              <div id="left-semicircle"></div>
-              <div id="color-picker-icon"></div>
-              <input id="color-picker" type="color" style="display:none">
-              </input>
+          <div id="color-picker-wrapper" class="bg-sel-tile-wrapper">
+            <div id="color-picker-container" class="bg-sel-tile-bg"
+                aria-label="$i18n{colorPickerLabel}"
+                title="$i18n{colorPickerLabel}"  tabindex="-1">
+              <div id="color-picker-tile" class="bg-sel-tile">
+                <div id="left-semicircle"></div>
+                <div id="color-picker-icon"></div>
+                <input id="color-picker" type="color" style="display:none">
+                </input>
+              </div>
             </div>
           </div>
-          <div id="colors-default" class="bg-sel-tile-bg"
-              aria-label="$i18n{defaultThemeLabel}"
-              title="$i18n{defaultThemeLabel}">
-            <div id="colors-default-icon" class="bg-sel-tile" tabindex="-1">
+          <div id="colors-default-wrapper" class="bg-sel-tile-wrapper">
+            <div id="colors-default" class="bg-sel-tile-bg"
+                aria-label="$i18n{defaultThemeLabel}"
+                title="$i18n{defaultThemeLabel}" tabindex="-1">
+              <div id="colors-default-icon" class="bg-sel-tile"></div>
             </div>
           </div>
         </div>
diff --git a/chrome/test/data/webui/app_management/pwa_permission_view_test.js b/chrome/test/data/webui/app_management/pwa_permission_view_test.js
index 218cc5dc..c49f165 100644
--- a/chrome/test/data/webui/app_management/pwa_permission_view_test.js
+++ b/chrome/test/data/webui/app_management/pwa_permission_view_test.js
@@ -25,6 +25,12 @@
     await fakeHandler.flushPipesForTesting();
   }
 
+  function getSelectedAppFromStore() {
+    const storeData = app_management.Store.getInstance().data;
+    const selectedAppId = storeData.currentPage.selectedAppId;
+    return storeData.apps[selectedAppId];
+  }
+
   setup(async function() {
     fakeHandler = setupFakeHandler();
     replaceStore();
@@ -66,4 +72,29 @@
     await checkToggle('CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA');
     await checkToggle('CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC');
   });
+
+  test('Pin to shelf toggle', async function() {
+    const pinToShelfItem = pwaPermissionView.$['pin-to-shelf-setting'];
+    const toggle = pinToShelfItem.$['toggle-row'].$.toggle;
+
+    assertFalse(toggle.checked);
+    assertEquals(
+        toggle.checked,
+        app_management.util.convertOptionalBoolToBool(
+            getSelectedAppFromStore().isPinned));
+    pinToShelfItem.click();
+    await fakeHandler.flushPipesForTesting();
+    assertTrue(toggle.checked);
+    assertEquals(
+        toggle.checked,
+        app_management.util.convertOptionalBoolToBool(
+            getSelectedAppFromStore().isPinned));
+    pinToShelfItem.click();
+    await fakeHandler.flushPipesForTesting();
+    assertFalse(toggle.checked);
+    assertEquals(
+        toggle.checked,
+        app_management.util.convertOptionalBoolToBool(
+            getSelectedAppFromStore().isPinned));
+  });
 });
diff --git a/chrome/test/data/webui/settings/appearance_page_test.js b/chrome/test/data/webui/settings/appearance_page_test.js
index 409087bb..539d8bc 100644
--- a/chrome/test/data/webui/settings/appearance_page_test.js
+++ b/chrome/test/data/webui/settings/appearance_page_test.js
@@ -9,9 +9,6 @@
       'getDefaultZoom',
       'getThemeInfo',
       'isSupervised',
-      'isWallpaperSettingVisible',
-      'isWallpaperPolicyControlled',
-      'openWallpaperManager',
       'useDefaultTheme',
       'useSystemTheme',
       'validateStartupPage',
@@ -25,12 +22,6 @@
 
     /** @private */
     this.isHomeUrlValid_ = true;
-
-    /** @private */
-    this.isWallpaperSettingVisible_ = true;
-
-    /** @private */
-    this.isWallpaperPolicyControlled_ = false;
   }
 
   /** @override */
@@ -52,23 +43,6 @@
   }
 
   /** @override */
-  isWallpaperSettingVisible() {
-    this.methodCalled('isWallpaperSettingVisible');
-    return Promise.resolve(this.isWallpaperSettingVisible_);
-  }
-
-  /** @override */
-  isWallpaperPolicyControlled() {
-    this.methodCalled('isWallpaperPolicyControlled');
-    return Promise.resolve(this.isWallpaperPolicyControlled_);
-  }
-
-  /** @override */
-  openWallpaperManager() {
-    this.methodCalled('openWallpaperManager');
-  }
-
-  /** @override */
   useDefaultTheme() {
     this.methodCalled('useDefaultTheme');
   }
@@ -100,11 +74,6 @@
   setValidStartupPageResponse(isValid) {
     this.isHomeUrlValid_ = isValid;
   }
-
-  /** @param {boolean} Whether the wallpaper is policy controlled. */
-  setIsWallpaperPolicyControlled(isPolicyControlled) {
-    this.isWallpaperPolicyControlled_ = isPolicyControlled;
-  }
 }
 
 let appearancePage = null;
@@ -112,8 +81,14 @@
 /** @type {?TestAppearanceBrowserProxy} */
 let appearanceBrowserProxy = null;
 
+/** @type {?TestWallpaperBrowserProxy} */
+let wallpaperBrowserProxy = null;
+
 function createAppearancePage() {
   appearanceBrowserProxy.reset();
+  if (cr.isChromeOS) {
+    wallpaperBrowserProxy.reset();
+  }
   PolymerTest.clearBody();
 
   appearancePage = document.createElement('settings-appearance-page');
@@ -142,6 +117,12 @@
   setup(function() {
     appearanceBrowserProxy = new TestAppearanceBrowserProxy();
     settings.AppearanceBrowserProxyImpl.instance_ = appearanceBrowserProxy;
+
+    if (cr.isChromeOS) {
+      wallpaperBrowserProxy = new TestWallpaperBrowserProxy();
+      settings.WallpaperBrowserProxyImpl.instance_ = wallpaperBrowserProxy;
+    }
+
     createAppearancePage();
   });
 
@@ -152,24 +133,24 @@
   if (cr.isChromeOS) {
     // TODO(crbug/950007): Remove when SplitSettings is complete.
     test('wallpaperManager', function() {
-      appearanceBrowserProxy.setIsWallpaperPolicyControlled(false);
+      wallpaperBrowserProxy.setIsWallpaperPolicyControlled(false);
       // TODO(dschuyler): This should notice the policy change without needing
       // the page to be recreated.
       createAppearancePage();
-      return appearanceBrowserProxy.whenCalled('isWallpaperPolicyControlled')
+      return wallpaperBrowserProxy.whenCalled('isWallpaperPolicyControlled')
           .then(() => {
             const button = appearancePage.$.wallpaperButton;
             assertTrue(!!button);
             assertFalse(button.disabled);
             button.click();
-            return appearanceBrowserProxy.whenCalled('openWallpaperManager');
+            return wallpaperBrowserProxy.whenCalled('openWallpaperManager');
           });
     });
 
     // TODO(crbug/950007): Remove when SplitSettings is complete.
     test('wallpaperSettingVisible', function() {
       appearancePage.set('pageVisibility.setWallpaper', false);
-      return appearanceBrowserProxy.whenCalled('isWallpaperSettingVisible')
+      return wallpaperBrowserProxy.whenCalled('isWallpaperSettingVisible')
           .then(function() {
             Polymer.dom.flush();
             assertTrue(appearancePage.$$('#wallpaperButton').hidden);
@@ -180,9 +161,9 @@
     test('wallpaperPolicyControlled', function() {
       // Should show the wallpaper policy indicator and disable the toggle
       // button if the wallpaper is policy controlled.
-      appearanceBrowserProxy.setIsWallpaperPolicyControlled(true);
+      wallpaperBrowserProxy.setIsWallpaperPolicyControlled(true);
       createAppearancePage();
-      return appearanceBrowserProxy.whenCalled('isWallpaperPolicyControlled')
+      return wallpaperBrowserProxy.whenCalled('isWallpaperPolicyControlled')
           .then(function() {
             Polymer.dom.flush();
             assertFalse(appearancePage.$$('#wallpaperPolicyIndicator').hidden);
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js b/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
index 20f2760..bae3b51 100644
--- a/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
+++ b/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
@@ -699,6 +699,7 @@
     return super.extraLibraries.concat([
       '//ui/webui/resources/js/promise_resolver.js',
       BROWSER_SETTINGS_PATH + '../test_browser_proxy.js',
+      BROWSER_SETTINGS_PATH + 'test_wallpaper_browser_proxy.js',
       'personalization_page_test.js',
     ]);
   }
diff --git a/chrome/test/data/webui/settings/chromeos/personalization_page_test.js b/chrome/test/data/webui/settings/chromeos/personalization_page_test.js
index 437755da..27d5841 100644
--- a/chrome/test/data/webui/settings/chromeos/personalization_page_test.js
+++ b/chrome/test/data/webui/settings/chromeos/personalization_page_test.js
@@ -2,52 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-/** @implements {settings.AppearanceBrowserProxy} */
-class TestPersonalizationBrowserProxy extends TestBrowserProxy {
-  constructor() {
-    super([
-      'isWallpaperSettingVisible',
-      'isWallpaperPolicyControlled',
-      'openWallpaperManager',
-    ]);
-
-    /** @private */
-    this.isWallpaperSettingVisible_ = true;
-
-    /** @private */
-    this.isWallpaperPolicyControlled_ = false;
-  }
-
-  /** @override */
-  isWallpaperSettingVisible() {
-    this.methodCalled('isWallpaperSettingVisible');
-    return Promise.resolve(this.isWallpaperSettingVisible_);
-  }
-
-  /** @override */
-  isWallpaperPolicyControlled() {
-    this.methodCalled('isWallpaperPolicyControlled');
-    return Promise.resolve(this.isWallpaperPolicyControlled_);
-  }
-
-  /** @override */
-  openWallpaperManager() {
-    this.methodCalled('openWallpaperManager');
-  }
-
-  /** @param {boolean} Whether the wallpaper is policy controlled. */
-  setIsWallpaperPolicyControlled(isPolicyControlled) {
-    this.isWallpaperPolicyControlled_ = isPolicyControlled;
-  }
-}
-
 let personalizationPage = null;
 
-/** @type {?TestPersonalizationBrowserProxy} */
-let personalizationBrowserProxy = null;
+/** @type {?TestWallpaperBrowserProxy} */
+let WallpaperBrowserProxy = null;
 
 function createPersonalizationPage() {
-  personalizationBrowserProxy.reset();
+  WallpaperBrowserProxy.reset();
   PolymerTest.clearBody();
 
   personalizationPage = document.createElement('settings-personalization-page');
@@ -78,9 +39,8 @@
   });
 
   setup(function() {
-    personalizationBrowserProxy = new TestPersonalizationBrowserProxy();
-    settings.PersonalizationBrowserProxyImpl.instance_ =
-        personalizationBrowserProxy;
+    WallpaperBrowserProxy = new TestWallpaperBrowserProxy();
+    settings.WallpaperBrowserProxyImpl.instance_ = WallpaperBrowserProxy;
     createPersonalizationPage();
   });
 
@@ -89,16 +49,16 @@
   });
 
   test('wallpaperManager', async () => {
-    personalizationBrowserProxy.setIsWallpaperPolicyControlled(false);
+    WallpaperBrowserProxy.setIsWallpaperPolicyControlled(false);
     // TODO(dschuyler): This should notice the policy change without needing
     // the page to be recreated.
     createPersonalizationPage();
-    await personalizationBrowserProxy.whenCalled('isWallpaperPolicyControlled');
+    await WallpaperBrowserProxy.whenCalled('isWallpaperPolicyControlled');
     const button = personalizationPage.$.wallpaperButton;
     assertTrue(!!button);
     assertFalse(button.disabled);
     button.click();
-    await personalizationBrowserProxy.whenCalled('openWallpaperManager');
+    await WallpaperBrowserProxy.whenCalled('openWallpaperManager');
   });
 
   test('wallpaperSettingVisible', function() {
@@ -110,9 +70,9 @@
   test('wallpaperPolicyControlled', async () => {
     // Should show the wallpaper policy indicator and disable the toggle
     // button if the wallpaper is policy controlled.
-    personalizationBrowserProxy.setIsWallpaperPolicyControlled(true);
+    WallpaperBrowserProxy.setIsWallpaperPolicyControlled(true);
     createPersonalizationPage();
-    await personalizationBrowserProxy.whenCalled('isWallpaperPolicyControlled');
+    await WallpaperBrowserProxy.whenCalled('isWallpaperPolicyControlled');
     Polymer.dom.flush();
     assertFalse(personalizationPage.$$('#wallpaperPolicyIndicator').hidden);
     assertTrue(personalizationPage.$$('#wallpaperButton').disabled);
diff --git a/chrome/test/data/webui/settings/cr_settings_browsertest.js b/chrome/test/data/webui/settings/cr_settings_browsertest.js
index 314373e..073ee56 100644
--- a/chrome/test/data/webui/settings/cr_settings_browsertest.js
+++ b/chrome/test/data/webui/settings/cr_settings_browsertest.js
@@ -858,6 +858,7 @@
   extraLibraries: CrSettingsBrowserTest.prototype.extraLibraries.concat([
     '//ui/webui/resources/js/promise_resolver.js',
     '../test_browser_proxy.js',
+    'test_wallpaper_browser_proxy.js',
     'appearance_page_test.js',
   ]),
 };
diff --git a/chrome/test/data/webui/settings/test_wallpaper_browser_proxy.js b/chrome/test/data/webui/settings/test_wallpaper_browser_proxy.js
new file mode 100644
index 0000000..204f33e1
--- /dev/null
+++ b/chrome/test/data/webui/settings/test_wallpaper_browser_proxy.js
@@ -0,0 +1,42 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/** @implements {settings.WallpaperBrowserProxy} */
+class TestWallpaperBrowserProxy extends TestBrowserProxy {
+  constructor() {
+    super([
+      'isWallpaperSettingVisible',
+      'isWallpaperPolicyControlled',
+      'openWallpaperManager',
+    ]);
+
+    /** @private */
+    this.isWallpaperSettingVisible_ = true;
+
+    /** @private */
+    this.isWallpaperPolicyControlled_ = false;
+  }
+
+  /** @override */
+  isWallpaperSettingVisible() {
+    this.methodCalled('isWallpaperSettingVisible');
+    return Promise.resolve(true);
+  }
+
+  /** @override */
+  isWallpaperPolicyControlled() {
+    this.methodCalled('isWallpaperPolicyControlled');
+    return Promise.resolve(this.isWallpaperPolicyControlled_);
+  }
+
+  /** @override */
+  openWallpaperManager() {
+    this.methodCalled('openWallpaperManager');
+  }
+
+  /** @param {boolean} Whether the wallpaper is policy controlled. */
+  setIsWallpaperPolicyControlled(isPolicyControlled) {
+    this.isWallpaperPolicyControlled_ = isPolicyControlled;
+  }
+}
diff --git a/chrome/utility/BUILD.gn b/chrome/utility/BUILD.gn
index 5e94d02..8b076c0 100644
--- a/chrome/utility/BUILD.gn
+++ b/chrome/utility/BUILD.gn
@@ -219,13 +219,6 @@
       "//device/vr/public/mojom",
     ]
   }
-
-  if (enable_simple_browser_service_out_of_process) {
-    deps += [
-      "//services/content/simple_browser",
-      "//services/content/simple_browser/public/mojom",
-    ]
-  }
 }
 
 if (!is_android) {
diff --git a/chrome/utility/DEPS b/chrome/utility/DEPS
index cfd2aa3..ed6ca1f 100644
--- a/chrome/utility/DEPS
+++ b/chrome/utility/DEPS
@@ -6,15 +6,13 @@
   "+chrome/services/file_util/file_util_service.h",
   "+chrome/services/file_util/public/mojom",
   "+chrome/services/isolated_xr_device",
-  "+chrome/services/media_gallery_util/media_gallery_util_service.h",
-  "+chrome/services/media_gallery_util/public/mojom",
+  "+chrome/services/media_gallery_util",
   # TODO(crbug.com/798782): remove dependency to pdf_to_emf_converter_factory.h
   # when Cloud print chrome/service is removed.
   "+chrome/services/printing/pdf_to_emf_converter_factory.h",
   "+chrome/services/printing/printing_service.h",
   "+chrome/services/printing/public/mojom",
-  "+chrome/services/removable_storage_writer/removable_storage_writer_service.h",
-  "+chrome/services/removable_storage_writer/public/mojom",
+  "+chrome/services/removable_storage_writer",
   "+chrome/services/util_win/util_win_impl.h",
   "+chrome/services/util_win/public/mojom",
   "+chrome/services/wifi_util_win/wifi_util_win_service.h",
@@ -42,7 +40,6 @@
   "+extensions/common",
   "+extensions/buildflags",
   "+media",
-  "+services/content/simple_browser",
   "+services/network/public",
   "+services/network/url_request_context_builder_mojo.h",
   "+services/proxy_resolver",
diff --git a/chrome/utility/chrome_content_utility_client.cc b/chrome/utility/chrome_content_utility_client.cc
index 9c4e531..87f5f89 100644
--- a/chrome/utility/chrome_content_utility_client.cc
+++ b/chrome/utility/chrome_content_utility_client.cc
@@ -26,7 +26,6 @@
 #include "content/public/common/service_manager_connection.h"
 #include "content/public/common/simple_connection_filter.h"
 #include "content/public/utility/utility_thread.h"
-#include "device/vr/buildflags/buildflags.h"
 #include "extensions/buildflags/buildflags.h"
 #include "services/service_manager/public/cpp/binder_registry.h"
 #include "services/service_manager/sandbox/switches.h"
@@ -44,23 +43,10 @@
 #include "components/services/quarantine/quarantine_service.h"  // nogncheck
 #endif
 
-#if BUILDFLAG(ENABLE_VR) && !defined(OS_ANDROID)
-#include "chrome/services/isolated_xr_device/xr_device_service.h"
-#endif
-
-#if BUILDFLAG(ENABLE_EXTENSIONS)
-#include "chrome/services/removable_storage_writer/public/mojom/constants.mojom.h"
-#include "chrome/services/removable_storage_writer/removable_storage_writer_service.h"
-#if defined(OS_WIN)
+#if BUILDFLAG(ENABLE_EXTENSIONS) && defined(OS_WIN)
 #include "chrome/services/wifi_util_win/public/mojom/constants.mojom.h"
 #include "chrome/services/wifi_util_win/wifi_util_win_service.h"
 #endif
-#endif
-
-#if BUILDFLAG(ENABLE_EXTENSIONS) || defined(OS_ANDROID)
-#include "chrome/services/media_gallery_util/media_gallery_util_service.h"
-#include "chrome/services/media_gallery_util/public/mojom/constants.mojom.h"
-#endif
 
 #if defined(OS_CHROMEOS)
 #include "chromeos/assistant/buildflags.h"  // nogncheck
@@ -79,12 +65,6 @@
 #include "components/services/pdf_compositor/public/mojom/pdf_compositor.mojom.h"  // nogncheck
 #endif
 
-#if BUILDFLAG(ENABLE_PRINT_PREVIEW) || \
-    (BUILDFLAG(ENABLE_PRINTING) && defined(OS_WIN))
-#include "chrome/services/printing/printing_service.h"
-#include "chrome/services/printing/public/mojom/constants.mojom.h"
-#endif
-
 #if BUILDFLAG(ENABLE_PRINTING) && defined(OS_WIN)
 #include "chrome/services/printing/pdf_to_emf_converter_factory.h"
 #endif
@@ -93,11 +73,6 @@
 #include "chrome/utility/printing_handler.h"
 #endif
 
-#if BUILDFLAG(ENABLE_SIMPLE_BROWSER_SERVICE_OUT_OF_PROCESS)
-#include "services/content/simple_browser/public/mojom/constants.mojom.h"  // nogncheck
-#include "services/content/simple_browser/simple_browser_service.h"  // nogncheck
-#endif
-
 namespace {
 
 base::LazyInstance<ChromeContentUtilityClient::NetworkBinderCreationCallback>::
@@ -198,17 +173,6 @@
     return printing::CreatePdfCompositorService(std::move(request));
 #endif
 
-#if BUILDFLAG(ENABLE_VR) && !defined(OS_ANDROID)
-  if (service_name == device::mojom::kVrIsolatedServiceName)
-    return std::make_unique<device::XrDeviceService>(std::move(request));
-#endif
-
-#if BUILDFLAG(ENABLE_PRINT_PREVIEW) || \
-    (BUILDFLAG(ENABLE_PRINTING) && defined(OS_WIN))
-  if (service_name == printing::mojom::kChromePrintingServiceName)
-    return std::make_unique<printing::PrintingService>(std::move(request));
-#endif
-
 #if defined(OS_WIN)
   if (service_name == quarantine::mojom::kServiceName &&
       base::FeatureList::IsEnabled(quarantine::kOutOfProcessQuarantine)) {
@@ -228,25 +192,6 @@
   }
 #endif
 
-#if BUILDFLAG(ENABLE_EXTENSIONS) && !defined(OS_WIN)
-  // On Windows the service is running elevated.
-  if (service_name == chrome::mojom::kRemovableStorageWriterServiceName)
-    return std::make_unique<RemovableStorageWriterService>(std::move(request));
-#endif  // BUILDFLAG(ENABLE_EXTENSIONS) && !defined(OS_WIN)
-
-#if BUILDFLAG(ENABLE_EXTENSIONS) || defined(OS_ANDROID)
-  if (service_name == chrome::mojom::kMediaGalleryUtilServiceName)
-    return std::make_unique<MediaGalleryUtilService>(std::move(request));
-#endif  // BUILDFLAG(ENABLE_EXTENSIONS) || defined(OS_ANDROID)
-
-#if BUILDFLAG(ENABLE_SIMPLE_BROWSER_SERVICE_OUT_OF_PROCESS)
-  if (service_name == simple_browser::mojom::kServiceName) {
-    return std::make_unique<simple_browser::SimpleBrowserService>(
-        std::move(request), simple_browser::SimpleBrowserService::
-                                UIInitializationMode::kInitializeUI);
-  }
-#endif
-
 #if defined(OS_CHROMEOS)
   if (service_name == chromeos::ime::mojom::kServiceName)
     return std::make_unique<chromeos::ime::ImeService>(std::move(request));
@@ -270,9 +215,6 @@
 #if defined(OS_WIN) && BUILDFLAG(ENABLE_EXTENSIONS)
   if (service_name == chrome::mojom::kWifiUtilWinServiceName)
     return std::make_unique<WifiUtilWinService>(std::move(request));
-
-  if (service_name == chrome::mojom::kRemovableStorageWriterServiceName)
-    return std::make_unique<RemovableStorageWriterService>(std::move(request));
 #endif
 
   return nullptr;
@@ -286,6 +228,8 @@
 
 mojo::ServiceFactory*
 ChromeContentUtilityClient::GetMainThreadServiceFactory() {
+  if (utility_process_running_elevated_)
+    return ::GetElevatedMainThreadServiceFactory();
   return ::GetMainThreadServiceFactory();
 }
 
diff --git a/chrome/utility/services.cc b/chrome/utility/services.cc
index 33063ad..3b6ed97 100644
--- a/chrome/utility/services.cc
+++ b/chrome/utility/services.cc
@@ -15,6 +15,8 @@
 #include "components/services/patch/public/mojom/file_patcher.mojom.h"
 #include "components/services/unzip/public/mojom/unzipper.mojom.h"
 #include "components/services/unzip/unzipper_impl.h"
+#include "device/vr/buildflags/buildflags.h"
+#include "extensions/buildflags/buildflags.h"
 #include "mojo/public/cpp/bindings/service_factory.h"
 #include "printing/buildflags/buildflags.h"
 
@@ -37,6 +39,27 @@
 #include "chrome/services/file_util/file_util_service.h"  // nogncheck
 #endif
 
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+#include "chrome/services/removable_storage_writer/public/mojom/removable_storage_writer.mojom.h"
+#include "chrome/services/removable_storage_writer/removable_storage_writer.h"
+#endif
+
+#if BUILDFLAG(ENABLE_EXTENSIONS) || defined(OS_ANDROID)
+#include "chrome/services/media_gallery_util/media_parser_factory.h"
+#include "chrome/services/media_gallery_util/public/mojom/media_parser.mojom.h"
+#endif
+
+#if BUILDFLAG(ENABLE_VR) && !defined(OS_ANDROID)
+#include "chrome/services/isolated_xr_device/xr_device_service.h"  // nogncheck
+#include "device/vr/public/mojom/isolated_xr_service.mojom.h"      // nogncheck
+#endif
+
+#if BUILDFLAG(ENABLE_PRINT_PREVIEW) || \
+    (BUILDFLAG(ENABLE_PRINTING) && defined(OS_WIN))
+#include "chrome/services/printing/printing_service.h"
+#include "chrome/services/printing/public/mojom/printing_service.mojom.h"
+#endif
+
 namespace {
 
 auto RunFilePatcher(mojo::PendingReceiver<patch::mojom::FilePatcher> receiver) {
@@ -76,8 +99,51 @@
 }
 #endif
 
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+auto RunRemovableStorageWriter(
+    mojo::PendingReceiver<chrome::mojom::RemovableStorageWriter> receiver) {
+  return std::make_unique<RemovableStorageWriter>(std::move(receiver));
+}
+#endif
+
+#if BUILDFLAG(ENABLE_EXTENSIONS) || defined(OS_ANDROID)
+auto RunMediaParserFactory(
+    mojo::PendingReceiver<chrome::mojom::MediaParserFactory> receiver) {
+  return std::make_unique<MediaParserFactory>(std::move(receiver));
+}
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS) || defined(OS_ANDROID)
+
+#if BUILDFLAG(ENABLE_VR) && !defined(OS_ANDROID)
+auto RunXrDeviceService(
+    mojo::PendingReceiver<device::mojom::XRDeviceService> receiver) {
+  return std::make_unique<device::XrDeviceService>(std::move(receiver));
+}
+#endif
+
+#if BUILDFLAG(ENABLE_PRINT_PREVIEW) || \
+    (BUILDFLAG(ENABLE_PRINTING) && defined(OS_WIN))
+auto RunPrintingService(
+    mojo::PendingReceiver<printing::mojom::PrintingService> receiver) {
+  return std::make_unique<printing::PrintingService>(std::move(receiver));
+}
+#endif
+
 }  // namespace
 
+mojo::ServiceFactory* GetElevatedMainThreadServiceFactory() {
+  // NOTE: This ServiceFactory is only used in utility processes which are run
+  // with elevated system privileges.
+  // clang-format off
+  static base::NoDestructor<mojo::ServiceFactory> factory {
+#if BUILDFLAG(ENABLE_EXTENSIONS) && defined(OS_WIN)
+    // On non-Windows, this service runs in a regular utility process.
+    RunRemovableStorageWriter,
+#endif
+  };
+  // clang-format on
+  return factory.get();
+}
+
 mojo::ServiceFactory* GetMainThreadServiceFactory() {
   // clang-format off
   static base::NoDestructor<mojo::ServiceFactory> factory {
@@ -95,6 +161,24 @@
 #if BUILDFLAG(FULL_SAFE_BROWSING) || defined(OS_CHROMEOS)
     RunFileUtil,
 #endif
+
+#if BUILDFLAG(ENABLE_EXTENSIONS) && !defined(OS_WIN)
+    // On Windows, this service runs in an elevated utility process.
+    RunRemovableStorageWriter,
+#endif
+
+#if BUILDFLAG(ENABLE_EXTENSIONS) || defined(OS_ANDROID)
+    RunMediaParserFactory,
+#endif
+
+#if BUILDFLAG(ENABLE_VR) && !defined(OS_ANDROID)
+    RunXrDeviceService,
+#endif
+
+#if BUILDFLAG(ENABLE_PRINT_PREVIEW) || \
+    (BUILDFLAG(ENABLE_PRINTING) && defined(OS_WIN))
+    RunPrintingService,
+#endif
   };
   // clang-format on
   return factory.get();
diff --git a/chrome/utility/services.h b/chrome/utility/services.h
index 6fa90ac..23b6d1e 100644
--- a/chrome/utility/services.h
+++ b/chrome/utility/services.h
@@ -12,6 +12,7 @@
 // Helpers to run out-of-process services in a dedicated utility process. All
 // out-of-process services will need to have their implementation hooked up in
 // one of these helpers.
+mojo::ServiceFactory* GetElevatedMainThreadServiceFactory();
 mojo::ServiceFactory* GetMainThreadServiceFactory();
 mojo::ServiceFactory* GetIOThreadServiceFactory();
 
diff --git a/chromeos/constants/chromeos_features.cc b/chromeos/constants/chromeos_features.cc
index 9fea707..5aa8039c 100644
--- a/chromeos/constants/chromeos_features.cc
+++ b/chromeos/constants/chromeos_features.cc
@@ -155,9 +155,8 @@
     "ChromeOSParentalControlsSettings", base::FEATURE_DISABLED_BY_DEFAULT};
 
 // Enables or disables Release Notes on Chrome OS.
-// TODO(yulunwu): http://crbug.com/978201
 const base::Feature kReleaseNotes{"ReleaseNotes",
-                                  base::FEATURE_DISABLED_BY_DEFAULT};
+                                  base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Enables or disables showing the battery level in the System Tray and Settings
 // UI for supported Bluetooth Devices.
diff --git a/chromeos/dbus/session_manager/BUILD.gn b/chromeos/dbus/session_manager/BUILD.gn
index ca8831f..7b42b2d 100644
--- a/chromeos/dbus/session_manager/BUILD.gn
+++ b/chromeos/dbus/session_manager/BUILD.gn
@@ -35,6 +35,7 @@
 proto_library("login_manager_proto") {
   sources = [
     "//third_party/cros_system_api/dbus/login_manager/arc.proto",
+    "//third_party/cros_system_api/dbus/login_manager/login_screen_storage.proto",
     "//third_party/cros_system_api/dbus/login_manager/policy_descriptor.proto",
   ]
 
diff --git a/chromeos/dbus/session_manager/fake_session_manager_client.cc b/chromeos/dbus/session_manager/fake_session_manager_client.cc
index 4fcef56..f1ea6c5 100644
--- a/chromeos/dbus/session_manager/fake_session_manager_client.cc
+++ b/chromeos/dbus/session_manager/fake_session_manager_client.cc
@@ -13,6 +13,7 @@
 #include "base/files/scoped_file.h"
 #include "base/location.h"
 #include "base/numerics/safe_conversions.h"
+#include "base/optional.h"
 #include "base/path_service.h"
 #include "base/sequenced_task_runner.h"
 #include "base/strings/string_number_conversions.h"
@@ -285,6 +286,22 @@
   login_password_ = password;
 }
 
+void FakeSessionManagerClient::LoginScreenStorageStore(
+    const std::string& key,
+    const login_manager::LoginScreenStorageMetadata& metadata,
+    const std::string& data,
+    LoginScreenStorageStoreCallback callback) {
+  PostReply(FROM_HERE, std::move(callback), base::nullopt /* error */);
+}
+
+void FakeSessionManagerClient::LoginScreenStorageRetrieve(
+    const std::string& key,
+    LoginScreenStorageRetrieveCallback callback) {
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(std::move(callback), "Test" /* data */,
+                                base::nullopt /* error */));
+}
+
 void FakeSessionManagerClient::StartSession(
     const cryptohome::AccountIdentifier& cryptohome_id) {
   DCHECK_EQ(0UL, user_sessions_.count(cryptohome_id.account_id()));
diff --git a/chromeos/dbus/session_manager/fake_session_manager_client.h b/chromeos/dbus/session_manager/fake_session_manager_client.h
index b2dd3ba..67a0ccf7 100644
--- a/chromeos/dbus/session_manager/fake_session_manager_client.h
+++ b/chromeos/dbus/session_manager/fake_session_manager_client.h
@@ -59,6 +59,17 @@
                   const std::vector<std::string>& argv,
                   VoidDBusMethodCallback callback) override;
   void SaveLoginPassword(const std::string& password) override;
+
+  void LoginScreenStorageStore(
+      const std::string& key,
+      const login_manager::LoginScreenStorageMetadata& metadata,
+      const std::string& data,
+      LoginScreenStorageStoreCallback callback) override;
+
+  void LoginScreenStorageRetrieve(
+      const std::string& key,
+      LoginScreenStorageRetrieveCallback callback) override;
+
   void StartSession(
       const cryptohome::AccountIdentifier& cryptohome_id) override;
   void StopSession() override;
diff --git a/chromeos/dbus/session_manager/session_manager_client.cc b/chromeos/dbus/session_manager/session_manager_client.cc
index 0925ad1..26782d83 100644
--- a/chromeos/dbus/session_manager/session_manager_client.cc
+++ b/chromeos/dbus/session_manager/session_manager_client.cc
@@ -15,9 +15,11 @@
 #include "base/callback.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
+#include "base/files/scoped_file.h"
 #include "base/location.h"
 #include "base/macros.h"
 #include "base/metrics/histogram_macros.h"
+#include "base/optional.h"
 #include "base/path_service.h"
 #include "base/strings/string_util.h"
 #include "base/threading/thread_task_runner_handle.h"
@@ -25,6 +27,7 @@
 #include "chromeos/dbus/cryptohome/cryptohome_client.h"
 #include "chromeos/dbus/cryptohome/rpc.pb.h"
 #include "chromeos/dbus/login_manager/arc.pb.h"
+#include "chromeos/dbus/login_manager/login_screen_storage.pb.h"
 #include "chromeos/dbus/login_manager/policy_descriptor.pb.h"
 #include "chromeos/dbus/session_manager/fake_session_manager_client.h"
 #include "components/policy/proto/device_management_backend.pb.h"
@@ -48,6 +51,11 @@
 // The timeout used when starting the android container is 90 seconds
 constexpr int kStartArcTimeout = 90 * 1000;
 
+// 64k of data, minus 64 bits for a preceding size. This number was chosen to
+// fit all the data in a single pipe buffer and avoid blocking on write.
+// (http://man7.org/linux/man-pages/man7/pipe.7.html)
+const size_t kLoginScreenStorageDataSizeLimit = 1024 * 64 - sizeof(size_t);
+
 // Helper to get the enum type of RetrievePolicyResponseType based on error
 // name.
 RetrievePolicyResponseType GetPolicyResponseTypeByError(
@@ -103,10 +111,14 @@
 }
 
 // Creates a pipe that contains the given data. The data will be prefixed by a
-// size_t sized variable containing the size of the data to read.
+// size_t sized variable containing the size of the data to read. Since we don't
+// pass this pipe's read end anywhere, we can be sure that the only FD that can
+// read from that pipe will be closed on browser's exit, therefore the password
+// won't be leaked if the browser crashes.
 base::ScopedFD CreatePasswordPipe(const std::string& data) {
   int pipe_fds[2];
-  if (!base::CreateLocalNonBlockingPipe(pipe_fds)) {
+  if (data.size() > kLoginScreenStorageDataSizeLimit ||
+      !base::CreateLocalNonBlockingPipe(pipe_fds)) {
     DLOG(ERROR) << "Failed to create pipe";
     return base::ScopedFD();
   }
@@ -123,6 +135,21 @@
   return pipe_read_end;
 }
 
+// Reads a secret from the given pipe. Secret is preceded by a 'size_t' value
+// representing its size.
+bool ReadSecretFromPipe(base::ScopedFD fd, std::vector<uint8_t>* secret) {
+  size_t secret_size = 0;
+  if (!base::ReadFromFD(fd.get(), reinterpret_cast<char*>(&secret_size),
+                        sizeof(size_t)) ||
+      secret_size > kLoginScreenStorageDataSizeLimit) {
+    return false;
+  }
+
+  secret->resize(secret_size);
+  return base::ReadFromFD(fd.get(), reinterpret_cast<char*>(secret->data()),
+                          secret_size);
+}
+
 }  // namespace
 
 // The SessionManagerClient implementation used in production.
@@ -200,6 +227,51 @@
                                        base::DoNothing());
   }
 
+  void LoginScreenStorageStore(
+      const std::string& key,
+      const login_manager::LoginScreenStorageMetadata& metadata,
+      const std::string& data,
+      LoginScreenStorageStoreCallback callback) override {
+    dbus::MethodCall method_call(
+        login_manager::kSessionManagerInterface,
+        login_manager::kSessionManagerLoginScreenStorageStore);
+    dbus::MessageWriter writer(&method_call);
+    writer.AppendString(key);
+
+    const std::string metadata_blob = metadata.SerializeAsString();
+    writer.AppendArrayOfBytes(
+        reinterpret_cast<const uint8_t*>(metadata_blob.data()),
+        metadata_blob.size());
+
+    base::ScopedFD fd = CreatePasswordPipe(data);
+    if (fd.get() == -1) {
+      std::string error = "Could not create password pipe.";
+      LOG(ERROR) << error;
+      std::move(callback).Run(std::move(error));
+      return;
+    }
+    writer.AppendFileDescriptor(fd.get());
+
+    session_manager_proxy_->CallMethod(
+        &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
+        base::BindOnce(&SessionManagerClientImpl::OnLoginScreenStorageStore,
+                       weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+  }
+
+  void LoginScreenStorageRetrieve(
+      const std::string& key,
+      LoginScreenStorageRetrieveCallback callback) override {
+    dbus::MethodCall method_call(
+        login_manager::kSessionManagerInterface,
+        login_manager::kSessionManagerLoginScreenStorageRetrieve);
+    dbus::MessageWriter writer(&method_call);
+    writer.AppendString(key);
+    session_manager_proxy_->CallMethod(
+        &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
+        base::BindOnce(&SessionManagerClientImpl::OnLoginScreenStorageRetrieve,
+                       weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+  }
+
   void StartSession(
       const cryptohome::AccountIdentifier& cryptohome_id) override {
     dbus::MethodCall method_call(login_manager::kSessionManagerInterface,
@@ -646,6 +718,37 @@
     std::move(callback).Run(std::move(sessions));
   }
 
+  void OnLoginScreenStorageStore(LoginScreenStorageStoreCallback callback,
+                                 dbus::Response* response) {
+    std::move(callback).Run(base::nullopt);
+  }
+
+  void OnLoginScreenStorageRetrieve(LoginScreenStorageRetrieveCallback callback,
+                                    dbus::Response* response) {
+    if (!response) {
+      std::move(callback).Run(base::nullopt /* data */,
+                              "LoginScreenStorageRetrieve() D-Bus method "
+                              "returned an empty response");
+      return;
+    }
+
+    dbus::MessageReader reader(response);
+    base::ScopedFD result_fd;
+    if (!reader.PopFileDescriptor(&result_fd)) {
+      std::string error = "Invalid response: " + response->ToString();
+      std::move(callback).Run(base::nullopt /* data */, error);
+      return;
+    }
+    std::vector<uint8_t> result_data;
+    if (!ReadSecretFromPipe(std::move(result_fd), &result_data)) {
+      std::string error = "Couldn't read retrieved data from pipe.";
+      std::move(callback).Run(base::nullopt /* data */, error);
+      return;
+    }
+    std::move(callback).Run(std::string(result_data.begin(), result_data.end()),
+                            base::nullopt /* error */);
+  }
+
   // Reads an array of policy data bytes data as std::string.
   void ExtractPolicyResponseString(
       login_manager::PolicyAccountType account_type,
diff --git a/chromeos/dbus/session_manager/session_manager_client.h b/chromeos/dbus/session_manager/session_manager_client.h
index 2446146..cf020d6 100644
--- a/chromeos/dbus/session_manager/session_manager_client.h
+++ b/chromeos/dbus/session_manager/session_manager_client.h
@@ -26,6 +26,7 @@
 }
 
 namespace login_manager {
+class LoginScreenStorageMetadata;
 class PolicyDescriptor;
 class StartArcMiniContainerRequest;
 class UpgradeArcContainerRequest;
@@ -141,6 +142,33 @@
   // Sends the user's password to the session manager.
   virtual void SaveLoginPassword(const std::string& password) = 0;
 
+  // Used to report errors from |LoginScreenStorageStore()|. |error| should
+  // contain an error message if an error occurred.
+  using LoginScreenStorageStoreCallback =
+      DBusMethodCallback<std::string /* error */>;
+
+  // Stores data to the login screen storage. login screen storage is a D-Bus
+  // API that is used by the custom login screen implementations to inject
+  // credentials into the session and store persistent data across the login
+  // screen restarts.
+  virtual void LoginScreenStorageStore(
+      const std::string& key,
+      const login_manager::LoginScreenStorageMetadata& metadata,
+      const std::string& data,
+      LoginScreenStorageStoreCallback callback) = 0;
+
+  // Used for |LoginScreenStorageRetrieve()| method. |data| argument is the data
+  // returned by the session manager. |error| contains an error message if an
+  // error occurred, otherwise empty.
+  using LoginScreenStorageRetrieveCallback =
+      base::OnceCallback<void(base::Optional<std::string> /* data */,
+                              base::Optional<std::string> /* error */)>;
+
+  // Retrieve data stored earlier with the |LoginScreenStorageStore()| method.
+  virtual void LoginScreenStorageRetrieve(
+      const std::string& key,
+      LoginScreenStorageRetrieveCallback callback) = 0;
+
   // Starts the session for the user.
   virtual void StartSession(
       const cryptohome::AccountIdentifier& cryptohome_id) = 0;
diff --git a/chromeos/services/device_sync/BUILD.gn b/chromeos/services/device_sync/BUILD.gn
index de565fb..db08180d 100644
--- a/chromeos/services/device_sync/BUILD.gn
+++ b/chromeos/services/device_sync/BUILD.gn
@@ -85,6 +85,8 @@
     "cryptauth_scheduler.h",
     "cryptauth_scheduler_impl.cc",
     "cryptauth_scheduler_impl.h",
+    "cryptauth_task_metrics_logger.cc",
+    "cryptauth_task_metrics_logger.h",
     "cryptauth_v2_device_manager.cc",
     "cryptauth_v2_device_manager.h",
     "cryptauth_v2_device_manager_impl.cc",
diff --git a/chromeos/services/device_sync/cryptauth_task_metrics_logger.cc b/chromeos/services/device_sync/cryptauth_task_metrics_logger.cc
new file mode 100644
index 0000000..df9450e
--- /dev/null
+++ b/chromeos/services/device_sync/cryptauth_task_metrics_logger.cc
@@ -0,0 +1,45 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/services/device_sync/cryptauth_task_metrics_logger.h"
+
+#include "base/metrics/histogram_functions.h"
+
+namespace chromeos {
+
+namespace device_sync {
+
+CryptAuthApiCallResult CryptAuthApiCallResultFromNetworkRequestError(
+    NetworkRequestError network_request_error) {
+  switch (network_request_error) {
+    case NetworkRequestError::kOffline:
+      return CryptAuthApiCallResult::kNetworkRequestErrorOffline;
+    case NetworkRequestError::kEndpointNotFound:
+      return CryptAuthApiCallResult::kNetworkRequestErrorEndpointNotFound;
+    case NetworkRequestError::kAuthenticationError:
+      return CryptAuthApiCallResult::kNetworkRequestErrorAuthenticationError;
+    case NetworkRequestError::kBadRequest:
+      return CryptAuthApiCallResult::kNetworkRequestErrorBadRequest;
+    case NetworkRequestError::kResponseMalformed:
+      return CryptAuthApiCallResult::kNetworkRequestErrorResponseMalformed;
+    case NetworkRequestError::kInternalServerError:
+      return CryptAuthApiCallResult::kNetworkRequestErrorInternalServerError;
+    case NetworkRequestError::kUnknown:
+      return CryptAuthApiCallResult::kNetworkRequestErrorUnknown;
+  }
+}
+
+void LogCryptAuthAsyncTaskSuccessMetric(const std::string& metric_name,
+                                        CryptAuthAsyncTaskResult result) {
+  base::UmaHistogramEnumeration(metric_name, result);
+}
+
+void LogCryptAuthApiCallSuccessMetric(const std::string& metric_name,
+                                      CryptAuthApiCallResult result) {
+  base::UmaHistogramEnumeration(metric_name, result);
+}
+
+}  // namespace device_sync
+
+}  // namespace chromeos
diff --git a/chromeos/services/device_sync/cryptauth_task_metrics_logger.h b/chromeos/services/device_sync/cryptauth_task_metrics_logger.h
new file mode 100644
index 0000000..bafe30a
--- /dev/null
+++ b/chromeos/services/device_sync/cryptauth_task_metrics_logger.h
@@ -0,0 +1,58 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_SERVICES_DEVICE_SYNC_CRYPTAUTH_TASK_METRICS_LOGGER_H_
+#define CHROMEOS_SERVICES_DEVICE_SYNC_CRYPTAUTH_TASK_METRICS_LOGGER_H_
+
+#include <string>
+
+#include "chromeos/services/device_sync/network_request_error.h"
+
+namespace chromeos {
+
+namespace device_sync {
+
+// A group of functions and enums used to log success metrics for individual
+// tasks in the CryptAuth v2 Enrollment and v2 DeviceSync flows.
+
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused. If entries are added, kMaxValue should
+// be updated.
+enum class CryptAuthAsyncTaskResult {
+  kSuccess = 0,
+  kTimeout = 1,
+  kError = 2,
+  kMaxValue = kError
+};
+
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused. If entries are added, kMaxValue should
+// be updated.
+enum class CryptAuthApiCallResult {
+  kSuccess = 0,
+  kTimeout = 1,
+  kNetworkRequestErrorOffline = 2,
+  kNetworkRequestErrorEndpointNotFound = 3,
+  kNetworkRequestErrorAuthenticationError = 4,
+  kNetworkRequestErrorBadRequest = 5,
+  kNetworkRequestErrorResponseMalformed = 6,
+  kNetworkRequestErrorInternalServerError = 7,
+  kNetworkRequestErrorUnknown = 8,
+  kMaxValue = kNetworkRequestErrorUnknown
+};
+
+CryptAuthApiCallResult CryptAuthApiCallResultFromNetworkRequestError(
+    NetworkRequestError network_request_error);
+
+void LogCryptAuthAsyncTaskSuccessMetric(const std::string& metric_name,
+                                        CryptAuthAsyncTaskResult result);
+
+void LogCryptAuthApiCallSuccessMetric(const std::string& metric_name,
+                                      CryptAuthApiCallResult result);
+
+}  // namespace device_sync
+
+}  // namespace chromeos
+
+#endif  // CHROMEOS_SERVICES_DEVICE_SYNC_CRYPTAUTH_TASK_METRICS_LOGGER_H_
diff --git a/components/chromeos_camera/common/BUILD.gn b/components/chromeos_camera/common/BUILD.gn
index cb50b458..dae2005 100644
--- a/components/chromeos_camera/common/BUILD.gn
+++ b/components/chromeos_camera/common/BUILD.gn
@@ -15,7 +15,4 @@
     "//mojo/public/mojom/base",
     "//ui/gfx/geometry/mojom",
   ]
-
-  # TODO(https://crbug.com/968369): Change to use new names.
-  use_old_js_lite_bindings_names = true
 }
diff --git a/components/optimization_guide/hints_component_util.cc b/components/optimization_guide/hints_component_util.cc
index 4e89c40..d66d7a3 100644
--- a/components/optimization_guide/hints_component_util.cc
+++ b/components/optimization_guide/hints_component_util.cc
@@ -5,13 +5,17 @@
 #include "components/optimization_guide/hints_component_util.h"
 
 #include <string>
+#include <utility>
 
 #include "base/files/file.h"
 #include "base/files/file_util.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/stringprintf.h"
+#include "components/optimization_guide/bloom_filter.h"
 #include "components/optimization_guide/hints_component_info.h"
 #include "components/optimization_guide/hints_processing_util.h"
+#include "components/optimization_guide/optimization_filter.h"
+#include "components/optimization_guide/optimization_guide_features.h"
 
 namespace optimization_guide {
 
@@ -20,13 +24,22 @@
 const char kProcessHintsComponentResultHistogramString[] =
     "OptimizationGuide.ProcessHintsResult";
 
-void MaybePopulateProcessHintsComponentResult(
+// Populates |out_result| with |result| if |out_result| is provided.
+void PopulateProcessHintsComponentResultIfSet(
     ProcessHintsComponentResult result,
     ProcessHintsComponentResult* out_result) {
   if (out_result)
     *out_result = result;
 }
 
+// Populates |out_status| with |status| if |out_status| is provided.
+void PopulateOptimizationFilterStatusIfSet(
+    OptimizationFilterStatus status,
+    OptimizationFilterStatus* out_status) {
+  if (out_status)
+    *out_status = status;
+}
+
 }  // namespace
 
 const char kComponentHintsUpdatedResultHistogramString[] =
@@ -37,6 +50,35 @@
                             result);
 }
 
+std::unique_ptr<proto::Configuration> ProcessHintsComponent(
+    const HintsComponentInfo& component_info,
+    ProcessHintsComponentResult* out_result) {
+  if (!component_info.version.IsValid() || component_info.path.empty()) {
+    PopulateProcessHintsComponentResultIfSet(
+        ProcessHintsComponentResult::kFailedInvalidParameters, out_result);
+    return nullptr;
+  }
+
+  std::string binary_pb;
+  if (!base::ReadFileToString(component_info.path, &binary_pb)) {
+    PopulateProcessHintsComponentResultIfSet(
+        ProcessHintsComponentResult::kFailedReadingFile, out_result);
+    return nullptr;
+  }
+
+  std::unique_ptr<proto::Configuration> proto_configuration =
+      std::make_unique<proto::Configuration>();
+  if (!proto_configuration->ParseFromString(binary_pb)) {
+    PopulateProcessHintsComponentResultIfSet(
+        ProcessHintsComponentResult::kFailedInvalidConfiguration, out_result);
+    return nullptr;
+  }
+
+  PopulateProcessHintsComponentResultIfSet(
+      ProcessHintsComponentResult::kSuccess, out_result);
+  return proto_configuration;
+}
+
 void RecordOptimizationFilterStatus(proto::OptimizationType optimization_type,
                                     OptimizationFilterStatus status) {
   std::string histogram_name = base::StringPrintf(
@@ -45,33 +87,38 @@
   UMA_HISTOGRAM_ENUMERATION(histogram_name, status);
 }
 
-std::unique_ptr<proto::Configuration> ProcessHintsComponent(
-    const HintsComponentInfo& component_info,
-    ProcessHintsComponentResult* out_result) {
-  if (!component_info.version.IsValid() || component_info.path.empty()) {
-    MaybePopulateProcessHintsComponentResult(
-        ProcessHintsComponentResult::kFailedInvalidParameters, out_result);
+std::unique_ptr<OptimizationFilter> ProcessOptimizationFilter(
+    const proto::OptimizationFilter& optimization_filter,
+    OptimizationFilterStatus* out_status) {
+  const auto& bloom_filter_proto = optimization_filter.bloom_filter();
+  DCHECK_GT(bloom_filter_proto.num_hash_functions(), 0u);
+  DCHECK_GT(bloom_filter_proto.num_bits(), 0u);
+  DCHECK(bloom_filter_proto.has_data());
+
+  if (!bloom_filter_proto.has_data() || bloom_filter_proto.num_bits() <= 0 ||
+      bloom_filter_proto.num_bits() > bloom_filter_proto.data().size() * 8) {
+    DLOG(ERROR) << "Bloom filter config issue";
+    PopulateOptimizationFilterStatusIfSet(
+        OptimizationFilterStatus::kFailedServerBlacklistBadConfig, out_status);
     return nullptr;
   }
 
-  std::string binary_pb;
-  if (!base::ReadFileToString(component_info.path, &binary_pb)) {
-    MaybePopulateProcessHintsComponentResult(
-        ProcessHintsComponentResult::kFailedReadingFile, out_result);
+  if (static_cast<int>(bloom_filter_proto.num_bits()) >
+      features::MaxServerBloomFilterByteSize() * 8) {
+    DLOG(ERROR) << "Bloom filter data exceeds maximum size of "
+                << optimization_guide::features::MaxServerBloomFilterByteSize()
+                << " bytes";
+    PopulateOptimizationFilterStatusIfSet(
+        OptimizationFilterStatus::kFailedServerBlacklistTooBig, out_status);
     return nullptr;
   }
 
-  std::unique_ptr<proto::Configuration> proto_configuration =
-      std::make_unique<proto::Configuration>();
-  if (!proto_configuration->ParseFromString(binary_pb)) {
-    MaybePopulateProcessHintsComponentResult(
-        ProcessHintsComponentResult::kFailedInvalidConfiguration, out_result);
-    return nullptr;
-  }
-
-  MaybePopulateProcessHintsComponentResult(
-      ProcessHintsComponentResult::kSuccess, out_result);
-  return proto_configuration;
+  std::unique_ptr<BloomFilter> bloom_filter = std::make_unique<BloomFilter>(
+      bloom_filter_proto.num_hash_functions(), bloom_filter_proto.num_bits(),
+      bloom_filter_proto.data());
+  PopulateOptimizationFilterStatusIfSet(
+      OptimizationFilterStatus::kCreatedServerBlacklist, out_status);
+  return std::make_unique<OptimizationFilter>(std::move(bloom_filter));
 }
 
 }  // namespace optimization_guide
diff --git a/components/optimization_guide/hints_component_util.h b/components/optimization_guide/hints_component_util.h
index 620c936e..ac31f37 100644
--- a/components/optimization_guide/hints_component_util.h
+++ b/components/optimization_guide/hints_component_util.h
@@ -12,6 +12,7 @@
 namespace optimization_guide {
 
 struct HintsComponentInfo;
+class OptimizationFilter;
 
 // The local histogram used to record that the component hints are stored in
 // the cache and are ready for use.
@@ -38,6 +39,17 @@
 // Records the ProcessHintsComponentResult to UMA.
 void RecordProcessHintsComponentResult(ProcessHintsComponentResult result);
 
+// Processes the specified hints component.
+//
+// If successful, returns the component's Configuration protobuf. Otherwise,
+// returns a nullptr.
+//
+// If |out_result| provided, it will be populated with the result up to that
+// point.
+std::unique_ptr<proto::Configuration> ProcessHintsComponent(
+    const HintsComponentInfo& info,
+    ProcessHintsComponentResult* out_result);
+
 // Enumerates status event of processing optimization filters (such as the
 // lite page redirect blacklist). Used in UMA histograms, so the order of
 // enumerators should not be changed.
@@ -59,16 +71,12 @@
 void RecordOptimizationFilterStatus(proto::OptimizationType optimization_type,
                                     OptimizationFilterStatus status);
 
-// Processes the specified hints component.
-//
-// If successful, returns the component's Configuration protobuf. Otherwise,
-// returns a nullptr.
-//
-// If |out_result| provided, it will be populated with the result up to that
-// point.
-std::unique_ptr<proto::Configuration> ProcessHintsComponent(
-    const HintsComponentInfo& info,
-    ProcessHintsComponentResult* out_result);
+// Validates and parses |optimization_filter_proto| and creates one that is
+// intended to be queried to make decisions for whether an optimization type
+// should be applied on a navigation.
+std::unique_ptr<OptimizationFilter> ProcessOptimizationFilter(
+    const proto::OptimizationFilter& optimization_filter_proto,
+    OptimizationFilterStatus* out_status);
 
 }  // namespace optimization_guide
 
diff --git a/components/optimization_guide/hints_component_util_unittest.cc b/components/optimization_guide/hints_component_util_unittest.cc
index ca28196..ac8b8d7 100644
--- a/components/optimization_guide/hints_component_util_unittest.cc
+++ b/components/optimization_guide/hints_component_util_unittest.cc
@@ -12,9 +12,12 @@
 #include "base/macros.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/version.h"
+#include "components/optimization_guide/bloom_filter.h"
 #include "components/optimization_guide/hints_component_info.h"
-#include "components/optimization_guide/proto/hints.pb.h"
+#include "components/optimization_guide/optimization_filter.h"
+#include "components/optimization_guide/optimization_guide_features.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
 
 namespace optimization_guide {
 
@@ -52,16 +55,6 @@
                                       ProcessHintsComponentResult::kSuccess, 1);
 }
 
-TEST_F(HintsComponentUtilTest, RecordOptimizationFilterStatus) {
-  base::HistogramTester histogram_tester;
-  RecordOptimizationFilterStatus(
-      proto::OptimizationType::NOSCRIPT,
-      OptimizationFilterStatus::kFoundServerBlacklistConfig);
-  histogram_tester.ExpectUniqueSample(
-      "OptimizationGuide.OptimizationFilterStatus.NoScript",
-      OptimizationFilterStatus::kFoundServerBlacklistConfig, 1);
-}
-
 TEST_F(HintsComponentUtilTest, ProcessHintsComponentInvalidVersion) {
   ProcessHintsComponentResult result;
   std::unique_ptr<proto::Configuration> config = ProcessHintsComponent(
@@ -138,4 +131,110 @@
   EXPECT_EQ("google.com", processed_config->hints()[0].key());
 }
 
+TEST_F(HintsComponentUtilTest, RecordOptimizationFilterStatus) {
+  base::HistogramTester histogram_tester;
+  RecordOptimizationFilterStatus(
+      proto::OptimizationType::NOSCRIPT,
+      OptimizationFilterStatus::kFoundServerBlacklistConfig);
+  histogram_tester.ExpectUniqueSample(
+      "OptimizationGuide.OptimizationFilterStatus.NoScript",
+      OptimizationFilterStatus::kFoundServerBlacklistConfig, 1);
+}
+
+TEST_F(HintsComponentUtilTest, ProcessOptimizationFilter) {
+  int num_hash_functions = 7;
+  int num_bits = 1234;
+
+  proto::OptimizationFilter optimization_filter_proto;
+  BloomFilter bloom_filter(num_hash_functions, num_bits);
+  bloom_filter.Add("black.com");
+  proto::BloomFilter* bloom_filter_proto =
+      optimization_filter_proto.mutable_bloom_filter();
+  bloom_filter_proto->set_num_hash_functions(num_hash_functions);
+  bloom_filter_proto->set_num_bits(num_bits);
+  std::string blacklist_data(
+      reinterpret_cast<const char*>(&bloom_filter.bytes()[0]),
+      bloom_filter.bytes().size());
+  bloom_filter_proto->set_data(blacklist_data);
+
+  OptimizationFilterStatus status;
+  std::unique_ptr<OptimizationFilter> optimization_filter =
+      ProcessOptimizationFilter(optimization_filter_proto, &status);
+
+  EXPECT_EQ(status, OptimizationFilterStatus::kCreatedServerBlacklist);
+  ASSERT_TRUE(optimization_filter);
+  EXPECT_TRUE(
+      optimization_filter->ContainsHostSuffix(GURL("https://m.black.com")));
+}
+
+TEST_F(HintsComponentUtilTest, ProcessOptimizationFilterWithBadNumBits) {
+  proto::OptimizationFilter optimization_filter_proto;
+  BloomFilter bloom_filter(7, 1234);
+  bloom_filter.Add("black.com");
+  proto::BloomFilter* bloom_filter_proto =
+      optimization_filter_proto.mutable_bloom_filter();
+  bloom_filter_proto->set_num_hash_functions(7);
+  bloom_filter_proto->set_num_bits(bloom_filter.bytes().size() * 8 + 1);
+  std::string blacklist_data(
+      reinterpret_cast<const char*>(&bloom_filter.bytes()[0]),
+      bloom_filter.bytes().size());
+  bloom_filter_proto->set_data(blacklist_data);
+
+  OptimizationFilterStatus status;
+  std::unique_ptr<OptimizationFilter> optimization_filter =
+      ProcessOptimizationFilter(optimization_filter_proto, &status);
+
+  EXPECT_EQ(status, OptimizationFilterStatus::kFailedServerBlacklistBadConfig);
+  EXPECT_EQ(nullptr, optimization_filter);
+}
+
+TEST_F(HintsComponentUtilTest, ProcessOptimizationFilterWithTooLargeBlacklist) {
+  int too_many_bits = features::MaxServerBloomFilterByteSize() * 8 + 1;
+
+  proto::OptimizationFilter optimization_filter_proto;
+  BloomFilter bloom_filter(7, too_many_bits);
+  bloom_filter.Add("black.com");
+  proto::BloomFilter* bloom_filter_proto =
+      optimization_filter_proto.mutable_bloom_filter();
+  bloom_filter_proto->set_num_hash_functions(7);
+  bloom_filter_proto->set_num_bits(too_many_bits);
+  std::string blacklist_data(
+      reinterpret_cast<const char*>(&bloom_filter.bytes()[0]),
+      bloom_filter.bytes().size());
+  bloom_filter_proto->set_data(blacklist_data);
+
+  OptimizationFilterStatus status;
+  std::unique_ptr<OptimizationFilter> optimization_filter =
+      ProcessOptimizationFilter(optimization_filter_proto, &status);
+
+  EXPECT_EQ(status, OptimizationFilterStatus::kFailedServerBlacklistTooBig);
+  EXPECT_EQ(nullptr, optimization_filter);
+}
+
+TEST_F(HintsComponentUtilTest,
+       ProcessOptimizationFilterNoResultProvidedDoesntCrash) {
+  int num_hash_functions = 7;
+  int num_bits = 1234;
+
+  proto::OptimizationFilter optimization_filter_proto;
+  BloomFilter bloom_filter(num_hash_functions, num_bits);
+  bloom_filter.Add("black.com");
+  proto::BloomFilter* bloom_filter_proto =
+      optimization_filter_proto.mutable_bloom_filter();
+  bloom_filter_proto->set_num_hash_functions(num_hash_functions);
+  bloom_filter_proto->set_num_bits(num_bits);
+  std::string blacklist_data(
+      reinterpret_cast<const char*>(&bloom_filter.bytes()[0]),
+      bloom_filter.bytes().size());
+  bloom_filter_proto->set_data(blacklist_data);
+
+  std::unique_ptr<OptimizationFilter> optimization_filter =
+      ProcessOptimizationFilter(optimization_filter_proto,
+                                /*out_status=*/nullptr);
+
+  ASSERT_TRUE(optimization_filter);
+  EXPECT_TRUE(
+      optimization_filter->ContainsHostSuffix(GURL("https://m.black.com")));
+}
+
 }  // namespace optimization_guide
diff --git a/components/optimization_guide/optimization_guide_features.cc b/components/optimization_guide/optimization_guide_features.cc
index d135a7c9..9eeccbb 100644
--- a/components/optimization_guide/optimization_guide_features.cc
+++ b/components/optimization_guide/optimization_guide_features.cc
@@ -119,5 +119,11 @@
   return base::FeatureList::IsEnabled(features::kOptimizationGuideKeyedService);
 }
 
+int MaxServerBloomFilterByteSize() {
+  return base::GetFieldTrialParamByFeatureAsInt(features::kOptimizationHints,
+                                                "max_bloom_filter_byte_size",
+                                                250 * 1024 /* 250KB */);
+}
+
 }  // namespace features
 }  // namespace optimization_guide
diff --git a/components/optimization_guide/optimization_guide_features.h b/components/optimization_guide/optimization_guide_features.h
index 069aebb..8030646 100644
--- a/components/optimization_guide/optimization_guide_features.h
+++ b/components/optimization_guide/optimization_guide_features.h
@@ -54,6 +54,11 @@
 // enabled.
 bool IsOptimizationGuideKeyedServiceEnabled();
 
+// The maximum data byte size for a server-provided bloom filter. This is
+// a client-side safety limit for RAM use in case server sends too large of
+// a bloom filter.
+int MaxServerBloomFilterByteSize();
+
 }  // namespace features
 }  // namespace optimization_guide
 
diff --git a/components/password_manager/core/browser/leak_detection/BUILD.gn b/components/password_manager/core/browser/leak_detection/BUILD.gn
index 3d1b7355..29db9fb 100644
--- a/components/password_manager/core/browser/leak_detection/BUILD.gn
+++ b/components/password_manager/core/browser/leak_detection/BUILD.gn
@@ -27,6 +27,8 @@
   sources = [
     "authenticated_leak_check.cc",
     "authenticated_leak_check.h",
+    "encryption_utils.cc",
+    "encryption_utils.h",
     "leak_detection_check.h",
     "leak_detection_request_factory.h",
     "leak_detection_request_factory_impl.cc",
@@ -65,6 +67,7 @@
   testonly = true
   sources = [
     "authenticated_leak_check_unittest.cc",
+    "encryption_utils_unittest.cc",
     "leak_detection_request_factory_impl_unittest.cc",
   ]
 
diff --git a/components/password_manager/core/browser/leak_detection/encryption_utils.cc b/components/password_manager/core/browser/leak_detection/encryption_utils.cc
new file mode 100644
index 0000000..9e35908
--- /dev/null
+++ b/components/password_manager/core/browser/leak_detection/encryption_utils.cc
@@ -0,0 +1,47 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/password_manager/core/browser/leak_detection/encryption_utils.h"
+
+#include "base/stl_util.h"
+#include "base/strings/strcat.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/string_util.h"
+#include "crypto/openssl_util.h"
+#include "third_party/boringssl/src/include/openssl/evp.h"
+
+namespace password_manager {
+
+std::string ScryptHashUsernameAndPassword(const std::string& username,
+                                          const std::string& password) {
+  // Constant salt added to the password hash on top of username.
+  static constexpr char kPasswordHashSalt[] = {
+      48,   118, 42,  -46,  63,  123, -95, -101, -8,  -29, 66,
+      -4,   -95, -89, -115, 6,   -26, 107, -28,  -37, -72, 79,
+      -127, 83,  -59, 3,    -56, -37, -67, -34,  -91, 32};
+  static constexpr size_t kHashKeyLength = 32;
+  static constexpr uint64_t kScryptCost = 1 << 12;  // It must be a power of 2.
+  static constexpr uint64_t kScryptBlockSize = 8;
+  static constexpr uint64_t kScryptParallelization = 1;
+  static constexpr size_t kScryptMaxMemory = 1024 * 1024 * 32;
+
+  crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
+  std::string username_password = username + password;
+  std::string salt = base::StrCat(
+      {username,
+       base::StringPiece(kPasswordHashSalt, base::size(kPasswordHashSalt))});
+
+  std::string result;
+  uint8_t* key_data =
+      reinterpret_cast<uint8_t*>(base::WriteInto(&result, kHashKeyLength + 1));
+
+  int scrypt_ok =
+      EVP_PBE_scrypt(username_password.data(), username_password.size(),
+                     reinterpret_cast<const uint8_t*>(salt.data()), salt.size(),
+                     kScryptCost, kScryptBlockSize, kScryptParallelization,
+                     kScryptMaxMemory, key_data, kHashKeyLength);
+  return scrypt_ok == 1 ? std::move(result) : std::string();
+}
+
+}  // namespace password_manager
diff --git a/components/password_manager/core/browser/leak_detection/encryption_utils.h b/components/password_manager/core/browser/leak_detection/encryption_utils.h
new file mode 100644
index 0000000..c157bca
--- /dev/null
+++ b/components/password_manager/core/browser/leak_detection/encryption_utils.h
@@ -0,0 +1,19 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_LEAK_DETECTION_ENCRYPTION_UTILS_H_
+#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_LEAK_DETECTION_ENCRYPTION_UTILS_H_
+
+#include <string>
+
+namespace password_manager {
+
+// Produces the username/password pair hash using scrypt algorithm.
+// |username| and |password| are UTF-8 strings.
+std::string ScryptHashUsernameAndPassword(const std::string& username,
+                                          const std::string& password);
+
+}  // namespace password_manager
+
+#endif  // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_LEAK_DETECTION_ENCRYPTION_UTILS_H_
diff --git a/components/password_manager/core/browser/leak_detection/encryption_utils_unittest.cc b/components/password_manager/core/browser/leak_detection/encryption_utils_unittest.cc
new file mode 100644
index 0000000..4767564
--- /dev/null
+++ b/components/password_manager/core/browser/leak_detection/encryption_utils_unittest.cc
@@ -0,0 +1,25 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/password_manager/core/browser/leak_detection/encryption_utils.h"
+
+#include "base/strings/string_util.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace password_manager {
+
+TEST(EncryptionUtils, ScryptHashUsernameAndPassword) {
+  // The expected result was obtained by running the Java implementation of the
+  // hash.
+  // Needs to stay in sync with server side constant: go/passwords-leak-salts.
+  constexpr char kExpected[] = {-103, 126, -10, 118,  7,    76,  -51, -76,
+                                -56,  -82, -38, 31,   114,  61,  -7,  103,
+                                76,   91,  52,  -52,  47,   -22, 107, 77,
+                                118,  123, -14, -125, -123, 85,  115, -3};
+  std::string result = ScryptHashUsernameAndPassword("user", "password123");
+  EXPECT_THAT(result, ::testing::ElementsAreArray(kExpected));
+}
+
+}  // namespace password_manager
diff --git a/components/previews/content/previews_hints.cc b/components/previews/content/previews_hints.cc
index 7ea0939e..a57cd91 100644
--- a/components/previews/content/previews_hints.cc
+++ b/components/previews/content/previews_hints.cc
@@ -5,6 +5,7 @@
 #include "components/previews/content/previews_hints.h"
 
 #include <unordered_set>
+#include <utility>
 
 #include "base/files/file.h"
 #include "base/files/file_util.h"
@@ -232,46 +233,11 @@
                 kFailedServerBlacklistDuplicateConfig);
         continue;
       }
-      const auto& bloom_filter_proto = blacklist.bloom_filter();
-      DCHECK_GT(bloom_filter_proto.num_hash_functions(), 0u);
-      DCHECK_GT(bloom_filter_proto.num_bits(), 0u);
-      DCHECK(bloom_filter_proto.has_data());
-      if (!bloom_filter_proto.has_data() ||
-          bloom_filter_proto.num_bits() <= 0 ||
-          bloom_filter_proto.num_bits() >
-              bloom_filter_proto.data().size() * 8) {
-        DLOG(ERROR) << "Bloom filter config issue";
-        RecordOptimizationFilterStatus(
-            blacklist.optimization_type(),
-            optimization_guide::OptimizationFilterStatus::
-                kFailedServerBlacklistBadConfig);
-        continue;
-      }
-      if (static_cast<int>(bloom_filter_proto.num_bits()) >
-          previews::params::
-                  LitePageRedirectPreviewMaxServerBlacklistByteSize() *
-              8) {
-        DLOG(ERROR) << "Bloom filter data exceeds maximum size of "
-                    << previews::params::
-                           LitePageRedirectPreviewMaxServerBlacklistByteSize()
-                    << " bytes";
-        RecordOptimizationFilterStatus(
-            blacklist.optimization_type(),
-            optimization_guide::OptimizationFilterStatus::
-                kFailedServerBlacklistTooBig);
-        continue;
-      }
-      std::unique_ptr<optimization_guide::BloomFilter> bloom_filter =
-          std::make_unique<optimization_guide::BloomFilter>(
-              bloom_filter_proto.num_hash_functions(),
-              bloom_filter_proto.num_bits(), bloom_filter_proto.data());
+
+      optimization_guide::OptimizationFilterStatus status;
       lite_page_redirect_blacklist_ =
-          std::make_unique<optimization_guide::OptimizationFilter>(
-              std::move(bloom_filter));
-      RecordOptimizationFilterStatus(
-          blacklist.optimization_type(),
-          optimization_guide::OptimizationFilterStatus::
-              kCreatedServerBlacklist);
+          optimization_guide::ProcessOptimizationFilter(blacklist, &status);
+      RecordOptimizationFilterStatus(blacklist.optimization_type(), status);
     }
   }
 }
diff --git a/components/previews/content/previews_hints_unittest.cc b/components/previews/content/previews_hints_unittest.cc
index 2c02021..e4013f1 100644
--- a/components/previews/content/previews_hints_unittest.cc
+++ b/components/previews/content/previews_hints_unittest.cc
@@ -344,9 +344,7 @@
   scoped_list.InitAndEnableFeature(features::kLitePageServerPreviews);
 
   int too_many_bits =
-      previews::params::LitePageRedirectPreviewMaxServerBlacklistByteSize() *
-          8 +
-      1;
+      optimization_guide::features::MaxServerBloomFilterByteSize() * 8 + 1;
 
   optimization_guide::BloomFilter blacklist_bloom_filter(
       kBlackBlacklistBloomFilterNumHashFunctions, too_many_bits);
diff --git a/components/previews/core/previews_experiments.cc b/components/previews/core/previews_experiments.cc
index 8f026c2f..e671e81 100644
--- a/components/previews/core/previews_experiments.cc
+++ b/components/previews/core/previews_experiments.cc
@@ -160,12 +160,6 @@
                                              30 * 1000));
 }
 
-int LitePageRedirectPreviewMaxServerBlacklistByteSize() {
-  return base::GetFieldTrialParamByFeatureAsInt(
-      features::kLitePageServerPreviews, "max_blacklist_byte_size",
-      250 * 1024 /* 250KB */);
-}
-
 size_t LitePageRedirectPreviewMaxNavigationRestarts() {
   return base::GetFieldTrialParamByFeatureAsInt(
       features::kLitePageServerPreviews, "max_navigation_restart", 5);
diff --git a/components/previews/core/previews_experiments.h b/components/previews/core/previews_experiments.h
index 2c291914..b963bf67 100644
--- a/components/previews/core/previews_experiments.h
+++ b/components/previews/core/previews_experiments.h
@@ -126,11 +126,6 @@
 // page hints for the host.
 bool LitePagePreviewsOverridePageHints();
 
-// The maximum data byte size for the server-provided blacklist. This is
-// a client-side safety limit for RAM use in case server sends too large of
-// a blacklist.
-int LitePageRedirectPreviewMaxServerBlacklistByteSize();
-
 // The maximum number of times that a Lite Page Redirect preview should restart
 // a navigation.
 size_t LitePageRedirectPreviewMaxNavigationRestarts();
diff --git a/components/resources/BUILD.gn b/components/resources/BUILD.gn
index 209ff73..e608270 100644
--- a/components/resources/BUILD.gn
+++ b/components/resources/BUILD.gn
@@ -28,6 +28,8 @@
   ]
   output_dir = "$root_gen_dir/components"
 
+  use_brotli = true
+
   grit_flags = [
     "-E",
     "about_credits_file=" + rebase_path(about_credits_file, root_build_dir),
diff --git a/components/signin/public/identity_manager/identity_manager.h b/components/signin/public/identity_manager/identity_manager.h
index 44ff84d..9264caa 100644
--- a/components/signin/public/identity_manager/identity_manager.h
+++ b/components/signin/public/identity_manager/identity_manager.h
@@ -33,9 +33,7 @@
 class TestURLLoaderFactory;
 }  // namespace network
 
-// Necessary to declare these classes as friends.
 class PrefRegistrySimple;
-class SigninManagerAndroid;
 
 class AccountFetcherService;
 class AccountTrackerService;
@@ -537,10 +535,6 @@
       const std::string& locale,
       const std::string& picture_url);
 
-  // These friends are temporary during the conversion process.
-  // TODO(https://crbug.com/889902): Delete this when conversion is done.
-  friend SigninManagerAndroid;
-
   // Temporary access to getters (e.g. GetTokenService()).
   // TODO(https://crbug.com/944127): Remove this friendship by
   // extending identity_test_utils.h as needed.
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index fb4db9f9..b4c80de 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -1981,6 +1981,8 @@
     deps += [
       "//chromeos/resources",
       "//components/chromeos_camera:mojo_mjpeg_decode_accelerator",
+      "//services/data_decoder/public/cpp",
+      "//services/data_decoder/public/mojom",
     ]
   }
 
diff --git a/content/browser/DEPS b/content/browser/DEPS
index e37025d..e2e790d1 100644
--- a/content/browser/DEPS
+++ b/content/browser/DEPS
@@ -39,6 +39,7 @@
   "+content/public/app",
   "+content/public/browser",
   "+device/base/synchronization",
+  "+device/bluetooth",
   "+device/gamepad", # For gamepad API
   "+device/nfc",
   "+device/vr",  # For WebVR API
diff --git a/content/browser/background_fetch/background_fetch_context.cc b/content/browser/background_fetch/background_fetch_context.cc
index 4bf2fed..b7a41529 100644
--- a/content/browser/background_fetch/background_fetch_context.cc
+++ b/content/browser/background_fetch/background_fetch_context.cc
@@ -230,7 +230,8 @@
 
 void BackgroundFetchContext::AddRegistrationObserver(
     const std::string& unique_id,
-    blink::mojom::BackgroundFetchRegistrationObserverPtr observer) {
+    mojo::PendingRemote<blink::mojom::BackgroundFetchRegistrationObserver>
+        observer) {
   registration_notifier_->AddObserver(unique_id, std::move(observer));
 }
 
diff --git a/content/browser/background_fetch/background_fetch_context.h b/content/browser/background_fetch/background_fetch_context.h
index 47645912a..20b9cfe9 100644
--- a/content/browser/background_fetch/background_fetch_context.h
+++ b/content/browser/background_fetch/background_fetch_context.h
@@ -111,7 +111,8 @@
   // will unregister itself when the Mojo endpoint goes away.
   void AddRegistrationObserver(
       const std::string& unique_id,
-      blink::mojom::BackgroundFetchRegistrationObserverPtr observer);
+      mojo::PendingRemote<blink::mojom::BackgroundFetchRegistrationObserver>
+          observer);
 
   // Updates the title or icon of the Background Fetch identified by
   // |registration_id|. The |callback| will be invoked when the title has been
diff --git a/content/browser/background_fetch/background_fetch_registration_notifier.cc b/content/browser/background_fetch/background_fetch_registration_notifier.cc
index 5608432..3e26d6d 100644
--- a/content/browser/background_fetch/background_fetch_registration_notifier.cc
+++ b/content/browser/background_fetch/background_fetch_registration_notifier.cc
@@ -18,15 +18,18 @@
 
 void BackgroundFetchRegistrationNotifier::AddObserver(
     const std::string& unique_id,
-    blink::mojom::BackgroundFetchRegistrationObserverPtr observer) {
+    mojo::PendingRemote<blink::mojom::BackgroundFetchRegistrationObserver>
+        observer) {
   // Observe connection errors, which occur when the JavaScript object or the
   // renderer hosting them goes away. (For example through navigation.) The
   // observer gets freed together with |this|, thus the Unretained is safe.
-  observer.set_connection_error_handler(
-      base::BindOnce(&BackgroundFetchRegistrationNotifier::OnConnectionError,
-                     base::Unretained(this), unique_id, observer.get()));
+  mojo::Remote<blink::mojom::BackgroundFetchRegistrationObserver>
+      registration_observer(std::move(observer));
+  registration_observer.set_disconnect_handler(base::BindOnce(
+      &BackgroundFetchRegistrationNotifier::OnConnectionError,
+      base::Unretained(this), unique_id, registration_observer.get()));
 
-  observers_.emplace(unique_id, std::move(observer));
+  observers_.emplace(unique_id, std::move(registration_observer));
 }
 
 void BackgroundFetchRegistrationNotifier::Notify(
diff --git a/content/browser/background_fetch/background_fetch_registration_notifier.h b/content/browser/background_fetch/background_fetch_registration_notifier.h
index c974108..1bb9a01 100644
--- a/content/browser/background_fetch/background_fetch_registration_notifier.h
+++ b/content/browser/background_fetch/background_fetch_registration_notifier.h
@@ -13,6 +13,7 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "content/common/content_export.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "third_party/blink/public/mojom/background_fetch/background_fetch.mojom.h"
 
 namespace content {
@@ -29,7 +30,8 @@
   // identified by the |unique_id| progress.
   void AddObserver(
       const std::string& unique_id,
-      blink::mojom::BackgroundFetchRegistrationObserverPtr observer);
+      mojo::PendingRemote<blink::mojom::BackgroundFetchRegistrationObserver>
+          observer);
 
   // Notifies any registered observers for the |registration_data| of the
   // progress. This will cause JavaScript events to fire. Completed fetches must
@@ -68,7 +70,7 @@
 
   // Storage of observers keyed by the |unique_id| of a registration.
   std::multimap<std::string,
-                blink::mojom::BackgroundFetchRegistrationObserverPtr>
+                mojo::Remote<blink::mojom::BackgroundFetchRegistrationObserver>>
       observers_;
 
   // URLs the observers care about, indexed by the unique_id of the observer.
diff --git a/content/browser/background_fetch/background_fetch_registration_notifier_unittest.cc b/content/browser/background_fetch/background_fetch_registration_notifier_unittest.cc
index de8f5f9..a4e2e48 100644
--- a/content/browser/background_fetch/background_fetch_registration_notifier_unittest.cc
+++ b/content/browser/background_fetch/background_fetch_registration_notifier_unittest.cc
@@ -15,8 +15,6 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "content/browser/background_fetch/background_fetch_registration_service_impl.h"
 #include "content/common/background_fetch/background_fetch_types.h"
-#include "mojo/public/cpp/bindings/binding.h"
-#include "mojo/public/cpp/bindings/interface_request.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/mojom/background_fetch/background_fetch.mojom.h"
 
@@ -63,17 +61,16 @@
         blink::mojom::BackgroundFetchFailureReason::NONE;
   };
 
-  TestRegistrationObserver() : binding_(this) {}
+  TestRegistrationObserver() = default;
   ~TestRegistrationObserver() override = default;
 
   // Closes the bindings associated with this observer.
-  void Close() { binding_.Close(); }
+  void Close() { receiver_.reset(); }
 
   // Returns an InterfacePtr to this observer.
-  blink::mojom::BackgroundFetchRegistrationObserverPtr GetPtr() {
-    blink::mojom::BackgroundFetchRegistrationObserverPtr ptr;
-    binding_.Bind(mojo::MakeRequest(&ptr));
-    return ptr;
+  mojo::PendingRemote<blink::mojom::BackgroundFetchRegistrationObserver>
+  GetRemote() {
+    return receiver_.BindNewPipeAndPassRemote();
   }
 
   // Returns the vector of progress updates received by this observer.
@@ -111,7 +108,8 @@
  private:
   std::vector<ProgressUpdate> progress_updates_;
   CompletedRequests completed_requests_;
-  mojo::Binding<blink::mojom::BackgroundFetchRegistrationObserver> binding_;
+  mojo::Receiver<blink::mojom::BackgroundFetchRegistrationObserver> receiver_{
+      this};
   bool records_available_ = true;
 
   DISALLOW_COPY_AND_ASSIGN(TestRegistrationObserver);
@@ -162,7 +160,7 @@
 TEST_F(BackgroundFetchRegistrationNotifierTest, NotifySingleObserver) {
   auto observer = std::make_unique<TestRegistrationObserver>();
 
-  notifier_->AddObserver(kPrimaryUniqueId, observer->GetPtr());
+  notifier_->AddObserver(kPrimaryUniqueId, observer->GetRemote());
   ASSERT_EQ(observer->progress_updates().size(), 0u);
 
   Notify(kPrimaryUniqueId,
@@ -192,11 +190,11 @@
   auto secondary_observer = std::make_unique<TestRegistrationObserver>();
 
   for (auto& observer : primary_observers) {
-    notifier_->AddObserver(kPrimaryUniqueId, observer->GetPtr());
+    notifier_->AddObserver(kPrimaryUniqueId, observer->GetRemote());
     ASSERT_EQ(observer->progress_updates().size(), 0u);
   }
 
-  notifier_->AddObserver(kSecondaryUniqueId, secondary_observer->GetPtr());
+  notifier_->AddObserver(kSecondaryUniqueId, secondary_observer->GetRemote());
   ASSERT_EQ(secondary_observer->progress_updates().size(), 0u);
 
   // Notify the |kPrimaryUniqueId|.
@@ -227,7 +225,7 @@
        NotifyFollowingObserverInitiatedRemoval) {
   auto observer = std::make_unique<TestRegistrationObserver>();
 
-  notifier_->AddObserver(kPrimaryUniqueId, observer->GetPtr());
+  notifier_->AddObserver(kPrimaryUniqueId, observer->GetRemote());
   ASSERT_EQ(observer->progress_updates().size(), 0u);
 
   Notify(kPrimaryUniqueId,
@@ -255,7 +253,7 @@
 TEST_F(BackgroundFetchRegistrationNotifierTest, NotifyWithoutObservers) {
   auto observer = std::make_unique<TestRegistrationObserver>();
 
-  notifier_->AddObserver(kPrimaryUniqueId, observer->GetPtr());
+  notifier_->AddObserver(kPrimaryUniqueId, observer->GetRemote());
   ASSERT_EQ(observer->progress_updates().size(), 0u);
 
   Notify(kSecondaryUniqueId,
@@ -274,7 +272,7 @@
   auto observer = std::make_unique<TestRegistrationObserver>();
 
   notifier_->NoteTotalRequests(kPrimaryUniqueId, /* num_total_requests= */ 1);
-  notifier_->AddObserver(kPrimaryUniqueId, observer->GetPtr());
+  notifier_->AddObserver(kPrimaryUniqueId, observer->GetRemote());
   ASSERT_TRUE(observer->records_available());
 
   NotifyRecordsUnavailable(kPrimaryUniqueId);
@@ -284,7 +282,7 @@
 TEST_F(BackgroundFetchRegistrationNotifierTest, NotifyRequestCompleted) {
   auto observer = std::make_unique<TestRegistrationObserver>();
 
-  notifier_->AddObserver(kPrimaryUniqueId, observer->GetPtr());
+  notifier_->AddObserver(kPrimaryUniqueId, observer->GetRemote());
   notifier_->NoteTotalRequests(kPrimaryUniqueId, /* num_total_requests= */ 1);
 
   // No observed URLs. Observers shouldn't have been notified.
diff --git a/content/browser/background_fetch/background_fetch_registration_service_impl.cc b/content/browser/background_fetch/background_fetch_registration_service_impl.cc
index a6fbb38..da9e975 100644
--- a/content/browser/background_fetch/background_fetch_registration_service_impl.cc
+++ b/content/browser/background_fetch/background_fetch_registration_service_impl.cc
@@ -22,8 +22,7 @@
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/render_process_host.h"
-#include "mojo/public/cpp/bindings/interface_request.h"
-#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "mojo/public/cpp/bindings/self_owned_receiver.h"
 #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom.h"
 
 namespace content {
@@ -36,28 +35,28 @@
 }  // namespace
 
 // static
-blink::mojom::BackgroundFetchRegistrationServicePtrInfo
+mojo::PendingRemote<blink::mojom::BackgroundFetchRegistrationService>
 BackgroundFetchRegistrationServiceImpl::CreateInterfaceInfo(
     BackgroundFetchRegistrationId registration_id,
     scoped_refptr<BackgroundFetchContext> background_fetch_context) {
   DCHECK(background_fetch_context);
 
-  blink::mojom::BackgroundFetchRegistrationServicePtr mojo_interface;
+  mojo::PendingRemote<blink::mojom::BackgroundFetchRegistrationService>
+      mojo_interface;
 
-  mojo::MakeStrongBinding(
+  mojo::MakeSelfOwnedReceiver(
       base::WrapUnique(new BackgroundFetchRegistrationServiceImpl(
           std::move(registration_id), std::move(background_fetch_context))),
-      mojo::MakeRequest(&mojo_interface));
+      mojo_interface.InitWithNewPipeAndPassReceiver());
 
-  return mojo_interface.PassInterface();
+  return mojo_interface;
 }
 
 BackgroundFetchRegistrationServiceImpl::BackgroundFetchRegistrationServiceImpl(
     BackgroundFetchRegistrationId registration_id,
     scoped_refptr<BackgroundFetchContext> background_fetch_context)
     : registration_id_(std::move(registration_id)),
-      background_fetch_context_(std::move(background_fetch_context)),
-      binding_(this) {
+      background_fetch_context_(std::move(background_fetch_context)) {
   DCHECK(background_fetch_context_);
   DCHECK(!registration_id_.is_null());
 }
@@ -105,18 +104,14 @@
 }
 
 void BackgroundFetchRegistrationServiceImpl::AddRegistrationObserver(
-    blink::mojom::BackgroundFetchRegistrationObserverPtr observer) {
+    mojo::PendingRemote<blink::mojom::BackgroundFetchRegistrationObserver>
+        observer) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   background_fetch_context_->AddRegistrationObserver(
       registration_id_.unique_id(), std::move(observer));
 }
 
-void BackgroundFetchRegistrationServiceImpl::Bind(
-    blink::mojom::BackgroundFetchRegistrationServicePtr* interface_ptr) {
-  binding_.Bind(mojo::MakeRequest(interface_ptr));
-}
-
 bool BackgroundFetchRegistrationServiceImpl::ValidateTitle(
     const std::string& title) {
   if (title.empty() || title.size() > kMaxTitleLength) {
diff --git a/content/browser/background_fetch/background_fetch_registration_service_impl.h b/content/browser/background_fetch/background_fetch_registration_service_impl.h
index 87508439..9700abb 100644
--- a/content/browser/background_fetch/background_fetch_registration_service_impl.h
+++ b/content/browser/background_fetch/background_fetch_registration_service_impl.h
@@ -17,7 +17,7 @@
 class CONTENT_EXPORT BackgroundFetchRegistrationServiceImpl
     : public blink::mojom::BackgroundFetchRegistrationService {
  public:
-  static blink::mojom::BackgroundFetchRegistrationServicePtrInfo
+  static mojo::PendingRemote<blink::mojom::BackgroundFetchRegistrationService>
   CreateInterfaceInfo(
       BackgroundFetchRegistrationId registration_id,
       scoped_refptr<BackgroundFetchContext> background_fetch_context);
@@ -32,7 +32,8 @@
                 UpdateUICallback callback) override;
   void Abort(AbortCallback callback) override;
   void AddRegistrationObserver(
-      blink::mojom::BackgroundFetchRegistrationObserverPtr observer) override;
+      mojo::PendingRemote<blink::mojom::BackgroundFetchRegistrationObserver>
+          observer) override;
 
   ~BackgroundFetchRegistrationServiceImpl() override;
 
@@ -41,13 +42,12 @@
       BackgroundFetchRegistrationId registration_id,
       scoped_refptr<BackgroundFetchContext> background_fetch_context);
 
-  void Bind(blink::mojom::BackgroundFetchRegistrationServicePtr* interface_ptr);
-
   bool ValidateTitle(const std::string& title) WARN_UNUSED_RESULT;
 
   BackgroundFetchRegistrationId registration_id_;
   scoped_refptr<BackgroundFetchContext> background_fetch_context_;
-  mojo::Binding<blink::mojom::BackgroundFetchRegistrationService> binding_;
+  mojo::Receiver<blink::mojom::BackgroundFetchRegistrationService> receiver_{
+      this};
 
   DISALLOW_COPY_AND_ASSIGN(BackgroundFetchRegistrationServiceImpl);
 };
diff --git a/content/browser/blob_storage/blob_url_unittest.cc b/content/browser/blob_storage/blob_url_unittest.cc
index 31b19402..770de71 100644
--- a/content/browser/blob_storage/blob_url_unittest.cc
+++ b/content/browser/blob_storage/blob_url_unittest.cc
@@ -17,7 +17,6 @@
 #include "base/stl_util.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
-#include "content/browser/url_loader_factory_getter.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "mojo/public/cpp/system/data_pipe_utils.h"
 #include "net/base/net_errors.h"
@@ -28,16 +27,12 @@
 #include "net/http/http_response_headers.h"
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "net/url_request/url_request.h"
-#include "net/url_request/url_request_context.h"
-#include "net/url_request/url_request_job_factory_impl.h"
-#include "net/url_request/url_request_test_util.h"
 #include "services/network/test/test_url_loader_client.h"
 #include "storage/browser/blob/blob_data_builder.h"
 #include "storage/browser/blob/blob_data_handle.h"
 #include "storage/browser/blob/blob_data_snapshot.h"
 #include "storage/browser/blob/blob_impl.h"
 #include "storage/browser/blob/blob_storage_context.h"
-#include "storage/browser/blob/blob_url_request_job.h"
 #include "storage/browser/blob/blob_url_store_impl.h"
 #include "storage/browser/fileapi/file_system_context.h"
 #include "storage/browser/fileapi/file_system_operation_context.h"
@@ -50,7 +45,6 @@
 
 using storage::BlobDataSnapshot;
 using storage::BlobDataBuilder;
-using storage::BlobURLRequestJob;
 
 namespace content {
 
@@ -73,34 +67,11 @@
 const storage::FileSystemType kFileSystemType =
     storage::kFileSystemTypeTemporary;
 
-enum class RequestTestType {
-  kNetRequest,
-  kRequestFromBlobImpl
-};
-
 }  // namespace
 
-class BlobURLRequestJobTest : public testing::TestWithParam<RequestTestType> {
+class BlobURLTest : public testing::Test {
  public:
-  // A simple ProtocolHandler implementation to create BlobURLRequestJob.
-  class MockProtocolHandler
-      : public net::URLRequestJobFactory::ProtocolHandler {
-   public:
-    MockProtocolHandler(BlobURLRequestJobTest* test) : test_(test) {}
-
-    // net::URLRequestJobFactory::ProtocolHandler override.
-    net::URLRequestJob* MaybeCreateJob(
-        net::URLRequest* request,
-        net::NetworkDelegate* network_delegate) const override {
-      return new BlobURLRequestJob(request, network_delegate,
-                                   test_->GetHandleFromBuilder());
-    }
-
-   private:
-    BlobURLRequestJobTest* test_;
-  };
-
-  BlobURLRequestJobTest()
+  BlobURLTest()
       : thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP),
         blob_data_(new BlobDataBuilder("uuid")),
         blob_uuid_(blob_data_->uuid()),
@@ -126,10 +97,6 @@
     base::File::Info file_info2;
     base::GetFileInfo(temp_file2_, &file_info2);
     temp_file_modification_time2_ = file_info2.last_modified;
-
-    url_request_job_factory_.SetProtocolHandler(
-        "blob", std::make_unique<MockProtocolHandler>(this));
-    url_request_context_.set_job_factory(&url_request_job_factory_);
   }
 
   void TearDown() override {
@@ -147,7 +114,7 @@
     file_system_context_->OpenFileSystem(
         GURL(kFileSystemURLOrigin), kFileSystemType,
         storage::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
-        base::BindOnce(&BlobURLRequestJobTest::OnValidateFileSystem,
+        base::BindOnce(&BlobURLTest::OnValidateFileSystem,
                        base::Unretained(this)));
     base::RunLoop().RunUntilIdle();
     ASSERT_TRUE(file_system_root_url_.is_valid());
@@ -222,64 +189,36 @@
     request.method = method;
     request.headers = extra_headers;
 
-    switch (GetParam()) {
-      case RequestTestType::kNetRequest: {
-        std::unique_ptr<net::URLRequest> request =
-            url_request_context_.CreateRequest(url, net::DEFAULT_PRIORITY,
-                                               &url_request_delegate_,
-                                               TRAFFIC_ANNOTATION_FOR_TESTS);
-        request->set_method(method);
-        if (!extra_headers.IsEmpty())
-          request->SetExtraRequestHeaders(extra_headers);
-        request->Start();
+    storage::MockBlobRegistryDelegate delegate;
+    storage::BlobURLStoreImpl url_store(GetStorageContext(), &delegate);
 
-        base::RunLoop().Run();
-        response_ = url_request_delegate_.data_received();
-        response_headers_ = request->response_headers();
-        if (request->response_info().metadata) {
-          response_metadata_ =
-              std::string(request->response_info().metadata->data(),
-                          request->response_info().metadata->size());
-        }
+    blink::mojom::BlobPtr blob_ptr;
+    storage::BlobImpl::Create(
+        std::make_unique<storage::BlobDataHandle>(*GetHandleFromBuilder()),
+        MakeRequest(&blob_ptr));
 
-        response_error_code_ = url_request_delegate_.request_status();
-      } break;
-      case RequestTestType::kRequestFromBlobImpl: {
-        storage::MockBlobRegistryDelegate delegate;
-        storage::BlobURLStoreImpl url_store(GetStorageContext(), &delegate);
+    base::RunLoop loop;
+    url_store.Register(std::move(blob_ptr), url, loop.QuitClosure());
+    loop.Run();
 
-        blink::mojom::BlobPtr blob_ptr;
-        storage::BlobImpl::Create(
-            std::make_unique<storage::BlobDataHandle>(*GetHandleFromBuilder()),
-            MakeRequest(&blob_ptr));
+    network::mojom::URLLoaderFactoryPtr url_loader_factory;
+    url_store.ResolveAsURLLoaderFactory(url, MakeRequest(&url_loader_factory));
 
-        base::RunLoop loop;
-        url_store.Register(std::move(blob_ptr), url, loop.QuitClosure());
-        loop.Run();
+    network::mojom::URLLoaderPtr url_loader;
+    network::TestURLLoaderClient url_loader_client;
+    url_loader_factory->CreateLoaderAndStart(
+        MakeRequest(&url_loader), 0, 0, network::mojom::kURLLoadOptionNone,
+        request, url_loader_client.CreateInterfacePtr(),
+        net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS));
+    url_loader_client.RunUntilComplete();
 
-        network::mojom::URLLoaderFactoryPtr url_loader_factory;
-        url_store.ResolveAsURLLoaderFactory(url,
-                                            MakeRequest(&url_loader_factory));
-
-        network::mojom::URLLoaderPtr url_loader;
-        network::TestURLLoaderClient url_loader_client;
-        url_loader_factory->CreateLoaderAndStart(
-            MakeRequest(&url_loader), 0, 0, network::mojom::kURLLoadOptionNone,
-            request, url_loader_client.CreateInterfacePtr(),
-            net::MutableNetworkTrafficAnnotationTag(
-                TRAFFIC_ANNOTATION_FOR_TESTS));
-        url_loader_client.RunUntilComplete();
-
-        if (url_loader_client.response_body().is_valid()) {
-          EXPECT_TRUE(mojo::BlockingCopyToString(
-              url_loader_client.response_body_release(), &response_));
-        }
-        response_headers_ = url_loader_client.response_head().headers;
-        response_metadata_ = url_loader_client.cached_metadata();
-        response_error_code_ = url_loader_client.completion_status().error_code;
-
-      } break;
+    if (url_loader_client.response_body().is_valid()) {
+      EXPECT_TRUE(mojo::BlockingCopyToString(
+          url_loader_client.response_body_release(), &response_));
     }
+    response_headers_ = url_loader_client.response_head().headers;
+    response_metadata_ = url_loader_client.cached_metadata();
+    response_error_code_ = url_loader_client.completion_status().error_code;
 
     // Verify response.
     EXPECT_EQ(expected_error_code_, response_error_code_);
@@ -364,9 +303,6 @@
   std::unique_ptr<BlobDataBuilder> blob_data_;
   std::string blob_uuid_;
   std::unique_ptr<BlobDataSnapshot> blob_data_snapshot_;
-  net::URLRequestJobFactoryImpl url_request_job_factory_;
-  net::URLRequestContext url_request_context_;
-  net::TestDelegate url_request_delegate_;
   std::string response_;
   int response_error_code_;
   scoped_refptr<net::HttpResponseHeaders> response_headers_;
@@ -377,18 +313,18 @@
   std::string expected_response_;
 };
 
-TEST_P(BlobURLRequestJobTest, TestGetSimpleDataRequest) {
+TEST_F(BlobURLTest, TestGetSimpleDataRequest) {
   blob_data_->AppendData(kTestData1);
   TestSuccessNonrangeRequest(kTestData1, base::size(kTestData1) - 1);
 }
 
-TEST_P(BlobURLRequestJobTest, TestGetSimpleFileRequest) {
+TEST_F(BlobURLTest, TestGetSimpleFileRequest) {
   blob_data_->AppendFile(temp_file1_, 0, std::numeric_limits<uint64_t>::max(),
                          base::Time());
   TestSuccessNonrangeRequest(kTestFileData1, base::size(kTestFileData1) - 1);
 }
 
-TEST_P(BlobURLRequestJobTest, TestGetLargeFileRequest) {
+TEST_F(BlobURLTest, TestGetLargeFileRequest) {
   base::FilePath large_temp_file =
       temp_dir_.GetPath().AppendASCII("LargeBlob.dat");
   std::string large_data;
@@ -403,7 +339,7 @@
   TestSuccessNonrangeRequest(large_data, large_data.size());
 }
 
-TEST_P(BlobURLRequestJobTest, TestGetNonExistentFileRequest) {
+TEST_F(BlobURLTest, TestGetNonExistentFileRequest) {
   base::FilePath non_existent_file =
       temp_file1_.InsertBeforeExtension(FILE_PATH_LITERAL("-na"));
   blob_data_->AppendFile(non_existent_file, 0,
@@ -411,20 +347,20 @@
   TestErrorRequest(net::ERR_FILE_NOT_FOUND);
 }
 
-TEST_P(BlobURLRequestJobTest, TestGetChangedFileRequest) {
+TEST_F(BlobURLTest, TestGetChangedFileRequest) {
   base::Time old_time =
       temp_file_modification_time1_ - base::TimeDelta::FromSeconds(10);
   blob_data_->AppendFile(temp_file1_, 0, 3, old_time);
   TestErrorRequest(net::ERR_UPLOAD_FILE_CHANGED);
 }
 
-TEST_P(BlobURLRequestJobTest, TestGetSlicedFileRequest) {
+TEST_F(BlobURLTest, TestGetSlicedFileRequest) {
   blob_data_->AppendFile(temp_file1_, 2, 4, temp_file_modification_time1_);
   std::string result(kTestFileData1 + 2, 4);
   TestSuccessNonrangeRequest(result, 4);
 }
 
-TEST_P(BlobURLRequestJobTest, TestGetSimpleFileSystemFileRequest) {
+TEST_F(BlobURLTest, TestGetSimpleFileSystemFileRequest) {
   SetUpFileSystem();
   blob_data_->AppendFileSystemFile(temp_file_system_file1_, 0,
                                    std::numeric_limits<uint64_t>::max(),
@@ -433,7 +369,7 @@
                              base::size(kTestFileSystemFileData1) - 1);
 }
 
-TEST_P(BlobURLRequestJobTest, TestGetLargeFileSystemFileRequest) {
+TEST_F(BlobURLTest, TestGetLargeFileSystemFileRequest) {
   SetUpFileSystem();
   std::string large_data;
   large_data.reserve(kBufferSize * 5);
@@ -449,7 +385,7 @@
   TestSuccessNonrangeRequest(large_data, large_data.size());
 }
 
-TEST_P(BlobURLRequestJobTest, TestGetNonExistentFileSystemFileRequest) {
+TEST_F(BlobURLTest, TestGetNonExistentFileSystemFileRequest) {
   SetUpFileSystem();
   GURL non_existent_file = GetFileSystemURL("non-existent.dat");
   blob_data_->AppendFileSystemFile(non_existent_file, 0,
@@ -458,7 +394,7 @@
   TestErrorRequest(net::ERR_FILE_NOT_FOUND);
 }
 
-TEST_P(BlobURLRequestJobTest, TestGetInvalidFileSystemFileRequest) {
+TEST_F(BlobURLTest, TestGetInvalidFileSystemFileRequest) {
   SetUpFileSystem();
   GURL invalid_file;
   blob_data_->AppendFileSystemFile(invalid_file, 0,
@@ -467,7 +403,7 @@
   TestErrorRequest(net::ERR_FILE_NOT_FOUND);
 }
 
-TEST_P(BlobURLRequestJobTest, TestGetChangedFileSystemFileRequest) {
+TEST_F(BlobURLTest, TestGetChangedFileSystemFileRequest) {
   SetUpFileSystem();
   base::Time old_time = temp_file_system_file_modification_time1_ -
                         base::TimeDelta::FromSeconds(10);
@@ -476,7 +412,7 @@
   TestErrorRequest(net::ERR_UPLOAD_FILE_CHANGED);
 }
 
-TEST_P(BlobURLRequestJobTest, TestGetSlicedFileSystemFileRequest) {
+TEST_F(BlobURLTest, TestGetSlicedFileSystemFileRequest) {
   SetUpFileSystem();
   blob_data_->AppendFileSystemFile(temp_file_system_file1_, 2, 4,
                                    temp_file_system_file_modification_time1_,
@@ -485,7 +421,7 @@
   TestSuccessNonrangeRequest(result, 4);
 }
 
-TEST_P(BlobURLRequestJobTest, TestGetSimpleDataHandleRequest) {
+TEST_F(BlobURLTest, TestGetSimpleDataHandleRequest) {
   blob_data_->AppendReadableDataHandle(
       base::MakeRefCounted<storage::FakeBlobDataHandle>(kTestDataHandleData1,
                                                         ""));
@@ -493,14 +429,14 @@
                              base::size(kTestDataHandleData1) - 1);
 }
 
-TEST_P(BlobURLRequestJobTest, TestGetComplicatedDataFileAndDiskCacheRequest) {
+TEST_F(BlobURLTest, TestGetComplicatedDataFileAndDiskCacheRequest) {
   SetUpFileSystem();
   std::string result;
   BuildComplicatedData(&result);
   TestSuccessNonrangeRequest(result, GetTotalBlobLength());
 }
 
-TEST_P(BlobURLRequestJobTest, TestGetRangeRequest1) {
+TEST_F(BlobURLTest, TestGetRangeRequest1) {
   SetUpFileSystem();
   std::string result;
   BuildComplicatedData(&result);
@@ -521,7 +457,7 @@
   EXPECT_EQ(GetTotalBlobLength(), length);
 }
 
-TEST_P(BlobURLRequestJobTest, TestGetRangeRequest2) {
+TEST_F(BlobURLTest, TestGetRangeRequest2) {
   SetUpFileSystem();
   std::string result;
   BuildComplicatedData(&result);
@@ -543,7 +479,7 @@
   EXPECT_EQ(total, length);
 }
 
-TEST_P(BlobURLRequestJobTest, TestGetRangeRequest3) {
+TEST_F(BlobURLTest, TestGetRangeRequest3) {
   SetUpFileSystem();
   std::string result;
   BuildComplicatedData(&result);
@@ -564,7 +500,7 @@
   EXPECT_EQ(GetTotalBlobLength(), length);
 }
 
-TEST_P(BlobURLRequestJobTest, TestExtraHeaders) {
+TEST_F(BlobURLTest, TestExtraHeaders) {
   blob_data_->set_content_type(kTestContentType);
   blob_data_->set_content_disposition(kTestContentDisposition);
   blob_data_->AppendData(kTestData1);
@@ -583,7 +519,7 @@
   EXPECT_EQ(kTestContentDisposition, content_disposition);
 }
 
-TEST_P(BlobURLRequestJobTest, TestSideData) {
+TEST_F(BlobURLTest, TestSideData) {
   blob_data_->AppendReadableDataHandle(
       base::MakeRefCounted<storage::FakeBlobDataHandle>(
           kTestDataHandleData2, kTestDiskCacheSideData));
@@ -596,7 +532,7 @@
   EXPECT_EQ(std::string(kTestDiskCacheSideData), response_metadata_);
 }
 
-TEST_P(BlobURLRequestJobTest, TestZeroSizeSideData) {
+TEST_F(BlobURLTest, TestZeroSizeSideData) {
   blob_data_->AppendReadableDataHandle(
       base::MakeRefCounted<storage::FakeBlobDataHandle>(kTestDataHandleData2,
                                                         ""));
@@ -609,17 +545,10 @@
   EXPECT_TRUE(response_metadata_.empty());
 }
 
-TEST_P(BlobURLRequestJobTest, BrokenBlob) {
+TEST_F(BlobURLTest, BrokenBlob) {
   blob_handle_ = blob_context_.AddBrokenBlob(
       "uuid", "", "", storage::BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS);
   TestErrorRequest(net::ERR_FAILED);
 }
 
-// The parameter's value determines whether BlobURLLoaderFactory is used.
-INSTANTIATE_TEST_SUITE_P(
-    BlobURLRequestJobTest,
-    BlobURLRequestJobTest,
-    ::testing::Values(RequestTestType::kNetRequest,
-                      RequestTestType::kRequestFromBlobImpl));
-
 }  // namespace content
diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc
index ff21b67..5acff0fb 100644
--- a/content/browser/browser_main_loop.cc
+++ b/content/browser/browser_main_loop.cc
@@ -194,6 +194,8 @@
 #if defined(OS_CHROMEOS)
 #include "base/memory/memory_pressure_monitor_chromeos.h"
 #include "chromeos/constants/chromeos_switches.h"
+#include "device/bluetooth/bluetooth_adapter_factory.h"
+#include "services/data_decoder/public/mojom/constants.mojom.h"
 #endif
 
 #if defined(USE_GLIB)
@@ -389,6 +391,16 @@
 #endif
 }
 
+#if defined(OS_CHROMEOS)
+mojo::PendingRemote<data_decoder::mojom::BleScanParser> GetBleScanParser() {
+  mojo::PendingRemote<data_decoder::mojom::BleScanParser> ble_scan_parser;
+  GetSystemConnector()->Connect(
+      data_decoder::mojom::kServiceName,
+      ble_scan_parser.InitWithNewPipeAndPassReceiver());
+  return ble_scan_parser;
+}
+#endif  // defined(OS_CHROMEOS)
+
 }  // namespace
 
 #if defined(USE_X11)
@@ -773,7 +785,10 @@
       skia::SkiaMemoryDumpProvider::GetInstance(), "Skia", nullptr);
   base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
       sql::SqlMemoryDumpProvider::GetInstance(), "Sql", nullptr);
-#if !defined(OS_CHROMEOS)
+#if defined(OS_CHROMEOS)
+  device::BluetoothAdapterFactory::SetBleScanParserCallback(
+      base::BindRepeating(&GetBleScanParser));
+#else
   // Chrome Remote Desktop needs TransitionalURLLoaderFactoryOwner on ChromeOS.
   network::TransitionalURLLoaderFactoryOwner::DisallowUsageInProcess();
 #endif
diff --git a/content/browser/cache_storage/cache_storage_blob_to_disk_cache.cc b/content/browser/cache_storage/cache_storage_blob_to_disk_cache.cc
index d8617e1..429876d 100644
--- a/content/browser/cache_storage/cache_storage_blob_to_disk_cache.cc
+++ b/content/browser/cache_storage/cache_storage_blob_to_disk_cache.cc
@@ -12,7 +12,6 @@
 #include "net/url_request/url_request_context.h"
 #include "net/url_request/url_request_context_getter.h"
 #include "storage/browser/blob/blob_data_handle.h"
-#include "storage/browser/blob/blob_url_request_job_factory.h"
 #include "storage/common/storage_histograms.h"
 #include "third_party/blink/public/common/blob/blob_utils.h"
 
diff --git a/content/browser/cache_storage/cache_storage_blob_to_disk_cache_unittest.cc b/content/browser/cache_storage/cache_storage_blob_to_disk_cache_unittest.cc
index b5ed62a..77d8489 100644
--- a/content/browser/cache_storage/cache_storage_blob_to_disk_cache_unittest.cc
+++ b/content/browser/cache_storage/cache_storage_blob_to_disk_cache_unittest.cc
@@ -26,7 +26,6 @@
 #include "storage/browser/blob/blob_data_builder.h"
 #include "storage/browser/blob/blob_impl.h"
 #include "storage/browser/blob/blob_storage_context.h"
-#include "storage/browser/blob/blob_url_request_job_factory.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace content {
diff --git a/content/browser/cache_storage/cache_storage_cache_unittest.cc b/content/browser/cache_storage/cache_storage_cache_unittest.cc
index bcfd8a59..da50a599 100644
--- a/content/browser/cache_storage/cache_storage_cache_unittest.cc
+++ b/content/browser/cache_storage/cache_storage_cache_unittest.cc
@@ -49,7 +49,6 @@
 #include "storage/browser/blob/blob_data_snapshot.h"
 #include "storage/browser/blob/blob_impl.h"
 #include "storage/browser/blob/blob_storage_context.h"
-#include "storage/browser/blob/blob_url_request_job_factory.h"
 #include "storage/browser/quota/quota_manager_proxy.h"
 #include "storage/browser/test/fake_blob.h"
 #include "storage/browser/test/mock_quota_manager_proxy.h"
@@ -76,14 +75,6 @@
 const GURL kNoBodyUrl("http://example.com/no_body.html");
 const FetchAPIRequestHeadersMap kHeaders({{"a", "a"}, {"b", "b"}});
 
-// Returns a BlobProtocolHandler that uses |blob_storage_context|. Caller owns
-// the memory.
-std::unique_ptr<storage::BlobProtocolHandler> CreateMockBlobProtocolHandler(
-    storage::BlobStorageContext* blob_storage_context) {
-  return base::WrapUnique(
-      new storage::BlobProtocolHandler(blob_storage_context));
-}
-
 void SizeCallback(base::RunLoop* run_loop,
                   bool* callback_called,
                   int64_t* out_size,
diff --git a/content/browser/cache_storage/cache_storage_manager_unittest.cc b/content/browser/cache_storage/cache_storage_manager_unittest.cc
index d4605b2..5d7806c 100644
--- a/content/browser/cache_storage/cache_storage_manager_unittest.cc
+++ b/content/browser/cache_storage/cache_storage_manager_unittest.cc
@@ -48,7 +48,6 @@
 #include "storage/browser/blob/blob_data_handle.h"
 #include "storage/browser/blob/blob_impl.h"
 #include "storage/browser/blob/blob_storage_context.h"
-#include "storage/browser/blob/blob_url_request_job_factory.h"
 #include "storage/browser/quota/padding_key.h"
 #include "storage/browser/quota/quota_manager_proxy.h"
 #include "storage/browser/test/fake_blob.h"
diff --git a/content/browser/cache_storage/legacy/legacy_cache_storage_cache.cc b/content/browser/cache_storage/legacy/legacy_cache_storage_cache.cc
index 7e2a5d6..a5078bd 100644
--- a/content/browser/cache_storage/legacy/legacy_cache_storage_cache.cc
+++ b/content/browser/cache_storage/legacy/legacy_cache_storage_cache.cc
@@ -54,7 +54,6 @@
 #include "services/network/public/mojom/fetch_api.mojom.h"
 #include "storage/browser/blob/blob_data_handle.h"
 #include "storage/browser/blob/blob_storage_context.h"
-#include "storage/browser/blob/blob_url_request_job_factory.h"
 #include "storage/browser/quota/padding_key.h"
 #include "storage/browser/quota/quota_manager_proxy.h"
 #include "storage/common/blob_storage/blob_handle.h"
diff --git a/content/browser/download/download_manager_impl.cc b/content/browser/download/download_manager_impl.cc
index cfaae06..cde58a3 100644
--- a/content/browser/download/download_manager_impl.cc
+++ b/content/browser/download/download_manager_impl.cc
@@ -76,8 +76,6 @@
 #include "services/network/public/cpp/features.h"
 #include "services/network/public/cpp/wrapper_shared_url_loader_factory.h"
 #include "services/service_manager/public/cpp/connector.h"
-#include "storage/browser/blob/blob_url_loader_factory.h"
-#include "storage/browser/blob/blob_url_request_job_factory.h"
 
 #if defined(USE_X11)
 #include "base/nix/xdg_util.h"
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index 28ddfd0..9eae121 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -6464,7 +6464,7 @@
   // Error pages may sometimes commit a URL in the wrong process, which requires
   // an exception for the CanCommitURL checks.  This is ok as long as the origin
   // is unique.
-  bool is_permitted_error_page = false;
+  bool bypass_checks_for_error_page = false;
   if (SiteIsolationPolicy::IsErrorPageIsolationEnabled(
           frame_tree_node_->IsMainFrame())) {
     if (site_instance_->GetSiteURL() == GURL(content::kUnreachableWebDataURL)) {
@@ -6477,18 +6477,8 @@
             process, bad_message::RFH_ERROR_PROCESS_NON_ERROR_COMMIT);
         return false;
       }
-
-      // Error pages must commit in a unique origin. Terminate the renderer
-      // process if this is violated.
-      if (!validated_params->origin.opaque()) {
-        DEBUG_ALIAS_FOR_ORIGIN(origin_debug_alias, validated_params->origin);
-        bad_message::ReceivedBadMessage(
-            process, bad_message::RFH_ERROR_PROCESS_NON_UNIQUE_ORIGIN_COMMIT);
-        return false;
-      }
-
       // With error page isolation, any URL can commit in an error page process.
-      is_permitted_error_page = true;
+      bypass_checks_for_error_page = true;
     }
   } else {
     // Without error page isolation, a blocked navigation is expected to
@@ -6497,19 +6487,19 @@
     if (navigation_request &&
         navigation_request->navigation_handle()->GetNetErrorCode() ==
             net::ERR_BLOCKED_BY_CLIENT) {
-      // Since this is known to be an error page commit, verify it happened in
-      // a unique origin, terminating the renderer process otherwise.
-      if (!validated_params->origin.opaque()) {
-        DEBUG_ALIAS_FOR_ORIGIN(origin_debug_alias, validated_params->origin);
-        bad_message::ReceivedBadMessage(
-            process, bad_message::RFH_ERROR_PROCESS_NON_UNIQUE_ORIGIN_COMMIT);
-        return false;
-      }
-
-      is_permitted_error_page = true;
+      bypass_checks_for_error_page = true;
     }
   }
 
+  // Error pages must commit in a opaque origin. Terminate the renderer
+  // process if this is violated.
+  if (bypass_checks_for_error_page && !validated_params->origin.opaque()) {
+    DEBUG_ALIAS_FOR_ORIGIN(origin_debug_alias, validated_params->origin);
+    bad_message::ReceivedBadMessage(
+        process, bad_message::RFH_ERROR_PROCESS_NON_UNIQUE_ORIGIN_COMMIT);
+    return false;
+  }
+
   // file: URLs can be allowed to access any other origin, based on settings.
   bool bypass_checks_for_file_scheme = false;
   if (validated_params->origin.scheme() == url::kFileScheme) {
@@ -6518,200 +6508,34 @@
       bypass_checks_for_file_scheme = true;
   }
 
-  // Attempts to commit certain off-limits URL should be caught more strictly
-  // than our FilterURL checks.  If a renderer violates this policy, it
-  // should be killed.
-  if (!is_permitted_error_page && !bypass_checks_for_file_scheme &&
-      !CanCommitURL(validated_params->url)) {
-    VLOG(1) << "Blocked URL " << validated_params->url.spec();
-    LogRendererKillCrashKeys(GetSiteInstance()->GetSiteURL());
+  if (!bypass_checks_for_error_page && !bypass_checks_for_file_scheme) {
+    // Attempts to commit certain off-limits URL should be caught more strictly
+    // than our FilterURL checks.  If a renderer violates this policy, it
+    // should be killed.
+    if (!CanCommitURL(validated_params->url)) {
+      VLOG(1) << "Blocked URL " << validated_params->url.spec();
+      LogCannotCommitUrlCrashKeys(validated_params->url,
+                                  is_same_document_navigation,
+                                  navigation_request);
 
-    // Temporary instrumentation to debug the root cause of renderer process
-    // terminations. See https://crbug.com/931895.
-    auto bool_to_crash_key = [](bool b) { return b ? "true" : "false"; };
-    base::debug::SetCrashKeyString(
-        base::debug::AllocateCrashKeyString("is_same_document",
-                                            base::debug::CrashKeySize::Size32),
-        bool_to_crash_key(is_same_document_navigation));
-
-    base::debug::SetCrashKeyString(
-        base::debug::AllocateCrashKeyString("is_main_frame",
-                                            base::debug::CrashKeySize::Size32),
-        bool_to_crash_key(frame_tree_node_->IsMainFrame()));
-
-    base::debug::SetCrashKeyString(
-        base::debug::AllocateCrashKeyString("is_cross_process_subframe",
-                                            base::debug::CrashKeySize::Size32),
-        bool_to_crash_key(IsCrossProcessSubframe()));
-
-    base::debug::SetCrashKeyString(
-        base::debug::AllocateCrashKeyString("site_lock",
-                                            base::debug::CrashKeySize::Size256),
-        GetSiteInstance()->lock_url().spec());
-
-    base::debug::SetCrashKeyString(
-        base::debug::AllocateCrashKeyString("original_url_origin",
-                                            base::debug::CrashKeySize::Size256),
-        GetSiteInstance()->original_url().GetOrigin().spec());
-
-    base::debug::SetCrashKeyString(
-        base::debug::AllocateCrashKeyString("is_transfer_needed",
-                                            base::debug::CrashKeySize::Size32),
-        bool_to_crash_key(frame_tree_node_->render_manager()
-                              ->IsRendererTransferNeededForNavigation(
-                                  this, validated_params->url)));
-
-    base::debug::SetCrashKeyString(
-        base::debug::AllocateCrashKeyString("is_mhtml_document",
-                                            base::debug::CrashKeySize::Size32),
-        bool_to_crash_key(is_mhtml_document()));
-
-    base::debug::SetCrashKeyString(
-        base::debug::AllocateCrashKeyString("last_committed_url_origin",
-                                            base::debug::CrashKeySize::Size256),
-        GetLastCommittedURL().GetOrigin().spec());
-
-    base::debug::SetCrashKeyString(
-        base::debug::AllocateCrashKeyString("last_successful_url_origin",
-                                            base::debug::CrashKeySize::Size256),
-        last_successful_url().GetOrigin().spec());
-
-    if (navigation_request && navigation_request->navigation_handle()) {
-      NavigationHandleImpl* handle = navigation_request->navigation_handle();
-      base::debug::SetCrashKeyString(
-          base::debug::AllocateCrashKeyString(
-              "is_renderer_initiated", base::debug::CrashKeySize::Size32),
-          bool_to_crash_key(handle->IsRendererInitiated()));
-
-      base::debug::SetCrashKeyString(
-          base::debug::AllocateCrashKeyString(
-              "is_server_redirect", base::debug::CrashKeySize::Size32),
-          bool_to_crash_key(handle->WasServerRedirect()));
-
-      base::debug::SetCrashKeyString(
-          base::debug::AllocateCrashKeyString(
-              "is_form_submission", base::debug::CrashKeySize::Size32),
-          bool_to_crash_key(handle->IsFormSubmission()));
-
-      base::debug::SetCrashKeyString(
-          base::debug::AllocateCrashKeyString(
-              "is_error_page", base::debug::CrashKeySize::Size32),
-          bool_to_crash_key(handle->IsErrorPage()));
-
-      base::debug::SetCrashKeyString(
-          base::debug::AllocateCrashKeyString(
-              "from_begin_navigation", base::debug::CrashKeySize::Size32),
-          bool_to_crash_key(navigation_request->from_begin_navigation()));
-
-      base::debug::SetCrashKeyString(
-          base::debug::AllocateCrashKeyString(
-              "net_error", base::debug::CrashKeySize::Size32),
-          base::NumberToString(navigation_request->net_error()));
-
-      base::debug::SetCrashKeyString(
-          base::debug::AllocateCrashKeyString(
-              "initiator_origin", base::debug::CrashKeySize::Size64),
-          handle->GetInitiatorOrigin()
-              ? handle->GetInitiatorOrigin()->GetDebugString()
-              : "none");
-
-      base::debug::SetCrashKeyString(
-          base::debug::AllocateCrashKeyString(
-              "starting_site_instance", base::debug::CrashKeySize::Size64),
-          handle->GetStartingSiteInstance()->GetSiteURL().spec());
-
-      // Recompute the target SiteInstance to see if it matches the current one
-      // at commit time.
-      scoped_refptr<SiteInstance> dest_instance =
-          frame_tree_node_->render_manager()
-              ->GetSiteInstanceForNavigationRequest(*navigation_request);
-      base::debug::SetCrashKeyString(
-          base::debug::AllocateCrashKeyString(
-              "does_recomputed_site_instance_match",
-              base::debug::CrashKeySize::Size32),
-          bool_to_crash_key(dest_instance == GetSiteInstance()));
+      // Kills the process.
+      bad_message::ReceivedBadMessage(process,
+                                      bad_message::RFH_CAN_COMMIT_URL_BLOCKED);
+      return false;
     }
 
-    // Kills the process.
-    bad_message::ReceivedBadMessage(process,
-                                    bad_message::RFH_CAN_COMMIT_URL_BLOCKED);
-    return false;
-  }
+    // Verify that the origin passed from the renderer process is valid and can
+    // be allowed to commit in this RenderFrameHost.
+    if (!CanCommitOrigin(validated_params->origin, validated_params->url)) {
+      DEBUG_ALIAS_FOR_ORIGIN(origin_debug_alias, validated_params->origin);
+      LogCannotCommitOriginCrashKeys(is_same_document_navigation,
+                                     navigation_request);
 
-  // Verify that the origin passed from the renderer process is valid and can
-  // be allowed to commit in this RenderFrameHost.
-  if (!bypass_checks_for_file_scheme &&
-      !CanCommitOrigin(validated_params->origin, validated_params->url)) {
-    DEBUG_ALIAS_FOR_ORIGIN(origin_debug_alias, validated_params->origin);
-    LogRendererKillCrashKeys(GetSiteInstance()->GetSiteURL());
-
-    // Temporary instrumentation to debug the root cause of
-    // https://crbug.com/923144.
-    auto bool_to_crash_key = [](bool b) { return b ? "true" : "false"; };
-    base::debug::SetCrashKeyString(
-        base::debug::AllocateCrashKeyString("is_same_document",
-                                            base::debug::CrashKeySize::Size32),
-        bool_to_crash_key(is_same_document_navigation));
-
-    base::debug::SetCrashKeyString(
-        base::debug::AllocateCrashKeyString("is_subframe",
-                                            base::debug::CrashKeySize::Size32),
-        bool_to_crash_key(!frame_tree_node_->IsMainFrame()));
-
-    base::debug::SetCrashKeyString(
-        base::debug::AllocateCrashKeyString("is_active",
-                                            base::debug::CrashKeySize::Size32),
-        bool_to_crash_key(is_active()));
-
-    base::debug::SetCrashKeyString(
-        base::debug::AllocateCrashKeyString("is_current",
-                                            base::debug::CrashKeySize::Size32),
-        bool_to_crash_key(IsCurrent()));
-
-    base::debug::SetCrashKeyString(
-        base::debug::AllocateCrashKeyString("is_cross_process_subframe",
-                                            base::debug::CrashKeySize::Size32),
-        bool_to_crash_key(IsCrossProcessSubframe()));
-
-    if (navigation_request && navigation_request->navigation_handle()) {
-      NavigationHandleImpl* handle = navigation_request->navigation_handle();
-      base::debug::SetCrashKeyString(
-          base::debug::AllocateCrashKeyString(
-              "is_renderer_initiated", base::debug::CrashKeySize::Size32),
-          bool_to_crash_key(handle->IsRendererInitiated()));
-
-      base::debug::SetCrashKeyString(
-          base::debug::AllocateCrashKeyString(
-              "is_server_redirect", base::debug::CrashKeySize::Size32),
-          bool_to_crash_key(handle->WasServerRedirect()));
-
-      base::debug::SetCrashKeyString(
-          base::debug::AllocateCrashKeyString(
-              "is_form_submission", base::debug::CrashKeySize::Size32),
-          bool_to_crash_key(handle->IsFormSubmission()));
-
-      base::debug::SetCrashKeyString(
-          base::debug::AllocateCrashKeyString(
-              "is_error_page", base::debug::CrashKeySize::Size32),
-          bool_to_crash_key(handle->IsErrorPage()));
-
-      base::debug::SetCrashKeyString(
-          base::debug::AllocateCrashKeyString(
-              "initiator_origin", base::debug::CrashKeySize::Size64),
-          handle->GetInitiatorOrigin()
-              ? handle->GetInitiatorOrigin()->GetDebugString()
-              : "none");
-
-      base::debug::SetCrashKeyString(
-          base::debug::AllocateCrashKeyString(
-              "starting_site_instance", base::debug::CrashKeySize::Size64),
-          handle->GetStartingSiteInstance()->GetSiteURL().spec());
+      // Kills the process.
+      bad_message::ReceivedBadMessage(
+          process, bad_message::RFH_INVALID_ORIGIN_ON_COMMIT);
+      return false;
     }
-
-    // Kills the process.
-    bad_message::ReceivedBadMessage(process,
-                                    bad_message::RFH_INVALID_ORIGIN_ON_COMMIT);
-    return false;
   }
 
   // Without this check, an evil renderer can trick the browser into creating
@@ -7391,4 +7215,186 @@
   return true;
 }
 
+void RenderFrameHostImpl::LogCannotCommitUrlCrashKeys(
+    const GURL& url,
+    bool is_same_document_navigation,
+    NavigationRequest* navigation_request) {
+  LogRendererKillCrashKeys(GetSiteInstance()->GetSiteURL());
+
+  // Temporary instrumentation to debug the root cause of renderer process
+  // terminations. See https://crbug.com/931895.
+  auto bool_to_crash_key = [](bool b) { return b ? "true" : "false"; };
+  base::debug::SetCrashKeyString(
+      base::debug::AllocateCrashKeyString("is_same_document",
+                                          base::debug::CrashKeySize::Size32),
+      bool_to_crash_key(is_same_document_navigation));
+
+  base::debug::SetCrashKeyString(
+      base::debug::AllocateCrashKeyString("is_main_frame",
+                                          base::debug::CrashKeySize::Size32),
+      bool_to_crash_key(frame_tree_node_->IsMainFrame()));
+
+  base::debug::SetCrashKeyString(
+      base::debug::AllocateCrashKeyString("is_cross_process_subframe",
+                                          base::debug::CrashKeySize::Size32),
+      bool_to_crash_key(IsCrossProcessSubframe()));
+
+  base::debug::SetCrashKeyString(
+      base::debug::AllocateCrashKeyString("site_lock",
+                                          base::debug::CrashKeySize::Size256),
+      GetSiteInstance()->lock_url().spec());
+
+  base::debug::SetCrashKeyString(
+      base::debug::AllocateCrashKeyString("original_url_origin",
+                                          base::debug::CrashKeySize::Size256),
+      GetSiteInstance()->original_url().GetOrigin().spec());
+
+  base::debug::SetCrashKeyString(
+      base::debug::AllocateCrashKeyString("is_transfer_needed",
+                                          base::debug::CrashKeySize::Size32),
+      bool_to_crash_key(
+          frame_tree_node_->render_manager()
+              ->IsRendererTransferNeededForNavigation(this, url)));
+
+  base::debug::SetCrashKeyString(
+      base::debug::AllocateCrashKeyString("is_mhtml_document",
+                                          base::debug::CrashKeySize::Size32),
+      bool_to_crash_key(is_mhtml_document()));
+
+  base::debug::SetCrashKeyString(
+      base::debug::AllocateCrashKeyString("last_committed_url_origin",
+                                          base::debug::CrashKeySize::Size256),
+      GetLastCommittedURL().GetOrigin().spec());
+
+  base::debug::SetCrashKeyString(
+      base::debug::AllocateCrashKeyString("last_successful_url_origin",
+                                          base::debug::CrashKeySize::Size256),
+      last_successful_url().GetOrigin().spec());
+
+  if (navigation_request && navigation_request->navigation_handle()) {
+    NavigationHandleImpl* handle = navigation_request->navigation_handle();
+    base::debug::SetCrashKeyString(
+        base::debug::AllocateCrashKeyString("is_renderer_initiated",
+                                            base::debug::CrashKeySize::Size32),
+        bool_to_crash_key(handle->IsRendererInitiated()));
+
+    base::debug::SetCrashKeyString(
+        base::debug::AllocateCrashKeyString("is_server_redirect",
+                                            base::debug::CrashKeySize::Size32),
+        bool_to_crash_key(handle->WasServerRedirect()));
+
+    base::debug::SetCrashKeyString(
+        base::debug::AllocateCrashKeyString("is_form_submission",
+                                            base::debug::CrashKeySize::Size32),
+        bool_to_crash_key(handle->IsFormSubmission()));
+
+    base::debug::SetCrashKeyString(
+        base::debug::AllocateCrashKeyString("is_error_page",
+                                            base::debug::CrashKeySize::Size32),
+        bool_to_crash_key(handle->IsErrorPage()));
+
+    base::debug::SetCrashKeyString(
+        base::debug::AllocateCrashKeyString("from_begin_navigation",
+                                            base::debug::CrashKeySize::Size32),
+        bool_to_crash_key(navigation_request->from_begin_navigation()));
+
+    base::debug::SetCrashKeyString(
+        base::debug::AllocateCrashKeyString("net_error",
+                                            base::debug::CrashKeySize::Size32),
+        base::NumberToString(navigation_request->net_error()));
+
+    base::debug::SetCrashKeyString(
+        base::debug::AllocateCrashKeyString("initiator_origin",
+                                            base::debug::CrashKeySize::Size64),
+        handle->GetInitiatorOrigin()
+            ? handle->GetInitiatorOrigin()->GetDebugString()
+            : "none");
+
+    base::debug::SetCrashKeyString(
+        base::debug::AllocateCrashKeyString("starting_site_instance",
+                                            base::debug::CrashKeySize::Size64),
+        handle->GetStartingSiteInstance()->GetSiteURL().spec());
+
+    // Recompute the target SiteInstance to see if it matches the current
+    // one at commit time.
+    scoped_refptr<SiteInstance> dest_instance =
+        frame_tree_node_->render_manager()->GetSiteInstanceForNavigationRequest(
+            *navigation_request);
+    base::debug::SetCrashKeyString(
+        base::debug::AllocateCrashKeyString(
+            "does_recomputed_site_instance_match",
+            base::debug::CrashKeySize::Size32),
+        bool_to_crash_key(dest_instance == GetSiteInstance()));
+  }
+}
+
+void RenderFrameHostImpl::LogCannotCommitOriginCrashKeys(
+    bool is_same_document_navigation,
+    NavigationRequest* navigation_request) {
+  LogRendererKillCrashKeys(GetSiteInstance()->GetSiteURL());
+
+  // Temporary instrumentation to debug the root cause of
+  // https://crbug.com/923144.
+  auto bool_to_crash_key = [](bool b) { return b ? "true" : "false"; };
+  base::debug::SetCrashKeyString(
+      base::debug::AllocateCrashKeyString("is_same_document",
+                                          base::debug::CrashKeySize::Size32),
+      bool_to_crash_key(is_same_document_navigation));
+
+  base::debug::SetCrashKeyString(
+      base::debug::AllocateCrashKeyString("is_subframe",
+                                          base::debug::CrashKeySize::Size32),
+      bool_to_crash_key(!frame_tree_node_->IsMainFrame()));
+
+  base::debug::SetCrashKeyString(
+      base::debug::AllocateCrashKeyString("is_active",
+                                          base::debug::CrashKeySize::Size32),
+      bool_to_crash_key(is_active()));
+
+  base::debug::SetCrashKeyString(
+      base::debug::AllocateCrashKeyString("is_current",
+                                          base::debug::CrashKeySize::Size32),
+      bool_to_crash_key(IsCurrent()));
+
+  base::debug::SetCrashKeyString(
+      base::debug::AllocateCrashKeyString("is_cross_process_subframe",
+                                          base::debug::CrashKeySize::Size32),
+      bool_to_crash_key(IsCrossProcessSubframe()));
+
+  if (navigation_request && navigation_request->navigation_handle()) {
+    NavigationHandleImpl* handle = navigation_request->navigation_handle();
+    base::debug::SetCrashKeyString(
+        base::debug::AllocateCrashKeyString("is_renderer_initiated",
+                                            base::debug::CrashKeySize::Size32),
+        bool_to_crash_key(handle->IsRendererInitiated()));
+
+    base::debug::SetCrashKeyString(
+        base::debug::AllocateCrashKeyString("is_server_redirect",
+                                            base::debug::CrashKeySize::Size32),
+        bool_to_crash_key(handle->WasServerRedirect()));
+
+    base::debug::SetCrashKeyString(
+        base::debug::AllocateCrashKeyString("is_form_submission",
+                                            base::debug::CrashKeySize::Size32),
+        bool_to_crash_key(handle->IsFormSubmission()));
+
+    base::debug::SetCrashKeyString(
+        base::debug::AllocateCrashKeyString("is_error_page",
+                                            base::debug::CrashKeySize::Size32),
+        bool_to_crash_key(handle->IsErrorPage()));
+
+    base::debug::SetCrashKeyString(
+        base::debug::AllocateCrashKeyString("initiator_origin",
+                                            base::debug::CrashKeySize::Size64),
+        handle->GetInitiatorOrigin()
+            ? handle->GetInitiatorOrigin()->GetDebugString()
+            : "none");
+
+    base::debug::SetCrashKeyString(
+        base::debug::AllocateCrashKeyString("starting_site_instance",
+                                            base::debug::CrashKeySize::Size64),
+        handle->GetStartingSiteInstance()->GetSiteURL().spec());
+  }
+}
+
 }  // namespace content
diff --git a/content/browser/frame_host/render_frame_host_impl.h b/content/browser/frame_host/render_frame_host_impl.h
index 916f673..5b2a16e 100644
--- a/content/browser/frame_host/render_frame_host_impl.h
+++ b/content/browser/frame_host/render_frame_host_impl.h
@@ -1734,6 +1734,14 @@
       const std::string& cookie_url,
       base::circular_deque<size_t>* already_seen_url_hashes);
 
+  // Helper functions for logging crash keys when ValidateDidCommitParams()
+  // determines it cannot commit a URL or origin.
+  void LogCannotCommitUrlCrashKeys(const GURL& url,
+                                   bool is_same_document_navigation,
+                                   NavigationRequest* navigation_request);
+  void LogCannotCommitOriginCrashKeys(bool is_same_document_navigation,
+                                      NavigationRequest* navigation_request);
+
   // The RenderViewHost that this RenderFrameHost is associated with.
   //
   // It is kept alive as long as any RenderFrameHosts or RenderFrameProxyHosts
diff --git a/content/browser/loader/navigation_url_loader_impl.cc b/content/browser/loader/navigation_url_loader_impl.cc
index 9d96fb5..12586a4 100644
--- a/content/browser/loader/navigation_url_loader_impl.cc
+++ b/content/browser/loader/navigation_url_loader_impl.cc
@@ -440,7 +440,7 @@
     // Set up an interceptor for service workers.
     if (service_worker_navigation_handle_) {
       std::unique_ptr<NavigationLoaderInterceptor> service_worker_interceptor =
-          ServiceWorkerRequestHandler::CreateForNavigationUI(
+          ServiceWorkerRequestHandler::CreateForNavigation(
               resource_request_->url,
               service_worker_navigation_handle_->AsWeakPtr(), *request_info);
       // The interceptor may not be created in certain cases (e.g., the origin
@@ -1031,15 +1031,20 @@
             base::PostTask(
                 FROM_HERE, {BrowserThread::IO},
                 base::BindOnce(
-                    [](base::WeakPtr<ServiceWorkerProviderHost> host) {
+                    [](ServiceWorkerNavigationHandleCore* core) {
+                      base::WeakPtr<ServiceWorkerProviderHost> host =
+                          core->provider_host();
                       if (host) {
                         host->SetControllerRegistration(
                             nullptr, false /* notify_controllerchange */);
                         host->UpdateUrls(GURL(), GURL());
                       }
                     },
-                    service_worker_navigation_handle_->core()
-                        ->provider_host()));
+                    // Unretained() is safe because the handle owns the core,
+                    // and core gets deleted on the IO thread in a task that
+                    // must occur after this task.
+                    base::Unretained(
+                        service_worker_navigation_handle_->core())));
           }
         }
         return true;
diff --git a/content/browser/native_file_system/file_system_chooser.cc b/content/browser/native_file_system/file_system_chooser.cc
index 41a6221..55ca206 100644
--- a/content/browser/native_file_system/file_system_chooser.cc
+++ b/content/browser/native_file_system/file_system_chooser.cc
@@ -156,10 +156,8 @@
   DCHECK(isolated_context);
 
   if (type_ == blink::mojom::ChooseFileSystemEntryType::kSaveFile) {
-    // Create files if they don't yet exist.
-    // TODO(mek): If we change FileSystemFileHandle to be able to represent a
-    // file that doesn't exist on disk, we should be able to get rid of this
-    // step and make the whole API slightly more robust.
+    // Create files if they don't yet exist, and truncate files if they do
+    // exist.
     base::PostTask(
         FROM_HERE,
         {base::ThreadPool(), base::TaskPriority::USER_BLOCKING,
@@ -169,16 +167,8 @@
                scoped_refptr<base::TaskRunner> callback_runner,
                ResultCallback callback) {
               for (const auto& path : files) {
-                // Checking if a path exists, and then creating it if it doesn't
-                // is of course racy, but external applications could just as
-                // well be deleting the entire directory, or similar problematic
-                // cases, so no matter what we do there are always going to be
-                // race conditions and websites will just have to deal with any
-                // method being able to fail in unexpected ways.
-                if (base::PathExists(path))
-                  continue;
                 int creation_flags =
-                    base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_READ;
+                    base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE;
                 base::File file(path, creation_flags);
 
                 if (!file.IsValid()) {
diff --git a/content/browser/native_file_system/file_system_chooser_browsertest.cc b/content/browser/native_file_system/file_system_chooser_browsertest.cc
index 057142d..33fa3eb 100644
--- a/content/browser/native_file_system/file_system_chooser_browsertest.cc
+++ b/content/browser/native_file_system/file_system_chooser_browsertest.cc
@@ -110,10 +110,10 @@
       file_contents,
       EvalJs(shell(),
              "(async () => { const file = await self.selected_entry.getFile(); "
-             "return await new Response(file).text(); })()"));
+             "return await file.text(); })()"));
 }
 
-IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest, SaveFile) {
+IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest, SaveFile_NonExistingFile) {
   const std::string file_contents = "file contents to write";
   const base::FilePath test_file = CreateTestFile("");
   {
@@ -151,6 +151,29 @@
   }
 }
 
+IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest,
+                       SaveFile_TruncatesExistingFile) {
+  const base::FilePath test_file = CreateTestFile("Hello World");
+
+  SelectFileDialogParams dialog_params;
+  ui::SelectFileDialog::SetFactory(
+      new FakeSelectFileDialogFactory({test_file}, &dialog_params));
+  ASSERT_TRUE(
+      NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
+  EXPECT_EQ(test_file.BaseName().AsUTF8Unsafe(),
+            EvalJs(shell(),
+                   "(async () => {"
+                   "  let e = await self.chooseFileSystemEntries("
+                   "      {type: 'saveFile'});"
+                   "  self.entry = e;"
+                   "  return e.name; })()"));
+  EXPECT_EQ(ui::SelectFileDialog::SELECT_SAVEAS_FILE, dialog_params.type);
+  EXPECT_EQ("",
+            EvalJs(shell(),
+                   "(async () => { const file = await self.entry.getFile(); "
+                   "return await file.text(); })()"));
+}
+
 IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest, OpenMultipleFiles) {
   const base::FilePath test_file1 = CreateTestFile("file1");
   const base::FilePath test_file2 = CreateTestFile("file2");
diff --git a/content/browser/process_internals/BUILD.gn b/content/browser/process_internals/BUILD.gn
index bf7888e..a85f61cf 100644
--- a/content/browser/process_internals/BUILD.gn
+++ b/content/browser/process_internals/BUILD.gn
@@ -12,7 +12,4 @@
   deps = [
     "//url/mojom:url_mojom_gurl",
   ]
-
-  # TODO(https://crbug.com/968369): Change to use new names.
-  use_old_js_lite_bindings_names = true
 }
diff --git a/content/browser/resources/process/process_internals.js b/content/browser/resources/process/process_internals.js
index 797b8121..9e2b0eb 100644
--- a/content/browser/resources/process/process_internals.js
+++ b/content/browser/resources/process/process_internals.js
@@ -7,9 +7,9 @@
 
 /**
  * Reference to the backend providing all the data.
- * @type {mojom.ProcessInternalsHandlerProxy}
+ * @type {mojom.ProcessInternalsHandlerRemote}
  */
-let uiHandler = null;
+let pageHandler = null;
 
 /**
  * @param {string} id Tab id.
@@ -170,7 +170,7 @@
  * current browser profile. The result is passed to populateWebContentsTab.
  */
 function loadWebContentsInfo() {
-  uiHandler.getAllWebContentsInfo().then(populateWebContentsTab);
+  pageHandler.getAllWebContentsInfo().then(populateWebContentsTab);
 }
 
 /**
@@ -183,7 +183,7 @@
 function loadIsolatedOriginInfo() {
   // Retrieve any persistent isolated origins for the current profile. Insert
   // them into a list on the page if there is at least one such origin.
-  uiHandler.getUserTriggeredIsolatedOrigins().then((response) => {
+  pageHandler.getUserTriggeredIsolatedOrigins().then((response) => {
     const originCount = response.isolatedOrigins.length;
     if (!originCount) {
       return;
@@ -208,7 +208,7 @@
   // Retrieve global isolated origins and insert them into a separate list if
   // there is at least one such origin.  Since these origins may come from
   // multiple sources, include the source info for each origin in parens.
-  uiHandler.getGloballyIsolatedOrigins().then((response) => {
+  pageHandler.getGloballyIsolatedOrigins().then((response) => {
     const originCount = response.isolatedOrigins.length;
     if (!originCount) {
       return;
@@ -231,10 +231,10 @@
 
 document.addEventListener('DOMContentLoaded', function() {
   // Setup Mojo interface to the backend.
-  uiHandler = mojom.ProcessInternalsHandler.getProxy();
+  pageHandler = mojom.ProcessInternalsHandler.getRemote();
 
   // Get the Site Isolation mode and populate it.
-  uiHandler.getIsolationMode().then((response) => {
+  pageHandler.getIsolationMode().then((response) => {
     $('isolation-mode').innerText = response.mode;
   });
 
diff --git a/content/browser/service_worker/embedded_worker_instance.cc b/content/browser/service_worker/embedded_worker_instance.cc
index 174c92b3..31fba50 100644
--- a/content/browser/service_worker/embedded_worker_instance.cc
+++ b/content/browser/service_worker/embedded_worker_instance.cc
@@ -782,9 +782,12 @@
 
   instance_host_binding_.Bind(mojo::MakeRequest(&params->instance_host));
 
-  content_settings_ = std::make_unique<ServiceWorkerContentSettingsProxyImpl>(
-      params->script_url, context_,
-      mojo::MakeRequest(&params->content_settings_proxy));
+  content_settings_ =
+      base::SequenceBound<ServiceWorkerContentSettingsProxyImpl>(
+          base::CreateSequencedTaskRunner({BrowserThread::UI}),
+          params->script_url,
+          scoped_refptr<ServiceWorkerContextWrapper>(context_->wrapper()),
+          mojo::MakeRequest(&params->content_settings_proxy));
 
   const bool is_script_streaming = !params->installed_scripts_info.is_null();
   inflight_start_task_->set_start_worker_sent_time(base::TimeTicks::Now());
diff --git a/content/browser/service_worker/embedded_worker_instance.h b/content/browser/service_worker/embedded_worker_instance.h
index b7f8469..f58c999 100644
--- a/content/browser/service_worker/embedded_worker_instance.h
+++ b/content/browser/service_worker/embedded_worker_instance.h
@@ -18,6 +18,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
 #include "base/strings/string16.h"
+#include "base/threading/sequence_bound.h"
 #include "base/time/time.h"
 #include "base/unguessable_token.h"
 #include "content/browser/service_worker/embedded_worker_status.h"
@@ -357,7 +358,7 @@
   ServiceWorkerMetrics::StartSituation start_situation_ =
       ServiceWorkerMetrics::StartSituation::UNKNOWN;
 
-  std::unique_ptr<ServiceWorkerContentSettingsProxyImpl> content_settings_;
+  base::SequenceBound<ServiceWorkerContentSettingsProxyImpl> content_settings_;
 
   mojo::StrongBindingPtr<network::mojom::URLLoaderFactory>
       script_loader_factory_;
diff --git a/content/browser/service_worker/embedded_worker_instance_unittest.cc b/content/browser/service_worker/embedded_worker_instance_unittest.cc
index a1514df..6ed4247 100644
--- a/content/browser/service_worker/embedded_worker_instance_unittest.cc
+++ b/content/browser/service_worker/embedded_worker_instance_unittest.cc
@@ -113,9 +113,7 @@
   RegistrationAndVersionPair PrepareRegistrationAndVersion(
       const GURL& scope,
       const GURL& script_url) {
-    base::RunLoop loop;
-    context()->storage()->LazyInitializeForTest(loop.QuitClosure());
-    loop.Run();
+    context()->storage()->LazyInitializeForTest();
 
     RegistrationAndVersionPair pair;
     blink::mojom::ServiceWorkerRegistrationOptions options;
diff --git a/content/browser/service_worker/service_worker_content_settings_proxy_impl.cc b/content/browser/service_worker/service_worker_content_settings_proxy_impl.cc
index f23f068..63c81beb 100644
--- a/content/browser/service_worker/service_worker_content_settings_proxy_impl.cc
+++ b/content/browser/service_worker/service_worker_content_settings_proxy_impl.cc
@@ -18,20 +18,22 @@
 
 ServiceWorkerContentSettingsProxyImpl::ServiceWorkerContentSettingsProxyImpl(
     const GURL& script_url,
-    base::WeakPtr<ServiceWorkerContextCore> context,
+    scoped_refptr<ServiceWorkerContextWrapper> context_wrapper,
     blink::mojom::WorkerContentSettingsProxyRequest request)
     : origin_(url::Origin::Create(script_url)),
-      context_(context),
-      binding_(this, std::move(request)) {}
+      context_wrapper_(context_wrapper),
+      binding_(this, std::move(request)) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+}
 
 ServiceWorkerContentSettingsProxyImpl::
     ~ServiceWorkerContentSettingsProxyImpl() = default;
 
 void ServiceWorkerContentSettingsProxyImpl::AllowIndexedDB(
     AllowIndexedDBCallback callback) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   // May be shutting down.
-  if (!context_ || !context_->wrapper()->resource_context()) {
+  if (!context_wrapper_->browser_context()) {
     std::move(callback).Run(false);
     return;
   }
@@ -45,15 +47,14 @@
   // so just pass an empty |render_frames|.
   std::vector<GlobalFrameRoutingId> render_frames;
   std::move(callback).Run(GetContentClient()->browser()->AllowWorkerIndexedDB(
-      origin_.GetURL(), context_->wrapper()->resource_context(),
-      render_frames));
+      origin_.GetURL(), context_wrapper_->browser_context(), render_frames));
 }
 
 void ServiceWorkerContentSettingsProxyImpl::AllowCacheStorage(
     AllowCacheStorageCallback callback) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   // May be shutting down.
-  if (!context_ || !context_->wrapper()->resource_context()) {
+  if (!context_wrapper_->browser_context()) {
     std::move(callback).Run(false);
     return;
   }
@@ -68,7 +69,7 @@
   std::vector<GlobalFrameRoutingId> render_frames;
   std::move(callback).Run(
       GetContentClient()->browser()->AllowWorkerCacheStorage(
-          origin_.GetURL(), context_->wrapper()->resource_context(),
+          origin_.GetURL(), context_wrapper_->browser_context(),
           render_frames));
 }
 
diff --git a/content/browser/service_worker/service_worker_content_settings_proxy_impl.h b/content/browser/service_worker/service_worker_content_settings_proxy_impl.h
index a736928..f5d85a1e 100644
--- a/content/browser/service_worker/service_worker_content_settings_proxy_impl.h
+++ b/content/browser/service_worker/service_worker_content_settings_proxy_impl.h
@@ -5,6 +5,7 @@
 #ifndef CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_CONTENT_SETTINGS_PROXY_IMPL_H_
 #define CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_CONTENT_SETTINGS_PROXY_IMPL_H_
 
+#include "base/memory/ref_counted.h"
 #include "content/browser/service_worker/service_worker_context_core.h"
 #include "content/browser/service_worker/service_worker_context_wrapper.h"
 #include "content/common/content_export.h"
@@ -14,20 +15,20 @@
 
 namespace content {
 
-class ServiceWorkerContextCore;
+class ServiceWorkerContextWrapper;
 
 // ServiceWorkerContentSettingsProxyImpl passes content settings to its renderer
 // counterpart blink::ServiceWorkerContentSettingsProxy
 // Created on EmbeddedWorkerInstance::SendStartWorker() and connects to the
 // counterpart at the moment.
 // EmbeddedWorkerInstance owns this class, so the lifetime of this class is
-// strongly associated to it.
+// strongly associated to it. This class lives on the UI thread.
 class ServiceWorkerContentSettingsProxyImpl final
     : public blink::mojom::WorkerContentSettingsProxy {
  public:
   ServiceWorkerContentSettingsProxyImpl(
       const GURL& script_url,
-      base::WeakPtr<ServiceWorkerContextCore> context,
+      scoped_refptr<ServiceWorkerContextWrapper> context_wrapper,
       blink::mojom::WorkerContentSettingsProxyRequest request);
 
   ~ServiceWorkerContentSettingsProxyImpl() override;
@@ -41,7 +42,7 @@
  private:
 
   const url::Origin origin_;
-  base::WeakPtr<ServiceWorkerContextCore> context_;
+  scoped_refptr<ServiceWorkerContextWrapper> context_wrapper_;
   mojo::Binding<blink::mojom::WorkerContentSettingsProxy> binding_;
 };
 
diff --git a/content/browser/service_worker/service_worker_context_core.h b/content/browser/service_worker/service_worker_context_core.h
index 735bc15..1757310 100644
--- a/content/browser/service_worker/service_worker_context_core.h
+++ b/content/browser/service_worker/service_worker_context_core.h
@@ -118,6 +118,7 @@
       base::ObserverListThreadSafe<ServiceWorkerContextCoreObserver>*
           observer_list,
       ServiceWorkerContextWrapper* wrapper);
+  // TODO(https://crbug.com/877356): Remove this copy mechanism.
   ServiceWorkerContextCore(
       ServiceWorkerContextCore* old_context,
       ServiceWorkerContextWrapper* wrapper);
diff --git a/content/browser/service_worker/service_worker_context_wrapper_unittest.cc b/content/browser/service_worker/service_worker_context_wrapper_unittest.cc
index 6443cf5a..7b8b0e5 100644
--- a/content/browser/service_worker/service_worker_context_wrapper_unittest.cc
+++ b/content/browser/service_worker/service_worker_context_wrapper_unittest.cc
@@ -55,9 +55,7 @@
     // Init() posts a couple tasks to the IO thread. Let them finish.
     base::RunLoop().RunUntilIdle();
 
-    base::RunLoop loop;
-    storage()->LazyInitializeForTest(loop.QuitClosure());
-    loop.Run();
+    storage()->LazyInitializeForTest();
   }
 
   ServiceWorkerContextCore* context() { return wrapper_->context(); }
diff --git a/content/browser/service_worker/service_worker_controllee_request_handler_unittest.cc b/content/browser/service_worker/service_worker_controllee_request_handler_unittest.cc
index fe959e1..a0678b0 100644
--- a/content/browser/service_worker/service_worker_controllee_request_handler_unittest.cc
+++ b/content/browser/service_worker/service_worker_controllee_request_handler_unittest.cc
@@ -18,6 +18,7 @@
 #include "components/offline_pages/buildflags/buildflags.h"
 #include "content/browser/service_worker/embedded_worker_test_helper.h"
 #include "content/browser/service_worker/service_worker_context_core.h"
+#include "content/browser/service_worker/service_worker_context_wrapper.h"
 #include "content/browser/service_worker/service_worker_provider_host.h"
 #include "content/browser/service_worker/service_worker_registration.h"
 #include "content/browser/service_worker/service_worker_test_utils.h"
@@ -104,9 +105,7 @@
     version_ = new ServiceWorkerVersion(registration_.get(), script_url_,
                                         blink::mojom::ScriptType::kClassic, 1L,
                                         context()->AsWeakPtr());
-
-    context()->storage()->LazyInitializeForTest(base::DoNothing());
-    base::RunLoop().RunUntilIdle();
+    context()->storage()->LazyInitializeForTest();
 
     std::vector<ServiceWorkerDatabase::ResourceRecord> records;
     records.push_back(WriteToDiskCacheSync(
@@ -413,6 +412,50 @@
   EXPECT_EQ(GURL("https://host/scope/doc"), provider_host_->url());
 }
 
+// Tests interception after the context core has been destroyed and the provider
+// host is transferred to a new context.
+// TODO(crbug.com/877356): Remove this test when transferring contexts is
+// removed.
+TEST_F(ServiceWorkerControlleeRequestHandlerTest, NullContext) {
+  // Store an activated worker.
+  version_->set_fetch_handler_existence(
+      ServiceWorkerVersion::FetchHandlerExistence::EXISTS);
+  version_->SetStatus(ServiceWorkerVersion::ACTIVATED);
+  registration_->SetActiveVersion(version_);
+  base::RunLoop loop;
+  context()->storage()->StoreRegistration(
+      registration_.get(), version_.get(),
+      base::BindLambdaForTesting(
+          [&loop](blink::ServiceWorkerStatusCode status) { loop.Quit(); }));
+  loop.Run();
+
+  // Create an interceptor.
+  ServiceWorkerRequestTestResources test_resources(
+      this, GURL("https://host/scope/doc"), ResourceType::kMainFrame);
+  test_resources.SetHandler(
+      std::make_unique<ServiceWorkerControlleeRequestHandler>(
+          context()->AsWeakPtr(), provider_host_, ResourceType::kMainFrame,
+          /*skip_service_worker=*/false));
+
+  // Destroy the context and make a new one.
+  helper_->context_wrapper()->DeleteAndStartOver();
+  base::RunLoop().RunUntilIdle();
+
+  // Conduct a main resource load. The loader won't be created because the
+  // interceptor's context is now null.
+  test_resources.MaybeCreateLoader();
+  EXPECT_FALSE(test_resources.loader());
+
+  base::RunLoop().RunUntilIdle();
+
+  // Verify we did not use the worker.
+  EXPECT_FALSE(test_resources.loader());
+  EXPECT_FALSE(version_->HasControllee());
+
+  // The host should still have the correct URL.
+  EXPECT_EQ(GURL("https://host/scope/doc"), provider_host_->url());
+}
+
 #if BUILDFLAG(ENABLE_OFFLINE_PAGES)
 TEST_F(ServiceWorkerControlleeRequestHandlerTest, FallbackWithOfflineHeader) {
   version_->set_fetch_handler_existence(
diff --git a/content/browser/service_worker/service_worker_installed_scripts_sender_unittest.cc b/content/browser/service_worker/service_worker_installed_scripts_sender_unittest.cc
index 6744d0e6..c830efc 100644
--- a/content/browser/service_worker/service_worker_installed_scripts_sender_unittest.cc
+++ b/content/browser/service_worker/service_worker_installed_scripts_sender_unittest.cc
@@ -174,8 +174,7 @@
   void SetUp() override {
     helper_ = std::make_unique<EmbeddedWorkerTestHelper>(base::FilePath());
 
-    context()->storage()->LazyInitializeForTest(base::DoNothing());
-    base::RunLoop().RunUntilIdle();
+    context()->storage()->LazyInitializeForTest();
 
     scope_ = GURL("http://www.example.com/test/");
     blink::mojom::ServiceWorkerRegistrationOptions options;
diff --git a/content/browser/service_worker/service_worker_navigation_handle.h b/content/browser/service_worker/service_worker_navigation_handle.h
index 3f4cf29e..c7427eb 100644
--- a/content/browser/service_worker/service_worker_navigation_handle.h
+++ b/content/browser/service_worker/service_worker_navigation_handle.h
@@ -79,7 +79,7 @@
 
   bool has_provider_info() const { return !!provider_info_; }
 
-  ServiceWorkerNavigationHandleCore* core() const { return core_; }
+  ServiceWorkerNavigationHandleCore* core() { return core_; }
 
   const ServiceWorkerContextWrapper* context_wrapper() const {
     return context_wrapper_.get();
diff --git a/content/browser/service_worker/service_worker_navigation_handle_core.h b/content/browser/service_worker/service_worker_navigation_handle_core.h
index f3ab8705..331422b 100644
--- a/content/browser/service_worker/service_worker_navigation_handle_core.h
+++ b/content/browser/service_worker/service_worker_navigation_handle_core.h
@@ -47,9 +47,6 @@
     return context_wrapper_.get();
   }
 
-  /////////////////////////////////////////////////////////////////////////////
-  // NavigationLoaderOnUI:
-
   void set_provider_host(
       base::WeakPtr<ServiceWorkerProviderHost> provider_host) {
     provider_host_ = std::move(provider_host);
@@ -72,16 +69,11 @@
     return weak_factory_.GetWeakPtr();
   }
 
-  /////////////////////////////////////////////////////////////////////////////
-
  private:
   scoped_refptr<ServiceWorkerContextWrapper> context_wrapper_;
   base::WeakPtr<ServiceWorkerNavigationHandle> ui_handle_;
   base::WeakPtr<ServiceWorkerProviderHost> provider_host_;
-
-  // NavigationLoaderOnUI:
   std::unique_ptr<ServiceWorkerControlleeRequestHandler> interceptor_;
-
   base::WeakPtrFactory<ServiceWorkerNavigationHandleCore> weak_factory_{this};
 
   DISALLOW_COPY_AND_ASSIGN(ServiceWorkerNavigationHandleCore);
diff --git a/content/browser/service_worker/service_worker_navigation_loader_interceptor.h b/content/browser/service_worker/service_worker_navigation_loader_interceptor.h
index 461ce03..a8955a8f 100644
--- a/content/browser/service_worker/service_worker_navigation_loader_interceptor.h
+++ b/content/browser/service_worker/service_worker_navigation_loader_interceptor.h
@@ -36,9 +36,6 @@
   int process_id = ChildProcessHost::kInvalidUniqueID;
 };
 
-// This class is a work in progress for https://crbug.com/824858. It is used
-// only when NavigationLoaderOnUI is enabled.
-//
 // Handles navigations for service worker clients (windows and web workers).
 // Lives on the UI thread.
 //
diff --git a/content/browser/service_worker/service_worker_navigation_loader_unittest.cc b/content/browser/service_worker/service_worker_navigation_loader_unittest.cc
index 8e363a8..6fda1f0 100644
--- a/content/browser/service_worker/service_worker_navigation_loader_unittest.cc
+++ b/content/browser/service_worker/service_worker_navigation_loader_unittest.cc
@@ -334,8 +334,7 @@
     helper_ = std::make_unique<EmbeddedWorkerTestHelper>(base::FilePath());
 
     // Create an active service worker.
-    storage()->LazyInitializeForTest(base::DoNothing());
-    base::RunLoop().RunUntilIdle();
+    storage()->LazyInitializeForTest();
     blink::mojom::ServiceWorkerRegistrationOptions options;
     options.scope = GURL("https://example.com/");
     registration_ =
diff --git a/content/browser/service_worker/service_worker_new_script_loader_unittest.cc b/content/browser/service_worker/service_worker_new_script_loader_unittest.cc
index 7dc02c0..8d84435 100644
--- a/content/browser/service_worker/service_worker_new_script_loader_unittest.cc
+++ b/content/browser/service_worker/service_worker_new_script_loader_unittest.cc
@@ -161,7 +161,7 @@
   void SetUp() override {
     helper_ = std::make_unique<EmbeddedWorkerTestHelper>(base::FilePath());
 
-    InitializeStorage();
+    context()->storage()->LazyInitializeForTest();
 
     mock_server_->Set(GURL(kNormalScriptURL),
                       MockHTTPServer::Response(
@@ -182,12 +182,6 @@
     helper_->SetNetworkFactory(mock_url_loader_factory_.get());
   }
 
-  void InitializeStorage() {
-    base::RunLoop run_loop;
-    context()->storage()->LazyInitializeForTest(run_loop.QuitClosure());
-    run_loop.Run();
-  }
-
   // Sets up ServiceWorkerRegistration and ServiceWorkerVersion. This should be
   // called before DoRequest().
   void SetUpRegistration(const GURL& script_url) {
diff --git a/content/browser/service_worker/service_worker_object_host_unittest.cc b/content/browser/service_worker/service_worker_object_host_unittest.cc
index f3d3e77..42d503f 100644
--- a/content/browser/service_worker/service_worker_object_host_unittest.cc
+++ b/content/browser/service_worker/service_worker_object_host_unittest.cc
@@ -110,8 +110,7 @@
   }
 
   void SetUpRegistration(const GURL& scope, const GURL& script_url) {
-    helper_->context()->storage()->LazyInitializeForTest(base::DoNothing());
-    base::RunLoop().RunUntilIdle();
+    helper_->context()->storage()->LazyInitializeForTest();
 
     blink::mojom::ServiceWorkerRegistrationOptions options;
     options.scope = scope;
diff --git a/content/browser/service_worker/service_worker_provider_host.cc b/content/browser/service_worker/service_worker_provider_host.cc
index 20da7c8..75ad35ed 100644
--- a/content/browser/service_worker/service_worker_provider_host.cc
+++ b/content/browser/service_worker/service_worker_provider_host.cc
@@ -412,10 +412,13 @@
     // updating UUID since ServiceWorkerVersion has a map from uuid to provider
     // hosts.
     SetControllerRegistration(nullptr, false /* notify_controllerchange */);
+
     // Set UUID to the new one.
-    context_->UnregisterProviderHostByClientID(client_uuid_);
+    if (context_)
+      context_->UnregisterProviderHostByClientID(client_uuid_);
     client_uuid_ = base::GenerateGUID();
-    context_->RegisterProviderHostByClientID(client_uuid_, this);
+    if (context_)
+      context_->RegisterProviderHostByClientID(client_uuid_, this);
   }
 
   SyncMatchingRegistrations();
@@ -684,10 +687,11 @@
 }
 
 void ServiceWorkerProviderHost::SyncMatchingRegistrations() {
-  DCHECK(context_);
   DCHECK(!controller_registration());
 
   RemoveAllMatchingRegistrations();
+  if (!context_)
+    return;
   const auto& registrations = context_->GetLiveRegistrations();
   for (const auto& key_registration : registrations) {
     ServiceWorkerRegistration* registration = key_registration.second;
diff --git a/content/browser/service_worker/service_worker_provider_host.h b/content/browser/service_worker/service_worker_provider_host.h
index 6eabfd63..66ac098 100644
--- a/content/browser/service_worker/service_worker_provider_host.h
+++ b/content/browser/service_worker/service_worker_provider_host.h
@@ -658,6 +658,11 @@
   // service worker this is a provider for.
   scoped_refptr<ServiceWorkerVersion> running_hosted_version_;
 
+  // |context_| owns |this| but if the context is destroyed and a new one is
+  // created, the provider host becomes owned by the new context, while this
+  // |context_| is reset to null.
+  // TODO(https://crbug.com/877356): Don't support copying context, so this can
+  // just be a raw ptr that is never null.
   base::WeakPtr<ServiceWorkerContextCore> context_;
 
   // |container_| is the Mojo endpoint to the renderer-side
diff --git a/content/browser/service_worker/service_worker_registration_unittest.cc b/content/browser/service_worker/service_worker_registration_unittest.cc
index 183ff45..d433ba47 100644
--- a/content/browser/service_worker/service_worker_registration_unittest.cc
+++ b/content/browser/service_worker/service_worker_registration_unittest.cc
@@ -186,9 +186,7 @@
     helper_->context_wrapper()->set_storage_partition(
         storage_partition_impl_.get());
 
-    base::RunLoop loop;
-    context()->storage()->LazyInitializeForTest(loop.QuitClosure());
-    loop.Run();
+    context()->storage()->LazyInitializeForTest();
   }
 
   ServiceWorkerContextCore* context() { return helper_->context(); }
@@ -902,8 +900,7 @@
   }
 
   int64_t SetUpRegistration(const GURL& scope, const GURL& script_url) {
-    storage()->LazyInitializeForTest(base::DoNothing());
-    base::RunLoop().RunUntilIdle();
+    storage()->LazyInitializeForTest();
 
     // Prepare ServiceWorkerRegistration and ServiceWorkerVersion.
     scoped_refptr<ServiceWorkerRegistration> registration =
diff --git a/content/browser/service_worker/service_worker_request_handler.cc b/content/browser/service_worker/service_worker_request_handler.cc
index b51f6ae..305eb4f 100644
--- a/content/browser/service_worker/service_worker_request_handler.cc
+++ b/content/browser/service_worker/service_worker_request_handler.cc
@@ -4,17 +4,11 @@
 
 #include "content/browser/service_worker/service_worker_request_handler.h"
 
-#include <string>
 #include <utility>
 
-#include "base/command_line.h"
 #include "base/macros.h"
 #include "content/browser/frame_host/navigation_request_info.h"
-#include "content/browser/service_worker/service_worker_context_core.h"
-#include "content/browser/service_worker/service_worker_context_wrapper.h"
-#include "content/browser/service_worker/service_worker_controllee_request_handler.h"
 #include "content/browser/service_worker/service_worker_navigation_handle.h"
-#include "content/browser/service_worker/service_worker_navigation_handle_core.h"
 #include "content/browser/service_worker/service_worker_navigation_loader_interceptor.h"
 #include "content/browser/service_worker/service_worker_provider_host.h"
 #include "content/public/common/origin_util.h"
@@ -39,7 +33,7 @@
 
 // static
 std::unique_ptr<NavigationLoaderInterceptor>
-ServiceWorkerRequestHandler::CreateForNavigationUI(
+ServiceWorkerRequestHandler::CreateForNavigation(
     const GURL& url,
     base::WeakPtr<ServiceWorkerNavigationHandle> navigation_handle,
     const NavigationRequestInfo& request_info) {
@@ -66,54 +60,7 @@
 
 // static
 std::unique_ptr<NavigationLoaderInterceptor>
-ServiceWorkerRequestHandler::CreateForNavigationIO(
-    const GURL& url,
-    ServiceWorkerNavigationHandleCore* navigation_handle_core,
-    const NavigationRequestInfo& request_info,
-    base::WeakPtr<ServiceWorkerProviderHost>* out_provider_host) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  DCHECK(navigation_handle_core);
-
-  // Create the handler even for insecure HTTP since it's used in the
-  // case of redirect to HTTPS.
-  if (!url.SchemeIsHTTPOrHTTPS() && !OriginCanAccessServiceWorkers(url) &&
-      !SchemeMaySupportRedirectingToHTTPS(url)) {
-    return nullptr;
-  }
-
-  if (!navigation_handle_core->context_wrapper())
-    return nullptr;
-  ServiceWorkerContextCore* context =
-      navigation_handle_core->context_wrapper()->context();
-  if (!context)
-    return nullptr;
-
-  blink::mojom::ServiceWorkerContainerAssociatedPtrInfo client_ptr_info;
-  blink::mojom::ServiceWorkerContainerHostAssociatedRequest host_request;
-
-  auto provider_info = blink::mojom::ServiceWorkerProviderInfoForClient::New();
-  provider_info->client_request = mojo::MakeRequest(&client_ptr_info);
-  host_request = mojo::MakeRequest(&provider_info->host_ptr_info);
-
-  // Initialize the SWProviderHost.
-  *out_provider_host = ServiceWorkerProviderHost::PreCreateNavigationHost(
-      context->AsWeakPtr(), request_info.are_ancestors_secure,
-      request_info.frame_tree_node_id, std::move(host_request),
-      std::move(client_ptr_info));
-  navigation_handle_core->OnCreatedProviderHost(*out_provider_host,
-                                                std::move(provider_info));
-
-  const ResourceType resource_type = request_info.is_main_frame
-                                         ? ResourceType::kMainFrame
-                                         : ResourceType::kSubFrame;
-  return std::make_unique<ServiceWorkerControlleeRequestHandler>(
-      context->AsWeakPtr(), *out_provider_host, resource_type,
-      request_info.begin_params->skip_service_worker);
-}
-
-// static
-std::unique_ptr<NavigationLoaderInterceptor>
-ServiceWorkerRequestHandler::CreateForWorkerUI(
+ServiceWorkerRequestHandler::CreateForWorker(
     const network::ResourceRequest& resource_request,
     int process_id,
     base::WeakPtr<ServiceWorkerNavigationHandle> navigation_handle) {
@@ -141,61 +88,4 @@
       params, std::move(navigation_handle));
 }
 
-// static
-std::unique_ptr<NavigationLoaderInterceptor>
-ServiceWorkerRequestHandler::CreateForWorkerIO(
-    const network::ResourceRequest& resource_request,
-    int process_id,
-    ServiceWorkerNavigationHandleCore* navigation_handle_core) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-
-  // Create the handler even for insecure HTTP since it's used in the
-  // case of redirect to HTTPS.
-  if (!resource_request.url.SchemeIsHTTPOrHTTPS() &&
-      !OriginCanAccessServiceWorkers(resource_request.url)) {
-    return nullptr;
-  }
-
-  if (!navigation_handle_core->context_wrapper())
-    return nullptr;
-  ServiceWorkerContextCore* context =
-      navigation_handle_core->context_wrapper()->context();
-  if (!context)
-    return nullptr;
-
-  auto resource_type =
-      static_cast<ResourceType>(resource_request.resource_type);
-  auto provider_type = blink::mojom::ServiceWorkerProviderType::kUnknown;
-  switch (resource_type) {
-    case ResourceType::kWorker:
-      provider_type =
-          blink::mojom::ServiceWorkerProviderType::kForDedicatedWorker;
-      break;
-    case ResourceType::kSharedWorker:
-      provider_type = blink::mojom::ServiceWorkerProviderType::kForSharedWorker;
-      break;
-    default:
-      NOTREACHED() << resource_request.resource_type;
-      return nullptr;
-  }
-
-  blink::mojom::ServiceWorkerContainerAssociatedPtrInfo client_ptr_info;
-  blink::mojom::ServiceWorkerContainerHostAssociatedRequest host_request;
-
-  auto provider_info = blink::mojom::ServiceWorkerProviderInfoForClient::New();
-  provider_info->client_request = mojo::MakeRequest(&client_ptr_info);
-  host_request = mojo::MakeRequest(&provider_info->host_ptr_info);
-
-  // Initialize the SWProviderHost.
-  base::WeakPtr<ServiceWorkerProviderHost> host =
-      ServiceWorkerProviderHost::PreCreateForWebWorker(
-          context->AsWeakPtr(), process_id, provider_type,
-          std::move(host_request), std::move(client_ptr_info));
-  navigation_handle_core->OnCreatedProviderHost(host, std::move(provider_info));
-
-  return std::make_unique<ServiceWorkerControlleeRequestHandler>(
-      context->AsWeakPtr(), host, resource_type,
-      resource_request.skip_service_worker);
-}
-
 }  // namespace content
diff --git a/content/browser/service_worker/service_worker_request_handler.h b/content/browser/service_worker/service_worker_request_handler.h
index e2e0e82..64294ae 100644
--- a/content/browser/service_worker/service_worker_request_handler.h
+++ b/content/browser/service_worker/service_worker_request_handler.h
@@ -7,7 +7,6 @@
 
 #include <memory>
 
-#include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "content/browser/loader/navigation_loader_interceptor.h"
 #include "content/common/content_export.h"
@@ -21,8 +20,6 @@
 namespace content {
 
 class ServiceWorkerNavigationHandle;
-class ServiceWorkerNavigationHandleCore;
-class ServiceWorkerProviderHost;
 struct NavigationRequestInfo;
 
 // Contains factory methods for creating the NavigationLoaderInterceptors for
@@ -33,34 +30,18 @@
  public:
   // Returns a loader interceptor for a navigation. May return nullptr if the
   // navigation cannot use service workers. Called on the UI thread.
-  static std::unique_ptr<NavigationLoaderInterceptor> CreateForNavigationUI(
+  static std::unique_ptr<NavigationLoaderInterceptor> CreateForNavigation(
       const GURL& url,
       base::WeakPtr<ServiceWorkerNavigationHandle> navigation_handle,
       const NavigationRequestInfo& request_info);
 
-  // Returns a loader interceptor for a navigation. May return nullptr if the
-  // navigation cannot use service workers. Called on the IO thread.
-  static std::unique_ptr<NavigationLoaderInterceptor> CreateForNavigationIO(
-      const GURL& url,
-      ServiceWorkerNavigationHandleCore* navigation_handle_core,
-      const NavigationRequestInfo& request_info,
-      base::WeakPtr<ServiceWorkerProviderHost>* out_provider_host);
-
-  // Returns a loader interceptor for a dedicated worker or shared worker. May
-  // return nullptr if the worker cannot use service workers. Called on the IO
-  // thread.
-  static std::unique_ptr<NavigationLoaderInterceptor> CreateForWorkerUI(
-      const network::ResourceRequest& resource_request,
-      int process_id,
-      base::WeakPtr<ServiceWorkerNavigationHandle> navigation_handle);
-
   // Returns a loader interceptor for a dedicated worker or shared worker. May
   // return nullptr if the worker cannot use service workers. Called on the UI
   // thread.
-  static std::unique_ptr<NavigationLoaderInterceptor> CreateForWorkerIO(
+  static std::unique_ptr<NavigationLoaderInterceptor> CreateForWorker(
       const network::ResourceRequest& resource_request,
       int process_id,
-      ServiceWorkerNavigationHandleCore* navigation_handle_core);
+      base::WeakPtr<ServiceWorkerNavigationHandle> navigation_handle);
 
  private:
   DISALLOW_COPY_AND_ASSIGN(ServiceWorkerRequestHandler);
diff --git a/content/browser/service_worker/service_worker_request_handler_unittest.cc b/content/browser/service_worker/service_worker_request_handler_unittest.cc
index daff3d84..0471a5b 100644
--- a/content/browser/service_worker/service_worker_request_handler_unittest.cc
+++ b/content/browser/service_worker/service_worker_request_handler_unittest.cc
@@ -14,7 +14,7 @@
 #include "content/browser/service_worker/embedded_worker_test_helper.h"
 #include "content/browser/service_worker/service_worker_context_core.h"
 #include "content/browser/service_worker/service_worker_context_wrapper.h"
-#include "content/browser/service_worker/service_worker_navigation_handle_core.h"
+#include "content/browser/service_worker/service_worker_navigation_handle.h"
 #include "content/browser/service_worker/service_worker_provider_host.h"
 #include "content/browser/service_worker/service_worker_test_utils.h"
 #include "content/common/navigation_params.mojom.h"
@@ -52,32 +52,10 @@
   }
 
  protected:
-  static std::unique_ptr<ServiceWorkerNavigationHandleCore>
-  CreateNavigationHandleCore(ServiceWorkerContextWrapper* context_wrapper) {
-    std::unique_ptr<ServiceWorkerNavigationHandleCore> navigation_handle_core;
-    base::PostTaskAndReplyWithResult(
-        FROM_HERE, {BrowserThread::UI},
-        base::BindOnce(
-            [](ServiceWorkerContextWrapper* wrapper) {
-              return std::make_unique<ServiceWorkerNavigationHandleCore>(
-                  nullptr, wrapper);
-            },
-            base::RetainedRef(context_wrapper)),
-        base::BindOnce(
-            [](std::unique_ptr<ServiceWorkerNavigationHandleCore>* dest,
-               std::unique_ptr<ServiceWorkerNavigationHandleCore> src) {
-              *dest = std::move(src);
-            },
-            &navigation_handle_core));
-    base::RunLoop().RunUntilIdle();
-    return navigation_handle_core;
-  }
-
   void InitializeHandlerForNavigationSimpleTest(const std::string& url,
                                                 bool expected_handler_created) {
-    std::unique_ptr<ServiceWorkerNavigationHandleCore> navigation_handle_core =
-        CreateNavigationHandleCore(helper_->context_wrapper());
-    base::WeakPtr<ServiceWorkerProviderHost> service_worker_provider_host;
+    auto navigation_handle =
+        std::make_unique<ServiceWorkerNavigationHandle>(context_wrapper());
     GURL gurl(url);
     auto begin_params = mojom::BeginNavigationParams::New();
     begin_params->request_context_type =
@@ -93,9 +71,8 @@
         base::UnguessableToken::Create() /* devtools_navigation_token */,
         base::UnguessableToken::Create() /* devtools_frame_token */);
     std::unique_ptr<NavigationLoaderInterceptor> interceptor =
-        ServiceWorkerRequestHandler::CreateForNavigationIO(
-            GURL(url), navigation_handle_core.get(), request_info,
-            &service_worker_provider_host);
+        ServiceWorkerRequestHandler::CreateForNavigation(
+            gurl, navigation_handle->AsWeakPtr(), request_info);
     EXPECT_EQ(expected_handler_created, !!interceptor.get());
   }
 
diff --git a/content/browser/service_worker/service_worker_script_loader_factory_unittest.cc b/content/browser/service_worker/service_worker_script_loader_factory_unittest.cc
index 0ad7288..5fd9767 100644
--- a/content/browser/service_worker/service_worker_script_loader_factory_unittest.cc
+++ b/content/browser/service_worker/service_worker_script_loader_factory_unittest.cc
@@ -30,8 +30,7 @@
   void SetUp() override {
     helper_ = std::make_unique<EmbeddedWorkerTestHelper>(base::FilePath());
     ServiceWorkerContextCore* context = helper_->context();
-    context->storage()->LazyInitializeForTest(base::DoNothing());
-    base::RunLoop().RunUntilIdle();
+    context->storage()->LazyInitializeForTest();
 
     scope_ = GURL("https://host/scope");
     script_url_ = GURL("https://host/script.js");
diff --git a/content/browser/service_worker/service_worker_single_script_update_checker_unittest.cc b/content/browser/service_worker/service_worker_single_script_update_checker_unittest.cc
index 12b2277..5b23253 100644
--- a/content/browser/service_worker/service_worker_single_script_update_checker_unittest.cc
+++ b/content/browser/service_worker/service_worker_single_script_update_checker_unittest.cc
@@ -69,9 +69,7 @@
     feature_list_.InitAndEnableFeature(
         blink::features::kServiceWorkerImportedScriptUpdateCheck);
     helper_ = std::make_unique<EmbeddedWorkerTestHelper>(base::FilePath());
-    base::RunLoop run_loop;
-    storage()->LazyInitializeForTest(run_loop.QuitClosure());
-    run_loop.Run();
+    storage()->LazyInitializeForTest();
   }
 
   size_t TotalBytes(const std::vector<std::string>& data_chunks) {
diff --git a/content/browser/service_worker/service_worker_storage.cc b/content/browser/service_worker/service_worker_storage.cc
index 62cfa0bec..d89ce278 100644
--- a/content/browser/service_worker/service_worker_storage.cc
+++ b/content/browser/service_worker/service_worker_storage.cc
@@ -5,12 +5,14 @@
 #include "content/browser/service_worker/service_worker_storage.h"
 
 #include <stddef.h>
+
 #include <memory>
 #include <utility>
 
 #include "base/bind_helpers.h"
 #include "base/files/file_util.h"
 #include "base/memory/ptr_util.h"
+#include "base/run_loop.h"
 #include "base/sequenced_task_runner.h"
 #include "base/task/post_task.h"
 #include "base/task_runner_util.h"
@@ -1176,13 +1178,14 @@
       .Append(kDiskCacheName);
 }
 
-void ServiceWorkerStorage::LazyInitializeForTest(base::OnceClosure callback) {
-  if (state_ == STORAGE_STATE_UNINITIALIZED ||
-      state_ == STORAGE_STATE_INITIALIZING) {
-    LazyInitialize(std::move(callback));
+void ServiceWorkerStorage::LazyInitializeForTest() {
+  DCHECK_NE(state_, STORAGE_STATE_DISABLED);
+
+  if (state_ == STORAGE_STATE_INITIALIZED)
     return;
-  }
-  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, std::move(callback));
+  base::RunLoop loop;
+  LazyInitialize(loop.QuitClosure());
+  loop.Run();
 }
 
 void ServiceWorkerStorage::LazyInitialize(base::OnceClosure callback) {
diff --git a/content/browser/service_worker/service_worker_storage.h b/content/browser/service_worker/service_worker_storage.h
index 26c3847..fad0cd8 100644
--- a/content/browser/service_worker/service_worker_storage.h
+++ b/content/browser/service_worker/service_worker_storage.h
@@ -287,7 +287,7 @@
   // the uncommitted resource keys.
   void PurgeResources(const ResourceList& resources);
 
-  void LazyInitializeForTest(base::OnceClosure callback);
+  void LazyInitializeForTest();
 
  private:
   friend class service_worker_storage_unittest::ServiceWorkerStorageTest;
diff --git a/content/browser/service_worker/service_worker_storage_unittest.cc b/content/browser/service_worker/service_worker_storage_unittest.cc
index 6dec052..6fff2a3 100644
--- a/content/browser/service_worker/service_worker_storage_unittest.cc
+++ b/content/browser/service_worker/service_worker_storage_unittest.cc
@@ -360,10 +360,7 @@
     return storage()->registered_origins_;
   }
 
-  void LazyInitialize() {
-    storage()->LazyInitializeForTest(base::DoNothing());
-    base::RunLoop().RunUntilIdle();
-  }
+  void LazyInitialize() { storage()->LazyInitializeForTest(); }
 
   blink::ServiceWorkerStatusCode StoreRegistration(
       scoped_refptr<ServiceWorkerRegistration> registration,
diff --git a/content/browser/service_worker/service_worker_updated_script_loader_unittest.cc b/content/browser/service_worker/service_worker_updated_script_loader_unittest.cc
index ed157d9..7dfb8b5 100644
--- a/content/browser/service_worker/service_worker_updated_script_loader_unittest.cc
+++ b/content/browser/service_worker/service_worker_updated_script_loader_unittest.cc
@@ -83,7 +83,7 @@
 
   void SetUp() override {
     helper_ = std::make_unique<EmbeddedWorkerTestHelper>(base::FilePath());
-    InitializeStorage();
+    context()->storage()->LazyInitializeForTest();
     SetUpRegistration(kScriptURL);
 
     // Create the old script resource in storage.
@@ -91,12 +91,6 @@
                          kOldHeaders, kOldData, std::string());
   }
 
-  void InitializeStorage() {
-    base::RunLoop run_loop;
-    context()->storage()->LazyInitializeForTest(run_loop.QuitClosure());
-    run_loop.Run();
-  }
-
   // Sets up ServiceWorkerRegistration and ServiceWorkerVersion. This should be
   // called before DoRequest().
   void SetUpRegistration(const GURL& script_url) {
diff --git a/content/browser/service_worker/service_worker_version_unittest.cc b/content/browser/service_worker/service_worker_version_unittest.cc
index 45dcada..6c3cfc5c 100644
--- a/content/browser/service_worker/service_worker_version_unittest.cc
+++ b/content/browser/service_worker/service_worker_version_unittest.cc
@@ -140,8 +140,7 @@
 
   void SetUp() override {
     helper_ = GetHelper();
-    helper_->context()->storage()->LazyInitializeForTest(base::DoNothing());
-    base::RunLoop().RunUntilIdle();
+    helper_->context()->storage()->LazyInitializeForTest();
 
     scope_ = GURL("https://www.example.com/test/");
     blink::mojom::ServiceWorkerRegistrationOptions options;
diff --git a/content/browser/storage_partition_impl_map.cc b/content/browser/storage_partition_impl_map.cc
index 2d76c4b..a68b4c5 100644
--- a/content/browser/storage_partition_impl_map.cc
+++ b/content/browser/storage_partition_impl_map.cc
@@ -46,42 +46,11 @@
 #include "crypto/sha2.h"
 #include "services/network/public/cpp/features.h"
 #include "storage/browser/blob/blob_storage_context.h"
-#include "storage/browser/blob/blob_url_request_job_factory.h"
-
-using storage::FileSystemContext;
-using storage::BlobStorageContext;
 
 namespace content {
 
 namespace {
 
-// Wrapper to call ChromeBlobStorageContext::context() on the IO thread.
-class BlobProtocolHandler : public net::URLRequestJobFactory::ProtocolHandler {
- public:
-  explicit BlobProtocolHandler(ChromeBlobStorageContext* blob_storage_context)
-      : blob_storage_context_(blob_storage_context) {}
-
-  ~BlobProtocolHandler() override {}
-
-  net::URLRequestJob* MaybeCreateJob(
-      net::URLRequest* request,
-      net::NetworkDelegate* network_delegate) const override {
-    if (!blob_protocol_handler_) {
-      // Construction is deferred because 'this' is constructed on
-      // the main thread but we want blob_protocol_handler_ constructed
-      // on the IO thread.
-      blob_protocol_handler_.reset(
-          new storage::BlobProtocolHandler(blob_storage_context_->context()));
-    }
-    return blob_protocol_handler_->MaybeCreateJob(request, network_delegate);
-  }
-
- private:
-  const scoped_refptr<ChromeBlobStorageContext> blob_storage_context_;
-  mutable std::unique_ptr<storage::BlobProtocolHandler> blob_protocol_handler_;
-  DISALLOW_COPY_AND_ASSIGN(BlobProtocolHandler);
-};
-
 // These constants are used to create the directory structure under the profile
 // where renderers with a non-default storage partition keep their persistent
 // state. This will contain a set of directories that partially mirror the
diff --git a/content/browser/web_package/bundled_exchanges_url_loader_factory.cc b/content/browser/web_package/bundled_exchanges_url_loader_factory.cc
index fc14677..99790c1 100644
--- a/content/browser/web_package/bundled_exchanges_url_loader_factory.cc
+++ b/content/browser/web_package/bundled_exchanges_url_loader_factory.cc
@@ -92,10 +92,8 @@
     // TODO(crbug.com/990733): For the initial implementation, we allow only
     // net::HTTP_OK, but we should clarify acceptable status code in the spec.
     if (!response || response->response_code != net::HTTP_OK) {
-      // TODO(crbug.com/966753): Define and use
-      // net::ERR_INVALID_BUNDLED_EXCHANGES.
-      loader_client_->OnComplete(
-          network::URLLoaderCompletionStatus(net::ERR_FAILED));
+      loader_client_->OnComplete(network::URLLoaderCompletionStatus(
+          net::ERR_INVALID_BUNDLED_EXCHANGES));
       return;
     }
 
diff --git a/content/browser/web_package/bundled_exchanges_url_loader_factory_unittest.cc b/content/browser/web_package/bundled_exchanges_url_loader_factory_unittest.cc
index 0ae8b732..e6ce18c 100644
--- a/content/browser/web_package/bundled_exchanges_url_loader_factory_unittest.cc
+++ b/content/browser/web_package/bundled_exchanges_url_loader_factory_unittest.cc
@@ -134,10 +134,10 @@
     EXPECT_EQ(net::OK, test_client_.completion_status().error_code);
   }
 
-  void RunAndCheckFailure() {
+  void RunAndCheckFailure(int net_error) {
     test_client_.RunUntilComplete();
     ASSERT_TRUE(test_client_.has_received_completion());
-    EXPECT_EQ(net::ERR_FAILED, test_client_.completion_status().error_code);
+    EXPECT_EQ(net_error, test_client_.completion_status().error_code);
   }
 
   void SetFallbackFactory(
@@ -207,7 +207,7 @@
       CreateLoaderAndStart(GetPrimaryURL(), net::HttpRequestHeaders::kGetMethod,
                            /*response=*/nullptr);
 
-  RunAndCheckFailure();
+  RunAndCheckFailure(net::ERR_INVALID_BUNDLED_EXCHANGES);
 }
 
 TEST_F(BundledExchangesURLLoaderFactoryTest, CreateLoaderForPost) {
@@ -215,7 +215,7 @@
   auto loader =
       CreateLoaderAndStart(GetPrimaryURL(), "POST", /*response=*/base::nullopt);
 
-  RunAndCheckFailure();
+  RunAndCheckFailure(net::ERR_FAILED);
 }
 
 TEST_F(BundledExchangesURLLoaderFactoryTest, CreateLoaderForNotSupportedURL) {
@@ -225,7 +225,7 @@
                                      net::HttpRequestHeaders::kGetMethod,
                                      /*response=*/base::nullopt);
 
-  RunAndCheckFailure();
+  RunAndCheckFailure(net::ERR_FAILED);
 }
 
 TEST_F(BundledExchangesURLLoaderFactoryTest, CreateFallbackLoader) {
diff --git a/content/browser/webrtc/webrtc_audio_debug_recordings_browsertest.cc b/content/browser/webrtc/webrtc_audio_debug_recordings_browsertest.cc
index 8ebd2f5..f529fc0a 100644
--- a/content/browser/webrtc/webrtc_audio_debug_recordings_browsertest.cc
+++ b/content/browser/webrtc/webrtc_audio_debug_recordings_browsertest.cc
@@ -119,7 +119,7 @@
   ASSERT_TRUE(embedded_test_server()->Start());
 
   // We must navigate somewhere first so that the render process is created.
-  NavigateToURL(shell(), GURL(""));
+  EXPECT_TRUE(NavigateToURL(shell(), GURL("")));
 
   // Create a temp directory and setup base file path.
   base::FilePath temp_dir_path;
@@ -133,7 +133,7 @@
 
   // Make a call.
   GURL url(embedded_test_server()->GetURL("/media/peerconnection-call.html"));
-  NavigateToURL(shell(), url);
+  EXPECT_TRUE(NavigateToURL(shell(), url));
   ExecuteJavascriptAndWaitForOk("call({video: true, audio: true});");
   ExecuteJavascriptAndWaitForOk("hangup();");
 
@@ -205,7 +205,7 @@
   ASSERT_TRUE(embedded_test_server()->Start());
 
   // We must navigate somewhere first so that the render process is created.
-  NavigateToURL(shell(), GURL(""));
+  EXPECT_TRUE(NavigateToURL(shell(), GURL("")));
 
   // Create a temp directory and setup base file path.
   base::FilePath temp_dir_path;
@@ -220,7 +220,7 @@
 
   // Make a call.
   GURL url(embedded_test_server()->GetURL("/media/peerconnection-call.html"));
-  NavigateToURL(shell(), url);
+  EXPECT_TRUE(NavigateToURL(shell(), url));
   ExecuteJavascriptAndWaitForOk("call({video: true, audio: true});");
   ExecuteJavascriptAndWaitForOk("hangup();");
 
@@ -254,11 +254,11 @@
   ASSERT_TRUE(embedded_test_server()->Start());
 
   // We must navigate somewhere first so that the render process is created.
-  NavigateToURL(shell(), GURL(""));
+  EXPECT_TRUE(NavigateToURL(shell(), GURL("")));
 
   // Create a second window.
   Shell* shell2 = CreateBrowser();
-  NavigateToURL(shell2, GURL(""));
+  EXPECT_TRUE(NavigateToURL(shell2, GURL("")));
 
   // Create a temp directory and setup base file path.
   base::FilePath temp_dir_path;
@@ -272,8 +272,8 @@
 
   // Make the calls.
   GURL url(embedded_test_server()->GetURL("/media/peerconnection-call.html"));
-  NavigateToURL(shell(), url);
-  NavigateToURL(shell2, url);
+  EXPECT_TRUE(NavigateToURL(shell(), url));
+  EXPECT_TRUE(NavigateToURL(shell2, url));
   ExecuteJavascriptAndWaitForOk("call({video: true, audio: true});");
   std::string result;
   EXPECT_TRUE(ExecuteScriptAndExtractString(
diff --git a/content/browser/webrtc/webrtc_constraints_browsertest.cc b/content/browser/webrtc/webrtc_constraints_browsertest.cc
index 30f8ab0b..1fb7773 100644
--- a/content/browser/webrtc/webrtc_constraints_browsertest.cc
+++ b/content/browser/webrtc/webrtc_constraints_browsertest.cc
@@ -74,7 +74,7 @@
                                               user_media().min_frame_rate,
                                               user_media().max_frame_rate);
   DVLOG(1) << "Calling getUserMedia: " << call;
-  NavigateToURL(shell(), url);
+  EXPECT_TRUE(NavigateToURL(shell(), url));
   ExecuteJavascriptAndWaitForOk(call);
 }
 
diff --git a/content/browser/webrtc/webrtc_content_browsertest_base.cc b/content/browser/webrtc/webrtc_content_browsertest_base.cc
index 61672cc..1fa2f98e 100644
--- a/content/browser/webrtc/webrtc_content_browsertest_base.cc
+++ b/content/browser/webrtc/webrtc_content_browsertest_base.cc
@@ -84,7 +84,7 @@
     ASSERT_TRUE(embedded_test_server()->Start());
 
   GURL url(embedded_test_server()->GetURL(html_file));
-  NavigateToURL(shell(), url);
+  EXPECT_TRUE(NavigateToURL(shell(), url));
 
   ExecuteJavascriptAndWaitForOk(javascript);
 }
diff --git a/content/browser/webrtc/webrtc_depth_capture_browsertest.cc b/content/browser/webrtc/webrtc_depth_capture_browsertest.cc
index a82d8fc6..11e34a7 100644
--- a/content/browser/webrtc/webrtc_depth_capture_browsertest.cc
+++ b/content/browser/webrtc/webrtc_depth_capture_browsertest.cc
@@ -89,7 +89,7 @@
 
   GURL url(
       embedded_test_server()->GetURL("/media/getusermedia-depth-capture.html"));
-  NavigateToURL(shell(), url);
+  EXPECT_TRUE(NavigateToURL(shell(), url));
 
   ExecuteJavascriptAndWaitForOk(base::StringPrintf(
       "%s({video: true});", kGetDepthStreamAndCallCreateImageBitmap));
@@ -101,7 +101,7 @@
 
   GURL url(
       embedded_test_server()->GetURL("/media/getusermedia-depth-capture.html"));
-  NavigateToURL(shell(), url);
+  EXPECT_TRUE(NavigateToURL(shell(), url));
 
   ExecuteJavascriptAndWaitForOk(
       base::StringPrintf("%s({video: true});", kGetStreamsByVideoKind));
@@ -113,7 +113,7 @@
 
   GURL url(
       embedded_test_server()->GetURL("/media/getusermedia-depth-capture.html"));
-  NavigateToURL(shell(), url);
+  EXPECT_TRUE(NavigateToURL(shell(), url));
 
   ExecuteJavascriptAndWaitForOk(
       base::StringPrintf("%s({video: true});", kGetStreamsByVideoKindNoDepth));
diff --git a/content/browser/webrtc/webrtc_getusermedia_browsertest.cc b/content/browser/webrtc/webrtc_getusermedia_browsertest.cc
index 10ab3d4..0f66441 100644
--- a/content/browser/webrtc/webrtc_getusermedia_browsertest.cc
+++ b/content/browser/webrtc/webrtc_getusermedia_browsertest.cc
@@ -130,7 +130,7 @@
     ASSERT_TRUE(embedded_test_server()->Start());
 
     GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
-    NavigateToURL(shell(), url);
+    EXPECT_TRUE(NavigateToURL(shell(), url));
 
     std::string command = "twoGetUserMedia(" + constraints1 + ',' +
         constraints2 + ')';
@@ -141,7 +141,7 @@
   void GetInputDevices(std::vector<std::string>* audio_ids,
                        std::vector<std::string>* video_ids) {
     GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
-    NavigateToURL(shell(), url);
+    EXPECT_TRUE(NavigateToURL(shell(), url));
 
     std::string devices_as_json = ExecuteJavascriptAndReturnResult(
         "getSources()");
@@ -198,7 +198,7 @@
   ASSERT_TRUE(embedded_test_server()->Start());
 
   GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
-  NavigateToURL(shell(), url);
+  EXPECT_TRUE(NavigateToURL(shell(), url));
 
   ExecuteJavascriptAndWaitForOk(
       base::StringPrintf("%s({video: true});", kGetUserMediaAndStop));
@@ -217,7 +217,7 @@
   ASSERT_TRUE(embedded_test_server()->Start());
 
   GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
-  NavigateToURL(shell(), url);
+  EXPECT_TRUE(NavigateToURL(shell(), url));
 
   ExecuteJavascriptAndWaitForOk(
       base::StringPrintf("%s({video: true});",
@@ -229,8 +229,7 @@
   ASSERT_TRUE(embedded_test_server()->Start());
 
   GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
-  NavigateToURL(shell(), url);
-
+  EXPECT_TRUE(NavigateToURL(shell(), url));
 
   ExecuteJavascriptAndWaitForOk(
       base::StringPrintf("%s({video: true});",
@@ -242,7 +241,7 @@
   ASSERT_TRUE(embedded_test_server()->Start());
 
   GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
-  NavigateToURL(shell(), url);
+  EXPECT_TRUE(NavigateToURL(shell(), url));
 
   ExecuteJavascriptAndWaitForOk(
       base::StringPrintf("%s({video: true});",
@@ -254,7 +253,7 @@
   ASSERT_TRUE(embedded_test_server()->Start());
 
   GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
-  NavigateToURL(shell(), url);
+  EXPECT_TRUE(NavigateToURL(shell(), url));
 
   ExecuteJavascriptAndWaitForOk(
       base::StringPrintf("%s({video: true});",
@@ -266,7 +265,7 @@
   ASSERT_TRUE(embedded_test_server()->Start());
 
   GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
-  NavigateToURL(shell(), url);
+  EXPECT_TRUE(NavigateToURL(shell(), url));
 
   ExecuteJavascriptAndWaitForOk(base::StringPrintf(
       "%s({video: true, audio: true});", kGetUserMediaAndStop));
@@ -277,7 +276,7 @@
   ASSERT_TRUE(embedded_test_server()->Start());
 
   GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
-  NavigateToURL(shell(), url);
+  EXPECT_TRUE(NavigateToURL(shell(), url));
 
   ExecuteJavascriptAndWaitForOk("getUserMediaAndClone();");
 }
@@ -288,7 +287,7 @@
   ASSERT_TRUE(embedded_test_server()->Start());
 
   GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
-  NavigateToURL(shell(), url);
+  EXPECT_TRUE(NavigateToURL(shell(), url));
 
   ExecuteJavascriptAndWaitForOk("getUserMediaAndRenderInSeveralVideoTags();");
 }
@@ -310,7 +309,7 @@
   GetInputDevices(&audio_ids, &video_ids);
 
   GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
-  NavigateToURL(shell(), url);
+  EXPECT_TRUE(NavigateToURL(shell(), url));
 
   // Test all combinations of mandatory sourceID;
   for (std::vector<std::string>::const_iterator video_it = video_ids.begin();
@@ -338,7 +337,7 @@
   GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
 
   // Test with invalid mandatory audio sourceID.
-  NavigateToURL(shell(), url);
+  EXPECT_TRUE(NavigateToURL(shell(), url));
   EXPECT_EQ("OverconstrainedError",
             ExecuteJavascriptAndReturnResult(
                 GenerateGetUserMediaWithMandatorySourceID(
@@ -370,7 +369,7 @@
   GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
 
   // Test with invalid optional audio sourceID.
-  NavigateToURL(shell(), url);
+  EXPECT_TRUE(NavigateToURL(shell(), url));
   EXPECT_EQ(kOK, ExecuteJavascriptAndReturnResult(
       GenerateGetUserMediaWithOptionalSourceID(
           kGetUserMediaAndStop,
@@ -396,7 +395,7 @@
   ASSERT_TRUE(embedded_test_server()->Start());
 
   GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
-  NavigateToURL(shell(), url);
+  EXPECT_TRUE(NavigateToURL(shell(), url));
 
   ExecuteJavascriptAndWaitForOk(
       "twoGetUserMediaAndStop({video: true, audio: true});");
@@ -465,7 +464,7 @@
                                               large_value,
                                               large_value,
                                               large_value);
-  NavigateToURL(shell(), url);
+  EXPECT_TRUE(NavigateToURL(shell(), url));
 
   EXPECT_EQ("OverconstrainedError", ExecuteJavascriptAndReturnResult(call));
 }
@@ -475,7 +474,7 @@
   ASSERT_TRUE(embedded_test_server()->Start());
 
   GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
-  NavigateToURL(shell(), url);
+  EXPECT_TRUE(NavigateToURL(shell(), url));
 
   // Make sure we'll fail creating the audio stream.
   base::CommandLine::ForCurrentProcess()->AppendSwitch(
@@ -495,7 +494,7 @@
   ASSERT_TRUE(embedded_test_server()->Start());
 
   GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
-  NavigateToURL(shell(), url);
+  EXPECT_TRUE(NavigateToURL(shell(), url));
 
   int large_value = 99999;
   const std::string gum_with_impossible_constraints =
@@ -535,7 +534,7 @@
   std::string constraints_4_3 = GenerateGetUserMediaCall(
       kGetUserMediaAndAnalyseAndStop, 640, 640, 480, 480, 10, 30);
 
-  NavigateToURL(shell(), url);
+  EXPECT_TRUE(NavigateToURL(shell(), url));
   ASSERT_EQ("w=640:h=480",
             ExecuteJavascriptAndReturnResult(constraints_4_3));
 }
@@ -551,7 +550,7 @@
   std::string constraints_16_9 = GenerateGetUserMediaCall(
       kGetUserMediaAndAnalyseAndStop, 640, 640, 360, 360, 10, 30);
 
-  NavigateToURL(shell(), url);
+  EXPECT_TRUE(NavigateToURL(shell(), url));
   ASSERT_EQ("w=640:h=360",
             ExecuteJavascriptAndReturnResult(constraints_16_9));
 }
@@ -566,7 +565,7 @@
   std::string constraints_1_1 = GenerateGetUserMediaCall(
       kGetUserMediaAndAnalyseAndStop, 320, 320, 320, 320, 10, 30);
 
-  NavigateToURL(shell(), url);
+  EXPECT_TRUE(NavigateToURL(shell(), url));
   ASSERT_EQ("w=320:h=320",
             ExecuteJavascriptAndReturnResult(constraints_1_1));
 }
@@ -579,7 +578,7 @@
   ASSERT_TRUE(embedded_test_server()->Start());
 
   GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
-  NavigateToURL(shell(), url);
+  EXPECT_TRUE(NavigateToURL(shell(), url));
 
   std::string call =
       "getUserMediaInIframeAndCloseInSuccessCb({audio: true});";
@@ -592,7 +591,7 @@
   ASSERT_TRUE(embedded_test_server()->Start());
 
   GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
-  NavigateToURL(shell(), url);
+  EXPECT_TRUE(NavigateToURL(shell(), url));
 
   std::string call =
       "getUserMediaInIframeAndCloseInSuccessCb({video: true});";
@@ -616,7 +615,7 @@
                                large_value,
                                large_value,
                                large_value);
-  NavigateToURL(shell(), url);
+  EXPECT_TRUE(NavigateToURL(shell(), url));
 
   ExecuteJavascriptAndWaitForOk(call);
 }
@@ -630,7 +629,7 @@
   std::string call =
       GenerateGetUserMediaWithMandatorySourceID(
           "getUserMediaInIframeAndCloseInFailureCb", "invalid", "invalid");
-  NavigateToURL(shell(), url);
+  EXPECT_TRUE(NavigateToURL(shell(), url));
 
   ExecuteJavascriptAndWaitForOk(call);
 }
@@ -642,7 +641,7 @@
   ASSERT_TRUE(embedded_test_server()->Start());
 
   GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
-  NavigateToURL(shell(), url);
+  EXPECT_TRUE(NavigateToURL(shell(), url));
 
   MediaStreamManager* manager =
       BrowserMainLoop::GetInstance()->media_stream_manager();
@@ -666,7 +665,7 @@
 IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest, GetAudioSettingsDefault) {
   ASSERT_TRUE(embedded_test_server()->Start());
   GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
-  NavigateToURL(shell(), url);
+  EXPECT_TRUE(NavigateToURL(shell(), url));
   ExecuteJavascriptAndWaitForOk("getAudioSettingsDefault()");
 }
 
@@ -674,7 +673,7 @@
                        GetAudioSettingsNoEchoCancellation) {
   ASSERT_TRUE(embedded_test_server()->Start());
   GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
-  NavigateToURL(shell(), url);
+  EXPECT_TRUE(NavigateToURL(shell(), url));
   ExecuteJavascriptAndWaitForOk("getAudioSettingsNoEchoCancellation()");
 }
 
@@ -682,14 +681,14 @@
                        GetAudioSettingsDeviceId) {
   ASSERT_TRUE(embedded_test_server()->Start());
   GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
-  NavigateToURL(shell(), url);
+  EXPECT_TRUE(NavigateToURL(shell(), url));
   ExecuteJavascriptAndWaitForOk("getAudioSettingsDeviceId()");
 }
 
 IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest, SrcObjectAddVideoTrack) {
   ASSERT_TRUE(embedded_test_server()->Start());
   GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
-  NavigateToURL(shell(), url);
+  EXPECT_TRUE(NavigateToURL(shell(), url));
   ExecuteJavascriptAndWaitForOk("srcObjectAddVideoTrack()");
 }
 
@@ -698,7 +697,7 @@
                        DISABLED_SrcObjectReplaceInactiveTracks) {
   ASSERT_TRUE(embedded_test_server()->Start());
   GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
-  NavigateToURL(shell(), url);
+  EXPECT_TRUE(NavigateToURL(shell(), url));
   ExecuteJavascriptAndWaitForOk("srcObjectReplaceInactiveTracks()");
 }
 
@@ -707,7 +706,7 @@
                        DISABLED_SrcObjectRemoveVideoTrack) {
   ASSERT_TRUE(embedded_test_server()->Start());
   GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
-  NavigateToURL(shell(), url);
+  EXPECT_TRUE(NavigateToURL(shell(), url));
   ExecuteJavascriptAndWaitForOk("srcObjectRemoveVideoTrack()");
 }
 
@@ -716,7 +715,7 @@
                        DISABLED_SrcObjectRemoveFirstOfTwoVideoTracks) {
   ASSERT_TRUE(embedded_test_server()->Start());
   GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
-  NavigateToURL(shell(), url);
+  EXPECT_TRUE(NavigateToURL(shell(), url));
   ExecuteJavascriptAndWaitForOk("srcObjectRemoveFirstOfTwoVideoTracks()");
 }
 
@@ -728,14 +727,14 @@
                        SrcObjectReassignSameObject) {
   ASSERT_TRUE(embedded_test_server()->Start());
   GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
-  NavigateToURL(shell(), url);
+  EXPECT_TRUE(NavigateToURL(shell(), url));
   ExecuteJavascriptAndWaitForOk("srcObjectReassignSameObject()");
 }
 
 IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest, ApplyConstraintsVideo) {
   ASSERT_TRUE(embedded_test_server()->Start());
   GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
-  NavigateToURL(shell(), url);
+  EXPECT_TRUE(NavigateToURL(shell(), url));
   ExecuteJavascriptAndWaitForOk("applyConstraintsVideo()");
 }
 
@@ -743,7 +742,7 @@
                        ApplyConstraintsVideoTwoStreams) {
   ASSERT_TRUE(embedded_test_server()->Start());
   GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
-  NavigateToURL(shell(), url);
+  EXPECT_TRUE(NavigateToURL(shell(), url));
   ExecuteJavascriptAndWaitForOk("applyConstraintsVideoTwoStreams()");
 }
 
@@ -751,7 +750,7 @@
                        ApplyConstraintsVideoOverconstrained) {
   ASSERT_TRUE(embedded_test_server()->Start());
   GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
-  NavigateToURL(shell(), url);
+  EXPECT_TRUE(NavigateToURL(shell(), url));
   ExecuteJavascriptAndWaitForOk("applyConstraintsVideoOverconstrained()");
 }
 
@@ -766,7 +765,7 @@
                        MAYBE_ApplyConstraintsNonDevice) {
   ASSERT_TRUE(embedded_test_server()->Start());
   GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
-  NavigateToURL(shell(), url);
+  EXPECT_TRUE(NavigateToURL(shell(), url));
   ExecuteJavascriptAndWaitForOk("applyConstraintsNonDevice()");
 }
 
@@ -774,7 +773,7 @@
                        ConcurrentGetUserMediaStop) {
   ASSERT_TRUE(embedded_test_server()->Start());
   GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
-  NavigateToURL(shell(), url);
+  EXPECT_TRUE(NavigateToURL(shell(), url));
   ExecuteJavascriptAndWaitForOk("concurrentGetUserMediaStop()");
 }
 
@@ -782,7 +781,7 @@
                        GetUserMediaAfterStopElementCapture) {
   ASSERT_TRUE(embedded_test_server()->Start());
   GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
-  NavigateToURL(shell(), url);
+  EXPECT_TRUE(NavigateToURL(shell(), url));
   ExecuteJavascriptAndWaitForOk("getUserMediaAfterStopCanvasCapture()");
 }
 
@@ -790,7 +789,7 @@
                        GetUserMediaEchoCancellationOnAndOff) {
   ASSERT_TRUE(embedded_test_server()->Start());
   GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
-  NavigateToURL(shell(), url);
+  EXPECT_TRUE(NavigateToURL(shell(), url));
   ExecuteJavascriptAndWaitForOk("getUserMediaEchoCancellationOnAndOff()");
 }
 
@@ -804,7 +803,7 @@
   ASSERT_TRUE(embedded_test_server()->Start());
 
   GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
-  NavigateToURL(shell(), url);
+  EXPECT_TRUE(NavigateToURL(shell(), url));
 
   // Expect stream to initially not be muted
   media::FakeAudioInputStream::SetGlobalMutedState(false);
@@ -833,7 +832,7 @@
   ASSERT_TRUE(embedded_test_server()->Start());
 
   GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
-  NavigateToURL(shell(), url);
+  EXPECT_TRUE(NavigateToURL(shell(), url));
 
   // Expect stream to initially be muted
   media::FakeAudioInputStream::SetGlobalMutedState(true);
@@ -862,7 +861,7 @@
 
   ASSERT_TRUE(embedded_test_server()->Start());
   GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
-  NavigateToURL(shell(), url);
+  EXPECT_TRUE(NavigateToURL(shell(), url));
 
   ExecuteJavascriptAndWaitForOk("setUpForAudioServiceCrash()");
 
@@ -887,7 +886,7 @@
                        MAYBE_GetUserMediaCloneAndApplyConstraints) {
   ASSERT_TRUE(embedded_test_server()->Start());
   GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
-  NavigateToURL(shell(), url);
+  EXPECT_TRUE(NavigateToURL(shell(), url));
   ExecuteJavascriptAndWaitForOk("getUserMediaCloneAndApplyConstraints()");
 }
 
diff --git a/content/browser/webrtc/webrtc_image_capture_browsertest.cc b/content/browser/webrtc/webrtc_image_capture_browsertest.cc
index 1946783..2dc1aee 100644
--- a/content/browser/webrtc/webrtc_image_capture_browsertest.cc
+++ b/content/browser/webrtc/webrtc_image_capture_browsertest.cc
@@ -118,7 +118,7 @@
 #endif
 
     GURL url(embedded_test_server()->GetURL(kImageCaptureHtmlFile));
-    NavigateToURL(shell(), url);
+    EXPECT_TRUE(NavigateToURL(shell(), url));
 
     if (!IsWebcamAvailableOnSystem(shell()->web_contents())) {
       DVLOG(1) << "No video device; skipping test...";
diff --git a/content/browser/webrtc/webrtc_internals_browsertest.cc b/content/browser/webrtc/webrtc_internals_browsertest.cc
index a8d2d8ae..d4bd38f0 100644
--- a/content/browser/webrtc/webrtc_internals_browsertest.cc
+++ b/content/browser/webrtc/webrtc_internals_browsertest.cc
@@ -503,7 +503,7 @@
 IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcInternalsBrowserTest,
                        AddAndRemovePeerConnection) {
   GURL url("chrome://webrtc-internals");
-  NavigateToURL(shell(), url);
+  EXPECT_TRUE(NavigateToURL(shell(), url));
 
   // Add two PeerConnections and then remove them.
   PeerConnectionEntry pc_1(1, 0);
@@ -525,7 +525,7 @@
 IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcInternalsBrowserTest,
                        UpdateAllPeerConnections) {
   GURL url("chrome://webrtc-internals");
-  NavigateToURL(shell(), url);
+  EXPECT_TRUE(NavigateToURL(shell(), url));
 
   PeerConnectionEntry pc_0(1, 0);
   pc_0.AddEvent("e1", "v1");
@@ -542,7 +542,7 @@
 
 IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcInternalsBrowserTest, UpdatePeerConnection) {
   GURL url("chrome://webrtc-internals");
-  NavigateToURL(shell(), url);
+  EXPECT_TRUE(NavigateToURL(shell(), url));
 
   // Add one PeerConnection and send one update.
   PeerConnectionEntry pc_1(1, 0);
@@ -579,7 +579,7 @@
 // Tests that adding random named stats updates the dataSeries and graphs.
 IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcInternalsBrowserTest, AddStats) {
   GURL url("chrome://webrtc-internals");
-  NavigateToURL(shell(), url);
+  EXPECT_TRUE(NavigateToURL(shell(), url));
 
   PeerConnectionEntry pc(1, 0);
   ExecuteAddPeerConnectionJs(pc);
@@ -605,7 +605,7 @@
 // Tests that the bandwidth estimation values are drawn on a single graph.
 IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcInternalsBrowserTest, BweCompoundGraph) {
   GURL url("chrome://webrtc-internals");
-  NavigateToURL(shell(), url);
+  EXPECT_TRUE(NavigateToURL(shell(), url));
 
   PeerConnectionEntry pc(1, 0);
   ExecuteAddPeerConnectionJs(pc);
@@ -645,7 +645,7 @@
 // and the converted data is drawn.
 IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcInternalsBrowserTest, ConvertedGraphs) {
   GURL url("chrome://webrtc-internals");
-  NavigateToURL(shell(), url);
+  EXPECT_TRUE(NavigateToURL(shell(), url));
 
   PeerConnectionEntry pc(1, 0);
   ExecuteAddPeerConnectionJs(pc);
@@ -690,14 +690,14 @@
   // Start a peerconnection call in the first window.
   ASSERT_TRUE(embedded_test_server()->Start());
   GURL url(embedded_test_server()->GetURL("/media/peerconnection-call.html"));
-  NavigateToURL(shell(), url);
+  EXPECT_TRUE(NavigateToURL(shell(), url));
   ASSERT_TRUE(ExecuteJavascript("call({video:true});"));
   ExpectTitle("OK");
 
   // Open webrtc-internals in the second window.
   GURL url2("chrome://webrtc-internals");
   Shell* shell2 = CreateBrowser();
-  NavigateToURL(shell2, url2);
+  EXPECT_TRUE(NavigateToURL(shell2, url2));
 
   const int NUMBER_OF_PEER_CONNECTIONS = 2;
 
@@ -770,7 +770,7 @@
 
 IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcInternalsBrowserTest, CreatePageDump) {
   GURL url("chrome://webrtc-internals");
-  NavigateToURL(shell(), url);
+  EXPECT_TRUE(NavigateToURL(shell(), url));
 
   PeerConnectionEntry pc_0(1, 0);
   pc_0.AddEvent("e1", "v1");
@@ -815,7 +815,7 @@
 
 IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcInternalsBrowserTest, UpdateGetUserMedia) {
   GURL url("chrome://webrtc-internals");
-  NavigateToURL(shell(), url);
+  EXPECT_TRUE(NavigateToURL(shell(), url));
 
   UserMediaRequestEntry request1(1, 1, "origin", "ac", "vc");
   UserMediaRequestEntry request2(2, 2, "origin2", "ac2", "vc2");
@@ -841,7 +841,7 @@
 IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcInternalsBrowserTest,
                        ReceivedPropagationDelta) {
   GURL url("chrome://webrtc-internals");
-  NavigateToURL(shell(), url);
+  EXPECT_TRUE(NavigateToURL(shell(), url));
 
   PeerConnectionEntry pc(1, 0);
   ExecuteAddPeerConnectionJs(pc);
diff --git a/content/browser/webrtc/webrtc_stress_image_capture_browsertest.cc b/content/browser/webrtc/webrtc_stress_image_capture_browsertest.cc
index 4f9c57a..526f30ff 100644
--- a/content/browser/webrtc/webrtc_stress_image_capture_browsertest.cc
+++ b/content/browser/webrtc/webrtc_stress_image_capture_browsertest.cc
@@ -73,7 +73,7 @@
   // skipped or it works as intended, or false otherwise.
   virtual bool RunImageCaptureTestCase(const std::string& command) {
     GURL url(embedded_test_server()->GetURL(kImageCaptureStressHtmlFile));
-    NavigateToURL(shell(), url);
+    EXPECT_TRUE(NavigateToURL(shell(), url));
 
     if (!IsWebcamAvailableOnSystem(shell()->web_contents())) {
       LOG(WARNING) << "No video device; skipping test...";
diff --git a/content/browser/webrtc/webrtc_video_capture_browsertest.cc b/content/browser/webrtc/webrtc_video_capture_browsertest.cc
index e2ad80c..b2d1d15 100644
--- a/content/browser/webrtc/webrtc_video_capture_browsertest.cc
+++ b/content/browser/webrtc/webrtc_video_capture_browsertest.cc
@@ -69,7 +69,7 @@
     return;
 
   GURL url(embedded_test_server()->GetURL(kVideoCaptureHtmlFile));
-  NavigateToURL(shell(), url);
+  EXPECT_TRUE(NavigateToURL(shell(), url));
 
   std::string result;
   // Start video capture and wait until it started rendering
diff --git a/content/browser/webrtc/webrtc_video_capture_service_browsertest.cc b/content/browser/webrtc/webrtc_video_capture_service_browsertest.cc
index 72d7d4f..3f3ab1d 100644
--- a/content/browser/webrtc/webrtc_video_capture_service_browsertest.cc
+++ b/content/browser/webrtc/webrtc_video_capture_service_browsertest.cc
@@ -471,7 +471,7 @@
     DCHECK(main_task_runner_->RunsTasksInCurrentSequence());
     embedded_test_server()->StartAcceptingConnections();
     GURL url(embedded_test_server()->GetURL(kVideoCaptureHtmlFile));
-    NavigateToURL(shell(), url);
+    EXPECT_TRUE(NavigateToURL(shell(), url));
 
     std::string javascript_to_execute = base::StringPrintf(
         kStartVideoCaptureAndVerify, video_size_.width(), video_size_.height());
diff --git a/content/browser/webrtc/webrtc_video_capture_service_enumeration_browsertest.cc b/content/browser/webrtc/webrtc_video_capture_service_enumeration_browsertest.cc
index aa96d37..fa160bf6 100644
--- a/content/browser/webrtc/webrtc_video_capture_service_enumeration_browsertest.cc
+++ b/content/browser/webrtc/webrtc_video_capture_service_enumeration_browsertest.cc
@@ -212,8 +212,8 @@
     ASSERT_TRUE(embedded_test_server()->InitializeAndListen());
     embedded_test_server()->StartAcceptingConnections();
 
-    NavigateToURL(shell(),
-                  GURL(embedded_test_server()->GetURL(kVideoCaptureHtmlFile)));
+    EXPECT_TRUE(NavigateToURL(
+        shell(), GURL(embedded_test_server()->GetURL(kVideoCaptureHtmlFile))));
   }
 
   std::map<std::string, video_capture::mojom::TextureVirtualDevicePtr>
diff --git a/content/browser/webrtc/webrtc_video_capture_shared_device_browsertest.cc b/content/browser/webrtc/webrtc_video_capture_shared_device_browsertest.cc
index 5b5ae1c4..467a2b3 100644
--- a/content/browser/webrtc/webrtc_video_capture_shared_device_browsertest.cc
+++ b/content/browser/webrtc/webrtc_video_capture_shared_device_browsertest.cc
@@ -99,7 +99,7 @@
     DCHECK(main_task_runner_->RunsTasksInCurrentSequence());
     embedded_test_server()->StartAcceptingConnections();
     GURL url(embedded_test_server()->GetURL(kVideoCaptureHtmlFile));
-    NavigateToURL(shell(), url);
+    EXPECT_TRUE(NavigateToURL(shell(), url));
 
     const std::string javascript_to_execute = base::StringPrintf(
         kStartVideoCaptureAndVerify, kVideoSize.width(), kVideoSize.height());
diff --git a/content/browser/webrtc/webrtc_webcam_browsertest.cc b/content/browser/webrtc/webrtc_webcam_browsertest.cc
index 353cc98..f9c31b6c 100644
--- a/content/browser/webrtc/webrtc_webcam_browsertest.cc
+++ b/content/browser/webrtc/webrtc_webcam_browsertest.cc
@@ -72,7 +72,7 @@
   ASSERT_TRUE(embedded_test_server()->Start());
   GURL url(embedded_test_server()->GetURL(
       "/media/getusermedia-real-webcam.html"));
-  NavigateToURL(shell(), url);
+  EXPECT_TRUE(NavigateToURL(shell(), url));
 
   if (!IsWebcamAvailableOnSystem(shell()->web_contents())) {
     DVLOG(0) << "No video device; skipping test...";
diff --git a/content/browser/worker_host/shared_worker_host.cc b/content/browser/worker_host/shared_worker_host.cc
index 858601c..fb45b175 100644
--- a/content/browser/worker_host/shared_worker_host.cc
+++ b/content/browser/worker_host/shared_worker_host.cc
@@ -46,40 +46,6 @@
   return *s_callback;
 }
 
-void AllowFileSystemOnIOThreadResponse(base::OnceCallback<void(bool)> callback,
-                                       bool result) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  base::PostTask(FROM_HERE, {BrowserThread::UI},
-                 base::BindOnce(std::move(callback), result));
-}
-
-void AllowFileSystemOnIOThread(const GURL& url,
-                               ResourceContext* resource_context,
-                               std::vector<GlobalFrameRoutingId> render_frames,
-                               base::OnceCallback<void(bool)> callback) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  GetContentClient()->browser()->AllowWorkerFileSystem(
-      url, resource_context, render_frames,
-      base::Bind(&AllowFileSystemOnIOThreadResponse, base::Passed(&callback)));
-}
-
-bool AllowIndexedDBOnIOThread(const GURL& url,
-                              ResourceContext* resource_context,
-                              std::vector<GlobalFrameRoutingId> render_frames) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  return GetContentClient()->browser()->AllowWorkerIndexedDB(
-      url, resource_context, render_frames);
-}
-
-bool AllowCacheStorageOnIOThread(
-    const GURL& url,
-    ResourceContext* resource_context,
-    std::vector<GlobalFrameRoutingId> render_frames) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  return GetContentClient()->browser()->AllowWorkerCacheStorage(
-      url, resource_context, render_frames);
-}
-
 }  // namespace
 
 // RAII helper class for talking to SharedWorkerDevToolsManager.
@@ -304,38 +270,26 @@
 void SharedWorkerHost::AllowFileSystem(
     const GURL& url,
     base::OnceCallback<void(bool)> callback) {
-  base::PostTask(
-      FROM_HERE, {BrowserThread::IO},
-      base::BindOnce(&AllowFileSystemOnIOThread, url,
-                     RenderProcessHost::FromID(worker_process_id_)
-                         ->GetBrowserContext()
-                         ->GetResourceContext(),
-                     GetRenderFrameIDsForWorker(), std::move(callback)));
+  GetContentClient()->browser()->AllowWorkerFileSystem(
+      url, RenderProcessHost::FromID(worker_process_id_)->GetBrowserContext(),
+      GetRenderFrameIDsForWorker(), std::move(callback));
 }
 
 void SharedWorkerHost::AllowIndexedDB(const GURL& url,
                                       base::OnceCallback<void(bool)> callback) {
-  base::PostTaskAndReplyWithResult(
-      FROM_HERE, {BrowserThread::IO},
-      base::BindOnce(&AllowIndexedDBOnIOThread, url,
-                     RenderProcessHost::FromID(worker_process_id_)
-                         ->GetBrowserContext()
-                         ->GetResourceContext(),
-                     GetRenderFrameIDsForWorker()),
-      std::move(callback));
+  std::move(callback).Run(GetContentClient()->browser()->AllowWorkerIndexedDB(
+      url, RenderProcessHost::FromID(worker_process_id_)->GetBrowserContext(),
+      GetRenderFrameIDsForWorker()));
 }
 
 void SharedWorkerHost::AllowCacheStorage(
     const GURL& url,
     base::OnceCallback<void(bool)> callback) {
-  base::PostTaskAndReplyWithResult(
-      FROM_HERE, {BrowserThread::IO},
-      base::BindOnce(&AllowCacheStorageOnIOThread, url,
-                     RenderProcessHost::FromID(worker_process_id_)
-                         ->GetBrowserContext()
-                         ->GetResourceContext(),
-                     GetRenderFrameIDsForWorker()),
-      std::move(callback));
+  std::move(callback).Run(
+      GetContentClient()->browser()->AllowWorkerCacheStorage(
+          url,
+          RenderProcessHost::FromID(worker_process_id_)->GetBrowserContext(),
+          GetRenderFrameIDsForWorker()));
 }
 
 void SharedWorkerHost::TerminateWorker() {
diff --git a/content/browser/worker_host/worker_script_fetch_initiator.cc b/content/browser/worker_host/worker_script_fetch_initiator.cc
index d652a74..1ac0fbb0 100644
--- a/content/browser/worker_host/worker_script_fetch_initiator.cc
+++ b/content/browser/worker_host/worker_script_fetch_initiator.cc
@@ -53,23 +53,6 @@
 
 namespace content {
 
-namespace {
-
-// Runs |task| on the thread specified by |thread_id| if already on that thread,
-// otherwise posts a task to that thread.
-void RunOrPostTask(const base::Location& from_here,
-                   BrowserThread::ID thread_id,
-                   base::OnceClosure task) {
-  if (BrowserThread::CurrentlyOn(thread_id)) {
-    std::move(task).Run();
-    return;
-  }
-
-  base::PostTask(from_here, {thread_id}, std::move(task));
-}
-
-}  // namespace
-
 // static
 void WorkerScriptFetchInitiator::Start(
     int worker_process_id,
@@ -175,13 +158,13 @@
 
   AddAdditionalRequestHeaders(resource_request.get(), browser_context);
 
-  CreateScriptLoaderOnUI(
-      worker_process_id, std::move(resource_request), storage_partition,
-      std::move(factory_bundle_for_browser),
-      std::move(subresource_loader_factories),
-      std::move(service_worker_context), service_worker_handle,
-      appcache_handle_core, std::move(blob_url_loader_factory),
-      std::move(url_loader_factory_override), std::move(callback));
+  CreateScriptLoader(worker_process_id, std::move(resource_request),
+                     storage_partition, std::move(factory_bundle_for_browser),
+                     std::move(subresource_loader_factories),
+                     std::move(service_worker_context), service_worker_handle,
+                     appcache_handle_core, std::move(blob_url_loader_factory),
+                     std::move(url_loader_factory_override),
+                     std::move(callback));
 }
 
 std::unique_ptr<blink::URLLoaderFactoryBundleInfo>
@@ -265,7 +248,7 @@
   SetFetchMetadataHeadersForBrowserInitiatedRequest(resource_request);
 }
 
-void WorkerScriptFetchInitiator::CreateScriptLoaderOnUI(
+void WorkerScriptFetchInitiator::CreateScriptLoader(
     int worker_process_id,
     std::unique_ptr<network::ResourceRequest> resource_request,
     StoragePartitionImpl* storage_partition,
@@ -372,12 +355,10 @@
         subresource_loader_params->controller_service_worker_object_host;
   }
 
-  RunOrPostTask(
-      FROM_HERE, BrowserThread::UI,
-      base::BindOnce(
-          std::move(callback), std::move(subresource_loader_factories),
-          std::move(main_script_load_params), std::move(controller),
-          std::move(controller_service_worker_object_host), success));
+  std::move(callback).Run(
+      std::move(subresource_loader_factories),
+      std::move(main_script_load_params), std::move(controller),
+      std::move(controller_service_worker_object_host), success);
 }
 
 }  // namespace content
diff --git a/content/browser/worker_host/worker_script_fetch_initiator.h b/content/browser/worker_host/worker_script_fetch_initiator.h
index ff075b53b..bbaacff 100644
--- a/content/browser/worker_host/worker_script_fetch_initiator.h
+++ b/content/browser/worker_host/worker_script_fetch_initiator.h
@@ -89,7 +89,7 @@
       network::ResourceRequest* resource_request,
       BrowserContext* browser_context);
 
-  static void CreateScriptLoaderOnUI(
+  static void CreateScriptLoader(
       int worker_process_id,
       std::unique_ptr<network::ResourceRequest> resource_request,
       StoragePartitionImpl* storage_partition,
diff --git a/content/browser/worker_host/worker_script_fetcher.h b/content/browser/worker_host/worker_script_fetcher.h
index 575d2d4..494e333 100644
--- a/content/browser/worker_host/worker_script_fetcher.h
+++ b/content/browser/worker_host/worker_script_fetcher.h
@@ -35,7 +35,7 @@
 // resource loader in the renderer process will take them over.
 //
 // WorkerScriptFetcher deletes itself when the ownership of the loader and
-// client is passed to the renderer, or on failure. It lives on the IO or UI
+// client is passed to the renderer, or on failure. It lives on the UI
 // thread.
 class WorkerScriptFetcher : public network::mojom::URLLoaderClient {
  public:
diff --git a/content/browser/worker_host/worker_script_loader.cc b/content/browser/worker_host/worker_script_loader.cc
index 021683d..eef8c2f 100644
--- a/content/browser/worker_host/worker_script_loader.cc
+++ b/content/browser/worker_host/worker_script_loader.cc
@@ -49,7 +49,7 @@
     Abort();
     return;
   }
-  service_worker_interceptor = ServiceWorkerRequestHandler::CreateForWorkerUI(
+  service_worker_interceptor = ServiceWorkerRequestHandler::CreateForWorker(
       resource_request_, process_id, service_worker_handle_);
 
   if (service_worker_interceptor)
diff --git a/content/browser/worker_host/worker_script_loader.h b/content/browser/worker_host/worker_script_loader.h
index 872c263..719981c 100644
--- a/content/browser/worker_host/worker_script_loader.h
+++ b/content/browser/worker_host/worker_script_loader.h
@@ -42,8 +42,7 @@
 // client. On redirects, it starts over with the new request URL, possibly
 // starting a new loader and becoming the client of that.
 //
-// Lives on the UI thread when NavigationLoaderOnUI is enabled, and the IO
-// thread otherwise.
+// Lives on the UI thread.
 class WorkerScriptLoader : public network::mojom::URLLoader,
                            public network::mojom::URLLoaderClient {
  public:
diff --git a/content/browser/worker_host/worker_script_loader_factory.h b/content/browser/worker_host/worker_script_loader_factory.h
index 6e28078..94a652f7 100644
--- a/content/browser/worker_host/worker_script_loader_factory.h
+++ b/content/browser/worker_host/worker_script_loader_factory.h
@@ -28,8 +28,7 @@
 // It's an error to call CreateLoaderAndStart() more than a total of one time
 // across this object or any of its clones.
 //
-// This is created per one web worker. It lives on the UI thread when
-// NavigationLoaderOnUI is enabled, and the IO thread otherwise.
+// This is created per one web worker. It lives on the UI thread.
 class CONTENT_EXPORT WorkerScriptLoaderFactory
     : public network::mojom::URLLoaderFactory {
  public:
diff --git a/content/browser/worker_host/worker_script_loader_factory_unittest.cc b/content/browser/worker_host/worker_script_loader_factory_unittest.cc
index 0f9c979..0504ead7 100644
--- a/content/browser/worker_host/worker_script_loader_factory_unittest.cc
+++ b/content/browser/worker_host/worker_script_loader_factory_unittest.cc
@@ -36,8 +36,7 @@
     // Set up the service worker system.
     helper_ = std::make_unique<EmbeddedWorkerTestHelper>(base::FilePath());
     ServiceWorkerContextCore* context = helper_->context();
-    context->storage()->LazyInitializeForTest(base::DoNothing());
-    base::RunLoop().RunUntilIdle();
+    context->storage()->LazyInitializeForTest();
 
     browser_context_getter_ =
         base::BindRepeating(&ServiceWorkerContextWrapper::browser_context,
diff --git a/content/child/blink_platform_impl.cc b/content/child/blink_platform_impl.cc
index d1f825ac..0fe0175 100644
--- a/content/child/blink_platform_impl.cc
+++ b/content/child/blink_platform_impl.cc
@@ -464,28 +464,10 @@
 #endif
     {"viewportTelevision.css", IDR_UASTYLE_VIEWPORT_TELEVISION_CSS,
      ui::SCALE_FACTOR_NONE, true},
-    {"inspect_tool_common.css", IDR_INSPECT_TOOL_COMMON_CSS,
-     ui::SCALE_FACTOR_NONE, true},
-    {"inspect_tool_common.js", IDR_INSPECT_TOOL_COMMON_JS,
-     ui::SCALE_FACTOR_NONE, true},
-    {"inspect_tool_distances.html", IDR_INSPECT_TOOL_DISTANCES_HTML,
-     ui::SCALE_FACTOR_NONE, true},
-    {"inspect_tool_highlight.html", IDR_INSPECT_TOOL_HIGHLIGHT_HTML,
-     ui::SCALE_FACTOR_NONE, true},
-    {"inspect_tool_paused.html", IDR_INSPECT_TOOL_PAUSED_HTML,
-     ui::SCALE_FACTOR_NONE, true},
-    {"inspect_tool_screenshot.html", IDR_INSPECT_TOOL_SCREENSHOT_HTML,
-     ui::SCALE_FACTOR_NONE, true},
-    {"inspect_tool_viewport_size.html", IDR_INSPECT_TOOL_VIEWPORT_SIZE_HTML,
-     ui::SCALE_FACTOR_NONE, true},
     {"DocumentXMLTreeViewer.css", IDR_DOCUMENTXMLTREEVIEWER_CSS,
      ui::SCALE_FACTOR_NONE, true},
     {"DocumentXMLTreeViewer.js", IDR_DOCUMENTXMLTREEVIEWER_JS,
      ui::SCALE_FACTOR_NONE, true},
-    {"input_alert.svg", IDR_VALIDATION_BUBBLE_ICON, ui::SCALE_FACTOR_NONE,
-     true},
-    {"validation_bubble.css", IDR_VALIDATION_BUBBLE_CSS, ui::SCALE_FACTOR_NONE,
-     true},
     {"placeholderIcon", IDR_PLACEHOLDER_ICON, ui::SCALE_FACTOR_100P, false},
     {"brokenCanvas", IDR_BROKENCANVAS, ui::SCALE_FACTOR_100P, false},
     {"brokenCanvas@2x", IDR_BROKENCANVAS, ui::SCALE_FACTOR_200P, false},
diff --git a/content/public/app/content_browser_manifest.cc b/content/public/app/content_browser_manifest.cc
index 955d38e..1c6919d 100644
--- a/content/public/app/content_browser_manifest.cc
+++ b/content/public/app/content_browser_manifest.cc
@@ -13,15 +13,16 @@
 namespace content {
 
 const service_manager::Manifest& GetContentBrowserManifest() {
+  // clang-format off
   static base::NoDestructor<service_manager::Manifest> manifest{
       service_manager::ManifestBuilder()
           .WithServiceName(mojom::kBrowserServiceName)
           .WithDisplayName("Content (browser process)")
           .WithOptions(service_manager::ManifestOptionsBuilder()
-                           .CanConnectToInstancesInAnyGroup(true)
-                           .CanConnectToInstancesWithAnyId(true)
-                           .CanRegisterOtherServiceInstances(true)
-                           .Build())
+                          .CanConnectToInstancesInAnyGroup(true)
+                          .CanConnectToInstancesWithAnyId(true)
+                          .CanRegisterOtherServiceInstances(true)
+                          .Build())
           .ExposeCapability("field_trials",
                             std::set<const char*>{
                                 "content.mojom.FieldTrialRecorder",
@@ -109,6 +110,9 @@
           .RequireCapability("data_decoder", "image_decoder")
           .RequireCapability("data_decoder", "json_parser")
           .RequireCapability("data_decoder", "xml_parser")
+  #if defined(OS_CHROMEOS)
+          .RequireCapability("data_decoder", "ble_scan_parser")
+  #endif  // OS_CHROMEOS
           .RequireCapability("cdm", "media:cdm")
           .RequireCapability("shape_detection", "barcode_detection")
           .RequireCapability("shape_detection", "face_detection")
@@ -123,7 +127,7 @@
           .RequireCapability("content", "navigation")
           .RequireCapability("resource_coordinator", "service_callbacks")
           .RequireCapability("service_manager",
-                             "service_manager:service_manager")
+              "service_manager:service_manager")
           .RequireCapability("chromecast", "multizone")
           .RequireCapability("content_plugin", "browser")
           .RequireCapability("metrics", "url_keyed_metrics")
@@ -182,8 +186,9 @@
                   "blink.mojom.NotificationService",
                   "blink.mojom.PermissionService",
                   "blink.mojom.QuotaDispatcherHost",
-                  "blink.mojom.SerialService", "blink.mojom.WebUsbService",
-                  "blink.mojom.SmsReceiver", "blink.mojom.WebSocketConnector",
+                  "blink.mojom.SerialService",
+                  "blink.mojom.WebUsbService", "blink.mojom.SmsReceiver",
+                  "blink.mojom.WebSocketConnector",
                   "media.mojom.VideoDecodePerfHistory",
                   "payments.mojom.PaymentManager",
                   "shape_detection.mojom.BarcodeDetectionProvider",
@@ -284,6 +289,7 @@
           .PackageService(file::GetManifest())
           .Build()};
   return *manifest;
+  // clang-format on
 }
 
 }  // namespace content
diff --git a/content/public/browser/content_browser_client.cc b/content/public/browser/content_browser_client.cc
index 7ba11e4..d9f7d1d 100644
--- a/content/public/browser/content_browser_client.cc
+++ b/content/public/browser/content_browser_client.cc
@@ -325,22 +325,22 @@
 
 void ContentBrowserClient::AllowWorkerFileSystem(
     const GURL& url,
-    ResourceContext* context,
+    BrowserContext* browser_context,
     const std::vector<GlobalFrameRoutingId>& render_frames,
-    base::Callback<void(bool)> callback) {
+    base::OnceCallback<void(bool)> callback) {
   std::move(callback).Run(true);
 }
 
 bool ContentBrowserClient::AllowWorkerIndexedDB(
     const GURL& url,
-    ResourceContext* context,
+    BrowserContext* browser_context,
     const std::vector<GlobalFrameRoutingId>& render_frames) {
   return true;
 }
 
 bool ContentBrowserClient::AllowWorkerCacheStorage(
     const GURL& url,
-    ResourceContext* context,
+    BrowserContext* browser_context,
     const std::vector<GlobalFrameRoutingId>& render_frames) {
   return true;
 }
diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h
index 1384a5d6d..02a0dbc 100644
--- a/content/public/browser/content_browser_client.h
+++ b/content/public/browser/content_browser_client.h
@@ -575,27 +575,24 @@
 
   // Allow the embedder to control if access to file system by a shared worker
   // is allowed.
-  // This is called on the IO thread.
   virtual void AllowWorkerFileSystem(
       const GURL& url,
-      ResourceContext* context,
+      BrowserContext* browser_context,
       const std::vector<GlobalFrameRoutingId>& render_frames,
-      base::Callback<void(bool)> callback);
+      base::OnceCallback<void(bool)> callback);
 
   // Allow the embedder to control if access to IndexedDB by a shared worker
   // is allowed.
-  // This is called on the IO thread.
   virtual bool AllowWorkerIndexedDB(
       const GURL& url,
-      ResourceContext* context,
+      BrowserContext* browser_context,
       const std::vector<GlobalFrameRoutingId>& render_frames);
 
   // Allow the embedder to control if access to CacheStorage by a shared worker
   // is allowed.
-  // This is called on the IO thread.
   virtual bool AllowWorkerCacheStorage(
       const GURL& url,
-      ResourceContext* context,
+      BrowserContext* browser_context,
       const std::vector<GlobalFrameRoutingId>& render_frames);
 
   // Allow the embedder to control whether we can use Web Bluetooth.
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc
index bff56efdb..5cb02a6 100644
--- a/content/renderer/render_thread_impl.cc
+++ b/content/renderer/render_thread_impl.cc
@@ -2082,7 +2082,9 @@
     init_end_ = base::TimeTicks();
   }
 
-  EmbeddedWorkerInstanceClientImpl::Create(std::move(client_request));
+  EmbeddedWorkerInstanceClientImpl::Create(
+      std::move(client_request),
+      GetWebMainThreadScheduler()->DefaultTaskRunner());
 }
 
 void RenderThreadImpl::OnNetworkConnectionChanged(
diff --git a/content/renderer/service_worker/embedded_worker_instance_client_impl.cc b/content/renderer/service_worker/embedded_worker_instance_client_impl.cc
index 4aa6d26..7de92b78 100644
--- a/content/renderer/service_worker/embedded_worker_instance_client_impl.cc
+++ b/content/renderer/service_worker/embedded_worker_instance_client_impl.cc
@@ -16,7 +16,6 @@
 #include "content/renderer/render_thread_impl.h"
 #include "content/renderer/service_worker/service_worker_context_client.h"
 #include "third_party/blink/public/common/features.h"
-#include "third_party/blink/public/platform/scheduler/web_thread_scheduler.h"
 #include "third_party/blink/public/platform/web_content_settings_client.h"
 #include "third_party/blink/public/platform/web_security_origin.h"
 #include "third_party/blink/public/platform/web_url.h"
@@ -28,16 +27,19 @@
 
 // static
 void EmbeddedWorkerInstanceClientImpl::Create(
-    blink::mojom::EmbeddedWorkerInstanceClientRequest request) {
+    blink::mojom::EmbeddedWorkerInstanceClientRequest request,
+    scoped_refptr<base::SingleThreadTaskRunner> starter_thread_task_runner) {
   // This won't be leaked because the lifetime will be managed internally.
   // See the class documentation for detail.
   // We can't use MakeStrongBinding because must give the worker thread
   // a chance to stop by calling TerminateWorkerContext() and waiting
   // before destructing.
-  new EmbeddedWorkerInstanceClientImpl(std::move(request));
+  new EmbeddedWorkerInstanceClientImpl(std::move(request),
+                                       std::move(starter_thread_task_runner));
 }
 
 void EmbeddedWorkerInstanceClientImpl::WorkerContextDestroyed() {
+  DCHECK(starter_thread_task_runner_->BelongsToCurrentThread());
   TRACE_EVENT0("ServiceWorker",
                "EmbeddedWorkerInstanceClientImpl::WorkerContextDestroyed");
   delete this;
@@ -45,7 +47,7 @@
 
 void EmbeddedWorkerInstanceClientImpl::StartWorker(
     blink::mojom::EmbeddedWorkerStartParamsPtr params) {
-  DCHECK(ChildThreadImpl::current());
+  DCHECK(starter_thread_task_runner_->BelongsToCurrentThread());
   DCHECK(!service_worker_context_client_);
   TRACE_EVENT0("ServiceWorker",
                "EmbeddedWorkerInstanceClientImpl::StartWorker");
@@ -71,9 +73,7 @@
       std::move(params->preference_watcher_request),
       std::move(params->subresource_loader_factories),
       std::move(params->subresource_loader_updater),
-      RenderThreadImpl::current()
-          ->GetWebMainThreadScheduler()
-          ->DefaultTaskRunner());
+      starter_thread_task_runner_);
   // Record UMA to indicate StartWorker is received on renderer.
   StartWorkerHistogramEnum metric =
       params->is_installed ? StartWorkerHistogramEnum::RECEIVED_ON_INSTALLED
@@ -105,11 +105,12 @@
       std::move(installed_scripts_manager_params),
       params->content_settings_proxy.PassHandle(), cache_storage.PassHandle(),
       interface_provider.PassHandle());
-  service_worker_context_client_->StartWorkerContext(std::move(worker),
-                                                     start_data);
+  service_worker_context_client_->StartWorkerContextOnStarterThread(
+      std::move(worker), start_data);
 }
 
 void EmbeddedWorkerInstanceClientImpl::StopWorker() {
+  DCHECK(starter_thread_task_runner_->BelongsToCurrentThread());
   TRACE_EVENT0("ServiceWorker", "EmbeddedWorkerInstanceClientImpl::StopWorker");
   // StopWorker must be called after StartWorker is called.
   service_worker_context_client_->worker().TerminateWorkerContext();
@@ -117,12 +118,14 @@
 }
 
 void EmbeddedWorkerInstanceClientImpl::ResumeAfterDownload() {
+  DCHECK(starter_thread_task_runner_->BelongsToCurrentThread());
   service_worker_context_client_->worker().ResumeAfterDownload();
 }
 
 void EmbeddedWorkerInstanceClientImpl::AddMessageToConsole(
     blink::mojom::ConsoleMessageLevel level,
     const std::string& message) {
+  DCHECK(starter_thread_task_runner_->BelongsToCurrentThread());
   service_worker_context_client_->worker().AddMessageToConsole(
       blink::WebConsoleMessage(level, blink::WebString::FromUTF8(message)));
 }
@@ -135,15 +138,21 @@
 }
 
 EmbeddedWorkerInstanceClientImpl::EmbeddedWorkerInstanceClientImpl(
-    blink::mojom::EmbeddedWorkerInstanceClientRequest request)
-    : binding_(this, std::move(request)) {
+    blink::mojom::EmbeddedWorkerInstanceClientRequest request,
+    scoped_refptr<base::SingleThreadTaskRunner> starter_thread_task_runner)
+    : binding_(this, std::move(request)),
+      starter_thread_task_runner_(std::move(starter_thread_task_runner)) {
+  DCHECK(starter_thread_task_runner_->BelongsToCurrentThread());
   binding_.set_connection_error_handler(base::BindOnce(
       &EmbeddedWorkerInstanceClientImpl::OnError, base::Unretained(this)));
 }
 
-EmbeddedWorkerInstanceClientImpl::~EmbeddedWorkerInstanceClientImpl() {}
+EmbeddedWorkerInstanceClientImpl::~EmbeddedWorkerInstanceClientImpl() {
+  DCHECK(starter_thread_task_runner_->BelongsToCurrentThread());
+}
 
 void EmbeddedWorkerInstanceClientImpl::OnError() {
+  DCHECK(starter_thread_task_runner_->BelongsToCurrentThread());
   // The connection to the browser process broke.
   if (service_worker_context_client_) {
     // The worker is running, so tell it to stop. We continue in
@@ -159,6 +168,7 @@
 blink::WebEmbeddedWorkerStartData
 EmbeddedWorkerInstanceClientImpl::BuildStartData(
     const blink::mojom::EmbeddedWorkerStartParams& params) {
+  DCHECK(starter_thread_task_runner_->BelongsToCurrentThread());
   blink::WebEmbeddedWorkerStartData start_data;
   start_data.script_url = params.script_url;
   start_data.user_agent = blink::WebString::FromUTF8(params.user_agent);
diff --git a/content/renderer/service_worker/embedded_worker_instance_client_impl.h b/content/renderer/service_worker/embedded_worker_instance_client_impl.h
index d1e0984f..fb38d9a8 100644
--- a/content/renderer/service_worker/embedded_worker_instance_client_impl.h
+++ b/content/renderer/service_worker/embedded_worker_instance_client_impl.h
@@ -26,7 +26,9 @@
 // the Mojo connection to the browser breaks first, the instance waits for the
 // service worker to stop and then deletes itself.
 //
-// All methods are called on the main thread.
+// All methods are called on the thread that creates the instance of this class.
+// Currently it's the main thread but it could be the IO thread in the future.
+// https://crbug.com/692909
 class CONTENT_EXPORT EmbeddedWorkerInstanceClientImpl
     : public blink::mojom::EmbeddedWorkerInstanceClient {
  public:
@@ -42,7 +44,9 @@
   // documentation.
   // TODO(shimazu): Create a service worker's execution context by this method
   // instead of just creating an instance of EmbeddedWorkerInstanceClient.
-  static void Create(blink::mojom::EmbeddedWorkerInstanceClientRequest request);
+  static void Create(
+      blink::mojom::EmbeddedWorkerInstanceClientRequest request,
+      scoped_refptr<base::SingleThreadTaskRunner> starter_task_runner);
 
   ~EmbeddedWorkerInstanceClientImpl() override;
 
@@ -56,8 +60,9 @@
  private:
   friend class ServiceWorkerContextClientTest;
 
-  explicit EmbeddedWorkerInstanceClientImpl(
-      blink::mojom::EmbeddedWorkerInstanceClientRequest request);
+  EmbeddedWorkerInstanceClientImpl(
+      blink::mojom::EmbeddedWorkerInstanceClientRequest request,
+      scoped_refptr<base::SingleThreadTaskRunner> starter_thread_task_runner);
 
   // blink::mojom::EmbeddedWorkerInstanceClient implementation
   void StartWorker(blink::mojom::EmbeddedWorkerStartParamsPtr params) override;
@@ -76,6 +81,10 @@
 
   mojo::Binding<blink::mojom::EmbeddedWorkerInstanceClient> binding_;
 
+  // A copy of this runner is also passed to ServiceWorkerContextClient in
+  // StartWorker().
+  scoped_refptr<base::SingleThreadTaskRunner> starter_thread_task_runner_;
+
   // nullptr means worker is not running.
   std::unique_ptr<ServiceWorkerContextClient> service_worker_context_client_;
 
diff --git a/content/renderer/service_worker/service_worker_context_client.cc b/content/renderer/service_worker/service_worker_context_client.cc
index 0ac1926c..0f999bd 100644
--- a/content/renderer/service_worker/service_worker_context_client.cc
+++ b/content/renderer/service_worker/service_worker_context_client.cc
@@ -109,14 +109,14 @@
     std::unique_ptr<blink::URLLoaderFactoryBundleInfo> subresource_loaders,
     mojo::PendingReceiver<blink::mojom::ServiceWorkerSubresourceLoaderUpdater>
         subresource_loader_updater,
-    scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner)
+    scoped_refptr<base::SingleThreadTaskRunner> starter_thread_task_runner)
     : service_worker_version_id_(service_worker_version_id),
       service_worker_scope_(service_worker_scope),
       script_url_(script_url),
       is_starting_installed_worker_(is_starting_installed_worker),
       renderer_preferences_(std::move(renderer_preferences)),
       preference_watcher_request_(std::move(preference_watcher_request)),
-      main_thread_task_runner_(std::move(main_thread_task_runner)),
+      starter_thread_task_runner_(std::move(starter_thread_task_runner)),
       proxy_(nullptr),
       pending_service_worker_request_(std::move(service_worker_request)),
       controller_receiver_(std::move(controller_receiver)),
@@ -124,12 +124,12 @@
           std::move(subresource_loader_updater)),
       owner_(owner),
       start_timing_(std::move(start_timing)) {
-  DCHECK(main_thread_task_runner_->RunsTasksInCurrentSequence());
+  DCHECK(starter_thread_task_runner_->RunsTasksInCurrentSequence());
   DCHECK(owner_);
   DCHECK(subresource_loaders);
   instance_host_ =
       blink::mojom::ThreadSafeEmbeddedWorkerInstanceHostAssociatedPtr::Create(
-          std::move(instance_host), main_thread_task_runner_);
+          std::move(instance_host), starter_thread_task_runner_);
 
   if (IsOutOfProcessNetworkService()) {
     // If the network service crashes, this worker self-terminates, so it can
@@ -143,9 +143,9 @@
         subresource_loaders->pending_default_factory()
             .InitWithNewPipeAndPassReceiver());
     network_service_connection_error_handler_holder_
-        .set_connection_error_handler(
-            base::BindOnce(&ServiceWorkerContextClient::StopWorkerOnMainThread,
-                           base::Unretained(this)));
+        .set_connection_error_handler(base::BindOnce(
+            &ServiceWorkerContextClient::StopWorkerOnStarterThread,
+            base::Unretained(this)));
   }
 
   loader_factories_ = base::MakeRefCounted<ChildURLLoaderFactoryBundle>(
@@ -164,29 +164,29 @@
 }
 
 ServiceWorkerContextClient::~ServiceWorkerContextClient() {
-  DCHECK(main_thread_task_runner_->RunsTasksInCurrentSequence());
+  DCHECK(starter_thread_task_runner_->RunsTasksInCurrentSequence());
 }
 
-void ServiceWorkerContextClient::StartWorkerContext(
+void ServiceWorkerContextClient::StartWorkerContextOnStarterThread(
     std::unique_ptr<blink::WebEmbeddedWorker> worker,
     const blink::WebEmbeddedWorkerStartData& start_data) {
-  DCHECK(main_thread_task_runner_->RunsTasksInCurrentSequence());
+  DCHECK(starter_thread_task_runner_->RunsTasksInCurrentSequence());
   worker_ = std::move(worker);
   worker_->StartWorkerContext(start_data);
 }
 
 blink::WebEmbeddedWorker& ServiceWorkerContextClient::worker() {
-  DCHECK(main_thread_task_runner_->RunsTasksInCurrentSequence());
+  DCHECK(starter_thread_task_runner_->RunsTasksInCurrentSequence());
   return *worker_;
 }
 
 void ServiceWorkerContextClient::WorkerReadyForInspectionOnMainThread() {
-  DCHECK(main_thread_task_runner_->RunsTasksInCurrentSequence());
+  DCHECK(starter_thread_task_runner_->RunsTasksInCurrentSequence());
   (*instance_host_)->OnReadyForInspection();
 }
 
 void ServiceWorkerContextClient::WorkerContextFailedToStartOnMainThread() {
-  DCHECK(main_thread_task_runner_->RunsTasksInCurrentSequence());
+  DCHECK(starter_thread_task_runner_->RunsTasksInCurrentSequence());
   DCHECK(!proxy_);
 
   (*instance_host_)->OnStopped();
@@ -225,7 +225,7 @@
 }
 
 void ServiceWorkerContextClient::WorkerScriptLoadedOnMainThread() {
-  DCHECK(main_thread_task_runner_->RunsTasksInCurrentSequence());
+  DCHECK(starter_thread_task_runner_->RunsTasksInCurrentSequence());
   DCHECK(!is_starting_installed_worker_);
   (*instance_host_)->OnScriptLoaded();
   TRACE_EVENT_NESTABLE_ASYNC_END0("ServiceWorker", "LOAD_SCRIPT", this);
@@ -240,8 +240,9 @@
 void ServiceWorkerContextClient::WorkerContextStarted(
     blink::WebServiceWorkerContextProxy* proxy,
     scoped_refptr<base::SequencedTaskRunner> worker_task_runner) {
-  DCHECK_NE(0, WorkerThread::GetCurrentId())
-      << "service worker started on the main thread instead of a worker thread";
+  DCHECK(!starter_thread_task_runner_->RunsTasksInCurrentSequence())
+      << "service worker started on the starter thread instead of a worker "
+         "thread";
   DCHECK(worker_task_runner->RunsTasksInCurrentSequence());
   DCHECK(!worker_task_runner_);
   worker_task_runner_ = std::move(worker_task_runner);
@@ -344,7 +345,7 @@
 
   // base::Unretained is safe because |owner_| does not destroy itself until
   // WorkerContextDestroyed is called.
-  main_thread_task_runner_->PostTask(
+  starter_thread_task_runner_->PostTask(
       FROM_HERE,
       base::BindOnce(&EmbeddedWorkerInstanceClientImpl::WorkerContextDestroyed,
                      base::Unretained(owner_)));
@@ -378,7 +379,7 @@
 
 std::unique_ptr<blink::WebServiceWorkerNetworkProvider>
 ServiceWorkerContextClient::CreateServiceWorkerNetworkProviderOnMainThread() {
-  DCHECK(main_thread_task_runner_->RunsTasksInCurrentSequence());
+  DCHECK(starter_thread_task_runner_->RunsTasksInCurrentSequence());
   return std::make_unique<ServiceWorkerNetworkProviderForServiceWorker>(
       std::move(service_worker_provider_info_->script_loader_factory_ptr_info));
 }
@@ -386,7 +387,7 @@
 scoped_refptr<blink::WebWorkerFetchContext>
 ServiceWorkerContextClient::CreateWorkerFetchContextOnMainThreadLegacy(
     blink::WebServiceWorkerNetworkProvider* provider) {
-  DCHECK(main_thread_task_runner_->RunsTasksInCurrentSequence());
+  DCHECK(starter_thread_task_runner_->RunsTasksInCurrentSequence());
   DCHECK(preference_watcher_request_.is_pending());
 
   // TODO(crbug.com/796425): Temporarily wrap the raw
@@ -413,7 +414,7 @@
 
 scoped_refptr<blink::WebWorkerFetchContext>
 ServiceWorkerContextClient::CreateWorkerFetchContextOnMainThread() {
-  DCHECK(main_thread_task_runner_->RunsTasksInCurrentSequence());
+  DCHECK(starter_thread_task_runner_->RunsTasksInCurrentSequence());
   DCHECK(preference_watcher_request_.is_pending());
 
   // TODO(bashi): Consider changing ServiceWorkerFetchContextImpl to take
@@ -543,8 +544,8 @@
   (*instance_host_)->RequestTermination(std::move(callback));
 }
 
-void ServiceWorkerContextClient::StopWorkerOnMainThread() {
-  DCHECK(main_thread_task_runner_->RunsTasksInCurrentSequence());
+void ServiceWorkerContextClient::StopWorkerOnStarterThread() {
+  DCHECK(starter_thread_task_runner_->RunsTasksInCurrentSequence());
   owner_->StopWorker();
 }
 
diff --git a/content/renderer/service_worker/service_worker_context_client.h b/content/renderer/service_worker/service_worker_context_client.h
index 2ac29fd..16db81f 100644
--- a/content/renderer/service_worker/service_worker_context_client.h
+++ b/content/renderer/service_worker/service_worker_context_client.h
@@ -61,13 +61,16 @@
 // starting up, and destroyed when the service worker stops. It is owned by
 // WebEmbeddedWorkerImpl (which is owned by EmbeddedWorkerInstanceClientImpl).
 //
-// This class is created and destroyed on the main thread. Unless otherwise
-// noted (here or in base class documentation), all methods are called on the
-// worker thread.
+// This class is created and destroyed on the "starter" thread. The starter
+// thread is the thread that constructs this class. Currently it's the main
+// thread but could be the IO thread in the future. https://crbug.com/692909
+//
+// Unless otherwise noted (here or in base class documentation), all methods
+// are called on the worker thread.
 class CONTENT_EXPORT ServiceWorkerContextClient
     : public blink::WebServiceWorkerContextClient {
  public:
-  // Called on the main thread.
+  // Called on the starter thread.
   // - |is_starting_installed_worker| is true if the script is already installed
   //   and will be streamed from the browser process.
   // - |owner| must outlive this new instance.
@@ -94,17 +97,19 @@
       std::unique_ptr<blink::URLLoaderFactoryBundleInfo> subresource_loaders,
       mojo::PendingReceiver<blink::mojom::ServiceWorkerSubresourceLoaderUpdater>
           subresource_loader_updater,
-      scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner);
-  // Called on the main thread.
+      scoped_refptr<base::SingleThreadTaskRunner> starter_thread_task_runner);
+  // Called on the starter thread.
   ~ServiceWorkerContextClient() override;
 
-  // Called on the main thread.
-  void StartWorkerContext(std::unique_ptr<blink::WebEmbeddedWorker> worker,
-                          const blink::WebEmbeddedWorkerStartData& start_data);
-  // Called on the main thread.
+  // Called on the starter thread.
+  void StartWorkerContextOnStarterThread(
+      std::unique_ptr<blink::WebEmbeddedWorker> worker,
+      const blink::WebEmbeddedWorkerStartData& start_data);
+  // Called on the starter thread.
   blink::WebEmbeddedWorker& worker();
 
   // WebServiceWorkerContextClient overrides.
+  // TODO(bashi): Rename OnMainThread() methods.
   void WorkerReadyForInspectionOnMainThread() override;
   void WorkerContextFailedToStartOnMainThread() override;
   void FailedToLoadClassicScript() override;
@@ -186,8 +191,8 @@
 
   void SendWorkerStarted(blink::mojom::ServiceWorkerStartStatus status);
 
-  // Stops the worker context. Called on the main thread.
-  void StopWorkerOnMainThread();
+  // Stops the worker context. Called on the starter thread.
+  void StopWorkerOnStarterThread();
 
   base::WeakPtr<ServiceWorkerContextClient> GetWeakPtr();
 
@@ -202,7 +207,7 @@
   // Passed on creation of ServiceWorkerFetchContext.
   blink::mojom::RendererPreferenceWatcherRequest preference_watcher_request_;
 
-  scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_;
+  scoped_refptr<base::SingleThreadTaskRunner> starter_thread_task_runner_;
   scoped_refptr<base::SequencedTaskRunner> worker_task_runner_;
 
   // Not owned; |this| is destroyed when |proxy_| becomes invalid.
@@ -215,7 +220,7 @@
   mojo::PendingReceiver<blink::mojom::ServiceWorkerSubresourceLoaderUpdater>
       pending_subresource_loader_updater_;
 
-  // This is bound on the main thread.
+  // This is bound on the starter thread.
   scoped_refptr<blink::mojom::ThreadSafeEmbeddedWorkerInstanceHostAssociatedPtr>
       instance_host_;
 
@@ -227,7 +232,7 @@
   blink::mojom::ServiceWorkerProviderInfoForStartWorkerPtr
       service_worker_provider_info_;
 
-  // Must be accessed on the main thread only.
+  // Must be accessed on the starter thread only.
   EmbeddedWorkerInstanceClientImpl* owner_;
 
   blink::mojom::BlobRegistryPtr blob_registry_;
diff --git a/device/bluetooth/BUILD.gn b/device/bluetooth/BUILD.gn
index 0e00d73..5dfd6535 100644
--- a/device/bluetooth/BUILD.gn
+++ b/device/bluetooth/BUILD.gn
@@ -431,6 +431,7 @@
           "chromeos/bluetooth_utils.cc",
           "chromeos/bluetooth_utils.h",
         ]
+        deps += [ "//services/data_decoder/public/mojom" ]
       }
       deps += [ "//dbus" ]
     } else {  # !use_dbus
diff --git a/device/bluetooth/DEPS b/device/bluetooth/DEPS
index 556b1bc..906bc06a 100644
--- a/device/bluetooth/DEPS
+++ b/device/bluetooth/DEPS
@@ -9,6 +9,7 @@
   "+net/log",
   "+net/socket",
   "+net/traffic_annotation",
+  "+services/data_decoder/public/mojom",
   "+ui/base/l10n",
   "+third_party/cros_system_api/dbus",
   "+third_party/re2",
diff --git a/device/bluetooth/bluetooth_adapter_factory.cc b/device/bluetooth/bluetooth_adapter_factory.cc
index 50ff691..caa18e22 100644
--- a/device/bluetooth/bluetooth_adapter_factory.cc
+++ b/device/bluetooth/bluetooth_adapter_factory.cc
@@ -202,6 +202,20 @@
   return default_adapter.Get() != nullptr;
 }
 
+#if defined(OS_CHROMEOS)
+// static
+void BluetoothAdapterFactory::SetBleScanParserCallback(
+    BleScanParserCallback callback) {
+  Get().ble_scan_parser_ = callback;
+}
+
+// static
+BluetoothAdapterFactory::BleScanParserCallback
+BluetoothAdapterFactory::GetBleScanParserCallback() {
+  return Get().ble_scan_parser_;
+}
+#endif  // defined(OS_CHROMEOS)
+
 BluetoothAdapterFactory::GlobalValuesForTesting::GlobalValuesForTesting()
     : weak_ptr_factory_(this) {}
 
diff --git a/device/bluetooth/bluetooth_adapter_factory.h b/device/bluetooth/bluetooth_adapter_factory.h
index fc7e7da..12262054 100644
--- a/device/bluetooth/bluetooth_adapter_factory.h
+++ b/device/bluetooth/bluetooth_adapter_factory.h
@@ -13,6 +13,11 @@
 #include "device/bluetooth/bluetooth_adapter.h"
 #include "device/bluetooth/bluetooth_export.h"
 
+#if defined(OS_CHROMEOS)
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "services/data_decoder/public/mojom/ble_scan_parser.mojom.h"
+#endif  // defined(OS_CHROMEOS)
+
 namespace device {
 
 // A factory class for building a Bluetooth adapter on platforms where Bluetooth
@@ -30,6 +35,11 @@
   using AdapterCallback =
       base::OnceCallback<void(scoped_refptr<BluetoothAdapter> adapter)>;
 
+#if defined(OS_CHROMEOS)
+  using BleScanParserCallback = base::RepeatingCallback<
+      mojo::PendingRemote<data_decoder::mojom::BleScanParser>()>;
+#endif  // defined(OS_CHROMEOS)
+
   ~BluetoothAdapterFactory();
 
   static BluetoothAdapterFactory& Get();
@@ -75,6 +85,14 @@
   // adapter. Exposed for testing.
   static bool HasSharedInstanceForTesting();
 
+#if defined(OS_CHROMEOS)
+  // Sets the BleScanParserPtr callback used in Get*() below.
+  static void SetBleScanParserCallback(BleScanParserCallback callback);
+  // Returns a reference to a parser for BLE advertisement packets.
+  // This will be an empty callback until something calls Set*() above.
+  static BleScanParserCallback GetBleScanParserCallback();
+#endif  // defined(OS_CHROMEOS)
+
   // ValuestForTesting holds the return values for BluetoothAdapterFactory's
   // functions that have been set for testing.
   class DEVICE_BLUETOOTH_EXPORT GlobalValuesForTesting {
@@ -116,6 +134,10 @@
   BluetoothAdapterFactory();
 
   base::WeakPtr<GlobalValuesForTesting> values_for_testing_;
+
+#if defined(OS_CHROMEOS)
+  BleScanParserCallback ble_scan_parser_;
+#endif  // defined(OS_CHROMEOS)
 };
 
 }  // namespace device
diff --git a/device/bluetooth/bluez/bluetooth_adapter_bluez.cc b/device/bluetooth/bluez/bluetooth_adapter_bluez.cc
index 990703eb..ecf7cff 100644
--- a/device/bluetooth/bluez/bluetooth_adapter_bluez.cc
+++ b/device/bluetooth/bluez/bluetooth_adapter_bluez.cc
@@ -51,6 +51,7 @@
 
 #if defined(OS_CHROMEOS)
 #include "chromeos/constants/devicetype.h"
+#include "device/bluetooth/bluetooth_adapter_factory.h"
 #include "device/bluetooth/chromeos/bluetooth_utils.h"
 #endif
 
@@ -105,6 +106,24 @@
   }
 }
 
+#if defined(OS_CHROMEOS)
+device::BluetoothDevice::ServiceDataMap ConvertServiceDataMap(
+    const base::flat_map<std::string, std::vector<uint8_t>>& input) {
+  device::BluetoothDevice::ServiceDataMap output;
+  for (auto& i : input) {
+    output[BluetoothUUID(i.first)] = i.second;
+  }
+
+  return output;
+}
+
+device::BluetoothDevice::ManufacturerDataMap ConvertManufacturerDataMap(
+    const base::flat_map<uint16_t, std::vector<uint8_t>>& input) {
+  return device::BluetoothDevice::ManufacturerDataMap(input.begin(),
+                                                      input.end());
+}
+#endif  // defined(OS_CHROMEOS)
+
 }  // namespace
 
 namespace device {
@@ -1187,8 +1206,37 @@
 
   for (auto& observer : observers_)
     observer.DeviceAdvertisementReceived(this, device, rssi, eir);
+
+#if defined(OS_CHROMEOS)
+  if (ble_scan_parser_.is_bound()) {
+    ScanRecordCallback callback =
+        base::BindOnce(&BluetoothAdapterBlueZ::OnAdvertisementReceived,
+                       weak_ptr_factory_.GetWeakPtr(), device->GetAddress(),
+                       device->GetName() ? *(device->GetName()) : std::string(),
+                       rssi, device->GetAppearance());
+    ble_scan_parser_->Parse(eir, std::move(callback));
+  }
+#endif  // defined(OS_CHROMEOS)
 }
 
+#if defined(OS_CHROMEOS)
+void BluetoothAdapterBlueZ::OnAdvertisementReceived(std::string device_address,
+                                                    std::string device_name,
+                                                    uint8_t rssi,
+                                                    uint16_t device_appearance,
+                                                    ScanRecordPtr scan_record) {
+  auto service_data_map = ConvertServiceDataMap(scan_record->service_data_map);
+  auto manufacturer_data_map =
+      ConvertManufacturerDataMap(scan_record->manufacturer_data_map);
+  for (auto& observer : observers_) {
+    observer.DeviceAdvertisementReceived(
+        device_address, device_name, scan_record->advertisement_name, rssi,
+        scan_record->tx_power, device_appearance, scan_record->service_uuids,
+        service_data_map, manufacturer_data_map);
+  }
+}
+#endif  // defined(OS_CHROMEOS)
+
 void BluetoothAdapterBlueZ::NotifyDeviceConnectedStateChanged(
     BluetoothDeviceBlueZ* device,
     bool is_now_connected) {
@@ -1515,6 +1563,26 @@
     return;
   }
 
+#if defined(OS_CHROMEOS)
+  device::BluetoothAdapterFactory::BleScanParserCallback
+      ble_scan_parser_callback =
+          device::BluetoothAdapterFactory::GetBleScanParserCallback();
+  if (ble_scan_parser_callback) {
+    // To avoid repeatedly restarting a crashed data decoder service,
+    // don't add a connection error handler here. Wait to establish a
+    // new connection after all discovery sessions are stopped.
+    ble_scan_parser_.Bind(ble_scan_parser_callback.Run());
+  } else {
+#if DCHECK_IS_ON()
+    static bool logged_once = false;
+    DLOG_IF(ERROR, !logged_once)
+        << "Attempted to connect to "
+           "unconfigured BluetoothAdapterFactory::GetBleScanParserCallback()";
+    logged_once = true;
+#endif  // DCHECK_IS_ON()
+  }
+#endif  // defined(OS_CHROMEOS)
+
   auto copyable_callback = base::AdaptCallbackForRepeating(std::move(callback));
 
   if (discovery_filter) {
@@ -1593,6 +1661,10 @@
     return;
   }
 
+#if defined(OS_CHROMEOS)
+  ble_scan_parser_.reset();
+#endif  // defined(OS_CHROMEOS)
+
   // There is exactly one active discovery session. Request BlueZ to stop
   // discovery.
   DCHECK_EQ(NumDiscoverySessions(), 1);
diff --git a/device/bluetooth/bluez/bluetooth_adapter_bluez.h b/device/bluetooth/bluez/bluetooth_adapter_bluez.h
index 7dd9d5fc..62f1c82 100644
--- a/device/bluetooth/bluez/bluetooth_adapter_bluez.h
+++ b/device/bluetooth/bluez/bluetooth_adapter_bluez.h
@@ -33,6 +33,11 @@
 #include "device/bluetooth/dbus/bluetooth_profile_manager_client.h"
 #include "device/bluetooth/dbus/bluetooth_profile_service_provider.h"
 
+#if defined(OS_CHROMEOS)
+#include "mojo/public/cpp/bindings/remote.h"
+#include "services/data_decoder/public/mojom/ble_scan_parser.mojom.h"
+#endif  // defined(OS_CHROMEOS)
+
 namespace base {
 class TimeDelta;
 }  // namespace base
@@ -85,6 +90,11 @@
   using ServiceRecordErrorCallback =
       base::Callback<void(BluetoothServiceRecordBlueZ::ErrorCode)>;
 
+#if defined(OS_CHROMEOS)
+  using ScanRecordPtr = data_decoder::mojom::ScanRecordPtr;
+  using ScanRecordCallback = base::OnceCallback<void(ScanRecordPtr)>;
+#endif  // defined(OS_CHROMEOS)
+
   // Calls |init_callback| after a BluetoothAdapter is fully initialized.
   static base::WeakPtr<BluetoothAdapter> CreateAdapter(
       InitCallback init_callback);
@@ -175,6 +185,15 @@
                                          int16_t rssi,
                                          const std::vector<uint8_t>& eir);
 
+#if defined(OS_CHROMEOS)
+  // Announce to observers advertisement received from |device|.
+  void OnAdvertisementReceived(std::string device_address,
+                               std::string device_name,
+                               uint8_t rssi,
+                               uint16_t device_appearance,
+                               ScanRecordPtr scan_record);
+#endif  // defined(OS_CHROMEOS)
+
   // Announce to observers that |device| has changed its connected state.
   void NotifyDeviceConnectedStateChanged(BluetoothDeviceBlueZ* device,
                                          bool is_now_connected);
@@ -533,6 +552,9 @@
   // trials might not yet have been available. By scheduling a second update
   // sometime later, the field trials will be guaranteed to be present.
   base::OneShotTimer set_long_term_keys_after_first_time_install_timer_;
+
+  // Pointer for parsing BLE advertising packets out of process.
+  mojo::Remote<data_decoder::mojom::BleScanParser> ble_scan_parser_;
 #endif
 
   // Note: This should remain the last member so it'll be destroyed and
diff --git a/device/vr/public/mojom/BUILD.gn b/device/vr/public/mojom/BUILD.gn
index bbbf494..2edf3678 100644
--- a/device/vr/public/mojom/BUILD.gn
+++ b/device/vr/public/mojom/BUILD.gn
@@ -15,6 +15,7 @@
   ]
 
   public_deps = [
+    ":test_mojom",
     "//device/gamepad/public/mojom",
     "//gpu/ipc/common:interfaces",
     "//mojo/public/mojom/base",
diff --git a/device/vr/public/mojom/isolated_xr_service.mojom b/device/vr/public/mojom/isolated_xr_service.mojom
index 4b680da..e16e645 100644
--- a/device/vr/public/mojom/isolated_xr_service.mojom
+++ b/device/vr/public/mojom/isolated_xr_service.mojom
@@ -4,12 +4,11 @@
 
 module device.mojom;
 
+import "device/vr/public/mojom/browser_test_interfaces.mojom";
 import "device/vr/public/mojom/vr_service.mojom";
 import "mojo/public/mojom/base/time.mojom";
 import "ui/gfx/geometry/mojom/geometry.mojom";
 
-const string kVrIsolatedServiceName = "xr_device_service";
-
 // The XRSessionController lives in the xr runtime service, and corresponds to
 // a set of the XRSession bindings.  The client is the browser process, which
 // will pause or stop sessions depending events/state such as focus or other
@@ -213,3 +212,13 @@
   // Should only be called once.
   RequestDevices(IsolatedXRRuntimeProviderClient client);
 };
+
+// The main interface for the XR Device Service.
+interface XRDeviceService {
+  // Binds a IsolatedXRRuntimeProvider pipe in the service.
+  BindRuntimeProvider(pending_receiver<IsolatedXRRuntimeProvider> receiver);
+
+  // Binds the main testing interface pipe in the service. Only used by
+  // browser tests.
+  BindTestHook(pending_receiver<device_test.mojom.XRServiceTestHook> receiver);
+};
diff --git a/docs/ui/android/night_mode.md b/docs/ui/android/night_mode.md
index d6693be..26ae1bd7 100644
--- a/docs/ui/android/night_mode.md
+++ b/docs/ui/android/night_mode.md
@@ -144,7 +144,7 @@
 * Turn on power save mode (aka **battery saver**) on Android P+
 * Go to **Android Settings -> Developer options -> Night mode** on Android P
 * Go to **Android Settings -> Display -> Theme** on Android Q
-* [Set color scheme](https://cs.chromium.org/chromium/src/third_party/custom_tabs_client/src/customtabs/src/android/support/customtabs/CustomTabsIntent.java?) to `COLOR_SCHEME_DARK` on creating a `CustomTabsIntent.Builder`
+* [Set color scheme](https://cs.chromium.org/chromium/src/third_party/android_sdk/androidx_browser/browser/src/main/java/androidx/browser/customtabs/CustomTabsIntent.java?) to `COLOR_SCHEME_DARK` on creating a `CustomTabsIntent.Builder`
 
 Some tips:
 
diff --git a/extensions/browser/api/api_resource_manager.h b/extensions/browser/api/api_resource_manager.h
index ca038e0..9c0bd27 100644
--- a/extensions/browser/api/api_resource_manager.h
+++ b/extensions/browser/api/api_resource_manager.h
@@ -54,7 +54,7 @@
   }
 
   static scoped_refptr<base::SequencedTaskRunner> GetSequencedTaskRunner() {
-    return base::CreateSingleThreadTaskRunnerWithTraits({T::kThreadId});
+    return base::CreateSingleThreadTaskRunner({T::kThreadId});
   }
 };
 
diff --git a/extensions/browser/api/async_api_function.cc b/extensions/browser/api/async_api_function.cc
index 0d98aa8..5e5441f 100644
--- a/extensions/browser/api/async_api_function.cc
+++ b/extensions/browser/api/async_api_function.cc
@@ -17,7 +17,7 @@
 // AsyncApiFunction
 AsyncApiFunction::AsyncApiFunction()
     : work_task_runner_(
-          base::CreateSingleThreadTaskRunnerWithTraits({BrowserThread::IO})) {}
+          base::CreateSingleThreadTaskRunner({BrowserThread::IO})) {}
 
 AsyncApiFunction::~AsyncApiFunction() {}
 
@@ -58,7 +58,7 @@
 
 void AsyncApiFunction::AsyncWorkCompleted() {
   if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
-    bool rv = base::PostTaskWithTraits(
+    bool rv = base::PostTask(
         FROM_HERE, {BrowserThread::UI},
         base::BindOnce(&AsyncApiFunction::RespondOnUIThread, this));
     DCHECK(rv);
diff --git a/extensions/browser/api/bluetooth/bluetooth_api.cc b/extensions/browser/api/bluetooth/bluetooth_api.cc
index f4df9a0..ca254b92 100644
--- a/extensions/browser/api/bluetooth/bluetooth_api.cc
+++ b/extensions/browser/api/bluetooth/bluetooth_api.cc
@@ -31,7 +31,7 @@
 using device::BluetoothAdapter;
 using device::BluetoothDevice;
 
-namespace bluetooth = extensions::api::bluetooth;
+namespace bluetooth_api = extensions::api::bluetooth;
 namespace GetDevice = extensions::api::bluetooth::GetDevice;
 namespace GetDevices = extensions::api::bluetooth::GetDevices;
 
@@ -72,11 +72,14 @@
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   BLUETOOTH_LOG(EVENT) << "BluetoothAPI: " << browser_context_;
   EventRouter* event_router = EventRouter::Get(browser_context_);
+  event_router->RegisterObserver(
+      this, bluetooth_api::OnAdapterStateChanged::kEventName);
   event_router->RegisterObserver(this,
-                                 bluetooth::OnAdapterStateChanged::kEventName);
-  event_router->RegisterObserver(this, bluetooth::OnDeviceAdded::kEventName);
-  event_router->RegisterObserver(this, bluetooth::OnDeviceChanged::kEventName);
-  event_router->RegisterObserver(this, bluetooth::OnDeviceRemoved::kEventName);
+                                 bluetooth_api::OnDeviceAdded::kEventName);
+  event_router->RegisterObserver(this,
+                                 bluetooth_api::OnDeviceChanged::kEventName);
+  event_router->RegisterObserver(this,
+                                 bluetooth_api::OnDeviceRemoved::kEventName);
 }
 
 BluetoothAPI::~BluetoothAPI() {
@@ -116,9 +119,9 @@
 
 void BluetoothGetAdapterStateFunction::DoWork(
     scoped_refptr<BluetoothAdapter> adapter) {
-  bluetooth::AdapterState state;
+  bluetooth_api::AdapterState state;
   PopulateAdapterState(*adapter, &state);
-  Respond(ArgumentList(bluetooth::GetAdapterState::Results::Create(state)));
+  Respond(ArgumentList(bluetooth_api::GetAdapterState::Results::Create(state)));
 }
 
 BluetoothGetDevicesFunction::BluetoothGetDevicesFunction() = default;
@@ -139,7 +142,8 @@
   BluetoothAdapter::DeviceList devices;
 #if defined(OS_CHROMEOS)
   // Default filter values.
-  bluetooth::FilterType filter_type = bluetooth::FilterType::FILTER_TYPE_ALL;
+  bluetooth_api::FilterType filter_type =
+      bluetooth_api::FilterType::FILTER_TYPE_ALL;
   int limit = 0; /*no limit*/
   if (params_->filter) {
     filter_type = params_->filter->filter_type;
@@ -159,8 +163,8 @@
     const BluetoothDevice* device = *iter;
     DCHECK(device);
 
-    bluetooth::Device extension_device;
-    bluetooth::BluetoothDeviceToApiDevice(*device, &extension_device);
+    bluetooth_api::Device extension_device;
+    bluetooth_api::BluetoothDeviceToApiDevice(*device, &extension_device);
 
     device_list->Append(extension_device.ToValue());
   }
@@ -183,8 +187,8 @@
 
   BluetoothDevice* device = adapter->GetDevice(params_->device_address);
   if (device) {
-    bluetooth::Device extension_device;
-    bluetooth::BluetoothDeviceToApiDevice(*device, &extension_device);
+    bluetooth_api::Device extension_device;
+    bluetooth_api::BluetoothDeviceToApiDevice(*device, &extension_device);
     Respond(OneArgument(extension_device.ToValue()));
   } else {
     Respond(Error(kInvalidDevice));
diff --git a/extensions/browser/api/declarative/declarative_api.cc b/extensions/browser/api/declarative/declarative_api.cc
index 6fa9f7b3..e5cf129 100644
--- a/extensions/browser/api/declarative/declarative_api.cc
+++ b/extensions/browser/api/declarative/declarative_api.cc
@@ -188,8 +188,7 @@
     return RespondNow(RunAsyncOnCorrectThread());
 
   scoped_refptr<base::SingleThreadTaskRunner> thread_task_runner =
-      base::CreateSingleThreadTaskRunnerWithTraits(
-          {rules_registry_->owner_thread()});
+      base::CreateSingleThreadTaskRunner({rules_registry_->owner_thread()});
   base::PostTaskAndReplyWithResult(
       thread_task_runner.get(), FROM_HERE,
       base::BindOnce(&RulesFunction::RunAsyncOnCorrectThread, this),
diff --git a/extensions/browser/api/declarative/rules_cache_delegate.cc b/extensions/browser/api/declarative/rules_cache_delegate.cc
index 4e29c36..22e5f35d 100644
--- a/extensions/browser/api/declarative/rules_cache_delegate.cc
+++ b/extensions/browser/api/declarative/rules_cache_delegate.cc
@@ -143,7 +143,7 @@
   if (notified_registry_ || !waiting_for_extensions_.empty())
     return;
 
-  base::PostTaskWithTraits(
+  base::PostTask(
       FROM_HERE, {rules_registry_thread_},
       base::BindOnce(&RulesRegistry::MarkReady, registry_, storage_init_time_));
   notified_registry_ = true;
@@ -211,10 +211,9 @@
     std::unique_ptr<base::Value> value) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   DCHECK_EQ(Type::kPersistent, type_);
-  base::PostTaskWithTraits(
-      FROM_HERE, {rules_registry_thread_},
-      base::BindOnce(&RulesRegistry::DeserializeAndAddRules, registry_,
-                     extension_id, std::move(value)));
+  base::PostTask(FROM_HERE, {rules_registry_thread_},
+                 base::BindOnce(&RulesRegistry::DeserializeAndAddRules,
+                                registry_, extension_id, std::move(value)));
 
   waiting_for_extensions_.erase(extension_id);
 
diff --git a/extensions/browser/api/declarative/rules_registry.cc b/extensions/browser/api/declarative/rules_registry.cc
index 0081dab..ab59b6b 100644
--- a/extensions/browser/api/declarative/rules_registry.cc
+++ b/extensions/browser/api/declarative/rules_registry.cc
@@ -348,7 +348,7 @@
 
   std::vector<const api::events::Rule*> new_rules;
   GetRules(extension_id, &rules_, &new_rules);
-  base::PostTaskWithTraits(
+  base::PostTask(
       FROM_HERE, {content::BrowserThread::UI},
       base::BindOnce(&RulesCacheDelegate::UpdateRules, cache_delegate_,
                      extension_id, RulesToValue(new_rules)));
diff --git a/extensions/browser/api/declarative/rules_registry_service.cc b/extensions/browser/api/declarative/rules_registry_service.cc
index dedb8a3..de614d4 100644
--- a/extensions/browser/api/declarative/rules_registry_service.cc
+++ b/extensions/browser/api/declarative/rules_registry_service.cc
@@ -231,10 +231,10 @@
     if (content::BrowserThread::CurrentlyOn(registry->owner_thread())) {
       (registry.get()->*notification_callback)(extension);
     } else {
-      base::PostTaskWithTraits(FROM_HERE, {registry->owner_thread()},
-                               base::BindOnce(&NotifyWithExtensionSafe,
-                                              base::WrapRefCounted(extension),
-                                              notification_callback, registry));
+      base::PostTask(FROM_HERE, {registry->owner_thread()},
+                     base::BindOnce(&NotifyWithExtensionSafe,
+                                    base::WrapRefCounted(extension),
+                                    notification_callback, registry));
     }
   }
 }
diff --git a/extensions/browser/api/declarative_net_request/file_sequence_helper.cc b/extensions/browser/api/declarative_net_request/file_sequence_helper.cc
index d113845..705f376 100644
--- a/extensions/browser/api/declarative_net_request/file_sequence_helper.cc
+++ b/extensions/browser/api/declarative_net_request/file_sequence_helper.cc
@@ -369,7 +369,7 @@
   }
 
   if (success) {
-    base::PostTaskWithTraits(
+    base::PostTask(
         FROM_HERE, {content::BrowserThread::UI},
         base::BindOnce(std::move(ui_callback), std::move(load_data)));
     return;
@@ -402,10 +402,9 @@
                                               UpdateDynamicRulesStatus status) {
     base::UmaHistogramEnumeration(kUpdateDynamicRulesStatusHistogram, status);
 
-    base::PostTaskWithTraits(
-        FROM_HERE, {content::BrowserThread::UI},
-        base::BindOnce(std::move(ui_callback), std::move(load_data),
-                       std::move(error)));
+    base::PostTask(FROM_HERE, {content::BrowserThread::UI},
+                   base::BindOnce(std::move(ui_callback), std::move(load_data),
+                                  std::move(error)));
   };
 
   int new_ruleset_checksum = -1;
@@ -450,9 +449,8 @@
   }
 
   // The UI thread will handle success or failure.
-  base::PostTaskWithTraits(
-      FROM_HERE, {content::BrowserThread::UI},
-      base::BindOnce(std::move(ui_callback), std::move(load_data)));
+  base::PostTask(FROM_HERE, {content::BrowserThread::UI},
+                 base::BindOnce(std::move(ui_callback), std::move(load_data)));
 }
 
 }  // namespace declarative_net_request
diff --git a/extensions/browser/api/declarative_net_request/utils.cc b/extensions/browser/api/declarative_net_request/utils.cc
index 393db78..2a1a19f 100644
--- a/extensions/browser/api/declarative_net_request/utils.cc
+++ b/extensions/browser/api/declarative_net_request/utils.cc
@@ -150,8 +150,8 @@
   if (content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) {
     ClearRendererCacheOnUI();
   } else {
-    base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI},
-                             base::BindOnce(&ClearRendererCacheOnUI));
+    base::PostTask(FROM_HERE, {content::BrowserThread::UI},
+                   base::BindOnce(&ClearRendererCacheOnUI));
   }
 }
 
diff --git a/extensions/browser/api/execute_code_function.cc b/extensions/browser/api/execute_code_function.cc
index 26854ab..002b4997 100644
--- a/extensions/browser/api/execute_code_function.cc
+++ b/extensions/browser/api/execute_code_function.cc
@@ -245,9 +245,10 @@
     std::unique_ptr<std::string> data(
         new std::string(resource.data(), resource.size()));
 
-    base::PostTaskWithTraitsAndReplyWithResult(
+    base::PostTaskAndReplyWithResult(
         FROM_HERE,
-        {base::MayBlock(), base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
+        {base::ThreadPool(), base::MayBlock(),
+         base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
         base::BindOnce(&ExecuteCodeFunction::
                            GetFileURLAndLocalizeComponentResourceInBackground,
                        this, std::move(data), extension_id, extension_path,
diff --git a/extensions/browser/content_verifier.cc b/extensions/browser/content_verifier.cc
index 83ccff6..3a3679ca 100644
--- a/extensions/browser/content_verifier.cc
+++ b/extensions/browser/content_verifier.cc
@@ -280,7 +280,7 @@
     if (was_cancelled)
       return;
 
-    base::PostTaskWithTraits(
+    base::PostTask(
         FROM_HERE, {content::BrowserThread::IO},
         base::BindOnce(std::move(callback), content_hash, was_cancelled));
   }
@@ -404,9 +404,8 @@
 void ContentVerifier::Shutdown() {
   shutdown_on_ui_ = true;
   delegate_->Shutdown();
-  base::PostTaskWithTraits(
-      FROM_HERE, {content::BrowserThread::IO},
-      base::BindOnce(&ContentVerifier::ShutdownOnIO, this));
+  base::PostTask(FROM_HERE, {content::BrowserThread::IO},
+                 base::BindOnce(&ContentVerifier::ShutdownOnIO, this));
   observer_.RemoveAll();
 }
 
@@ -468,10 +467,9 @@
     // TODO(lazyboy): Make CreateJobFor return a scoped_refptr instead of raw
     // pointer to fix this. Also add unit test to exercise this code path
     // explicitly.
-    base::PostTaskWithTraits(
-        FROM_HERE, {content::BrowserThread::IO},
-        base::BindOnce(base::DoNothing::Once<ContentHashCallback>(),
-                       std::move(callback)));
+    base::PostTask(FROM_HERE, {content::BrowserThread::IO},
+                   base::BindOnce(base::DoNothing::Once<ContentHashCallback>(),
+                                  std::move(callback)));
     return;
   }
 
@@ -480,9 +478,8 @@
   auto cache_iter = cache_.find(cache_key);
   if (cache_iter != cache_.end()) {
     // Currently, we expect |callback| to be called asynchronously.
-    base::PostTaskWithTraits(
-        FROM_HERE, {content::BrowserThread::IO},
-        base::BindOnce(std::move(callback), cache_iter->second));
+    base::PostTask(FROM_HERE, {content::BrowserThread::IO},
+                   base::BindOnce(std::move(callback), cache_iter->second));
     return;
   }
 
@@ -503,9 +500,9 @@
 void ContentVerifier::VerifyFailed(const ExtensionId& extension_id,
                                    ContentVerifyJob::FailureReason reason) {
   if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) {
-    base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI},
-                             base::BindOnce(&ContentVerifier::VerifyFailed,
-                                            this, extension_id, reason));
+    base::PostTask(FROM_HERE, {content::BrowserThread::UI},
+                   base::BindOnce(&ContentVerifier::VerifyFailed, this,
+                                  extension_id, reason));
     return;
   }
   if (shutdown_on_ui_)
@@ -524,7 +521,7 @@
     return;
 
   if (delegate_->ShouldBeVerified(*extension)) {
-    base::PostTaskWithTraits(
+    base::PostTask(
         FROM_HERE, {content::BrowserThread::IO},
         base::BindOnce(&ContentVerifier::OnExtensionLoadedOnIO, this,
                        extension->id(), extension->path(), extension->version(),
@@ -553,10 +550,9 @@
     UnloadedExtensionReason reason) {
   if (shutdown_on_ui_)
     return;
-  base::PostTaskWithTraits(
-      FROM_HERE, {content::BrowserThread::IO},
-      base::BindOnce(&ContentVerifier::OnExtensionUnloadedOnIO, this,
-                     extension->id(), extension->version()));
+  base::PostTask(FROM_HERE, {content::BrowserThread::IO},
+                 base::BindOnce(&ContentVerifier::OnExtensionUnloadedOnIO, this,
+                                extension->id(), extension->version()));
 }
 
 GURL ContentVerifier::GetSignatureFetchUrlForTest(
@@ -616,7 +612,7 @@
   // Create a new mojo pipe. It's safe to pass this around and use immediately,
   // even though it needs to finish initialization on the UI thread.
   network::mojom::URLLoaderFactoryPtr url_loader_factory_ptr;
-  base::PostTaskWithTraits(
+  base::PostTask(
       FROM_HERE, {content::BrowserThread::UI},
       base::BindOnce(&ContentVerifier::BindURLLoaderFactoryRequestOnUIThread,
                      this, mojo::MakeRequest(&url_loader_factory_ptr)));
diff --git a/extensions/browser/content_verifier/test_utils.cc b/extensions/browser/content_verifier/test_utils.cc
index 0927ab8..3830b2e1 100644
--- a/extensions/browser/content_verifier/test_utils.cc
+++ b/extensions/browser/content_verifier/test_utils.cc
@@ -101,11 +101,10 @@
     const base::FilePath& relative_path,
     ContentVerifyJob::FailureReason failure_reason) {
   if (!content::BrowserThread::CurrentlyOn(creation_thread_)) {
-    base::PostTaskWithTraits(
-        FROM_HERE, {creation_thread_},
-        base::BindOnce(&TestContentVerifyJobObserver::JobFinished,
-                       base::Unretained(this), extension_id, relative_path,
-                       failure_reason));
+    base::PostTask(FROM_HERE, {creation_thread_},
+                   base::BindOnce(&TestContentVerifyJobObserver::JobFinished,
+                                  base::Unretained(this), extension_id,
+                                  relative_path, failure_reason));
     return;
   }
   Result result = failure_reason == ContentVerifyJob::NONE ? Result::SUCCESS
@@ -194,7 +193,7 @@
 void VerifierObserver::OnFetchComplete(const ExtensionId& extension_id,
                                        bool success) {
   if (!content::BrowserThread::CurrentlyOn(creation_thread_)) {
-    base::PostTaskWithTraits(
+    base::PostTask(
         FROM_HERE, {creation_thread_},
         base::BindOnce(&VerifierObserver::OnFetchComplete,
                        base::Unretained(this), extension_id, success));
diff --git a/extensions/browser/content_verify_job.cc b/extensions/browser/content_verify_job.cc
index f36b751..f7d77dc 100644
--- a/extensions/browser/content_verify_job.cc
+++ b/extensions/browser/content_verify_job.cc
@@ -90,8 +90,9 @@
     g_content_verify_job_test_observer->JobStarted(extension_id_,
                                                    relative_path_);
   // Build |hash_reader_|.
-  base::PostTaskWithTraitsAndReplyWithResult(
-      FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
+  base::PostTaskAndReplyWithResult(
+      FROM_HERE,
+      {base::ThreadPool(), base::MayBlock(), base::TaskPriority::USER_VISIBLE},
       base::BindOnce(&ContentHashReader::Create, relative_path_, content_hash),
       base::BindOnce(&ContentVerifyJob::OnHashesReady, this));
 }
diff --git a/extensions/browser/content_verify_job_unittest.cc b/extensions/browser/content_verify_job_unittest.cc
index 6b3e498c..f6d528b1 100644
--- a/extensions/browser/content_verify_job_unittest.cc
+++ b/extensions/browser/content_verify_job_unittest.cc
@@ -148,10 +148,9 @@
 
  private:
   void StartJob(scoped_refptr<ContentVerifyJob> job) {
-    base::PostTaskWithTraits(
-        FROM_HERE, {content::BrowserThread::IO},
-        base::BindOnce(&ContentVerifyJob::Start, job,
-                       base::Unretained(content_verifier_.get())));
+    base::PostTask(FROM_HERE, {content::BrowserThread::IO},
+                   base::BindOnce(&ContentVerifyJob::Start, job,
+                                  base::Unretained(content_verifier_.get())));
   }
 
   scoped_refptr<InfoMap> extension_info_map_;
diff --git a/extensions/browser/events/event_ack_data.cc b/extensions/browser/events/event_ack_data.cc
index a668352..d9d0080 100644
--- a/extensions/browser/events/event_ack_data.cc
+++ b/extensions/browser/events/event_ack_data.cc
@@ -101,8 +101,7 @@
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
   content::ServiceWorkerContext::RunTask(
-      base::CreateSingleThreadTaskRunnerWithTraits(
-          {content::BrowserThread::IO}),
+      base::CreateSingleThreadTaskRunner({content::BrowserThread::IO}),
       FROM_HERE, context,
       base::BindOnce(&EventAckData::StartExternalRequestOnIO, context,
                      render_process_id, version_id, event_id, unacked_events_));
@@ -117,8 +116,7 @@
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
   content::ServiceWorkerContext::RunTask(
-      base::CreateSingleThreadTaskRunnerWithTraits(
-          {content::BrowserThread::IO}),
+      base::CreateSingleThreadTaskRunner({content::BrowserThread::IO}),
       FROM_HERE, context,
       base::BindOnce(&EventAckData::FinishExternalRequestOnIO, context,
                      render_process_id, version_id, event_id, unacked_events_,
diff --git a/extensions/browser/extension_function_histogram_value.h b/extensions/browser/extension_function_histogram_value.h
index 69e40ad..efc7df8 100644
--- a/extensions/browser/extension_function_histogram_value.h
+++ b/extensions/browser/extension_function_histogram_value.h
@@ -1429,6 +1429,11 @@
   AUTOTESTPRIVATE_GETSHELFITEMS = 1366,
   MANAGEMENT_INSTALLREPLACEMENTANDROIDAPP = 1367,
   MANAGEMENT_CANINSTALLREPLACEMENTANDROIDAPP = 1368,
+  AUTOTESTPRIVATE_REGISTERCOMPONENT = 1369,
+  LOGINSCREENSTORAGE_STOREPERSISTENTDATA = 1370,
+  LOGINSCREENSTORAGE_RETRIEVEPERSISTENTDATA = 1371,
+  LOGINSCREENSTORAGE_STORECREDENTIALS = 1372,
+  LOGINSCREENSTORAGE_RETRIEVECREDENTIALS = 1373,
   // Last entry: Add new entries above, then run:
   // python tools/metrics/histograms/update_extension_histograms.py
   ENUM_BOUNDARY
diff --git a/extensions/browser/extension_protocols.cc b/extensions/browser/extension_protocols.cc
index 6000521..771b0909 100644
--- a/extensions/browser/extension_protocols.cc
+++ b/extensions/browser/extension_protocols.cc
@@ -535,8 +535,8 @@
 
     scoped_refptr<ContentVerifier> content_verifier =
         extension_info_map_->content_verifier();
-    base::PostTaskWithTraitsAndReply(
-        FROM_HERE, {base::MayBlock()},
+    base::PostTaskAndReply(
+        FROM_HERE, {base::ThreadPool(), base::MayBlock()},
         base::BindOnce(&ReadResourceFilePathAndLastModifiedTime, resource,
                        directory_path, base::Unretained(read_file_path),
                        base::Unretained(last_modified_time)),
@@ -559,7 +559,7 @@
       bool send_cors_header) {
     request.url = net::FilePathToFileURL(*read_file_path);
 
-    base::PostTaskWithTraits(
+    base::PostTask(
         FROM_HERE, {content::BrowserThread::IO},
         base::BindOnce(
             &StartVerifyJob, std::move(request), std::move(loader),
diff --git a/extensions/browser/extension_user_script_loader.cc b/extensions/browser/extension_user_script_loader.cc
index 483767bd1..276012c9 100644
--- a/extensions/browser/extension_user_script_loader.cc
+++ b/extensions/browser/extension_user_script_loader.cc
@@ -78,8 +78,8 @@
 
 void ForwardVerifyContentToIO(const VerifyContentInfo& info) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO},
-                           base::BindOnce(&VerifyContent, info));
+  base::PostTask(FROM_HERE, {content::BrowserThread::IO},
+                 base::BindOnce(&VerifyContent, info));
 }
 
 // Loads user scripts from the extension who owns these scripts.
@@ -116,7 +116,7 @@
     if (verifier.get()) {
       // Call VerifyContent() after yielding on UI thread so it is ensured that
       // ContentVerifierIOData is populated at the time we call VerifyContent().
-      base::PostTaskWithTraits(
+      base::PostTask(
           FROM_HERE, {content::BrowserThread::UI},
           base::BindOnce(
               &ForwardVerifyContentToIO,
@@ -194,10 +194,9 @@
   LoadUserScripts(user_scripts.get(), hosts_info, added_script_ids, verifier);
   base::ReadOnlySharedMemoryRegion memory =
       UserScriptLoader::Serialize(*user_scripts);
-  base::PostTaskWithTraits(
-      FROM_HERE, {content::BrowserThread::UI},
-      base::BindOnce(std::move(callback), std::move(user_scripts),
-                     std::move(memory)));
+  base::PostTask(FROM_HERE, {content::BrowserThread::UI},
+                 base::BindOnce(std::move(callback), std::move(user_scripts),
+                                std::move(memory)));
 }
 
 }  // namespace
diff --git a/extensions/browser/guest_view/extensions_guest_view_message_filter.cc b/extensions/browser/guest_view/extensions_guest_view_message_filter.cc
index 55c88ac..c935537 100644
--- a/extensions/browser/guest_view/extensions_guest_view_message_filter.cc
+++ b/extensions/browser/guest_view/extensions_guest_view_message_filter.cc
@@ -117,20 +117,19 @@
     int32_t element_instance_id,
     const gfx::Size& element_size,
     mime_handler::BeforeUnloadControlPtr before_unload_control) {
-  base::PostTaskWithTraits(
-      FROM_HERE, {content::BrowserThread::UI},
-      base::BindOnce(&ExtensionsGuestViewMessageFilter::
-                         CreateMimeHandlerViewGuestOnUIThread,
-                     this, render_frame_id, view_id, element_instance_id,
-                     element_size, before_unload_control.PassInterface(),
-                     false));
+  base::PostTask(FROM_HERE, {content::BrowserThread::UI},
+                 base::BindOnce(&ExtensionsGuestViewMessageFilter::
+                                    CreateMimeHandlerViewGuestOnUIThread,
+                                this, render_frame_id, view_id,
+                                element_instance_id, element_size,
+                                before_unload_control.PassInterface(), false));
 }
 
 void ExtensionsGuestViewMessageFilter::ReadyToCreateMimeHandlerView(
     int32_t render_frame_id,
     bool success) {
   if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) {
-    base::PostTaskWithTraits(
+    base::PostTask(
         FROM_HERE, {content::BrowserThread::UI},
         base::BindOnce(
             &ExtensionsGuestViewMessageFilter::ReadyToCreateMimeHandlerView,
@@ -203,13 +202,12 @@
     const gfx::Size& element_size,
     content::mojom::TransferrableURLLoaderPtr transferrable_url_loader) {
   if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) {
-    base::PostTaskWithTraits(
-        FROM_HERE, {content::BrowserThread::UI},
-        base::BindOnce(&ExtensionsGuestViewMessageFilter::
-                           CreateEmbeddedMimeHandlerViewGuest,
-                       this, render_frame_id, tab_id, original_url,
-                       element_instance_id, element_size,
-                       base::Passed(&transferrable_url_loader)));
+    base::PostTask(FROM_HERE, {content::BrowserThread::UI},
+                   base::BindOnce(&ExtensionsGuestViewMessageFilter::
+                                      CreateEmbeddedMimeHandlerViewGuest,
+                                  this, render_frame_id, tab_id, original_url,
+                                  element_instance_id, element_size,
+                                  base::Passed(&transferrable_url_loader)));
     return;
   }
 
diff --git a/extensions/browser/guest_view/mime_handler_view/mime_handler_view_attach_helper.cc b/extensions/browser/guest_view/mime_handler_view/mime_handler_view_attach_helper.cc
index fd82f54e..e5c2371 100644
--- a/extensions/browser/guest_view/mime_handler_view/mime_handler_view_attach_helper.cc
+++ b/extensions/browser/guest_view/mime_handler_view/mime_handler_view_attach_helper.cc
@@ -102,7 +102,7 @@
       SkColorGetB(color), mime_type.c_str(), token.c_str());
   payload->assign(html_str);
   *data_pipe_size = kFullPageMimeHandlerViewDataPipeSize;
-  base::PostTaskWithTraitsAndReply(
+  base::PostTaskAndReply(
       FROM_HERE, {BrowserThread::UI},
       base::BindOnce(CreateFullPageMimeHandlerView,
                      navigating_frame_tree_node_id, resource_url, mime_type,
diff --git a/extensions/browser/guest_view/mime_handler_view/test_mime_handler_view_guest.cc b/extensions/browser/guest_view/mime_handler_view/test_mime_handler_view_guest.cc
index 533e711f..1bff192 100644
--- a/extensions/browser/guest_view/mime_handler_view/test_mime_handler_view_guest.cc
+++ b/extensions/browser/guest_view/mime_handler_view/test_mime_handler_view_guest.cc
@@ -45,7 +45,7 @@
   // Delay the creation of the guest's WebContents if |delay_| is set.
   if (delay_) {
     auto delta = base::TimeDelta::FromMilliseconds(delay_);
-    base::PostDelayedTaskWithTraits(
+    base::PostDelayedTask(
         FROM_HERE, {content::BrowserThread::UI},
         base::BindOnce(&TestMimeHandlerViewGuest::CallBaseCreateWebContents,
                        weak_ptr_factory_.GetWeakPtr(),
diff --git a/extensions/browser/guest_view/web_view/web_view_permission_helper.cc b/extensions/browser/guest_view/web_view/web_view_permission_helper.cc
index 985550c1..ab68297 100644
--- a/extensions/browser/guest_view/web_view/web_view_permission_helper.cc
+++ b/extensions/browser/guest_view/web_view/web_view_permission_helper.cc
@@ -279,9 +279,9 @@
 void WebViewPermissionHelper::RequestFileSystemPermission(
     const GURL& url,
     bool allowed_by_default,
-    const base::Callback<void(bool)>& callback) {
+    base::OnceCallback<void(bool)> callback) {
   web_view_permission_helper_delegate_->RequestFileSystemPermission(
-      url, allowed_by_default, callback);
+      url, allowed_by_default, std::move(callback));
 }
 
 void WebViewPermissionHelper::FileSystemAccessedAsync(int render_process_id,
diff --git a/extensions/browser/guest_view/web_view/web_view_permission_helper.h b/extensions/browser/guest_view/web_view/web_view_permission_helper.h
index d368a221..33cb12ef 100644
--- a/extensions/browser/guest_view/web_view/web_view_permission_helper.h
+++ b/extensions/browser/guest_view/web_view/web_view_permission_helper.h
@@ -82,7 +82,7 @@
 
   void RequestFileSystemPermission(const GURL& url,
                                    bool allowed_by_default,
-                                   const base::Callback<void(bool)>& callback);
+                                   base::OnceCallback<void(bool)> callback);
 
   // Called when file system access is requested by the guest content using the
   // asynchronous HTML5 file system API. The request is plumbed through the
diff --git a/extensions/browser/guest_view/web_view/web_view_permission_helper_delegate.h b/extensions/browser/guest_view/web_view/web_view_permission_helper_delegate.h
index 925bf95..0aaecd8 100644
--- a/extensions/browser/guest_view/web_view/web_view_permission_helper_delegate.h
+++ b/extensions/browser/guest_view/web_view/web_view_permission_helper_delegate.h
@@ -42,7 +42,7 @@
   virtual void RequestFileSystemPermission(
       const GURL& url,
       bool allowed_by_default,
-      const base::Callback<void(bool)>& callback) {}
+      base::OnceCallback<void(bool)> callback) {}
 
   // Called when file system access is requested by the guest content using the
   // asynchronous HTML5 file system API. The request is plumbed through the
diff --git a/extensions/browser/image_loader.cc b/extensions/browser/image_loader.cc
index d5e674cf..7c58a7e 100644
--- a/extensions/browser/image_loader.cc
+++ b/extensions/browser/image_loader.cc
@@ -263,8 +263,9 @@
     const std::vector<ImageRepresentation>& info_list,
     ImageLoaderImageCallback callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  base::PostTaskWithTraitsAndReplyWithResult(
-      FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
+  base::PostTaskAndReplyWithResult(
+      FROM_HERE,
+      {base::ThreadPool(), base::MayBlock(), base::TaskPriority::USER_VISIBLE},
       base::BindOnce(LoadImagesBlocking, info_list,
                      LoadResourceBitmaps(extension, info_list)),
       base::BindOnce(&ImageLoader::ReplyBack, weak_ptr_factory_.GetWeakPtr(),
@@ -276,8 +277,9 @@
     const std::vector<ImageRepresentation>& info_list,
     ImageLoaderImageFamilyCallback callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  base::PostTaskWithTraitsAndReplyWithResult(
-      FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
+  base::PostTaskAndReplyWithResult(
+      FROM_HERE,
+      {base::ThreadPool(), base::MayBlock(), base::TaskPriority::USER_VISIBLE},
       base::BindOnce(LoadImagesBlocking, info_list,
                      LoadResourceBitmaps(extension, info_list)),
       base::BindOnce(&ImageLoader::ReplyBackWithImageFamily,
diff --git a/extensions/browser/process_manager.cc b/extensions/browser/process_manager.cc
index e0f053ce..145c7ef 100644
--- a/extensions/browser/process_manager.cc
+++ b/extensions/browser/process_manager.cc
@@ -255,8 +255,8 @@
     : extension_registry_(extension_registry),
       site_instance_(content::SiteInstance::Create(context)),
       browser_context_(context),
-      worker_task_runner_(base::CreateSingleThreadTaskRunnerWithTraits(
-          {content::BrowserThread::IO})),
+      worker_task_runner_(
+          base::CreateSingleThreadTaskRunner({content::BrowserThread::IO})),
       startup_background_hosts_created_(false),
       last_background_close_sequence_id_(0),
       process_observer_(this) {
diff --git a/extensions/browser/requirements_checker.cc b/extensions/browser/requirements_checker.cc
index 307ba79..f2858eb 100644
--- a/extensions/browser/requirements_checker.cc
+++ b/extensions/browser/requirements_checker.cc
@@ -77,9 +77,9 @@
   // to maintain the assumption in
   // ExtensionService::LoadExtensionsFromCommandLineFlag(). Remove these helper
   // functions after crbug.com/708354 is addressed.
-  base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI},
-                           base::BindOnce(&RequirementsChecker::RunCallback,
-                                          weak_ptr_factory_.GetWeakPtr()));
+  base::PostTask(FROM_HERE, {content::BrowserThread::UI},
+                 base::BindOnce(&RequirementsChecker::RunCallback,
+                                weak_ptr_factory_.GetWeakPtr()));
 }
 
 void RequirementsChecker::RunCallback() {
diff --git a/extensions/browser/sandboxed_unpacker.cc b/extensions/browser/sandboxed_unpacker.cc
index 37caab6..17f70a1 100644
--- a/extensions/browser/sandboxed_unpacker.cc
+++ b/extensions/browser/sandboxed_unpacker.cc
@@ -217,8 +217,7 @@
 
 SandboxedUnpackerClient::SandboxedUnpackerClient()
     : RefCountedDeleteOnSequence<SandboxedUnpackerClient>(
-          base::CreateSingleThreadTaskRunnerWithTraits(
-              {content::BrowserThread::UI})) {
+          base::CreateSingleThreadTaskRunner({content::BrowserThread::UI})) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
diff --git a/extensions/browser/service_worker_task_queue.cc b/extensions/browser/service_worker_task_queue.cc
index 7f43eb3..3c00ffb9 100644
--- a/extensions/browser/service_worker_task_queue.cc
+++ b/extensions/browser/service_worker_task_queue.cc
@@ -70,11 +70,10 @@
     int process_id,
     int thread_id) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-  base::PostTaskWithTraits(
-      FROM_HERE, {content::BrowserThread::UI},
-      base::BindOnce(&ServiceWorkerTaskQueue::DidStartWorkerForScope,
-                     task_queue, context_id, version_id, process_id,
-                     thread_id));
+  base::PostTask(FROM_HERE, {content::BrowserThread::UI},
+                 base::BindOnce(&ServiceWorkerTaskQueue::DidStartWorkerForScope,
+                                task_queue, context_id, version_id, process_id,
+                                thread_id));
 }
 
 // static
@@ -241,8 +240,7 @@
       partition->GetServiceWorkerContext();
 
   content::ServiceWorkerContext::RunTask(
-      base::CreateSingleThreadTaskRunnerWithTraits(
-          {content::BrowserThread::IO}),
+      base::CreateSingleThreadTaskRunner({content::BrowserThread::IO}),
       FROM_HERE, service_worker_context,
       base::BindOnce(&ServiceWorkerTaskQueue::StartServiceWorkerOnIOToRunTasks,
                      weak_factory_.GetWeakPtr(), context_id,
diff --git a/extensions/browser/updater/extension_installer.cc b/extensions/browser/updater/extension_installer.cc
index ea5d3d7..902c291 100644
--- a/extensions/browser/updater/extension_installer.cc
+++ b/extensions/browser/updater/extension_installer.cc
@@ -42,8 +42,8 @@
 void ExtensionInstaller::Install(const base::FilePath& unpack_path,
                                  const std::string& public_key,
                                  UpdateClientCallback update_client_callback) {
-  auto ui_thread = base::CreateSingleThreadTaskRunnerWithTraits(
-      {content::BrowserThread::UI});
+  auto ui_thread =
+      base::CreateSingleThreadTaskRunner({content::BrowserThread::UI});
   DCHECK(ui_thread);
   DCHECK(!extension_installer_callback_.is_null());
   if (base::PathExists(unpack_path)) {
diff --git a/extensions/browser/updater/update_data_provider.cc b/extensions/browser/updater/update_data_provider.cc
index f653bd5c..ecb4356 100644
--- a/extensions/browser/updater/update_data_provider.cc
+++ b/extensions/browser/updater/update_data_provider.cc
@@ -143,14 +143,15 @@
           << public_key;
 
   if (!browser_context_) {
-    base::PostTaskWithTraits(
-        FROM_HERE, {base::TaskPriority::BEST_EFFORT, base::MayBlock()},
+    base::PostTask(
+        FROM_HERE,
+        {base::ThreadPool(), base::TaskPriority::BEST_EFFORT, base::MayBlock()},
         base::BindOnce(base::IgnoreResult(&base::DeleteFile), unpacked_dir,
                        true));
     return;
   }
 
-  base::CreateSingleThreadTaskRunnerWithTraits({content::BrowserThread::UI})
+  base::CreateSingleThreadTaskRunner({content::BrowserThread::UI})
       ->PostTask(
           FROM_HERE,
           base::BindOnce(InstallUpdateCallback, browser_context_, extension_id,
diff --git a/extensions/browser/value_store/value_store_frontend.cc b/extensions/browser/value_store/value_store_frontend.cc
index f167cc5..6e7348c 100644
--- a/extensions/browser/value_store/value_store_frontend.cc
+++ b/extensions/browser/value_store/value_store_frontend.cc
@@ -45,10 +45,9 @@
                    << " failed: " << result.status().message;
     }
 
-    base::PostTaskWithTraits(
-        FROM_HERE, {BrowserThread::UI},
-        base::BindOnce(&ValueStoreFrontend::Backend::RunCallback, this,
-                       callback, std::move(value)));
+    base::PostTask(FROM_HERE, {BrowserThread::UI},
+                   base::BindOnce(&ValueStoreFrontend::Backend::RunCallback,
+                                  this, callback, std::move(value)));
   }
 
   void Set(const std::string& key, std::unique_ptr<base::Value> value) {
diff --git a/extensions/browser/web_ui_user_script_loader.cc b/extensions/browser/web_ui_user_script_loader.cc
index 4bd64d9..01f1af4e 100644
--- a/extensions/browser/web_ui_user_script_loader.cc
+++ b/extensions/browser/web_ui_user_script_loader.cc
@@ -154,9 +154,9 @@
 }
 
 void WebUIUserScriptLoader::OnWebUIURLFetchComplete() {
-  base::PostTaskWithTraits(
-      FROM_HERE, {base::MayBlock()},
-      base::BindOnce(
-          &SerializeOnBlockingTask, base::SequencedTaskRunnerHandle::Get(),
-          std::move(user_scripts_cache_), std::move(scripts_loaded_callback_)));
+  base::PostTask(FROM_HERE, {base::ThreadPool(), base::MayBlock()},
+                 base::BindOnce(&SerializeOnBlockingTask,
+                                base::SequencedTaskRunnerHandle::Get(),
+                                std::move(user_scripts_cache_),
+                                std::move(scripts_loaded_callback_)));
 }
diff --git a/extensions/common/api/_behavior_features.json b/extensions/common/api/_behavior_features.json
index 5bff24f..dfdf09c 100644
--- a/extensions/common/api/_behavior_features.json
+++ b/extensions/common/api/_behavior_features.json
@@ -89,7 +89,8 @@
     "whitelist": [
       "FA84F98B32AFC3013F5711F8711F8F38DB210AB7", // Sign-in Screen Test Extension
       "7FE4A999359A456C4B0FB7B7AD85CEA29CA50519", // chrome.loginScreenUi Test Extension
-      "E219EE36A3B40612FD2A8CD6937B03EF0C97D3FE"  // Imprivata (login screen)
+      "E219EE36A3B40612FD2A8CD6937B03EF0C97D3FE", // Imprivata (login screen)
+      "4DBFC1C52D6660DD90791976DF7FEF7B3D360509"  // Imprivata (login screen) DEV
     ]
   }],
   "allow_deprecated_audio_api": {
diff --git a/extensions/common/api/_permission_features.json b/extensions/common/api/_permission_features.json
index 9663eda0..222459a 100644
--- a/extensions/common/api/_permission_features.json
+++ b/extensions/common/api/_permission_features.json
@@ -569,6 +569,7 @@
       "min_manifest_version": 2,
       "whitelist": [
         "E219EE36A3B40612FD2A8CD6937B03EF0C97D3FE", // Imprivata (login screen)
+        "4DBFC1C52D6660DD90791976DF7FEF7B3D360509", // Imprivata (login screen) DEV
         "7FE4A999359A456C4B0FB7B7AD85CEA29CA50519"  // Login screen APIs test extension
       ]
     }
diff --git a/extensions/common/permissions/api_permission.h b/extensions/common/permissions/api_permission.h
index 609cab4..08be0b1 100644
--- a/extensions/common/permissions/api_permission.h
+++ b/extensions/common/permissions/api_permission.h
@@ -262,6 +262,7 @@
     kDeclarativeNetRequestFeedback = 218,
     kTransientBackground = 219,
     kLogin = 220,
+    kLoginScreenStorage = 221,
     // Last entry: Add new entries above and ensure to update the
     // "ExtensionPermission3" enum in tools/metrics/histograms/enums.xml
     // (by running update_extension_permission.py).
diff --git a/extensions/common/permissions/extensions_api_permissions.cc b/extensions/common/permissions/extensions_api_permissions.cc
index 5ab5e13..fbcb281b 100644
--- a/extensions/common/permissions/extensions_api_permissions.cc
+++ b/extensions/common/permissions/extensions_api_permissions.cc
@@ -77,6 +77,7 @@
     {APIPermission::kLockWindowFullscreenPrivate, "lockWindowFullscreenPrivate",
      APIPermissionInfo::kFlagCannotBeOptional},
     {APIPermission::kLogin, "login"},
+    {APIPermission::kLoginScreenStorage, "loginScreenStorage"},
     {APIPermission::kLoginScreenUi, "loginScreenUi"},
     {APIPermission::kMediaPerceptionPrivate, "mediaPerceptionPrivate"},
     {APIPermission::kMetricsPrivate, "metricsPrivate",
diff --git a/extensions/shell/browser/shell_browser_main_parts.cc b/extensions/shell/browser/shell_browser_main_parts.cc
index 762d165..99712ab 100644
--- a/extensions/shell/browser/shell_browser_main_parts.cc
+++ b/extensions/shell/browser/shell_browser_main_parts.cc
@@ -235,8 +235,8 @@
   // Track the task so it can be canceled if app_shell shuts down very quickly,
   // such as in browser tests.
   task_tracker_.PostTask(
-      base::CreateSingleThreadTaskRunnerWithTraits({BrowserThread::IO}).get(),
-      FROM_HERE, base::Bind(nacl::NaClProcessHost::EarlyStartup));
+      base::CreateSingleThreadTaskRunner({BrowserThread::IO}).get(), FROM_HERE,
+      base::Bind(nacl::NaClProcessHost::EarlyStartup));
 #endif
 
   content::ShellDevToolsManagerDelegate::StartHttpHandler(
diff --git a/extensions/shell/browser/shell_content_browser_client.cc b/extensions/shell/browser/shell_content_browser_client.cc
index cb29c84..fecd20e 100644
--- a/extensions/shell/browser/shell_content_browser_client.cc
+++ b/extensions/shell/browser/shell_content_browser_client.cc
@@ -177,7 +177,7 @@
                site_instance->GetProcess()->GetID(),
                site_instance->GetId());
 
-  base::PostTaskWithTraits(
+  base::PostTask(
       FROM_HERE, {BrowserThread::IO},
       base::BindOnce(&InfoMap::RegisterExtensionProcess,
                      browser_main_parts_->extension_system()->info_map(),
@@ -201,7 +201,7 @@
                site_instance->GetProcess()->GetID(),
                site_instance->GetId());
 
-  base::PostTaskWithTraits(
+  base::PostTask(
       FROM_HERE, {BrowserThread::IO},
       base::BindOnce(&InfoMap::UnregisterExtensionProcess,
                      browser_main_parts_->extension_system()->info_map(),
diff --git a/extensions/shell/browser/shell_extension_system.cc b/extensions/shell/browser/shell_extension_system.cc
index d7a3bd7..3bb48fc 100644
--- a/extensions/shell/browser/shell_extension_system.cc
+++ b/extensions/shell/browser/shell_extension_system.cc
@@ -139,7 +139,7 @@
 void ShellExtensionSystem::RegisterExtensionWithRequestContexts(
     const Extension* extension,
     const base::Closure& callback) {
-  base::PostTaskWithTraitsAndReply(
+  base::PostTaskAndReply(
       FROM_HERE, {BrowserThread::IO},
       base::Bind(&InfoMap::AddExtension, info_map(),
                  base::RetainedRef(extension), base::Time::Now(), false, false),
diff --git a/extensions/shell/browser/shell_extensions_browser_client.cc b/extensions/shell/browser/shell_extensions_browser_client.cc
index 0f2a197e..d34b948f 100644
--- a/extensions/shell/browser/shell_extensions_browser_client.cc
+++ b/extensions/shell/browser/shell_extensions_browser_client.cc
@@ -237,7 +237,7 @@
     const std::string& event_name,
     std::unique_ptr<base::ListValue> args) {
   if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
-    base::PostTaskWithTraits(
+    base::PostTask(
         FROM_HERE, {BrowserThread::UI},
         base::BindOnce(&ShellExtensionsBrowserClient::BroadcastEventToRenderers,
                        base::Unretained(this), histogram_value, event_name,
diff --git a/extensions/shell/browser/shell_speech_recognition_manager_delegate.cc b/extensions/shell/browser/shell_speech_recognition_manager_delegate.cc
index 0f4a794..23c8638 100644
--- a/extensions/shell/browser/shell_speech_recognition_manager_delegate.cc
+++ b/extensions/shell/browser/shell_speech_recognition_manager_delegate.cc
@@ -76,7 +76,7 @@
   // |render_process_id| field, which is needed later to retrieve the profile.
   DCHECK_NE(context.render_process_id, 0);
 
-  base::PostTaskWithTraits(
+  base::PostTask(
       FROM_HERE, {BrowserThread::UI},
       base::BindOnce(&CheckRenderFrameType, std::move(callback),
                      context.render_process_id, context.render_frame_id));
@@ -119,7 +119,7 @@
     }
   }
 
-  base::PostTaskWithTraits(
+  base::PostTask(
       FROM_HERE, {BrowserThread::IO},
       base::BindOnce(std::move(callback), check_permission, allowed));
 }
diff --git a/ios/chrome/browser/ui/settings/autofill/BUILD.gn b/ios/chrome/browser/ui/settings/autofill/BUILD.gn
index ef46670..d504301 100644
--- a/ios/chrome/browser/ui/settings/autofill/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/autofill/BUILD.gn
@@ -69,6 +69,7 @@
   configs += [ "//build/config/compiler:enable_arc" ]
   testonly = true
   sources = [
+    "autofill_add_credit_card_mediator_unittest.mm",
     "autofill_credit_card_table_view_controller_unittest.mm",
     "autofill_profile_edit_table_view_controller_unittest.mm",
     "autofill_profile_table_view_controller_unittest.mm",
@@ -88,6 +89,7 @@
     "//ios/chrome/browser/ui/table_view:test_support",
     "//ios/web/public/test",
     "//testing/gtest",
+    "//third_party/ocmock",
   ]
 }
 
diff --git a/ios/chrome/browser/ui/settings/autofill/autofill_add_credit_card_mediator_unittest.mm b/ios/chrome/browser/ui/settings/autofill/autofill_add_credit_card_mediator_unittest.mm
new file mode 100644
index 0000000..e1f96e99
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/autofill/autofill_add_credit_card_mediator_unittest.mm
@@ -0,0 +1,192 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/settings/autofill/autofill_add_credit_card_mediator.h"
+#include "base/strings/sys_string_conversions.h"
+#include "components/autofill/core/browser/personal_data_manager.h"
+#include "ios/chrome/browser/autofill/personal_data_manager_factory.h"
+#include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
+#include "ios/chrome/browser/ui/settings/personal_data_manager_finished_profile_tasks_waiter.h"
+#include "ios/web/public/test/test_web_thread_bundle.h"
+#include "testing/platform_test.h"
+#import "third_party/ocmock/OCMock/OCMock.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+class AutofillAddCreditCardMediatorTest : public PlatformTest {
+ protected:
+  AutofillAddCreditCardMediatorTest() {
+    TestChromeBrowserState::Builder test_cbs_builder;
+    chrome_browser_state_ = test_cbs_builder.Build();
+    // Credit card import requires a PersonalDataManager which itself needs the
+    // WebDataService; this is not initialized on a TestChromeBrowserState by
+    // default.
+    chrome_browser_state_->CreateWebDataService();
+    personal_data_manager_ =
+        autofill::PersonalDataManagerFactory::GetForBrowserState(
+            chrome_browser_state_.get());
+
+    add_credit_card_mediator_delegate_mock_ =
+        OCMProtocolMock(@protocol(AddCreditCardMediatorDelegate));
+
+    add_credit_card_mediator_ = [[AutofillAddCreditCardMediator alloc]
+           initWithDelegate:add_credit_card_mediator_delegate_mock_
+        personalDataManager:personal_data_manager_];
+  }
+
+  web::TestWebThreadBundle thread_bundle_;
+  std::unique_ptr<TestChromeBrowserState> chrome_browser_state_;
+  autofill::PersonalDataManager* personal_data_manager_;
+  AutofillAddCreditCardMediator* add_credit_card_mediator_;
+  id add_credit_card_mediator_delegate_mock_;
+};
+
+// Test saving a credit card with invalid card number.
+TEST_F(AutofillAddCreditCardMediatorTest,
+       TestSavingCreditCardWithInvalidNumber) {
+  PersonalDataManagerFinishedProfileTasksWaiter waiter(personal_data_manager_);
+
+  // |creditCardMediatorHasInvalidCardNumber|expected to be called by
+  // |add_credit_card_mediator_| if the credit card has invalid number.
+  OCMExpect([add_credit_card_mediator_delegate_mock_
+      creditCardMediatorHasInvalidCardNumber:[OCMArg any]]);
+
+  [add_credit_card_mediator_
+      addCreditCardViewController:nil
+      addCreditCardWithHolderName:@"Test"
+                       cardNumber:@"4111111111111112"  // This is invalid
+                                                       // Card number.
+                  expirationMonth:@"11"
+                   expirationYear:@"2020"];
+
+  waiter.Wait();  // Wait for completion of the asynchronous operation.
+
+  int number_of_credit_cards = personal_data_manager_->GetCreditCards().size();
+
+  // A credit card with invalid number shouldn't be saved so the number of
+  // credit cards has to equal zero.
+  EXPECT_EQ(number_of_credit_cards, 0);
+
+  [add_credit_card_mediator_delegate_mock_ verify];
+}
+
+// Test saving a credit card with invalid expiration month.
+TEST_F(AutofillAddCreditCardMediatorTest,
+       TestSavingCreditCardWithInvalidMonth) {
+  PersonalDataManagerFinishedProfileTasksWaiter waiter(personal_data_manager_);
+
+  // |creditCardMediatorHasInvalidExpirationDate| expected to be called by
+  // |add_credit_card_mediator_| if the credit card has invalid expiration date.
+  OCMExpect([add_credit_card_mediator_delegate_mock_
+      creditCardMediatorHasInvalidExpirationDate:[OCMArg any]]);
+
+  [add_credit_card_mediator_
+      addCreditCardViewController:nil
+      addCreditCardWithHolderName:@"Test"
+                       cardNumber:@"4111111111111111"
+                  expirationMonth:@"15"  // This is invalid month.
+                   expirationYear:@"2020"];
+
+  waiter.Wait();  // Wait for completion of the asynchronous operation.
+
+  //  A credit card with invalid expiration date shouldn't be saved so the
+  //  number of credit cards has to equal zero.
+  int number_of_credit_cards = personal_data_manager_->GetCreditCards().size();
+  EXPECT_EQ(number_of_credit_cards, 0);
+
+  [add_credit_card_mediator_delegate_mock_ verify];
+}
+
+// Test saving a credit card with invalid expiration year.
+TEST_F(AutofillAddCreditCardMediatorTest, TestSavingCreditCardWithInvalidYear) {
+  PersonalDataManagerFinishedProfileTasksWaiter waiter(personal_data_manager_);
+
+  // |creditCardMediatorHasInvalidExpirationDate| expected to be called by
+  // |add_credit_card_mediator_| if the credit card has invalid expiration date.
+  OCMExpect([add_credit_card_mediator_delegate_mock_
+      creditCardMediatorHasInvalidExpirationDate:[OCMArg any]]);
+
+  [add_credit_card_mediator_
+      addCreditCardViewController:nil
+      addCreditCardWithHolderName:@"Test"
+                       cardNumber:@"4111111111111111"
+                  expirationMonth:@"15"
+                   expirationYear:@"2010"];  // This is invalid year.
+
+  waiter.Wait();  // Wait for completion of the asynchronous operation.
+
+  // A credit card with invalid expiration date shouldn't be saved so the number
+  // of credit cards has to equal zero.
+  int number_of_credit_cards = personal_data_manager_->GetCreditCards().size();
+  EXPECT_EQ(number_of_credit_cards, 0);
+
+  [add_credit_card_mediator_delegate_mock_ verify];
+}
+
+// Test saving a valid credit card.
+TEST_F(AutofillAddCreditCardMediatorTest, TestSavingValidCreditCard) {
+  PersonalDataManagerFinishedProfileTasksWaiter waiter(personal_data_manager_);
+
+  // |creditCardMediatorDidFinish| expected to be called by
+  // |add_credit_card_mediator_| if the credit card has valid data.
+  OCMExpect([add_credit_card_mediator_delegate_mock_
+      creditCardMediatorDidFinish:[OCMArg any]]);
+
+  [add_credit_card_mediator_ addCreditCardViewController:nil
+                             addCreditCardWithHolderName:@"Test"
+                                              cardNumber:@"4111111111111111"
+                                         expirationMonth:@"11"
+                                          expirationYear:@"2020"];
+
+  waiter.Wait();  // Wait for completion of the asynchronous operation.
+
+  // A valid credit card expected to be savd so the number of credit cards has
+  // to equal one.
+  int number_of_credit_cards = personal_data_manager_->GetCreditCards().size();
+  EXPECT_EQ(number_of_credit_cards, 1);
+
+  [add_credit_card_mediator_delegate_mock_ verify];
+}
+
+// Test saving duplicated credit card with the same card number.
+TEST_F(AutofillAddCreditCardMediatorTest, TestAlreadyExistsCreditCardNumber) {
+  PersonalDataManagerFinishedProfileTasksWaiter waiter(personal_data_manager_);
+
+  // |creditCardMediatorDidFinish| expected to be called by
+  // |add_credit_card_mediator_| if the credit card has valid data.
+  OCMExpect([add_credit_card_mediator_delegate_mock_
+      creditCardMediatorDidFinish:[OCMArg any]]);
+
+  [add_credit_card_mediator_ addCreditCardViewController:nil
+                             addCreditCardWithHolderName:@"Test2"
+                                              cardNumber:@"4111111111111111"
+                                         expirationMonth:@"12"
+                                          expirationYear:@"2022"];
+
+  waiter.Wait();  // Wait for completion of the asynchronous operation.
+
+  // A duplicated credit card expected to be updated not saved as new one so the
+  // number of credit cards has to remain eqal one.
+  int number_of_credit_cards = personal_data_manager_->GetCreditCards().size();
+  EXPECT_EQ(number_of_credit_cards, 1);
+
+  autofill::CreditCard* savedCreditCard =
+      personal_data_manager_->GetCreditCardByNumber("4111111111111111");
+
+  // Test if the credit card data was replaced by the new data.
+  EXPECT_TRUE(savedCreditCard);
+
+  EXPECT_EQ(savedCreditCard->ExpirationMonthAsString(),
+            base::SysNSStringToUTF16(@"12"));
+
+  EXPECT_EQ(savedCreditCard->ExpirationMonthAsString(),
+            base::SysNSStringToUTF16(@"12"));
+
+  EXPECT_EQ(savedCreditCard->Expiration4DigitYearAsString(),
+            base::SysNSStringToUTF16(@"2022"));
+
+  [add_credit_card_mediator_delegate_mock_ verify];
+}
diff --git a/ios/web/navigation/crw_wk_navigation_handler.mm b/ios/web/navigation/crw_wk_navigation_handler.mm
index 699cb33..b00724c 100644
--- a/ios/web/navigation/crw_wk_navigation_handler.mm
+++ b/ios/web/navigation/crw_wk_navigation_handler.mm
@@ -1804,7 +1804,7 @@
                                 forContext:std::move(originalContext)];
     } break;
 
-    case web::ErrorRetryCommand::kLoadErrorView:
+    case web::ErrorRetryCommand::kLoadError:
       [self loadErrorPageForNavigationItem:item
                          navigationContext:context
                                    webView:webView];
diff --git a/ios/web/navigation/error_retry_state_machine.h b/ios/web/navigation/error_retry_state_machine.h
index 3e7d6d3..79d61f0 100644
--- a/ios/web/navigation/error_retry_state_machine.h
+++ b/ios/web/navigation/error_retry_state_machine.h
@@ -13,11 +13,10 @@
 // CRWWebController to coordinate the display of native error views such that
 // back/forward navigation to a native error view automatically triggers a
 // reload of the original URL. This is achieved in four steps:
-// 1) A NavigationItem is put into
-//    kDisplaying(Native|Web)ErrorForFailedNavigation when it first failed to
-//    load and a web error is displayed. If the failure occurred during
-//    provisional navigation, a placeholder entry is inserted into
-//    WKBackForwardList for this item.
+// 1) A NavigationItem is set to kDisplayingError when it first failed to load
+//    and a web error is displayed. If the failure occurred during provisional
+//    navigation, a placeholder entry is inserted into WKBackForwardList for
+//    this item.
 // 2) Upon navigation to this item, use |loadHTMLString:| to modify the URL of
 //    the placeholder entry to the original URL and change the item state to
 //    kNavigatingToFailedNavigationItem.
@@ -40,11 +39,9 @@
   kRetryPlaceholderNavigation,
   // This navigation item has an entry in WKBackForwardList. Ready to present
   // error in native view.
-  kReadyToDisplayErrorForFailedNavigation,
-  // This navigation item failed to load and a native error is displayed.
-  kDisplayingNativeErrorForFailedNavigation,
+  kReadyToDisplayError,
   // This navigation item failed to load and a web error is displayed.
-  kDisplayingWebErrorForFailedNavigation,
+  kDisplayingError,
   // This navigation item is reactivated due to back/forward navigation and
   // needs to try reloading.
   kNavigatingToFailedNavigationItem,
@@ -57,7 +54,7 @@
   // WebView should load placeholder request.
   kLoadPlaceholder,
   // WebView should load error view.
-  kLoadErrorView,
+  kLoadError,
   // WebView should reload.
   kReload,
   // WebView should rewrite its URL (assumed to be a placeholder URL) to the
@@ -89,7 +86,7 @@
   // Transitions the state machine to kNoNavigationError.
   void SetNoNavigationError();
 
-  // Transitions the state machine to kDisplayingWebErrorForFailedNavigation.
+  // Transitions the state machine to kDisplayingError.
   void SetDisplayingWebError();
 
   // Transitions the state machine to kRetryPlaceholderNavigation.
@@ -107,8 +104,7 @@
   ErrorRetryCommand DidFinishNavigation(const GURL& web_view_url);
 
  private:
-  ErrorRetryCommand BackForwardOrReloadFailed(const GURL& web_view_url,
-                                              const GURL& error_url);
+  ErrorRetryCommand BackForwardOrReloadFailed();
 
   ErrorRetryState state_;
   GURL url_;
diff --git a/ios/web/navigation/error_retry_state_machine.mm b/ios/web/navigation/error_retry_state_machine.mm
index d711df2..1e434f23 100644
--- a/ios/web/navigation/error_retry_state_machine.mm
+++ b/ios/web/navigation/error_retry_state_machine.mm
@@ -43,10 +43,10 @@
   //     stuck in the transient kRetryFailedNavigationItem state. So for this
   //     specific case, treat the SSL interstitial as a web error so that
   //     error retry works as expected on subsequent back/forward navigations.
-  DCHECK(state_ == ErrorRetryState::kReadyToDisplayErrorForFailedNavigation ||
+  DCHECK(state_ == ErrorRetryState::kReadyToDisplayError ||
          state_ == ErrorRetryState::kRetryFailedNavigationItem)
       << "Unexpected error retry state: " << static_cast<int>(state_);
-  state_ = ErrorRetryState::kDisplayingWebErrorForFailedNavigation;
+  state_ = ErrorRetryState::kDisplayingError;
 }
 
 void ErrorRetryStateMachine::SetRetryPlaceholderNavigation() {
@@ -63,8 +63,8 @@
         // Client redirect in restore_session.html failed. A placeholder is not
         // needed here because a back/forward item already exists for
         // restore_session.html.
-        state_ = ErrorRetryState::kReadyToDisplayErrorForFailedNavigation;
-        return ErrorRetryCommand::kLoadErrorView;
+        state_ = ErrorRetryState::kReadyToDisplayError;
+        return ErrorRetryCommand::kLoadError;
       }
       // Provisional navigation failed on a new item.
       state_ = ErrorRetryState::kLoadingPlaceholder;
@@ -76,9 +76,7 @@
     case ErrorRetryState::kRetryFailedNavigationItem:
     // This case happens for the second back/forward navigation in offline mode
     // to a page that initially loaded successfully.
-    case ErrorRetryState::kDisplayingNativeErrorForFailedNavigation:
-    // Retry of a previous failure still fails.
-    case ErrorRetryState::kDisplayingWebErrorForFailedNavigation:
+    case ErrorRetryState::kDisplayingError:
       if (web::GetWebClient()->IsSlimNavigationManagerEnabled()) {
         // In this case, a back/forward item already exists. Rewriting the
         // WebView's URL to the placeholder URL before loading the error page
@@ -88,12 +86,12 @@
         state_ = ErrorRetryState::kLoadingPlaceholder;
         return ErrorRetryCommand::kRewriteToPlaceholderURL;
       } else {
-        return BackForwardOrReloadFailed(web_view_url, error_url);
+        return BackForwardOrReloadFailed();
       }
 
     case ErrorRetryState::kLoadingPlaceholder:
     case ErrorRetryState::kRetryPlaceholderNavigation:
-    case ErrorRetryState::kReadyToDisplayErrorForFailedNavigation:
+    case ErrorRetryState::kReadyToDisplayError:
     case ErrorRetryState::kNavigatingToFailedNavigationItem:
       NOTREACHED() << "Unexpected error retry state: "
                    << static_cast<unsigned>(state_);
@@ -106,21 +104,20 @@
     const GURL& error_url) {
   switch (state_) {
     case ErrorRetryState::kNewRequest:
-      state_ = ErrorRetryState::kReadyToDisplayErrorForFailedNavigation;
-      return ErrorRetryCommand::kLoadErrorView;
+      state_ = ErrorRetryState::kReadyToDisplayError;
+      return ErrorRetryCommand::kLoadError;
 
     // Reload of a previously successful load fails.
     case ErrorRetryState::kNoNavigationError:
     // Retry of a previous failure still fails.
     case ErrorRetryState::kRetryFailedNavigationItem:
     // Retry of a previous failure still fails.
-    case ErrorRetryState::kDisplayingNativeErrorForFailedNavigation:
-    case ErrorRetryState::kDisplayingWebErrorForFailedNavigation:
-      return BackForwardOrReloadFailed(web_view_url, error_url);
+    case ErrorRetryState::kDisplayingError:
+      return BackForwardOrReloadFailed();
 
     case ErrorRetryState::kLoadingPlaceholder:
     case ErrorRetryState::kRetryPlaceholderNavigation:
-    case ErrorRetryState::kReadyToDisplayErrorForFailedNavigation:
+    case ErrorRetryState::kReadyToDisplayError:
     case ErrorRetryState::kNavigatingToFailedNavigationItem:
       NOTREACHED() << "Unexpected error retry state: "
                    << static_cast<unsigned>(state_);
@@ -135,8 +132,8 @@
       // (1) Placeholder load for initial failure succeeded.
       DCHECK_EQ(web_view_url,
                 wk_navigation_util::CreatePlaceholderUrlForUrl(url_));
-      state_ = ErrorRetryState::kReadyToDisplayErrorForFailedNavigation;
-      return ErrorRetryCommand::kLoadErrorView;
+      state_ = ErrorRetryState::kReadyToDisplayError;
+      return ErrorRetryCommand::kLoadError;
 
     case ErrorRetryState::kRetryPlaceholderNavigation:
       if (wk_navigation_util::IsPlaceholderUrl(web_view_url)) {
@@ -149,8 +146,8 @@
       } else {
         // The url was written by kRewriteToWebViewURL in the if block, so on
         // this navigation load an error view.
-        state_ = ErrorRetryState::kReadyToDisplayErrorForFailedNavigation;
-        return ErrorRetryCommand::kLoadErrorView;
+        state_ = ErrorRetryState::kReadyToDisplayError;
+        return ErrorRetryCommand::kLoadError;
       }
     case ErrorRetryState::kNewRequest:
       if (wk_navigation_util::IsRestoreSessionUrl(web_view_url)) {
@@ -162,14 +159,13 @@
       }
       break;
 
-    case ErrorRetryState::kReadyToDisplayErrorForFailedNavigation:
+    case ErrorRetryState::kReadyToDisplayError:
       // (3) Finished loading error in web view.
       DCHECK_EQ(web_view_url, url_);
-      state_ = ErrorRetryState::kDisplayingWebErrorForFailedNavigation;
+      state_ = ErrorRetryState::kDisplayingError;
       break;
 
-    case ErrorRetryState::kDisplayingNativeErrorForFailedNavigation:
-    case ErrorRetryState::kDisplayingWebErrorForFailedNavigation:
+    case ErrorRetryState::kDisplayingError:
       if (web_view_url ==
           wk_navigation_util::CreatePlaceholderUrlForUrl(url_)) {
         // (4) Back/forward to or reload of placeholder URL. Rewrite WebView URL
@@ -222,11 +218,9 @@
   return ErrorRetryCommand::kDoNothing;
 }
 
-ErrorRetryCommand ErrorRetryStateMachine::BackForwardOrReloadFailed(
-    const GURL& web_view_url,
-    const GURL& error_url) {
-  state_ = ErrorRetryState::kReadyToDisplayErrorForFailedNavigation;
-  return ErrorRetryCommand::kLoadErrorView;
+ErrorRetryCommand ErrorRetryStateMachine::BackForwardOrReloadFailed() {
+  state_ = ErrorRetryState::kReadyToDisplayError;
+  return ErrorRetryCommand::kLoadError;
 }
 
 }  // namespace web
diff --git a/ios/web/navigation/error_retry_state_machine_unittest.mm b/ios/web/navigation/error_retry_state_machine_unittest.mm
index c58016f7..4d5cb0b 100644
--- a/ios/web/navigation/error_retry_state_machine_unittest.mm
+++ b/ios/web/navigation/error_retry_state_machine_unittest.mm
@@ -37,15 +37,13 @@
   ASSERT_EQ(ErrorRetryState::kLoadingPlaceholder, machine.state());
 
   // Placeholder load finishes.
-  ASSERT_EQ(ErrorRetryCommand::kLoadErrorView,
+  ASSERT_EQ(ErrorRetryCommand::kLoadError,
             machine.DidFinishNavigation(placeholder_url));
-  ASSERT_EQ(ErrorRetryState::kReadyToDisplayErrorForFailedNavigation,
-            machine.state());
+  ASSERT_EQ(ErrorRetryState::kReadyToDisplayError, machine.state());
 
   // Presents error.
   machine.SetDisplayingWebError();
-  ASSERT_EQ(ErrorRetryState::kDisplayingWebErrorForFailedNavigation,
-            machine.state());
+  ASSERT_EQ(ErrorRetryState::kDisplayingError, machine.state());
 
   // Reload the failed navigation.
   ASSERT_EQ(ErrorRetryCommand::kRewriteToWebViewURL,
@@ -73,20 +71,18 @@
                 clone.DidFailProvisionalNavigation(test_url, test_url));
       ASSERT_EQ(ErrorRetryState::kLoadingPlaceholder, clone.state());
     } else {
-      ASSERT_EQ(ErrorRetryCommand::kLoadErrorView,
+      ASSERT_EQ(ErrorRetryCommand::kLoadError,
                 clone.DidFailProvisionalNavigation(test_url, test_url));
-      ASSERT_EQ(ErrorRetryState::kReadyToDisplayErrorForFailedNavigation,
-                clone.state());
+      ASSERT_EQ(ErrorRetryState::kReadyToDisplayError, clone.state());
     }
   }
 
   // Reload fails after navigation is committed.
   {
     ErrorRetryStateMachine clone(machine);
-    ASSERT_EQ(ErrorRetryCommand::kLoadErrorView,
+    ASSERT_EQ(ErrorRetryCommand::kLoadError,
               clone.DidFailNavigation(test_url, test_url));
-    ASSERT_EQ(ErrorRetryState::kReadyToDisplayErrorForFailedNavigation,
-              clone.state());
+    ASSERT_EQ(ErrorRetryState::kReadyToDisplayError, clone.state());
   }
 }
 
@@ -106,16 +102,14 @@
   ASSERT_EQ(ErrorRetryState::kLoadingPlaceholder, machine.state());
 
   // Placeholder load finishes.
-  ASSERT_EQ(ErrorRetryCommand::kLoadErrorView,
+  ASSERT_EQ(ErrorRetryCommand::kLoadError,
             machine.DidFinishNavigation(placeholder_url));
-  ASSERT_EQ(ErrorRetryState::kReadyToDisplayErrorForFailedNavigation,
-            machine.state());
+  ASSERT_EQ(ErrorRetryState::kReadyToDisplayError, machine.state());
 
   // Presents error in web view.
   ASSERT_EQ(ErrorRetryCommand::kDoNothing,
             machine.DidFinishNavigation(test_url));
-  ASSERT_EQ(ErrorRetryState::kDisplayingWebErrorForFailedNavigation,
-            machine.state());
+  ASSERT_EQ(ErrorRetryState::kDisplayingError, machine.state());
 
   // Reload the failed navigation succeeds.
   {
@@ -142,20 +136,18 @@
                 clone.DidFailProvisionalNavigation(test_url, test_url));
       ASSERT_EQ(ErrorRetryState::kLoadingPlaceholder, clone.state());
     } else {
-      ASSERT_EQ(ErrorRetryCommand::kLoadErrorView,
+      ASSERT_EQ(ErrorRetryCommand::kLoadError,
                 clone.DidFailProvisionalNavigation(test_url, test_url));
-      ASSERT_EQ(ErrorRetryState::kReadyToDisplayErrorForFailedNavigation,
-                clone.state());
+      ASSERT_EQ(ErrorRetryState::kReadyToDisplayError, clone.state());
     }
   }
 
   // Reload fails after navigation is committed.
   {
     ErrorRetryStateMachine clone(machine);
-    ASSERT_EQ(ErrorRetryCommand::kLoadErrorView,
+    ASSERT_EQ(ErrorRetryCommand::kLoadError,
               clone.DidFailNavigation(test_url, test_url));
-    ASSERT_EQ(ErrorRetryState::kReadyToDisplayErrorForFailedNavigation,
-              clone.state());
+    ASSERT_EQ(ErrorRetryState::kReadyToDisplayError, clone.state());
   }
 
   // Simulate back/forward navigation to a restored session entry that never
@@ -176,10 +168,9 @@
   ErrorRetryStateMachine machine;
   machine.SetURL(test_url);
 
-  ASSERT_EQ(ErrorRetryCommand::kLoadErrorView,
+  ASSERT_EQ(ErrorRetryCommand::kLoadError,
             machine.DidFailNavigation(GURL::EmptyGURL(), test_url));
-  ASSERT_EQ(ErrorRetryState::kReadyToDisplayErrorForFailedNavigation,
-            machine.state());
+  ASSERT_EQ(ErrorRetryState::kReadyToDisplayError, machine.state());
 }
 
 // Tests reloading a previously successful navigation.
@@ -211,20 +202,18 @@
                 clone.DidFailProvisionalNavigation(test_url, test_url));
       ASSERT_EQ(ErrorRetryState::kLoadingPlaceholder, clone.state());
     } else {
-      ASSERT_EQ(ErrorRetryCommand::kLoadErrorView,
+      ASSERT_EQ(ErrorRetryCommand::kLoadError,
                 clone.DidFailProvisionalNavigation(test_url, test_url));
-      ASSERT_EQ(ErrorRetryState::kReadyToDisplayErrorForFailedNavigation,
-                clone.state());
+      ASSERT_EQ(ErrorRetryState::kReadyToDisplayError, clone.state());
     }
   }
 
   // Reload fails after commit.
   {
     ErrorRetryStateMachine clone(machine);
-    ASSERT_EQ(ErrorRetryCommand::kLoadErrorView,
+    ASSERT_EQ(ErrorRetryCommand::kLoadError,
               clone.DidFailNavigation(test_url, test_url));
-    ASSERT_EQ(ErrorRetryState::kReadyToDisplayErrorForFailedNavigation,
-              clone.state());
+    ASSERT_EQ(ErrorRetryState::kReadyToDisplayError, clone.state());
   }
 }
 
@@ -242,10 +231,9 @@
   ASSERT_EQ(ErrorRetryState::kNewRequest, machine.state());
 
   // Simulate failure in restore_session.html client redirect.
-  ASSERT_EQ(ErrorRetryCommand::kLoadErrorView,
+  ASSERT_EQ(ErrorRetryCommand::kLoadError,
             machine.DidFailProvisionalNavigation(redirect_url, test_url));
-  ASSERT_EQ(ErrorRetryState::kReadyToDisplayErrorForFailedNavigation,
-            machine.state());
+  ASSERT_EQ(ErrorRetryState::kReadyToDisplayError, machine.state());
 }
 
 // Tests retrying a placeholder navigation.
@@ -271,10 +259,9 @@
   // Lastly trigger the error view.
   const GURL target_url =
       wk_navigation_util::ExtractUrlFromPlaceholderUrl(placeholder_url);
-  ASSERT_EQ(ErrorRetryCommand::kLoadErrorView,
+  ASSERT_EQ(ErrorRetryCommand::kLoadError,
             machine.DidFinishNavigation(target_url));
-  ASSERT_EQ(ErrorRetryState::kReadyToDisplayErrorForFailedNavigation,
-            machine.state());
+  ASSERT_EQ(ErrorRetryState::kReadyToDisplayError, machine.state());
 }
 
 }  // namespace
diff --git a/ios/web/web_state/web_state_impl.mm b/ios/web/web_state/web_state_impl.mm
index 0712d00..dc22e13 100644
--- a/ios/web/web_state/web_state_impl.mm
+++ b/ios/web/web_state/web_state_impl.mm
@@ -678,7 +678,7 @@
     if ([[web_controller_ nativeContentHolder].nativeController
             respondsToSelector:@selector(virtualURL)] ||
         item->error_retry_state_machine().state() ==
-            ErrorRetryState::kReadyToDisplayErrorForFailedNavigation) {
+            ErrorRetryState::kReadyToDisplayError) {
       // For native content, or when webView.URL is a placeholder URL,
       // |currentURLWithTrustLevel:| returns virtual URL if one is available.
       lastCommittedURL = item->GetVirtualURL();
diff --git a/media/capture/video/chromeos/mojom/BUILD.gn b/media/capture/video/chromeos/mojom/BUILD.gn
index 9084ff8..e19736e 100644
--- a/media/capture/video/chromeos/mojom/BUILD.gn
+++ b/media/capture/video/chromeos/mojom/BUILD.gn
@@ -19,7 +19,4 @@
     "//media/capture/mojom:image_capture",
     "//media/mojo/mojom",
   ]
-
-  # TODO(https://crbug.com/968369): Change to use new names.
-  use_old_js_lite_bindings_names = true
 }
diff --git a/media/gpu/test/video_player/video_decoder_client.cc b/media/gpu/test/video_player/video_decoder_client.cc
index 6f8a73d..264151d 100644
--- a/media/gpu/test/video_player/video_decoder_client.cc
+++ b/media/gpu/test/video_player/video_decoder_client.cc
@@ -29,7 +29,6 @@
 namespace test {
 
 VideoDecoderClient::VideoDecoderClient(
-    const Video* video,
     const VideoPlayer::EventCallback& event_cb,
     std::unique_ptr<FrameRenderer> renderer,
     std::vector<std::unique_ptr<VideoFrameProcessor>> frame_processors,
@@ -39,8 +38,7 @@
       frame_processors_(std::move(frame_processors)),
       decoder_client_config_(config),
       decoder_client_thread_("VDAClientDecoderThread"),
-      decoder_client_state_(VideoDecoderClientState::kUninitialized),
-      video_(video) {
+      decoder_client_state_(VideoDecoderClientState::kUninitialized) {
   DETACH_FROM_SEQUENCE(decoder_client_sequence_checker_);
 
   weak_this_ = weak_this_factory_.GetWeakPtr();
@@ -64,47 +62,36 @@
 
 // static
 std::unique_ptr<VideoDecoderClient> VideoDecoderClient::Create(
-    const Video* video,
     const VideoPlayer::EventCallback& event_cb,
     std::unique_ptr<FrameRenderer> frame_renderer,
     std::vector<std::unique_ptr<VideoFrameProcessor>> frame_processors,
     const VideoDecoderClientConfig& config) {
   auto decoder_client = base::WrapUnique(
-      new VideoDecoderClient(video, event_cb, std::move(frame_renderer),
+      new VideoDecoderClient(event_cb, std::move(frame_renderer),
                              std::move(frame_processors), config));
-  if (!decoder_client->Initialize()) {
+  if (!decoder_client->CreateDecoder()) {
     return nullptr;
   }
   return decoder_client;
 }
 
-bool VideoDecoderClient::Initialize() {
+bool VideoDecoderClient::CreateDecoder() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(video_player_sequence_checker_);
   DCHECK(!decoder_client_thread_.IsRunning());
   DCHECK(event_cb_ && frame_renderer_);
-  DCHECK(video_);
-
-  encoded_data_helper_ =
-      std::make_unique<EncodedDataHelper>(video_->Data(), video_->Profile());
 
   if (!decoder_client_thread_.Start()) {
     VLOGF(1) << "Failed to start decoder thread";
     return false;
   }
 
-  CreateDecoder();
-
-  return true;
-}
-
-void VideoDecoderClient::CreateDecoder() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(video_player_sequence_checker_);
-
+  bool success = false;
   base::WaitableEvent done;
   decoder_client_thread_.task_runner()->PostTask(
       FROM_HERE, base::BindOnce(&VideoDecoderClient::CreateDecoderTask,
-                                weak_this_, &done));
+                                weak_this_, &success, &done));
   done.Wait();
+  return success;
 }
 
 void VideoDecoderClient::DestroyDecoder() {
@@ -133,6 +120,17 @@
   return frame_renderer_.get();
 }
 
+void VideoDecoderClient::Initialize(const Video* video) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(video_player_sequence_checker_);
+  DCHECK(video);
+
+  base::WaitableEvent done;
+  decoder_client_thread_.task_runner()->PostTask(
+      FROM_HERE, base::BindOnce(&VideoDecoderClient::InitializeDecoderTask,
+                                weak_this_, video, &done));
+  done.Wait();
+}
+
 void VideoDecoderClient::Play() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(video_player_sequence_checker_);
 
@@ -154,11 +152,45 @@
       FROM_HERE, base::BindOnce(&VideoDecoderClient::ResetTask, weak_this_));
 }
 
-void VideoDecoderClient::CreateDecoderTask(base::WaitableEvent* done) {
+void VideoDecoderClient::CreateDecoderTask(bool* success,
+                                           base::WaitableEvent* done) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(decoder_client_sequence_checker_);
+  DCHECK_EQ(decoder_client_state_, VideoDecoderClientState::kUninitialized);
   LOG_ASSERT(!decoder_) << "Can't create decoder: already created";
-  LOG_ASSERT(video_);
 
+  if (decoder_client_config_.use_vd) {
+#if defined(OS_CHROMEOS)
+    decoder_ = ChromeosVideoDecoderFactory::Create(
+        base::ThreadTaskRunnerHandle::Get(),
+        std::make_unique<PlatformVideoFramePool>(),
+        std::make_unique<VideoFrameConverter>());
+#endif  // defined(OS_CHROMEOS)
+  } else {
+    // The video decoder client expects decoders to use the VD interface. We
+    // can use the TestVDAVideoDecoder wrapper here to test VDA-based video
+    // decoders.
+    decoder_ = std::make_unique<TestVDAVideoDecoder>(
+        decoder_client_config_.allocation_mode, gfx::ColorSpace(),
+        frame_renderer_.get());
+  }
+
+  *success = (decoder_ != nullptr);
+  done->Signal();
+}
+
+void VideoDecoderClient::InitializeDecoderTask(const Video* video,
+                                               base::WaitableEvent* done) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(decoder_client_sequence_checker_);
+  DCHECK(decoder_client_state_ == VideoDecoderClientState::kUninitialized ||
+         decoder_client_state_ == VideoDecoderClientState::kIdle);
+  LOG_ASSERT(decoder_) << "Can't initialize decoder: not created yet";
+  LOG_ASSERT(video);
+
+  video_ = video;
+  encoded_data_helper_ =
+      std::make_unique<EncodedDataHelper>(video_->Data(), video_->Profile());
+
+  // (Re-)initialize the decoder.
   VideoDecoderConfig config(
       video_->Codec(), video_->Profile(),
       VideoDecoderConfig::AlphaMode::kIsOpaque, VideoColorSpace(),
@@ -169,28 +201,9 @@
       base::BindOnce(&VideoDecoderClient::DecoderInitializedTask, weak_this_));
   VideoDecoder::OutputCB output_cb = BindToCurrentLoop(
       base::BindRepeating(&VideoDecoderClient::FrameReadyTask, weak_this_));
-  WaitingCB waiting_cb =
-      base::BindRepeating([](WaitingReason) { NOTIMPLEMENTED(); });
-
-  if (decoder_client_config_.use_vd) {
-#if defined(OS_CHROMEOS)
-    decoder_ = ChromeosVideoDecoderFactory::Create(
-        base::ThreadTaskRunnerHandle::Get(),
-        std::make_unique<PlatformVideoFramePool>(),
-        std::make_unique<VideoFrameConverter>());
-#endif  // defined(OS_CHROMEOS)
-    LOG_ASSERT(decoder_) << "Failed to create decoder.";
-  } else {
-    // The video decoder client expects decoders to use the VD interface. We can
-    // use the TestVDAVideoDecoder wrapper here to test VDA-based video
-    // decoders.
-    decoder_ = std::make_unique<TestVDAVideoDecoder>(
-        decoder_client_config_.allocation_mode, gfx::ColorSpace(),
-        frame_renderer_.get());
-  }
 
   decoder_->Initialize(config, false, nullptr, std::move(init_cb), output_cb,
-                       waiting_cb);
+                       WaitingCB());
 
   DCHECK_LE(decoder_client_config_.max_outstanding_decode_requests,
             static_cast<size_t>(decoder_->GetMaxDecodeRequests()));
@@ -200,7 +213,8 @@
 
 void VideoDecoderClient::DestroyDecoderTask(base::WaitableEvent* done) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(decoder_client_sequence_checker_);
-  DCHECK_EQ(VideoDecoderClientState::kIdle, decoder_client_state_);
+  DCHECK(decoder_client_state_ == VideoDecoderClientState::kUninitialized ||
+         decoder_client_state_ == VideoDecoderClientState::kIdle);
   DCHECK_EQ(0u, num_outstanding_decode_requests_);
   DVLOGF(4);
 
@@ -304,7 +318,8 @@
 
 void VideoDecoderClient::DecoderInitializedTask(bool status) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(decoder_client_sequence_checker_);
-  DCHECK_EQ(VideoDecoderClientState::kUninitialized, decoder_client_state_);
+  DCHECK(decoder_client_state_ == VideoDecoderClientState::kUninitialized ||
+         decoder_client_state_ == VideoDecoderClientState::kIdle);
   LOG_ASSERT(status) << "Initializing decoder failed";
 
   decoder_client_state_ = VideoDecoderClientState::kIdle;
diff --git a/media/gpu/test/video_player/video_decoder_client.h b/media/gpu/test/video_player/video_decoder_client.h
index 1b3b521..1d6fd5ff 100644
--- a/media/gpu/test/video_player/video_decoder_client.h
+++ b/media/gpu/test/video_player/video_decoder_client.h
@@ -68,7 +68,6 @@
   // thread-safe. Initialization is performed asynchronous, upon completion a
   // 'kInitialized' event will be thrown.
   static std::unique_ptr<VideoDecoderClient> Create(
-      const Video* const video,
       const VideoPlayer::EventCallback& event_cb,
       std::unique_ptr<FrameRenderer> frame_renderer,
       std::vector<std::unique_ptr<VideoFrameProcessor>> frame_processors,
@@ -82,6 +81,11 @@
   // Get the frame renderer associated with the video decoder client.
   FrameRenderer* GetFrameRenderer() const;
 
+  // Initialize the video decoder for the specified |video|. This function can
+  // be called multiple times and needs to be called before Play().
+  // Initialization is performed asynchronous, upon completion a 'kInitialized'
+  // event is thrown.
+  void Initialize(const Video* video);
   // Start decoding the video stream, decoder should be idle when this function
   // is called. This function is non-blocking, for each frame decoded a
   // 'kFrameDecoded' event will be thrown.
@@ -103,24 +107,23 @@
   };
 
   VideoDecoderClient(
-      const Video* const video,
       const VideoPlayer::EventCallback& event_cb,
       std::unique_ptr<FrameRenderer> renderer,
       std::vector<std::unique_ptr<VideoFrameProcessor>> frame_processors,
       const VideoDecoderClientConfig& config);
 
-  // Initialize the video decoder client.
-  bool Initialize();
-
-  // Create a new decoder for |video_|.
-  void CreateDecoder();
+  // Create a new decoder, returns whether creating was successful.
+  bool CreateDecoder();
   // Destroy the currently active decoder.
   void DestroyDecoder();
 
-  // Create a video decoder for |video_| on the |decoder_client_thread_|.
-  void CreateDecoderTask(base::WaitableEvent* done);
+  // Create a new video |decoder_| on the |decoder_client_thread_|.
+  void CreateDecoderTask(bool* success, base::WaitableEvent* done);
   // Destroy the active video |decoder_| on the |decoder_client_thread_|.
   void DestroyDecoderTask(base::WaitableEvent* done);
+  // Initialize the video |decoder_| with |video| on the
+  // |decoder_client_thread_|.
+  void InitializeDecoderTask(const Video* video, base::WaitableEvent* done);
 
   // Start decoding video stream fragments on the |decoder_client_thread_|.
   void PlayTask();
@@ -167,7 +170,7 @@
   // TODO(dstaessens@) Replace with StreamParser.
   std::unique_ptr<media::test::EncodedDataHelper> encoded_data_helper_;
   // The video being decoded.
-  const Video* const video_;
+  const Video* video_ = nullptr;
 
   SEQUENCE_CHECKER(video_player_sequence_checker_);
   SEQUENCE_CHECKER(decoder_client_sequence_checker_);
diff --git a/media/gpu/test/video_player/video_player.cc b/media/gpu/test/video_player/video_player.cc
index deeffa22..e55ee1a 100644
--- a/media/gpu/test/video_player/video_player.cc
+++ b/media/gpu/test/video_player/video_player.cc
@@ -6,6 +6,7 @@
 
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
+#include "media/gpu/macros.h"
 #include "media/gpu/test/video_player/video.h"
 #include "media/gpu/test/video_player/video_decoder_client.h"
 
@@ -30,39 +31,37 @@
 
 // static
 std::unique_ptr<VideoPlayer> VideoPlayer::Create(
-    const Video* video,
+    const VideoDecoderClientConfig& config,
     std::unique_ptr<FrameRenderer> frame_renderer,
-    std::vector<std::unique_ptr<VideoFrameProcessor>> frame_processors,
-    const VideoDecoderClientConfig& config) {
+    std::vector<std::unique_ptr<VideoFrameProcessor>> frame_processors) {
   auto video_player = base::WrapUnique(new VideoPlayer());
-  video_player->Initialize(video, std::move(frame_renderer),
-                           std::move(frame_processors), config);
+  if (!video_player->CreateDecoderClient(config, std::move(frame_renderer),
+                                         std::move(frame_processors))) {
+    return nullptr;
+  }
   return video_player;
 }
 
-void VideoPlayer::Initialize(
-    const Video* video,
+bool VideoPlayer::CreateDecoderClient(
+    const VideoDecoderClientConfig& config,
     std::unique_ptr<FrameRenderer> frame_renderer,
-    std::vector<std::unique_ptr<VideoFrameProcessor>> frame_processors,
-    const VideoDecoderClientConfig& config) {
+    std::vector<std::unique_ptr<VideoFrameProcessor>> frame_processors) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK_EQ(video_player_state_, VideoPlayerState::kUninitialized);
-  DCHECK(frame_renderer && video);
+  DCHECK(frame_renderer);
   DVLOGF(4);
 
   EventCallback event_cb =
       base::BindRepeating(&VideoPlayer::NotifyEvent, base::Unretained(this));
 
-  decoder_client_ =
-      VideoDecoderClient::Create(video, event_cb, std::move(frame_renderer),
-                                 std::move(frame_processors), config);
-  CHECK(decoder_client_) << "Failed to create decoder client";
+  decoder_client_ = VideoDecoderClient::Create(
+      event_cb, std::move(frame_renderer), std::move(frame_processors), config);
+  if (!decoder_client_) {
+    VLOGF(1) << "Failed to create video decoder client";
+    return false;
+  }
 
-  // Wait until initialization is done.
-  WaitForEvent(VideoPlayerEvent::kInitialized);
-
-  video_ = video;
-  video_player_state_ = VideoPlayerState::kIdle;
+  return true;
 }
 
 void VideoPlayer::Destroy() {
@@ -81,8 +80,27 @@
   event_timeout_ = timeout;
 }
 
+bool VideoPlayer::Initialize(const Video* video) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(video_player_state_ == VideoPlayerState::kUninitialized ||
+         video_player_state_ == VideoPlayerState::kIdle);
+  DCHECK(video);
+  DVLOGF(4);
+
+  decoder_client_->Initialize(video);
+
+  // Wait until the video decoder is initialized.
+  if (!WaitForEvent(VideoPlayerEvent::kInitialized))
+    return false;
+
+  video_ = video;
+  video_player_state_ = VideoPlayerState::kIdle;
+  return true;
+}
+
 void VideoPlayer::Play() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_EQ(video_player_state_, VideoPlayerState::kIdle);
   DVLOGF(4);
 
   // Play until the end of the video.
diff --git a/media/gpu/test/video_player/video_player.h b/media/gpu/test/video_player/video_player.h
index e6d6ef5..7fac7df 100644
--- a/media/gpu/test/video_player/video_player.h
+++ b/media/gpu/test/video_player/video_player.h
@@ -57,14 +57,13 @@
 
   ~VideoPlayer();
 
-  // Create an instance of the video player. The |video|, |frame_renderer| and
+  // Create an instance of the video player. The |frame_renderer| and
   // |frame_processors| will not be owned by the video player. The caller should
   // guarantee they outlive the video player.
   static std::unique_ptr<VideoPlayer> Create(
-      const Video* video,
+      const VideoDecoderClientConfig& config,
       std::unique_ptr<FrameRenderer> frame_renderer,
-      std::vector<std::unique_ptr<VideoFrameProcessor>> frame_processors,
-      const VideoDecoderClientConfig& config);
+      std::vector<std::unique_ptr<VideoFrameProcessor>> frame_processors = {});
 
   // Wait until all frame processors have finished processing. Returns whether
   // processing was successful.
@@ -75,6 +74,11 @@
   // Set the maximum time we will wait for an event to finish.
   void SetEventWaitTimeout(base::TimeDelta timeout);
 
+  // Initialize the video player for the specified |video|. This function can be
+  // called multiple times and needs to be called before Play(). The |video|
+  // will not be owned by the video player, the caller should guarantee it
+  // outlives the video player.
+  bool Initialize(const Video* video);
   // Play the video asynchronously.
   void Play();
   // Play the video asynchronously. Automatically pause decoding when the
@@ -118,11 +122,10 @@
  private:
   VideoPlayer();
 
-  void Initialize(
-      const Video* video,
+  bool CreateDecoderClient(
+      const VideoDecoderClientConfig& config,
       std::unique_ptr<FrameRenderer> frame_renderer,
-      std::vector<std::unique_ptr<VideoFrameProcessor>> frame_processors,
-      const VideoDecoderClientConfig& config);
+      std::vector<std::unique_ptr<VideoFrameProcessor>> frame_processors);
   void Destroy();
 
   // Notify the video player an event has occurred (e.g. frame decoded). Returns
diff --git a/media/gpu/v4l2/v4l2_decode_surface.cc b/media/gpu/v4l2/v4l2_decode_surface.cc
index 2ce74ab..ba7f7901 100644
--- a/media/gpu/v4l2/v4l2_decode_surface.cc
+++ b/media/gpu/v4l2/v4l2_decode_surface.cc
@@ -62,6 +62,11 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   DCHECK(reference_surfaces_.empty());
+#if DCHECK_IS_ON()
+  for (const auto& ref : reference_surfaces_)
+    DCHECK_NE(ref->output_record(), output_record_);
+#endif
+
   reference_surfaces_ = std::move(ref_surfaces);
 }
 
diff --git a/media/gpu/vaapi/vaapi_video_decoder.cc b/media/gpu/vaapi/vaapi_video_decoder.cc
index c990f906..77d7d50 100644
--- a/media/gpu/vaapi/vaapi_video_decoder.cc
+++ b/media/gpu/vaapi/vaapi_video_decoder.cc
@@ -243,9 +243,11 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
   VLOGF(2);
 
-  decoder_thread_task_runner_->PostTask(
-      FROM_HERE, base::BindOnce(&VaapiVideoDecoder::DestroyTask, weak_this_));
-  decoder_thread_.Stop();
+  if (decoder_thread_task_runner_) {
+    decoder_thread_task_runner_->PostTask(
+        FROM_HERE, base::BindOnce(&VaapiVideoDecoder::DestroyTask, weak_this_));
+    decoder_thread_.Stop();
+  }
 
   delete this;
   VLOGF(2) << "Destroying VAAPI VD done";
diff --git a/media/gpu/video_decode_accelerator_perf_tests.cc b/media/gpu/video_decode_accelerator_perf_tests.cc
index 0b1a6ff..13f5c1f 100644
--- a/media/gpu/video_decode_accelerator_perf_tests.cc
+++ b/media/gpu/video_decode_accelerator_perf_tests.cc
@@ -321,8 +321,10 @@
     if (!g_env->ImportSupported())
       config.allocation_mode = AllocationMode::kAllocate;
 
-    auto video_player = VideoPlayer::Create(
-        video, std::move(frame_renderer), std::move(frame_processors), config);
+    auto video_player = VideoPlayer::Create(config, std::move(frame_renderer),
+                                            std::move(frame_processors));
+    LOG_ASSERT(video_player);
+    LOG_ASSERT(video_player->Initialize(video));
 
     // Make sure the event timeout is at least as long as the video's duration.
     video_player->SetEventWaitTimeout(
diff --git a/media/gpu/video_decode_accelerator_tests.cc b/media/gpu/video_decode_accelerator_tests.cc
index 2f5a830..8704d9f34 100644
--- a/media/gpu/video_decode_accelerator_tests.cc
+++ b/media/gpu/video_decode_accelerator_tests.cc
@@ -86,8 +86,10 @@
     // Use the new VD-based video decoders if requested.
     config.use_vd = g_env->UseVD();
 
-    auto video_player = VideoPlayer::Create(
-        video, std::move(frame_renderer), std::move(frame_processors), config);
+    auto video_player = VideoPlayer::Create(config, std::move(frame_renderer),
+                                            std::move(frame_processors));
+    LOG_ASSERT(video_player);
+    LOG_ASSERT(video_player->Initialize(video));
 
     // Increase event timeout when outputting video frames.
     if (g_env->IsFramesOutputEnabled()) {
@@ -317,6 +319,44 @@
   EXPECT_TRUE(tvp->WaitForFrameProcessors());
 }
 
+// Test video decoder re-initialization. Re-initialization is only supported by
+// the media::VideoDecoder interface, so the test will be skipped if --use_vd
+// is not specified.
+TEST_F(VideoDecoderTest, Reinitialize) {
+  if (!g_env->UseVD())
+    GTEST_SKIP();
+
+  // Create and initialize the video decoder.
+  auto tvp = CreateVideoPlayer(g_env->Video());
+  EXPECT_EQ(tvp->GetEventCount(VideoPlayerEvent::kInitialized), 1u);
+
+  // Re-initialize the video decoder, without having played the video.
+  EXPECT_TRUE(tvp->Initialize(g_env->Video()));
+  EXPECT_EQ(tvp->GetEventCount(VideoPlayerEvent::kInitialized), 2u);
+
+  // Play the video from start to end.
+  tvp->Play();
+  EXPECT_TRUE(tvp->WaitForFlushDone());
+  EXPECT_EQ(tvp->GetFlushDoneCount(), 1u);
+  EXPECT_EQ(tvp->GetFrameDecodedCount(), g_env->Video()->NumFrames());
+  EXPECT_TRUE(tvp->WaitForFrameProcessors());
+
+  // Try re-initializing the video decoder again.
+  EXPECT_TRUE(tvp->Initialize(g_env->Video()));
+  EXPECT_EQ(tvp->GetEventCount(VideoPlayerEvent::kInitialized), 3u);
+}
+
+// Create a video decoder and immediately destroy it without initializing. The
+// video decoder will be automatically destroyed when the video player goes out
+// of scope at the end of the test. The test will pass if no asserts or crashes
+// are triggered upon destroying.
+TEST_F(VideoDecoderTest, DestroyBeforeInitialize) {
+  VideoDecoderClientConfig config = VideoDecoderClientConfig();
+  config.use_vd = g_env->UseVD();
+  auto tvp = VideoPlayer::Create(config, FrameRendererDummy::Create());
+  EXPECT_NE(tvp, nullptr);
+}
+
 }  // namespace test
 }  // namespace media
 
diff --git a/media/mojo/services/gpu_mojo_media_client.cc b/media/mojo/services/gpu_mojo_media_client.cc
index 51cce17c..e25937a5 100644
--- a/media/mojo/services/gpu_mojo_media_client.cc
+++ b/media/mojo/services/gpu_mojo_media_client.cc
@@ -221,7 +221,6 @@
               MaybeRenderEarlyManager::Create(gpu_task_runner_)));
 
 #elif defined(OS_CHROMEOS)
-      std::unique_ptr<VideoDecoder> cros_video_decoder;
       if (base::FeatureList::IsEnabled(kChromeosVideoDecoder)) {
 #if BUILDFLAG(USE_V4L2_CODEC) || BUILDFLAG(USE_VAAPI)
         auto frame_pool = std::make_unique<PlatformVideoFramePool>();
@@ -233,13 +232,9 @@
                            media_gpu_channel_manager_,
                            command_buffer_id->channel_token,
                            command_buffer_id->route_id));
-        cros_video_decoder = ChromeosVideoDecoderFactory::Create(
+        video_decoder = ChromeosVideoDecoderFactory::Create(
             task_runner, std::move(frame_pool), std::move(frame_converter));
 #endif  // BUILDFLAG(USE_V4L2_CODEC) || BUILDFLAG(USE_VAAPI)
-      }
-
-      if (cros_video_decoder) {
-        video_decoder = std::move(cros_video_decoder);
       } else {
         video_decoder = VdaVideoDecoder::Create(
             task_runner, gpu_task_runner_, media_log->Clone(),
diff --git a/net/base/net_error_list.h b/net/base/net_error_list.h
index a028a469..3150f3e 100644
--- a/net/base/net_error_list.h
+++ b/net/base/net_error_list.h
@@ -838,6 +838,9 @@
 // An error occurred while handling a signed exchange.
 NET_ERROR(INVALID_SIGNED_EXCHANGE, -504)
 
+// An error occurred while handling a bundled-exchanges source.
+NET_ERROR(INVALID_BUNDLED_EXCHANGES, -505)
+
 // *** Code -600 is reserved (was FTP_PASV_COMMAND_FAILED). ***
 
 // A generic error for failed FTP control connection command.
diff --git a/net/http/http_auth_gssapi_posix.cc b/net/http/http_auth_gssapi_posix.cc
index f8efa9f7..ad7ecad 100644
--- a/net/http/http_auth_gssapi_posix.cc
+++ b/net/http/http_auth_gssapi_posix.cc
@@ -261,9 +261,11 @@
 }
 
 base::Value ContextFlagsToValue(OM_uint32 flags) {
-  // TODO(asanka): This should break down known flags. At least the
-  // GSS_C_DELEG_FLAG.
-  return base::Value(base::StringPrintf("%08x", flags));
+  base::Value rv{base::Value::Type::DICTIONARY};
+  rv.SetStringKey("value", base::StringPrintf("0x%08x", flags));
+  rv.SetBoolKey("delegated", (flags & GSS_C_DELEG_FLAG) == GSS_C_DELEG_FLAG);
+  rv.SetBoolKey("mutual", (flags & GSS_C_MUTUAL_FLAG) == GSS_C_MUTUAL_FLAG);
+  return rv;
 }
 
 base::Value GetContextStateAsValue(GSSAPILibrary* gssapi_lib,
diff --git a/net/http/http_auth_gssapi_posix_unittest.cc b/net/http/http_auth_gssapi_posix_unittest.cc
index 7ae41f7..27dbfab 100644
--- a/net/http/http_auth_gssapi_posix_unittest.cc
+++ b/net/http/http_auth_gssapi_posix_unittest.cc
@@ -632,7 +632,11 @@
         "mechanism": {
           "oid": "<Empty OID>"
         },
-        "flags": "00000000",
+        "flags": {
+          "value": "0x00000000",
+          "delegated": false,
+          "mutual": false
+        },
         "open": false
       }
   )");
diff --git a/net/log/net_log_event_type_list.h b/net/log/net_log_event_type_list.h
index b6db4a7..6adbd93 100644
--- a/net/log/net_log_event_type_list.h
+++ b/net/log/net_log_event_type_list.h
@@ -2221,6 +2221,19 @@
 //               contains the error in the form of a GSSAPI Status.>
 //   }
 //
+// ** GSSAPI Context Flags
+//
+// Bitmask indicating properties of the negotiated security context. Values may
+// be only advisory if the "open" flag of the enclosing security context is
+// True. I.e. flags are not final until the security context is closed.
+//
+//   {
+//     "flags"     : <Flags. See RFC 2744 Section 5.19 for meanings. Flag
+//                    bitmasks can be found in RFC 2744 Appendix A.>
+//     "delegated" : <True if credentials were delegated to the target.>
+//     "mutual"    : <True if mutual authentication was successful.>
+//   }
+//
 // ** GSSAPI Context Description
 //
 // A serialization of the GSSAPI context. It takes the following form:
diff --git a/net/quic/quic_test_packet_maker.cc b/net/quic/quic_test_packet_maker.cc
index ba796c9..d18d7a0 100644
--- a/net/quic/quic_test_packet_maker.cc
+++ b/net/quic/quic_test_packet_maker.cc
@@ -131,8 +131,7 @@
       spdy_response_framer_(spdy::SpdyFramer::ENABLE_COMPRESSION),
       coalesce_http_frames_(false),
       save_packet_frames_(false),
-      qpack_encoder_(&decoder_stream_error_delegate_,
-                     &encoder_stream_sender_delegate_),
+      qpack_encoder_(&decoder_stream_error_delegate_),
       perspective_(perspective),
       encryption_level_(quic::ENCRYPTION_FORWARD_SECURE),
       long_header_type_(quic::INVALID_PACKET_TYPE),
@@ -141,6 +140,9 @@
           version.transport_version >= quic::QUIC_VERSION_43) {
   DCHECK(!(perspective_ == quic::Perspective::IS_SERVER &&
            client_headers_include_h2_stream_dependency_));
+
+  qpack_encoder_.set_qpack_stream_sender_delegate(
+      &encoder_stream_sender_delegate_);
 }
 
 QuicTestPacketMaker::~QuicTestPacketMaker() {
diff --git a/remoting/resources/BUILD.gn b/remoting/resources/BUILD.gn
index bbe591f..cf5d4cd 100644
--- a/remoting/resources/BUILD.gn
+++ b/remoting/resources/BUILD.gn
@@ -31,7 +31,6 @@
     "../host/win/core.rc.jinja2",
     "../host/win/host_messages.mc.jinja2",
     "../host/win/version.rc.jinja2",
-    "../resources/play_store_resources.cc",
     "../webapp/crd/js/background.js",
     "../webapp/base/js/error.js",
     "../webapp/crd/js/host_list.js",
diff --git a/remoting/resources/play_store_resources.cc b/remoting/resources/play_store_resources.cc
deleted file mode 100644
index 976f51e..0000000
--- a/remoting/resources/play_store_resources.cc
+++ /dev/null
@@ -1,10 +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.
-
-// This file exists only to mark the Play Store string resources as used.
-IDS_PLAY_STORE_TITLE
-IDS_PLAY_STORE_TAGLINE
-IDS_PLAY_STORE_DESCRIPTION
-IDS_PLAY_STORE_DESCRIPTION_ALTERNATE
-IDS_PLAY_STORE_CHANGES
diff --git a/remoting/resources/remoting_strings.grd b/remoting/resources/remoting_strings.grd
index 805d19eb..cefd43c 100644
--- a/remoting/resources/remoting_strings.grd
+++ b/remoting/resources/remoting_strings.grd
@@ -659,65 +659,6 @@
         <message name="IDS_PRIVACY_POLICY" desc="The label to access the privacy policy, displayed in navigation menu." formatter_data="android_java">
           Privacy Policy
         </message>
-
-<!-- Play Store listings text. These Android-specific strings are not marked
-     with formatter_data="android_java" since they are used only for the Play
-     Store listings, and should not be bundled in the APK.
--->
-        <message name="IDS_PLAY_STORE_TITLE" desc="Play Store title of the Chrome Remote Desktop app for Android. If needed to meet the 30-char limit, please translate just 'Remote Desktop' instead. [CHAR-LIMIT=30] [NAME=play_store_title]" meaning="Title used in Play Store">
-          Chrome Remote Desktop
-        </message>
-        <message name="IDS_PLAY_STORE_TAGLINE" desc="Play Store tagline for the Chrome Remote Desktop app for Android. [CHAR-LIMIT=80] [NAME=play_store_tagline]">
-          Securely access your computers from your Android device.
-        </message>
-        <message name="IDS_PLAY_STORE_DESCRIPTION" desc="Play Store description of the Chrome Remote Desktop app for Android. [CHAR-LIMIT=4000] [NAME=play_store_description]">
-Securely access your computers from your Android device.
-
-• On each of your computers, set up remote access using the Chrome Remote Desktop app from Chrome Web Store: https://chrome.google.com/remotedesktop
-• On your Android device, open the app and tap on any of your online computers to connect.
-
-For information about privacy, please see the Google Privacy Policy (http://goo.gl/SyrVzj) and the Chrome Privacy Policy (http://goo.gl/0uXE5d).
-        </message>
-        <message name="IDS_PLAY_STORE_DESCRIPTION_ALTERNATE" desc="Play Store description of the Chrome Remote Desktop app for Android. [CHAR-LIMIT=4000] [NAME=play_store_description_alternate]">
-Securely access your computers from your Android device.
-
-• On each of your computers, set up remote access using the Chrome Remote Desktop app from Chrome Web Store: https://chrome.google.com/remotedesktop
-• On your Android device, open the app and tap on any of your online computers to connect.
-
-Remote computers with non US-English keyboards may receive incorrect text input. Support for other keyboard layouts is coming soon!
-
-For information about privacy, please see the Google Privacy Policy (http://goo.gl/SyrVzj) and the Chrome Privacy Policy (http://goo.gl/0uXE5d).
-        </message>
-        <message name="IDS_PLAY_STORE_CHANGES" desc="List of what's changed in this release of Chrome Remote Desktop for Android. [CHAR-LIMIT=500] [NAME=play_store_changes]">
-Stability improvements and bug fixes.
-        </message>
-
-        <!-- iOS App Store metadata text. Won't be bundled into the app. -->
-        <message name="IDS_APP_STORE_TITLE" desc="App Store title of the Chrome Remote Desktop app for iOS. If needed to meet the 50-char limit, please translate just 'Remote Desktop' instead. [CHAR-LIMIT=50] [NAME=app_store_title]" meaning="Title used in App Store">
-          Chrome Remote Desktop
-        </message>
-        <message name="IDS_APP_STORE_SUBTITLE" desc="App Store subtitle text. To be displayed below the app title in App Store. No period at the end. [CHAR-LIMIT=30]" meaning="Please try to keep translation below 30 characters. If not possible, please try to translate 'access your computer' instead.">
-          Securely access your computer
-        </message>
-        <message name="IDS_APP_STORE_KEYWORDS" desc="App Store keywords (to be used to search for the app) for the Chrome Remote Desktop app for iOS. Separate keywords with newline. [NAME=app_store_keywords]">
-Chrome
-Remote
-Desktop
-Access
-Support
-Computer
-PC
-        </message>
-        <message name="IDS_APP_STORE_DESCRIPTION" desc="App Store description of the Chrome Remote Desktop app for iOS. [CHAR-LIMIT=4000] [NAME=app_store_description]">
-Securely access your computer from your iOS device. It's fast, simple and free.
-
-• Download the Chrome Remote Desktop app from the Chrome WebStore on the computer you want to access remotely.
-• Install Chrome Remote Desktop software and follow the instructions to complete setup.
-• On your iOS device, open the app and tap on any of your online computers to connect.
-        </message>
-        <message name="IDS_APP_STORE_CHANGES" desc="List of what's changed in this release of Chrome Remote Desktop for iOS. [CHAR-LIMIT=4000] [NAME=app_store_changes]">
-Stability improvements and bug fixes.
-        </message>
       </if>  <!-- is_android or is_ios -->
 
       <if expr="is_ios">
diff --git a/services/content/simple_browser/BUILD.gn b/services/content/simple_browser/BUILD.gn
deleted file mode 100644
index 05185e2..0000000
--- a/services/content/simple_browser/BUILD.gn
+++ /dev/null
@@ -1,47 +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.
-
-import("//build/config/ui.gni")
-
-component("simple_browser") {
-  public = [
-    "simple_browser_service.h",
-  ]
-
-  sources = [
-    "simple_browser_service.cc",
-    "window.cc",
-    "window.h",
-  ]
-
-  defines = [ "IS_SIMPLE_BROWSER_IMPL" ]
-
-  public_deps = [
-    "//base",
-    "//mojo/public/cpp/bindings",
-    "//mojo/public/cpp/system",
-    "//services/content/simple_browser/public/mojom",
-    "//services/service_manager/public/cpp",
-  ]
-
-  deps = [
-    "//services/content/public/cpp",
-    "//services/content/public/mojom",
-  ]
-
-  if (toolkit_views) {
-    deps += [ "//ui/views" ]
-  }
-
-  if (use_aura) {
-    deps += [ "//ui/aura" ]
-  }
-
-  if (is_linux) {
-    public_deps += [
-      "//components/services/font/public/cpp",
-      "//components/services/font/public/mojom",
-    ]
-  }
-}
diff --git a/services/content/simple_browser/DEPS b/services/content/simple_browser/DEPS
deleted file mode 100644
index 10525a0b..0000000
--- a/services/content/simple_browser/DEPS
+++ /dev/null
@@ -1,6 +0,0 @@
-include_rules = [
-  "+components/services/font/public",
-  "+third_party/skia/include",
-  "+ui/aura",
-  "+ui/views",
-]
diff --git a/services/content/simple_browser/OWNERS b/services/content/simple_browser/OWNERS
deleted file mode 100644
index e69de29..0000000
--- a/services/content/simple_browser/OWNERS
+++ /dev/null
diff --git a/services/content/simple_browser/README.md b/services/content/simple_browser/README.md
deleted file mode 100644
index 57170d50..0000000
--- a/services/content/simple_browser/README.md
+++ /dev/null
@@ -1,18 +0,0 @@
-This directory contains an implementation of a simple Content Service client
-application which serves as a tool for manual developer testing of the Content
-Service, a straightforward example browser application consuming the Content
-Service, and potentially as a target for driving automated Content Service
-integration tests.
-
-The simple_browser application can run in an isolated sandboxed process on
-platforms which support the UI Service (currently only Chrome OS), or within the
-browser process on platforms which otherwise support NavigableContentsView
-embedding (currently Chrome OS, Linux, Mac, and Windows).
-
-To play around with simple_browser today, run a DCHECK-enabled build of Chrome
-with `--launch-in-process-simple-browser` on any supported platform listed
-above.
-
-To test the sandboxed, out-of-process version, use a DCHECK-enabled Chrome OS
-build and run with both `--enable-features=Mash` and `--launch-simple-browser`
-flags.
diff --git a/services/content/simple_browser/public/cpp/BUILD.gn b/services/content/simple_browser/public/cpp/BUILD.gn
deleted file mode 100644
index 008d5c4..0000000
--- a/services/content/simple_browser/public/cpp/BUILD.gn
+++ /dev/null
@@ -1,16 +0,0 @@
-# Copyright 2019 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-source_set("manifest") {
-  sources = [
-    "manifest.cc",
-    "manifest.h",
-  ]
-
-  deps = [
-    "//base",
-    "//services/content/simple_browser/public/mojom",
-    "//services/service_manager/public/cpp",
-  ]
-}
diff --git a/services/content/simple_browser/public/cpp/OWNERS b/services/content/simple_browser/public/cpp/OWNERS
deleted file mode 100644
index 6faeaa47..0000000
--- a/services/content/simple_browser/public/cpp/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-per-file manifest.cc=set noparent
-per-file manifest.cc=file://ipc/SECURITY_OWNERS
-per-file manifest.h=set noparent
-per-file manifest.h=file://ipc/SECURITY_OWNERS
diff --git a/services/content/simple_browser/public/cpp/manifest.cc b/services/content/simple_browser/public/cpp/manifest.cc
deleted file mode 100644
index 05301ff..0000000
--- a/services/content/simple_browser/public/cpp/manifest.cc
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "services/content/simple_browser/public/cpp/manifest.h"
-
-#include "base/no_destructor.h"
-#include "services/content/simple_browser/public/mojom/constants.mojom.h"
-#include "services/service_manager/public/cpp/manifest_builder.h"
-
-namespace simple_browser {
-
-const service_manager::Manifest& GetManifest() {
-  static base::NoDestructor<service_manager::Manifest> manifest{
-      service_manager::ManifestBuilder()
-          .WithServiceName(mojom::kServiceName)
-          .WithDisplayName("Simple Browser")
-          .WithOptions(service_manager::ManifestOptionsBuilder()
-                           .WithSandboxType("utility")
-                           .Build())
-          .ExposeCapability("app", {})
-          .RequireCapability("content", "navigation")
-          .RequireCapability("font_service", "font_service")
-          .RequireCapability("ui", "app")
-          .Build()};
-  return *manifest;
-}
-
-}  // namespace simple_browser
diff --git a/services/content/simple_browser/public/cpp/manifest.h b/services/content/simple_browser/public/cpp/manifest.h
deleted file mode 100644
index ff91acab..0000000
--- a/services/content/simple_browser/public/cpp/manifest.h
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef SERVICES_CONTENT_SIMPLE_BROWSER_PUBLIC_CPP_MANIFEST_H_
-#define SERVICES_CONTENT_SIMPLE_BROWSER_PUBLIC_CPP_MANIFEST_H_
-
-#include "services/service_manager/public/cpp/manifest.h"
-
-namespace simple_browser {
-
-const service_manager::Manifest& GetManifest();
-
-}  // namespace simple_browser
-
-#endif  // SERVICES_CONTENT_SIMPLE_BROWSER_PUBLIC_CPP_MANIFEST_H_
diff --git a/services/content/simple_browser/public/mojom/BUILD.gn b/services/content/simple_browser/public/mojom/BUILD.gn
deleted file mode 100644
index 97d6cd4..0000000
--- a/services/content/simple_browser/public/mojom/BUILD.gn
+++ /dev/null
@@ -1,11 +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.
-
-import("//mojo/public/tools/bindings/mojom.gni")
-
-mojom("mojom") {
-  sources = [
-    "constants.mojom",
-  ]
-}
diff --git a/services/content/simple_browser/public/mojom/OWNERS b/services/content/simple_browser/public/mojom/OWNERS
deleted file mode 100644
index 08850f4..0000000
--- a/services/content/simple_browser/public/mojom/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-per-file *.mojom=set noparent
-per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/services/content/simple_browser/public/mojom/constants.mojom b/services/content/simple_browser/public/mojom/constants.mojom
deleted file mode 100644
index fb751d7..0000000
--- a/services/content/simple_browser/public/mojom/constants.mojom
+++ /dev/null
@@ -1,7 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-module simple_browser.mojom;
-
-const string kServiceName = "simple_browser";
diff --git a/services/content/simple_browser/simple_browser_service.cc b/services/content/simple_browser/simple_browser_service.cc
deleted file mode 100644
index f9d8afb..0000000
--- a/services/content/simple_browser/simple_browser_service.cc
+++ /dev/null
@@ -1,36 +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.
-
-#include "services/content/simple_browser/simple_browser_service.h"
-
-#include "build/build_config.h"
-#include "services/content/simple_browser/window.h"
-
-#if defined(OS_LINUX)
-#include "third_party/skia/include/ports/SkFontConfigInterface.h"  // nogncheck
-#endif
-
-namespace simple_browser {
-
-SimpleBrowserService::SimpleBrowserService(
-    service_manager::mojom::ServiceRequest request,
-    UIInitializationMode ui_initialization_mode)
-    : service_binding_(this, std::move(request)),
-      ui_initialization_mode_(ui_initialization_mode) {}
-
-SimpleBrowserService::~SimpleBrowserService() = default;
-
-void SimpleBrowserService::OnStart() {
-  if (ui_initialization_mode_ == UIInitializationMode::kInitializeUI) {
-#if defined(OS_LINUX)
-    font_loader_ =
-        sk_make_sp<font_service::FontLoader>(service_binding_.GetConnector());
-    SkFontConfigInterface::SetGlobal(font_loader_);
-#endif
-  }
-
-  window_ = std::make_unique<Window>(service_binding_.GetConnector());
-}
-
-}  // namespace simple_browser
diff --git a/services/content/simple_browser/simple_browser_service.h b/services/content/simple_browser/simple_browser_service.h
deleted file mode 100644
index 7a8e866..0000000
--- a/services/content/simple_browser/simple_browser_service.h
+++ /dev/null
@@ -1,62 +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.
-
-#ifndef SERVICES_CONTENT_SIMPLE_BROWSER_SIMPLE_BROWSER_SERVICE_H_
-#define SERVICES_CONTENT_SIMPLE_BROWSER_SIMPLE_BROWSER_SERVICE_H_
-
-#include <map>
-#include <memory>
-
-#include "base/component_export.h"
-#include "base/macros.h"
-#include "build/build_config.h"
-#include "services/service_manager/public/cpp/service.h"
-#include "services/service_manager/public/cpp/service_binding.h"
-#include "services/service_manager/public/mojom/service.mojom.h"
-
-#if defined(OS_LINUX)
-#include "components/services/font/public/cpp/font_loader.h"  // nogncheck
-#endif
-
-namespace simple_browser {
-
-class Window;
-
-class COMPONENT_EXPORT(SIMPLE_BROWSER) SimpleBrowserService
-    : public service_manager::Service {
- public:
-  // Determines how a SimpleBrowserService instance is initialized.
-  enum class UIInitializationMode {
-    // The service is being run in an isolated process which has not yet
-    // initialized a UI framework.
-    kInitializeUI,
-
-    // The service is being run in a process which has already initialized a
-    // UI framework. No need to do that.
-    kUseEnvironmentUI,
-  };
-
-  SimpleBrowserService(service_manager::mojom::ServiceRequest request,
-                       UIInitializationMode mode);
-  ~SimpleBrowserService() override;
-
- private:
-  // service_manager::Service:
-  void OnStart() override;
-
-#if defined(OS_LINUX)
-  sk_sp<font_service::FontLoader> font_loader_;
-#endif
-
-  service_manager::ServiceBinding service_binding_;
-  const UIInitializationMode ui_initialization_mode_;
-
-  std::unique_ptr<Window> window_;
-
-  DISALLOW_COPY_AND_ASSIGN(SimpleBrowserService);
-};
-
-}  // namespace simple_browser
-
-#endif  // SERVICES_CONTENT_SIMPLE_BROWSER_SIMPLE_BROWSER_SERVICE_H_
diff --git a/services/content/simple_browser/window.cc b/services/content/simple_browser/window.cc
deleted file mode 100644
index 97e417a9..0000000
--- a/services/content/simple_browser/window.cc
+++ /dev/null
@@ -1,114 +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.
-
-#include "services/content/simple_browser/window.h"
-
-#include <memory>
-#include <utility>
-
-#include "base/strings/utf_string_conversions.h"
-#include "mojo/public/cpp/bindings/remote.h"
-#include "services/content/public/cpp/navigable_contents.h"
-#include "services/content/public/cpp/navigable_contents_view.h"
-#include "services/content/public/mojom/constants.mojom.h"
-#include "services/content/public/mojom/navigable_contents_factory.mojom.h"
-#include "services/service_manager/public/cpp/connector.h"
-#include "ui/aura/window.h"
-#include "ui/aura/window_tree_host.h"
-#include "ui/views/background.h"
-#include "ui/views/border.h"
-#include "ui/views/controls/textfield/textfield.h"
-#include "ui/views/controls/textfield/textfield_controller.h"
-#include "ui/views/widget/widget.h"
-#include "ui/views/widget/widget_delegate.h"
-
-namespace simple_browser {
-
-namespace {
-
-class SimpleBrowserUI : public views::WidgetDelegateView,
-                        public views::TextfieldController {
- public:
-  explicit SimpleBrowserUI(service_manager::Connector* connector)
-      : connector_(connector), location_bar_(new views::Textfield) {
-    SetBackground(views::CreateStandardPanelBackground());
-    location_bar_->set_controller(this);
-    AddChildView(location_bar_);
-
-    connector_->Connect(
-        content::mojom::kServiceName,
-        navigable_contents_factory_.BindNewPipeAndPassReceiver());
-    navigable_contents_ = std::make_unique<content::NavigableContents>(
-        navigable_contents_factory_.get());
-    navigable_contents_view_ = navigable_contents_->GetView();
-
-    AddChildView(navigable_contents_view_->view());
-  }
-
-  ~SimpleBrowserUI() override = default;
-
- private:
-  // views::WidgetDelegate:
-  base::string16 GetWindowTitle() const override {
-    return base::ASCIIToUTF16("Simple Browser");
-  }
-
-  // views::View:
-  void Layout() override {
-    gfx::Rect location_bar_bounds{GetLocalBounds().width(), 20};
-    location_bar_bounds.Inset(5, 0);
-    location_bar_->SetBoundsRect(location_bar_bounds);
-
-    gfx::Rect content_view_bounds = GetLocalBounds();
-    content_view_bounds.Inset(5, 25, 5, 5);
-    navigable_contents_view_->view()->SetBoundsRect(content_view_bounds);
-  }
-
-  gfx::Size CalculatePreferredSize() const override {
-    return gfx::Size(640, 480);
-  }
-
-  // views::TextFieldController:
-  bool HandleKeyEvent(views::Textfield* sender,
-                      const ui::KeyEvent& key_event) override {
-    if (key_event.type() != ui::ET_KEY_PRESSED)
-      return false;
-
-    if (key_event.key_code() == ui::VKEY_RETURN) {
-      navigable_contents_->Navigate(
-          GURL(base::UTF16ToUTF8(location_bar_->GetText())));
-    }
-
-    return false;
-  }
-
-  service_manager::Connector* const connector_;
-
-  mojo::Remote<content::mojom::NavigableContentsFactory>
-      navigable_contents_factory_;
-  std::unique_ptr<content::NavigableContents> navigable_contents_;
-  content::NavigableContentsView* navigable_contents_view_ = nullptr;
-
-  views::Textfield* location_bar_;
-
-  DISALLOW_COPY_AND_ASSIGN(SimpleBrowserUI);
-};
-
-}  // namespace
-
-Window::Window(service_manager::Connector* connector) {
-  window_widget_ = views::Widget::CreateWindowWithContextAndBounds(
-      new SimpleBrowserUI(connector), nullptr, gfx::Rect(10, 10, 640, 480));
-
-#if defined(USE_AURA)
-  window_widget_->GetNativeWindow()->GetHost()->window()->SetName(
-      "SimpleBrowser");
-#endif
-
-  window_widget_->Show();
-}
-
-Window::~Window() = default;
-
-}  // namespace simple_browser
diff --git a/services/content/simple_browser/window.h b/services/content/simple_browser/window.h
deleted file mode 100644
index d597806..0000000
--- a/services/content/simple_browser/window.h
+++ /dev/null
@@ -1,33 +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.
-
-#ifndef SERVICES_CONTENT_SIMPLE_BROWSER_WINDOW_H_
-#define SERVICES_CONTENT_SIMPLE_BROWSER_WINDOW_H_
-
-#include "base/macros.h"
-
-namespace service_manager {
-class Connector;
-}
-
-namespace views {
-class Widget;
-}
-
-namespace simple_browser {
-
-class Window {
- public:
-  explicit Window(service_manager::Connector* connector);
-  ~Window();
-
- private:
-  views::Widget* window_widget_;
-
-  DISALLOW_COPY_AND_ASSIGN(Window);
-};
-
-}  // namespace simple_browser
-
-#endif  // SERVICES_CONTENT_SIMPLE_BROWSER_WINDOW_H_
diff --git a/services/data_decoder/bundled_exchanges_parser.cc b/services/data_decoder/bundled_exchanges_parser.cc
index f3ea3b1..dcc90b7 100644
--- a/services/data_decoder/bundled_exchanges_parser.cc
+++ b/services/data_decoder/bundled_exchanges_parser.cc
@@ -29,8 +29,8 @@
 // The maximum size of the section-lengths CBOR item.
 constexpr uint64_t kMaxSectionLengthsCBORSize = 8192;
 
-// The maximum size of the index section allowed in this implementation.
-constexpr uint64_t kMaxIndexSectionSize = 1 * 1024 * 1024;
+// The maximum size of a metadata section allowed in this implementation.
+constexpr uint64_t kMaxMetadataSectionSize = 1 * 1024 * 1024;
 
 // The maximum size of the response header CBOR.
 constexpr uint64_t kMaxResponseHeaderLength = 512 * 1024;
@@ -52,7 +52,9 @@
 };
 
 // Section names.
+constexpr char kCriticalSection[] = "critical";
 constexpr char kIndexSection[] = "index";
+constexpr char kManifestSection[] = "manifest";
 constexpr char kResponsesSection[] = "responses";
 
 // https://tools.ietf.org/html/draft-ietf-cbor-7049bis-05#section-3.1
@@ -65,6 +67,14 @@
 // A list of (section-name, length) pairs.
 using SectionLengths = std::vector<std::pair<std::string, uint64_t>>;
 
+// A map from section name to (offset, length) pair.
+using SectionOffsets = std::map<std::string, std::pair<uint64_t, uint64_t>>;
+
+bool IsMetadataSection(const std::string& name) {
+  return (name == kIndexSection || name == kCriticalSection ||
+          name == kManifestSection);
+}
+
 // Parses a `section-lengths` CBOR item.
 // https://wicg.github.io/webpackage/draft-yasskin-wpack-bundled-exchanges.html#load-metadata
 base::Optional<SectionLengths> ParseSectionLengths(
@@ -266,11 +276,8 @@
   DISALLOW_COPY_AND_ASSIGN(Observer);
 };
 
-// A parser for bundle's metadata. This currently looks only at the "index"
-// section. This class owns itself and will self destruct after calling the
-// ParseMetadataCallback.
-// TODO(crbug.com/966753): Add support for the "manifest" section and "critical"
-// section.
+// A parser for bundle's metadata. This class owns itself and will self destruct
+// after calling the ParseMetadataCallback.
 class BundledExchangesParser::MetadataParser
     : BundledExchangesParser::SharedBundleDataSource::Observer {
  public:
@@ -361,7 +368,7 @@
                                       *url_length, input.CurrentOffset()));
   }
 
-  // Step 5-20 of
+  // Step 5-21 of
   // https://wicg.github.io/webpackage/draft-yasskin-wpack-bundled-exchanges.html#load-metadata
   void ParseBundleHeader(uint64_t expected_data_length,
                          uint64_t url_length,
@@ -521,49 +528,115 @@
       return;
     }
 
-    // Read the index section.
-    auto index_section = section_offsets_.find(kIndexSection);
-    if (index_section == section_offsets_.end()) {
-      RunErrorCallbackAndDestroy("No index section.");
+    // Step 21. "Let metadata be a map ([INFRA]) initially containing the single
+    // key/value pair "primaryUrl"/fallbackUrl."
+    metadata_ = mojom::BundleMetadata::New();
+    metadata_->primary_url = fallback_url_;
+
+    ReadMetadataSections(section_offsets_.begin());
+  }
+
+  // Step 22-25 of
+  // https://wicg.github.io/webpackage/draft-yasskin-wpack-bundled-exchanges.html#load-metadata
+  void ReadMetadataSections(SectionOffsets::const_iterator section_iter) {
+    // Step 22. "For each "name" -> (offset, length) triple in sectionOffsets:"
+    for (; section_iter != section_offsets_.end(); ++section_iter) {
+      const auto& name = section_iter->first;
+      // Step 22.1. "If "name" isn't in knownSections, continue to the next
+      // triple."
+      // Step 22.2. "If "name"'s Metadata field (Section 6.2) is "No", continue
+      // to the next triple."
+      if (!IsMetadataSection(name))
+        continue;
+
+      // Step 22.3. "If "name" is in ignoredSections, continue to the next
+      // triple."
+      // In the current spec, ignoredSections is always empty.
+
+      const uint64_t section_offset = section_iter->second.first;
+      const uint64_t section_length = section_iter->second.second;
+      if (section_length > kMaxMetadataSectionSize) {
+        RunErrorCallbackAndDestroy(
+            "Metadata sections larger than 1MB are not supported.");
+        return;
+      }
+
+      data_source_->Read(section_offset, section_length,
+                         base::BindOnce(&MetadataParser::ParseMetadataSection,
+                                        weak_factory_.GetWeakPtr(),
+                                        section_iter, section_length));
+      // This loop will be resumed by ParseMetadataSection().
       return;
     }
-    const uint64_t index_section_offset = index_section->second.first;
-    const uint64_t index_section_length = index_section->second.second;
 
-    if (index_section_length > kMaxIndexSectionSize) {
+    // Step 23. "Assert: metadata has an entry with the key "primaryUrl"."
+    DCHECK(!metadata_->primary_url.is_empty());
+
+    // Step 24. "If metadata doesn't have entries with keys "requests" and
+    // "manifest", return a "format error" with fallbackUrl."
+    if (metadata_->requests.empty()) {
+      RunErrorCallbackAndDestroy("Bundle must have an index section.");
+      return;
+    }
+    if (metadata_->manifest_url.is_empty()) {
+      RunErrorCallbackAndDestroy("Bundle must have a manifest URL.");
+      return;
+    }
+
+    // Step 25. "Return metadata."
+    RunSuccessCallbackAndDestroy();
+  }
+
+  void ParseMetadataSection(SectionOffsets::const_iterator section_iter,
+                            uint64_t expected_data_length,
+                            const base::Optional<std::vector<uint8_t>>& data) {
+    if (!data || data->size() != expected_data_length) {
+      RunErrorCallbackAndDestroy("Error reading section content.");
+      return;
+    }
+
+    // Parse the section contents as a CBOR item.
+    cbor::Reader::DecoderError error;
+    base::Optional<cbor::Value> section_value =
+        cbor::Reader::Read(*data, &error);
+    if (!section_value) {
       RunErrorCallbackAndDestroy(
-          "Index section larger than 1MB is not supported.");
+          std::string("Error parsing section contents as CBOR: ") +
+          cbor::Reader::ErrorCodeToString(error));
       return;
     }
 
-    data_source_->Read(
-        index_section_offset, index_section_length,
-        base::BindOnce(&MetadataParser::ParseIndexSection,
-                       weak_factory_.GetWeakPtr(), index_section_length));
+    // Step 22.6. "Follow "name"'s specification from knownSections to process
+    // the section, passing sectionContents, stream, sectionOffsets, and
+    // metadata. If this returns an error, return a "format error" with
+    // fallbackUrl."
+    const auto& name = section_iter->first;
+    // Note: Parse*Section() delete |this| on failure.
+    if (name == kIndexSection) {
+      if (!ParseIndexSection(*section_value))
+        return;
+    } else if (name == kManifestSection) {
+      if (!ParseManifestSection(*section_value))
+        return;
+    } else if (name == kCriticalSection) {
+      if (!ParseCriticalSection(*section_value))
+        return;
+    } else {
+      // TODO(crbug.com/969596): Support the "signatures" section.
+      NOTIMPLEMENTED();
+    }
+    // Resume the loop of Step 22.
+    ReadMetadataSections(++section_iter);
   }
 
   // https://wicg.github.io/webpackage/draft-yasskin-wpack-bundled-exchanges.html#index-section
-  void ParseIndexSection(uint64_t expected_data_length,
-                         const base::Optional<std::vector<uint8_t>>& data) {
-    if (!data || data->size() != expected_data_length) {
-      RunErrorCallbackAndDestroy("Error reading index section.");
-      return;
-    }
-
+  bool ParseIndexSection(const cbor::Value& section_value) {
     // Step 1. "Let index be the result of parsing sectionContents as a CBOR
     // item matching the index rule in the above CDDL (Section 3.5). If index is
     // an error, return an error."
-    cbor::Reader::DecoderError error;
-    base::Optional<cbor::Value> index_section_value =
-        cbor::Reader::Read(*data, &error);
-    if (!index_section_value) {
-      VLOG(1) << cbor::Reader::ErrorCodeToString(error);
-      RunErrorCallbackAndDestroy("Error parsing index section.");
-      return;
-    }
-    if (!index_section_value->is_map()) {
+    if (!section_value.is_map()) {
       RunErrorCallbackAndDestroy("Index section must be a map.");
-      return;
+      return false;
     }
 
     // Step 2. "Let requests be an initially-empty map ([INFRA]) from URLs to
@@ -584,14 +657,14 @@
     const uint64_t responses_section_length = responses_section->second.second;
 
     // Step 4. "For each (url, responses) entry in the index map:"
-    for (const auto& item : index_section_value->GetMap()) {
+    for (const auto& item : section_value.GetMap()) {
       if (!item.first.is_string()) {
         RunErrorCallbackAndDestroy("Index section: key must be a string.");
-        return;
+        return false;
       }
       if (!item.second.is_array()) {
         RunErrorCallbackAndDestroy("Index section: value must be an array.");
-        return;
+        return false;
       }
       const std::string& url = item.first.GetString();
       const cbor::Value::ArrayValue& responses_array = item.second.GetArray();
@@ -601,7 +674,7 @@
       if (!base::IsStringUTF8(url)) {
         RunErrorCallbackAndDestroy(
             "Index section: URL must be a valid UTF-8 string.");
-        return;
+        return false;
       }
       GURL parsed_url(url);
 
@@ -612,7 +685,7 @@
         RunErrorCallbackAndDestroy(
             "Index section: exchange URL must be a valid URL without fragment "
             "or credentials.");
-        return;
+        return false;
       }
 
       // Step 4.3. "If the first element of responses is the empty string:"
@@ -620,7 +693,7 @@
         RunErrorCallbackAndDestroy(
             "Index section: the first element of responses array must be a "
             "bytestring.");
-        return;
+        return false;
       }
       base::StringPiece variants_value =
           responses_array[0].GetBytestringAsString();
@@ -630,7 +703,7 @@
         if (responses_array.size() != 3) {
           RunErrorCallbackAndDestroy(
               "Index section: unexpected size of responses array.");
-          return;
+          return false;
         }
       } else {
         // Step 4.4. "Otherwise:"
@@ -640,7 +713,7 @@
         if (responses_array.size() < 3 || responses_array.size() % 2 != 1) {
           RunErrorCallbackAndDestroy(
               "Index section: unexpected size of responses array.");
-          return;
+          return false;
         }
       }
       // Instead of constructing a map from Variant-Keys to location-in-stream,
@@ -652,7 +725,7 @@
             !responses_array[i + 1].is_unsigned()) {
           RunErrorCallbackAndDestroy(
               "Index section: offset and length values must be unsigned.");
-          return;
+          return false;
         }
         uint64_t offset = responses_array[i].GetUnsigned();
         uint64_t length = responses_array[i + 1].GetUnsigned();
@@ -664,7 +737,7 @@
         if (!base::CheckAdd(offset, length).AssignIfValid(&response_end) ||
             response_end > responses_section_length) {
           RunErrorCallbackAndDestroy("Index section: response out of range.");
-          return;
+          return false;
         }
         // Step 3.2. "Otherwise, return a ResponseMetadata struct whose offset
         // is sectionOffsets["responses"].offset + offset and whose length is
@@ -683,23 +756,76 @@
                                        std::move(response_locations))));
     }
 
-    // We're done.
-    RunSuccessCallbackAndDestroy(mojom::BundleMetadata::New(
-        fallback_url_, std::move(requests), manifest_url_));
+    // Step 5. "Set metadata["requests"] to requests."
+    metadata_->requests = std::move(requests);
+    return true;
   }
 
-  void RunSuccessCallbackAndDestroy(mojom::BundleMetadataPtr metadata) {
-    std::move(callback_).Run(std::move(metadata), nullptr);
+  // https://wicg.github.io/webpackage/draft-yasskin-wpack-bundled-exchanges.html#manifest-section
+  bool ParseManifestSection(const cbor::Value& section_value) {
+    // Step 1. "Let urlString be the result of parsing sectionContents as a CBOR
+    // item matching the above manifest rule (Section 3.5). If urlString is an
+    // error, return that error."
+    if (!section_value.is_string() ||
+        !base::IsStringUTF8(section_value.GetString())) {
+      RunErrorCallbackAndDestroy("Manifest section must be a UTF-8 string.");
+      return false;
+    }
+    // Step 2. "Let url be the result of parsing ([URL]) urlString with no base
+    // URL."
+    GURL parsed_url(section_value.GetString());
+    // Step 3. "If url is a failure, its fragment is not null, or it includes
+    // credentials, return an error."
+    if (!parsed_url.is_valid() || parsed_url.has_ref() ||
+        parsed_url.has_username() || parsed_url.has_password()) {
+      RunErrorCallbackAndDestroy(
+          "Manifest URL must be a valid URL without fragment or credentials.");
+      return false;
+    }
+    // Step 4. "Set metadata["manifest"] to url."
+    metadata_->manifest_url = std::move(parsed_url);
+    return true;
+  }
+
+  // https://wicg.github.io/webpackage/draft-yasskin-wpack-bundled-exchanges.html#critical-section
+  bool ParseCriticalSection(const cbor::Value& section_value) {
+    // Step 1. "Let critical be the result of parsing sectionContents as a CBOR
+    // item matching the above critical rule (Section 3.5). If critical is an
+    // error, return that error."
+    if (!section_value.is_array()) {
+      RunErrorCallbackAndDestroy("Critical section must be an array.");
+      return false;
+    }
+    // Step 2. "For each value sectionName in the critical list, if the client
+    // has not implemented sections named sectionName, return an error."
+    for (const cbor::Value& elem : section_value.GetArray()) {
+      if (!elem.is_string()) {
+        RunErrorCallbackAndDestroy(
+            "Non-string element in the critical section.");
+        return false;
+      }
+      const auto& section_name = elem.GetString();
+      if (!IsMetadataSection(section_name) &&
+          section_name != kResponsesSection) {
+        RunErrorCallbackAndDestroy("Unknown critical section.");
+        return false;
+      }
+    }
+    return true;
+  }
+
+  void RunSuccessCallbackAndDestroy() {
+    std::move(callback_).Run(std::move(metadata_), nullptr);
     delete this;
   }
 
   void RunErrorCallbackAndDestroy(
-      const base::Optional<std::string>& error_message,
+      const std::string& message,
       mojom::BundleParseErrorType error_type =
           mojom::BundleParseErrorType::kFormatError) {
     mojom::BundleMetadataParseErrorPtr err =
         mojom::BundleMetadataParseError::New(error_type, fallback_url_,
-                                             *error_message);
+                                             message);
     std::move(callback_).Run(nullptr, std::move(err));
     delete this;
   }
@@ -714,9 +840,8 @@
   uint64_t size_;
   bool version_mismatch_ = false;
   GURL fallback_url_;
-  // name -> (offset, length)
-  std::map<std::string, std::pair<uint64_t, uint64_t>> section_offsets_;
-  GURL manifest_url_;
+  SectionOffsets section_offsets_;
+  mojom::BundleMetadataPtr metadata_;
 
   base::WeakPtrFactory<MetadataParser> weak_factory_{this};
 
diff --git a/services/data_decoder/bundled_exchanges_parser_unittest.cc b/services/data_decoder/bundled_exchanges_parser_unittest.cc
index c29edeea..d929e2c3 100644
--- a/services/data_decoder/bundled_exchanges_parser_unittest.cc
+++ b/services/data_decoder/bundled_exchanges_parser_unittest.cc
@@ -19,6 +19,7 @@
 namespace {
 
 constexpr char kFallbackUrl[] = "https://test.example.com/";
+constexpr char kManifestUrl[] = "https://test.example.com/manifest";
 
 std::string GetTestFileContents(const base::FilePath& path) {
   base::FilePath test_data_dir;
@@ -147,9 +148,14 @@
     int64_t length;
   };
 
-  explicit BundleBuilder(const std::string& fallback_url)
+  BundleBuilder(const std::string& fallback_url,
+                const std::string& manifest_url)
       : fallback_url_(fallback_url) {
     writer_config_.allow_invalid_utf8_for_testing = true;
+    if (!manifest_url.empty()) {
+      AddSection("manifest",
+                 cbor::Value::InvalidUTF8StringValueForTesting(manifest_url));
+    }
   }
 
   void AddExchange(base::StringPiece url,
@@ -253,17 +259,8 @@
   base::test::ScopedTaskEnvironment task_environment_;
 };
 
-TEST_F(BundledExchangeParserTest, EmptyBundle) {
-  BundleBuilder builder(kFallbackUrl);
-  TestDataSource data_source(builder.CreateBundle());
-
-  mojom::BundleMetadataPtr metadata = ParseBundle(&data_source).first;
-  ASSERT_TRUE(metadata);
-  ASSERT_EQ(metadata->requests.size(), 0u);
-}
-
 TEST_F(BundledExchangeParserTest, WrongMagic) {
-  BundleBuilder builder(kFallbackUrl);
+  BundleBuilder builder(kFallbackUrl, kManifestUrl);
   std::vector<uint8_t> bundle = builder.CreateBundle();
   bundle[3] ^= 1;
   TestDataSource data_source(bundle);
@@ -275,7 +272,7 @@
 }
 
 TEST_F(BundledExchangeParserTest, UnknownVersion) {
-  BundleBuilder builder(kFallbackUrl);
+  BundleBuilder builder(kFallbackUrl, kManifestUrl);
   std::vector<uint8_t> bundle = builder.CreateBundle();
   // Modify the version string from "b1\0\0" to "q1\0\0".
   ASSERT_EQ(bundle[11], 'b');
@@ -289,7 +286,7 @@
 }
 
 TEST_F(BundledExchangeParserTest, FallbackURLIsNotUTF8) {
-  BundleBuilder builder("https://test.example.com/\xcc");
+  BundleBuilder builder("https://test.example.com/\xcc", kManifestUrl);
   std::vector<uint8_t> bundle = builder.CreateBundle();
   TestDataSource data_source(bundle);
 
@@ -300,7 +297,7 @@
 }
 
 TEST_F(BundledExchangeParserTest, SectionLengthsTooLarge) {
-  BundleBuilder builder(kFallbackUrl);
+  BundleBuilder builder(kFallbackUrl, kManifestUrl);
   std::string too_long_section_name(8192, 'x');
   builder.AddSection(too_long_section_name, cbor::Value(0));
   TestDataSource data_source(builder.CreateBundle());
@@ -309,7 +306,7 @@
 }
 
 TEST_F(BundledExchangeParserTest, DuplicateSectionName) {
-  BundleBuilder builder(kFallbackUrl);
+  BundleBuilder builder(kFallbackUrl, kManifestUrl);
   builder.AddSection("foo", cbor::Value(0));
   builder.AddSection("foo", cbor::Value(0));
   TestDataSource data_source(builder.CreateBundle());
@@ -318,7 +315,7 @@
 }
 
 TEST_F(BundledExchangeParserTest, SingleEntry) {
-  BundleBuilder builder(kFallbackUrl);
+  BundleBuilder builder(kFallbackUrl, kManifestUrl);
   builder.AddExchange("https://test.example.com/",
                       {{":status", "200"}, {"content-type", "text/plain"}},
                       "payload");
@@ -338,7 +335,7 @@
 }
 
 TEST_F(BundledExchangeParserTest, InvalidRequestURL) {
-  BundleBuilder builder(kFallbackUrl);
+  BundleBuilder builder(kFallbackUrl, kManifestUrl);
   builder.AddExchange("", {{":status", "200"}, {"content-type", "text/plain"}},
                       "payload");
   TestDataSource data_source(builder.CreateBundle());
@@ -347,7 +344,7 @@
 }
 
 TEST_F(BundledExchangeParserTest, RequestURLIsNotUTF8) {
-  BundleBuilder builder(kFallbackUrl);
+  BundleBuilder builder(kFallbackUrl, kManifestUrl);
   builder.AddExchange("https://test.example.com/\xcc",
                       {{":status", "200"}, {"content-type", "text/plain"}},
                       "payload");
@@ -357,7 +354,7 @@
 }
 
 TEST_F(BundledExchangeParserTest, RequestURLHasCredentials) {
-  BundleBuilder builder(kFallbackUrl);
+  BundleBuilder builder(kFallbackUrl, kManifestUrl);
   builder.AddExchange("https://user:passwd@test.example.com/",
                       {{":status", "200"}, {"content-type", "text/plain"}},
                       "payload");
@@ -367,7 +364,7 @@
 }
 
 TEST_F(BundledExchangeParserTest, RequestURLHasFragment) {
-  BundleBuilder builder(kFallbackUrl);
+  BundleBuilder builder(kFallbackUrl, kManifestUrl);
   builder.AddExchange("https://test.example.com/#fragment",
                       {{":status", "200"}, {"content-type", "text/plain"}},
                       "payload");
@@ -377,7 +374,7 @@
 }
 
 TEST_F(BundledExchangeParserTest, NoStatusInResponseHeaders) {
-  BundleBuilder builder(kFallbackUrl);
+  BundleBuilder builder(kFallbackUrl, kManifestUrl);
   builder.AddExchange("https://test.example.com/",
                       {{"content-type", "text/plain"}},
                       "payload");  // ":status" is missing.
@@ -391,7 +388,7 @@
 }
 
 TEST_F(BundledExchangeParserTest, InvalidResponseStatus) {
-  BundleBuilder builder(kFallbackUrl);
+  BundleBuilder builder(kFallbackUrl, kManifestUrl);
   builder.AddExchange("https://test.example.com/",
                       {{":status", "0200"}, {"content-type", "text/plain"}},
                       "payload");
@@ -405,7 +402,7 @@
 }
 
 TEST_F(BundledExchangeParserTest, ExtraPseudoInResponseHeaders) {
-  BundleBuilder builder(kFallbackUrl);
+  BundleBuilder builder(kFallbackUrl, kManifestUrl);
   builder.AddExchange(
       "https://test.example.com/",
       {{":status", "200"}, {":foo", ""}, {"content-type", "text/plain"}},
@@ -420,7 +417,7 @@
 }
 
 TEST_F(BundledExchangeParserTest, UpperCaseCharacterInHeaderName) {
-  BundleBuilder builder(kFallbackUrl);
+  BundleBuilder builder(kFallbackUrl, kManifestUrl);
   builder.AddExchange("https://test.example.com/",
                       {{":status", "200"}, {"Content-Type", "text/plain"}},
                       "payload");
@@ -434,7 +431,7 @@
 }
 
 TEST_F(BundledExchangeParserTest, InvalidHeaderValue) {
-  BundleBuilder builder(kFallbackUrl);
+  BundleBuilder builder(kFallbackUrl, kManifestUrl);
   builder.AddExchange("https://test.example.com/",
                       {{":status", "200"}, {"content-type", "\n"}}, "payload");
   TestDataSource data_source(builder.CreateBundle());
@@ -447,7 +444,7 @@
 }
 
 TEST_F(BundledExchangeParserTest, Variants) {
-  BundleBuilder builder(kFallbackUrl);
+  BundleBuilder builder(kFallbackUrl, kManifestUrl);
   auto location1 = builder.AddResponse(
       {{":status", "200"}, {"content-type", "text/html"}}, "payload1");
   auto location2 = builder.AddResponse(
@@ -476,7 +473,7 @@
 }
 
 TEST_F(BundledExchangeParserTest, EmptyIndexEntry) {
-  BundleBuilder builder(kFallbackUrl);
+  BundleBuilder builder(kFallbackUrl, kManifestUrl);
   builder.AddIndexEntry("https://test.example.com/", "", {});
   TestDataSource data_source(builder.CreateBundle());
 
@@ -484,7 +481,7 @@
 }
 
 TEST_F(BundledExchangeParserTest, EmptyIndexEntryWithVariants) {
-  BundleBuilder builder(kFallbackUrl);
+  BundleBuilder builder(kFallbackUrl, kManifestUrl);
   builder.AddIndexEntry("https://test.example.com/",
                         "Accept;text/html;text/plain", {});
   TestDataSource data_source(builder.CreateBundle());
@@ -493,7 +490,7 @@
 }
 
 TEST_F(BundledExchangeParserTest, MultipleResponsesWithoutVariantsValue) {
-  BundleBuilder builder(kFallbackUrl);
+  BundleBuilder builder(kFallbackUrl, kManifestUrl);
   auto location1 = builder.AddResponse(
       {{":status", "200"}, {"content-type", "text/html"}}, "payload1");
   auto location2 = builder.AddResponse(
@@ -505,12 +502,64 @@
   ExpectFormatErrorWithFallbackURL(ParseBundle(&data_source));
 }
 
+TEST_F(BundledExchangeParserTest, AllKnownSectionInCritical) {
+  BundleBuilder builder(kFallbackUrl, kManifestUrl);
+  builder.AddExchange("https://test.example.com/",
+                      {{":status", "200"}, {"content-type", "text/plain"}},
+                      "payload");
+  cbor::Value::ArrayValue critical_section;
+  critical_section.emplace_back("manifest");
+  critical_section.emplace_back("index");
+  critical_section.emplace_back("critical");
+  critical_section.emplace_back("responses");
+  builder.AddSection("critical", cbor::Value(critical_section));
+  TestDataSource data_source(builder.CreateBundle());
+
+  mojom::BundleMetadataPtr metadata = ParseBundle(&data_source).first;
+  ASSERT_TRUE(metadata);
+}
+
+TEST_F(BundledExchangeParserTest, UnknownSectionInCritical) {
+  BundleBuilder builder(kFallbackUrl, kManifestUrl);
+  builder.AddExchange("https://test.example.com/",
+                      {{":status", "200"}, {"content-type", "text/plain"}},
+                      "payload");
+  cbor::Value::ArrayValue critical_section;
+  critical_section.emplace_back("unknown_section_name");
+  builder.AddSection("critical", cbor::Value(critical_section));
+  TestDataSource data_source(builder.CreateBundle());
+
+  ExpectFormatErrorWithFallbackURL(ParseBundle(&data_source));
+}
+
+TEST_F(BundledExchangeParserTest, NoManifest) {
+  BundleBuilder builder(kFallbackUrl, std::string());
+  builder.AddExchange("https://test.example.com/",
+                      {{":status", "200"}, {"content-type", "text/plain"}},
+                      "payload");
+  TestDataSource data_source(builder.CreateBundle());
+
+  ExpectFormatErrorWithFallbackURL(ParseBundle(&data_source));
+}
+
+TEST_F(BundledExchangeParserTest, InvalidManifestURL) {
+  BundleBuilder builder(kFallbackUrl, "not-an-absolute-url");
+  builder.AddExchange("https://test.example.com/",
+                      {{":status", "200"}, {"content-type", "text/plain"}},
+                      "payload");
+  TestDataSource data_source(builder.CreateBundle());
+
+  ExpectFormatErrorWithFallbackURL(ParseBundle(&data_source));
+}
+
 TEST_F(BundledExchangeParserTest, ParseGoldenFile) {
   TestDataSource data_source(base::FilePath(FILE_PATH_LITERAL("hello.wbn")));
 
   mojom::BundleMetadataPtr metadata = ParseBundle(&data_source).first;
   ASSERT_TRUE(metadata);
   ASSERT_EQ(metadata->requests.size(), 4u);
+  EXPECT_EQ(metadata->manifest_url,
+            "https://test.example.org/manifest.webmanifest");
 
   std::map<std::string, mojom::BundleResponsePtr> responses;
   for (const auto& item : metadata->requests) {
diff --git a/storage/browser/BUILD.gn b/storage/browser/BUILD.gn
index d9d4253..718481c 100644
--- a/storage/browser/BUILD.gn
+++ b/storage/browser/BUILD.gn
@@ -37,10 +37,6 @@
     "blob/blob_url_loader.h",
     "blob/blob_url_loader_factory.cc",
     "blob/blob_url_loader_factory.h",
-    "blob/blob_url_request_job.cc",
-    "blob/blob_url_request_job.h",
-    "blob/blob_url_request_job_factory.cc",
-    "blob/blob_url_request_job_factory.h",
     "blob/blob_url_store_impl.cc",
     "blob/blob_url_store_impl.h",
     "blob/blob_url_utils.cc",
@@ -338,8 +334,8 @@
     "test/fileapi_test_file_set.h",
     "test/mock_blob_registry_delegate.cc",
     "test/mock_blob_registry_delegate.h",
-    "test/mock_blob_url_request_context.cc",
-    "test/mock_blob_url_request_context.h",
+    "test/mock_blob_util.cc",
+    "test/mock_blob_util.h",
     "test/mock_bytes_provider.cc",
     "test/mock_bytes_provider.h",
     "test/mock_file_change_observer.cc",
diff --git a/storage/browser/blob/blob_url_loader.cc b/storage/browser/blob/blob_url_loader.cc
index c21b9475e..4846edf 100644
--- a/storage/browser/blob/blob_url_loader.cc
+++ b/storage/browser/blob/blob_url_loader.cc
@@ -7,23 +7,78 @@
 #include <stddef.h>
 #include <utility>
 #include "base/bind.h"
+#include "base/format_macros.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/weak_ptr.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/stringprintf.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "net/base/io_buffer.h"
 #include "net/http/http_byte_range.h"
 #include "net/http/http_request_headers.h"
 #include "net/http/http_response_headers.h"
+#include "net/http/http_response_info.h"
 #include "net/http/http_status_code.h"
 #include "net/http/http_util.h"
+#include "net/url_request/url_request.h"
+#include "net/url_request/url_request_status.h"
 #include "services/network/public/cpp/constants.h"
 #include "storage/browser/blob/blob_data_handle.h"
-#include "storage/browser/blob/blob_url_request_job.h"
 #include "storage/browser/blob/mojo_blob_reader.h"
 
 namespace storage {
 
+namespace {
+
+scoped_refptr<net::HttpResponseHeaders> GenerateHeaders(
+    net::HttpStatusCode status_code,
+    BlobDataHandle* blob_handle,
+    net::HttpByteRange* byte_range,
+    uint64_t total_size,
+    uint64_t content_size) {
+  std::string status("HTTP/1.1 ");
+  status.append(base::NumberToString(status_code));
+  status.append(" ");
+  status.append(net::GetHttpReasonPhrase(status_code));
+  status.append("\0\0", 2);
+  scoped_refptr<net::HttpResponseHeaders> headers =
+      new net::HttpResponseHeaders(status);
+
+  if (status_code == net::HTTP_OK || status_code == net::HTTP_PARTIAL_CONTENT) {
+    std::string content_length_header(net::HttpRequestHeaders::kContentLength);
+    content_length_header.append(": ");
+    content_length_header.append(base::NumberToString(content_size));
+    headers->AddHeader(content_length_header);
+    if (status_code == net::HTTP_PARTIAL_CONTENT) {
+      DCHECK(byte_range->IsValid());
+      std::string content_range_header(net::HttpResponseHeaders::kContentRange);
+      content_range_header.append(": bytes ");
+      content_range_header.append(base::StringPrintf(
+          "%" PRId64 "-%" PRId64, byte_range->first_byte_position(),
+          byte_range->last_byte_position()));
+      content_range_header.append("/");
+      content_range_header.append(base::StringPrintf("%" PRId64, total_size));
+      headers->AddHeader(content_range_header);
+    }
+    if (!blob_handle->content_type().empty()) {
+      std::string content_type_header(net::HttpRequestHeaders::kContentType);
+      content_type_header.append(": ");
+      content_type_header.append(blob_handle->content_type());
+      headers->AddHeader(content_type_header);
+    }
+    if (!blob_handle->content_disposition().empty()) {
+      std::string content_disposition_header("Content-Disposition: ");
+      content_disposition_header.append(blob_handle->content_disposition());
+      headers->AddHeader(content_disposition_header);
+    }
+  }
+
+  return headers;
+}
+
+}  // namespace
+
 // static
 void BlobURLLoader::CreateAndStart(
     network::mojom::URLLoaderRequest url_loader_request,
@@ -149,8 +204,8 @@
   response.content_length = 0;
   if (status_code == net::HTTP_OK || status_code == net::HTTP_PARTIAL_CONTENT)
     response.content_length = content_size;
-  response.headers = storage::BlobURLRequestJob::GenerateHeaders(
-      status_code, blob_handle_.get(), &byte_range_, total_size_, content_size);
+  response.headers = GenerateHeaders(status_code, blob_handle_.get(),
+                                     &byte_range_, total_size_, content_size);
 
   std::string mime_type;
   response.headers->GetMimeType(&mime_type);
diff --git a/storage/browser/blob/blob_url_request_job.cc b/storage/browser/blob/blob_url_request_job.cc
deleted file mode 100644
index c4390c0c..0000000
--- a/storage/browser/blob/blob_url_request_job.cc
+++ /dev/null
@@ -1,304 +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 "storage/browser/blob/blob_url_request_job.h"
-
-#include <stdint.h>
-
-#include <algorithm>
-#include <limits>
-#include <string>
-#include <vector>
-
-#include "base/bind.h"
-#include "base/compiler_specific.h"
-#include "base/format_macros.h"
-#include "base/location.h"
-#include "base/numerics/safe_conversions.h"
-#include "base/single_thread_task_runner.h"
-#include "base/stl_util.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/stringprintf.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "base/trace_event/trace_event.h"
-#include "net/base/io_buffer.h"
-#include "net/base/net_errors.h"
-#include "net/http/http_request_headers.h"
-#include "net/http/http_response_headers.h"
-#include "net/http/http_response_info.h"
-#include "net/http/http_util.h"
-#include "net/url_request/url_request.h"
-#include "net/url_request/url_request_context.h"
-#include "net/url_request/url_request_error_job.h"
-#include "net/url_request/url_request_status.h"
-#include "storage/browser/blob/blob_data_handle.h"
-#include "storage/browser/blob/blob_reader.h"
-#include "storage/browser/fileapi/file_stream_reader.h"
-#include "storage/browser/fileapi/file_system_url.h"
-
-namespace storage {
-
-BlobURLRequestJob::BlobURLRequestJob(net::URLRequest* request,
-                                     net::NetworkDelegate* network_delegate,
-                                     BlobDataHandle* blob_handle)
-    : net::URLRequestJob(request, network_delegate),
-      error_(false),
-      byte_range_set_(false),
-      weak_factory_(this) {
-  TRACE_EVENT_ASYNC_BEGIN1("Blob", "BlobRequest", this, "uuid",
-                           blob_handle ? blob_handle->uuid() : "NotFound");
-  if (blob_handle) {
-    blob_handle_.reset(new BlobDataHandle(*blob_handle));
-    blob_reader_ = blob_handle_->CreateReader();
-  }
-}
-
-void BlobURLRequestJob::Start() {
-  // Continue asynchronously.
-  base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE,
-      base::BindOnce(&BlobURLRequestJob::DidStart, weak_factory_.GetWeakPtr()));
-}
-
-void BlobURLRequestJob::Kill() {
-  if (blob_reader_) {
-    blob_reader_->Kill();
-  }
-  net::URLRequestJob::Kill();
-  weak_factory_.InvalidateWeakPtrs();
-}
-
-int BlobURLRequestJob::ReadRawData(net::IOBuffer* dest, int dest_size) {
-  TRACE_EVENT_ASYNC_BEGIN1("Blob", "BlobRequest::ReadRawData", this, "uuid",
-                           blob_handle_ ? blob_handle_->uuid() : "NotFound");
-  DCHECK_NE(dest_size, 0);
-
-  // Bail out immediately if we encounter an error. This happens if a previous
-  // ReadRawData signalled an error to its caller but the caller called
-  // ReadRawData again anyway.
-  if (error_)
-    return 0;
-
-  int bytes_read = 0;
-  BlobReader::Status read_status =
-      blob_reader_->Read(dest, dest_size, &bytes_read,
-                         base::BindOnce(&BlobURLRequestJob::DidReadRawData,
-                                        weak_factory_.GetWeakPtr()));
-
-  switch (read_status) {
-    case BlobReader::Status::NET_ERROR:
-      TRACE_EVENT_ASYNC_END1("Blob", "BlobRequest::ReadRawData", this, "uuid",
-                             blob_handle_ ? blob_handle_->uuid() : "NotFound");
-      return blob_reader_->net_error();
-    case BlobReader::Status::IO_PENDING:
-      return net::ERR_IO_PENDING;
-    case BlobReader::Status::DONE:
-      TRACE_EVENT_ASYNC_END1("Blob", "BlobRequest::ReadRawData", this, "uuid",
-                             blob_handle_ ? blob_handle_->uuid() : "NotFound");
-      return bytes_read;
-  }
-  NOTREACHED();
-  return 0;
-}
-
-bool BlobURLRequestJob::GetMimeType(std::string* mime_type) const {
-  if (!response_info_)
-    return false;
-
-  return response_info_->headers->GetMimeType(mime_type);
-}
-
-void BlobURLRequestJob::GetResponseInfo(net::HttpResponseInfo* info) {
-  if (response_info_)
-    *info = *response_info_;
-}
-
-void BlobURLRequestJob::SetExtraRequestHeaders(
-    const net::HttpRequestHeaders& headers) {
-  std::string range_header;
-  if (headers.GetHeader(net::HttpRequestHeaders::kRange, &range_header)) {
-    // We only care about "Range" header here.
-    std::vector<net::HttpByteRange> ranges;
-    if (net::HttpUtil::ParseRangeHeader(range_header, &ranges)) {
-      if (ranges.size() == 1) {
-        byte_range_set_ = true;
-        byte_range_ = ranges[0];
-      } else {
-        // We don't support multiple range requests in one single URL request,
-        // because we need to do multipart encoding here.
-        // TODO(jianli): Support multipart byte range requests.
-        NotifyFailure(net::ERR_REQUEST_RANGE_NOT_SATISFIABLE);
-      }
-    }
-  }
-}
-
-scoped_refptr<net::HttpResponseHeaders> BlobURLRequestJob::GenerateHeaders(
-    net::HttpStatusCode status_code,
-    BlobDataHandle* blob_handle,
-    net::HttpByteRange* byte_range,
-    uint64_t total_size,
-    uint64_t content_size) {
-  std::string status("HTTP/1.1 ");
-  status.append(base::NumberToString(status_code));
-  status.append(" ");
-  status.append(net::GetHttpReasonPhrase(status_code));
-  status.append("\0\0", 2);
-  scoped_refptr<net::HttpResponseHeaders> headers =
-      new net::HttpResponseHeaders(status);
-
-  if (status_code == net::HTTP_OK || status_code == net::HTTP_PARTIAL_CONTENT) {
-    std::string content_length_header(net::HttpRequestHeaders::kContentLength);
-    content_length_header.append(": ");
-    content_length_header.append(base::NumberToString(content_size));
-    headers->AddHeader(content_length_header);
-    if (status_code == net::HTTP_PARTIAL_CONTENT) {
-      DCHECK(byte_range->IsValid());
-      std::string content_range_header(net::HttpResponseHeaders::kContentRange);
-      content_range_header.append(": bytes ");
-      content_range_header.append(base::StringPrintf(
-          "%" PRId64 "-%" PRId64, byte_range->first_byte_position(),
-          byte_range->last_byte_position()));
-      content_range_header.append("/");
-      content_range_header.append(base::StringPrintf("%" PRId64, total_size));
-      headers->AddHeader(content_range_header);
-    }
-    if (!blob_handle->content_type().empty()) {
-      std::string content_type_header(net::HttpRequestHeaders::kContentType);
-      content_type_header.append(": ");
-      content_type_header.append(blob_handle->content_type());
-      headers->AddHeader(content_type_header);
-    }
-    if (!blob_handle->content_disposition().empty()) {
-      std::string content_disposition_header("Content-Disposition: ");
-      content_disposition_header.append(blob_handle->content_disposition());
-      headers->AddHeader(content_disposition_header);
-    }
-  }
-
-  return headers;
-}
-
-BlobURLRequestJob::~BlobURLRequestJob() {
-  TRACE_EVENT_ASYNC_END1("Blob", "BlobRequest", this, "uuid",
-                         blob_handle_ ? blob_handle_->uuid() : "NotFound");
-}
-
-void BlobURLRequestJob::DidStart() {
-  error_ = false;
-
-  // We only support GET request per the spec.
-  if (request()->method() != "GET") {
-    NotifyFailure(net::ERR_METHOD_NOT_SUPPORTED);
-    return;
-  }
-
-  // If the blob data is not present, bail out.
-  if (!blob_handle_) {
-    NotifyFailure(net::ERR_FILE_NOT_FOUND);
-    return;
-  }
-  if (blob_reader_->net_error()) {
-    NotifyFailure(blob_reader_->net_error());
-    return;
-  }
-
-  TRACE_EVENT_ASYNC_BEGIN1("Blob", "BlobRequest::CountSize", this, "uuid",
-                           blob_handle_->uuid());
-  BlobReader::Status size_status = blob_reader_->CalculateSize(base::BindOnce(
-      &BlobURLRequestJob::DidCalculateSize, weak_factory_.GetWeakPtr()));
-  switch (size_status) {
-    case BlobReader::Status::NET_ERROR:
-      NotifyFailure(blob_reader_->net_error());
-      return;
-    case BlobReader::Status::IO_PENDING:
-      return;
-    case BlobReader::Status::DONE:
-      DidCalculateSize(net::OK);
-      return;
-  }
-}
-
-void BlobURLRequestJob::DidCalculateSize(int result) {
-  TRACE_EVENT_ASYNC_END1("Blob", "BlobRequest::CountSize", this, "uuid",
-                         blob_handle_->uuid());
-
-  if (result != net::OK) {
-    NotifyFailure(result);
-    return;
-  }
-
-  // Apply the range requirement.
-  if (!byte_range_.ComputeBounds(blob_reader_->total_size())) {
-    NotifyFailure(net::ERR_REQUEST_RANGE_NOT_SATISFIABLE);
-    return;
-  }
-
-  DCHECK_LE(byte_range_.first_byte_position(),
-            byte_range_.last_byte_position() + 1);
-  uint64_t length = base::checked_cast<uint64_t>(
-      byte_range_.last_byte_position() - byte_range_.first_byte_position() + 1);
-
-  if (byte_range_set_)
-    blob_reader_->SetReadRange(byte_range_.first_byte_position(), length);
-
-  net::HttpStatusCode status_code = net::HTTP_OK;
-  if (byte_range_set_ && byte_range_.IsValid()) {
-    status_code = net::HTTP_PARTIAL_CONTENT;
-  } else {
-    // TODO(horo): When the requester doesn't need the side data (ex:FileReader)
-    // we should skip reading the side data.
-    if (blob_reader_->has_side_data() &&
-        blob_reader_->ReadSideData(base::BindOnce(
-            &BlobURLRequestJob::DidReadMetadata, weak_factory_.GetWeakPtr())) ==
-            BlobReader::Status::IO_PENDING) {
-      return;
-    }
-  }
-
-  HeadersCompleted(status_code);
-}
-
-void BlobURLRequestJob::DidReadMetadata(BlobReader::Status result) {
-  if (result != BlobReader::Status::DONE) {
-    NotifyFailure(blob_reader_->net_error());
-    return;
-  }
-  HeadersCompleted(net::HTTP_OK);
-}
-
-void BlobURLRequestJob::DidReadRawData(int result) {
-  TRACE_EVENT_ASYNC_END1("Blob", "BlobRequest::ReadRawData", this, "uuid",
-                         blob_handle_ ? blob_handle_->uuid() : "NotFound");
-  ReadRawDataComplete(result);
-}
-
-void BlobURLRequestJob::NotifyFailure(int error_code) {
-  error_ = true;
-
-  // If we already return the headers on success, we can't change the headers
-  // now. Instead, we just error out.
-  DCHECK(!response_info_) << "Cannot NotifyFailure after headers.";
-
-  NotifyStartError(net::URLRequestStatus::FromError(error_code));
-}
-
-void BlobURLRequestJob::HeadersCompleted(net::HttpStatusCode status_code) {
-  uint64_t content_size = 0;
-  uint64_t total_size = 0;
-  if (status_code == net::HTTP_OK || status_code == net::HTTP_PARTIAL_CONTENT) {
-    content_size = blob_reader_->remaining_bytes();
-    set_expected_content_size(content_size);
-    total_size = blob_reader_->total_size();
-  }
-  response_info_.reset(new net::HttpResponseInfo());
-  response_info_->headers = GenerateHeaders(
-      status_code, blob_handle_.get(), &byte_range_, total_size, content_size);
-  if (blob_reader_)
-    response_info_->metadata = blob_reader_->side_data();
-
-  NotifyHeadersComplete();
-}
-
-}  // namespace storage
diff --git a/storage/browser/blob/blob_url_request_job.h b/storage/browser/blob/blob_url_request_job.h
deleted file mode 100644
index 5305044..0000000
--- a/storage/browser/blob/blob_url_request_job.h
+++ /dev/null
@@ -1,87 +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 STORAGE_BROWSER_BLOB_BLOB_URL_REQUEST_JOB_H_
-#define STORAGE_BROWSER_BLOB_BLOB_URL_REQUEST_JOB_H_
-
-#include <stddef.h>
-
-#include <map>
-#include <memory>
-#include <vector>
-
-#include "base/component_export.h"
-#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-#include "net/http/http_byte_range.h"
-#include "net/http/http_status_code.h"
-#include "net/url_request/url_request_job.h"
-#include "storage/browser/blob/blob_reader.h"
-
-namespace net {
-class HttpResponseHeaders;
-class IOBuffer;
-}
-
-namespace storage {
-
-class BlobDataHandle;
-
-// A request job that handles reading blob URLs.
-class COMPONENT_EXPORT(STORAGE_BROWSER) BlobURLRequestJob
-    : public net::URLRequestJob {
- public:
-  BlobURLRequestJob(net::URLRequest* request,
-                    net::NetworkDelegate* network_delegate,
-                    BlobDataHandle* blob_handle);
-
-  // net::URLRequestJob methods.
-  void Start() override;
-  void Kill() override;
-  int ReadRawData(net::IOBuffer* buf, int buf_size) override;
-  bool GetMimeType(std::string* mime_type) const override;
-  void GetResponseInfo(net::HttpResponseInfo* info) override;
-  void SetExtraRequestHeaders(const net::HttpRequestHeaders& headers) override;
-
-  // Helper method to create the HTTP headers for the response.
-  // |blob_handles|, |total_size|, |byte_range| and |content_size| are only
-  // used if status_code isn't an error.
-  static scoped_refptr<net::HttpResponseHeaders> GenerateHeaders(
-      net::HttpStatusCode status_code,
-      BlobDataHandle* blob_handle,
-      net::HttpByteRange* byte_range,
-      uint64_t total_size,
-      uint64_t content_size);
-
- protected:
-  ~BlobURLRequestJob() override;
-
- private:
-  // For preparing for read: get the size, apply the range and perform seek.
-  void DidStart();
-  void DidCalculateSize(int result);
-  void DidReadMetadata(BlobReader::Status result);
-  void DidReadRawData(int result);
-
-  void NotifyFailure(int);
-  void HeadersCompleted(net::HttpStatusCode status_code);
-
-  // Is set when NotifyFailure() is called and reset when DidStart is called.
-  bool error_;
-
-  bool byte_range_set_;
-  net::HttpByteRange byte_range_;
-
-  std::unique_ptr<BlobDataHandle> blob_handle_;
-  std::unique_ptr<BlobReader> blob_reader_;
-  std::unique_ptr<net::HttpResponseInfo> response_info_;
-
-  base::WeakPtrFactory<BlobURLRequestJob> weak_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(BlobURLRequestJob);
-};
-
-}  // namespace storage
-
-#endif  // STORAGE_BROWSER_BLOB_BLOB_URL_REQUEST_JOB_H_
diff --git a/storage/browser/blob/blob_url_request_job_factory.cc b/storage/browser/blob/blob_url_request_job_factory.cc
deleted file mode 100644
index bef6ce2..0000000
--- a/storage/browser/blob/blob_url_request_job_factory.cc
+++ /dev/null
@@ -1,120 +0,0 @@
-// Copyright (c) 2011 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 "storage/browser/blob/blob_url_request_job_factory.h"
-
-#include <memory>
-#include <utility>
-
-#include "base/strings/string_util.h"
-#include "net/base/load_flags.h"
-#include "net/base/request_priority.h"
-#include "net/traffic_annotation/network_traffic_annotation.h"
-#include "net/url_request/url_request_context.h"
-#include "storage/browser/blob/blob_data_handle.h"
-#include "storage/browser/blob/blob_storage_context.h"
-#include "storage/browser/blob/blob_url_request_job.h"
-
-namespace storage {
-
-namespace {
-
-int kUserDataKey;  // The value is not important, the addr is a key.
-
-}  // namespace
-
-// static
-std::unique_ptr<net::URLRequest> BlobProtocolHandler::CreateBlobRequest(
-    std::unique_ptr<BlobDataHandle> blob_data_handle,
-    const net::URLRequestContext* request_context,
-    net::URLRequest::Delegate* request_delegate) {
-  const GURL kBlobUrl("blob://see_user_data/");
-  net::NetworkTrafficAnnotationTag traffic_annotation =
-      net::DefineNetworkTrafficAnnotation("blob_read", R"(
-        semantics {
-          sender: "BlobProtocolHandler"
-          description:
-            "Blobs are used for a variety of use cases, and are basically "
-            "immutable blocks of data. See https://chromium.googlesource.com/"
-            "chromium/src/+/master/storage/browser/blob/README.md for an "
-            "explanation of blobs and their implementation in Chrome. These "
-            "can be created by scripts in a website, web platform features, or "
-            "internally in the browser."
-          trigger:
-            "Request for reading the contents of a blob."
-          data:
-            "A reference to a Blob, File, or CacheStorage entry created from "
-            "script, a web platform feature, or browser internals."
-          destination: LOCAL
-        }
-        policy {
-          cookies_allowed: NO
-          setting: "This feature cannot be disabled by settings."
-          policy_exception_justification:
-            "Not implemented. This is a local data fetch request and has no "
-            "network activity."
-        })");
-  std::unique_ptr<net::URLRequest> request = request_context->CreateRequest(
-      kBlobUrl, net::DEFAULT_PRIORITY, request_delegate, traffic_annotation);
-  request->SetLoadFlags(net::LOAD_DO_NOT_SAVE_COOKIES |
-                        net::LOAD_DO_NOT_SEND_COOKIES);
-  SetRequestedBlobDataHandle(request.get(), std::move(blob_data_handle));
-  return request;
-}
-
-// static
-void BlobProtocolHandler::SetRequestedBlobDataHandle(
-    net::URLRequest* request,
-    std::unique_ptr<BlobDataHandle> blob_data_handle) {
-  request->SetUserData(&kUserDataKey, std::move(blob_data_handle));
-}
-
-// static
-BlobDataHandle* BlobProtocolHandler::GetRequestBlobDataHandle(
-    net::URLRequest* request) {
-  return static_cast<BlobDataHandle*>(request->GetUserData(&kUserDataKey));
-}
-
-BlobProtocolHandler::BlobProtocolHandler(BlobStorageContext* context) {
-  if (context)
-    context_ = context->AsWeakPtr();
-}
-
-BlobProtocolHandler::~BlobProtocolHandler() = default;
-
-net::URLRequestJob* BlobProtocolHandler::MaybeCreateJob(
-    net::URLRequest* request, net::NetworkDelegate* network_delegate) const {
-  return new storage::BlobURLRequestJob(request, network_delegate,
-                                        LookupBlobHandle(request));
-}
-
-bool BlobProtocolHandler::IsSafeRedirectTarget(const GURL& location) const {
-  return false;
-}
-
-BlobDataHandle* BlobProtocolHandler::LookupBlobHandle(
-    net::URLRequest* request) const {
-  BlobDataHandle* blob_data_handle = GetRequestBlobDataHandle(request);
-  if (blob_data_handle)
-    return blob_data_handle;
-  if (!context_.get())
-    return nullptr;
-
-  // Support looking up based on uuid, the FeedbackExtensionAPI relies on this.
-  // TODO(michaeln): Replace this use case and others like it with a BlobReader
-  // impl that does not depend on urlfetching to perform this function.
-  const std::string kPrefix("blob:uuid/");
-  if (!base::StartsWith(request->url().spec(), kPrefix,
-                        base::CompareCase::SENSITIVE))
-    return nullptr;
-  std::string uuid = request->url().spec().substr(kPrefix.length());
-  std::unique_ptr<BlobDataHandle> handle = context_->GetBlobDataFromUUID(uuid);
-  BlobDataHandle* handle_ptr = handle.get();
-  if (handle) {
-    SetRequestedBlobDataHandle(request, std::move(handle));
-  }
-  return handle_ptr;
-}
-
-}  // namespace storage
diff --git a/storage/browser/blob/blob_url_request_job_factory.h b/storage/browser/blob/blob_url_request_job_factory.h
deleted file mode 100644
index 38c8cc5..0000000
--- a/storage/browser/blob/blob_url_request_job_factory.h
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright (c) 2011 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 STORAGE_BROWSER_BLOB_BLOB_URL_REQUEST_JOB_FACTORY_H_
-#define STORAGE_BROWSER_BLOB_BLOB_URL_REQUEST_JOB_FACTORY_H_
-
-#include <memory>
-
-#include "base/compiler_specific.h"
-#include "base/component_export.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "net/url_request/url_request.h"
-#include "net/url_request/url_request_job_factory.h"
-
-namespace net {
-class URLRequestContext;
-}  // namespace net
-
-namespace storage {
-
-class BlobDataHandle;
-class BlobStorageContext;
-
-class COMPONENT_EXPORT(STORAGE_BROWSER) BlobProtocolHandler
-    : public net::URLRequestJobFactory::ProtocolHandler {
- public:
-  // A helper to manufacture an URLRequest to retrieve the given blob.
-  static std::unique_ptr<net::URLRequest> CreateBlobRequest(
-      std::unique_ptr<BlobDataHandle> blob_data_handle,
-      const net::URLRequestContext* request_context,
-      net::URLRequest::Delegate* request_delegate);
-
-  // This class ignores the request's URL and uses the value given
-  // to SetRequestedBlobDataHandle instead.
-  static void SetRequestedBlobDataHandle(
-      net::URLRequest* request,
-      std::unique_ptr<BlobDataHandle> blob_data_handle);
-
-  // This gets the handle on the request if it exists.
-  static BlobDataHandle* GetRequestBlobDataHandle(net::URLRequest* request);
-
-  explicit BlobProtocolHandler(BlobStorageContext* context);
-  ~BlobProtocolHandler() override;
-
-  // net::URLRequestJobFactory::ProtocolHandler implementation:
-  net::URLRequestJob* MaybeCreateJob(
-      net::URLRequest* request,
-      net::NetworkDelegate* network_delegate) const override;
-  bool IsSafeRedirectTarget(const GURL& location) const override;
-
- private:
-  BlobDataHandle* LookupBlobHandle(net::URLRequest* request) const;
-
-  base::WeakPtr<BlobStorageContext> context_;
-
-  DISALLOW_COPY_AND_ASSIGN(BlobProtocolHandler);
-};
-
-}  // namespace storage
-
-#endif  // STORAGE_BROWSER_BLOB_BLOB_URL_REQUEST_JOB_FACTORY_H_
diff --git a/storage/browser/fileapi/file_system_operation_impl_write_unittest.cc b/storage/browser/fileapi/file_system_operation_impl_write_unittest.cc
index b3f9c68..a705c39 100644
--- a/storage/browser/fileapi/file_system_operation_impl_write_unittest.cc
+++ b/storage/browser/fileapi/file_system_operation_impl_write_unittest.cc
@@ -16,17 +16,13 @@
 #include "base/test/scoped_task_environment.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "net/url_request/url_request.h"
-#include "net/url_request/url_request_context.h"
-#include "net/url_request/url_request_job.h"
-#include "net/url_request/url_request_job_factory_impl.h"
 #include "storage/browser/blob/blob_storage_context.h"
-#include "storage/browser/blob/blob_url_request_job.h"
 #include "storage/browser/fileapi/file_system_context.h"
 #include "storage/browser/fileapi/file_system_file_util.h"
 #include "storage/browser/fileapi/file_system_operation_context.h"
 #include "storage/browser/fileapi/file_system_operation_runner.h"
 #include "storage/browser/fileapi/local_file_util.h"
-#include "storage/browser/test/mock_blob_url_request_context.h"
+#include "storage/browser/test/mock_blob_util.h"
 #include "storage/browser/test/mock_file_change_observer.h"
 #include "storage/browser/test/mock_quota_manager.h"
 #include "storage/browser/test/test_file_system_backend.h"
@@ -38,8 +34,7 @@
 using storage::FileSystemOperation;
 using storage::FileSystemOperationRunner;
 using storage::FileSystemURL;
-using content::MockBlobURLRequestContext;
-using content::ScopedTextBlob;
+using storage::ScopedTextBlob;
 
 namespace content {
 
@@ -79,7 +74,7 @@
 
     file_system_context_ = CreateFileSystemContextForTesting(
         quota_manager_->proxy(), dir_.GetPath());
-    url_request_context_.reset(new MockBlobURLRequestContext());
+    blob_storage_context_.reset(new storage::BlobStorageContext);
 
     file_system_context_->operation_runner()->CreateFile(
         URLForPath(virtual_path_), true /* exclusive */,
@@ -148,8 +143,8 @@
 
   void DidCancel(base::File::Error status) { cancel_status_ = status; }
 
-  const MockBlobURLRequestContext& url_request_context() const {
-    return *url_request_context_;
+  storage::BlobStorageContext* blob_storage_context() const {
+    return blob_storage_context_.get();
   }
 
   base::test::ScopedTaskEnvironment scoped_task_environment_;
@@ -166,7 +161,7 @@
   int64_t bytes_written_;
   bool complete_;
 
-  std::unique_ptr<MockBlobURLRequestContext> url_request_context_;
+  std::unique_ptr<storage::BlobStorageContext> blob_storage_context_;
 
   storage::MockFileChangeObserver change_observer_;
   storage::ChangeObserverList change_observers_;
@@ -177,7 +172,7 @@
 };
 
 TEST_F(FileSystemOperationImplWriteTest, TestWriteSuccess) {
-  ScopedTextBlob blob(url_request_context(), "blob-id:success",
+  ScopedTextBlob blob(blob_storage_context(), "blob-id:success",
                       "Hello, world!\n");
   file_system_context_->operation_runner()->Write(URLForPath(virtual_path_),
                                                   blob.GetBlobDataHandle(), 0,
@@ -192,7 +187,7 @@
 }
 
 TEST_F(FileSystemOperationImplWriteTest, TestWriteZero) {
-  ScopedTextBlob blob(url_request_context(), "blob_id:zero", "");
+  ScopedTextBlob blob(blob_storage_context(), "blob_id:zero", "");
   file_system_context_->operation_runner()->Write(URLForPath(virtual_path_),
                                                   blob.GetBlobDataHandle(), 0,
                                                   RecordWriteCallback());
@@ -220,7 +215,7 @@
 }
 
 TEST_F(FileSystemOperationImplWriteTest, TestWriteInvalidFile) {
-  ScopedTextBlob blob(url_request_context(), "blob_id:writeinvalidfile",
+  ScopedTextBlob blob(blob_storage_context(), "blob_id:writeinvalidfile",
                       "It\'ll not be written.");
   file_system_context_->operation_runner()->Write(
       URLForPath(base::FilePath(FILE_PATH_LITERAL("nonexist"))),
@@ -240,7 +235,7 @@
       URLForPath(virtual_dir_path), true /* exclusive */, false /* recursive */,
       base::BindOnce(&AssertStatusEq, base::File::FILE_OK));
 
-  ScopedTextBlob blob(url_request_context(), "blob:writedir",
+  ScopedTextBlob blob(blob_storage_context(), "blob:writedir",
                       "It\'ll not be written, too.");
   file_system_context_->operation_runner()->Write(URLForPath(virtual_dir_path),
                                                   blob.GetBlobDataHandle(), 0,
@@ -259,7 +254,8 @@
 }
 
 TEST_F(FileSystemOperationImplWriteTest, TestWriteFailureByQuota) {
-  ScopedTextBlob blob(url_request_context(), "blob:success", "Hello, world!\n");
+  ScopedTextBlob blob(blob_storage_context(), "blob:success",
+                      "Hello, world!\n");
   quota_manager_->SetQuota(url::Origin::Create(kOrigin),
                            FileSystemTypeToQuotaStorageType(kFileSystemType),
                            10);
@@ -276,7 +272,8 @@
 }
 
 TEST_F(FileSystemOperationImplWriteTest, TestImmediateCancelSuccessfulWrite) {
-  ScopedTextBlob blob(url_request_context(), "blob:success", "Hello, world!\n");
+  ScopedTextBlob blob(blob_storage_context(), "blob:success",
+                      "Hello, world!\n");
   FileSystemOperationRunner::OperationID id =
       file_system_context_->operation_runner()->Write(URLForPath(virtual_path_),
                                                       blob.GetBlobDataHandle(),
@@ -298,7 +295,7 @@
 }
 
 TEST_F(FileSystemOperationImplWriteTest, TestImmediateCancelFailingWrite) {
-  ScopedTextBlob blob(url_request_context(), "blob:writeinvalidfile",
+  ScopedTextBlob blob(blob_storage_context(), "blob:writeinvalidfile",
                       "It\'ll not be written.");
   FileSystemOperationRunner::OperationID id =
       file_system_context_->operation_runner()->Write(
diff --git a/storage/browser/fileapi/file_system_operation_runner.cc b/storage/browser/fileapi/file_system_operation_runner.cc
index d9b52d6c4..af2c6fc 100644
--- a/storage/browser/fileapi/file_system_operation_runner.cc
+++ b/storage/browser/fileapi/file_system_operation_runner.cc
@@ -17,7 +17,6 @@
 #include "base/stl_util.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "net/url_request/url_request_context.h"
-#include "storage/browser/blob/blob_url_request_job_factory.h"
 #include "storage/browser/blob/shareable_file_reference.h"
 #include "storage/browser/fileapi/file_observers.h"
 #include "storage/browser/fileapi/file_stream_writer.h"
diff --git a/storage/browser/fileapi/file_writer_delegate_unittest.cc b/storage/browser/fileapi/file_writer_delegate_unittest.cc
index a40f98e..cb4e4581 100644
--- a/storage/browser/fileapi/file_writer_delegate_unittest.cc
+++ b/storage/browser/fileapi/file_writer_delegate_unittest.cc
@@ -89,8 +89,6 @@
   FileWriterDelegate::WriteProgressStatus write_status_;
 };
 
-class BlobURLRequestJobFactory;
-
 }  // namespace (anonymous)
 
 class FileWriterDelegateTest : public PlatformTest {
diff --git a/storage/browser/test/mock_blob_url_request_context.cc b/storage/browser/test/mock_blob_url_request_context.cc
deleted file mode 100644
index 54fb8e6..0000000
--- a/storage/browser/test/mock_blob_url_request_context.cc
+++ /dev/null
@@ -1,47 +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 "storage/browser/test/mock_blob_url_request_context.h"
-
-#include <memory>
-
-#include "base/threading/thread_task_runner_handle.h"
-#include "storage/browser/blob/blob_data_builder.h"
-#include "storage/browser/blob/blob_storage_context.h"
-#include "storage/browser/blob/blob_url_request_job.h"
-#include "storage/browser/blob/blob_url_request_job_factory.h"
-
-namespace content {
-
-MockBlobURLRequestContext::MockBlobURLRequestContext()
-    : blob_storage_context_(new storage::BlobStorageContext) {
-  // Job factory owns the protocol handler.
-  job_factory_.SetProtocolHandler(
-      "blob", std::make_unique<storage::BlobProtocolHandler>(
-                  blob_storage_context_.get()));
-  set_job_factory(&job_factory_);
-}
-
-MockBlobURLRequestContext::~MockBlobURLRequestContext() {
-  AssertNoURLRequests();
-}
-
-ScopedTextBlob::ScopedTextBlob(const MockBlobURLRequestContext& request_context,
-                               const std::string& blob_id,
-                               const std::string& data)
-    : blob_id_(blob_id), context_(request_context.blob_storage_context()) {
-  DCHECK(context_);
-  auto blob_builder = std::make_unique<storage::BlobDataBuilder>(blob_id_);
-  if (!data.empty())
-    blob_builder->AppendData(data);
-  handle_ = context_->AddFinishedBlob(std::move(blob_builder));
-}
-
-ScopedTextBlob::~ScopedTextBlob() = default;
-
-std::unique_ptr<storage::BlobDataHandle> ScopedTextBlob::GetBlobDataHandle() {
-  return context_->GetBlobDataFromUUID(blob_id_);
-}
-
-}  // namespace content
diff --git a/storage/browser/test/mock_blob_url_request_context.h b/storage/browser/test/mock_blob_url_request_context.h
deleted file mode 100644
index aa6997a7..0000000
--- a/storage/browser/test/mock_blob_url_request_context.h
+++ /dev/null
@@ -1,57 +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.
-
-#ifndef CONTENT_PUBLIC_TEST_MOCK_BLOB_URL_REQUEST_CONTEXT_H_
-#define CONTENT_PUBLIC_TEST_MOCK_BLOB_URL_REQUEST_CONTEXT_H_
-
-#include "base/macros.h"
-#include "net/url_request/url_request_context.h"
-#include "net/url_request/url_request_job.h"
-#include "net/url_request/url_request_job_factory_impl.h"
-
-namespace storage {
-class BlobDataHandle;
-class BlobStorageContext;
-}
-
-namespace content {
-
-class MockBlobURLRequestContext : public net::URLRequestContext {
- public:
-  MockBlobURLRequestContext();
-  ~MockBlobURLRequestContext() override;
-
-  storage::BlobStorageContext* blob_storage_context() const {
-    return blob_storage_context_.get();
-  }
-
- private:
-  net::URLRequestJobFactoryImpl job_factory_;
-  std::unique_ptr<storage::BlobStorageContext> blob_storage_context_;
-
-  DISALLOW_COPY_AND_ASSIGN(MockBlobURLRequestContext);
-};
-
-class ScopedTextBlob {
- public:
-  // Registers a blob with the given |id| that contains |data|.
-  ScopedTextBlob(const MockBlobURLRequestContext& request_context,
-                 const std::string& blob_id,
-                 const std::string& data);
-  ~ScopedTextBlob();
-
-  // Returns a BlobDataHandle referring to the scoped blob.
-  std::unique_ptr<storage::BlobDataHandle> GetBlobDataHandle();
-
- private:
-  const std::string blob_id_;
-  storage::BlobStorageContext* context_;
-  std::unique_ptr<storage::BlobDataHandle> handle_;
-
-  DISALLOW_COPY_AND_ASSIGN(ScopedTextBlob);
-};
-
-}  // namespace content
-
-#endif  // CONTENT_PUBLIC_TEST_MOCK_BLOB_URL_REQUEST_CONTEXT_H_
diff --git a/storage/browser/test/mock_blob_util.cc b/storage/browser/test/mock_blob_util.cc
new file mode 100644
index 0000000..f2d30b0
--- /dev/null
+++ b/storage/browser/test/mock_blob_util.cc
@@ -0,0 +1,32 @@
+// 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 "storage/browser/test/mock_blob_util.h"
+
+#include <memory>
+
+#include "base/threading/thread_task_runner_handle.h"
+#include "storage/browser/blob/blob_data_builder.h"
+#include "storage/browser/blob/blob_storage_context.h"
+
+namespace storage {
+
+ScopedTextBlob::ScopedTextBlob(BlobStorageContext* context,
+                               const std::string& blob_id,
+                               const std::string& data)
+    : blob_id_(blob_id), context_(context) {
+  DCHECK(context_);
+  auto blob_builder = std::make_unique<BlobDataBuilder>(blob_id_);
+  if (!data.empty())
+    blob_builder->AppendData(data);
+  handle_ = context_->AddFinishedBlob(std::move(blob_builder));
+}
+
+ScopedTextBlob::~ScopedTextBlob() = default;
+
+std::unique_ptr<BlobDataHandle> ScopedTextBlob::GetBlobDataHandle() {
+  return context_->GetBlobDataFromUUID(blob_id_);
+}
+
+}  // namespace storage
diff --git a/storage/browser/test/mock_blob_util.h b/storage/browser/test/mock_blob_util.h
new file mode 100644
index 0000000..a985ec22
--- /dev/null
+++ b/storage/browser/test/mock_blob_util.h
@@ -0,0 +1,38 @@
+// 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.
+
+#ifndef STORAGE_BROWSER_TEST_MOCK_BLOB_UTIL_H_
+#define STORAGE_BROWSER_TEST_MOCK_BLOB_UTIL_H_
+
+#include <string>
+
+#include "base/macros.h"
+
+namespace storage {
+
+class BlobDataHandle;
+class BlobStorageContext;
+
+class ScopedTextBlob {
+ public:
+  // Registers a blob with the given |id| that contains |data|.
+  ScopedTextBlob(BlobStorageContext* context,
+                 const std::string& blob_id,
+                 const std::string& data);
+  ~ScopedTextBlob();
+
+  // Returns a BlobDataHandle referring to the scoped blob.
+  std::unique_ptr<BlobDataHandle> GetBlobDataHandle();
+
+ private:
+  const std::string blob_id_;
+  BlobStorageContext* context_;
+  std::unique_ptr<BlobDataHandle> handle_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedTextBlob);
+};
+
+}  // namespace storage
+
+#endif  // STORAGE_BROWSER_TEST_MOCK_BLOB_UTIL_H_
diff --git a/testing/buildbot/chrome.json b/testing/buildbot/chrome.json
index 7bef184..1544356 100644
--- a/testing/buildbot/chrome.json
+++ b/testing/buildbot/chrome.json
@@ -614,8 +614,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -630,8 +631,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -646,8 +648,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -662,8 +665,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -678,8 +682,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -694,8 +699,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -710,8 +716,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -726,8 +733,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -742,8 +750,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -758,8 +767,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -774,8 +784,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -790,8 +801,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -807,8 +819,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -823,8 +836,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -839,8 +853,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -858,8 +873,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ],
           "shards": 10
@@ -880,8 +896,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ],
           "shards": 10
@@ -902,8 +919,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -918,8 +936,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -937,8 +956,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -953,8 +973,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -969,8 +990,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -985,8 +1007,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -1001,8 +1024,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -1017,8 +1041,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -1033,8 +1058,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -1049,8 +1075,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -1065,8 +1092,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -1081,8 +1109,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -1097,8 +1126,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ],
           "shards": 6
@@ -1118,8 +1148,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ],
           "shards": 10
@@ -1140,8 +1171,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -1156,8 +1188,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -1176,8 +1209,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -1192,8 +1226,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -1208,8 +1243,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -1224,8 +1260,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -1240,8 +1277,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -1256,8 +1294,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -1272,8 +1311,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -1288,8 +1328,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -1304,8 +1345,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -1320,8 +1362,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -1336,8 +1379,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -1352,8 +1396,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -1368,8 +1413,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -1384,8 +1430,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -1400,8 +1447,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -1416,8 +1464,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -1432,8 +1481,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -1451,8 +1501,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ],
           "shards": 3
@@ -1473,8 +1524,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -1489,8 +1541,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -1505,8 +1558,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -1521,8 +1575,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -1537,8 +1592,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -1553,8 +1609,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -1569,8 +1626,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -1585,8 +1643,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -1601,8 +1660,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -1617,8 +1677,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -1633,8 +1694,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -1649,8 +1711,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -1665,8 +1728,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -1681,8 +1745,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -1697,8 +1762,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -1713,8 +1779,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -1729,8 +1796,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -1745,8 +1813,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -1764,8 +1833,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -1783,8 +1853,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -1799,8 +1870,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -1815,8 +1887,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -1831,8 +1904,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -1847,8 +1921,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -1863,8 +1938,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -1879,8 +1955,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -1895,8 +1972,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -1911,8 +1989,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -1927,8 +2006,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -1943,8 +2023,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -1959,8 +2040,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -1975,8 +2057,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -1991,8 +2074,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -2007,8 +2091,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -2023,8 +2108,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -2039,8 +2125,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -2055,8 +2142,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -2071,8 +2159,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -2087,8 +2176,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -2106,8 +2196,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -2122,8 +2213,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -2139,8 +2231,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -2155,8 +2248,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -2171,8 +2265,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -2187,8 +2282,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -2203,8 +2299,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
@@ -2219,8 +2316,9 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-14.04",
-              "pool": "chrome.tests"
+              "os": "Ubuntu-16.04",
+              "pool": "chrome.tests",
+              "ssd": "0"
             }
           ]
         },
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 50eacaf6..5e13b21 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -9718,33 +9718,6 @@
         },
         "test": "wtf_unittests"
       }
-    ],
-    "isolated_scripts": [
-      {
-        "args": [
-          "--num-retries=3",
-          "--release",
-          "--additional-driver-flag=--enable-features=BlinkHeapConcurrentMarking"
-        ],
-        "isolate_name": "blink_web_tests_exparchive",
-        "merge": {
-          "args": [
-            "--verbose"
-          ],
-          "script": "//third_party/blink/tools/merge_web_test_results.py"
-        },
-        "name": "webkit_layout_tests",
-        "results_handler": "layout tests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ],
-          "shards": 12
-        }
-      }
     ]
   },
   "linux-blink-heap-unified-gc": {
diff --git a/testing/buildbot/client.v8.fyi.json b/testing/buildbot/client.v8.fyi.json
index e658664..d1776355 100644
--- a/testing/buildbot/client.v8.fyi.json
+++ b/testing/buildbot/client.v8.fyi.json
@@ -1351,6 +1351,55 @@
       }
     ]
   },
+  "V8 Blink Linux Future": {
+    "gtest_tests": [
+      {
+        "args": [
+          "--disable-blink-features=LayoutNG"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "webkit_unit_tests_ng",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ]
+        },
+        "test": "blink_unittests"
+      }
+    ],
+    "isolated_scripts": [
+      {
+        "args": [
+          "--num-retries=3",
+          "--additional-driver-flag=--js-flags=--future"
+        ],
+        "isolate_name": "blink_web_tests_exparchive",
+        "merge": {
+          "args": [
+            "--verbose"
+          ],
+          "script": "//third_party/blink/tools/merge_web_test_results.py"
+        },
+        "name": "webkit_layout_tests",
+        "results_handler": "layout tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ],
+          "shards": 12
+        }
+      }
+    ]
+  },
   "V8 Blink Linux Layout NG": {
     "gtest_tests": [
       {
@@ -1450,27 +1499,6 @@
     ]
   },
   "V8 Blink Win": {
-    "gtest_tests": [
-      {
-        "args": [
-          "--disable-blink-features=LayoutNG"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "webkit_unit_tests_ng",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Windows-7-SP1"
-            }
-          ]
-        },
-        "test": "blink_unittests"
-      }
-    ],
     "isolated_scripts": [
       {
         "args": [
@@ -1489,7 +1517,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Windows-7-SP1"
+              "os": "Windows-10-15063"
             }
           ],
           "shards": 12
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index 6166511..ad3042e 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -2046,6 +2046,11 @@
           '--debug',
         ],
       },
+      'V8 Blink Linux Future': {
+        'args': [
+          '--additional-driver-flag=--js-flags=--future',
+        ],
+      },
     },
   },
   'webkit_unit_tests': {
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index c702c8d..fdfa82f 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -62,8 +62,15 @@
         ],
         'mixins': [
           'chrome-swarming-pool',
-          'linux-trusty',
+          'linux-xenial',
         ],
+        'swarming': {
+          'dimension_sets': [
+            {
+              'ssd': '0',
+            },
+          ],
+        },
         'test_suites': {
           'gtest_tests': 'linux_chromeos_gtests',
         },
@@ -1741,7 +1748,6 @@
         ],
         'test_suites': {
           'gtest_tests': 'chromium_gtests',
-          'isolated_scripts': 'chromium_webkit_isolated_scripts',
         },
         'gtest_args': [
           '--enable-features=BlinkHeapConcurrentMarking',
@@ -4166,6 +4172,15 @@
           'isolated_scripts': 'chromium_webkit_isolated_scripts',
         },
       },
+      'V8 Blink Linux Future': {
+        'mixins': [
+          'linux-xenial',
+        ],
+        'test_suites': {
+          'gtest_tests': 'layout_ng_gtests',
+          'isolated_scripts': 'chromium_webkit_isolated_scripts',
+        },
+      },
       'V8 Blink Linux Layout NG': {
         'swarming': {
           'dimension_sets': [
@@ -4195,10 +4210,9 @@
       },
       'V8 Blink Win': {
         'mixins': [
-          'win7',
+          'win10',
         ],
         'test_suites': {
-          'gtest_tests': 'layout_ng_gtests',
           'isolated_scripts': 'chromium_webkit_isolated_scripts',
         },
       },
diff --git a/testing/merge_scripts/code_coverage/merge_results.py b/testing/merge_scripts/code_coverage/merge_results.py
index e6f9c90..0b5ca9c63 100755
--- a/testing/merge_scripts/code_coverage/merge_results.py
+++ b/testing/merge_scripts/code_coverage/merge_results.py
@@ -85,9 +85,13 @@
               'w') as f:
       json.dump(invalid_profiles, f)
 
-    mark_invalid_shards(
-        coverage_merger.get_shards_to_retry(invalid_profiles),
-        params.jsons_to_merge)
+    # We don't want to invalidate shards in a CQ build, which we determine by
+    # the existence of the 'patch_storage' property.
+    build_properties = json.loads(params.build_properties)
+    if not build_properties.get('patch_storage'):
+      mark_invalid_shards(
+          coverage_merger.get_shards_to_retry(invalid_profiles),
+          params.jsons_to_merge)
   logging.info('Merging %d test results', len(params.jsons_to_merge))
   failed = False
 
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 9235e92..92955bd 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -411,6 +411,21 @@
             ]
         }
     ],
+    "AndroidManualPasswordGeneration": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "ManualPasswordGenerationAndroid"
+                    ]
+                }
+            ]
+        }
+    ],
     "AndroidMediaProcessPriority": [
         {
             "platforms": [
diff --git a/third_party/.gitignore b/third_party/.gitignore
index c21d7d8..e1b200c7 100644
--- a/third_party/.gitignore
+++ b/third_party/.gitignore
@@ -13,6 +13,7 @@
 /android_build_tools/art/profman
 /android_build_tools/bundletool/*.jar
 /android_ndk/
+/android_sdk/androidx_browser/src
 /android_sdk/public/
 /android_sdk/sources/
 /android_protobuf/src
diff --git a/third_party/android_sdk/androidx_browser/BUILD.gn b/third_party/android_sdk/androidx_browser/BUILD.gn
new file mode 100644
index 0000000..6077716
--- /dev/null
+++ b/third_party/android_sdk/androidx_browser/BUILD.gn
@@ -0,0 +1,54 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/android/rules.gni")
+
+android_resources("androidx_browser_resources") {
+  resource_dirs = [ "src/browser/src/main/res" ]
+  custom_package = "android.support.customtabs"
+}
+
+android_library("androidx_browser_java") {
+  java_files = [
+    "./src/browser/src/main/java/androidx/browser/customtabs/CustomTabsIntent.java",
+    "./src/browser/src/main/java/androidx/browser/customtabs/CustomTabColorSchemeParams.java",
+    "./src/browser/src/main/java/androidx/browser/customtabs/CustomTabsSessionToken.java",
+    "./src/browser/src/main/java/androidx/browser/customtabs/PostMessageService.java",
+    "./src/browser/src/main/java/androidx/browser/customtabs/PostMessageServiceConnection.java",
+    "./src/browser/src/main/java/androidx/browser/customtabs/CustomTabsServiceConnection.java",
+    "./src/browser/src/main/java/androidx/browser/customtabs/CustomTabsCallback.java",
+    "./src/browser/src/main/java/androidx/browser/customtabs/CustomTabsSession.java",
+    "./src/browser/src/main/java/androidx/browser/customtabs/PostMessageBackend.java",
+    "./src/browser/src/main/java/androidx/browser/customtabs/CustomTabsService.java",
+    "./src/browser/src/main/java/androidx/browser/customtabs/CustomTabsClient.java",
+    "./src/browser/src/main/java/androidx/browser/customtabs/TrustedWebUtils.java",
+    "./src/browser/src/main/java/androidx/browser/trusted/NotificationApiHelperForM.java",
+    "./src/browser/src/main/java/androidx/browser/trusted/NotificationApiHelperForO.java",
+    "./src/browser/src/main/java/androidx/browser/trusted/TrustedWebActivityServiceWrapper.java",
+    "./src/browser/src/main/java/androidx/browser/trusted/TrustedWebActivityServiceConnectionManager.java",
+    "./src/browser/src/main/java/androidx/browser/trusted/TrustedWebActivityService.java",
+    "./src/browser/src/main/java/androidx/browser/trusted/TrustedWebActivityBuilder.java",
+
+  ]
+  deps = [
+    ":androidx_browser_resources",
+    "//third_party/android_deps:android_support_v7_appcompat_java",
+    "//third_party/android_deps:com_android_support_support_annotations_java",
+    "//third_party/android_deps:com_android_support_support_compat_java",
+  ]
+  srcjar_deps = [ ":androidx_browser_service_aidl" ]
+  android_manifest_for_lint = "src/browser/src/main/AndroidManifest.xml"
+  chromium_code = false
+}
+
+android_aidl("androidx_browser_service_aidl") {
+  interface_file = "common.aidl"
+
+  sources = [
+    "./src/browser/src/main/aidl/android/support/customtabs/ICustomTabsService.aidl",
+    "./src/browser/src/main/aidl/android/support/customtabs/IPostMessageService.aidl",
+    "./src/browser/src/main/aidl/android/support/customtabs/trusted/ITrustedWebActivityService.aidl",
+    "./src/browser/src/main/aidl/android/support/customtabs/ICustomTabsCallback.aidl",
+  ]
+}
diff --git a/third_party/android_sdk/androidx_browser/LICENSE b/third_party/android_sdk/androidx_browser/LICENSE
new file mode 100644
index 0000000..67db858
--- /dev/null
+++ b/third_party/android_sdk/androidx_browser/LICENSE
@@ -0,0 +1,175 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
diff --git a/third_party/android_sdk/androidx_browser/OWNERS b/third_party/android_sdk/androidx_browser/OWNERS
new file mode 100644
index 0000000..f728cfa
--- /dev/null
+++ b/third_party/android_sdk/androidx_browser/OWNERS
@@ -0,0 +1,6 @@
+lizeb@chromium.org
+peconn@chromium.org
+yusufo@chromium.org
+
+per-file *.aidl=set noparent
+per-file *.aidl=file://ipc/SECURITY_OWNERS
diff --git a/third_party/android_sdk/androidx_browser/README.chromium b/third_party/android_sdk/androidx_browser/README.chromium
new file mode 100644
index 0000000..27f4bb86
--- /dev/null
+++ b/third_party/android_sdk/androidx_browser/README.chromium
@@ -0,0 +1,28 @@
+Name: AndroidX Browser
+Short Name: AndroidX Browser
+URL: https://chromium.googlesource.com/external/gob/android/platform/frameworks/support/browser
+Version: 226da6c0dfb265404d3a67305802ab70a9ca6c80
+License: Apache 2.0
+Security Critical: yes
+License Android Compatible: yes
+
+Description:
+This is a copy of the files from androidx.browser to be used in Chromium.
+This covers Custom Tabs and Trusted Web Activities. The original code (not the
+Chromium hosted modified copy) can be found at:
+https://android.googlesource.com/platform/frameworks/support/+/androidx-master-dev/browser/
+
+Local Modifications:
+Since Chromium does not yet rely on AndroidX (it still relies on the Android
+Support Library), various parts of androidx.browser have been omitted:
+- Browser Actions, which rely on androidx.concurrent.futures.ResolvableFuture.
+  Chromium has dropped support for Browser Actions so we don't need this code.
+- Tests, which rely on androidx.testutils.PollingCheck. New development on
+  the code in androidx.browser should happen in the AndroidX repository
+  (and will be automatically copied to the Chromium one).
+
+In addition, we don't compile with the included AndroidManifest.xml because
+in the AndroidX the minSdkVersion is set in build.gradle. Chromium's tooling
+can't read this and expects the minSdkVersion to be present in the manifest
+(which conversely Android's tooling complains about). If we omit the
+AndroidManifest, Chromium's tooling chooses a sensible default.
diff --git a/third_party/android_sdk/androidx_browser/common.aidl b/third_party/android_sdk/androidx_browser/common.aidl
new file mode 100644
index 0000000..0eb2a8a
--- /dev/null
+++ b/third_party/android_sdk/androidx_browser/common.aidl
@@ -0,0 +1,8 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+interface android.support.customtabs.ICustomTabsService;
+interface android.support.customtabs.ICustomTabsCallback;
+interface android.support.customtabs.IPostMessageService;
+interface android.support.customtabs.trusted.ITrustedWebActivityService;
diff --git a/third_party/blink/public/mojom/background_fetch/background_fetch.mojom b/third_party/blink/public/mojom/background_fetch/background_fetch.mojom
index 2a5f3eb..cb838c3 100644
--- a/third_party/blink/public/mojom/background_fetch/background_fetch.mojom
+++ b/third_party/blink/public/mojom/background_fetch/background_fetch.mojom
@@ -170,5 +170,5 @@
                 bool match_all) => (array<BackgroundFetchSettledFetch> fetches);
 
   // Registers the |observer| to receive events for the given registration.
-  AddRegistrationObserver(BackgroundFetchRegistrationObserver observer);
+  AddRegistrationObserver(pending_remote<BackgroundFetchRegistrationObserver> observer);
 };
diff --git a/third_party/blink/renderer/bindings/core/v8/generated_code_helper.cc b/third_party/blink/renderer/bindings/core/v8/generated_code_helper.cc
index 1042a3e..d455494 100644
--- a/third_party/blink/renderer/bindings/core/v8/generated_code_helper.cc
+++ b/third_party/blink/renderer/bindings/core/v8/generated_code_helper.cc
@@ -4,9 +4,13 @@
 
 #include "third_party/blink/renderer/bindings/core/v8/generated_code_helper.h"
 
+#include "third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h"
 #include "third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_element.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
+#include "third_party/blink/renderer/core/html/custom/ce_reactions_scope.h"
+#include "third_party/blink/renderer/core/html/custom/v0_custom_element_processing_stack.h"
 #include "third_party/blink/renderer/platform/bindings/v8_per_context_data.h"
 
 namespace blink {
@@ -100,4 +104,58 @@
                                             IgnorePause::kIgnore);
 }
 
+void V8SetReflectedBooleanAttribute(
+    const v8::FunctionCallbackInfo<v8::Value>& info,
+    const char* interface_name,
+    const char* idl_attribute_name,
+    const QualifiedName& content_attr) {
+  v8::Isolate* isolate = info.GetIsolate();
+  Element* impl = V8Element::ToImpl(info.Holder());
+
+  V0CustomElementProcessingStack::CallbackDeliveryScope delivery_scope;
+  ExceptionState exception_state(isolate, ExceptionState::kSetterContext,
+                                 interface_name, idl_attribute_name);
+  CEReactionsScope ce_reactions_scope;
+
+  // Prepare the value to be set.
+  bool cpp_value = NativeValueTraits<IDLBoolean>::NativeValue(isolate, info[0],
+                                                              exception_state);
+  if (exception_state.HadException())
+    return;
+
+  impl->SetBooleanAttribute(content_attr, cpp_value);
+}
+
+void V8SetReflectedDOMStringAttribute(
+    const v8::FunctionCallbackInfo<v8::Value>& info,
+    const QualifiedName& content_attr) {
+  Element* impl = V8Element::ToImpl(info.Holder());
+
+  V0CustomElementProcessingStack::CallbackDeliveryScope delivery_scope;
+  CEReactionsScope ce_reactions_scope;
+
+  // Prepare the value to be set.
+  V8StringResource<> cpp_value = info[0];
+  if (!cpp_value.Prepare())
+    return;
+
+  impl->setAttribute(content_attr, cpp_value);
+}
+
+void V8SetReflectedNullableDOMStringAttribute(
+    const v8::FunctionCallbackInfo<v8::Value>& info,
+    const QualifiedName& content_attr) {
+  Element* impl = V8Element::ToImpl(info.Holder());
+
+  V0CustomElementProcessingStack::CallbackDeliveryScope delivery_scope;
+  CEReactionsScope ce_reactions_scope;
+
+  // Prepare the value to be set.
+  V8StringResource<kTreatNullAndUndefinedAsNullString> cpp_value = info[0];
+  if (!cpp_value.Prepare())
+    return;
+
+  impl->setAttribute(content_attr, cpp_value);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/bindings/core/v8/generated_code_helper.h b/third_party/blink/renderer/bindings/core/v8/generated_code_helper.h
index 43c33dd..28acbe69 100644
--- a/third_party/blink/renderer/bindings/core/v8/generated_code_helper.h
+++ b/third_party/blink/renderer/bindings/core/v8/generated_code_helper.h
@@ -19,6 +19,7 @@
 
 namespace blink {
 
+class QualifiedName;
 class ScriptState;
 class SerializedScriptValue;
 
@@ -82,6 +83,19 @@
 
 using InstallRuntimeEnabledFeaturesOnTemplateFunction = InstallTemplateFunction;
 
+// Helpers for [CEReactions, Reflect] IDL attributes.
+void V8SetReflectedBooleanAttribute(
+    const v8::FunctionCallbackInfo<v8::Value>& info,
+    const char* interface_name,
+    const char* idl_attribute_name,
+    const QualifiedName& content_attr);
+void V8SetReflectedDOMStringAttribute(
+    const v8::FunctionCallbackInfo<v8::Value>& info,
+    const QualifiedName& content_attr);
+void V8SetReflectedNullableDOMStringAttribute(
+    const v8::FunctionCallbackInfo<v8::Value>& info,
+    const QualifiedName& content_attr);
+
 }  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_GENERATED_CODE_HELPER_H_
diff --git a/third_party/blink/renderer/bindings/core/v8/module_record.cc b/third_party/blink/renderer/bindings/core/v8/module_record.cc
index b13469ab..ccaa849b 100644
--- a/third_party/blink/renderer/bindings/core/v8/module_record.cc
+++ b/third_party/blink/renderer/bindings/core/v8/module_record.cc
@@ -103,14 +103,17 @@
   return ModuleRecord(isolate, module, source_url);
 }
 
-ScriptValue ModuleRecord::Instantiate(ScriptState* script_state) {
+ScriptValue ModuleRecord::Instantiate(ScriptState* script_state,
+                                      const KURL& source_url) {
+  // TODO(rikaf): Remove source_url_
+  DCHECK_EQ(source_url, source_url_);
   v8::Isolate* isolate = script_state->GetIsolate();
   v8::TryCatch try_catch(isolate);
   try_catch.SetVerbose(true);
 
   DCHECK(!IsNull());
   v8::Local<v8::Context> context = script_state->GetContext();
-  probe::ExecuteScript probe(ExecutionContext::From(script_state), source_url_);
+  probe::ExecuteScript probe(ExecutionContext::From(script_state), source_url);
   bool success;
   if (!NewLocal(script_state->GetIsolate())
            ->InstantiateModule(context, &ResolveModuleCallback)
diff --git a/third_party/blink/renderer/bindings/core/v8/module_record.h b/third_party/blink/renderer/bindings/core/v8/module_record.h
index b3db902..646a8ca3 100644
--- a/third_party/blink/renderer/bindings/core/v8/module_record.h
+++ b/third_party/blink/renderer/bindings/core/v8/module_record.h
@@ -87,7 +87,7 @@
   ModuleRecord(v8::Isolate*, v8::Local<v8::Module>, const KURL&);
 
   // Returns exception, if any.
-  ScriptValue Instantiate(ScriptState*);
+  ScriptValue Instantiate(ScriptState*, const KURL& source_url);
 
   // Returns exception, if any.
   ScriptValue Evaluate(ScriptState*) const;
diff --git a/third_party/blink/renderer/bindings/core/v8/module_record_test.cc b/third_party/blink/renderer/bindings/core/v8/module_record_test.cc
index fb5f9ae9..af03fab8 100644
--- a/third_party/blink/renderer/bindings/core/v8/module_record_test.cc
+++ b/third_party/blink/renderer/bindings/core/v8/module_record_test.cc
@@ -180,7 +180,7 @@
       ScriptFetchOptions(), TextPosition::MinimumPosition(),
       ASSERT_NO_EXCEPTION);
   ASSERT_FALSE(module.IsNull());
-  ScriptValue exception = module.Instantiate(scope.GetScriptState());
+  ScriptValue exception = module.Instantiate(scope.GetScriptState(), js_url);
   ASSERT_TRUE(exception.IsEmpty());
 
   EXPECT_EQ(0u, resolver->ResolveCount());
@@ -216,7 +216,7 @@
       js_url_c, js_url_c, ScriptFetchOptions(), TextPosition::MinimumPosition(),
       ASSERT_NO_EXCEPTION);
   ASSERT_FALSE(module.IsNull());
-  ScriptValue exception = module.Instantiate(scope.GetScriptState());
+  ScriptValue exception = module.Instantiate(scope.GetScriptState(), js_url_c);
   ASSERT_TRUE(exception.IsEmpty());
 
   ASSERT_EQ(2u, resolver->ResolveCount());
@@ -238,7 +238,8 @@
       ScriptFetchOptions(), TextPosition::MinimumPosition(),
       ASSERT_NO_EXCEPTION);
   ASSERT_FALSE(module_failure.IsNull());
-  ASSERT_TRUE(module_failure.Instantiate(scope.GetScriptState()).IsEmpty());
+  ASSERT_TRUE(
+      module_failure.Instantiate(scope.GetScriptState(), js_url_f).IsEmpty());
   ScriptValue evaluation_error =
       module_failure.Evaluate(scope.GetScriptState());
   EXPECT_FALSE(evaluation_error.IsEmpty());
@@ -251,7 +252,7 @@
       js_url_c, ScriptFetchOptions(), TextPosition::MinimumPosition(),
       scope.GetExceptionState());
   ASSERT_FALSE(module.IsNull());
-  ASSERT_TRUE(module.Instantiate(scope.GetScriptState()).IsEmpty());
+  ASSERT_TRUE(module.Instantiate(scope.GetScriptState(), js_url_c).IsEmpty());
   ScriptValue evaluation_error2 = module.Evaluate(scope.GetScriptState());
   EXPECT_FALSE(evaluation_error2.IsEmpty());
 
@@ -273,7 +274,7 @@
       js_url, ScriptFetchOptions(), TextPosition::MinimumPosition(),
       ASSERT_NO_EXCEPTION);
   ASSERT_FALSE(module.IsNull());
-  ScriptValue exception = module.Instantiate(scope.GetScriptState());
+  ScriptValue exception = module.Instantiate(scope.GetScriptState(), js_url);
   ASSERT_TRUE(exception.IsEmpty());
 
   EXPECT_TRUE(module.Evaluate(scope.GetScriptState()).IsEmpty());
@@ -306,7 +307,7 @@
       scope.GetIsolate(), "throw 'bar';", js_url, js_url, ScriptFetchOptions(),
       TextPosition::MinimumPosition(), ASSERT_NO_EXCEPTION);
   ASSERT_FALSE(module.IsNull());
-  ScriptValue exception = module.Instantiate(scope.GetScriptState());
+  ScriptValue exception = module.Instantiate(scope.GetScriptState(), js_url);
   ASSERT_TRUE(exception.IsEmpty());
 
   ScriptValue error = module.Evaluate(scope.GetScriptState());
diff --git a/third_party/blink/renderer/bindings/scripts/build_web_idl_database.pydeps b/third_party/blink/renderer/bindings/scripts/build_web_idl_database.pydeps
index 058c70d..d0168c2 100644
--- a/third_party/blink/renderer/bindings/scripts/build_web_idl_database.pydeps
+++ b/third_party/blink/renderer/bindings/scripts/build_web_idl_database.pydeps
@@ -18,6 +18,7 @@
 web_idl/enumeration.py
 web_idl/exposure.py
 web_idl/extended_attribute.py
+web_idl/function_like.py
 web_idl/identifier_ir_map.py
 web_idl/idl_compiler.py
 web_idl/idl_member.py
diff --git a/third_party/blink/renderer/bindings/scripts/collect_idl_files.pydeps b/third_party/blink/renderer/bindings/scripts/collect_idl_files.pydeps
index 0fe734d..92f5d46 100644
--- a/third_party/blink/renderer/bindings/scripts/collect_idl_files.pydeps
+++ b/third_party/blink/renderer/bindings/scripts/collect_idl_files.pydeps
@@ -27,6 +27,7 @@
 web_idl/enumeration.py
 web_idl/exposure.py
 web_idl/extended_attribute.py
+web_idl/function_like.py
 web_idl/identifier_ir_map.py
 web_idl/idl_compiler.py
 web_idl/idl_member.py
diff --git a/third_party/blink/renderer/bindings/scripts/v8_attributes.py b/third_party/blink/renderer/bindings/scripts/v8_attributes.py
index b28b21f..326927f1 100644
--- a/third_party/blink/renderer/bindings/scripts/v8_attributes.py
+++ b/third_party/blink/renderer/bindings/scripts/v8_attributes.py
@@ -468,6 +468,20 @@
 
     has_type_checking_interface = idl_type.is_wrapper_type
 
+    use_common_reflection_setter = False
+    # Enable use_common_reflection_setter if
+    #  * extended_attributes is [CEReactions, Reflect] or
+    #    [CEReactions, Reflect, RuntimeEnabled],
+    #  * the type is boolean, DOMString, or DOMString?, and
+    #  * the interface inherits from 'Element'.
+    if ('Reflect' in extended_attributes and
+            'CEReactions' in extended_attributes and
+            str(idl_type) in ('boolean', 'DOMString', 'DOMString?') and
+            inherits_interface(interface.name, 'Element')):
+        if (len(extended_attributes) == 2 or
+                (len(extended_attributes) == 3 and 'RuntimeEnabled' in extended_attributes)):
+            use_common_reflection_setter = True
+
     context.update({
         'has_setter_exception_state':
             is_setter_raises_exception or has_type_checking_interface or
@@ -478,6 +492,7 @@
         'is_setter_call_with_script_state': has_extended_attribute_value(
             attribute, 'SetterCallWith', 'ScriptState'),
         'is_setter_raises_exception': is_setter_raises_exception,
+        'use_common_reflection_setter': use_common_reflection_setter,
         'v8_value_to_local_cpp_value': idl_type.v8_value_to_local_cpp_value(
             extended_attributes, 'v8_value', 'cpp_value'),
     })
@@ -521,6 +536,18 @@
         arguments.append('is_null')
     if context['is_setter_raises_exception']:
         arguments.append('exception_state')
+    if context['use_common_reflection_setter']:
+        attr_name = scoped_content_attribute_name(interface, attribute)
+        if idl_type.base_type == 'boolean':
+            setter_name = 'V8SetReflectedBooleanAttribute'
+            arguments = ['info', '"%s"' % interface.name,
+                         '"%s"' % attribute.name, attr_name]
+        elif idl_type.base_type == 'DOMString':
+            if idl_type.is_nullable:
+                setter_name = 'V8SetReflectedNullableDOMStringAttribute'
+            else:
+                setter_name = 'V8SetReflectedDOMStringAttribute'
+            arguments = ['info', attr_name]
 
     return '%s(%s)' % (setter_name, ', '.join(arguments))
 
diff --git a/third_party/blink/renderer/bindings/scripts/web_idl/argument.py b/third_party/blink/renderer/bindings/scripts/web_idl/argument.py
index 4e199b0..a018a92 100644
--- a/third_party/blink/renderer/bindings/scripts/web_idl/argument.py
+++ b/third_party/blink/renderer/bindings/scripts/web_idl/argument.py
@@ -3,31 +3,20 @@
 # found in the LICENSE file.
 
 from .composition_parts import WithIdentifier
-from .composition_parts import WithExtendedAttributes
-from .composition_parts import WithCodeGeneratorInfo
 from .composition_parts import WithOwner
 from .idl_type import IdlType
 from .values import DefaultValue
 
 
-class Argument(WithIdentifier, WithExtendedAttributes, WithCodeGeneratorInfo,
-               WithOwner):
-    class IR(WithIdentifier, WithExtendedAttributes, WithCodeGeneratorInfo):
-        def __init__(self,
-                     identifier,
-                     index,
-                     idl_type,
-                     default_value=None,
-                     extended_attributes=None,
-                     code_generator_info=None):
+class Argument(WithIdentifier, WithOwner):
+    class IR(WithIdentifier):
+        def __init__(self, identifier, index, idl_type, default_value=None):
             assert isinstance(index, int)
             assert isinstance(idl_type, IdlType)
             assert (default_value is None
                     or isinstance(default_value, DefaultValue))
 
             WithIdentifier.__init__(self, identifier)
-            WithExtendedAttributes.__init__(self, extended_attributes)
-            WithCodeGeneratorInfo.__init__(self, code_generator_info)
 
             self.index = index
             self.idl_type = idl_type
@@ -38,46 +27,39 @@
                 identifier=self.identifier,
                 index=self.index,
                 idl_type=self.idl_type,
-                default_value=self.default_value,
-                extended_attributes=self.extended_attributes.make_copy(),
-                code_generator_info=self.code_generator_info.make_copy())
+                default_value=self.default_value)
 
-    @property
-    def idl_type(self):
-        """
-        Returns type of this argument.
-        @return IdlType
-        """
-        assert False, 'To be implemented'
+    def __init__(self, ir, owner):
+        assert isinstance(ir, Argument.IR)
 
-    @property
-    def is_optional(self):
-        """
-        Returns True if this argument is optional.
-        @return bool
-        """
-        assert False, 'To be implemented'
+        WithIdentifier.__init__(self, ir.identifier)
+        WithOwner.__init__(self, owner)
 
-    @property
-    def is_variadic(self):
-        """
-        Returns True if this argument is variadic.
-        @return bool
-        """
-        assert False, 'To be implemented'
-
-    @property
-    def default_value(self):
-        """
-        Returns the default value if it is specified. Otherwise, None
-        @return DefaultValue
-        """
-        assert False, 'To be implemented'
+        self._index = ir.index
+        self._idl_type = ir.idl_type
+        self._default_value = ir.default_value
 
     @property
     def index(self):
-        """
-        Returns its index in an operation's arguments
-        @return int
-        """
-        assert False, 'To be implemented'
+        """Returns the argument index."""
+        return self._index
+
+    @property
+    def idl_type(self):
+        """Returns the type of the argument."""
+        return self._idl_type
+
+    @property
+    def is_optional(self):
+        """Returns True if this is an optional argument."""
+        return self.idl_type.is_optional
+
+    @property
+    def is_variadic(self):
+        """Returns True if this is a variadic argument."""
+        return self.idl_type.is_variadic
+
+    @property
+    def default_value(self):
+        """Returns the default value or None."""
+        return self._default_value
diff --git a/third_party/blink/renderer/bindings/scripts/web_idl/callback_function.py b/third_party/blink/renderer/bindings/scripts/web_idl/callback_function.py
index 153aa6b..357756b 100644
--- a/third_party/blink/renderer/bindings/scripts/web_idl/callback_function.py
+++ b/third_party/blink/renderer/bindings/scripts/web_idl/callback_function.py
@@ -4,6 +4,7 @@
 
 import exceptions
 
+from .function_like import FunctionLike
 from .composition_parts import WithCodeGeneratorInfo
 from .composition_parts import WithComponent
 from .composition_parts import WithDebugInfo
@@ -12,14 +13,16 @@
 from .user_defined_type import UserDefinedType
 
 
-class CallbackFunction(UserDefinedType, WithExtendedAttributes,
+class CallbackFunction(UserDefinedType, FunctionLike, WithExtendedAttributes,
                        WithCodeGeneratorInfo, WithComponent, WithDebugInfo):
     """https://heycam.github.io/webidl/#idl-callback-functions"""
 
-    class IR(IdentifierIRMap.IR, WithExtendedAttributes, WithCodeGeneratorInfo,
-             WithComponent, WithDebugInfo):
+    class IR(IdentifierIRMap.IR, FunctionLike.IR, WithExtendedAttributes,
+             WithCodeGeneratorInfo, WithComponent, WithDebugInfo):
         def __init__(self,
                      identifier,
+                     arguments,
+                     return_type,
                      extended_attributes=None,
                      code_generator_info=None,
                      component=None,
@@ -28,26 +31,24 @@
                 self,
                 identifier=identifier,
                 kind=IdentifierIRMap.IR.Kind.CALLBACK_FUNCTION)
+            FunctionLike.IR.__init__(
+                self, arguments=arguments, return_type=return_type)
             WithExtendedAttributes.__init__(self, extended_attributes)
             WithCodeGeneratorInfo.__init__(self, code_generator_info)
             WithComponent.__init__(self, component)
             WithDebugInfo.__init__(self, debug_info)
 
-    @property
-    def return_type(self):
-        """
-        Returns the type of return value.
-        @return IdlType
-        """
-        raise exceptions.NotImplementedError()
+    def __init__(self, ir):
+        assert isinstance(ir, CallbackFunction.IR)
 
-    @property
-    def arguments(self):
-        """
-        Returns a list of arguments.
-        @return Argument
-        """
-        raise exceptions.NotImplementedError()
+        UserDefinedType.__init__(self, ir.identifier)
+        FunctionLike.__init__(self, ir)
+        WithExtendedAttributes.__init__(self,
+                                        ir.extended_attributes.make_copy())
+        WithCodeGeneratorInfo.__init__(self,
+                                       ir.code_generator_info.make_copy())
+        WithComponent.__init__(self, components=ir.components)
+        WithDebugInfo.__init__(self, ir.debug_info.make_copy())
 
     # UserDefinedType overrides
     @property
diff --git a/third_party/blink/renderer/bindings/scripts/web_idl/function_like.py b/third_party/blink/renderer/bindings/scripts/web_idl/function_like.py
new file mode 100644
index 0000000..27c2b3c4e
--- /dev/null
+++ b/third_party/blink/renderer/bindings/scripts/web_idl/function_like.py
@@ -0,0 +1,34 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from .argument import Argument
+from .idl_type import IdlType
+
+
+class FunctionLike(object):
+    class IR(object):
+        def __init__(self, arguments, return_type):
+            assert isinstance(arguments, (list, tuple)) and all(
+                isinstance(arg, Argument.IR) for arg in arguments)
+            assert isinstance(return_type, IdlType)
+
+            self.arguments = list(arguments)
+            self.return_type = return_type
+
+    def __init__(self, ir):
+        assert isinstance(ir, FunctionLike.IR)
+
+        self._arguments = tuple(
+            [Argument(arg_ir, self) for arg_ir in ir.arguments])
+        self._return_type = ir.return_type
+
+    @property
+    def arguments(self):
+        """Returns a list of arguments."""
+        return self._arguments
+
+    @property
+    def return_type(self):
+        """Returns the return type."""
+        return self._return_type
diff --git a/third_party/blink/renderer/bindings/scripts/web_idl/idl_compiler.py b/third_party/blink/renderer/bindings/scripts/web_idl/idl_compiler.py
index 86875bac..0616ac2 100644
--- a/third_party/blink/renderer/bindings/scripts/web_idl/idl_compiler.py
+++ b/third_party/blink/renderer/bindings/scripts/web_idl/idl_compiler.py
@@ -2,6 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+from .callback_function import CallbackFunction
 from .composition_parts import Identifier
 from .database import Database
 from .database import DatabaseBody
@@ -201,6 +202,12 @@
         for ir in dictionary_irs.itervalues():
             self._db.register(DatabaseBody.Kind.DICTIONARY, Dictionary(ir))
 
+        callback_function_irs = self._ir_map.find_by_kind(
+            IdentifierIRMap.IR.Kind.CALLBACK_FUNCTION)
+        for ir in callback_function_irs.itervalues():
+            self._db.register(DatabaseBody.Kind.CALLBACK_FUNCTION,
+                              CallbackFunction(ir))
+
         typedef_irs = self._ir_map.find_by_kind(
             IdentifierIRMap.IR.Kind.TYPEDEF)
         for ir in typedef_irs.itervalues():
diff --git a/third_party/blink/renderer/bindings/scripts/web_idl/ir_builder.py b/third_party/blink/renderer/bindings/scripts/web_idl/ir_builder.py
index d65b8d99..b3a6088 100644
--- a/third_party/blink/renderer/bindings/scripts/web_idl/ir_builder.py
+++ b/third_party/blink/renderer/bindings/scripts/web_idl/ir_builder.py
@@ -104,13 +104,13 @@
             return self._build_callback_interface(node)
 
         child_nodes = list(node.GetChildren())
-        extended_attributes = self._take_extended_attributes(child_nodes)
         inherited = self._take_inheritance(child_nodes)
         iterable = self._take_iterable(child_nodes)
         maplike = self._take_maplike(child_nodes)
         setlike = self._take_setlike(child_nodes)
         # TODO(peria): Implement stringifier.
         _ = self._take_stringifier(child_nodes)
+        extended_attributes = self._take_extended_attributes(child_nodes)
 
         members = map(self._build_interface_or_namespace_member, child_nodes)
         attributes = []
@@ -145,13 +145,12 @@
             debug_info=self._build_debug_info(node))
 
     def _build_namespace(self, node):
-        namespace = Namespace.IR(
+        # TODO(peria): Build members and register them in |namespace|
+        return Namespace.IR(
             identifier=Identifier(node.GetName()),
             is_partial=bool(node.GetProperty('PARTIAL')),
             component=self._component,
             debug_info=self._build_debug_info(node))
-        # TODO(peria): Build members and register them in |namespace|
-        return namespace
 
     def _build_interface_or_namespace_member(self, node):
         def build_attribute(node):
@@ -209,8 +208,8 @@
 
     def _build_dictionary(self, node):
         child_nodes = list(node.GetChildren())
-        extended_attributes = self._take_extended_attributes(child_nodes)
         inherited = self._take_inheritance(child_nodes)
+        extended_attributes = self._take_extended_attributes(child_nodes)
         own_members = map(self._build_dictionary_member, child_nodes)
 
         return Dictionary.IR(
@@ -241,48 +240,51 @@
             debug_info=self._build_debug_info(node))
 
     def _build_callback_interface(self, node):
-        callback_interface = CallbackInterface.IR(
+        assert node.GetProperty('CALLBACK')
+        # TODO(peria): Build members and register them in |callback_interface|
+        return CallbackInterface.IR(
             identifier=Identifier(node.GetName()),
             component=self._component,
             debug_info=self._build_debug_info(node))
-        # TODO(peria): Build members and register them in |callback_interface|
-        return callback_interface
 
     def _build_callback_function(self, node):
-        callback_function = CallbackFunction.IR(
+        child_nodes = list(node.GetChildren())
+        arguments = self._take_arguments(child_nodes)
+        return_type = self._take_type(child_nodes)
+        extended_attributes = self._take_extended_attributes(child_nodes)
+        assert len(child_nodes) == 0
+        return CallbackFunction.IR(
             identifier=Identifier(node.GetName()),
+            arguments=arguments,
+            return_type=return_type,
+            extended_attributes=extended_attributes,
             component=self._component,
             debug_info=self._build_debug_info(node))
-        # TODO(peria): Build members and register them in |callback_function|
-        return callback_function
 
     def _build_enumeration(self, node):
-        enumeration = Enumeration.IR(
+        return Enumeration.IR(
             identifier=Identifier(node.GetName()),
             values=[child.GetName() for child in node.GetChildren()],
             component=self._component,
             debug_info=self._build_debug_info(node))
-        return enumeration
 
     def _build_typedef(self, node):
         child_nodes = list(node.GetChildren())
         idl_type = self._take_type(child_nodes)
         assert len(child_nodes) == 0
 
-        typedef = Typedef.IR(
+        return Typedef.IR(
             identifier=Identifier(node.GetName()),
             idl_type=idl_type,
             component=self._component,
             debug_info=self._build_debug_info(node))
-        return typedef
 
     def _build_includes(self, node):
-        includes = Includes.IR(
+        return Includes.IR(
             interface_identifier=Identifier(node.GetName()),
             mixin_identifier=Identifier(node.GetProperty('REFERENCE')),
             component=self._component,
             debug_info=self._build_debug_info(node))
-        return includes
 
     # Helper functions sorted alphabetically
 
@@ -295,14 +297,17 @@
             idl_type = self._take_type(
                 child_nodes, is_optional=is_optional, is_variadic=is_variadic)
             default_value = self._take_default_value(child_nodes)
-            extended_attributes = self._take_extended_attributes(child_nodes)
+            # The parser may place extended attributes on arguments, but they
+            # should be applied to types.
+            # TODO(yukishiino): Move the extended attributes on this argument
+            # into |idl_type|.
+            _ = self._take_extended_attributes(child_nodes)
             assert len(child_nodes) == 0
             return Argument.IR(
                 identifier=Identifier(node.GetName()),
                 index=index,
                 idl_type=idl_type,
-                default_value=default_value,
-                extended_attributes=extended_attributes)
+                default_value=default_value)
 
         assert node.GetClass() == 'Arguments'
         return [
diff --git a/third_party/blink/renderer/bindings/templates/attributes.cc.tmpl b/third_party/blink/renderer/bindings/templates/attributes.cc.tmpl
index dc3f0ed..0130b03 100644
--- a/third_party/blink/renderer/bindings/templates/attributes.cc.tmpl
+++ b/third_party/blink/renderer/bindings/templates/attributes.cc.tmpl
@@ -310,6 +310,7 @@
 
 {##############################################################################}
 {% macro attribute_setter(attribute, world_suffix) %}
+{% if not attribute.use_common_reflection_setter %}
 static void {{attribute.camel_case_name}}AttributeSetter{{world_suffix}}(
     {% if attribute.has_cross_origin_setter %}
     v8::Local<v8::Value> v8_value, const V8CrossOriginSetterInfo& info
@@ -471,6 +472,7 @@
   {% endif %}{# attribute.is_put_forwards #}
   {% endfilter %}{# format_remove_duplicates #}
 }
+{% endif %}
 {% endmacro %}
 
 
@@ -491,7 +493,7 @@
   {{ runtime_timer_scope_disabled_by_default(attribute.runtime_call_stats.setter_counter) }}
   {% endif %}
 
-  {% if not attribute.is_data_type_property %}
+  {% if not attribute.is_data_type_property and not attribute.use_common_reflection_setter %}
   v8::Local<v8::Value> v8_value = info[0];
   {% endif %}
 
@@ -524,6 +526,8 @@
   {% elif attribute.has_cross_origin_setter %}
   {{internal_namespace}}::{{attribute.camel_case_name}}AttributeSetter(
       v8_value, V8CrossOriginSetterInfo(info.GetIsolate(), info.Holder()));
+  {% elif attribute.use_common_reflection_setter %}
+  {{attribute.cpp_setter}};
   {% else %}
   {{internal_namespace}}::{{attribute.camel_case_name}}AttributeSetter{{world_suffix}}(v8_value, info);
   {% endif %}
diff --git a/third_party/blink/renderer/bindings/tests/idls/core/test_element.idl b/third_party/blink/renderer/bindings/tests/idls/core/test_element.idl
new file mode 100644
index 0000000..f96504f
--- /dev/null
+++ b/third_party/blink/renderer/bindings/tests/idls/core/test_element.idl
@@ -0,0 +1,10 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+interface TestElement : Element {
+    [Reflect, CEReactions] attribute boolean reflectedBoolAttribute;
+    [Reflect, CEReactions] attribute DOMString reflectedStringAttribute;
+    [Reflect=rnsa, CEReactions] attribute DOMString? reflectedNullableStringAttribute;
+    [Reflect, RuntimeEnabled=RuntimeFeature, CEReactions] attribute DOMString reflectedRuntimeEnabeld;
+};
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_element.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_element.cc
new file mode 100644
index 0000000..c294578
--- /dev/null
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_element.cc
@@ -0,0 +1,252 @@
+// 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.
+
+// This file has been auto-generated from the Jinja2 template
+// third_party/blink/renderer/bindings/templates/interface.cc.tmpl
+// by the script code_generator_v8.py.
+// DO NOT MODIFY!
+
+// clang-format off
+#include "third_party/blink/renderer/bindings/tests/results/core/v8_test_element.h"
+
+#include <algorithm>
+
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/bindings/core/v8/idl_types.h"
+#include "third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_dom_configuration.h"
+#include "third_party/blink/renderer/core/css/cssom/element_computed_style_map.h"
+#include "third_party/blink/renderer/core/execution_context/execution_context.h"
+#include "third_party/blink/renderer/core/fullscreen/element_fullscreen.h"
+#include "third_party/blink/renderer/core/html/custom/ce_reactions_scope.h"
+#include "third_party/blink/renderer/core/html/custom/v0_custom_element_processing_stack.h"
+#include "third_party/blink/renderer/core/html_names.h"
+#include "third_party/blink/renderer/platform/bindings/exception_messages.h"
+#include "third_party/blink/renderer/platform/bindings/exception_state.h"
+#include "third_party/blink/renderer/platform/bindings/runtime_call_stats.h"
+#include "third_party/blink/renderer/platform/bindings/v8_object_constructor.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/scheduler/public/cooperative_scheduling_manager.h"
+#include "third_party/blink/renderer/platform/wtf/get_ptr.h"
+
+namespace blink {
+
+// Suppress warning: global constructors, because struct WrapperTypeInfo is trivial
+// and does not depend on another global objects.
+#if defined(COMPONENT_BUILD) && defined(WIN32) && defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wglobal-constructors"
+#endif
+const WrapperTypeInfo v8_test_element_wrapper_type_info = {
+    gin::kEmbedderBlink,
+    V8TestElement::DomTemplate,
+    nullptr,
+    "TestElement",
+    V8Element::GetWrapperTypeInfo(),
+    WrapperTypeInfo::kWrapperTypeObjectPrototype,
+    WrapperTypeInfo::kNodeClassId,
+    WrapperTypeInfo::kNotInheritFromActiveScriptWrappable,
+};
+#if defined(COMPONENT_BUILD) && defined(WIN32) && defined(__clang__)
+#pragma clang diagnostic pop
+#endif
+
+// This static member must be declared by DEFINE_WRAPPERTYPEINFO in TestElement.h.
+// For details, see the comment of DEFINE_WRAPPERTYPEINFO in
+// platform/bindings/ScriptWrappable.h.
+const WrapperTypeInfo& TestElement::wrapper_type_info_ = v8_test_element_wrapper_type_info;
+
+// not [ActiveScriptWrappable]
+static_assert(
+    !std::is_base_of<ActiveScriptWrappableBase, TestElement>::value,
+    "TestElement inherits from ActiveScriptWrappable<>, but is not specifying "
+    "[ActiveScriptWrappable] extended attribute in the IDL file.  "
+    "Be consistent.");
+static_assert(
+    std::is_same<decltype(&TestElement::HasPendingActivity),
+                 decltype(&ScriptWrappable::HasPendingActivity)>::value,
+    "TestElement is overriding hasPendingActivity(), but is not specifying "
+    "[ActiveScriptWrappable] extended attribute in the IDL file.  "
+    "Be consistent.");
+
+namespace test_element_v8_internal {
+
+static void ReflectedBoolAttributeAttributeGetter(const v8::FunctionCallbackInfo<v8::Value>& info) {
+  v8::Local<v8::Object> holder = info.Holder();
+
+  TestElement* impl = V8TestElement::ToImpl(holder);
+
+  V8SetReturnValueBool(info, impl->FastHasAttribute(html_names::kReflectedboolattributeAttr));
+}
+
+static void ReflectedStringAttributeAttributeGetter(const v8::FunctionCallbackInfo<v8::Value>& info) {
+  v8::Local<v8::Object> holder = info.Holder();
+
+  TestElement* impl = V8TestElement::ToImpl(holder);
+
+  V8SetReturnValueString(info, impl->FastGetAttribute(html_names::kReflectedstringattributeAttr), info.GetIsolate());
+}
+
+static void ReflectedNullableStringAttributeAttributeGetter(const v8::FunctionCallbackInfo<v8::Value>& info) {
+  v8::Local<v8::Object> holder = info.Holder();
+
+  TestElement* impl = V8TestElement::ToImpl(holder);
+
+  V8SetReturnValueStringOrNull(info, impl->FastGetAttribute(html_names::kRnsaAttr), info.GetIsolate());
+}
+
+static void ReflectedRuntimeEnabeldAttributeGetter(const v8::FunctionCallbackInfo<v8::Value>& info) {
+  v8::Local<v8::Object> holder = info.Holder();
+
+  TestElement* impl = V8TestElement::ToImpl(holder);
+
+  V8SetReturnValueString(info, impl->FastGetAttribute(html_names::kReflectedruntimeenabeldAttr), info.GetIsolate());
+}
+
+}  // namespace test_element_v8_internal
+
+void V8TestElement::ReflectedBoolAttributeAttributeGetterCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
+  RUNTIME_CALL_TIMER_SCOPE_DISABLED_BY_DEFAULT(info.GetIsolate(), "Blink_TestElement_reflectedBoolAttribute_Getter");
+
+  test_element_v8_internal::ReflectedBoolAttributeAttributeGetter(info);
+}
+
+void V8TestElement::ReflectedBoolAttributeAttributeSetterCallback(
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  RUNTIME_CALL_TIMER_SCOPE_DISABLED_BY_DEFAULT(info.GetIsolate(), "Blink_TestElement_reflectedBoolAttribute_Setter");
+
+  V8SetReflectedBooleanAttribute(info, "TestElement", "reflectedBoolAttribute", html_names::kReflectedboolattributeAttr);
+}
+
+void V8TestElement::ReflectedStringAttributeAttributeGetterCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
+  RUNTIME_CALL_TIMER_SCOPE_DISABLED_BY_DEFAULT(info.GetIsolate(), "Blink_TestElement_reflectedStringAttribute_Getter");
+
+  test_element_v8_internal::ReflectedStringAttributeAttributeGetter(info);
+}
+
+void V8TestElement::ReflectedStringAttributeAttributeSetterCallback(
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  RUNTIME_CALL_TIMER_SCOPE_DISABLED_BY_DEFAULT(info.GetIsolate(), "Blink_TestElement_reflectedStringAttribute_Setter");
+
+  V8SetReflectedDOMStringAttribute(info, html_names::kReflectedstringattributeAttr);
+}
+
+void V8TestElement::ReflectedNullableStringAttributeAttributeGetterCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
+  RUNTIME_CALL_TIMER_SCOPE_DISABLED_BY_DEFAULT(info.GetIsolate(), "Blink_TestElement_reflectedNullableStringAttribute_Getter");
+
+  test_element_v8_internal::ReflectedNullableStringAttributeAttributeGetter(info);
+}
+
+void V8TestElement::ReflectedNullableStringAttributeAttributeSetterCallback(
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  RUNTIME_CALL_TIMER_SCOPE_DISABLED_BY_DEFAULT(info.GetIsolate(), "Blink_TestElement_reflectedNullableStringAttribute_Setter");
+
+  V8SetReflectedNullableDOMStringAttribute(info, html_names::kRnsaAttr);
+}
+
+void V8TestElement::ReflectedRuntimeEnabeldAttributeGetterCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
+  RUNTIME_CALL_TIMER_SCOPE_DISABLED_BY_DEFAULT(info.GetIsolate(), "Blink_TestElement_reflectedRuntimeEnabeld_Getter");
+
+  test_element_v8_internal::ReflectedRuntimeEnabeldAttributeGetter(info);
+}
+
+void V8TestElement::ReflectedRuntimeEnabeldAttributeSetterCallback(
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  RUNTIME_CALL_TIMER_SCOPE_DISABLED_BY_DEFAULT(info.GetIsolate(), "Blink_TestElement_reflectedRuntimeEnabeld_Setter");
+
+  V8SetReflectedDOMStringAttribute(info, html_names::kReflectedruntimeenabeldAttr);
+}
+
+static void InstallV8TestElementTemplate(
+    v8::Isolate* isolate,
+    const DOMWrapperWorld& world,
+    v8::Local<v8::FunctionTemplate> interface_template) {
+  // Initialize the interface object's template.
+  V8DOMConfiguration::InitializeDOMInterfaceTemplate(isolate, interface_template, V8TestElement::GetWrapperTypeInfo()->interface_name, V8Element::DomTemplate(isolate, world), V8TestElement::kInternalFieldCount);
+
+  v8::Local<v8::Signature> signature = v8::Signature::New(isolate, interface_template);
+  ALLOW_UNUSED_LOCAL(signature);
+  v8::Local<v8::ObjectTemplate> instance_template = interface_template->InstanceTemplate();
+  ALLOW_UNUSED_LOCAL(instance_template);
+  v8::Local<v8::ObjectTemplate> prototype_template = interface_template->PrototypeTemplate();
+  ALLOW_UNUSED_LOCAL(prototype_template);
+
+  // Register IDL constants, attributes and operations.
+  static constexpr V8DOMConfiguration::AccessorConfiguration
+  kAccessorConfigurations[] = {
+      { "reflectedBoolAttribute", V8TestElement::ReflectedBoolAttributeAttributeGetterCallback, V8TestElement::ReflectedBoolAttributeAttributeSetterCallback, V8PrivateProperty::kNoCachedAccessor, static_cast<v8::PropertyAttribute>(v8::None), V8DOMConfiguration::kOnPrototype, V8DOMConfiguration::kCheckHolder, V8DOMConfiguration::kHasSideEffect, V8DOMConfiguration::kAlwaysCallGetter, V8DOMConfiguration::kAllWorlds },
+      { "reflectedStringAttribute", V8TestElement::ReflectedStringAttributeAttributeGetterCallback, V8TestElement::ReflectedStringAttributeAttributeSetterCallback, V8PrivateProperty::kNoCachedAccessor, static_cast<v8::PropertyAttribute>(v8::None), V8DOMConfiguration::kOnPrototype, V8DOMConfiguration::kCheckHolder, V8DOMConfiguration::kHasSideEffect, V8DOMConfiguration::kAlwaysCallGetter, V8DOMConfiguration::kAllWorlds },
+      { "reflectedNullableStringAttribute", V8TestElement::ReflectedNullableStringAttributeAttributeGetterCallback, V8TestElement::ReflectedNullableStringAttributeAttributeSetterCallback, V8PrivateProperty::kNoCachedAccessor, static_cast<v8::PropertyAttribute>(v8::None), V8DOMConfiguration::kOnPrototype, V8DOMConfiguration::kCheckHolder, V8DOMConfiguration::kHasSideEffect, V8DOMConfiguration::kAlwaysCallGetter, V8DOMConfiguration::kAllWorlds },
+  };
+  V8DOMConfiguration::InstallAccessors(
+      isolate, world, instance_template, prototype_template, interface_template,
+      signature, kAccessorConfigurations,
+      base::size(kAccessorConfigurations));
+
+  // Custom signature
+
+  V8TestElement::InstallRuntimeEnabledFeaturesOnTemplate(
+      isolate, world, interface_template);
+}
+
+void V8TestElement::InstallRuntimeEnabledFeaturesOnTemplate(
+    v8::Isolate* isolate,
+    const DOMWrapperWorld& world,
+    v8::Local<v8::FunctionTemplate> interface_template) {
+  v8::Local<v8::Signature> signature = v8::Signature::New(isolate, interface_template);
+  ALLOW_UNUSED_LOCAL(signature);
+  v8::Local<v8::ObjectTemplate> instance_template = interface_template->InstanceTemplate();
+  ALLOW_UNUSED_LOCAL(instance_template);
+  v8::Local<v8::ObjectTemplate> prototype_template = interface_template->PrototypeTemplate();
+  ALLOW_UNUSED_LOCAL(prototype_template);
+
+  // Register IDL constants, attributes and operations.
+
+  if (RuntimeEnabledFeatures::RuntimeFeatureEnabled()) {
+    static constexpr V8DOMConfiguration::AccessorConfiguration
+    kAccessorConfigurations[] = {
+        { "reflectedRuntimeEnabeld", V8TestElement::ReflectedRuntimeEnabeldAttributeGetterCallback, V8TestElement::ReflectedRuntimeEnabeldAttributeSetterCallback, V8PrivateProperty::kNoCachedAccessor, static_cast<v8::PropertyAttribute>(v8::None), V8DOMConfiguration::kOnPrototype, V8DOMConfiguration::kCheckHolder, V8DOMConfiguration::kHasSideEffect, V8DOMConfiguration::kAlwaysCallGetter, V8DOMConfiguration::kAllWorlds },
+    };
+    V8DOMConfiguration::InstallAccessors(
+        isolate, world, instance_template, prototype_template, interface_template,
+        signature, kAccessorConfigurations,
+        base::size(kAccessorConfigurations));
+  }
+
+  // Custom signature
+}
+
+v8::Local<v8::FunctionTemplate> V8TestElement::DomTemplate(
+    v8::Isolate* isolate, const DOMWrapperWorld& world) {
+  return V8DOMConfiguration::DomClassTemplate(
+      isolate, world, const_cast<WrapperTypeInfo*>(V8TestElement::GetWrapperTypeInfo()),
+      InstallV8TestElementTemplate);
+}
+
+bool V8TestElement::HasInstance(v8::Local<v8::Value> v8_value, v8::Isolate* isolate) {
+  return V8PerIsolateData::From(isolate)->HasInstance(V8TestElement::GetWrapperTypeInfo(), v8_value);
+}
+
+v8::Local<v8::Object> V8TestElement::FindInstanceInPrototypeChain(
+    v8::Local<v8::Value> v8_value, v8::Isolate* isolate) {
+  return V8PerIsolateData::From(isolate)->FindInstanceInPrototypeChain(
+      V8TestElement::GetWrapperTypeInfo(), v8_value);
+}
+
+TestElement* V8TestElement::ToImplWithTypeCheck(
+    v8::Isolate* isolate, v8::Local<v8::Value> value) {
+  return HasInstance(value, isolate) ? ToImpl(v8::Local<v8::Object>::Cast(value)) : nullptr;
+}
+
+TestElement* NativeValueTraits<TestElement>::NativeValue(
+    v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exception_state) {
+  TestElement* native_value = V8TestElement::ToImplWithTypeCheck(isolate, value);
+  if (!native_value) {
+    exception_state.ThrowTypeError(ExceptionMessages::FailedToConvertJSValue(
+        "TestElement"));
+  }
+  return native_value;
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_element.h b/third_party/blink/renderer/bindings/tests/results/core/v8_test_element.h
new file mode 100644
index 0000000..5dcc36c
--- /dev/null
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_element.h
@@ -0,0 +1,77 @@
+// 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.
+
+// This file has been auto-generated from the Jinja2 template
+// third_party/blink/renderer/bindings/templates/interface.h.tmpl
+// by the script code_generator_v8.py.
+// DO NOT MODIFY!
+
+// clang-format off
+#ifndef THIRD_PARTY_BLINK_RENDERER_BINDINGS_TESTS_RESULTS_CORE_V8_TEST_ELEMENT_H_
+#define THIRD_PARTY_BLINK_RENDERER_BINDINGS_TESTS_RESULTS_CORE_V8_TEST_ELEMENT_H_
+
+#include "third_party/blink/renderer/bindings/core/v8/generated_code_helper.h"
+#include "third_party/blink/renderer/bindings/core/v8/native_value_traits.h"
+#include "third_party/blink/renderer/bindings/core/v8/to_v8_for_core.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_element.h"
+#include "third_party/blink/renderer/bindings/tests/idls/core/test_element.h"
+#include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
+#include "third_party/blink/renderer/platform/bindings/v8_dom_wrapper.h"
+#include "third_party/blink/renderer/platform/bindings/wrapper_type_info.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+
+namespace blink {
+
+CORE_EXPORT extern const WrapperTypeInfo v8_test_element_wrapper_type_info;
+
+class V8TestElement {
+  STATIC_ONLY(V8TestElement);
+ public:
+  CORE_EXPORT static bool HasInstance(v8::Local<v8::Value>, v8::Isolate*);
+  static v8::Local<v8::Object> FindInstanceInPrototypeChain(v8::Local<v8::Value>, v8::Isolate*);
+  CORE_EXPORT static v8::Local<v8::FunctionTemplate> DomTemplate(v8::Isolate*, const DOMWrapperWorld&);
+  static TestElement* ToImpl(v8::Local<v8::Object> object) {
+    return ToScriptWrappable(object)->ToImpl<TestElement>();
+  }
+  CORE_EXPORT static TestElement* ToImplWithTypeCheck(v8::Isolate*, v8::Local<v8::Value>);
+
+  CORE_EXPORT static constexpr const WrapperTypeInfo* GetWrapperTypeInfo() {
+    return &v8_test_element_wrapper_type_info;
+  }
+
+  static constexpr int kInternalFieldCount = kV8DefaultWrapperInternalFieldCount;
+
+  // Callback functions
+
+  CORE_EXPORT static void ReflectedBoolAttributeAttributeGetterCallback(const v8::FunctionCallbackInfo<v8::Value>&);
+  CORE_EXPORT static void ReflectedBoolAttributeAttributeSetterCallback(const v8::FunctionCallbackInfo<v8::Value>&);
+  CORE_EXPORT static void ReflectedStringAttributeAttributeGetterCallback(const v8::FunctionCallbackInfo<v8::Value>&);
+  CORE_EXPORT static void ReflectedStringAttributeAttributeSetterCallback(const v8::FunctionCallbackInfo<v8::Value>&);
+  CORE_EXPORT static void ReflectedNullableStringAttributeAttributeGetterCallback(const v8::FunctionCallbackInfo<v8::Value>&);
+  CORE_EXPORT static void ReflectedNullableStringAttributeAttributeSetterCallback(const v8::FunctionCallbackInfo<v8::Value>&);
+  CORE_EXPORT static void ReflectedRuntimeEnabeldAttributeGetterCallback(const v8::FunctionCallbackInfo<v8::Value>&);
+  CORE_EXPORT static void ReflectedRuntimeEnabeldAttributeSetterCallback(const v8::FunctionCallbackInfo<v8::Value>&);
+
+  static void InstallRuntimeEnabledFeaturesOnTemplate(
+      v8::Isolate*,
+      const DOMWrapperWorld&,
+      v8::Local<v8::FunctionTemplate> interface_template);
+};
+
+template <>
+struct NativeValueTraits<TestElement> : public NativeValueTraitsBase<TestElement> {
+  CORE_EXPORT static TestElement* NativeValue(v8::Isolate*, v8::Local<v8::Value>, ExceptionState&);
+  CORE_EXPORT static TestElement* NullValue() { return nullptr; }
+};
+
+template <>
+struct V8TypeOf<TestElement> {
+  typedef V8TestElement Type;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_BINDINGS_TESTS_RESULTS_CORE_V8_TEST_ELEMENT_H_
diff --git a/third_party/blink/renderer/core/aom/accessible_node.cc b/third_party/blink/renderer/core/aom/accessible_node.cc
index 2e1576d..aea0ff2 100644
--- a/third_party/blink/renderer/core/aom/accessible_node.cc
+++ b/third_party/blink/renderer/core/aom/accessible_node.cc
@@ -10,6 +10,7 @@
 #include "third_party/blink/renderer/core/dom/qualified_name.h"
 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
 #include "third_party/blink/renderer/core/frame/settings.h"
+#include "third_party/blink/renderer/core/html/custom/element_internals.h"
 #include "third_party/blink/renderer/core/page/page.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 #include "third_party/blink/renderer/platform/weborigin/security_origin.h"
@@ -366,6 +367,23 @@
 }
 
 // static
+const AtomicString& AccessibleNode::GetElementOrInternalsARIAAttribute(
+    Element& element,
+    const QualifiedName& attribute,
+    bool is_token_attr) {
+  const AtomicString& attr_value = element.FastGetAttribute(attribute);
+  if ((attr_value != g_null_atom) &&
+      (!is_token_attr || !IsUndefinedAttrValue(attr_value))) {
+    return attr_value;
+  }
+
+  if (!element.DidAttachInternals())
+    return g_null_atom;
+
+  return element.EnsureElementInternals().FastGetAttribute(attribute);
+}
+
+// static
 const AtomicString& AccessibleNode::GetPropertyOrARIAAttribute(
     Element* element,
     AOMStringProperty property) {
@@ -377,7 +395,8 @@
   // We are currently only checking ARIA attributes, instead of AccessibleNode
   // properties. Further refactoring will be happening as the API is finalised.
   QualifiedName attribute = GetCorrespondingARIAAttribute(property);
-  const AtomicString& attr_value = element->FastGetAttribute(attribute);
+  const AtomicString& attr_value =
+      GetElementOrInternalsARIAAttribute(*element, attribute, is_token_attr);
   if (is_token_attr && IsUndefinedAttrValue(attr_value))
     return g_null_atom;  // Attribute not set or explicitly undefined.
 
@@ -391,7 +410,7 @@
   if (!element)
     return nullptr;
   QualifiedName attribute = GetCorrespondingARIAAttribute(property);
-  AtomicString value = element->FastGetAttribute(attribute);
+  AtomicString value = GetElementOrInternalsARIAAttribute(*element, attribute);
   return element->GetTreeScope().getElementById(value);
 }
 
@@ -403,9 +422,12 @@
   if (!element)
     return false;
   QualifiedName attribute = GetCorrespondingARIAAttribute(property);
-  String value = element->FastGetAttribute(attribute).GetString();
-  if (value.IsEmpty() && property == AOMRelationListProperty::kLabeledBy)
-    value = element->FastGetAttribute(kAriaLabeledbyAttr).GetString();
+  String value =
+      GetElementOrInternalsARIAAttribute(*element, attribute).GetString();
+  if (value.IsEmpty() && property == AOMRelationListProperty::kLabeledBy) {
+    value = GetElementOrInternalsARIAAttribute(*element, kAriaLabeledbyAttr)
+                .GetString();
+  }
   if (value.IsEmpty())
     return false;
 
@@ -432,7 +454,8 @@
 
   // Fall back on the equivalent ARIA attribute.
   QualifiedName attribute = GetCorrespondingARIAAttribute(property);
-  AtomicString attr_value = element->FastGetAttribute(attribute);
+  AtomicString attr_value =
+      GetElementOrInternalsARIAAttribute(*element, attribute);
   is_null = IsUndefinedAttrValue(attr_value);
   return !is_null && !EqualIgnoringASCIICase(attr_value, "false");
 }
@@ -447,7 +470,8 @@
 
   // Fall back on the equivalent ARIA attribute.
   QualifiedName attribute = GetCorrespondingARIAAttribute(property);
-  AtomicString attr_value = element->FastGetAttribute(attribute);
+  AtomicString attr_value =
+      GetElementOrInternalsARIAAttribute(*element, attribute);
   is_null = attr_value.IsNull();
   return attr_value.ToFloat();
 }
@@ -462,7 +486,8 @@
 
   // Fall back on the equivalent ARIA attribute.
   QualifiedName attribute = GetCorrespondingARIAAttribute(property);
-  AtomicString attr_value = element->FastGetAttribute(attribute);
+  AtomicString attr_value =
+      GetElementOrInternalsARIAAttribute(*element, attribute);
   is_null = attr_value.IsNull();
   return attr_value.GetString().ToUInt();
 }
@@ -477,7 +502,8 @@
 
   // Fall back on the equivalent ARIA attribute.
   QualifiedName attribute = GetCorrespondingARIAAttribute(property);
-  AtomicString attr_value = element->FastGetAttribute(attribute);
+  AtomicString attr_value =
+      GetElementOrInternalsARIAAttribute(*element, attribute);
   is_null = attr_value.IsNull();
   return attr_value.ToInt();
 }
@@ -1146,18 +1172,6 @@
   // By definition, any attribute on an AccessibleNode is interesting to
   // AXObjectCache, so no need to check return value.
   cache->HandleAttributeChanged(attribute, element_);
-
-  auto* page = GetDocument()->GetPage();
-  auto* view = GetDocument()->View();
-  if (!page || !view)
-    return;
-
-  // TODO(aboxhall): add a lifecycle phase for accessibility updates.
-  if (!view->CanThrottleRendering())
-    page->Animator().ScheduleVisualUpdate(GetDocument()->GetFrame());
-
-  GetDocument()->Lifecycle().EnsureStateAtMost(
-      DocumentLifecycle::kVisualUpdatePending);
 }
 
 AXObjectCache* AccessibleNode::GetAXObjectCache() {
diff --git a/third_party/blink/renderer/core/aom/accessible_node.h b/third_party/blink/renderer/core/aom/accessible_node.h
index 477b3e9..c8271ce 100644
--- a/third_party/blink/renderer/core/aom/accessible_node.h
+++ b/third_party/blink/renderer/core/aom/accessible_node.h
@@ -363,6 +363,10 @@
 
  private:
   static bool IsStringTokenProperty(AOMStringProperty);
+  static const AtomicString& GetElementOrInternalsARIAAttribute(
+      Element& element,
+      const QualifiedName& attribute,
+      bool is_token_attr = false);
   void SetStringProperty(AOMStringProperty, const AtomicString&);
   void SetRelationProperty(AOMRelationProperty, AccessibleNode*);
   void SetRelationListProperty(AOMRelationListProperty, AccessibleNodeList*);
diff --git a/third_party/blink/renderer/core/css/resolver/style_adjuster.cc b/third_party/blink/renderer/core/css/resolver/style_adjuster.cc
index 4a6b0193..60824d0 100644
--- a/third_party/blink/renderer/core/css/resolver/style_adjuster.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_adjuster.cc
@@ -229,7 +229,7 @@
     return;
   }
 
-  if (IsHTMLFrameElement(element) || IsHTMLFrameSetElement(element)) {
+  if (IsA<HTMLFrameElement>(element) || IsHTMLFrameSetElement(element)) {
     // Frames and framesets never honor position:relative or position:absolute.
     // This is necessary to fix a crash where a site tries to position these
     // objects. They also never honor display nor floating.
diff --git a/third_party/blink/renderer/core/dom/accessibility_role.idl b/third_party/blink/renderer/core/dom/accessibility_role.idl
index 3685a82..e185b19 100644
--- a/third_party/blink/renderer/core/dom/accessibility_role.idl
+++ b/third_party/blink/renderer/core/dom/accessibility_role.idl
@@ -11,3 +11,4 @@
 };
 
 Element includes AccessibilityRole;
+ElementInternals includes AccessibilityRole;
diff --git a/third_party/blink/renderer/core/dom/aria_attributes.idl b/third_party/blink/renderer/core/dom/aria_attributes.idl
index d0ad259..985aa16 100644
--- a/third_party/blink/renderer/core/dom/aria_attributes.idl
+++ b/third_party/blink/renderer/core/dom/aria_attributes.idl
@@ -54,3 +54,4 @@
 };
 
 Element includes AriaAttributes;
+ElementInternals includes AriaAttributes;
diff --git a/third_party/blink/renderer/core/dom/container_node.cc b/third_party/blink/renderer/core/dom/container_node.cc
index 59e200ee..c146cec 100644
--- a/third_party/blink/renderer/core/dom/container_node.cc
+++ b/third_party/blink/renderer/core/dom/container_node.cc
@@ -1553,7 +1553,7 @@
 
 RadioNodeList* ContainerNode::GetRadioNodeList(const AtomicString& name,
                                                bool only_match_img_elements) {
-  DCHECK(IsHTMLFormElement(this) || IsA<HTMLFieldSetElement>(this));
+  DCHECK(IsA<HTMLFormElement>(this) || IsA<HTMLFieldSetElement>(this));
   CollectionType type =
       only_match_img_elements ? kRadioImgNodeListType : kRadioNodeListType;
   return EnsureCachedCollection<RadioNodeList>(type, name);
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc
index d71cc273..ceda072 100644
--- a/third_party/blink/renderer/core/dom/element.cc
+++ b/third_party/blink/renderer/core/dom/element.cc
@@ -1918,22 +1918,8 @@
 
   if (isConnected()) {
     if (AXObjectCache* cache = GetDocument().ExistingAXObjectCache()) {
-      if (params.old_value != params.new_value) {
-        auto* page = GetDocument().GetPage();
-        auto* view = GetDocument().View();
-        // If this attribute is interesting for accessibility (e.g. `role` or
-        // `alt`), but doesn't trigger a lifecycle update on its own
-        // (e.g. because it doesn't make layout dirty), make sure we run
-        // lifecycle phases to update the computed accessibility tree.
-        if (cache->HandleAttributeChanged(name, this) && page && view) {
-          if (!view->CanThrottleRendering())
-            page->Animator().ScheduleVisualUpdate(GetDocument().GetFrame());
-
-          // TODO(aboxhall): add a lifecycle phase for accessibility updates.
-          GetDocument().Lifecycle().EnsureStateAtMost(
-              DocumentLifecycle::kVisualUpdatePending);
-        }
-      }
+      if (params.old_value != params.new_value)
+        cache->HandleAttributeChanged(name, this);
     }
   }
 
diff --git a/third_party/blink/renderer/core/exported/web_document.cc b/third_party/blink/renderer/core/exported/web_document.cc
index 281fdefc..f90d5653 100644
--- a/third_party/blink/renderer/core/exported/web_document.cc
+++ b/third_party/blink/renderer/core/exported/web_document.cc
@@ -186,8 +186,8 @@
   for (size_t i = 0; i < source_length; ++i) {
     Element* element = forms->item(i);
     // Strange but true, sometimes node can be 0.
-    if (element && element->IsHTMLElement())
-      temp.push_back(WebFormElement(ToHTMLFormElement(element)));
+    if (auto* html_form_element = DynamicTo<HTMLFormElement>(element))
+      temp.push_back(WebFormElement(html_form_element));
   }
   results.Assign(temp);
 }
diff --git a/third_party/blink/renderer/core/exported/web_form_element.cc b/third_party/blink/renderer/core/exported/web_form_element.cc
index d6f8ba5a..a86727cd 100644
--- a/third_party/blink/renderer/core/exported/web_form_element.cc
+++ b/third_party/blink/renderer/core/exported/web_form_element.cc
@@ -87,7 +87,7 @@
 }
 
 WebFormElement::operator HTMLFormElement*() const {
-  return ToHTMLFormElement(private_.Get());
+  return blink::To<HTMLFormElement>(private_.Get());
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/html/custom/element_internals.cc b/third_party/blink/renderer/core/html/custom/element_internals.cc
index 44a00cf..527a58f1 100644
--- a/third_party/blink/renderer/core/html/custom/element_internals.cc
+++ b/third_party/blink/renderer/core/html/custom/element_internals.cc
@@ -4,6 +4,7 @@
 
 #include "third_party/blink/renderer/core/html/custom/element_internals.h"
 
+#include "third_party/blink/renderer/core/accessibility/ax_object_cache.h"
 #include "third_party/blink/renderer/core/dom/node_lists_node_data.h"
 #include "third_party/blink/renderer/core/fileapi/file.h"
 #include "third_party/blink/renderer/core/html/custom/custom_element.h"
@@ -211,6 +212,27 @@
   return Target().labels();
 }
 
+const AtomicString& ElementInternals::FastGetAttribute(
+    const QualifiedName& attribute) const {
+  return accessibility_semantics_map_.at(attribute);
+}
+
+const HashMap<QualifiedName, AtomicString>& ElementInternals::GetAttributes()
+    const {
+  return accessibility_semantics_map_;
+}
+
+void ElementInternals::setAttribute(const QualifiedName& attribute,
+                                    const AtomicString& value) {
+  accessibility_semantics_map_.Set(attribute, value);
+  if (AXObjectCache* cache = Target().GetDocument().ExistingAXObjectCache())
+    cache->HandleAttributeChanged(attribute, &Target());
+}
+
+bool ElementInternals::HasAttribute(const QualifiedName& attribute) const {
+  return accessibility_semantics_map_.Contains(attribute);
+}
+
 void ElementInternals::DidUpgrade() {
   ContainerNode* parent = Target().parentNode();
   if (!parent)
diff --git a/third_party/blink/renderer/core/html/custom/element_internals.h b/third_party/blink/renderer/core/html/custom/element_internals.h
index 2000c55..abde18f 100644
--- a/third_party/blink/renderer/core/html/custom/element_internals.h
+++ b/third_party/blink/renderer/core/html/custom/element_internals.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_HTML_CUSTOM_ELEMENT_INTERNALS_H_
 
 #include "third_party/blink/renderer/bindings/core/v8/file_or_usv_string_or_form_data.h"
+#include "third_party/blink/renderer/core/dom/qualified_name.h"
 #include "third_party/blink/renderer/core/html/forms/listed_element.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
 #include "third_party/blink/renderer/platform/wtf/casting.h"
@@ -16,7 +17,8 @@
 class LabelsNodeList;
 class ValidityStateFlags;
 
-class ElementInternals : public ScriptWrappable, public ListedElement {
+class CORE_EXPORT ElementInternals : public ScriptWrappable,
+                                     public ListedElement {
   DEFINE_WRAPPERTYPEINFO();
   USING_GARBAGE_COLLECTED_MIXIN(ElementInternals);
 
@@ -49,6 +51,14 @@
   bool reportValidity(ExceptionState& exception_state);
   LabelsNodeList* labels(ExceptionState& exception_state);
 
+  // We need these functions because we are reflecting ARIA attributes.
+  // See dom/aria_attributes.idl.
+  const AtomicString& FastGetAttribute(const QualifiedName&) const;
+  void setAttribute(const QualifiedName& attribute, const AtomicString& value);
+
+  bool HasAttribute(const QualifiedName& attribute) const;
+  const HashMap<QualifiedName, AtomicString>& GetAttributes() const;
+
  private:
   bool IsTargetFormAssociated() const;
 
@@ -84,6 +94,7 @@
   bool is_disabled_ = false;
   Member<ValidityStateFlags> validity_flags_;
   Member<Element> validation_anchor_;
+  HashMap<QualifiedName, AtomicString> accessibility_semantics_map_;
 
   DISALLOW_COPY_AND_ASSIGN(ElementInternals);
 };
diff --git a/third_party/blink/renderer/core/html/document_name_collection.cc b/third_party/blink/renderer/core/html/document_name_collection.cc
index 44224681..cd10e2e 100644
--- a/third_party/blink/renderer/core/html/document_name_collection.cc
+++ b/third_party/blink/renderer/core/html/document_name_collection.cc
@@ -25,7 +25,7 @@
   // Match images, forms, embeds, objects and iframes by name,
   // object by id, and images by id but only if they have
   // a name attribute (this very strange rule matches IE)
-  if (IsHTMLFormElement(element) || IsHTMLIFrameElement(element) ||
+  if (IsA<HTMLFormElement>(element) || IsHTMLIFrameElement(element) ||
       (IsHTMLEmbedElement(element) && ToHTMLEmbedElement(element).IsExposed()))
     return element.GetNameAttribute() == name_;
   if (IsHTMLObjectElement(element) && ToHTMLObjectElement(element).IsExposed())
diff --git a/third_party/blink/renderer/core/html/forms/form_controller_test.cc b/third_party/blink/renderer/core/html/forms/form_controller_test.cc
index ed04ac88..dddbe3c7d 100644
--- a/third_party/blink/renderer/core/html/forms/form_controller_test.cc
+++ b/third_party/blink/renderer/core/html/forms/form_controller_test.cc
@@ -51,7 +51,7 @@
   Element* form = doc.QuerySelector("form", ASSERT_NO_EXCEPTION);
   ASSERT_TRUE(form);
   EXPECT_EQ(String("http://example.com/ [1cb 3s ]"),
-            FormSignature(*ToHTMLFormElement(form)))
+            FormSignature(*To<HTMLFormElement>(form)))
       << "[] should contain names of the first and the third controls.";
 }
 
diff --git a/third_party/blink/renderer/core/html/forms/html_form_controls_collection.cc b/third_party/blink/renderer/core/html/forms/html_form_controls_collection.cc
index 10d02d0..39908f4 100644
--- a/third_party/blink/renderer/core/html/forms/html_form_controls_collection.cc
+++ b/third_party/blink/renderer/core/html/forms/html_form_controls_collection.cc
@@ -43,7 +43,7 @@
     : HTMLCollection(owner_node, kFormControls, kOverridesItemAfter),
       cached_element_(nullptr),
       cached_element_offset_in_array_(0) {
-  DCHECK(IsHTMLFormElement(owner_node));
+  DCHECK(IsA<HTMLFormElement>(owner_node));
 }
 
 HTMLFormControlsCollection::HTMLFormControlsCollection(
@@ -56,12 +56,12 @@
 HTMLFormControlsCollection::~HTMLFormControlsCollection() = default;
 
 const ListedElement::List& HTMLFormControlsCollection::ListedElements() const {
-  return ToHTMLFormElement(ownerNode()).ListedElements();
+  return To<HTMLFormElement>(ownerNode()).ListedElements();
 }
 
 const HeapVector<Member<HTMLImageElement>>&
 HTMLFormControlsCollection::FormImageElements() const {
-  return ToHTMLFormElement(ownerNode()).ImageElements();
+  return To<HTMLFormElement>(ownerNode()).ImageElements();
 }
 
 static unsigned FindListedElement(const ListedElement::List& listed_elements,
diff --git a/third_party/blink/renderer/core/html/forms/html_form_element_test.cc b/third_party/blink/renderer/core/html/forms/html_form_element_test.cc
index a9ce244e..1f79394e 100644
--- a/third_party/blink/renderer/core/html/forms/html_form_element_test.cc
+++ b/third_party/blink/renderer/core/html/forms/html_form_element_test.cc
@@ -22,12 +22,12 @@
 TEST_F(HTMLFormElementTest, UniqueRendererFormId) {
   SetHtmlInnerHTML(
       "<body><form id='form1'></form><form id='form2'></form></body>");
-  auto* form1 = ToHTMLFormElement(GetElementById("form1"));
+  auto* form1 = To<HTMLFormElement>(GetElementById("form1"));
   unsigned first_id = form1->UniqueRendererFormId();
-  auto* form2 = ToHTMLFormElement(GetElementById("form2"));
+  auto* form2 = To<HTMLFormElement>(GetElementById("form2"));
   EXPECT_EQ(first_id + 1, form2->UniqueRendererFormId());
   SetHtmlInnerHTML("<body><form id='form3'></form></body>");
-  auto* form3 = ToHTMLFormElement(GetElementById("form3"));
+  auto* form3 = To<HTMLFormElement>(GetElementById("form3"));
   EXPECT_EQ(first_id + 2, form3->UniqueRendererFormId());
 }
 
diff --git a/third_party/blink/renderer/core/html/forms/listed_element.cc b/third_party/blink/renderer/core/html/forms/listed_element.cc
index 2cfe9d3..72ea666 100644
--- a/third_party/blink/renderer/core/html/forms/listed_element.cc
+++ b/third_party/blink/renderer/core/html/forms/listed_element.cc
@@ -172,7 +172,7 @@
     // 3.2. Abort the "reset the form owner" steps.
     Element* new_form_candidate =
         element->GetTreeScope().getElementById(form_id);
-    return ToHTMLFormElementOrNull(new_form_candidate);
+    return DynamicTo<HTMLFormElement>(new_form_candidate);
   }
   // 4. Otherwise, if the form-associated element in question has an ancestor
   // form element, then associate the form-associated element with the nearest
diff --git a/third_party/blink/renderer/core/html/forms/radio_node_list.cc b/third_party/blink/renderer/core/html/forms/radio_node_list.cc
index 4044004..99e3c19d 100644
--- a/third_party/blink/renderer/core/html/forms/radio_node_list.cc
+++ b/third_party/blink/renderer/core/html/forms/radio_node_list.cc
@@ -42,7 +42,7 @@
     : LiveNodeList(owner_node,
                    type,
                    kInvalidateForFormControls,
-                   IsHTMLFormElement(owner_node)
+                   IsA<HTMLFormElement>(owner_node)
                        ? NodeListSearchRoot::kTreeScope
                        : NodeListSearchRoot::kOwnerNode),
       name_(name) {
@@ -97,7 +97,7 @@
   DCHECK(!ShouldOnlyMatchImgElements());
   DCHECK(IsHTMLObjectElement(test_element) ||
          test_element.IsFormControlElement());
-  if (IsHTMLFormElement(ownerNode())) {
+  if (IsA<HTMLFormElement>(ownerNode())) {
     auto* form_element = To<HTMLElement>(test_element).formOwner();
     if (!form_element || form_element != ownerNode())
       return false;
diff --git a/third_party/blink/renderer/core/html/html_collection.cc b/third_party/blink/renderer/core/html/html_collection.cc
index 445dca257..6072f639 100644
--- a/third_party/blink/renderer/core/html/html_collection.cc
+++ b/third_party/blink/renderer/core/html/html_collection.cc
@@ -115,7 +115,7 @@
     case kFormControls:
       if (IsA<HTMLFieldSetElement>(owner))
         return NodeListSearchRoot::kOwnerNode;
-      DCHECK(IsHTMLFormElement(owner));
+      DCHECK(IsA<HTMLFormElement>(owner));
       return NodeListSearchRoot::kTreeScope;
     case kNameNodeListType:
     case kRadioNodeListType:
diff --git a/third_party/blink/renderer/core/html/html_frame_element_base.h b/third_party/blink/renderer/core/html/html_frame_element_base.h
index cfe6e81..129eef82 100644
--- a/third_party/blink/renderer/core/html/html_frame_element_base.h
+++ b/third_party/blink/renderer/core/html/html_frame_element_base.h
@@ -88,7 +88,7 @@
 };
 
 inline bool IsHTMLFrameElementBase(const HTMLElement& element) {
-  return IsHTMLFrameElement(element) || IsHTMLIFrameElement(element);
+  return IsA<HTMLFrameElement>(element) || IsHTMLIFrameElement(element);
 }
 
 DEFINE_HTMLELEMENT_TYPE_CASTS_WITH_FUNCTION(HTMLFrameElementBase);
diff --git a/third_party/blink/renderer/core/html/parser/html_construction_site.cc b/third_party/blink/renderer/core/html/parser/html_construction_site.cc
index eb5b858..ba1023e 100644
--- a/third_party/blink/renderer/core/html/parser/html_construction_site.cc
+++ b/third_party/blink/renderer/core/html/parser/html_construction_site.cc
@@ -689,7 +689,7 @@
 void HTMLConstructionSite::InsertHTMLFormElement(AtomicHTMLToken* token,
                                                  bool is_demoted) {
   auto* form_element =
-      ToHTMLFormElement(CreateElement(token, xhtmlNamespaceURI));
+      To<HTMLFormElement>(CreateElement(token, xhtmlNamespaceURI));
   if (!OpenElements()->HasTemplateInHTMLScope())
     form_ = form_element;
   if (is_demoted) {
diff --git a/third_party/blink/renderer/core/html/window_name_collection.cc b/third_party/blink/renderer/core/html/window_name_collection.cc
index 9bb81c33..d43990f 100644
--- a/third_party/blink/renderer/core/html/window_name_collection.cc
+++ b/third_party/blink/renderer/core/html/window_name_collection.cc
@@ -22,7 +22,7 @@
 bool WindowNameCollection::ElementMatches(const Element& element) const {
   // Match only images, forms, embeds and objects by name,
   // but anything by id
-  if (IsHTMLImageElement(element) || IsHTMLFormElement(element) ||
+  if (IsHTMLImageElement(element) || IsA<HTMLFormElement>(element) ||
       IsHTMLEmbedElement(element) || IsHTMLObjectElement(element)) {
     if (element.GetNameAttribute() == name_)
       return true;
diff --git a/third_party/blink/renderer/core/inspector/browser_protocol.pdl b/third_party/blink/renderer/core/inspector/browser_protocol.pdl
index 68dc591..0638975 100644
--- a/third_party/blink/renderer/core/inspector/browser_protocol.pdl
+++ b/third_party/blink/renderer/core/inspector/browser_protocol.pdl
@@ -7035,8 +7035,8 @@
 # https://webaudio.github.io/web-audio-api/
 experimental domain WebAudio
 
-  # Context's UUID in string
-  type ContextId extends string
+  # An unique ID for a graph object (AudioContext, AudioNode, AudioParam) in Web Audio API
+  type GraphObjectId extends string
 
   # Enum of BaseAudioContext types
   type ContextType extends string
@@ -7051,6 +7051,31 @@
       running
       closed
 
+  # Enum of AudioNode types
+  type NodeType extends string
+
+  # Enum of AudioNode::ChannelCountMode from the spec
+  type ChannelCountMode extends string
+    enum
+      clamped-max
+      explicit
+      max
+
+  # Enum of AudioNode::ChannelInterpretation from the spec
+  type ChannelInterpretation extends string
+    enum
+      discrete
+      speakers
+
+  # Enum of AudioParam types
+  type ParamType extends string
+
+  # Enum of AudioParam::AutomationRate from the spec
+  type AutomationRate extends string
+    enum
+      a-rate
+      k-rate
+
   # Fields in AudioContext that change in real-time.
   type ContextRealtimeData extends object
     properties
@@ -7068,7 +7093,7 @@
   # Protocol object for BaseAudioContext
   type BaseAudioContext extends object
     properties
-      ContextId contextId
+      GraphObjectId contextId
       ContextType contextType
       ContextState contextState
       optional ContextRealtimeData realtimeData
@@ -7079,6 +7104,36 @@
       # Context sample rate.
       number sampleRate
 
+# Protocol object for AudioListner
+  type AudioListener extends object
+    properties
+      GraphObjectId listenerId
+      GraphObjectId contextId
+
+  # Protocol object for AudioNode
+  type AudioNode extends object
+    properties
+      GraphObjectId nodeId
+      GraphObjectId contextId
+      NodeType nodeType
+      number numberOfInputs
+      number numberOfOutputs
+      number channelCount
+      ChannelCountMode channelCountMode
+      ChannelInterpretation channelInterpretation
+
+  # Protocol object for AudioParam
+  type AudioParam extends object
+    properties
+      GraphObjectId paramId
+      GraphObjectId nodeId
+      GraphObjectId contextId
+      ParamType paramType
+      AutomationRate rate
+      number defaultValue
+      number minValue
+      number maxValue
+
   # Enables the WebAudio domain and starts sending context lifetime events.
   command enable
 
@@ -7088,7 +7143,7 @@
   # Fetch the realtime data from the registered contexts.
   command getRealtimeData
     parameters
-      ContextId contextId
+      GraphObjectId contextId
     returns
       ContextRealtimeData realtimeData
 
@@ -7100,13 +7155,81 @@
   # Notifies that an existing BaseAudioContext will be destroyed.
   event contextWillBeDestroyed
     parameters
-      ContextId contextId
+      GraphObjectId contextId
 
   # Notifies that existing BaseAudioContext has changed some properties (id stays the same)..
   event contextChanged
     parameters
       BaseAudioContext context
 
+# Notifies that the construction of an AudioListener has finished.
+  event audioListenerCreated
+    parameters
+      AudioListener listener
+
+  # Notifies that a new AudioListener has been created.
+  event audioListenerWillBeDestroyed
+    parameters
+      GraphObjectId contextId
+      GraphObjectId listenerId
+
+  # Notifies that a new AudioNode has been created.
+  event audioNodeCreated
+    parameters
+      AudioNode node
+
+  # Notifies that an existing AudioNode has been destroyed.
+  event audioNodeWillBeDestroyed
+    parameters
+      GraphObjectId contextId
+      GraphObjectId nodeId
+
+  # Notifies that a new AudioParam has been created.
+  event audioParamCreated
+    parameters
+      AudioParam param
+
+  # Notifies that an existing AudioParam has been destroyed.
+  event audioParamWillBeDestroyed
+    parameters
+      GraphObjectId contextId
+      GraphObjectId nodeId
+      GraphObjectId paramId
+
+  # Notifies that two AudioNodes are connected.
+  event nodesConnected
+    parameters
+      GraphObjectId contextId
+      GraphObjectId sourceId
+      GraphObjectId destinationId
+      optional number sourceOutputIndex
+      optional number destinationInputIndex
+
+  # Notifies that AudioNodes are disconnected. The destination can be null, and it means all the outgoing connections from the source are disconnected.
+  event nodesDisconnected
+    parameters
+      GraphObjectId contextId
+      GraphObjectId sourceId
+      GraphObjectId destinationId
+      optional number sourceOutputIndex
+      optional number destinationInputIndex
+
+  # Notifies that an AudioNode is connected to an AudioParam.
+  event nodeParamConnected
+    parameters
+      GraphObjectId contextId
+      GraphObjectId sourceId
+      GraphObjectId destinationId
+      optional number sourceOutputIndex
+
+  # Notifies that an AudioNode is disconnected to an AudioParam.
+  event nodeParamDisconnected
+    parameters
+      GraphObjectId contextId
+      GraphObjectId sourceId
+      GraphObjectId destinationId
+      optional number sourceOutputIndex
+
 # This domain allows configuring virtual authenticators to test the WebAuthn
 # API.
 experimental domain WebAuthn
diff --git a/third_party/blink/renderer/core/inspector/inspect_tools.cc b/third_party/blink/renderer/core/inspector/inspect_tools.cc
index 9adeb94..a0f0465 100644
--- a/third_party/blink/renderer/core/inspector/inspect_tools.cc
+++ b/third_party/blink/renderer/core/inspector/inspect_tools.cc
@@ -9,6 +9,7 @@
 #include "third_party/blink/public/platform/web_input_event_result.h"
 #include "third_party/blink/public/platform/web_keyboard_event.h"
 #include "third_party/blink/public/platform/web_pointer_event.h"
+#include "third_party/blink/public/resources/grit/blink_resources.h"
 #include "third_party/blink/renderer/core/css/css_color_value.h"
 #include "third_party/blink/renderer/core/css/css_computed_style_declaration.h"
 #include "third_party/blink/renderer/core/display_lock/display_lock_utilities.h"
@@ -343,8 +344,8 @@
 
 // NearbyDistanceTool ----------------------------------------------------------
 
-String NearbyDistanceTool::GetDataResourceName() {
-  return String("inspect_tool_distances.html");
+int NearbyDistanceTool::GetDataResourceId() {
+  return IDR_INSPECT_TOOL_DISTANCES_HTML;
 }
 
 bool NearbyDistanceTool::HandleMouseDown(const WebMouseEvent& event,
@@ -413,8 +414,8 @@
   overlay_->EvaluateInOverlay("drawViewSize", "");
 }
 
-String ShowViewSizeTool::GetDataResourceName() {
-  return String("inspect_tool_viewport_size.html");
+int ShowViewSizeTool::GetDataResourceId() {
+  return IDR_INSPECT_TOOL_VIEWPORT_SIZE_HTML;
 }
 
 bool ShowViewSizeTool::ForwardEventsToOverlay() {
@@ -430,8 +431,8 @@
   client.SetCursorOverridden(true);
 }
 
-String ScreenshotTool::GetDataResourceName() {
-  return String("inspect_tool_screenshot.html");
+int ScreenshotTool::GetDataResourceId() {
+  return IDR_INSPECT_TOOL_SCREENSHOT_HTML;
 }
 
 void ScreenshotTool::Dispatch(const String& message) {
@@ -500,8 +501,8 @@
 
 // PausedInDebuggerTool --------------------------------------------------------
 
-String PausedInDebuggerTool::GetDataResourceName() {
-  return String("inspect_tool_paused.html");
+int PausedInDebuggerTool::GetDataResourceId() {
+  return IDR_INSPECT_TOOL_PAUSED_HTML;
 }
 
 void PausedInDebuggerTool::Draw(float scale) {
diff --git a/third_party/blink/renderer/core/inspector/inspect_tools.h b/third_party/blink/renderer/core/inspector/inspect_tools.h
index ede4dec..34d440c 100644
--- a/third_party/blink/renderer/core/inspector/inspect_tools.h
+++ b/third_party/blink/renderer/core/inspector/inspect_tools.h
@@ -96,7 +96,7 @@
   NearbyDistanceTool() = default;
 
  private:
-  String GetDataResourceName() override;
+  int GetDataResourceId() override;
   bool HandleMouseDown(const WebMouseEvent& event,
                        bool* swallow_next_mouse_up) override;
   bool HandleMouseMove(const WebMouseEvent& event) override;
@@ -116,7 +116,7 @@
 
  private:
   bool ForwardEventsToOverlay() override;
-  String GetDataResourceName() override;
+  int GetDataResourceId() override;
   void Draw(float scale) override;
   DISALLOW_COPY_AND_ASSIGN(ShowViewSizeTool);
 };
@@ -128,7 +128,7 @@
   ScreenshotTool() = default;
 
  private:
-  String GetDataResourceName() override;
+  int GetDataResourceId() override;
   void DoInit() override;
   void Dispatch(const String& message) override;
 
@@ -144,7 +144,7 @@
       : v8_session_(v8_session), message_(message) {}
 
  private:
-  String GetDataResourceName() override;
+  int GetDataResourceId() override;
   void Draw(float scale) override;
   void Dispatch(const String& message) override;
   v8_inspector::V8InspectorSession* v8_session_;
diff --git a/third_party/blink/renderer/core/inspector/inspector_overlay_agent.cc b/third_party/blink/renderer/core/inspector/inspector_overlay_agent.cc
index 99bf4d3c..429eddd8 100644
--- a/third_party/blink/renderer/core/inspector/inspector_overlay_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_overlay_agent.cc
@@ -38,6 +38,7 @@
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/task_type.h"
 #include "third_party/blink/public/platform/web_data.h"
+#include "third_party/blink/public/resources/grit/blink_resources.h"
 #include "third_party/blink/public/web/web_widget_client.h"
 #include "third_party/blink/renderer/bindings/core/v8/sanitize_script_errors.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_controller.h"
@@ -73,6 +74,7 @@
 #include "third_party/blink/renderer/core/page/chrome_client.h"
 #include "third_party/blink/renderer/core/page/page.h"
 #include "third_party/blink/renderer/platform/bindings/script_forbidden_scope.h"
+#include "third_party/blink/renderer/platform/data_resource_helper.h"
 #include "third_party/blink/renderer/platform/graphics/color.h"
 #include "third_party/blink/renderer/platform/graphics/graphics_context.h"
 #include "third_party/blink/renderer/platform/graphics/paint/cull_rect.h"
@@ -113,8 +115,8 @@
   DoInit();
 }
 
-String InspectTool::GetDataResourceName() {
-  return String("inspect_tool_highlight.html");
+int InspectTool::GetDataResourceId() {
+  return IDR_INSPECT_TOOL_HIGHLIGHT_HTML;
 }
 
 bool InspectTool::HandleInputEvent(LocalFrameView* frame_view,
@@ -408,7 +410,7 @@
   resize_timer_.Stop();
   resize_timer_active_ = false;
   frame_overlay_.reset();
-  frame_resource_name_ = String();
+  frame_resource_name_ = 0;
   PickTheRightTool();
   SetNeedsUnbufferedInput(false);
   return Response::OK();
@@ -879,10 +881,10 @@
 }
 
 void InspectorOverlayAgent::LoadFrameForTool() {
-  if (frame_resource_name_ == inspect_tool_->GetDataResourceName())
+  if (frame_resource_name_ == inspect_tool_->GetDataResourceId())
     return;
 
-  frame_resource_name_ = inspect_tool_->GetDataResourceName();
+  frame_resource_name_ = inspect_tool_->GetDataResourceId();
 
   DEFINE_STATIC_LOCAL(Persistent<LocalFrameClient>, dummy_local_frame_client,
                       (MakeGarbageCollected<EmptyLocalFrameClient>()));
@@ -895,13 +897,12 @@
 
   scoped_refptr<SharedBuffer> data = SharedBuffer::Create();
   data->Append("<style>", static_cast<size_t>(7));
-  data->Append(Platform::Current()->GetDataResource("inspect_tool_common.css"));
+  data->Append(UncompressResourceAsBinary(IDR_INSPECT_TOOL_COMMON_CSS));
   data->Append("</style>", static_cast<size_t>(8));
   data->Append("<script>", static_cast<size_t>(8));
-  data->Append(Platform::Current()->GetDataResource("inspect_tool_common.js"));
+  data->Append(UncompressResourceAsBinary(IDR_INSPECT_TOOL_COMMON_JS));
   data->Append("</script>", static_cast<size_t>(9));
-  data->Append(Platform::Current()->GetDataResource(
-      frame_resource_name_.Utf8().c_str()));
+  data->Append(UncompressResourceAsBinary(frame_resource_name_));
 
   frame->ForceSynchronousDocumentInstall("text/html", data);
 
diff --git a/third_party/blink/renderer/core/inspector/inspector_overlay_agent.h b/third_party/blink/renderer/core/inspector/inspector_overlay_agent.h
index ab0b77fb..6c9175f2 100644
--- a/third_party/blink/renderer/core/inspector/inspector_overlay_agent.h
+++ b/third_party/blink/renderer/core/inspector/inspector_overlay_agent.h
@@ -76,7 +76,7 @@
  public:
   virtual ~InspectTool() = default;
   void Init(InspectorOverlayAgent* overlay, OverlayFrontend* frontend);
-  virtual String GetDataResourceName();
+  virtual int GetDataResourceId();
   virtual bool HandleInputEvent(LocalFrameView* frame_view,
                                 const WebInputEvent& input_event,
                                 bool* swallow_next_mouse_up);
@@ -216,7 +216,7 @@
   Member<WebLocalFrameImpl> frame_impl_;
   Member<InspectedFrames> inspected_frames_;
   Member<Page> overlay_page_;
-  String frame_resource_name_;
+  int frame_resource_name_;
   Member<InspectorOverlayChromeClient> overlay_chrome_client_;
   Member<InspectorOverlayHost> overlay_host_;
   bool resize_timer_active_;
diff --git a/third_party/blink/renderer/core/intersection_observer/intersection_geometry.cc b/third_party/blink/renderer/core/intersection_observer/intersection_geometry.cc
index 402133b..379c3e7 100644
--- a/third_party/blink/renderer/core/intersection_observer/intersection_geometry.cc
+++ b/third_party/blink/renderer/core/intersection_observer/intersection_geometry.cc
@@ -43,13 +43,15 @@
       rect, kTraverseDocumentBoundaries);
 }
 
-LayoutUnit ComputeMargin(const Length& length, LayoutUnit reference_length) {
+LayoutUnit ComputeMargin(const Length& length,
+                         LayoutUnit reference_length,
+                         float zoom) {
   if (length.IsPercent()) {
     return LayoutUnit(static_cast<int>(reference_length.ToFloat() *
                                        length.Percent() / 100.0));
   }
   DCHECK(length.IsFixed());
-  return LayoutUnit(length.IntValue());
+  return LayoutUnit(length.Value() * zoom);
 }
 
 LayoutView* LocalRootView(Element& element) {
@@ -235,21 +237,22 @@
   } else {
     result = PhysicalRect(ToLayoutBoxModelObject(root)->BorderBoundingBox());
   }
-  ApplyRootMargin(result, margin);
+  ApplyRootMargin(result, margin, root->StyleRef().EffectiveZoom());
   return result;
 }
 
 void IntersectionGeometry::ApplyRootMargin(PhysicalRect& rect,
-                                           const Vector<Length>& margin) {
+                                           const Vector<Length>& margin,
+                                           float zoom) {
   if (margin.IsEmpty())
     return;
 
   // TODO(szager): Make sure the spec is clear that left/right margins are
   // resolved against width and not height.
-  LayoutRectOutsets outsets(ComputeMargin(margin[0], rect.Height()),
-                            ComputeMargin(margin[1], rect.Width()),
-                            ComputeMargin(margin[2], rect.Height()),
-                            ComputeMargin(margin[3], rect.Width()));
+  LayoutRectOutsets outsets(ComputeMargin(margin[0], rect.Height(), zoom),
+                            ComputeMargin(margin[1], rect.Width(), zoom),
+                            ComputeMargin(margin[2], rect.Height(), zoom),
+                            ComputeMargin(margin[3], rect.Width(), zoom));
   rect.Expand(outsets);
 }
 
diff --git a/third_party/blink/renderer/core/intersection_observer/intersection_geometry.h b/third_party/blink/renderer/core/intersection_observer/intersection_geometry.h
index e66bce93..d2e21e75 100644
--- a/third_party/blink/renderer/core/intersection_observer/intersection_geometry.h
+++ b/third_party/blink/renderer/core/intersection_observer/intersection_geometry.h
@@ -83,7 +83,9 @@
   PhysicalRect InitializeTargetRect(LayoutObject* target);
   PhysicalRect InitializeRootRect(LayoutObject* root,
                                   const Vector<Length>& margin);
-  void ApplyRootMargin(PhysicalRect& rect, const Vector<Length>& margin);
+  void ApplyRootMargin(PhysicalRect& rect,
+                       const Vector<Length>& margin,
+                       float zoom);
   bool ClipToRoot(LayoutObject* root,
                   LayoutObject* target,
                   const PhysicalRect& root_rect,
diff --git a/third_party/blink/renderer/core/intersection_observer/intersection_observer_test.cc b/third_party/blink/renderer/core/intersection_observer/intersection_observer_test.cc
index 702378e..7c6cff0 100644
--- a/third_party/blink/renderer/core/intersection_observer/intersection_observer_test.cc
+++ b/third_party/blink/renderer/core/intersection_observer/intersection_observer_test.cc
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "build/build_config.h"
+
 #include "third_party/blink/renderer/core/intersection_observer/intersection_observer.h"
 
 #include "third_party/blink/renderer/core/dom/element.h"
@@ -455,6 +457,46 @@
   EXPECT_EQ(controller.GetTrackedTargetCountForTesting(), 0u);
 }
 
+TEST_F(IntersectionObserverTest, RootMarginDevicePixelRatio) {
+  WebView().SetZoomFactorForDeviceScaleFactor(3.5f);
+  WebView().MainFrameWidget()->Resize(WebSize(2800, 2100));
+  SimRequest main_resource("https://example.com/", "text/html");
+  LoadURL("https://example.com/");
+  main_resource.Complete(R"HTML(
+    <style>
+    body {
+      margin: 0;
+    }
+    #target {
+      height: 30px;
+    }
+    </style>
+    <div id='target'>Hello, world!</div>
+  )HTML");
+  IntersectionObserverInit* observer_init = IntersectionObserverInit::Create();
+  observer_init->setRootMargin("-31px 0px 0px 0px");
+  DummyExceptionStateForTesting exception_state;
+  TestIntersectionObserverDelegate* observer_delegate =
+      MakeGarbageCollected<TestIntersectionObserverDelegate>(GetDocument());
+  IntersectionObserver* observer = IntersectionObserver::Create(
+      observer_init, *observer_delegate, exception_state);
+  ASSERT_FALSE(exception_state.HadException());
+  Element* target = GetDocument().getElementById("target");
+  ASSERT_TRUE(target);
+  observer->observe(target, exception_state);
+  ASSERT_FALSE(exception_state.HadException());
+
+  Compositor().BeginFrame();
+  test::RunPendingTasks();
+
+  EXPECT_EQ(observer_delegate->CallCount(), 1);
+  EXPECT_EQ(observer_delegate->EntryCount(), 1);
+  EXPECT_FALSE(observer_delegate->LastEntry()->isIntersecting());
+  EXPECT_EQ(PixelSnappedIntRect(
+                observer_delegate->LastEntry()->GetGeometry().RootRect()),
+            IntRect(0, 31, 800, 600 - 31));
+}
+
 TEST_F(IntersectionObserverV2Test, TrackVisibilityInit) {
   IntersectionObserverInit* observer_init = IntersectionObserverInit::Create();
   DummyExceptionStateForTesting exception_state;
diff --git a/third_party/blink/renderer/core/layout/custom/layout_worklet_test.cc b/third_party/blink/renderer/core/layout/custom/layout_worklet_test.cc
index 8671618..429ae0a 100644
--- a/third_party/blink/renderer/core/layout/custom/layout_worklet_test.cc
+++ b/third_party/blink/renderer/core/layout/custom/layout_worklet_test.cc
@@ -53,7 +53,7 @@
         ASSERT_NO_EXCEPTION);
     EXPECT_FALSE(module.IsNull());
 
-    ScriptValue exception = module.Instantiate(script_state);
+    ScriptValue exception = module.Instantiate(script_state, js_url);
     EXPECT_TRUE(exception.IsEmpty());
     return module.Evaluate(script_state);
   }
diff --git a/third_party/blink/renderer/core/layout/layout_frame.cc b/third_party/blink/renderer/core/layout/layout_frame.cc
index 1b8a9256..350e48e 100644
--- a/third_party/blink/renderer/core/layout/layout_frame.cc
+++ b/third_party/blink/renderer/core/layout/layout_frame.cc
@@ -37,7 +37,7 @@
 }
 
 FrameEdgeInfo LayoutFrame::EdgeInfo() const {
-  HTMLFrameElement* element = ToHTMLFrameElement(GetNode());
+  auto* element = To<HTMLFrameElement>(GetNode());
   return FrameEdgeInfo(element->NoResize(), element->HasFrameBorder());
 }
 
diff --git a/third_party/blink/renderer/core/loader/modulescript/module_tree_linker.cc b/third_party/blink/renderer/core/loader/modulescript/module_tree_linker.cc
index b2bc0222..24e7d39 100644
--- a/third_party/blink/renderer/core/loader/modulescript/module_tree_linker.cc
+++ b/third_party/blink/renderer/core/loader/modulescript/module_tree_linker.cc
@@ -516,7 +516,8 @@
 
     // <spec step="5.2">Perform record.Instantiate(). ...</spec>
     AdvanceState(State::kInstantiating);
-    ScriptValue instantiation_error = modulator_->InstantiateModule(record);
+    ScriptValue instantiation_error =
+        modulator_->InstantiateModule(record, result_->SourceURL());
 
     // <spec step="5.2">... If this throws an exception, set result's error to
     // rethrow to that exception.</spec>
diff --git a/third_party/blink/renderer/core/loader/modulescript/module_tree_linker_test.cc b/third_party/blink/renderer/core/loader/modulescript/module_tree_linker_test.cc
index f4503aa8..e28b8fb1 100644
--- a/third_party/blink/renderer/core/loader/modulescript/module_tree_linker_test.cc
+++ b/third_party/blink/renderer/core/loader/modulescript/module_tree_linker_test.cc
@@ -148,7 +148,8 @@
     return it->value;
   }
 
-  ScriptValue InstantiateModule(ModuleRecord record) override {
+  ScriptValue InstantiateModule(ModuleRecord record,
+                                const KURL& source_url) override {
     if (instantiate_should_fail_) {
       ScriptState::Scope scope(script_state_);
       v8::Local<v8::Value> error = V8ThrowException::CreateError(
diff --git a/third_party/blink/renderer/core/page/validation_message_overlay_delegate.cc b/third_party/blink/renderer/core/page/validation_message_overlay_delegate.cc
index 8f73880..4f35483e 100644
--- a/third_party/blink/renderer/core/page/validation_message_overlay_delegate.cc
+++ b/third_party/blink/renderer/core/page/validation_message_overlay_delegate.cc
@@ -7,6 +7,7 @@
 #include <memory>
 
 #include "base/memory/ptr_util.h"
+#include "third_party/blink/public/resources/grit/blink_resources.h"
 #include "third_party/blink/renderer/core/dom/dom_token_list.h"
 #include "third_party/blink/renderer/core/dom/element.h"
 #include "third_party/blink/renderer/core/dom/events/event_dispatch_forbidden_scope.h"
@@ -16,6 +17,7 @@
 #include "third_party/blink/renderer/core/loader/empty_clients.h"
 #include "third_party/blink/renderer/core/page/page.h"
 #include "third_party/blink/renderer/core/page/page_popup_client.h"
+#include "third_party/blink/renderer/platform/data_resource_helper.h"
 #include "third_party/blink/renderer/platform/graphics/paint/cull_rect.h"
 #include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h"
 #include "third_party/blink/renderer/platform/graphics/paint/paint_record_builder.h"
@@ -198,7 +200,7 @@
 void ValidationMessageOverlayDelegate::WriteDocument(SharedBuffer* data) {
   DCHECK(data);
   PagePopupClient::AddString("<!DOCTYPE html><html><head><style>", data);
-  data->Append(Platform::Current()->GetDataResource("validation_bubble.css"));
+  data->Append(UncompressResourceAsBinary(IDR_VALIDATION_BUBBLE_CSS));
   PagePopupClient::AddString("</style></head>", data);
   PagePopupClient::AddString(
       Locale::DefaultLocale().IsRTL() ? "<body dir=rtl>" : "<body dir=ltr>",
@@ -210,7 +212,7 @@
       "<div id=spacer-top></div>"
       "<main id=bubble-body>",
       data);
-  data->Append(Platform::Current()->GetDataResource("input_alert.svg"));
+  data->Append(UncompressResourceAsBinary(IDR_VALIDATION_BUBBLE_ICON));
   PagePopupClient::AddString(message_dir_ == TextDirection::kLtr
                                  ? "<div dir=ltr id=main-message></div>"
                                  : "<div dir=rtl id=main-message></div>",
diff --git a/third_party/blink/renderer/core/script/dynamic_module_resolver_test.cc b/third_party/blink/renderer/core/script/dynamic_module_resolver_test.cc
index c85586c..e4245ae 100644
--- a/third_party/blink/renderer/core/script/dynamic_module_resolver_test.cc
+++ b/third_party/blink/renderer/core/script/dynamic_module_resolver_test.cc
@@ -231,7 +231,8 @@
       ASSERT_NO_EXCEPTION);
   ModuleScript* module_script =
       JSModuleScript::CreateForTest(modulator, record, TestDependencyURL());
-  EXPECT_TRUE(record.Instantiate(scope.GetScriptState()).IsEmpty());
+  EXPECT_TRUE(
+      record.Instantiate(scope.GetScriptState(), TestReferrerURL()).IsEmpty());
   modulator->ResolveTreeFetch(module_script);
 
   v8::MicrotasksScope::PerformCheckpoint(scope.GetIsolate());
@@ -323,7 +324,8 @@
       ASSERT_NO_EXCEPTION);
   ModuleScript* module_script =
       JSModuleScript::CreateForTest(modulator, record, TestDependencyURL());
-  EXPECT_TRUE(record.Instantiate(scope.GetScriptState()).IsEmpty());
+  EXPECT_TRUE(
+      record.Instantiate(scope.GetScriptState(), TestReferrerURL()).IsEmpty());
   modulator->ResolveTreeFetch(module_script);
 
   v8::MicrotasksScope::PerformCheckpoint(scope.GetIsolate());
@@ -363,7 +365,8 @@
       TextPosition::MinimumPosition(), ASSERT_NO_EXCEPTION);
   ModuleScript* module_script =
       JSModuleScript::CreateForTest(modulator, record, TestDependencyURL());
-  EXPECT_TRUE(record.Instantiate(scope.GetScriptState()).IsEmpty());
+  EXPECT_TRUE(record.Instantiate(scope.GetScriptState(), TestDependencyURL())
+                  .IsEmpty());
   modulator->ResolveTreeFetch(module_script);
 
   v8::MicrotasksScope::PerformCheckpoint(scope.GetIsolate());
diff --git a/third_party/blink/renderer/core/script/modulator.h b/third_party/blink/renderer/core/script/modulator.h
index 9f67b4e..bbabc0f 100644
--- a/third_party/blink/renderer/core/script/modulator.h
+++ b/third_party/blink/renderer/core/script/modulator.h
@@ -184,7 +184,7 @@
 
   virtual bool HasValidContext() = 0;
 
-  virtual ScriptValue InstantiateModule(ModuleRecord) = 0;
+  virtual ScriptValue InstantiateModule(ModuleRecord, const KURL&) = 0;
 
   struct ModuleRequest {
     String specifier;
diff --git a/third_party/blink/renderer/core/script/modulator_impl_base.cc b/third_party/blink/renderer/core/script/modulator_impl_base.cc
index a3bcbe4..0cac6b62 100644
--- a/third_party/blink/renderer/core/script/modulator_impl_base.cc
+++ b/third_party/blink/renderer/core/script/modulator_impl_base.cc
@@ -270,12 +270,13 @@
   return ModuleImportMeta(url_string);
 }
 
-ScriptValue ModulatorImplBase::InstantiateModule(ModuleRecord module_record) {
+ScriptValue ModulatorImplBase::InstantiateModule(ModuleRecord module_record,
+                                                 const KURL& source_url) {
   UseCounter::Count(GetExecutionContext(),
                     WebFeature::kInstantiateModuleScript);
 
   ScriptState::Scope scope(script_state_);
-  return module_record.Instantiate(script_state_);
+  return module_record.Instantiate(script_state_, source_url);
 }
 
 Vector<Modulator::ModuleRequest>
diff --git a/third_party/blink/renderer/core/script/modulator_impl_base.h b/third_party/blink/renderer/core/script/modulator_impl_base.h
index 6721f1c..f2f2599 100644
--- a/third_party/blink/renderer/core/script/modulator_impl_base.h
+++ b/third_party/blink/renderer/core/script/modulator_impl_base.h
@@ -81,7 +81,7 @@
   bool IsAcquiringImportMaps() const final { return acquiring_import_maps_; }
   void ClearIsAcquiringImportMaps() final { acquiring_import_maps_ = false; }
   ModuleImportMeta HostGetImportMetaProperties(ModuleRecord) const override;
-  ScriptValue InstantiateModule(ModuleRecord) override;
+  ScriptValue InstantiateModule(ModuleRecord, const KURL&) override;
   Vector<ModuleRequest> ModuleRequestsFromModuleRecord(ModuleRecord) override;
   ScriptValue ExecuteModule(ModuleScript*, CaptureEvalErrorFlag) override;
 
diff --git a/third_party/blink/renderer/core/script/module_script.h b/third_party/blink/renderer/core/script/module_script.h
index 50d32f0e..3b578966 100644
--- a/third_party/blink/renderer/core/script/module_script.h
+++ b/third_party/blink/renderer/core/script/module_script.h
@@ -51,6 +51,7 @@
   void Trace(Visitor*) override;
 
   virtual void ProduceCache() {}
+  const KURL& SourceURL() const { return source_url_; }
 
  protected:
   ModuleScript(Modulator*,
@@ -59,7 +60,6 @@
                const KURL& base_url,
                const ScriptFetchOptions&);
 
-  const KURL& SourceURL() const { return source_url_; }
   Modulator* SettingsObject() const { return settings_object_; }
 
  private:
diff --git a/third_party/blink/renderer/core/script/module_script_test.cc b/third_party/blink/renderer/core/script/module_script_test.cc
index 6d1849c..880f654 100644
--- a/third_party/blink/renderer/core/script/module_script_test.cc
+++ b/third_party/blink/renderer/core/script/module_script_test.cc
@@ -142,7 +142,9 @@
 
     // Check that the module script is instantiated/evaluated correctly.
     ASSERT_TRUE(
-        module_script->Record().Instantiate(scope.GetScriptState()).IsEmpty());
+        module_script->Record()
+            .Instantiate(scope.GetScriptState(), module_script->SourceURL())
+            .IsEmpty());
     ASSERT_TRUE(
         module_script->Record().Evaluate(scope.GetScriptState()).IsEmpty());
     TestFoo(scope);
diff --git a/third_party/blink/renderer/core/testing/dummy_modulator.cc b/third_party/blink/renderer/core/testing/dummy_modulator.cc
index 7afbf4da..f74913d 100644
--- a/third_party/blink/renderer/core/testing/dummy_modulator.cc
+++ b/third_party/blink/renderer/core/testing/dummy_modulator.cc
@@ -144,7 +144,7 @@
   return ModuleImportMeta(String());
 }
 
-ScriptValue DummyModulator::InstantiateModule(ModuleRecord) {
+ScriptValue DummyModulator::InstantiateModule(ModuleRecord, const KURL&) {
   NOTREACHED();
   return ScriptValue();
 }
diff --git a/third_party/blink/renderer/core/testing/dummy_modulator.h b/third_party/blink/renderer/core/testing/dummy_modulator.h
index 9959681..09d32c08 100644
--- a/third_party/blink/renderer/core/testing/dummy_modulator.h
+++ b/third_party/blink/renderer/core/testing/dummy_modulator.h
@@ -65,7 +65,7 @@
   bool IsAcquiringImportMaps() const override;
   void ClearIsAcquiringImportMaps() override;
   ModuleImportMeta HostGetImportMetaProperties(ModuleRecord) const override;
-  ScriptValue InstantiateModule(ModuleRecord) override;
+  ScriptValue InstantiateModule(ModuleRecord, const KURL&) override;
   Vector<ModuleRequest> ModuleRequestsFromModuleRecord(ModuleRecord) override;
   ScriptValue ExecuteModule(ModuleScript*, CaptureEvalErrorFlag) override;
   ModuleScriptFetcher* CreateModuleScriptFetcher(
diff --git a/third_party/blink/renderer/devtools/front_end/web_audio/AudioContextContentBuilder.js b/third_party/blink/renderer/devtools/front_end/web_audio/AudioContextContentBuilder.js
index c503d933..9c35e34 100644
--- a/third_party/blink/renderer/devtools/front_end/web_audio/AudioContextContentBuilder.js
+++ b/third_party/blink/renderer/devtools/front_end/web_audio/AudioContextContentBuilder.js
@@ -64,7 +64,7 @@
 
 WebAudio.AudioContextSummaryBuilder = class {
   /**
-   * @param {!Protocol.WebAudio.ContextId} contextId
+   * @param {!Protocol.WebAudio.GraphObjectId} contextId
    * @param {!Protocol.WebAudio.ContextRealtimeData} contextRealtimeData
    */
   constructor(contextId, contextRealtimeData) {
diff --git a/third_party/blink/renderer/devtools/front_end/web_audio/AudioContextSelector.js b/third_party/blink/renderer/devtools/front_end/web_audio/AudioContextSelector.js
index f40f41cf..7fc659d 100644
--- a/third_party/blink/renderer/devtools/front_end/web_audio/AudioContextSelector.js
+++ b/third_party/blink/renderer/devtools/front_end/web_audio/AudioContextSelector.js
@@ -46,7 +46,7 @@
    * @param {!Common.Event} event
    */
   contextDestroyed(event) {
-    const contextId = /** @type {!Protocol.WebAudio.ContextId} */ (event.data);
+    const contextId = /** @type {!Protocol.WebAudio.GraphObjectId} */ (event.data);
     const contextIndex = this._items.findIndex(context => context.contextId === contextId);
     if (contextIndex > -1)
       this._items.remove(contextIndex);
diff --git a/third_party/blink/renderer/devtools/front_end/web_audio/WebAudioModel.js b/third_party/blink/renderer/devtools/front_end/web_audio/WebAudioModel.js
index 7ec0e5d1..29dfd1bc 100644
--- a/third_party/blink/renderer/devtools/front_end/web_audio/WebAudioModel.js
+++ b/third_party/blink/renderer/devtools/front_end/web_audio/WebAudioModel.js
@@ -14,7 +14,7 @@
 
     this._enabled = false;
 
-    /** @type {!Map<!Protocol.WebAudio.ContextId, !Protocol.WebAudio.BaseAudioContext>} */
+    /** @type {!Map<!Protocol.WebAudio.GraphObjectId, !Protocol.WebAudio.BaseAudioContext>} */
     this._contextMapById = new Map();
 
     this._agent = target.webAudioAgent();
@@ -73,7 +73,7 @@
   }
 
   /**
-   * @param {!Protocol.WebAudio.ContextId} contextId
+   * @param {!Protocol.WebAudio.GraphObjectId} contextId
    * @override
    */
   contextWillBeDestroyed(contextId) {
@@ -94,7 +94,85 @@
   }
 
   /**
-   * @param {!Protocol.WebAudio.ContextId} contextId
+   * @param {!Protocol.WebAudio.AudioListener} listener
+   * @override
+   */
+  audioListenerCreated(listener) {}
+
+  /**
+   * @param {!Protocol.WebAudio.GraphObjectId} contextId
+   * @param {!Protocol.WebAudio.GraphObjectId} listenerId
+   * @override
+   */
+  audioListenerWillBeDestroyed(contextId, listenerId) {}
+
+  /**
+   * @param {!Protocol.WebAudio.AudioNode} node
+   * @override
+   */
+  audioNodeCreated(node) {}
+
+  /**
+   * @param {!Protocol.WebAudio.GraphObjectId} contextId
+   * @param {!Protocol.WebAudio.GraphObjectId} nodeId
+   * @override
+   */
+  audioNodeWillBeDestroyed(contextId, nodeId) {}
+
+  /**
+   * @param {!Protocol.WebAudio.AudioParam} param
+   * @override
+   */
+  audioParamCreated(param) {}
+
+  /**
+   * @param {!Protocol.WebAudio.GraphObjectId} contextId
+   * @param {!Protocol.WebAudio.GraphObjectId} nodeId
+   * @param {!Protocol.WebAudio.GraphObjectId} paramId
+   * @override
+   */
+  audioParamWillBeDestroyed(contextId, nodeId, paramId) {}
+
+  /**
+   * @param {!Protocol.WebAudio.GraphObjectId} contextId
+   * @param {!Protocol.WebAudio.GraphObjectId} sourceId
+   * @param {!Protocol.WebAudio.GraphObjectId} destinationId
+   * @param {number=} sourceOutputIndex
+   * @param {number=} destinationInputIndex
+   * @override
+   */
+  nodesConnected(contextId, sourceId, destinationId, sourceOutputIndex, destinationInputIndex) {}
+
+  /**
+   * @param {!Protocol.WebAudio.GraphObjectId} contextId
+   * @param {!Protocol.WebAudio.GraphObjectId} sourceId
+   * @param {?Protocol.WebAudio.GraphObjectId=} destinationId
+   * @param {number=} sourceOutputIndex
+   * @param {number=} destinationInputIndex
+   * @override
+   */
+  nodesDisconnected(contextId, sourceId, destinationId, sourceOutputIndex, destinationInputIndex) {}
+
+  /**
+   * @param {!Protocol.WebAudio.GraphObjectId} contextId
+   * @param {!Protocol.WebAudio.GraphObjectId} sourceId
+   * @param {!Protocol.WebAudio.GraphObjectId} destinationId
+   * @param {number=} sourceOutputIndex
+   * @override
+   */
+  nodeParamConnected(contextId, sourceId, destinationId, sourceOutputIndex) {}
+
+  /**
+   * @param {!Protocol.WebAudio.GraphObjectId} contextId
+   * @param {!Protocol.WebAudio.GraphObjectId} sourceId
+   * @param {!Protocol.WebAudio.GraphObjectId} destinationId
+   * @param {number=} sourceOutputIndex
+   * @override
+   */
+  nodeParamDisconnected(contextId, sourceId, destinationId, sourceOutputIndex) {}
+
+  /**
+   * @param {!Protocol.WebAudio.GraphObjectId} contextId
    * @return {!Promise<?Protocol.WebAudio.ContextRealtimeData>}
    */
   async requestRealtimeData(contextId) {
diff --git a/third_party/blink/renderer/devtools/front_end/web_audio/WebAudioView.js b/third_party/blink/renderer/devtools/front_end/web_audio/WebAudioView.js
index 9944ea7..58f5a71 100644
--- a/third_party/blink/renderer/devtools/front_end/web_audio/WebAudioView.js
+++ b/third_party/blink/renderer/devtools/front_end/web_audio/WebAudioView.js
@@ -151,7 +151,7 @@
   }
 
   /**
-   * @param {!Protocol.WebAudio.ContextId} contextId
+   * @param {!Protocol.WebAudio.GraphObjectId} contextId
    * @param {!Protocol.WebAudio.ContextRealtimeData} contextRealtimeData
    */
   _updateSummaryBar(contextId, contextRealtimeData) {
diff --git a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
index 6235293..4c09c9d 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
@@ -45,6 +45,7 @@
 #include "third_party/blink/renderer/core/editing/position.h"
 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
 #include "third_party/blink/renderer/core/html/canvas/html_canvas_element.h"
+#include "third_party/blink/renderer/core/html/custom/element_internals.h"
 #include "third_party/blink/renderer/core/html/forms/html_field_set_element.h"
 #include "third_party/blink/renderer/core/html/forms/html_input_element.h"
 #include "third_party/blink/renderer/core/html/forms/html_label_element.h"
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.cc b/third_party/blink/renderer/modules/accessibility/ax_object.cc
index 6207051..d6a8c88 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object.cc
@@ -37,6 +37,7 @@
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
 #include "third_party/blink/renderer/core/frame/settings.h"
+#include "third_party/blink/renderer/core/html/custom/element_internals.h"
 #include "third_party/blink/renderer/core/html/forms/html_input_element.h"
 #include "third_party/blink/renderer/core/html/forms/html_select_element.h"
 #include "third_party/blink/renderer/core/html/html_dialog_element.h"
@@ -661,7 +662,9 @@
   AXSparseAttributeSetterMap& ax_sparse_attribute_setter_map =
       GetSparseAttributeSetterMap();
   AttributeCollection attributes = element->AttributesWithoutUpdate();
+  HashSet<QualifiedName> set_attributes;
   for (const Attribute& attr : attributes) {
+    set_attributes.insert(attr.GetName());
     if (shadowed_aria_attributes.Contains(attr.GetName()))
       continue;
 
@@ -670,6 +673,19 @@
     if (setter)
       setter->Run(*this, sparse_attribute_client, attr.Value());
   }
+  if (!element->DidAttachInternals())
+    return;
+  const auto& internals_attributes =
+      element->EnsureElementInternals().GetAttributes();
+  for (const QualifiedName& attr : internals_attributes.Keys()) {
+    if (set_attributes.Contains(attr))
+      continue;
+    AXSparseAttributeSetter* setter = ax_sparse_attribute_setter_map.at(attr);
+    if (setter) {
+      setter->Run(*this, sparse_attribute_client,
+                  internals_attributes.at(attr));
+    }
+  }
 }
 
 bool AXObject::IsARIATextControl() const {
@@ -1873,59 +1889,74 @@
   }
 }
 
+bool IsGlobalARIAAttribute(const AtomicString& name) {
+  if (!name.StartsWith("ARIA"))
+    return false;
+  if (name.StartsWith("ARIA-ATOMIC"))
+    return true;
+  if (name.StartsWith("ARIA-BUSY"))
+    return true;
+  if (name.StartsWith("ARIA-CONTROLS"))
+    return true;
+  if (name.StartsWith("ARIA-CURRENT"))
+    return true;
+  if (name.StartsWith("ARIA-DESCRIBEDBY"))
+    return true;
+  if (name.StartsWith("ARIA-DETAILS"))
+    return true;
+  if (name.StartsWith("ARIA-DISABLED"))
+    return true;
+  if (name.StartsWith("ARIA-DROPEFFECT"))
+    return true;
+  if (name.StartsWith("ARIA-ERRORMESSAGE"))
+    return true;
+  if (name.StartsWith("ARIA-FLOWTO"))
+    return true;
+  if (name.StartsWith("ARIA-GRABBED"))
+    return true;
+  if (name.StartsWith("ARIA-HASPOPUP"))
+    return true;
+  if (name.StartsWith("ARIA-HIDDEN"))
+    return true;
+  if (name.StartsWith("ARIA-INVALID"))
+    return true;
+  if (name.StartsWith("ARIA-KEYSHORTCUTS"))
+    return true;
+  if (name.StartsWith("ARIA-LABEL"))
+    return true;
+  if (name.StartsWith("ARIA-LABELEDBY"))
+    return true;
+  if (name.StartsWith("ARIA-LABELLEDBY"))
+    return true;
+  if (name.StartsWith("ARIA-LIVE"))
+    return true;
+  if (name.StartsWith("ARIA-OWNS"))
+    return true;
+  if (name.StartsWith("ARIA-RELEVANT"))
+    return true;
+  if (name.StartsWith("ARIA-ROLEDESCRIPTION"))
+    return true;
+  return false;
+}
+
 bool AXObject::HasGlobalARIAAttribute() const {
-  if (!GetElement())
+  auto* element = GetElement();
+  if (!element)
     return false;
 
-  AttributeCollection attributes = GetElement()->AttributesWithoutUpdate();
+  AttributeCollection attributes = element->AttributesWithoutUpdate();
   for (const Attribute& attr : attributes) {
     // Attributes cache their uppercase names.
     auto name = attr.GetName().LocalNameUpper();
-    if (!name.StartsWith("ARIA"))
-      continue;
-    if (name.StartsWith("ARIA-ATOMIC"))
+    if (IsGlobalARIAAttribute(name))
       return true;
-    if (name.StartsWith("ARIA-BUSY"))
-      return true;
-    if (name.StartsWith("ARIA-CONTROLS"))
-      return true;
-    if (name.StartsWith("ARIA-CURRENT"))
-      return true;
-    if (name.StartsWith("ARIA-DESCRIBEDBY"))
-      return true;
-    if (name.StartsWith("ARIA-DETAILS"))
-      return true;
-    if (name.StartsWith("ARIA-DISABLED"))
-      return true;
-    if (name.StartsWith("ARIA-DROPEFFECT"))
-      return true;
-    if (name.StartsWith("ARIA-ERRORMESSAGE"))
-      return true;
-    if (name.StartsWith("ARIA-FLOWTO"))
-      return true;
-    if (name.StartsWith("ARIA-GRABBED"))
-      return true;
-    if (name.StartsWith("ARIA-HASPOPUP"))
-      return true;
-    if (name.StartsWith("ARIA-HIDDEN"))
-      return true;
-    if (name.StartsWith("ARIA-INVALID"))
-      return true;
-    if (name.StartsWith("ARIA-KEYSHORTCUTS"))
-      return true;
-    if (name.StartsWith("ARIA-LABEL"))
-      return true;
-    if (name.StartsWith("ARIA-LABELEDBY"))
-      return true;
-    if (name.StartsWith("ARIA-LABELLEDBY"))
-      return true;
-    if (name.StartsWith("ARIA-LIVE"))
-      return true;
-    if (name.StartsWith("ARIA-OWNS"))
-      return true;
-    if (name.StartsWith("ARIA-RELEVANT"))
-      return true;
-    if (name.StartsWith("ARIA-ROLEDESCRIPTION"))
+  }
+  if (!element->DidAttachInternals())
+    return false;
+  const auto& internals_attributes =
+      element->EnsureElementInternals().GetAttributes();
+  for (const QualifiedName& attr : internals_attributes.Keys()) {
+    if (IsGlobalARIAAttribute(attr.LocalNameUpper()))
       return true;
   }
   return false;
@@ -2566,16 +2597,38 @@
 }
 
 bool AXObject::HasAttribute(const QualifiedName& attribute) const {
-  if (Element* element = GetElement())
-    return element->FastHasAttribute(attribute);
-  return false;
+  Element* element = GetElement();
+  if (!element)
+    return false;
+  if (element->FastHasAttribute(attribute))
+    return true;
+  return HasInternalsAttribute(*element, attribute);
 }
 
 const AtomicString& AXObject::GetAttribute(
     const QualifiedName& attribute) const {
-  if (Element* element = GetElement())
-    return element->FastGetAttribute(attribute);
-  return g_null_atom;
+  Element* element = GetElement();
+  if (!element)
+    return g_null_atom;
+  const AtomicString& value = element->FastGetAttribute(attribute);
+  if (!value.IsNull())
+    return value;
+  return GetInternalsAttribute(*element, attribute);
+}
+
+bool AXObject::HasInternalsAttribute(Element& element,
+                                     const QualifiedName& attribute) const {
+  if (!element.DidAttachInternals())
+    return false;
+  return element.EnsureElementInternals().HasAttribute(attribute);
+}
+
+const AtomicString& AXObject::GetInternalsAttribute(
+    Element& element,
+    const QualifiedName& attribute) const {
+  if (!element.DidAttachInternals())
+    return g_null_atom;
+  return element.EnsureElementInternals().FastGetAttribute(attribute);
 }
 
 //
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.h b/third_party/blink/renderer/modules/accessibility/ax_object.h
index 046fe4ec..efe16d1 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_object.h
@@ -1138,6 +1138,9 @@
   ax::mojom::Role RemapAriaRoleDueToParent(ax::mojom::Role) const;
   unsigned ComputeAriaColumnIndex() const;
   unsigned ComputeAriaRowIndex() const;
+  bool HasInternalsAttribute(Element&, const QualifiedName&) const;
+  const AtomicString& GetInternalsAttribute(Element&,
+                                            const QualifiedName&) const;
 
   static unsigned number_of_live_ax_objects_;
 
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
index 275a1b3..a32ec80 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
@@ -1245,15 +1245,33 @@
   DeferTreeUpdate(&AXObjectCacheImpl::HandleAttributeChangedWithCleanLayout,
                   attr_name, element);
 
-  if (attr_name == kRoleAttr || attr_name == kTypeAttr ||
-      attr_name == kSizeAttr || attr_name == kAltAttr ||
-      attr_name == kTitleAttr ||
-      (attr_name == kForAttr && IsHTMLLabelElement(*element)) ||
-      attr_name == kIdAttr || attr_name == kTabindexAttr ||
-      attr_name == kDisabledAttr) {
-    return true;
+  if (attr_name != kRoleAttr && attr_name != kTypeAttr &&
+      attr_name != kSizeAttr && attr_name != kAltAttr &&
+      attr_name != kTitleAttr &&
+      (attr_name != kForAttr && !IsHTMLLabelElement(*element)) &&
+      attr_name != kIdAttr && attr_name != kTabindexAttr &&
+      attr_name != kDisabledAttr &&
+      !attr_name.LocalName().StartsWith("aria-")) {
+    return false;
   }
-  return attr_name.LocalName().StartsWith("aria-");
+
+  // If this attribute is interesting for accessibility (e.g. `role` or
+  // `alt`), but doesn't trigger a lifecycle update on its own
+  // (e.g. because it doesn't make layout dirty), make sure we run
+  // lifecycle phases to update the computed accessibility tree.
+  auto* view = element->GetDocument().View();
+  auto* page = element->GetDocument().GetPage();
+  if (!view || !page)
+    return true;
+  if (!view->CanThrottleRendering()) {
+    page->Animator().ScheduleVisualUpdate(GetDocument().GetFrame());
+  }
+
+  // TODO(aboxhall): add a lifecycle phase for accessibility updates.
+  GetDocument().Lifecycle().EnsureStateAtMost(
+      DocumentLifecycle::kVisualUpdatePending);
+
+  return true;
 }
 
 void AXObjectCacheImpl::HandleAttributeChangedWithCleanLayout(
diff --git a/third_party/blink/renderer/modules/background_fetch/background_fetch_bridge.cc b/third_party/blink/renderer/modules/background_fetch/background_fetch_bridge.cc
index cffb929..1d442f7d 100644
--- a/third_party/blink/renderer/modules/background_fetch/background_fetch_bridge.cc
+++ b/third_party/blink/renderer/modules/background_fetch/background_fetch_bridge.cc
@@ -78,11 +78,9 @@
   if (registration) {
     DCHECK_EQ(error, mojom::blink::BackgroundFetchError::NONE);
     DCHECK_EQ(registration->result(), "");
-    mojom::blink::BackgroundFetchRegistrationServicePtr registration_service(
+    registration->Initialize(
+        GetSupplementable(),
         std::move(registration_ptr->registration_interface));
-    DCHECK(registration_service);
-    registration->Initialize(GetSupplementable(),
-                             std::move(registration_service));
   }
 
   std::move(callback).Run(error, registration);
diff --git a/third_party/blink/renderer/modules/background_fetch/background_fetch_registration.cc b/third_party/blink/renderer/modules/background_fetch/background_fetch_registration.cc
index 8a8f297..63e740a0 100644
--- a/third_party/blink/renderer/modules/background_fetch/background_fetch_registration.cc
+++ b/third_party/blink/renderer/modules/background_fetch/background_fetch_registration.cc
@@ -40,8 +40,7 @@
       download_total_(download_total),
       downloaded_(downloaded),
       result_(result),
-      failure_reason_(failure_reason),
-      observer_binding_(this) {}
+      failure_reason_(failure_reason) {}
 
 BackgroundFetchRegistration::BackgroundFetchRegistration(
     ServiceWorkerRegistration* service_worker_registration,
@@ -52,34 +51,30 @@
       download_total_(registration->registration_data->download_total),
       downloaded_(registration->registration_data->downloaded),
       result_(registration->registration_data->result),
-      failure_reason_(registration->registration_data->failure_reason),
-      observer_binding_(this) {
+      failure_reason_(registration->registration_data->failure_reason) {
   DCHECK(service_worker_registration);
   Initialize(service_worker_registration,
-             mojom::blink::BackgroundFetchRegistrationServicePtr(
-                 std::move(registration->registration_interface)));
+             std::move(registration->registration_interface));
 }
 
 BackgroundFetchRegistration::~BackgroundFetchRegistration() = default;
 
 void BackgroundFetchRegistration::Initialize(
     ServiceWorkerRegistration* registration,
-    mojom::blink::BackgroundFetchRegistrationServicePtr registration_service) {
+    mojo::PendingRemote<mojom::blink::BackgroundFetchRegistrationService>
+        registration_service) {
   DCHECK(!registration_);
   DCHECK(registration);
   DCHECK(!registration_service_);
   DCHECK(registration_service);
 
   registration_ = registration;
-  registration_service_ = std::move(registration_service);
+  registration_service_.Bind(std::move(registration_service));
 
   auto task_runner =
       GetExecutionContext()->GetTaskRunner(TaskType::kBackgroundFetch);
-  mojom::blink::BackgroundFetchRegistrationObserverPtr observer;
-  observer_binding_.Bind(mojo::MakeRequest(&observer, task_runner),
-                         task_runner);
-
-  registration_service_->AddRegistrationObserver(std::move(observer));
+  registration_service_->AddRegistrationObserver(
+      observer_receiver_.BindNewPipeAndPassRemote(task_runner));
 }
 
 void BackgroundFetchRegistration::OnProgress(
@@ -384,7 +379,7 @@
 }
 
 void BackgroundFetchRegistration::Dispose() {
-  observer_binding_.Close();
+  observer_receiver_.reset();
 }
 
 bool BackgroundFetchRegistration::HasPendingActivity() const {
@@ -396,6 +391,15 @@
   return !observers_.IsEmpty();
 }
 
+void BackgroundFetchRegistration::UpdateUI(
+    const String& in_title,
+    const SkBitmap& in_icon,
+    mojom::blink::BackgroundFetchRegistrationService::UpdateUICallback
+        callback) {
+  DCHECK(registration_service_);
+  registration_service_->UpdateUI(in_title, in_icon, std::move(callback));
+}
+
 void BackgroundFetchRegistration::Trace(Visitor* visitor) {
   visitor->Trace(registration_);
   visitor->Trace(observers_);
diff --git a/third_party/blink/renderer/modules/background_fetch/background_fetch_registration.h b/third_party/blink/renderer/modules/background_fetch/background_fetch_registration.h
index 2ecb70a..6addfd1 100644
--- a/third_party/blink/renderer/modules/background_fetch/background_fetch_registration.h
+++ b/third_party/blink/renderer/modules/background_fetch/background_fetch_registration.h
@@ -5,7 +5,8 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_BACKGROUND_FETCH_BACKGROUND_FETCH_REGISTRATION_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_BACKGROUND_FETCH_BACKGROUND_FETCH_REGISTRATION_H_
 
-#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "third_party/blink/public/mojom/background_fetch/background_fetch.mojom-blink.h"
 #include "third_party/blink/renderer/bindings/core/v8/active_script_wrappable.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
@@ -55,7 +56,8 @@
   // progress events, powering the `progress` JavaScript event.
   void Initialize(
       ServiceWorkerRegistration* registration,
-      mojom::blink::BackgroundFetchRegistrationServicePtr registration_service);
+      mojo::PendingRemote<mojom::blink::BackgroundFetchRegistrationService>
+          registration_service);
 
   // BackgroundFetchRegistrationObserver implementation.
   void OnProgress(uint64_t upload_total,
@@ -108,10 +110,11 @@
   // Keeps the object alive until there are non-zero number of |observers_|.
   bool HasPendingActivity() const final;
 
-  const mojom::blink::BackgroundFetchRegistrationServicePtr&
-  GetRegistrationService() const {
-    return registration_service_;
-  }
+  void UpdateUI(
+      const String& in_title,
+      const SkBitmap& in_icon,
+      mojom::blink::BackgroundFetchRegistrationService::UpdateUICallback
+          callback);
 
  private:
   void DidAbort(ScriptPromiseResolver* resolver,
@@ -149,10 +152,11 @@
   mojom::BackgroundFetchFailureReason failure_reason_;
   HeapVector<Member<BackgroundFetchRecord>> observers_;
 
-  mojom::blink::BackgroundFetchRegistrationServicePtr registration_service_;
+  mojo::Remote<mojom::blink::BackgroundFetchRegistrationService>
+      registration_service_;
 
-  mojo::Binding<blink::mojom::blink::BackgroundFetchRegistrationObserver>
-      observer_binding_;
+  mojo::Receiver<blink::mojom::blink::BackgroundFetchRegistrationObserver>
+      observer_receiver_{this};
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/background_fetch/background_fetch_update_ui_event.cc b/third_party/blink/renderer/modules/background_fetch/background_fetch_update_ui_event.cc
index d804b216..f74dda6 100644
--- a/third_party/blink/renderer/modules/background_fetch/background_fetch_update_ui_event.cc
+++ b/third_party/blink/renderer/modules/background_fetch/background_fetch_update_ui_event.cc
@@ -100,7 +100,7 @@
     const String& title,
     const SkBitmap& icon,
     int64_t ideal_to_chosen_icon_size) {
-  registration()->GetRegistrationService()->UpdateUI(
+  registration()->UpdateUI(
       title, icon,
       WTF::Bind(&BackgroundFetchUpdateUIEvent::DidUpdateUI,
                 WrapPersistent(this), WrapPersistent(resolver)));
diff --git a/third_party/blink/renderer/modules/credentialmanager/password_credential_test.cc b/third_party/blink/renderer/modules/credentialmanager/password_credential_test.cc
index a915b71..2d7d62bc 100644
--- a/third_party/blink/renderer/modules/credentialmanager/password_credential_test.cc
+++ b/third_party/blink/renderer/modules/credentialmanager/password_credential_test.cc
@@ -29,7 +29,7 @@
     b.Append(html);
     b.Append("</form></body></html>");
     SetHtmlInnerHTML(b.ToString().Utf8());
-    HTMLFormElement* form = ToHTMLFormElement(GetElementById("theForm"));
+    auto* form = To<HTMLFormElement>(GetElementById("theForm"));
     EXPECT_NE(nullptr, form);
     return form;
   }
diff --git a/third_party/blink/renderer/modules/webaudio/audio_graph_tracer.cc b/third_party/blink/renderer/modules/webaudio/audio_graph_tracer.cc
index 99a1856..82fd3202 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_graph_tracer.cc
+++ b/third_party/blink/renderer/modules/webaudio/audio_graph_tracer.cc
@@ -7,6 +7,9 @@
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/page/page.h"
+#include "third_party/blink/renderer/modules/webaudio/audio_listener.h"
+#include "third_party/blink/renderer/modules/webaudio/audio_node.h"
+#include "third_party/blink/renderer/modules/webaudio/audio_param.h"
 #include "third_party/blink/renderer/modules/webaudio/base_audio_context.h"
 #include "third_party/blink/renderer/modules/webaudio/inspector_web_audio_agent.h"
 
@@ -67,6 +70,77 @@
   return nullptr;
 }
 
+void AudioGraphTracer::DidCreateAudioListener(AudioListener* listener) {
+  if (inspector_agent_)
+    inspector_agent_->DidCreateAudioListener(listener);
+}
+
+void AudioGraphTracer::WillDestroyAudioListener(AudioListener* listener) {
+  if (inspector_agent_)
+    inspector_agent_->WillDestroyAudioListener(listener);
+}
+
+void AudioGraphTracer::DidCreateAudioNode(AudioNode* node) {
+  if (inspector_agent_)
+    inspector_agent_->DidCreateAudioNode(node);
+}
+
+void AudioGraphTracer::WillDestroyAudioNode(AudioNode* node) {
+  if (inspector_agent_ && contexts_.Contains(node->context()))
+    inspector_agent_->WillDestroyAudioNode(node);
+}
+
+void AudioGraphTracer::DidCreateAudioParam(AudioParam* param) {
+  if (inspector_agent_)
+    inspector_agent_->DidCreateAudioParam(param);
+}
+
+void AudioGraphTracer::WillDestroyAudioParam(AudioParam* param) {
+  if (inspector_agent_ && contexts_.Contains(param->Context()))
+    inspector_agent_->WillDestroyAudioParam(param);
+}
+
+void AudioGraphTracer::DidConnectNodes(AudioNode* source_node,
+                                       AudioNode* destination_node,
+                                       unsigned source_output_index,
+                                       unsigned destination_input_index) {
+  if (inspector_agent_) {
+    inspector_agent_->DidConnectNodes(source_node, destination_node,
+        source_output_index, destination_input_index);
+  }
+}
+
+void AudioGraphTracer::DidDisconnectNodes(
+    AudioNode* source_node,
+    AudioNode* destination_node,
+    unsigned source_output_index,
+    unsigned destination_input_index) {
+  if (inspector_agent_) {
+    inspector_agent_->DidDisconnectNodes(source_node, destination_node,
+        source_output_index, destination_input_index);
+  }
+}
+
+void AudioGraphTracer::DidConnectNodeParam(
+    AudioNode* source_node,
+    AudioParam* destination_param,
+    unsigned source_output_index) {
+  if (inspector_agent_) {
+    inspector_agent_->DidConnectNodeParam(source_node, destination_param,
+        source_output_index);
+  }
+}
+
+void AudioGraphTracer::DidDisconnectNodeParam(
+    AudioNode* source_node,
+    AudioParam* destination_param,
+    unsigned source_output_index) {
+  if (inspector_agent_) {
+    inspector_agent_->DidDisconnectNodeParam(source_node, destination_param,
+        source_output_index);
+  }
+}
+
 AudioGraphTracer* AudioGraphTracer::FromPage(Page* page) {
   return Supplement<Page>::From<AudioGraphTracer>(page);
 }
diff --git a/third_party/blink/renderer/modules/webaudio/audio_graph_tracer.h b/third_party/blink/renderer/modules/webaudio/audio_graph_tracer.h
index 1267bc7c..4cc03fe 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_graph_tracer.h
+++ b/third_party/blink/renderer/modules/webaudio/audio_graph_tracer.h
@@ -42,29 +42,29 @@
   // AudioParam.
   void DidCreateBaseAudioContext(BaseAudioContext*);
   void WillDestroyBaseAudioContext(BaseAudioContext*);
-  void DidCreateAudioListener(AudioListener*) {}
-  void WillDestroyAudioListener(AudioListener*) {}
-  void DidCreateAudioNode(AudioNode*) {}
-  void WillDestroyAudioNode(AudioNode*) {}
-  void DidCreateAudioParam(AudioParam*) {}
-  void WillDestroyAudioParam(AudioParam*) {}
+  void DidCreateAudioListener(AudioListener*);
+  void WillDestroyAudioListener(AudioListener*);
+  void DidCreateAudioNode(AudioNode*);
+  void WillDestroyAudioNode(AudioNode*);
+  void DidCreateAudioParam(AudioParam*);
+  void WillDestroyAudioParam(AudioParam*);
 
   // Graph connection events: notifies an associated inspector agent about
   // when a connection between graph objects happens.
   void DidConnectNodes(AudioNode* source_node,
                        AudioNode* destination_node,
                        unsigned source_output_index = 0,
-                       unsigned destination_input_index = 0) {}
+                       unsigned destination_input_index = 0);
   void DidDisconnectNodes(AudioNode* source_node,
                           AudioNode* destination_node = nullptr,
                           unsigned source_output_index = 0,
-                          unsigned destination_input_index = 0) {}
+                          unsigned destination_input_index = 0);
   void DidConnectNodeParam(AudioNode* source_node,
                            AudioParam* destination_param,
-                           unsigned source_output_index = 0) {}
+                           unsigned source_output_index = 0);
   void DidDisconnectNodeParam(AudioNode* source_node,
                               AudioParam* destination_param,
-                              unsigned source_output_index = 0) {}
+                              unsigned source_output_index = 0);
 
   // Notifies an associated inspector agent when a BaseAudioContext is changed.
   void DidChangeBaseAudioContext(BaseAudioContext*);
diff --git a/third_party/blink/renderer/modules/webaudio/audio_node.h b/third_party/blink/renderer/modules/webaudio/audio_node.h
index 2d335e4..f9c9b06 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_node.h
+++ b/third_party/blink/renderer/modules/webaudio/audio_node.h
@@ -333,6 +333,7 @@
   AudioHandler& Handler() const;
 
   void HandleChannelOptions(const AudioNodeOptions*, ExceptionState&);
+  String GetNodeName() const { return Handler().NodeTypeName(); }
 
   AudioNode* connect(AudioNode*,
                      unsigned output_index,
diff --git a/third_party/blink/renderer/modules/webaudio/audio_param.cc b/third_party/blink/renderer/modules/webaudio/audio_param.cc
index a455cbe..961362a 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_param.cc
+++ b/third_party/blink/renderer/modules/webaudio/audio_param.cc
@@ -93,7 +93,7 @@
     case kParamTypeBiquadFilterQ:
       return "BiquadFilter.Q";
     case kParamTypeBiquadFilterGain:
-      return "BiquadFilter.Gain";
+      return "BiquadFilter.gain";
     case kParamTypeBiquadFilterDetune:
       return "BiquadFilter.detune";
     case kParamTypeDelayDelayTime:
diff --git a/third_party/blink/renderer/modules/webaudio/audio_param.h b/third_party/blink/renderer/modules/webaudio/audio_param.h
index 299f12f..08e6c8c2 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_param.h
+++ b/third_party/blink/renderer/modules/webaudio/audio_param.h
@@ -291,6 +291,7 @@
   AudioParamHandler::AudioParamType GetParamType() const {
     return Handler().GetParamType();
   }
+  String GetParamName() const { return Handler().GetParamName(); }
   void SetParamType(AudioParamHandler::AudioParamType);
   void SetCustomParamName(const String name);
 
diff --git a/third_party/blink/renderer/modules/webaudio/audio_worklet_global_scope_test.cc b/third_party/blink/renderer/modules/webaudio/audio_worklet_global_scope_test.cc
index 190b3381..b3a7fcc 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_worklet_global_scope_test.cc
+++ b/third_party/blink/renderer/modules/webaudio/audio_worklet_global_scope_test.cc
@@ -140,7 +140,7 @@
         ScriptFetchOptions(), TextPosition::MinimumPosition(),
         ASSERT_NO_EXCEPTION);
     EXPECT_FALSE(module.IsNull());
-    ScriptValue exception = module.Instantiate(script_state);
+    ScriptValue exception = module.Instantiate(script_state, js_url);
     EXPECT_TRUE(exception.IsEmpty());
     ScriptValue value = module.Evaluate(script_state);
     return value.IsEmpty();
diff --git a/third_party/blink/renderer/modules/webaudio/audio_worklet_thread_test.cc b/third_party/blink/renderer/modules/webaudio/audio_worklet_thread_test.cc
index 3b2aa18..b37720f 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_worklet_thread_test.cc
+++ b/third_party/blink/renderer/modules/webaudio/audio_worklet_thread_test.cc
@@ -90,7 +90,7 @@
         js_url, ScriptFetchOptions(), TextPosition::MinimumPosition(),
         ASSERT_NO_EXCEPTION);
     EXPECT_FALSE(module.IsNull());
-    ScriptValue exception = module.Instantiate(script_state);
+    ScriptValue exception = module.Instantiate(script_state, js_url);
     EXPECT_TRUE(exception.IsEmpty());
     ScriptValue value = module.Evaluate(script_state);
     EXPECT_TRUE(value.IsEmpty());
diff --git a/third_party/blink/renderer/modules/webaudio/inspector_web_audio_agent.cc b/third_party/blink/renderer/modules/webaudio/inspector_web_audio_agent.cc
index bc85ec1..533ac430 100644
--- a/third_party/blink/renderer/modules/webaudio/inspector_web_audio_agent.cc
+++ b/third_party/blink/renderer/modules/webaudio/inspector_web_audio_agent.cc
@@ -10,6 +10,9 @@
 #include "third_party/blink/renderer/modules/webaudio/base_audio_context.h"
 #include "third_party/blink/renderer/modules/webaudio/audio_context.h"
 #include "third_party/blink/renderer/modules/webaudio/audio_graph_tracer.h"
+#include "third_party/blink/renderer/modules/webaudio/audio_listener.h"
+#include "third_party/blink/renderer/modules/webaudio/audio_node.h"
+#include "third_party/blink/renderer/modules/webaudio/audio_param.h"
 
 namespace blink {
 
@@ -35,6 +38,21 @@
   }
 }
 
+// Strips "Node" from the node name string. For example, "GainNode" will return
+// "Gain".
+String StripNodeSuffix(const String& nodeName) {
+  return nodeName.EndsWith("Node") ? nodeName.Left(nodeName.length() - 4)
+                                   : "Unknown";
+}
+
+// Strips out the prefix and returns the actual parameter name. If the name
+// does not match |NodeName.ParamName| pattern, returns "Unknown" instead.
+String StripParamPrefix(const String& paramName) {
+  Vector<String> name_tokens;
+  paramName.Split('.', name_tokens);
+  return name_tokens.size() == 2 ? name_tokens.at(1) : "Unknown";
+}
+
 }  // namespace
 
 using protocol::Response;
@@ -73,7 +91,7 @@
 }
 
 Response InspectorWebAudioAgent::getRealtimeData(
-    const protocol::WebAudio::ContextId& contextId,
+    const protocol::WebAudio::GraphObjectId& contextId,
     std::unique_ptr<ContextRealtimeData>* out_data) {
   auto* const graph_tracer = AudioGraphTracer::FromPage(page_);
   if (!enabled_.Get())
@@ -115,6 +133,104 @@
   GetFrontend()->contextChanged(BuildProtocolContext(context));
 }
 
+void InspectorWebAudioAgent::DidCreateAudioListener(AudioListener* listener) {
+  GetFrontend()->audioListenerCreated(
+      protocol::WebAudio::AudioListener::create()
+          .setListenerId(listener->Uuid())
+          .setContextId(listener->ParentUuid())
+          .build());
+}
+
+void InspectorWebAudioAgent::WillDestroyAudioListener(AudioListener* listener) {
+  GetFrontend()->audioListenerWillBeDestroyed(
+      listener->ParentUuid(), listener->Uuid());
+}
+
+void InspectorWebAudioAgent::DidCreateAudioNode(AudioNode* node) {
+  GetFrontend()->audioNodeCreated(
+      protocol::WebAudio::AudioNode::create()
+          .setNodeId(node->Uuid())
+          .setNodeType(StripNodeSuffix(node->GetNodeName()))
+          .setNumberOfInputs(node->numberOfInputs())
+          .setNumberOfOutputs(node->numberOfOutputs())
+          .setChannelCount(node->channelCount())
+          .setChannelCountMode(node->channelCountMode())
+          .setChannelInterpretation(node->channelInterpretation())
+          .setContextId(node->ParentUuid())
+          .build());
+}
+
+void InspectorWebAudioAgent::WillDestroyAudioNode(AudioNode* node) {
+  GetFrontend()->audioNodeWillBeDestroyed(node->ParentUuid(), node->Uuid());
+}
+
+void InspectorWebAudioAgent::DidCreateAudioParam(AudioParam* param) {
+  GetFrontend()->audioParamCreated(
+      protocol::WebAudio::AudioParam::create()
+          .setParamId(param->Uuid())
+          .setParamType(StripParamPrefix(param->GetParamName()))
+          .setRate(param->automationRate())
+          .setDefaultValue(param->defaultValue())
+          .setMinValue(param->minValue())
+          .setMaxValue(param->maxValue())
+          .setContextId(param->Context()->Uuid())
+          .setNodeId(param->ParentUuid())
+          .build());
+}
+
+void InspectorWebAudioAgent::WillDestroyAudioParam(AudioParam* param) {
+  GetFrontend()->audioParamWillBeDestroyed(
+      param->Context()->Uuid(), param->ParentUuid(), param->Uuid());
+}
+
+void InspectorWebAudioAgent::DidConnectNodes(
+    AudioNode* source_node,
+    AudioNode* destination_node,
+    int32_t source_output_index,
+    int32_t destination_input_index) {
+  GetFrontend()->nodesConnected(
+      source_node->ParentUuid(),
+      source_node->Uuid(),
+      destination_node->Uuid(),
+      source_output_index,
+      destination_input_index);
+}
+
+void InspectorWebAudioAgent::DidDisconnectNodes(
+    AudioNode* source_node,
+    AudioNode* destination_node,
+    int32_t source_output_index,
+    int32_t destination_input_index) {
+  GetFrontend()->nodesDisconnected(
+      source_node->ParentUuid(),
+      source_node->Uuid(),
+      destination_node ? destination_node->Uuid() : String(),
+      source_output_index,
+      destination_input_index);
+}
+
+void InspectorWebAudioAgent::DidConnectNodeParam(
+    AudioNode* source_node,
+    AudioParam* destination_param,
+    int32_t source_output_index) {
+  GetFrontend()->nodeParamConnected(
+      source_node->ParentUuid(),
+      source_node->Uuid(),
+      destination_param->Uuid(),
+      source_output_index);
+}
+
+void InspectorWebAudioAgent::DidDisconnectNodeParam(
+    AudioNode* source_node,
+    AudioParam* destination_param,
+    int32_t source_output_index) {
+  GetFrontend()->nodeParamDisconnected(
+      source_node->ParentUuid(),
+      source_node->Uuid(),
+      destination_param->Uuid(),
+      source_output_index);
+}
+
 std::unique_ptr<protocol::WebAudio::BaseAudioContext>
 InspectorWebAudioAgent::BuildProtocolContext(BaseAudioContext* context) {
   return protocol::WebAudio::BaseAudioContext::create()
diff --git a/third_party/blink/renderer/modules/webaudio/inspector_web_audio_agent.h b/third_party/blink/renderer/modules/webaudio/inspector_web_audio_agent.h
index cad96395..506299d6 100644
--- a/third_party/blink/renderer/modules/webaudio/inspector_web_audio_agent.h
+++ b/third_party/blink/renderer/modules/webaudio/inspector_web_audio_agent.h
@@ -12,6 +12,9 @@
 
 namespace blink {
 
+class AudioListener;
+class AudioNode;
+class AudioParam;
 class BaseAudioContext;
 class Page;
 
@@ -30,13 +33,33 @@
   protocol::Response enable() override;
   protocol::Response disable() override;
   protocol::Response getRealtimeData(
-      const protocol::WebAudio::ContextId&,
+      const protocol::WebAudio::GraphObjectId&,
       std::unique_ptr<ContextRealtimeData>*) override;
 
   // API for InspectorInstrumentation (modules/webaudio -> agent)
   void DidCreateBaseAudioContext(BaseAudioContext*);
   void WillDestroyBaseAudioContext(BaseAudioContext*);
   void DidChangeBaseAudioContext(BaseAudioContext*);
+  void DidCreateAudioListener(AudioListener*);
+  void WillDestroyAudioListener(AudioListener*);
+  void DidCreateAudioNode(AudioNode*);
+  void WillDestroyAudioNode(AudioNode*);
+  void DidCreateAudioParam(AudioParam*);
+  void WillDestroyAudioParam(AudioParam*);
+  void DidConnectNodes(AudioNode* source_node,
+                       AudioNode* destination_node,
+                       int32_t source_output_index = 0,
+                       int32_t destination_input_index = 0);
+  void DidDisconnectNodes(AudioNode* source_node,
+                          AudioNode* destination_node = nullptr,
+                          int32_t source_output_index = 0,
+                          int32_t destination_input_index = 0);
+  void DidConnectNodeParam(AudioNode* source_node,
+                           AudioParam* destination_param,
+                           int32_t source_output_index = 0);
+  void DidDisconnectNodeParam(AudioNode* source_node,
+                              AudioParam* destination_param,
+                              int32_t source_output_index = 0);
 
   void Trace(blink::Visitor*) override;
 
diff --git a/third_party/blink/renderer/modules/worklet/animation_and_paint_worklet_thread_test.cc b/third_party/blink/renderer/modules/worklet/animation_and_paint_worklet_thread_test.cc
index cd4aa2f..b0a9dce 100644
--- a/third_party/blink/renderer/modules/worklet/animation_and_paint_worklet_thread_test.cc
+++ b/third_party/blink/renderer/modules/worklet/animation_and_paint_worklet_thread_test.cc
@@ -78,7 +78,7 @@
         js_url, ScriptFetchOptions(), TextPosition::MinimumPosition(),
         ASSERT_NO_EXCEPTION);
     EXPECT_FALSE(module.IsNull());
-    ScriptValue exception = module.Instantiate(script_state);
+    ScriptValue exception = module.Instantiate(script_state, js_url);
     EXPECT_TRUE(exception.IsEmpty());
     ScriptValue value = module.Evaluate(script_state);
     EXPECT_TRUE(value.IsEmpty());
diff --git a/third_party/blink/renderer/modules/xr/xr.cc b/third_party/blink/renderer/modules/xr/xr.cc
index cd8d2a4..88b1274 100644
--- a/third_party/blink/renderer/modules/xr/xr.cc
+++ b/third_party/blink/renderer/modules/xr/xr.cc
@@ -76,6 +76,22 @@
   return XRSession::kModeInline;
 }
 
+XRSessionFeature stringToSessionFeature(const String& feature_string) {
+  if (feature_string == "viewer") {
+    return XRSessionFeature::kViewer;
+  } else if (feature_string == "local") {
+    return XRSessionFeature::kLocal;
+  } else if (feature_string == "local-floor") {
+    return XRSessionFeature::kLocalFloor;
+  } else if (feature_string == "bounded-floor") {
+    return XRSessionFeature::kBoundedFloor;
+  } else if (feature_string == "unbounded") {
+    return XRSessionFeature::kUnbounded;
+  }
+
+  return XRSessionFeature::kUnknownFeature;
+}
+
 // When updating this list, also update XRRuntimeManager's
 // AreArFeaturesEnabled() until https://crbug.com/966647 is fixed.
 // TODO(https://crbug.com/966647) remove the above comment when fixed.
@@ -139,10 +155,12 @@
     int64_t ukm_source_id,
     ScriptPromiseResolver* resolver,
     XRSession::SessionMode session_mode,
-    XRSessionInit* session_init)
+    const WTF::HashSet<XRSessionFeature>& required_features,
+    const WTF::HashSet<XRSessionFeature>& optional_features)
     : resolver_(resolver),
       mode_(session_mode),
-      session_init_(session_init),
+      required_features_(required_features),
+      optional_features_(optional_features),
       ukm_source_id_(ukm_source_id) {}
 
 void XR::PendingRequestSessionQuery::Resolve(XRSession* session) {
@@ -164,8 +182,14 @@
   return mode_;
 }
 
-const XRSessionInit* XR::PendingRequestSessionQuery::SessionInit() const {
-  return session_init_;
+const WTF::HashSet<XRSessionFeature>&
+XR::PendingRequestSessionQuery::RequiredFeatures() const {
+  return required_features_;
+}
+
+const WTF::HashSet<XRSessionFeature>&
+XR::PendingRequestSessionQuery::OptionalFeatures() const {
+  return optional_features_;
 }
 
 ScriptState* XR::PendingRequestSessionQuery::GetScriptState() const {
@@ -174,7 +198,6 @@
 
 void XR::PendingRequestSessionQuery::Trace(blink::Visitor* visitor) {
   visitor->Trace(resolver_);
-  visitor->Trace(session_init_);
 }
 
 void XR::PendingRequestSessionQuery::ReportRequestSessionResult(
@@ -351,9 +374,45 @@
   auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
   ScriptPromise promise = resolver->Promise();
 
+  WTF::HashSet<XRSessionFeature> required_features;
+  if (session_init && session_init->hasRequiredFeatures()) {
+    for (const auto& feature : session_init->requiredFeatures()) {
+      // Add all features so that we can track the presence of an unknown
+      // required feature later, to reject it at the appropriate time.
+      auto feature_enum = stringToSessionFeature(feature);
+      required_features.insert(feature_enum);
+
+      if (feature_enum == XRSessionFeature::kUnknownFeature) {
+        GetExecutionContext()->AddConsoleMessage(ConsoleMessage::Create(
+            mojom::ConsoleMessageSource::kJavaScript,
+            mojom::ConsoleMessageLevel::kError,
+            "Unrecognized required feature requested: " + feature));
+      }
+    }
+  }
+
+  WTF::HashSet<XRSessionFeature> optional_features;
+  if (session_init && session_init->hasOptionalFeatures()) {
+    for (const auto& feature : session_init->optionalFeatures()) {
+      auto feature_enum = stringToSessionFeature(feature);
+
+      // Unrecognized optional features can be silently ignored, as they won't
+      // be supported.
+      if (feature_enum != XRSessionFeature::kUnknownFeature) {
+        optional_features.insert(feature_enum);
+      } else {
+        GetExecutionContext()->AddConsoleMessage(ConsoleMessage::Create(
+            mojom::ConsoleMessageSource::kJavaScript,
+            mojom::ConsoleMessageLevel::kWarning,
+            "Unrecognized optional feature requested: " + feature));
+      }
+    }
+  }
+
   PendingRequestSessionQuery* query =
       MakeGarbageCollected<PendingRequestSessionQuery>(
-          GetSourceId(), resolver, session_mode, session_init);
+          GetSourceId(), resolver, session_mode, required_features,
+          optional_features);
 
   if (session_mode == XRSession::kModeImmersiveAR &&
       !AreArRuntimeFeaturesEnabled(doc)) {
@@ -427,6 +486,12 @@
     return promise;
   }
 
+  if (query->RequiredFeatures().Contains(XRSessionFeature::kUnknownFeature)) {
+    query->Reject(MakeGarbageCollected<DOMException>(
+        DOMExceptionCode::kNotSupportedError, kSessionNotSupported));
+    return promise;
+  }
+
   device::mojom::blink::XRSessionOptionsPtr session_options =
       convertModeToMojo(query->mode());
 
@@ -505,9 +570,31 @@
   if (environment_integration)
     blend_mode = XRSession::kBlendModeAlphaBlend;
 
+  // TODO(crbug.com/991605): enabled_features should be coming back on something
+  // in the session request result.  Which includes populating any granted
+  // implied optional features.
+  WTF::HashSet<XRSessionFeature> enabled_features = query->RequiredFeatures();
+  for (const auto& feature : query->OptionalFeatures()) {
+    enabled_features.insert(feature);
+  }
+
+  // Add any implied optional features per the spec.
+  switch (query->mode()) {
+    case XRSession::kModeImmersiveVR:
+      enabled_features.insert(XRSessionFeature::kLocal);
+      FALLTHROUGH;
+    case XRSession::kModeInline:
+      enabled_features.insert(XRSessionFeature::kViewer);
+      break;
+    default:
+      break;
+  }
+
+  DCHECK(!enabled_features.Contains(XRSessionFeature::kUnknownFeature));
   XRSession* session = CreateSession(
       query->mode(), blend_mode, std::move(session_ptr->client_request),
-      std::move(session_ptr->display_info), session_ptr->uses_input_eventing);
+      std::move(session_ptr->display_info), session_ptr->uses_input_eventing,
+      enabled_features);
 
   if (query->mode() == XRSession::kModeImmersiveVR ||
       query->mode() == XRSession::kModeImmersiveAR) {
@@ -583,10 +670,11 @@
     device::mojom::blink::XRSessionClientRequest client_request,
     device::mojom::blink::VRDisplayInfoPtr display_info,
     bool uses_input_eventing,
+    const WTF::HashSet<XRSessionFeature>& enabled_features,
     bool sensorless_session) {
   XRSession* session = MakeGarbageCollected<XRSession>(
       this, client_request ? std::move(client_request) : nullptr, mode,
-      blend_mode, uses_input_eventing, sensorless_session);
+      blend_mode, uses_input_eventing, sensorless_session, enabled_features);
   if (display_info)
     session->SetXRDisplayInfo(std::move(display_info));
   sessions_.insert(session);
@@ -596,10 +684,10 @@
 XRSession* XR::CreateSensorlessInlineSession() {
   // TODO(https://crbug.com/944936): The blend mode could be "additive".
   XRSession::EnvironmentBlendMode blend_mode = XRSession::kBlendModeOpaque;
-  return CreateSession(XRSession::kModeInline, blend_mode,
-                       nullptr /* client request */, nullptr /* display_info */,
-                       false /* uses_input_eventing */,
-                       true /* sensorless_session */);
+  return CreateSession(
+      XRSession::kModeInline, blend_mode, nullptr /* client request */,
+      nullptr /* display_info */, false /* uses_input_eventing */,
+      {XRSessionFeature::kViewer}, true /* sensorless_session */);
 }
 
 void XR::Dispose() {
diff --git a/third_party/blink/renderer/modules/xr/xr.h b/third_party/blink/renderer/modules/xr/xr.h
index 82056b48..7662e25 100644
--- a/third_party/blink/renderer/modules/xr/xr.h
+++ b/third_party/blink/renderer/modules/xr/xr.h
@@ -24,6 +24,16 @@
 class XRFrameProvider;
 class XRSessionInit;
 
+enum class XRSessionFeature {
+  kInvalid = 0,
+  kUnknownFeature,
+  kViewer,
+  kLocal,
+  kLocalFloor,
+  kBoundedFloor,
+  kUnbounded,
+};
+
 class XR final : public EventTargetWithInlineData,
                  public ContextLifecycleObserver,
                  public device::mojom::blink::VRServiceClient,
@@ -95,12 +105,14 @@
   // ScriptPromiseResolver that allows us to add additional logic as certain
   // things related to promise's life cycle happen.
   class PendingRequestSessionQuery final
-      : public GarbageCollected<PendingRequestSessionQuery> {
+      : public GarbageCollectedFinalized<PendingRequestSessionQuery> {
    public:
-    PendingRequestSessionQuery(int64_t ukm_source_id,
-                               ScriptPromiseResolver* resolver,
-                               XRSession::SessionMode mode,
-                               XRSessionInit*);
+    PendingRequestSessionQuery(
+        int64_t ukm_source_id,
+        ScriptPromiseResolver* resolver,
+        XRSession::SessionMode mode,
+        const WTF::HashSet<XRSessionFeature>& required_features,
+        const WTF::HashSet<XRSessionFeature>& optional_features);
     virtual ~PendingRequestSessionQuery() = default;
 
     // Resolves underlying promise with passed in XR session.
@@ -112,7 +124,8 @@
     void Reject(v8::Local<v8::Value> value);
 
     XRSession::SessionMode mode() const;
-    const XRSessionInit* SessionInit() const;
+    const WTF::HashSet<XRSessionFeature>& RequiredFeatures() const;
+    const WTF::HashSet<XRSessionFeature>& OptionalFeatures() const;
 
     // Returns underlying resolver's script state.
     ScriptState* GetScriptState() const;
@@ -124,7 +137,8 @@
 
     Member<ScriptPromiseResolver> resolver_;
     const XRSession::SessionMode mode_;
-    Member<XRSessionInit> session_init_;
+    WTF::HashSet<XRSessionFeature> required_features_;
+    WTF::HashSet<XRSessionFeature> optional_features_;
 
     const int64_t ukm_source_id_;
 
@@ -177,6 +191,7 @@
       device::mojom::blink::XRSessionClientRequest client_request,
       device::mojom::blink::VRDisplayInfoPtr display_info,
       bool uses_input_eventing,
+      const WTF::HashSet<XRSessionFeature>& enabled_features,
       bool sensorless_session = false);
   XRSession* CreateSensorlessInlineSession();
 
diff --git a/third_party/blink/renderer/modules/xr/xr_session.cc b/third_party/blink/renderer/modules/xr/xr_session.cc
index 4f9eba9..e032d0b 100644
--- a/third_party/blink/renderer/modules/xr/xr_session.cc
+++ b/third_party/blink/renderer/modules/xr/xr_session.cc
@@ -116,12 +116,14 @@
     XRSession::SessionMode mode,
     EnvironmentBlendMode environment_blend_mode,
     bool uses_input_eventing,
-    bool sensorless_session)
+    bool sensorless_session,
+    const WTF::HashSet<XRSessionFeature>& enabled_features)
     : xr_(xr),
       mode_(mode),
       environment_integration_(mode == kModeImmersiveAR),
       world_tracking_state_(MakeGarbageCollected<XRWorldTrackingState>()),
       world_information_(MakeGarbageCollected<XRWorldInformation>(this)),
+      enabled_features_(enabled_features),
       input_sources_(MakeGarbageCollected<XRInputSourceArray>()),
       client_binding_(this, std::move(client_request)),
       input_binding_(this),
diff --git a/third_party/blink/renderer/modules/xr/xr_session.h b/third_party/blink/renderer/modules/xr/xr_session.h
index aad56dd..4105210 100644
--- a/third_party/blink/renderer/modules/xr/xr_session.h
+++ b/third_party/blink/renderer/modules/xr/xr_session.h
@@ -43,6 +43,8 @@
 class XRWorldTrackingState;
 class XRWorldTrackingStateInit;
 
+enum class XRSessionFeature;
+
 class XRSession final
     : public EventTargetWithInlineData,
       public device::mojom::blink::XRSessionClient,
@@ -65,7 +67,8 @@
             SessionMode mode,
             EnvironmentBlendMode environment_blend_mode,
             bool uses_input_eventing,
-            bool sensorless_session);
+            bool sensorless_session,
+            const WTF::HashSet<XRSessionFeature>& enabled_features);
   ~XRSession() override = default;
 
   XR* xr() const { return xr_; }
@@ -234,6 +237,7 @@
   Member<XRWorldInformation> world_information_;
   HeapVector<Member<XRRenderStateInit>> pending_render_state_;
 
+  WTF::HashSet<XRSessionFeature> enabled_features_;
   WTF::Vector<XRViewData> views_;
 
   Member<XRInputSourceArray> input_sources_;
diff --git a/third_party/blink/renderer/platform/data_resource_helper.cc b/third_party/blink/renderer/platform/data_resource_helper.cc
index 92114883..454f3311 100644
--- a/third_party/blink/renderer/platform/data_resource_helper.cc
+++ b/third_party/blink/renderer/platform/data_resource_helper.cc
@@ -35,4 +35,13 @@
   return String::FromUTF8(uncompressed.data());
 }
 
+Vector<char> UncompressResourceAsBinary(int resource_id) {
+  ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
+  std::string uncompressed = bundle.DecompressDataResourceScaled(
+      resource_id, bundle.GetMaxScaleFactor());
+  Vector<char> result;
+  result.Append(uncompressed.data(), uncompressed.size());
+  return result;
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/data_resource_helper.h b/third_party/blink/renderer/platform/data_resource_helper.h
index 224d410..f6d84a8a 100644
--- a/third_party/blink/renderer/platform/data_resource_helper.h
+++ b/third_party/blink/renderer/platform/data_resource_helper.h
@@ -7,6 +7,7 @@
 
 #include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
 
 namespace blink {
 
@@ -20,6 +21,10 @@
 // is specified by the resource id from Grit.
 PLATFORM_EXPORT String UncompressResourceAsString(int resource_id);
 
+// Uncompresses a gzipped resource and returns it as a vector of characters.
+// The resource is specified by the resource id from Grit.
+PLATFORM_EXPORT Vector<char> UncompressResourceAsBinary(int resource_id);
+
 }  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_DATA_RESOURCE_HELPER_H_
diff --git a/third_party/blink/renderer/platform/instrumentation/instance_counters.cc b/third_party/blink/renderer/platform/instrumentation/instance_counters.cc
index 14d27b1..0337b003 100644
--- a/third_party/blink/renderer/platform/instrumentation/instance_counters.cc
+++ b/third_party/blink/renderer/platform/instrumentation/instance_counters.cc
@@ -39,6 +39,10 @@
 int InstanceCounters::node_counter_ = 0;
 
 int InstanceCounters::CounterValue(CounterType type) {
+  if (type == kNodeCounter) {
+    DCHECK(IsMainThread());
+    return node_counter_;
+  }
   return counters_[type].load(std::memory_order_relaxed);
 }
 
diff --git a/third_party/blink/renderer/platform/instrumentation/instance_counters.h b/third_party/blink/renderer/platform/instrumentation/instance_counters.h
index ccb12f2..23867b96 100644
--- a/third_party/blink/renderer/platform/instrumentation/instance_counters.h
+++ b/third_party/blink/renderer/platform/instrumentation/instance_counters.h
@@ -81,7 +81,7 @@
     // should be avoided for the sake of performance. See crbug.com/641019
     if (type == kNodeCounter) {
       DCHECK(IsMainThread());
-      ++counters_[kNodeCounter];
+      ++node_counter_;
     } else {
       counters_[type].fetch_add(1, std::memory_order_relaxed);
     }
@@ -90,7 +90,7 @@
   static inline void DecrementCounter(CounterType type) {
     if (type == kNodeCounter) {
       DCHECK(IsMainThread());
-      --counters_[kNodeCounter];
+      --node_counter_;
     } else {
       counters_[type].fetch_sub(1, std::memory_order_relaxed);
     }
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 1950e7b..d7b1bb4 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -2517,6 +2517,25 @@
 crbug.com/972992 external/wpt/css/css-text/white-space/white-space-pre-wrap-trailing-spaces-001.html [ Failure ]
 crbug.com/972992 external/wpt/css/css-text/white-space/white-space-pre-wrap-trailing-spaces-003.html [ Failure ]
 
+# Handling of trailing other spaces is not implemented yet.
+crbug.com/991413 external/wpt/css/css-text/white-space/trailing-other-space-separators-001.html [ Skip ]
+crbug.com/991413 external/wpt/css/css-text/white-space/trailing-other-space-separators-002.html [ Skip ]
+crbug.com/991413 external/wpt/css/css-text/white-space/trailing-other-space-separators-003.html [ Skip ]
+crbug.com/991413 external/wpt/css/css-text/white-space/trailing-other-space-separators-004.html [ Skip ]
+crbug.com/991413 external/wpt/css/css-text/white-space/trailing-other-space-separators-break-spaces-001.html [ Skip ]
+crbug.com/991413 external/wpt/css/css-text/white-space/trailing-other-space-separators-break-spaces-002.html [ Skip ]
+crbug.com/991413 external/wpt/css/css-text/white-space/trailing-other-space-separators-break-spaces-003.html [ Skip ]
+crbug.com/991413 external/wpt/css/css-text/white-space/trailing-other-space-separators-break-spaces-004.html [ Skip ]
+crbug.com/991413 external/wpt/css/css-text/white-space/trailing-other-space-separators-break-spaces-005.html [ Skip ]
+crbug.com/991413 external/wpt/css/css-text/white-space/trailing-other-space-separators-break-spaces-006.html [ Skip ]
+crbug.com/991413 external/wpt/css/css-text/white-space/trailing-other-space-separators-break-spaces-007.html [ Skip ]
+crbug.com/991413 external/wpt/css/css-text/white-space/trailing-other-space-separators-break-spaces-008.html [ Skip ]
+crbug.com/991413 external/wpt/css/css-text/white-space/trailing-other-space-separators-break-spaces-010.html [ Skip ]
+crbug.com/991413 external/wpt/css/css-text/white-space/trailing-other-space-separators-break-spaces-011.html [ Skip ]
+crbug.com/991413 external/wpt/css/css-text/white-space/trailing-other-space-separators-break-spaces-012.html [ Skip ]
+crbug.com/991413 external/wpt/css/css-text/white-space/trailing-other-space-separators-break-spaces-014.html [ Skip ]
+crbug.com/991413 external/wpt/css/css-text/white-space/trailing-other-space-separators-break-spaces-015.html [ Skip ]
+
 # The 'overflow-wrap: anywhere' feature is not implemented yet
 crbug.com/905315 external/wpt/css/css-text/overflow-wrap/overflow-wrap-anywhere-001.html [ Skip ]
 crbug.com/905315 external/wpt/css/css-text/overflow-wrap/overflow-wrap-anywhere-002.html [ Skip ]
@@ -2534,7 +2553,7 @@
 crbug.com/905315 external/wpt/css/css-text/white-space/break-spaces-before-first-char-003.html [ Skip ]
 crbug.com/905315 external/wpt/css/css-text/white-space/break-spaces-before-first-char-006.html [ Skip ]
 crbug.com/905315 external/wpt/css/css-text/white-space/break-spaces-before-first-char-013.html [ Skip ]
-crbug.com/905315 external/wpt/css/css-text/white-space/break-spaces-tab-004.html [ Failure ]
+crbug.com/905315 external/wpt/css/css-text/white-space/break-spaces-tab-004.html [ Skip ]
 
 crbug.com/6122 external/wpt/css/css-text/shaping/shaping-024.html [ Failure ]
 crbug.com/6122 external/wpt/css/css-text/shaping/shaping-025.html [ Failure ]
@@ -3324,33 +3343,21 @@
 crbug.com/991295 external/wpt/web-animations/timing-model/timelines/document-timelines.html [ Pass Failure ]
 
 # ====== New tests from wpt-importer added here ======
+crbug.com/626703 [ Linux ] external/wpt/css/css-text-decor/text-decoration-skip-ink-003.html [ Failure ]
+crbug.com/626703 [ Mac ] external/wpt/css/css-text-decor/text-decoration-skip-ink-003.html [ Failure ]
+crbug.com/626703 [ Win ] external/wpt/css/css-text-decor/text-decoration-skip-ink-003.html [ Failure ]
+crbug.com/626703 [ Mac10.13 ] external/wpt/webaudio/the-audio-api/the-mediastreamaudiosourcenode-interface/mediastreamaudiosourcenode-routing.html [ Timeout ]
+crbug.com/626703 [ Retina ] external/wpt/webaudio/the-audio-api/the-mediastreamaudiosourcenode-interface/mediastreamaudiosourcenode-routing.html [ Timeout ]
 crbug.com/626703 [ Win7 ] virtual/omt-worker-fetch/external/wpt/referrer-policy/unsafe-url/attr-referrer/same-origin/http-http/img-tag/keep-origin-redirect/generic.http.html [ Timeout ]
-crbug.com/626703 external/wpt/css/css-text/white-space/trailing-other-space-separators-break-spaces-015.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-text/line-break/line-break-anywhere-002.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-text/white-space/trailing-other-space-separators-002.html [ Failure ]
 crbug.com/626703 [ Win7 ] external/wpt/IndexedDB/structured-clone.any.html [ Timeout ]
-crbug.com/626703 external/wpt/css/css-text/white-space/trailing-other-space-separators-break-spaces-004.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-text/white-space/trailing-other-space-separators-break-spaces-011.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-text/white-space/trailing-other-space-separators-break-spaces-007.html [ Failure ]
 crbug.com/626703 [ Win7 ] virtual/not-omt-sw-fetch/external/wpt/referrer-policy/unsafe-url/attr-referrer/same-origin/http-http/img-tag/no-redirect/generic.http.html [ Timeout ]
 crbug.com/626703 [ Win7 ] virtual/omt-worker-fetch/external/wpt/referrer-policy/unset-referrer-policy/attr-referrer/cross-origin/http-http/img-tag/no-redirect/insecure-protocol.http.html [ Timeout ]
 crbug.com/626703 [ Win7 ] virtual/not-omt-sw-fetch/external/wpt/referrer-policy/unsafe-url/http-rp/same-origin/http-http/img-tag/swap-origin-redirect/generic.http.html [ Timeout ]
 crbug.com/626703 [ Win7 ] virtual/omt-worker-fetch/external/wpt/referrer-policy/unset-referrer-policy/attr-referrer/cross-origin/http-http/img-tag/keep-origin-redirect/insecure-protocol.http.html [ Timeout ]
-crbug.com/626703 external/wpt/css/css-text/white-space/trailing-other-space-separators-break-spaces-012.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-text/white-space/trailing-other-space-separators-break-spaces-010.html [ Failure ]
 crbug.com/626703 [ Win7 ] virtual/not-omt-sw-fetch/external/wpt/referrer-policy/strict-origin-when-cross-origin/attr-referrer/same-origin/http-http/img-tag/no-redirect/same-insecure.http.html [ Timeout ]
 crbug.com/626703 [ Win7 ] virtual/blink-cors/external/wpt/referrer-policy/unsafe-url/meta-referrer/same-origin/http-http/img-tag/keep-origin-redirect/generic.http.html [ Timeout ]
-crbug.com/626703 external/wpt/css/css-text/white-space/trailing-other-space-separators-break-spaces-006.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-text/white-space/trailing-other-space-separators-break-spaces-014.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-text/white-space/trailing-other-space-separators-001.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-text/white-space/trailing-other-space-separators-break-spaces-002.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-text/white-space/trailing-other-space-separators-004.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-text/white-space/trailing-other-space-separators-break-spaces-008.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-text/white-space/trailing-other-space-separators-break-spaces-001.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-text/white-space/trailing-other-space-separators-break-spaces-003.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-text/white-space/trailing-other-space-separators-break-spaces-005.html [ Failure ]
 crbug.com/626703 [ Win7 ] virtual/blink-cors/external/wpt/referrer-policy/unsafe-url/attr-referrer/same-origin/http-http/img-tag/swap-origin-redirect/generic.http.html [ Timeout ]
-crbug.com/626703 external/wpt/css/css-text/white-space/trailing-other-space-separators-003.html [ Failure ]
 crbug.com/626703 [ Mac10.12 ] external/wpt/html/semantics/embedded-content/media-elements/track/track-element/track-cues-sorted-before-dispatch.html [ Timeout ]
 crbug.com/626703 [ Mac10.12 ] external/wpt/mediacapture-fromelement/ended.html [ Timeout ]
 crbug.com/626703 [ Mac10.12 ] external/wpt/html/semantics/embedded-content/media-elements/track/track-element/track-cues-pause-on-exit.html [ Timeout ]
@@ -6034,9 +6041,7 @@
 # which is still in flux.
 crbug.com/924670 external/wpt/webxr/webxr-supported-by-feature-policy.html [ Failure ]
 
-# Chrome does not check for unknown required features and doesn't enforce
-# that apps need to ask for non-default reference spaces before using them.
-crbug.com/985095 external/wpt/webxr/xrDevice_requestSession_requiredFeatures_unknown.https.html [ Failure ]
+# Chrome does not enforce that apps need to ask for non-default reference spaces before using them.
 crbug.com/985095 external/wpt/webxr/xrSession_requestReferenceSpace_features.https.html [ Failure ]
 
 crbug.com/961015 [ Mac ] external/wpt/webxr/xrSession_requestAnimationFrame_callback_calls.https.html [ Timeout ]
diff --git a/third_party/blink/web_tests/accessibility/aom-element-internals-aria.html b/third_party/blink/web_tests/accessibility/aom-element-internals-aria.html
new file mode 100644
index 0000000..b850ee5
--- /dev/null
+++ b/third_party/blink/web_tests/accessibility/aom-element-internals-aria.html
@@ -0,0 +1,209 @@
+<!DOCTYPE HTML>
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+<body>
+
+<script>
+class TestElement extends HTMLElement {
+  constructor() {
+    super();
+    this._internals = this.attachInternals();
+  }
+
+  get internals() {
+    return this._internals;
+  }
+
+  set internals(val) {
+    throw "Can't set internals!";
+  }
+}
+customElements.define("test-element", TestElement);
+
+function createTestElement(id) {
+  const element = document.createElement("test-element");
+  element.id = id;
+  element.innerText = "foo";
+  document.body.appendChild(element);
+  return element;
+}
+
+function axElementById(id) {
+  return accessibilityController.accessibleElementById(id);
+}
+
+test(function() {
+  const element = createTestElement("ariaActiveDescendant");
+  element.role = "radiogroup"
+  const descendant = document.createElement("div");
+  descendant.id = "descendant";
+  descendant.role = "radio";
+  element.appendChild(descendant);
+  element.internals.ariaActiveDescendant = descendant.id;
+
+  const axElement = axElementById(element.id);
+  assert_equals(axElement.activeDescendant, axElementById(descendant.id),
+    "ariaActiveDescendant can be set through ElementInternals");
+
+  const descendant2 = document.createElement("div");
+  descendant2.id = "descendant2";
+  descendant2.role = "radio";
+  element.appendChild(descendant2);
+
+  element.internals.ariaActiveDescendant = descendant2.id;
+  assert_equals(axElement.activeDescendant, axElementById(descendant2.id),
+    "ariaActiveDescendant can change through ElementInternals");
+
+  element.ariaActiveDescendant = descendant.id;
+  assert_equals(axElement.activeDescendant, axElementById(descendant.id),
+    "ariaActiveDescendant value set on Element overrides value from ElementInternals");
+  element.remove();
+}, "aria-activedescendant");
+
+test(function() {
+  const element = createTestElement("ariaAtomic");
+  element.role = "status";
+  const axElement = axElementById(element.id);
+  element.internals.ariaAtomic = true;
+  assert_equals(axElement.isAtomic, true,
+    "ariaAtomic can be set through ElementInternals");
+
+  element.internals.ariaAtomic = "false";
+  assert_equals(axElement.isAtomic, false,
+    "ariaAtomic can change through ElementInternals");
+
+  element.ariaAtomic = "true";
+  assert_equals(axElement.isAtomic, true,
+    "ariaAtomic value set on Element overrides value from ElementInternals");
+  element.remove();
+}, "aria-atomic");
+
+test(function() {
+  const element = createTestElement("ariaBusy");
+  element.role = "status";
+  const axElement = axElementById(element.id);
+  element.internals.ariaBusy = true;
+  assert_equals(axElement.isBusy, true,
+    "ariaBusy can be set through ElementInternals");
+
+  element.internals.ariaBusy = "false";
+  assert_equals(axElement.isBusy, false,
+    "ariaBusy can change through ElementInternals");
+
+  element.ariaBusy = "true";
+  assert_equals(axElement.isBusy, true,
+    "ariaBusy value set on Element overrides value from ElementInternals");
+  element.remove();
+}, "aria-busy");
+
+test(function() {
+  const element = createTestElement("ariaChecked");
+  element.tabIndex = 0;
+  element.role = "checkbox";
+  element.innerText = "x";
+  element.internals.ariaChecked = "true";
+  const axElement = axElementById(element.id);
+  assert_equals(axElement.checked, "true",
+    "ariaChecked can be set through ElementInternals");
+
+  element.internals.ariaChecked = "false";
+  assert_equals(axElement.checked, "false",
+    "ariaChecked can change through ElementInternals");
+
+  element.toggleAttribute("checked", true);
+  assert_equals(axElement.checked, "false",
+    "checked attribute on element doesn't affect AX checked value");
+
+  element.internals.ariaChecked = "true";
+  assert_equals(axElement.checked, "true",
+    "ariaChecked value set on Element overrides value from ElementInternals");
+  element.remove();
+}, "aria-checked");
+
+test(function() {
+  const element = createTestElement("ariaCurrent");
+  element.tabIndex = 0;
+  element.role = "checkbox";
+  element.innerText = "x";
+  element.internals.ariaCurrent = "true";
+  const axElement = axElementById(element.id);
+  assert_equals(axElement.current, "true",
+    "ariaCurrent can be set through ElementInternals");
+
+  element.internals.ariaCurrent = "false";
+  assert_equals(axElement.current, "false",
+    "ariaCurrent can change through ElementInternals");
+
+  element.ariaCurrent = "date";
+  assert_equals(axElement.current, "date",
+    "ariaCurrent value set on Element overrides value from ElementInternals");
+  element.remove();
+}, "aria-current");
+
+test(function() {
+  const element = createTestElement("ariaHidden");
+  element.role = "status";
+  const axElement = axElementById(element.id);
+  element.internals.ariaHidden = true;
+  assert_equals(axElement.isIgnored, true,
+    "ariaHidden can be set through ElementInternals");
+
+  element.internals.ariaHidden = "false";
+  assert_equals(axElement.isIgnored, false,
+    "ariaHidden can change through ElementInternals");
+
+  element.ariaHidden = "true";
+  assert_equals(axElement.isIgnored, true,
+    "ariaHidden value set on Element overrides value from ElementInternals");
+  element.remove();
+}, "aria-hidden");
+
+test(function() {
+  const element = createTestElement("ariaValue");
+  element.role = "progressbar";
+  const axElement = axElementById(element.id);
+  element.internals.ariaValueMin = 1;
+  assert_equals(axElement.minValue, 1,
+    "ariaValueMin can be set through ElementInternals");
+
+  element.internals.ariaValueMax = 100;
+  assert_equals(axElement.maxValue, 100,
+    "ariaValueMax can be set through ElementInternals");
+
+  element.internals.ariaValueNow = 42;
+  assert_equals(axElement.intValue, 42,
+    "ariaValueNow can be set through ElementInternals");
+
+  element.internals.ariaValueText = "foo";
+  assert_equals(axElement.valueDescription, "AXValueDescription: foo",
+    "ariaValueText can be set through ElementInternals");
+  element.remove();
+}, "aria-value");
+
+test(function() {
+  const container = document.createElement("div");
+  container.role = "listbox";
+  document.body.appendChild(container);
+
+  const element = document.createElement("test-element");
+  element.role = "option";
+  container.appendChild(element);
+  element.setAttribute("aria-setsize", "2");
+  element.internals.ariaPosInSet = "1";
+  element.id = "posinset";
+  const axElement = axElementById(element.id);
+
+  assert_equals(axElement.setSize, 2,
+    "ariaSetSize can be set through ElementInternals");
+  assert_equals(axElement.posInSet, 1,
+    "ariaPosInSet can be set through ElementInternals");
+
+  element.ariaPosInSet = 2;
+  assert_equals(axElement.posInSet, 2,
+    "ariaPosInSet value set on Element overrides value from ElementInternals");
+  container.remove();
+}, "aria-posinset");
+
+
+</script>
+</body>
diff --git a/third_party/blink/web_tests/accessibility/aom-element-internals-role.html b/third_party/blink/web_tests/accessibility/aom-element-internals-role.html
new file mode 100644
index 0000000..a56f4fe
--- /dev/null
+++ b/third_party/blink/web_tests/accessibility/aom-element-internals-role.html
@@ -0,0 +1,63 @@
+<!DOCTYPE HTML>
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+<body>
+
+<script>
+class TestElement extends HTMLElement {
+  constructor() {
+    super();
+    this._internals = this.attachInternals();
+  }
+
+  get internals() {
+    return this._internals;
+  }
+
+  set internals(val) {
+    throw "Can't set internals!";
+  }
+}
+customElements.define("test-element", TestElement);
+
+function createTestElement(id) {
+  const element = document.createElement("test-element");
+  element.id = id;
+  document.body.appendChild(element);
+  return element;
+}
+
+function axElementById(id) {
+  return accessibilityController.accessibleElementById(id);
+}
+
+test(function(t) {
+  const element = createTestElement("test1");
+  element.internals.role = "button";
+  assert_equals(element.role, null);
+  assert_equals(element.internals.role, "button");
+  assert_equals(axElementById("test1").role, "AXRole: AXButton");
+}, "Role set through element internals affects role in AX object, doesn't affect element.role");
+
+test(function(t) {
+  const element = createTestElement("test2");
+  element.internals.role = "button";
+  assert_equals(element.role, null);
+  assert_equals(element.internals.role, "button");
+  assert_equals(axElementById("test2").role, "AXRole: AXButton");
+  element.role = "checkbox";
+  assert_equals(element.internals.role, "button");
+  assert_equals(axElementById("test2").role, "AXRole: AXCheckBox");
+}, "Role set through element internals can be overridden by element.role");
+
+test(function(t) {
+  const element = createTestElement("test3");
+  element.internals.role = "button";
+  assert_equals(axElementById("test3").role, "AXRole: AXButton");
+  element.internals.role = "checkbox";
+  assert_equals(element.internals.role, "checkbox");
+  assert_equals(axElementById("test3").role, "AXRole: AXCheckBox");;
+}, "Role set through element internals can be changed.");
+
+</script>
+</body>
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json
index fe2d77a3..e4d0f5b 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json
@@ -42385,6 +42385,18 @@
      {}
     ]
    ],
+   "css/css-flexbox/fit-content-item-001.html": [
+    [
+     "css/css-flexbox/fit-content-item-001.html",
+     [
+      [
+       "/css/reference/ref-filled-green-100px-square.xht",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-flexbox/flex-001.htm": [
     [
      "css/css-flexbox/flex-001.htm",
@@ -52643,18 +52655,6 @@
      {}
     ]
    ],
-   "css/css-layout-api/fallback-layout-invalid-fragment-request.https.html": [
-    [
-     "css/css-layout-api/fallback-layout-invalid-fragment-request.https.html",
-     [
-      [
-       "/css/css-layout-api/fallback-ref.html",
-       "=="
-      ]
-     ],
-     {}
-    ]
-   ],
    "css/css-layout-api/fallback-layout-invalid-fragment.https.html": [
     [
      "css/css-layout-api/fallback-layout-invalid-fragment.https.html",
@@ -52667,6 +52667,18 @@
      {}
     ]
    ],
+   "css/css-layout-api/fallback-layout-no-promise.https.html": [
+    [
+     "css/css-layout-api/fallback-layout-no-promise.https.html",
+     [
+      [
+       "/css/css-layout-api/fallback-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-layout-api/fallback-layout-return.https.html": [
     [
      "css/css-layout-api/fallback-layout-return.https.html",
@@ -52679,6 +52691,18 @@
      {}
     ]
    ],
+   "css/css-layout-api/fallback-layout-unresolved-promise.https.html": [
+    [
+     "css/css-layout-api/fallback-layout-unresolved-promise.https.html",
+     [
+      [
+       "/css/css-layout-api/fallback-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-layout-api/fragment-data-function-failure.https.html": [
     [
      "css/css-layout-api/fragment-data-function-failure.https.html",
@@ -64739,6 +64763,58 @@
      {}
     ]
    ],
+   "css/css-text-decor/text-decoration-skip-ink-001.html": [
+    [
+     "css/css-text-decor/text-decoration-skip-ink-001.html",
+     [
+      [
+       "/css/css-text-decor/reference/text-decoration-skip-ink-001-notref.html",
+       "!="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-text-decor/text-decoration-skip-ink-002.html": [
+    [
+     "css/css-text-decor/text-decoration-skip-ink-002.html",
+     [
+      [
+       "/css/css-text-decor/reference/text-decoration-skip-ink-002-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-text-decor/text-decoration-skip-ink-003.html": [
+    [
+     "css/css-text-decor/text-decoration-skip-ink-003.html",
+     [
+      [
+       "/css/css-text-decor/reference/text-decoration-skip-ink-003-ref.html",
+       "=="
+      ],
+      [
+       "/css/css-text-decor/reference/text-decoration-skip-ink-003-notref.html",
+       "!="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-text-decor/text-decoration-skip-ink-004.html": [
+    [
+     "css/css-text-decor/text-decoration-skip-ink-004.html",
+     [
+      [
+       "/css/css-text-decor/reference/text-decoration-skip-ink-004-notref.html",
+       "!="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-text-decor/text-decoration-style-multiple.html": [
     [
      "css/css-text-decor/text-decoration-style-multiple.html",
@@ -141943,6 +142019,21 @@
    "css/css-text-decor/reference/text-decoration-line-ref.html": [
     []
    ],
+   "css/css-text-decor/reference/text-decoration-skip-ink-001-notref.html": [
+    []
+   ],
+   "css/css-text-decor/reference/text-decoration-skip-ink-002-ref.html": [
+    []
+   ],
+   "css/css-text-decor/reference/text-decoration-skip-ink-003-notref.html": [
+    []
+   ],
+   "css/css-text-decor/reference/text-decoration-skip-ink-003-ref.html": [
+    []
+   ],
+   "css/css-text-decor/reference/text-decoration-skip-ink-004-notref.html": [
+    []
+   ],
    "css/css-text-decor/reference/text-decoration-style-multiple-ref.html": [
     []
    ],
@@ -154312,9 +154403,6 @@
    "fonts/math/scripts-superscriptshiftupcramped5000.woff": [
     []
    ],
-   "fonts/math/stack-axisheight7000.woff": [
-    []
-   ],
    "fonts/math/stack-bottomdisplaystyleshiftdown5000-axisheight1000.woff": [
     []
    ],
@@ -163471,9 +163559,6 @@
    "mediasession/README.md": [
     []
    ],
-   "mediasession/idlharness.window-expected.txt": [
-    []
-   ],
    "mimesniff/META.yml": [
     []
    ],
@@ -204543,6 +204628,12 @@
      {}
     ]
    ],
+   "css/css-grid/chrome-crash-001.html": [
+    [
+     "css/css-grid/chrome-crash-001.html",
+     {}
+    ]
+   ],
    "css/css-grid/grid-definition/grid-auto-repeat-max-size-001.html": [
     [
      "css/css-grid/grid-definition/grid-auto-repeat-max-size-001.html",
@@ -205299,6 +205390,12 @@
      {}
     ]
    ],
+   "css/css-layout-api/sync-layout-microtasks.https.html": [
+    [
+     "css/css-layout-api/sync-layout-microtasks.https.html",
+     {}
+    ]
+   ],
    "css/css-lists/inheritance.html": [
     [
      "css/css-lists/inheritance.html",
@@ -252550,6 +252647,12 @@
      {}
     ]
    ],
+   "mediasession/positionstate.html": [
+    [
+     "mediasession/positionstate.html",
+     {}
+    ]
+   ],
    "mediasession/setactionhandler.html": [
     [
      "mediasession/setactionhandler.html",
@@ -266074,7 +266177,8 @@
     [
      "payment-request/payment-is-showing.https.html",
      {
-      "testdriver": true
+      "testdriver": true,
+      "timeout": "long"
      }
     ]
    ],
@@ -352114,6 +352218,10 @@
    "7148f26e2770b669dfcc69435e3a506882ffd457",
    "reftest"
   ],
+  "css/css-flexbox/fit-content-item-001.html": [
+   "c5dfb74711f5c22c2f12b456f78cd7dd373f1659",
+   "reftest"
+  ],
   "css/css-flexbox/flex-001.htm": [
    "df3fbe533aa59a87ba1e7670ac8c7446527c98aa",
    "reftest"
@@ -364822,6 +364930,10 @@
    "c1ff70c49ec1e77ba0e794c7537ae90cd503498e",
    "testharness"
   ],
+  "css/css-grid/chrome-crash-001.html": [
+   "c9ea9332a1e49f300c0520b798653869ad82b5c6",
+   "testharness"
+  ],
   "css/css-grid/grid-definition/fr-unit-with-percentage.html": [
    "82c97abdd5740d466f049d8b45b67e241f204899",
    "reftest"
@@ -366531,7 +366643,7 @@
    "support"
   ],
   "css/css-layout-api/auto-block-size-absolute.https.html": [
-   "c2a58c9a2a375ed5716d3aa783260a7806fb4da3",
+   "bf81b21a4bbd2ded687b1aa18bc71036550206b2",
    "reftest"
   ],
   "css/css-layout-api/auto-block-size-flex-ref.html": [
@@ -366539,7 +366651,7 @@
    "support"
   ],
   "css/css-layout-api/auto-block-size-flex.https.html": [
-   "fe95c7e23cc7372a305e3ffb5e4694413bbfde71",
+   "5b152bb645ff21750c8d6b703d4ad6225a6504e0",
    "reftest"
   ],
   "css/css-layout-api/auto-block-size-floats-ref.html": [
@@ -366547,7 +366659,7 @@
    "support"
   ],
   "css/css-layout-api/auto-block-size-floats.https.html": [
-   "67775eb9954e3a503a98432406c12b39d74ac165",
+   "342c57b5300e257c8fd456489c9c57f9be0913bc",
    "reftest"
   ],
   "css/css-layout-api/auto-block-size-inflow-ref.html": [
@@ -366555,7 +366667,7 @@
    "support"
   ],
   "css/css-layout-api/auto-block-size-inflow.https.html": [
-   "b8bc0d1934ecc0143a55eb41de2c4007a065301b",
+   "7daef6e4c85c1931d16548720d528e4adfe98589",
    "reftest"
   ],
   "css/css-layout-api/auto-block-size-negative-ref.html": [
@@ -366563,7 +366675,7 @@
    "support"
   ],
   "css/css-layout-api/auto-block-size-negative.https.html": [
-   "4e912ed9f2a17be20046df35db22b086caf5d5cb",
+   "392edd125aea56db857f39264abf22c6e84b337b",
    "reftest"
   ],
   "css/css-layout-api/box-tree-registered-ref.html": [
@@ -366571,7 +366683,7 @@
    "support"
   ],
   "css/css-layout-api/box-tree-registered.https.html": [
-   "94790c178dbd394e8ccc8c0172d6354b2599cb2b",
+   "c3024f9f3aac08205f613014d61724aa4f1c40e8",
    "reftest"
   ],
   "css/css-layout-api/box-tree-unregistered-ref.html": [
@@ -366671,15 +366783,15 @@
    "testharness"
   ],
   "css/css-layout-api/constraints-data-function-failure.https.html": [
-   "394f68c614144c449c71cab7a50a0c4b3e802232",
+   "2d5e5240d794749ec2f9c7ee5eb9c4f9a6e54c9f",
    "reftest"
   ],
   "css/css-layout-api/constraints-data-sab-failure.https.html": [
-   "c03d35074aa6322057045de60fc7fb73d89c2456",
+   "8659c2edef56aa2fc72d3ba8ba807cb1d80dfe13",
    "reftest"
   ],
   "css/css-layout-api/constraints-data.https.html": [
-   "b7b6964acb2cbb2864f5eecec6fbfcc8a622e69f",
+   "4bea061917ab74c542534e23c33b5135c8a01d15",
    "reftest"
   ],
   "css/css-layout-api/constraints-fixed-block-size-absolute-left-right-vrl.https.html": [
@@ -366855,27 +366967,31 @@
    "testharness"
   ],
   "css/css-layout-api/fallback-constructor-error.https.html": [
-   "590a9d01f9056265799ca2f2bfe8247dacc62c38",
+   "3f711af2e7ae955ded29895bc64eb52cac7f487e",
    "reftest"
   ],
   "css/css-layout-api/fallback-layout-error.https.html": [
-   "54ea80b36b8863386a6411e7c50dee1c479ff28b",
+   "4c44f2168b2fb1e5c1071f40a355ea9a79714730",
    "reftest"
   ],
   "css/css-layout-api/fallback-layout-invalid-child.https.html": [
-   "19b4188a83992f4b330a44331a0f0d638678a382",
-   "reftest"
-  ],
-  "css/css-layout-api/fallback-layout-invalid-fragment-request.https.html": [
-   "ccbf38b4fd2bf4ba3e6b4103387b3289a494133c",
+   "567c3f7f74590eb8fcb9defd724533738f36023f",
    "reftest"
   ],
   "css/css-layout-api/fallback-layout-invalid-fragment.https.html": [
-   "e4253ff39352308de046fa4ceff98a8875d6a824",
+   "2e33e8761d32118154ced40882a095d30001c0fc",
+   "reftest"
+  ],
+  "css/css-layout-api/fallback-layout-no-promise.https.html": [
+   "e7b22a4c5cd21a892799e4f711704bcdc6d57ce3",
    "reftest"
   ],
   "css/css-layout-api/fallback-layout-return.https.html": [
-   "42ccfc5c83cde198d1d1da693358e020eac4fde1",
+   "4c90ae7d336682c948a237937c01475a6fa4cdae",
+   "reftest"
+  ],
+  "css/css-layout-api/fallback-layout-unresolved-promise.https.html": [
+   "369c56e4f9483537cf531fc1f20aea6f93fea88b",
    "reftest"
   ],
   "css/css-layout-api/fallback-ref.html": [
@@ -366883,19 +366999,19 @@
    "support"
   ],
   "css/css-layout-api/fragment-data-function-failure.https.html": [
-   "5e21be0bfd6704356d5f5796c1dc2dbd7c9e3dda",
+   "8496967be06401d5a21d097f4ad9db410758b49d",
    "reftest"
   ],
   "css/css-layout-api/fragment-data-immutable.https.html": [
-   "cc1875bd61d88540edca4ff2b7a556ed84b1961f",
+   "e33a6ff0eea1c8877130493c8897e778ed7d448e",
    "reftest"
   ],
   "css/css-layout-api/fragment-data-sab-failure.https.html": [
-   "5d3619b3647df885005083504fe8d4dd81b2c6a8",
+   "a6cfcf663dcf36a6d36fa019dc38ec49c6b29a09",
    "reftest"
   ],
   "css/css-layout-api/fragment-data.https.html": [
-   "589ee9c76fd77c9e778aa6cba16f33ad90e0b14b",
+   "ffc360ab35c20327ad40e5445b3a97f9401039fc",
    "reftest"
   ],
   "css/css-layout-api/green-square-ref.html": [
@@ -366971,7 +367087,7 @@
    "support"
   ],
   "css/css-layout-api/style-map-multi.https.html": [
-   "dd0617ce2b515298626de65d6953c6e0a0403752",
+   "9c49d0f928dd90a657bc21d43343b5f7f5edf7e6",
    "reftest"
   ],
   "css/css-layout-api/style-map-ref.html": [
@@ -366979,7 +367095,7 @@
    "support"
   ],
   "css/css-layout-api/style-map.https.html": [
-   "d16054da3afc0a35340cc3c3eeaf3be093788a4f",
+   "0300b8dfb4ac2b98c54216b8edb6388e7cb60f2a",
    "reftest"
   ],
   "css/css-layout-api/support/constraints-fixed-block-size-quirky-body-iframe.html": [
@@ -366987,29 +367103,33 @@
    "support"
   ],
   "css/css-layout-api/support/constraints-fixed-block-size.js": [
-   "50f802f47eb6bc4ff0df78798c6b9c33648aa51a",
+   "25d73ef61568477784a002dfeb591b7f5c8d0b30",
    "support"
   ],
   "css/css-layout-api/support/constraints-fixed-inline-size.js": [
-   "4e591f7a7365c38123a2e486445d6d982cacf71b",
+   "3636f366547342a04253864016bf89ed392ea48a",
    "support"
   ],
   "css/css-layout-api/support/layout-child-sizes-worklet.js": [
-   "28546d6e422d42c134f32d78282b5cd20597a85f",
+   "5956c9a70c2ba650318fd6bbbc859f1cc8002f08",
    "support"
   ],
   "css/css-layout-api/support/layout-child-worklet.js": [
-   "db20e2ec76b308544d20553477e0b3b47460aad6",
+   "70d1b7e4572bd09b2c201705bf643e2979caccf1",
    "support"
   ],
   "css/css-layout-api/support/layout-position-child-worklet.js": [
-   "1ccfeab5aeb0f71854e28f1a811751c77533f666",
+   "7d5c494952d58038a6bc71719be3c6d8d6e363cb",
    "support"
   ],
   "css/css-layout-api/supports.https.html": [
    "e269b292146cbab4f87041ab4cd2362010de5cf5",
    "testharness"
   ],
+  "css/css-layout-api/sync-layout-microtasks.https.html": [
+   "84457c0c9d9d8808d7d4ccf6a3b1d0db234233d4",
+   "testharness"
+  ],
   "css/css-lists/META.yml": [
    "05e60af1d8a558ec87a6c6f8acdeecdc0c7bb78d",
    "support"
@@ -377034,6 +377154,26 @@
    "f14d25a70ba3ccfc80a99e924bb6b6a7d4694b1e",
    "support"
   ],
+  "css/css-text-decor/reference/text-decoration-skip-ink-001-notref.html": [
+   "783a2b2ea3e2f7e69735f75aef5d4092dfffc79d",
+   "support"
+  ],
+  "css/css-text-decor/reference/text-decoration-skip-ink-002-ref.html": [
+   "dce958427c59e119eb3fb5d5a88fac09ee9e116d",
+   "support"
+  ],
+  "css/css-text-decor/reference/text-decoration-skip-ink-003-notref.html": [
+   "7458b7fe98c70c7bfbfb59e01511a30ff947a6f1",
+   "support"
+  ],
+  "css/css-text-decor/reference/text-decoration-skip-ink-003-ref.html": [
+   "e13e52976e2866259f8c291a8cfb59a05d8ec18c",
+   "support"
+  ],
+  "css/css-text-decor/reference/text-decoration-skip-ink-004-notref.html": [
+   "2861918d87f3cebaa3fe0d4978668e970424d84e",
+   "support"
+  ],
   "css/css-text-decor/reference/text-decoration-style-multiple-ref.html": [
    "f80396930e1bbaf06df7b999cf657cf74af99bc0",
    "support"
@@ -377446,6 +377586,22 @@
    "2f7b2f55fea63bbb2c752c4c79e4e3415f1578be",
    "testharness"
   ],
+  "css/css-text-decor/text-decoration-skip-ink-001.html": [
+   "4c707650868bfdb3c2db5621db18ec31e5a1bf56",
+   "reftest"
+  ],
+  "css/css-text-decor/text-decoration-skip-ink-002.html": [
+   "c073cd3d874c8a4d866966d056e282b109f96de1",
+   "reftest"
+  ],
+  "css/css-text-decor/text-decoration-skip-ink-003.html": [
+   "74a30481cad3ebc598bcbf2e4c963e402cb36a7c",
+   "reftest"
+  ],
+  "css/css-text-decor/text-decoration-skip-ink-004.html": [
+   "9d763f287c42491df3d445d3faf1ae444e4f9702",
+   "reftest"
+  ],
   "css/css-text-decor/text-decoration-skip-ink.html": [
    "cd6f7a049c673e3dc92546f557e1813b20448128",
    "testharness"
@@ -405079,7 +405235,7 @@
    "reftest"
   ],
   "css/filter-effects/inheritance.html": [
-   "2bd854ac051d78048e2d83b681d346762d9c5728",
+   "acb811bfad154e5a0b18eaddb799fc5dc679b114",
    "testharness"
   ],
   "css/filter-effects/interfaces.any-expected.txt": [
@@ -421095,7 +421251,7 @@
    "reftest"
   ],
   "fetch/corb/img-mime-types-coverage.tentative.sub.html": [
-   "6d7fd60f449ac78f8ff7c07265acb2101b15bd39",
+   "c2aa93236360de7c34ecdbddac161b381de8ac7d",
    "testharness"
   ],
   "fetch/corb/img-png-mislabeled-as-html-nosniff.tentative.sub-ref.html": [
@@ -422402,10 +422558,6 @@
    "c85e7f65135328f6bea5a9e35bf8c16c4f189ee6",
    "support"
   ],
-  "fonts/math/stack-axisheight7000.woff": [
-   "7a9dc5d4cb1bbc758d2ecf34641e331e9ac7acb0",
-   "support"
-  ],
   "fonts/math/stack-bottomdisplaystyleshiftdown5000-axisheight1000.woff": [
    "d8f8ae48a6d4c24ba17ed9185b769a8956418002",
    "support"
@@ -445667,7 +445819,7 @@
    "testharness"
   ],
   "mathml/presentation-markup/fractions/frac-parameters-2.html": [
-   "9c363866de08ab15202cab10e014a25fdd7e2f0d",
+   "151265787ab3f5c8eb7b80588dc30d3e633d6e79",
    "testharness"
   ],
   "mathml/presentation-markup/fractions/frac-parameters-gap-001-ref.html": [
@@ -446423,7 +446575,7 @@
    "support"
   ],
   "mathml/tools/stacks.py": [
-   "18626291336352157b8b450dfaac77caf37cfd89",
+   "9a4c291e6ea2412b3ac85b297c8ad1eac3d55109",
    "support"
   ],
   "mathml/tools/stretchstacks.py": [
@@ -447586,12 +447738,8 @@
    "7c7c9f8d57a46ae310b1a63df7c6117f89b56d63",
    "support"
   ],
-  "mediasession/idlharness.window-expected.txt": [
-   "90ec4e06dfd6a2d52b4375034327329c91a0bc57",
-   "support"
-  ],
   "mediasession/idlharness.window.js": [
-   "5fa0d22c10a543f187bcc047fd2a244ec699eba9",
+   "e4d914544ed5df95b20287f8bd5482fdd7fe1045",
    "testharness"
   ],
   "mediasession/mediametadata.html": [
@@ -447602,6 +447750,10 @@
    "63f3548d941a1cec895fc8029eb8f661f9008b67",
    "testharness"
   ],
+  "mediasession/positionstate.html": [
+   "2eab2986d4583c1a289b7876010193c8d3d44b3f",
+   "testharness"
+  ],
   "mediasession/setactionhandler.html": [
    "6e1bb26c67a828ae847734070024b404138e7932",
    "testharness"
@@ -457983,7 +458135,7 @@
    "manual"
   ],
   "payment-request/blank.html": [
-   "7852a427e861a9be3c9068deb306b67ad909843f",
+   "edeaa45bb620788242711725cfd8189b681835ae",
    "support"
   ],
   "payment-request/change-shipping-option-manual.https.html": [
@@ -458031,7 +458183,7 @@
    "support"
   ],
   "payment-request/payment-is-showing.https.html": [
-   "cb5b2e16dd25af8b01cb9b485490e05a84504dc4",
+   "618bfe97c1d4367331855f01f6dc9513528360c6",
    "testharness"
   ],
   "payment-request/payment-request-abort-method.https-expected.txt": [
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text-decor/reference/text-decoration-skip-ink-001-notref.html b/third_party/blink/web_tests/external/wpt/css/css-text-decor/reference/text-decoration-skip-ink-001-notref.html
new file mode 100644
index 0000000..783a2b2e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text-decor/reference/text-decoration-skip-ink-001-notref.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <meta charset="utf-8">
+        <title>Non-reference case for text-decoration-skip-ink</title>
+        <style>
+         div{
+             text-decoration: green underline;
+             text-decoration-skip-ink: none;
+         }
+        </style>
+    </head>
+    <body>
+        <p>Test passes if the underline skips the p and g's in the text below</p>
+        <div>ping pong</div>
+    </body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text-decor/reference/text-decoration-skip-ink-002-ref.html b/third_party/blink/web_tests/external/wpt/css/css-text-decor/reference/text-decoration-skip-ink-002-ref.html
new file mode 100644
index 0000000..dce9584
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text-decor/reference/text-decoration-skip-ink-002-ref.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <meta charset="utf-8">
+        <title>Reference case for text-decoration-skip-ink</title>
+        <link rel="stylesheet" type="text/css" href="/fonts/ahem.css">
+    </head>
+    <body>
+        <p>Test passes if no underline is visible</p>
+    </body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text-decor/reference/text-decoration-skip-ink-003-notref.html b/third_party/blink/web_tests/external/wpt/css/css-text-decor/reference/text-decoration-skip-ink-003-notref.html
new file mode 100644
index 0000000..7458b7f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text-decor/reference/text-decoration-skip-ink-003-notref.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <meta charset="utf-8">
+        <title>Non-reference case for text-decoration-skip-ink</title>
+        <link rel="stylesheet" type="text/css" href="/fonts/ahem.css">
+        <style>
+         /*
+            This non-reference case ensures that the test does
+            not frivolously pass if the underline is not rendered at all
+          */
+         div{
+             font: 20px/1 Ahem;
+             color: rgba(255,255,0,0.25);
+         }
+        </style>
+    </head>
+    <body>
+        <p>Test passes if underline is rendered below the text</p>
+        <div>XXXX</div>
+    </body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text-decor/reference/text-decoration-skip-ink-003-ref.html b/third_party/blink/web_tests/external/wpt/css/css-text-decor/reference/text-decoration-skip-ink-003-ref.html
new file mode 100644
index 0000000..e13e529
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text-decor/reference/text-decoration-skip-ink-003-ref.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <meta charset="utf-8">
+        <title>Reference case for text-decoration-skip-ink</title>
+        <link rel="stylesheet" type="text/css" href="/fonts/ahem.css">
+        <style>
+         div{
+             font: 20px/1 Ahem;
+             color: rgba(255,255,0,0.25);
+             text-decoration: green underline;
+             text-decoration-skip-ink: none;
+             text-underline-offset: .5em;
+         }
+        </style>
+    </head>
+    <body>
+        <p>Test passes if underline is rendered below the text</p>
+        <div>XXXX</div>
+    </body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text-decor/reference/text-decoration-skip-ink-004-notref.html b/third_party/blink/web_tests/external/wpt/css/css-text-decor/reference/text-decoration-skip-ink-004-notref.html
new file mode 100644
index 0000000..2861918d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text-decor/reference/text-decoration-skip-ink-004-notref.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <meta charset="utf-8">
+        <title>Non-reference case for text-decoration-skip-ink</title>
+        <link rel="stylesheet" type="text/css" href="/fonts/ahem.css">
+        <style>
+         div{
+             font: 20px/1 Ahem;
+             color: rgba(255,255,0,0.25);
+             text-decoration: green underline;
+             text-decoration-skip-ink: none;
+             text-underline-offset: -.3em;
+         }
+        </style>
+    </head>
+    <body>
+        <p>Test passes if underline appears only across the 'p' glyph</p>
+        <div>XXpX</div>
+    </body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-skip-ink-001.html b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-skip-ink-001.html
new file mode 100644
index 0000000..4c707650
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-skip-ink-001.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <meta charset="utf-8">
+        <title>Test case for text-decoration-skip-ink</title>
+        <meta name="assert" content="text-decoration-skip-ink: descenders are skipped">
+        <link rel="author" title="Charlie Marlow" href="mailto:cmarlow@mozilla.com">
+        <link rel="author" title="Mozilla" href="https://www.mozilla.org">
+        <link rel="help" href="https://drafts.csswg.org/css-text-decor-4/#text-decoration-skip-ink-property">
+        <link rel="mismatch" href="reference/text-decoration-skip-ink-001-notref.html">
+        <style>
+         div{
+             text-decoration: green underline;
+             text-decoration-skip-ink: auto;
+         }
+        </style>
+    </head>
+    <body>
+        <p>Test passes if the underline skips the p and g's in the text below</p>
+        <div>ping pong</div>
+    </body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-skip-ink-002.html b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-skip-ink-002.html
new file mode 100644
index 0000000..c073cd3
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-skip-ink-002.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <meta charset="utf-8">
+        <title>Test case for text-decoration-skip-ink</title>
+        <meta name="assert" content="text-decoration-skip-ink: no underline is rendered for transparent Ahem font">
+        <link rel="author" title="Charlie Marlow" href="mailto:cmarlow@mozilla.com">
+        <link rel="author" title="Mozilla" href="https://www.mozilla.org">
+        <link rel="help" href="https://drafts.csswg.org/css-text-decor-4/#text-decoration-skip-ink-property">
+        <link rel="match" href="reference/text-decoration-skip-ink-002-ref.html">
+        <link rel="stylesheet" type="text/css" href="/fonts/ahem.css">
+        <style>
+         /*
+            No underline should be rendered for Ahem text when
+            text-decoration-skip-ink is applied.
+          */
+         div{
+             font: 20px/1 Ahem;
+             text-decoration: green underline;
+             text-decoration-skip-ink: auto;
+             color: transparent;
+         }
+        </style>
+    </head>
+    <body>
+        <p>Test passes if no underline is visible</p>
+        <div>XXXX</div>
+    </body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-skip-ink-003.html b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-skip-ink-003.html
new file mode 100644
index 0000000..74a3048
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-skip-ink-003.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <meta charset="utf-8">
+        <title>Test case for text-decoration-skip-ink</title>
+        <meta name="assert" content="text-decoration-skip-ink: an uninterrupted underline is rendered below the text">
+        <link rel="author" title="Charlie Marlow" href="mailto:cmarlow@mozilla.com">
+        <link rel="author" title="Mozilla" href="https://www.mozilla.org">
+        <link rel="help" href="https://drafts.csswg.org/css-text-decor-4/#text-decoration-skip-ink-property">
+        <link rel="match" href="reference/text-decoration-skip-ink-003-ref.html">
+        <link rel="mismatch" href="reference/text-decoration-skip-ink-003-notref.html">
+        <link rel="stylesheet" type="text/css" href="/fonts/ahem.css">
+        <style>
+         /*
+            This test ensures that the underline offset is taken into account
+            when calculating the skip-ink. No skip-ink should be applied
+            because the underline is being moved below the text.
+          */
+         div{
+             font: 20px/1 Ahem;
+             color: rgba(255,255,0,0.25);
+             text-decoration: green underline;
+             text-decoration-skip-ink: auto;
+             text-underline-offset: .5em;
+         }
+        </style>
+    </head>
+    <body>
+        <p>Test passes if underline is rendered below the text</p>
+        <div>XXXX</div>
+    </body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-skip-ink-004.html b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-skip-ink-004.html
new file mode 100644
index 0000000..9d763f2
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-skip-ink-004.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <meta charset="utf-8">
+        <title>Test case for text-decoration-skip-ink</title>
+        <meta name="assert" content="text-decoration-skip-ink: a partial underline is rendered above the 'p'">
+        <link rel="author" title="Charlie Marlow" href="mailto:cmarlow@mozilla.com">
+        <link rel="author" title="Mozilla" href="https://www.mozilla.org">
+        <link rel="help" href="https://drafts.csswg.org/css-text-decor-4/#text-decoration-skip-ink-property">
+        <link rel="mismatch" href="reference/text-decoration-skip-ink-004-notref.html">
+        <link rel="stylesheet" type="text/css" href="/fonts/ahem.css">
+        <style>
+         /*
+            This test ensures that the underline offset is taken into account
+            when calculating the skip-ink. The underline should skip all
+            characters except the third.
+          */
+         div{
+             font: 20px/1 Ahem;
+             color: rgba(255,255,0,0.25);
+             text-decoration: green underline;
+             text-decoration-skip-ink: auto;
+             text-underline-offset: -.3em;
+         }
+        </style>
+    </head>
+    <body>
+        <p>Test passes if underline appears only across the 'p' glyph</p>
+        <div>XXpX</div>
+    </body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/custom-elements/form-associated/ElementInternals-accessibility.html b/third_party/blink/web_tests/external/wpt/custom-elements/form-associated/ElementInternals-accessibility.html
new file mode 100644
index 0000000..f5d9664
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/custom-elements/form-associated/ElementInternals-accessibility.html
@@ -0,0 +1,78 @@
+<!DOCTYPE HTML>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+class TestElement extends HTMLElement {
+  constructor() {
+    super();
+    this._internals = this.attachInternals();
+  }
+
+  get internals() {
+    return this._internals;
+  }
+
+  set internals(val) {
+    throw "Can't set internals!";
+  }
+}
+customElements.define("test-element", TestElement);
+</script>
+
+<test-element id= "testElement"></test-element>
+
+<script>
+const element = document.getElementById("testElement");
+const properties = [
+  "role",
+  "ariaActiveDescendant",
+  "ariaAtomic",
+  "ariaAutoComplete",
+  "ariaBusy",
+  "ariaChecked",
+  "ariaColCount",
+  "ariaColIndex",
+  "ariaColSpan",
+  "ariaControls",
+  "ariaCurrent",
+  "ariaDescribedBy",
+  "ariaDetails",
+  "ariaDisabled",
+  "ariaErrorMessage",
+  "ariaExpanded",
+  "ariaFlowTo",
+  "ariaHasPopup",
+  "ariaHidden",
+  "ariaKeyShortcuts",
+  "ariaLabel",
+  "ariaLabelledBy",
+  "ariaLevel",
+  "ariaLive",
+  "ariaModal",
+  "ariaMultiLine",
+  "ariaMultiSelectable",
+  "ariaOrientation",
+  "ariaOwns",
+  "ariaPlaceholder",
+  "ariaPosInSet",
+  "ariaPressed",
+  "ariaReadOnly",
+  "ariaRelevant",
+  "ariaRequired",
+  "ariaRoleDescription",
+  "ariaRowCount",
+  "ariaRowIndex",
+  "ariaRowSpan",
+  "ariaSelected",
+  "ariaSort",
+  "ariaValueMax",
+  "ariaValueMin",
+  "ariaValueNow",
+  "ariaValueText"
+];
+for (const property of properties) {
+  test(() => {
+    assert_inherits(element.internals, property);
+  }, property + " is defined in ElementInternals");
+}
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/stack-axisheight7000.woff b/third_party/blink/web_tests/external/wpt/fonts/math/stack-axisheight7000.woff
deleted file mode 100644
index 7a9dc5d4..0000000
--- a/third_party/blink/web_tests/external/wpt/fonts/math/stack-axisheight7000.woff
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-parameters-2.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-parameters-2.html
index 9c363866..1512657 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-parameters-2.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-parameters-2.html
@@ -13,10 +13,6 @@
     font-size: 10px;
   }
   @font-face {
-    font-family: axisheight7000;
-    src: url("/fonts/math/stack-axisheight7000.woff");
-  }
-  @font-face {
     font-family: bottomdisplaystyleshiftdown5000-axisheight1000;
     src: url("/fonts/math/stack-bottomdisplaystyleshiftdown5000-axisheight1000.woff");
   }
@@ -56,13 +52,6 @@
   });
 
   function runTests() {
-    test(function() {
-      assert_true(MathMLFeatureDetection.has_mspace());
-
-      var v = 7000 * emToPx;
-      assert_approx_equals(getBox("ref0001").top - getBox("num0001").bottom,
-                           v, epsilon, "mfrac: axis height");
-    }, "AxisHeight");
 
     test(function() {
       assert_true(MathMLFeatureDetection.has_mspace());
@@ -119,16 +108,6 @@
 <body>
   <div id="log"></div>
   <p>
-    <math style="font-family: axisheight7000;">
-      <mspace id="ref0001" depth="1em" width="3em" style="background: green"/>
-      <mfrac linethickness="0px">
-        <mspace width="3em" height="1em" id="num0001" style="background: blue"/>
-        <mspace width="3em"/>
-      </mfrac>
-    </math>
-  </p>
-  <hr/>
-  <p>
     <math display="block" style="font-family: bottomdisplaystyleshiftdown5000-axisheight1000;">
       <mspace id="ref0002" width="3em" height="1em" style="background: green"/>
       <mfrac linethickness="0px">
diff --git a/third_party/blink/web_tests/external/wpt/mathml/tools/stacks.py b/third_party/blink/web_tests/external/wpt/mathml/tools/stacks.py
index 18626291..9a4c291e 100755
--- a/third_party/blink/web_tests/external/wpt/mathml/tools/stacks.py
+++ b/third_party/blink/web_tests/external/wpt/mathml/tools/stacks.py
@@ -3,17 +3,6 @@
 from utils import mathfont
 import fontforge
 
-v = 7 * mathfont.em
-f = mathfont.create("stack-axisheight%d" % v)
-f.math.AxisHeight = v
-f.math.StackBottomDisplayStyleShiftDown = 0
-f.math.StackBottomShiftDown = 0
-f.math.StackDisplayStyleGapMin = 0
-f.math.StackGapMin = 0
-f.math.StackTopDisplayStyleShiftUp = 0
-f.math.StackTopShiftUp = 0
-mathfont.save(f)
-
 v1 = 5 * mathfont.em
 v2 = 1 * mathfont.em
 f = mathfont.create("stack-bottomdisplaystyleshiftdown%d-axisheight%d" % (v1, v2))
diff --git a/third_party/blink/web_tests/external/wpt/payment-request/blank.html b/third_party/blink/web_tests/external/wpt/payment-request/blank.html
index 7852a42..edeaa45bb 100644
--- a/third_party/blink/web_tests/external/wpt/payment-request/blank.html
+++ b/third_party/blink/web_tests/external/wpt/payment-request/blank.html
@@ -1 +1,16 @@
-<!DOCTYPE html> <meta charset="utf-8" />
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8" />
+    <title>Blank Page</title>
+    <script>
+      window.onload = function(event) {
+        // This is needed to ensure the onload event fires when this page is
+        // opened as a popup.
+        // See https://github.com/web-platform-tests/wpt/pull/18157
+      };
+    </script>
+  </head>
+  <body>
+  </body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/payment-request/payment-is-showing.https.html b/third_party/blink/web_tests/external/wpt/payment-request/payment-is-showing.https.html
index cb5b2e1..618bfe9 100644
--- a/third_party/blink/web_tests/external/wpt/payment-request/payment-is-showing.https.html
+++ b/third_party/blink/web_tests/external/wpt/payment-request/payment-is-showing.https.html
@@ -4,30 +4,31 @@
   rel="help"
   href="https://w3c.github.io/browser-payment-api/#dfn-payment-request-is-showing"
 />
+<meta name="timeout" content="long">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="/resources/testdriver-vendor.js"></script>
 <script src="/resources/testdriver.js"></script>
 <body>
   <script>
-    'use strict';
+    "use strict";
     const applePayMethod = {
-      supportedMethods: 'https://apple.com/apple-pay',
+      supportedMethods: "https://apple.com/apple-pay",
       data: {
         version: 3,
-        merchantIdentifier: 'merchant.com.example',
-        countryCode: 'US',
-        merchantCapabilities: ['supports3DS'],
-        supportedNetworks: ['visa'],
+        merchantIdentifier: "merchant.com.example",
+        countryCode: "US",
+        merchantCapabilities: ["supports3DS"],
+        supportedNetworks: ["visa"],
       },
     };
-    const methods = [{supportedMethods: 'basic-card'}, applePayMethod];
+    const methods = [{supportedMethods: "basic-card"}, applePayMethod];
     const details = {
       total: {
-        label: 'Total',
+        label: "Total",
         amount: {
-          currency: 'USD',
-          value: '1.00',
+          currency: "USD",
+          value: "1.00",
         },
       },
     };
@@ -38,13 +39,13 @@
      * @param {String} src Optional resource URL to load.
      * @returns {Promise} Resolves when the src loads.
      */
-    async function attachIframe(src = 'blank.html') {
-      const iframe = document.createElement('iframe');
+    async function attachIframe(src = "blank.html") {
+      const iframe = document.createElement("iframe");
       iframe.allowPaymentRequest = true;
       iframe.src = src;
       document.body.appendChild(iframe);
       await new Promise(resolve => {
-        iframe.addEventListener('load', resolve, {once: true});
+        iframe.addEventListener("load", resolve, {once: true});
       });
       return iframe;
     }
@@ -55,10 +56,10 @@
      * @param {String} src Optional resource URL to load.
      * @returns {Promise} Resolves when the src loads.
      */
-    async function loadPopupInsideUserGesture(src = 'blank.html') {
-      const popupWindow = window.open(src, '', 'width=400,height=400');
+    async function loadPopupInsideUserGesture(src = "blank.html") {
+      const popupWindow = window.open(src, "", "width=400,height=400");
       await new Promise(resolve => {
-        popupWindow.addEventListener('load', resolve, {once: true});
+        popupWindow.addEventListener("load", resolve, {once: true});
       });
       popupWindow.focus();
       return popupWindow;
@@ -72,24 +73,24 @@
       // showing boolean" to true and then try to show a second payment sheet in
       // the same window. The second show() should reject.
       const [showPromise1, showPromise2] = await test_driver.bless(
-        'testing one payment sheet per window',
+        "testing one payment sheet per window",
         () => {
           return [request1.show(), request2.show()];
         },
       );
       await promise_rejects(
         t,
-        'AbortError',
+        "AbortError",
         showPromise2,
-        'Attempting to show a second payment request must reject.',
+        "Attempting to show a second payment request must reject.",
       );
 
       await request1.abort();
       await promise_rejects(
         t,
-        'AbortError',
+        "AbortError",
         showPromise1,
-        'request1 was aborted via .abort()',
+        "request1 was aborted via .abort()",
       );
 
       // Finally, request2 should have been "closed", so trying to show
@@ -98,17 +99,17 @@
       const rejectedPromise = request2.show();
       await promise_rejects(
         t,
-        'InvalidStateError',
+        "InvalidStateError",
         rejectedPromise,
-        'Attempting to show a second payment request must reject.',
+        "Attempting to show a second payment request must reject.",
       );
       // Finally, we confirm that request2's returned promises are unique.
       assert_not_equals(
         showPromise2,
         rejectedPromise,
-        'Returned Promises be unique',
+        "Returned Promises be unique",
       );
-    }, 'The top browsing context can only show one payment sheet at a time.');
+    }, "The top browsing context can only show one payment sheet at a time.");
 
     promise_test(async t => {
       const iframe = await attachIframe();
@@ -120,7 +121,7 @@
 
       // Let's get some blessed showPromises
       const [showPromise] = await test_driver.bless(
-        'testing top window show() blocked by payment sheet in iframe',
+        "testing top window show() blocked by payment sheet in iframe",
         () => {
           // iframe sets "is showing boolean", ignore the returned promise.
           iframeRequest.show();
@@ -131,9 +132,9 @@
 
       await promise_rejects(
         t,
-        'AbortError',
+        "AbortError",
         showPromise,
-        'iframe is already showing a payment request.',
+        "iframe is already showing a payment request.",
       );
 
       // Cleanup
@@ -152,25 +153,30 @@
       // We first show a payment request via the the top level browsing context,
       // windowRequest.show() sets "is showing boolean" to true. Then we try to
       // show a payment request in the iframe, which should reject.
-      const [iframeShowPromise] = await test_driver.bless(
-        'testing iframe show() blocked by payment sheet in top window',
+      const [windowShowPromise, iframeShowPromise] = await test_driver.bless(
+        "testing iframe show() blocked by payment sheet in top window",
         () => {
-          windowRequest.show();
-          return [iframeRequest.show()];
+          return [windowRequest.show(), iframeRequest.show()];
         },
       );
 
       await promise_rejects(
         t,
-        'AbortError',
+        "AbortError",
         iframeShowPromise,
-        'The top window is already showing a payment request.',
+        "The top window is already showing a payment request.",
       );
 
       // Cleanup
       await windowRequest.abort();
+      await promise_rejects(
+        t,
+        "AbortError",
+        windowShowPromise,
+        "The window payment request should be aborted by test.",
+        );
       iframe.remove();
-    }, 'An iframe cannot show a payment request if the top-level window is already showing one.');
+    }, "An iframe cannot show a payment request if the top-level window is already showing one.");
 
     promise_test(async t => {
       // Create a PaymentReuqest in top-level window.
@@ -185,7 +191,7 @@
         popupShowPromise,
         windowShowPromise,
       ] = await test_driver.bless(
-        'testing top-level show() blocked by payment sheet in popup',
+        "testing top-level show() blocked by payment sheet in popup",
         async () => {
           const popupWindow = await loadPopupInsideUserGesture();
           const popupRequest = new popupWindow.PaymentRequest(methods, details);
@@ -199,16 +205,23 @@
           ];
         },
       );
-      await popupRequest.abort();
-      popupWindow.close();
 
       await promise_rejects(
         t,
-        'AbortError',
+        "AbortError",
         windowShowPromise,
         "Expected window's showPromise to reject, request is already showing",
       );
-    }, 'Using a popup window prevents the top-browsing context from showing a payment request');
+
+      await popupRequest.abort();
+      await promise_rejects(
+        t,
+        "AbortError",
+        popupShowPromise,
+        "Expected popupShowPromise to be aborted by test.",
+      );
+      popupWindow.close();
+    }, "Using a popup window prevents the top-browsing context from showing a payment request");
 
     promise_test(async t => {
       const iframe = await attachIframe();
@@ -221,7 +234,7 @@
       // Open a popup window
       const [popupWindow, popupRequest] =
       await test_driver.bless(
-        'open popup to test multiple context and window calls show() first',
+        "open popup to test multiple context and window calls show() first",
         async () => {
           const popupWindow = await loadPopupInsideUserGesture();
           const popupRequest = new popupWindow.PaymentRequest(methods, details);
@@ -237,7 +250,7 @@
         popupShowPromise,
         iframeShowPromise,
       ] = await test_driver.bless(
-        'test multiple nested browsing context',
+        "test multiple nested browsing context",
         () => {
           return [
             windowRequest.show(),
@@ -249,19 +262,25 @@
       // popupRequest and iframeRequest will both reject
       await promise_rejects(
         t,
-        'AbortError',
+        "AbortError",
         popupShowPromise,
-        'Expected popupShowPromise to reject, request is already showing.',
+        "Expected popupShowPromise to reject, request is already showing.",
       );
 
       await promise_rejects(
         t,
-        'AbortError',
+        "AbortError",
         iframeShowPromise,
-        'Expected iframeShowPromise to reject, request is already showing.',
+        "Expected iframeShowPromise to reject, request is already showing.",
       );
 
       await windowRequest.abort();
+      await promise_rejects(
+        t,
+        "AbortError",
+        windowShowPromise,
+        "Expect window promise to be aborted by test."
+      );
       popupWindow.close();
       iframe.remove();
     }, "Given multiple nested browsing contexts, and window calls show() first, other nested browsing contexts can't show a request.");
@@ -282,7 +301,7 @@
         windowShowPromise,
         iframeShowPromise
       ] = await test_driver.bless(
-        'test multiple browsing context and iframe calls show() first',
+        "test multiple browsing context and iframe calls show() first",
         async () => {
           const popupWindow = await loadPopupInsideUserGesture();
           const popupRequest = new popupWindow.PaymentRequest(methods, details);
@@ -299,19 +318,25 @@
       // windowShowPromise and iframeRequest will both reject
       await promise_rejects(
         t,
-        'AbortError',
+        "AbortError",
         windowShowPromise,
-        'Expected windowShowPromise to reject, the popup is showing a payment request.',
+        "Expected windowShowPromise to reject, the popup is showing a payment request.",
       );
 
       await promise_rejects(
         t,
-        'AbortError',
+        "AbortError",
         iframeShowPromise,
-        'Expected iframeShowPromise to reject, the popup is showing a payment request.',
+        "Expected iframeShowPromise to reject, the popup is showing a payment request.",
       );
 
       await popupRequest.abort();
+      await promise_rejects(
+        t,
+        "AbortError",
+        popupShowPromise,
+        "Expected popupShowPromise to be aborted by test.",
+      );
       popupWindow.close();
       iframe.remove();
     }, "Given multiple nested browsing contexts, and popup calls show() first, other nested browsing contexts can't show a request.");
@@ -325,7 +350,7 @@
       const iframeRequest = new iframeWindow.PaymentRequest(methods, details);
 
       const [popupWindow, popupRequest] = await test_driver.bless(
-        'open popup to test multiple context and iframe calls show() first',
+        "open popup to test multiple context and iframe calls show() first",
         async () => {
           const w = await loadPopupInsideUserGesture();
           const r = new w.PaymentRequest(methods, details);
@@ -341,7 +366,7 @@
         popupShowPromise,
         windowShowPromise,
       ] = await test_driver.bless(
-        'test multiple browsing context and iframe calls show() first',
+        "test multiple browsing context and iframe calls show() first",
         async () => {
           return [
             iframeRequest.show(),
@@ -354,19 +379,25 @@
       // windowShowPromise and iframeRequest will both reject
       await promise_rejects(
         t,
-        'AbortError',
+        "AbortError",
         windowShowPromise,
-        'Expected windowShowPromise to reject, the popup is showing a payment request.',
+        "Expected windowShowPromise to reject, the popup is showing a payment request.",
       );
 
       await promise_rejects(
         t,
-        'AbortError',
+        "AbortError",
         popupShowPromise,
-        'Expected popupShowPromise to reject, the popup is showing a payment request.',
+        "Expected popupShowPromise to reject, the popup is showing a payment request.",
       );
 
       await iframeRequest.abort();
+      await promise_rejects(
+        t,
+        "AbortError",
+        iframeShowPromise,
+        "Expected iframeShowPromise to be aborted by test."
+      );
       popupWindow.close();
       iframe.remove();
     }, "Given multiple nested browsing contexts, and an iframe calls show() first, other nested browsing contexts can't show a request.");
@@ -376,54 +407,96 @@
       const iframeWindow = iframe.contentWindow;
       const iframeRequest = new iframeWindow.PaymentRequest(methods, details);
       const iframeShowPromise = test_driver.bless(
-        'test navigating iframe after show()',
+        "test navigating iframe after show()",
         () => iframeRequest.show(),
       );
 
       // We navigate away, causing the payment sheet to close
       // and the request is showing boolean to become false.
-      iframe.src = 'blank.html?abc=123';
+      iframe.src = "blank.html?abc=123";
       await new Promise(resolve => (iframe.onload = resolve));
       await promise_rejects(
         t,
-        'AbortError',
+        "AbortError",
         iframeShowPromise,
-        'Navigating iframe away must cause the iframeShowPromise to reject.',
+        "Navigating iframe away must cause the iframeShowPromise to reject.",
       );
       iframe.remove();
 
       // Now we should be ok to spin up a new payment request
       const request = new window.PaymentRequest(methods, details);
-      const showPromise = request.show();
-      await request.abort();
+      const [showPromise] = await test_driver.bless(
+          "start a new payment request",
+          () => {
+            return [request.show()];
+          });
+
+      // If a payment sheet fails to show, it should reject immediately. If it
+      // hasn't rejected in 1 second, then the test has passed.
+      t.step_timeout(async () => {
+        // We're done. Clean up.
+        await request.abort();
+        t.done();
+      });
+
+      // If the navigation in iframe failed to close the original payment sheet
+      // there, |showPromise| should reject immediately and this indicates a
+      // failure of this test.
+      await showPromise.then(() => {
+        assert_true(false,
+          "Second payment sheet should be pending but is resolved.");
+      })
+      .catch(e => {
+        assert_true(false,
+          "Second payment sheet should be pending but is rejected." + e.message);
+      });
     }, "Navigating an iframe as a nested browsing context sets 'payment request is showing boolean' to false.");
 
     promise_test(async t => {
-      const [popupWindow, popupRequest, showPromise] = await test_driver.bless(
-        'test navigating popup after show()',
-        async () => {
-          const popupWindow = await loadPopupInsideUserGesture();
-          const popupRequest = new popupWindow.PaymentRequest(methods, details);
-          return [popupWindow, popupRequest, popupRequest.show()];
-        },
-      );
+      const [popupWindow, popupRequest, popupShowPromise] =
+        await test_driver.bless(
+          "trigger payment in a popup window",
+          async () => {
+            const popupWindow = await loadPopupInsideUserGesture();
+            const popupRequest = new popupWindow.PaymentRequest(methods, details);
+            return [popupWindow, popupRequest, popupRequest.show()];
+          });
 
       // We navigate away, causing the payment sheet to close
       // and the request is showing boolean to become false.
-      popupWindow.location = 'blank.html?abc=123';
+      popupWindow.location = "blank.html?abc=123";
       await new Promise(resolve => (popupWindow.onload = resolve));
-      await promise_rejects(
-        t,
-        'AbortError',
-        showPromise,
-        'Navigating away must cause the showPromise to reject with an AbortError',
-      );
-      popupWindow.close();
 
-      // Now we should be ok to spin up a new payment request.
+      // Don't wait for |popupShowPromise| to reject because it may never do
+      // (see https://github.com/w3c/payment-request/issues/872). Instead, try
+      // to spin up a new payment request and make sure it succeeds.
       const request = new window.PaymentRequest(methods, details);
-      request.show();
-      await request.abort();
+      const [showPromise] = await test_driver.bless(
+        "trigger payment in main window",
+        () => {
+          return [request.show()];
+        });
+
+      // If a payment sheet fails to show, it should reject immediately. If it
+      // hasn't rejected in 1 second, then the test has passed.
+      t.step_timeout(async () => {
+        // We're done. Clean up.
+        popupWindow.close();
+        await request.abort();
+        t.done();
+      }, 1000);
+
+      // If the navigation in popup window failed to close the original payment
+      // sheet there, |showPromise| should reject immediately and this indicates
+      // a failure of this test.
+      await showPromise.then(() => {
+        assert_true(false,
+          "Second payment sheet should be pending but is resolved.");
+      })
+      .catch(e => {
+        assert_true(false,
+          "Second payment sheet should be pending but is rejected.");
+      });
     }, "Navigating a popup as a nested browsing context sets 'payment request is showing boolean' to false.");
   </script>
 </body>
diff --git a/third_party/blink/web_tests/fast/spatial-navigation/snav-not-below-expected.txt b/third_party/blink/web_tests/fast/spatial-navigation/snav-not-below-expected.txt
deleted file mode 100644
index 9873e6e..0000000
--- a/third_party/blink/web_tests/fast/spatial-navigation/snav-not-below-expected.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-This test ensures that e2 is NOT below e1 and e3 is NOT below e2.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-Element 1
-Element 2
-Element 3
-PASS gFocusedDocument.activeElement.getAttribute("id") is "e1"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "e2"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "e2"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "e3"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "e3"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "e2"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "e2"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "e1"
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/blink/web_tests/fast/spatial-navigation/snav-not-below.html b/third_party/blink/web_tests/fast/spatial-navigation/snav-not-below.html
index f760860c..e24e8597 100644
--- a/third_party/blink/web_tests/fast/spatial-navigation/snav-not-below.html
+++ b/third_party/blink/web_tests/fast/spatial-navigation/snav-not-below.html
@@ -1,43 +1,41 @@
 <!DOCTYPE html>
-<head>
-<script src="../../resources/js-test.js"></script>
-<script src="resources/spatial-navigation-utils.js"></script>
-</head>
 <style>
-body a {
+  body a {
     position: absolute;
     height: 100px;
     width: 100px;
     border: 1px solid black;
-}
-#e1 {
+  }
+
+  #e1 {
     left: 10px;
     top: 210px;
-}
-#e2 {
+  }
+
+  #e2 {
     left: 120px;
     top: 250px;
-}
-#e3 {
+  }
+
+  #e3 {
     left: 250px;
     top: 250px;
     height: 120px;
-}
+  }
 </style>
-<body id="some-content" onload="runTest()">
-<p id="description"></p>
+
+<p id="description">This test ensures that e2 is NOT below e1 and e3 is NOT below e2.</p>
 <div id="elements">
-<a id="e1" href="#e1">Element 1</a>
-<a id="e2" href="#e2">Element 2</a>
-<a id="e3" href="#e3">Element 3</a>
+  <a id="e1" href="#e1">Element 1</a>
+  <a id="e2" href="#e2">Element 2</a>
+  <a id="e3" href="#e3">Element 3</a>
 </div>
-<div id="console"></div>
+
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="resources/snav-testharness.js"></script>
 <script type="application/javascript">
-description('This test ensures that e2 is NOT below e1 and e3 is NOT below e2.');
-
-jsTestIsAsync = true;
-
-var resultMap = [
+  var resultMap = [
     ["Down", "e1"],
     ["Right", "e2"],
     ["Down", "e2"],
@@ -45,22 +43,10 @@
     ["Up", "e3"],
     ["Left", "e2"],
     ["Up", "e2"],
-    ["Left", "e1"],
-    ["DONE", "DONE"]
-];
+    ["Left", "e1"]
+  ];
 
-if (window.testRunner) {
-    testRunner.overridePreference("WebKitTabToLinksPreferenceKey", 1);
-    testRunner.overridePreference("WebKitSpatialNavigationEnabled", 1);
-}
-
-function runTest()
-{
-    // starting the test itself: get to a known place.
-    document.getElementById("e1").focus();
-
-    initTest(resultMap, finishJSTest);
-}
+  // starting the test itself: get to a known place.
+  document.getElementById("e1").focus();
+  snav.assertFocusMoves(resultMap);
 </script>
-</body>
-</html>
diff --git a/third_party/blink/web_tests/fast/spatial-navigation/snav-not-rightof-expected.txt b/third_party/blink/web_tests/fast/spatial-navigation/snav-not-rightof-expected.txt
deleted file mode 100644
index 746df74..0000000
--- a/third_party/blink/web_tests/fast/spatial-navigation/snav-not-rightof-expected.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-This test ensures that e2 is NOT right of e1 and e3 is NOT right of e2.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-Element 1
-Element 2
-Element 3
-PASS gFocusedDocument.activeElement.getAttribute("id") is "e1"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "e2"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "e2"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "e3"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "e3"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "e2"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "e2"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "e1"
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/blink/web_tests/fast/spatial-navigation/snav-not-rightof.html b/third_party/blink/web_tests/fast/spatial-navigation/snav-not-rightof.html
index 97b79385..78ec2fe 100644
--- a/third_party/blink/web_tests/fast/spatial-navigation/snav-not-rightof.html
+++ b/third_party/blink/web_tests/fast/spatial-navigation/snav-not-rightof.html
@@ -1,43 +1,41 @@
 <!DOCTYPE html>
-<head>
-<script src="../../resources/js-test.js"></script>
-<script src="resources/spatial-navigation-utils.js"></script>
-</head>
 <style>
-body a {
+  body a {
     position: absolute;
     height: 100px;
     width: 100px;
     border: 1px solid black;
-}
-#e1 {
+  }
+
+  #e1 {
     left: 10px;
     top: 210px;
-}
-#e2 {
+  }
+
+  #e2 {
     left: 60px;
     top: 330px;
-}
-#e3 {
+  }
+
+  #e3 {
     left: 60px;
     top: 450px;
     width: 150px;
-}
+  }
 </style>
-<body id="some-content" onload="runTest()">
-<p id="description"></p>
+
+<p id="description">This test ensures that e2 is NOT right of e1 and e3 is NOT right of e2.</p>
 <div id="elements">
-<a id="e1" href="#e1">Element 1</a>
-<a id="e2" href="#e2">Element 2</a>
-<a id="e3" href="#e3">Element 3</a>
+  <a id="e1" href="#e1">Element 1</a>
+  <a id="e2" href="#e2">Element 2</a>
+  <a id="e3" href="#e3">Element 3</a>
 </div>
-<div id="console"></div>
+
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="resources/snav-testharness.js"></script>
 <script type="application/javascript">
-description('This test ensures that e2 is NOT right of e1 and e3 is NOT right of e2.');
-
-jsTestIsAsync = true;
-
-var resultMap = [
+  var resultMap = [
     ["Right", "e1"],
     ["Down", "e2"],
     ["Right", "e2"],
@@ -45,22 +43,10 @@
     ["Left", "e3"],
     ["Up", "e2"],
     ["Left", "e2"],
-    ["Up", "e1"],
-    ["DONE", "DONE"]
-];
+    ["Up", "e1"]
+  ];
 
-if (window.testRunner) {
-    testRunner.overridePreference("WebKitTabToLinksPreferenceKey", 1);
-    testRunner.overridePreference("WebKitSpatialNavigationEnabled", 1);
-}
-
-function runTest()
-{
-    // starting the test itself: get to a known place.
-    document.getElementById("e1").focus();
-
-    initTest(resultMap, finishJSTest);
-}
+  // starting the test itself: get to a known place.
+  document.getElementById("e1").focus();
+  snav.assertFocusMoves(resultMap);
 </script>
-</body>
-</html>
diff --git a/third_party/blink/web_tests/fast/spatial-navigation/snav-offscreen-content-expected.txt b/third_party/blink/web_tests/fast/spatial-navigation/snav-offscreen-content-expected.txt
deleted file mode 100644
index 9a389f8..0000000
--- a/third_party/blink/web_tests/fast/spatial-navigation/snav-offscreen-content-expected.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-offscreen content
-Right link
-
-Down link
-
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
-PASS gFocusedDocument.activeElement.getAttribute("id") is "1"
diff --git a/third_party/blink/web_tests/fast/spatial-navigation/snav-offscreen-content.html b/third_party/blink/web_tests/fast/spatial-navigation/snav-offscreen-content.html
index 89d064b..5c6fff8d 100644
--- a/third_party/blink/web_tests/fast/spatial-navigation/snav-offscreen-content.html
+++ b/third_party/blink/web_tests/fast/spatial-navigation/snav-offscreen-content.html
@@ -1,5 +1,5 @@
-<html>
-  <!--
+<!DOCTYPE html>
+<!--
     This test ensures the content overflow traversal correctness of Spatial Navigation
     algorithm: focusable elements in an scrollable containers (e.g. <div>) should be
     accessible, including offscreen content.
@@ -16,58 +16,29 @@
 
     * Expected results: All focusable element in scrollable content, including
     offscreen ones, should be accessible via SNav. -->
-  <head>
-    <style type="text/css">
-      .offscreen {
-        position: absolute;
-        left: -1000em;
-      }
-    </style>
 
-    <script src="../../resources/js-test.js"></script>
-    <script src="resources/spatial-navigation-utils.js"></script>
-    <script type="application/javascript">
+<style type="text/css">
+  .offscreen {
+    position: absolute;
+    left: -1000em;
+  }
+</style>
 
-    var resultMap = [
-      ["Left", "1"],
-      ["DONE", "DONE"]
-    ];
+<div style="margin-left:120px">
+  <a id="off" class="offscreen" href="a">offscreen content</a>
+  <a id="start" href="a">Right link</a>
+</div><br>
+<a id="1" href="a">Down link</a><br>
 
-    if (window.testRunner) {
-      testRunner.dumpAsText();
-      testRunner.overridePreference("WebKitTabToLinksPreferenceKey", 1);
-      testRunner.overridePreference("WebKitSpatialNavigationEnabled", 1);
-      testRunner.waitUntilDone();
-    }
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="resources/snav-testharness.js"></script>
+<script type="application/javascript">
+  var resultMap = [
+    ["Left", "1"]
+  ];
 
-    function runTest()
-    {
-      // starting the test itself: get to a known place.
-      document.getElementById("start").focus();
-
-      initTest(resultMap, testCompleted);
-    }
-
-    function testCompleted()
-    {
-      if (window.testRunner)
-        testRunner.notifyDone();
-    }
-
-    window.onload = runTest;
-
-    </script>
-  </head>
-
-
-  <body id="some-content" xmlns="http://www.w3.org/1999/xhtml">
-
-    <div style="margin-left:120px">
-      <a id="off" class="offscreen" href="a">offscreen content</a>
-      <a id="start" href="a">Right link</a>
-    </div><br>
-    <a id="1" href="a">Down link</a><br>
-    <div id="console"></div>
-  </body>
-</html>
-
+  // starting the test itself: get to a known place.
+  document.getElementById("start").focus();
+  snav.assertFocusMoves(resultMap);
+</script>
diff --git a/third_party/blink/web_tests/fast/spatial-navigation/snav-overlapping-elements-expected.txt b/third_party/blink/web_tests/fast/spatial-navigation/snav-overlapping-elements-expected.txt
deleted file mode 100644
index 16f10510..0000000
--- a/third_party/blink/web_tests/fast/spatial-navigation/snav-overlapping-elements-expected.txt
+++ /dev/null
@@ -1,25 +0,0 @@
-This test ensures that Spatial Navigation works with overlapping elements
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-Element 1
-Element 2
-Element 3
-Element 4
-Element 5
-Element 6
-PASS gFocusedDocument.activeElement.getAttribute("id") is "e2"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "e3"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "e2"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "e1"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "e2"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "e3"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "e4"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "e5"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "e6"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "e5"
-PASS gFocusedDocument.activeElement.getAttribute("id") is "e4"
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/blink/web_tests/fast/spatial-navigation/snav-overlapping-elements.html b/third_party/blink/web_tests/fast/spatial-navigation/snav-overlapping-elements.html
index b2100b4..01ade43 100644
--- a/third_party/blink/web_tests/fast/spatial-navigation/snav-overlapping-elements.html
+++ b/third_party/blink/web_tests/fast/spatial-navigation/snav-overlapping-elements.html
@@ -1,59 +1,60 @@
 <!DOCTYPE html>
-<head>
-<script src="../../resources/js-test.js"></script>
-<script src="resources/spatial-navigation-utils.js"></script>
-</head>
 <style>
-body a {
+  body a {
     position: absolute;
     height: 100px;
     width: 100px;
     border: 1px solid black;
-}
-#e1 {
+  }
+
+  #e1 {
     left: 10px;
     top: 10px;
-}
-#e2 {
+  }
+
+  #e2 {
     left: 20px;
     top: 75px;
-}
-#e3 {
+  }
+
+  #e3 {
     left: 10px;
     top: 150px;
-}
-#e4 {
+  }
+
+  #e4 {
     left: 10px;
     top: 300px;
-}
-#e5 {
+  }
+
+  #e5 {
     left: 75px;
     top: 300px;
-}
-#e6 {
+  }
+
+  #e6 {
     left: 150px;
     top: 300px;
-}
+  }
 </style>
-<body id="some-content" onload="runTest()">
-<p id="description"></p>
+
+<p id="description">This test ensures that Spatial Navigation works with overlapping elements.</p>
 <div id="elements">
-<!-- Vertical: -->
-<a id="e1" href="#e1">Element 1</a>
-<a id="e2" href="#e2">Element 2</a>
-<a id="e3" href="#e3">Element 3</a>
-<!-- Horizontal: -->
-<a id="e4" href="#e1">Element 4</a>
-<a id="e5" href="#e2">Element 5</a>
-<a id="e6" href="#e3">Element 6</a>
+  <!-- Vertical: -->
+  <a id="e1" href="#e1">Element 1</a>
+  <a id="e2" href="#e2">Element 2</a>
+  <a id="e3" href="#e3">Element 3</a>
+  <!-- Horizontal: -->
+  <a id="e4" href="#e1">Element 4</a>
+  <a id="e5" href="#e2">Element 5</a>
+  <a id="e6" href="#e3">Element 6</a>
 </div>
-<div id="console"></div>
+
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="resources/snav-testharness.js"></script>
 <script type="application/javascript">
-description('This test ensures that Spatial Navigation works with overlapping elements');
-
-jsTestIsAsync = true;
-
-var resultMap = [
+  var resultMap = [
     // Vertical:
     ["Down", "e2"],
     ["Down", "e3"],
@@ -66,22 +67,10 @@
     ["Right", "e5"],
     ["Right", "e6"],
     ["Left", "e5"],
-    ["Left", "e4"],
-    ["DONE", "DONE"]
-];
+    ["Left", "e4"]
+  ];
 
-if (window.testRunner) {
-    testRunner.overridePreference("WebKitTabToLinksPreferenceKey", 1);
-    testRunner.overridePreference("WebKitSpatialNavigationEnabled", 1);
-}
-
-function runTest()
-{
-    // starting the test itself: get to a known place.
-    document.getElementById("e1").focus();
-
-    initTest(resultMap, finishJSTest);
-}
+  // starting the test itself: get to a known place.
+  document.getElementById("e1").focus();
+  snav.assertFocusMoves(resultMap);
 </script>
-</body>
-</html>
diff --git a/third_party/blink/web_tests/fast/spatial-navigation/snav-simple-content-overflow-expected.txt b/third_party/blink/web_tests/fast/spatial-navigation/snav-simple-content-overflow-expected.txt
deleted file mode 100644
index 15ba911a..0000000
--- a/third_party/blink/web_tests/fast/spatial-navigation/snav-simple-content-overflow-expected.txt
+++ /dev/null
@@ -1,31 +0,0 @@
-It has a visible link_1.
-
-
-
-
-
-
-
-
-
-
-
-
-... and an overflowed link like this.
-
-
-
-
-
-
-
-
-
-
-This link should NOT get focused.
-
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
-PASS gFocusedDocument.activeElement.getAttribute("id") is "start"
diff --git a/third_party/blink/web_tests/fast/spatial-navigation/snav-simple-content-overflow.html b/third_party/blink/web_tests/fast/spatial-navigation/snav-simple-content-overflow.html
index cd977b6..cf4dceb2 100644
--- a/third_party/blink/web_tests/fast/spatial-navigation/snav-simple-content-overflow.html
+++ b/third_party/blink/web_tests/fast/spatial-navigation/snav-simple-content-overflow.html
@@ -1,5 +1,5 @@
-<html>
-  <!--
+<!DOCTYPE html>
+<!--
     This test ensures that overflowed content can be focused by Spatial Navigation.
 
     * Pre-conditions:
@@ -13,61 +13,34 @@
     * Expected results: After 'start' gets the focus, tapping down arrow should scroll its
       enclosing scrollable container (<div>) down, and *not* move focus to the link whose id is '2'.
   -->
-  <head>
-    <style type="text/css">
-      div.scroll {
-        height: 200px;
-        width: 300px;
-        overflow: auto;
-        border: 1px solid #666;
-        background-color: #ccc;
-        padding: 8px;
-      }
-    </style>
+<style type="text/css">
+  div.scroll {
+    height: 200px;
+    width: 300px;
+    overflow: auto;
+    border: 1px solid #666;
+    background-color: #ccc;
+    padding: 8px;
+  }
+</style>
 
-    <script src="../../resources/js-test.js"></script>
-    <script src="resources/spatial-navigation-utils.js"></script>
-    <script type="application/javascript">
+<div class="scroll">
+  <p>It has a visible <a id="start" href="a">link_1</a>.</p>
+  <br><br><br><br><br><br><br><br><br>
+  <p>... and an overflowed link like <a id="1" href="a">this</a>.</p>
+</div>
+<br><br><br><br><br><br><br>
+<p>This link should NOT get <a id="start" href="a">focused</a>.</p>
 
-    var resultMap = [
-      ["Down", "start"],
-      ["DONE", "DONE"]
-    ];
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="resources/snav-testharness.js"></script>
+<script type="application/javascript">
+  var resultMap = [
+    ["Down", "start"]
+  ];
 
-    if (window.testRunner) {
-      testRunner.dumpAsText();
-      testRunner.overridePreference("WebKitTabToLinksPreferenceKey", 1);
-      testRunner.overridePreference("WebKitSpatialNavigationEnabled", 1);
-      testRunner.waitUntilDone();
-    }
-
-    function runTest()
-    {
-      // starting the test itself: get to a known place.
-      document.getElementById("start").focus();
-
-      initTest(resultMap, testCompleted);
-    }
-
-    function testCompleted()
-    {
-      if (window.testRunner)
-        testRunner.notifyDone();
-    }
-
-    window.onload = runTest;
-
-    </script>
-  </head>
-
-  <body id="some-content" xmlns="http://www.w3.org/1999/xhtml">
-    <div class="scroll">
-      <p>It has a visible <a id="start" href="a">link_1</a>.</p>
-      <br><br><br><br><br><br><br><br><br>
-      <p>... and an overflowed link like <a id="1" href="a">this</a>.</p>
-    </div>
-    <br><br><br><br><br><br><br>
-    <p>This link should NOT get <a id="start" href="a">focused</a>.</p>
-    <div id="console"></div>
-  </body>
-</html>
+  // starting the test itself: get to a known place.
+  document.getElementById("start").focus();
+  snav.assertFocusMoves(resultMap);
+</script>
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/webaudio/graph-event-connection-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/webaudio/graph-event-connection-expected.txt
new file mode 100644
index 0000000..76d117d
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/webaudio/graph-event-connection-expected.txt
@@ -0,0 +1,56 @@
+Test graph events for the object connection.
+EventType = WebAudio.nodesConnected
+ contextId : <GraphObjectId>
+ sourceId : <GraphObjectId>
+ destinationId : <GraphObjectId>
+ sourceOutputIndex : 0
+ destinationInputIndex : 0
+EventType = WebAudio.nodesDisconnected
+ contextId : <GraphObjectId>
+ sourceId : <GraphObjectId>
+ destinationId : <GraphObjectId>
+ sourceOutputIndex : 0
+ destinationInputIndex : 0
+EventType = WebAudio.nodesDisconnected
+ contextId : <GraphObjectId>
+ sourceId : <GraphObjectId>
+ destinationId : null
+ sourceOutputIndex : 0
+ destinationInputIndex : 0
+EventType = WebAudio.nodesConnected
+ contextId : <GraphObjectId>
+ sourceId : <GraphObjectId>
+ destinationId : <GraphObjectId>
+ sourceOutputIndex : 1
+ destinationInputIndex : 4
+EventType = WebAudio.nodesDisconnected
+ contextId : <GraphObjectId>
+ sourceId : <GraphObjectId>
+ destinationId : <GraphObjectId>
+ sourceOutputIndex : 1
+ destinationInputIndex : 4
+EventType = WebAudio.nodeParamConnected
+ contextId : <GraphObjectId>
+ sourceId : <GraphObjectId>
+ destinationId : <GraphObjectId>
+ sourceOutputIndex : 0
+ destinationInputIndex : undefined
+EventType = WebAudio.nodeParamDisconnected
+ contextId : <GraphObjectId>
+ sourceId : <GraphObjectId>
+ destinationId : <GraphObjectId>
+ sourceOutputIndex : 0
+ destinationInputIndex : undefined
+EventType = WebAudio.nodeParamConnected
+ contextId : <GraphObjectId>
+ sourceId : <GraphObjectId>
+ destinationId : <GraphObjectId>
+ sourceOutputIndex : 1
+ destinationInputIndex : undefined
+EventType = WebAudio.nodeParamDisconnected
+ contextId : <GraphObjectId>
+ sourceId : <GraphObjectId>
+ destinationId : <GraphObjectId>
+ sourceOutputIndex : 1
+ destinationInputIndex : undefined
+
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/webaudio/graph-event-connection.js b/third_party/blink/web_tests/http/tests/inspector-protocol/webaudio/graph-event-connection.js
new file mode 100644
index 0000000..8f4f5e7
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/webaudio/graph-event-connection.js
@@ -0,0 +1,82 @@
+(async function(testRunner) {
+  const {_, session, dp} = await testRunner.startBlank(`Test graph events for the object connection.`);
+
+  await dp.WebAudio.enable();
+
+  function anonymizeUuid(uuid) {
+    return (typeof uuid === 'string' && uuid.length === 36) ? '<GraphObjectId>' : null;
+  }
+
+  function logEvent(event) {
+    testRunner.log(`EventType = ${event.method}`);
+    testRunner.log(` contextId : ${anonymizeUuid(event.params.contextId)}`);
+    testRunner.log(` sourceId : ${anonymizeUuid(event.params.sourceId)}`);
+    testRunner.log(` destinationId : ${anonymizeUuid(event.params.destinationId)}`);
+    testRunner.log(` sourceOutputIndex : ${event.params.sourceOutputIndex}`);
+    testRunner.log(` destinationInputIndex : ${event.params.destinationInputIndex}`);
+  }
+
+  // AudioNode-AudioNode connection without specified indices.
+  session.evaluate(`
+    const context = new AudioContext();
+    const gain1 = new GainNode(context);
+    const gain2 = new GainNode(context);
+    gain1.connect(gain2);
+  `);
+  logEvent(await dp.WebAudio.onceNodesConnected());
+
+  // AudioNode disconnection with a specified destination.
+  session.evaluate(`
+    gain1.disconnect(gain2);
+  `);
+  logEvent(await dp.WebAudio.onceNodesDisconnected());
+
+  // AudioNode disconnection without a specified destination.
+  session.evaluate(`
+    gain2.connect(context.destination);
+    gain2.disconnect();
+  `);
+  logEvent(await dp.WebAudio.onceNodesDisconnected());
+
+  // AudioNode-AudioNode connection with specified input/output index.
+  session.evaluate(`
+    const splitter = new ChannelSplitterNode(context);
+    const merger = new ChannelMergerNode(context);
+    splitter.connect(merger, 1, 4);
+  `);
+  logEvent(await dp.WebAudio.onceNodesConnected());
+
+  // AudioNode-AudioNode disconnection with specified input/output index.
+  session.evaluate(`
+    splitter.disconnect(merger, 1, 4);
+  `);
+  logEvent(await dp.WebAudio.onceNodesDisconnected());
+
+  // AudioNode-AudioParam connection.
+  session.evaluate(`
+    const osc = new OscillatorNode(context);
+    gain2.connect(osc.frequency);
+  `);
+  logEvent(await dp.WebAudio.onceNodeParamConnected());
+
+  // AudioNode-AudioParam disconnection.
+  session.evaluate(`
+    gain2.disconnect(osc.frequency);
+  `);
+  logEvent(await dp.WebAudio.onceNodeParamDisconnected());
+
+  // AudioNode.outputIndex-AudioParam connection.
+  session.evaluate(`
+    splitter.connect(osc.frequency, 1);
+  `);
+  logEvent(await dp.WebAudio.onceNodeParamConnected());
+
+  // AudioNode.outputIndex-AudioParam disconnection.
+  session.evaluate(`
+    splitter.disconnect(osc.frequency, 1);
+  `);
+  logEvent(await dp.WebAudio.onceNodeParamDisconnected());
+
+  await dp.WebAudio.disable();
+  testRunner.completeTest();
+});
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/webaudio/graph-event-lifecycle-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/webaudio/graph-event-lifecycle-expected.txt
new file mode 100644
index 0000000..6d678f0
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/webaudio/graph-event-lifecycle-expected.txt
@@ -0,0 +1,57 @@
+Test graph events for the object lifecycle.
+Successfully created: BaseAudioContext(realtime)
+Successfully created: AudioDestinationNode
+Successfully created: AudioListener
+  - AudioParam::positionX
+  - AudioParam::positionY
+  - AudioParam::positionZ
+  - AudioParam::forwardX
+  - AudioParam::forwardY
+  - AudioParam::forwardZ
+  - AudioParam::upX
+  - AudioParam::upY
+  - AudioParam::upZ
+Successfully created: Analyser
+Successfully created: AudioBufferSource
+  - AudioParam::detune
+  - AudioParam::playbackRate
+Successfully created: AudioWorklet
+Successfully created: BiquadFilter
+  - AudioParam::detune
+  - AudioParam::frequency
+  - AudioParam::gain
+  - AudioParam::Q
+Successfully created: ChannelMerger
+Successfully created: ChannelSplitter
+Successfully created: ConstantSource
+  - AudioParam::offset
+Successfully created: Convolver
+Successfully created: Delay
+  - AudioParam::delayTime
+Successfully created: DynamicsCompressor
+  - AudioParam::attack
+  - AudioParam::knee
+  - AudioParam::ratio
+  - AudioParam::release
+  - AudioParam::threshold
+Successfully created: Gain
+  - AudioParam::gain
+Successfully created: IIRFilter
+Successfully created: MediaElementAudioSource
+Successfully created: MediaStreamAudioDestination
+Successfully created: MediaStreamAudioSource
+Successfully created: Oscillator
+  - AudioParam::detune
+  - AudioParam::frequency
+Successfully created: Panner
+  - AudioParam::positionX
+  - AudioParam::positionY
+  - AudioParam::positionZ
+  - AudioParam::orientationX
+  - AudioParam::orientationY
+  - AudioParam::orientationZ
+Successfully created: ScriptProcessor
+Successfully created: StereoPanner
+  - AudioParam::pan
+Successfully created: WaveShaper
+
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/webaudio/graph-event-lifecycle.js b/third_party/blink/web_tests/http/tests/inspector-protocol/webaudio/graph-event-lifecycle.js
new file mode 100644
index 0000000..2ea337e
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/webaudio/graph-event-lifecycle.js
@@ -0,0 +1,139 @@
+(async function(testRunner) {
+  // Data structure for the comprehensive testing. Each AudioNode can have
+  // an unique constructor pattern and also can have a different set of
+  // AudioParams. Each data entry has:
+  //   {string} nodeName AudioNode type
+  //   {string=} ctorString JS code string for the node construction. This is
+  //                        needed when the simple construction doesn't work.
+  //   {Array<string>=} audioParams Names of the associated AudioParam objects
+  const testData = [
+    {
+      nodeName: 'AnalyserNode',
+    },
+    {
+      nodeName: 'AudioBufferSourceNode',
+      audioParams: ['detune', 'playbackRate'],
+    },
+    {
+      nodeName: 'AudioWorkletNode',
+      ctorString: `
+        const processorSource =
+            'registerProcessor("test-processor", class extends AudioWorkletProcessor { process() {} });';
+        const blob = new Blob([processorSource], {type: 'application/javascript'});
+        const objectURL = URL.createObjectURL(blob);
+        context.audioWorklet.addModule(objectURL).then(() => {
+          new AudioWorkletNode(context, 'test-processor');
+        });
+      `,
+    },
+    {
+      nodeName: 'BiquadFilterNode',
+      audioParams: ['frequency', 'detune', 'Q', 'gain'],
+    },
+    {
+      nodeName: 'ChannelMergerNode',
+    },
+    {
+      nodeName: 'ChannelSplitterNode',
+    },
+    {
+      nodeName: 'ConstantSourceNode',
+      audioParams: ['offset'],
+    },
+    {
+      nodeName: 'ConvolverNode',
+    },
+    {
+      nodeName: 'DelayNode',
+      audioParams: ['delayTime'],
+    },
+    {
+      nodeName: 'DynamicsCompressorNode',
+      audioParams: ['threshold', 'knee', 'ratio', 'attack', 'release'],
+    },
+    {
+      nodeName: 'GainNode',
+      audioParams: ['gain'],
+    },
+    {
+      nodeName: 'IIRFilterNode',
+      ctorString: `new IIRFilterNode(context, {feedforward: [1], feedback: [1, -0.99]});`,
+    },
+    {
+      nodeName: 'MediaElementAudioSourceNode',
+      ctorString: `
+        const audioElement = new Audio();
+        new MediaElementAudioSourceNode(context, {mediaElement: audioElement});
+      `,
+    },
+    {
+      nodeName: 'MediaStreamAudioDestinationNode',
+      ctorString: `new MediaStreamAudioDestinationNode(context);`,
+    },
+    {
+      nodeName: 'MediaStreamAudioSourceNode',
+      ctorString: `
+        navigator.mediaDevices.getUserMedia({audio: true})
+            .then(stream => new MediaStreamAudioSourceNode(context, {mediaStream: stream}));
+      `,
+    },
+    {
+      nodeName: 'OscillatorNode',
+      audioParams: ['frequency', 'detune'],
+    },
+    {
+      nodeName: 'PannerNode',
+      audioParams: ['positionX', 'positionY', 'positionZ', 'orientationX', 'orientationY', 'orientationZ'],
+    },
+    {
+      nodeName: 'ScriptProcessorNode',
+      ctorString: `context.createScriptProcessor();`,
+    },
+    {
+      nodeName: 'StereoPannerNode',
+      audioParams: ['pan'],
+    },
+    {
+      nodeName: 'WaveShaperNode',
+    },
+  ];
+
+  const {_, session, dp} = await testRunner.startBlank(`Test graph events for the object lifecycle.`);
+
+  await dp.WebAudio.enable();
+
+  let event;
+
+  // Create an AudioContext. A context contains pre-constructed
+  // AudioDestinationNode and AudioListener. Note that AudioListener contains
+  // 9 AudioParams.
+  session.evaluate('const context = new AudioContext();');
+  event = await dp.WebAudio.onceContextCreated();
+  testRunner.log(`Successfully created: BaseAudioContext(${event.params.context.contextType})`);
+  event = await dp.WebAudio.onceAudioNodeCreated();
+  testRunner.log(`Successfully created: AudioDestinationNode`);
+  event = await dp.WebAudio.onceAudioListenerCreated();
+  testRunner.log(`Successfully created: AudioListener`);
+  for (let i = 0; i < 9; ++i) {
+    event = await dp.WebAudio.onceAudioParamCreated();
+    testRunner.log(`  - AudioParam::${event.params.param.paramType}`);
+  }
+
+  for (const entry of testData) {
+    session.evaluate(entry.ctorString || `new ${entry.nodeName}(context);`);
+    event = await dp.WebAudio.onceAudioNodeCreated();
+    testRunner.log(`Successfully created: ${event.params.node.nodeType}`);
+    if (entry.audioParams) {
+      for (let i = 0; i < entry.audioParams.length; ++i) {
+        event = await dp.WebAudio.onceAudioParamCreated();
+        testRunner.log(`  - AudioParam::${event.params.param.paramType}`);
+      }
+    }
+  }
+
+  // There is no way to invoke GC in the session, |fooWillBeDestroyed()| events
+  // cannot be tested.
+
+  await dp.WebAudio.disable();
+  testRunner.completeTest();
+});
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
index db95b723..b534cf5d 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
@@ -2147,8 +2147,53 @@
     setter slot
 interface ElementInternals
     attribute @@toStringTag
+    getter ariaActiveDescendant
+    getter ariaAtomic
+    getter ariaAutoComplete
+    getter ariaBusy
+    getter ariaChecked
+    getter ariaColCount
+    getter ariaColIndex
+    getter ariaColSpan
+    getter ariaControls
+    getter ariaCurrent
+    getter ariaDescribedBy
+    getter ariaDetails
+    getter ariaDisabled
+    getter ariaErrorMessage
+    getter ariaExpanded
+    getter ariaFlowTo
+    getter ariaHasPopup
+    getter ariaHidden
+    getter ariaKeyShortcuts
+    getter ariaLabel
+    getter ariaLabelledBy
+    getter ariaLevel
+    getter ariaLive
+    getter ariaModal
+    getter ariaMultiLine
+    getter ariaMultiSelectable
+    getter ariaOrientation
+    getter ariaOwns
+    getter ariaPlaceholder
+    getter ariaPosInSet
+    getter ariaPressed
+    getter ariaReadOnly
+    getter ariaRelevant
+    getter ariaRequired
+    getter ariaRoleDescription
+    getter ariaRowCount
+    getter ariaRowIndex
+    getter ariaRowSpan
+    getter ariaSelected
+    getter ariaSort
+    getter ariaValueMax
+    getter ariaValueMin
+    getter ariaValueNow
+    getter ariaValueText
     getter form
     getter labels
+    getter role
     getter validationMessage
     getter validity
     getter willValidate
@@ -2157,6 +2202,51 @@
     method reportValidity
     method setFormValue
     method setValidity
+    setter ariaActiveDescendant
+    setter ariaAtomic
+    setter ariaAutoComplete
+    setter ariaBusy
+    setter ariaChecked
+    setter ariaColCount
+    setter ariaColIndex
+    setter ariaColSpan
+    setter ariaControls
+    setter ariaCurrent
+    setter ariaDescribedBy
+    setter ariaDetails
+    setter ariaDisabled
+    setter ariaErrorMessage
+    setter ariaExpanded
+    setter ariaFlowTo
+    setter ariaHasPopup
+    setter ariaHidden
+    setter ariaKeyShortcuts
+    setter ariaLabel
+    setter ariaLabelledBy
+    setter ariaLevel
+    setter ariaLive
+    setter ariaModal
+    setter ariaMultiLine
+    setter ariaMultiSelectable
+    setter ariaOrientation
+    setter ariaOwns
+    setter ariaPlaceholder
+    setter ariaPosInSet
+    setter ariaPressed
+    setter ariaReadOnly
+    setter ariaRelevant
+    setter ariaRequired
+    setter ariaRoleDescription
+    setter ariaRowCount
+    setter ariaRowIndex
+    setter ariaRowSpan
+    setter ariaSelected
+    setter ariaSort
+    setter ariaValueMax
+    setter ariaValueMin
+    setter ariaValueNow
+    setter ariaValueText
+    setter role
 interface EnterPictureInPictureEvent : Event
     attribute @@toStringTag
     getter pictureInPictureWindow
diff --git a/third_party/custom_tabs_client/README.chromium b/third_party/custom_tabs_client/README.chromium
index 95d433cc..0bf829b 100644
--- a/third_party/custom_tabs_client/README.chromium
+++ b/third_party/custom_tabs_client/README.chromium
@@ -3,7 +3,7 @@
 URL: https://chromium.googlesource.com/external/github.com/GoogleChrome/custom-tabs-client
 Version: unknown
 License: Apache 2.0
-Security Critical: no
+Security Critical: yes
 License Android Compatible: yes
 
 Description:
@@ -16,4 +16,11 @@
 The example applicaton also presents how to use Browser Actions, including
 creating request intent and adding custom items.
 
+The actual code that Chromium builds from is in
+//third_party/android_sdk/androidx_browser, this subdirectory is kept around
+for the example app (the custom_tabs_client_example_apk target).
+
+TODO(peconn): Get rid of src/customtabs and depend instead on
+androidx_browser.
+
 Local Modifications: None
diff --git a/third_party/polymer/v3_0/chromium.patch b/third_party/polymer/v3_0/chromium.patch
index 1a873a7..354e5b4 100644
--- a/third_party/polymer/v3_0/chromium.patch
+++ b/third_party/polymer/v3_0/chromium.patch
@@ -11,6 +11,17 @@
 +  link.href = 'chrome://resources/css/roboto.css';
    document.head.appendChild(link);
  }
+--- a/components-chromium/iron-a11y-announcer/iron-a11y-announcer.js
++++ b/components-chromium/iron-a11y-announcer/iron-a11y-announcer.js
+@@ -46,7 +46,7 @@ Note: announcements are only audible if you have a screen reader enabled.
+ @group Iron Elements
+ @demo demo/index.html
+ */
+-export const IronA11yAnnouncer = Polymer({
++export var IronA11yAnnouncer = Polymer({
+   _template: html`
+     <style>
+       :host {
 diff --git a/components-chromium/polymer/lib/legacy/legacy-element-mixin.js b/components-chromium/polymer/lib/legacy/legacy-element-mixin.js
 index 21fa65c0208d..bd591b6dd341 100644
 --- a/components-chromium/polymer/lib/legacy/legacy-element-mixin.js
diff --git a/third_party/polymer/v3_0/components-chromium/iron-a11y-announcer/iron-a11y-announcer.js b/third_party/polymer/v3_0/components-chromium/iron-a11y-announcer/iron-a11y-announcer.js
index 7d8d859..2db3c7a 100644
--- a/third_party/polymer/v3_0/components-chromium/iron-a11y-announcer/iron-a11y-announcer.js
+++ b/third_party/polymer/v3_0/components-chromium/iron-a11y-announcer/iron-a11y-announcer.js
@@ -46,7 +46,7 @@
 @group Iron Elements
 @demo demo/index.html
 */
-export const IronA11yAnnouncer = Polymer({
+export var IronA11yAnnouncer = Polymer({
   _template: html`
     <style>
       :host {
diff --git a/third_party/zlib/deflate.c b/third_party/zlib/deflate.c
index 98da939c..c950d6fb 100644
--- a/third_party/zlib/deflate.c
+++ b/third_party/zlib/deflate.c
@@ -215,13 +215,23 @@
 
 local INLINE Pos insert_string(deflate_state *const s, const Pos str)
 {
+/* String dictionary insertion: faster symbol hashing has a positive impact
+ * on data compression speeds (around 20% on Intel and 36% on ARM Cortex big
+ * cores).
+ * A misfeature is that the generated compressed output will differ from
+ * vanilla zlib (even though it is still valid 'DEFLATE-d' content).
+ *
+ * We offer here a way to disable the optimization if there is the expectation
+ * that compressed content should match when compared to vanilla zlib.
+ */
+#if !defined(CHROMIUM_ZLIB_NO_CASTAGNOLI)
 #if defined(CRC32_ARMV8_CRC32)
     if (arm_cpu_enable_crc32)
         return insert_string_arm(s, str);
 #endif
     if (x86_cpu_enable_simd)
         return insert_string_sse(s, str);
-
+#endif
     return insert_string_c(s, str);
 }
 
diff --git a/tools/android/customtabs_benchmark/BUILD.gn b/tools/android/customtabs_benchmark/BUILD.gn
index 27cf5e0..e8dec60 100644
--- a/tools/android/customtabs_benchmark/BUILD.gn
+++ b/tools/android/customtabs_benchmark/BUILD.gn
@@ -21,6 +21,6 @@
   deps = [
     ":customtabs_benchmark_apk_resources",
     "//third_party/android_deps:android_support_v4_java",
-    "//third_party/custom_tabs_client:custom_tabs_support_java",
+    "//third_party/android_sdk/androidx_browser:androidx_browser_java",
   ]
 }
diff --git a/tools/android/customtabs_benchmark/java/src/org/chromium/customtabs/test/MainActivity.java b/tools/android/customtabs_benchmark/java/src/org/chromium/customtabs/test/MainActivity.java
index 3e205ec8..b16f4c13 100644
--- a/tools/android/customtabs_benchmark/java/src/org/chromium/customtabs/test/MainActivity.java
+++ b/tools/android/customtabs_benchmark/java/src/org/chromium/customtabs/test/MainActivity.java
@@ -18,11 +18,6 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.SystemClock;
-import android.support.customtabs.CustomTabsCallback;
-import android.support.customtabs.CustomTabsClient;
-import android.support.customtabs.CustomTabsIntent;
-import android.support.customtabs.CustomTabsServiceConnection;
-import android.support.customtabs.CustomTabsSession;
 import android.support.v4.app.BundleCompat;
 import android.util.Log;
 import android.view.View;
@@ -39,6 +34,12 @@
 import java.util.Random;
 import java.util.Set;
 
+import androidx.browser.customtabs.CustomTabsCallback;
+import androidx.browser.customtabs.CustomTabsClient;
+import androidx.browser.customtabs.CustomTabsIntent;
+import androidx.browser.customtabs.CustomTabsServiceConnection;
+import androidx.browser.customtabs.CustomTabsSession;
+
 /** Activity used to benchmark Custom Tabs PLT.
  *
  * This activity contains benchmark code for two modes:
diff --git a/tools/android/eclipse/.classpath b/tools/android/eclipse/.classpath
index e9af384..17f6778 100644
--- a/tools/android/eclipse/.classpath
+++ b/tools/android/eclipse/.classpath
@@ -114,10 +114,8 @@
     <classpathentry kind="src" path="third_party/android_data_chart/java/src"/>
     <classpathentry kind="src" path="third_party/android_protobuf/src/java/src/main/java" including="com/google/protobuf/nano/*"/>
     <classpathentry kind="src" path="third_party/android_swipe_refresh/java/src"/>
+    <classpathentry kind="src" path="third_party/androidx_browser/src/browser/src"/>
     <classpathentry kind="src" path="third_party/cacheinvalidation/src/java"/>
-    <classpathentry kind="src" path="third_party/custom_tabs_client/src/customtabs/src"/>
-    <classpathentry kind="src" path="third_party/custom_tabs_client/src/Application/src/main/java"/>
-    <classpathentry kind="src" path="third_party/custom_tabs_client/src/shared/src/main/java"/>
     <classpathentry kind="src" path="third_party/gif_player/src"/>
     <classpathentry kind="src" path="third_party/junit/src/src/main/java"/>
     <classpathentry kind="src" path="third_party/mockito/src/src/main/java"/>
diff --git a/tools/grit/grit/node/base.py b/tools/grit/grit/node/base.py
index 80446a8..0519b59 100644
--- a/tools/grit/grit/node/base.py
+++ b/tools/grit/grit/node/base.py
@@ -610,7 +610,7 @@
       The data in gzipped or brotli compressed format. If the format is
       unspecified then this returns the data uncompressed.
     '''
-    if self.attrs.get('compress') == 'gzip':
+    if self.attrs.get('compress') in ('gzip', 'true'):
       # We only use rsyncable compression on Linux.
       # We exclude ChromeOS since ChromeOS bots are Linux based but do not have
       # the --rsyncable option built in for gzip. See crbug.com/617950.
@@ -618,7 +618,7 @@
         return grit.format.gzip_string.GzipStringRsyncable(data)
       return grit.format.gzip_string.GzipString(data)
 
-    elif self.attrs.get('compress') in ('true', 'brotli'):
+    elif self.attrs.get('compress') == 'brotli':
       # The length of the uncompressed data as 8 bytes little-endian.
       size_bytes = struct.pack("<q", len(data))
       data = brotli_util.BrotliCompress(data)
diff --git a/tools/grit/grit/node/brotli_util.py b/tools/grit/grit/node/brotli_util.py
index d518215..77f70e4 100644
--- a/tools/grit/grit/node/brotli_util.py
+++ b/tools/grit/grit/node/brotli_util.py
@@ -18,7 +18,12 @@
 
 def BrotliCompress(data):
   if not __brotli_executable:
-    raise Exception('SetBrotliCommand has not been called yet!')
+    raise Exception('Add "use_brotli = true" to you GN grit(...) target ' +
+                    'if you want to use brotli.')
   compress = subprocess.Popen(__brotli_executable + ['-', '-f'],
                               stdin=subprocess.PIPE, stdout=subprocess.PIPE)
   return compress.communicate(data)[0]
+
+def IsInitialized():
+  global __brotli_executable
+  return __brotli_executable is not None
diff --git a/tools/grit/grit/node/structure_unittest.py b/tools/grit/grit/node/structure_unittest.py
index fe22d4a..ea43f41 100755
--- a/tools/grit/grit/node/structure_unittest.py
+++ b/tools/grit/grit/node/structure_unittest.py
@@ -126,24 +126,13 @@
         base_dir=test_data_root)
     node, = root.GetChildrenOfType(structure.StructureNode)
     node.RunPreSubstitutionGatherer()
-
-    # Using the mock brotli decompression executable.
-    brotli_util.SetBrotliCommand([sys.executable,
-                                 os.path.join(os.path.dirname(__file__),
-                                 'mock_brotli.py')])
     compressed = node.GetDataPackValue(lang='en', encoding=1)
-    # Assert that the first two bytes in compressed format is BROTLI_CONST.
-    self.assertEqual(constants.BROTLI_CONST, compressed[0:2])
 
-    # Compare the actual size of the uncompressed test data with
-    # the size appended during compression.
-    actual_size = len(util.ReadFile(
-        os.path.join(test_data_root, 'test_text.txt'), util.BINARY))
-    uncompress_size = struct.unpack('<i', compressed[2:6])[0]
-    uncompress_size += struct.unpack('<h', compressed[6:8])[0] << 4*8
-    self.assertEqual(actual_size, uncompress_size)
-
-    self.assertEqual('This has been mock compressed!', compressed[8:])
+    decompressed_data = zlib.decompress(compressed, 16 + zlib.MAX_WBITS)
+    self.assertEqual(
+        util.ReadFile(
+            os.path.join(test_data_root, 'test_text.txt'), util.BINARY),
+        decompressed_data)
 
   def testCompressBrotli(self):
     test_data_root = util.PathFromRoot('grit/testdata')
diff --git a/tools/grit/grit/tool/build.py b/tools/grit/grit/tool/build.py
index 436830a..2fb8df3 100644
--- a/tools/grit/grit/tool/build.py
+++ b/tools/grit/grit/tool/build.py
@@ -149,6 +149,7 @@
     return 'A tool that builds RC files for compilation.'
 
   def Run(self, opts, args):
+    brotli_util.SetBrotliCommand(None)
     os.environ['cwd'] = os.getcwd()
     self.output_directory = '.'
     first_ids_file = None
@@ -256,6 +257,16 @@
 
     self.Process()
 
+    # Check that brotli was used if initialized.
+    if brotli_util.IsInitialized():
+      brotli_used = False
+      for node in self.res.ActiveDescendants():
+        if node.attrs.get('compress') == 'brotli':
+          brotli_used = True
+      if not brotli_used:
+        raise Exception('"use_brotli" was set to true in GN grit(...) target, ' +
+                        'but no resources were brotli compressed.')
+
     if assert_output_files:
       if not self.CheckAssertedOutputFiles(assert_output_files):
         return 2
diff --git a/tools/grit/grit_rule.gni b/tools/grit/grit_rule.gni
index 636628a..002d8dc9 100644
--- a/tools/grit/grit_rule.gni
+++ b/tools/grit/grit_rule.gni
@@ -347,16 +347,6 @@
     depfile = "$depfile_dir/${grit_output_name}_stamp.d"
     outputs = [ "${depfile}.stamp" ] + grit_outputs + pak_info_outputs
 
-    brotli_target = "//third_party/brotli:brotli($host_toolchain)"
-
-    brotli_executable = get_label_info(brotli_target, "root_out_dir") + "/" +
-                        get_label_info(brotli_target, "name")
-    if (host_os == "win") {
-      brotli_executable += ".exe"
-    }
-
-    inputs += [ brotli_executable ]
-
     args = [
              "-i",
              source_path,
@@ -369,10 +359,24 @@
              rebase_path(depfile, root_build_dir),
              "--write-only-new=1",
              "--depend-on-stamp",
-             "--brotli",
-             rebase_path(brotli_executable, root_build_dir),
            ] + grit_defines
 
+    # Add brotli executable if using brotli.
+    if (defined(invoker.use_brotli) && invoker.use_brotli) {
+      brotli_target = "//third_party/brotli:brotli($host_toolchain)"
+      brotli_executable = get_label_info(brotli_target, "root_out_dir") + "/" +
+                          get_label_info(brotli_target, "name")
+      if (host_os == "win") {
+        brotli_executable += ".exe"
+      }
+
+      inputs += [ brotli_executable ]
+      args += [
+        "--brotli",
+        rebase_path(brotli_executable, root_build_dir),
+      ]
+    }
+
     # Add extra defines with -D flags.
     define_args = []
     if (defined(invoker.defines)) {
@@ -448,10 +452,12 @@
     deps = [
       "//tools/grit:grit_sources",
     ]
-    if (is_mac && is_asan) {
-      deps += [ "//tools/grit:brotli_mac_asan_workaround" ]
-    } else {
-      deps += [ brotli_target ]
+    if (defined(invoker.use_brotli) && invoker.use_brotli) {
+      if (is_mac && is_asan) {
+        deps += [ "//tools/grit:brotli_mac_asan_workaround" ]
+      } else {
+        deps += [ "//third_party/brotli:brotli($host_toolchain)" ]
+      }
     }
     if (defined(invoker.deps)) {
       deps += invoker.deps
diff --git a/tools/gritsettings/resource_ids b/tools/gritsettings/resource_ids
index def08e61..de165fe4 100644
--- a/tools/gritsettings/resource_ids
+++ b/tools/gritsettings/resource_ids
@@ -147,7 +147,7 @@
     "structures": [13350],
   },
   "chrome/browser/resources/sync_file_system_internals/sync_file_system_internals_resources.grd": {
-    "includes": [13850],
+    "includes": [13860],
   },
   "chrome/browser/resources/tab_strip/tab_strip_resources.grd": {
     "structures": [13880],
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index de18776a..3a2a6e5 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -20253,6 +20253,11 @@
   <int value="1366" label="AUTOTESTPRIVATE_GETSHELFITEMS"/>
   <int value="1367" label="MANAGEMENT_INSTALLREPLACEMENTANDROIDAPP"/>
   <int value="1368" label="MANAGEMENT_CANINSTALLREPLACEMENTANDROIDAPP"/>
+  <int value="1369" label="AUTOTESTPRIVATE_REGISTERCOMPONENT"/>
+  <int value="1370" label="LOGINSCREENSTORAGE_STOREPERSISTENTDATA"/>
+  <int value="1371" label="LOGINSCREENSTORAGE_RETRIEVEPERSISTENTDATA"/>
+  <int value="1372" label="LOGINSCREENSTORAGE_STORECREDENTIALS"/>
+  <int value="1373" label="LOGINSCREENSTORAGE_RETRIEVECREDENTIALS"/>
 </enum>
 
 <enum name="ExtensionIconState">
@@ -20790,6 +20795,7 @@
   <int value="218" label="kDeclarativeNetRequestFeedback"/>
   <int value="219" label="kTransientBackground"/>
   <int value="220" label="kLogin"/>
+  <int value="221" label="kLoginScreenStorage"/>
 </enum>
 
 <enum name="ExtensionServiceVerifyAllSuccess">
@@ -51832,6 +51838,27 @@
   <int value="4" label="NOT_SUPPORTED"/>
 </enum>
 
+<enum name="SamlInSessionPasswordChangeEvent">
+  <summary>
+    Track how often we try to guide users through the in-session flow to change
+    their SAML password, and how often they complete the flow.
+  </summary>
+  <int value="0" label="In-session PW-change manager enabled and created."/>
+  <int value="1" label="User notified that password will soon expire."/>
+  <int value="2" label="User notified urgently: password will soon expire."/>
+  <int value="3" label="User notified urgently: password already expired."/>
+  <int value="4" label="User acknowledges and begins change password flow."/>
+  <int value="5" label="SAML password is changed during in-session flow."/>
+  <int value="6" label="Password scraping succeeds during in-session change."/>
+  <int value="7" label="Password scraping partial success - 1 password only."/>
+  <int value="8" label="Password scraping fails - no passwords scraped."/>
+  <int value="9" label="Cryptohome password changed using scraped passwords."/>
+  <int value="10" label="Cryptohome password changed using retyped passwords."/>
+  <int value="11" label="Cryptohome change failed: wrong password scraped."/>
+  <int value="12" label="Cryptohome change failed: wrong password retyped."/>
+  <int value="13" label="In-session change password flow complete."/>
+</enum>
+
 <enum name="SamplingProfilerUnwindResult">
   <summary>Track reason for unwind failures in sampling profiler.</summary>
   <int value="0" label="Futex wait for signal handler failed."/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index f12eb20..0178db9 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -520,6 +520,7 @@
   <owner>jessejames@chromium.org</owner>
   <owner>rsorokin@chromium.org</owner>
   <owner>alemate@chromium.org</owner>
+  <owner>cros-oac@google.com</owner>
   <summary>
     Delay between login_prompt_visible and Chrome OS OOBE startup sound
     playback. Depends on sound subsystem initialization time.
@@ -6746,6 +6747,7 @@
 <histogram name="Ash.Login.Lock.UserClicks" enum="LockScreenUserClickTarget"
     expires_after="M82">
   <owner>rsorokin@chromium.org</owner>
+  <owner>cros-oac@google.com</owner>
   <summary>
     The numbers of times that users click on the shelf buttons, trays and lock
     screen note on the ChromeOS lock screen.
@@ -6755,6 +6757,7 @@
 <histogram name="Ash.Login.Login.UserClicks" enum="LoginScreenUserClickTarget"
     expires_after="M82">
   <owner>rsorokin@chromium.org</owner>
+  <owner>cros-oac@google.com</owner>
   <summary>
     The numbers of times that users click on the shelf buttons and trays on the
     ChromeOS login screen.
@@ -17748,6 +17751,7 @@
 <histogram name="CaptivePortal.Notification.Status"
     enum="CaptivePortalNotificationStatus" expires_after="2020-02-01">
   <owner>rsorokin@chromium.org</owner>
+  <owner>cros-oac@google.com</owner>
   <summary>
     Count of displayed and not displayed due to errors notifications about
     captive portal.
@@ -17757,6 +17761,7 @@
 <histogram name="CaptivePortal.Notification.UserAction"
     enum="CaptivePortalNotificationUserAction" expires_after="2020-02-01">
   <owner>rsorokin@chromium.org</owner>
+  <owner>cros-oac@google.com</owner>
   <summary>
     Count of clicked, closed and ignored captive portal notifications.
   </summary>
@@ -17765,6 +17770,7 @@
 <histogram name="CaptivePortal.OOBE.DetectionDuration" units="ms"
     expires_after="2020-02-01">
   <owner>rsorokin@chromium.org</owner>
+  <owner>cros-oac@google.com</owner>
   <summary>
     Duration of the captive portal detection process for a particular network at
     OOBE. Detection duration is recorded each time portal detection is completed
@@ -17775,6 +17781,7 @@
 <histogram name="CaptivePortal.OOBE.DetectionResult" enum="CaptivePortalStatus"
     expires_after="2020-02-01">
   <owner>rsorokin@chromium.org</owner>
+  <owner>cros-oac@google.com</owner>
   <summary>
     The result of captive portal detection attempts performed at OOBE. Detection
     result is recorded when portal detection is completed for an active network
@@ -17785,6 +17792,7 @@
 <histogram name="CaptivePortal.OOBE.DiscrepancyWithShill"
     expires_after="2020-02-01">
   <owner>rsorokin@chromium.org</owner>
+  <owner>cros-oac@google.com</owner>
   <summary>
     The result of captive portal detection attempts at OOBE if it diverges from
     network manager results. Detection result is recorded each time portal
@@ -17795,6 +17803,7 @@
 <histogram name="CaptivePortal.OOBE.PortalToOnlineTransition" units="ms"
     expires_after="2020-02-01">
   <owner>rsorokin@chromium.org</owner>
+  <owner>cros-oac@google.com</owner>
   <summary>
     Number of milliseconds passed between consecutive reports for the same
     network about portal and online states.
@@ -17804,6 +17813,7 @@
 <histogram name="CaptivePortal.RedirectTime" units="ms"
     expires_after="2020-02-01">
   <owner>rsorokin@chromium.org</owner>
+  <owner>cros-oac@google.com</owner>
   <summary>
     Number of milliseconds between start of request to gstatic.com/generate_204
     and receipt of response with redirect to captive portal login page.
@@ -17813,6 +17823,7 @@
 <histogram name="CaptivePortal.Session.DetectionDuration" units="ms"
     expires_after="2020-02-01">
   <owner>rsorokin@chromium.org</owner>
+  <owner>cros-oac@google.com</owner>
   <summary>
     Duration of the captive portal detection process for a particular network in
     user session. Detection duration is recorded each time portal detection is
@@ -17824,6 +17835,7 @@
     enum="CaptivePortalStatus" expires_after="2020-02-01">
   <owner>rsorokin@chromium.org</owner>
   <owner>michaeldo@chromium.org</owner>
+  <owner>cros-oac@google.com</owner>
   <summary>
     The result of captive portal detection attempts performed in user session.
     Detection result is recorded when portal detection is completed for an
@@ -17835,6 +17847,7 @@
 <histogram name="CaptivePortal.Session.DiscrepancyWithShill"
     enum="CaptivePortalStatus" expires_after="2020-02-01">
   <owner>rsorokin@chromium.org</owner>
+  <owner>cros-oac@google.com</owner>
   <summary>
     The result of captive portal detection attempts in session if it diverges
     from network manager results. Detection result is recorded each time portal
@@ -17845,6 +17858,7 @@
 <histogram name="CaptivePortal.Session.PortalToOnlineTransition" units="ms"
     expires_after="2020-02-01">
   <owner>rsorokin@chromium.org</owner>
+  <owner>cros-oac@google.com</owner>
   <summary>
     Number of milliseconds passed between consecutive reports for the same
     network about portal and online states.
@@ -18928,7 +18942,7 @@
 </histogram>
 
 <histogram name="ChromeOS.Apps.ExternalProtocolDialog"
-    enum="ArcIntentHandlerAction" expires_after="2019-09-17">
+    enum="ArcIntentHandlerAction" expires_after="M82">
   <owner>dominickn@chromium.org</owner>
   <owner>melzhang@chromium.org</owner>
   <owner>mxcai@chromium.org</owner>
@@ -18939,7 +18953,7 @@
 </histogram>
 
 <histogram name="ChromeOS.Apps.ExternalProtocolDialog.Accepted"
-    enum="ArcExternalProtocolAction" expires_after="2019-09-17">
+    enum="ArcExternalProtocolAction" expires_after="M82">
   <owner>dominickn@chromium.org</owner>
   <owner>melzhang@chromium.org</owner>
   <owner>mxcai@chromium.org</owner>
@@ -18951,7 +18965,7 @@
 </histogram>
 
 <histogram name="ChromeOS.Apps.ExternalProtocolDialog.Rejected"
-    enum="ArcExternalProtocolAction" expires_after="2019-09-17">
+    enum="ArcExternalProtocolAction" expires_after="M82">
   <owner>dominickn@chromium.org</owner>
   <owner>melzhang@chromium.org</owner>
   <owner>mxcai@chromium.org</owner>
@@ -19272,6 +19286,16 @@
   </summary>
 </histogram>
 
+<histogram name="ChromeOS.SAML.InSessionPasswordChangeEvent"
+    enum="SamlInSessionPasswordChangeEvent" expires_after="M83">
+  <owner>olsen@chromium.org</owner>
+  <owner>rsorokin@chromium.org</owner>
+  <summary>
+    Records how often users are guided through the SAML in-session password
+    change flow, and how often it is completed succesfully.
+  </summary>
+</histogram>
+
 <histogram name="ChromeOS.SAML.Scraping.PasswordCount" expires_after="M77">
   <owner>bartfab@chromium.org</owner>
   <summary>
@@ -25063,6 +25087,7 @@
     enum="CryptohomeMigrationToGaiaId" expires_after="M97">
   <owner>alemate@chromium.org</owner>
   <owner>rsorokin@chromium.org</owner>
+  <owner>cros-oac@google.com</owner>
   <summary>
     This is the status of cryptohome migration to GaiaId. Every time user logs
     in current migration status is recorded.
@@ -45581,6 +45606,7 @@
     expires_after="M85">
   <owner>rsorokin@chromium.org</owner>
   <owner>jessejames@chromium.org</owner>
+  <owner>cros-oac@google.com</owner>
   <summary>
     Counts the number of fingerprint attempts until successful screen unlock.
   </summary>
@@ -45590,6 +45616,7 @@
     expires_after="M85">
   <owner>rsorokin@chromium.org</owner>
   <owner>jessejames@chromium.org</owner>
+  <owner>cros-oac@google.com</owner>
   <summary>
     Counts the number of times that the fingerprint match successfully vs.
     rejected.
@@ -45601,6 +45628,7 @@
   <owner>norvez@chromium.org</owner>
   <owner>rsorokin@chromium.org</owner>
   <owner>jessejames@chromium.org</owner>
+  <owner>cros-oac@google.com</owner>
   <summary>Counts the number of fingers enrolled by the user.</summary>
 </histogram>
 
@@ -45609,6 +45637,7 @@
   <owner>norvez@chromium.org</owner>
   <owner>rsorokin@chromium.org</owner>
   <owner>jessejames@chromium.org</owner>
+  <owner>cros-oac@google.com</owner>
   <summary>
     Measures the time it took to capture the fingerprint image in the 'match'
     case.
@@ -45620,6 +45649,7 @@
   <owner>norvez@chromium.org</owner>
   <owner>rsorokin@chromium.org</owner>
   <owner>jessejames@chromium.org</owner>
+  <owner>cros-oac@google.com</owner>
   <summary>
     Measures the time it took to run matcher in the 'match' case.
   </summary>
@@ -45630,6 +45660,7 @@
   <owner>norvez@chromium.org</owner>
   <owner>rsorokin@chromium.org</owner>
   <owner>jessejames@chromium.org</owner>
+  <owner>cros-oac@google.com</owner>
   <summary>
     Measures the time it took between the detection of a finger and the 'match'
     event being sent to the AP.
@@ -45641,6 +45672,7 @@
   <owner>norvez@chromium.org</owner>
   <owner>rsorokin@chromium.org</owner>
   <owner>jessejames@chromium.org</owner>
+  <owner>cros-oac@google.com</owner>
   <summary>
     Measures the time it took to capture the fingerprint image in the 'no-match'
     case.
@@ -45652,6 +45684,7 @@
   <owner>norvez@chromium.org</owner>
   <owner>rsorokin@chromium.org</owner>
   <owner>jessejames@chromium.org</owner>
+  <owner>cros-oac@google.com</owner>
   <summary>
     Measures the time it took to run the matcher in the 'no-match' case.
   </summary>
@@ -45662,6 +45695,7 @@
   <owner>norvez@chromium.org</owner>
   <owner>rsorokin@chromium.org</owner>
   <owner>jessejames@chromium.org</owner>
+  <owner>cros-oac@google.com</owner>
   <summary>
     Measures the time it took between the detection of a finger and the
     'no-match' event being sent to the AP.
@@ -45672,6 +45706,7 @@
     expires_after="M85">
   <owner>rsorokin@chromium.org</owner>
   <owner>jessejames@chromium.org</owner>
+  <owner>cros-oac@google.com</owner>
   <summary>
     Track whether fingerprint is enabled to unlock the screen, when the user
     logs in.
@@ -49319,7 +49354,7 @@
 <histogram name="HIDDetection.OOBEDevicesDetectedOnContinuePressed"
     enum="HIDContinueScenarioType" expires_after="M87">
   <owner>rsorokin@chromium.org</owner>
-  <owner>oac-team@google.com</owner>
+  <owner>cros-oac@google.com</owner>
   <summary>
     Which HID were detected when user pressed Continue on OOBE dialog. This
     metric is specific to Chrome OS.
@@ -49328,7 +49363,7 @@
 
 <histogram name="HIDDetection.OOBEDialogShown" expires_after="M87">
   <owner>rsorokin@chromium.org</owner>
-  <owner>oac-team@google.com</owner>
+  <owner>cros-oac@google.com</owner>
   <summary>
     Whether HID detection dialog was shown on OOBE. Logged on screen show or on
     screen skip respectively. This metric is specific to Chrome OS.
@@ -49339,6 +49374,7 @@
     expires_after="M97">
   <owner>rsorokin@chromium.org</owner>
   <owner>alemate@chromium.org</owner>
+  <owner>cros-oac@google.com</owner>
   <summary>
     Records number of times the dialog was shown by the time OOBE is completed.
     This metric is specific to Chrome OS.
@@ -90655,6 +90691,7 @@
 
   <owner>rsorokin@chromium.org</owner>
   <owner>alemate@chromium.org</owner>
+  <owner>cros-oac@google.com</owner>
   <summary>
     Time from acceptance of the EULA until the login screen is first displayed.
   </summary>
@@ -90748,6 +90785,7 @@
   <owner>alemate@chromium.org</owner>
   <owner>antrim@chromium.org</owner>
   <owner>rsorokin@chromium.org</owner>
+  <owner>cros-oac@google.com</owner>
   <summary>Time spent on specific OOBE screen.</summary>
 </histogram>
 
@@ -125536,6 +125574,7 @@
     expires_after="M97">
   <owner>rsorokin@chromium.org</owner>
   <owner>jessejames@chromium.org</owner>
+  <owner>cros-oac@google.com</owner>
   <summary>
     The users progress through the pin unlock setup wizard. Each type
     corresponds to the user completeing a different stage of the setup wizard.
@@ -145253,6 +145292,7 @@
 
   <owner>alemate@chromium.org</owner>
   <owner>rsorokin@chromium.org</owner>
+  <owner>cros-oac@google.com</owner>
   <summary>
     The time between one regular user logging out and a different regular user
     logging in (Chrome OS). Delays above thirty minutes or which span system
@@ -145276,6 +145316,7 @@
   <owner>alemate@chromium.org</owner>
   <owner>michaelpg@chromium.org</owner>
   <owner>rsorokin@chromium.org</owner>
+  <owner>cros-oac@google.com</owner>
   <summary>
     This is logged when new user type reported by Gaia is different from the old
     one (for existing user).
diff --git a/tools/perf/benchmark.csv b/tools/perf/benchmark.csv
index da452385..dad36437 100644
--- a/tools/perf/benchmark.csv
+++ b/tools/perf/benchmark.csv
@@ -1,6 +1,7 @@
 AUTOGENERATED FILE DO NOT EDIT
 See https://bit.ly/update-benchmarks-info to make changes
 Benchmark name,Individual owners,Component,Documentation,Tags
+UNSCHEDULED_jetstream2,"hablich@chromium.org, tcwang@chromium.org",Blink>JavaScript,https://browserbench.org/JetStream/in-depth.html,
 angle_perftests,"jmadill@chromium.org, chrome-gpu-perf-owners@chromium.org",Internals>GPU>ANGLE,,
 base_perftests,"skyostil@chromium.org, gab@chromium.org",Internals>SequenceManager,https://chromium.googlesource.com/chromium/src/+/HEAD/base/README.md#performance-testing,
 blink_perf.accessibility,dmazzoni@chromium.org,Blink>Accessibility,https://bit.ly/blink-perf-benchmarks,
diff --git a/tools/perf/benchmarks/jetstream2.py b/tools/perf/benchmarks/jetstream2.py
new file mode 100644
index 0000000..19f31f16c
--- /dev/null
+++ b/tools/perf/benchmarks/jetstream2.py
@@ -0,0 +1,44 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Runs Apple's JetStream 2 benchmark.
+
+JetStream 2 combines together a variety of JavaScript and Web Assembly
+benchmarks, covering a variety of advanced workloads and programming
+techniques, and reports a single score that balances them using a geometric
+mean.
+
+Each benchmark measures a distinct workload, and no single optimization
+technique is sufficient to speed up all benchmarks. Some benchmarks
+demonstrate tradeoffs, and aggressive or specialized optimizations for one
+benchmark might make another benchmark slower. JetStream 2 rewards browsers
+that start up quickly, execute code quickly, and continue running smoothly.
+
+Each benchmark in JetStream 2 computes its own individual score. JetStream 2
+weighs each benchmark equally, taking the geometric mean over each individual
+benchmark's score to compute the overall JetStream 2 score.
+
+"""
+
+from telemetry import benchmark
+
+import page_sets
+from benchmarks import press
+
+
+@benchmark.Info(emails=['hablich@chromium.org', 'tcwang@chromium.org'],
+                component='Blink>JavaScript',
+                documentation_url='https://browserbench.org/JetStream/in-depth.html')
+
+class Jetstream2(press._PressBenchmark): # pylint: disable=protected-access
+  """JetStream2, a combination of JavaScript and Web Assembly benchmarks.
+
+  Run all the Jetstream 2 benchmarks by default.
+  """
+  @classmethod
+  def Name(cls):
+    return 'UNSCHEDULED_jetstream2'
+
+  def CreateStorySet(self, options):
+    return page_sets.Jetstream2StorySet()
diff --git a/tools/perf/page_sets/data/jetstream2.json b/tools/perf/page_sets/data/jetstream2.json
new file mode 100644
index 0000000..e9b63f4
--- /dev/null
+++ b/tools/perf/page_sets/data/jetstream2.json
@@ -0,0 +1,9 @@
+{
+    "archives": {
+        "http://browserbench.org/JetStream/": {
+            "DEFAULT": "jetstream2_3c4406a678.wprgo"
+        }
+    },
+    "description": "Describes the Web Page Replay archives for a story set. Don't edit by hand! Use record_wpr for updating.",
+    "platform_specific": true
+}
\ No newline at end of file
diff --git a/tools/perf/page_sets/data/jetstream2_3c4406a678.wprgo.sha1 b/tools/perf/page_sets/data/jetstream2_3c4406a678.wprgo.sha1
new file mode 100644
index 0000000..b6e6392
--- /dev/null
+++ b/tools/perf/page_sets/data/jetstream2_3c4406a678.wprgo.sha1
@@ -0,0 +1 @@
+3c4406a678be8e2a2db04bef7a7d98202461b64d
\ No newline at end of file
diff --git a/tools/perf/page_sets/jetstream2_pages.py b/tools/perf/page_sets/jetstream2_pages.py
new file mode 100644
index 0000000..29f5a87e
--- /dev/null
+++ b/tools/perf/page_sets/jetstream2_pages.py
@@ -0,0 +1,82 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from page_sets import press_story
+from telemetry import story
+from telemetry.value import scalar
+
+class Jetstream2Story(press_story.PressStory):
+  URL='http://browserbench.org/JetStream/'
+
+  def __init__(self, ps):
+    super(Jetstream2Story, self).__init__(ps)
+
+  def ExecuteTest(self, action_runner):
+    action_runner.tab.WaitForDocumentReadyStateToBeComplete()
+    action_runner.EvaluateJavaScript('JetStream.start()')
+
+  def ParseTestResults(self, action_runner):
+    # JetStream2 is using document object to set done of benchmark runs.
+    action_runner.WaitForJavaScriptCondition("""
+        (function() {
+          let summaryElement = document.getElementById("result-summary");
+          return (summaryElement.classList.contains('done'));
+        })();
+        """, timeout=60*20)
+    # JetStream2 calculates scores for each benchmark across iterations
+    # so for each benchmark, return its calculated score and sub-results(
+    # For JavaScript benchmarks, it's scores of "First", "Worst", "Average"
+    # For WSL benchmarks, it's scores of "Stdlib", "MainRun"
+    # For Wasm benchmarks, it's scores of "Startup", "Runtime"
+    # Also use Javascript to calculate the geomean of all scores
+    result, score = action_runner.EvaluateJavaScript("""
+        (function() {
+          let result = {};
+          let allScores = [];
+          for (let benchmark of JetStream.benchmarks) {
+            const subResults = {};
+            const subTimes = benchmark.subTimes();
+            for (const name in subTimes) {
+                subResults[name] = subTimes[name];
+            };
+            result[benchmark.name] = {
+                "Score" : benchmark.score,
+                "Iterations" : benchmark.iterations,
+                "SubResults": subResults,
+            };
+            allScores.push(benchmark.score);
+          };
+          return [result, geomean(allScores)];
+        })();"""
+    )
+
+    self.AddJavascriptMetricSummaryValue(
+      scalar.ScalarValue(
+          None, 'Score', 'score', score))
+
+    for k, v in result.iteritems():
+      # Replace '.' in the benchmark name, because '.' is interpreted
+      # as a sub-category of the metric
+      benchmark = str(k).replace('.', '_')
+      self.AddJavascriptMetricValue(scalar.ScalarValue(
+          self, benchmark, 'score', v['Score'],
+          important=False,
+          description='Geometric mean of the iterations'))
+      self.AddJavascriptMetricValue(scalar.ScalarValue(
+          self, benchmark+'.Iterations', 'number', v['Iterations'],
+          important=False,
+          description='Total number of iterations'))
+      for sub_k, sub_v in v['SubResults'].iteritems():
+        self.AddJavascriptMetricValue(scalar.ScalarValue(
+            self, benchmark+'.'+str(sub_k), 'score', sub_v,
+            important=False))
+
+
+class Jetstream2StorySet(story.StorySet):
+  def __init__(self):
+    super(Jetstream2StorySet, self).__init__(
+        archive_data_file='data/jetstream2.json',
+        cloud_storage_bucket=story.INTERNAL_BUCKET)
+
+    self.AddStory(Jetstream2Story(self))
diff --git a/ui/file_manager/file_manager/foreground/js/file_manager_commands.js b/ui/file_manager/file_manager/foreground/js/file_manager_commands.js
index 307b0950..07f83ef 100644
--- a/ui/file_manager/file_manager/foreground/js/file_manager_commands.js
+++ b/ui/file_manager/file_manager/foreground/js/file_manager_commands.js
@@ -61,6 +61,15 @@
     return [element.selectedItem.entry];
   }
 
+  // The event target could still be a descendant of a DirectoryItem element
+  // (e.g. the eject button).
+  if (fileManager.ui.directoryTree.contains(/** @type {Node} */ (element))) {
+    const treeItem = element.closest('.tree-item');
+    if (treeItem && treeItem.entry) {
+      return [treeItem.entry];
+    }
+  }
+
   // File list (cr.ui.List).
   if (element.selectedItems && element.selectedItems.length) {
     const entries = element.selectedItems;
diff --git a/ui/file_manager/integration_tests/file_manager/directory_tree_context_menu.js b/ui/file_manager/integration_tests/file_manager/directory_tree_context_menu.js
index 5fe7c74..e361d1a 100644
--- a/ui/file_manager/integration_tests/file_manager/directory_tree_context_menu.js
+++ b/ui/file_manager/integration_tests/file_manager/directory_tree_context_menu.js
@@ -800,13 +800,76 @@
         !!await remoteCall.callRemoteTestUtil('fakeKeyDown', appId, key),
         'fakeKeyDown failed');
 
+    // Check the context menu for a folder inside the zip.
+    await checkContextMenu(
+        appId, '/archive.zip/folder', folderMenus, false /* rootMenu */);
+
     // Check the context menu is on desired state.
     await checkContextMenu(
         appId, '/archive.zip', zipMenus, true /* rootMenu */);
 
-    // Check the context menu for a folder inside the zip.
-    await checkContextMenu(
-        appId, '/archive.zip/folder', folderMenus, false /* rootMenu */);
+    // checkContextMenu leaves the context menu open, so just click on the eject
+    // menu item.
+    await remoteCall.waitAndClickElement(
+        appId, '#roots-context-menu [command="#unmount"]:not([disabled])');
+
+    // Ensure the archive has been removed.
+    await remoteCall.waitForElementLost(
+        appId, '#directory-tree [entry-label="archive.zip"]');
+  };
+
+  /**
+   * Tests context menu on the eject button of a zip root.
+   * crbug.com/991002
+   */
+  testcase.dirEjectContextMenuZip = async () => {
+    await sendTestMessage({
+      name: 'expectFileTask',
+      fileNames: [ENTRIES.zipArchive.targetPath],
+      openType: 'launch'
+    });
+
+    // Open Files app on Downloads containing a zip file.
+    const appId = await setupAndWaitUntilReady(
+        RootPath.DOWNLOADS, [ENTRIES.zipArchive], []);
+
+    // Select the zip file.
+    chrome.test.assertTrue(
+        !!await remoteCall.callRemoteTestUtil(
+            'selectFile', appId, ['archive.zip']),
+        'selectFile failed');
+
+    // Press the Enter key to mount the zip file.
+    const key = ['#file-list', 'Enter', false, false, false];
+    chrome.test.assertTrue(
+        !!await remoteCall.callRemoteTestUtil('fakeKeyDown', appId, key),
+        'fakeKeyDown failed');
+
+    // Wait for the eject button to appear.
+    const ejectButtonQuery =
+        ['#directory-tree [entry-label="archive.zip"] button.root-eject'];
+    await remoteCall.waitForElement(appId, ejectButtonQuery);
+
+    // Focus on the eject button.
+    chrome.test.assertTrue(
+        !!await remoteCall.callRemoteTestUtil('focus', appId, ejectButtonQuery),
+        'focus failed: eject button');
+
+    // Right click the eject button.
+    chrome.test.assertTrue(
+        !!await remoteCall.callRemoteTestUtil(
+            'fakeMouseRightClick', appId, ejectButtonQuery),
+        'fakeMouseRightClick failed');
+
+    // Wait for, and click the eject menu item.
+    await remoteCall.waitAndClickElement(
+        appId,
+        '#roots-context-menu:not([hidden]) ' +
+            '[command="#unmount"]:not([disabled])');
+
+    // Ensure the archive has been removed.
+    await remoteCall.waitForElementLost(
+        appId, '#directory-tree [entry-label="archive.zip"]');
   };
 
   /**
diff --git a/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.cc b/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.cc
index 9c8cc56..d8afc21 100644
--- a/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.cc
+++ b/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.cc
@@ -63,10 +63,22 @@
   ~Surface() = default;
 
   bool CommitBuffer(uint32_t buffer_id, const gfx::Rect& damage_region) {
+    DCHECK(!pending_buffer_);
+
     WaylandBuffer* buffer = GetBuffer(buffer_id);
     if (!buffer)
       return false;
 
+    buffer->damage_region = damage_region;
+
+    // If the wl_buffer has been attached, but the wl_buffer still is null, it
+    // means the Wayland server failed to create the buffer and we have to fail
+    // here.
+    //
+    // TODO(msisov): should we ask to recreate buffers instead of failing?
+    if (buffer->attached && !buffer->wl_buffer)
+      return false;
+
     // This request may come earlier than the Wayland compositor has imported a
     // wl_buffer. Wait until the buffer is created. The wait takes place only
     // once. Though, the case when a request to attach a buffer comes earlier
@@ -78,46 +90,12 @@
     // Another case, which always happen is waiting until the frame callback is
     // completed. Thus, wait here when the Wayland compositor fires the frame
     // callback.
-    while (!buffer->wl_buffer || !!wl_frame_callback_) {
-      // If the wl_buffer has been attached, but the wl_buffer still has been
-      // null, it means the Wayland server failed to create the buffer and we
-      // have to fail here.
-      if ((buffer->attached && !buffer->wl_buffer) ||
-          wl_display_roundtrip(connection_->display()) == -1)
-        return false;
+    if (!buffer->wl_buffer || wl_frame_callback_) {
+      pending_buffer_ = buffer;
+      return true;
     }
 
-    // Once the BufferRelease is called, the buffer will be released.
-    DCHECK(buffer->released);
-    buffer->released = false;
-
-    DCHECK(!submitted_buffer_);
-    submitted_buffer_ = buffer;
-
-    AttachAndDamageBuffer(buffer, damage_region);
-
-    SetupFrameCallback();
-    SetupPresentationFeedback(buffer_id);
-
-    CommitSurface();
-
-    connection_->ScheduleFlush();
-
-    // If the contents were reset, there is no buffer attached. It means we have
-    // to behave the same way as if it was the very first frame. Check the
-    // comment below where the |contents_reset_| is declared.
-    if (contents_reset_) {
-      prev_submitted_buffer_ = nullptr;
-      contents_reset_ = false;
-    }
-
-    // If it was the very first frame, the surface has not had a back buffer
-    // before, and Wayland won't release the front buffer until next buffer is
-    // attached. Thus, notify about successful submission immediately.
-    if (!prev_submitted_buffer_)
-      CompleteSubmission();
-
-    return true;
+    return CommitBufferInternal(buffer);
   }
 
   bool CreateBuffer(const gfx::Size& size, uint32_t buffer_id) {
@@ -150,6 +128,9 @@
     if (prev_submitted_buffer_ == buffer)
       prev_submitted_buffer_ = nullptr;
 
+    if (pending_buffer_ == buffer)
+      pending_buffer_ = nullptr;
+
     auto result = buffers_.erase(buffer_id);
     return result;
   }
@@ -167,6 +148,9 @@
 
     if (buffer->wl_buffer)
       SetupBufferReleaseListener(buffer);
+
+    if (pending_buffer_ == buffer)
+      ProcessPendingBuffer();
   }
 
   void ClearState() {
@@ -213,6 +197,10 @@
     // Actual buffer size.
     const gfx::Size size;
 
+    // Damage region this buffer describes. Must be emptied once buffer is
+    // submitted.
+    gfx::Rect damage_region;
+
     // The id of this buffer.
     const uint32_t buffer_id;
 
@@ -239,9 +227,45 @@
     DISALLOW_COPY_AND_ASSIGN(WaylandBuffer);
   };
 
-  void AttachAndDamageBuffer(WaylandBuffer* buffer,
-                             const gfx::Rect& damage_region) {
-    gfx::Rect pending_damage_region = damage_region;
+  bool CommitBufferInternal(WaylandBuffer* buffer) {
+    DCHECK(buffer);
+    DCHECK(!pending_buffer_);
+
+    // Once the BufferRelease is called, the buffer will be released.
+    DCHECK(buffer->released);
+    buffer->released = false;
+
+    DCHECK(!submitted_buffer_);
+    submitted_buffer_ = buffer;
+
+    AttachAndDamageBuffer(buffer);
+
+    SetupFrameCallback();
+    SetupPresentationFeedback(buffer->buffer_id);
+
+    CommitSurface();
+
+    connection_->ScheduleFlush();
+
+    // If the contents were reset, there is no buffer attached. It means we have
+    // to behave the same way as if it was the very first frame. Check the
+    // comment below where the |contents_reset_| is declared.
+    if (contents_reset_) {
+      prev_submitted_buffer_ = nullptr;
+      contents_reset_ = false;
+    }
+
+    // If it was the very first frame, the surface has not had a back buffer
+    // before, and Wayland won't release the front buffer until next buffer is
+    // attached. Thus, notify about successful submission immediately.
+    if (!prev_submitted_buffer_)
+      CompleteSubmission();
+
+    return true;
+  }
+
+  void AttachAndDamageBuffer(WaylandBuffer* buffer) {
+    gfx::Rect pending_damage_region = std::move(buffer->damage_region);
     // If the size of the damage region is empty, wl_surface_damage must be
     // supplied with the actual size of the buffer, which is going to be
     // committed.
@@ -299,6 +323,8 @@
   void OnFrameCallback(struct wl_callback* callback) {
     DCHECK(wl_frame_callback_.get() == callback);
     wl_frame_callback_.reset();
+
+    ProcessPendingBuffer();
   }
 
   // wl_callback_listener
@@ -390,7 +416,8 @@
                       const gfx::PresentationFeedback& feedback) {
     // The order of submission and presentation callbacks cannot be controlled.
     // Some Wayland compositors may fire presentation callbacks earlier than we
-    // are able to send submission callbacks and this is bad. Thus, handle it here.
+    // are able to send submission callbacks and this is bad. Thus, handle it
+    // here.
     if (submitted_buffer_ && submitted_buffer_->buffer_id == buffer_id) {
       submitted_buffer_->needs_send_feedback = true;
       submitted_buffer_->feedback = feedback;
@@ -441,6 +468,15 @@
                          gfx::PresentationFeedback::Failure());
   }
 
+  void ProcessPendingBuffer() {
+    if (!pending_buffer_)
+      return;
+
+    auto* buffer = pending_buffer_;
+    pending_buffer_ = nullptr;
+    CommitBufferInternal(buffer);
+  }
+
   // Widget this helper surface backs and has 1:1 relationship with the
   // WaylandWindow.
 
@@ -465,6 +501,9 @@
   // shown.
   PresentationFeedbackQueue presentation_feedbacks_;
 
+  // A buffer, which is pending to be submitted (look the comment in the
+  // CommitBuffer method).
+  WaylandBuffer* pending_buffer_ = nullptr;
   // Current submitted buffer.
   WaylandBuffer* submitted_buffer_ = nullptr;
   // Previous submitted buffer.
diff --git a/ui/ozone/platform/wayland/host/wayland_connection.cc b/ui/ozone/platform/wayland/host/wayland_connection.cc
index ce094d20..e841f6c 100644
--- a/ui/ozone/platform/wayland/host/wayland_connection.cc
+++ b/ui/ozone/platform/wayland/host/wayland_connection.cc
@@ -100,20 +100,29 @@
     return true;
 
   DCHECK(display_);
+
+  MaybePrepareReadQueue();
+
+  // Displatch event from display to server.
   wl_display_flush(display_.get());
 
-  DCHECK(base::MessageLoopCurrentForUI::IsSet());
-  if (!base::MessageLoopCurrentForUI::Get()->WatchFileDescriptor(
-          wl_display_get_fd(display_.get()), true,
-          base::MessagePumpLibevent::WATCH_READ, &controller_, this))
-    return false;
+  return BeginWatchingFd(base::MessagePumpLibevent::WATCH_READ);
+}
 
-  watching_ = true;
-  return true;
+void WaylandConnection::MaybePrepareReadQueue() {
+  if (prepared_)
+    return;
+
+  if (wl_display_prepare_read(display()) != -1) {
+    prepared_ = true;
+    return;
+  }
+  // Nothing to read, send events to the queue.
+  wl_display_dispatch_pending(display());
 }
 
 void WaylandConnection::ScheduleFlush() {
-  if (scheduled_flush_ || !watching_)
+  if (scheduled_flush_)
     return;
   DCHECK(base::MessageLoopCurrentForUI::IsSet());
   base::ThreadTaskRunnerHandle::Get()->PostTask(
@@ -193,13 +202,38 @@
 }
 
 void WaylandConnection::OnFileCanReadWithoutBlocking(int fd) {
-  wl_display_dispatch(display_.get());
-  std::vector<WaylandWindow*> windows = wayland_window_manager_.GetAllWindows();
-  for (auto* window : windows)
-    window->ApplyPendingBounds();
+  if (prepared_) {
+    prepared_ = false;
+    if (wl_display_read_events(display()) == -1)
+      return;
+    wl_display_dispatch_pending(display());
+  }
+
+  MaybePrepareReadQueue();
+
+  if (!prepared_)
+    return;
+
+  // Automatic Flush.
+  int ret = wl_display_flush(display_.get());
+  if (ret != -1 || errno != EAGAIN)
+    return;
+
+  // if all data could not be written, errno will be set to EAGAIN and -1
+  // returned. In that case, use poll on the display file descriptor to wait for
+  // it to become writable again.
+  BeginWatchingFd(base::MessagePumpLibevent::WATCH_WRITE);
 }
 
-void WaylandConnection::OnFileCanWriteWithoutBlocking(int fd) {}
+void WaylandConnection::OnFileCanWriteWithoutBlocking(int fd) {
+  int ret = wl_display_flush(display_.get());
+  if (ret != -1 || errno != EAGAIN)
+    BeginWatchingFd(base::MessagePumpLibevent::WATCH_READ);
+  else if (ret < 0 && errno != EPIPE && prepared_)
+    wl_display_cancel_read(display());
+
+  // Otherwise just continue watching in the same mode.
+}
 
 void WaylandConnection::EnsureDataDevice() {
   if (!data_device_manager_ || !seat_)
@@ -211,6 +245,20 @@
                                                   data_device_.get());
 }
 
+bool WaylandConnection::BeginWatchingFd(
+    base::WatchableIOMessagePumpPosix::Mode mode) {
+  if (watching_) {
+    // Stop watching first.
+    watching_ = !controller_.StopWatchingFileDescriptor();
+    DCHECK(!watching_);
+  }
+
+  DCHECK(base::MessageLoopCurrentForUI::IsSet());
+  watching_ = base::MessageLoopCurrentForUI::Get()->WatchFileDescriptor(
+      wl_display_get_fd(display_.get()), true, mode, &controller_, this);
+  return watching_;
+}
+
 // static
 void WaylandConnection::Global(void* data,
                                wl_registry* registry,
diff --git a/ui/ozone/platform/wayland/host/wayland_connection.h b/ui/ozone/platform/wayland/host/wayland_connection.h
index a5687af7..37230c34 100644
--- a/ui/ozone/platform/wayland/host/wayland_connection.h
+++ b/ui/ozone/platform/wayland/host/wayland_connection.h
@@ -146,6 +146,9 @@
   // Make sure data device is properly initialized
   void EnsureDataDevice();
 
+  bool BeginWatchingFd(base::WatchableIOMessagePumpPosix::Mode mode);
+  void MaybePrepareReadQueue();
+
   // wl_registry_listener
   static void Global(void* data,
                      wl_registry* registry,
@@ -192,6 +195,7 @@
 
   bool scheduled_flush_ = false;
   bool watching_ = false;
+  bool prepared_ = false;
   base::MessagePumpLibevent::FdWatchController controller_;
 
   uint32_t serial_ = 0;
diff --git a/ui/ozone/platform/wayland/host/wayland_data_device.cc b/ui/ozone/platform/wayland/host/wayland_data_device.cc
index a36cd3b7a..56e568fb 100644
--- a/ui/ozone/platform/wayland/host/wayland_data_device.cc
+++ b/ui/ozone/platform/wayland/host/wayland_data_device.cc
@@ -355,7 +355,7 @@
   deferred_read_callback_.reset(wl_display_sync(connection_->display()));
   wl_callback_add_listener(deferred_read_callback_.get(),
                            &kDeferredReadListener, this);
-  wl_display_flush(connection_->display());
+  connection_->ScheduleFlush();
 }
 
 void WaylandDataDevice::DeferredReadCallback(void* data,
diff --git a/ui/ozone/platform/wayland/host/wayland_data_source.cc b/ui/ozone/platform/wayland/host/wayland_data_source.cc
index dcd6b75..1f41fd6 100644
--- a/ui/ozone/platform/wayland/host/wayland_data_source.cc
+++ b/ui/ozone/platform/wayland/host/wayland_data_source.cc
@@ -36,7 +36,7 @@
   wl_data_device_set_selection(connection_->data_device(), data_source_.get(),
                                connection_->serial());
 
-  wl_display_flush(connection_->display());
+  connection_->ScheduleFlush();
 }
 
 void WaylandDataSource::UpdateDataMap(
diff --git a/ui/ozone/platform/wayland/host/wayland_keyboard.cc b/ui/ozone/platform/wayland/host/wayland_keyboard.cc
index c082b30..fbadb0e 100644
--- a/ui/ozone/platform/wayland/host/wayland_keyboard.cc
+++ b/ui/ozone/platform/wayland/host/wayland_keyboard.cc
@@ -167,7 +167,7 @@
   // get spurious repeats.
   sync_callback_.reset(wl_display_sync(connection_->display()));
   wl_callback_add_listener(sync_callback_.get(), &callback_listener_, this);
-  wl_display_flush(connection_->display());
+  connection_->ScheduleFlush();
 }
 
 void WaylandKeyboard::DispatchKey(uint32_t key,
diff --git a/ui/ozone/platform/wayland/host/wayland_window.cc b/ui/ozone/platform/wayland/host/wayland_window.cc
index d0a9861..4909a5d 100644
--- a/ui/ozone/platform/wayland/host/wayland_window.cc
+++ b/ui/ozone/platform/wayland/host/wayland_window.cc
@@ -291,7 +291,6 @@
 
   SetBoundsDip(pending_bounds_dip_);
   xdg_surface_->SetWindowGeometry(pending_bounds_dip_);
-  xdg_surface_->AckConfigure();
   pending_bounds_dip_ = gfx::Rect();
   connection_->ScheduleFlush();
 
@@ -670,6 +669,8 @@
     delegate_->OnWindowStateChanged(state_);
   }
 
+  ApplyPendingBounds();
+
   if (did_active_change)
     delegate_->OnActivationChanged(is_active_);
 
diff --git a/ui/ozone/platform/wayland/host/wayland_window_unittest.cc b/ui/ozone/platform/wayland/host/wayland_window_unittest.cc
index df45f16..05fb82d2 100644
--- a/ui/ozone/platform/wayland/host/wayland_window_unittest.cc
+++ b/ui/ozone/platform/wayland/host/wayland_window_unittest.cc
@@ -675,19 +675,26 @@
 
 TEST_P(WaylandWindowTest, ConfigureEvent) {
   ScopedWlArray states;
-  SendConfigureEvent(1000, 1000, 12, states.get());
-  SendConfigureEvent(1500, 1000, 13, states.get());
 
-  // Make sure that the implementation does not call OnBoundsChanged for each
-  // configure event if it receives multiple in a row.
-  EXPECT_CALL(delegate_, OnBoundsChanged(Eq(gfx::Rect(0, 0, 1500, 1000))));
+  // The surface must react on each configure event and send bounds to its
+  // delegate.
+
+  EXPECT_CALL(delegate_, OnBoundsChanged(Eq(gfx::Rect(0, 0, 1000, 1000))));
   // Responding to a configure event, the window geometry in here must respect
   // the sizing negotiations specified by the configure event.
   // |xdg_surface_| must receive the following calls in both xdg_shell_v5 and
   // xdg_shell_v6. Other calls like SetTitle or SetMaximized are recieved by
   // xdg_toplevel in xdg_shell_v6 and by xdg_surface_ in xdg_shell_v5.
+  EXPECT_CALL(*xdg_surface_, SetWindowGeometry(0, 0, 1000, 1000)).Times(1);
+  EXPECT_CALL(*xdg_surface_, AckConfigure(12));
+  SendConfigureEvent(1000, 1000, 12, states.get());
+
+  Sync();
+
+  EXPECT_CALL(delegate_, OnBoundsChanged(Eq(gfx::Rect(0, 0, 1500, 1000))));
   EXPECT_CALL(*xdg_surface_, SetWindowGeometry(0, 0, 1500, 1000)).Times(1);
   EXPECT_CALL(*xdg_surface_, AckConfigure(13));
+  SendConfigureEvent(1500, 1000, 13, states.get());
 }
 
 TEST_P(WaylandWindowTest, ConfigureEventWithNulledSize) {
diff --git a/ui/ozone/platform/wayland/host/xdg_surface_wrapper_v5.cc b/ui/ozone/platform/wayland/host/xdg_surface_wrapper_v5.cc
index 5cc5c0f..f521953 100644
--- a/ui/ozone/platform/wayland/host/xdg_surface_wrapper_v5.cc
+++ b/ui/ozone/platform/wayland/host/xdg_surface_wrapper_v5.cc
@@ -99,6 +99,7 @@
   surface->pending_configure_serial_ = serial;
   surface->wayland_window_->HandleSurfaceConfigure(width, height, is_maximized,
                                                    is_fullscreen, is_activated);
+  surface->AckConfigure();
 }
 
 // static
diff --git a/ui/ozone/platform/wayland/host/xdg_surface_wrapper_v6.cc b/ui/ozone/platform/wayland/host/xdg_surface_wrapper_v6.cc
index 691d2bd..a6d1b9e 100644
--- a/ui/ozone/platform/wayland/host/xdg_surface_wrapper_v6.cc
+++ b/ui/ozone/platform/wayland/host/xdg_surface_wrapper_v6.cc
@@ -124,8 +124,7 @@
   DCHECK(surface);  
   surface->pending_configure_serial_ = serial;
 
-  if (surface->surface_for_popup_)
-    surface->AckConfigure();
+  surface->AckConfigure();
 }
 
 // static
diff --git a/ui/ozone/platform/wayland/test/mock_zwp_linux_buffer_params.cc b/ui/ozone/platform/wayland/test/mock_zwp_linux_buffer_params.cc
index ca7fa6a..8ebe4e6 100644
--- a/ui/ozone/platform/wayland/test/mock_zwp_linux_buffer_params.cc
+++ b/ui/ozone/platform/wayland/test/mock_zwp_linux_buffer_params.cc
@@ -5,6 +5,7 @@
 #include "ui/ozone/platform/wayland/test/mock_zwp_linux_buffer_params.h"
 
 #include "ui/ozone/platform/wayland/test/mock_buffer.h"
+#include "ui/ozone/platform/wayland/test/mock_zwp_linux_dmabuf.h"
 
 namespace wl {
 
@@ -25,6 +26,20 @@
                      modifier_hi, modifier_lo);
 }
 
+void CreateCommon(MockZwpLinuxBufferParamsV1* buffer_params,
+                  wl_client* client,
+                  int32_t width,
+                  int32_t height,
+                  uint32_t format,
+                  uint32_t flags) {
+  wl_resource* buffer_resource =
+      CreateResourceWithImpl<::testing::NiceMock<MockBuffer>>(
+          client, &wl_buffer_interface, 1, &kMockWlBufferImpl, 0,
+          std::move(buffer_params->fds_));
+
+  buffer_params->SetBufferResource(buffer_resource);
+}
+
 void Create(wl_client* client,
             wl_resource* buffer_params_resource,
             int32_t width,
@@ -33,28 +48,23 @@
             uint32_t flags) {
   auto* buffer_params =
       GetUserDataAs<MockZwpLinuxBufferParamsV1>(buffer_params_resource);
-
-  wl_resource* buffer_resource =
-      CreateResourceWithImpl<::testing::NiceMock<MockBuffer>>(
-          client, &wl_buffer_interface, 1, &kMockWlBufferImpl, 0,
-          std::move(buffer_params->fds_));
-
-  zwp_linux_buffer_params_v1_send_created(buffer_params_resource,
-                                          buffer_resource);
-
+  CreateCommon(buffer_params, client, width, height, format, flags);
   buffer_params->Create(client, buffer_params_resource, width, height, format,
                         flags);
 }
 
 void CreateImmed(wl_client* client,
-                 wl_resource* resource,
+                 wl_resource* buffer_params_resource,
                  uint32_t buffer_id,
                  int32_t width,
                  int32_t height,
                  uint32_t format,
                  uint32_t flags) {
-  GetUserDataAs<MockZwpLinuxBufferParamsV1>(resource)->CreateImmed(
-      client, resource, buffer_id, width, height, format, flags);
+  auto* buffer_params =
+      GetUserDataAs<MockZwpLinuxBufferParamsV1>(buffer_params_resource);
+  CreateCommon(buffer_params, client, width, height, format, flags);
+  buffer_params->CreateImmed(client, buffer_params_resource, buffer_id, width,
+                             height, format, flags);
 }
 
 }  // namespace
@@ -66,6 +76,20 @@
 MockZwpLinuxBufferParamsV1::MockZwpLinuxBufferParamsV1(wl_resource* resource)
     : ServerObject(resource) {}
 
-MockZwpLinuxBufferParamsV1::~MockZwpLinuxBufferParamsV1() {}
+MockZwpLinuxBufferParamsV1::~MockZwpLinuxBufferParamsV1() {
+  DCHECK(linux_dmabuf_);
+  linux_dmabuf_->OnBufferParamsDestroyed(this);
+}
+
+void MockZwpLinuxBufferParamsV1::SetZwpLinuxDmabuf(
+    MockZwpLinuxDmabufV1* linux_dmabuf) {
+  DCHECK(!linux_dmabuf_);
+  linux_dmabuf_ = linux_dmabuf;
+}
+
+void MockZwpLinuxBufferParamsV1::SetBufferResource(wl_resource* resource) {
+  DCHECK(!buffer_resource_);
+  buffer_resource_ = resource;
+}
 
 }  // namespace wl
diff --git a/ui/ozone/platform/wayland/test/mock_zwp_linux_buffer_params.h b/ui/ozone/platform/wayland/test/mock_zwp_linux_buffer_params.h
index 57b4b92..7da794a 100644
--- a/ui/ozone/platform/wayland/test/mock_zwp_linux_buffer_params.h
+++ b/ui/ozone/platform/wayland/test/mock_zwp_linux_buffer_params.h
@@ -20,6 +20,8 @@
 extern const struct zwp_linux_buffer_params_v1_interface
     kMockZwpLinuxBufferParamsV1Impl;
 
+class MockZwpLinuxDmabufV1;
+
 // Manage zwp_linux_buffer_params_v1
 class MockZwpLinuxBufferParamsV1 : public ServerObject {
  public:
@@ -52,9 +54,24 @@
                     uint32_t format,
                     uint32_t flags));
 
+  wl_resource* buffer_resource() const { return buffer_resource_; }
+
+  void SetZwpLinuxDmabuf(MockZwpLinuxDmabufV1* linux_dmabuf);
+
+  void SetBufferResource(wl_resource* resource);
+
   std::vector<base::ScopedFD> fds_;
 
  private:
+  // Non-owned pointer to the linux dmabuf object, which created this params
+  // resource and holds a pointer to it. On destruction, must notify it about
+  // going out of scope.
+  MockZwpLinuxDmabufV1* linux_dmabuf_ = nullptr;
+
+  // A buffer resource, which is created on Create or CreateImmed call. Can be
+  // null if not created/failed to be created.
+  wl_resource* buffer_resource_ = nullptr;
+
   DISALLOW_COPY_AND_ASSIGN(MockZwpLinuxBufferParamsV1);
 };
 
diff --git a/ui/ozone/platform/wayland/test/mock_zwp_linux_dmabuf.cc b/ui/ozone/platform/wayland/test/mock_zwp_linux_dmabuf.cc
index c44d41a4..006d55ff 100644
--- a/ui/ozone/platform/wayland/test/mock_zwp_linux_dmabuf.cc
+++ b/ui/ozone/platform/wayland/test/mock_zwp_linux_dmabuf.cc
@@ -17,12 +17,20 @@
 constexpr uint32_t kLinuxDmabufVersion = 1;
 
 void CreateParams(wl_client* client, wl_resource* resource, uint32_t id) {
-  CreateResourceWithImpl<::testing::NiceMock<MockZwpLinuxBufferParamsV1>>(
-      client, &zwp_linux_buffer_params_v1_interface,
-      wl_resource_get_version(resource), &kMockZwpLinuxBufferParamsV1Impl, id);
+  wl_resource* params_resource =
+      CreateResourceWithImpl<::testing::NiceMock<MockZwpLinuxBufferParamsV1>>(
+          client, &zwp_linux_buffer_params_v1_interface,
+          wl_resource_get_version(resource), &kMockZwpLinuxBufferParamsV1Impl,
+          id);
 
-  GetUserDataAs<MockZwpLinuxDmabufV1>(resource)->CreateParams(client, resource,
-                                                              id);
+  auto* zwp_linux_dmabuf = GetUserDataAs<MockZwpLinuxDmabufV1>(resource);
+  auto* buffer_params =
+      GetUserDataAs<MockZwpLinuxBufferParamsV1>(params_resource);
+
+  DCHECK(buffer_params);
+  zwp_linux_dmabuf->StoreBufferParams(buffer_params);
+  buffer_params->SetZwpLinuxDmabuf(zwp_linux_dmabuf);
+  zwp_linux_dmabuf->CreateParams(client, resource, id);
 }
 
 }  // namespace
@@ -37,6 +45,20 @@
                    &kMockZwpLinuxDmabufV1Impl,
                    kLinuxDmabufVersion) {}
 
-MockZwpLinuxDmabufV1::~MockZwpLinuxDmabufV1() {}
+MockZwpLinuxDmabufV1::~MockZwpLinuxDmabufV1() {
+  DCHECK(buffer_params_.empty());
+}
+
+void MockZwpLinuxDmabufV1::StoreBufferParams(
+    MockZwpLinuxBufferParamsV1* params) {
+  buffer_params_.push_back(params);
+}
+
+void MockZwpLinuxDmabufV1::OnBufferParamsDestroyed(
+    MockZwpLinuxBufferParamsV1* params) {
+  auto it = std::find(buffer_params_.begin(), buffer_params_.end(), params);
+  DCHECK(it != buffer_params_.end());
+  buffer_params_.erase(it);
+}
 
 }  // namespace wl
diff --git a/ui/ozone/platform/wayland/test/mock_zwp_linux_dmabuf.h b/ui/ozone/platform/wayland/test/mock_zwp_linux_dmabuf.h
index f63b92d..9d2e107 100644
--- a/ui/ozone/platform/wayland/test/mock_zwp_linux_dmabuf.h
+++ b/ui/ozone/platform/wayland/test/mock_zwp_linux_dmabuf.h
@@ -18,6 +18,8 @@
 
 extern const struct zwp_linux_dmabuf_v1_interface kMockZwpLinuxDmabufV1Impl;
 
+class MockZwpLinuxBufferParamsV1;
+
 // Manage zwp_linux_dmabuf_v1 object.
 class MockZwpLinuxDmabufV1 : public GlobalObject {
  public:
@@ -30,7 +32,17 @@
                     wl_resource* resource,
                     uint32_t params_id));
 
+  const std::vector<MockZwpLinuxBufferParamsV1*>& buffer_params() const {
+    return buffer_params_;
+  }
+
+  void StoreBufferParams(MockZwpLinuxBufferParamsV1* params);
+
+  void OnBufferParamsDestroyed(MockZwpLinuxBufferParamsV1* params);
+
  private:
+  std::vector<MockZwpLinuxBufferParamsV1*> buffer_params_;
+
   DISALLOW_COPY_AND_ASSIGN(MockZwpLinuxDmabufV1);
 };
 
diff --git a/ui/ozone/platform/wayland/wayland_buffer_manager_unittest.cc b/ui/ozone/platform/wayland/wayland_buffer_manager_unittest.cc
index d8585ca..93d1b69 100644
--- a/ui/ozone/platform/wayland/wayland_buffer_manager_unittest.cc
+++ b/ui/ozone/platform/wayland/wayland_buffer_manager_unittest.cc
@@ -16,6 +16,8 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/ozone/platform/wayland/gpu/wayland_surface_gpu.h"
 #include "ui/ozone/platform/wayland/test/mock_surface.h"
+#include "ui/ozone/platform/wayland/test/mock_zwp_linux_buffer_params.h"
+#include "ui/ozone/platform/wayland/test/mock_zwp_linux_dmabuf.h"
 #include "ui/ozone/platform/wayland/test/wayland_test.h"
 
 using testing::_;
@@ -157,6 +159,22 @@
     Sync();
   }
 
+  void ProcessCreatedBufferResourcesWithExpectation(size_t expected_size,
+                                                    bool fail) {
+    auto params_vector = server_.zwp_linux_dmabuf_v1()->buffer_params();
+    // To ensure, no other buffers are created, test the size of the vector.
+    EXPECT_EQ(params_vector.size(), expected_size);
+
+    for (auto* mock_params : params_vector) {
+      if (!fail) {
+        zwp_linux_buffer_params_v1_send_created(mock_params->resource(),
+                                                mock_params->buffer_resource());
+      } else {
+        zwp_linux_buffer_params_v1_send_failed(mock_params->resource());
+      }
+    }
+  }
+
   MockTerminateGpuCallback callback_;
   WaylandBufferManagerHost* manager_host_;
 
@@ -289,7 +307,8 @@
   MockSurfaceGpu mock_surface_gpu;
   buffer_manager_gpu_->RegisterSurface(widget, &mock_surface_gpu);
 
-  EXPECT_CALL(*server_.zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(2);
+  auto* linux_dmabuf = server_.zwp_linux_dmabuf_v1();
+  EXPECT_CALL(*linux_dmabuf, CreateParams(_, _, _)).Times(2);
   CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, widget,
                                                    kBufferId1);
   CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, widget,
@@ -297,6 +316,9 @@
 
   Sync();
 
+  ProcessCreatedBufferResourcesWithExpectation(2u /* expected size */,
+                                               false /* fail */);
+
   auto* mock_surface = server_.GetObject<wl::MockSurface>(widget);
 
   constexpr uint32_t kNumberOfCommits = 3;
@@ -380,6 +402,75 @@
   Sync();
 }
 
+TEST_P(WaylandBufferManagerTest, TestCommitBufferConditions) {
+  constexpr uint32_t kDmabufBufferId = 1;
+  constexpr uint32_t kDmabufBufferId2 = 2;
+
+  const gfx::AcceleratedWidget widget = window_->GetWidget();
+  auto* mock_surface = server_.GetObject<wl::MockSurface>(widget);
+
+  auto* linux_dmabuf = server_.zwp_linux_dmabuf_v1();
+  EXPECT_CALL(*linux_dmabuf, CreateParams(_, _, _)).Times(1);
+
+  CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, widget,
+                                                   kDmabufBufferId);
+
+  // Part 1: the surface mustn't have a buffer attached until
+  // zwp_linux_buffer_params_v1_send_created is called. Instead, the buffer must
+  // be set as pending buffer.
+
+  EXPECT_CALL(*mock_surface, Attach(_, _, _)).Times(0);
+  EXPECT_CALL(*mock_surface, Frame(_)).Times(0);
+  EXPECT_CALL(*mock_surface, Commit()).Times(0);
+
+  buffer_manager_gpu_->CommitBuffer(widget, kDmabufBufferId,
+                                    window_->GetBounds());
+  Sync();
+
+  EXPECT_CALL(*mock_surface, Attach(_, _, _)).Times(1);
+  EXPECT_CALL(*mock_surface, Frame(_)).Times(1);
+  EXPECT_CALL(*mock_surface, Commit()).Times(1);
+
+  ProcessCreatedBufferResourcesWithExpectation(1u /* expected size */,
+                                               false /* fail */);
+
+  Sync();
+
+  // Once the client receives a "...send_created" call, it must destroy the
+  // params resource.
+  EXPECT_TRUE(linux_dmabuf->buffer_params().empty());
+
+  // Part 2: the surface mustn't have a buffer attached until frame callback is
+  // sent by the server.
+
+  EXPECT_CALL(*linux_dmabuf, CreateParams(_, _, _)).Times(1);
+  CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, widget,
+                                                   kDmabufBufferId2);
+
+  ProcessCreatedBufferResourcesWithExpectation(1u /* expected size */,
+                                               false /* fail */);
+
+  Sync();
+
+  EXPECT_CALL(*mock_surface, Attach(_, _, _)).Times(0);
+  EXPECT_CALL(*mock_surface, Frame(_)).Times(0);
+  EXPECT_CALL(*mock_surface, Commit()).Times(0);
+
+  buffer_manager_gpu_->CommitBuffer(widget, kDmabufBufferId2,
+                                    window_->GetBounds());
+
+  Sync();
+
+  // After the frame callback is sent, the pending buffer will be committed.
+  EXPECT_CALL(*mock_surface, Attach(_, _, _)).Times(1);
+  EXPECT_CALL(*mock_surface, Frame(_)).Times(1);
+  EXPECT_CALL(*mock_surface, Commit()).Times(1);
+
+  mock_surface->SendFrameCallback();
+
+  Sync();
+}
+
 INSTANTIATE_TEST_SUITE_P(XdgVersionV5Test,
                          WaylandBufferManagerTest,
                          ::testing::Values(kXdgShellV5));
diff --git a/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup.js b/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup.js
index 9aa8509..f44405c3 100644
--- a/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup.js
+++ b/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup.js
@@ -2,17 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-cr.exportPath('multidevice_setup');
-
-/** @enum {string} */
-multidevice_setup.PageName = {
-  PASSWORD: 'password-page',
-  SUCCESS: 'setup-succeeded-page',
-  START: 'start-setup-page',
-};
-
 cr.define('multidevice_setup', function() {
-  const PageName = multidevice_setup.PageName;
+  /** @enum {string} */
+  const PageName = {
+    PASSWORD: 'password-page',
+    SUCCESS: 'setup-succeeded-page',
+    START: 'start-setup-page',
+  };
 
   const MultiDeviceSetup = Polymer({
     is: 'multidevice-setup',
@@ -322,5 +318,6 @@
 
   return {
     MultiDeviceSetup: MultiDeviceSetup,
+    PageName: PageName,
   };
 });
diff --git a/ui/webui/resources/cr_components/chromeos/multidevice_setup/setup_succeeded_page.js b/ui/webui/resources/cr_components/chromeos/multidevice_setup/setup_succeeded_page.js
index a679e6d..589a727 100644
--- a/ui/webui/resources/cr_components/chromeos/multidevice_setup/setup_succeeded_page.js
+++ b/ui/webui/resources/cr_components/chromeos/multidevice_setup/setup_succeeded_page.js
@@ -2,8 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-cr.exportPath('multidevice_setup');
-
 Polymer({
   is: 'setup-succeeded-page',
 
diff --git a/ui/webui/resources/cr_elements/cr_slider/cr_slider.js b/ui/webui/resources/cr_elements/cr_slider/cr_slider.js
index 2da0101c..909620c 100644
--- a/ui/webui/resources/cr_elements/cr_slider/cr_slider.js
+++ b/ui/webui/resources/cr_elements/cr_slider/cr_slider.js
@@ -7,23 +7,23 @@
  * a continuous or discrete range of numbers.
  */
 
-cr.exportPath('cr_slider');
 
-/**
- * The |value| is the corresponding value that the current slider tick is
- * associated with. The string |label| is shown in the UI as the label for the
- * current slider value. The |ariaValue| number is used for aria-valuemin,
- * aria-valuemax, and aria-valuenow, and is optional. If missing, |value| will
- * be used instead.
- * @typedef {{
- *   value: number,
- *   label: string,
- *   ariaValue: (number|undefined),
- * }}
- */
-cr_slider.SliderTick;
 
-(() => {
+cr.define('cr_slider', function() {
+  /**
+   * The |value| is the corresponding value that the current slider tick is
+   * associated with. The string |label| is shown in the UI as the label for the
+   * current slider value. The |ariaValue| number is used for aria-valuemin,
+   * aria-valuemax, and aria-valuenow, and is optional. If missing, |value| will
+   * be used instead.
+   * @typedef {{
+   *   value: number,
+   *   label: string,
+   *   ariaValue: (number|undefined),
+   * }}
+   */
+  let SliderTick;
+
   /**
    * @param {number} min
    * @param {number} max
@@ -472,4 +472,8 @@
       return ripple;
     },
   });
-})();
+
+  return {
+    SliderTick: SliderTick,
+  };
+});
diff --git a/ui/webui/resources/cr_elements/cr_toggle/cr_toggle.js b/ui/webui/resources/cr_elements/cr_toggle/cr_toggle.js
index 3f0f7e6b..4eb58ff 100644
--- a/ui/webui/resources/cr_elements/cr_toggle/cr_toggle.js
+++ b/ui/webui/resources/cr_elements/cr_toggle/cr_toggle.js
@@ -143,12 +143,6 @@
     e.stopPropagation();
     e.preventDefault();
 
-    // Ignore case where 'click' handler is triggered while disabled. Can happen
-    // via calling the click() method.
-    if (this.disabled) {
-      return;
-    }
-
     // User gesture has already been taken care of inside |pointermove|
     // handlers, Do nothing here.
     if (this.handledInPointerMove_) {
@@ -165,12 +159,17 @@
    * @private
    */
   toggleState_: function(fromKeyboard) {
-    this.checked = !this.checked;
+    // Ignore cases where the 'click' or 'keypress' handlers are triggered while
+    // disabled.
+    if (this.disabled) {
+      return;
+    }
 
     if (!fromKeyboard) {
       this.hideRipple_();
     }
 
+    this.checked = !this.checked;
     this.fire('change', this.checked);
   },
 
diff --git a/ui/webui/resources/js/cr/ui/list.js b/ui/webui/resources/js/cr/ui/list.js
index a6948c9..b8d5e70 100644
--- a/ui/webui/resources/js/cr/ui/list.js
+++ b/ui/webui/resources/js/cr/ui/list.js
@@ -7,20 +7,6 @@
 // require: list_selection_controller.js
 // require: list_item.js
 
-cr.exportPath('cr.ui');
-
-/**
- *  @typedef {{
- *    height: number,
- *    marginBottom: number,
- *    marginLeft: number,
- *    marginRight: number,
- *    marginTop: number,
- *    width: number
- *  }}
- */
-cr.ui.Size;
-
 /**
  * @fileoverview This implements a list control.
  */
@@ -31,6 +17,18 @@
   /** @const */ const ArrayDataModel = cr.ui.ArrayDataModel;
 
   /**
+   *  @typedef {{
+   *    height: number,
+   *    marginBottom: number,
+   *    marginLeft: number,
+   *    marginRight: number,
+   *    marginTop: number,
+   *    width: number
+   *  }}
+   */
+  let Size;
+
+  /**
    * Whether a mouse event is inside the element viewport. This will return
    * false if the mouseevent was generated over a border or a scrollbar.
    * @param {!HTMLElement} el The element to test the event with.
@@ -1445,5 +1443,8 @@
     return false;
   }
 
-  return {List: List};
+  return {
+    List: List,
+    Size: Size,
+  };
 });
diff --git a/ui/webui/resources/js/cr/ui/menu_button.js b/ui/webui/resources/js/cr/ui/menu_button.js
index 33dc00c..85d6c45 100644
--- a/ui/webui/resources/js/cr/ui/menu_button.js
+++ b/ui/webui/resources/js/cr/ui/menu_button.js
@@ -4,24 +4,20 @@
 
 // <include src="../../assert.js">
 
-cr.exportPath('cr.ui');
-
-/**
- * Enum for type of hide. Delayed is used when called by clicking on a
- * checkable menu item.
- * @enum {number}
- */
-cr.ui.HideType = {
-  INSTANT: 0,
-  DELAYED: 1
-};
 
 cr.define('cr.ui', function() {
   /** @const */
   const Menu = cr.ui.Menu;
 
-  /** @const */
-  const HideType = cr.ui.HideType;
+  /**
+   * Enum for type of hide. Delayed is used when called by clicking on a
+   * checkable menu item.
+   * @enum {number}
+   */
+  const HideType = {
+    INSTANT: 0,
+    DELAYED: 1,
+  };
 
   /** @const */
   const positionPopupAroundElement = cr.ui.positionPopupAroundElement;
@@ -350,6 +346,7 @@
 
   // Export
   return {
+    HideType: HideType,
     MenuButton: MenuButton,
   };
 });
diff --git a/ui/webui/resources/js/cr/ui/node_utils.js b/ui/webui/resources/js/cr/ui/node_utils.js
index ed5912a..29c13ca4 100644
--- a/ui/webui/resources/js/cr/ui/node_utils.js
+++ b/ui/webui/resources/js/cr/ui/node_utils.js
@@ -2,54 +2,59 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-cr.exportPath('cr.ui');
-
-/**
- * Reverse the child elements of any found button strips if they haven't already
- * been reversed. This is necessary because WebKit does not alter the tab order
- * for elements that are visually reversed using flex-direction: reverse, and
- * the button order is reversed for views. See http://webk.it/62664 for more
- * information.
- * @param {Node=} opt_root Starting point for button strips to reverse.
- */
-cr.ui.reverseButtonStrips = function(opt_root) {
-  if (!(cr.isWindows || cr.isChromeOS)) {
-    // Only reverse on platforms that need it (differ from the HTML order).
-    return;
-  }
-
-  const root = opt_root || document;
-  const buttonStrips = root.querySelectorAll('.button-strip:not([reversed])');
-  for (let j = 0; j < buttonStrips.length; j++) {
-    const buttonStrip = buttonStrips[j];
-
-    const childNodes = buttonStrip.childNodes;
-    for (let i = childNodes.length - 1; i >= 0; i--) {
-      buttonStrip.appendChild(childNodes[i]);
-    }
-
-    buttonStrip.setAttribute('reversed', '');
-  }
-};
-
-/**
- * Finds a good place to set initial focus. Generally called when UI is shown.
- * @param {!Element} root Where to start looking for focusable controls.
- */
-cr.ui.setInitialFocus = function(root) {
-  // Do not change focus if any element in |root| is already focused.
-  if (root.contains(document.activeElement)) {
-    return;
-  }
-
-  const elements =
-      root.querySelectorAll('input, list, select, textarea, button');
-  for (let i = 0; i < elements.length; i++) {
-    const element = elements[i];
-    element.focus();
-    // .focus() isn't guaranteed to work. Continue until it does.
-    if (document.activeElement == element) {
+cr.define('cr.ui', function() {
+  /**
+   * Reverse the child elements of any found button strips if they haven't
+   * already been reversed. This is necessary because WebKit does not alter the
+   * tab order for elements that are visually reversed using flex-direction:
+   * reverse, and the button order is reversed for views. See
+   * http://webk.it/62664 for more information.
+   * @param {Node=} opt_root Starting point for button strips to reverse.
+   */
+  function reverseButtonStrips(opt_root) {
+    if (!(cr.isWindows || cr.isChromeOS)) {
+      // Only reverse on platforms that need it (differ from the HTML order).
       return;
     }
+
+    const root = opt_root || document;
+    const buttonStrips = root.querySelectorAll('.button-strip:not([reversed])');
+    for (let j = 0; j < buttonStrips.length; j++) {
+      const buttonStrip = buttonStrips[j];
+
+      const childNodes = buttonStrip.childNodes;
+      for (let i = childNodes.length - 1; i >= 0; i--) {
+        buttonStrip.appendChild(childNodes[i]);
+      }
+
+      buttonStrip.setAttribute('reversed', '');
+    }
   }
-};
+
+  /**
+   * Finds a good place to set initial focus. Generally called when UI is shown.
+   * @param {!Element} root Where to start looking for focusable controls.
+   */
+  function setInitialFocus(root) {
+    // Do not change focus if any element in |root| is already focused.
+    if (root.contains(document.activeElement)) {
+      return;
+    }
+
+    const elements =
+        root.querySelectorAll('input, list, select, textarea, button');
+    for (let i = 0; i < elements.length; i++) {
+      const element = elements[i];
+      element.focus();
+      // .focus() isn't guaranteed to work. Continue until it does.
+      if (document.activeElement == element) {
+        return;
+      }
+    }
+  }
+
+  return {
+    reverseButtonStrips: reverseButtonStrips,
+    setInitialFocus: setInitialFocus,
+  };
+});
diff --git a/ui/webui/resources/js/cr/ui/position_util.js b/ui/webui/resources/js/cr/ui/position_util.js
index edca43d0..0b9bd9f4 100644
--- a/ui/webui/resources/js/cr/ui/position_util.js
+++ b/ui/webui/resources/js/cr/ui/position_util.js
@@ -6,52 +6,47 @@
  * @fileoverview This file provides utility functions for position popups.
  */
 
-cr.exportPath('cr.ui');
-
-/**
- * Type def for rects as returned by getBoundingClientRect.
- * @typedef {{left: number, top: number, width: number, height: number,
- *            right: number, bottom: number}}
- */
-cr.ui.Rect;
-
-/**
- * Enum for defining how to anchor a popup to an anchor element.
- * @enum {number}
- */
-cr.ui.AnchorType = {
-  /**
-   * The popup's right edge is aligned with the left edge of the anchor.
-   * The popup's top edge is aligned with the top edge of the anchor.
-   */
-  BEFORE: 1,  // p: right, a: left, p: top, a: top
-
-  /**
-   * The popop's left edge is aligned with the right edge of the anchor.
-   * The popup's top edge is aligned with the top edge of the anchor.
-   */
-  AFTER: 2,  // p: left a: right, p: top, a: top
-
-  /**
-   * The popop's bottom edge is aligned with the top edge of the anchor.
-   * The popup's left edge is aligned with the left edge of the anchor.
-   */
-  ABOVE: 3,  // p: bottom, a: top, p: left, a: left
-
-  /**
-   * The popop's top edge is aligned with the bottom edge of the anchor.
-   * The popup's left edge is aligned with the left edge of the anchor.
-   */
-  BELOW: 4  // p: top, a: bottom, p: left, a: left
-};
-
 cr.define('cr.ui', function() {
-  /** @const */
-  const AnchorType = cr.ui.AnchorType;
+  /**
+   * Type def for rects as returned by getBoundingClientRect.
+   * @typedef {{left: number, top: number, width: number, height: number,
+   *            right: number, bottom: number}}
+   */
+  let Rect;
+
+  /**
+   * Enum for defining how to anchor a popup to an anchor element.
+   * @enum {number}
+   */
+  const AnchorType = {
+    /**
+     * The popup's right edge is aligned with the left edge of the anchor.
+     * The popup's top edge is aligned with the top edge of the anchor.
+     */
+    BEFORE: 1,  // p: right, a: left, p: top, a: top
+
+    /**
+     * The popop's left edge is aligned with the right edge of the anchor.
+     * The popup's top edge is aligned with the top edge of the anchor.
+     */
+    AFTER: 2,  // p: left a: right, p: top, a: top
+
+    /**
+     * The popop's bottom edge is aligned with the top edge of the anchor.
+     * The popup's left edge is aligned with the left edge of the anchor.
+     */
+    ABOVE: 3,  // p: bottom, a: top, p: left, a: left
+
+    /**
+     * The popop's top edge is aligned with the bottom edge of the anchor.
+     * The popup's left edge is aligned with the left edge of the anchor.
+     */
+    BELOW: 4  // p: top, a: bottom, p: left, a: left
+  };
 
   /**
    * Helper function for positionPopupAroundElement and positionPopupAroundRect.
-   * @param {!cr.ui.Rect} anchorRect The rect for the anchor.
+   * @param {!Rect} anchorRect The rect for the anchor.
    * @param {!HTMLElement} popupElement The element used for the popup.
    * @param {cr.ui.AnchorType} type The type of anchoring to do.
    * @param {boolean=} opt_invertLeftRight Whether to invert the right/left
@@ -243,6 +238,7 @@
 
   // Export
   return {
+    AnchorType: AnchorType,
     positionPopupAroundElement: positionPopupAroundElement,
     positionPopupAtPoint: positionPopupAtPoint
   };